filterlang.pegjs 5.13 KB
Newer Older
ransome1's avatar
ransome1 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{{
    import { addIntervalToDate } from "./recurrences.mjs";
}}

filterQuery
    = _ left:orExpr _  { return left; }
    / _ { return []; }

orExpr
    = left:andExpr _ OrOp _ right:orExpr { return left.concat(right, ["||"]); }
    / left:andExpr { return left; }

andExpr
    = left:notExpr _ AndOp _ right:andExpr { return left.concat(right, ["&&"]); }
    / left:notExpr { return left; }

notExpr
    = NotOp _ left:notExpr { return left.concat(["!!"]); }
    / left:boolExpr { return left; }

boolExpr
    = left:project { return left; }
    / left:context { return left; }
    / "(" _ left:orExpr _ ")" { return left; }
    / left:comparison  { return left; }
    / "complete"  { return ["complete"]; }
    / left:StringLiteral  { return ["string", left]; }
    / left:RegexLiteral  { return ["regex", left]; }

project
    = "+" left:name      { return ["++", left]; }
    / "+"               { return ["++", "*"]; }

context
    = "@" left:name      { return ["@@", left]; }
    / "@"               { return ["@@", "*"]; }

OrOp
    = "||"
    / "or"i

AndOp
    = "&&"
    / "and"i

NotOp
    = "!"
    / "not"i

comparison
    = left:priorityComparison { return left; }
    / left:dueComparison { return left; }
53
    / left:thresholdComparison { return left; }
ransome1's avatar
ransome1 committed
54
55

priorityComparison
zerodat's avatar
zerodat committed
56
57
58
    = priorityKeyword _ op:compareOp _ right:priorityLiteral  { return ["pri", right, op]; }
    / priorityKeyword  { return ["pri"]; }
    / "(" right:priorityLiteral ")"  { return ["pri", right, "=="]; }
ransome1's avatar
ransome1 committed
59
60
61
62
63
64
65
66
67

priorityLiteral
    = [A-Z]  { return text(); }

priorityKeyword
    = "pri" ("o" ("r" ("i" ("t" "y"?)?)?)?)?

dueComparison
    = "due" _ op:compareOp _ right:dateExpr  { return ["due"].concat(right, [op]); }
zerodat's avatar
zerodat committed
68
    / "due:" right:dateStr  { return ["duestr", right]; }
ransome1's avatar
ransome1 committed
69
70
    / "due"  { return ["due"]; }

71
72
73
74
75
thresholdComparison
    = "t" _ op:compareOp _ right:dateExpr  { return ["threshold"].concat(right, [op]); }
    / "t:" right:dateStr  { return ["tstr", right]; }
    / "t"  { return ["threshold"]; }

ransome1's avatar
ransome1 committed
76
77
dateExpr
    = left:dateLiteral _ op:dateOp _ count:number unit:[dbwmy]  {
78
79
80
81
        if (count.length == 0) {
            /* empty count string means default "1" value */
            count = 1;
        }
ransome1's avatar
ransome1 committed
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
        if (op == "-") {
            count = count * -1;
        }
        // we do our date math with the same code as we use for
        // recurrence calculations.  All dates are returned from
        // the parser as millisec since epoch (getTime()) to
        // simplify comparisons in the filter lang execution engine.
        let d = addIntervalToDate(new Date(left), count, unit);
        return d.getTime();
    }
    / left:dateLiteral  { return left; }

dateOp
    = "+"  { return text(); }
    / "-"  { return text(); }

compareOp
    = "=="  { return text(); }
zerodat's avatar
zerodat committed
100
    / "="   { return "==";   }
ransome1's avatar
ransome1 committed
101
102
103
104
105
106
    / "!="  { return text(); }
    / ">="  { return text(); }
    / "<="  { return text(); }
    / ">"   { return text(); }
    / "<"   { return text(); }

zerodat's avatar
zerodat committed
107
108
109
dateStr
    = [0-9]+ ("-" [0-9]+ ("-" [0-9]+)?)? { return text(); }

ransome1's avatar
ransome1 committed
110
111
dateLiteral
    = year:number4 "-" month: number2 "-" day:number2  {
112
113
114
        let m = month > 0 ? (month <= 12 ? month-1 : 11) : 0;
        let d = day > 0 ? (day <= 31 ? day : 31) : 1;  /* ignores lengths of months */
        return new Date(year, m, d).getTime();
ransome1's avatar
ransome1 committed
115
    }
zerodat's avatar
zerodat committed
116
    / year:number4 "-" month: number2  {
117
118
        let m = month > 0 ? (month <= 12 ? month-1 : 11) : 0;
        return new Date(year, m, 1).getTime();
zerodat's avatar
zerodat committed
119
120
    }
    / year:number4  {
121
        return new Date(year, 0, 1).getTime();
zerodat's avatar
zerodat committed
122
    }
ransome1's avatar
ransome1 committed
123
    / "today" {
124
        let d = new Date();  // now, w current time of day
ransome1's avatar
ransome1 committed
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
        return d.getTime();
    }
    / "tomorrow" {
        let d = new Date();  // now, w current time of day
        d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
        return d.getTime() + 24*60*60*1000;
    }
    / "yesterday" {
        let d = new Date();  // now, w current time of day
        d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
        return d.getTime() - 24*60*60*1000;
    }

number4
    = [0-9][0-9][0-9][0-9] { return text(); }

number2
143
    = [0-9][0-9]? { return text(); }
ransome1's avatar
ransome1 committed
144
145

number
146
    = [0-9]* { return text(); }  /* used in date intervals only */
ransome1's avatar
ransome1 committed
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

StringLiteral "string"
    = '"' chars:DoubleStringCharacter* '"'? {
        return chars.join("");
    }
    / "'" chars:SingleStringCharacter* "'"? {
        return chars.join("");
    }

DoubleStringCharacter
    = '\\' '"' { return '"'; }
    / !'"' SourceCharacter { return text(); }

SingleStringCharacter
    = '\\' "'" { return "'"; }
    / !"'" SourceCharacter { return text(); }

RegexLiteral "regex"
    = "/" chars:RegexCharacter* "/"  "i" {
        return new RegExp(chars.join(""), "i");
    }
    / "/" chars:RegexCharacter* "/"? {
        return new RegExp(chars.join(""));
    }

RegexCharacter
    = "\\" "/" { return "/"; }
    / !"/" SourceCharacter { return text(); }

SourceCharacter
    = .

name
180
181
182
    = '"' nonblank+ '"'         { return text(); }
    / nonblankparen+  '"'       { return '"' + text(); }
    / nonblankparen+            { return text(); }
ransome1's avatar
ransome1 committed
183
184

nonblank
zerodat's avatar
zerodat committed
185
186
187
    = [^ \t\n\r"]

nonblankparen
ransome1's avatar
ransome1 committed
188
189
190
    = [^ \t\n\r"()]

_ "whitespace"
191
    = [ \t\n\r]*