| 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.tokenizer; |
| 6 |
| 7 const int _TAB = 9; |
| 8 const int _LF = 10; |
| 9 const int _VTAB = 11; |
| 10 const int _FF = 12; |
| 11 const int _CR = 13; |
| 12 const int _SPACE = 32; |
| 13 const int _BANG = 33; |
| 14 const int _DQ = 34; |
| 15 const int _$ = 36; |
| 16 const int _AMPERSAND = 38; |
| 17 const int _SQ = 39; |
| 18 const int _OPEN_PAREN = 40; |
| 19 const int _CLOSE_PAREN = 41; |
| 20 const int _STAR = 42; |
| 21 const int _PLUS = 43; |
| 22 const int _COMMA = 44; |
| 23 const int _MINUS = 45; |
| 24 const int _PERIOD = 46; |
| 25 const int _SLASH = 47; |
| 26 const int _0 = 48; |
| 27 const int _9 = 57; |
| 28 const int _COLON = 58; |
| 29 const int _LT = 60; |
| 30 const int _EQ = 61; |
| 31 const int _GT = 62; |
| 32 const int _QUESTION = 63; |
| 33 const int _A = 65; |
| 34 const int _Z = 90; |
| 35 const int _OPEN_SQUARE_BRACKET = 91; |
| 36 const int _BACKSLASH = 92; |
| 37 const int _CLOSE_SQUARE_BRACKET = 93; |
| 38 const int _CARET = 94; |
| 39 const int _US = 95; |
| 40 const int _a = 97; |
| 41 const int _f = 102; |
| 42 const int _n = 110; |
| 43 const int _r = 114; |
| 44 const int _t = 116; |
| 45 const int _v = 118; |
| 46 const int _z = 122; |
| 47 const int _OPEN_CURLY_BRACKET = 123; |
| 48 const int _BAR = 124; |
| 49 const int _CLOSE_CURLY_BRACKET = 125; |
| 50 const int _NBSP = 160; |
| 51 |
| 52 const _OPERATORS = const [_PLUS, _MINUS, _STAR, _SLASH, _BANG, _AMPERSAND, |
| 53 /*_COMMA,*/ _LT, _EQ, _GT, _QUESTION, _CARET, _BAR]; |
| 54 |
| 55 const _GROUPERS = const [_OPEN_PAREN, _CLOSE_PAREN, |
| 56 _OPEN_SQUARE_BRACKET, _CLOSE_SQUARE_BRACKET, |
| 57 _OPEN_CURLY_BRACKET, _CLOSE_CURLY_BRACKET]; |
| 58 |
| 59 const _TWO_CHAR_OPS = const ['==', '!=', '<=', '>=', '||', '&&']; |
| 60 |
| 61 const _KEYWORDS = const ['in', 'this']; |
| 62 |
| 63 const _PRECEDENCE = const { |
| 64 '!': 0, |
| 65 ':': 0, |
| 66 ',': 0, |
| 67 ')': 0, |
| 68 ']': 0, |
| 69 '}': 0, // ? |
| 70 '?': 1, |
| 71 '||': 2, |
| 72 '&&': 3, |
| 73 '|': 4, |
| 74 '^': 5, |
| 75 '&': 6, |
| 76 |
| 77 // equality |
| 78 '!=': 7, |
| 79 '==': 7, |
| 80 |
| 81 // relational |
| 82 '>=': 8, |
| 83 '>': 8, |
| 84 '<=': 8, |
| 85 '<': 8, |
| 86 |
| 87 // additive |
| 88 '+': 9, |
| 89 '-': 9, |
| 90 |
| 91 // multiplicative |
| 92 '%': 10, |
| 93 '/': 10, |
| 94 '*': 10, |
| 95 |
| 96 // postfix |
| 97 '(': 11, |
| 98 '[': 11, |
| 99 '.': 11, |
| 100 '{': 11, //not sure this is correct |
| 101 }; |
| 102 |
| 103 const POSTFIX_PRECEDENCE = 11; |
| 104 |
| 105 const int STRING_TOKEN = 1; |
| 106 const int IDENTIFIER_TOKEN = 2; |
| 107 const int DOT_TOKEN = 3; |
| 108 const int COMMA_TOKEN = 4; |
| 109 const int COLON_TOKEN = 5; |
| 110 const int INTEGER_TOKEN = 6; |
| 111 const int DECIMAL_TOKEN = 7; |
| 112 const int OPERATOR_TOKEN = 8; |
| 113 const int GROUPER_TOKEN = 9; |
| 114 const int KEYWORD_TOKEN = 10; |
| 115 |
| 116 bool isWhitespace(int next) => next == _SPACE || next == _TAB || next == _NBSP; |
| 117 |
| 118 bool isIdentifierOrKeywordStart(int next) => (_a <= next && next <= _z) || |
| 119 (_A <= next && next <= _Z) || next == _US || next == _$ || next > 127; |
| 120 |
| 121 bool isIdentifier(int next) => (_a <= next && next <= _z) || |
| 122 (_A <= next && next <= _Z) || (_0 <= next && next <= _9) || |
| 123 next == _US || next == _$ || next > 127; |
| 124 |
| 125 bool isQuote(int next) => next == _DQ || next == _SQ; |
| 126 |
| 127 bool isNumber(int next) => _0 <= next && next <= _9; |
| 128 |
| 129 bool isOperator(int next) => _OPERATORS.contains(next); |
| 130 |
| 131 bool isGrouper(int next) => _GROUPERS.contains(next); |
| 132 |
| 133 int escape(int c) { |
| 134 switch (c) { |
| 135 case _f: return _FF; break; |
| 136 case _n: return _LF; break; |
| 137 case _r: return _CR; break; |
| 138 case _t: return _TAB; break; |
| 139 case _v: return _VTAB; break; |
| 140 default: return c; |
| 141 } |
| 142 } |
| 143 |
| 144 class Token { |
| 145 final int kind; |
| 146 final String value; |
| 147 final int precedence; |
| 148 |
| 149 Token(this.kind, this.value, [this.precedence = 0]); |
| 150 |
| 151 String toString() => "($kind, '$value')"; |
| 152 } |
| 153 |
| 154 class Tokenizer { |
| 155 final List<Token> _tokens = <Token>[]; |
| 156 final StringBuffer _sb = new StringBuffer(); |
| 157 final RuneIterator _iterator; |
| 158 |
| 159 int _next; |
| 160 |
| 161 Tokenizer(String input) : _iterator = new RuneIterator(input); |
| 162 |
| 163 _advance() { |
| 164 _next = _iterator.moveNext() ? _iterator.current : null; |
| 165 } |
| 166 |
| 167 List<Token> tokenize() { |
| 168 _advance(); |
| 169 while(_next != null) { |
| 170 if (isWhitespace(_next)) { |
| 171 _advance(); |
| 172 } else if (isQuote(_next)) { |
| 173 tokenizeString(); |
| 174 } else if (isIdentifierOrKeywordStart(_next)) { |
| 175 tokenizeIdentifierOrKeyword(); |
| 176 } else if (isNumber(_next)) { |
| 177 tokenizeNumber(); |
| 178 } else if (_next == _PERIOD) { |
| 179 tokenizeDot(); |
| 180 } else if (_next == _COMMA) { |
| 181 tokenizeComma(); |
| 182 } else if (_next == _COLON) { |
| 183 tokenizeColon(); |
| 184 } else if (isOperator(_next)) { |
| 185 tokenizeOperator(); |
| 186 } else if (isGrouper(_next)) { |
| 187 tokenizeGrouper(); |
| 188 } else { |
| 189 _advance(); |
| 190 } |
| 191 } |
| 192 return _tokens; |
| 193 } |
| 194 |
| 195 tokenizeString() { |
| 196 int quoteChar = _next; |
| 197 _advance(); |
| 198 while (_next != quoteChar) { |
| 199 if (_next == null) throw new ParseException("unterminated string"); |
| 200 if (_next == _BACKSLASH) { |
| 201 _advance(); |
| 202 if (_next == null) throw new ParseException("unterminated string"); |
| 203 _sb.writeCharCode(escape(_next)); |
| 204 } else { |
| 205 _sb.writeCharCode(_next); |
| 206 } |
| 207 _advance(); |
| 208 } |
| 209 _tokens.add(new Token(STRING_TOKEN, _sb.toString())); |
| 210 _sb.clear(); |
| 211 _advance(); |
| 212 } |
| 213 |
| 214 tokenizeIdentifierOrKeyword() { |
| 215 while (_next != null && isIdentifier(_next)) { |
| 216 _sb.writeCharCode(_next); |
| 217 _advance(); |
| 218 } |
| 219 var value = _sb.toString(); |
| 220 if (_KEYWORDS.contains(value)) { |
| 221 _tokens.add(new Token(KEYWORD_TOKEN, value)); |
| 222 } else { |
| 223 _tokens.add(new Token(IDENTIFIER_TOKEN, value)); |
| 224 } |
| 225 _sb.clear(); |
| 226 } |
| 227 |
| 228 tokenizeNumber() { |
| 229 while (_next != null && isNumber(_next)) { |
| 230 _sb.writeCharCode(_next); |
| 231 _advance(); |
| 232 } |
| 233 if (_next == _PERIOD) { |
| 234 tokenizeDot(); |
| 235 } else { |
| 236 _tokens.add(new Token(INTEGER_TOKEN, _sb.toString())); |
| 237 _sb.clear(); |
| 238 } |
| 239 } |
| 240 |
| 241 tokenizeDot() { |
| 242 _advance(); |
| 243 if (isNumber(_next)) { |
| 244 tokenizeFraction(); |
| 245 } else { |
| 246 _tokens.add(new Token(DOT_TOKEN, '.', POSTFIX_PRECEDENCE)); |
| 247 } |
| 248 } |
| 249 |
| 250 tokenizeComma() { |
| 251 _advance(); |
| 252 _tokens.add(new Token(COMMA_TOKEN, ',')); |
| 253 } |
| 254 |
| 255 tokenizeColon() { |
| 256 _advance(); |
| 257 _tokens.add(new Token(COLON_TOKEN, ':')); |
| 258 } |
| 259 |
| 260 tokenizeFraction() { |
| 261 _sb.writeCharCode(_PERIOD); |
| 262 while (_next != null && isNumber(_next)) { |
| 263 _sb.writeCharCode(_next); |
| 264 _advance(); |
| 265 } |
| 266 _tokens.add(new Token(DECIMAL_TOKEN, _sb.toString())); |
| 267 _sb.clear(); |
| 268 } |
| 269 |
| 270 tokenizeOperator() { |
| 271 int startChar = _next; |
| 272 _advance(); |
| 273 var op; |
| 274 // check for 2 character operators |
| 275 if (isOperator(_next)) { |
| 276 var op2 = new String.fromCharCodes([startChar, _next]); |
| 277 if (_TWO_CHAR_OPS.contains(op2)) { |
| 278 op = op2; |
| 279 _advance(); |
| 280 } else { |
| 281 op = new String.fromCharCode(startChar); |
| 282 } |
| 283 } else { |
| 284 op = new String.fromCharCode(startChar); |
| 285 } |
| 286 _tokens.add(new Token(OPERATOR_TOKEN, op, _PRECEDENCE[op])); |
| 287 } |
| 288 |
| 289 tokenizeGrouper() { |
| 290 var value = new String.fromCharCode(_next); |
| 291 _tokens.add(new Token(GROUPER_TOKEN, value, _PRECEDENCE[value])); |
| 292 _advance(); |
| 293 } |
| 294 } |
| 295 |
| 296 class ParseException implements Exception { |
| 297 final String message; |
| 298 ParseException(this.message); |
| 299 String toString() => "ParseException: $message"; |
| 300 } |
| OLD | NEW |