2022-01-21 08:28:41 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
A JavaScript tokenizer / parser / beautifier / compressor .
https : //github.com/mishoo/UglifyJS
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ( 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 >
Parser based on parse - js ( http : //marijn.haverbeke.nl/parse-js/).
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
"use strict" ;
var KEYWORDS = "break case catch class const continue debugger default delete do else extends finally for function if in instanceof new return switch throw try typeof var void while with" ;
var KEYWORDS _ATOM = "false null true" ;
var RESERVED _WORDS = [
"abstract async await boolean byte char double enum export final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield" ,
KEYWORDS _ATOM ,
KEYWORDS ,
] . join ( " " ) ;
var KEYWORDS _BEFORE _EXPRESSION = "return new delete throw else case" ;
KEYWORDS = makePredicate ( KEYWORDS ) ;
RESERVED _WORDS = makePredicate ( RESERVED _WORDS ) ;
KEYWORDS _BEFORE _EXPRESSION = makePredicate ( KEYWORDS _BEFORE _EXPRESSION ) ;
KEYWORDS _ATOM = makePredicate ( KEYWORDS _ATOM ) ;
var RE _BIN _NUMBER = /^0b([01]+)$/i ;
var RE _HEX _NUMBER = /^0x([0-9a-f]+)$/i ;
var RE _OCT _NUMBER = /^0o?([0-7]+)$/i ;
var OPERATORS = makePredicate ( [
"in" ,
"instanceof" ,
"typeof" ,
"new" ,
"void" ,
"delete" ,
"++" ,
"--" ,
"+" ,
"-" ,
"!" ,
"~" ,
"&" ,
"|" ,
"^" ,
"*" ,
"/" ,
"%" ,
"**" ,
">>" ,
"<<" ,
">>>" ,
"<" ,
">" ,
"<=" ,
">=" ,
"==" ,
"===" ,
"!=" ,
"!==" ,
"?" ,
"=" ,
"+=" ,
"-=" ,
"/=" ,
"*=" ,
"%=" ,
"**=" ,
">>=" ,
"<<=" ,
">>>=" ,
"&=" ,
"|=" ,
"^=" ,
"&&" ,
"||" ,
"??" ,
"&&=" ,
"||=" ,
"??=" ,
] ) ;
var NEWLINE _CHARS = "\n\r\u2028\u2029" ;
var OPERATOR _CHARS = "+-*&%=<>!?|~^" ;
var PUNC _OPENERS = "[{(" ;
var PUNC _SEPARATORS = ",;:" ;
var PUNC _CLOSERS = ")}]" ;
var PUNC _AFTER _EXPRESSION = PUNC _SEPARATORS + PUNC _CLOSERS ;
var PUNC _BEFORE _EXPRESSION = PUNC _OPENERS + PUNC _SEPARATORS ;
var PUNC _CHARS = PUNC _BEFORE _EXPRESSION + "`" + PUNC _CLOSERS ;
var WHITESPACE _CHARS = NEWLINE _CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF" ;
var NON _IDENTIFIER _CHARS = makePredicate ( characters ( "./'\"#" + OPERATOR _CHARS + PUNC _CHARS + WHITESPACE _CHARS ) ) ;
NEWLINE _CHARS = makePredicate ( characters ( NEWLINE _CHARS ) ) ;
OPERATOR _CHARS = makePredicate ( characters ( OPERATOR _CHARS ) ) ;
PUNC _AFTER _EXPRESSION = makePredicate ( characters ( PUNC _AFTER _EXPRESSION ) ) ;
PUNC _BEFORE _EXPRESSION = makePredicate ( characters ( PUNC _BEFORE _EXPRESSION ) ) ;
PUNC _CHARS = makePredicate ( characters ( PUNC _CHARS ) ) ;
WHITESPACE _CHARS = makePredicate ( characters ( WHITESPACE _CHARS ) ) ;
/* -----[ Tokenizer ]----- */
function is _surrogate _pair _head ( code ) {
return code >= 0xd800 && code <= 0xdbff ;
}
function is _surrogate _pair _tail ( code ) {
return code >= 0xdc00 && code <= 0xdfff ;
}
function is _digit ( code ) {
return code >= 48 && code <= 57 ;
}
function is _identifier _char ( ch ) {
return ! NON _IDENTIFIER _CHARS [ ch ] ;
}
function is _identifier _string ( str ) {
return /^[a-z_$][a-z0-9_$]*$/i . test ( str ) ;
}
function decode _escape _sequence ( seq ) {
switch ( seq [ 0 ] ) {
case "b" : return "\b" ;
case "f" : return "\f" ;
case "n" : return "\n" ;
case "r" : return "\r" ;
case "t" : return "\t" ;
case "u" :
var code ;
if ( seq [ 1 ] == "{" && seq . slice ( - 1 ) == "}" ) {
code = seq . slice ( 2 , - 1 ) ;
} else if ( seq . length == 5 ) {
code = seq . slice ( 1 ) ;
} else {
return ;
}
var num = parseInt ( code , 16 ) ;
if ( num < 0 || isNaN ( num ) ) return ;
if ( num < 0x10000 ) return String . fromCharCode ( num ) ;
if ( num > 0x10ffff ) return ;
return String . fromCharCode ( ( num >> 10 ) + 0xd7c0 ) + String . fromCharCode ( ( num & 0x03ff ) + 0xdc00 ) ;
case "v" : return "\u000b" ;
case "x" :
if ( seq . length != 3 ) return ;
var num = parseInt ( seq . slice ( 1 ) , 16 ) ;
if ( num < 0 || isNaN ( num ) ) return ;
return String . fromCharCode ( num ) ;
case "\r" :
case "\n" :
return "" ;
default :
if ( seq == "0" ) return "\0" ;
if ( seq [ 0 ] >= "0" && seq [ 0 ] <= "9" ) return ;
return seq ;
}
}
function parse _js _number ( num ) {
var match ;
if ( match = RE _BIN _NUMBER . exec ( num ) ) return parseInt ( match [ 1 ] , 2 ) ;
if ( match = RE _HEX _NUMBER . exec ( num ) ) return parseInt ( match [ 1 ] , 16 ) ;
if ( match = RE _OCT _NUMBER . exec ( num ) ) return parseInt ( match [ 1 ] , 8 ) ;
var val = parseFloat ( num ) ;
if ( val == num ) return val ;
}
function JS _Parse _Error ( message , filename , line , col , pos ) {
this . message = message ;
this . filename = filename ;
this . line = line ;
this . col = col ;
this . pos = pos ;
}
JS _Parse _Error . prototype = Object . create ( Error . prototype ) ;
JS _Parse _Error . prototype . constructor = JS _Parse _Error ;
JS _Parse _Error . prototype . name = "SyntaxError" ;
configure _error _stack ( JS _Parse _Error ) ;
function js _error ( message , filename , line , col , pos ) {
throw new JS _Parse _Error ( message , filename , line , col , pos ) ;
}
function is _token ( token , type , val ) {
return token . type == type && ( val == null || token . value == val ) ;
}
var EX _EOF = { } ;
function tokenizer ( $TEXT , filename , html5 _comments , shebang ) {
var S = {
text : $TEXT ,
filename : filename ,
pos : 0 ,
tokpos : 0 ,
line : 1 ,
tokline : 0 ,
col : 0 ,
tokcol : 0 ,
newline _before : false ,
regex _allowed : false ,
comments _before : [ ] ,
directives : { } ,
directive _stack : [ ] ,
read _template : with _eof _error ( "Unterminated template literal" , function ( strings ) {
var s = "" ;
for ( ; ; ) {
var ch = read ( ) ;
switch ( ch ) {
case "\\" :
ch += read ( ) ;
break ;
case "`" :
strings . push ( s ) ;
return ;
case "$" :
if ( peek ( ) == "{" ) {
next ( ) ;
strings . push ( s ) ;
S . regex _allowed = true ;
return true ;
}
}
s += ch ;
}
function read ( ) {
var ch = next ( true , true ) ;
return ch == "\r" ? "\n" : ch ;
}
} ) ,
} ;
var prev _was _dot = false ;
function peek ( ) {
return S . text . charAt ( S . pos ) ;
}
function next ( signal _eof , in _string ) {
var ch = S . text . charAt ( S . pos ++ ) ;
if ( signal _eof && ! ch )
throw EX _EOF ;
if ( NEWLINE _CHARS [ ch ] ) {
S . col = 0 ;
S . line ++ ;
if ( ! in _string ) S . newline _before = true ;
if ( ch == "\r" && peek ( ) == "\n" ) {
// treat `\r\n` as `\n`
S . pos ++ ;
ch = "\n" ;
}
} else {
S . col ++ ;
}
return ch ;
}
function forward ( i ) {
while ( i -- > 0 ) next ( ) ;
}
function looking _at ( str ) {
return S . text . substr ( S . pos , str . length ) == str ;
}
function find _eol ( ) {
var text = S . text ;
for ( var i = S . pos ; i < S . text . length ; ++ i ) {
if ( NEWLINE _CHARS [ text [ i ] ] ) return i ;
}
return - 1 ;
}
function find ( what , signal _eof ) {
var pos = S . text . indexOf ( what , S . pos ) ;
if ( signal _eof && pos == - 1 ) throw EX _EOF ;
return pos ;
}
function start _token ( ) {
S . tokline = S . line ;
S . tokcol = S . col ;
S . tokpos = S . pos ;
}
function token ( type , value , is _comment ) {
S . regex _allowed = type == "operator" && ! UNARY _POSTFIX [ value ]
|| type == "keyword" && KEYWORDS _BEFORE _EXPRESSION [ value ]
|| type == "punc" && PUNC _BEFORE _EXPRESSION [ value ] ;
if ( type == "punc" && value == "." ) prev _was _dot = true ;
else if ( ! is _comment ) prev _was _dot = false ;
var ret = {
type : type ,
value : value ,
line : S . tokline ,
col : S . tokcol ,
pos : S . tokpos ,
endline : S . line ,
endcol : S . col ,
endpos : S . pos ,
nlb : S . newline _before ,
file : filename
} ;
if ( /^(?:num|string|regexp)$/i . test ( type ) ) {
ret . raw = $TEXT . substring ( ret . pos , ret . endpos ) ;
}
if ( ! is _comment ) {
ret . comments _before = S . comments _before ;
ret . comments _after = S . comments _before = [ ] ;
}
S . newline _before = false ;
return new AST _Token ( ret ) ;
}
function skip _whitespace ( ) {
while ( WHITESPACE _CHARS [ peek ( ) ] )
next ( ) ;
}
function read _while ( pred ) {
var ret = "" , ch ;
while ( ( ch = peek ( ) ) && pred ( ch , ret ) ) ret += next ( ) ;
return ret ;
}
function parse _error ( err ) {
js _error ( err , filename , S . tokline , S . tokcol , S . tokpos ) ;
}
function is _octal ( num ) {
return /^0[0-7_]+$/ . test ( num ) ;
}
function read _num ( prefix ) {
var has _e = false , after _e = false , has _x = false , has _dot = prefix == "." ;
var num = read _while ( function ( ch , str ) {
switch ( ch ) {
case "x" : case "X" :
return has _x ? false : ( has _x = true ) ;
case "e" : case "E" :
return has _x ? true : has _e ? false : ( has _e = after _e = true ) ;
case "+" : case "-" :
return after _e ;
case ( after _e = false , "." ) :
return has _dot || has _e || has _x || is _octal ( str ) ? false : ( has _dot = true ) ;
}
return /[_0-9a-dfo]/i . test ( ch ) ;
} ) ;
if ( prefix ) num = prefix + num ;
if ( is _octal ( num ) ) {
if ( next _token . has _directive ( "use strict" ) ) parse _error ( "Legacy octal literals are not allowed in strict mode" ) ;
} else {
num = num . replace ( has _x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi , "$1" ) ;
}
var valid = parse _js _number ( num ) ;
if ( isNaN ( valid ) ) parse _error ( "Invalid syntax: " + num ) ;
if ( has _dot || has _e || peek ( ) != "n" ) return token ( "num" , valid ) ;
return token ( "bigint" , num . toLowerCase ( ) + next ( ) ) ;
}
function read _escaped _char ( in _string ) {
var seq = next ( true , in _string ) ;
if ( seq >= "0" && seq <= "7" ) return read _octal _escape _sequence ( seq ) ;
if ( seq == "u" ) {
var ch = next ( true , in _string ) ;
seq += ch ;
if ( ch != "{" ) {
seq += next ( true , in _string ) + next ( true , in _string ) + next ( true , in _string ) ;
} else do {
ch = next ( true , in _string ) ;
seq += ch ;
} while ( ch != "}" ) ;
} else if ( seq == "x" ) {
seq += next ( true , in _string ) + next ( true , in _string ) ;
}
var str = decode _escape _sequence ( seq ) ;
if ( typeof str != "string" ) parse _error ( "Invalid escape sequence: \\" + seq ) ;
return str ;
}
function read _octal _escape _sequence ( ch ) {
// Read
var p = peek ( ) ;
if ( p >= "0" && p <= "7" ) {
ch += next ( true ) ;
if ( ch [ 0 ] <= "3" && ( p = peek ( ) ) >= "0" && p <= "7" )
ch += next ( true ) ;
}
// Parse
if ( ch === "0" ) return "\0" ;
if ( ch . length > 0 && next _token . has _directive ( "use strict" ) )
parse _error ( "Legacy octal escape sequences are not allowed in strict mode" ) ;
return String . fromCharCode ( parseInt ( ch , 8 ) ) ;
}
var read _string = with _eof _error ( "Unterminated string constant" , function ( quote _char ) {
var quote = next ( ) , ret = "" ;
for ( ; ; ) {
var ch = next ( true , true ) ;
if ( ch == "\\" ) ch = read _escaped _char ( true ) ;
else if ( NEWLINE _CHARS [ ch ] ) parse _error ( "Unterminated string constant" ) ;
else if ( ch == quote ) break ;
ret += ch ;
}
var tok = token ( "string" , ret ) ;
tok . quote = quote _char ;
return tok ;
} ) ;
function skip _line _comment ( type ) {
var regex _allowed = S . regex _allowed ;
var i = find _eol ( ) , ret ;
if ( i == - 1 ) {
ret = S . text . substr ( S . pos ) ;
S . pos = S . text . length ;
} else {
ret = S . text . substring ( S . pos , i ) ;
S . pos = i ;
}
S . col = S . tokcol + ( S . pos - S . tokpos ) ;
S . comments _before . push ( token ( type , ret , true ) ) ;
S . regex _allowed = regex _allowed ;
return next _token ;
}
var skip _multiline _comment = with _eof _error ( "Unterminated multiline comment" , function ( ) {
var regex _allowed = S . regex _allowed ;
var i = find ( "*/" , true ) ;
var text = S . text . substring ( S . pos , i ) . replace ( /\r\n|\r|\u2028|\u2029/g , "\n" ) ;
// update stream position
forward ( text . length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2 ) ;
S . comments _before . push ( token ( "comment2" , text , true ) ) ;
S . regex _allowed = regex _allowed ;
return next _token ;
} ) ;
function read _name ( ) {
var backslash = false , ch , escaped = false , name = peek ( ) == "#" ? next ( ) : "" ;
while ( ch = peek ( ) ) {
if ( ! backslash ) {
if ( ch == "\\" ) escaped = backslash = true , next ( ) ;
else if ( is _identifier _char ( ch ) ) name += next ( ) ;
else break ;
} else {
if ( ch != "u" ) parse _error ( "Expecting UnicodeEscapeSequence -- uXXXX" ) ;
ch = read _escaped _char ( ) ;
if ( ! is _identifier _char ( ch ) ) parse _error ( "Unicode char: " + ch . charCodeAt ( 0 ) + " is not valid in identifier" ) ;
name += ch ;
backslash = false ;
}
}
if ( KEYWORDS [ name ] && escaped ) {
var hex = name . charCodeAt ( 0 ) . toString ( 16 ) . toUpperCase ( ) ;
name = "\\u" + "0000" . substr ( hex . length ) + hex + name . slice ( 1 ) ;
}
return name ;
}
var read _regexp = with _eof _error ( "Unterminated regular expression" , function ( source ) {
var prev _backslash = false , ch , in _class = false ;
while ( ( ch = next ( true ) ) ) if ( NEWLINE _CHARS [ ch ] ) {
parse _error ( "Unexpected line terminator" ) ;
} else if ( prev _backslash ) {
source += "\\" + ch ;
prev _backslash = false ;
} else if ( ch == "[" ) {
in _class = true ;
source += ch ;
} else if ( ch == "]" && in _class ) {
in _class = false ;
source += ch ;
} else if ( ch == "/" && ! in _class ) {
break ;
} else if ( ch == "\\" ) {
prev _backslash = true ;
} else {
source += ch ;
}
var mods = read _name ( ) ;
try {
var regexp = new RegExp ( source , mods ) ;
regexp . raw _source = source ;
return token ( "regexp" , regexp ) ;
} catch ( e ) {
parse _error ( e . message ) ;
}
} ) ;
function read _operator ( prefix ) {
function grow ( op ) {
if ( ! peek ( ) ) return op ;
var bigger = op + peek ( ) ;
if ( OPERATORS [ bigger ] ) {
next ( ) ;
return grow ( bigger ) ;
} else {
return op ;
}
}
return token ( "operator" , grow ( prefix || next ( ) ) ) ;
}
function handle _slash ( ) {
next ( ) ;
switch ( peek ( ) ) {
case "/" :
next ( ) ;
return skip _line _comment ( "comment1" ) ;
case "*" :
next ( ) ;
return skip _multiline _comment ( ) ;
}
return S . regex _allowed ? read _regexp ( "" ) : read _operator ( "/" ) ;
}
function handle _dot ( ) {
next ( ) ;
var ch = peek ( ) ;
if ( ch == "." ) {
var op = "." ;
do {
op += "." ;
next ( ) ;
} while ( peek ( ) == "." ) ;
return token ( "operator" , op ) ;
}
return is _digit ( ch . charCodeAt ( 0 ) ) ? read _num ( "." ) : token ( "punc" , "." ) ;
}
function read _word ( ) {
var word = read _name ( ) ;
if ( prev _was _dot ) return token ( "name" , word ) ;
return KEYWORDS _ATOM [ word ] ? token ( "atom" , word )
: ! KEYWORDS [ word ] ? token ( "name" , word )
: OPERATORS [ word ] ? token ( "operator" , word )
: token ( "keyword" , word ) ;
}
function with _eof _error ( eof _error , cont ) {
return function ( x ) {
try {
return cont ( x ) ;
} catch ( ex ) {
if ( ex === EX _EOF ) parse _error ( eof _error ) ;
else throw ex ;
}
} ;
}
function next _token ( force _regexp ) {
if ( force _regexp != null )
return read _regexp ( force _regexp ) ;
if ( shebang && S . pos == 0 && looking _at ( "#!" ) ) {
start _token ( ) ;
forward ( 2 ) ;
skip _line _comment ( "comment5" ) ;
}
for ( ; ; ) {
skip _whitespace ( ) ;
start _token ( ) ;
if ( html5 _comments ) {
if ( looking _at ( "<!--" ) ) {
forward ( 4 ) ;
skip _line _comment ( "comment3" ) ;
continue ;
}
if ( looking _at ( "-->" ) && S . newline _before ) {
forward ( 3 ) ;
skip _line _comment ( "comment4" ) ;
continue ;
}
}
var ch = peek ( ) ;
if ( ! ch ) return token ( "eof" ) ;
var code = ch . charCodeAt ( 0 ) ;
switch ( code ) {
case 34 : case 39 : return read _string ( ch ) ;
case 46 : return handle _dot ( ) ;
case 47 :
var tok = handle _slash ( ) ;
if ( tok === next _token ) continue ;
return tok ;
}
if ( is _digit ( code ) ) return read _num ( ) ;
if ( PUNC _CHARS [ ch ] ) return token ( "punc" , next ( ) ) ;
if ( looking _at ( "=>" ) ) return token ( "punc" , next ( ) + next ( ) ) ;
if ( OPERATOR _CHARS [ ch ] ) return read _operator ( ) ;
if ( code == 35 || code == 92 || ! NON _IDENTIFIER _CHARS [ ch ] ) return read _word ( ) ;
break ;
}
parse _error ( "Unexpected character '" + ch + "'" ) ;
}
next _token . context = function ( nc ) {
if ( nc ) S = nc ;
return S ;
} ;
next _token . add _directive = function ( directive ) {
S . directive _stack [ S . directive _stack . length - 1 ] . push ( directive ) ;
if ( S . directives [ directive ] ) S . directives [ directive ] ++ ;
else S . directives [ directive ] = 1 ;
}
next _token . push _directives _stack = function ( ) {
S . directive _stack . push ( [ ] ) ;
}
next _token . pop _directives _stack = function ( ) {
var directives = S . directive _stack . pop ( ) ;
for ( var i = directives . length ; -- i >= 0 ; ) {
S . directives [ directives [ i ] ] -- ;
}
}
next _token . has _directive = function ( directive ) {
return S . directives [ directive ] > 0 ;
}
return next _token ;
}
/* -----[ Parser (constants) ]----- */
var UNARY _PREFIX = makePredicate ( "typeof void delete -- ++ ! ~ - +" ) ;
var UNARY _POSTFIX = makePredicate ( "-- ++" ) ;
var ASSIGNMENT = makePredicate ( "= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=" ) ;
var PRECEDENCE = function ( a , ret ) {
for ( var i = 0 ; i < a . length ; ) {
var b = a [ i ++ ] ;
for ( var j = 0 ; j < b . length ; j ++ ) {
ret [ b [ j ] ] = i ;
}
}
return ret ;
} ( [
[ "??" ] ,
[ "||" ] ,
[ "&&" ] ,
[ "|" ] ,
[ "^" ] ,
[ "&" ] ,
[ "==" , "===" , "!=" , "!==" ] ,
[ "<" , ">" , "<=" , ">=" , "in" , "instanceof" ] ,
[ ">>" , "<<" , ">>>" ] ,
[ "+" , "-" ] ,
[ "*" , "/" , "%" ] ,
[ "**" ] ,
] , { } ) ;
var ATOMIC _START _TOKEN = makePredicate ( "atom bigint num regexp string" ) ;
/* -----[ Parser ]----- */
function parse ( $TEXT , options ) {
options = defaults ( options , {
bare _returns : false ,
expression : false ,
filename : null ,
html5 _comments : true ,
shebang : true ,
strict : false ,
toplevel : null ,
} , true ) ;
var S = {
input : typeof $TEXT == "string"
? tokenizer ( $TEXT , options . filename , options . html5 _comments , options . shebang )
: $TEXT ,
in _async : false ,
in _directives : true ,
in _funarg : - 1 ,
in _function : 0 ,
in _generator : false ,
in _loop : 0 ,
labels : [ ] ,
peeked : null ,
prev : null ,
token : null ,
} ;
S . token = next ( ) ;
function is ( type , value ) {
return is _token ( S . token , type , value ) ;
}
function peek ( ) {
return S . peeked || ( S . peeked = S . input ( ) ) ;
}
function next ( ) {
S . prev = S . token ;
if ( S . peeked ) {
S . token = S . peeked ;
S . peeked = null ;
} else {
S . token = S . input ( ) ;
}
S . in _directives = S . in _directives && (
S . token . type == "string" || is ( "punc" , ";" )
) ;
return S . token ;
}
function prev ( ) {
return S . prev ;
}
function croak ( msg , line , col , pos ) {
var ctx = S . input . context ( ) ;
js _error ( msg ,
ctx . filename ,
line != null ? line : ctx . tokline ,
col != null ? col : ctx . tokcol ,
pos != null ? pos : ctx . tokpos ) ;
}
function token _error ( token , msg ) {
croak ( msg , token . line , token . col ) ;
}
function token _to _string ( type , value ) {
return type + ( value === undefined ? "" : " «" + value + "»" ) ;
}
function unexpected ( token ) {
if ( token == null ) token = S . token ;
token _error ( token , "Unexpected token: " + token _to _string ( token . type , token . value ) ) ;
}
function expect _token ( type , val ) {
if ( is ( type , val ) ) return next ( ) ;
token _error ( S . token , "Unexpected token: " + token _to _string ( S . token . type , S . token . value ) + ", expected: " + token _to _string ( type , val ) ) ;
}
function expect ( punc ) {
return expect _token ( "punc" , punc ) ;
}
function has _newline _before ( token ) {
return token . nlb || ! all ( token . comments _before , function ( comment ) {
return ! comment . nlb ;
} ) ;
}
function can _insert _semicolon ( ) {
return ! options . strict
&& ( is ( "eof" ) || is ( "punc" , "}" ) || has _newline _before ( S . token ) ) ;
}
function semicolon ( optional ) {
if ( is ( "punc" , ";" ) ) next ( ) ;
else if ( ! optional && ! can _insert _semicolon ( ) ) expect ( ";" ) ;
}
function parenthesised ( ) {
expect ( "(" ) ;
var exp = expression ( ) ;
expect ( ")" ) ;
return exp ;
}
function embed _tokens ( parser ) {
return function ( ) {
var start = S . token ;
var expr = parser . apply ( null , arguments ) ;
var end = prev ( ) ;
expr . start = start ;
expr . end = end ;
return expr ;
} ;
}
function handle _regexp ( ) {
if ( is ( "operator" , "/" ) || is ( "operator" , "/=" ) ) {
S . peeked = null ;
S . token = S . input ( S . token . value . substr ( 1 ) ) ; // force regexp
}
}
var statement = embed _tokens ( function ( ) {
handle _regexp ( ) ;
switch ( S . token . type ) {
case "string" :
var dir = S . in _directives ;
var body = expression ( ) ;
if ( dir ) {
if ( body instanceof AST _String ) {
var value = body . start . raw . slice ( 1 , - 1 ) ;
S . input . add _directive ( value ) ;
body . value = value ;
} else {
S . in _directives = dir = false ;
}
}
semicolon ( ) ;
return dir ? new AST _Directive ( body ) : new AST _SimpleStatement ( { body : body } ) ;
case "num" :
case "bigint" :
case "regexp" :
case "operator" :
case "atom" :
return simple _statement ( ) ;
case "name" :
switch ( S . token . value ) {
case "async" :
if ( is _token ( peek ( ) , "keyword" , "function" ) ) {
next ( ) ;
next ( ) ;
if ( ! is ( "operator" , "*" ) ) return function _ ( AST _AsyncDefun ) ;
next ( ) ;
return function _ ( AST _AsyncGeneratorDefun ) ;
}
break ;
case "await" :
if ( S . in _async ) return simple _statement ( ) ;
break ;
case "export" :
next ( ) ;
return export _ ( ) ;
case "import" :
var token = peek ( ) ;
if ( ! ( token . type == "punc" && /^[(.]$/ . test ( token . value ) ) ) {
next ( ) ;
return import _ ( ) ;
}
break ;
case "let" :
if ( is _vardefs ( ) ) {
next ( ) ;
var node = let _ ( ) ;
semicolon ( ) ;
return node ;
}
break ;
case "yield" :
if ( S . in _generator ) return simple _statement ( ) ;
break ;
}
return is _token ( peek ( ) , "punc" , ":" )
? labeled _statement ( )
: simple _statement ( ) ;
case "punc" :
switch ( S . token . value ) {
case "{" :
return new AST _BlockStatement ( {
start : S . token ,
body : block _ ( ) ,
end : prev ( )
} ) ;
case "[" :
case "(" :
case "`" :
return simple _statement ( ) ;
case ";" :
S . in _directives = false ;
next ( ) ;
return new AST _EmptyStatement ( ) ;
default :
unexpected ( ) ;
}
case "keyword" :
switch ( S . token . value ) {
case "break" :
next ( ) ;
return break _cont ( AST _Break ) ;
case "class" :
next ( ) ;
return class _ ( AST _DefClass ) ;
case "const" :
next ( ) ;
var node = const _ ( ) ;
semicolon ( ) ;
return node ;
case "continue" :
next ( ) ;
return break _cont ( AST _Continue ) ;
case "debugger" :
next ( ) ;
semicolon ( ) ;
return new AST _Debugger ( ) ;
case "do" :
next ( ) ;
var body = in _loop ( statement ) ;
expect _token ( "keyword" , "while" ) ;
var condition = parenthesised ( ) ;
semicolon ( true ) ;
return new AST _Do ( {
body : body ,
condition : condition
} ) ;
case "while" :
next ( ) ;
return new AST _While ( {
condition : parenthesised ( ) ,
body : in _loop ( statement )
} ) ;
case "for" :
next ( ) ;
return for _ ( ) ;
case "function" :
next ( ) ;
if ( ! is ( "operator" , "*" ) ) return function _ ( AST _Defun ) ;
next ( ) ;
return function _ ( AST _GeneratorDefun ) ;
case "if" :
next ( ) ;
return if _ ( ) ;
case "return" :
if ( S . in _function == 0 && ! options . bare _returns )
croak ( "'return' outside of function" ) ;
next ( ) ;
var value = null ;
if ( is ( "punc" , ";" ) ) {
next ( ) ;
} else if ( ! can _insert _semicolon ( ) ) {
value = expression ( ) ;
semicolon ( ) ;
}
return new AST _Return ( {
value : value
} ) ;
case "switch" :
next ( ) ;
return new AST _Switch ( {
expression : parenthesised ( ) ,
body : in _loop ( switch _body _ )
} ) ;
case "throw" :
next ( ) ;
if ( has _newline _before ( S . token ) )
croak ( "Illegal newline after 'throw'" ) ;
var value = expression ( ) ;
semicolon ( ) ;
return new AST _Throw ( {
value : value
} ) ;
case "try" :
next ( ) ;
return try _ ( ) ;
case "var" :
next ( ) ;
var node = var _ ( ) ;
semicolon ( ) ;
return node ;
case "with" :
if ( S . input . has _directive ( "use strict" ) ) {
croak ( "Strict mode may not include a with statement" ) ;
}
next ( ) ;
return new AST _With ( {
expression : parenthesised ( ) ,
body : statement ( )
} ) ;
}
}
unexpected ( ) ;
} ) ;
function labeled _statement ( ) {
var label = as _symbol ( AST _Label ) ;
if ( ! all ( S . labels , function ( l ) {
return l . name != label . name ;
} ) ) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a
// LabelledStatement with the same Identifier as label.
croak ( "Label " + label . name + " defined twice" ) ;
}
expect ( ":" ) ;
S . labels . push ( label ) ;
var stat = statement ( ) ;
S . labels . pop ( ) ;
if ( ! ( stat instanceof AST _IterationStatement ) ) {
// check for `continue` that refers to this label.
// those should be reported as syntax errors.
// https://github.com/mishoo/UglifyJS/issues/287
label . references . forEach ( function ( ref ) {
if ( ref instanceof AST _Continue ) {
token _error ( ref . label . start , "Continue label `" + label . name + "` must refer to IterationStatement" ) ;
}
} ) ;
}
return new AST _LabeledStatement ( { body : stat , label : label } ) ;
}
function simple _statement ( ) {
var body = expression ( ) ;
semicolon ( ) ;
return new AST _SimpleStatement ( { body : body } ) ;
}
function break _cont ( type ) {
var label = null , ldef ;
if ( ! can _insert _semicolon ( ) ) {
label = as _symbol ( AST _LabelRef , true ) ;
}
if ( label != null ) {
ldef = find _if ( function ( l ) {
return l . name == label . name ;
} , S . labels ) ;
if ( ! ldef ) token _error ( label . start , "Undefined label " + label . name ) ;
label . thedef = ldef ;
} else if ( S . in _loop == 0 ) croak ( type . TYPE + " not inside a loop or switch" ) ;
semicolon ( ) ;
var stat = new type ( { label : label } ) ;
if ( ldef ) ldef . references . push ( stat ) ;
return stat ;
}
function has _modifier ( name ) {
if ( ! is ( "name" , name ) ) return ;
var token = peek ( ) ;
if ( ! token ) return ;
if ( is _token ( token , "operator" , "=" ) ) return ;
if ( token . type == "punc" && /^[(;}]$/ . test ( token . value ) ) return ;
if ( has _newline _before ( token ) ) return ;
return next ( ) ;
}
function class _ ( ctor ) {
var was _async = S . in _async ;
var was _gen = S . in _generator ;
S . input . push _directives _stack ( ) ;
S . input . add _directive ( "use strict" ) ;
var name ;
if ( ctor === AST _DefClass ) {
name = as _symbol ( AST _SymbolDefClass ) ;
} else {
name = as _symbol ( AST _SymbolClass , true ) ;
}
var parent = null ;
if ( is ( "keyword" , "extends" ) ) {
next ( ) ;
handle _regexp ( ) ;
parent = expr _atom ( true ) ;
}
expect ( "{" ) ;
var props = [ ] ;
while ( ! is ( "punc" , "}" ) ) {
if ( is ( "punc" , ";" ) ) {
next ( ) ;
continue ;
}
var start = S . token ;
var fixed = ! ! has _modifier ( "static" ) ;
var async = has _modifier ( "async" ) ;
if ( is ( "operator" , "*" ) ) {
next ( ) ;
var internal = is ( "name" ) && /^#/ . test ( S . token . value ) ;
var key = as _property _key ( ) ;
var gen _start = S . token ;
var gen = function _ ( async ? AST _AsyncGeneratorFunction : AST _GeneratorFunction ) ;
gen . start = gen _start ;
gen . end = prev ( ) ;
props . push ( new AST _ClassMethod ( {
start : start ,
static : fixed ,
private : internal ,
key : key ,
value : gen ,
end : prev ( ) ,
} ) ) ;
continue ;
}
var internal = is ( "name" ) && /^#/ . test ( S . token . value ) ;
var key = as _property _key ( ) ;
if ( is ( "punc" , "(" ) ) {
var func _start = S . token ;
var func = function _ ( async ? AST _AsyncFunction : AST _Function ) ;
func . start = func _start ;
func . end = prev ( ) ;
props . push ( new AST _ClassMethod ( {
start : start ,
static : fixed ,
private : internal ,
key : key ,
value : func ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( async ) unexpected ( async ) ;
var value = null ;
if ( is ( "operator" , "=" ) ) {
next ( ) ;
S . in _async = false ;
S . in _generator = false ;
value = maybe _assign ( ) ;
S . in _generator = was _gen ;
S . in _async = was _async ;
} else if ( ! ( is ( "punc" , ";" ) || is ( "punc" , "}" ) ) ) {
var type = null ;
switch ( key ) {
case "get" :
type = AST _ClassGetter ;
break ;
case "set" :
type = AST _ClassSetter ;
break ;
}
if ( type ) {
props . push ( new type ( {
start : start ,
static : fixed ,
private : is ( "name" ) && /^#/ . test ( S . token . value ) ,
key : as _property _key ( ) ,
value : create _accessor ( ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
}
semicolon ( ) ;
props . push ( new AST _ClassField ( {
start : start ,
static : fixed ,
private : internal ,
key : key ,
value : value ,
end : prev ( ) ,
} ) ) ;
}
next ( ) ;
S . input . pop _directives _stack ( ) ;
S . in _generator = was _gen ;
S . in _async = was _async ;
return new ctor ( {
extends : parent ,
name : name ,
properties : props ,
} ) ;
}
function for _ ( ) {
2022-04-11 08:07:00 +00:00
var await _token = is ( "name" , "await" ) && next ( ) ;
2022-01-21 08:28:41 +00:00
expect ( "(" ) ;
var init = null ;
2022-04-11 08:07:00 +00:00
if ( await _token || ! is ( "punc" , ";" ) ) {
2022-01-21 08:28:41 +00:00
init = is ( "keyword" , "const" )
? ( next ( ) , const _ ( true ) )
: is ( "name" , "let" ) && is _vardefs ( )
? ( next ( ) , let _ ( true ) )
: is ( "keyword" , "var" )
? ( next ( ) , var _ ( true ) )
: expression ( true ) ;
var ctor ;
2022-04-11 08:07:00 +00:00
if ( await _token ) {
2022-01-21 08:28:41 +00:00
expect _token ( "name" , "of" ) ;
ctor = AST _ForAwaitOf ;
} else if ( is ( "operator" , "in" ) ) {
next ( ) ;
ctor = AST _ForIn ;
} else if ( is ( "name" , "of" ) ) {
next ( ) ;
ctor = AST _ForOf ;
}
if ( ctor ) {
if ( init instanceof AST _Definitions ) {
if ( init . definitions . length > 1 ) {
token _error ( init . start , "Only one variable declaration allowed in for..in/of loop" ) ;
}
if ( ctor !== AST _ForIn && init . definitions [ 0 ] . value ) {
token _error ( init . definitions [ 0 ] . value . start , "No initializers allowed in for..of loop" ) ;
}
} else if ( ! ( is _assignable ( init ) || ( init = to _destructured ( init ) ) instanceof AST _Destructured ) ) {
token _error ( init . start , "Invalid left-hand side in for..in/of loop" ) ;
}
return for _enum ( ctor , init ) ;
}
}
return regular _for ( init ) ;
}
function regular _for ( init ) {
expect ( ";" ) ;
var test = is ( "punc" , ";" ) ? null : expression ( ) ;
expect ( ";" ) ;
var step = is ( "punc" , ")" ) ? null : expression ( ) ;
expect ( ")" ) ;
return new AST _For ( {
init : init ,
condition : test ,
step : step ,
body : in _loop ( statement )
} ) ;
}
function for _enum ( ctor , init ) {
handle _regexp ( ) ;
var obj = expression ( ) ;
expect ( ")" ) ;
return new ctor ( {
init : init ,
object : obj ,
body : in _loop ( statement )
} ) ;
}
function to _funarg ( node ) {
if ( node instanceof AST _Array ) {
var rest = null ;
if ( node . elements [ node . elements . length - 1 ] instanceof AST _Spread ) {
rest = to _funarg ( node . elements . pop ( ) . expression ) ;
}
return new AST _DestructuredArray ( {
start : node . start ,
elements : node . elements . map ( to _funarg ) ,
rest : rest ,
end : node . end ,
} ) ;
}
if ( node instanceof AST _Assign ) return new AST _DefaultValue ( {
start : node . start ,
name : to _funarg ( node . left ) ,
value : node . right ,
end : node . end ,
} ) ;
if ( node instanceof AST _DefaultValue ) {
node . name = to _funarg ( node . name ) ;
return node ;
}
if ( node instanceof AST _DestructuredArray ) {
node . elements = node . elements . map ( to _funarg ) ;
if ( node . rest ) node . rest = to _funarg ( node . rest ) ;
return node ;
}
if ( node instanceof AST _DestructuredObject ) {
node . properties . forEach ( function ( prop ) {
prop . value = to _funarg ( prop . value ) ;
} ) ;
if ( node . rest ) node . rest = to _funarg ( node . rest ) ;
return node ;
}
if ( node instanceof AST _Hole ) return node ;
if ( node instanceof AST _Object ) {
var rest = null ;
if ( node . properties [ node . properties . length - 1 ] instanceof AST _Spread ) {
rest = to _funarg ( node . properties . pop ( ) . expression ) ;
}
return new AST _DestructuredObject ( {
start : node . start ,
properties : node . properties . map ( function ( prop ) {
if ( ! ( prop instanceof AST _ObjectKeyVal ) ) token _error ( prop . start , "Invalid destructuring assignment" ) ;
return new AST _DestructuredKeyVal ( {
start : prop . start ,
key : prop . key ,
value : to _funarg ( prop . value ) ,
end : prop . end ,
} ) ;
} ) ,
rest : rest ,
end : node . end ,
} ) ;
}
if ( node instanceof AST _SymbolFunarg ) return node ;
if ( node instanceof AST _SymbolRef ) return new AST _SymbolFunarg ( node ) ;
if ( node instanceof AST _Yield ) return new AST _SymbolFunarg ( {
start : node . start ,
name : "yield" ,
end : node . end ,
} ) ;
token _error ( node . start , "Invalid arrow parameter" ) ;
}
function arrow ( exprs , start , async ) {
var was _async = S . in _async ;
var was _gen = S . in _generator ;
S . in _async = async ;
S . in _generator = false ;
var was _funarg = S . in _funarg ;
S . in _funarg = S . in _function ;
var argnames = exprs . map ( to _funarg ) ;
var rest = exprs . rest || null ;
if ( rest ) rest = to _funarg ( rest ) ;
S . in _funarg = was _funarg ;
expect ( "=>" ) ;
var body , value ;
var loop = S . in _loop ;
var labels = S . labels ;
++ S . in _function ;
S . in _directives = true ;
S . input . push _directives _stack ( ) ;
S . in _loop = 0 ;
S . labels = [ ] ;
if ( is ( "punc" , "{" ) ) {
body = block _ ( ) ;
value = null ;
} else {
body = [ ] ;
handle _regexp ( ) ;
value = maybe _assign ( ) ;
}
var is _strict = S . input . has _directive ( "use strict" ) ;
S . input . pop _directives _stack ( ) ;
-- S . in _function ;
S . in _loop = loop ;
S . labels = labels ;
S . in _generator = was _gen ;
S . in _async = was _async ;
var node = new ( async ? AST _AsyncArrow : AST _Arrow ) ( {
start : start ,
argnames : argnames ,
rest : rest ,
body : body ,
value : value ,
end : prev ( ) ,
} ) ;
if ( is _strict ) node . each _argname ( strict _verify _symbol ) ;
return node ;
}
var function _ = function ( ctor ) {
var was _async = S . in _async ;
var was _gen = S . in _generator ;
var name ;
if ( /Defun$/ . test ( ctor . TYPE ) ) {
name = as _symbol ( AST _SymbolDefun ) ;
S . in _async = /^Async/ . test ( ctor . TYPE ) ;
S . in _generator = /Generator/ . test ( ctor . TYPE ) ;
} else {
S . in _async = /^Async/ . test ( ctor . TYPE ) ;
S . in _generator = /Generator/ . test ( ctor . TYPE ) ;
name = as _symbol ( AST _SymbolLambda , true ) ;
}
if ( name && ctor !== AST _Accessor && ! ( name instanceof AST _SymbolDeclaration ) )
unexpected ( prev ( ) ) ;
expect ( "(" ) ;
var was _funarg = S . in _funarg ;
S . in _funarg = S . in _function ;
var argnames = expr _list ( ")" , ! options . strict , false , function ( ) {
return maybe _default ( AST _SymbolFunarg ) ;
} ) ;
S . in _funarg = was _funarg ;
var loop = S . in _loop ;
var labels = S . labels ;
++ S . in _function ;
S . in _directives = true ;
S . input . push _directives _stack ( ) ;
S . in _loop = 0 ;
S . labels = [ ] ;
var body = block _ ( ) ;
var is _strict = S . input . has _directive ( "use strict" ) ;
S . input . pop _directives _stack ( ) ;
-- S . in _function ;
S . in _loop = loop ;
S . labels = labels ;
S . in _generator = was _gen ;
S . in _async = was _async ;
var node = new ctor ( {
name : name ,
argnames : argnames ,
rest : argnames . rest || null ,
body : body
} ) ;
if ( is _strict ) {
if ( name ) strict _verify _symbol ( name ) ;
node . each _argname ( strict _verify _symbol ) ;
}
return node ;
} ;
function if _ ( ) {
var cond = parenthesised ( ) , body = statement ( ) , belse = null ;
if ( is ( "keyword" , "else" ) ) {
next ( ) ;
belse = statement ( ) ;
}
return new AST _If ( {
condition : cond ,
body : body ,
alternative : belse
} ) ;
}
function is _alias ( ) {
return is ( "name" ) || is _identifier _string ( S . token . value ) ;
}
function export _ ( ) {
if ( is ( "operator" , "*" ) ) {
next ( ) ;
var alias = "*" ;
if ( is ( "name" , "as" ) ) {
next ( ) ;
if ( ! is _alias ( ) ) expect _token ( "name" ) ;
alias = S . token . value ;
next ( ) ;
}
expect _token ( "name" , "from" ) ;
var path = S . token ;
expect _token ( "string" ) ;
semicolon ( ) ;
return new AST _ExportForeign ( {
aliases : [ alias ] ,
keys : [ "*" ] ,
path : path . value ,
quote : path . quote ,
} ) ;
}
if ( is ( "punc" , "{" ) ) {
next ( ) ;
var aliases = [ ] ;
var keys = [ ] ;
while ( is _alias ( ) ) {
var key = S . token ;
next ( ) ;
keys . push ( key ) ;
if ( is ( "name" , "as" ) ) {
next ( ) ;
if ( ! is _alias ( ) ) expect _token ( "name" ) ;
aliases . push ( S . token . value ) ;
next ( ) ;
} else {
aliases . push ( key . value ) ;
}
if ( ! is ( "punc" , "}" ) ) expect ( "," ) ;
}
expect ( "}" ) ;
if ( is ( "name" , "from" ) ) {
next ( ) ;
var path = S . token ;
expect _token ( "string" ) ;
semicolon ( ) ;
return new AST _ExportForeign ( {
aliases : aliases ,
keys : keys . map ( function ( token ) {
return token . value ;
} ) ,
path : path . value ,
quote : path . quote ,
} ) ;
}
semicolon ( ) ;
return new AST _ExportReferences ( {
properties : keys . map ( function ( token , index ) {
if ( ! is _token ( token , "name" ) ) token _error ( token , "Name expected" ) ;
var sym = _make _symbol ( AST _SymbolExport , token ) ;
sym . alias = aliases [ index ] ;
return sym ;
} ) ,
} ) ;
}
if ( is ( "keyword" , "default" ) ) {
next ( ) ;
var start = S . token ;
var body = export _default _decl ( ) ;
if ( body ) {
body . start = start ;
body . end = prev ( ) ;
} else {
handle _regexp ( ) ;
body = expression ( ) ;
semicolon ( ) ;
}
return new AST _ExportDefault ( { body : body } ) ;
}
return new AST _ExportDeclaration ( { body : export _decl ( ) } ) ;
}
function maybe _named ( def , expr ) {
if ( expr . name ) {
expr = new def ( expr ) ;
expr . name = new ( def === AST _DefClass ? AST _SymbolDefClass : AST _SymbolDefun ) ( expr . name ) ;
}
return expr ;
}
function export _default _decl ( ) {
if ( is ( "name" , "async" ) ) {
if ( ! is _token ( peek ( ) , "keyword" , "function" ) ) return ;
next ( ) ;
next ( ) ;
if ( ! is ( "operator" , "*" ) ) return maybe _named ( AST _AsyncDefun , function _ ( AST _AsyncFunction ) ) ;
next ( ) ;
return maybe _named ( AST _AsyncGeneratorDefun , function _ ( AST _AsyncGeneratorFunction ) ) ;
} else if ( is ( "keyword" ) ) switch ( S . token . value ) {
case "class" :
next ( ) ;
return maybe _named ( AST _DefClass , class _ ( AST _ClassExpression ) ) ;
case "function" :
next ( ) ;
if ( ! is ( "operator" , "*" ) ) return maybe _named ( AST _Defun , function _ ( AST _Function ) ) ;
next ( ) ;
return maybe _named ( AST _GeneratorDefun , function _ ( AST _GeneratorFunction ) ) ;
}
}
var export _decl = embed _tokens ( function ( ) {
if ( is ( "name" ) ) switch ( S . token . value ) {
case "async" :
next ( ) ;
expect _token ( "keyword" , "function" ) ;
if ( ! is ( "operator" , "*" ) ) return function _ ( AST _AsyncDefun ) ;
next ( ) ;
return function _ ( AST _AsyncGeneratorDefun ) ;
case "let" :
next ( ) ;
var node = let _ ( ) ;
semicolon ( ) ;
return node ;
} else if ( is ( "keyword" ) ) switch ( S . token . value ) {
case "class" :
next ( ) ;
return class _ ( AST _DefClass ) ;
case "const" :
next ( ) ;
var node = const _ ( ) ;
semicolon ( ) ;
return node ;
case "function" :
next ( ) ;
if ( ! is ( "operator" , "*" ) ) return function _ ( AST _Defun ) ;
next ( ) ;
return function _ ( AST _GeneratorDefun ) ;
case "var" :
next ( ) ;
var node = var _ ( ) ;
semicolon ( ) ;
return node ;
}
unexpected ( ) ;
} ) ;
function import _ ( ) {
var all = null ;
var def = as _symbol ( AST _SymbolImport , true ) ;
var props = null ;
if ( def ? ( def . key = "" , is ( "punc" , "," ) && next ( ) ) : ! is ( "string" ) ) {
if ( is ( "operator" , "*" ) ) {
next ( ) ;
expect _token ( "name" , "as" ) ;
all = as _symbol ( AST _SymbolImport ) ;
all . key = "*" ;
} else {
expect ( "{" ) ;
props = [ ] ;
while ( is _alias ( ) ) {
var alias ;
if ( is _token ( peek ( ) , "name" , "as" ) ) {
var key = S . token . value ;
next ( ) ;
next ( ) ;
alias = as _symbol ( AST _SymbolImport ) ;
alias . key = key ;
} else {
alias = as _symbol ( AST _SymbolImport ) ;
alias . key = alias . name ;
}
props . push ( alias ) ;
if ( ! is ( "punc" , "}" ) ) expect ( "," ) ;
}
expect ( "}" ) ;
}
}
if ( all || def || props ) expect _token ( "name" , "from" ) ;
var path = S . token ;
expect _token ( "string" ) ;
semicolon ( ) ;
return new AST _Import ( {
all : all ,
default : def ,
path : path . value ,
properties : props ,
quote : path . quote ,
} ) ;
}
function block _ ( ) {
expect ( "{" ) ;
var a = [ ] ;
while ( ! is ( "punc" , "}" ) ) {
if ( is ( "eof" ) ) expect ( "}" ) ;
a . push ( statement ( ) ) ;
}
next ( ) ;
return a ;
}
function switch _body _ ( ) {
expect ( "{" ) ;
var a = [ ] , branch , cur , default _branch , tmp ;
while ( ! is ( "punc" , "}" ) ) {
if ( is ( "eof" ) ) expect ( "}" ) ;
if ( is ( "keyword" , "case" ) ) {
if ( branch ) branch . end = prev ( ) ;
cur = [ ] ;
branch = new AST _Case ( {
start : ( tmp = S . token , next ( ) , tmp ) ,
expression : expression ( ) ,
body : cur
} ) ;
a . push ( branch ) ;
expect ( ":" ) ;
} else if ( is ( "keyword" , "default" ) ) {
if ( branch ) branch . end = prev ( ) ;
if ( default _branch ) croak ( "More than one default clause in switch statement" ) ;
cur = [ ] ;
branch = new AST _Default ( {
start : ( tmp = S . token , next ( ) , expect ( ":" ) , tmp ) ,
body : cur
} ) ;
a . push ( branch ) ;
default _branch = branch ;
} else {
if ( ! cur ) unexpected ( ) ;
cur . push ( statement ( ) ) ;
}
}
if ( branch ) branch . end = prev ( ) ;
next ( ) ;
return a ;
}
function try _ ( ) {
var body = block _ ( ) , bcatch = null , bfinally = null ;
if ( is ( "keyword" , "catch" ) ) {
var start = S . token ;
next ( ) ;
var name = null ;
if ( is ( "punc" , "(" ) ) {
next ( ) ;
name = maybe _destructured ( AST _SymbolCatch ) ;
expect ( ")" ) ;
}
bcatch = new AST _Catch ( {
start : start ,
argname : name ,
body : block _ ( ) ,
end : prev ( )
} ) ;
}
if ( is ( "keyword" , "finally" ) ) {
var start = S . token ;
next ( ) ;
bfinally = new AST _Finally ( {
start : start ,
body : block _ ( ) ,
end : prev ( )
} ) ;
}
if ( ! bcatch && ! bfinally )
croak ( "Missing catch/finally blocks" ) ;
return new AST _Try ( {
body : body ,
bcatch : bcatch ,
bfinally : bfinally
} ) ;
}
function vardefs ( type , no _in ) {
var a = [ ] ;
for ( ; ; ) {
var start = S . token ;
var name = maybe _destructured ( type ) ;
var value = null ;
if ( is ( "operator" , "=" ) ) {
next ( ) ;
value = maybe _assign ( no _in ) ;
} else if ( ! no _in && ( type === AST _SymbolConst || name instanceof AST _Destructured ) ) {
croak ( "Missing initializer in declaration" ) ;
}
a . push ( new AST _VarDef ( {
start : start ,
name : name ,
value : value ,
end : prev ( )
} ) ) ;
if ( ! is ( "punc" , "," ) )
break ;
next ( ) ;
}
return a ;
}
function is _vardefs ( ) {
var token = peek ( ) ;
return is _token ( token , "name" ) || is _token ( token , "punc" , "[" ) || is _token ( token , "punc" , "{" ) ;
}
var const _ = function ( no _in ) {
return new AST _Const ( {
start : prev ( ) ,
definitions : vardefs ( AST _SymbolConst , no _in ) ,
end : prev ( )
} ) ;
} ;
var let _ = function ( no _in ) {
return new AST _Let ( {
start : prev ( ) ,
definitions : vardefs ( AST _SymbolLet , no _in ) ,
end : prev ( )
} ) ;
} ;
var var _ = function ( no _in ) {
return new AST _Var ( {
start : prev ( ) ,
definitions : vardefs ( AST _SymbolVar , no _in ) ,
end : prev ( )
} ) ;
} ;
var new _ = function ( allow _calls ) {
var start = S . token ;
expect _token ( "operator" , "new" ) ;
var call ;
if ( is ( "punc" , "." ) && is _token ( peek ( ) , "name" , "target" ) ) {
next ( ) ;
next ( ) ;
call = new AST _NewTarget ( ) ;
} else {
var exp = expr _atom ( false ) , args ;
if ( is ( "punc" , "(" ) ) {
next ( ) ;
args = expr _list ( ")" , ! options . strict ) ;
} else {
args = [ ] ;
}
call = new AST _New ( { expression : exp , args : args } ) ;
}
call . start = start ;
call . end = prev ( ) ;
return subscripts ( call , allow _calls ) ;
} ;
function as _atom _node ( ) {
var ret , tok = S . token , value = tok . value ;
switch ( tok . type ) {
case "num" :
if ( isFinite ( value ) ) {
ret = new AST _Number ( { value : value } ) ;
} else {
ret = new AST _Infinity ( ) ;
if ( value < 0 ) ret = new AST _UnaryPrefix ( { operator : "-" , expression : ret } ) ;
}
break ;
case "bigint" :
ret = new AST _BigInt ( { value : value } ) ;
break ;
case "string" :
ret = new AST _String ( { value : value , quote : tok . quote } ) ;
break ;
case "regexp" :
ret = new AST _RegExp ( { value : value } ) ;
break ;
case "atom" :
switch ( value ) {
case "false" :
ret = new AST _False ( ) ;
break ;
case "true" :
ret = new AST _True ( ) ;
break ;
case "null" :
ret = new AST _Null ( ) ;
break ;
default :
unexpected ( ) ;
}
break ;
default :
unexpected ( ) ;
}
next ( ) ;
ret . start = ret . end = tok ;
return ret ;
}
var expr _atom = function ( allow _calls ) {
if ( is ( "operator" , "new" ) ) {
return new _ ( allow _calls ) ;
}
var start = S . token ;
if ( is ( "punc" ) ) {
switch ( start . value ) {
case "`" :
return subscripts ( template ( null ) , allow _calls ) ;
case "(" :
next ( ) ;
if ( is ( "punc" , ")" ) ) {
next ( ) ;
return arrow ( [ ] , start ) ;
}
var ex = expression ( false , true ) ;
var len = start . comments _before . length ;
[ ] . unshift . apply ( ex . start . comments _before , start . comments _before ) ;
start . comments _before . length = 0 ;
start . comments _before = ex . start . comments _before ;
start . comments _before _length = len ;
if ( len == 0 && start . comments _before . length > 0 ) {
var comment = start . comments _before [ 0 ] ;
if ( ! comment . nlb ) {
comment . nlb = start . nlb ;
start . nlb = false ;
}
}
start . comments _after = ex . start . comments _after ;
ex . start = start ;
expect ( ")" ) ;
var end = prev ( ) ;
end . comments _before = ex . end . comments _before ;
end . comments _after . forEach ( function ( comment ) {
ex . end . comments _after . push ( comment ) ;
if ( comment . nlb ) S . token . nlb = true ;
} ) ;
end . comments _after . length = 0 ;
end . comments _after = ex . end . comments _after ;
ex . end = end ;
if ( is ( "punc" , "=>" ) ) return arrow ( ex instanceof AST _Sequence ? ex . expressions : [ ex ] , start ) ;
return subscripts ( ex , allow _calls ) ;
case "[" :
return subscripts ( array _ ( ) , allow _calls ) ;
case "{" :
return subscripts ( object _ ( ) , allow _calls ) ;
}
unexpected ( ) ;
}
if ( is ( "keyword" ) ) switch ( start . value ) {
case "class" :
next ( ) ;
var clazz = class _ ( AST _ClassExpression ) ;
clazz . start = start ;
clazz . end = prev ( ) ;
return subscripts ( clazz , allow _calls ) ;
case "function" :
next ( ) ;
var func ;
if ( is ( "operator" , "*" ) ) {
next ( ) ;
func = function _ ( AST _GeneratorFunction ) ;
} else {
func = function _ ( AST _Function ) ;
}
func . start = start ;
func . end = prev ( ) ;
return subscripts ( func , allow _calls ) ;
}
if ( is ( "name" ) ) {
var sym = _make _symbol ( AST _SymbolRef , start ) ;
next ( ) ;
if ( sym . name == "async" ) {
if ( is ( "keyword" , "function" ) ) {
next ( ) ;
var func ;
if ( is ( "operator" , "*" ) ) {
next ( ) ;
func = function _ ( AST _AsyncGeneratorFunction ) ;
} else {
func = function _ ( AST _AsyncFunction ) ;
}
func . start = start ;
func . end = prev ( ) ;
return subscripts ( func , allow _calls ) ;
}
if ( is ( "name" ) && is _token ( peek ( ) , "punc" , "=>" ) ) {
start = S . token ;
sym = _make _symbol ( AST _SymbolRef , start ) ;
next ( ) ;
return arrow ( [ sym ] , start , true ) ;
}
if ( is ( "punc" , "(" ) ) {
var call = subscripts ( sym , allow _calls ) ;
if ( ! is ( "punc" , "=>" ) ) return call ;
var args = call . args ;
if ( args [ args . length - 1 ] instanceof AST _Spread ) {
args . rest = args . pop ( ) . expression ;
}
return arrow ( args , start , true ) ;
}
}
return is ( "punc" , "=>" ) ? arrow ( [ sym ] , start ) : subscripts ( sym , allow _calls ) ;
}
if ( ATOMIC _START _TOKEN [ S . token . type ] ) {
return subscripts ( as _atom _node ( ) , allow _calls ) ;
}
unexpected ( ) ;
} ;
function expr _list ( closing , allow _trailing _comma , allow _empty , parser ) {
if ( ! parser ) parser = maybe _assign ;
var first = true , a = [ ] ;
while ( ! is ( "punc" , closing ) ) {
if ( first ) first = false ; else expect ( "," ) ;
if ( allow _trailing _comma && is ( "punc" , closing ) ) break ;
if ( allow _empty && is ( "punc" , "," ) ) {
a . push ( new AST _Hole ( { start : S . token , end : S . token } ) ) ;
} else if ( ! is ( "operator" , "..." ) ) {
a . push ( parser ( ) ) ;
} else if ( parser === maybe _assign ) {
a . push ( new AST _Spread ( {
start : S . token ,
expression : ( next ( ) , parser ( ) ) ,
end : prev ( ) ,
} ) ) ;
} else {
next ( ) ;
a . rest = parser ( ) ;
if ( a . rest instanceof AST _DefaultValue ) token _error ( a . rest . start , "Invalid rest parameter" ) ;
break ;
}
}
expect ( closing ) ;
return a ;
}
var array _ = embed _tokens ( function ( ) {
expect ( "[" ) ;
return new AST _Array ( {
elements : expr _list ( "]" , ! options . strict , true )
} ) ;
} ) ;
var create _accessor = embed _tokens ( function ( ) {
return function _ ( AST _Accessor ) ;
} ) ;
var object _ = embed _tokens ( function ( ) {
expect ( "{" ) ;
var first = true , a = [ ] ;
while ( ! is ( "punc" , "}" ) ) {
if ( first ) first = false ; else expect ( "," ) ;
// allow trailing comma
if ( ! options . strict && is ( "punc" , "}" ) ) break ;
var start = S . token ;
if ( is ( "operator" , "*" ) ) {
next ( ) ;
var key = as _property _key ( ) ;
var gen _start = S . token ;
var gen = function _ ( AST _GeneratorFunction ) ;
gen . start = gen _start ;
gen . end = prev ( ) ;
a . push ( new AST _ObjectMethod ( {
start : start ,
key : key ,
value : gen ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( is ( "operator" , "..." ) ) {
next ( ) ;
a . push ( new AST _Spread ( {
start : start ,
expression : maybe _assign ( ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( is _token ( peek ( ) , "operator" , "=" ) ) {
var name = as _symbol ( AST _SymbolRef ) ;
next ( ) ;
a . push ( new AST _ObjectKeyVal ( {
start : start ,
key : start . value ,
value : new AST _Assign ( {
start : start ,
left : name ,
operator : "=" ,
right : maybe _assign ( ) ,
end : prev ( ) ,
} ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( is _token ( peek ( ) , "punc" , "," ) || is _token ( peek ( ) , "punc" , "}" ) ) {
a . push ( new AST _ObjectKeyVal ( {
start : start ,
key : start . value ,
value : as _symbol ( AST _SymbolRef ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
var key = as _property _key ( ) ;
if ( is ( "punc" , "(" ) ) {
var func _start = S . token ;
var func = function _ ( AST _Function ) ;
func . start = func _start ;
func . end = prev ( ) ;
a . push ( new AST _ObjectMethod ( {
start : start ,
key : key ,
value : func ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( is ( "punc" , ":" ) ) {
next ( ) ;
a . push ( new AST _ObjectKeyVal ( {
start : start ,
key : key ,
value : maybe _assign ( ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( start . type == "name" ) switch ( key ) {
case "async" :
var is _gen = is ( "operator" , "*" ) && next ( ) ;
key = as _property _key ( ) ;
var func _start = S . token ;
var func = function _ ( is _gen ? AST _AsyncGeneratorFunction : AST _AsyncFunction ) ;
func . start = func _start ;
func . end = prev ( ) ;
a . push ( new AST _ObjectMethod ( {
start : start ,
key : key ,
value : func ,
end : prev ( ) ,
} ) ) ;
continue ;
case "get" :
a . push ( new AST _ObjectGetter ( {
start : start ,
key : as _property _key ( ) ,
value : create _accessor ( ) ,
end : prev ( ) ,
} ) ) ;
continue ;
case "set" :
a . push ( new AST _ObjectSetter ( {
start : start ,
key : as _property _key ( ) ,
value : create _accessor ( ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
unexpected ( ) ;
}
next ( ) ;
return new AST _Object ( { properties : a } ) ;
} ) ;
function as _property _key ( ) {
var tmp = S . token ;
switch ( tmp . type ) {
case "operator" :
if ( ! KEYWORDS [ tmp . value ] ) unexpected ( ) ;
case "num" :
case "string" :
case "name" :
case "keyword" :
case "atom" :
next ( ) ;
return "" + tmp . value ;
case "punc" :
expect ( "[" ) ;
var key = maybe _assign ( ) ;
expect ( "]" ) ;
return key ;
default :
unexpected ( ) ;
}
}
function as _name ( ) {
var name = S . token . value ;
expect _token ( "name" ) ;
return name ;
}
function _make _symbol ( type , token ) {
var name = token . value ;
switch ( name ) {
case "await" :
if ( S . in _async ) unexpected ( token ) ;
break ;
case "super" :
type = AST _Super ;
break ;
case "this" :
type = AST _This ;
break ;
case "yield" :
if ( S . in _generator ) unexpected ( token ) ;
break ;
}
return new type ( {
name : "" + name ,
start : token ,
end : token ,
} ) ;
}
function strict _verify _symbol ( sym ) {
if ( sym . name == "arguments" || sym . name == "eval" || sym . name == "let" )
token _error ( sym . start , "Unexpected " + sym . name + " in strict mode" ) ;
}
function as _symbol ( type , noerror ) {
if ( ! is ( "name" ) ) {
if ( ! noerror ) croak ( "Name expected" ) ;
return null ;
}
var sym = _make _symbol ( type , S . token ) ;
if ( S . input . has _directive ( "use strict" ) && sym instanceof AST _SymbolDeclaration ) {
strict _verify _symbol ( sym ) ;
}
next ( ) ;
return sym ;
}
function maybe _destructured ( type ) {
var start = S . token ;
if ( is ( "punc" , "[" ) ) {
next ( ) ;
var elements = expr _list ( "]" , ! options . strict , true , function ( ) {
return maybe _default ( type ) ;
} ) ;
return new AST _DestructuredArray ( {
start : start ,
elements : elements ,
rest : elements . rest || null ,
end : prev ( ) ,
} ) ;
}
if ( is ( "punc" , "{" ) ) {
next ( ) ;
var first = true , a = [ ] , rest = null ;
while ( ! is ( "punc" , "}" ) ) {
if ( first ) first = false ; else expect ( "," ) ;
// allow trailing comma
if ( ! options . strict && is ( "punc" , "}" ) ) break ;
var key _start = S . token ;
if ( is ( "punc" , "[" ) || is _token ( peek ( ) , "punc" , ":" ) ) {
var key = as _property _key ( ) ;
expect ( ":" ) ;
a . push ( new AST _DestructuredKeyVal ( {
start : key _start ,
key : key ,
value : maybe _default ( type ) ,
end : prev ( ) ,
} ) ) ;
continue ;
}
if ( is ( "operator" , "..." ) ) {
next ( ) ;
rest = maybe _destructured ( type ) ;
break ;
}
var name = as _symbol ( type ) ;
if ( is ( "operator" , "=" ) ) {
next ( ) ;
name = new AST _DefaultValue ( {
start : name . start ,
name : name ,
value : maybe _assign ( ) ,
end : prev ( ) ,
} ) ;
}
a . push ( new AST _DestructuredKeyVal ( {
start : key _start ,
key : key _start . value ,
value : name ,
end : prev ( ) ,
} ) ) ;
}
expect ( "}" ) ;
return new AST _DestructuredObject ( {
start : start ,
properties : a ,
rest : rest ,
end : prev ( ) ,
} ) ;
}
return as _symbol ( type ) ;
}
function maybe _default ( type ) {
var start = S . token ;
var name = maybe _destructured ( type ) ;
if ( ! is ( "operator" , "=" ) ) return name ;
next ( ) ;
return new AST _DefaultValue ( {
start : start ,
name : name ,
value : maybe _assign ( ) ,
end : prev ( ) ,
} ) ;
}
function template ( tag ) {
var start = tag ? tag . start : S . token ;
var read = S . input . context ( ) . read _template ;
var strings = [ ] ;
var expressions = [ ] ;
while ( read ( strings ) ) {
next ( ) ;
expressions . push ( expression ( ) ) ;
if ( ! is ( "punc" , "}" ) ) unexpected ( ) ;
}
next ( ) ;
return new AST _Template ( {
start : start ,
expressions : expressions ,
strings : strings ,
tag : tag ,
end : prev ( ) ,
} ) ;
}
function subscripts ( expr , allow _calls ) {
var start = expr . start ;
var optional = null ;
while ( true ) {
if ( is ( "operator" , "?" ) && is _token ( peek ( ) , "punc" , "." ) ) {
next ( ) ;
next ( ) ;
optional = expr ;
}
if ( is ( "punc" , "[" ) ) {
next ( ) ;
var prop = expression ( ) ;
expect ( "]" ) ;
expr = new AST _Sub ( {
start : start ,
optional : optional === expr ,
expression : expr ,
property : prop ,
end : prev ( ) ,
} ) ;
} else if ( allow _calls && is ( "punc" , "(" ) ) {
next ( ) ;
expr = new AST _Call ( {
start : start ,
optional : optional === expr ,
expression : expr ,
args : expr _list ( ")" , ! options . strict ) ,
end : prev ( ) ,
} ) ;
} else if ( optional === expr || is ( "punc" , "." ) ) {
if ( optional !== expr ) next ( ) ;
expr = new AST _Dot ( {
start : start ,
optional : optional === expr ,
expression : expr ,
property : as _name ( ) ,
end : prev ( ) ,
} ) ;
} else if ( is ( "punc" , "`" ) ) {
if ( optional ) croak ( "Invalid template on optional chain" ) ;
expr = template ( expr ) ;
} else {
break ;
}
}
if ( optional ) expr . terminal = true ;
if ( expr instanceof AST _Call && ! expr . pure ) {
var start = expr . start ;
var comments = start . comments _before ;
var i = HOP ( start , "comments_before_length" ) ? start . comments _before _length : comments . length ;
while ( -- i >= 0 ) {
if ( /[@#]__PURE__/ . test ( comments [ i ] . value ) ) {
expr . pure = true ;
break ;
}
}
}
return expr ;
}
function maybe _unary ( no _in ) {
var start = S . token ;
if ( S . in _async && is ( "name" , "await" ) ) {
if ( S . in _funarg === S . in _function ) croak ( "Invalid use of await in function argument" ) ;
S . input . context ( ) . regex _allowed = true ;
next ( ) ;
return new AST _Await ( {
start : start ,
expression : maybe _unary ( no _in ) ,
end : prev ( ) ,
} ) ;
}
if ( S . in _generator && is ( "name" , "yield" ) ) {
if ( S . in _funarg === S . in _function ) croak ( "Invalid use of yield in function argument" ) ;
S . input . context ( ) . regex _allowed = true ;
next ( ) ;
var exp = null ;
var nested = false ;
if ( is ( "operator" , "*" ) ) {
next ( ) ;
exp = maybe _assign ( no _in ) ;
nested = true ;
} else if ( is ( "punc" ) ? ! PUNC _AFTER _EXPRESSION [ S . token . value ] : ! can _insert _semicolon ( ) ) {
exp = maybe _assign ( no _in ) ;
}
return new AST _Yield ( {
start : start ,
expression : exp ,
nested : nested ,
end : prev ( ) ,
} ) ;
}
if ( is ( "operator" ) && UNARY _PREFIX [ start . value ] ) {
next ( ) ;
handle _regexp ( ) ;
var ex = make _unary ( AST _UnaryPrefix , start , maybe _unary ( no _in ) ) ;
ex . start = start ;
ex . end = prev ( ) ;
return ex ;
}
var val = expr _atom ( true ) ;
while ( is ( "operator" ) && UNARY _POSTFIX [ S . token . value ] && ! has _newline _before ( S . token ) ) {
val = make _unary ( AST _UnaryPostfix , S . token , val ) ;
val . start = start ;
val . end = S . token ;
next ( ) ;
}
return val ;
}
function make _unary ( ctor , token , expr ) {
var op = token . value ;
switch ( op ) {
case "++" :
case "--" :
if ( ! is _assignable ( expr ) )
token _error ( token , "Invalid use of " + op + " operator" ) ;
break ;
case "delete" :
if ( expr instanceof AST _SymbolRef && S . input . has _directive ( "use strict" ) )
token _error ( expr . start , "Calling delete on expression not allowed in strict mode" ) ;
break ;
}
return new ctor ( { operator : op , expression : expr } ) ;
}
var expr _op = function ( left , min _prec , no _in ) {
var op = is ( "operator" ) ? S . token . value : null ;
if ( op == "in" && no _in ) op = null ;
var prec = op != null ? PRECEDENCE [ op ] : null ;
if ( prec != null && prec > min _prec ) {
next ( ) ;
var right = expr _op ( maybe _unary ( no _in ) , op == "**" ? prec - 1 : prec , no _in ) ;
return expr _op ( new AST _Binary ( {
start : left . start ,
left : left ,
operator : op ,
right : right ,
end : right . end
} ) , min _prec , no _in ) ;
}
return left ;
} ;
function expr _ops ( no _in ) {
return expr _op ( maybe _unary ( no _in ) , 0 , no _in ) ;
}
var maybe _conditional = function ( no _in ) {
var start = S . token ;
var expr = expr _ops ( no _in ) ;
if ( is ( "operator" , "?" ) ) {
next ( ) ;
var yes = maybe _assign ( ) ;
expect ( ":" ) ;
return new AST _Conditional ( {
start : start ,
condition : expr ,
consequent : yes ,
alternative : maybe _assign ( no _in ) ,
end : prev ( )
} ) ;
}
return expr ;
} ;
function is _assignable ( expr ) {
return expr instanceof AST _PropAccess && ! expr . optional || expr instanceof AST _SymbolRef ;
}
function to _destructured ( node ) {
if ( node instanceof AST _Array ) {
var rest = null ;
if ( node . elements [ node . elements . length - 1 ] instanceof AST _Spread ) {
rest = to _destructured ( node . elements . pop ( ) . expression ) ;
if ( ! ( rest instanceof AST _Destructured || is _assignable ( rest ) ) ) return node ;
}
var elements = node . elements . map ( to _destructured ) ;
return all ( elements , function ( node ) {
return node instanceof AST _DefaultValue
|| node instanceof AST _Destructured
|| node instanceof AST _Hole
|| is _assignable ( node ) ;
} ) ? new AST _DestructuredArray ( {
start : node . start ,
elements : elements ,
rest : rest ,
end : node . end ,
} ) : node ;
}
if ( node instanceof AST _Assign ) {
var name = to _destructured ( node . left ) ;
return name instanceof AST _Destructured || is _assignable ( name ) ? new AST _DefaultValue ( {
start : node . start ,
name : name ,
value : node . right ,
end : node . end ,
} ) : node ;
}
if ( ! ( node instanceof AST _Object ) ) return node ;
var rest = null ;
if ( node . properties [ node . properties . length - 1 ] instanceof AST _Spread ) {
rest = to _destructured ( node . properties . pop ( ) . expression ) ;
if ( ! ( rest instanceof AST _Destructured || is _assignable ( rest ) ) ) return node ;
}
var props = [ ] ;
for ( var i = 0 ; i < node . properties . length ; i ++ ) {
var prop = node . properties [ i ] ;
if ( ! ( prop instanceof AST _ObjectKeyVal ) ) return node ;
var value = to _destructured ( prop . value ) ;
if ( ! ( value instanceof AST _DefaultValue || value instanceof AST _Destructured || is _assignable ( value ) ) ) {
return node ;
}
props . push ( new AST _DestructuredKeyVal ( {
start : prop . start ,
key : prop . key ,
value : value ,
end : prop . end ,
} ) ) ;
}
return new AST _DestructuredObject ( {
start : node . start ,
properties : props ,
rest : rest ,
end : node . end ,
} ) ;
}
function maybe _assign ( no _in ) {
var start = S . token ;
var left = maybe _conditional ( no _in ) , val = S . token . value ;
if ( is ( "operator" ) && ASSIGNMENT [ val ] ) {
if ( is _assignable ( left ) || val == "=" && ( left = to _destructured ( left ) ) instanceof AST _Destructured ) {
next ( ) ;
return new AST _Assign ( {
start : start ,
left : left ,
operator : val ,
right : maybe _assign ( no _in ) ,
end : prev ( )
} ) ;
}
croak ( "Invalid assignment" ) ;
}
return left ;
}
function expression ( no _in , maybe _arrow ) {
var start = S . token ;
var exprs = [ ] ;
while ( true ) {
if ( maybe _arrow && is ( "operator" , "..." ) ) {
next ( ) ;
exprs . rest = maybe _destructured ( AST _SymbolFunarg ) ;
break ;
}
exprs . push ( maybe _assign ( no _in ) ) ;
if ( ! is ( "punc" , "," ) ) break ;
next ( ) ;
if ( maybe _arrow && is ( "punc" , ")" ) && is _token ( peek ( ) , "punc" , "=>" ) ) break ;
}
return exprs . length == 1 && ! exprs . rest ? exprs [ 0 ] : new AST _Sequence ( {
start : start ,
expressions : exprs ,
end : prev ( ) ,
} ) ;
}
function in _loop ( cont ) {
++ S . in _loop ;
var ret = cont ( ) ;
-- S . in _loop ;
return ret ;
}
if ( options . expression ) {
handle _regexp ( ) ;
var exp = expression ( ) ;
expect _token ( "eof" ) ;
return exp ;
}
return function ( ) {
var start = S . token ;
var body = [ ] ;
S . input . push _directives _stack ( ) ;
while ( ! is ( "eof" ) )
body . push ( statement ( ) ) ;
S . input . pop _directives _stack ( ) ;
var end = prev ( ) || start ;
var toplevel = options . toplevel ;
if ( toplevel ) {
toplevel . body = toplevel . body . concat ( body ) ;
toplevel . end = end ;
} else {
toplevel = new AST _Toplevel ( { start : start , body : body , end : end } ) ;
}
return toplevel ;
} ( ) ;
}