ndp.lua

changeset 0
44416491923e
child 1
29a492373a7c
equal deleted inserted replaced
-1:000000000000 0:44416491923e
1 module(..., package.seeall);
2
3 require "luarocks.require"
4 require "lpeg"
5
6 -- Add case-insensitive string matching to Lpeg
7 function lpeg.Pi(s)
8 local patt = lpeg.P(true);
9 for c in s:gmatch(".") do
10 patt = patt * (lpeg.P(c:lower()) + lpeg.P(c:upper()));
11 end
12 return patt;
13 end
14
15 function lpeg.one_of(list)
16 local patt = lpeg.P(false);
17 for _, match in ipairs(list) do
18 patt = patt + lpeg.Pi(match);
19 end
20 return patt;
21 end
22
23 local ordinal = lpeg.P{ lpeg.C(lpeg.R("09")^-2) * (lpeg.Pi("st") + lpeg.Pi("nd") + lpeg.Pi("rd") + lpeg.Pi("th")) + 1 * lpeg.V(1) };
24 local number = lpeg.R "09"^1
25
26 local day_name = lpeg.one_of {'monday', 'tuesday', 'wednesday',
27 'thursday', 'friday', 'saturday', 'sunday'}
28
29 local month_name = lpeg.one_of {'january', 'february', 'march', 'april', 'may', 'june',
30 'july', 'august', 'september', 'october', 'november', 'december' }
31
32 local year = (lpeg.R("09")^4)^-4
33
34 local unit_of_time = lpeg.one_of { 'second', 'minute', 'hour', 'day', 'week', 'month', 'year' }
35
36 local time_of_day = lpeg.one_of { 'morning', 'noon', 'afternoon', 'evening', 'night', 'midnight' }
37 local time_of_days = { morning = 09, noon = 12, afternoon = 13, evening = 17, night = 21, midnight = 00 }
38
39 local quantity;
40 local quantities = {
41 ["a"] = 1;
42 ["an"] = 1;
43
44 ["a couple of"] = 2;
45
46 ["a few"] = 3;
47 ["several"] = 3;
48 };
49
50 -- Create 'quantity' to match any of the quantities we know
51 do
52 local quantity_list = {};
53 for k in pairs(quantities) do
54 quantity_list[#quantity_list+1] = k;
55 end
56 table.sort(quantity_list, function (a,b) return #a>#b; end);
57 quantity = number + lpeg.one_of(quantity_list);
58 end
59
60 seconds_in_a = { second = 1 }
61 seconds_in_a.minute = seconds_in_a.second * 60;
62 seconds_in_a.hour = seconds_in_a.minute * 60;
63 seconds_in_a.day = seconds_in_a.hour * 24;
64 seconds_in_a.week = seconds_in_a.day * 7;
65 seconds_in_a.month = seconds_in_a.week * 4;
66 seconds_in_a.year = seconds_in_a.day * 365;
67
68 local function get_time_part(time, part)
69 return os.date("*t", time)[part];
70 end
71
72 local function adjust_time(time, part, value)
73 local split_time = os.date("*t", time);
74
75 split_time[part] = value;
76
77 return os.time(split_time);
78 end
79
80 function when(str, relative_to)
81 local time = relative_to or os.time();
82 local P = lpeg.P;
83
84 local patterns =
85 {
86 { P"tomorrow" /
87 function ()
88 time = time + seconds_in_a.day;
89 end };
90 { P"next week" /
91 function ()
92 time = time + seconds_in_a.week;
93 end };
94 { P"next year" /
95 function ()
96 time = adjust_time(time, "year", get_time_part(time, "year") + 1);
97 end };
98 { P"on "^0 * day_name /
99 function (day_name)
100 time = find_nearest_day_by_name(time, day_name);
101 end };
102 { P"in "^0 * month_name /
103 function (month_name)
104 time = find_nearest_month_by_name(time, month_name);
105 end };
106 { P"in "^0 * ( quantity * P" " * unit_of_time ) * (P"s"^-1) /
107 function (number_and_unit)
108 local number, unit = number_and_unit:gsub("^in ", ""):match("^(.+)%s+(.-)s?$");
109
110 number = quantities[number] or tonumber(number);
111
112 time = time + seconds_in_a[unit] * number;
113 end };
114 { (P"this " + P"in the " + P"at ")^0 * time_of_day /
115 function (time_of_day)
116 time_of_day = time_of_day:match("%S+$");
117 time = adjust_time(time, "hour", time_of_days[time_of_day]);
118 if time_of_day == "noon" or time_of_day == "midnight" then
119 time = adjust_time(time, "min", 00);
120 else
121 time = adjust_time(time, "min", 30);
122 end
123 end };
124 }
125
126 local ret, pos;
127 for _, pattern in pairs(patterns) do
128 ret = lpeg.match(lpeg.P{ pattern[1] + 1 * lpeg.V(1) }, str);
129 if ret then
130 pos = ret;
131 --print("Matches ".._.." until "..ret);
132 end
133 end
134
135 return time, pos;
136 end
137

mercurial