Tim van der Lippe | 652ccb7 | 2021-05-27 17:07:12 +0100 | [diff] [blame^] | 1 | /*********************************************************************** |
| 2 | |
| 3 | A JavaScript tokenizer / parser / beautifier / compressor. |
| 4 | https://github.com/mishoo/UglifyJS |
| 5 | |
| 6 | -------------------------------- (C) --------------------------------- |
| 7 | |
| 8 | Author: Mihai Bazon |
| 9 | <mihai.bazon@gmail.com> |
| 10 | http://mihai.bazon.net/blog |
| 11 | |
| 12 | Distributed under the BSD license: |
| 13 | |
| 14 | Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> |
| 15 | |
| 16 | Redistribution and use in source and binary forms, with or without |
| 17 | modification, are permitted provided that the following conditions |
| 18 | are met: |
| 19 | |
| 20 | * Redistributions of source code must retain the above |
| 21 | copyright notice, this list of conditions and the following |
| 22 | disclaimer. |
| 23 | |
| 24 | * Redistributions in binary form must reproduce the above |
| 25 | copyright notice, this list of conditions and the following |
| 26 | disclaimer in the documentation and/or other materials |
| 27 | provided with the distribution. |
| 28 | |
| 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY |
| 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE |
| 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, |
| 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
| 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
| 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 40 | SUCH DAMAGE. |
| 41 | |
| 42 | ***********************************************************************/ |
| 43 | |
| 44 | "use strict"; |
| 45 | |
| 46 | function DEFNODE(type, props, methods, base) { |
| 47 | if (typeof base === "undefined") base = AST_Node; |
| 48 | props = props ? props.split(/\s+/) : []; |
| 49 | var self_props = props; |
| 50 | if (base && base.PROPS) props = props.concat(base.PROPS); |
| 51 | var code = [ |
| 52 | "return function AST_", type, "(props){", |
| 53 | "if(props){", |
| 54 | ]; |
| 55 | props.forEach(function(prop) { |
| 56 | code.push("this.", prop, "=props.", prop, ";"); |
| 57 | }); |
| 58 | code.push("}"); |
| 59 | var proto = base && new base; |
| 60 | if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();"); |
| 61 | code.push("}"); |
| 62 | var ctor = new Function(code.join(""))(); |
| 63 | if (proto) { |
| 64 | ctor.prototype = proto; |
| 65 | ctor.BASE = base; |
| 66 | } |
| 67 | if (base) base.SUBCLASSES.push(ctor); |
| 68 | ctor.prototype.CTOR = ctor; |
| 69 | ctor.PROPS = props || null; |
| 70 | ctor.SELF_PROPS = self_props; |
| 71 | ctor.SUBCLASSES = []; |
| 72 | if (type) { |
| 73 | ctor.prototype.TYPE = ctor.TYPE = type; |
| 74 | } |
| 75 | if (methods) for (var name in methods) if (HOP(methods, name)) { |
| 76 | if (/^\$/.test(name)) { |
| 77 | ctor[name.substr(1)] = methods[name]; |
| 78 | } else { |
| 79 | ctor.prototype[name] = methods[name]; |
| 80 | } |
| 81 | } |
| 82 | ctor.DEFMETHOD = function(name, method) { |
| 83 | this.prototype[name] = method; |
| 84 | }; |
| 85 | if (typeof exports !== "undefined") { |
| 86 | exports["AST_" + type] = ctor; |
| 87 | } |
| 88 | return ctor; |
| 89 | } |
| 90 | |
| 91 | var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw", { |
| 92 | }, null); |
| 93 | |
| 94 | var AST_Node = DEFNODE("Node", "start end", { |
| 95 | _clone: function(deep) { |
| 96 | if (deep) { |
| 97 | var self = this.clone(); |
| 98 | return self.transform(new TreeTransformer(function(node) { |
| 99 | if (node !== self) { |
| 100 | return node.clone(true); |
| 101 | } |
| 102 | })); |
| 103 | } |
| 104 | return new this.CTOR(this); |
| 105 | }, |
| 106 | clone: function(deep) { |
| 107 | return this._clone(deep); |
| 108 | }, |
| 109 | $documentation: "Base class of all AST nodes", |
| 110 | $propdoc: { |
| 111 | start: "[AST_Token] The first token of this node", |
| 112 | end: "[AST_Token] The last token of this node" |
| 113 | }, |
| 114 | walk: function(visitor) { |
| 115 | visitor.visit(this); |
| 116 | }, |
| 117 | _validate: function() { |
| 118 | if (this.TYPE == "Node") throw new Error("should not instantiate AST_Node"); |
| 119 | }, |
| 120 | validate: function() { |
| 121 | var ctor = this.CTOR; |
| 122 | do { |
| 123 | ctor.prototype._validate.call(this); |
| 124 | } while (ctor = ctor.BASE); |
| 125 | }, |
| 126 | validate_ast: function() { |
| 127 | var marker = {}; |
| 128 | this.walk(new TreeWalker(function(node) { |
| 129 | if (node.validate_visited === marker) { |
| 130 | throw new Error(string_template("cannot reuse {type} from [{file}:{line},{col}]", { |
| 131 | type: "AST_" + node.TYPE, |
| 132 | file: node.start.file, |
| 133 | line: node.start.line, |
| 134 | col: node.start.col, |
| 135 | })); |
| 136 | } |
| 137 | node.validate_visited = marker; |
| 138 | })); |
| 139 | }, |
| 140 | }, null); |
| 141 | |
| 142 | (AST_Node.log_function = function(fn, verbose) { |
| 143 | if (typeof fn != "function") { |
| 144 | AST_Node.info = AST_Node.warn = noop; |
| 145 | return; |
| 146 | } |
| 147 | var printed = Object.create(null); |
| 148 | AST_Node.info = verbose ? function(text, props) { |
| 149 | log("INFO: " + string_template(text, props)); |
| 150 | } : noop; |
| 151 | AST_Node.warn = function(text, props) { |
| 152 | log("WARN: " + string_template(text, props)); |
| 153 | }; |
| 154 | |
| 155 | function log(msg) { |
| 156 | if (printed[msg]) return; |
| 157 | printed[msg] = true; |
| 158 | fn(msg); |
| 159 | } |
| 160 | })(); |
| 161 | |
| 162 | var restore_transforms = []; |
| 163 | AST_Node.enable_validation = function() { |
| 164 | AST_Node.disable_validation(); |
| 165 | (function validate_transform(ctor) { |
| 166 | ctor.SUBCLASSES.forEach(validate_transform); |
| 167 | if (!HOP(ctor.prototype, "transform")) return; |
| 168 | var transform = ctor.prototype.transform; |
| 169 | ctor.prototype.transform = function(tw, in_list) { |
| 170 | var node = transform.call(this, tw, in_list); |
| 171 | if (node instanceof AST_Node) { |
| 172 | node.validate(); |
| 173 | } else if (!(node === null || in_list && List.is_op(node))) { |
| 174 | throw new Error("invalid transformed value: " + node); |
| 175 | } |
| 176 | return node; |
| 177 | }; |
| 178 | restore_transforms.push(function() { |
| 179 | ctor.prototype.transform = transform; |
| 180 | }); |
| 181 | })(this); |
| 182 | }; |
| 183 | |
| 184 | AST_Node.disable_validation = function() { |
| 185 | var restore; |
| 186 | while (restore = restore_transforms.pop()) restore(); |
| 187 | }; |
| 188 | |
| 189 | /* -----[ statements ]----- */ |
| 190 | |
| 191 | var AST_Statement = DEFNODE("Statement", null, { |
| 192 | $documentation: "Base class of all statements", |
| 193 | _validate: function() { |
| 194 | if (this.TYPE == "Statement") throw new Error("should not instantiate AST_Statement"); |
| 195 | }, |
| 196 | }); |
| 197 | |
| 198 | var AST_Debugger = DEFNODE("Debugger", null, { |
| 199 | $documentation: "Represents a debugger statement", |
| 200 | }, AST_Statement); |
| 201 | |
| 202 | var AST_Directive = DEFNODE("Directive", "quote value", { |
| 203 | $documentation: "Represents a directive, like \"use strict\";", |
| 204 | $propdoc: { |
| 205 | quote: "[string?] the original quote character", |
| 206 | value: "[string] The value of this directive as a plain string (it's not an AST_String!)", |
| 207 | }, |
| 208 | _validate: function() { |
| 209 | if (this.quote != null) { |
| 210 | if (typeof this.quote != "string") throw new Error("quote must be string"); |
| 211 | if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); |
| 212 | } |
| 213 | if (typeof this.value != "string") throw new Error("value must be string"); |
| 214 | }, |
| 215 | }, AST_Statement); |
| 216 | |
| 217 | var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { |
| 218 | $documentation: "The empty statement (empty block or simply a semicolon)" |
| 219 | }, AST_Statement); |
| 220 | |
| 221 | function is_statement(node) { |
| 222 | return node instanceof AST_Statement |
| 223 | && !(node instanceof AST_ClassExpression) |
| 224 | && !(node instanceof AST_LambdaExpression); |
| 225 | } |
| 226 | |
| 227 | function validate_expression(value, prop, multiple, allow_spread, allow_hole) { |
| 228 | multiple = multiple ? "contain" : "be"; |
| 229 | if (!(value instanceof AST_Node)) throw new Error(prop + " must " + multiple + " AST_Node"); |
| 230 | if (value instanceof AST_DefaultValue) throw new Error(prop + " cannot " + multiple + " AST_DefaultValue"); |
| 231 | if (value instanceof AST_Destructured) throw new Error(prop + " cannot " + multiple + " AST_Destructured"); |
| 232 | if (value instanceof AST_Hole && !allow_hole) throw new Error(prop + " cannot " + multiple + " AST_Hole"); |
| 233 | if (value instanceof AST_Spread && !allow_spread) throw new Error(prop + " cannot " + multiple + " AST_Spread"); |
| 234 | if (is_statement(value)) throw new Error(prop + " cannot " + multiple + " AST_Statement"); |
| 235 | if (value instanceof AST_SymbolDeclaration) { |
| 236 | throw new Error(prop + " cannot " + multiple + " AST_SymbolDeclaration"); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | function must_be_expression(node, prop) { |
| 241 | validate_expression(node[prop], prop); |
| 242 | } |
| 243 | |
| 244 | var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { |
| 245 | $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", |
| 246 | $propdoc: { |
| 247 | body: "[AST_Node] an expression node (should not be instanceof AST_Statement)", |
| 248 | }, |
| 249 | walk: function(visitor) { |
| 250 | var node = this; |
| 251 | visitor.visit(node, function() { |
| 252 | node.body.walk(visitor); |
| 253 | }); |
| 254 | }, |
| 255 | _validate: function() { |
| 256 | must_be_expression(this, "body"); |
| 257 | }, |
| 258 | }, AST_Statement); |
| 259 | |
| 260 | var AST_BlockScope = DEFNODE("BlockScope", "enclosed functions make_def parent_scope variables", { |
| 261 | $documentation: "Base class for all statements introducing a lexical scope", |
| 262 | $propdoc: { |
| 263 | enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", |
| 264 | functions: "[Dictionary/S] like `variables`, but only lists function declarations", |
| 265 | parent_scope: "[AST_Scope?/S] link to the parent scope", |
| 266 | variables: "[Dictionary/S] a map of name ---> SymbolDef for all variables/functions defined in this scope", |
| 267 | }, |
| 268 | clone: function(deep) { |
| 269 | var node = this._clone(deep); |
| 270 | if (this.enclosed) node.enclosed = this.enclosed.slice(); |
| 271 | if (this.functions) node.functions = this.functions.clone(); |
| 272 | if (this.variables) node.variables = this.variables.clone(); |
| 273 | return node; |
| 274 | }, |
| 275 | pinned: function() { |
| 276 | return this.resolve().pinned(); |
| 277 | }, |
| 278 | resolve: function() { |
| 279 | return this.parent_scope.resolve(); |
| 280 | }, |
| 281 | _validate: function() { |
| 282 | if (this.TYPE == "BlockScope") throw new Error("should not instantiate AST_BlockScope"); |
| 283 | if (this.parent_scope == null) return; |
| 284 | if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope"); |
| 285 | if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope"); |
| 286 | }, |
| 287 | }, AST_Statement); |
| 288 | |
| 289 | function walk_body(node, visitor) { |
| 290 | node.body.forEach(function(node) { |
| 291 | node.walk(visitor); |
| 292 | }); |
| 293 | } |
| 294 | |
| 295 | var AST_Block = DEFNODE("Block", "body", { |
| 296 | $documentation: "A body of statements (usually braced)", |
| 297 | $propdoc: { |
| 298 | body: "[AST_Statement*] an array of statements" |
| 299 | }, |
| 300 | walk: function(visitor) { |
| 301 | var node = this; |
| 302 | visitor.visit(node, function() { |
| 303 | walk_body(node, visitor); |
| 304 | }); |
| 305 | }, |
| 306 | _validate: function() { |
| 307 | if (this.TYPE == "Block") throw new Error("should not instantiate AST_Block"); |
| 308 | this.body.forEach(function(node) { |
| 309 | if (!is_statement(node)) throw new Error("body must contain AST_Statement"); |
| 310 | }); |
| 311 | }, |
| 312 | }, AST_BlockScope); |
| 313 | |
| 314 | var AST_BlockStatement = DEFNODE("BlockStatement", null, { |
| 315 | $documentation: "A block statement", |
| 316 | }, AST_Block); |
| 317 | |
| 318 | var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { |
| 319 | $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", |
| 320 | $propdoc: { |
| 321 | body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" |
| 322 | }, |
| 323 | _validate: function() { |
| 324 | if (this.TYPE == "StatementWithBody") throw new Error("should not instantiate AST_StatementWithBody"); |
| 325 | if (!is_statement(this.body)) throw new Error("body must be AST_Statement"); |
| 326 | }, |
| 327 | }, AST_BlockScope); |
| 328 | |
| 329 | var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { |
| 330 | $documentation: "Statement with a label", |
| 331 | $propdoc: { |
| 332 | label: "[AST_Label] a label definition" |
| 333 | }, |
| 334 | walk: function(visitor) { |
| 335 | var node = this; |
| 336 | visitor.visit(node, function() { |
| 337 | node.label.walk(visitor); |
| 338 | node.body.walk(visitor); |
| 339 | }); |
| 340 | }, |
| 341 | clone: function(deep) { |
| 342 | var node = this._clone(deep); |
| 343 | if (deep) { |
| 344 | var label = node.label; |
| 345 | var def = this.label; |
| 346 | node.walk(new TreeWalker(function(node) { |
| 347 | if (node instanceof AST_LoopControl) { |
| 348 | if (!node.label || node.label.thedef !== def) return; |
| 349 | node.label.thedef = label; |
| 350 | label.references.push(node); |
| 351 | return true; |
| 352 | } |
| 353 | if (node instanceof AST_Scope) return true; |
| 354 | })); |
| 355 | } |
| 356 | return node; |
| 357 | }, |
| 358 | _validate: function() { |
| 359 | if (!(this.label instanceof AST_Label)) throw new Error("label must be AST_Label"); |
| 360 | }, |
| 361 | }, AST_StatementWithBody); |
| 362 | |
| 363 | var AST_IterationStatement = DEFNODE("IterationStatement", null, { |
| 364 | $documentation: "Internal class. All loops inherit from it.", |
| 365 | _validate: function() { |
| 366 | if (this.TYPE == "IterationStatement") throw new Error("should not instantiate AST_IterationStatement"); |
| 367 | }, |
| 368 | }, AST_StatementWithBody); |
| 369 | |
| 370 | var AST_DWLoop = DEFNODE("DWLoop", "condition", { |
| 371 | $documentation: "Base class for do/while statements", |
| 372 | $propdoc: { |
| 373 | condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" |
| 374 | }, |
| 375 | _validate: function() { |
| 376 | if (this.TYPE == "DWLoop") throw new Error("should not instantiate AST_DWLoop"); |
| 377 | must_be_expression(this, "condition"); |
| 378 | }, |
| 379 | }, AST_IterationStatement); |
| 380 | |
| 381 | var AST_Do = DEFNODE("Do", null, { |
| 382 | $documentation: "A `do` statement", |
| 383 | walk: function(visitor) { |
| 384 | var node = this; |
| 385 | visitor.visit(node, function() { |
| 386 | node.body.walk(visitor); |
| 387 | node.condition.walk(visitor); |
| 388 | }); |
| 389 | } |
| 390 | }, AST_DWLoop); |
| 391 | |
| 392 | var AST_While = DEFNODE("While", null, { |
| 393 | $documentation: "A `while` statement", |
| 394 | walk: function(visitor) { |
| 395 | var node = this; |
| 396 | visitor.visit(node, function() { |
| 397 | node.condition.walk(visitor); |
| 398 | node.body.walk(visitor); |
| 399 | }); |
| 400 | } |
| 401 | }, AST_DWLoop); |
| 402 | |
| 403 | var AST_For = DEFNODE("For", "init condition step", { |
| 404 | $documentation: "A `for` statement", |
| 405 | $propdoc: { |
| 406 | init: "[AST_Node?] the `for` initialization code, or null if empty", |
| 407 | condition: "[AST_Node?] the `for` termination clause, or null if empty", |
| 408 | step: "[AST_Node?] the `for` update clause, or null if empty" |
| 409 | }, |
| 410 | walk: function(visitor) { |
| 411 | var node = this; |
| 412 | visitor.visit(node, function() { |
| 413 | if (node.init) node.init.walk(visitor); |
| 414 | if (node.condition) node.condition.walk(visitor); |
| 415 | if (node.step) node.step.walk(visitor); |
| 416 | node.body.walk(visitor); |
| 417 | }); |
| 418 | }, |
| 419 | _validate: function() { |
| 420 | if (this.init != null) { |
| 421 | if (!(this.init instanceof AST_Node)) throw new Error("init must be AST_Node"); |
| 422 | if (is_statement(this.init) && !(this.init instanceof AST_Definitions)) { |
| 423 | throw new Error("init cannot be AST_Statement"); |
| 424 | } |
| 425 | } |
| 426 | if (this.condition != null) must_be_expression(this, "condition"); |
| 427 | if (this.step != null) must_be_expression(this, "step"); |
| 428 | }, |
| 429 | }, AST_IterationStatement); |
| 430 | |
| 431 | var AST_ForEnumeration = DEFNODE("ForEnumeration", "init object", { |
| 432 | $documentation: "Base class for enumeration loops, i.e. `for ... in`, `for ... of` & `for await ... of`", |
| 433 | $propdoc: { |
| 434 | init: "[AST_Node] the assignment target during iteration", |
| 435 | object: "[AST_Node] the object to iterate over" |
| 436 | }, |
| 437 | walk: function(visitor) { |
| 438 | var node = this; |
| 439 | visitor.visit(node, function() { |
| 440 | node.init.walk(visitor); |
| 441 | node.object.walk(visitor); |
| 442 | node.body.walk(visitor); |
| 443 | }); |
| 444 | }, |
| 445 | _validate: function() { |
| 446 | if (this.TYPE == "ForEnumeration") throw new Error("should not instantiate AST_ForEnumeration"); |
| 447 | if (this.init instanceof AST_Definitions) { |
| 448 | if (this.init.definitions.length != 1) throw new Error("init must have single declaration"); |
| 449 | } else { |
| 450 | validate_destructured(this.init, function(node) { |
| 451 | if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) { |
| 452 | throw new Error("init must be assignable: " + node.TYPE); |
| 453 | } |
| 454 | }); |
| 455 | } |
| 456 | must_be_expression(this, "object"); |
| 457 | }, |
| 458 | }, AST_IterationStatement); |
| 459 | |
| 460 | var AST_ForIn = DEFNODE("ForIn", null, { |
| 461 | $documentation: "A `for ... in` statement", |
| 462 | }, AST_ForEnumeration); |
| 463 | |
| 464 | var AST_ForOf = DEFNODE("ForOf", null, { |
| 465 | $documentation: "A `for ... of` statement", |
| 466 | }, AST_ForEnumeration); |
| 467 | |
| 468 | var AST_ForAwaitOf = DEFNODE("ForAwaitOf", null, { |
| 469 | $documentation: "A `for await ... of` statement", |
| 470 | }, AST_ForOf); |
| 471 | |
| 472 | var AST_With = DEFNODE("With", "expression", { |
| 473 | $documentation: "A `with` statement", |
| 474 | $propdoc: { |
| 475 | expression: "[AST_Node] the `with` expression" |
| 476 | }, |
| 477 | walk: function(visitor) { |
| 478 | var node = this; |
| 479 | visitor.visit(node, function() { |
| 480 | node.expression.walk(visitor); |
| 481 | node.body.walk(visitor); |
| 482 | }); |
| 483 | }, |
| 484 | _validate: function() { |
| 485 | must_be_expression(this, "expression"); |
| 486 | }, |
| 487 | }, AST_StatementWithBody); |
| 488 | |
| 489 | /* -----[ scope and functions ]----- */ |
| 490 | |
| 491 | var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", { |
| 492 | $documentation: "Base class for all statements introducing a lexical scope", |
| 493 | $propdoc: { |
| 494 | uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", |
| 495 | uses_with: "[boolean/S] tells whether this scope uses the `with` statement", |
| 496 | }, |
| 497 | pinned: function() { |
| 498 | return this.uses_eval || this.uses_with; |
| 499 | }, |
| 500 | resolve: return_this, |
| 501 | _validate: function() { |
| 502 | if (this.TYPE == "Scope") throw new Error("should not instantiate AST_Scope"); |
| 503 | }, |
| 504 | }, AST_Block); |
| 505 | |
| 506 | var AST_Toplevel = DEFNODE("Toplevel", "globals", { |
| 507 | $documentation: "The toplevel scope", |
| 508 | $propdoc: { |
| 509 | globals: "[Dictionary/S] a map of name ---> SymbolDef for all undeclared names", |
| 510 | }, |
| 511 | wrap: function(name) { |
| 512 | var body = this.body; |
| 513 | return parse([ |
| 514 | "(function(exports){'$ORIG';})(typeof ", |
| 515 | name, |
| 516 | "=='undefined'?(", |
| 517 | name, |
| 518 | "={}):", |
| 519 | name, |
| 520 | ");" |
| 521 | ].join(""), { |
| 522 | filename: "wrap=" + JSON.stringify(name) |
| 523 | }).transform(new TreeTransformer(function(node) { |
| 524 | if (node instanceof AST_Directive && node.value == "$ORIG") { |
| 525 | return List.splice(body); |
| 526 | } |
| 527 | })); |
| 528 | }, |
| 529 | enclose: function(args_values) { |
| 530 | if (typeof args_values != "string") args_values = ""; |
| 531 | var index = args_values.indexOf(":"); |
| 532 | if (index < 0) index = args_values.length; |
| 533 | var body = this.body; |
| 534 | return parse([ |
| 535 | "(function(", |
| 536 | args_values.slice(0, index), |
| 537 | '){"$ORIG"})(', |
| 538 | args_values.slice(index + 1), |
| 539 | ")" |
| 540 | ].join(""), { |
| 541 | filename: "enclose=" + JSON.stringify(args_values) |
| 542 | }).transform(new TreeTransformer(function(node) { |
| 543 | if (node instanceof AST_Directive && node.value == "$ORIG") { |
| 544 | return List.splice(body); |
| 545 | } |
| 546 | })); |
| 547 | } |
| 548 | }, AST_Scope); |
| 549 | |
| 550 | var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", { |
| 551 | $documentation: "Base class for functions", |
| 552 | $propdoc: { |
| 553 | argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals", |
| 554 | length_read: "[boolean/S] whether length property of this function is accessed", |
| 555 | rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent", |
| 556 | uses_arguments: "[boolean/S] whether this function accesses the arguments array", |
| 557 | }, |
| 558 | each_argname: function(visit) { |
| 559 | var tw = new TreeWalker(function(node) { |
| 560 | if (node instanceof AST_DefaultValue) { |
| 561 | node.name.walk(tw); |
| 562 | return true; |
| 563 | } |
| 564 | if (node instanceof AST_DestructuredKeyVal) { |
| 565 | node.value.walk(tw); |
| 566 | return true; |
| 567 | } |
| 568 | if (node instanceof AST_SymbolFunarg) visit(node); |
| 569 | }); |
| 570 | this.argnames.forEach(function(argname) { |
| 571 | argname.walk(tw); |
| 572 | }); |
| 573 | if (this.rest) this.rest.walk(tw); |
| 574 | }, |
| 575 | walk: function(visitor) { |
| 576 | var node = this; |
| 577 | visitor.visit(node, function() { |
| 578 | if (node.name) node.name.walk(visitor); |
| 579 | node.argnames.forEach(function(argname) { |
| 580 | argname.walk(visitor); |
| 581 | }); |
| 582 | if (node.rest) node.rest.walk(visitor); |
| 583 | walk_body(node, visitor); |
| 584 | }); |
| 585 | }, |
| 586 | _validate: function() { |
| 587 | if (this.TYPE == "Lambda") throw new Error("should not instantiate AST_Lambda"); |
| 588 | this.argnames.forEach(function(node) { |
| 589 | validate_destructured(node, function(node) { |
| 590 | if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]"); |
| 591 | }, true); |
| 592 | }); |
| 593 | if (this.rest != null) validate_destructured(this.rest, function(node) { |
| 594 | if (!(node instanceof AST_SymbolFunarg)) throw new Error("rest must be AST_SymbolFunarg"); |
| 595 | }); |
| 596 | }, |
| 597 | }, AST_Scope); |
| 598 | |
| 599 | var AST_Accessor = DEFNODE("Accessor", null, { |
| 600 | $documentation: "A getter/setter function", |
| 601 | _validate: function() { |
| 602 | if (this.name != null) throw new Error("name must be null"); |
| 603 | }, |
| 604 | }, AST_Lambda); |
| 605 | |
| 606 | var AST_LambdaExpression = DEFNODE("LambdaExpression", "inlined", { |
| 607 | $documentation: "Base class for function expressions", |
| 608 | $propdoc: { |
| 609 | inlined: "[boolean/S] whether this function has been inlined", |
| 610 | }, |
| 611 | _validate: function() { |
| 612 | if (this.TYPE == "LambdaExpression") throw new Error("should not instantiate AST_LambdaExpression"); |
| 613 | }, |
| 614 | }, AST_Lambda); |
| 615 | |
| 616 | function is_arrow(node) { |
| 617 | return node instanceof AST_Arrow || node instanceof AST_AsyncArrow; |
| 618 | } |
| 619 | |
| 620 | function is_async(node) { |
| 621 | return node instanceof AST_AsyncArrow |
| 622 | || node instanceof AST_AsyncDefun |
| 623 | || node instanceof AST_AsyncFunction |
| 624 | || node instanceof AST_AsyncGeneratorDefun |
| 625 | || node instanceof AST_AsyncGeneratorFunction; |
| 626 | } |
| 627 | |
| 628 | function is_generator(node) { |
| 629 | return node instanceof AST_AsyncGeneratorDefun |
| 630 | || node instanceof AST_AsyncGeneratorFunction |
| 631 | || node instanceof AST_GeneratorDefun |
| 632 | || node instanceof AST_GeneratorFunction; |
| 633 | } |
| 634 | |
| 635 | function walk_lambda(node, tw) { |
| 636 | if (is_arrow(node) && node.value) { |
| 637 | node.value.walk(tw); |
| 638 | } else { |
| 639 | walk_body(node, tw); |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | var AST_Arrow = DEFNODE("Arrow", "value", { |
| 644 | $documentation: "An arrow function expression", |
| 645 | $propdoc: { |
| 646 | value: "[AST_Node?] simple return expression, or null if using function body.", |
| 647 | }, |
| 648 | walk: function(visitor) { |
| 649 | var node = this; |
| 650 | visitor.visit(node, function() { |
| 651 | node.argnames.forEach(function(argname) { |
| 652 | argname.walk(visitor); |
| 653 | }); |
| 654 | if (node.rest) node.rest.walk(visitor); |
| 655 | if (node.value) { |
| 656 | node.value.walk(visitor); |
| 657 | } else { |
| 658 | walk_body(node, visitor); |
| 659 | } |
| 660 | }); |
| 661 | }, |
| 662 | _validate: function() { |
| 663 | if (this.name != null) throw new Error("name must be null"); |
| 664 | if (this.uses_arguments) throw new Error("uses_arguments must be false"); |
| 665 | if (this.value != null) { |
| 666 | must_be_expression(this, "value"); |
| 667 | if (this.body.length) throw new Error("body must be empty if value exists"); |
| 668 | } |
| 669 | }, |
| 670 | }, AST_LambdaExpression); |
| 671 | |
| 672 | var AST_AsyncArrow = DEFNODE("AsyncArrow", "value", { |
| 673 | $documentation: "An asynchronous arrow function expression", |
| 674 | $propdoc: { |
| 675 | value: "[AST_Node?] simple return expression, or null if using function body.", |
| 676 | }, |
| 677 | walk: function(visitor) { |
| 678 | var node = this; |
| 679 | visitor.visit(node, function() { |
| 680 | node.argnames.forEach(function(argname) { |
| 681 | argname.walk(visitor); |
| 682 | }); |
| 683 | if (node.rest) node.rest.walk(visitor); |
| 684 | if (node.value) { |
| 685 | node.value.walk(visitor); |
| 686 | } else { |
| 687 | walk_body(node, visitor); |
| 688 | } |
| 689 | }); |
| 690 | }, |
| 691 | _validate: function() { |
| 692 | if (this.name != null) throw new Error("name must be null"); |
| 693 | if (this.uses_arguments) throw new Error("uses_arguments must be false"); |
| 694 | if (this.value != null) { |
| 695 | must_be_expression(this, "value"); |
| 696 | if (this.body.length) throw new Error("body must be empty if value exists"); |
| 697 | } |
| 698 | }, |
| 699 | }, AST_LambdaExpression); |
| 700 | |
| 701 | var AST_AsyncFunction = DEFNODE("AsyncFunction", "name", { |
| 702 | $documentation: "An asynchronous function expression", |
| 703 | $propdoc: { |
| 704 | name: "[AST_SymbolLambda?] the name of this function, or null if not specified", |
| 705 | }, |
| 706 | _validate: function() { |
| 707 | if (this.name != null) { |
| 708 | if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); |
| 709 | } |
| 710 | }, |
| 711 | }, AST_LambdaExpression); |
| 712 | |
| 713 | var AST_AsyncGeneratorFunction = DEFNODE("AsyncGeneratorFunction", "name", { |
| 714 | $documentation: "An asynchronous generator function expression", |
| 715 | $propdoc: { |
| 716 | name: "[AST_SymbolLambda?] the name of this function, or null if not specified", |
| 717 | }, |
| 718 | _validate: function() { |
| 719 | if (this.name != null) { |
| 720 | if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); |
| 721 | } |
| 722 | }, |
| 723 | }, AST_LambdaExpression); |
| 724 | |
| 725 | var AST_Function = DEFNODE("Function", "name", { |
| 726 | $documentation: "A function expression", |
| 727 | $propdoc: { |
| 728 | name: "[AST_SymbolLambda?] the name of this function, or null if not specified", |
| 729 | }, |
| 730 | _validate: function() { |
| 731 | if (this.name != null) { |
| 732 | if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); |
| 733 | } |
| 734 | }, |
| 735 | }, AST_LambdaExpression); |
| 736 | |
| 737 | var AST_GeneratorFunction = DEFNODE("GeneratorFunction", "name", { |
| 738 | $documentation: "A generator function expression", |
| 739 | $propdoc: { |
| 740 | name: "[AST_SymbolLambda?] the name of this function, or null if not specified", |
| 741 | }, |
| 742 | _validate: function() { |
| 743 | if (this.name != null) { |
| 744 | if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda"); |
| 745 | } |
| 746 | }, |
| 747 | }, AST_LambdaExpression); |
| 748 | |
| 749 | var AST_LambdaDefinition = DEFNODE("LambdaDefinition", "inlined name", { |
| 750 | $documentation: "Base class for function definitions", |
| 751 | $propdoc: { |
| 752 | inlined: "[boolean/S] whether this function has been inlined", |
| 753 | name: "[AST_SymbolDefun] the name of this function", |
| 754 | }, |
| 755 | _validate: function() { |
| 756 | if (this.TYPE == "LambdaDefinition") throw new Error("should not instantiate AST_LambdaDefinition"); |
| 757 | if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun"); |
| 758 | }, |
| 759 | }, AST_Lambda); |
| 760 | |
| 761 | var AST_AsyncDefun = DEFNODE("AsyncDefun", null, { |
| 762 | $documentation: "An asynchronous function definition", |
| 763 | }, AST_LambdaDefinition); |
| 764 | |
| 765 | var AST_AsyncGeneratorDefun = DEFNODE("AsyncGeneratorDefun", null, { |
| 766 | $documentation: "An asynchronous generator function definition", |
| 767 | }, AST_LambdaDefinition); |
| 768 | |
| 769 | var AST_Defun = DEFNODE("Defun", null, { |
| 770 | $documentation: "A function definition", |
| 771 | }, AST_LambdaDefinition); |
| 772 | |
| 773 | var AST_GeneratorDefun = DEFNODE("GeneratorDefun", null, { |
| 774 | $documentation: "A generator function definition", |
| 775 | }, AST_LambdaDefinition); |
| 776 | |
| 777 | /* -----[ classes ]----- */ |
| 778 | |
| 779 | var AST_Class = DEFNODE("Class", "extends name properties", { |
| 780 | $documentation: "Base class for class literals", |
| 781 | $propdoc: { |
| 782 | extends: "[AST_Node?] the super class, or null if not specified", |
| 783 | properties: "[AST_ClassProperty*] array of class properties", |
| 784 | }, |
| 785 | walk: function(visitor) { |
| 786 | var node = this; |
| 787 | visitor.visit(node, function() { |
| 788 | if (node.name) node.name.walk(visitor); |
| 789 | if (node.extends) node.extends.walk(visitor); |
| 790 | node.properties.forEach(function(prop) { |
| 791 | prop.walk(visitor); |
| 792 | }); |
| 793 | }); |
| 794 | }, |
| 795 | _validate: function() { |
| 796 | if (this.TYPE == "Class") throw new Error("should not instantiate AST_Class"); |
| 797 | if (this.extends != null) must_be_expression(this, "extends"); |
| 798 | this.properties.forEach(function(node) { |
| 799 | if (!(node instanceof AST_ClassProperty)) throw new Error("properties must contain AST_ClassProperty"); |
| 800 | }); |
| 801 | }, |
| 802 | }, AST_BlockScope); |
| 803 | |
| 804 | var AST_DefClass = DEFNODE("DefClass", null, { |
| 805 | $documentation: "A class definition", |
| 806 | $propdoc: { |
| 807 | name: "[AST_SymbolDefClass] the name of this class", |
| 808 | }, |
| 809 | _validate: function() { |
| 810 | if (!(this.name instanceof AST_SymbolDefClass)) throw new Error("name must be AST_SymbolDefClass"); |
| 811 | }, |
| 812 | }, AST_Class); |
| 813 | |
| 814 | var AST_ClassExpression = DEFNODE("ClassExpression", null, { |
| 815 | $documentation: "A class expression", |
| 816 | $propdoc: { |
| 817 | name: "[AST_SymbolClass?] the name of this class, or null if not specified", |
| 818 | }, |
| 819 | _validate: function() { |
| 820 | if (this.name != null) { |
| 821 | if (!(this.name instanceof AST_SymbolClass)) throw new Error("name must be AST_SymbolClass"); |
| 822 | } |
| 823 | }, |
| 824 | }, AST_Class); |
| 825 | |
| 826 | var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", { |
| 827 | $documentation: "Base class for `class` properties", |
| 828 | $propdoc: { |
| 829 | key: "[string|AST_Node] property name (AST_Node for computed property)", |
| 830 | private: "[boolean] whether this is a private property", |
| 831 | static: "[boolean] whether this is a static property", |
| 832 | value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)", |
| 833 | }, |
| 834 | walk: function(visitor) { |
| 835 | var node = this; |
| 836 | visitor.visit(node, function() { |
| 837 | if (node.key instanceof AST_Node) node.key.walk(visitor); |
| 838 | if (node.value) node.value.walk(visitor); |
| 839 | }); |
| 840 | }, |
| 841 | _validate: function() { |
| 842 | if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty"); |
| 843 | if (typeof this.key != "string") { |
| 844 | if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); |
| 845 | must_be_expression(this, "key"); |
| 846 | } |
| 847 | if(this.value != null) { |
| 848 | if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); |
| 849 | } |
| 850 | }, |
| 851 | }); |
| 852 | |
| 853 | var AST_ClassField = DEFNODE("ClassField", null, { |
| 854 | $documentation: "A `class` field", |
| 855 | _validate: function() { |
| 856 | if(this.value != null) must_be_expression(this, "value"); |
| 857 | }, |
| 858 | }, AST_ClassProperty); |
| 859 | |
| 860 | var AST_ClassGetter = DEFNODE("ClassGetter", null, { |
| 861 | $documentation: "A `class` getter", |
| 862 | _validate: function() { |
| 863 | if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); |
| 864 | }, |
| 865 | }, AST_ClassProperty); |
| 866 | |
| 867 | var AST_ClassSetter = DEFNODE("ClassSetter", null, { |
| 868 | $documentation: "A `class` setter", |
| 869 | _validate: function() { |
| 870 | if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); |
| 871 | }, |
| 872 | }, AST_ClassProperty); |
| 873 | |
| 874 | var AST_ClassMethod = DEFNODE("ClassMethod", null, { |
| 875 | $documentation: "A `class` method", |
| 876 | _validate: function() { |
| 877 | if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); |
| 878 | if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow"); |
| 879 | if (this.value.name != null) throw new Error("name of class method's lambda must be null"); |
| 880 | }, |
| 881 | }, AST_ClassProperty); |
| 882 | |
| 883 | /* -----[ JUMPS ]----- */ |
| 884 | |
| 885 | var AST_Jump = DEFNODE("Jump", null, { |
| 886 | $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)", |
| 887 | _validate: function() { |
| 888 | if (this.TYPE == "Jump") throw new Error("should not instantiate AST_Jump"); |
| 889 | }, |
| 890 | }, AST_Statement); |
| 891 | |
| 892 | var AST_Exit = DEFNODE("Exit", "value", { |
| 893 | $documentation: "Base class for “exits” (`return` and `throw`)", |
| 894 | $propdoc: { |
| 895 | value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" |
| 896 | }, |
| 897 | walk: function(visitor) { |
| 898 | var node = this; |
| 899 | visitor.visit(node, function() { |
| 900 | if (node.value) node.value.walk(visitor); |
| 901 | }); |
| 902 | }, |
| 903 | _validate: function() { |
| 904 | if (this.TYPE == "Exit") throw new Error("should not instantiate AST_Exit"); |
| 905 | }, |
| 906 | }, AST_Jump); |
| 907 | |
| 908 | var AST_Return = DEFNODE("Return", null, { |
| 909 | $documentation: "A `return` statement", |
| 910 | _validate: function() { |
| 911 | if (this.value != null) must_be_expression(this, "value"); |
| 912 | }, |
| 913 | }, AST_Exit); |
| 914 | |
| 915 | var AST_Throw = DEFNODE("Throw", null, { |
| 916 | $documentation: "A `throw` statement", |
| 917 | _validate: function() { |
| 918 | must_be_expression(this, "value"); |
| 919 | }, |
| 920 | }, AST_Exit); |
| 921 | |
| 922 | var AST_LoopControl = DEFNODE("LoopControl", "label", { |
| 923 | $documentation: "Base class for loop control statements (`break` and `continue`)", |
| 924 | $propdoc: { |
| 925 | label: "[AST_LabelRef?] the label, or null if none", |
| 926 | }, |
| 927 | walk: function(visitor) { |
| 928 | var node = this; |
| 929 | visitor.visit(node, function() { |
| 930 | if (node.label) node.label.walk(visitor); |
| 931 | }); |
| 932 | }, |
| 933 | _validate: function() { |
| 934 | if (this.TYPE == "LoopControl") throw new Error("should not instantiate AST_LoopControl"); |
| 935 | if (this.label != null) { |
| 936 | if (!(this.label instanceof AST_LabelRef)) throw new Error("label must be AST_LabelRef"); |
| 937 | } |
| 938 | }, |
| 939 | }, AST_Jump); |
| 940 | |
| 941 | var AST_Break = DEFNODE("Break", null, { |
| 942 | $documentation: "A `break` statement" |
| 943 | }, AST_LoopControl); |
| 944 | |
| 945 | var AST_Continue = DEFNODE("Continue", null, { |
| 946 | $documentation: "A `continue` statement" |
| 947 | }, AST_LoopControl); |
| 948 | |
| 949 | /* -----[ IF ]----- */ |
| 950 | |
| 951 | var AST_If = DEFNODE("If", "condition alternative", { |
| 952 | $documentation: "A `if` statement", |
| 953 | $propdoc: { |
| 954 | condition: "[AST_Node] the `if` condition", |
| 955 | alternative: "[AST_Statement?] the `else` part, or null if not present" |
| 956 | }, |
| 957 | walk: function(visitor) { |
| 958 | var node = this; |
| 959 | visitor.visit(node, function() { |
| 960 | node.condition.walk(visitor); |
| 961 | node.body.walk(visitor); |
| 962 | if (node.alternative) node.alternative.walk(visitor); |
| 963 | }); |
| 964 | }, |
| 965 | _validate: function() { |
| 966 | must_be_expression(this, "condition"); |
| 967 | if (this.alternative != null) { |
| 968 | if (!is_statement(this.alternative)) throw new Error("alternative must be AST_Statement"); |
| 969 | } |
| 970 | }, |
| 971 | }, AST_StatementWithBody); |
| 972 | |
| 973 | /* -----[ SWITCH ]----- */ |
| 974 | |
| 975 | var AST_Switch = DEFNODE("Switch", "expression", { |
| 976 | $documentation: "A `switch` statement", |
| 977 | $propdoc: { |
| 978 | expression: "[AST_Node] the `switch` “discriminant”" |
| 979 | }, |
| 980 | walk: function(visitor) { |
| 981 | var node = this; |
| 982 | visitor.visit(node, function() { |
| 983 | node.expression.walk(visitor); |
| 984 | walk_body(node, visitor); |
| 985 | }); |
| 986 | }, |
| 987 | _validate: function() { |
| 988 | must_be_expression(this, "expression"); |
| 989 | this.body.forEach(function(node) { |
| 990 | if (!(node instanceof AST_SwitchBranch)) throw new Error("body must be AST_SwitchBranch[]"); |
| 991 | }); |
| 992 | }, |
| 993 | }, AST_Block); |
| 994 | |
| 995 | var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { |
| 996 | $documentation: "Base class for `switch` branches", |
| 997 | _validate: function() { |
| 998 | if (this.TYPE == "SwitchBranch") throw new Error("should not instantiate AST_SwitchBranch"); |
| 999 | }, |
| 1000 | }, AST_Block); |
| 1001 | |
| 1002 | var AST_Default = DEFNODE("Default", null, { |
| 1003 | $documentation: "A `default` switch branch", |
| 1004 | }, AST_SwitchBranch); |
| 1005 | |
| 1006 | var AST_Case = DEFNODE("Case", "expression", { |
| 1007 | $documentation: "A `case` switch branch", |
| 1008 | $propdoc: { |
| 1009 | expression: "[AST_Node] the `case` expression" |
| 1010 | }, |
| 1011 | walk: function(visitor) { |
| 1012 | var node = this; |
| 1013 | visitor.visit(node, function() { |
| 1014 | node.expression.walk(visitor); |
| 1015 | walk_body(node, visitor); |
| 1016 | }); |
| 1017 | }, |
| 1018 | _validate: function() { |
| 1019 | must_be_expression(this, "expression"); |
| 1020 | }, |
| 1021 | }, AST_SwitchBranch); |
| 1022 | |
| 1023 | /* -----[ EXCEPTIONS ]----- */ |
| 1024 | |
| 1025 | var AST_Try = DEFNODE("Try", "bcatch bfinally", { |
| 1026 | $documentation: "A `try` statement", |
| 1027 | $propdoc: { |
| 1028 | bcatch: "[AST_Catch?] the catch block, or null if not present", |
| 1029 | bfinally: "[AST_Finally?] the finally block, or null if not present" |
| 1030 | }, |
| 1031 | walk: function(visitor) { |
| 1032 | var node = this; |
| 1033 | visitor.visit(node, function() { |
| 1034 | walk_body(node, visitor); |
| 1035 | if (node.bcatch) node.bcatch.walk(visitor); |
| 1036 | if (node.bfinally) node.bfinally.walk(visitor); |
| 1037 | }); |
| 1038 | }, |
| 1039 | _validate: function() { |
| 1040 | if (this.bcatch != null) { |
| 1041 | if (!(this.bcatch instanceof AST_Catch)) throw new Error("bcatch must be AST_Catch"); |
| 1042 | } |
| 1043 | if (this.bfinally != null) { |
| 1044 | if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally"); |
| 1045 | } |
| 1046 | }, |
| 1047 | }, AST_Block); |
| 1048 | |
| 1049 | var AST_Catch = DEFNODE("Catch", "argname", { |
| 1050 | $documentation: "A `catch` node; only makes sense as part of a `try` statement", |
| 1051 | $propdoc: { |
| 1052 | argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present", |
| 1053 | }, |
| 1054 | walk: function(visitor) { |
| 1055 | var node = this; |
| 1056 | visitor.visit(node, function() { |
| 1057 | if (node.argname) node.argname.walk(visitor); |
| 1058 | walk_body(node, visitor); |
| 1059 | }); |
| 1060 | }, |
| 1061 | _validate: function() { |
| 1062 | if (this.argname != null) validate_destructured(this.argname, function(node) { |
| 1063 | if (!(node instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch"); |
| 1064 | }); |
| 1065 | }, |
| 1066 | }, AST_Block); |
| 1067 | |
| 1068 | var AST_Finally = DEFNODE("Finally", null, { |
| 1069 | $documentation: "A `finally` node; only makes sense as part of a `try` statement" |
| 1070 | }, AST_Block); |
| 1071 | |
| 1072 | /* -----[ VAR ]----- */ |
| 1073 | |
| 1074 | var AST_Definitions = DEFNODE("Definitions", "definitions", { |
| 1075 | $documentation: "Base class for `var` nodes (variable declarations/initializations)", |
| 1076 | $propdoc: { |
| 1077 | definitions: "[AST_VarDef*] array of variable definitions" |
| 1078 | }, |
| 1079 | walk: function(visitor) { |
| 1080 | var node = this; |
| 1081 | visitor.visit(node, function() { |
| 1082 | node.definitions.forEach(function(defn) { |
| 1083 | defn.walk(visitor); |
| 1084 | }); |
| 1085 | }); |
| 1086 | }, |
| 1087 | _validate: function() { |
| 1088 | if (this.TYPE == "Definitions") throw new Error("should not instantiate AST_Definitions"); |
| 1089 | if (this.definitions.length < 1) throw new Error("must have at least one definition"); |
| 1090 | }, |
| 1091 | }, AST_Statement); |
| 1092 | |
| 1093 | var AST_Const = DEFNODE("Const", null, { |
| 1094 | $documentation: "A `const` statement", |
| 1095 | _validate: function() { |
| 1096 | this.definitions.forEach(function(node) { |
| 1097 | if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); |
| 1098 | validate_destructured(node.name, function(node) { |
| 1099 | if (!(node instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst"); |
| 1100 | }); |
| 1101 | }); |
| 1102 | }, |
| 1103 | }, AST_Definitions); |
| 1104 | |
| 1105 | var AST_Let = DEFNODE("Let", null, { |
| 1106 | $documentation: "A `let` statement", |
| 1107 | _validate: function() { |
| 1108 | this.definitions.forEach(function(node) { |
| 1109 | if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); |
| 1110 | validate_destructured(node.name, function(node) { |
| 1111 | if (!(node instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet"); |
| 1112 | }); |
| 1113 | }); |
| 1114 | }, |
| 1115 | }, AST_Definitions); |
| 1116 | |
| 1117 | var AST_Var = DEFNODE("Var", null, { |
| 1118 | $documentation: "A `var` statement", |
| 1119 | _validate: function() { |
| 1120 | this.definitions.forEach(function(node) { |
| 1121 | if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); |
| 1122 | validate_destructured(node.name, function(node) { |
| 1123 | if (!(node instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar"); |
| 1124 | }); |
| 1125 | }); |
| 1126 | }, |
| 1127 | }, AST_Definitions); |
| 1128 | |
| 1129 | var AST_VarDef = DEFNODE("VarDef", "name value", { |
| 1130 | $documentation: "A variable declaration; only appears in a AST_Definitions node", |
| 1131 | $propdoc: { |
| 1132 | name: "[AST_Destructured|AST_SymbolVar] name of the variable", |
| 1133 | value: "[AST_Node?] initializer, or null of there's no initializer", |
| 1134 | }, |
| 1135 | walk: function(visitor) { |
| 1136 | var node = this; |
| 1137 | visitor.visit(node, function() { |
| 1138 | node.name.walk(visitor); |
| 1139 | if (node.value) node.value.walk(visitor); |
| 1140 | }); |
| 1141 | }, |
| 1142 | _validate: function() { |
| 1143 | if (this.value != null) must_be_expression(this, "value"); |
| 1144 | }, |
| 1145 | }); |
| 1146 | |
| 1147 | /* -----[ OTHER ]----- */ |
| 1148 | |
| 1149 | var AST_ExportDeclaration = DEFNODE("ExportDeclaration", "body", { |
| 1150 | $documentation: "An `export` statement", |
| 1151 | $propdoc: { |
| 1152 | body: "[AST_DefClass|AST_Definitions|AST_LambdaDefinition] the statement to export", |
| 1153 | }, |
| 1154 | walk: function(visitor) { |
| 1155 | var node = this; |
| 1156 | visitor.visit(node, function() { |
| 1157 | node.body.walk(visitor); |
| 1158 | }); |
| 1159 | }, |
| 1160 | _validate: function() { |
| 1161 | if (!(this.body instanceof AST_DefClass |
| 1162 | || this.body instanceof AST_Definitions |
| 1163 | || this.body instanceof AST_LambdaDefinition)) { |
| 1164 | throw new Error("body must be AST_DefClass, AST_Definitions or AST_LambdaDefinition"); |
| 1165 | } |
| 1166 | }, |
| 1167 | }, AST_Statement); |
| 1168 | |
| 1169 | var AST_ExportDefault = DEFNODE("ExportDefault", "body", { |
| 1170 | $documentation: "An `export default` statement", |
| 1171 | $propdoc: { |
| 1172 | body: "[AST_Node] the default export", |
| 1173 | }, |
| 1174 | walk: function(visitor) { |
| 1175 | var node = this; |
| 1176 | visitor.visit(node, function() { |
| 1177 | node.body.walk(visitor); |
| 1178 | }); |
| 1179 | }, |
| 1180 | _validate: function() { |
| 1181 | if (!(this.body instanceof AST_DefClass || this.body instanceof AST_LambdaDefinition)) { |
| 1182 | must_be_expression(this, "body"); |
| 1183 | } |
| 1184 | }, |
| 1185 | }, AST_Statement); |
| 1186 | |
| 1187 | var AST_ExportForeign = DEFNODE("ExportForeign", "aliases keys path quote", { |
| 1188 | $documentation: "An `export ... from '...'` statement", |
| 1189 | $propdoc: { |
| 1190 | aliases: "[string*] array of aliases to export", |
| 1191 | keys: "[string*] array of keys to import", |
| 1192 | path: "[string] the path to import module", |
| 1193 | quote: "[string?] the original quote character", |
| 1194 | }, |
| 1195 | _validate: function() { |
| 1196 | if (this.aliases.length != this.keys.length) { |
| 1197 | throw new Error("aliases:key length mismatch: " + this.aliases.length + " != " + this.keys.length); |
| 1198 | } |
| 1199 | this.aliases.forEach(function(name) { |
| 1200 | if (typeof name != "string") throw new Error("aliases must contain string"); |
| 1201 | }); |
| 1202 | this.keys.forEach(function(name) { |
| 1203 | if (typeof name != "string") throw new Error("keys must contain string"); |
| 1204 | }); |
| 1205 | if (typeof this.path != "string") throw new Error("path must be string"); |
| 1206 | if (this.quote != null) { |
| 1207 | if (typeof this.quote != "string") throw new Error("quote must be string"); |
| 1208 | if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); |
| 1209 | } |
| 1210 | }, |
| 1211 | }, AST_Statement); |
| 1212 | |
| 1213 | var AST_ExportReferences = DEFNODE("ExportReferences", "properties", { |
| 1214 | $documentation: "An `export { ... }` statement", |
| 1215 | $propdoc: { |
| 1216 | properties: "[AST_SymbolExport*] array of aliases to export", |
| 1217 | }, |
| 1218 | walk: function(visitor) { |
| 1219 | var node = this; |
| 1220 | visitor.visit(node, function() { |
| 1221 | node.properties.forEach(function(prop) { |
| 1222 | prop.walk(visitor); |
| 1223 | }); |
| 1224 | }); |
| 1225 | }, |
| 1226 | _validate: function() { |
| 1227 | this.properties.forEach(function(prop) { |
| 1228 | if (!(prop instanceof AST_SymbolExport)) throw new Error("properties must contain AST_SymbolExport"); |
| 1229 | }); |
| 1230 | }, |
| 1231 | }, AST_Statement); |
| 1232 | |
| 1233 | var AST_Import = DEFNODE("Import", "all default path properties quote", { |
| 1234 | $documentation: "An `import` statement", |
| 1235 | $propdoc: { |
| 1236 | all: "[AST_SymbolImport?] the imported namespace, or null if not specified", |
| 1237 | default: "[AST_SymbolImport?] the alias for default `export`, or null if not specified", |
| 1238 | path: "[string] the path to import module", |
| 1239 | properties: "[(AST_SymbolImport*)?] array of aliases, or null if not specified", |
| 1240 | quote: "[string?] the original quote character", |
| 1241 | }, |
| 1242 | walk: function(visitor) { |
| 1243 | var node = this; |
| 1244 | visitor.visit(node, function() { |
| 1245 | if (node.all) node.all.walk(visitor); |
| 1246 | if (node.default) node.default.walk(visitor); |
| 1247 | if (node.properties) node.properties.forEach(function(prop) { |
| 1248 | prop.walk(visitor); |
| 1249 | }); |
| 1250 | }); |
| 1251 | }, |
| 1252 | _validate: function() { |
| 1253 | if (this.all != null) { |
| 1254 | if (!(this.all instanceof AST_SymbolImport)) throw new Error("all must be AST_SymbolImport"); |
| 1255 | if (this.properties != null) throw new Error("cannot import both * and {} in the same statement"); |
| 1256 | } |
| 1257 | if (this.default != null) { |
| 1258 | if (!(this.default instanceof AST_SymbolImport)) throw new Error("default must be AST_SymbolImport"); |
| 1259 | if (this.default.key !== "") throw new Error("invalid default key: " + this.default.key); |
| 1260 | } |
| 1261 | if (typeof this.path != "string") throw new Error("path must be string"); |
| 1262 | if (this.properties != null) this.properties.forEach(function(node) { |
| 1263 | if (!(node instanceof AST_SymbolImport)) throw new Error("properties must contain AST_SymbolImport"); |
| 1264 | }); |
| 1265 | if (this.quote != null) { |
| 1266 | if (typeof this.quote != "string") throw new Error("quote must be string"); |
| 1267 | if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); |
| 1268 | } |
| 1269 | }, |
| 1270 | }, AST_Statement); |
| 1271 | |
| 1272 | var AST_DefaultValue = DEFNODE("DefaultValue", "name value", { |
| 1273 | $documentation: "A default value declaration", |
| 1274 | $propdoc: { |
| 1275 | name: "[AST_Destructured|AST_SymbolDeclaration] name of the variable", |
| 1276 | value: "[AST_Node] value to assign if variable is `undefined`", |
| 1277 | }, |
| 1278 | walk: function(visitor) { |
| 1279 | var node = this; |
| 1280 | visitor.visit(node, function() { |
| 1281 | node.name.walk(visitor); |
| 1282 | node.value.walk(visitor); |
| 1283 | }); |
| 1284 | }, |
| 1285 | _validate: function() { |
| 1286 | must_be_expression(this, "value"); |
| 1287 | }, |
| 1288 | }); |
| 1289 | |
| 1290 | function must_be_expressions(node, prop, allow_spread, allow_hole) { |
| 1291 | node[prop].forEach(function(node) { |
| 1292 | validate_expression(node, prop, true, allow_spread, allow_hole); |
| 1293 | }); |
| 1294 | } |
| 1295 | |
| 1296 | var AST_Call = DEFNODE("Call", "args expression optional pure", { |
| 1297 | $documentation: "A function call expression", |
| 1298 | $propdoc: { |
| 1299 | args: "[AST_Node*] array of arguments", |
| 1300 | expression: "[AST_Node] expression to invoke as function", |
| 1301 | optional: "[boolean] whether the expression is optional chaining", |
| 1302 | pure: "[string/S] marker for side-effect-free call expression", |
| 1303 | }, |
| 1304 | walk: function(visitor) { |
| 1305 | var node = this; |
| 1306 | visitor.visit(node, function() { |
| 1307 | node.expression.walk(visitor); |
| 1308 | node.args.forEach(function(arg) { |
| 1309 | arg.walk(visitor); |
| 1310 | }); |
| 1311 | }); |
| 1312 | }, |
| 1313 | _validate: function() { |
| 1314 | must_be_expression(this, "expression"); |
| 1315 | must_be_expressions(this, "args", true); |
| 1316 | }, |
| 1317 | }); |
| 1318 | |
| 1319 | var AST_New = DEFNODE("New", null, { |
| 1320 | $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties", |
| 1321 | _validate: function() { |
| 1322 | if (this.optional) throw new Error("optional must be false"); |
| 1323 | }, |
| 1324 | }, AST_Call); |
| 1325 | |
| 1326 | var AST_Sequence = DEFNODE("Sequence", "expressions", { |
| 1327 | $documentation: "A sequence expression (comma-separated expressions)", |
| 1328 | $propdoc: { |
| 1329 | expressions: "[AST_Node*] array of expressions (at least two)" |
| 1330 | }, |
| 1331 | walk: function(visitor) { |
| 1332 | var node = this; |
| 1333 | visitor.visit(node, function() { |
| 1334 | node.expressions.forEach(function(expr) { |
| 1335 | expr.walk(visitor); |
| 1336 | }); |
| 1337 | }); |
| 1338 | }, |
| 1339 | _validate: function() { |
| 1340 | if (this.expressions.length < 2) throw new Error("expressions must contain multiple elements"); |
| 1341 | must_be_expressions(this, "expressions"); |
| 1342 | }, |
| 1343 | }); |
| 1344 | |
| 1345 | var AST_PropAccess = DEFNODE("PropAccess", "expression optional property", { |
| 1346 | $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", |
| 1347 | $propdoc: { |
| 1348 | expression: "[AST_Node] the “container” expression", |
| 1349 | optional: "[boolean] whether the expression is optional chaining", |
| 1350 | property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node", |
| 1351 | }, |
| 1352 | getProperty: function() { |
| 1353 | var p = this.property; |
| 1354 | if (p instanceof AST_Constant) { |
| 1355 | return p.value; |
| 1356 | } |
| 1357 | if (p instanceof AST_UnaryPrefix |
| 1358 | && p.operator == "void" |
| 1359 | && p.expression instanceof AST_Constant) { |
| 1360 | return; |
| 1361 | } |
| 1362 | return p; |
| 1363 | }, |
| 1364 | _validate: function() { |
| 1365 | if (this.TYPE == "PropAccess") throw new Error("should not instantiate AST_PropAccess"); |
| 1366 | must_be_expression(this, "expression"); |
| 1367 | }, |
| 1368 | }); |
| 1369 | |
| 1370 | var AST_Dot = DEFNODE("Dot", null, { |
| 1371 | $documentation: "A dotted property access expression", |
| 1372 | walk: function(visitor) { |
| 1373 | var node = this; |
| 1374 | visitor.visit(node, function() { |
| 1375 | node.expression.walk(visitor); |
| 1376 | }); |
| 1377 | }, |
| 1378 | _validate: function() { |
| 1379 | if (typeof this.property != "string") throw new Error("property must be string"); |
| 1380 | }, |
| 1381 | }, AST_PropAccess); |
| 1382 | |
| 1383 | var AST_Sub = DEFNODE("Sub", null, { |
| 1384 | $documentation: "Index-style property access, i.e. `a[\"foo\"]`", |
| 1385 | walk: function(visitor) { |
| 1386 | var node = this; |
| 1387 | visitor.visit(node, function() { |
| 1388 | node.expression.walk(visitor); |
| 1389 | node.property.walk(visitor); |
| 1390 | }); |
| 1391 | }, |
| 1392 | _validate: function() { |
| 1393 | must_be_expression(this, "property"); |
| 1394 | }, |
| 1395 | }, AST_PropAccess); |
| 1396 | |
| 1397 | var AST_Spread = DEFNODE("Spread", "expression", { |
| 1398 | $documentation: "Spread expression in array/object literals or function calls", |
| 1399 | $propdoc: { |
| 1400 | expression: "[AST_Node] expression to be expanded", |
| 1401 | }, |
| 1402 | walk: function(visitor) { |
| 1403 | var node = this; |
| 1404 | visitor.visit(node, function() { |
| 1405 | node.expression.walk(visitor); |
| 1406 | }); |
| 1407 | }, |
| 1408 | _validate: function() { |
| 1409 | must_be_expression(this, "expression"); |
| 1410 | }, |
| 1411 | }); |
| 1412 | |
| 1413 | var AST_Unary = DEFNODE("Unary", "operator expression", { |
| 1414 | $documentation: "Base class for unary expressions", |
| 1415 | $propdoc: { |
| 1416 | operator: "[string] the operator", |
| 1417 | expression: "[AST_Node] expression that this unary operator applies to" |
| 1418 | }, |
| 1419 | walk: function(visitor) { |
| 1420 | var node = this; |
| 1421 | visitor.visit(node, function() { |
| 1422 | node.expression.walk(visitor); |
| 1423 | }); |
| 1424 | }, |
| 1425 | _validate: function() { |
| 1426 | if (this.TYPE == "Unary") throw new Error("should not instantiate AST_Unary"); |
| 1427 | if (typeof this.operator != "string") throw new Error("operator must be string"); |
| 1428 | must_be_expression(this, "expression"); |
| 1429 | }, |
| 1430 | }); |
| 1431 | |
| 1432 | var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { |
| 1433 | $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" |
| 1434 | }, AST_Unary); |
| 1435 | |
| 1436 | var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { |
| 1437 | $documentation: "Unary postfix expression, i.e. `i++`" |
| 1438 | }, AST_Unary); |
| 1439 | |
| 1440 | var AST_Binary = DEFNODE("Binary", "operator left right", { |
| 1441 | $documentation: "Binary expression, i.e. `a + b`", |
| 1442 | $propdoc: { |
| 1443 | left: "[AST_Node] left-hand side expression", |
| 1444 | operator: "[string] the operator", |
| 1445 | right: "[AST_Node] right-hand side expression" |
| 1446 | }, |
| 1447 | walk: function(visitor) { |
| 1448 | var node = this; |
| 1449 | visitor.visit(node, function() { |
| 1450 | node.left.walk(visitor); |
| 1451 | node.right.walk(visitor); |
| 1452 | }); |
| 1453 | }, |
| 1454 | _validate: function() { |
| 1455 | if (!(this instanceof AST_Assign)) must_be_expression(this, "left"); |
| 1456 | if (typeof this.operator != "string") throw new Error("operator must be string"); |
| 1457 | must_be_expression(this, "right"); |
| 1458 | }, |
| 1459 | }); |
| 1460 | |
| 1461 | var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { |
| 1462 | $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", |
| 1463 | $propdoc: { |
| 1464 | condition: "[AST_Node]", |
| 1465 | consequent: "[AST_Node]", |
| 1466 | alternative: "[AST_Node]" |
| 1467 | }, |
| 1468 | walk: function(visitor) { |
| 1469 | var node = this; |
| 1470 | visitor.visit(node, function() { |
| 1471 | node.condition.walk(visitor); |
| 1472 | node.consequent.walk(visitor); |
| 1473 | node.alternative.walk(visitor); |
| 1474 | }); |
| 1475 | }, |
| 1476 | _validate: function() { |
| 1477 | must_be_expression(this, "condition"); |
| 1478 | must_be_expression(this, "consequent"); |
| 1479 | must_be_expression(this, "alternative"); |
| 1480 | }, |
| 1481 | }); |
| 1482 | |
| 1483 | var AST_Assign = DEFNODE("Assign", null, { |
| 1484 | $documentation: "An assignment expression — `a = b + 5`", |
| 1485 | _validate: function() { |
| 1486 | if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="'); |
| 1487 | if (this.left instanceof AST_Destructured) { |
| 1488 | if (this.operator != "=") throw new Error("invalid destructuring operator: " + this.operator); |
| 1489 | validate_destructured(this.left, function(node) { |
| 1490 | if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) { |
| 1491 | throw new Error("left must be assignable: " + node.TYPE); |
| 1492 | } |
| 1493 | }); |
| 1494 | } |
| 1495 | }, |
| 1496 | }, AST_Binary); |
| 1497 | |
| 1498 | var AST_Await = DEFNODE("Await", "expression", { |
| 1499 | $documentation: "An await expression", |
| 1500 | $propdoc: { |
| 1501 | expression: "[AST_Node] expression with Promise to resolve on", |
| 1502 | }, |
| 1503 | walk: function(visitor) { |
| 1504 | var node = this; |
| 1505 | visitor.visit(node, function() { |
| 1506 | node.expression.walk(visitor); |
| 1507 | }); |
| 1508 | }, |
| 1509 | _validate: function() { |
| 1510 | must_be_expression(this, "expression"); |
| 1511 | }, |
| 1512 | }); |
| 1513 | |
| 1514 | var AST_Yield = DEFNODE("Yield", "expression nested", { |
| 1515 | $documentation: "A yield expression", |
| 1516 | $propdoc: { |
| 1517 | expression: "[AST_Node?] return value for iterator, or null if undefined", |
| 1518 | nested: "[boolean] whether to iterate over expression as generator", |
| 1519 | }, |
| 1520 | walk: function(visitor) { |
| 1521 | var node = this; |
| 1522 | visitor.visit(node, function() { |
| 1523 | if (node.expression) node.expression.walk(visitor); |
| 1524 | }); |
| 1525 | }, |
| 1526 | _validate: function() { |
| 1527 | if (this.expression != null) { |
| 1528 | must_be_expression(this, "expression"); |
| 1529 | } else if (this.nested) { |
| 1530 | throw new Error("yield* must contain expression"); |
| 1531 | } |
| 1532 | }, |
| 1533 | }); |
| 1534 | |
| 1535 | /* -----[ LITERALS ]----- */ |
| 1536 | |
| 1537 | var AST_Array = DEFNODE("Array", "elements", { |
| 1538 | $documentation: "An array literal", |
| 1539 | $propdoc: { |
| 1540 | elements: "[AST_Node*] array of elements" |
| 1541 | }, |
| 1542 | walk: function(visitor) { |
| 1543 | var node = this; |
| 1544 | visitor.visit(node, function() { |
| 1545 | node.elements.forEach(function(element) { |
| 1546 | element.walk(visitor); |
| 1547 | }); |
| 1548 | }); |
| 1549 | }, |
| 1550 | _validate: function() { |
| 1551 | must_be_expressions(this, "elements", true, true); |
| 1552 | }, |
| 1553 | }); |
| 1554 | |
| 1555 | var AST_Destructured = DEFNODE("Destructured", "rest", { |
| 1556 | $documentation: "Base class for destructured literal", |
| 1557 | $propdoc: { |
| 1558 | rest: "[(AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)?] rest parameter, or null if absent", |
| 1559 | }, |
| 1560 | _validate: function() { |
| 1561 | if (this.TYPE == "Destructured") throw new Error("should not instantiate AST_Destructured"); |
| 1562 | }, |
| 1563 | }); |
| 1564 | |
| 1565 | function validate_destructured(node, check, allow_default) { |
| 1566 | if (node instanceof AST_DefaultValue && allow_default) return validate_destructured(node.name, check); |
| 1567 | if (node instanceof AST_Destructured) { |
| 1568 | if (node.rest != null) validate_destructured(node.rest, check); |
| 1569 | if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) { |
| 1570 | if (!(node instanceof AST_Hole)) validate_destructured(node, check, true); |
| 1571 | }); |
| 1572 | if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) { |
| 1573 | validate_destructured(prop.value, check, true); |
| 1574 | }); |
| 1575 | } |
| 1576 | check(node); |
| 1577 | } |
| 1578 | |
| 1579 | var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", { |
| 1580 | $documentation: "A destructured array literal", |
| 1581 | $propdoc: { |
| 1582 | elements: "[(AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef)*] array of elements", |
| 1583 | }, |
| 1584 | walk: function(visitor) { |
| 1585 | var node = this; |
| 1586 | visitor.visit(node, function() { |
| 1587 | node.elements.forEach(function(element) { |
| 1588 | element.walk(visitor); |
| 1589 | }); |
| 1590 | if (node.rest) node.rest.walk(visitor); |
| 1591 | }); |
| 1592 | }, |
| 1593 | }, AST_Destructured); |
| 1594 | |
| 1595 | var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", { |
| 1596 | $documentation: "A key: value destructured property", |
| 1597 | $propdoc: { |
| 1598 | key: "[string|AST_Node] property name. For computed property this is an AST_Node.", |
| 1599 | value: "[AST_DefaultValue|AST_Destructured|AST_SymbolDeclaration|AST_SymbolRef] property value", |
| 1600 | }, |
| 1601 | walk: function(visitor) { |
| 1602 | var node = this; |
| 1603 | visitor.visit(node, function() { |
| 1604 | if (node.key instanceof AST_Node) node.key.walk(visitor); |
| 1605 | node.value.walk(visitor); |
| 1606 | }); |
| 1607 | }, |
| 1608 | _validate: function() { |
| 1609 | if (typeof this.key != "string") { |
| 1610 | if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); |
| 1611 | must_be_expression(this, "key"); |
| 1612 | } |
| 1613 | if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); |
| 1614 | }, |
| 1615 | }); |
| 1616 | |
| 1617 | var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", { |
| 1618 | $documentation: "A destructured object literal", |
| 1619 | $propdoc: { |
| 1620 | properties: "[AST_DestructuredKeyVal*] array of properties", |
| 1621 | }, |
| 1622 | walk: function(visitor) { |
| 1623 | var node = this; |
| 1624 | visitor.visit(node, function() { |
| 1625 | node.properties.forEach(function(prop) { |
| 1626 | prop.walk(visitor); |
| 1627 | }); |
| 1628 | if (node.rest) node.rest.walk(visitor); |
| 1629 | }); |
| 1630 | }, |
| 1631 | _validate: function() { |
| 1632 | this.properties.forEach(function(node) { |
| 1633 | if (!(node instanceof AST_DestructuredKeyVal)) throw new Error("properties must be AST_DestructuredKeyVal[]"); |
| 1634 | }); |
| 1635 | }, |
| 1636 | }, AST_Destructured); |
| 1637 | |
| 1638 | var AST_Object = DEFNODE("Object", "properties", { |
| 1639 | $documentation: "An object literal", |
| 1640 | $propdoc: { |
| 1641 | properties: "[(AST_ObjectProperty|AST_Spread)*] array of properties" |
| 1642 | }, |
| 1643 | walk: function(visitor) { |
| 1644 | var node = this; |
| 1645 | visitor.visit(node, function() { |
| 1646 | node.properties.forEach(function(prop) { |
| 1647 | prop.walk(visitor); |
| 1648 | }); |
| 1649 | }); |
| 1650 | }, |
| 1651 | _validate: function() { |
| 1652 | this.properties.forEach(function(node) { |
| 1653 | if (!(node instanceof AST_ObjectProperty || node instanceof AST_Spread)) { |
| 1654 | throw new Error("properties must contain AST_ObjectProperty and/or AST_Spread only"); |
| 1655 | } |
| 1656 | }); |
| 1657 | }, |
| 1658 | }); |
| 1659 | |
| 1660 | var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { |
| 1661 | $documentation: "Base class for literal object properties", |
| 1662 | $propdoc: { |
| 1663 | key: "[string|AST_Node] property name. For computed property this is an AST_Node.", |
| 1664 | value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.", |
| 1665 | }, |
| 1666 | walk: function(visitor) { |
| 1667 | var node = this; |
| 1668 | visitor.visit(node, function() { |
| 1669 | if (node.key instanceof AST_Node) node.key.walk(visitor); |
| 1670 | node.value.walk(visitor); |
| 1671 | }); |
| 1672 | }, |
| 1673 | _validate: function() { |
| 1674 | if (this.TYPE == "ObjectProperty") throw new Error("should not instantiate AST_ObjectProperty"); |
| 1675 | if (typeof this.key != "string") { |
| 1676 | if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node"); |
| 1677 | must_be_expression(this, "key"); |
| 1678 | } |
| 1679 | if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node"); |
| 1680 | }, |
| 1681 | }); |
| 1682 | |
| 1683 | var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, { |
| 1684 | $documentation: "A key: value object property", |
| 1685 | _validate: function() { |
| 1686 | must_be_expression(this, "value"); |
| 1687 | }, |
| 1688 | }, AST_ObjectProperty); |
| 1689 | |
| 1690 | var AST_ObjectMethod = DEFNODE("ObjectMethod", null, { |
| 1691 | $documentation: "A key(){} object property", |
| 1692 | _validate: function() { |
| 1693 | if (!(this.value instanceof AST_LambdaExpression)) throw new Error("value must be AST_LambdaExpression"); |
| 1694 | if (is_arrow(this.value)) throw new Error("value cannot be AST_Arrow or AST_AsyncArrow"); |
| 1695 | if (this.value.name != null) throw new Error("name of object method's lambda must be null"); |
| 1696 | }, |
| 1697 | }, AST_ObjectKeyVal); |
| 1698 | |
| 1699 | var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { |
| 1700 | $documentation: "An object setter property", |
| 1701 | _validate: function() { |
| 1702 | if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); |
| 1703 | }, |
| 1704 | }, AST_ObjectProperty); |
| 1705 | |
| 1706 | var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { |
| 1707 | $documentation: "An object getter property", |
| 1708 | _validate: function() { |
| 1709 | if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor"); |
| 1710 | }, |
| 1711 | }, AST_ObjectProperty); |
| 1712 | |
| 1713 | var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { |
| 1714 | $documentation: "Base class for all symbols", |
| 1715 | $propdoc: { |
| 1716 | name: "[string] name of this symbol", |
| 1717 | scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", |
| 1718 | thedef: "[SymbolDef/S] the definition of this symbol" |
| 1719 | }, |
| 1720 | _validate: function() { |
| 1721 | if (this.TYPE == "Symbol") throw new Error("should not instantiate AST_Symbol"); |
| 1722 | if (typeof this.name != "string") throw new Error("name must be string"); |
| 1723 | }, |
| 1724 | }); |
| 1725 | |
| 1726 | var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { |
| 1727 | $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", |
| 1728 | }, AST_Symbol); |
| 1729 | |
| 1730 | var AST_SymbolConst = DEFNODE("SymbolConst", null, { |
| 1731 | $documentation: "Symbol defining a constant", |
| 1732 | }, AST_SymbolDeclaration); |
| 1733 | |
| 1734 | var AST_SymbolImport = DEFNODE("SymbolImport", "key", { |
| 1735 | $documentation: "Symbol defined by an `import` statement", |
| 1736 | $propdoc: { |
| 1737 | key: "[string] the original `export` name", |
| 1738 | }, |
| 1739 | _validate: function() { |
| 1740 | if (typeof this.key != "string") throw new Error("key must be string"); |
| 1741 | }, |
| 1742 | }, AST_SymbolConst); |
| 1743 | |
| 1744 | var AST_SymbolLet = DEFNODE("SymbolLet", null, { |
| 1745 | $documentation: "Symbol defining a lexical-scoped variable", |
| 1746 | }, AST_SymbolDeclaration); |
| 1747 | |
| 1748 | var AST_SymbolVar = DEFNODE("SymbolVar", null, { |
| 1749 | $documentation: "Symbol defining a variable", |
| 1750 | }, AST_SymbolDeclaration); |
| 1751 | |
| 1752 | var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { |
| 1753 | $documentation: "Symbol naming a function argument", |
| 1754 | }, AST_SymbolVar); |
| 1755 | |
| 1756 | var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { |
| 1757 | $documentation: "Symbol defining a function", |
| 1758 | }, AST_SymbolDeclaration); |
| 1759 | |
| 1760 | var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { |
| 1761 | $documentation: "Symbol naming a function expression", |
| 1762 | }, AST_SymbolDeclaration); |
| 1763 | |
| 1764 | var AST_SymbolDefClass = DEFNODE("SymbolDefClass", null, { |
| 1765 | $documentation: "Symbol defining a class", |
| 1766 | }, AST_SymbolLet); |
| 1767 | |
| 1768 | var AST_SymbolClass = DEFNODE("SymbolClass", null, { |
| 1769 | $documentation: "Symbol naming a class expression", |
| 1770 | }, AST_SymbolLet); |
| 1771 | |
| 1772 | var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { |
| 1773 | $documentation: "Symbol naming the exception in catch", |
| 1774 | }, AST_SymbolDeclaration); |
| 1775 | |
| 1776 | var AST_Label = DEFNODE("Label", "references", { |
| 1777 | $documentation: "Symbol naming a label (declaration)", |
| 1778 | $propdoc: { |
| 1779 | references: "[AST_LoopControl*] a list of nodes referring to this label" |
| 1780 | }, |
| 1781 | initialize: function() { |
| 1782 | this.references = []; |
| 1783 | this.thedef = this; |
| 1784 | } |
| 1785 | }, AST_Symbol); |
| 1786 | |
| 1787 | var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg redef", { |
| 1788 | $documentation: "Reference to some symbol (not definition/declaration)", |
| 1789 | }, AST_Symbol); |
| 1790 | |
| 1791 | var AST_SymbolExport = DEFNODE("SymbolExport", "alias", { |
| 1792 | $documentation: "Reference in an `export` statement", |
| 1793 | $propdoc: { |
| 1794 | alias: "[string] the `export` alias", |
| 1795 | }, |
| 1796 | _validate: function() { |
| 1797 | if (typeof this.alias != "string") throw new Error("alias must be string"); |
| 1798 | }, |
| 1799 | }, AST_SymbolRef); |
| 1800 | |
| 1801 | var AST_LabelRef = DEFNODE("LabelRef", null, { |
| 1802 | $documentation: "Reference to a label symbol", |
| 1803 | }, AST_Symbol); |
| 1804 | |
| 1805 | var AST_ObjectIdentity = DEFNODE("ObjectIdentity", null, { |
| 1806 | $documentation: "Base class for `super` & `this`", |
| 1807 | _validate: function() { |
| 1808 | if (this.TYPE == "ObjectIdentity") throw new Error("should not instantiate AST_ObjectIdentity"); |
| 1809 | }, |
| 1810 | }, AST_Symbol); |
| 1811 | |
| 1812 | var AST_Super = DEFNODE("Super", null, { |
| 1813 | $documentation: "The `super` symbol", |
| 1814 | _validate: function() { |
| 1815 | if (this.name !== "super") throw new Error('name must be "super"'); |
| 1816 | }, |
| 1817 | }, AST_ObjectIdentity); |
| 1818 | |
| 1819 | var AST_This = DEFNODE("This", null, { |
| 1820 | $documentation: "The `this` symbol", |
| 1821 | _validate: function() { |
| 1822 | if (this.TYPE == "This" && this.name !== "this") throw new Error('name must be "this"'); |
| 1823 | }, |
| 1824 | }, AST_ObjectIdentity); |
| 1825 | |
| 1826 | var AST_NewTarget = DEFNODE("NewTarget", null, { |
| 1827 | $documentation: "The `new.target` symbol", |
| 1828 | initialize: function() { |
| 1829 | this.name = "new.target"; |
| 1830 | }, |
| 1831 | _validate: function() { |
| 1832 | if (this.name !== "new.target") throw new Error('name must be "new.target": ' + this.name); |
| 1833 | }, |
| 1834 | }, AST_This); |
| 1835 | |
| 1836 | var AST_Template = DEFNODE("Template", "expressions strings tag", { |
| 1837 | $documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`", |
| 1838 | $propdoc: { |
| 1839 | expressions: "[AST_Node*] the placeholder expressions", |
| 1840 | strings: "[string*] the raw text segments", |
| 1841 | tag: "[AST_Node] tag function, or null if absent", |
| 1842 | }, |
| 1843 | walk: function(visitor) { |
| 1844 | var node = this; |
| 1845 | visitor.visit(node, function() { |
| 1846 | if (node.tag) node.tag.walk(visitor); |
| 1847 | node.expressions.forEach(function(expr) { |
| 1848 | expr.walk(visitor); |
| 1849 | }); |
| 1850 | }); |
| 1851 | }, |
| 1852 | _validate: function() { |
| 1853 | if (this.expressions.length + 1 != this.strings.length) { |
| 1854 | throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)"); |
| 1855 | } |
| 1856 | must_be_expressions(this, "expressions"); |
| 1857 | this.strings.forEach(function(string) { |
| 1858 | if (typeof string != "string") throw new Error("strings must contain string"); |
| 1859 | }); |
| 1860 | if (this.tag != null) must_be_expression(this, "tag"); |
| 1861 | }, |
| 1862 | }); |
| 1863 | |
| 1864 | var AST_Constant = DEFNODE("Constant", null, { |
| 1865 | $documentation: "Base class for all constants", |
| 1866 | _validate: function() { |
| 1867 | if (this.TYPE == "Constant") throw new Error("should not instantiate AST_Constant"); |
| 1868 | }, |
| 1869 | }); |
| 1870 | |
| 1871 | var AST_String = DEFNODE("String", "quote value", { |
| 1872 | $documentation: "A string literal", |
| 1873 | $propdoc: { |
| 1874 | quote: "[string?] the original quote character", |
| 1875 | value: "[string] the contents of this string", |
| 1876 | }, |
| 1877 | _validate: function() { |
| 1878 | if (this.quote != null) { |
| 1879 | if (typeof this.quote != "string") throw new Error("quote must be string"); |
| 1880 | if (!/^["']$/.test(this.quote)) throw new Error("invalid quote: " + this.quote); |
| 1881 | } |
| 1882 | if (typeof this.value != "string") throw new Error("value must be string"); |
| 1883 | }, |
| 1884 | }, AST_Constant); |
| 1885 | |
| 1886 | var AST_Number = DEFNODE("Number", "value", { |
| 1887 | $documentation: "A number literal", |
| 1888 | $propdoc: { |
| 1889 | value: "[number] the numeric value", |
| 1890 | }, |
| 1891 | _validate: function() { |
| 1892 | if (typeof this.value != "number") throw new Error("value must be number"); |
| 1893 | if (!isFinite(this.value)) throw new Error("value must be finite"); |
| 1894 | if (this.value < 0) throw new Error("value cannot be negative"); |
| 1895 | }, |
| 1896 | }, AST_Constant); |
| 1897 | |
| 1898 | var AST_BigInt = DEFNODE("BigInt", "value", { |
| 1899 | $documentation: "A BigInt literal", |
| 1900 | $propdoc: { |
| 1901 | value: "[string] the numeric representation", |
| 1902 | }, |
| 1903 | _validate: function() { |
| 1904 | if (typeof this.value != "string") throw new Error("value must be string"); |
| 1905 | if (this.value[0] == "-") throw new Error("value cannot be negative"); |
| 1906 | }, |
| 1907 | }, AST_Constant); |
| 1908 | |
| 1909 | var AST_RegExp = DEFNODE("RegExp", "value", { |
| 1910 | $documentation: "A regexp literal", |
| 1911 | $propdoc: { |
| 1912 | value: "[RegExp] the actual regexp" |
| 1913 | }, |
| 1914 | _validate: function() { |
| 1915 | if (!(this.value instanceof RegExp)) throw new Error("value must be RegExp"); |
| 1916 | }, |
| 1917 | }, AST_Constant); |
| 1918 | |
| 1919 | var AST_Atom = DEFNODE("Atom", null, { |
| 1920 | $documentation: "Base class for atoms", |
| 1921 | _validate: function() { |
| 1922 | if (this.TYPE == "Atom") throw new Error("should not instantiate AST_Atom"); |
| 1923 | }, |
| 1924 | }, AST_Constant); |
| 1925 | |
| 1926 | var AST_Null = DEFNODE("Null", null, { |
| 1927 | $documentation: "The `null` atom", |
| 1928 | value: null |
| 1929 | }, AST_Atom); |
| 1930 | |
| 1931 | var AST_NaN = DEFNODE("NaN", null, { |
| 1932 | $documentation: "The impossible value", |
| 1933 | value: 0/0 |
| 1934 | }, AST_Atom); |
| 1935 | |
| 1936 | var AST_Undefined = DEFNODE("Undefined", null, { |
| 1937 | $documentation: "The `undefined` value", |
| 1938 | value: function(){}() |
| 1939 | }, AST_Atom); |
| 1940 | |
| 1941 | var AST_Hole = DEFNODE("Hole", null, { |
| 1942 | $documentation: "A hole in an array", |
| 1943 | value: function(){}() |
| 1944 | }, AST_Atom); |
| 1945 | |
| 1946 | var AST_Infinity = DEFNODE("Infinity", null, { |
| 1947 | $documentation: "The `Infinity` value", |
| 1948 | value: 1/0 |
| 1949 | }, AST_Atom); |
| 1950 | |
| 1951 | var AST_Boolean = DEFNODE("Boolean", null, { |
| 1952 | $documentation: "Base class for booleans", |
| 1953 | _validate: function() { |
| 1954 | if (this.TYPE == "Boolean") throw new Error("should not instantiate AST_Boolean"); |
| 1955 | }, |
| 1956 | }, AST_Atom); |
| 1957 | |
| 1958 | var AST_False = DEFNODE("False", null, { |
| 1959 | $documentation: "The `false` atom", |
| 1960 | value: false |
| 1961 | }, AST_Boolean); |
| 1962 | |
| 1963 | var AST_True = DEFNODE("True", null, { |
| 1964 | $documentation: "The `true` atom", |
| 1965 | value: true |
| 1966 | }, AST_Boolean); |
| 1967 | |
| 1968 | /* -----[ TreeWalker ]----- */ |
| 1969 | |
| 1970 | function TreeWalker(callback) { |
| 1971 | this.callback = callback; |
| 1972 | this.directives = Object.create(null); |
| 1973 | this.stack = []; |
| 1974 | } |
| 1975 | TreeWalker.prototype = { |
| 1976 | visit: function(node, descend) { |
| 1977 | this.push(node); |
| 1978 | var done = this.callback(node, descend || noop); |
| 1979 | if (!done && descend) descend(); |
| 1980 | this.pop(); |
| 1981 | }, |
| 1982 | parent: function(n) { |
| 1983 | return this.stack[this.stack.length - 2 - (n || 0)]; |
| 1984 | }, |
| 1985 | push: function(node) { |
| 1986 | if (node instanceof AST_Lambda) { |
| 1987 | this.directives = Object.create(this.directives); |
| 1988 | } else if (node instanceof AST_Directive && !this.directives[node.value]) { |
| 1989 | this.directives[node.value] = node; |
| 1990 | } |
| 1991 | this.stack.push(node); |
| 1992 | }, |
| 1993 | pop: function() { |
| 1994 | var node = this.stack.pop(); |
| 1995 | if (node instanceof AST_Lambda) { |
| 1996 | this.directives = Object.getPrototypeOf(this.directives); |
| 1997 | } |
| 1998 | }, |
| 1999 | self: function() { |
| 2000 | return this.stack[this.stack.length - 1]; |
| 2001 | }, |
| 2002 | find_parent: function(type) { |
| 2003 | var stack = this.stack; |
| 2004 | for (var i = stack.length; --i >= 0;) { |
| 2005 | var x = stack[i]; |
| 2006 | if (x instanceof type) return x; |
| 2007 | } |
| 2008 | }, |
| 2009 | has_directive: function(type) { |
| 2010 | var dir = this.directives[type]; |
| 2011 | if (dir) return dir; |
| 2012 | var node = this.stack[this.stack.length - 1]; |
| 2013 | if (node instanceof AST_Scope) { |
| 2014 | for (var i = 0; i < node.body.length; ++i) { |
| 2015 | var st = node.body[i]; |
| 2016 | if (!(st instanceof AST_Directive)) break; |
| 2017 | if (st.value == type) return st; |
| 2018 | } |
| 2019 | } |
| 2020 | }, |
| 2021 | loopcontrol_target: function(node) { |
| 2022 | var stack = this.stack; |
| 2023 | if (node.label) for (var i = stack.length; --i >= 0;) { |
| 2024 | var x = stack[i]; |
| 2025 | if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) |
| 2026 | return x.body; |
| 2027 | } else for (var i = stack.length; --i >= 0;) { |
| 2028 | var x = stack[i]; |
| 2029 | if (x instanceof AST_IterationStatement |
| 2030 | || node instanceof AST_Break && x instanceof AST_Switch) |
| 2031 | return x; |
| 2032 | } |
| 2033 | }, |
| 2034 | in_boolean_context: function() { |
| 2035 | var self = this.self(); |
| 2036 | for (var i = 0, p; p = this.parent(i); i++) { |
| 2037 | if (p instanceof AST_Conditional && p.condition === self |
| 2038 | || p instanceof AST_DWLoop && p.condition === self |
| 2039 | || p instanceof AST_For && p.condition === self |
| 2040 | || p instanceof AST_If && p.condition === self |
| 2041 | || p instanceof AST_Return && p.in_bool |
| 2042 | || p instanceof AST_Sequence && p.tail_node() !== self |
| 2043 | || p instanceof AST_SimpleStatement |
| 2044 | || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { |
| 2045 | return true; |
| 2046 | } |
| 2047 | if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||") |
| 2048 | || p instanceof AST_Conditional |
| 2049 | || p.tail_node() === self) { |
| 2050 | self = p; |
| 2051 | } else if (p instanceof AST_Return) { |
| 2052 | for (var call, fn = p; call = this.parent(++i); fn = call) { |
| 2053 | if (call.TYPE == "Call") { |
| 2054 | if (!(fn instanceof AST_Lambda) || fn.name) return false; |
| 2055 | } else if (fn instanceof AST_Lambda) { |
| 2056 | return false; |
| 2057 | } |
| 2058 | } |
| 2059 | } else { |
| 2060 | return false; |
| 2061 | } |
| 2062 | } |
| 2063 | } |
| 2064 | }; |