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