forked from linkedin/dustjs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dust.pegjs
255 lines (219 loc) · 11.5 KB
/
dust.pegjs
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
{
function makeInteger(arr) {
return parseInt(arr.join(''), 10);
}
function withPosition(arr) {
arr.location = location();
return arr;
}
}
start
= body
/*-------------------------------------------------------------------------------------------------------------------------------------
body is defined as anything that matches with the part 0 or more times
---------------------------------------------------------------------------------------------------------------------------------------*/
body
= p:part* {
var body = ["body"].concat(p);
return withPosition(body);
}
/*-------------------------------------------------------------------------------------------------------------------------------------
part is defined as anything that matches with raw or comment or section or partial or special or reference or buffer
---------------------------------------------------------------------------------------------------------------------------------------*/
part
= raw / comment / section / partial / special / reference / buffer
/*-------------------------------------------------------------------------------------------------------------------------------------
section is defined as matching with with sec_tag_start followed by 0 or more white spaces plus a closing brace plus body
plus bodies plus end_tag or sec_tag_start followed by a slash and closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
section "section"
= t:sec_tag_start ws* rd b:body e:bodies n:end_tag?
// non-self-closing format
&{
if( (!n) || (t[1].text !== n.text) ) {
error("Expected end tag for "+t[1].text+" but it was not found.");
}
return true;
}
{
e.push(["param", ["literal", "block"], b]);
t.push(e, ["filters"]);
return withPosition(t)
}
// self-closing format
/ t:sec_tag_start ws* "/" rd
{
t.push(["bodies"], ["filters"]);
return withPosition(t)
}
/*-------------------------------------------------------------------------------------------------------------------------------------
sec_tag_start is defined as matching an opening brace followed by one of #?^<+@% plus identifier plus context plus param
followed by 0 or more white spaces
---------------------------------------------------------------------------------------------------------------------------------------*/
sec_tag_start
= ld t:[#?^<+@%] ws* n:identifier c:context p:params
{ return [t, n, c, p] }
/*-------------------------------------------------------------------------------------------------------------------------------------
end_tag is defined as matching an opening brace followed by a slash plus 0 or more white spaces plus identifier followed
by 0 or more white spaces and ends with closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
end_tag "end tag"
= ld "/" ws* n:identifier ws* rd
{ return n }
/*-------------------------------------------------------------------------------------------------------------------------------------
context is defined as matching a colon followed by an identifier
---------------------------------------------------------------------------------------------------------------------------------------*/
context
= n:(":" n:identifier {return n})?
{ return n ? ["context", n] : ["context"] }
/*-------------------------------------------------------------------------------------------------------------------------------------
params is defined as matching white space followed by = and identfier or inline
---------------------------------------------------------------------------------------------------------------------------------------*/
params "params"
= p:(ws+ k:key "=" v:(number / identifier / inline) {return ["param", ["literal", k], v]})*
{ return ["params"].concat(p) }
/*-------------------------------------------------------------------------------------------------------------------------------------
bodies is defined as matching a opening brace followed by key and closing brace, plus body 0 or more times.
---------------------------------------------------------------------------------------------------------------------------------------*/
bodies "bodies"
= p:(ld ":" k:key rd v:body {return ["param", ["literal", k], v]})*
{ return ["bodies"].concat(p) }
/*-------------------------------------------------------------------------------------------------------------------------------------
reference is defined as matching a opening brace followed by an identifier plus one or more filters and a closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
reference "reference"
= ld n:identifier f:filters rd
{ return withPosition(["reference", n, f]) }
/*-------------------------------------------------------------------------------------------------------------------------------------
partial is defined as matching a opening brace followed by a > plus anything that matches with key or inline plus
context followed by slash and closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
partial "partial"
= ld s:(">"/"+") ws* n:(k:key {return ["literal", k]} / inline) c:context p:params ws* "/" rd
{
var key = (s === ">") ? "partial" : s;
return withPosition([key, n, c, p])
}
/*-------------------------------------------------------------------------------------------------------------------------------------
filters is defined as matching a pipe character followed by anything that matches the key
---------------------------------------------------------------------------------------------------------------------------------------*/
filters "filters"
= f:("|" n:key {return n})*
{ return ["filters"].concat(f) }
/*-------------------------------------------------------------------------------------------------------------------------------------
special is defined as matching a opening brace followed by tilde plus key plus a closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
special "special"
= ld "~" k:key rd
{ return withPosition(["special", k]) }
/*-------------------------------------------------------------------------------------------------------------------------------------
identifier is defined as matching a path or key
---------------------------------------------------------------------------------------------------------------------------------------*/
identifier "identifier"
= p:path
{
var arr = ["path"].concat(p);
arr.text = p[1].join('.');
return arr;
}
/ k:key
{
var arr = ["key", k];
arr.text = k;
return arr;
}
number "number"
= n:(float / integer) { return ['literal', n]; }
float "float"
= l:integer "." r:unsigned_integer { return parseFloat(l + "." + r); }
unsigned_integer "unsigned_integer"
= digits:[0-9]+ { return makeInteger(digits); }
signed_integer "signed_integer"
= sign:'-' n:unsigned_integer { return n * -1; }
integer "integer"
= signed_integer / unsigned_integer
/*-------------------------------------------------------------------------------------------------------------------------------------
path is defined as matching a key plus one or more characters of key preceded by a dot
---------------------------------------------------------------------------------------------------------------------------------------*/
path "path"
= k:key? d:(array_part / array)+
{
d = d[0];
if (k && d) {
d.unshift(k);
return withPosition([false, d])
}
return withPosition([true, d])
}
/ "." d:(array_part / array)*
{
if (d.length > 0) {
return withPosition([true, d[0]])
}
return withPosition([true, []])
}
/*-------------------------------------------------------------------------------------------------------------------------------------
key is defined as a character matching a to z, upper or lower case, followed by 0 or more alphanumeric characters
---------------------------------------------------------------------------------------------------------------------------------------*/
key "key"
= h:[a-zA-Z_$] t:[0-9a-zA-Z_$-]*
{ return h + t.join('') }
array "array"
= i:( lb a:( n:([0-9]+) {return n.join('')} / identifier) rb {return a; }) nk: array_part? { if(nk) { nk.unshift(i); } else {nk = [i] } return nk; }
array_part "array_part"
= d:("." k:key {return k})+ a:(array)? { if (a) { return d.concat(a); } else { return d; } }
/*-------------------------------------------------------------------------------------------------------------------------------------
inline params is defined as matching two double quotes or double quotes plus literal followed by closing double quotes or
double quotes plus inline_part followed by the closing double quotes
---------------------------------------------------------------------------------------------------------------------------------------*/
inline "inline"
= '"' '"' { return withPosition(["literal", ""]) }
/ '"' l:literal '"' { return withPosition(["literal", l]) }
/ '"' p:inline_part+ '"' { return withPosition(["body"].concat(p)) }
/*-------------------------------------------------------------------------------------------------------------------------------------
inline_part is defined as matching a special or reference or literal
---------------------------------------------------------------------------------------------------------------------------------------*/
inline_part
= special / reference / l:literal { return ["buffer", l] }
buffer "buffer"
= e:eol w:ws*
{ return withPosition(["format", e, w.join('')]) }
/ b:(!tag !raw !comment !eol c:. {return c})+
{ return withPosition(["buffer", b.join('')]) }
/*-------------------------------------------------------------------------------------------------------------------------------------
literal is defined as matching esc or any character except the double quotes and it cannot be a tag
---------------------------------------------------------------------------------------------------------------------------------------*/
literal "literal"
= b:(!tag c:(esc / [^"]) {return c})+
{ return b.join('') }
esc
= '\\"' { return '"' }
raw "raw"
= "{`" rawText:(!"`}" character:. {return character})* "`}"
{ return withPosition(["raw", rawText.join('')]) }
comment "comment"
= "{!" c:(!"!}" c:. {return c})* "!}"
{ return withPosition(["comment", c.join('')]) }
/*-------------------------------------------------------------------------------------------------------------------------------------
tag is defined as matching an opening brace plus any of #?^><+%:@/~% plus 0 or more whitespaces plus any character or characters that
doesn't match rd or eol plus 0 or more whitespaces plus a closing brace
---------------------------------------------------------------------------------------------------------------------------------------*/
tag
= ld ws* [#?^><+%:@/~%] ws* (!rd !eol .)+ ws* rd
/ reference
ld
= "{"
rd
= "}"
lb
= "["
rb
= "]"
eol
= "\n" //line feed
/ "\r\n" //carriage + line feed
/ "\r" //carriage return
/ "\u2028" //line separator
/ "\u2029" //paragraph separator
ws
= [\t\v\f \u00A0\uFEFF] / eol