UNPKG

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