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 polymer_expressions.parser; | |
6 | |
7 import 'tokenizer.dart'; | |
8 export 'tokenizer.dart' show ParseException; | |
9 import 'expression.dart'; | |
10 | |
11 const _UNARY_OPERATORS = const <String>['+', '-', '!']; | |
12 const _BINARY_OPERATORS = const <String>['+', '-', '*', '/', '%', '^', '==', | |
13 '!=', '>', '<', '>=', '<=', '||', '&&', '&', '===', '!==', '|']; | |
14 | |
15 Expression parse(String expr) => new Parser(expr).parse(); | |
16 | |
17 class Parser { | |
18 final AstFactory _astFactory; | |
19 final Tokenizer _tokenizer; | |
20 List<Token> _tokens; | |
21 Iterator _iterator; | |
22 Token get _token => _iterator.current; | |
23 | |
24 Parser(String input, {AstFactory astFactory}) | |
25 : _tokenizer = new Tokenizer(input), | |
26 _astFactory = (astFactory == null) ? new AstFactory() : astFactory; | |
27 | |
28 Expression parse() { | |
29 _tokens = _tokenizer.tokenize(); | |
30 _iterator = _tokens.iterator; | |
31 _advance(); | |
32 return _parseExpression(); | |
33 } | |
34 | |
35 _advance([int kind, String value]) { | |
36 if ((kind != null && (_token == null || _token.kind != kind)) | |
37 || (value != null && (_token == null || _token.value != value))) { | |
38 throw new ParseException("Expected kind $kind ($value): $_token"); | |
39 } | |
40 _iterator.moveNext(); | |
41 } | |
42 | |
43 Expression _parseExpression() { | |
44 if (_token == null) return _astFactory.empty(); | |
45 var expr = _parseUnary(); | |
46 return (expr == null) ? null : _parsePrecedence(expr, 0); | |
47 } | |
48 | |
49 // _parsePrecedence and _parseBinary implement the precedence climbing | |
50 // algorithm as described in: | |
51 // http://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing
_method | |
52 Expression _parsePrecedence(Expression left, int precedence) { | |
53 assert(left != null); | |
54 while (_token != null) { | |
55 if (_token.kind == GROUPER_TOKEN) { | |
56 if (_token.value == '(') { | |
57 var args = _parseArguments(); | |
58 assert(args != null); | |
59 left = _astFactory.invoke(left, null, args); | |
60 } else if (_token.value == '[') { | |
61 var indexExpr = _parseIndex(); | |
62 left = _astFactory.index(left, indexExpr); | |
63 } else { | |
64 break; | |
65 } | |
66 } else if (_token.kind == DOT_TOKEN) { | |
67 _advance(); | |
68 var right = _parseUnary(); | |
69 left = _makeInvokeOrGetter(left, right); | |
70 } else if (_token.kind == KEYWORD_TOKEN) { | |
71 if (_token.value == 'in') { | |
72 left = _parseInExpression(left); | |
73 } else if (_token.value == 'as') { | |
74 left = _parseAsExpression(left); | |
75 } else { | |
76 break; | |
77 } | |
78 } else if (_token.kind == OPERATOR_TOKEN | |
79 && _token.precedence >= precedence) { | |
80 left = _token.value == '?' ? _parseTernary(left) : _parseBinary(left); | |
81 } else { | |
82 break; | |
83 } | |
84 } | |
85 return left; | |
86 } | |
87 | |
88 // invoke or getter | |
89 Expression _makeInvokeOrGetter(left, right) { | |
90 if (right is Identifier) { | |
91 return _astFactory.getter(left, right.value); | |
92 } else if (right is Invoke && right.receiver is Identifier) { | |
93 Identifier method = right.receiver; | |
94 return _astFactory.invoke(left, method.value, right.arguments); | |
95 } else { | |
96 throw new ParseException("expected identifier: $right"); | |
97 } | |
98 } | |
99 | |
100 Expression _parseBinary(left) { | |
101 var op = _token; | |
102 if (!_BINARY_OPERATORS.contains(op.value)) { | |
103 throw new ParseException("unknown operator: ${op.value}"); | |
104 } | |
105 _advance(); | |
106 var right = _parseUnary(); | |
107 while (_token != null | |
108 && (_token.kind == OPERATOR_TOKEN | |
109 || _token.kind == DOT_TOKEN | |
110 || _token.kind == GROUPER_TOKEN) | |
111 && _token.precedence > op.precedence) { | |
112 right = _parsePrecedence(right, _token.precedence); | |
113 } | |
114 return _astFactory.binary(left, op.value, right); | |
115 } | |
116 | |
117 Expression _parseUnary() { | |
118 if (_token.kind == OPERATOR_TOKEN) { | |
119 var value = _token.value; | |
120 if (value == '+' || value == '-') { | |
121 _advance(); | |
122 if (_token.kind == INTEGER_TOKEN) { | |
123 return _parseInteger(value); | |
124 } else if (_token.kind == DECIMAL_TOKEN) { | |
125 return _parseDecimal(value); | |
126 } else { | |
127 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | |
128 return _astFactory.unary(value, expr); | |
129 } | |
130 } else if (value == '!') { | |
131 _advance(); | |
132 var expr = _parsePrecedence(_parsePrimary(), POSTFIX_PRECEDENCE); | |
133 return _astFactory.unary(value, expr); | |
134 } else { | |
135 throw new ParseException("unexpected token: $value"); | |
136 } | |
137 } | |
138 return _parsePrimary(); | |
139 } | |
140 | |
141 Expression _parseTernary(condition) { | |
142 _advance(OPERATOR_TOKEN, '?'); | |
143 var trueExpr = _parseExpression(); | |
144 _advance(COLON_TOKEN); | |
145 var falseExpr = _parseExpression(); | |
146 return _astFactory.ternary(condition, trueExpr, falseExpr); | |
147 } | |
148 | |
149 Expression _parsePrimary() { | |
150 var kind = _token.kind; | |
151 switch (kind) { | |
152 case KEYWORD_TOKEN: | |
153 var keyword = _token.value; | |
154 if (keyword == 'this') { | |
155 _advance(); | |
156 // TODO(justin): return keyword node | |
157 return _astFactory.identifier('this'); | |
158 } else if (KEYWORDS.contains(keyword)) { | |
159 throw new ParseException('unexpected keyword: $keyword'); | |
160 } | |
161 throw new ParseException('unrecognized keyword: $keyword'); | |
162 case IDENTIFIER_TOKEN: | |
163 return _parseInvokeOrIdentifier(); | |
164 case STRING_TOKEN: | |
165 return _parseString(); | |
166 case INTEGER_TOKEN: | |
167 return _parseInteger(); | |
168 case DECIMAL_TOKEN: | |
169 return _parseDecimal(); | |
170 case GROUPER_TOKEN: | |
171 if (_token.value == '(') { | |
172 return _parseParenthesized(); | |
173 } else if (_token.value == '{') { | |
174 return _parseMapLiteral(); | |
175 } else if (_token.value == '[') { | |
176 return _parseListLiteral(); | |
177 } | |
178 return null; | |
179 case COLON_TOKEN: | |
180 throw new ParseException('unexpected token ":"'); | |
181 default: | |
182 return null; | |
183 } | |
184 } | |
185 | |
186 ListLiteral _parseListLiteral() { | |
187 var items = []; | |
188 do { | |
189 _advance(); | |
190 if (_token.kind == GROUPER_TOKEN && _token.value == ']') { | |
191 break; | |
192 } | |
193 items.add(_parseExpression()); | |
194 } while(_token != null && _token.value == ','); | |
195 _advance(GROUPER_TOKEN, ']'); | |
196 return new ListLiteral(items); | |
197 } | |
198 | |
199 MapLiteral _parseMapLiteral() { | |
200 var entries = []; | |
201 do { | |
202 _advance(); | |
203 if (_token.kind == GROUPER_TOKEN && _token.value == '}') { | |
204 break; | |
205 } | |
206 entries.add(_parseMapLiteralEntry()); | |
207 } while(_token != null && _token.value == ','); | |
208 _advance(GROUPER_TOKEN, '}'); | |
209 return new MapLiteral(entries); | |
210 } | |
211 | |
212 MapLiteralEntry _parseMapLiteralEntry() { | |
213 var key = _parseString(); | |
214 _advance(COLON_TOKEN, ':'); | |
215 var value = _parseExpression(); | |
216 return _astFactory.mapLiteralEntry(key, value); | |
217 } | |
218 | |
219 InExpression _parseInExpression(Expression left) { | |
220 assert(_token.value == 'in'); | |
221 if (left is! Identifier) { | |
222 throw new ParseException( | |
223 "in... statements must start with an identifier"); | |
224 } | |
225 _advance(); | |
226 var right = _parseExpression(); | |
227 return _astFactory.inExpr(left, right); | |
228 } | |
229 | |
230 AsExpression _parseAsExpression(Expression left) { | |
231 assert(_token.value == 'as'); | |
232 _advance(); | |
233 var right = _parseExpression(); | |
234 if (right is! Identifier) { | |
235 throw new ParseException( | |
236 "'as' statements must end with an identifier"); | |
237 } | |
238 return _astFactory.asExpr(left, right); | |
239 } | |
240 | |
241 Expression _parseInvokeOrIdentifier() { | |
242 if (_token.value == 'true') { | |
243 _advance(); | |
244 return _astFactory.literal(true); | |
245 } | |
246 if (_token.value == 'false') { | |
247 _advance(); | |
248 return _astFactory.literal(false); | |
249 } | |
250 if (_token.value == 'null') { | |
251 _advance(); | |
252 return _astFactory.literal(null); | |
253 } | |
254 var identifier = _parseIdentifier(); | |
255 var args = _parseArguments(); | |
256 if (args == null) { | |
257 return identifier; | |
258 } else { | |
259 return _astFactory.invoke(identifier, null, args); | |
260 } | |
261 } | |
262 | |
263 Identifier _parseIdentifier() { | |
264 if (_token.kind != IDENTIFIER_TOKEN) { | |
265 throw new ParseException("expected identifier: $_token.value"); | |
266 } | |
267 var value = _token.value; | |
268 _advance(); | |
269 return _astFactory.identifier(value); | |
270 } | |
271 | |
272 List<Expression> _parseArguments() { | |
273 if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '(') { | |
274 var args = []; | |
275 do { | |
276 _advance(); | |
277 if (_token.kind == GROUPER_TOKEN && _token.value == ')') { | |
278 break; | |
279 } | |
280 var expr = _parseExpression(); | |
281 args.add(expr); | |
282 } while(_token != null && _token.value == ','); | |
283 _advance(GROUPER_TOKEN, ')'); | |
284 return args; | |
285 } | |
286 return null; | |
287 } | |
288 | |
289 Expression _parseIndex() { | |
290 if (_token != null && _token.kind == GROUPER_TOKEN && _token.value == '[') { | |
291 _advance(); | |
292 var expr = _parseExpression(); | |
293 _advance(GROUPER_TOKEN, ']'); | |
294 return expr; | |
295 } | |
296 return null; | |
297 } | |
298 | |
299 ParenthesizedExpression _parseParenthesized() { | |
300 _advance(); | |
301 var expr = _parseExpression(); | |
302 _advance(GROUPER_TOKEN, ')'); | |
303 return _astFactory.parenthesized(expr); | |
304 } | |
305 | |
306 Literal<String> _parseString() { | |
307 var value = _astFactory.literal(_token.value); | |
308 _advance(); | |
309 return value; | |
310 } | |
311 | |
312 Literal<int> _parseInteger([String prefix = '']) { | |
313 var value = _astFactory.literal(int.parse('$prefix${_token.value}')); | |
314 _advance(); | |
315 return value; | |
316 } | |
317 | |
318 Literal<double> _parseDecimal([String prefix = '']) { | |
319 var value = _astFactory.literal(double.parse('$prefix${_token.value}')); | |
320 _advance(); | |
321 return value; | |
322 } | |
323 | |
324 } | |
OLD | NEW |