| OLD | NEW |
| 1 library angular.core.parser.lexer; | 1 library angular.core.parser.lexer; |
| 2 | 2 |
| 3 import 'package:angular/core/annotation_src.dart'; | 3 import 'package:angular/core/module.dart' show NgInjectableService; |
| 4 import 'package:angular/core/parser/characters.dart'; | 4 import 'package:angular/core/parser/characters.dart'; |
| 5 | 5 |
| 6 part 'tokens.dart'; | 6 class Token { |
| 7 final int index; |
| 8 final String text; |
| 7 | 9 |
| 8 @Injectable() | 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 |
| 31 @NgInjectableService() |
| 9 class Lexer { | 32 class Lexer { |
| 10 List<Token> call(String text) { | 33 List<Token> call(String text) { |
| 11 Scanner scanner = new Scanner(text); | 34 Scanner scanner = new Scanner(text); |
| 12 List<Token> tokens = []; | 35 List<Token> tokens = []; |
| 13 Token token = scanner.scanToken(); | 36 Token token = scanner.scanToken(); |
| 14 while (token != null) { | 37 while (token != null) { |
| 15 tokens.add(token); | 38 tokens.add(token); |
| 16 token = scanner.scanToken(); | 39 token = scanner.scanToken(); |
| 17 } | 40 } |
| 18 return tokens; | 41 return tokens; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 42 } | 65 } |
| 43 | 66 |
| 44 // Handle identifiers and numbers. | 67 // Handle identifiers and numbers. |
| 45 if (isIdentifierStart(peek)) return scanIdentifier(); | 68 if (isIdentifierStart(peek)) return scanIdentifier(); |
| 46 if (isDigit(peek)) return scanNumber(index); | 69 if (isDigit(peek)) return scanNumber(index); |
| 47 | 70 |
| 48 int start = index; | 71 int start = index; |
| 49 switch (peek) { | 72 switch (peek) { |
| 50 case $PERIOD: | 73 case $PERIOD: |
| 51 advance(); | 74 advance(); |
| 52 return isDigit(peek) ? scanNumber(start) : new CharacterToken(start, $PE
RIOD); | 75 return isDigit(peek) ? scanNumber(start) : new Token(start, '.'); |
| 53 case $LPAREN: | 76 case $LPAREN: |
| 54 case $RPAREN: | 77 case $RPAREN: |
| 55 case $LBRACE: | 78 case $LBRACE: |
| 56 case $RBRACE: | 79 case $RBRACE: |
| 57 case $LBRACKET: | 80 case $LBRACKET: |
| 58 case $RBRACKET: | 81 case $RBRACKET: |
| 59 case $COMMA: | 82 case $COMMA: |
| 60 case $COLON: | 83 case $COLON: |
| 61 case $SEMICOLON: | 84 case $SEMICOLON: |
| 62 return scanCharacter(start, peek); | 85 return scanCharacter(start, new String.fromCharCode(peek)); |
| 63 case $SQ: | 86 case $SQ: |
| 64 case $DQ: | 87 case $DQ: |
| 65 return scanString(); | 88 return scanString(); |
| 66 case $PLUS: | 89 case $PLUS: |
| 67 case $MINUS: | 90 case $MINUS: |
| 68 case $STAR: | 91 case $STAR: |
| 69 case $SLASH: | 92 case $SLASH: |
| 70 case $PERCENT: | 93 case $PERCENT: |
| 71 case $CARET: | 94 case $CARET: |
| 72 case $QUESTION: | 95 case $QUESTION: |
| (...skipping 12 matching lines...) Expand all Loading... |
| 85 case $NBSP: | 108 case $NBSP: |
| 86 while (isWhitespace(peek)) advance(); | 109 while (isWhitespace(peek)) advance(); |
| 87 return scanToken(); | 110 return scanToken(); |
| 88 } | 111 } |
| 89 | 112 |
| 90 String character = new String.fromCharCode(peek); | 113 String character = new String.fromCharCode(peek); |
| 91 error('Unexpected character [$character]'); | 114 error('Unexpected character [$character]'); |
| 92 return null; | 115 return null; |
| 93 } | 116 } |
| 94 | 117 |
| 95 Token scanCharacter(int start, int code) { | 118 Token scanCharacter(int start, String string) { |
| 96 assert(peek == code); | 119 assert(peek == string.codeUnitAt(0)); |
| 97 advance(); | 120 advance(); |
| 98 return new CharacterToken(start, code); | 121 return new Token(start, string); |
| 99 } | 122 } |
| 100 | 123 |
| 101 Token scanOperator(int start, String string) { | 124 Token scanOperator(int start, String string) { |
| 102 assert(peek == string.codeUnitAt(0)); | 125 assert(peek == string.codeUnitAt(0)); |
| 103 assert(OPERATORS.contains(string)); | 126 assert(OPERATORS.contains(string)); |
| 104 advance(); | 127 advance(); |
| 105 return new OperatorToken(start, string); | 128 return new Token(start, string)..withOp(string); |
| 106 } | 129 } |
| 107 | 130 |
| 108 Token scanComplexOperator(int start, int code, String one, String two) { | 131 Token scanComplexOperator(int start, int code, String one, String two) { |
| 109 assert(peek == one.codeUnitAt(0)); | 132 assert(peek == one.codeUnitAt(0)); |
| 110 advance(); | 133 advance(); |
| 111 String string = one; | 134 String string = one; |
| 112 if (peek == code) { | 135 if (peek == code) { |
| 113 advance(); | 136 advance(); |
| 114 string += two; | 137 string += two; |
| 115 } | 138 } |
| 116 assert(OPERATORS.contains(string)); | 139 assert(OPERATORS.contains(string)); |
| 117 return new OperatorToken(start, string); | 140 return new Token(start, string)..withOp(string); |
| 118 } | 141 } |
| 119 | 142 |
| 120 Token scanIdentifier() { | 143 Token scanIdentifier() { |
| 121 assert(isIdentifierStart(peek)); | 144 assert(isIdentifierStart(peek)); |
| 122 int start = index; | 145 int start = index; |
| 123 advance(); | 146 advance(); |
| 124 while (isIdentifierPart(peek)) advance(); | 147 while (isIdentifierPart(peek)) advance(); |
| 125 String string = input.substring(start, index); | 148 String string = input.substring(start, index); |
| 126 return new IdentifierToken(start, string, KEYWORDS.contains(string)); | 149 Token result = new Token(start, string); |
| 150 // TODO(kasperl): Deal with null, undefined, true, and false in |
| 151 // a cleaner and faster way. |
| 152 if (OPERATORS.contains(string)) { |
| 153 result.withOp(string); |
| 154 } else { |
| 155 result.withGetterSetter(string); |
| 156 } |
| 157 return result; |
| 127 } | 158 } |
| 128 | 159 |
| 129 Token scanNumber(int start) { | 160 Token scanNumber(int start) { |
| 130 assert(isDigit(peek)); | 161 assert(isDigit(peek)); |
| 131 bool simple = (index == start); | 162 bool simple = (index == start); |
| 132 advance(); // Skip initial digit. | 163 advance(); // Skip initial digit. |
| 133 while (true) { | 164 while (true) { |
| 134 if (isDigit(peek)) { | 165 if (isDigit(peek)) { |
| 135 // Do nothing. | 166 // Do nothing. |
| 136 } else if (peek == $PERIOD) { | 167 } else if (peek == $PERIOD) { |
| 137 simple = false; | 168 simple = false; |
| 138 } else if (isExponentStart(peek)) { | 169 } else if (isExponentStart(peek)) { |
| 139 advance(); | 170 advance(); |
| 140 if (isExponentSign(peek)) advance(); | 171 if (isExponentSign(peek)) advance(); |
| 141 if (!isDigit(peek)) error('Invalid exponent', -1); | 172 if (!isDigit(peek)) error('Invalid exponent', -1); |
| 142 simple = false; | 173 simple = false; |
| 143 } else { | 174 } else { |
| 144 break; | 175 break; |
| 145 } | 176 } |
| 146 advance(); | 177 advance(); |
| 147 } | 178 } |
| 148 String string = input.substring(start, index); | 179 String string = input.substring(start, index); |
| 149 num value = simple ? int.parse(string) : double.parse(string); | 180 num value = simple ? int.parse(string) : double.parse(string); |
| 150 return new NumberToken(start, value); | 181 return new Token(start, string)..withValue(value); |
| 151 } | 182 } |
| 152 | 183 |
| 153 Token scanString() { | 184 Token scanString() { |
| 154 assert(peek == $SQ || peek == $DQ); | 185 assert(peek == $SQ || peek == $DQ); |
| 155 int start = index; | 186 int start = index; |
| 156 int quote = peek; | 187 int quote = peek; |
| 157 advance(); // Skip initial quote. | 188 advance(); // Skip initial quote. |
| 158 | 189 |
| 159 StringBuffer buffer; | 190 StringBuffer buffer; |
| 160 int marker = index; | 191 int marker = index; |
| 161 | 192 |
| 162 while (peek != quote) { | 193 while (peek != quote) { |
| 163 if (peek == $BACKSLASH) { | 194 if (peek == $BACKSLASH) { |
| 164 if (buffer == null) buffer = new StringBuffer(); | 195 if (buffer == null) buffer = new StringBuffer(); |
| 165 buffer.write(input.substring(marker, index)); | 196 buffer.write(input.substring(marker, index)); |
| 166 advance(); | 197 advance(); |
| 167 int unescaped; | 198 int unescaped; |
| 168 if (peek == $u) { | 199 if (peek == $u) { |
| 169 // TODO(kasperl): Check bounds? Make sure we have test | 200 // TODO(kasperl): Check bounds? Make sure we have test |
| 170 // coverage for this. | 201 // coverage for this. |
| 171 String hex = input.substring(index + 1, index + 5); | 202 String hex = input.substring(index + 1, index + 5); |
| 172 unescaped = int.parse(hex, radix: 16, onError: (ignore) { | 203 unescaped = int.parse(hex, radix: 16, onError: (ignore) { |
| 173 error('Invalid unicode escape [\\u$hex]'); }); | 204 error('Invalid unicode escape [\\u$hex]'); }); |
| 174 for (int i = 0; i < 5; i++) { | 205 for (int i = 0; i < 5; i++) advance(); |
| 175 advance(); | |
| 176 } | |
| 177 } else { | 206 } else { |
| 178 unescaped = unescape(peek); | 207 unescaped = unescape(peek); |
| 179 advance(); | 208 advance(); |
| 180 } | 209 } |
| 181 buffer.writeCharCode(unescaped); | 210 buffer.writeCharCode(unescaped); |
| 182 marker = index; | 211 marker = index; |
| 183 } else if (peek == $EOF) { | 212 } else if (peek == $EOF) { |
| 184 error('Unterminated quote'); | 213 error('Unterminated quote'); |
| 185 } else { | 214 } else { |
| 186 advance(); | 215 advance(); |
| 187 } | 216 } |
| 188 } | 217 } |
| 189 | 218 |
| 190 String last = input.substring(marker, index); | 219 String last = input.substring(marker, index); |
| 191 advance(); // Skip terminating quote. | 220 advance(); // Skip terminating quote. |
| 192 String string = input.substring(start, index); | 221 String string = input.substring(start, index); |
| 193 | 222 |
| 194 // Compute the unescaped string value. | 223 // Compute the unescaped string value. |
| 195 String unescaped = last; | 224 String unescaped = last; |
| 196 if (buffer != null) { | 225 if (buffer != null) { |
| 197 buffer.write(last); | 226 buffer.write(last); |
| 198 unescaped = buffer.toString(); | 227 unescaped = buffer.toString(); |
| 199 } | 228 } |
| 200 return new StringToken(start, string, unescaped); | 229 return new Token(start, string)..withValue(unescaped); |
| 201 } | 230 } |
| 202 | 231 |
| 203 void advance() { | 232 void advance() { |
| 204 peek = ++index >= length ? $EOF : input.codeUnitAt(index); | 233 if (++index >= length) peek = $EOF; |
| 234 else peek = input.codeUnitAt(index); |
| 205 } | 235 } |
| 206 | 236 |
| 207 void error(String message, [int offset = 0]) { | 237 void error(String message, [int offset = 0]) { |
| 208 // TODO(kasperl): Try to get rid of the offset. It is only used to match | 238 // TODO(kasperl): Try to get rid of the offset. It is only used to match |
| 209 // the error expectations in the lexer tests for numbers with exponents. | 239 // the error expectations in the lexer tests for numbers with exponents. |
| 210 int position = index + offset; | 240 int position = index + offset; |
| 211 throw "Lexer Error: $message at column $position in expression [$input]"; | 241 throw "Lexer Error: $message at column $position in expression [$input]"; |
| 212 } | 242 } |
| 213 } | 243 } |
| 214 | 244 |
| 215 Set<String> KEYWORDS = new Set<String>.from([ | 245 Set<String> OPERATORS = new Set<String>.from([ |
| 246 'undefined', |
| 216 'null', | 247 'null', |
| 217 'undefined', | |
| 218 'true', | 248 'true', |
| 219 'false', | 249 'false', |
| 220 ]); | |
| 221 | |
| 222 Set<String> OPERATORS = new Set<String>.from([ | |
| 223 '+', | 250 '+', |
| 224 '-', | 251 '-', |
| 225 '*', | 252 '*', |
| 226 '/', | 253 '/', |
| 227 '~/', | 254 '~/', |
| 228 '%', | 255 '%', |
| 229 '^', | 256 '^', |
| 230 '=', | 257 '=', |
| 231 '==', | 258 '==', |
| 232 '!=', | 259 '!=', |
| 233 '<', | 260 '<', |
| 234 '>', | 261 '>', |
| 235 '<=', | 262 '<=', |
| 236 '>=', | 263 '>=', |
| 237 '&&', | 264 '&&', |
| 238 '||', | 265 '||', |
| 239 '&', | 266 '&', |
| 240 '|', | 267 '|', |
| 241 '!', | 268 '!', |
| 242 '?', | 269 '?', |
| 243 ]); | 270 ]); |
| 244 | 271 |
| OLD | NEW |