OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #import('pegparser.dart'); | |
6 | |
7 testParens() { | |
8 Grammar g = new Grammar(); | |
9 Symbol a = g['A']; | |
10 | |
11 a.def = ['(', MANY(a, min:0), ')', (a) => a]; | |
12 | |
13 check(g, a, "", null); | |
14 check(g, a, "()", '[]'); | |
15 check(g, a, "(()())", '[[],[]]'); | |
16 check(g, a, "(()((()))())", '[[],[[[]]],[]]'); | |
17 } | |
18 | |
19 testBlockComment() { | |
20 | |
21 // Block comment in whitespace. | |
22 | |
23 Grammar g = new Grammar(); | |
24 Symbol blockComment = g['blockComment']; | |
25 | |
26 blockComment.def = | |
27 ['/*', | |
28 MANY(OR([blockComment, | |
29 [NOT('*/'), CHAR()], | |
30 [END, ERROR('EOF in block comment')] | |
31 ]), | |
32 min: 0), | |
33 '*/']; | |
34 print(blockComment); | |
35 | |
36 var a = MANY(TEXT('x')); | |
37 | |
38 g.whitespace = OR([g.whitespace, blockComment]); | |
39 | |
40 check(g, a, "x /**/ x", '[x,x]'); | |
41 check(g, a, "x /*/**/*/ x", '[x,x]'); | |
42 check(g, a, "x /*/***/ x", 'EOF in block comment'); | |
43 check(g, a, "x /*/*/x**/**/ x", '[x,x]'); | |
44 | |
45 check(g, a, @""" | |
46 /* Comment */ | |
47 /* Following comment with /* nested comment*/ */ | |
48 x | |
49 /* x in comment */ | |
50 x /* outside comment */ | |
51 """, | |
52 '[x,x]'); | |
53 } | |
54 | |
55 testTEXT() { | |
56 Grammar g = new Grammar(); | |
57 | |
58 // TEXT grabs the parsed text, | |
59 check(g, TEXT(LEX(MANY(OR(['1','a'])))), ' 1a1 ', '1a1'); | |
60 | |
61 // Without the lexical context, TEXT will grab intervening whitespace. | |
62 check(g, TEXT(MANY(OR(['1','a']))), ' 1a1 ', '1a1'); | |
63 check(g, TEXT(MANY(OR(['1','a']))), ' 1 a 1 ', '1 a 1'); | |
64 | |
65 // Custom processing of the TEXT substring. | |
66 var binaryNumber = | |
67 TEXT(LEX(MANY(OR(['0','1']))), | |
68 (str, start, end) { | |
69 var r = 0; | |
70 var zero = '0'.charCodeAt(0); | |
71 for (int i = start; i < end; i++) | |
72 r = r * 2 + (str.charCodeAt(i) - zero); | |
73 return r; | |
74 }); | |
75 | |
76 check(g, binaryNumber, ' 10101 ', 21); | |
77 check(g, binaryNumber, '1010111', 87); | |
78 check(g, binaryNumber, '1010 111', null); | |
79 } | |
80 | |
81 testOR() { | |
82 // OR matches the first match. | |
83 Grammar g = new Grammar(); | |
84 check(g, OR([['a', NOT(END), () => 1], | |
85 ['a', () => 2], | |
86 ['a', () => 3]]), | |
87 'a', 2); | |
88 } | |
89 | |
90 testCODE() { | |
91 Grammar g = new Grammar(); | |
92 var a = TEXT(LEX('thing', MANY(CHAR('bcd')))); | |
93 | |
94 check(g, a, 'bbb', 'bbb'); | |
95 check(g, a, 'ccc', 'ccc'); | |
96 check(g, a, 'ddd', 'ddd'); | |
97 check(g, a, 'bad', null); // a is outside range. | |
98 check(g, a, 'bed', null); // e is outside range. | |
99 } | |
100 | |
101 testC() { | |
102 // Curried tree builders. | |
103 binary(operation) => (second) => (first) => [operation, first, second]; | |
104 unary(operation) => () => (first) => [operation, first]; | |
105 reform(a, fns) { | |
106 var r = a; | |
107 for (var fn in fns) | |
108 r = fn(r); | |
109 return r; | |
110 } | |
111 | |
112 Grammar g = new Grammar(); | |
113 | |
114 Symbol expression = g['expression']; | |
115 Symbol postfix_e = g['postfix_e']; | |
116 Symbol unary_e = g['unary_e']; | |
117 Symbol cast_e = g['cast_e']; | |
118 Symbol mult_e = g['mult_e']; | |
119 Symbol add_e = g['add_e']; | |
120 Symbol shift_e = g['shift_e']; | |
121 Symbol relational_e = g['relational_e']; | |
122 Symbol equality_e = g['equality_e']; | |
123 Symbol cond_e = g['cond_e']; | |
124 Symbol assignment_e = g['assignment_e']; | |
125 | |
126 // Lexical elements. | |
127 var idStartChar = CHAR( | |
128 @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); | |
129 var idNextChar = CHAR( | |
130 @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_"); | |
131 | |
132 var id = TEXT(LEX('identifier', [idStartChar, MANY(idNextChar, min: 0)])); | |
133 | |
134 var lit = TEXT(LEX('literal', MANY(CHAR('0123456789')))); | |
135 | |
136 | |
137 var type_name = id; | |
138 | |
139 | |
140 // Expression grammar. | |
141 var primary_e = OR([id, | |
142 lit, | |
143 ['(', expression, ')', (e) => e] | |
144 ]); | |
145 | |
146 var postfixes = OR([['(', MANY(assignment_e, ',', 0), ')', binary('apply')], | |
147 ['++', unary('postinc')], | |
148 ['--', unary('postdec')], | |
149 ['.', id, binary('field')], | |
150 ['->', id, binary('ptr')], | |
151 ]); | |
152 | |
153 postfix_e.def = [primary_e, MANY(postfixes, min:0), reform]; | |
154 | |
155 | |
156 var unary_op = OR([['&', () => 'address'], | |
157 ['*', () => 'indir'], | |
158 ['!', () => 'not'], | |
159 ['~', () => 'not'], | |
160 ['-', () => 'negate'], | |
161 ['+', () => 'uplus'], | |
162 ]); | |
163 var sizeof = LEX('sizeof', ['sizeof', NOT(idNextChar)]); | |
164 | |
165 Symbol unary_e_plain = g['unary_e_plain']; | |
166 unary_e_plain.def = | |
167 OR([ ['++', unary_e, (e) => ['preinc', e]], | |
168 ['--', unary_e, (e) => ['predec', e]], | |
169 [unary_op, cast_e, (o, e) => [o, e]], | |
170 [sizeof, unary_e, (e) => ['sizeof-expr', e]], | |
171 [sizeof, '(', type_name , ')', (t) => ['sizeof-type', t]], | |
172 postfix_e | |
173 ]); | |
174 | |
175 unary_e.def = MEMO(unary_e_plain); | |
176 //unary_e.def = unary_e_plain; | |
177 | |
178 cast_e.def = OR([ ['(', type_name, ')', cast_e, (t, e) => ['cast', t, e]], | |
179 unary_e, | |
180 ]); | |
181 | |
182 var mult_ops = OR([['*', cast_e, binary('mult')], | |
183 ['/', cast_e, binary('div')], | |
184 ['%', cast_e, binary('rem')], | |
185 ]); | |
186 mult_e.def = [cast_e, MANY(mult_ops, min:0), reform]; | |
187 | |
188 var add_ops = OR([['+', mult_e, binary('add')], | |
189 ['-', mult_e, binary('sub')], | |
190 ]); | |
191 add_e.def = [mult_e, MANY(add_ops, min:0), reform]; | |
192 | |
193 var shift_ops = OR([['>>', add_e, binary('shl')], | |
194 ['<<', add_e, binary('shr')], | |
195 ]); | |
196 shift_e.def = [add_e, MANY(shift_ops, min:0), reform]; | |
197 | |
198 var relational_ops = OR([['<=', shift_e, binary('le')], | |
199 ['>=', shift_e, binary('ge')], | |
200 ['<', shift_e, binary('lt')], | |
201 ['>', shift_e, binary('gt')], | |
202 ]); | |
203 relational_e.def = [shift_e, MANY(relational_ops, min:0), reform]; | |
204 | |
205 | |
206 var equality_ops = OR([['==', shift_e, binary('eq')], | |
207 ['!=', shift_e, binary('ne')], | |
208 ]); | |
209 equality_e.def = [relational_e, MANY(equality_ops, min:0), reform]; | |
210 | |
211 | |
212 var bit_and_op = LEX('&', ['&', NOT('&')]); // Don't see '&&' and '&', '&' | |
213 var bit_or_op = LEX('|', ['|', NOT('|')]); | |
214 | |
215 var and_e = [equality_e, MANY([bit_and_op, equality_e, binary('bitand')], min:
0), reform]; | |
216 var xor_e = [and_e, MANY(['^', and_e, binary('bitxor')], min:0), reform]; | |
217 var or_e = [xor_e, MANY([bit_or_op, xor_e, binary('bitor')], min:0), reform]; | |
218 | |
219 var log_and_e = [or_e, MANY(['&&', or_e, binary('and')], min:0), reform]; | |
220 | |
221 var log_or_e = [log_and_e, MANY(['||', log_and_e, binary('or')], min:0), refor
m]; | |
222 | |
223 //cond_e.def = OR([ [log_or_e, '?', expression, ':', cond_e, | |
224 // (p,a,b) => ['cond', p, a, b]], | |
225 // log_or_e]); | |
226 // Alternate version avoids reparsing log_or_e. | |
227 cond_e.def = [log_or_e, MAYBE(['?', expression, ':', cond_e]), | |
228 (p, r) => r == null || r == false ? p : ['cond', p, r[0], r[1]]]
; | |
229 | |
230 var assign_op = OR([['*=', () => 'mulassign'], | |
231 ['=', () => 'assign']]); | |
232 | |
233 // TODO: Figure out how not to re-parse a unary_e. | |
234 // Order matters - cond_e can't go first since cond_e will succeed on, e.g. 'a
'. | |
235 assignment_e.def = OR([[unary_e, assign_op, assignment_e, | |
236 (u, op, a) => [op, u, a]], | |
237 cond_e]); | |
238 | |
239 expression.def = [assignment_e, | |
240 MANY([',', assignment_e, binary('comma')], min:0), | |
241 reform]; | |
242 | |
243 show(g, expression, 'a'); | |
244 check(g, expression, 'a', 'a'); | |
245 check(g, expression, '(a)', 'a'); | |
246 check(g, expression, ' ( ( a ) ) ', 'a'); | |
247 | |
248 check(g, expression, 'a(~1,2)', '[apply,a,[[not,1],2]]'); | |
249 check(g, expression, 'a(1)(x,2)', '[apply,[apply,a,[1]],[x,2]]'); | |
250 check(g, expression, 'a(1,2())', '[apply,a,[1,[apply,2,[]]]]'); | |
251 | |
252 check(g, expression, '++a++', '[preinc,[postinc,a]]'); | |
253 check(g, expression, 'a++++b', null); | |
254 check(g, expression, 'a++ ++b', null); | |
255 check(g, expression, 'a+ +++b', '[add,a,[preinc,[uplus,b]]]'); | |
256 check(g, expression, 'a+ + ++b', '[add,a,[uplus,[preinc,b]]]'); | |
257 check(g, expression, 'a+ + + +b', '[add,a,[uplus,[uplus,[uplus,b]]]]'); | |
258 check(g, expression, 'a+ ++ +b', '[add,a,[preinc,[uplus,b]]]'); | |
259 check(g, expression, 'a++ + +b', '[add,[postinc,a],[uplus,b]]'); | |
260 check(g, expression, 'a+++ +b', '[add,[postinc,a],[uplus,b]]'); | |
261 | |
262 check(g, expression, '((T)f)(x)', '[apply,[cast,T,f],[x]]'); | |
263 check(g, expression, '(T)f(x)', '[cast,T,[apply,f,[x]]]'); | |
264 | |
265 check(g, expression, 'a++*++b', '[mult,[postinc,a],[preinc,b]]'); | |
266 | |
267 check(g, expression, 'a<<1>>++b', '[shl,[shr,a,1],[preinc,b]]'); | |
268 | |
269 check(g, expression, 'a<1&&b', '[and,[lt,a,1],b]'); | |
270 | |
271 check(g, expression, 'a<1 & &b', '[bitand,[lt,a,1],[address,b]]'); | |
272 check(g, expression, | |
273 'a ? b ? c : d : e ? f : g', | |
274 '[cond,a,[cond,b,c,d],[cond,e,f,g]]'); | |
275 | |
276 check(g, expression, 'a,b,c', '[comma,[comma,a,b],c]'); | |
277 check(g, expression, 'a=1,b,c', '[comma,[comma,[assign,a,1],b],c]'); | |
278 | |
279 check(g, expression, | |
280 '((((((((((((a))))))))))))=1,b,c', '[comma,[comma,[assign,a,1],b],c]'); | |
281 | |
282 check(g, expression, 'sizeof a', '[sizeof-expr,a]'); | |
283 check(g, expression, 'sizeofa', 'sizeofa'); | |
284 check(g, expression, 'sizeof (a)', '[sizeof-expr,a]'); | |
285 } | |
286 | |
287 | |
288 show(grammar, rule, input) { | |
289 print('show: "$input"'); | |
290 var ast; | |
291 try { | |
292 ast = grammar.parse(rule, input); | |
293 } catch (var exception) { | |
294 if (exception is ParseError) | |
295 ast = exception; | |
296 else | |
297 throw; | |
298 } | |
299 print('${printList(ast)}'); | |
300 } | |
301 | |
302 void check(grammar, rule, input, expected) { | |
303 // If [expected] is String then the result is coerced to string. | |
304 // If [expected] is !String, the result is compared directly. | |
305 print('check: "$input"'); | |
306 var ast; | |
307 try { | |
308 ast = grammar.parse(rule, input); | |
309 } catch (var exception) { | |
310 ast = exception; | |
311 } | |
312 | |
313 var formatted = ast; | |
314 if (expected is String) | |
315 formatted = printList(ast); | |
316 | |
317 Expect.equals(expected, formatted, "parse: $input"); | |
318 } | |
319 | |
320 // Prints the list in [1,2,3] notation, including nested lists. | |
321 void printList(item) { | |
322 if (item is List) { | |
323 StringBuffer sb = new StringBuffer(); | |
324 sb.add('['); | |
325 var sep = ''; | |
326 for (var x in item) { | |
327 sb.add(sep); | |
328 sb.add(printList(x)); | |
329 sep = ','; | |
330 } | |
331 sb.add(']'); | |
332 return sb.toString(); | |
333 } | |
334 if (item == null) | |
335 return 'null'; | |
336 return item.toString(); | |
337 } | |
338 | |
339 main() { | |
340 testCODE(); | |
341 testParens(); | |
342 testOR(); | |
343 testTEXT(); | |
344 testBlockComment(); | |
345 testC(); | |
346 } | |
OLD | NEW |