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