Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit e582218

Browse files
Kasper Lundmhevery
authored andcommitted
chore(lexer): Introduce different classes of tokens.
Use the new token classes to restrict the grammar to only allow identifiers or keywords as filter and member names. For object literal keys we also allow strings. Closes #711
1 parent f16536e commit e582218

File tree

6 files changed

+353
-224
lines changed

6 files changed

+353
-224
lines changed

bin/parser_generator_for_spec.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,5 +434,14 @@ main(arguments) {
434434
"o.void()",
435435
"o.while()",
436436
"o.with()",
437+
438+
'"Foo"|(',
439+
'"Foo"|1234',
440+
'"Foo"|"uppercase"',
441+
'x.(',
442+
'x. 1234',
443+
'x."foo"',
444+
'{(:0}',
445+
'{1234:0}',
437446
]);
438447
}

lib/core/parser/dynamic_parser_impl.dart

Lines changed: 112 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ library angular.core.parser.dynamic_parser_impl;
33
import 'package:angular/core/parser/parser.dart' show ParserBackend;
44
import 'package:angular/core/parser/lexer.dart';
55
import 'package:angular/core/parser/syntax.dart';
6+
import 'package:angular/core/parser/characters.dart';
67

78
class DynamicParserImpl {
8-
static Token EOF = new Token(-1, null);
99
final ParserBackend backend;
1010
final String input;
1111
final List<Token> tokens;
@@ -15,26 +15,28 @@ class DynamicParserImpl {
1515
: this.input = input, tokens = lexer.call(input);
1616

1717
Token get peek {
18-
return (index < tokens.length) ? tokens[index] : EOF;
18+
return (index < tokens.length) ? tokens[index] : Token.EOF;
1919
}
2020

2121
parseChain() {
2222
bool isChain = false;
23-
while (optional(';')) {
23+
while (optionalCharacter($SEMICOLON)) {
2424
isChain = true;
2525
}
2626
List expressions = [];
2727
while (index < tokens.length) {
28-
if (peek.text == ')' || peek.text == '}' || peek.text == ']') {
29-
error('Unconsumed token ${peek.text}');
28+
if (peek.isCharacter($RPAREN) ||
29+
peek.isCharacter($RBRACE) ||
30+
peek.isCharacter($RBRACKET)) {
31+
error('Unconsumed token $peek');
3032
}
3133
var expr = parseFilter();
3234
expressions.add(expr);
33-
while (optional(';')) {
35+
while (optionalCharacter($SEMICOLON)) {
3436
isChain = true;
3537
}
3638
if (isChain && expr is Filter) {
37-
error('cannot have a filter in a chain');
39+
error('Cannot have a filter in a chain');
3840
}
3941
}
4042
return (expressions.length == 1)
@@ -44,11 +46,10 @@ class DynamicParserImpl {
4446

4547
parseFilter() {
4648
var result = parseExpression();
47-
while (optional('|')) {
48-
String name = peek.text; // TODO(kasperl): Restrict to identifier?
49-
advance();
49+
while (optionalOperator('|')) {
50+
String name = expectIdentifierOrKeyword();
5051
List arguments = [];
51-
while (optional(':')) {
52+
while (optionalCharacter($COLON)) {
5253
// TODO(kasperl): Is this really supposed to be expressions?
5354
arguments.add(parseExpression());
5455
}
@@ -60,13 +61,13 @@ class DynamicParserImpl {
6061
parseExpression() {
6162
int start = peek.index;
6263
var result = parseConditional();
63-
while (peek.text == '=') {
64+
while (peek.isOperator('=')) {
6465
if (!backend.isAssignable(result)) {
6566
int end = (index < tokens.length) ? peek.index : input.length;
6667
String expression = input.substring(start, end);
6768
error('Expression $expression is not assignable');
6869
}
69-
expect('=');
70+
expectOperator('=');
7071
result = backend.newAssign(result, parseConditional());
7172
}
7273
return result;
@@ -75,9 +76,9 @@ class DynamicParserImpl {
7576
parseConditional() {
7677
int start = peek.index;
7778
var result = parseLogicalOr();
78-
if (optional('?')) {
79+
if (optionalOperator('?')) {
7980
var yes = parseExpression();
80-
if (!optional(':')) {
81+
if (!optionalCharacter($COLON)) {
8182
int end = (index < tokens.length) ? peek.index : input.length;
8283
String expression = input.substring(start, end);
8384
error('Conditional expression $expression requires all 3 expressions');
@@ -91,7 +92,7 @@ class DynamicParserImpl {
9192
parseLogicalOr() {
9293
// '||'
9394
var result = parseLogicalAnd();
94-
while (optional('||')) {
95+
while (optionalOperator('||')) {
9596
result = backend.newBinaryLogicalOr(result, parseLogicalAnd());
9697
}
9798
return result;
@@ -100,7 +101,7 @@ class DynamicParserImpl {
100101
parseLogicalAnd() {
101102
// '&&'
102103
var result = parseEquality();
103-
while (optional('&&')) {
104+
while (optionalOperator('&&')) {
104105
result = backend.newBinaryLogicalAnd(result, parseEquality());
105106
}
106107
return result;
@@ -110,9 +111,9 @@ class DynamicParserImpl {
110111
// '==','!='
111112
var result = parseRelational();
112113
while (true) {
113-
if (optional('==')) {
114+
if (optionalOperator('==')) {
114115
result = backend.newBinaryEqual(result, parseRelational());
115-
} else if (optional('!=')) {
116+
} else if (optionalOperator('!=')) {
116117
result = backend.newBinaryNotEqual(result, parseRelational());
117118
} else {
118119
return result;
@@ -124,13 +125,13 @@ class DynamicParserImpl {
124125
// '<', '>', '<=', '>='
125126
var result = parseAdditive();
126127
while (true) {
127-
if (optional('<')) {
128+
if (optionalOperator('<')) {
128129
result = backend.newBinaryLessThan(result, parseAdditive());
129-
} else if (optional('>')) {
130+
} else if (optionalOperator('>')) {
130131
result = backend.newBinaryGreaterThan(result, parseAdditive());
131-
} else if (optional('<=')) {
132+
} else if (optionalOperator('<=')) {
132133
result = backend.newBinaryLessThanEqual(result, parseAdditive());
133-
} else if (optional('>=')) {
134+
} else if (optionalOperator('>=')) {
134135
result = backend.newBinaryGreaterThanEqual(result, parseAdditive());
135136
} else {
136137
return result;
@@ -142,9 +143,9 @@ class DynamicParserImpl {
142143
// '+', '-'
143144
var result = parseMultiplicative();
144145
while (true) {
145-
if (optional('+')) {
146+
if (optionalOperator('+')) {
146147
result = backend.newBinaryPlus(result, parseMultiplicative());
147-
} else if (optional('-')) {
148+
} else if (optionalOperator('-')) {
148149
result = backend.newBinaryMinus(result, parseMultiplicative());
149150
} else {
150151
return result;
@@ -156,13 +157,13 @@ class DynamicParserImpl {
156157
// '*', '%', '/', '~/'
157158
var result = parsePrefix();
158159
while (true) {
159-
if (optional('*')) {
160+
if (optionalOperator('*')) {
160161
result = backend.newBinaryMultiply(result, parsePrefix());
161-
} else if (optional('%')) {
162+
} else if (optionalOperator('%')) {
162163
result = backend.newBinaryModulo(result, parsePrefix());
163-
} else if (optional('/')) {
164+
} else if (optionalOperator('/')) {
164165
result = backend.newBinaryDivide(result, parsePrefix());
165-
} else if (optional('~/')) {
166+
} else if (optionalOperator('~/')) {
166167
result = backend.newBinaryTruncatingDivide(result, parsePrefix());
167168
} else {
168169
return result;
@@ -171,12 +172,12 @@ class DynamicParserImpl {
171172
}
172173

173174
parsePrefix() {
174-
if (optional('+')) {
175+
if (optionalOperator('+')) {
175176
// TODO(kasperl): This is different than the original parser.
176177
return backend.newPrefixPlus(parsePrefix());
177-
} else if (optional('-')) {
178+
} else if (optionalOperator('-')) {
178179
return backend.newPrefixMinus(parsePrefix());
179-
} else if (optional('!')) {
180+
} else if (optionalOperator('!')) {
180181
return backend.newPrefixNot(parsePrefix());
181182
} else {
182183
return parseAccessOrCallMember();
@@ -186,24 +187,22 @@ class DynamicParserImpl {
186187
parseAccessOrCallMember() {
187188
var result = parsePrimary();
188189
while (true) {
189-
if (optional('.')) {
190-
// TODO(kasperl): Check that this is an identifier. Are keywords okay?
191-
String name = peek.text;
192-
advance();
193-
if (optional('(')) {
194-
List arguments = parseExpressionList(')');
195-
expect(')');
190+
if (optionalCharacter($PERIOD)) {
191+
String name = expectIdentifierOrKeyword();
192+
if (optionalCharacter($LPAREN)) {
193+
List arguments = parseExpressionList($RPAREN);
194+
expectCharacter($RPAREN);
196195
result = backend.newCallMember(result, name, arguments);
197196
} else {
198197
result = backend.newAccessMember(result, name);
199198
}
200-
} else if (optional('[')) {
199+
} else if (optionalCharacter($LBRACKET)) {
201200
var key = parseExpression();
202-
expect(']');
201+
expectCharacter($RBRACKET);
203202
result = backend.newAccessKeyed(result, key);
204-
} else if (optional('(')) {
205-
List arguments = parseExpressionList(')');
206-
expect(')');
203+
} else if (optionalCharacter($LPAREN)) {
204+
List arguments = parseExpressionList($RPAREN);
205+
expectCharacter($RPAREN);
207206
result = backend.newCallFunction(result, arguments);
208207
} else {
209208
return result;
@@ -212,90 +211,120 @@ class DynamicParserImpl {
212211
}
213212

214213
parsePrimary() {
215-
if (optional('(')) {
214+
if (optionalCharacter($LPAREN)) {
216215
var result = parseExpression();
217-
expect(')');
216+
expectCharacter($RPAREN);
218217
return result;
219-
} else if (optional('null') || optional('undefined')) {
218+
} else if (peek.isKeywordNull || peek.isKeywordUndefined) {
219+
advance();
220220
return backend.newLiteralNull();
221-
} else if (optional('true')) {
221+
} else if (peek.isKeywordTrue) {
222+
advance();
222223
return backend.newLiteralBoolean(true);
223-
} else if (optional('false')) {
224+
} else if (peek.isKeywordFalse) {
225+
advance();
224226
return backend.newLiteralBoolean(false);
225-
} else if (optional('[')) {
226-
List elements = parseExpressionList(']');
227-
expect(']');
227+
} else if (optionalCharacter($LBRACKET)) {
228+
List elements = parseExpressionList($RBRACKET);
229+
expectCharacter($RBRACKET);
228230
return backend.newLiteralArray(elements);
229-
} else if (peek.text == '{') {
231+
} else if (peek.isCharacter($LBRACE)) {
230232
return parseObject();
231-
} else if (peek.key != null) {
233+
} else if (peek.isIdentifier) {
232234
return parseAccessOrCallScope();
233-
} else if (peek.value != null) {
234-
var value = peek.value;
235+
} else if (peek.isNumber) {
236+
num value = peek.toNumber();
237+
advance();
238+
return backend.newLiteralNumber(value);
239+
} else if (peek.isString) {
240+
String value = peek.toString();
235241
advance();
236-
return (value is num)
237-
? backend.newLiteralNumber(value)
238-
: backend.newLiteralString(value);
242+
return backend.newLiteralString(value);
239243
} else if (index >= tokens.length) {
240244
throw 'Unexpected end of expression: $input';
241245
} else {
242-
error('Unexpected token ${peek.text}');
246+
error('Unexpected token $peek');
243247
}
244248
}
245249

246250
parseAccessOrCallScope() {
247-
String name = peek.key;
248-
advance();
249-
if (!optional('(')) return backend.newAccessScope(name);
250-
List arguments = parseExpressionList(')');
251-
expect(')');
251+
String name = expectIdentifierOrKeyword();
252+
if (!optionalCharacter($LPAREN)) return backend.newAccessScope(name);
253+
List arguments = parseExpressionList($RPAREN);
254+
expectCharacter($RPAREN);
252255
return backend.newCallScope(name, arguments);
253256
}
254257

255258
parseObject() {
256259
List<String> keys = [];
257260
List values = [];
258-
expect('{');
259-
if (peek.text != '}') {
261+
expectCharacter($LBRACE);
262+
if (!optionalCharacter($RBRACE)) {
260263
do {
261-
// TODO(kasperl): Stricter checking. Only allow identifiers
262-
// and strings as keys. Maybe also keywords?
263-
var value = peek.value;
264-
keys.add(value is String ? value : peek.text);
265-
advance();
266-
expect(':');
264+
String key = expectIdentifierOrKeywordOrString();
265+
keys.add(key);
266+
expectCharacter($COLON);
267267
values.add(parseExpression());
268-
} while (optional(','));
268+
} while (optionalCharacter($COMMA));
269+
expectCharacter($RBRACE);
269270
}
270-
expect('}');
271271
return backend.newLiteralObject(keys, values);
272272
}
273273

274-
List parseExpressionList(String terminator) {
274+
List parseExpressionList(int terminator) {
275275
List result = [];
276-
if (peek.text != terminator) {
276+
if (!peek.isCharacter(terminator)) {
277277
do {
278278
result.add(parseExpression());
279-
} while (optional(','));
279+
} while (optionalCharacter($COMMA));
280280
}
281281
return result;
282282
}
283283

284-
bool optional(text) {
285-
if (peek.text == text) {
284+
bool optionalCharacter(int code) {
285+
if (peek.isCharacter(code)) {
286286
advance();
287287
return true;
288288
} else {
289289
return false;
290290
}
291291
}
292292

293-
void expect(text) {
294-
if (peek.text == text) {
293+
bool optionalOperator(String operator) {
294+
if (peek.isOperator(operator)) {
295295
advance();
296+
return true;
296297
} else {
297-
error('Missing expected $text');
298+
return false;
299+
}
300+
}
301+
302+
void expectCharacter(int code) {
303+
if (optionalCharacter(code)) return;
304+
error('Missing expected ${new String.fromCharCode(code)}');
305+
}
306+
307+
void expectOperator(String operator) {
308+
if (optionalOperator(operator)) return;
309+
error('Missing expected operator $operator');
310+
}
311+
312+
String expectIdentifierOrKeyword() {
313+
if (!peek.isIdentifier && !peek.isKeyword) {
314+
error('Unexpected token $peek, expected identifier or keyword');
298315
}
316+
String result = peek.toString();
317+
advance();
318+
return result;
319+
}
320+
321+
String expectIdentifierOrKeywordOrString() {
322+
if (!peek.isIdentifier && !peek.isKeyword && !peek.isString) {
323+
error('Unexpected token $peek, expected identifier, keyword, or string');
324+
}
325+
String result = peek.toString();
326+
advance();
327+
return result;
299328
}
300329

301330
void advance() {

0 commit comments

Comments
 (0)