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