/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
A JavaScript tokenizer / parser / beautifier / compressor .
https : //github.com/mishoo/UglifyJS2
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ( C ) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
Author : Mihai Bazon
< mihai . bazon @ gmail . com >
http : //mihai.bazon.net/blog
Distributed under the BSD license :
Copyright 2012 ( c ) Mihai Bazon < mihai . bazon @ gmail . com >
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
* Redistributions of source code must retain the above
copyright notice , this list of conditions and the following
disclaimer .
* Redistributions in binary form must reproduce the above
copyright notice , this list of conditions and the following
disclaimer in the documentation and / or other materials
provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “ AS IS ” AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY ,
OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO ,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR
PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
import {
HOP ,
MAP ,
noop
} from "./utils/index.js" ;
import { parse } from "./parse.js" ;
function DEFNODE ( type , props , methods , base = AST _Node ) {
if ( ! props ) props = [ ] ;
else props = props . split ( /\s+/ ) ;
var self _props = props ;
if ( base && base . PROPS )
props = props . concat ( base . PROPS ) ;
var code = "return function AST_" + type + "(props){ if (props) { " ;
for ( var i = props . length ; -- i >= 0 ; ) {
code += "this." + props [ i ] + " = props." + props [ i ] + ";" ;
}
const proto = base && Object . create ( base . prototype ) ;
if ( proto && proto . initialize || ( methods && methods . initialize ) )
code += "this.initialize();" ;
code += "}" ;
code += "this.flags = 0;" ;
code += "}" ;
var ctor = new Function ( code ) ( ) ;
if ( proto ) {
ctor . prototype = proto ;
ctor . BASE = base ;
}
if ( base ) base . SUBCLASSES . push ( ctor ) ;
ctor . prototype . CTOR = ctor ;
ctor . prototype . constructor = ctor ;
ctor . PROPS = props || null ;
ctor . SELF _PROPS = self _props ;
ctor . SUBCLASSES = [ ] ;
if ( type ) {
ctor . prototype . TYPE = ctor . TYPE = type ;
}
if ( methods ) for ( i in methods ) if ( HOP ( methods , i ) ) {
if ( i [ 0 ] === "$" ) {
ctor [ i . substr ( 1 ) ] = methods [ i ] ;
} else {
ctor . prototype [ i ] = methods [ i ] ;
}
}
ctor . DEFMETHOD = function ( name , method ) {
this . prototype [ name ] = method ;
} ;
return ctor ;
}
const has _tok _flag = ( tok , flag ) => Boolean ( tok . flags & flag ) ;
const set _tok _flag = ( tok , flag , truth ) => {
if ( truth ) {
tok . flags |= flag ;
} else {
tok . flags &= ~ flag ;
}
} ;
const TOK _FLAG _NLB = 0b0001 ;
const TOK _FLAG _QUOTE _SINGLE = 0b0010 ;
const TOK _FLAG _QUOTE _EXISTS = 0b0100 ;
class AST _Token {
constructor ( type , value , line , col , pos , nlb , comments _before , comments _after , file ) {
this . flags = ( nlb ? 1 : 0 ) ;
this . type = type ;
this . value = value ;
this . line = line ;
this . col = col ;
this . pos = pos ;
this . comments _before = comments _before ;
this . comments _after = comments _after ;
this . file = file ;
Object . seal ( this ) ;
}
get nlb ( ) {
return has _tok _flag ( this , TOK _FLAG _NLB ) ;
}
set nlb ( new _nlb ) {
set _tok _flag ( this , TOK _FLAG _NLB , new _nlb ) ;
}
get quote ( ) {
return ! has _tok _flag ( this , TOK _FLAG _QUOTE _EXISTS )
? ""
: ( has _tok _flag ( this , TOK _FLAG _QUOTE _SINGLE ) ? "'" : '"' ) ;
}
set quote ( quote _type ) {
set _tok _flag ( this , TOK _FLAG _QUOTE _SINGLE , quote _type === "'" ) ;
set _tok _flag ( this , TOK _FLAG _QUOTE _EXISTS , ! ! quote _type ) ;
}
}
var AST _Node = DEFNODE ( "Node" , "start end" , {
_clone : function ( deep ) {
if ( deep ) {
var self = this . clone ( ) ;
return self . transform ( new TreeTransformer ( function ( node ) {
if ( node !== self ) {
return node . clone ( true ) ;
}
} ) ) ;
}
return new this . CTOR ( this ) ;
} ,
clone : function ( deep ) {
return this . _clone ( deep ) ;
} ,
$documentation : "Base class of all AST nodes" ,
$propdoc : {
start : "[AST_Token] The first token of this node" ,
end : "[AST_Token] The last token of this node"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this ) ;
} ,
walk : function ( visitor ) {
return this . _walk ( visitor ) ; // not sure the indirection will be any help
} ,
_children _backwards : ( ) => { }
} , null ) ;
/* -----[ statements ]----- */
var AST _Statement = DEFNODE ( "Statement" , null , {
$documentation : "Base class of all statements" ,
} ) ;
var AST _Debugger = DEFNODE ( "Debugger" , null , {
$documentation : "Represents a debugger statement" ,
} , AST _Statement ) ;
var AST _Directive = DEFNODE ( "Directive" , "value quote" , {
$documentation : "Represents a directive, like \"use strict\";" ,
$propdoc : {
value : "[string] The value of this directive as a plain string (it's not an AST_String!)" ,
quote : "[string] the original quote character"
} ,
} , AST _Statement ) ;
var AST _SimpleStatement = DEFNODE ( "SimpleStatement" , "body" , {
$documentation : "A statement consisting of an expression, i.e. a = 1 + 2" ,
$propdoc : {
body : "[AST_Node] an expression node (should not be instanceof AST_Statement)"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
}
} , AST _Statement ) ;
function walk _body ( node , visitor ) {
const body = node . body ;
for ( var i = 0 , len = body . length ; i < len ; i ++ ) {
body [ i ] . _walk ( visitor ) ;
}
}
function clone _block _scope ( deep ) {
var clone = this . _clone ( deep ) ;
if ( this . block _scope ) {
clone . block _scope = this . block _scope . clone ( ) ;
}
return clone ;
}
var AST _Block = DEFNODE ( "Block" , "body block_scope" , {
$documentation : "A body of statements (usually braced)" ,
$propdoc : {
body : "[AST_Statement*] an array of statements" ,
block _scope : "[AST_Scope] the block scope"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
walk _body ( this , visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
} ,
clone : clone _block _scope
} , AST _Statement ) ;
var AST _BlockStatement = DEFNODE ( "BlockStatement" , null , {
$documentation : "A block statement" ,
} , AST _Block ) ;
var AST _EmptyStatement = DEFNODE ( "EmptyStatement" , null , {
$documentation : "The empty statement (empty block or simply a semicolon)"
} , AST _Statement ) ;
var AST _StatementWithBody = DEFNODE ( "StatementWithBody" , "body" , {
$documentation : "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`" ,
$propdoc : {
body : "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement"
}
} , AST _Statement ) ;
var AST _LabeledStatement = DEFNODE ( "LabeledStatement" , "label" , {
$documentation : "Statement with a label" ,
$propdoc : {
label : "[AST_Label] a label definition"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . label . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
push ( this . label ) ;
} ,
clone : function ( deep ) {
var node = this . _clone ( deep ) ;
if ( deep ) {
var label = node . label ;
var def = this . label ;
node . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _LoopControl
&& node . label && node . label . thedef === def ) {
node . label . thedef = label ;
label . references . push ( node ) ;
}
} ) ) ;
}
return node ;
}
} , AST _StatementWithBody ) ;
var AST _IterationStatement = DEFNODE ( "IterationStatement" , "block_scope" , {
$documentation : "Internal class. All loops inherit from it." ,
$propdoc : {
block _scope : "[AST_Scope] the block scope for this iteration statement."
} ,
clone : clone _block _scope
} , AST _StatementWithBody ) ;
var AST _DWLoop = DEFNODE ( "DWLoop" , "condition" , {
$documentation : "Base class for do/while statements" ,
$propdoc : {
condition : "[AST_Node] the loop condition. Should not be instanceof AST_Statement"
}
} , AST _IterationStatement ) ;
var AST _Do = DEFNODE ( "Do" , null , {
$documentation : "A `do` statement" ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . body . _walk ( visitor ) ;
this . condition . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . condition ) ;
push ( this . body ) ;
}
} , AST _DWLoop ) ;
var AST _While = DEFNODE ( "While" , null , {
$documentation : "A `while` statement" ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . condition . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
push ( this . condition ) ;
} ,
} , AST _DWLoop ) ;
var AST _For = DEFNODE ( "For" , "init condition step" , {
$documentation : "A `for` statement" ,
$propdoc : {
init : "[AST_Node?] the `for` initialization code, or null if empty" ,
condition : "[AST_Node?] the `for` termination clause, or null if empty" ,
step : "[AST_Node?] the `for` update clause, or null if empty"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . init ) this . init . _walk ( visitor ) ;
if ( this . condition ) this . condition . _walk ( visitor ) ;
if ( this . step ) this . step . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
if ( this . step ) push ( this . step ) ;
if ( this . condition ) push ( this . condition ) ;
if ( this . init ) push ( this . init ) ;
} ,
} , AST _IterationStatement ) ;
var AST _ForIn = DEFNODE ( "ForIn" , "init object" , {
$documentation : "A `for ... in` statement" ,
$propdoc : {
init : "[AST_Node] the `for/in` initialization code" ,
object : "[AST_Node] the object that we're looping through"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . init . _walk ( visitor ) ;
this . object . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
if ( this . object ) push ( this . object ) ;
if ( this . init ) push ( this . init ) ;
} ,
} , AST _IterationStatement ) ;
var AST _ForOf = DEFNODE ( "ForOf" , "await" , {
$documentation : "A `for ... of` statement" ,
} , AST _ForIn ) ;
var AST _With = DEFNODE ( "With" , "expression" , {
$documentation : "A `with` statement" ,
$propdoc : {
expression : "[AST_Node] the `with` expression"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . body ) ;
push ( this . expression ) ;
} ,
} , AST _StatementWithBody ) ;
/* -----[ scope and functions ]----- */
var AST _Scope = DEFNODE ( "Scope" , "variables functions uses_with uses_eval parent_scope enclosed cname" , {
$documentation : "Base class for all statements introducing a lexical scope" ,
$propdoc : {
variables : "[Map/S] a map of name -> SymbolDef for all variables/functions defined in this scope" ,
uses _with : "[boolean/S] tells whether this scope uses the `with` statement" ,
uses _eval : "[boolean/S] tells whether this scope contains a direct call to the global `eval`" ,
parent _scope : "[AST_Scope?/S] link to the parent scope" ,
enclosed : "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes" ,
cname : "[integer/S] current index for mangling variables (used internally by the mangler)" ,
} ,
get _defun _scope : function ( ) {
var self = this ;
while ( self . is _block _scope ( ) ) {
self = self . parent _scope ;
}
return self ;
} ,
clone : function ( deep , toplevel ) {
var node = this . _clone ( deep ) ;
if ( deep && this . variables && toplevel && ! this . _block _scope ) {
node . figure _out _scope ( { } , {
toplevel : toplevel ,
parent _scope : this . parent _scope
} ) ;
} else {
if ( this . variables ) node . variables = new Map ( this . variables ) ;
if ( this . enclosed ) node . enclosed = this . enclosed . slice ( ) ;
if ( this . _block _scope ) node . _block _scope = this . _block _scope ;
}
return node ;
} ,
pinned : function ( ) {
return this . uses _eval || this . uses _with ;
}
} , AST _Block ) ;
var AST _Toplevel = DEFNODE ( "Toplevel" , "globals" , {
$documentation : "The toplevel scope" ,
$propdoc : {
globals : "[Map/S] a map of name -> SymbolDef for all undeclared names" ,
} ,
wrap _commonjs : function ( name ) {
var body = this . body ;
var wrapped _tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");" ;
wrapped _tl = parse ( wrapped _tl ) ;
wrapped _tl = wrapped _tl . transform ( new TreeTransformer ( function ( node ) {
if ( node instanceof AST _Directive && node . value == "$ORIG" ) {
return MAP . splice ( body ) ;
}
} ) ) ;
return wrapped _tl ;
} ,
wrap _enclose : function ( args _values ) {
if ( typeof args _values != "string" ) args _values = "" ;
var index = args _values . indexOf ( ":" ) ;
if ( index < 0 ) index = args _values . length ;
var body = this . body ;
return parse ( [
"(function(" ,
args _values . slice ( 0 , index ) ,
'){"$ORIG"})(' ,
args _values . slice ( index + 1 ) ,
")"
] . join ( "" ) ) . transform ( new TreeTransformer ( function ( node ) {
if ( node instanceof AST _Directive && node . value == "$ORIG" ) {
return MAP . splice ( body ) ;
}
} ) ) ;
}
} , AST _Scope ) ;
var AST _Expansion = DEFNODE ( "Expansion" , "expression" , {
$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" ,
$propdoc : {
expression : "[AST_Node] the thing to be expanded"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} ) ;
var AST _Lambda = DEFNODE ( "Lambda" , "name argnames uses_arguments is_generator async" , {
$documentation : "Base class for functions" ,
$propdoc : {
name : "[AST_SymbolDeclaration?] the name of this function" ,
argnames : "[AST_SymbolFunarg|AST_Destructuring|AST_Expansion|AST_DefaultAssign*] array of function arguments, destructurings, or expanding arguments" ,
uses _arguments : "[boolean/S] tells whether this function accesses the arguments array" ,
is _generator : "[boolean] is this a generator method" ,
async : "[boolean] is this method async" ,
} ,
args _as _names : function ( ) {
var out = [ ] ;
for ( var i = 0 ; i < this . argnames . length ; i ++ ) {
if ( this . argnames [ i ] instanceof AST _Destructuring ) {
out . push ( ... this . argnames [ i ] . all _symbols ( ) ) ;
} else {
out . push ( this . argnames [ i ] ) ;
}
}
return out ;
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . name ) this . name . _walk ( visitor ) ;
var argnames = this . argnames ;
for ( var i = 0 , len = argnames . length ; i < len ; i ++ ) {
argnames [ i ] . _walk ( visitor ) ;
}
walk _body ( this , visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
i = this . argnames . length ;
while ( i -- ) push ( this . argnames [ i ] ) ;
if ( this . name ) push ( this . name ) ;
} ,
is _braceless ( ) {
return this . body [ 0 ] instanceof AST _Return && this . body [ 0 ] . value ;
} ,
// Default args and expansion don't count, so .argnames.length doesn't cut it
length _property ( ) {
let length = 0 ;
for ( const arg of this . argnames ) {
if ( arg instanceof AST _SymbolFunarg || arg instanceof AST _Destructuring ) {
length ++ ;
}
}
return length ;
}
} , AST _Scope ) ;
var AST _Accessor = DEFNODE ( "Accessor" , null , {
$documentation : "A setter/getter function. The `name` property is always null."
} , AST _Lambda ) ;
var AST _Function = DEFNODE ( "Function" , null , {
$documentation : "A function expression"
} , AST _Lambda ) ;
var AST _Arrow = DEFNODE ( "Arrow" , null , {
$documentation : "An ES6 Arrow function ((a) => b)"
} , AST _Lambda ) ;
var AST _Defun = DEFNODE ( "Defun" , null , {
$documentation : "A function definition"
} , AST _Lambda ) ;
/* -----[ DESTRUCTURING ]----- */
var AST _Destructuring = DEFNODE ( "Destructuring" , "names is_array" , {
$documentation : "A destructuring of several names. Used in destructuring assignment and with destructuring function argument names" ,
$propdoc : {
"names" : "[AST_Node*] Array of properties or elements" ,
"is_array" : "[Boolean] Whether the destructuring represents an object or array"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . names . forEach ( function ( name ) {
name . _walk ( visitor ) ;
} ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . names . length ;
while ( i -- ) push ( this . names [ i ] ) ;
} ,
all _symbols : function ( ) {
var out = [ ] ;
this . walk ( new TreeWalker ( function ( node ) {
if ( node instanceof AST _Symbol ) {
out . push ( node ) ;
}
} ) ) ;
return out ;
}
} ) ;
var AST _PrefixedTemplateString = DEFNODE ( "PrefixedTemplateString" , "template_string prefix" , {
$documentation : "A templatestring with a prefix, such as String.raw`foobarbaz`" ,
$propdoc : {
template _string : "[AST_TemplateString] The template string" ,
prefix : "[AST_Node] The prefix, which will get called."
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . prefix . _walk ( visitor ) ;
this . template _string . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . template _string ) ;
push ( this . prefix ) ;
} ,
} ) ;
var AST _TemplateString = DEFNODE ( "TemplateString" , "segments" , {
$documentation : "A template string literal" ,
$propdoc : {
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."
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . segments . forEach ( function ( seg ) {
seg . _walk ( visitor ) ;
} ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . segments . length ;
while ( i -- ) push ( this . segments [ i ] ) ;
}
} ) ;
var AST _TemplateSegment = DEFNODE ( "TemplateSegment" , "value raw" , {
$documentation : "A segment of a template string literal" ,
$propdoc : {
value : "Content of the segment" ,
raw : "Raw source of the segment" ,
}
} ) ;
/* -----[ JUMPS ]----- */
var AST _Jump = DEFNODE ( "Jump" , null , {
$documentation : "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)"
} , AST _Statement ) ;
var AST _Exit = DEFNODE ( "Exit" , "value" , {
$documentation : "Base class for “exits” (`return` and `throw`)" ,
$propdoc : {
value : "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , this . value && function ( ) {
this . value . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . value ) push ( this . value ) ;
} ,
} , AST _Jump ) ;
var AST _Return = DEFNODE ( "Return" , null , {
$documentation : "A `return` statement"
} , AST _Exit ) ;
var AST _Throw = DEFNODE ( "Throw" , null , {
$documentation : "A `throw` statement"
} , AST _Exit ) ;
var AST _LoopControl = DEFNODE ( "LoopControl" , "label" , {
$documentation : "Base class for loop control statements (`break` and `continue`)" ,
$propdoc : {
label : "[AST_LabelRef?] the label, or null if none" ,
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , this . label && function ( ) {
this . label . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . label ) push ( this . label ) ;
} ,
} , AST _Jump ) ;
var AST _Break = DEFNODE ( "Break" , null , {
$documentation : "A `break` statement"
} , AST _LoopControl ) ;
var AST _Continue = DEFNODE ( "Continue" , null , {
$documentation : "A `continue` statement"
} , AST _LoopControl ) ;
var AST _Await = DEFNODE ( "Await" , "expression" , {
$documentation : "An `await` statement" ,
$propdoc : {
expression : "[AST_Node] the mandatory expression being awaited" ,
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} ) ;
var AST _Yield = DEFNODE ( "Yield" , "expression is_star" , {
$documentation : "A `yield` statement" ,
$propdoc : {
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" ,
is _star : "[Boolean] Whether this is a yield or yield* statement"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , this . expression && function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . expression ) push ( this . expression ) ;
}
} ) ;
/* -----[ IF ]----- */
var AST _If = DEFNODE ( "If" , "condition alternative" , {
$documentation : "A `if` statement" ,
$propdoc : {
condition : "[AST_Node] the `if` condition" ,
alternative : "[AST_Statement?] the `else` part, or null if not present"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . condition . _walk ( visitor ) ;
this . body . _walk ( visitor ) ;
if ( this . alternative ) this . alternative . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . alternative ) {
push ( this . alternative ) ;
}
push ( this . body ) ;
push ( this . condition ) ;
}
} , AST _StatementWithBody ) ;
/* -----[ SWITCH ]----- */
var AST _Switch = DEFNODE ( "Switch" , "expression" , {
$documentation : "A `switch` statement" ,
$propdoc : {
expression : "[AST_Node] the `switch` “discriminant”"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
walk _body ( this , visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
push ( this . expression ) ;
}
} , AST _Block ) ;
var AST _SwitchBranch = DEFNODE ( "SwitchBranch" , null , {
$documentation : "Base class for `switch` branches" ,
} , AST _Block ) ;
var AST _Default = DEFNODE ( "Default" , null , {
$documentation : "A `default` switch branch" ,
} , AST _SwitchBranch ) ;
var AST _Case = DEFNODE ( "Case" , "expression" , {
$documentation : "A `case` switch branch" ,
$propdoc : {
expression : "[AST_Node] the `case` expression"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
walk _body ( this , visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
push ( this . expression ) ;
} ,
} , AST _SwitchBranch ) ;
/* -----[ EXCEPTIONS ]----- */
var AST _Try = DEFNODE ( "Try" , "bcatch bfinally" , {
$documentation : "A `try` statement" ,
$propdoc : {
bcatch : "[AST_Catch?] the catch block, or null if not present" ,
bfinally : "[AST_Finally?] the finally block, or null if not present"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
walk _body ( this , visitor ) ;
if ( this . bcatch ) this . bcatch . _walk ( visitor ) ;
if ( this . bfinally ) this . bfinally . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . bfinally ) push ( this . bfinally ) ;
if ( this . bcatch ) push ( this . bcatch ) ;
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
} ,
} , AST _Block ) ;
var AST _Catch = DEFNODE ( "Catch" , "argname" , {
$documentation : "A `catch` node; only makes sense as part of a `try` statement" ,
$propdoc : {
argname : "[AST_SymbolCatch|AST_Destructuring|AST_Expansion|AST_DefaultAssign] symbol for the exception"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . argname ) this . argname . _walk ( visitor ) ;
walk _body ( this , visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . body . length ;
while ( i -- ) push ( this . body [ i ] ) ;
if ( this . argname ) push ( this . argname ) ;
} ,
} , AST _Block ) ;
var AST _Finally = DEFNODE ( "Finally" , null , {
$documentation : "A `finally` node; only makes sense as part of a `try` statement"
} , AST _Block ) ;
/* -----[ VAR/CONST ]----- */
var AST _Definitions = DEFNODE ( "Definitions" , "definitions" , {
$documentation : "Base class for `var` or `const` nodes (variable declarations/initializations)" ,
$propdoc : {
definitions : "[AST_VarDef*] array of variable definitions"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
var definitions = this . definitions ;
for ( var i = 0 , len = definitions . length ; i < len ; i ++ ) {
definitions [ i ] . _walk ( visitor ) ;
}
} ) ;
} ,
_children _backwards ( push ) {
let i = this . definitions . length ;
while ( i -- ) push ( this . definitions [ i ] ) ;
} ,
} , AST _Statement ) ;
var AST _Var = DEFNODE ( "Var" , null , {
$documentation : "A `var` statement"
} , AST _Definitions ) ;
var AST _Let = DEFNODE ( "Let" , null , {
$documentation : "A `let` statement"
} , AST _Definitions ) ;
var AST _Const = DEFNODE ( "Const" , null , {
$documentation : "A `const` statement"
} , AST _Definitions ) ;
var AST _VarDef = DEFNODE ( "VarDef" , "name value" , {
$documentation : "A variable declaration; only appears in a AST_Definitions node" ,
$propdoc : {
name : "[AST_Destructuring|AST_SymbolConst|AST_SymbolLet|AST_SymbolVar] name of the variable" ,
value : "[AST_Node?] initializer, or null of there's no initializer"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . name . _walk ( visitor ) ;
if ( this . value ) this . value . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . value ) push ( this . value ) ;
push ( this . name ) ;
} ,
} ) ;
var AST _NameMapping = DEFNODE ( "NameMapping" , "foreign_name name" , {
$documentation : "The part of the export/import statement that declare names from a module." ,
$propdoc : {
foreign _name : "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)" ,
name : "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module."
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . foreign _name . _walk ( visitor ) ;
this . name . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . name ) ;
push ( this . foreign _name ) ;
} ,
} ) ;
var AST _Import = DEFNODE ( "Import" , "imported_name imported_names module_name" , {
$documentation : "An `import` statement" ,
$propdoc : {
imported _name : "[AST_SymbolImport] The name of the variable holding the module's default export." ,
imported _names : "[AST_NameMapping*] The names of non-default imported variables" ,
module _name : "[AST_String] String literal describing where this module came from" ,
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . imported _name ) {
this . imported _name . _walk ( visitor ) ;
}
if ( this . imported _names ) {
this . imported _names . forEach ( function ( name _import ) {
name _import . _walk ( visitor ) ;
} ) ;
}
this . module _name . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . module _name ) ;
if ( this . imported _names ) {
let i = this . imported _names . length ;
while ( i -- ) push ( this . imported _names [ i ] ) ;
}
if ( this . imported _name ) push ( this . imported _name ) ;
} ,
} ) ;
var AST _ImportMeta = DEFNODE ( "ImportMeta" , null , {
$documentation : "A reference to import.meta" ,
} ) ;
var AST _Export = DEFNODE ( "Export" , "exported_definition exported_value is_default exported_names module_name" , {
$documentation : "An `export` statement" ,
$propdoc : {
exported _definition : "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition" ,
exported _value : "[AST_Node?] An exported value" ,
exported _names : "[AST_NameMapping*?] List of exported names" ,
module _name : "[AST_String?] Name of the file to load exports from" ,
is _default : "[Boolean] Whether this is the default exported value of this module"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . exported _definition ) {
this . exported _definition . _walk ( visitor ) ;
}
if ( this . exported _value ) {
this . exported _value . _walk ( visitor ) ;
}
if ( this . exported _names ) {
this . exported _names . forEach ( function ( name _export ) {
name _export . _walk ( visitor ) ;
} ) ;
}
if ( this . module _name ) {
this . module _name . _walk ( visitor ) ;
}
} ) ;
} ,
_children _backwards ( push ) {
if ( this . module _name ) push ( this . module _name ) ;
if ( this . exported _names ) {
let i = this . exported _names . length ;
while ( i -- ) push ( this . exported _names [ i ] ) ;
}
if ( this . exported _value ) push ( this . exported _value ) ;
if ( this . exported _definition ) push ( this . exported _definition ) ;
}
} , AST _Statement ) ;
/* -----[ OTHER ]----- */
var AST _Call = DEFNODE ( "Call" , "expression args optional _annotations" , {
$documentation : "A function call expression" ,
$propdoc : {
expression : "[AST_Node] expression to invoke as function" ,
args : "[AST_Node*] array of arguments" ,
optional : "[boolean] whether this is an optional call (IE ?.() )" ,
_annotations : "[number] bitfield containing information about the call"
} ,
initialize ( ) {
if ( this . _annotations == null ) this . _annotations = 0 ;
} ,
_walk ( visitor ) {
return visitor . _visit ( this , function ( ) {
var args = this . args ;
for ( var i = 0 , len = args . length ; i < len ; i ++ ) {
args [ i ] . _walk ( visitor ) ;
}
this . expression . _walk ( visitor ) ; // TODO why do we need to crawl this last?
} ) ;
} ,
_children _backwards ( push ) {
let i = this . args . length ;
while ( i -- ) push ( this . args [ i ] ) ;
push ( this . expression ) ;
} ,
} ) ;
var AST _New = DEFNODE ( "New" , null , {
$documentation : "An object instantiation. Derives from a function call since it has exactly the same properties"
} , AST _Call ) ;
var AST _Sequence = DEFNODE ( "Sequence" , "expressions" , {
$documentation : "A sequence expression (comma-separated expressions)" ,
$propdoc : {
expressions : "[AST_Node*] array of expressions (at least two)"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expressions . forEach ( function ( node ) {
node . _walk ( visitor ) ;
} ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . expressions . length ;
while ( i -- ) push ( this . expressions [ i ] ) ;
} ,
} ) ;
var AST _PropAccess = DEFNODE ( "PropAccess" , "expression property optional" , {
$documentation : "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`" ,
$propdoc : {
expression : "[AST_Node] the “container” expression" ,
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" ,
optional : "[boolean] whether this is an optional property access (IE ?.)"
}
} ) ;
var AST _Dot = DEFNODE ( "Dot" , "quote" , {
$documentation : "A dotted property access expression" ,
$propdoc : {
quote : "[string] the original quote character when transformed from AST_Sub" ,
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} , AST _PropAccess ) ;
var AST _DotHash = DEFNODE ( "DotHash" , "" , {
$documentation : "A dotted property access to a private property" ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} , AST _PropAccess ) ;
var AST _Sub = DEFNODE ( "Sub" , null , {
$documentation : "Index-style property access, i.e. `a[\"foo\"]`" ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
this . property . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . property ) ;
push ( this . expression ) ;
} ,
} , AST _PropAccess ) ;
var AST _Chain = DEFNODE ( "Chain" , "expression" , {
$documentation : "A chain expression like a?.b?.(c)?.[d]" ,
$propdoc : {
expression : "[AST_Call|AST_Dot|AST_DotHash|AST_Sub] chain element."
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} ) ;
var AST _Unary = DEFNODE ( "Unary" , "operator expression" , {
$documentation : "Base class for unary expressions" ,
$propdoc : {
operator : "[string] the operator" ,
expression : "[AST_Node] expression that this unary operator applies to"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . expression . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . expression ) ;
} ,
} ) ;
var AST _UnaryPrefix = DEFNODE ( "UnaryPrefix" , null , {
$documentation : "Unary prefix expression, i.e. `typeof i` or `++i`"
} , AST _Unary ) ;
var AST _UnaryPostfix = DEFNODE ( "UnaryPostfix" , null , {
$documentation : "Unary postfix expression, i.e. `i++`"
} , AST _Unary ) ;
var AST _Binary = DEFNODE ( "Binary" , "operator left right" , {
$documentation : "Binary expression, i.e. `a + b`" ,
$propdoc : {
left : "[AST_Node] left-hand side expression" ,
operator : "[string] the operator" ,
right : "[AST_Node] right-hand side expression"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . left . _walk ( visitor ) ;
this . right . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . right ) ;
push ( this . left ) ;
} ,
} ) ;
var AST _Conditional = DEFNODE ( "Conditional" , "condition consequent alternative" , {
$documentation : "Conditional expression using the ternary operator, i.e. `a ? b : c`" ,
$propdoc : {
condition : "[AST_Node]" ,
consequent : "[AST_Node]" ,
alternative : "[AST_Node]"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
this . condition . _walk ( visitor ) ;
this . consequent . _walk ( visitor ) ;
this . alternative . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . alternative ) ;
push ( this . consequent ) ;
push ( this . condition ) ;
} ,
} ) ;
var AST _Assign = DEFNODE ( "Assign" , "logical" , {
$documentation : "An assignment expression — `a = b + 5`" ,
$propdoc : {
logical : "Whether it's a logical assignment"
}
} , AST _Binary ) ;
var AST _DefaultAssign = DEFNODE ( "DefaultAssign" , null , {
$documentation : "A default assignment expression like in `(a = 3) => a`"
} , AST _Binary ) ;
/* -----[ LITERALS ]----- */
var AST _Array = DEFNODE ( "Array" , "elements" , {
$documentation : "An array literal" ,
$propdoc : {
elements : "[AST_Node*] array of elements"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
var elements = this . elements ;
for ( var i = 0 , len = elements . length ; i < len ; i ++ ) {
elements [ i ] . _walk ( visitor ) ;
}
} ) ;
} ,
_children _backwards ( push ) {
let i = this . elements . length ;
while ( i -- ) push ( this . elements [ i ] ) ;
} ,
} ) ;
var AST _Object = DEFNODE ( "Object" , "properties" , {
$documentation : "An object literal" ,
$propdoc : {
properties : "[AST_ObjectProperty*] array of properties"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
var properties = this . properties ;
for ( var i = 0 , len = properties . length ; i < len ; i ++ ) {
properties [ i ] . _walk ( visitor ) ;
}
} ) ;
} ,
_children _backwards ( push ) {
let i = this . properties . length ;
while ( i -- ) push ( this . properties [ i ] ) ;
} ,
} ) ;
var AST _ObjectProperty = DEFNODE ( "ObjectProperty" , "key value" , {
$documentation : "Base class for literal object properties" ,
$propdoc : {
key : "[string|AST_Node] property name. For ObjectKeyVal this is a string. For getters, setters and computed property this is an AST_Node." ,
value : "[AST_Node] property value. For getters and setters this is an AST_Accessor."
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . key instanceof AST _Node )
this . key . _walk ( visitor ) ;
this . value . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
push ( this . value ) ;
if ( this . key instanceof AST _Node ) push ( this . key ) ;
}
} ) ;
var AST _ObjectKeyVal = DEFNODE ( "ObjectKeyVal" , "quote" , {
$documentation : "A key: value object property" ,
$propdoc : {
quote : "[string] the original quote character"
} ,
computed _key ( ) {
return this . key instanceof AST _Node ;
}
} , AST _ObjectProperty ) ;
var AST _PrivateSetter = DEFNODE ( "PrivateSetter" , "static" , {
$propdoc : {
static : "[boolean] whether this is a static private setter"
} ,
$documentation : "A private setter property" ,
computed _key ( ) {
return false ;
}
} , AST _ObjectProperty ) ;
var AST _PrivateGetter = DEFNODE ( "PrivateGetter" , "static" , {
$propdoc : {
static : "[boolean] whether this is a static private getter"
} ,
$documentation : "A private getter property" ,
computed _key ( ) {
return false ;
}
} , AST _ObjectProperty ) ;
var AST _ObjectSetter = DEFNODE ( "ObjectSetter" , "quote static" , {
$propdoc : {
quote : "[string|undefined] the original quote character, if any" ,
static : "[boolean] whether this is a static setter (classes only)"
} ,
$documentation : "An object setter property" ,
computed _key ( ) {
return ! ( this . key instanceof AST _SymbolMethod ) ;
}
} , AST _ObjectProperty ) ;
var AST _ObjectGetter = DEFNODE ( "ObjectGetter" , "quote static" , {
$propdoc : {
quote : "[string|undefined] the original quote character, if any" ,
static : "[boolean] whether this is a static getter (classes only)"
} ,
$documentation : "An object getter property" ,
computed _key ( ) {
return ! ( this . key instanceof AST _SymbolMethod ) ;
}
} , AST _ObjectProperty ) ;
var AST _ConciseMethod = DEFNODE ( "ConciseMethod" , "quote static is_generator async" , {
$propdoc : {
quote : "[string|undefined] the original quote character, if any" ,
static : "[boolean] is this method static (classes only)" ,
is _generator : "[boolean] is this a generator method" ,
async : "[boolean] is this method async" ,
} ,
$documentation : "An ES6 concise method inside an object or class" ,
computed _key ( ) {
return ! ( this . key instanceof AST _SymbolMethod ) ;
}
} , AST _ObjectProperty ) ;
var AST _PrivateMethod = DEFNODE ( "PrivateMethod" , "" , {
$documentation : "A private class method inside a class" ,
} , AST _ConciseMethod ) ;
var AST _Class = DEFNODE ( "Class" , "name extends properties" , {
$propdoc : {
name : "[AST_SymbolClass|AST_SymbolDefClass?] optional class name." ,
extends : "[AST_Node]? optional parent class" ,
properties : "[AST_ObjectProperty*] array of properties"
} ,
$documentation : "An ES6 class" ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . name ) {
this . name . _walk ( visitor ) ;
}
if ( this . extends ) {
this . extends . _walk ( visitor ) ;
}
this . properties . forEach ( ( prop ) => prop . _walk ( visitor ) ) ;
} ) ;
} ,
_children _backwards ( push ) {
let i = this . properties . length ;
while ( i -- ) push ( this . properties [ i ] ) ;
if ( this . extends ) push ( this . extends ) ;
if ( this . name ) push ( this . name ) ;
} ,
} , AST _Scope /* TODO a class might have a scope but it's not a scope */ ) ;
var AST _ClassProperty = DEFNODE ( "ClassProperty" , "static quote" , {
$documentation : "A class property" ,
$propdoc : {
static : "[boolean] whether this is a static key" ,
quote : "[string] which quote is being used"
} ,
_walk : function ( visitor ) {
return visitor . _visit ( this , function ( ) {
if ( this . key instanceof AST _Node )
this . key . _walk ( visitor ) ;
if ( this . value instanceof AST _Node )
this . value . _walk ( visitor ) ;
} ) ;
} ,
_children _backwards ( push ) {
if ( this . value instanceof AST _Node ) push ( this . value ) ;
if ( this . key instanceof AST _Node ) push ( this . key ) ;
} ,
computed _key ( ) {
return ! ( this . key instanceof AST _SymbolClassProperty ) ;
}
} , AST _ObjectProperty ) ;
var AST _ClassPrivateProperty = DEFNODE ( "ClassPrivateProperty" , "" , {
$documentation : "A class property for a private property" ,
} , AST _ClassProperty ) ;
var AST _DefClass = DEFNODE ( "DefClass" , null , {
$documentation : "A class definition" ,
} , AST _Class ) ;
var AST _ClassExpression = DEFNODE ( "ClassExpression" , null , {
$documentation : "A class expression."
} , AST _Class ) ;
var AST _Symbol = DEFNODE ( "Symbol" , "scope name thedef" , {
$propdoc : {
name : "[string] name of this symbol" ,
scope : "[AST_Scope/S] the current scope (not necessarily the definition scope)" ,
thedef : "[SymbolDef/S] the definition of this symbol"
} ,
$documentation : "Base class for all symbols"
} ) ;
var AST _NewTarget = DEFNODE ( "NewTarget" , null , {
$documentation : "A reference to new.target"
} ) ;
var AST _SymbolDeclaration = DEFNODE ( "SymbolDeclaration" , "init" , {
$documentation : "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)" ,
} , AST _Symbol ) ;
var AST _SymbolVar = DEFNODE ( "SymbolVar" , null , {
$documentation : "Symbol defining a variable" ,
} , AST _SymbolDeclaration ) ;
var AST _SymbolBlockDeclaration = DEFNODE ( "SymbolBlockDeclaration" , null , {
$documentation : "Base class for block-scoped declaration symbols"
} , AST _SymbolDeclaration ) ;
var AST _SymbolConst = DEFNODE ( "SymbolConst" , null , {
$documentation : "A constant declaration"
} , AST _SymbolBlockDeclaration ) ;
var AST _SymbolLet = DEFNODE ( "SymbolLet" , null , {
$documentation : "A block-scoped `let` declaration"
} , AST _SymbolBlockDeclaration ) ;
var AST _SymbolFunarg = DEFNODE ( "SymbolFunarg" , null , {
$documentation : "Symbol naming a function argument" ,
} , AST _SymbolVar ) ;
var AST _SymbolDefun = DEFNODE ( "SymbolDefun" , null , {
$documentation : "Symbol defining a function" ,
} , AST _SymbolDeclaration ) ;
var AST _SymbolMethod = DEFNODE ( "SymbolMethod" , null , {
$documentation : "Symbol in an object defining a method" ,
} , AST _Symbol ) ;
var AST _SymbolClassProperty = DEFNODE ( "SymbolClassProperty" , null , {
$documentation : "Symbol for a class property" ,
} , AST _Symbol ) ;
var AST _SymbolLambda = DEFNODE ( "SymbolLambda" , null , {
$documentation : "Symbol naming a function expression" ,
} , AST _SymbolDeclaration ) ;
var AST _SymbolDefClass = DEFNODE ( "SymbolDefClass" , null , {
$documentation : "Symbol naming a class's name in a class declaration. Lexically scoped to its containing scope, and accessible within the class."
} , AST _SymbolBlockDeclaration ) ;
var AST _SymbolClass = DEFNODE ( "SymbolClass" , null , {
$documentation : "Symbol naming a class's name. Lexically scoped to the class."
} , AST _SymbolDeclaration ) ;
var AST _SymbolCatch = DEFNODE ( "SymbolCatch" , null , {
$documentation : "Symbol naming the exception in catch" ,
} , AST _SymbolBlockDeclaration ) ;
var AST _SymbolImport = DEFNODE ( "SymbolImport" , null , {
$documentation : "Symbol referring to an imported name" ,
} , AST _SymbolBlockDeclaration ) ;
var AST _SymbolImportForeign = DEFNODE ( "SymbolImportForeign" , null , {
$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" ,
} , AST _Symbol ) ;
var AST _Label = DEFNODE ( "Label" , "references" , {
$documentation : "Symbol naming a label (declaration)" ,
$propdoc : {
references : "[AST_LoopControl*] a list of nodes referring to this label"
} ,
initialize : function ( ) {
this . references = [ ] ;
this . thedef = this ;
}
} , AST _Symbol ) ;
var AST _SymbolRef = DEFNODE ( "SymbolRef" , null , {
$documentation : "Reference to some symbol (not definition/declaration)" ,
} , AST _Symbol ) ;
var AST _SymbolExport = DEFNODE ( "SymbolExport" , null , {
$documentation : "Symbol referring to a name to export" ,
} , AST _SymbolRef ) ;
var AST _SymbolExportForeign = DEFNODE ( "SymbolExportForeign" , null , {
$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" ,
} , AST _Symbol ) ;
var AST _LabelRef = DEFNODE ( "LabelRef" , null , {
$documentation : "Reference to a label symbol" ,
} , AST _Symbol ) ;
var AST _This = DEFNODE ( "This" , null , {
$documentation : "The `this` symbol" ,
} , AST _Symbol ) ;
var AST _Super = DEFNODE ( "Super" , null , {
$documentation : "The `super` symbol" ,
} , AST _This ) ;
var AST _Constant = DEFNODE ( "Constant" , null , {
$documentation : "Base class for all constants" ,
getValue : function ( ) {
return this . value ;
}
} ) ;
var AST _String = DEFNODE ( "String" , "value quote" , {
$documentation : "A string literal" ,
$propdoc : {
value : "[string] the contents of this string" ,
quote : "[string] the original quote character"
}
} , AST _Constant ) ;
var AST _Number = DEFNODE ( "Number" , "value raw" , {
$documentation : "A number literal" ,
$propdoc : {
value : "[number] the numeric value" ,
raw : "[string] numeric value as string"
}
} , AST _Constant ) ;
var AST _BigInt = DEFNODE ( "BigInt" , "value" , {
$documentation : "A big int literal" ,
$propdoc : {
value : "[string] big int value"
}
} , AST _Constant ) ;
var AST _RegExp = DEFNODE ( "RegExp" , "value" , {
$documentation : "A regexp literal" ,
$propdoc : {
value : "[RegExp] the actual regexp" ,
}
} , AST _Constant ) ;
var AST _Atom = DEFNODE ( "Atom" , null , {
$documentation : "Base class for atoms" ,
} , AST _Constant ) ;
var AST _Null = DEFNODE ( "Null" , null , {
$documentation : "The `null` atom" ,
value : null
} , AST _Atom ) ;
var AST _NaN = DEFNODE ( "NaN" , null , {
$documentation : "The impossible value" ,
value : 0 / 0
} , AST _Atom ) ;
var AST _Undefined = DEFNODE ( "Undefined" , null , {
$documentation : "The `undefined` value" ,
value : ( function ( ) { } ( ) )
} , AST _Atom ) ;
var AST _Hole = DEFNODE ( "Hole" , null , {
$documentation : "A hole in an array" ,
value : ( function ( ) { } ( ) )
} , AST _Atom ) ;
var AST _Infinity = DEFNODE ( "Infinity" , null , {
$documentation : "The `Infinity` value" ,
value : 1 / 0
} , AST _Atom ) ;
var AST _Boolean = DEFNODE ( "Boolean" , null , {
$documentation : "Base class for booleans" ,
} , AST _Atom ) ;
var AST _False = DEFNODE ( "False" , null , {
$documentation : "The `false` atom" ,
value : false
} , AST _Boolean ) ;
var AST _True = DEFNODE ( "True" , null , {
$documentation : "The `true` atom" ,
value : true
} , AST _Boolean ) ;
/* -----[ Walk function ]---- */
/ * *
* Walk nodes in depth - first search fashion .
* Callback can return ` walk_abort ` symbol to stop iteration .
* It can also return ` true ` to stop iteration just for child nodes .
* Iteration can be stopped and continued by passing the ` to_visit ` argument ,
* which is given to the callback in the second argument .
* * /
function walk ( node , cb , to _visit = [ node ] ) {
const push = to _visit . push . bind ( to _visit ) ;
while ( to _visit . length ) {
const node = to _visit . pop ( ) ;
const ret = cb ( node , to _visit ) ;
if ( ret ) {
if ( ret === walk _abort ) return true ;
continue ;
}
node . _children _backwards ( push ) ;
}
return false ;
}
function walk _parent ( node , cb , initial _stack ) {
const to _visit = [ node ] ;
const push = to _visit . push . bind ( to _visit ) ;
const stack = initial _stack ? initial _stack . slice ( ) : [ ] ;
const parent _pop _indices = [ ] ;
let current ;
const info = {
parent : ( n = 0 ) => {
if ( n === - 1 ) {
return current ;
}
// [ p1 p0 ] [ 1 0 ]
if ( initial _stack && n >= stack . length ) {
n -= stack . length ;
return initial _stack [
initial _stack . length - ( n + 1 )
] ;
}
return stack [ stack . length - ( 1 + n ) ] ;
} ,
} ;
while ( to _visit . length ) {
current = to _visit . pop ( ) ;
while (
parent _pop _indices . length &&
to _visit . length == parent _pop _indices [ parent _pop _indices . length - 1 ]
) {
stack . pop ( ) ;
parent _pop _indices . pop ( ) ;
}
const ret = cb ( current , info ) ;
if ( ret ) {
if ( ret === walk _abort ) return true ;
continue ;
}
const visit _length = to _visit . length ;
current . _children _backwards ( push ) ;
// Push only if we're going to traverse the children
if ( to _visit . length > visit _length ) {
stack . push ( current ) ;
parent _pop _indices . push ( visit _length - 1 ) ;
}
}
return false ;
}
const walk _abort = Symbol ( "abort walk" ) ;
/* -----[ TreeWalker ]----- */
class TreeWalker {
constructor ( callback ) {
this . visit = callback ;
this . stack = [ ] ;
this . directives = Object . create ( null ) ;
}
_visit ( node , descend ) {
this . push ( node ) ;
var ret = this . visit ( node , descend ? function ( ) {
descend . call ( node ) ;
} : noop ) ;
if ( ! ret && descend ) {
descend . call ( node ) ;
}
this . pop ( ) ;
return ret ;
}
parent ( n ) {
return this . stack [ this . stack . length - 2 - ( n || 0 ) ] ;
}
push ( node ) {
if ( node instanceof AST _Lambda ) {
this . directives = Object . create ( this . directives ) ;
} else if ( node instanceof AST _Directive && ! this . directives [ node . value ] ) {
this . directives [ node . value ] = node ;
} else if ( node instanceof AST _Class ) {
this . directives = Object . create ( this . directives ) ;
if ( ! this . directives [ "use strict" ] ) {
this . directives [ "use strict" ] = node ;
}
}
this . stack . push ( node ) ;
}
pop ( ) {
var node = this . stack . pop ( ) ;
if ( node instanceof AST _Lambda || node instanceof AST _Class ) {
this . directives = Object . getPrototypeOf ( this . directives ) ;
}
}
self ( ) {
return this . stack [ this . stack . length - 1 ] ;
}
find _parent ( type ) {
var stack = this . stack ;
for ( var i = stack . length ; -- i >= 0 ; ) {
var x = stack [ i ] ;
if ( x instanceof type ) return x ;
}
}
has _directive ( type ) {
var dir = this . directives [ type ] ;
if ( dir ) return dir ;
var node = this . stack [ this . stack . length - 1 ] ;
if ( node instanceof AST _Scope && node . body ) {
for ( var i = 0 ; i < node . body . length ; ++ i ) {
var st = node . body [ i ] ;
if ( ! ( st instanceof AST _Directive ) ) break ;
if ( st . value == type ) return st ;
}
}
}
loopcontrol _target ( node ) {
var stack = this . stack ;
if ( node . label ) for ( var i = stack . length ; -- i >= 0 ; ) {
var x = stack [ i ] ;
if ( x instanceof AST _LabeledStatement && x . label . name == node . label . name )
return x . body ;
} else for ( var i = stack . length ; -- i >= 0 ; ) {
var x = stack [ i ] ;
if ( x instanceof AST _IterationStatement
|| node instanceof AST _Break && x instanceof AST _Switch )
return x ;
}
}
}
// Tree transformer helpers.
class TreeTransformer extends TreeWalker {
constructor ( before , after ) {
super ( ) ;
this . before = before ;
this . after = after ;
}
}
const _PURE = 0b00000001 ;
const _INLINE = 0b00000010 ;
const _NOINLINE = 0b00000100 ;
export {
AST _Accessor ,
AST _Array ,
AST _Arrow ,
AST _Assign ,
AST _Atom ,
AST _Await ,
AST _BigInt ,
AST _Binary ,
AST _Block ,
AST _BlockStatement ,
AST _Boolean ,
AST _Break ,
AST _Call ,
AST _Case ,
AST _Catch ,
AST _Chain ,
AST _Class ,
AST _ClassExpression ,
AST _ClassPrivateProperty ,
AST _ClassProperty ,
AST _ConciseMethod ,
AST _Conditional ,
AST _Const ,
AST _Constant ,
AST _Continue ,
AST _Debugger ,
AST _Default ,
AST _DefaultAssign ,
AST _DefClass ,
AST _Definitions ,
AST _Defun ,
AST _Destructuring ,
AST _Directive ,
AST _Do ,
AST _Dot ,
AST _DotHash ,
AST _DWLoop ,
AST _EmptyStatement ,
AST _Exit ,
AST _Expansion ,
AST _Export ,
AST _False ,
AST _Finally ,
AST _For ,
AST _ForIn ,
AST _ForOf ,
AST _Function ,
AST _Hole ,
AST _If ,
AST _Import ,
AST _ImportMeta ,
AST _Infinity ,
AST _IterationStatement ,
AST _Jump ,
AST _Label ,
AST _LabeledStatement ,
AST _LabelRef ,
AST _Lambda ,
AST _Let ,
AST _LoopControl ,
AST _NameMapping ,
AST _NaN ,
AST _New ,
AST _NewTarget ,
AST _Node ,
AST _Null ,
AST _Number ,
AST _Object ,
AST _ObjectGetter ,
AST _ObjectKeyVal ,
AST _ObjectProperty ,
AST _ObjectSetter ,
AST _PrefixedTemplateString ,
AST _PrivateGetter ,
AST _PrivateMethod ,
AST _PrivateSetter ,
AST _PropAccess ,
AST _RegExp ,
AST _Return ,
AST _Scope ,
AST _Sequence ,
AST _SimpleStatement ,
AST _Statement ,
AST _StatementWithBody ,
AST _String ,
AST _Sub ,
AST _Super ,
AST _Switch ,
AST _SwitchBranch ,
AST _Symbol ,
AST _SymbolBlockDeclaration ,
AST _SymbolCatch ,
AST _SymbolClass ,
AST _SymbolClassProperty ,
AST _SymbolConst ,
AST _SymbolDeclaration ,
AST _SymbolDefClass ,
AST _SymbolDefun ,
AST _SymbolExport ,
AST _SymbolExportForeign ,
AST _SymbolFunarg ,
AST _SymbolImport ,
AST _SymbolImportForeign ,
AST _SymbolLambda ,
AST _SymbolLet ,
AST _SymbolMethod ,
AST _SymbolRef ,
AST _SymbolVar ,
AST _TemplateSegment ,
AST _TemplateString ,
AST _This ,
AST _Throw ,
AST _Token ,
AST _Toplevel ,
AST _True ,
AST _Try ,
AST _Unary ,
AST _UnaryPostfix ,
AST _UnaryPrefix ,
AST _Undefined ,
AST _Var ,
AST _VarDef ,
AST _While ,
AST _With ,
AST _Yield ,
// Walkers
TreeTransformer ,
TreeWalker ,
walk ,
walk _abort ,
walk _body ,
walk _parent ,
// annotations
_INLINE ,
_NOINLINE ,
_PURE ,
} ;