OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, 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 library parser_test; | |
6 | |
7 import 'package:polymer_expressions/parser.dart'; | |
8 import 'package:polymer_expressions/expression.dart'; | |
9 import 'package:unittest/unittest.dart'; | |
10 | |
11 expectParse(String s, Expression e) => | |
12 expect(new Parser(s).parse(), e, reason: s); | |
13 | |
14 final Matcher throwsParseException = | |
15 throwsA(new isInstanceOf<ParseException>('ParseException')); | |
16 | |
17 main() { | |
18 | |
19 group('parser', () { | |
20 | |
21 test('should parse an empty expression', () { | |
22 expectParse('', empty()); | |
23 }); | |
24 | |
25 test('should parse an identifier', () { | |
26 expectParse('abc', ident('abc')); | |
27 }); | |
28 | |
29 test('should parse a string literal', () { | |
30 expectParse('"abc"', literal('abc')); | |
31 }); | |
32 | |
33 test('should parse a bool literal', () { | |
34 expectParse('true', literal(true)); | |
35 expectParse('false', literal(false)); | |
36 }); | |
37 | |
38 test('should parse a null literal', () { | |
39 expectParse('null', literal(null)); | |
40 }); | |
41 | |
42 test('should parse an integer literal', () { | |
43 expectParse('123', literal(123)); | |
44 }); | |
45 | |
46 test('should parse a double literal', () { | |
47 expectParse('1.23', literal(1.23)); | |
48 }); | |
49 | |
50 test('should parse a positive double literal', () { | |
51 expectParse('+1.23', literal(1.23)); | |
52 }); | |
53 | |
54 test('should parse a negative double literal', () { | |
55 expectParse('-1.23', literal(-1.23)); | |
56 }); | |
57 | |
58 test('should parse binary operators', () { | |
59 var operators = ['+', '-', '*', '/', '%', '^', '==', '!=', '>', '<', | |
60 '>=', '<=', '||', '&&', '&', '===', '!==', '|']; | |
61 for (var op in operators) { | |
62 expectParse('a $op b', binary(ident('a'), op, ident('b'))); | |
63 expectParse('1 $op 2', binary(literal(1), op, literal(2))); | |
64 expectParse('this $op null', binary(ident('this'), op, literal(null))); | |
65 } | |
66 }); | |
67 | |
68 test('should thrown on unknown operators', () { | |
69 expect(() => parse('a ?? b'), throwsParseException); | |
70 expect(() => parse('a &&& b'), throwsParseException); | |
71 expect(() => parse('a ==== b'), throwsParseException); | |
72 }); | |
73 | |
74 test('should give multiply higher associativity than plus', () { | |
75 expectParse('a + b * c', | |
76 binary(ident('a'), '+', binary(ident('b'), '*', ident('c')))); | |
77 expectParse('a * b + c', | |
78 binary(binary(ident('a'), '*', ident('b')), '+', ident('c'))); | |
79 }); | |
80 | |
81 test('should parse a dot operator', () { | |
82 expectParse('a.b', getter(ident('a'), 'b')); | |
83 }); | |
84 | |
85 test('should parse chained dot operators', () { | |
86 expectParse('a.b.c', getter(getter(ident('a'), 'b'), 'c')); | |
87 }); | |
88 | |
89 test('should give dot high associativity', () { | |
90 expectParse('a * b.c', binary(ident('a'), '*', getter(ident('b'), 'c'))); | |
91 }); | |
92 | |
93 test('should parse a function with no arguments', () { | |
94 expectParse('a()', invoke(ident('a'), null, [])); | |
95 }); | |
96 | |
97 test('should parse a single function argument', () { | |
98 expectParse('a(b)', invoke(ident('a'), null, [ident('b')])); | |
99 }); | |
100 | |
101 test('should parse a function call as a subexpression', () { | |
102 expectParse('a() + 1', | |
103 binary( | |
104 invoke(ident('a'), null, []), | |
105 '+', | |
106 literal(1))); | |
107 }); | |
108 | |
109 test('should parse multiple function arguments', () { | |
110 expectParse('a(b, c)', | |
111 invoke(ident('a'), null, [ident('b'), ident('c')])); | |
112 }); | |
113 | |
114 test('should parse nested function calls', () { | |
115 expectParse('a(b(c))', invoke(ident('a'), null, [ | |
116 invoke(ident('b'), null, [ident('c')])])); | |
117 }); | |
118 | |
119 test('should parse an empty method call', () { | |
120 expectParse('a.b()', invoke(ident('a'), 'b', [])); | |
121 }); | |
122 | |
123 test('should parse a method call with a single argument', () { | |
124 expectParse('a.b(c)', invoke(ident('a'), 'b', [ident('c')])); | |
125 }); | |
126 | |
127 test('should parse a method call with multiple arguments', () { | |
128 expectParse('a.b(c, d)', | |
129 invoke(ident('a'), 'b', [ident('c'), ident('d')])); | |
130 }); | |
131 | |
132 test('should parse chained method calls', () { | |
133 expectParse('a.b().c()', invoke(invoke(ident('a'), 'b', []), 'c', [])); | |
134 }); | |
135 | |
136 test('should parse chained function calls', () { | |
137 expectParse('a()()', invoke(invoke(ident('a'), null, []), null, [])); | |
138 }); | |
139 | |
140 test('should parse parenthesized expression', () { | |
141 expectParse('(a)', paren(ident('a'))); | |
142 expectParse('(( 3 * ((1 + 2)) ))', paren(paren( | |
143 binary(literal(3), '*', paren(paren( | |
144 binary(literal(1), '+', literal(2)))))))); | |
145 }); | |
146 | |
147 test('should parse an index operator', () { | |
148 expectParse('a[b]', index(ident('a'), ident('b'))); | |
149 expectParse('a.b[c]', index(getter(ident('a'), 'b'), ident('c'))); | |
150 }); | |
151 | |
152 test('should parse chained index operators', () { | |
153 expectParse('a[][]', index(index(ident('a'), null), null)); | |
154 }); | |
155 | |
156 test('should parse multiple index operators', () { | |
157 expectParse('a[b] + c[d]', binary( | |
158 index(ident('a'), ident('b')), | |
159 '+', | |
160 index(ident('c'), ident('d')))); | |
161 }); | |
162 | |
163 test('should parse ternary operators', () { | |
164 expectParse('a ? b : c', ternary(ident('a'), ident('b'), ident('c'))); | |
165 expectParse('a.a ? b.a : c.a', ternary(getter(ident('a'), 'a'), | |
166 getter(ident('b'), 'a'), getter(ident('c'), 'a'))); | |
167 expect(() => parse('a + 1 ? b + 1 :: c.d + 3'), throwsParseException); | |
168 }); | |
169 | |
170 test('ternary operators have lowest associativity', () { | |
171 expectParse('a == b ? c + d : e - f', ternary( | |
172 binary(ident('a'), '==', ident('b')), | |
173 binary(ident('c'), '+', ident('d')), | |
174 binary(ident('e'), '-', ident('f')))); | |
175 | |
176 expectParse('a.x == b.y ? c + d : e - f', ternary( | |
177 binary(getter(ident('a'), 'x'), '==', getter(ident('b'), 'y')), | |
178 binary(ident('c'), '+', ident('d')), | |
179 binary(ident('e'), '-', ident('f')))); | |
180 | |
181 expectParse('a.x == b.y ? c + d : e - f', ternary( | |
182 binary(getter(ident('a'), 'x'), '==', getter(ident('b'), 'y')), | |
183 binary(ident('c'), '+', ident('d')), | |
184 binary(ident('e'), '-', ident('f')))); | |
185 }); | |
186 | |
187 test('should parse a filter chain', () { | |
188 expectParse('a | b | c', binary(binary(ident('a'), '|', ident('b')), | |
189 '|', ident('c'))); | |
190 }); | |
191 | |
192 test('should parse "in" expression', () { | |
193 expectParse('a in b', inExpr(ident('a'), ident('b'))); | |
194 expectParse('a in b.c', | |
195 inExpr(ident('a'), getter(ident('b'), 'c'))); | |
196 expectParse('a in b + c', | |
197 inExpr(ident('a'), binary(ident('b'), '+', ident('c')))); | |
198 }); | |
199 | |
200 test('should reject comprehension with non-assignable left expression', () { | |
201 expect(() => parse('a + 1 in b'), throwsParseException); | |
202 }); | |
203 | |
204 test('should parse "as" expressions', () { | |
205 expectParse('a as b', asExpr(ident('a'), ident('b'))); | |
206 }); | |
207 | |
208 skip_test('should reject keywords as identifiers', () { | |
209 expect(() => parse('a.in'), throwsParseException); | |
210 expect(() => parse('a.as'), throwsParseException); | |
211 expect(() => parse('a.this'), throwsParseException); | |
212 }); | |
213 | |
214 test('should parse map literals', () { | |
215 expectParse("{'a': 1}", | |
216 mapLiteral([mapLiteralEntry(literal('a'), literal(1))])); | |
217 expectParse("{'a': 1, 'b': 2 + 3}", | |
218 mapLiteral([ | |
219 mapLiteralEntry(literal('a'), literal(1)), | |
220 mapLiteralEntry(literal('b'), | |
221 binary(literal(2), '+', literal(3)))])); | |
222 expectParse("{'a': foo()}", | |
223 mapLiteral([mapLiteralEntry( | |
224 literal('a'), invoke(ident('foo'), null, []))])); | |
225 expectParse("{'a': foo('a')}", | |
226 mapLiteral([mapLiteralEntry( | |
227 literal('a'), invoke(ident('foo'), null, [literal('a')]))])); | |
228 }); | |
229 | |
230 test('should parse map literals with method calls', () { | |
231 expectParse("{'a': 1}.length", | |
232 getter(mapLiteral([mapLiteralEntry(literal('a'), literal(1))]), | |
233 'length')); | |
234 }); | |
235 | |
236 test('should parse list literals', () { | |
237 expectParse('[1, "a", b]', | |
238 listLiteral([literal(1), literal('a'), ident('b')])); | |
239 expectParse('[[1, 2], [3, 4]]', | |
240 listLiteral([listLiteral([literal(1), literal(2)]), | |
241 listLiteral([literal(3), literal(4)])])); | |
242 }); | |
243 }); | |
244 } | |
OLD | NEW |