├── .gitignore ├── LICENSE ├── README ├── bin ├── jsparser.dart ├── lexer.dart ├── nodes.dart ├── parser.dart ├── precedence.dart ├── printer.dart ├── resolver.dart ├── sample.js ├── sample2.js └── var.dart ├── lib ├── javascript_parser.dart └── javascript_parser.peg └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | **/packages 4 | **/.idea 5 | **/*.iml 6 | **/pubspec.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A simple JavaScript? parser written in Dart during the London Dart Hackathon 2 | 2012. 3 | 4 | The project's focus is on simplicity rather than speed. This allowed it to be 5 | implemented in a relatively short amount of time, and makes it easier to 6 | understand. 7 | -------------------------------------------------------------------------------- /bin/jsparser.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library jsdart; 16 | 17 | import "dart:io"; 18 | import 'package:jsparser2/javascript_parser.dart'; 19 | import "package:parser_error/parser_error.dart"; 20 | 21 | void main(List args) { 22 | bool printResolution = (args.length == 2 && args[0] == "--print-resolution"); 23 | if (args.length != 1 && !printResolution) { 24 | printUsage(); 25 | return; 26 | } 27 | File file = new File(args[printResolution ? 1 : 0]); 28 | file.readAsString().then((String content) { 29 | (parse(content)); 30 | }); 31 | } 32 | 33 | dynamic parse(String content) { 34 | var parser = new JavascriptParser(content); 35 | var result = parser.parse_Start(); 36 | if(!parser.success) { 37 | var messages = []; 38 | for(var error in parser.errors()) { 39 | messages.add(new ParserErrorMessage(error.message, error.start, error.position)); 40 | } 41 | 42 | var strings = ParserErrorFormatter.format(parser.text, messages); 43 | print(strings.join("\n")); 44 | throw new FormatException(); 45 | } 46 | 47 | print(joinAll(result)); 48 | 49 | return result; 50 | } 51 | 52 | String joinAll(result) { 53 | String res = ''; 54 | for(var val in result) { 55 | if(val is String) { 56 | res += val; 57 | } else if(val is List && val.isNotEmpty) { 58 | res += joinAll(val); 59 | } 60 | } 61 | return res; 62 | } 63 | 64 | void printUsage() { 65 | print("Usage: dart jsparser.dart [--print-resolution] "); 66 | } 67 | -------------------------------------------------------------------------------- /bin/lexer.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library lexer; 16 | 17 | final List KEYWORDS = const [ 18 | "as", "break", "case", "catch", "class", "const", "continue", "default", 19 | "delete", "do", "else", "extends", "false", "finally", "for", "function", 20 | "if", "import", "in", "instanceof", "is", "namespace", "new", "null", 21 | "package", "private", "public", "return", "super", "switch", "this", 22 | "throw", "true", "try", "typeof", "use", "var", "void", "while", "with"]; 23 | 24 | final List FUTURE_RESERVED = const [ 25 | "abstract", "debugger", "enum", "export", "goto", "implements", "interface", 26 | "native", "protected", "synchronized", "throws", "transient", "volatile"]; 27 | 28 | Set _keywordSet = null; 29 | Set get keywordSet { 30 | if (_keywordSet == null) { 31 | _keywordSet = new Set(); 32 | for (String keyword in KEYWORDS) _keywordSet.add(keyword); 33 | } 34 | return _keywordSet; 35 | } 36 | 37 | Set _futureReservedSet = null; 38 | Set get futureReservedSet { 39 | if (_futureReservedSet == null) { 40 | _futureReservedSet = new Set(); 41 | for (String reserved in FUTURE_RESERVED) _futureReservedSet.add(reserved); 42 | } 43 | return _futureReservedSet; 44 | } 45 | 46 | bool CARE_FUTURE_RESERVED = true; 47 | 48 | bool isReserved(String symbol) { 49 | if (keywordSet.contains(symbol)) return true; 50 | if (CARE_FUTURE_RESERVED && futureReservedSet.contains(symbol)) return true; 51 | return false; 52 | } 53 | 54 | class Token { 55 | final String type; 56 | final int position; 57 | final value; 58 | const Token(this.type, this.position, [this.value]); 59 | 60 | String toString() => "$type ($position): $value"; 61 | } 62 | 63 | final LINE_TERMINATORS = "\x0A\x0D"; 64 | final BLANKS_NO_LINE_TERMINATORS = "\x20\x09\x0B\x0C\xA0"; 65 | final BLANKS = "$BLANKS_NO_LINE_TERMINATORS$LINE_TERMINATORS"; 66 | final DIGITS = "0123456789"; 67 | final HEX ="${DIGITS}ABCDEFabcdef"; 68 | 69 | class Lexer { 70 | final String input; 71 | int position = 0; 72 | Lexer(this.input); 73 | 74 | bool isInStringSet(String c, String set) { 75 | for (int i = 0; i < set.length; i++) { 76 | if (set[i] == c) return true; 77 | } 78 | return false; 79 | } 80 | 81 | bool isBlank(String c) => isInStringSet(c, BLANKS); 82 | bool isDigit(String c) => isInStringSet(c, DIGITS); 83 | bool isHex(String c) => isInStringSet(c, HEX); 84 | bool isLineTerminator(String c) => isInStringSet(c, LINE_TERMINATORS); 85 | bool isBlankNoLineTerminator(String c) 86 | => isInStringSet(c, BLANKS_NO_LINE_TERMINATORS); 87 | bool isIdStart(String c) { 88 | if (c == "\$" || c == "_") return true; 89 | int $a = "a".codeUnitAt(0); 90 | int $z = "z".codeUnitAt(0); 91 | int $A = "A".codeUnitAt(0); 92 | int $Z = "Z".codeUnitAt(0); 93 | int cValue = c.codeUnitAt(0); 94 | return ($a <= cValue && cValue <= $z || 95 | $A <= cValue && cValue <= $Z); 96 | } 97 | bool isIdPart(String c) => isIdStart(c) || isDigit(c); 98 | 99 | 100 | bool charsLeft() => position < input.length; 101 | 102 | void eatBlanks() { 103 | while (charsLeft() && isBlankNoLineTerminator(input[position])) { 104 | position++; 105 | } 106 | } 107 | 108 | void eatLineTerminators() { 109 | while (charsLeft() && isLineTerminator(input[position])) { 110 | position++; 111 | } 112 | } 113 | 114 | bool pointsTo(String str) { 115 | if (position + str.length < input.length) { 116 | for (int i = 0; i < str.length; i++) { 117 | if (input[position + i] != str[i]) return false; 118 | } 119 | return true; 120 | } 121 | return false; 122 | } 123 | 124 | void eatUntilLineTerminator() { 125 | while (charsLeft() && !isLineTerminator(input[position])) position++; 126 | } 127 | 128 | void eatDigits() { 129 | while (charsLeft() && isDigit(input[position])) position++; 130 | } 131 | 132 | void eatHex() { 133 | while (charsLeft() && isHex(input[position])) position++; 134 | } 135 | 136 | Token readString(String startChar) { 137 | assert(charsLeft()); 138 | assert(input[position] == startChar); 139 | int startPos = position++; 140 | bool sawBackslash = false; 141 | while (charsLeft() && !isLineTerminator(input[position])) { 142 | if (sawBackslash) { 143 | sawBackslash = false; 144 | position++; 145 | } else { 146 | if (input[position] == startChar) { 147 | position++; 148 | String value = input.substring(startPos, position); 149 | return new Token("STRING", startPos, value); 150 | } 151 | sawBackslash = (input[position++] == "\\"); 152 | } 153 | } 154 | throw "Unterminated string $startPos"; 155 | } 156 | 157 | Token readSingleLineComment() { 158 | assert(charsLeft()); 159 | int startPos = position; 160 | while (charsLeft() && !isLineTerminator(input[position])) position++; 161 | return new Token("SINGLE_LINE_COMMENT", position, input.substring(startPos, position)); 162 | } 163 | 164 | Token readKeywordOrIdentifier() { 165 | assert(isIdStart(input[position])); 166 | int startPos = position; 167 | while (charsLeft() && isIdPart(input[position])) position++; 168 | String value = input.substring(startPos, position); 169 | if (isReserved(value)) { 170 | return new Token(value.toUpperCase(), startPos, value); 171 | } 172 | return new Token("ID", startPos, value); 173 | } 174 | 175 | /** 176 | * Returns null if no line terminator was found. Throws if the comment was not 177 | * terminated. Otherwise returns the position of the first new line in the 178 | * comment. 179 | */ 180 | int eatMultiLineComment() { 181 | int startPos = position; 182 | bool sawStar = false; 183 | int lineTerminatorPosition = null; 184 | while (charsLeft()) { 185 | String c = input[position++]; 186 | if (c == "/" && sawStar) return lineTerminatorPosition; 187 | if (lineTerminatorPosition == null && isLineTerminator(c)) { 188 | lineTerminatorPosition = position - 1; 189 | } 190 | sawStar = (c == "*"); 191 | } 192 | throw "Unterminated multi-line comment $startPos"; 193 | } 194 | 195 | Token readNumber() { 196 | Token createNumberToken(int startPos) { 197 | String value = input.substring(startPos, position); 198 | return new Token("NUMBER", startPos, value); 199 | } 200 | 201 | assert(isDigit(input[position]) || input[position] == '.'); 202 | bool startsWithDot = input[position] == '.'; 203 | if (startsWithDot) position++; 204 | int startPos = position; 205 | eatDigits(); 206 | String c = input[position]; 207 | if (c == "." && !startsWithDot) { 208 | // Floating-point constant. 209 | position++; 210 | if (charsLeft() && isDigit(input[position])) { 211 | eatDigits(); 212 | if (charsLeft() && 213 | (input[position] == "e" || input[position] == "E")) { 214 | position++; // Eat the "e" or "E"; 215 | if (charsLeft() && 216 | (input[position] == "+" || input[position] == "-")) { 217 | position++; // Eat the exponent sign. 218 | } 219 | if (!charsLeft() || !isDigit(input[position])) { 220 | throw "Unterminated number literal $startPos"; 221 | } 222 | eatDigits(); 223 | } 224 | } 225 | } else if (c == "x" || c == "X") { 226 | // Hexadecimal 227 | position++; 228 | if (!charsLeft() || !isHex(input[position])) { 229 | throw "Unterminated number literal $startPos"; 230 | } 231 | eatHex(); 232 | } 233 | return createNumberToken(startPos); 234 | } 235 | 236 | 237 | Token get eofToken => new Token("EOF", input.length); 238 | Token consumeSymbolToken(String symbol) { 239 | int len = symbol.length; 240 | int pos = position; 241 | position += len; 242 | return new Token(symbol, pos, symbol); 243 | } 244 | 245 | Token next() { 246 | eatBlanks(); 247 | // Line comments. 248 | if (pointsTo("//")) { 249 | return readSingleLineComment(); 250 | // eatUntilLineTerminator(); 251 | } 252 | if (pointsTo("/*")) { 253 | // Make sure we don't skip line terminators; 254 | int lineTerminatorPosition = eatMultiLineComment(); 255 | if (lineTerminatorPosition != null) { 256 | return new Token("NEW_LINE", lineTerminatorPosition); 257 | } 258 | } 259 | if (position >= input.length) return eofToken; 260 | String c = input[position]; 261 | // New lines. 262 | if (isLineTerminator(c)) { 263 | Token result = new Token("NEW_LINE", position); 264 | eatLineTerminators(); 265 | return result; 266 | } 267 | // Number constants. 268 | // TODO(floitsch): handle octal numbers? 269 | if (isInStringSet(c, DIGITS)) { 270 | return readNumber(); 271 | } 272 | switch (c) { 273 | case "{": return new Token("LBRACE", position++, c); 274 | case "}": return new Token("RBRACE", position++, c); 275 | case "(": return new Token("LPAREN", position++, c); 276 | case ")": return new Token("RPAREN", position++, c); 277 | case "[": return new Token("LBRACKET", position++, c); 278 | case "]": return new Token("RBRACKET", position++, c); 279 | case ";": return new Token("SEMICOLON", position++, c); 280 | case ",": return new Token("COMMA", position++, c); 281 | case ":": 282 | case "?": 283 | case "~": 284 | return new Token(c, position++, c); 285 | case ".": 286 | position++; 287 | if (charsLeft() && isDigit(input[position])) { 288 | position--; 289 | return readNumber(); 290 | } 291 | return new Token("DOT", position, c); 292 | case "|": 293 | if (pointsTo("||")) return consumeSymbolToken("||"); 294 | if (pointsTo("|=")) return consumeSymbolToken("|="); 295 | return new Token(c, position++, c); 296 | case "&": 297 | if (pointsTo("&&")) return consumeSymbolToken("&&"); 298 | if (pointsTo("&=")) return consumeSymbolToken("&="); 299 | return new Token(c, position++, c); 300 | case "<": 301 | if (pointsTo("<<=")) return consumeSymbolToken("<<="); 302 | if (pointsTo("<<")) return consumeSymbolToken("<<"); 303 | if (pointsTo("<=")) return consumeSymbolToken("<="); 304 | return new Token(c, position++, c); 305 | case ">": 306 | if (pointsTo(">>>")) return consumeSymbolToken(">>>"); 307 | if (pointsTo(">>=")) return consumeSymbolToken(">>="); 308 | if (pointsTo(">>")) return consumeSymbolToken(">>"); 309 | if (pointsTo(">=")) return consumeSymbolToken(">="); 310 | return new Token(c, position++, c); 311 | case "!": 312 | if (pointsTo("!==")) return consumeSymbolToken("!=="); 313 | if (pointsTo("!=")) return consumeSymbolToken("!="); 314 | return new Token(c, position++, c); 315 | case "=": 316 | if (pointsTo("===")) return consumeSymbolToken("==="); 317 | if (pointsTo("==")) return consumeSymbolToken("=="); 318 | return new Token(c, position++, c); 319 | case "+": 320 | case "-": 321 | case "*": 322 | case "/": 323 | case "%": 324 | case "^": 325 | position++; 326 | if (charsLeft() && input[position] == "=") { 327 | String symbol = "$c="; 328 | return new Token(symbol, (position++) - 1, symbol); 329 | } 330 | // ++ and --. 331 | if ((c == "+" || c == "-") && input[position] == c) { 332 | String symbol = "$c$c"; 333 | return new Token(symbol, (position++) - 1, symbol); 334 | } 335 | return new Token(c, position - 1, c); 336 | case "'": 337 | case '"': 338 | return readString(c); 339 | default: 340 | if (isIdStart(c)) { 341 | return readKeywordOrIdentifier(); 342 | } 343 | throw "Unexpected character $c $position"; 344 | } 345 | } 346 | 347 | Token lexRegExp() { 348 | int startPos = position; 349 | bool sawBackslash = false; 350 | while (charsLeft() && !isLineTerminator(input[position])) { 351 | if (sawBackslash) { 352 | sawBackslash = false; 353 | position++; 354 | } else { 355 | if (input[position] == "/") { 356 | position++; 357 | // Now read the regexp flags. 358 | while (isIdPart(input[position])) position++; 359 | String value = input.substring(startPos, position); 360 | return new Token("REGEXP", startPos, value); 361 | } 362 | sawBackslash = (input[position++] == "\\"); 363 | } 364 | } 365 | throw "Unterminated regexp $startPos"; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /bin/nodes.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library nodes; 16 | 17 | import 'precedence.dart'; 18 | 19 | abstract class NodeVisitor { 20 | T visitProgram(Program node); 21 | 22 | T visitBlock(Block node); 23 | T visitExpressionStatement(ExpressionStatement node); 24 | T visitEmptyStatement(EmptyStatement node); 25 | T visitIf(If node); 26 | T visitFor(For node); 27 | T visitForIn(ForIn node); 28 | T visitWhile(While node); 29 | T visitDo(Do node); 30 | T visitContinue(Continue node); 31 | T visitBreak(Break node); 32 | T visitReturn(Return node); 33 | T visitThrow(Throw node); 34 | T visitTry(Try node); 35 | T visitCatch(Catch node); 36 | T visitWith(With node); 37 | T visitSwitch(Switch node); 38 | T visitCase(Case node); 39 | T visitDefault(Default node); 40 | T visitFunctionDeclaration(FunctionDeclaration node); 41 | T visitLabeledStatement(LabeledStatement node); 42 | 43 | T visitVariableDeclarationList(VariableDeclarationList node); 44 | T visitSequence(Sequence node); 45 | T visitAssignment(Assignment node); 46 | T visitVariableInitialization(VariableInitialization node); 47 | T visitConditional(Conditional node); 48 | T visitNew(New node); 49 | T visitCall(Call node); 50 | T visitBinary(Binary node); 51 | T visitPrefix(Prefix node); 52 | T visitPostfix(Postfix node); 53 | 54 | T visitVariableUse(VariableUse node); 55 | T visitThis(This node); 56 | T visitVariableDeclaration(VariableDeclaration node); 57 | T visitParameter(Parameter node); 58 | T visitPropertyAccess(PropertyAccess node); 59 | 60 | T visitNamedFunction(NamedFunction node); 61 | T visitFun(Fun node); 62 | 63 | T visitLiteralBool(LiteralBool node); 64 | T visitLiteralString(LiteralString node); 65 | T visitLiteralNumber(LiteralNumber node); 66 | T visitLiteralNull(LiteralNull node); 67 | T visitLiteralUndefined(LiteralUndefined node); 68 | T visitSingleLineComment(SingleLineComment node); 69 | 70 | T visitArrayInitializer(ArrayInitializer node); 71 | T visitArrayElement(ArrayElement node); 72 | T visitObjectInitializer(ObjectInitializer node); 73 | T visitProperty(Property node); 74 | T visitRegExpLiteral(RegExpLiteral node); 75 | } 76 | 77 | class BaseVisitor implements NodeVisitor { 78 | T visitNode(Node node) { 79 | node.visitChildren(this); 80 | return null; 81 | } 82 | 83 | T visitProgram(Program node) => visitNode(node); 84 | 85 | T visitStatement(Statement node) => visitNode(node); 86 | T visitLoop(Loop node) => visitStatement(node); 87 | T visitJump(Statement node) => visitStatement(node); 88 | 89 | T visitBlock(Block node) => visitStatement(node); 90 | T visitExpressionStatement(ExpressionStatement node) 91 | => visitStatement(node); 92 | T visitEmptyStatement(EmptyStatement node) => visitStatement(node); 93 | T visitIf(If node) => visitStatement(node); 94 | T visitFor(For node) => visitLoop(node); 95 | T visitForIn(ForIn node) => visitLoop(node); 96 | T visitWhile(While node) => visitLoop(node); 97 | T visitDo(Do node) => visitLoop(node); 98 | T visitContinue(Continue node) => visitJump(node); 99 | T visitBreak(Break node) => visitJump(node); 100 | T visitReturn(Return node) => visitJump(node); 101 | T visitThrow(Throw node) => visitJump(node); 102 | T visitTry(Try node) => visitStatement(node); 103 | T visitWith(With node) => visitStatement(node); 104 | T visitSwitch(Switch node) => visitStatement(node); 105 | T visitFunctionDeclaration(FunctionDeclaration node) 106 | => visitStatement(node); 107 | T visitLabeledStatement(LabeledStatement node) => visitStatement(node); 108 | 109 | T visitCatch(Catch node) => visitNode(node); 110 | T visitCase(Case node) => visitNode(node); 111 | T visitDefault(Default node) => visitNode(node); 112 | 113 | T visitExpression(Expression node) => visitNode(node); 114 | T visitVariableReference(VariableReference node) => visitExpression(node); 115 | 116 | T visitVariableDeclarationList(VariableDeclarationList node) 117 | => visitExpression(node); 118 | T visitSequence(Sequence node) => visitExpression(node); 119 | T visitAssignment(Assignment node) => visitExpression(node); 120 | T visitVariableInitialization(VariableInitialization node) { 121 | if (node.value != null) { 122 | visitAssignment(node); 123 | } else { 124 | visitExpression(node); 125 | } 126 | } 127 | T visitConditional(Conditional node) => visitExpression(node); 128 | T visitNew(New node) => visitExpression(node); 129 | T visitCall(Call node) => visitExpression(node); 130 | T visitBinary(Binary node) => visitCall(node); 131 | T visitPrefix(Prefix node) => visitCall(node); 132 | T visitPostfix(Postfix node) => visitCall(node); 133 | T visitPropertyAccess(PropertyAccess node) => visitExpression(node); 134 | 135 | T visitVariableUse(VariableUse node) => visitVariableReference(node); 136 | T visitVariableDeclaration(VariableDeclaration node) 137 | => visitVariableReference(node); 138 | T visitParameter(Parameter node) => visitVariableDeclaration(node); 139 | T visitThis(This node) => visitParameter(node); 140 | 141 | T visitNamedFunction(NamedFunction node) => visitExpression(node); 142 | T visitFun(Fun node) => visitExpression(node); 143 | 144 | T visitLiteral(Literal node) => visitExpression(node); 145 | 146 | T visitLiteralBool(LiteralBool node) => visitLiteral(node); 147 | T visitLiteralString(LiteralString node) => visitLiteral(node); 148 | T visitLiteralNumber(LiteralNumber node) => visitLiteral(node); 149 | T visitLiteralNull(LiteralNull node) => visitLiteral(node); 150 | T visitLiteralUndefined(LiteralUndefined node) => visitLiteral(node); 151 | 152 | T visitArrayInitializer(ArrayInitializer node) => visitExpression(node); 153 | T visitArrayElement(ArrayElement node) => visitNode(node); 154 | T visitObjectInitializer(ObjectInitializer node) => visitExpression(node); 155 | T visitProperty(Property node) => visitNode(node); 156 | T visitRegExpLiteral(RegExpLiteral node) => visitExpression(node); 157 | } 158 | 159 | abstract class Node { 160 | accept(NodeVisitor visitor); 161 | void visitChildren(NodeVisitor visitor); 162 | } 163 | 164 | class Program extends Node { 165 | final List body; 166 | Program(this.body); 167 | 168 | accept(NodeVisitor visitor) => visitor.visitProgram(this); 169 | void visitChildren(NodeVisitor visitor) { 170 | for (Statement statement in body) statement.accept(visitor); 171 | } 172 | } 173 | 174 | abstract class Statement extends Node { 175 | } 176 | 177 | class Block extends Statement { 178 | final List statements; 179 | Block(this.statements); 180 | Block.empty() : this.statements = []; 181 | 182 | accept(NodeVisitor visitor) => visitor.visitBlock(this); 183 | void visitChildren(NodeVisitor visitor) { 184 | for (Statement statement in statements) statement.accept(visitor); 185 | } 186 | } 187 | 188 | class ExpressionStatement extends Statement { 189 | final Expression expression; 190 | ExpressionStatement(this.expression); 191 | 192 | accept(NodeVisitor visitor) => visitor.visitExpressionStatement(this); 193 | void visitChildren(NodeVisitor visitor) { expression.accept(visitor); } 194 | } 195 | 196 | class EmptyStatement extends Statement { 197 | EmptyStatement(); 198 | 199 | accept(NodeVisitor visitor) => visitor.visitEmptyStatement(this); 200 | void visitChildren(NodeVisitor visitor) {} 201 | } 202 | 203 | class If extends Statement { 204 | final Expression condition; 205 | final Node then; 206 | final Node otherwise; 207 | 208 | If(this.condition, this.then, this.otherwise); 209 | If.noElse(this.condition, this.then) : this.otherwise = new EmptyStatement(); 210 | 211 | bool get hasElse => otherwise is !EmptyStatement; 212 | 213 | accept(NodeVisitor visitor) => visitor.visitIf(this); 214 | 215 | void visitChildren(NodeVisitor visitor) { 216 | condition.accept(visitor); 217 | then.accept(visitor); 218 | otherwise.accept(visitor); 219 | } 220 | } 221 | 222 | abstract class Loop extends Statement { 223 | final Statement body; 224 | Loop(this.body); 225 | } 226 | 227 | class For extends Loop { 228 | final Expression init; 229 | final Expression condition; 230 | final Expression update; 231 | 232 | For(this.init, this.condition, this.update, Statement body) : super(body); 233 | 234 | accept(NodeVisitor visitor) => visitor.visitFor(this); 235 | 236 | void visitChildren(NodeVisitor visitor) { 237 | if (init != null) init.accept(visitor); 238 | if (condition != null) condition.accept(visitor); 239 | if (update != null) update.accept(visitor); 240 | body.accept(visitor); 241 | } 242 | } 243 | 244 | class ForIn extends Loop { 245 | // Note that [VariableDeclarationList] is a subclass of [Expression]. 246 | // Therefore we can type the leftHandSide as [Expression]. 247 | final Expression leftHandSide; 248 | final Expression object; 249 | 250 | ForIn(this.leftHandSide, this.object, Statement body) : super(body); 251 | 252 | accept(NodeVisitor visitor) => visitor.visitForIn(this); 253 | 254 | void visitChildren(NodeVisitor visitor) { 255 | leftHandSide.accept(visitor); 256 | object.accept(visitor); 257 | body.accept(visitor); 258 | } 259 | } 260 | 261 | class While extends Loop { 262 | final Node condition; 263 | 264 | While(this.condition, Statement body) : super(body); 265 | 266 | accept(NodeVisitor visitor) => visitor.visitWhile(this); 267 | 268 | void visitChildren(NodeVisitor visitor) { 269 | condition.accept(visitor); 270 | body.accept(visitor); 271 | } 272 | } 273 | 274 | class Do extends Loop { 275 | final Expression condition; 276 | 277 | Do(Statement body, this.condition) : super(body); 278 | 279 | accept(NodeVisitor visitor) => visitor.visitDo(this); 280 | 281 | void visitChildren(NodeVisitor visitor) { 282 | body.accept(visitor); 283 | condition.accept(visitor); 284 | } 285 | } 286 | 287 | class Continue extends Statement { 288 | final String targetLabel; // Can be null. 289 | 290 | Continue(this.targetLabel); 291 | 292 | accept(NodeVisitor visitor) => visitor.visitContinue(this); 293 | void visitChildren(NodeVisitor visitor) {} 294 | } 295 | 296 | class Break extends Statement { 297 | final String targetLabel; // Can be null. 298 | 299 | Break(this.targetLabel); 300 | 301 | accept(NodeVisitor visitor) => visitor.visitBreak(this); 302 | void visitChildren(NodeVisitor visitor) {} 303 | } 304 | 305 | class Return extends Statement { 306 | final Expression value; // Can be null. 307 | 308 | Return([this.value = null]); 309 | 310 | accept(NodeVisitor visitor) => visitor.visitReturn(this); 311 | 312 | void visitChildren(NodeVisitor visitor) { 313 | if (value != null) value.accept(visitor); 314 | } 315 | } 316 | 317 | class Throw extends Statement { 318 | final Expression expression; 319 | 320 | Throw(this.expression); 321 | 322 | accept(NodeVisitor visitor) => visitor.visitThrow(this); 323 | 324 | void visitChildren(NodeVisitor visitor) { 325 | expression.accept(visitor); 326 | } 327 | } 328 | 329 | class Try extends Statement { 330 | final Block body; 331 | final Catch catchPart; // Can be null if [finallyPart] is non-null. 332 | final Block finallyPart; // Can be null if [catchPart] is non-null. 333 | 334 | Try(this.body, this.catchPart, this.finallyPart) { 335 | assert(catchPart != null || finallyPart != null); 336 | } 337 | 338 | accept(NodeVisitor visitor) => visitor.visitTry(this); 339 | 340 | void visitChildren(NodeVisitor visitor) { 341 | body.accept(visitor); 342 | if (catchPart != null) catchPart.accept(visitor); 343 | if (finallyPart != null) finallyPart.accept(visitor); 344 | } 345 | } 346 | 347 | class Catch extends Node { 348 | final VariableDeclaration declaration; 349 | final Block body; 350 | 351 | Catch(this.declaration, this.body); 352 | 353 | accept(NodeVisitor visitor) => visitor.visitCatch(this); 354 | 355 | void visitChildren(NodeVisitor visitor) { 356 | declaration.accept(visitor); 357 | body.accept(visitor); 358 | } 359 | } 360 | 361 | class With extends Statement { 362 | Expression object; 363 | Statement body; 364 | With(this.object, this.body); 365 | 366 | accept(NodeVisitor visitor) => visitor.visitWith(this); 367 | void visitChildren(NodeVisitor visitor) { 368 | object.accept(visitor); 369 | body.accept(visitor); 370 | } 371 | } 372 | 373 | class Switch extends Statement { 374 | final Expression key; 375 | final List cases; 376 | 377 | Switch(this.key, this.cases); 378 | 379 | accept(NodeVisitor visitor) => visitor.visitSwitch(this); 380 | 381 | void visitChildren(NodeVisitor visitor) { 382 | key.accept(visitor); 383 | for (SwitchClause clause in cases) clause.accept(visitor); 384 | } 385 | } 386 | 387 | abstract class SwitchClause extends Node { 388 | final Block body; 389 | 390 | SwitchClause(this.body); 391 | } 392 | 393 | class Case extends SwitchClause { 394 | final Expression expression; 395 | 396 | Case(this.expression, Block body) : super(body); 397 | 398 | accept(NodeVisitor visitor) => visitor.visitCase(this); 399 | 400 | void visitChildren(NodeVisitor visitor) { 401 | expression.accept(visitor); 402 | body.accept(visitor); 403 | } 404 | } 405 | 406 | class Default extends SwitchClause { 407 | Default(Block body) : super(body); 408 | 409 | accept(NodeVisitor visitor) => visitor.visitDefault(this); 410 | 411 | void visitChildren(NodeVisitor visitor) { 412 | body.accept(visitor); 413 | } 414 | } 415 | 416 | class FunctionDeclaration extends Statement { 417 | final VariableDeclaration name; 418 | final Fun function; 419 | 420 | FunctionDeclaration(this.name, this.function); 421 | 422 | accept(NodeVisitor visitor) => visitor.visitFunctionDeclaration(this); 423 | 424 | void visitChildren(NodeVisitor visitor) { 425 | name.accept(visitor); 426 | function.accept(visitor); 427 | } 428 | } 429 | 430 | class LabeledStatement extends Statement { 431 | final String label; 432 | final Statement body; 433 | 434 | LabeledStatement(this.label, this.body); 435 | 436 | accept(NodeVisitor visitor) => visitor.visitLabeledStatement(this); 437 | 438 | void visitChildren(NodeVisitor visitor) { 439 | body.accept(visitor); 440 | } 441 | } 442 | 443 | abstract class Expression extends Node { 444 | int get precedenceLevel; 445 | } 446 | 447 | /** 448 | * [VariableDeclarationList] is a subclass of [Expression] to simplify the 449 | * AST. 450 | */ 451 | class VariableDeclarationList extends Expression { 452 | final List declarations; 453 | 454 | VariableDeclarationList(this.declarations); 455 | 456 | accept(NodeVisitor visitor) => visitor.visitVariableDeclarationList(this); 457 | 458 | void visitChildren(NodeVisitor visitor) { 459 | for (VariableInitialization declaration in declarations) { 460 | declaration.accept(visitor); 461 | } 462 | } 463 | 464 | int get precedenceLevel => EXPRESSION; 465 | } 466 | 467 | class Sequence extends Expression { 468 | final List expressions; 469 | 470 | Sequence(this.expressions); 471 | 472 | accept(NodeVisitor visitor) => visitor.visitSequence(this); 473 | 474 | void visitChildren(NodeVisitor visitor) { 475 | for (Expression expr in expressions) expr.accept(visitor); 476 | } 477 | 478 | int get precedenceLevel => EXPRESSION; 479 | } 480 | 481 | class Assignment extends Expression { 482 | final Expression leftHandSide; 483 | // `null`, if the assignment is not compound. 484 | final VariableReference compoundTarget; 485 | final Expression value; // May be `null`, for [VariableInitialization]s. 486 | 487 | Assignment(this.leftHandSide, this.value) : compoundTarget = null; 488 | Assignment.compound(this.leftHandSide, String op, this.value) 489 | : compoundTarget = new VariableUse(op); 490 | 491 | int get precedenceLevel => ASSIGNMENT; 492 | 493 | bool get isCompound => compoundTarget != null; 494 | String get op => compoundTarget == null ? null : compoundTarget.name; 495 | 496 | accept(NodeVisitor visitor) => visitor.visitAssignment(this); 497 | 498 | void visitChildren(NodeVisitor visitor) { 499 | leftHandSide.accept(visitor); 500 | if (compoundTarget != null) compoundTarget.accept(visitor); 501 | if (value != null) value.accept(visitor); 502 | } 503 | } 504 | 505 | class VariableInitialization extends Assignment { 506 | /** [value] may be null. */ 507 | VariableInitialization(VariableDeclaration declaration, Expression value) 508 | : super(declaration, value); 509 | 510 | VariableDeclaration get declaration => leftHandSide; 511 | 512 | accept(NodeVisitor visitor) => visitor.visitVariableInitialization(this); 513 | } 514 | 515 | class Conditional extends Expression { 516 | final Expression condition; 517 | final Expression then; 518 | final Expression otherwise; 519 | 520 | Conditional(this.condition, this.then, this.otherwise); 521 | 522 | accept(NodeVisitor visitor) => visitor.visitConditional(this); 523 | 524 | void visitChildren(NodeVisitor visitor) { 525 | condition.accept(visitor); 526 | then.accept(visitor); 527 | otherwise.accept(visitor); 528 | } 529 | 530 | int get precedenceLevel => ASSIGNMENT; 531 | } 532 | 533 | class Call extends Expression { 534 | Expression target; 535 | List arguments; 536 | 537 | Call(this.target, this.arguments); 538 | 539 | accept(NodeVisitor visitor) => visitor.visitCall(this); 540 | 541 | void visitChildren(NodeVisitor visitor) { 542 | target.accept(visitor); 543 | for (Expression arg in arguments) arg.accept(visitor); 544 | } 545 | 546 | int get precedenceLevel => CALL; 547 | } 548 | 549 | class New extends Call { 550 | New(Expression cls, List arguments) : super(cls, arguments); 551 | 552 | accept(NodeVisitor visitor) => visitor.visitNew(this); 553 | } 554 | 555 | class Binary extends Call { 556 | Binary(String op, Expression left, Expression right) 557 | : super(new VariableUse(op), [left, right]); 558 | 559 | String get op { 560 | VariableUse use = target; 561 | return use.name; 562 | } 563 | 564 | Expression get left => arguments[0]; 565 | Expression get right => arguments[1]; 566 | 567 | accept(NodeVisitor visitor) => visitor.visitBinary(this); 568 | 569 | int get precedenceLevel { 570 | // TODO(floitsch): switch to constant map. 571 | switch (op) { 572 | case "*": 573 | case "/": 574 | case "%": 575 | return MULTIPLICATIVE; 576 | case "+": 577 | case "-": 578 | return ADDITIVE; 579 | case "<<": 580 | case ">>": 581 | case ">>>": 582 | return SHIFT; 583 | case "<": 584 | case ">": 585 | case "<=": 586 | case ">=": 587 | case "instanceof": 588 | case "in": 589 | return RELATIONAL; 590 | case "==": 591 | case "===": 592 | case "!=": 593 | case "!==": 594 | return EQUALITY; 595 | case "&": 596 | return BIT_AND; 597 | case "^": 598 | return BIT_XOR; 599 | case "|": 600 | return BIT_OR; 601 | case "&&": 602 | return LOGICAL_AND; 603 | case "||": 604 | return LOGICAL_OR; 605 | default: 606 | throw "Internal Error: Unhandled binary operator: $op"; 607 | } 608 | } 609 | } 610 | 611 | class Prefix extends Call { 612 | Prefix(String op, Expression arg) 613 | : super(new VariableUse(op), [arg]); 614 | 615 | String get op => (target as VariableUse).name; 616 | Expression get argument => arguments[0]; 617 | 618 | accept(NodeVisitor visitor) => visitor.visitPrefix(this); 619 | 620 | int get precedenceLevel => UNARY; 621 | } 622 | 623 | class Postfix extends Call { 624 | Postfix(String op, Expression arg) 625 | : super(new VariableUse(op), [arg]); 626 | 627 | String get op => (target as VariableUse).name; 628 | Expression get argument => arguments[0]; 629 | 630 | accept(NodeVisitor visitor) => visitor.visitPostfix(this); 631 | 632 | int get precedenceLevel => UNARY; 633 | } 634 | 635 | abstract class VariableReference extends Expression { 636 | final String name; 637 | 638 | // We treat operators as if they were special functions. They can thus be 639 | // referenced like other variables. 640 | VariableReference(this.name); 641 | 642 | accept(NodeVisitor visitor); 643 | int get precedenceLevel => PRIMARY; 644 | void visitChildren(NodeVisitor visitor) {} 645 | } 646 | 647 | class VariableUse extends VariableReference { 648 | VariableUse(String name) : super(name); 649 | 650 | accept(NodeVisitor visitor) => visitor.visitVariableUse(this); 651 | } 652 | 653 | class VariableDeclaration extends VariableReference { 654 | VariableDeclaration(String name) : super(name); 655 | 656 | accept(NodeVisitor visitor) => visitor.visitVariableDeclaration(this); 657 | } 658 | 659 | class Parameter extends VariableDeclaration { 660 | Parameter(String id) : super(id); 661 | 662 | accept(NodeVisitor visitor) => visitor.visitParameter(this); 663 | } 664 | 665 | class This extends Parameter { 666 | This() : super("this"); 667 | 668 | accept(NodeVisitor visitor) => visitor.visitThis(this); 669 | } 670 | 671 | class NamedFunction extends Expression { 672 | final VariableDeclaration name; 673 | final Fun function; 674 | 675 | NamedFunction(this.name, this.function); 676 | 677 | accept(NodeVisitor visitor) => visitor.visitNamedFunction(this); 678 | 679 | void visitChildren(NodeVisitor visitor) { 680 | name.accept(visitor); 681 | function.accept(visitor); 682 | } 683 | 684 | int get precedenceLevel => CALL; 685 | } 686 | 687 | class Fun extends Expression { 688 | final List params; 689 | final Block body; 690 | 691 | Fun(this.params, this.body); 692 | 693 | accept(NodeVisitor visitor) => visitor.visitFun(this); 694 | 695 | void visitChildren(NodeVisitor visitor) { 696 | for (Parameter param in params) param.accept(visitor); 697 | body.accept(visitor); 698 | } 699 | 700 | int get precedenceLevel => CALL; 701 | } 702 | 703 | class PropertyAccess extends Expression { 704 | final Expression receiver; 705 | final Expression selector; 706 | 707 | PropertyAccess(this.receiver, this.selector); 708 | PropertyAccess.field(this.receiver, String fieldName) 709 | : selector = new LiteralString("'$fieldName'"); 710 | PropertyAccess.indexed(this.receiver, int index) 711 | : selector = new LiteralNumber('$index'); 712 | 713 | accept(NodeVisitor visitor) => visitor.visitPropertyAccess(this); 714 | 715 | void visitChildren(NodeVisitor visitor) { 716 | receiver.accept(visitor); 717 | selector.accept(visitor); 718 | } 719 | 720 | int get precedenceLevel => CALL; 721 | } 722 | 723 | abstract class Literal extends Expression { 724 | void visitChildren(NodeVisitor visitor) {} 725 | 726 | int get precedenceLevel => PRIMARY; 727 | } 728 | 729 | class LiteralBool extends Literal { 730 | final bool value; 731 | 732 | LiteralBool(this.value); 733 | 734 | accept(NodeVisitor visitor) => visitor.visitLiteralBool(this); 735 | // [visitChildren] inherited from [Literal]. 736 | } 737 | 738 | class LiteralUndefined extends Literal { 739 | LiteralUndefined(); 740 | 741 | accept(NodeVisitor visitor) => visitor.visitLiteralUndefined(this); 742 | // [visitChildren] inherited from [Literal]. 743 | } 744 | 745 | class LiteralNull extends Literal { 746 | LiteralNull(); 747 | 748 | accept(NodeVisitor visitor) => visitor.visitLiteralNull(this); 749 | } 750 | 751 | class LiteralString extends Literal { 752 | final String value; 753 | 754 | LiteralString(this.value); 755 | 756 | accept(NodeVisitor visitor) => visitor.visitLiteralString(this); 757 | } 758 | 759 | class LiteralNumber extends Literal { 760 | final String value; 761 | 762 | LiteralNumber(this.value); 763 | 764 | accept(NodeVisitor visitor) => visitor.visitLiteralNumber(this); 765 | } 766 | 767 | class SingleLineComment extends Statement { 768 | 769 | final String value; 770 | 771 | SingleLineComment(this.value); 772 | 773 | accept(NodeVisitor visitor) => visitor.visitLiteralString(this); 774 | } 775 | 776 | class ArrayInitializer extends Expression { 777 | final int length; 778 | // We represent the array as sparse list of elements. Each element knows its 779 | // position in the array. 780 | final List elements; 781 | 782 | ArrayInitializer(this.length, this.elements); 783 | 784 | factory ArrayInitializer.from(Iterable expressions) => 785 | new ArrayInitializer(expressions.length, _convert(expressions)); 786 | 787 | accept(NodeVisitor visitor) => visitor.visitArrayInitializer(this); 788 | 789 | void visitChildren(NodeVisitor visitor) { 790 | for (ArrayElement element in elements) element.accept(visitor); 791 | } 792 | 793 | int get precedenceLevel => PRIMARY; 794 | 795 | static List _convert(Iterable expressions) { 796 | int index = 0; 797 | return expressions.map( 798 | (expression) => new ArrayElement(index++, expression)) 799 | .toList(); 800 | } 801 | } 802 | 803 | /** 804 | * An expression inside an [ArrayInitializer]. An [ArrayElement] knows 805 | * its position in the containing [ArrayInitializer]. 806 | */ 807 | class ArrayElement extends Node { 808 | int index; 809 | Expression value; 810 | 811 | ArrayElement(this.index, this.value); 812 | 813 | accept(NodeVisitor visitor) => visitor.visitArrayElement(this); 814 | 815 | void visitChildren(NodeVisitor visitor) { 816 | value.accept(visitor); 817 | } 818 | } 819 | 820 | class ObjectInitializer extends Expression { 821 | List properties; 822 | 823 | ObjectInitializer(this.properties); 824 | 825 | accept(NodeVisitor visitor) => visitor.visitObjectInitializer(this); 826 | 827 | void visitChildren(NodeVisitor visitor) { 828 | for (Property init in properties) init.accept(visitor); 829 | } 830 | 831 | int get precedenceLevel => PRIMARY; 832 | } 833 | 834 | class Property extends Node { 835 | Literal name; 836 | Expression value; 837 | 838 | Property(this.name, this.value); 839 | 840 | accept(NodeVisitor visitor) => visitor.visitProperty(this); 841 | 842 | void visitChildren(NodeVisitor visitor) { 843 | name.accept(visitor); 844 | value.accept(visitor); 845 | } 846 | } 847 | 848 | /** 849 | * [RegExpLiteral]s, despite being called "Literal", are not inheriting from 850 | * [Literal]. Indeed, regular expressions in JavaScript have a side-effect and 851 | * are thus not in the same category as numbers or strings. 852 | */ 853 | class RegExpLiteral extends Expression { 854 | /** Contains the pattern and the flags.*/ 855 | String pattern; 856 | 857 | RegExpLiteral(this.pattern); 858 | 859 | accept(NodeVisitor visitor) => visitor.visitRegExpLiteral(this); 860 | void visitChildren(NodeVisitor visitor) {} 861 | 862 | int get precedenceLevel => PRIMARY; 863 | } 864 | -------------------------------------------------------------------------------- /bin/parser.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library parser; 16 | import "lexer.dart"; 17 | import "nodes.dart"; 18 | 19 | class Parser { 20 | final Lexer lexer; 21 | List peekedTokens; 22 | // Includes new-lines. 23 | String previousTokenType = null; 24 | 25 | Parser(this.lexer) : this.peekedTokens = []; 26 | 27 | void error(String msg, var obj, Token token) { 28 | throw "$msg: $obj. $token"; 29 | } 30 | 31 | void unexpectedToken(Token token) { 32 | throw "Unexpected token: $token"; 33 | } 34 | 35 | Token peekToken() { 36 | if (peekedTokens.isEmpty) { 37 | Token nextToken; 38 | while (true) { 39 | nextToken = lexer.next(); 40 | if (nextToken.type == "NEW_LINE") { 41 | previousTokenType = nextToken.type; 42 | } else { 43 | break; 44 | } 45 | } 46 | peekedTokens.add(nextToken); 47 | } 48 | return peekedTokens.last; 49 | } 50 | 51 | void pushBackToken(Token token) { 52 | peekedTokens.add(token); 53 | } 54 | 55 | String peekTokenType() => peekToken().type; 56 | 57 | bool isAtNewLineToken() => previousTokenType == 'NEW_LINE'; 58 | 59 | // TODO(floitsch): this returns a value instead of the token. Inconsistent 60 | // with [consumeAny]. 61 | dynamic consume(String type) { 62 | Token token = consumeAny(); 63 | if (token.type == type) return token.value; 64 | print("Expected: $type"); 65 | unexpectedToken(token); 66 | } 67 | 68 | void consumeStatementSemicolon() { 69 | String nextTokenType = peekTokenType(); 70 | if (nextTokenType == "SEMICOLON") { 71 | consumeAny(); 72 | } else if (nextTokenType == "RBRACE" 73 | || isAtNewLineToken() 74 | || nextTokenType == "EOF") { 75 | // Do nothing. 76 | } else { 77 | unexpectedToken(peekToken()); 78 | } 79 | } 80 | 81 | Token consumeAny() { 82 | Token result = peekToken(); 83 | previousTokenType = result.type; 84 | peekedTokens.removeLast(); 85 | return result; 86 | } 87 | 88 | bool atEof() => peekTokenType() == "EOF"; 89 | 90 | Program parseProgram() => new Program(parseStatements()); 91 | 92 | List parseStatements() { 93 | List result = []; 94 | while (!atEof()) { 95 | Statement stmt = parseStatement(); 96 | result.add(stmt); 97 | } 98 | return result; 99 | } 100 | 101 | Statement parseStatement() { 102 | switch (peekTokenType()) { 103 | case "LBRACE": return parseBlock(); 104 | case "SEMICOLON": return parseEmptyStatement(); 105 | case "IF": return parseIf(); 106 | case "FOR": return parseFor(); 107 | case "WHILE": return parseWhile(); 108 | case "DO": return parseDoWhile(); 109 | case "CONTINUE": return parseContinue(); 110 | case "BREAK": return parseBreak(); 111 | case "RETURN": return parseReturn(); 112 | case "WITH": return parseWith(); 113 | case "SWITCH": return parseSwitch(); 114 | case "THROW": return parseThrow(); 115 | case "TRY": return parseTry(); 116 | case "FUNCTION": 117 | // The Ecmascript specification actually does not allow a function 118 | // declaration to be at any place where a statement can be, but every 119 | // engine implements it this way. 120 | return parseFunctionDeclaration(); 121 | case "ID": return parseLabeledOrExpression(); 122 | case 'SINGLE_LINE_COMMENT': 123 | var value = consumeAny().value; 124 | print("consuming SingleLineComment: " + value); 125 | return new SingleLineComment(value); 126 | default: 127 | // Errors will be handled in the expression parser. 128 | // Var declaration lists are handled by the expression statements too. 129 | return parseExpressionStatement(); 130 | } 131 | } 132 | 133 | Block parseBlock() { 134 | consume("LBRACE"); 135 | List statements = []; 136 | while (peekTokenType() != "RBRACE") { 137 | // Errors will be handled in the statement parser. 138 | statements.add(parseStatement()); 139 | } 140 | consumeAny(); 141 | return new Block(statements); 142 | } 143 | 144 | EmptyStatement parseEmptyStatement() { 145 | consume("SEMICOLON"); 146 | return new EmptyStatement(); 147 | } 148 | 149 | If parseIf() { 150 | consume("IF"); 151 | consume("LPAREN"); 152 | Expression test = parseExpression(false); 153 | consume("RPAREN"); 154 | Statement then = parseStatement(); 155 | Statement otherwise; 156 | if (peekTokenType() == "ELSE") { 157 | consumeAny(); 158 | otherwise = parseStatement(); 159 | } else { 160 | otherwise = new EmptyStatement(); 161 | } 162 | return new If(test, then, otherwise); 163 | } 164 | 165 | /** Returns either a [For] or a [ForIn] loop. */ 166 | Loop parseFor() { 167 | consume("FOR"); 168 | consume("LPAREN"); 169 | 170 | Expression firstPart; 171 | if (peekTokenType() != "SEMICOLON") { 172 | firstPart = parseVarExpression(true); 173 | } 174 | 175 | switch (peekTokenType()) { 176 | case "SEMICOLON": return parseForInitTestIncr(firstPart); 177 | case "IN": return parseForIn(firstPart); 178 | default: throw "internal error"; 179 | } 180 | } 181 | 182 | // for (init; test; incr) body; 183 | For parseForInitTestIncr(Expression init) { 184 | consume("SEMICOLON"); 185 | Expression test = null; 186 | if (peekTokenType() != "SEMICOLON") { 187 | test = parseExpression(false); 188 | } 189 | consume("SEMICOLON"); 190 | 191 | Expression incr; 192 | if (peekTokenType() != "RPAREN") { 193 | incr = parseExpression(false); 194 | } 195 | consume("RPAREN"); 196 | 197 | Statement body = parseStatement(); 198 | return new For(init, test, incr, body); 199 | } 200 | 201 | ForIn parseForIn(Expression firstPart) { 202 | Token errorToken = peekToken(); 203 | consume("IN"); 204 | Expression obj = parseExpression(false); 205 | consume("RPAREN"); 206 | Statement body = parseStatement(); 207 | if (firstPart is VariableDeclarationList) { 208 | VariableDeclarationList varDecl = firstPart; 209 | if (varDecl.declarations.length != 1) { 210 | error("Only one variable allowed in 'for-in' statement", 211 | varDecl.declarations[1].declaration.name, 212 | errorToken); 213 | } 214 | return new ForIn(firstPart, obj, body); 215 | } else if (firstPart is VariableUse || firstPart is PropertyAccess) { 216 | return new ForIn(firstPart, obj, body); 217 | } else { 218 | print(firstPart); 219 | } 220 | error("Bad left-hand side in 'for-in' loop construct", 221 | firstPart, 222 | errorToken); 223 | } 224 | 225 | While parseWhile() { 226 | consume("WHILE"); 227 | consume("LPAREN"); 228 | Expression test = parseExpression(false); 229 | consume("RPAREN"); 230 | Statement body = parseStatement(); 231 | return new While(test, body); 232 | } 233 | 234 | Do parseDoWhile() { 235 | consume("DO"); 236 | Statement body = parseStatement(); 237 | consume("WHILE"); 238 | consume("LPAREN"); 239 | Expression test = parseExpression(false); 240 | consume("RPAREN"); 241 | consumeStatementSemicolon(); 242 | return new Do(body, test); 243 | } 244 | 245 | Continue parseContinue() { 246 | consume("CONTINUE"); 247 | if (!isAtNewLineToken() && peekTokenType() == "ID") { 248 | String id = consume("ID"); 249 | consumeStatementSemicolon(); 250 | return new Continue(id); 251 | } else { 252 | consumeStatementSemicolon(); 253 | return new Continue(null); 254 | } 255 | } 256 | 257 | Break parseBreak() { 258 | consume("BREAK"); 259 | if (!isAtNewLineToken() && peekTokenType() == "ID") { 260 | String id = consume("ID"); 261 | consumeStatementSemicolon(); 262 | return new Break(id); 263 | } else { 264 | consumeStatementSemicolon(); 265 | return new Break(null); 266 | } 267 | } 268 | 269 | Return parseReturn() { 270 | consume("RETURN"); 271 | Expression value; 272 | if (isAtNewLineToken()) { 273 | value = new LiteralUndefined(); 274 | } else { 275 | switch (peekTokenType()) { 276 | case "EOF": 277 | case "ERROR": 278 | case "SEMICOLON": 279 | value = new LiteralUndefined(); 280 | break; 281 | default: 282 | value = parseExpression(false); 283 | } 284 | consumeStatementSemicolon(); 285 | return new Return(value); 286 | } 287 | } 288 | 289 | With parseWith() { 290 | consume("WITH"); 291 | consume("LPAREN"); 292 | Expression expr = parseExpression(false); 293 | consume("RPAREN"); 294 | Statement body = parseStatement(); 295 | return new With(expr, body); 296 | } 297 | 298 | Switch parseSwitch() { 299 | consume("SWITCH"); 300 | consume("LPAREN"); 301 | Expression key = parseExpression(false); 302 | consume("RPAREN"); 303 | List clauses = parseCaseBlock(); 304 | return new Switch(key, clauses); 305 | } 306 | 307 | List parseCaseBlock() { 308 | consume("LBRACE"); 309 | List clauses = []; 310 | bool defaultCaseIsDone = false; 311 | while(peekTokenType() != "RBRACE") { 312 | switch (peekTokenType()) { 313 | case "CASE": 314 | clauses.add(parseCaseClause()); 315 | break; 316 | case "DEFAULT": 317 | if (defaultCaseIsDone) { 318 | error("Only one default-clause allowed", peekToken(), peekToken()); 319 | } 320 | clauses.add(parseDefaultClause()); 321 | break; 322 | } 323 | } 324 | consume("RBRACE"); 325 | return clauses; 326 | } 327 | 328 | Case parseCaseClause() { 329 | consume("CASE"); 330 | Expression expr = parseExpression(false); 331 | consume(":"); 332 | Block body = parseSwitchClauseStatements(); 333 | return new Case(expr, body); 334 | } 335 | 336 | Default parseDefaultClause() { 337 | consume("DEFAULT"); 338 | consume(":"); 339 | return new Default(parseSwitchClauseStatements()); 340 | } 341 | 342 | Block parseSwitchClauseStatements() { 343 | List statements = []; 344 | while (true) { 345 | switch (peekTokenType()) { 346 | case "RBRACE": 347 | case "EOF": 348 | case "ERROR": 349 | case "DEFAULT": 350 | case "CASE": 351 | return new Block(statements); 352 | default: 353 | statements.add(parseStatement()); 354 | } 355 | } 356 | } 357 | 358 | Throw parseThrow() { 359 | consume("THROW"); 360 | if (isAtNewLineToken()) { 361 | error("throw must have a value", null, peekToken()); 362 | } 363 | Expression expr = parseExpression(false); 364 | consumeStatementSemicolon(); 365 | return new Throw(expr); 366 | } 367 | 368 | Try parseTry() { 369 | Token errorToken = peekToken(); 370 | consume("TRY"); 371 | Block body = parseBlock(); 372 | Catch catchPart = null; 373 | Block finallyPart = null; 374 | if (peekTokenType() == "CATCH") { 375 | catchPart = parseCatch(); 376 | } 377 | if (peekTokenType() == "FINALLY") { 378 | finallyPart = parseFinally(); 379 | } 380 | if (catchPart == null && finallyPart == null) { 381 | error("Try without catch and finally", null, errorToken); 382 | } 383 | return new Try(body, catchPart, finallyPart); 384 | } 385 | 386 | Catch parseCatch() { 387 | consume("CATCH"); 388 | consume("LPAREN"); 389 | String id = consume("ID"); 390 | consume("RPAREN"); 391 | Block body = parseBlock(); 392 | return new Catch(new Parameter(id), body); 393 | } 394 | 395 | Block parseFinally() { 396 | consume("FINALLY"); 397 | return parseBlock(); 398 | } 399 | 400 | Statement parseLabeledOrExpression() { 401 | Token idToken = consumeAny(); 402 | String nextTokenType = peekTokenType(); 403 | assert(idToken.type == "ID"); 404 | pushBackToken(idToken); 405 | if (nextTokenType == ":") { 406 | return parseLabeled(); 407 | } else { 408 | return parseExpressionStatement(); 409 | } 410 | } 411 | 412 | Statement parseExpressionStatement() { 413 | Expression expr = parseVarExpression(false); 414 | consumeStatementSemicolon(); 415 | return new ExpressionStatement(expr); 416 | } 417 | 418 | LabeledStatement parseLabeled() { 419 | String id = consume("ID"); 420 | consume(":"); 421 | return new LabeledStatement(id, parseStatement()); 422 | } 423 | 424 | FunctionDeclaration parseFunctionDeclaration() => parseFunction(true); 425 | /** Returns either a Function or a NamedFunction */ 426 | Expression parseFunctionExpression() => parseFunction(false); 427 | 428 | Node parseFunction(isDeclaration) { 429 | consume("FUNCTION"); 430 | String id = null; 431 | if (isDeclaration || peekTokenType() == "ID") id = consume("ID"); 432 | List params = parseParameters(); 433 | // According to the spec we cannot just parse a body, but must 434 | Block body = parseBlock(); 435 | Fun fun = new Fun(params, body); 436 | if (isDeclaration) return new FunctionDeclaration(new VariableDeclaration(id), fun); 437 | if (id != null) return new NamedFunction(new VariableDeclaration(id), fun); 438 | return fun; 439 | } 440 | 441 | List parseParameters() { 442 | List result = []; 443 | consume("LPAREN"); 444 | if (peekTokenType() == "RPAREN") { 445 | consumeAny(); 446 | return result; 447 | } 448 | while(true) { 449 | result.add(new Parameter(consume("ID"))); 450 | if (peekTokenType() != "COMMA") break; 451 | consumeAny(); 452 | } 453 | consume("RPAREN"); 454 | return result; 455 | } 456 | 457 | /** Also parses var-declarations. */ 458 | Expression parseVarExpression(bool inForInit) { 459 | if (peekTokenType() == "VAR") { 460 | return parseVariableDeclarationList(inForInit); 461 | } 462 | return parseExpression(inForInit); 463 | } 464 | 465 | VariableDeclarationList parseVariableDeclarationList(bool inForInit) { 466 | consume("VAR"); 467 | List declarations = [parseVar(inForInit)]; 468 | while (true) { 469 | switch (peekTokenType()) { 470 | case "SEMICOLON": 471 | return new VariableDeclarationList(declarations); 472 | case "COMMA": 473 | consumeAny(); 474 | declarations.add(parseVar(inForInit)); 475 | break; 476 | case "IN": 477 | if (!inForInit) error("bad token: ", "in", peekToken()); 478 | return new VariableDeclarationList(declarations); 479 | default: 480 | if (!inForInit && (isAtNewLineToken() || peekTokenType() == "EOF")) { 481 | return new VariableDeclarationList(declarations); 482 | } 483 | unexpectedToken(consumeAny()); 484 | } 485 | } 486 | } 487 | 488 | VariableInitialization parseVar(bool inForInit) { 489 | String id = consume("ID"); 490 | if (peekTokenType() == "=") { 491 | consumeAny(); 492 | return new VariableInitialization(new VariableDeclaration(id), parseAssignExpression(inForInit)); 493 | } 494 | return new VariableInitialization(new VariableDeclaration(id), null); 495 | } 496 | 497 | Expression parseExpression(bool inForInit) => parseSequence(inForInit); 498 | 499 | Expression parseSequence(bool inForInit) { 500 | Expression expr = parseAssignExpression(inForInit); 501 | if (peekTokenType() == "COMMA") { 502 | List expressions = [expr]; 503 | while (peekTokenType() == "COMMA") { 504 | consumeAny(); 505 | expressions.add(parseAssignExpression(inForInit)); 506 | } 507 | return new Sequence(expressions); 508 | } else { 509 | return expr; 510 | } 511 | } 512 | 513 | bool isAssignOperator(String tokenType) { 514 | switch (tokenType) { 515 | case "=": case "*=": case "/=": case "%=": case "+=": case "-=": 516 | case "<<=": case ">>=": case ">>>=": case "&=": case "^=": case "|=": 517 | return true; 518 | default: 519 | return false; 520 | } 521 | } 522 | 523 | Expression parseAssignExpression(bool inForInit) { 524 | removeEquals(String op) { 525 | assert(op[op.length - 1] == "="); 526 | return op.substring(0, op.length - 1); 527 | } 528 | 529 | Token errorToken = peekToken(); 530 | Expression expr = parseConditionalExpression(inForInit); 531 | if (!isAssignOperator(peekTokenType())) return expr; 532 | String op = consumeAny().value; 533 | Expression rhs = parseAssignExpression(inForInit); 534 | if ((op == "=") && expr is PropertyAccess) return new Assignment(expr, rhs); 535 | if ((op == "=") && expr is VariableUse) return new Assignment(expr, rhs); 536 | if (op == "=") error("bad assignment", null, errorToken); 537 | if (expr is PropertyAccess) { 538 | op = removeEquals(op); 539 | return new Assignment.compound(expr, op, rhs); 540 | } 541 | if (expr is VariableUse) { 542 | op = removeEquals(op); 543 | return new Assignment.compound(expr, op, rhs); 544 | } 545 | error("bad assignment", null, errorToken); 546 | } 547 | 548 | Expression parseConditionalExpression(bool inForInit) { 549 | Expression expr = parseBinaryExpression(inForInit); 550 | if (peekTokenType() != "?") return expr; 551 | consumeAny(); 552 | Expression then = parseAssignExpression(false); 553 | consume(":"); 554 | Expression otherwise = parseAssignExpression(inForInit); 555 | return new Conditional(expr, then, otherwise); 556 | } 557 | 558 | int operatorLevel(String tokenType) { 559 | switch (tokenType) { 560 | case "||": return 1; 561 | case "&&": return 2; 562 | case "|": return 3; 563 | case "^": return 4; 564 | case "&": return 5; 565 | case "==": 566 | case "!=": 567 | case "===": 568 | case "!==": return 6; 569 | case "<": 570 | case ">": 571 | case "<=": 572 | case ">=": 573 | case "INSTANCEOF": 574 | case "IN": return 7; 575 | case "<<": 576 | case ">>": 577 | case ">>>": return 8; 578 | case "+": 579 | case "-": return 9; 580 | case "*": 581 | case "/": 582 | case "%": return 10; 583 | default: return null; 584 | } 585 | } 586 | 587 | // Left-associative binary expression. 588 | Expression parseBinaryExpression(bool inForInit) { 589 | Expression parseBinaryExpressionOfLevel(int level) { 590 | if (level > 10) return parseUnary(); 591 | Expression expr = parseBinaryExpressionOfLevel(level + 1); 592 | while (true) { 593 | String type = peekTokenType(); 594 | if (inForInit && type == "IN") return expr; 595 | int newLevel = operatorLevel(type); 596 | if (newLevel == null) return expr; 597 | if (newLevel != level) return expr; 598 | String op = consumeAny().value; 599 | Expression other = parseBinaryExpressionOfLevel(level + 1); 600 | expr = new Binary(op, expr, other); 601 | } 602 | } 603 | 604 | return parseBinaryExpressionOfLevel(1); 605 | } 606 | 607 | Expression parseUnary() { 608 | switch (peekTokenType()) { 609 | case "DELETE": 610 | case "VOID": 611 | case "TYPEOF": 612 | case "~": 613 | case "!": 614 | String op = consumeAny().value; 615 | Expression expr = parseUnary(); 616 | return new Prefix(op, expr); 617 | case "++": 618 | case "--": 619 | case "+": 620 | case "-": 621 | String op = "prefix${consumeAny().value}"; 622 | Expression expr = parseUnary(); 623 | return new Prefix(op, expr); 624 | default: 625 | return parsePostfix(); 626 | } 627 | } 628 | 629 | Expression parsePostfix() { 630 | Expression lhs = parseLeftHandSide(); 631 | if (!isAtNewLineToken() && 632 | (peekTokenType() == "++" || peekTokenType() == "--")) { 633 | String op = consumeAny().value; 634 | return new Postfix(op, lhs); 635 | } 636 | return lhs; 637 | } 638 | 639 | // We start by getting all news (parseNewExpression). 640 | // The remaining access and calls are then caught by the parseAccessOrCall 641 | // invocation allowing call-parenthesis. 642 | // 643 | // The parseAccessOrCall in parseNewExpression does not allow any parenthesis 644 | // to be consumed as they would be part of the NewExpression. 645 | Expression parseLeftHandSide() { 646 | return parseAccessOrCall(parseNewExpression(), true); 647 | } 648 | 649 | Expression parseNewExpression() { 650 | if (peekTokenType() != "NEW") { 651 | // print(peekToken().value); 652 | return parseAccessOrCall(parsePrimary(), false); 653 | } 654 | consumeAny(); 655 | Expression cls = parseNewExpression(); 656 | List args = peekTokenType() == "LPAREN" 657 | ? parseArguments() 658 | : []; 659 | return new New(cls, args); 660 | } 661 | 662 | Expression parseAccessOrCall(Expression expr, bool callsAreAllowed) { 663 | String parseFieldName() { 664 | if (peekTokenType() == "ID") return consume("ID"); 665 | // TODO(floitsch): allow reserved identifiers as field names. 666 | unexpectedToken(consumeAny()); 667 | } 668 | 669 | while (true) { 670 | switch (peekTokenType()) { 671 | case "LBRACKET": 672 | consumeAny(); 673 | Expression field = parseExpression(false); 674 | consume("RBRACKET"); 675 | expr = new PropertyAccess(expr, field); 676 | break; 677 | case "DOT": 678 | consumeAny(); 679 | String field = parseFieldName(); 680 | // Transform o.x into o["x"]. 681 | expr = new PropertyAccess(expr, new LiteralString('$field')); 682 | break; 683 | case "LPAREN": 684 | if (callsAreAllowed) { 685 | expr = new Call(expr, parseArguments()); 686 | } else { 687 | return expr; 688 | } 689 | break; 690 | default: 691 | return expr; 692 | } 693 | } 694 | } 695 | 696 | List parseArguments() { 697 | consume("LPAREN"); 698 | List result = []; 699 | if (peekTokenType() == "RPAREN") { 700 | consumeAny(); 701 | return result; 702 | } 703 | result.add(parseAssignExpression(false)); 704 | while (peekTokenType() != "RPAREN") { 705 | consume("COMMA"); 706 | result.add(parseAssignExpression(false)); 707 | } 708 | consumeAny(); 709 | return result; 710 | } 711 | 712 | Expression parsePrimary() { 713 | switch (peekTokenType()) { 714 | case "FUNCTION": 715 | return parseFunctionExpression(); 716 | case "THIS": 717 | consumeAny(); 718 | return new This(); 719 | case "ID": 720 | return new VariableUse(consume("ID")); 721 | case "LPAREN": 722 | consumeAny(); 723 | Expression expr = parseExpression(false); 724 | consume("RPAREN"); 725 | return expr; 726 | case "LBRACKET": 727 | return parseArrayLiteral(); 728 | case "LBRACE": 729 | return parseObjectLiteral(); 730 | case "NULL": 731 | consumeAny(); 732 | return new LiteralNull(); 733 | case "TRUE": 734 | case "FALSE": 735 | return new LiteralBool(consumeAny().value == "true"); 736 | case "NUMBER": 737 | return new LiteralNumber(consumeAny().value); // Stays a string. 738 | case "STRING": 739 | return new LiteralString(consumeAny().value); // Still with quotes. 740 | case "/": 741 | case "/=": 742 | return parseRegExpLiteral(); 743 | default: 744 | unexpectedToken(peekToken()); 745 | } 746 | } 747 | 748 | ArrayInitializer parseArrayLiteral() { 749 | // Basically: every array-element finishes with ",". 750 | // However, the very last one can be avoided if the array-element is not 751 | // an ellision. 752 | // In other words: [a,] and [a] are equivalent, but [,] and [] are not. 753 | // Whenever we find a (non-empty) array-element, we automatically consume 754 | // the "," (if it exists). 755 | consume("LBRACKET"); 756 | List elements = []; 757 | int length = 0; 758 | while (true) { 759 | switch (peekTokenType()) { 760 | case "RBRACKET": 761 | consumeAny(); 762 | return new ArrayInitializer(length, elements); 763 | case "COMMA": 764 | consumeAny(); 765 | length++; 766 | break; 767 | default: 768 | elements.add(new ArrayElement(length, parseAssignExpression(false))); 769 | length++; 770 | if (peekTokenType() != "RBRACKET") { 771 | consume("COMMA"); 772 | } 773 | } 774 | } 775 | } 776 | 777 | ObjectInitializer parseObjectLiteral() { 778 | Literal parsePropertyName() { 779 | switch (peekTokenType()) { 780 | case "ID": 781 | // For simplicity transform the identifier into a string. 782 | // Example: foo -> "foo". 783 | String id = consume("ID"); 784 | return new LiteralString('"$id"'); 785 | case "STRING": 786 | return new LiteralString(consume("STRING")); 787 | case "NUMBER": 788 | return new LiteralNumber(consume("NUMBER")); 789 | default: 790 | // TODO(floitsch): allow reserved identifiers as field names. 791 | unexpectedToken(consumeAny()); 792 | } 793 | } 794 | 795 | Property parsePropertyInit() { 796 | Literal name = parsePropertyName(); 797 | consume(":"); 798 | Expression value = parseAssignExpression(false); 799 | return new Property(name, value); 800 | } 801 | 802 | consume("LBRACE"); 803 | List properties = []; 804 | while (peekTokenType() != "RBRACE") { 805 | if (!properties.isEmpty) consume("COMMA"); 806 | properties.add(parsePropertyInit()); 807 | } 808 | consumeAny(); // The "RBRACE"; 809 | return new ObjectInitializer(properties); 810 | } 811 | 812 | RegExpLiteral parseRegExpLiteral() { 813 | // We must not consume the token before we have parsed the regexp. 814 | // Otherwise we would request a new token from the lexer thus messing 815 | // up the regexp. 816 | Token token = lexer.lexRegExp(); 817 | if (token.type != "REGEXP") unexpectedToken(token); 818 | Token regExpStart = consumeAny(); 819 | // Note that no weeding has been done on the RegExp flags. 820 | return new RegExpLiteral("${regExpStart.value}${token.value}"); 821 | } 822 | } 823 | -------------------------------------------------------------------------------- /bin/precedence.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 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 precedence; 6 | 7 | const EXPRESSION = 0; 8 | const ASSIGNMENT = EXPRESSION + 1; 9 | const LOGICAL_OR = ASSIGNMENT + 1; 10 | const LOGICAL_AND = LOGICAL_OR + 1; 11 | const BIT_OR = LOGICAL_AND + 1; 12 | const BIT_XOR = BIT_OR + 1; 13 | const BIT_AND = BIT_XOR + 1; 14 | const EQUALITY = BIT_AND + 1; 15 | const RELATIONAL = EQUALITY + 1; 16 | const SHIFT = RELATIONAL + 1; 17 | const ADDITIVE = SHIFT + 1; 18 | const MULTIPLICATIVE = ADDITIVE + 1; 19 | const UNARY = MULTIPLICATIVE + 1; 20 | const LEFT_HAND_SIDE = UNARY + 1; 21 | // We merge new, call and member expressions. 22 | // This means that we have to emit parenthesis for 'new's. For example `new X;` 23 | // should be printed as `new X();`. This simplifies the requirements. 24 | const CALL = LEFT_HAND_SIDE; 25 | const PRIMARY = CALL + 1; 26 | -------------------------------------------------------------------------------- /bin/printer.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library printer; 16 | import "nodes.dart"; 17 | 18 | class Printer implements NodeVisitor { 19 | StringBuffer outBuffer; 20 | int indentLevel = 0; 21 | 22 | void out(String str) { 23 | outBuffer.write(str); 24 | } 25 | void outLn(String str) { outBuffer.write(str); outBuffer.write("\n"); } 26 | void outIndent(String str) { indent(); out(str); } 27 | void outIndentLn(String str) { indent(); outLn(str); } 28 | void indent() { 29 | for (int i = 0; i < indentLevel; i++) out(" "); 30 | } 31 | 32 | Printer() : outBuffer = new StringBuffer(); 33 | visit(Node node) { 34 | node.accept(this); 35 | } 36 | 37 | visitInterleaved(List nodes, String separator) { 38 | for (int i = 0; i < nodes.length; i++) { 39 | if (i != 0) out(separator); 40 | visit(nodes[i]); 41 | } 42 | } 43 | 44 | visitAll(List nodes) { 45 | nodes.forEach(visit); 46 | } 47 | 48 | visitProgram(Program program) { 49 | visitAll(program.body); 50 | } 51 | 52 | visitBlock(Block block) { 53 | out("{\n"); 54 | indentLevel++; 55 | visitAll(block.statements); 56 | indentLevel--; 57 | outIndent("}"); 58 | } 59 | 60 | visitExpressionStatement(ExpressionStatement expressionStatement) { 61 | indent(); 62 | visit(expressionStatement.expression); 63 | outLn(";"); 64 | } 65 | 66 | visitEmptyStatement(EmptyStatement nop) { 67 | outIndentLn(";"); 68 | } 69 | 70 | visitIf(If node) { 71 | outIndent("if ("); 72 | visit(node.condition); 73 | out(") "); 74 | // Visit dangling else problem. 75 | if (node.hasElse && node.then is If) { 76 | out("{\n"); 77 | visit(node.then); 78 | outIndent("}"); 79 | } else { 80 | visitBlock(node.then); 81 | } 82 | if (node.hasElse) { 83 | out(" else "); 84 | visit(node.otherwise); 85 | outLn(""); 86 | } else { 87 | outLn(""); 88 | } 89 | } 90 | 91 | visitFor(For loop) { 92 | outIndent("for ("); 93 | if (loop.init != null) visit(loop.init); 94 | out("; "); 95 | if (loop.condition != null) visit(loop.condition); 96 | out("; "); 97 | if (loop.update != null) visit(loop.update); 98 | outLn(")"); 99 | if (loop.body is Block) { 100 | visit(loop.body); 101 | } else { 102 | indentLevel++; 103 | visit(loop.body); 104 | indentLevel--; 105 | } 106 | } 107 | 108 | visitForIn(ForIn loop) { 109 | outIndent("for ("); 110 | visit(loop.leftHandSide); 111 | out(" in "); 112 | visit(loop.object); 113 | outLn(")"); 114 | if (loop.body is Block) { 115 | visit(loop.body); 116 | } else { 117 | indentLevel++; 118 | visit(loop.body); 119 | indentLevel--; 120 | } 121 | } 122 | 123 | visitWhile(While loop) { 124 | outIndent("while ("); 125 | visit(loop.condition); 126 | outLn(")"); 127 | if (loop.body is Block) { 128 | visit(loop.body); 129 | } else { 130 | indentLevel++; 131 | visit(loop.body); 132 | indentLevel--; 133 | } 134 | } 135 | 136 | visitDo(Do loop) { 137 | outIndentLn("do"); 138 | if (loop.body is Block) { 139 | visit(loop.body); 140 | } else { 141 | indentLevel++; 142 | visit(loop.body); 143 | indentLevel--; 144 | } 145 | outIndent("while ("); 146 | visit(loop.condition); 147 | outLn(");"); 148 | } 149 | 150 | visitContinue(Continue cont) { 151 | outIndentLn("continue;"); 152 | } 153 | 154 | visitBreak(Break node) { 155 | outIndentLn("break;"); 156 | } 157 | 158 | visitReturn(Return node) { 159 | outIndent("return "); 160 | visit(node.value); 161 | outLn(";"); 162 | } 163 | 164 | visitThrow(Throw node) { 165 | outIndent("throw "); 166 | visit(node.expression); 167 | outLn(";"); 168 | } 169 | 170 | visitTry(Try node) { 171 | outIndentLn("try"); 172 | visit(node.body); 173 | if (node.catchPart != null) { 174 | visit(node.catchPart); 175 | } 176 | if (node.finallyPart != null) { 177 | outIndentLn("finally"); 178 | visit(node.finallyPart); 179 | } 180 | } 181 | 182 | visitCatch(Catch node) { 183 | outIndent("catch ("); 184 | visit(node.declaration); 185 | outLn(")"); 186 | visit(node.body); 187 | } 188 | 189 | visitWith(With node) { 190 | outIndent("with("); 191 | visit(node.object); 192 | outLn(")"); 193 | } 194 | 195 | visitSwitch(Switch node) { 196 | outIndent("switch("); 197 | visit(node.key); 198 | outLn(") {"); 199 | indentLevel++; 200 | visitAll(node.cases); 201 | indentLevel--; 202 | outIndentLn("}"); 203 | } 204 | 205 | visitCase(Case node) { 206 | outIndent("case "); 207 | visit(node.expression); 208 | outLn(":"); 209 | if (!node.body.statements.isEmpty) { 210 | visit(node.body); 211 | } 212 | } 213 | 214 | visitDefault(Default node) { 215 | outIndentLn("default:"); 216 | if (!node.body.statements.isEmpty) { 217 | visit(node.body); 218 | } 219 | } 220 | 221 | visitLabeledStatement(LabeledStatement node) { 222 | outIndentLn("${node.label}:"); 223 | visit(node.body); 224 | } 225 | 226 | visitFunctionDeclaration(FunctionDeclaration declaration) { 227 | // outIndent("function "); 228 | visit(declaration.name); 229 | out("("); 230 | visitInterleaved(declaration.function.params, ", "); 231 | out(") "); 232 | visit(declaration.function.body); 233 | } 234 | 235 | visitVariableDeclarationList(VariableDeclarationList list) { 236 | out("var "); 237 | visitInterleaved(list.declarations, ", "); 238 | } 239 | 240 | visitSequence(Sequence sequence) { 241 | out("("); 242 | visitInterleaved(sequence.expressions, ", "); 243 | out(")"); 244 | } 245 | 246 | visitAssignment(Assignment node) { 247 | // out("("); 248 | visit(node.leftHandSide); 249 | if (node.isCompound) { 250 | out(node.op); 251 | } 252 | out(" = "); 253 | visit(node.value); 254 | // out(")"); 255 | } 256 | 257 | visitVariableInitialization(VariableInitialization init) { 258 | visit(init.declaration); 259 | if (init.value != null) { 260 | out(" = "); 261 | visit(init.value); 262 | } 263 | } 264 | 265 | visitConditional(Conditional cond) { 266 | out("("); 267 | visit(cond.condition); 268 | out(" ? "); 269 | visit(cond.then); 270 | out(" : "); 271 | visit(cond.otherwise); 272 | out(")"); 273 | } 274 | 275 | visitNew(New node) { 276 | out("new "); 277 | visit(node.target); 278 | out("("); 279 | visitInterleaved(node.arguments, ", "); 280 | out(")"); 281 | } 282 | 283 | visitCall(Call call) { 284 | 285 | var target = call.target; 286 | if(target is PropertyAccess) { 287 | var receiver = target.receiver, 288 | selector = target.selector; 289 | 290 | if(receiver is VariableUse && receiver.name == "document" 291 | && selector is LiteralString && selector.value == "getElementById") { 292 | out('querySelector("#'); 293 | if(call.arguments[0] is LiteralString) 294 | out((call.arguments[0].value as String).replaceAll('"', '')); 295 | else { 296 | out(r"$"); 297 | visit(call.arguments[0]); 298 | } 299 | 300 | out('")'); 301 | return; 302 | } 303 | } 304 | 305 | visit(target); 306 | out("("); 307 | visitInterleaved(call.arguments, ", "); 308 | out(")"); 309 | } 310 | 311 | visitBinary(Binary binary) { 312 | out("("); 313 | visit(binary.arguments[0]); 314 | visit(binary.target); 315 | visit(binary.arguments[1]); 316 | out(")"); 317 | } 318 | 319 | visitPrefix(Prefix unary) { 320 | out("("); 321 | visit(unary.target); 322 | visit(unary.arguments[0]); 323 | out(")"); 324 | } 325 | 326 | visitPostfix(Postfix postfix) { 327 | out("("); 328 | visit(postfix.arguments[0]); 329 | visit(postfix.target); 330 | out(")"); 331 | } 332 | 333 | visitVariableUse(VariableUse ref) { 334 | out(ref.name); 335 | } 336 | 337 | visitThis(This node) { 338 | out("this"); 339 | } 340 | 341 | visitVariableDeclaration(VariableDeclaration decl) { 342 | out(decl.name); 343 | } 344 | 345 | visitParameter(Parameter param) { 346 | out(param.name); 347 | } 348 | 349 | visitPropertyAccess(PropertyAccess access) { 350 | visit(access.receiver); 351 | out("."); 352 | visit(access.selector); 353 | } 354 | 355 | visitNamedFunction(NamedFunction namedFunction) { 356 | out("("); 357 | visit(namedFunction.name); 358 | out("("); 359 | visitInterleaved(namedFunction.function.params, ", "); 360 | outLn(")"); 361 | visit(namedFunction.function.body); 362 | out(")"); 363 | } 364 | 365 | visitFun(Fun fun) { 366 | out("(("); 367 | visitInterleaved(fun.params, ", "); 368 | outLn(")"); 369 | visit(fun.body); 370 | outIndent(")"); 371 | } 372 | 373 | visitLiteralBool(LiteralBool node) { 374 | out(node.value ? "true" : "false"); 375 | } 376 | 377 | visitLiteralString(LiteralString node) { 378 | out(node.value); 379 | } 380 | 381 | visitLiteralNumber(LiteralNumber node) { 382 | out(node.value); 383 | } 384 | 385 | visitLiteralNull(LiteralNull node) { 386 | out("null"); 387 | } 388 | 389 | visitLiteralUndefined(LiteralUndefined node) { 390 | out("(void 0)"); 391 | } 392 | 393 | visitArrayInitializer(ArrayInitializer node) { 394 | out("["); 395 | List elements = node.elements; 396 | int elementIndex = 0; 397 | for (int i = 0; i < node.length; i++) { 398 | if (elementIndex < elements.length && 399 | elements[elementIndex].index == i) { 400 | visit(elements[elementIndex].value); 401 | elementIndex++; 402 | if (i != node.length - 1) out(", "); 403 | } else { 404 | out(","); 405 | } 406 | } 407 | out("]"); 408 | } 409 | 410 | visitArrayElement(ArrayElement node) { 411 | throw "Unreachable"; 412 | } 413 | 414 | visitObjectInitializer(ObjectInitializer node) { 415 | out("({"); 416 | visitInterleaved(node.properties, ", "); 417 | out("})"); 418 | } 419 | 420 | visitProperty(Property node) { 421 | visit(node.name); 422 | out(": "); 423 | visit(node.value); 424 | } 425 | 426 | visitRegExpLiteral(RegExpLiteral node) { 427 | out(node.pattern); 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /bin/resolver.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library resolver; 16 | import "nodes.dart"; 17 | import "var.dart"; 18 | import "printer.dart"; 19 | 20 | class ResolverPrinter extends Printer { 21 | final Map resolution; 22 | final Map varIds = new Map(); 23 | int idCounter = 0; 24 | 25 | ResolverPrinter(this.resolution); 26 | 27 | outResolution(VariableReference node) { 28 | Var resolvedVar = resolution[node]; 29 | if (resolvedVar != null) { 30 | int varId = varIds.putIfAbsent(resolvedVar, () => idCounter++); 31 | out("<$varId>"); 32 | } 33 | } 34 | 35 | visitVariableUse(VariableUse node) { 36 | super.visitVariableUse(node); 37 | outResolution(node); 38 | } 39 | 40 | visitParameter(Parameter node) { 41 | super.visitParameter(node); 42 | outResolution(node); 43 | } 44 | 45 | visitVariableDeclaration(VariableDeclaration node) { 46 | super.visitVariableDeclaration(node); 47 | outResolution(node); 48 | } 49 | } 50 | 51 | 52 | Map resolve(Program program) { 53 | Collector collector = new Collector(); 54 | collector.collect(program); 55 | Resolver resolver = 56 | new Resolver(collector.declaredVars, collector.scopesContainingEval); 57 | resolver.visitProgram(program); 58 | return resolver.resolution; 59 | } 60 | 61 | class Collector extends BaseVisitor { 62 | /** Node is either [Program], [NamedFunction], [Fun] or [Try]. */ 63 | final Map> declaredVars; 64 | final Set scopesContainingEval; 65 | 66 | Node currentScope; 67 | Map declaredVarsInCurrentScope; 68 | 69 | bool get isGlobal => currentScope is Program; 70 | 71 | Collector() 72 | : declaredVars = new Map>(), 73 | scopesContainingEval = new Set(); 74 | 75 | void collect(Program program) { 76 | program.accept(this); 77 | } 78 | 79 | visitScope(Node node, void doInsideScope()) { 80 | Node oldScope = currentScope; 81 | Map oldDeclaredVars = declaredVarsInCurrentScope; 82 | currentScope = node; 83 | declaredVarsInCurrentScope = new Map(); 84 | declaredVars[node] = declaredVarsInCurrentScope; 85 | doInsideScope(); 86 | node.visitChildren(this); 87 | declaredVarsInCurrentScope = oldDeclaredVars; 88 | currentScope = oldScope; 89 | } 90 | 91 | void addThis() { 92 | declaredVarsInCurrentScope["this"] = new Var("this", isParam: true); 93 | } 94 | 95 | void addOperators() { 96 | for (Operator op in OPERATORS) { 97 | declaredVarsInCurrentScope[op.id] = op; 98 | } 99 | } 100 | 101 | void addArgumentsObject() { 102 | declaredVarsInCurrentScope["arguments"] = 103 | new Var("arguments", isParam: true); 104 | } 105 | 106 | visitProgram(Program node) { 107 | visitScope(node, () { 108 | addThis(); 109 | addOperators(); 110 | node.visitChildren(this); 111 | }); 112 | } 113 | 114 | visitNamedFunction(NamedFunction node) { 115 | visitScope(node, () { 116 | node.visitChildren(this); 117 | }); 118 | } 119 | 120 | visitFun(Fun node) { 121 | visitScope(node, () { 122 | addThis(); 123 | addArgumentsObject(); 124 | node.visitChildren(this); 125 | }); 126 | } 127 | 128 | visitWith(With node) { 129 | // Add empty map. This will be filled with interceptors in a later 130 | // traversal. 131 | Map withMap = new Map(); 132 | declaredVars[node] = withMap; 133 | node.visitChildren(this); 134 | } 135 | 136 | visitCatch(Catch node) { 137 | // A catch is only a partial scope. It introduces the exception into its 138 | // body, but 'var's that are defined inside the body are defined for the 139 | // whole function. This is even true, if the 'var' uses the same name as 140 | // the one of the exception. 141 | Map catchMap = new Map(); 142 | String exceptionId = node.declaration.name; 143 | catchMap[exceptionId] = new Var(exceptionId, isParam: true); 144 | declaredVars[node] = catchMap; 145 | node.body.accept(this); 146 | } 147 | 148 | visitVariableDeclaration(VariableDeclaration node) { 149 | String id = node.name; 150 | declaredVarsInCurrentScope[id] = new Var(id, isGlobal: isGlobal); 151 | } 152 | 153 | visitParameter(Parameter node) { 154 | String id = node.name; 155 | declaredVarsInCurrentScope[id] = new Var(id, isParam: true); 156 | } 157 | 158 | // This method is not collecting variables, but just marking all scopes that 159 | // contain 'eval' calls. 160 | visitCall(Call node) { 161 | if (node.target is VariableUse) { 162 | VariableUse target = node.target; 163 | if (target.name == "eval") scopesContainingEval.add(currentScope); 164 | } 165 | node.visitChildren(this); 166 | } 167 | } 168 | 169 | class Resolver extends BaseVisitor { 170 | final Map> declaredVars; 171 | final Set scopesContainingEval; 172 | 173 | final Map resolution; 174 | 175 | final List scopes; 176 | final List> scopesVars; 177 | 178 | Resolver(this.declaredVars, this.scopesContainingEval) 179 | : resolution = new Map(), 180 | scopes = [], 181 | scopesVars = >[]; 182 | 183 | visitScope(Node node) { 184 | scopes.add(node); 185 | scopesVars.add(declaredVars[node]); 186 | node.visitChildren(this); 187 | scopesVars.length--; 188 | scopes.length--; 189 | } 190 | 191 | visitProgram(Program node) => visitScope(node); 192 | visitNamedFunction(NamedFunction node) => visitScope(node); 193 | visitFun(Fun node) => visitScope(node); 194 | visitWith(With node) => visitScope(node); 195 | visitCatch(Catch node) => visitScope(node); 196 | 197 | visitVariableReference(VariableReference node) { 198 | Var resolved = resolve(node.name, scopes.length - 1); 199 | resolution[node] = resolved; 200 | } 201 | 202 | Var resolve(String id, int scopeIndex) { 203 | Map vars = scopesVars[scopeIndex]; 204 | Var v = vars[id]; 205 | if (v != null) return v; 206 | Node scope = scopes[scopeIndex]; 207 | 208 | if (scope is Program) { 209 | Var implicit = new Var(id, isGlobal: true, isImplicit: true); 210 | vars[id] = implicit; 211 | return implicit; 212 | } 213 | if (scope is With || scopesContainingEval.contains(scope)) { 214 | Var intercepted = resolve(id, scopeIndex - 1); 215 | Var interceptor = new Interceptor(id, intercepted, scope); 216 | vars[id] = interceptor; 217 | return interceptor; 218 | } 219 | return resolve(id, scopeIndex - 1); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /bin/sample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var DEFAULT_ICON_SIZE = 22; // px 4 | var SUPPORTED_TYPES = ['Link', 'Text', 'Widget']; 5 | 6 | var Annotation = (function AnnotationClosure() { 7 | // // 12.5.5: Algorithm: Appearance streams 8 | // function getTransformMatrix(rect, bbox, matrix) { 9 | // var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); 10 | // var minX = bounds[0]; 11 | // var minY = bounds[1]; 12 | // var maxX = bounds[2]; 13 | // var maxY = bounds[3]; 14 | // 15 | // if (minX === maxX || minY === maxY) { 16 | // // From real-life file, bbox was [0, 0, 0, 0]. In this case, 17 | // // just apply the transform for rect 18 | // return [1, 0, 0, 1, rect[0], rect[1]]; 19 | // } 20 | // 21 | // var xRatio = (rect[2] - rect[0]) / (maxX - minX); 22 | // var yRatio = (rect[3] - rect[1]) / (maxY - minY); 23 | // return [ 24 | // xRatio, 25 | // 0, 26 | // 0, 27 | // yRatio, 28 | // rect[0] - minX * xRatio, 29 | // rect[1] - minY * yRatio 30 | // ]; 31 | // } 32 | // 33 | // function getDefaultAppearance(dict) { 34 | // var appearanceState = dict.get('AP'); 35 | // if (!isDict(appearanceState)) { 36 | // return; 37 | // } 38 | // 39 | // var appearance; 40 | // var appearances = appearanceState.get('N'); 41 | // if (isDict(appearances)) { 42 | // var as_ = dict.get('AS'); 43 | // if (as_ && appearances.has(as_.name)) { 44 | // appearance = appearances.get(as_.name); 45 | // } 46 | // } else { 47 | // appearance = appearances; 48 | // } 49 | // return appearance; 50 | // } 51 | // 52 | // function Annotation(params) { 53 | // var dict = params.dict; 54 | // var data = this.data = {}; 55 | // 56 | // data.subtype = dict.get('Subtype').name; 57 | // var rect = dict.get('Rect') || [0, 0, 0, 0]; 58 | // data.rect = Util.normalizeRect(rect); 59 | // data.annotationFlags = dict.get('F'); 60 | // 61 | // var color = dict.get('C'); 62 | // if (!color) { 63 | // // The PDF spec does not mention how a missing color array is interpreted. 64 | // // Adobe Reader seems to default to black in this case. 65 | // data.color = [0, 0, 0]; 66 | // } else if (isArray(color)) { 67 | // switch (color.length) { 68 | // case 0: 69 | // // Empty array denotes transparent border. 70 | // data.color = null; 71 | // break; 72 | // case 1: 73 | // // TODO: implement DeviceGray 74 | // break; 75 | // case 3: 76 | // data.color = color; 77 | // break; 78 | // case 4: 79 | // // TODO: implement DeviceCMYK 80 | // break; 81 | // } 82 | // } 83 | // 84 | // this.borderStyle = data.borderStyle = new AnnotationBorderStyle(); 85 | // this.setBorderStyle(dict); 86 | // 87 | // this.appearance = getDefaultAppearance(dict); 88 | // data.hasAppearance = !!this.appearance; 89 | // data.id = params.ref.num; 90 | // } 91 | // 92 | // Annotation.prototype = { 93 | // setBorderStyle: function Annotation_setBorderStyle(borderStyle) { 94 | // if (!isDict(borderStyle)) { 95 | // return; 96 | // } 97 | // if (borderStyle.has('BS')) { 98 | // var dict = borderStyle.get('BS'); 99 | // var dictType; 100 | // 101 | // if (!dict.has('Type') || (isName(dictType = dict.get('Type')) && 102 | // dictType.name === 'Border')) { 103 | // this.borderStyle.setWidth(dict.get('W')); 104 | // this.borderStyle.setStyle(dict.get('S')); 105 | // this.borderStyle.setDashArray(dict.get('D')); 106 | // } 107 | // } else if (borderStyle.has('Border')) { 108 | // var array = borderStyle.get('Border'); 109 | // if (isArray(array) && array.length >= 3) { 110 | // this.borderStyle.setHorizontalCornerRadius(array[0]); 111 | // this.borderStyle.setVerticalCornerRadius(array[1]); 112 | // this.borderStyle.setWidth(array[2]); 113 | // this.borderStyle.setStyle('S'); 114 | // 115 | // if (array.length === 4) { // Dash array available 116 | // this.borderStyle.setDashArray(array[3]); 117 | // } 118 | // } 119 | // } else { 120 | // // There are no border entries in the dictionary. According to the 121 | // // specification, we should draw a solid border of width 1 in that 122 | // // case, but Adobe Reader did not implement that part of the 123 | // // specification and instead draws no border at all, so we do the same. 124 | // // See also https://github.com/mozilla/pdf.js/issues/6179. 125 | // this.borderStyle.setWidth(0); 126 | // } 127 | // }, 128 | // 129 | // getData: function Annotation_getData() { 130 | // return this.data; 131 | // }, 132 | // 133 | // isInvisible: function Annotation_isInvisible() { 134 | // var data = this.data; 135 | // if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) { 136 | // return false; 137 | // } else { 138 | // return !!(data && 139 | // data.annotationFlags && // Default: not invisible 140 | // data.annotationFlags & 0x1); // Invisible 141 | // } 142 | // }, 143 | // 144 | // isViewable: function Annotation_isViewable() { 145 | // var data = this.data; 146 | // return !!(!this.isInvisible() && 147 | // data && 148 | // (!data.annotationFlags || 149 | // !(data.annotationFlags & 0x22)) && // Hidden or NoView 150 | // data.rect); // rectangle is necessary 151 | // }, 152 | // 153 | // isPrintable: function Annotation_isPrintable() { 154 | // var data = this.data; 155 | // return !!(!this.isInvisible() && 156 | // data && 157 | // data.annotationFlags && // Default: not printable 158 | // data.annotationFlags & 0x4 && // Print 159 | // !(data.annotationFlags & 0x2) && // Hidden 160 | // data.rect); // rectangle is necessary 161 | // }, 162 | // 163 | // loadResources: function Annotation_loadResources(keys) { 164 | // return new Promise(function (resolve, reject) { 165 | // this.appearance.dict.getAsync('Resources').then(function (resources) { 166 | // if (!resources) { 167 | // resolve(); 168 | // return; 169 | // } 170 | // var objectLoader = new ObjectLoader(resources.map, 171 | // keys, 172 | // resources.xref); 173 | // objectLoader.load().then(function() { 174 | // resolve(resources); 175 | // }, reject); 176 | // }, reject); 177 | // }.bind(this)); 178 | // }, 179 | // 180 | // getOperatorList: function Annotation_getOperatorList(evaluator) { 181 | // 182 | // if (!this.appearance) { 183 | // return Promise.resolve(new OperatorList()); 184 | // } 185 | // 186 | // var data = this.data; 187 | // 188 | // var appearanceDict = this.appearance.dict; 189 | // var resourcesPromise = this.loadResources([ 190 | // 'ExtGState', 191 | // 'ColorSpace', 192 | // 'Pattern', 193 | // 'Shading', 194 | // 'XObject', 195 | // 'Font' 196 | // // ProcSet 197 | // // Properties 198 | // ]); 199 | // var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1]; 200 | // var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; 201 | // var transform = getTransformMatrix(data.rect, bbox, matrix); 202 | // var self = this; 203 | // 204 | // return resourcesPromise.then(function(resources) { 205 | // var opList = new OperatorList(); 206 | // opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); 207 | // return evaluator.getOperatorList(self.appearance, resources, opList). 208 | // then(function () { 209 | // opList.addOp(OPS.endAnnotation, []); 210 | // self.appearance.reset(); 211 | // return opList; 212 | // }); 213 | // }); 214 | // } 215 | // }; 216 | // 217 | // Annotation.getConstructor = function Annotation_getConstructor(subtype, fieldType) { 218 | // 219 | // if (!subtype) { 220 | // return; 221 | // } 222 | // 223 | // // TODO(mack): Implement FreeText annotations 224 | // if (subtype === 'Link') { 225 | // return LinkAnnotation; 226 | // } else if (subtype === 'Text') { 227 | // return TextAnnotation; 228 | // } else if (subtype === 'Widget') { 229 | // if (!fieldType) { 230 | // return; 231 | // } 232 | // 233 | // if (fieldType === 'Tx') { 234 | // return TextWidgetAnnotation; 235 | // } else { 236 | // return WidgetAnnotation; 237 | // } 238 | // } else { 239 | // return Annotation; 240 | // } 241 | // }; 242 | // 243 | // Annotation.fromRef = function Annotation_fromRef(xref, ref) { 244 | // 245 | // var dict = xref.fetchIfRef(ref); 246 | // if (!isDict(dict)) { 247 | // return; 248 | // } 249 | // 250 | // var subtype = dict.get('Subtype'); 251 | // subtype = isName(subtype) ? subtype.name : ''; 252 | // if (!subtype) { 253 | // return; 254 | // } 255 | // 256 | // var fieldType = Util.getInheritableProperty(dict, 'FT'); 257 | // fieldType = isName(fieldType) ? fieldType.name : ''; 258 | // 259 | // var Constructor = Annotation.getConstructor(subtype, fieldType); 260 | // if (!Constructor) { 261 | // return; 262 | // } 263 | // 264 | // var params = { 265 | // dict: dict, 266 | // ref: ref 267 | // }; 268 | // 269 | // var annotation = new Constructor(params); 270 | // 271 | // if (annotation.isViewable() || annotation.isPrintable()) { 272 | // return annotation; 273 | // } else { 274 | // if (SUPPORTED_TYPES.indexOf(subtype) === -1) { 275 | // warn('unimplemented annotation type: ' + subtype); 276 | // } 277 | // } 278 | // }; 279 | // 280 | // Annotation.appendToOperatorList = function Annotation_appendToOperatorList( 281 | // annotations, opList, pdfManager, partialEvaluator, intent) { 282 | // 283 | // function reject(e) { 284 | // annotationsReadyCapability.reject(e); 285 | // } 286 | // 287 | // var annotationsReadyCapability = createPromiseCapability(); 288 | // 289 | // var annotationPromises = []; 290 | // for (var i = 0, n = annotations.length; i < n; ++i) { 291 | // if (intent === 'display' && annotations[i].isViewable() || 292 | // intent === 'print' && annotations[i].isPrintable()) { 293 | // annotationPromises.push( 294 | // annotations[i].getOperatorList(partialEvaluator)); 295 | // } 296 | // } 297 | // Promise.all(annotationPromises).then(function(datas) { 298 | // opList.addOp(OPS.beginAnnotations, []); 299 | // for (var i = 0, n = datas.length; i < n; ++i) { 300 | // var annotOpList = datas[i]; 301 | // opList.addOpList(annotOpList); 302 | // } 303 | // opList.addOp(OPS.endAnnotations, []); 304 | // annotationsReadyCapability.resolve(); 305 | // }, reject); 306 | // 307 | // return annotationsReadyCapability.promise; 308 | // }; 309 | // 310 | // return Annotation; 311 | //})(); 312 | // 313 | //var AnnotationBorderStyle = (function AnnotationBorderStyleClosure() { 314 | // 315 | // function AnnotationBorderStyle() { 316 | // this.width = 1; 317 | // this.style = AnnotationBorderStyleType.SOLID; 318 | // this.dashArray = [3]; 319 | // this.horizontalCornerRadius = 0; 320 | // this.verticalCornerRadius = 0; 321 | // } 322 | // 323 | // AnnotationBorderStyle.prototype = { 324 | // 325 | // setWidth: function AnnotationBorderStyle_setWidth(width) { 326 | // if (width === (width | 0)) { 327 | // this.width = width; 328 | // } 329 | // }, 330 | // 331 | // setStyle: function AnnotationBorderStyle_setStyle(style) { 332 | // if (!style) { 333 | // return; 334 | // } 335 | // switch (style.name) { 336 | // case 'S': 337 | // this.style = AnnotationBorderStyleType.SOLID; 338 | // break; 339 | // 340 | // case 'D': 341 | // this.style = AnnotationBorderStyleType.DASHED; 342 | // break; 343 | // 344 | // case 'B': 345 | // this.style = AnnotationBorderStyleType.BEVELED; 346 | // break; 347 | // 348 | // case 'I': 349 | // this.style = AnnotationBorderStyleType.INSET; 350 | // break; 351 | // 352 | // case 'U': 353 | // this.style = AnnotationBorderStyleType.UNDERLINE; 354 | // break; 355 | // 356 | // default: 357 | // break; 358 | // } 359 | // }, 360 | // 361 | // /** 362 | // * Set the dash array. 363 | // * 364 | // * @public 365 | // * @memberof AnnotationBorderStyle 366 | // * @param {Array} dashArray - The dash array with at least one element 367 | // */ 368 | // setDashArray: function AnnotationBorderStyle_setDashArray(dashArray) { 369 | // // We validate the dash array, but we do not use it because CSS does not 370 | // // allow us to change spacing of dashes. For more information, visit 371 | // // http://www.w3.org/TR/css3-background/#the-border-style. 372 | // if (isArray(dashArray) && dashArray.length > 0) { 373 | // // According to the PDF specification: the elements in a dashArray 374 | // // shall be numbers that are nonnegative and not all equal to zero. 375 | // var isValid = true; 376 | // var allZeros = true; 377 | // for (var i = 0, len = dashArray.length; i < len; i++) { 378 | // var element = dashArray[i]; 379 | // var validNumber = (+element >= 0); 380 | // if (!validNumber) { 381 | // isValid = false; 382 | // break; 383 | // } else if (element > 0) { 384 | // allZeros = false; 385 | // } 386 | // } 387 | // if (isValid && !allZeros) { 388 | // this.dashArray = dashArray; 389 | // } else { 390 | // this.width = 0; // Adobe behavior when the array is invalid. 391 | // } 392 | // } else if (dashArray) { 393 | // this.width = 0; // Adobe behavior when the array is invalid. 394 | // } 395 | // }, 396 | // 397 | // /** 398 | // * Set the horizontal corner radius (from a Border dictionary). 399 | // * 400 | // * @public 401 | // * @memberof AnnotationBorderStyle 402 | // * @param {integer} radius - The horizontal corner radius 403 | // */ 404 | // setHorizontalCornerRadius: 405 | // function AnnotationBorderStyle_setHorizontalCornerRadius(radius) { 406 | // if (radius === (radius | 0)) { 407 | // this.horizontalCornerRadius = radius; 408 | // } 409 | // }, 410 | // 411 | // /** 412 | // * Set the vertical corner radius (from a Border dictionary). 413 | // * 414 | // * @public 415 | // * @memberof AnnotationBorderStyle 416 | // * @param {integer} radius - The vertical corner radius 417 | // */ 418 | // setVerticalCornerRadius: 419 | // function AnnotationBorderStyle_setVerticalCornerRadius(radius) { 420 | // if (radius === (radius | 0)) { 421 | // this.verticalCornerRadius = radius; 422 | // } 423 | // } 424 | // }; 425 | // 426 | // return AnnotationBorderStyle; 427 | //})(); 428 | // 429 | //var WidgetAnnotation = (function WidgetAnnotationClosure() { 430 | // 431 | // function WidgetAnnotation(params) { 432 | // Annotation.call(this, params); 433 | // 434 | // var dict = params.dict; 435 | // var data = this.data; 436 | // 437 | // data.fieldValue = stringToPDFString( 438 | // Util.getInheritableProperty(dict, 'V') || ''); 439 | // data.alternativeText = stringToPDFString(dict.get('TU') || ''); 440 | // data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || ''; 441 | // var fieldType = Util.getInheritableProperty(dict, 'FT'); 442 | // data.fieldType = isName(fieldType) ? fieldType.name : ''; 443 | // data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; 444 | // this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; 445 | // 446 | // // Building the full field name by collecting the field and 447 | // // its ancestors 'T' data and joining them using '.'. 448 | // var fieldName = []; 449 | // var namedItem = dict; 450 | // var ref = params.ref; 451 | // while (namedItem) { 452 | // var parent = namedItem.get('Parent'); 453 | // var parentRef = namedItem.getRaw('Parent'); 454 | // var name = namedItem.get('T'); 455 | // if (name) { 456 | // fieldName.unshift(stringToPDFString(name)); 457 | // } else if (parent && ref) { 458 | // // The field name is absent, that means more than one field 459 | // // with the same name may exist. Replacing the empty name 460 | // // with the '`' plus index in the parent's 'Kids' array. 461 | // // This is not in the PDF spec but necessary to id the 462 | // // the input controls. 463 | // var kids = parent.get('Kids'); 464 | // var j, jj; 465 | // for (j = 0, jj = kids.length; j < jj; j++) { 466 | // var kidRef = kids[j]; 467 | // if (kidRef.num === ref.num && kidRef.gen === ref.gen) { 468 | // break; 469 | // } 470 | // } 471 | // fieldName.unshift('`' + j); 472 | // } 473 | // namedItem = parent; 474 | // ref = parentRef; 475 | // } 476 | // data.fullName = fieldName.join('.'); 477 | // } 478 | // 479 | // var parent = Annotation.prototype; 480 | // Util.inherit(WidgetAnnotation, Annotation, { 481 | // isViewable: function WidgetAnnotation_isViewable() { 482 | // if (this.data.fieldType === 'Sig') { 483 | // warn('unimplemented annotation type: Widget signature'); 484 | // return false; 485 | // } 486 | // 487 | // return parent.isViewable.call(this); 488 | // } 489 | // }); 490 | // 491 | // return WidgetAnnotation; 492 | //})(); 493 | // 494 | //var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { 495 | // function TextWidgetAnnotation(params) { 496 | // WidgetAnnotation.call(this, params); 497 | // 498 | // this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q'); 499 | // this.data.annotationType = AnnotationType.WIDGET; 500 | // this.data.hasHtml = !this.data.hasAppearance && !!this.data.fieldValue; 501 | // } 502 | // 503 | // Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { 504 | // getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { 505 | // if (this.appearance) { 506 | // return Annotation.prototype.getOperatorList.call(this, evaluator); 507 | // } 508 | // 509 | // var opList = new OperatorList(); 510 | // var data = this.data; 511 | // 512 | // // Even if there is an appearance stream, ignore it. This is the 513 | // // behaviour used by Adobe Reader. 514 | // if (!data.defaultAppearance) { 515 | // return Promise.resolve(opList); 516 | // } 517 | // 518 | // var stream = new Stream(stringToBytes(data.defaultAppearance)); 519 | // return evaluator.getOperatorList(stream, this.fieldResources, opList). 520 | // then(function () { 521 | // return opList; 522 | // }); 523 | // } 524 | // }); 525 | // 526 | // return TextWidgetAnnotation; 527 | //})(); 528 | // 529 | //var TextAnnotation = (function TextAnnotationClosure() { 530 | // function TextAnnotation(params) { 531 | // Annotation.call(this, params); 532 | // 533 | // var dict = params.dict; 534 | // var data = this.data; 535 | // 536 | // var content = dict.get('Contents'); 537 | // var title = dict.get('T'); 538 | // data.annotationType = AnnotationType.TEXT; 539 | // data.content = stringToPDFString(content || ''); 540 | // data.title = stringToPDFString(title || ''); 541 | // data.hasHtml = true; 542 | // 543 | // if (data.hasAppearance) { 544 | // data.name = 'NoIcon'; 545 | // } else { 546 | // data.rect[1] = data.rect[3] - DEFAULT_ICON_SIZE; 547 | // data.rect[2] = data.rect[0] + DEFAULT_ICON_SIZE; 548 | // data.name = dict.has('Name') ? dict.get('Name').name : 'Note'; 549 | // } 550 | // 551 | // if (dict.has('C')) { 552 | // data.hasBgColor = true; 553 | // } 554 | // } 555 | // 556 | // Util.inherit(TextAnnotation, Annotation, { }); 557 | // 558 | // return TextAnnotation; 559 | //})(); 560 | // 561 | //var LinkAnnotation = (function LinkAnnotationClosure() { 562 | // function LinkAnnotation(params) { 563 | // Annotation.call(this, params); 564 | // 565 | // var dict = params.dict; 566 | // var data = this.data; 567 | // data.annotationType = AnnotationType.LINK; 568 | // data.hasHtml = true; 569 | // 570 | // var action = dict.get('A'); 571 | // if (action && isDict(action)) { 572 | // var linkType = action.get('S').name; 573 | // if (linkType === 'URI') { 574 | // var url = action.get('URI'); 575 | // if (isName(url)) { 576 | // // Some bad PDFs do not put parentheses around relative URLs. 577 | // url = '/' + url.name; 578 | // } else if (url) { 579 | // url = addDefaultProtocolToUrl(url); 580 | // } 581 | // // TODO: pdf spec mentions urls can be relative to a Base 582 | // // entry in the dictionary. 583 | // if (!isValidUrl(url, false)) { 584 | // url = ''; 585 | // } 586 | // // According to ISO 32000-1:2008, section 12.6.4.7, 587 | // // URI should to be encoded in 7-bit ASCII. 588 | // // Some bad PDFs may have URIs in UTF-8 encoding, see Bugzilla 1122280. 589 | // try { 590 | // data.url = stringToUTF8String(url); 591 | // } catch (e) { 592 | // // Fall back to a simple copy. 593 | // data.url = url; 594 | // } 595 | // } else if (linkType === 'GoTo') { 596 | // data.dest = action.get('D'); 597 | // } else if (linkType === 'GoToR') { 598 | // var urlDict = action.get('F'); 599 | // if (isDict(urlDict)) { 600 | // // We assume that the 'url' is a Filspec dictionary 601 | // // and fetch the url without checking any further 602 | // url = urlDict.get('F') || ''; 603 | // } 604 | // 605 | // // TODO: pdf reference says that GoToR 606 | // // can also have 'NewWindow' attribute 607 | // if (!isValidUrl(url, false)) { 608 | // url = ''; 609 | // } 610 | // data.url = url; 611 | // data.dest = action.get('D'); 612 | // } else if (linkType === 'Named') { 613 | // data.action = action.get('N').name; 614 | // } else { 615 | // warn('unrecognized link type: ' + linkType); 616 | // } 617 | // } else if (dict.has('Dest')) { 618 | // // simple destination link 619 | // var dest = dict.get('Dest'); 620 | // data.dest = isName(dest) ? dest.name : dest; 621 | // } 622 | // } 623 | // 624 | // // Lets URLs beginning with 'www.' default to using the 'http://' protocol. 625 | // function addDefaultProtocolToUrl(url) { 626 | // if (url && url.indexOf('www.') === 0) { 627 | // return ('http://' + url); 628 | // } 629 | // return url; 630 | // } 631 | // 632 | // Util.inherit(LinkAnnotation, Annotation, { }); 633 | // 634 | // return LinkAnnotation; 635 | })(); 636 | -------------------------------------------------------------------------------- /bin/sample2.js: -------------------------------------------------------------------------------- 1 | function hello() /*this is hello*/ 2 | { 3 | 4 | } -------------------------------------------------------------------------------- /bin/var.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | library variable; 16 | 17 | class Var { 18 | final String id; 19 | final bool isGlobal; 20 | final bool isImplicit; 21 | final bool isParam; 22 | 23 | const Var(this.id, {isGlobal: false, 24 | isImplicit: false, 25 | isParam: false}) 26 | : this.isGlobal = isGlobal, 27 | this.isImplicit = isImplicit, 28 | this.isParam = isParam; 29 | 30 | bool get isThis => id == "this"; 31 | bool get isOperator => false; 32 | } 33 | 34 | const PREFIX_PLUS = const Operator("prefix+"); 35 | const PREFIX_MINUS = const Operator("prefix-"); 36 | const PREFIX_PLUS_PLUS = const Operator("prefix++"); 37 | const PREFIX_MINUS_MINUS = const Operator("prefix--"); 38 | 39 | const OPERATORS = const [ 40 | PREFIX_PLUS, 41 | PREFIX_MINUS, 42 | PREFIX_PLUS_PLUS, 43 | PREFIX_MINUS_MINUS, 44 | const Operator("delete"), 45 | const Operator("void"), 46 | const Operator("typeof"), 47 | const Operator("||"), 48 | const Operator("&&"), 49 | const Operator("|"), 50 | const Operator("^"), 51 | const Operator("&"), 52 | const Operator("=="), 53 | const Operator("!="), 54 | const Operator("==="), 55 | const Operator("!=="), 56 | const Operator("<"), 57 | const Operator(">"), 58 | const Operator("<="), 59 | const Operator(">="), 60 | const Operator("instanceof"), 61 | const Operator("in"), 62 | const Operator("<<"), 63 | const Operator(">>"), 64 | const Operator(">>>"), 65 | const Operator("+"), 66 | const Operator("-"), 67 | const Operator("*"), 68 | const Operator("/"), 69 | const Operator("%"), 70 | ]; 71 | 72 | class Operator extends Var { 73 | const Operator(String id) : super(id, isGlobal: true); 74 | 75 | bool get isOperator => true; 76 | } 77 | 78 | // Note that Interceptors may live in the top-level. 79 | class Interceptor extends Var { 80 | Var intercepted; 81 | dynamic reason; 82 | Interceptor(String id, this.intercepted, this.reason) : super(id); 83 | } 84 | -------------------------------------------------------------------------------- /lib/javascript_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:peg/abstract_parser.dart'; 2 | 3 | class JavascriptParser extends AbstractParser { 4 | JavascriptParser(String text) : super(text); 5 | } -------------------------------------------------------------------------------- /lib/javascript_parser.peg: -------------------------------------------------------------------------------- 1 | ## 2 | ## JavaScript Grammar 3 | ## ================== 4 | ## 5 | ## Based on grammar from ECMA-262, 5.1 Edition [1]. Generated parser builds a 6 | ## syntax tree compatible with Mozilla SpiderMonkey Parser API [2]. Properties 7 | ## and node types reflecting features not present in ECMA-262 are not included. 8 | ## 9 | ## Limitations: 10 | ## 11 | ## # Non-BMP characters are completely ignored to avoid surrogate pair 12 | ## handling. 13 | ## 14 | ## # One can create identifiers containing illegal characters using Unicode 15 | ## escape sequences. For example, "abcd\u0020efgh" is not a valid 16 | ## identifier, but it is accepted by the parser. 17 | ## 18 | ## # Strict mode is not recognized. This means that within strict mode code, 19 | ## "implements", "interface", "let", "package", "private", "protected", 20 | ## "public", "static" and "yield" can be used as names. Many other 21 | ## restrictions and exceptions from Annex C are also not applied. 22 | ## 23 | ## All the limitations could be resolved, but the costs would likely outweigh 24 | ## the benefits. 25 | ## 26 | ## Many thanks to inimino [3] for his grammar [4] which helped me to solve some 27 | ## problems (such as automatic semicolon insertion) and also served to double 28 | ## check that I converted the original grammar correctly. 29 | ## 30 | ## [1] http:#www.ecma-international.org/publications/standards/Ecma-262.htm 31 | ## [2] https:#developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API 32 | ## [3] http:#inimino.org/~inimino/blog/ 33 | ## [4] http:#boshi.inimino.org/3box/asof/1270029991384/PEG/ECMAScript_unified.peg 34 | ## 35 | 36 | %{ 37 | 38 | }% 39 | 40 | Start 41 | <- __ Program __ 42 | 43 | # ----- A.1 Lexical Grammar ----- 44 | 45 | SourceCharacter 46 | <- . 47 | 48 | WhiteSpace 49 | <- "\t" 50 | #/ "\v" 51 | #/ "\f" 52 | / " " 53 | / "\u00A0" 54 | / "\uFEFF" 55 | / Zs 56 | 57 | LineTerminator 58 | <- [\n\r\u2028\u2029] 59 | 60 | LineTerminatorSequence 61 | <- "\n" 62 | / "\r\n" 63 | / "\r" 64 | / "\u2028" 65 | / "\u2029" 66 | 67 | Comment 68 | <- MultiLineComment 69 | / SingleLineComment 70 | 71 | MultiLineComment 72 | <- "/*" (!"*/" SourceCharacter)* "*/" 73 | 74 | MultiLineCommentNoLineTerminator 75 | <- "/*" (!("*/" / LineTerminator) SourceCharacter)* "*/" 76 | 77 | SingleLineComment 78 | <- "//" (!LineTerminator SourceCharacter)* 79 | 80 | Identifier 81 | <- !ReservedWord IdentifierName 82 | 83 | IdentifierName 84 | <- IdentifierStart IdentifierPart* 85 | 86 | IdentifierStart 87 | <- UnicodeLetter 88 | / "$" 89 | / "_" 90 | / "\\" UnicodeEscapeSequence 91 | 92 | IdentifierPart 93 | <- IdentifierStart 94 | / UnicodeCombiningMark 95 | / UnicodeDigit 96 | / UnicodeConnectorPunctuation 97 | / "\u200C" 98 | / "\u200D" 99 | 100 | UnicodeLetter 101 | <- Lu 102 | / Ll 103 | / Lt 104 | / Lm 105 | / Lo 106 | / Nl 107 | 108 | UnicodeCombiningMark 109 | <- Mn 110 | / Mc 111 | 112 | UnicodeDigit 113 | <- Nd 114 | 115 | UnicodeConnectorPunctuation 116 | <- Pc 117 | 118 | ReservedWord 119 | <- Keyword 120 | / FutureReservedWord 121 | / NullLiteral 122 | / BooleanLiteral 123 | 124 | Keyword 125 | <- BreakToken 126 | / CaseToken 127 | / CatchToken 128 | / ContinueToken 129 | / DebuggerToken 130 | / DefaultToken 131 | / DeleteToken 132 | / DoToken 133 | / ElseToken 134 | / FinallyToken 135 | / ForToken 136 | / FunctionToken 137 | / IfToken 138 | / InstanceofToken 139 | / InToken 140 | / NewToken 141 | / ReturnToken 142 | / SwitchToken 143 | / ThisToken 144 | / ThrowToken 145 | / TryToken 146 | / TypeofToken 147 | / VarToken 148 | / VoidToken 149 | / WhileToken 150 | / WithToken 151 | 152 | FutureReservedWord 153 | <- ClassToken 154 | / ConstToken 155 | / EnumToken 156 | / ExportToken 157 | / ExtendsToken 158 | / ImportToken 159 | / SuperToken 160 | 161 | Literal 162 | <- NullLiteral 163 | / BooleanLiteral 164 | / NumericLiteral 165 | / StringLiteral 166 | / RegularExpressionLiteral 167 | 168 | NullLiteral 169 | <- NullToken 170 | 171 | BooleanLiteral 172 | <- TrueToken 173 | / FalseToken 174 | 175 | # The "!(IdentifierStart / DecimalDigit)" predicate is not part of the official 176 | # grammar, it comes from text in section 7.8.3. 177 | 178 | NumericLiteral 179 | <- HexIntegerLiteral !(IdentifierStart / DecimalDigit) 180 | / DecimalLiteral !(IdentifierStart / DecimalDigit) 181 | 182 | DecimalLiteral 183 | <- DecimalIntegerLiteral "." DecimalDigit* ExponentPart? 184 | / "." DecimalDigit+ ExponentPart? 185 | / DecimalIntegerLiteral ExponentPart? 186 | 187 | DecimalIntegerLiteral 188 | <- "0" 189 | / NonZeroDigit DecimalDigit* 190 | 191 | DecimalDigit 192 | <- [0-9] 193 | 194 | NonZeroDigit 195 | <- [1-9] 196 | 197 | ExponentPart 198 | <- ExponentIndicator SignedInteger 199 | 200 | ExponentIndicator 201 | <- "e" / "E" 202 | 203 | SignedInteger 204 | <- ('+' / '-')? DecimalDigit+ 205 | 206 | HexIntegerLiteral 207 | <- ("0x" / "0X") HexDigit+ 208 | 209 | HexDigit 210 | <- [0-9a-fA-F] 211 | 212 | StringLiteral 213 | <- '"' DoubleStringCharacter* '"' 214 | / "'" SingleStringCharacter* "'" 215 | 216 | DoubleStringCharacter 217 | <- !('"' / "\\" / LineTerminator) SourceCharacter 218 | / "\\" EscapeSequence 219 | / LineContinuation 220 | 221 | SingleStringCharacter 222 | <- !("'" / "\\" / LineTerminator) SourceCharacter 223 | / "\\" EscapeSequence 224 | / LineContinuation 225 | 226 | LineContinuation 227 | <- "\\" LineTerminatorSequence 228 | 229 | EscapeSequence 230 | <- CharacterEscapeSequence 231 | / "0" !DecimalDigit 232 | / HexEscapeSequence 233 | / UnicodeEscapeSequence 234 | 235 | CharacterEscapeSequence 236 | <- SingleEscapeCharacter 237 | / NonEscapeCharacter 238 | 239 | SingleEscapeCharacter 240 | <- "'" 241 | / '"' 242 | / "\\" 243 | / "b" 244 | / "f" 245 | / "n" 246 | / "r" 247 | / "t" 248 | / "v" # IE does not recognize "\v". 249 | 250 | NonEscapeCharacter 251 | <- !(EscapeCharacter / LineTerminator) SourceCharacter 252 | 253 | EscapeCharacter 254 | <- SingleEscapeCharacter 255 | / DecimalDigit 256 | / "x" 257 | / "u" 258 | 259 | HexEscapeSequence 260 | <- "x" HexDigit HexDigit 261 | 262 | UnicodeEscapeSequence 263 | <- "u" (HexDigit HexDigit HexDigit HexDigit) 264 | 265 | RegularExpressionLiteral 266 | <- "/" RegularExpressionBody "/" RegularExpressionFlags 267 | 268 | RegularExpressionBody 269 | <- RegularExpressionFirstChar RegularExpressionChar* 270 | 271 | RegularExpressionFirstChar 272 | <- ![*\\/[] RegularExpressionNonTerminator 273 | / RegularExpressionBackslashSequence 274 | / RegularExpressionClass 275 | 276 | RegularExpressionChar 277 | <- ![\\/[] RegularExpressionNonTerminator 278 | / RegularExpressionBackslashSequence 279 | / RegularExpressionClass 280 | 281 | RegularExpressionBackslashSequence 282 | <- "\\" RegularExpressionNonTerminator 283 | 284 | RegularExpressionNonTerminator 285 | <- !LineTerminator SourceCharacter 286 | 287 | RegularExpressionClass 288 | <- "[" RegularExpressionClassChar* "]" 289 | 290 | RegularExpressionClassChar 291 | <- ![\]\\] RegularExpressionNonTerminator 292 | / RegularExpressionBackslashSequence 293 | 294 | RegularExpressionFlags 295 | <- IdentifierPart* 296 | 297 | # Unicode Character Categories 298 | # 299 | # Extracted from the following Unicode Character Database 300 | # 301 | # #www.unicode.org/Public/6.3.0/ucd/extracted/DerivedGeneralCategory.txt 302 | # 303 | # Unix magic 304 | # 305 | # grep "; $CATEGORY" DerivedGeneralCategory.txt | # Filter characters 306 | # cut -f1 -d " " | # Extract code points 307 | # grep -v '[0-9a-fA-F]\{5\}' | # Exclude non-BMP characters 308 | # sed -e 's/\.\./-/' | # Adjust formatting 309 | # sed -e 's/\([0-9a-fA-F]\{4\}\)/\\u\1/g' | # Adjust formatting 310 | # tr -d '\n' # Join lines 311 | # 312 | # ECMA-262 allows using Unicode 3.0 or later, version 6.3.0 was the latest one 313 | # at the time of writing. 314 | # 315 | # Non-BMP characters are completely ignored to avoid surrogate pair handling 316 | # (detecting surrogate pairs isn't possible with a simple character class and 317 | # other methods would degrade performance). I don't consider it a big deal as 318 | # even parsers in JavaScript engines of common browsers seem to ignore them. 319 | 320 | 321 | # Letter, Lowercase 322 | Ll <- [\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0561-\u0587\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7FA\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A] 323 | 324 | # Letter, Modifier 325 | Lm <- [\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0374\u037A\u0559\u0640\u06E5-\u06E6\u07F4-\u07F5\u07FA\u081A\u0824\u0828\u0971\u0E46\u0EC6\u10FC\u17D7\u1843\u1AA7\u1C78-\u1C7D\u1D2C-\u1D6A\u1D78\u1D9B-\u1DBF\u2071\u207F\u2090-\u209C\u2C7C-\u2C7D\u2D6F\u2E2F\u3005\u3031-\u3035\u303B\u309D-\u309E\u30FC-\u30FE\uA015\uA4F8-\uA4FD\uA60C\uA67F\uA717-\uA71F\uA770\uA788\uA7F8-\uA7F9\uA9CF\uAA70\uAADD\uAAF3-\uAAF4\uFF70\uFF9E-\uFF9F] 326 | 327 | # Letter, Other 328 | Lo <- [\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05F0-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0977\u0979-\u097F\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58-\u0C59\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u10D0-\u10FA\u10FD-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] 329 | 330 | # Letter, Titlecase 331 | Lt <- [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FCC\u1FFC] 332 | 333 | # Letter, Uppercase 334 | Lu <- [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA\uFF21-\uFF3A] 335 | 336 | # Mark, Spacing Combining 337 | Mc <- [\u0903\u093B\u093E-\u0940\u0949-\u094C\u094E-\u094F\u0982-\u0983\u09BE-\u09C0\u09C7-\u09C8\u09CB-\u09CC\u09D7\u0A03\u0A3E-\u0A40\u0A83\u0ABE-\u0AC0\u0AC9\u0ACB-\u0ACC\u0B02-\u0B03\u0B3E\u0B40\u0B47-\u0B48\u0B4B-\u0B4C\u0B57\u0BBE-\u0BBF\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD7\u0C01-\u0C03\u0C41-\u0C44\u0C82-\u0C83\u0CBE\u0CC0-\u0CC4\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D57\u0D82-\u0D83\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DF2-\u0DF3\u0F3E-\u0F3F\u0F7F\u102B-\u102C\u1031\u1038\u103B-\u103C\u1056-\u1057\u1062-\u1064\u1067-\u106D\u1083-\u1084\u1087-\u108C\u108F\u109A-\u109C\u17B6\u17BE-\u17C5\u17C7-\u17C8\u1923-\u1926\u1929-\u192B\u1930-\u1931\u1933-\u1938\u19B0-\u19C0\u19C8-\u19C9\u1A19-\u1A1A\u1A55\u1A57\u1A61\u1A63-\u1A64\u1A6D-\u1A72\u1B04\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B44\u1B82\u1BA1\u1BA6-\u1BA7\u1BAA\u1BAC-\u1BAD\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2-\u1BF3\u1C24-\u1C2B\u1C34-\u1C35\u1CE1\u1CF2-\u1CF3\u302E-\u302F\uA823-\uA824\uA827\uA880-\uA881\uA8B4-\uA8C3\uA952-\uA953\uA983\uA9B4-\uA9B5\uA9BA-\uA9BB\uA9BD-\uA9C0\uAA2F-\uAA30\uAA33-\uAA34\uAA4D\uAA7B\uAAEB\uAAEE-\uAAEF\uAAF5\uABE3-\uABE4\uABE6-\uABE7\uABE9-\uABEA\uABEC] 338 | 339 | # Mark, Nonspacing 340 | Mn <- [\u0300-\u036F\u0483-\u0487\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E4-\u08FE\u0900-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962-\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2-\u09E3\u0A01-\u0A02\u0A3C\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A51\u0A70-\u0A71\u0A75\u0A81-\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7-\u0AC8\u0ACD\u0AE2-\u0AE3\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62-\u0B63\u0B82\u0BC0\u0BCD\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C62-\u0C63\u0CBC\u0CBF\u0CC6\u0CCC-\u0CCD\u0CE2-\u0CE3\u0D41-\u0D44\u0D4D\u0D62-\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86-\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039-\u103A\u103D-\u103E\u1058-\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17B4-\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193B\u1A17-\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80-\u1B81\u1BA2-\u1BA5\u1BA8-\u1BA9\u1BAB\u1BE6\u1BE8-\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1DC0-\u1DE6\u1DFC-\u1DFF\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099-\u309A\uA66F\uA674-\uA67D\uA69F\uA6F0-\uA6F1\uA802\uA806\uA80B\uA825-\uA826\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uAA29-\uAA2E\uAA31-\uAA32\uAA35-\uAA36\uAA43\uAA4C\uAAB0\uAAB2-\uAAB4\uAAB7-\uAAB8\uAABE-\uAABF\uAAC1\uAAEC-\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE26] 341 | 342 | # Number, Decimal Digit 343 | Nd <- [\u0030-\u0039\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19] 344 | 345 | # Number, Letter 346 | Nl <- [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF] 347 | 348 | # Punctuation, Connector 349 | Pc <- [\u005F\u203F-\u2040\u2054\uFE33-\uFE34\uFE4D-\uFE4F\uFF3F] 350 | 351 | # Separator, Space 352 | Zs <- [\u0020\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000] 353 | 354 | # Tokens 355 | 356 | BreakToken <- "break" !IdentifierPart 357 | CaseToken <- "case" !IdentifierPart 358 | CatchToken <- "catch" !IdentifierPart 359 | ClassToken <- "class" !IdentifierPart 360 | ConstToken <- "const" !IdentifierPart 361 | ContinueToken <- "continue" !IdentifierPart 362 | DebuggerToken <- "debugger" !IdentifierPart 363 | DefaultToken <- "default" !IdentifierPart 364 | DeleteToken <- "delete" !IdentifierPart 365 | DoToken <- "do" !IdentifierPart 366 | ElseToken <- "else" !IdentifierPart 367 | EnumToken <- "enum" !IdentifierPart 368 | ExportToken <- "export" !IdentifierPart 369 | ExtendsToken <- "extends" !IdentifierPart 370 | FalseToken <- "false" !IdentifierPart 371 | FinallyToken <- "finally" !IdentifierPart 372 | ForToken <- "for" !IdentifierPart 373 | FunctionToken <- "function" !IdentifierPart 374 | GetToken <- "get" !IdentifierPart 375 | IfToken <- "if" !IdentifierPart 376 | ImportToken <- "import" !IdentifierPart 377 | InstanceofToken <- "instanceof" !IdentifierPart 378 | InToken <- "in" !IdentifierPart 379 | NewToken <- "new" !IdentifierPart 380 | NullToken <- "null" !IdentifierPart 381 | ReturnToken <- "return" !IdentifierPart 382 | SetToken <- "set" !IdentifierPart 383 | SuperToken <- "super" !IdentifierPart 384 | SwitchToken <- "switch" !IdentifierPart 385 | ThisToken <- "this" !IdentifierPart 386 | ThrowToken <- "throw" !IdentifierPart 387 | TrueToken <- "true" !IdentifierPart 388 | TryToken <- "try" !IdentifierPart 389 | TypeofToken <- "typeof" !IdentifierPart 390 | VarToken <- "var" !IdentifierPart 391 | VoidToken <- "void" !IdentifierPart 392 | WhileToken <- "while" !IdentifierPart 393 | WithToken <- "with" !IdentifierPart 394 | 395 | # Skipped 396 | 397 | __ 398 | <- (WhiteSpace / LineTerminatorSequence / Comment)* 399 | 400 | _ 401 | <- (WhiteSpace / MultiLineCommentNoLineTerminator)* 402 | 403 | # Automatic Semicolon Insertion 404 | 405 | EOS 406 | <- __ ";" 407 | / _ SingleLineComment? LineTerminatorSequence 408 | / _ &"}" 409 | / __ EOF 410 | 411 | EOF 412 | <- !. 413 | 414 | # ----- A.2 Number Conversions ----- 415 | 416 | # Irrelevant. 417 | 418 | # ----- A.3 Expressions ----- 419 | 420 | PrimaryExpression 421 | <- ThisToken 422 | / Identifier 423 | / Literal 424 | / ArrayLiteral 425 | / ObjectLiteral 426 | / "(" __ Expression __ ")" 427 | 428 | ArrayLiteral 429 | <- "[" __ (Elision __)? "]" 430 | / "[" __ ElementList __ "]" 431 | / "[" __ ElementList __ "," __ (Elision __)? "]" 432 | 433 | ElementList 434 | <- ((Elision __)? AssignmentExpression) 435 | (__ "," __ (Elision __)? AssignmentExpression)* 436 | 437 | 438 | Elision 439 | <- "," (__ ",")* 440 | 441 | ObjectLiteral 442 | <- "{" __ "}" 443 | / "{" __ PropertyNameAndValueList __ "}" 444 | / "{" __ PropertyNameAndValueList __ "," __ "}" 445 | PropertyNameAndValueList 446 | <- PropertyAssignment (__ "," __ PropertyAssignment)* 447 | 448 | PropertyAssignment 449 | <- PropertyName __ ":" __ AssignmentExpression 450 | / GetToken __ PropertyName __ 451 | "(" __ ")" __ 452 | "{" __ FunctionBody __ "}" 453 | / SetToken __ PropertyName __ 454 | "(" __ PropertySetParameterList __ ")" __ 455 | "{" __ FunctionBody __ "}" 456 | 457 | PropertyName 458 | <- IdentifierName 459 | / StringLiteral 460 | / NumericLiteral 461 | 462 | PropertySetParameterList 463 | <- Identifier 464 | 465 | MemberExpression 466 | <- ( 467 | PrimaryExpression 468 | / FunctionExpression 469 | / NewToken __ MemberExpression __ Arguments 470 | ) 471 | ( 472 | __ "[" __ Expression __ "]" 473 | / __ "." __ IdentifierName 474 | )* 475 | 476 | NewExpression 477 | <- MemberExpression 478 | / NewToken __ NewExpression 479 | 480 | CallExpression 481 | <- ( 482 | MemberExpression __ Arguments 483 | ) 484 | ( 485 | __ Arguments 486 | / __ "[" __ Expression __ "]" 487 | / __ "." __ IdentifierName 488 | )* 489 | 490 | Arguments 491 | <- "(" __ (ArgumentList __)? ")" 492 | 493 | ArgumentList 494 | <- AssignmentExpression (__ "," __ AssignmentExpression)* 495 | 496 | LeftHandSideExpression 497 | <- CallExpression 498 | / NewExpression 499 | 500 | PostfixExpression 501 | <- LeftHandSideExpression _ PostfixOperator 502 | / LeftHandSideExpression 503 | 504 | PostfixOperator 505 | <- "++" 506 | / "--" 507 | 508 | UnaryExpression 509 | <- PostfixExpression 510 | / UnaryOperator __ UnaryExpression 511 | 512 | UnaryOperator 513 | <- DeleteToken 514 | / VoidToken 515 | / TypeofToken 516 | / "++" 517 | / "--" 518 | / ("+" !"=") 519 | / ("-" !"=") 520 | / "~" 521 | / "!" 522 | 523 | MultiplicativeExpression 524 | <- UnaryExpression 525 | (__ MultiplicativeOperator __ UnaryExpression)* 526 | 527 | 528 | MultiplicativeOperator 529 | <- ("*" !"=") 530 | / ("/" !"=") 531 | / ("%" !"=") 532 | 533 | AdditiveExpression 534 | <- MultiplicativeExpression 535 | (__ AdditiveOperator __ MultiplicativeExpression)* 536 | 537 | AdditiveOperator 538 | <- ("+" ![+=]) 539 | / ("-" ![-=]) 540 | 541 | ShiftExpression 542 | <- AdditiveExpression 543 | (__ ShiftOperator __ AdditiveExpression)* 544 | 545 | ShiftOperator 546 | <- ("<<" !"=") 547 | / (">>>" !"=") 548 | / (">>" !"=") 549 | 550 | RelationalExpression 551 | <- ShiftExpression 552 | (__ RelationalOperator __ ShiftExpression)* 553 | 554 | RelationalOperator 555 | <- "<=" 556 | / ">=" 557 | / ("<" !"<") 558 | / (">" !">") 559 | / InstanceofToken 560 | / InToken 561 | 562 | RelationalExpressionNoIn 563 | <- ShiftExpression 564 | (__ RelationalOperatorNoIn __ ShiftExpression)* 565 | 566 | RelationalOperatorNoIn 567 | <- "<=" 568 | / ">=" 569 | / ("<" !"<") 570 | / (">" !">") 571 | / InstanceofToken 572 | 573 | EqualityExpression 574 | <- RelationalExpression 575 | (__ EqualityOperator __ RelationalExpression)* 576 | 577 | EqualityExpressionNoIn 578 | <- RelationalExpressionNoIn 579 | (__ EqualityOperator __ RelationalExpressionNoIn)* 580 | 581 | EqualityOperator 582 | <- "===" { $$ = '=='; } 583 | / "!==" 584 | / "==" 585 | / "!=" 586 | 587 | BitwiseANDExpression 588 | <- EqualityExpression 589 | (__ BitwiseANDOperator __ EqualityExpression)* 590 | 591 | BitwiseANDExpressionNoIn 592 | <- EqualityExpressionNoIn 593 | (__ BitwiseANDOperator __ EqualityExpressionNoIn)* 594 | 595 | BitwiseANDOperator 596 | <- ("&" ![&=]) 597 | 598 | BitwiseXORExpression 599 | <- BitwiseANDExpression 600 | (__ BitwiseXOROperator __ BitwiseANDExpression)* 601 | 602 | BitwiseXORExpressionNoIn 603 | <- BitwiseANDExpressionNoIn 604 | (__ BitwiseXOROperator __ BitwiseANDExpressionNoIn)* 605 | 606 | BitwiseXOROperator 607 | <- ("^" !"=") 608 | 609 | BitwiseORExpression 610 | <- BitwiseXORExpression 611 | (__ BitwiseOROperator __ BitwiseXORExpression)* 612 | 613 | BitwiseORExpressionNoIn 614 | <- BitwiseXORExpressionNoIn 615 | (__ BitwiseOROperator __ BitwiseXORExpressionNoIn)* 616 | 617 | BitwiseOROperator 618 | <- ("|" ![|=]) 619 | 620 | LogicalANDExpression 621 | <- BitwiseORExpression 622 | (__ LogicalANDOperator __ BitwiseORExpression)* 623 | 624 | LogicalANDExpressionNoIn 625 | <- BitwiseORExpressionNoIn 626 | (__ LogicalANDOperator __ BitwiseORExpressionNoIn)* 627 | 628 | LogicalANDOperator 629 | <- "&&" 630 | 631 | LogicalORExpression 632 | <- LogicalANDExpression 633 | (__ LogicalOROperator __ LogicalANDExpression)* 634 | 635 | LogicalORExpressionNoIn 636 | <- LogicalANDExpressionNoIn 637 | (__ LogicalOROperator __ LogicalANDExpressionNoIn)* 638 | 639 | LogicalOROperator 640 | <- "||" 641 | 642 | ConditionalExpression 643 | <- LogicalORExpression __ 644 | "?" __ AssignmentExpression __ 645 | ":" __ AssignmentExpression 646 | / LogicalORExpression 647 | 648 | ConditionalExpressionNoIn 649 | <- LogicalORExpressionNoIn __ 650 | "?" __ AssignmentExpression __ 651 | ":" __ AssignmentExpressionNoIn 652 | / LogicalORExpressionNoIn 653 | 654 | AssignmentExpression 655 | <- LeftHandSideExpression __ 656 | "=" !"=" __ 657 | AssignmentExpression 658 | / LeftHandSideExpression __ 659 | AssignmentOperator __ 660 | AssignmentExpression 661 | / ConditionalExpression 662 | 663 | AssignmentExpressionNoIn 664 | <- LeftHandSideExpression __ 665 | "=" !"=" __ 666 | AssignmentExpressionNoIn 667 | / LeftHandSideExpression __ 668 | AssignmentOperator __ 669 | AssignmentExpressionNoIn 670 | / ConditionalExpressionNoIn 671 | 672 | AssignmentOperator 673 | <- "*=" 674 | / "/=" 675 | / "%=" 676 | / "+=" 677 | / "-=" 678 | / "<<=" 679 | / ">>=" 680 | / ">>>=" 681 | / "&=" 682 | / "^=" 683 | / "|=" 684 | 685 | Expression 686 | <- AssignmentExpression (__ "," __ AssignmentExpression)* 687 | 688 | ExpressionNoIn 689 | <- AssignmentExpressionNoIn (__ "," __ AssignmentExpressionNoIn)* { 690 | return rest.length > 0 691 | ? { "SequenceExpression", buildList(first, rest, 3) } 692 | : first; 693 | } 694 | 695 | # ----- A.4 Statements ----- 696 | 697 | Statement 698 | <- Block 699 | / VariableStatement 700 | / EmptyStatement 701 | / ExpressionStatement 702 | / IfStatement 703 | / IterationStatement 704 | / ContinueStatement 705 | / BreakStatement 706 | / ReturnStatement 707 | / WithStatement 708 | / LabelledStatement 709 | / SwitchStatement 710 | / ThrowStatement 711 | / TryStatement 712 | / DebuggerStatement 713 | 714 | Block 715 | <- "{" __ (StatementList __)? "}" 716 | 717 | StatementList 718 | <- Statement (__ Statement)* 719 | 720 | VariableStatement 721 | <- VarToken __ VariableDeclarationList EOS 722 | 723 | VariableDeclarationList 724 | <- VariableDeclaration (__ "," __ VariableDeclaration)* 725 | 726 | VariableDeclarationListNoIn 727 | <- VariableDeclarationNoIn (__ "," __ VariableDeclarationNoIn)* 728 | 729 | VariableDeclaration 730 | <- Identifier (__ Initialiser)? 731 | 732 | VariableDeclarationNoIn 733 | <- Identifier (__ InitialiserNoIn)? 734 | 735 | Initialiser 736 | <- "=" !"=" __ AssignmentExpression 737 | 738 | InitialiserNoIn 739 | <- "=" !"=" __ AssignmentExpressionNoIn 740 | 741 | EmptyStatement 742 | <- ";" 743 | 744 | ExpressionStatement 745 | <- !("{" / FunctionToken) Expression EOS 746 | 747 | IfStatement 748 | <- IfToken __ "(" __ Expression __ ")" __ 749 | Statement __ 750 | ElseToken __ 751 | Statement 752 | / IfToken __ "(" __ Expression __ ")" __ 753 | Statement 754 | 755 | IterationStatement 756 | <- DoToken __ 757 | Statement __ 758 | WhileToken __ "(" __ Expression __ ")" EOS 759 | 760 | / WhileToken __ "(" __ Expression __ ")" __ 761 | Statement 762 | / ForToken __ 763 | "(" __ 764 | (ExpressionNoIn __)? ";" __ 765 | (Expression __)? ";" __ 766 | (Expression __)? 767 | ")" __ 768 | Statement 769 | / ForToken __ 770 | "(" __ 771 | VarToken __ VariableDeclarationListNoIn __ ";" __ 772 | (Expression __)? ";" __ 773 | (Expression __)? 774 | ")" __ 775 | Statement 776 | / ForToken __ 777 | "(" __ 778 | LeftHandSideExpression __ 779 | InToken __ 780 | Expression __ 781 | ")" __ 782 | Statement 783 | / ForToken __ 784 | "(" __ 785 | VarToken __ VariableDeclarationListNoIn __ 786 | InToken __ 787 | Expression __ 788 | ")" __ 789 | Statement 790 | 791 | ContinueStatement 792 | <- ContinueToken EOS 793 | / ContinueToken _ Identifier EOS 794 | 795 | BreakStatement 796 | <- BreakToken EOS 797 | / BreakToken _ Identifier EOS 798 | 799 | ReturnStatement 800 | <- ReturnToken EOS 801 | / ReturnToken _ Expression EOS 802 | 803 | WithStatement 804 | <- WithToken __ "(" __ Expression __ ")" __ 805 | Statement 806 | 807 | SwitchStatement 808 | <- SwitchToken __ "(" __ Expression __ ")" __ 809 | CaseBlock 810 | 811 | 812 | CaseBlock 813 | <- "{" __ (CaseClauses __)? "}" 814 | / "{" __ 815 | (CaseClauses __)? 816 | DefaultClause __ 817 | (CaseClauses __)? "}" 818 | 819 | CaseClauses 820 | <- CaseClause (__ CaseClause)* 821 | 822 | CaseClause 823 | <- CaseToken __ Expression __ ":" (__ StatementList)? 824 | 825 | DefaultClause 826 | <- DefaultToken __ ":" (__ StatementList)? 827 | 828 | LabelledStatement 829 | <- Identifier __ ":" __ Statement 830 | 831 | ThrowStatement 832 | <- ThrowToken _ Expression EOS 833 | 834 | TryStatement 835 | <- TryToken __ Block __ Catch __ Finally 836 | / TryToken __ Block __ Catch 837 | / TryToken __ Block __ Finally 838 | 839 | Catch 840 | <- CatchToken __ "(" __ Identifier __ ")" __ Block 841 | 842 | Finally 843 | <- FinallyToken __ Block 844 | 845 | DebuggerStatement 846 | <- DebuggerToken EOS 847 | 848 | # ----- A.5 Functions and Programs ----- 849 | 850 | FunctionDeclaration 851 | <- FunctionToken __ Identifier __ 852 | "(" __ (FormalParameterList __)? ")" __ 853 | "{" __ FunctionBody __ "}" 854 | 855 | FunctionExpression 856 | <- FunctionToken __ (Identifier __)? 857 | "(" __ (FormalParameterList __)? ")" __ 858 | "{" __ FunctionBody __ "}" 859 | 860 | 861 | FormalParameterList 862 | <- Identifier (__ "," __ Identifier)* 863 | 864 | FunctionBody 865 | <- SourceElements? 866 | 867 | Program 868 | <- SourceElements? 869 | 870 | SourceElements 871 | <- SourceElement (__ SourceElement)* 872 | 873 | SourceElement 874 | <- Statement 875 | / FunctionDeclaration -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: jsparser2 2 | version: 0.0.1 3 | description: A simple console application. 4 | dependencies: 5 | # foo_bar: '>=1.0.0 <2.0.0' 6 | peg: 7 | git: https://github.com/luisvt/peg.git 8 | transformers: 9 | - peg --------------------------------------------------------------------------------