| OLD | NEW |
| 1 part of angular.core.parser; | 1 library angular.core.parser.lexer; |
| 2 | 2 |
| 3 import 'package:angular/core/module.dart' show NgInjectableService; |
| 4 import 'package:angular/core/parser/characters.dart'; |
| 5 |
| 6 class Token { |
| 7 final int index; |
| 8 final String text; |
| 9 |
| 10 var value; |
| 11 // Tokens should have one of these set. |
| 12 String opKey; |
| 13 String key; |
| 14 |
| 15 Token(this.index, this.text); |
| 16 |
| 17 withOp(op) { |
| 18 this.opKey = op; |
| 19 } |
| 20 |
| 21 withGetterSetter(key) { |
| 22 this.key = key; |
| 23 } |
| 24 |
| 25 withValue(value) { this.value = value; } |
| 26 |
| 27 toString() => "Token($text)"; |
| 28 } |
| 29 |
| 30 |
| 3 @NgInjectableService() | 31 @NgInjectableService() |
| 4 class Lexer { | 32 class Lexer { |
| 5 List<Token> call(String text) { | 33 List<Token> call(String text) { |
| 6 Scanner scanner = new Scanner(text); | 34 Scanner scanner = new Scanner(text); |
| 7 List<Token> tokens = []; | 35 List<Token> tokens = []; |
| 8 Token token = scanner.scanToken(); | 36 Token token = scanner.scanToken(); |
| 9 while (token != null) { | 37 while (token != null) { |
| 10 tokens.add(token); | 38 tokens.add(token); |
| 11 token = scanner.scanToken(); | 39 token = scanner.scanToken(); |
| 12 } | 40 } |
| 13 return tokens; | 41 return tokens; |
| 14 } | 42 } |
| 15 } | 43 } |
| 16 | 44 |
| 17 class Scanner { | 45 class Scanner { |
| 18 final String input; | 46 final String input; |
| 19 final int length; | 47 final int length; |
| 20 | 48 |
| 21 // TODO(kasperl): Get rid of this buffer. It is currently used for | |
| 22 // pushing back tokens for method calls found while scanning | |
| 23 // identifiers. We should be able to do this in the parser instead. | |
| 24 final List<Token> buffer = []; | |
| 25 | |
| 26 int peek = 0; | 49 int peek = 0; |
| 27 int index = -1; | 50 int index = -1; |
| 28 | 51 |
| 29 Scanner(String input) : this.input = input, this.length = input.length { | 52 Scanner(String input) : this.input = input, this.length = input.length { |
| 30 advance(); | 53 advance(); |
| 31 } | 54 } |
| 32 | 55 |
| 33 Token scanToken() { | 56 Token scanToken() { |
| 34 // TODO(kasperl): The current handling of method calls is somewhat | |
| 35 // complicated. We should simplify it by dealing with it in the parser. | |
| 36 if (!buffer.isEmpty) return buffer.removeLast(); | |
| 37 | |
| 38 // Skip whitespace. | 57 // Skip whitespace. |
| 39 while (isWhitespace(peek)) advance(); | 58 while (peek <= $SPACE) { |
| 59 if (++index >= length) { |
| 60 peek = $EOF; |
| 61 return null; |
| 62 } else { |
| 63 peek = input.codeUnitAt(index); |
| 64 } |
| 65 } |
| 40 | 66 |
| 41 // Handle identifiers and numbers. | 67 // Handle identifiers and numbers. |
| 42 if (isIdentifierStart(peek)) return scanIdentifier(); | 68 if (isIdentifierStart(peek)) return scanIdentifier(); |
| 43 if (isDigit(peek)) return scanNumber(index); | 69 if (isDigit(peek)) return scanNumber(index); |
| 44 | 70 |
| 45 int start = index; | 71 int start = index; |
| 46 switch (peek) { | 72 switch (peek) { |
| 47 case $EOF: | |
| 48 return null; | |
| 49 case $PERIOD: | 73 case $PERIOD: |
| 50 advance(); | 74 advance(); |
| 51 return isDigit(peek) ? scanNumber(start) : new Token(start, '.'); | 75 return isDigit(peek) ? scanNumber(start) : new Token(start, '.'); |
| 52 case $LPAREN: | 76 case $LPAREN: |
| 53 return scanCharacter(start, '('); | |
| 54 case $RPAREN: | 77 case $RPAREN: |
| 55 return scanCharacter(start, ')'); | |
| 56 case $LBRACE: | 78 case $LBRACE: |
| 57 return scanCharacter(start, '{'); | |
| 58 case $RBRACE: | 79 case $RBRACE: |
| 59 return scanCharacter(start, '}'); | |
| 60 case $LBRACKET: | 80 case $LBRACKET: |
| 61 return scanCharacter(start, '['); | |
| 62 case $RBRACKET: | 81 case $RBRACKET: |
| 63 return scanCharacter(start, ']'); | |
| 64 case $COMMA: | 82 case $COMMA: |
| 65 return scanCharacter(start, ','); | |
| 66 case $COLON: | 83 case $COLON: |
| 67 return scanCharacter(start, ':'); | |
| 68 case $SEMICOLON: | 84 case $SEMICOLON: |
| 69 return scanCharacter(start, ';'); | 85 return scanCharacter(start, new String.fromCharCode(peek)); |
| 70 case $SQ: | 86 case $SQ: |
| 71 case $DQ: | 87 case $DQ: |
| 72 return scanString(); | 88 return scanString(); |
| 73 case $PLUS: | 89 case $PLUS: |
| 74 return scanOperator(start, '+'); | |
| 75 case $MINUS: | 90 case $MINUS: |
| 76 return scanOperator(start, '-'); | |
| 77 case $STAR: | 91 case $STAR: |
| 78 return scanOperator(start, '*'); | |
| 79 case $SLASH: | 92 case $SLASH: |
| 80 return scanOperator(start, '/'); | |
| 81 case $PERCENT: | 93 case $PERCENT: |
| 82 return scanOperator(start, '%'); | |
| 83 case $CARET: | 94 case $CARET: |
| 84 return scanOperator(start, '^'); | |
| 85 case $QUESTION: | 95 case $QUESTION: |
| 86 return scanOperator(start, '?'); | 96 return scanOperator(start, new String.fromCharCode(peek)); |
| 87 case $LT: | 97 case $LT: |
| 88 return scanComplexOperator(start, $EQ, '<', '<='); | |
| 89 case $GT: | 98 case $GT: |
| 90 return scanComplexOperator(start, $EQ, '>', '>='); | |
| 91 case $BANG: | 99 case $BANG: |
| 92 return scanComplexOperator(start, $EQ, '!', '!='); | |
| 93 case $EQ: | 100 case $EQ: |
| 94 return scanComplexOperator(start, $EQ, '=', '=='); | 101 return scanComplexOperator(start, $EQ, new String.fromCharCode(peek), '=
'); |
| 95 case $AMPERSAND: | 102 case $AMPERSAND: |
| 96 return scanComplexOperator(start, $AMPERSAND, '&', '&&'); | 103 return scanComplexOperator(start, $AMPERSAND, '&', '&'); |
| 97 case $BAR: | 104 case $BAR: |
| 98 return scanComplexOperator(start, $BAR, '|', '||'); | 105 return scanComplexOperator(start, $BAR, '|', '|'); |
| 99 case $TILDE: | 106 case $TILDE: |
| 100 return scanComplexOperator(start, $SLASH, '~', '~/'); | 107 return scanComplexOperator(start, $SLASH, '~', '/'); |
| 108 case $NBSP: |
| 109 while (isWhitespace(peek)) advance(); |
| 110 return scanToken(); |
| 101 } | 111 } |
| 102 | 112 |
| 103 String character = new String.fromCharCode(peek); | 113 String character = new String.fromCharCode(peek); |
| 104 error('Unexpected character [$character]'); | 114 error('Unexpected character [$character]'); |
| 105 } | 115 } |
| 106 | 116 |
| 107 Token scanCharacter(int start, String string) { | 117 Token scanCharacter(int start, String string) { |
| 108 assert(peek == string.codeUnitAt(0)); | 118 assert(peek == string.codeUnitAt(0)); |
| 109 advance(); | 119 advance(); |
| 110 return new Token(start, string); | 120 return new Token(start, string); |
| 111 } | 121 } |
| 112 | 122 |
| 113 Token scanOperator(int start, String string) { | 123 Token scanOperator(int start, String string) { |
| 114 assert(peek == string.codeUnitAt(0)); | 124 assert(peek == string.codeUnitAt(0)); |
| 115 assert(OPERATORS.containsKey(string)); | 125 assert(OPERATORS.contains(string)); |
| 116 advance(); | 126 advance(); |
| 117 return new Token(start, string)..withOp(string); | 127 return new Token(start, string)..withOp(string); |
| 118 } | 128 } |
| 119 | 129 |
| 120 Token scanComplexOperator(int start, int code, String one, String two) { | 130 Token scanComplexOperator(int start, int code, String one, String two) { |
| 121 assert(peek == one.codeUnitAt(0)); | 131 assert(peek == one.codeUnitAt(0)); |
| 122 advance(); | 132 advance(); |
| 123 String string = one; | 133 String string = one; |
| 124 if (peek == code) { | 134 if (peek == code) { |
| 125 advance(); | 135 advance(); |
| 126 string = two; | 136 string += two; |
| 127 } | 137 } |
| 128 assert(OPERATORS.containsKey(string)); | 138 assert(OPERATORS.contains(string)); |
| 129 return new Token(start, string)..withOp(string); | 139 return new Token(start, string)..withOp(string); |
| 130 } | 140 } |
| 131 | 141 |
| 132 Token scanIdentifier() { | 142 Token scanIdentifier() { |
| 133 assert(isIdentifierStart(peek)); | 143 assert(isIdentifierStart(peek)); |
| 134 int start = index; | 144 int start = index; |
| 135 int dot = -1; | |
| 136 advance(); | 145 advance(); |
| 137 while (true) { | 146 while (isIdentifierPart(peek)) advance(); |
| 138 if (peek == $PERIOD) { | 147 String string = input.substring(start, index); |
| 139 dot = index; | 148 Token result = new Token(start, string); |
| 140 } else if (!isIdentifierPart(peek)) { | 149 // TODO(kasperl): Deal with null, undefined, true, and false in |
| 141 break; | 150 // a cleaner and faster way. |
| 142 } | 151 if (OPERATORS.contains(string)) { |
| 143 advance(); | 152 result.withOp(string); |
| 153 } else { |
| 154 result.withGetterSetter(string); |
| 144 } | 155 } |
| 145 if (dot == -1) { | 156 return result; |
| 146 String string = input.substring(start, index); | |
| 147 Token result = new Token(start, string); | |
| 148 // TODO(kasperl): Deal with null, undefined, true, and false in | |
| 149 // a cleaner and faster way. | |
| 150 if (OPERATORS.containsKey(string)) { | |
| 151 result.withOp(string); | |
| 152 } else { | |
| 153 result.withGetterSetter(string); | |
| 154 } | |
| 155 return result; | |
| 156 } | |
| 157 | |
| 158 int end = index; | |
| 159 while (isWhitespace(peek)) advance(); | |
| 160 if (peek == $LPAREN) { | |
| 161 buffer.add(new Token(dot + 1, input.substring(dot + 1, end))); | |
| 162 buffer.add(new Token(dot, '.')); | |
| 163 end = dot; | |
| 164 } | |
| 165 String string = input.substring(start, end); | |
| 166 return new Token(start, string)..withGetterSetter(string); | |
| 167 } | 157 } |
| 168 | 158 |
| 169 Token scanNumber(int start) { | 159 Token scanNumber(int start) { |
| 170 assert(isDigit(peek)); | 160 assert(isDigit(peek)); |
| 171 bool simple = (index == start); | 161 bool simple = (index == start); |
| 162 advance(); // Skip initial digit. |
| 172 while (true) { | 163 while (true) { |
| 173 if (isDigit(peek)) { | 164 if (isDigit(peek)) { |
| 174 // Do nothing. | 165 // Do nothing. |
| 175 } else if (peek == $PERIOD) { | 166 } else if (peek == $PERIOD) { |
| 176 simple = false; | 167 simple = false; |
| 177 } else if (isExponentStart(peek)) { | 168 } else if (isExponentStart(peek)) { |
| 178 advance(); | 169 advance(); |
| 179 if (isExponentSign(peek)) advance(); | 170 if (isExponentSign(peek)) advance(); |
| 180 if (!isDigit(peek)) error('Invalid exponent', -1); | 171 if (!isDigit(peek)) error('Invalid exponent', -1); |
| 181 simple = false; | 172 simple = false; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 else peek = input.codeUnitAt(index); | 233 else peek = input.codeUnitAt(index); |
| 243 } | 234 } |
| 244 | 235 |
| 245 void error(String message, [int offset = 0]) { | 236 void error(String message, [int offset = 0]) { |
| 246 // TODO(kasperl): Try to get rid of the offset. It is only used to match | 237 // TODO(kasperl): Try to get rid of the offset. It is only used to match |
| 247 // the error expectations in the lexer tests for numbers with exponents. | 238 // the error expectations in the lexer tests for numbers with exponents. |
| 248 int position = index + offset; | 239 int position = index + offset; |
| 249 throw "Lexer Error: $message at column $position in expression [$input]"; | 240 throw "Lexer Error: $message at column $position in expression [$input]"; |
| 250 } | 241 } |
| 251 } | 242 } |
| 243 |
| 244 Set<String> OPERATORS = new Set<String>.from([ |
| 245 'undefined', |
| 246 'null', |
| 247 'true', |
| 248 'false', |
| 249 '+', |
| 250 '-', |
| 251 '*', |
| 252 '/', |
| 253 '~/', |
| 254 '%', |
| 255 '^', |
| 256 '=', |
| 257 '==', |
| 258 '!=', |
| 259 '<', |
| 260 '>', |
| 261 '<=', |
| 262 '>=', |
| 263 '&&', |
| 264 '||', |
| 265 '&', |
| 266 '|', |
| 267 '!', |
| 268 '?', |
| 269 ]); |
| 270 |
| OLD | NEW |