2012-08-22 18:28:59 +00:00
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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 :
2012-08-27 08:01:27 +00:00
Copyright 2012 ( c ) Mihai Bazon < mihai . bazon @ gmail . com >
2012-08-22 18:28:59 +00:00
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
2012-10-02 09:45:31 +00:00
"use strict" ;
2012-10-11 08:52:05 +00:00
var KEYWORDS = 'break case catch const continue debugger default delete do else 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 boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile'
+ " " + KEYWORDS _ATOM + " " + KEYWORDS ;
var KEYWORDS _BEFORE _EXPRESSION = 'return new delete throw else case' ;
2012-05-27 11:09:01 +00:00
2012-10-11 08:52:05 +00:00
KEYWORDS = makePredicate ( KEYWORDS ) ;
RESERVED _WORDS = makePredicate ( RESERVED _WORDS ) ;
KEYWORDS _BEFORE _EXPRESSION = makePredicate ( KEYWORDS _BEFORE _EXPRESSION ) ;
KEYWORDS _ATOM = makePredicate ( KEYWORDS _ATOM ) ;
2012-05-27 11:09:01 +00:00
2012-10-11 08:52:05 +00:00
var OPERATOR _CHARS = makePredicate ( characters ( "+-*&%=<>!?|~^" ) ) ;
2012-05-27 11:09:01 +00:00
var RE _HEX _NUMBER = /^0x[0-9a-f]+$/i ;
var RE _OCT _NUMBER = /^0[0-7]+$/ ;
var RE _DEC _NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i ;
2012-10-11 08:52:05 +00:00
var OPERATORS = makePredicate ( [
2012-05-27 11:09:01 +00:00
"in" ,
"instanceof" ,
"typeof" ,
"new" ,
"void" ,
"delete" ,
"++" ,
"--" ,
"+" ,
"-" ,
"!" ,
"~" ,
"&" ,
"|" ,
"^" ,
"*" ,
"/" ,
"%" ,
">>" ,
"<<" ,
">>>" ,
"<" ,
">" ,
"<=" ,
">=" ,
"==" ,
"===" ,
"!=" ,
"!==" ,
"?" ,
"=" ,
"+=" ,
"-=" ,
"/=" ,
"*=" ,
"%=" ,
">>=" ,
"<<=" ,
">>>=" ,
"|=" ,
"^=" ,
"&=" ,
"&&" ,
"||"
] ) ;
2012-10-11 08:52:05 +00:00
var WHITESPACE _CHARS = makePredicate ( characters ( " \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000" ) ) ;
2012-05-27 11:09:01 +00:00
2012-10-11 08:52:05 +00:00
var PUNC _BEFORE _EXPRESSION = makePredicate ( characters ( "[{(,.;:" ) ) ;
2012-05-27 11:09:01 +00:00
2012-10-11 08:52:05 +00:00
var PUNC _CHARS = makePredicate ( characters ( "[]{}(),;:" ) ) ;
2012-05-27 11:09:01 +00:00
2012-10-11 08:52:05 +00:00
var REGEXP _MODIFIERS = makePredicate ( characters ( "gmsiy" ) ) ;
2012-05-27 11:09:01 +00:00
/* -----[ Tokenizer ]----- */
// regexps adapted from http://xregexp.com/plugins/#unicode
var UNICODE = {
letter : new RegExp ( " [ \ \u0041 - \ \u005A \ \u0061 - \ \u007A \ \u00AA \ \u00B5 \ \u00BA \ \u00C0 - \ \u00D6 \ \u00D8 - \ \u00F6 \ \u00F8 - \ \u02C1 \ \u02C6 - \ \u02D1 \ \u02E0 - \ \u02E4 \ \u02EC \ \u02EE \ \u0370 - \ \u0374 \ \u0376 \ \u0377 \ \u037A - \ \u037D \ \u0386 \ \u0388 - \ \u038A \ \u038C \ \u038E - \ \u03A1 \ \u03A3 - \ \u03F5 \ \u03F7 - \ \u0481 \ \u048A - \ \u0523 \ \u0531 - \ \u0556 \ \u0559 \ \u0561 - \ \u0587 \ \u05D0 - \ \u05EA \ \u05F0 - \ \u05F2 \ \u0621 - \ \u064A \ \u066E \ \u066F \ \u0671 - \ \u06D3 \ \u06D5 \ \u06E5 \ \u06E6 \ \u06EE \ \u06EF \ \u06FA - \ \u06FC \ \u06FF \ \u0710 \ \u0712 - \ \u072F \ \u074D - \ \u07A5 \ \u07B1 \ \u07CA - \ \u07EA \ \u07F4 \ \u07F5 \ \u07FA \ \u0904 - \ \u0939 \ \u093D \ \u0950 \ \u0958 - \ \u0961 \ \u0971 \ \u0972 \ \u097B - \ \u097F \ \u0985 - \ \u098C \ \u098F \ \u0990 \ \u0993 - \ \u09A8 \ \u09AA - \ \u09B0 \ \u09B2 \ \u09B6 - \ \u09B9 \ \u09BD \ \u09CE \ \u09DC \ \u09DD \ \u09DF - \ \u09E1 \ \u09F0 \ \u09F1 \ \u0A05 - \ \u0A0A \ \u0A0F \ \u0A10 \ \u0A13 - \ \u0A28 \ \u0A2A - \ \u0A30 \ \u0A32 \ \u0A33 \ \u0A35 \ \u0A36 \ \u0A38 \ \u0A39 \ \u0A59 - \ \u0A5C \ \u0A5E \ \u0A72 - \ \u0A74 \ \u0A85 - \ \u0A8D \ \u0A8F - \ \u0A91 \ \u0A93 - \ \u0AA8 \ \u0AAA - \ \u0AB0 \ \u0AB2 \ \u0AB3 \ \u0AB5 - \ \u0AB9 \ \u0ABD \ \u0AD0 \ \u0AE0 \ \u0AE1 \ \u0B05 - \ \u0B0C \ \u0B0F \ \u0B10 \ \u0B13 - \ \u0B28 \ \u0B2A - \ \u0B30 \ \u0B32 \ \u0B33 \ \u0B35 - \ \u0B39 \ \u0B3D \ \u0B5C \ \u0B5D \ \u0B5F - \ \u0B61 \ \u0B71 \ \u0B83 \ \u0B85 - \ \u0B8A \ \u0B8E - \ \u0B90 \ \u0B92 - \ \u0B95 \ \u0B99 \ \u0B9A \ \u0B9C \ \u0B9E \ \u0B9F \ \u0BA3 \ \u0BA4 \ \u0BA8 - \ \u0BAA \ \u0BAE - \ \u0BB9 \ \u0BD0 \ \u0C05 - \ \u0C0C \ \u0C0E - \ \u0C10 \ \u0C12 - \ \u0C28 \ \u0C2A - \ \u0C33 \ \u0C35 - \ \u0C39 \ \u0C3D \ \u0C58 \ \u0C59 \ \u0C60 \ \u0C61 \ \u0C85 - \ \u0C8C \ \u0C8E - \ \u0C90 \ \u0C92 - \ \u0CA8 \ \u0CAA - \ \u0CB3 \ \u0CB5 - \ \u0CB9 \ \u0CBD \ \u0CDE \ \u0CE0 \ \u0CE1 \ \u0D05 - \ \u0D0C \ \u0D0E - \ \u0D10 \ \u0D12 - \ \u0D28 \ \u0D2A - \ \u0D39 \ \u0D3D \ \u0D60 \ \u0D61 \ \u0D7A - \ \u0D7F \ \u0D85 - \ \u0D96 \ \u0D9A - \ \u0DB1 \ \u0DB3 - \ \u0DBB \ \u0DBD \ \u0DC0 - \ \u0DC6 \ \u0E01 - \ \u0E30 \ \u0E32 \ \u0E33 \ \u0E40 - \ \u0E46 \ \u0E81 \ \u0E82 \ \u0E84 \ \u0E87 \ \u0E88 \ \u0E8A \ \u0E8D \ \u0E94 - \ \u0E97 \ \u0E99 - \ \u0E9F \ \u0EA1 - \ \u0EA3 \ \u0EA5 \ \u0EA7 \ \u0EAA \ \u0EAB \ \u0EAD - \ \u0EB0 \ \u0EB2 \ \u0EB3 \ \u0EBD \ \u0EC0 - \ \u0EC4 \ \u0EC6 \ \u0EDC \ \u0EDD \ \u0F00 \ \u0F40 - \ \u0F47 \ \u0F49 - \ \u0F6C \ \u0F88 - \ \u0F8B \ \u1000 - \ \u102A \ \u103F \ \u1050 - \ \u1055 \ \u105A - \ \u105D \ \u1061 \ \u1065 \ \u1066 \ \u106E - \ \u1070 \ \u1075 - \ \u1081 \ \u108E \ \u10A0 - \ \u10C5 \ \u10D0 - \ \u10FA \ \u10FC \ \u1100 - \ \u1159 \ \u115F - \ \u11A2 \ \u11A8 - \ \u11F9 \ \u1200 - \ \u1248 \ \u124A - \ \u124D \ \u1250 - \ \u1256 \ \u1258 \ \u125A - \ \u125D \ \u1260 - \ \u1288 \ \u128A - \ \u128D \ \u1290 - \ \u12B0 \ \u12B2 - \ \u12B5 \ \u12B8 - \ \u12BE \ \u12C0 \ \u12C2 - \ \u12C5 \ \u12C8 - \ \u12D6 \ \u12D8 - \ \u1310 \ \u1312 - \ \u1315 \ \u1318 - \ \u135A \ \u1380 - \ \u138F \ \u13A0 - \ \u13F4 \ \u1401 - \ \u166C \ \u166F - \ \u1676 \ \u1681 - \ \u169A \ \u16A0 - \ \u16EA \ \u1700 - \ \u170C \ \u170E - \ \u1711 \ \u1720 - \ \u1731 \ \u1740 - \ \u1751 \ \u1760 - \ \u176C \ \u176E - \ \u1770 \ \u1780 - \ \u17B3 \ \u17D7 \ \u17DC \ \u1820 - \ \u1877 \ \u1880 - \ \u18A8 \ \u18AA \ \u1900 - \ \u191C \ \u1950 - \ \u196D \ \u1970 - \ \u1974 \ \u1980 - \ \u19A9 \ \u19C1 - \ \u19C7 \ \u1A00 - \ \u1A16 \ \u1B05 - \ \u1B33 \ \u1B45 - \ \u1B4B \ \u1B83 - \ \u1BA0 \ \u1BAE \ \u1BAF \ \u1C00 - \ \u1C23 \ \u1C4D - \ \u1C4F \ \u1C5A - \ \u1C7D \ \u1D00 - \ \u1DBF \ \u1E00 - \ \u1F15 \ \u1F18 - \ \u1F1D \ \u1F20 - \ \u1F45 \ \u1F48 - \ \u1F4D \ \u1F50 - \ \u1F57 \ \u1F59 \ \u1F5B \ \u1F5D \ \u1F5F - \ \u1F7D \ \u1F80 - \ \u1FB4 \ \u1FB6 - \ \u1FBC \ \u1FBE \ \u1FC2 - \ \u1FC4 \ \u1FC6 - \ \u1FCC \ \u1FD0 - \ \u1FD3 \ \u1FD6 - \ \u1FDB \ \u1FE0 - \ \u1FEC \ \u1FF2 - \ \u1FF4 \ \u1FF6 - \ \u1FFC \ \u2071 \ \u207F \ \u2090 - \ \u2094 \ \u2102 \ \u2107 \ \u210A - \ \u2113 \ \u2115 \ \u2119 - \ \u211D \ \u2124 \ \u2126 \ \u2128 \ \u212A - \ \u212D \ \u212F - \ \u2139 \ \u213C - \ \u213F \ \u2145 - \ \u2149 \ \u214E \ \u2183 \ \u2184 \ \u2C00 - \ \u2C2E \ \u2C30 - \ \u2C5E \ \u2C60 - \ \u2C6F \ \u2C71 - \ \u2C7D \ \u2C80 - \ \u2CE4 \ \u2D00 - \ \u2D25 \ \u2D30 - \ \u2D65 \ \u2D6F \ \u2D80 - \ \u2D96 \ \u2DA0 - \ \u2DA6 \ \u2DA8 - \ \u2DAE \ \u2DB0 - \ \u2DB6 \ \u2DB8 - \ \u2DBE \ \u2DC0 - \ \u2DC6 \ \u2DC8 - \ \u2DCE \ \u2DD0 - \ \u2DD6 \ \u2DD8 - \ \u2DDE \ \u2E2F \ \u3005 \ \u3006 \ \u3031 - \ \u3035 \ \u303B \ \u303C \ \u3041 - \ \u3096 \ \u309D - \ \u309F \ \u30A1 - \ \u30FA \ \u30FC - \ \u30FF \ \u3105 - \ \u312D \ \u3131 - \ \u318E \ \u31A0 - \ \u31B7 \ \u31F0 - \ \u31FF \ \u3400 \ \u4DB5 \ \u4E00 \ \u9FC3 \ \uA000 - \ \uA48C \ \uA500 - \ \uA60C \ \uA610 - \ \uA61F \ \uA62A \ \uA62B \ \uA640 - \ \uA65F \ \uA662 - \ \uA66E \ \uA67F - \ \uA697 \ \uA717 - \ \uA71F \ \uA722 - \ \uA788 \ \uA78B \ \uA78C \ \uA7FB - \ \uA801 \ \uA803 - \ \uA805 \ \uA807 - \ \uA80A \ \uA80C - \ \uA822 \ \uA840 - \ \uA873 \ \uA882 - \ \uA8B3 \ \uA90A - \ \uA925 \ \uA930 - \ \uA946 \ \uAA00 - \ \uAA28 \ \uAA40 - \ \uAA42 \ \uAA44 - \ \uAA4B \ \uAC00 \ \uD7A3 \ \uF900 - \ \uFA2D \ \uFA30 - \ \uFA6A \ \uFA70 - \ \uFAD9 \ \uFB00 - \ \uFB06 \ \uFB13 - \ \
non _spacing _mark : new RegExp ( "[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]" ) ,
space _combining _mark : new RegExp ( "[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]" ) ,
connector _punctuation : new RegExp ( "[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]" )
} ;
2012-10-11 10:00:58 +00:00
function is _letter ( code ) {
return ( code >= 97 && code <= 122 )
|| ( code >= 65 && code <= 90 )
|| ( code >= 0xaa && UNICODE . letter . test ( String . fromCharCode ( code ) ) ) ;
2012-05-27 11:09:01 +00:00
} ;
2012-10-11 10:00:58 +00:00
function is _digit ( code ) {
return code >= 48 && code <= 57 ; //XXX: find out if "UnicodeDigit" means something else than 0..9
2012-05-27 11:09:01 +00:00
} ;
2012-10-11 10:00:58 +00:00
function is _alphanumeric _char ( code ) {
return is _digit ( code ) || is _letter ( code ) ;
2012-05-27 11:09:01 +00:00
} ;
function is _unicode _combining _mark ( ch ) {
return UNICODE . non _spacing _mark . test ( ch ) || UNICODE . space _combining _mark . test ( ch ) ;
} ;
function is _unicode _connector _punctuation ( ch ) {
return UNICODE . connector _punctuation . test ( ch ) ;
} ;
2012-08-17 12:59:42 +00:00
function is _identifier ( name ) {
2013-03-22 16:02:08 +00:00
return ! RESERVED _WORDS ( name ) && /^[a-z_$][a-z0-9_$]*$/i . test ( name ) ;
2012-08-17 12:59:42 +00:00
} ;
2012-10-11 10:00:58 +00:00
function is _identifier _start ( code ) {
return code == 36 || code == 95 || is _letter ( code ) ;
2012-05-27 11:09:01 +00:00
} ;
function is _identifier _char ( ch ) {
2012-10-11 10:00:58 +00:00
var code = ch . charCodeAt ( 0 ) ;
return is _identifier _start ( code )
|| is _digit ( code )
|| code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
|| code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
2012-05-27 11:09:01 +00:00
|| is _unicode _combining _mark ( ch )
|| is _unicode _connector _punctuation ( ch )
;
} ;
2013-05-05 18:45:41 +00:00
function is _identifier _string ( str ) {
2013-05-08 19:37:48 +00:00
var i = str . length ;
if ( i == 0 ) return false ;
2013-05-14 07:41:28 +00:00
if ( is _digit ( str . charCodeAt ( 0 ) ) ) return false ;
2013-05-08 19:37:48 +00:00
while ( -- i >= 0 ) {
2013-05-05 18:45:41 +00:00
if ( ! is _identifier _char ( str . charAt ( i ) ) )
return false ;
}
return true ;
} ;
2012-05-27 11:09:01 +00:00
function parse _js _number ( num ) {
if ( RE _HEX _NUMBER . test ( num ) ) {
return parseInt ( num . substr ( 2 ) , 16 ) ;
} else if ( RE _OCT _NUMBER . test ( num ) ) {
return parseInt ( num . substr ( 1 ) , 8 ) ;
} else if ( RE _DEC _NUMBER . test ( num ) ) {
return parseFloat ( num ) ;
}
} ;
function JS _Parse _Error ( message , line , col , pos ) {
this . message = message ;
2012-10-02 13:39:53 +00:00
this . line = line ;
this . col = col ;
2012-10-11 12:25:38 +00:00
this . pos = pos ;
2012-05-27 11:09:01 +00:00
this . stack = new Error ( ) . stack ;
} ;
JS _Parse _Error . prototype . toString = function ( ) {
return this . message + " (line: " + this . line + ", col: " + this . col + ", pos: " + this . pos + ")" + "\n\n" + this . stack ;
} ;
2012-09-21 11:38:52 +00:00
function js _error ( message , filename , line , col , pos ) {
2012-05-27 11:09:01 +00:00
throw new JS _Parse _Error ( message , line , col , pos ) ;
} ;
function is _token ( token , type , val ) {
return token . type == type && ( val == null || token . value == val ) ;
} ;
var EX _EOF = { } ;
2013-07-15 08:59:23 +00:00
function tokenizer ( $TEXT , filename ) {
2012-05-27 11:09:01 +00:00
var S = {
2012-09-25 12:30:59 +00:00
text : $TEXT . replace ( /\r\n?|[\n\u2028\u2029]/g , "\n" ) . replace ( /\uFEFF/g , '' ) ,
2012-09-21 11:38:52 +00:00
filename : filename ,
2013-07-15 08:59:23 +00:00
pos : 0 ,
tokpos : 0 ,
line : 1 ,
tokline : 0 ,
col : 0 ,
tokcol : 0 ,
2012-05-27 11:09:01 +00:00
newline _before : false ,
regex _allowed : false ,
comments _before : [ ]
} ;
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 ( ch == "\n" ) {
S . newline _before = S . newline _before || ! in _string ;
++ S . line ;
S . col = 0 ;
} else {
++ S . col ;
}
return ch ;
} ;
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 ;
} ;
2013-09-02 08:09:54 +00:00
var prev _was _dot = false ;
2012-05-27 11:09:01 +00:00
function token ( type , value , is _comment ) {
2013-07-12 06:56:58 +00:00
S . regex _allowed = ( ( type == "operator" && ! UNARY _POSTFIX ( value ) ) ||
2013-09-02 08:36:48 +00:00
( type == "keyword" && KEYWORDS _BEFORE _EXPRESSION ( value ) ) ||
2012-10-11 08:52:05 +00:00
( type == "punc" && PUNC _BEFORE _EXPRESSION ( value ) ) ) ;
2013-09-02 08:09:54 +00:00
prev _was _dot = ( type == "punc" && value == "." ) ;
2012-05-27 11:09:01 +00:00
var ret = {
type : type ,
value : value ,
line : S . tokline ,
col : S . tokcol ,
pos : S . tokpos ,
endpos : S . pos ,
2012-09-21 11:19:05 +00:00
nlb : S . newline _before ,
file : filename
2012-05-27 11:09:01 +00:00
} ;
if ( ! is _comment ) {
ret . comments _before = S . comments _before ;
S . comments _before = [ ] ;
// make note of any newlines in the comments that came before
for ( var i = 0 , len = ret . comments _before . length ; i < len ; i ++ ) {
ret . nlb = ret . nlb || ret . comments _before [ i ] . nlb ;
}
}
S . newline _before = false ;
return new AST _Token ( ret ) ;
} ;
function skip _whitespace ( ) {
2012-10-11 08:52:05 +00:00
while ( WHITESPACE _CHARS ( peek ( ) ) )
2012-05-27 11:09:01 +00:00
next ( ) ;
} ;
function read _while ( pred ) {
2012-10-11 10:00:58 +00:00
var ret = "" , ch , i = 0 ;
while ( ( ch = peek ( ) ) && pred ( ch , i ++ ) )
2012-05-27 11:09:01 +00:00
ret += next ( ) ;
return ret ;
} ;
function parse _error ( err ) {
2012-09-21 11:38:52 +00:00
js _error ( err , filename , S . tokline , S . tokcol , S . tokpos ) ;
2012-05-27 11:09:01 +00:00
} ;
function read _num ( prefix ) {
var has _e = false , after _e = false , has _x = false , has _dot = prefix == "." ;
var num = read _while ( function ( ch , i ) {
2012-10-11 10:00:58 +00:00
var code = ch . charCodeAt ( 0 ) ;
switch ( code ) {
case 120 : case 88 : // xX
return has _x ? false : ( has _x = true ) ;
case 101 : case 69 : // eE
return has _x ? true : has _e ? false : ( has _e = after _e = true ) ;
case 45 : // -
return after _e || ( i == 0 && ! prefix ) ;
case 43 : // +
return after _e ;
case ( after _e = false , 46 ) : // .
return ( ! has _dot && ! has _x && ! has _e ) ? ( has _dot = true ) : false ;
2012-05-27 11:09:01 +00:00
}
2012-10-11 10:00:58 +00:00
return is _alphanumeric _char ( code ) ;
2012-05-27 11:09:01 +00:00
} ) ;
2012-10-11 10:00:58 +00:00
if ( prefix ) num = prefix + num ;
2012-05-27 11:09:01 +00:00
var valid = parse _js _number ( num ) ;
if ( ! isNaN ( valid ) ) {
return token ( "num" , valid ) ;
} else {
parse _error ( "Invalid syntax: " + num ) ;
}
} ;
function read _escaped _char ( in _string ) {
var ch = next ( true , in _string ) ;
2012-10-11 10:00:58 +00:00
switch ( ch . charCodeAt ( 0 ) ) {
case 110 : return "\n" ;
case 114 : return "\r" ;
case 116 : return "\t" ;
case 98 : return "\b" ;
case 118 : return "\u000b" ; // \v
case 102 : return "\f" ;
case 48 : return "\0" ;
case 120 : return String . fromCharCode ( hex _bytes ( 2 ) ) ; // \x
case 117 : return String . fromCharCode ( hex _bytes ( 4 ) ) ; // \u
case 10 : return "" ; // newline
2012-05-27 11:09:01 +00:00
default : return ch ;
}
} ;
function hex _bytes ( n ) {
var num = 0 ;
for ( ; n > 0 ; -- n ) {
var digit = parseInt ( next ( true ) , 16 ) ;
if ( isNaN ( digit ) )
parse _error ( "Invalid hex-character pattern in string" ) ;
num = ( num << 4 ) | digit ;
}
return num ;
} ;
2012-10-11 10:00:58 +00:00
var read _string = with _eof _error ( "Unterminated string constant" , function ( ) {
var quote = next ( ) , ret = "" ;
for ( ; ; ) {
var ch = next ( true ) ;
if ( ch == "\\" ) {
// read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178
var octal _len = 0 , first = null ;
ch = read _while ( function ( ch ) {
if ( ch >= "0" && ch <= "7" ) {
if ( ! first ) {
first = ch ;
return ++ octal _len ;
2012-05-27 11:09:01 +00:00
}
2012-10-11 10:00:58 +00:00
else if ( first <= "3" && octal _len <= 2 ) return ++ octal _len ;
else if ( first >= "4" && octal _len <= 1 ) return ++ octal _len ;
}
return false ;
} ) ;
if ( octal _len > 0 ) ch = String . fromCharCode ( parseInt ( ch , 8 ) ) ;
else ch = read _escaped _char ( true ) ;
2012-05-27 11:09:01 +00:00
}
2012-10-11 10:00:58 +00:00
else if ( ch == quote ) break ;
ret += ch ;
}
return token ( "string" , ret ) ;
} ) ;
2012-05-27 11:09:01 +00:00
function read _line _comment ( ) {
next ( ) ;
var i = find ( "\n" ) , 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 ;
}
return token ( "comment1" , ret , true ) ;
} ;
2012-10-11 10:00:58 +00:00
var read _multiline _comment = with _eof _error ( "Unterminated multiline comment" , function ( ) {
2012-05-27 11:09:01 +00:00
next ( ) ;
2012-10-11 10:00:58 +00:00
var i = find ( "*/" , true ) ;
var text = S . text . substring ( S . pos , i ) ;
var a = text . split ( "\n" ) , n = a . length ;
// update stream position
S . pos = i + 2 ;
S . line += n - 1 ;
if ( n > 1 ) S . col = a [ n - 1 ] . length ;
else S . col += a [ n - 1 ] . length ;
S . col += 2 ;
S . newline _before = S . newline _before || text . indexOf ( "\n" ) >= 0 ;
return token ( "comment2" , text , true ) ;
} ) ;
2012-05-27 11:09:01 +00:00
function read _name ( ) {
var backslash = false , name = "" , ch , escaped = false , hex ;
while ( ( ch = peek ( ) ) != null ) {
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 ;
}
}
2012-10-11 08:52:05 +00:00
if ( KEYWORDS ( name ) && escaped ) {
2012-05-27 11:09:01 +00:00
hex = name . charCodeAt ( 0 ) . toString ( 16 ) . toUpperCase ( ) ;
name = "\\u" + "0000" . substr ( hex . length ) + hex + name . slice ( 1 ) ;
}
return name ;
} ;
2012-10-11 10:00:58 +00:00
var read _regexp = with _eof _error ( "Unterminated regular expression" , function ( regexp ) {
var prev _backslash = false , ch , in _class = false ;
while ( ( ch = next ( true ) ) ) if ( prev _backslash ) {
regexp += "\\" + ch ;
prev _backslash = false ;
} else if ( ch == "[" ) {
in _class = true ;
regexp += ch ;
} else if ( ch == "]" && in _class ) {
in _class = false ;
regexp += ch ;
} else if ( ch == "/" && ! in _class ) {
break ;
} else if ( ch == "\\" ) {
prev _backslash = true ;
} else {
regexp += ch ;
}
var mods = read _name ( ) ;
return token ( "regexp" , new RegExp ( regexp , mods ) ) ;
} ) ;
2012-05-27 11:09:01 +00:00
function read _operator ( prefix ) {
function grow ( op ) {
if ( ! peek ( ) ) return op ;
var bigger = op + peek ( ) ;
2012-10-11 08:52:05 +00:00
if ( OPERATORS ( bigger ) ) {
2012-05-27 11:09:01 +00:00
next ( ) ;
return grow ( bigger ) ;
} else {
return op ;
}
} ;
return token ( "operator" , grow ( prefix || next ( ) ) ) ;
} ;
function handle _slash ( ) {
next ( ) ;
var regex _allowed = S . regex _allowed ;
switch ( peek ( ) ) {
case "/" :
S . comments _before . push ( read _line _comment ( ) ) ;
S . regex _allowed = regex _allowed ;
return next _token ( ) ;
case "*" :
S . comments _before . push ( read _multiline _comment ( ) ) ;
S . regex _allowed = regex _allowed ;
return next _token ( ) ;
}
return S . regex _allowed ? read _regexp ( "" ) : read _operator ( "/" ) ;
} ;
function handle _dot ( ) {
next ( ) ;
2012-10-11 10:00:58 +00:00
return is _digit ( peek ( ) . charCodeAt ( 0 ) )
2012-05-27 11:09:01 +00:00
? read _num ( "." )
: token ( "punc" , "." ) ;
} ;
function read _word ( ) {
var word = read _name ( ) ;
2013-09-02 08:36:48 +00:00
if ( prev _was _dot ) return token ( "name" , word ) ;
2012-10-11 08:52:05 +00:00
return KEYWORDS _ATOM ( word ) ? token ( "atom" , word )
: ! KEYWORDS ( word ) ? token ( "name" , word )
: OPERATORS ( word ) ? token ( "operator" , word )
2012-05-27 11:09:01 +00:00
: token ( "keyword" , word ) ;
} ;
function with _eof _error ( eof _error , cont ) {
2012-10-11 10:00:58 +00:00
return function ( x ) {
try {
return cont ( x ) ;
} catch ( ex ) {
if ( ex === EX _EOF ) parse _error ( eof _error ) ;
else throw ex ;
}
} ;
2012-05-27 11:09:01 +00:00
} ;
function next _token ( force _regexp ) {
if ( force _regexp != null )
return read _regexp ( force _regexp ) ;
skip _whitespace ( ) ;
start _token ( ) ;
var ch = peek ( ) ;
if ( ! ch ) return token ( "eof" ) ;
2012-10-11 10:00:58 +00:00
var code = ch . charCodeAt ( 0 ) ;
switch ( code ) {
case 34 : case 39 : return read _string ( ) ;
case 46 : return handle _dot ( ) ;
case 47 : return handle _slash ( ) ;
}
if ( is _digit ( code ) ) return read _num ( ) ;
2012-10-11 08:52:05 +00:00
if ( PUNC _CHARS ( ch ) ) return token ( "punc" , next ( ) ) ;
if ( OPERATOR _CHARS ( ch ) ) return read _operator ( ) ;
2012-10-11 10:00:58 +00:00
if ( code == 92 || is _identifier _start ( code ) ) return read _word ( ) ;
2012-05-27 11:09:01 +00:00
parse _error ( "Unexpected character '" + ch + "'" ) ;
} ;
next _token . context = function ( nc ) {
if ( nc ) S = nc ;
return S ;
} ;
return next _token ;
} ;
/* -----[ Parser (constants) ]----- */
2012-10-11 10:00:58 +00:00
var UNARY _PREFIX = makePredicate ( [
2012-05-27 11:09:01 +00:00
"typeof" ,
"void" ,
"delete" ,
"--" ,
"++" ,
"!" ,
"~" ,
"-" ,
"+"
] ) ;
2012-10-11 10:00:58 +00:00
var UNARY _POSTFIX = makePredicate ( [ "--" , "++" ] ) ;
2012-05-27 11:09:01 +00:00
2012-10-11 10:00:58 +00:00
var ASSIGNMENT = makePredicate ( [ "=" , "+=" , "-=" , "/=" , "*=" , "%=" , ">>=" , "<<=" , ">>>=" , "|=" , "^=" , "&=" ] ) ;
2012-05-27 11:09:01 +00:00
var PRECEDENCE = ( function ( a , ret ) {
for ( var i = 0 , n = 1 ; i < a . length ; ++ i , ++ n ) {
var b = a [ i ] ;
for ( var j = 0 ; j < b . length ; ++ j ) {
ret [ b [ j ] ] = n ;
}
}
return ret ;
} ) (
[
[ "||" ] ,
[ "&&" ] ,
[ "|" ] ,
[ "^" ] ,
[ "&" ] ,
[ "==" , "===" , "!=" , "!==" ] ,
[ "<" , ">" , "<=" , ">=" , "in" , "instanceof" ] ,
[ ">>" , "<<" , ">>>" ] ,
[ "+" , "-" ] ,
[ "*" , "/" , "%" ]
] ,
{ }
) ;
var STATEMENTS _WITH _LABELS = array _to _hash ( [ "for" , "do" , "while" , "switch" ] ) ;
var ATOMIC _START _TOKEN = array _to _hash ( [ "atom" , "num" , "string" , "regexp" , "name" ] ) ;
/* -----[ Parser ]----- */
2012-09-21 11:19:05 +00:00
function parse ( $TEXT , options ) {
options = defaults ( options , {
2013-05-15 10:27:23 +00:00
strict : false ,
filename : null ,
toplevel : null ,
2013-07-15 08:59:23 +00:00
expression : false
2012-09-21 11:19:05 +00:00
} ) ;
2012-05-27 11:09:01 +00:00
var S = {
2012-09-21 11:19:05 +00:00
input : typeof $TEXT == "string" ? tokenizer ( $TEXT , options . filename ) : $TEXT ,
2012-05-27 11:09:01 +00:00
token : null ,
prev : null ,
peeked : null ,
in _function : 0 ,
in _directives : true ,
in _loop : 0 ,
labels : [ ]
} ;
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 ,
2012-09-21 11:38:52 +00:00
ctx . filename ,
2012-05-27 11:09:01 +00:00
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 unexpected ( token ) {
if ( token == null )
token = S . token ;
token _error ( token , "Unexpected token: " + token . type + " (" + token . value + ")" ) ;
} ;
function expect _token ( type , val ) {
if ( is ( type , val ) ) {
return next ( ) ;
}
2012-05-27 11:36:44 +00:00
token _error ( S . token , "Unexpected token " + S . token . type + " «" + S . token . value + "»" + ", expected " + type + " «" + val + "»" ) ;
2012-05-27 11:09:01 +00:00
} ;
function expect ( punc ) { return expect _token ( "punc" , punc ) ; } ;
function can _insert _semicolon ( ) {
2012-09-21 11:19:05 +00:00
return ! options . strict && (
2012-05-27 11:09:01 +00:00
S . token . nlb || is ( "eof" ) || is ( "punc" , "}" )
) ;
} ;
function semicolon ( ) {
if ( is ( "punc" , ";" ) ) next ( ) ;
else if ( ! can _insert _semicolon ( ) ) unexpected ( ) ;
} ;
function parenthesised ( ) {
2012-08-17 12:59:42 +00:00
expect ( "(" ) ;
2012-10-11 10:00:58 +00:00
var exp = expression ( true ) ;
2012-08-17 12:59:42 +00:00
expect ( ")" ) ;
return exp ;
2012-05-27 11:09:01 +00:00
} ;
function embed _tokens ( parser ) {
return function ( ) {
var start = S . token ;
2012-10-11 10:00:58 +00:00
var expr = parser ( ) ;
2012-05-27 11:09:01 +00:00
var end = prev ( ) ;
expr . start = start ;
expr . end = end ;
return expr ;
} ;
} ;
2013-09-02 06:56:27 +00:00
function handle _regexp ( ) {
2012-05-27 11:09:01 +00:00
if ( is ( "operator" , "/" ) || is ( "operator" , "/=" ) ) {
S . peeked = null ;
S . token = S . input ( S . token . value . substr ( 1 ) ) ; // force regexp
}
2013-09-02 06:56:27 +00:00
} ;
var statement = embed _tokens ( function ( ) {
var tmp ;
handle _regexp ( ) ;
2012-05-27 11:09:01 +00:00
switch ( S . token . type ) {
case "string" :
var dir = S . in _directives , stat = simple _statement ( ) ;
// XXXv2: decide how to fix directives
2012-09-18 10:21:09 +00:00
if ( dir && stat . body instanceof AST _String && ! is ( "punc" , "," ) )
return new AST _Directive ( { value : stat . body . value } ) ;
2012-05-27 11:09:01 +00:00
return stat ;
case "num" :
case "regexp" :
case "operator" :
case "atom" :
return simple _statement ( ) ;
case "name" :
return is _token ( peek ( ) , "punc" , ":" )
? labeled _statement ( )
: simple _statement ( ) ;
case "punc" :
switch ( S . token . value ) {
case "{" :
2012-09-05 08:31:02 +00:00
return new AST _BlockStatement ( {
start : S . token ,
body : block _ ( ) ,
end : prev ( )
} ) ;
2012-05-27 11:09:01 +00:00
case "[" :
case "(" :
return simple _statement ( ) ;
case ";" :
next ( ) ;
2012-08-15 10:32:37 +00:00
return new AST _EmptyStatement ( ) ;
2012-05-27 11:09:01 +00:00
default :
unexpected ( ) ;
}
case "keyword" :
2012-10-11 10:00:58 +00:00
switch ( tmp = S . token . value , next ( ) , tmp ) {
2012-05-27 11:09:01 +00:00
case "break" :
return break _cont ( AST _Break ) ;
case "continue" :
return break _cont ( AST _Continue ) ;
case "debugger" :
semicolon ( ) ;
return new AST _Debugger ( ) ;
case "do" :
return new AST _Do ( {
body : in _loop ( statement ) ,
2012-10-11 10:00:58 +00:00
condition : ( expect _token ( "keyword" , "while" ) , tmp = parenthesised ( ) , semicolon ( ) , tmp )
2012-05-27 11:09:01 +00:00
} ) ;
case "while" :
return new AST _While ( {
condition : parenthesised ( ) ,
body : in _loop ( statement )
} ) ;
case "for" :
return for _ ( ) ;
case "function" :
return function _ ( true ) ;
case "if" :
return if _ ( ) ;
case "return" :
if ( S . in _function == 0 )
croak ( "'return' outside of function" ) ;
return new AST _Return ( {
value : ( is ( "punc" , ";" )
? ( next ( ) , null )
: can _insert _semicolon ( )
? null
2012-10-11 10:00:58 +00:00
: ( tmp = expression ( true ) , semicolon ( ) , tmp ) )
2012-05-27 11:09:01 +00:00
} ) ;
case "switch" :
return new AST _Switch ( {
expression : parenthesised ( ) ,
2012-10-11 10:00:58 +00:00
body : in _loop ( switch _body _ )
2012-05-27 11:09:01 +00:00
} ) ;
case "throw" :
if ( S . token . nlb )
croak ( "Illegal newline after 'throw'" ) ;
return new AST _Throw ( {
2012-10-11 10:00:58 +00:00
value : ( tmp = expression ( true ) , semicolon ( ) , tmp )
2012-05-27 11:09:01 +00:00
} ) ;
case "try" :
return try _ ( ) ;
case "var" :
2012-10-11 10:00:58 +00:00
return tmp = var _ ( ) , semicolon ( ) , tmp ;
2012-05-27 11:09:01 +00:00
case "const" :
2012-10-11 10:00:58 +00:00
return tmp = const _ ( ) , semicolon ( ) , tmp ;
2012-05-27 11:09:01 +00:00
case "with" :
return new AST _With ( {
expression : parenthesised ( ) ,
body : statement ( )
} ) ;
default :
unexpected ( ) ;
}
}
} ) ;
function labeled _statement ( ) {
2012-08-19 12:57:50 +00:00
var label = as _symbol ( AST _Label ) ;
if ( find _if ( function ( l ) { return l . name == label . name } , S . labels ) ) {
// 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" ) ;
}
2012-05-27 11:09:01 +00:00
expect ( ":" ) ;
S . labels . push ( label ) ;
2012-10-04 05:49:18 +00:00
var stat = statement ( ) ;
2012-05-27 11:09:01 +00:00
S . labels . pop ( ) ;
2013-09-02 16:36:16 +00:00
if ( ! ( stat instanceof AST _IterationStatement ) ) {
// check for `continue` that refers to this label.
// those should be reported as syntax errors.
// https://github.com/mishoo/UglifyJS2/issues/287
label . references . forEach ( function ( ref ) {
if ( ref instanceof AST _Continue ) {
ref = ref . label . start ;
2013-09-02 16:38:00 +00:00
croak ( "Continue label `" + label . name + "` refers to non-IterationStatement." ,
2013-09-02 16:36:16 +00:00
ref . line , ref . col , ref . pos ) ;
}
} ) ;
}
2012-09-03 09:05:10 +00:00
return new AST _LabeledStatement ( { body : stat , label : label } ) ;
2012-05-27 11:09:01 +00:00
} ;
2012-10-11 10:00:58 +00:00
function simple _statement ( tmp ) {
return new AST _SimpleStatement ( { body : ( tmp = expression ( true ) , semicolon ( ) , tmp ) } ) ;
2012-05-27 11:09:01 +00:00
} ;
function break _cont ( type ) {
2013-09-02 16:36:16 +00:00
var label = null , ldef ;
2012-05-27 11:09:01 +00:00
if ( ! can _insert _semicolon ( ) ) {
2012-08-19 12:57:50 +00:00
label = as _symbol ( AST _LabelRef , true ) ;
2012-05-27 11:09:01 +00:00
}
2012-08-19 12:57:50 +00:00
if ( label != null ) {
2013-09-02 16:36:16 +00:00
ldef = find _if ( function ( l ) { return l . name == label . name } , S . labels ) ;
if ( ! ldef )
2012-08-19 12:57:50 +00:00
croak ( "Undefined label " + label . name ) ;
2013-09-02 16:36:16 +00:00
label . thedef = ldef ;
2012-05-27 11:09:01 +00:00
}
else if ( S . in _loop == 0 )
croak ( type . TYPE + " not inside a loop or switch" ) ;
semicolon ( ) ;
2013-09-02 16:36:16 +00:00
var stat = new type ( { label : label } ) ;
if ( ldef ) ldef . references . push ( stat ) ;
return stat ;
2012-05-27 11:09:01 +00:00
} ;
function for _ ( ) {
expect ( "(" ) ;
var init = null ;
if ( ! is ( "punc" , ";" ) ) {
init = is ( "keyword" , "var" )
? ( next ( ) , var _ ( true ) )
: expression ( true , true ) ;
if ( is ( "operator" , "in" ) ) {
if ( init instanceof AST _Var && init . definitions . length > 1 )
croak ( "Only one variable declaration allowed in for..in loop" ) ;
next ( ) ;
return for _in ( init ) ;
}
}
return regular _for ( init ) ;
} ;
function regular _for ( init ) {
expect ( ";" ) ;
2012-10-11 10:00:58 +00:00
var test = is ( "punc" , ";" ) ? null : expression ( true ) ;
2012-05-27 11:09:01 +00:00
expect ( ";" ) ;
2012-10-11 10:00:58 +00:00
var step = is ( "punc" , ")" ) ? null : expression ( true ) ;
2012-05-27 11:09:01 +00:00
expect ( ")" ) ;
return new AST _For ( {
init : init ,
condition : test ,
step : step ,
body : in _loop ( statement )
} ) ;
} ;
function for _in ( init ) {
2012-05-27 14:25:31 +00:00
var lhs = init instanceof AST _Var ? init . definitions [ 0 ] . name : null ;
2012-10-11 10:00:58 +00:00
var obj = expression ( true ) ;
2012-05-27 11:09:01 +00:00
expect ( ")" ) ;
return new AST _ForIn ( {
init : init ,
2012-05-27 14:25:31 +00:00
name : lhs ,
2012-05-27 11:09:01 +00:00
object : obj ,
body : in _loop ( statement )
} ) ;
} ;
2012-10-12 07:11:01 +00:00
var function _ = function ( in _statement , ctor ) {
2012-12-21 23:24:04 +00:00
var is _accessor = ctor === AST _Accessor ;
var name = ( is ( "name" ) ? as _symbol ( in _statement
? AST _SymbolDefun
: is _accessor
? AST _SymbolAccessor
: AST _SymbolLambda )
: is _accessor && ( is ( "string" ) || is ( "num" ) ) ? as _atom _node ( )
: null ) ;
2012-05-27 11:09:01 +00:00
if ( in _statement && ! name )
unexpected ( ) ;
expect ( "(" ) ;
2012-10-12 07:11:01 +00:00
if ( ! ctor ) ctor = in _statement ? AST _Defun : AST _Function ;
2012-05-27 11:09:01 +00:00
return new ctor ( {
name : name ,
argnames : ( function ( first , a ) {
while ( ! is ( "punc" , ")" ) ) {
if ( first ) first = false ; else expect ( "," ) ;
2012-08-19 12:57:50 +00:00
a . push ( as _symbol ( AST _SymbolFunarg ) ) ;
2012-05-27 11:09:01 +00:00
}
next ( ) ;
return a ;
} ) ( true , [ ] ) ,
2012-10-11 10:00:58 +00:00
body : ( function ( loop , labels ) {
2012-09-03 07:26:23 +00:00
++ S . in _function ;
S . in _directives = true ;
S . in _loop = 0 ;
S . labels = [ ] ;
2012-09-05 08:31:02 +00:00
var a = block _ ( ) ;
2012-09-03 07:26:23 +00:00
-- S . in _function ;
S . in _loop = loop ;
S . labels = labels ;
return a ;
2012-10-11 10:00:58 +00:00
} ) ( S . in _loop , S . labels )
2012-05-27 11:09:01 +00:00
} ) ;
} ;
function if _ ( ) {
var cond = parenthesised ( ) , body = statement ( ) , belse = null ;
if ( is ( "keyword" , "else" ) ) {
next ( ) ;
belse = statement ( ) ;
}
return new AST _If ( {
condition : cond ,
2012-09-03 09:11:44 +00:00
body : body ,
2012-05-27 11:09:01 +00:00
alternative : belse
} ) ;
} ;
2012-09-05 08:31:02 +00:00
function block _ ( ) {
2012-05-27 11:09:01 +00:00
expect ( "{" ) ;
var a = [ ] ;
while ( ! is ( "punc" , "}" ) ) {
if ( is ( "eof" ) ) unexpected ( ) ;
a . push ( statement ( ) ) ;
}
next ( ) ;
2012-09-05 08:31:02 +00:00
return a ;
2012-05-27 11:09:01 +00:00
} ;
2012-10-11 10:00:58 +00:00
function switch _body _ ( ) {
2012-05-27 11:09:01 +00:00
expect ( "{" ) ;
2012-10-11 10:00:58 +00:00
var a = [ ] , cur = null , branch = null , tmp ;
2012-05-27 11:09:01 +00:00
while ( ! is ( "punc" , "}" ) ) {
if ( is ( "eof" ) ) unexpected ( ) ;
if ( is ( "keyword" , "case" ) ) {
2012-05-27 14:25:31 +00:00
if ( branch ) branch . end = prev ( ) ;
2012-05-27 11:09:01 +00:00
cur = [ ] ;
2012-05-27 14:25:31 +00:00
branch = new AST _Case ( {
2012-10-11 10:00:58 +00:00
start : ( tmp = S . token , next ( ) , tmp ) ,
expression : expression ( true ) ,
2012-05-27 14:25:31 +00:00
body : cur
} ) ;
a . push ( branch ) ;
2012-05-27 11:09:01 +00:00
expect ( ":" ) ;
}
else if ( is ( "keyword" , "default" ) ) {
2012-05-27 14:25:31 +00:00
if ( branch ) branch . end = prev ( ) ;
2012-05-27 11:09:01 +00:00
cur = [ ] ;
2012-05-27 14:25:31 +00:00
branch = new AST _Default ( {
2012-10-11 10:00:58 +00:00
start : ( tmp = S . token , next ( ) , expect ( ":" ) , tmp ) ,
2012-05-27 14:25:31 +00:00
body : cur
2012-10-11 10:00:58 +00:00
} ) ;
2012-05-27 14:25:31 +00:00
a . push ( branch ) ;
2012-05-27 11:09:01 +00:00
}
else {
if ( ! cur ) unexpected ( ) ;
cur . push ( statement ( ) ) ;
}
}
2012-05-27 14:25:31 +00:00
if ( branch ) branch . end = prev ( ) ;
2012-05-27 11:09:01 +00:00
next ( ) ;
2012-10-03 12:52:01 +00:00
return a ;
2012-10-11 10:00:58 +00:00
} ;
2012-05-27 11:09:01 +00:00
function try _ ( ) {
2012-09-05 08:31:02 +00:00
var body = block _ ( ) , bcatch = null , bfinally = null ;
2012-05-27 11:09:01 +00:00
if ( is ( "keyword" , "catch" ) ) {
2012-05-27 14:25:31 +00:00
var start = S . token ;
2012-05-27 11:09:01 +00:00
next ( ) ;
expect ( "(" ) ;
2012-08-19 12:57:50 +00:00
var name = as _symbol ( AST _SymbolCatch ) ;
2012-05-27 11:09:01 +00:00
expect ( ")" ) ;
bcatch = new AST _Catch ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
argname : name ,
2012-09-05 08:31:02 +00:00
body : block _ ( ) ,
2012-05-27 14:25:31 +00:00
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) ;
}
if ( is ( "keyword" , "finally" ) ) {
2012-05-27 14:25:31 +00:00
var start = S . token ;
2012-05-27 11:09:01 +00:00
next ( ) ;
2012-05-27 14:25:31 +00:00
bfinally = new AST _Finally ( {
start : start ,
2012-09-05 08:31:02 +00:00
body : block _ ( ) ,
2012-05-27 14:25:31 +00:00
end : prev ( )
} ) ;
2012-05-27 11:09:01 +00:00
}
if ( ! bcatch && ! bfinally )
croak ( "Missing catch/finally blocks" ) ;
return new AST _Try ( {
2012-09-05 08:31:02 +00:00
body : body ,
2012-05-27 11:09:01 +00:00
bcatch : bcatch ,
bfinally : bfinally
} ) ;
} ;
2012-10-02 09:22:39 +00:00
function vardefs ( no _in , in _const ) {
2012-05-27 11:09:01 +00:00
var a = [ ] ;
for ( ; ; ) {
a . push ( new AST _VarDef ( {
start : S . token ,
2012-10-02 09:22:39 +00:00
name : as _symbol ( in _const ? AST _SymbolConst : AST _SymbolVar ) ,
2012-05-27 11:09:01 +00:00
value : is ( "operator" , "=" ) ? ( next ( ) , expression ( false , no _in ) ) : null ,
end : prev ( )
} ) ) ;
if ( ! is ( "punc" , "," ) )
break ;
next ( ) ;
}
return a ;
} ;
2012-05-27 14:25:31 +00:00
var var _ = function ( no _in ) {
2012-05-27 11:09:01 +00:00
return new AST _Var ( {
2012-05-27 14:25:31 +00:00
start : prev ( ) ,
2012-10-02 09:22:39 +00:00
definitions : vardefs ( no _in , false ) ,
2012-05-27 14:25:31 +00:00
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var const _ = function ( ) {
2012-05-27 11:09:01 +00:00
return new AST _Const ( {
2012-05-27 14:25:31 +00:00
start : prev ( ) ,
2012-10-02 09:22:39 +00:00
definitions : vardefs ( false , true ) ,
2012-05-27 14:25:31 +00:00
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var new _ = function ( ) {
var start = S . token ;
expect _token ( "operator" , "new" ) ;
2012-05-27 11:09:01 +00:00
var newexp = expr _atom ( false ) , args ;
if ( is ( "punc" , "(" ) ) {
next ( ) ;
args = expr _list ( ")" ) ;
} else {
args = [ ] ;
}
return subscripts ( new AST _New ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
expression : newexp ,
2012-05-27 14:25:31 +00:00
args : args ,
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) , true ) ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
function as _atom _node ( ) {
var tok = S . token , ret ;
switch ( tok . type ) {
case "name" :
2012-08-19 12:57:50 +00:00
return as _symbol ( AST _SymbolRef ) ;
2012-05-27 11:09:01 +00:00
case "num" :
ret = new AST _Number ( { start : tok , end : tok , value : tok . value } ) ;
break ;
case "string" :
ret = new AST _String ( { start : tok , end : tok , value : tok . value } ) ;
break ;
case "regexp" :
2012-10-09 13:25:45 +00:00
ret = new AST _RegExp ( { start : tok , end : tok , value : tok . value } ) ;
2012-05-27 11:09:01 +00:00
break ;
case "atom" :
switch ( tok . value ) {
case "false" :
ret = new AST _False ( { start : tok , end : tok } ) ;
break ;
case "true" :
ret = new AST _True ( { start : tok , end : tok } ) ;
break ;
case "null" :
ret = new AST _Null ( { start : tok , end : tok } ) ;
break ;
}
break ;
}
next ( ) ;
return ret ;
} ;
var expr _atom = function ( allow _calls ) {
if ( is ( "operator" , "new" ) ) {
return new _ ( ) ;
}
2012-05-27 14:25:31 +00:00
var start = S . token ;
2012-05-27 11:09:01 +00:00
if ( is ( "punc" ) ) {
2012-05-27 14:25:31 +00:00
switch ( start . value ) {
2012-05-27 11:09:01 +00:00
case "(" :
next ( ) ;
2012-10-11 10:00:58 +00:00
var ex = expression ( true ) ;
2012-05-27 14:25:31 +00:00
ex . start = start ;
ex . end = S . token ;
expect ( ")" ) ;
return subscripts ( ex , allow _calls ) ;
2012-05-27 11:09:01 +00:00
case "[" :
return subscripts ( array _ ( ) , allow _calls ) ;
case "{" :
return subscripts ( object _ ( ) , allow _calls ) ;
}
unexpected ( ) ;
}
if ( is ( "keyword" , "function" ) ) {
next ( ) ;
var func = function _ ( false ) ;
func . start = start ;
func . end = prev ( ) ;
return subscripts ( func , allow _calls ) ;
}
2012-10-11 08:07:42 +00:00
if ( ATOMIC _START _TOKEN [ S . token . type ] ) {
2012-05-27 11:09:01 +00:00
return subscripts ( as _atom _node ( ) , allow _calls ) ;
}
unexpected ( ) ;
} ;
function expr _list ( closing , allow _trailing _comma , allow _empty ) {
var first = true , a = [ ] ;
while ( ! is ( "punc" , closing ) ) {
if ( first ) first = false ; else expect ( "," ) ;
if ( allow _trailing _comma && is ( "punc" , closing ) ) break ;
if ( is ( "punc" , "," ) && allow _empty ) {
2013-01-16 19:59:19 +00:00
a . push ( new AST _Hole ( { start : S . token , end : S . token } ) ) ;
2012-05-27 11:09:01 +00:00
} else {
a . push ( expression ( false ) ) ;
}
}
next ( ) ;
return a ;
} ;
2012-05-27 14:25:31 +00:00
var array _ = embed _tokens ( function ( ) {
expect ( "[" ) ;
2012-05-27 11:09:01 +00:00
return new AST _Array ( {
2012-09-21 11:19:05 +00:00
elements : expr _list ( "]" , ! options . strict , true )
2012-05-27 11:09:01 +00:00
} ) ;
2012-05-27 14:25:31 +00:00
} ) ;
2012-05-27 11:09:01 +00:00
var object _ = embed _tokens ( function ( ) {
2012-05-27 14:25:31 +00:00
expect ( "{" ) ;
2012-05-27 11:09:01 +00:00
var first = true , a = [ ] ;
while ( ! is ( "punc" , "}" ) ) {
if ( first ) first = false ; else expect ( "," ) ;
2012-09-21 11:19:05 +00:00
if ( ! options . strict && is ( "punc" , "}" ) )
2012-05-27 11:09:01 +00:00
// allow trailing comma
break ;
var start = S . token ;
var type = start . type ;
var name = as _property _name ( ) ;
if ( type == "name" && ! is ( "punc" , ":" ) ) {
2012-08-17 12:59:42 +00:00
if ( name == "get" ) {
2012-05-27 11:09:01 +00:00
a . push ( new AST _ObjectGetter ( {
start : start ,
2012-08-19 12:57:50 +00:00
key : name ,
2012-11-07 10:43:27 +00:00
value : function _ ( false , AST _Accessor ) ,
2012-05-27 11:09:01 +00:00
end : prev ( )
} ) ) ;
continue ;
}
2012-08-17 12:59:42 +00:00
if ( name == "set" ) {
2012-05-27 11:09:01 +00:00
a . push ( new AST _ObjectSetter ( {
start : start ,
2012-08-19 12:57:50 +00:00
key : name ,
2012-11-07 10:43:27 +00:00
value : function _ ( false , AST _Accessor ) ,
2012-05-27 11:09:01 +00:00
end : prev ( )
} ) ) ;
continue ;
}
}
expect ( ":" ) ;
a . push ( new AST _ObjectKeyVal ( {
start : start ,
key : name ,
value : expression ( false ) ,
end : prev ( )
} ) ) ;
}
next ( ) ;
return new AST _Object ( { properties : a } ) ;
} ) ;
function as _property _name ( ) {
2012-10-13 09:42:01 +00:00
var tmp = S . token ;
next ( ) ;
switch ( tmp . type ) {
2012-05-27 11:09:01 +00:00
case "num" :
case "string" :
2012-05-27 14:25:31 +00:00
case "name" :
case "operator" :
case "keyword" :
case "atom" :
2012-10-13 09:42:01 +00:00
return tmp . value ;
2012-05-27 14:25:31 +00:00
default :
unexpected ( ) ;
2012-05-27 11:09:01 +00:00
}
} ;
function as _name ( ) {
2012-10-13 09:42:01 +00:00
var tmp = S . token ;
next ( ) ;
switch ( tmp . type ) {
2012-05-27 11:09:01 +00:00
case "name" :
case "operator" :
case "keyword" :
case "atom" :
2012-10-13 09:42:01 +00:00
return tmp . value ;
2012-05-27 11:09:01 +00:00
default :
unexpected ( ) ;
}
} ;
2012-08-19 12:57:50 +00:00
function as _symbol ( type , noerror ) {
if ( ! is ( "name" ) ) {
if ( ! noerror ) croak ( "Name expected" ) ;
return null ;
}
2012-05-27 14:25:31 +00:00
var name = S . token . value ;
2012-08-19 12:57:50 +00:00
var sym = new ( name == "this" ? AST _This : type ) ( {
2012-05-27 11:09:01 +00:00
name : String ( S . token . value ) ,
start : S . token ,
end : S . token
} ) ;
next ( ) ;
return sym ;
} ;
2012-05-27 14:25:31 +00:00
var subscripts = function ( expr , allow _calls ) {
var start = expr . start ;
2012-05-27 11:09:01 +00:00
if ( is ( "punc" , "." ) ) {
next ( ) ;
return subscripts ( new AST _Dot ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
expression : expr ,
2012-05-27 14:25:31 +00:00
property : as _name ( ) ,
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) , allow _calls ) ;
}
if ( is ( "punc" , "[" ) ) {
next ( ) ;
2012-10-11 10:00:58 +00:00
var prop = expression ( true ) ;
2012-05-27 14:25:31 +00:00
expect ( "]" ) ;
2012-05-27 11:09:01 +00:00
return subscripts ( new AST _Sub ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
expression : expr ,
2012-05-27 14:25:31 +00:00
property : prop ,
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) , allow _calls ) ;
}
if ( allow _calls && is ( "punc" , "(" ) ) {
next ( ) ;
return subscripts ( new AST _Call ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
expression : expr ,
2012-05-27 14:25:31 +00:00
args : expr _list ( ")" ) ,
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) , true ) ;
}
return expr ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var maybe _unary = function ( allow _calls ) {
2012-10-13 09:42:01 +00:00
var start = S . token ;
if ( is ( "operator" ) && UNARY _PREFIX ( start . value ) ) {
next ( ) ;
2013-09-02 06:56:27 +00:00
handle _regexp ( ) ;
2012-10-13 09:42:01 +00:00
var ex = make _unary ( AST _UnaryPrefix , start . value , maybe _unary ( allow _calls ) ) ;
2012-05-27 14:25:31 +00:00
ex . start = start ;
ex . end = prev ( ) ;
return ex ;
2012-05-27 11:09:01 +00:00
}
var val = expr _atom ( allow _calls ) ;
2012-10-11 10:00:58 +00:00
while ( is ( "operator" ) && UNARY _POSTFIX ( S . token . value ) && ! S . token . nlb ) {
2012-05-27 11:09:01 +00:00
val = make _unary ( AST _UnaryPostfix , S . token . value , val ) ;
2012-05-27 14:25:31 +00:00
val . start = start ;
val . end = S . token ;
2012-05-27 11:09:01 +00:00
next ( ) ;
}
return val ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
function make _unary ( ctor , op , expr ) {
if ( ( op == "++" || op == "--" ) && ! is _assignable ( expr ) )
croak ( "Invalid use of " + op + " operator" ) ;
return new ctor ( { operator : op , expression : expr } ) ;
} ;
2012-05-27 14:25:31 +00:00
var expr _op = function ( left , min _prec , no _in ) {
2012-05-27 11:09:01 +00:00
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 ( true ) , prec , no _in ) ;
return expr _op ( new AST _Binary ( {
2012-05-27 14:25:31 +00:00
start : left . start ,
2012-05-27 11:09:01 +00:00
left : left ,
operator : op ,
2012-05-27 14:25:31 +00:00
right : right ,
end : right . end
2012-05-27 11:09:01 +00:00
} ) , min _prec , no _in ) ;
}
return left ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
function expr _ops ( no _in ) {
return expr _op ( maybe _unary ( true ) , 0 , no _in ) ;
} ;
2012-05-27 14:25:31 +00:00
var maybe _conditional = function ( no _in ) {
var start = S . token ;
2012-05-27 11:09:01 +00:00
var expr = expr _ops ( no _in ) ;
if ( is ( "operator" , "?" ) ) {
next ( ) ;
var yes = expression ( false ) ;
expect ( ":" ) ;
return new AST _Conditional ( {
2012-05-27 14:25:31 +00:00
start : start ,
condition : expr ,
consequent : yes ,
alternative : expression ( false , no _in ) ,
end : peek ( )
2012-05-27 11:09:01 +00:00
} ) ;
}
return expr ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
function is _assignable ( expr ) {
2012-09-21 11:19:05 +00:00
if ( ! options . strict ) return true ;
2013-05-09 05:44:24 +00:00
if ( expr instanceof AST _This ) return false ;
return ( expr instanceof AST _PropAccess || expr instanceof AST _Symbol ) ;
2012-05-27 11:09:01 +00:00
} ;
2012-05-27 14:25:31 +00:00
var maybe _assign = function ( no _in ) {
var start = S . token ;
2012-05-27 11:09:01 +00:00
var left = maybe _conditional ( no _in ) , val = S . token . value ;
2012-10-11 10:00:58 +00:00
if ( is ( "operator" ) && ASSIGNMENT ( val ) ) {
2012-05-27 11:09:01 +00:00
if ( is _assignable ( left ) ) {
next ( ) ;
return new AST _Assign ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-05-27 11:09:01 +00:00
left : left ,
2012-10-11 10:00:58 +00:00
operator : val ,
2012-05-27 14:25:31 +00:00
right : maybe _assign ( no _in ) ,
2013-01-26 12:24:54 +00:00
end : prev ( )
2012-05-27 11:09:01 +00:00
} ) ;
}
croak ( "Invalid assignment" ) ;
}
return left ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var expression = function ( commas , no _in ) {
var start = S . token ;
2012-05-27 11:09:01 +00:00
var expr = maybe _assign ( no _in ) ;
if ( commas && is ( "punc" , "," ) ) {
next ( ) ;
return new AST _Seq ( {
2012-05-27 14:25:31 +00:00
start : start ,
2012-09-14 12:36:38 +00:00
car : expr ,
cdr : expression ( true , no _in ) ,
2012-05-27 14:25:31 +00:00
end : peek ( )
2012-05-27 11:09:01 +00:00
} ) ;
}
return expr ;
2012-05-27 14:25:31 +00:00
} ;
2012-05-27 11:09:01 +00:00
function in _loop ( cont ) {
++ S . in _loop ;
var ret = cont ( ) ;
-- S . in _loop ;
return ret ;
} ;
2013-05-15 10:27:23 +00:00
if ( options . expression ) {
return expression ( true ) ;
}
2012-09-21 11:19:05 +00:00
return ( function ( ) {
var start = S . token ;
var body = [ ] ;
while ( ! is ( "eof" ) )
body . push ( statement ( ) ) ;
var end = prev ( ) ;
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 ;
} ) ( ) ;
2012-05-27 11:09:01 +00:00
} ;