1 /***********************************************************************
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
6 -------------------------------- (C) ---------------------------------
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
12 Distributed under the BSD license:
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15 Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions
21 * Redistributions of source code must retain the above
22 copyright notice, this list of conditions and the following
25 * Redistributions in binary form must reproduce the above
26 copyright notice, this list of conditions and the following
27 disclaimer in the documentation and/or other materials
28 provided with the distribution.
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
34 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
37 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
39 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
40 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 ***********************************************************************/
47 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';
48 var KEYWORDS_ATOM = 'false null true';
49 var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield'
50 + " " + KEYWORDS_ATOM + " " + KEYWORDS;
51 var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
53 KEYWORDS = makePredicate(KEYWORDS);
54 RESERVED_WORDS = makePredicate(RESERVED_WORDS);
55 KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
56 KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
58 var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
60 var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
61 var RE_OCT_NUMBER = /^0[0-7]+$/;
63 var OPERATORS = makePredicate([
110 var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF"));
112 var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
114 var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
116 var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
118 var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
120 /* -----[ Tokenizer ]----- */
122 // regexps adapted from http://xregexp.com/plugins/#unicode
124 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\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\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-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\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-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\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-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\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-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
125 digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"),
126 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]"),
127 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]"),
128 connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
131 function is_letter(code) {
132 return (code >= 97 && code <= 122)
133 || (code >= 65 && code <= 90)
134 || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
137 function is_digit(code) {
138 return code >= 48 && code <= 57;
141 function is_alphanumeric_char(code) {
142 return is_digit(code) || is_letter(code);
145 function is_unicode_digit(code) {
146 return UNICODE.digit.test(String.fromCharCode(code));
149 function is_unicode_combining_mark(ch) {
150 return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
153 function is_unicode_connector_punctuation(ch) {
154 return UNICODE.connector_punctuation.test(ch);
157 function is_identifier(name) {
158 return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
161 function is_identifier_start(code) {
162 return code == 36 || code == 95 || is_letter(code);
165 function is_identifier_char(ch) {
166 var code = ch.charCodeAt(0);
167 return is_identifier_start(code)
169 || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
170 || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
171 || is_unicode_combining_mark(ch)
172 || is_unicode_connector_punctuation(ch)
173 || is_unicode_digit(code)
177 function is_identifier_string(str){
178 return /^[a-z_$][a-z0-9_$]*$/i.test(str);
181 function parse_js_number(num) {
182 if (RE_HEX_NUMBER.test(num)) {
183 return parseInt(num.substr(2), 16);
184 } else if (RE_OCT_NUMBER.test(num)) {
185 return parseInt(num.substr(1), 8);
187 var val = parseFloat(num);
188 if (val == num) return val;
192 function JS_Parse_Error(message, filename, line, col, pos) {
193 this.message = message;
194 this.filename = filename;
199 JS_Parse_Error.prototype = Object.create(Error.prototype);
200 JS_Parse_Error.prototype.constructor = JS_Parse_Error;
201 JS_Parse_Error.prototype.name = "SyntaxError";
202 configure_error_stack(JS_Parse_Error);
204 function js_error(message, filename, line, col, pos) {
205 throw new JS_Parse_Error(message, filename, line, col, pos);
208 function is_token(token, type, val) {
209 return token.type == type && (val == null || token.value == val);
214 function tokenizer($TEXT, filename, html5_comments, shebang) {
225 newline_before : false,
226 regex_allowed : false,
227 comments_before : [],
232 function peek() { return S.text.charAt(S.pos); };
234 function next(signal_eof, in_string) {
235 var ch = S.text.charAt(S.pos++);
236 if (signal_eof && !ch)
238 if (NEWLINE_CHARS(ch)) {
239 S.newline_before = S.newline_before || !in_string;
242 if (!in_string && ch == "\r" && peek() == "\n") {
243 // treat a \r\n sequence as a single \n
253 function forward(i) {
254 while (i-- > 0) next();
257 function looking_at(str) {
258 return S.text.substr(S.pos, str.length) == str;
261 function find_eol() {
263 for (var i = S.pos, n = S.text.length; i < n; ++i) {
265 if (NEWLINE_CHARS(ch))
271 function find(what, signal_eof) {
272 var pos = S.text.indexOf(what, S.pos);
273 if (signal_eof && pos == -1) throw EX_EOF;
277 function start_token() {
283 var prev_was_dot = false;
284 function token(type, value, is_comment) {
285 S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
286 (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
287 (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
288 prev_was_dot = (type == "punc" && value == ".");
298 nlb : S.newline_before,
301 if (/^(?:num|string|regexp)$/i.test(type)) {
302 ret.raw = $TEXT.substring(ret.pos, ret.endpos);
305 ret.comments_before = S.comments_before;
306 S.comments_before = [];
307 // make note of any newlines in the comments that came before
308 for (var i = 0, len = ret.comments_before.length; i < len; i++) {
309 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
312 S.newline_before = false;
313 return new AST_Token(ret);
316 function skip_whitespace() {
317 while (WHITESPACE_CHARS(peek()))
321 function read_while(pred) {
322 var ret = "", ch, i = 0;
323 while ((ch = peek()) && pred(ch, i++))
328 function parse_error(err) {
329 js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
332 function read_num(prefix) {
333 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
334 var num = read_while(function(ch, i){
335 var code = ch.charCodeAt(0);
337 case 120: case 88: // xX
338 return has_x ? false : (has_x = true);
339 case 101: case 69: // eE
340 return has_x ? true : has_e ? false : (has_e = after_e = true);
342 return after_e || (i == 0 && !prefix);
345 case (after_e = false, 46): // .
346 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
348 return is_alphanumeric_char(code);
350 if (prefix) num = prefix + num;
351 if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
352 parse_error("Legacy octal literals are not allowed in strict mode");
354 var valid = parse_js_number(num);
356 return token("num", valid);
358 parse_error("Invalid syntax: " + num);
362 function read_escaped_char(in_string) {
363 var ch = next(true, in_string);
364 switch (ch.charCodeAt(0)) {
365 case 110 : return "\n";
366 case 114 : return "\r";
367 case 116 : return "\t";
368 case 98 : return "\b";
369 case 118 : return "\u000b"; // \v
370 case 102 : return "\f";
371 case 120 : return String.fromCharCode(hex_bytes(2)); // \x
372 case 117 : return String.fromCharCode(hex_bytes(4)); // \u
373 case 10 : return ""; // newline
375 if (peek() == "\n") { // DOS newline
376 next(true, in_string);
380 if (ch >= "0" && ch <= "7")
381 return read_octal_escape_sequence(ch);
385 function read_octal_escape_sequence(ch) {
388 if (p >= "0" && p <= "7") {
390 if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
395 if (ch === "0") return "\0";
396 if (ch.length > 0 && next_token.has_directive("use strict"))
397 parse_error("Legacy octal escape sequences are not allowed in strict mode");
398 return String.fromCharCode(parseInt(ch, 8));
401 function hex_bytes(n) {
404 var digit = parseInt(next(true), 16);
406 parse_error("Invalid hex-character pattern in string");
407 num = (num << 4) | digit;
412 var read_string = with_eof_error("Unterminated string constant", function(quote_char){
413 var quote = next(), ret = "";
415 var ch = next(true, true);
416 if (ch == "\\") ch = read_escaped_char(true);
417 else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
418 else if (ch == quote) break;
421 var tok = token("string", ret);
422 tok.quote = quote_char;
426 function skip_line_comment(type) {
427 var regex_allowed = S.regex_allowed;
428 var i = find_eol(), ret;
430 ret = S.text.substr(S.pos);
431 S.pos = S.text.length;
433 ret = S.text.substring(S.pos, i);
436 S.col = S.tokcol + (S.pos - S.tokpos);
437 S.comments_before.push(token(type, ret, true));
438 S.regex_allowed = regex_allowed;
442 var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
443 var regex_allowed = S.regex_allowed;
444 var i = find("*/", true);
445 var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
446 // update stream position
447 forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
448 S.comments_before.push(token("comment2", text, true));
449 S.regex_allowed = regex_allowed;
453 function read_name() {
454 var backslash = false, name = "", ch, escaped = false, hex;
455 while ((ch = peek()) != null) {
457 if (ch == "\\") escaped = backslash = true, next();
458 else if (is_identifier_char(ch)) name += next();
462 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
463 ch = read_escaped_char();
464 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
469 if (KEYWORDS(name) && escaped) {
470 hex = name.charCodeAt(0).toString(16).toUpperCase();
471 name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
476 var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
477 var prev_backslash = false, ch, in_class = false;
478 while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
479 parse_error("Unexpected line terminator");
480 } else if (prev_backslash) {
482 prev_backslash = false;
483 } else if (ch == "[") {
486 } else if (ch == "]" && in_class) {
489 } else if (ch == "/" && !in_class) {
491 } else if (ch == "\\") {
492 prev_backslash = true;
496 var mods = read_name();
498 return token("regexp", new RegExp(regexp, mods));
500 parse_error(e.message);
504 function read_operator(prefix) {
506 if (!peek()) return op;
507 var bigger = op + peek();
508 if (OPERATORS(bigger)) {
515 return token("operator", grow(prefix || next()));
518 function handle_slash() {
523 return skip_line_comment("comment1");
526 return skip_multiline_comment();
528 return S.regex_allowed ? read_regexp("") : read_operator("/");
531 function handle_dot() {
533 return is_digit(peek().charCodeAt(0))
535 : token("punc", ".");
538 function read_word() {
539 var word = read_name();
540 if (prev_was_dot) return token("name", word);
541 return KEYWORDS_ATOM(word) ? token("atom", word)
542 : !KEYWORDS(word) ? token("name", word)
543 : OPERATORS(word) ? token("operator", word)
544 : token("keyword", word);
547 function with_eof_error(eof_error, cont) {
552 if (ex === EX_EOF) parse_error(eof_error);
558 function next_token(force_regexp) {
559 if (force_regexp != null)
560 return read_regexp(force_regexp);
561 if (shebang && S.pos == 0 && looking_at("#!")) {
564 skip_line_comment("comment5");
569 if (html5_comments) {
570 if (looking_at("<!--")) {
572 skip_line_comment("comment3");
575 if (looking_at("-->") && S.newline_before) {
577 skip_line_comment("comment4");
582 if (!ch) return token("eof");
583 var code = ch.charCodeAt(0);
585 case 34: case 39: return read_string(ch);
586 case 46: return handle_dot();
588 var tok = handle_slash();
589 if (tok === next_token) continue;
593 if (is_digit(code)) return read_num();
594 if (PUNC_CHARS(ch)) return token("punc", next());
595 if (OPERATOR_CHARS(ch)) return read_operator();
596 if (code == 92 || is_identifier_start(code)) return read_word();
599 parse_error("Unexpected character '" + ch + "'");
602 next_token.context = function(nc) {
607 next_token.add_directive = function(directive) {
608 S.directive_stack[S.directive_stack.length - 1].push(directive);
610 if (S.directives[directive] === undefined) {
611 S.directives[directive] = 1;
613 S.directives[directive]++;
617 next_token.push_directives_stack = function() {
618 S.directive_stack.push([]);
621 next_token.pop_directives_stack = function() {
622 var directives = S.directive_stack[S.directive_stack.length - 1];
624 for (var i = 0; i < directives.length; i++) {
625 S.directives[directives[i]]--;
628 S.directive_stack.pop();
631 next_token.has_directive = function(directive) {
632 return S.directives[directive] !== undefined &&
633 S.directives[directive] > 0;
640 /* -----[ Parser (constants) ]----- */
642 var UNARY_PREFIX = makePredicate([
654 var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
656 var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
658 var PRECEDENCE = (function(a, ret){
659 for (var i = 0; i < a.length; ++i) {
661 for (var j = 0; j < b.length; ++j) {
673 ["==", "===", "!=", "!=="],
674 ["<", ">", "<=", ">=", "in", "instanceof"],
682 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
684 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
686 /* -----[ Parser ]----- */
688 function parse($TEXT, options) {
690 options = defaults(options, {
695 html5_comments : true,
696 bare_returns : false,
701 input : (typeof $TEXT == "string"
702 ? tokenizer($TEXT, options.filename,
703 options.html5_comments, options.shebang)
709 in_directives : true,
716 function is(type, value) {
717 return is_token(S.token, type, value);
720 function peek() { return S.peeked || (S.peeked = S.input()); };
730 S.in_directives = S.in_directives && (
731 S.token.type == "string" || is("punc", ";")
740 function croak(msg, line, col, pos) {
741 var ctx = S.input.context();
744 line != null ? line : ctx.tokline,
745 col != null ? col : ctx.tokcol,
746 pos != null ? pos : ctx.tokpos);
749 function token_error(token, msg) {
750 croak(msg, token.line, token.col);
753 function unexpected(token) {
756 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
759 function expect_token(type, val) {
763 token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
766 function expect(punc) { return expect_token("punc", punc); };
768 function can_insert_semicolon() {
769 return !options.strict && (
770 S.token.nlb || is("eof") || is("punc", "}")
774 function semicolon(optional) {
775 if (is("punc", ";")) next();
776 else if (!optional && !can_insert_semicolon()) unexpected();
779 function parenthesised() {
781 var exp = expression(true);
786 function embed_tokens(parser) {
797 function handle_regexp() {
798 if (is("operator", "/") || is("operator", "/=")) {
800 S.token = S.input(S.token.value.substr(1)); // force regexp
804 var statement = embed_tokens(function() {
807 switch (S.token.type) {
810 if (S.in_directives === true) {
811 if ((is_token(peek(), "punc", ";") || peek().nlb) && S.token.raw.indexOf("\\") === -1) {
812 S.input.add_directive(S.token.value);
814 S.in_directives = false;
817 var dir = S.in_directives, stat = simple_statement();
819 return new AST_Directive({
820 start : stat.body.start,
822 quote : stat.body.quote,
823 value : stat.body.value,
831 return simple_statement();
834 return is_token(peek(), "punc", ":")
835 ? labeled_statement()
836 : simple_statement();
839 switch (S.token.value) {
841 return new AST_BlockStatement({
848 return simple_statement();
850 S.in_directives = false;
852 return new AST_EmptyStatement();
858 switch (tmp = S.token.value, next(), tmp) {
860 return break_cont(AST_Break);
863 return break_cont(AST_Continue);
867 return new AST_Debugger();
871 body : in_loop(statement),
872 condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
876 return new AST_While({
877 condition : parenthesised(),
878 body : in_loop(statement)
885 return function_(AST_Defun);
891 if (S.in_function == 0 && !options.bare_returns)
892 croak("'return' outside of function");
893 return new AST_Return({
894 value: ( is("punc", ";")
896 : can_insert_semicolon()
898 : (tmp = expression(true), semicolon(), tmp) )
902 return new AST_Switch({
903 expression : parenthesised(),
904 body : in_loop(switch_body_)
909 croak("Illegal newline after 'throw'");
910 return new AST_Throw({
911 value: (tmp = expression(true), semicolon(), tmp)
918 return tmp = var_(), semicolon(), tmp;
921 return tmp = const_(), semicolon(), tmp;
924 if (S.input.has_directive("use strict")) {
925 croak("Strict mode may not include a with statement");
927 return new AST_With({
928 expression : parenthesised(),
938 function labeled_statement() {
939 var label = as_symbol(AST_Label);
940 if (find_if(function(l){ return l.name == label.name }, S.labels)) {
941 // ECMA-262, 12.12: An ECMAScript program is considered
942 // syntactically incorrect if it contains a
943 // LabelledStatement that is enclosed by a
944 // LabelledStatement with the same Identifier as label.
945 croak("Label " + label.name + " defined twice");
948 S.labels.push(label);
949 var stat = statement();
951 if (!(stat instanceof AST_IterationStatement)) {
952 // check for `continue` that refers to this label.
953 // those should be reported as syntax errors.
954 // https://github.com/mishoo/UglifyJS2/issues/287
955 label.references.forEach(function(ref){
956 if (ref instanceof AST_Continue) {
957 ref = ref.label.start;
958 croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
959 ref.line, ref.col, ref.pos);
963 return new AST_LabeledStatement({ body: stat, label: label });
966 function simple_statement(tmp) {
967 return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
970 function break_cont(type) {
971 var label = null, ldef;
972 if (!can_insert_semicolon()) {
973 label = as_symbol(AST_LabelRef, true);
976 ldef = find_if(function(l){ return l.name == label.name }, S.labels);
978 croak("Undefined label " + label.name);
981 else if (S.in_loop == 0)
982 croak(type.TYPE + " not inside a loop or switch");
984 var stat = new type({ label: label });
985 if (ldef) ldef.references.push(stat);
992 if (!is("punc", ";")) {
993 init = is("keyword", "var")
994 ? (next(), var_(true))
995 : expression(true, true);
996 if (is("operator", "in")) {
997 if (init instanceof AST_Var && init.definitions.length > 1)
998 croak("Only one variable declaration allowed in for..in loop");
1000 return for_in(init);
1003 return regular_for(init);
1006 function regular_for(init) {
1008 var test = is("punc", ";") ? null : expression(true);
1010 var step = is("punc", ")") ? null : expression(true);
1012 return new AST_For({
1016 body : in_loop(statement)
1020 function for_in(init) {
1021 var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
1022 var obj = expression(true);
1024 return new AST_ForIn({
1028 body : in_loop(statement)
1032 var function_ = function(ctor) {
1033 var in_statement = ctor === AST_Defun;
1034 var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
1035 if (in_statement && !name)
1040 argnames: (function(first, a){
1041 while (!is("punc", ")")) {
1042 if (first) first = false; else expect(",");
1043 a.push(as_symbol(AST_SymbolFunarg));
1048 body: (function(loop, labels){
1050 S.in_directives = true;
1051 S.input.push_directives_stack();
1055 S.input.pop_directives_stack();
1060 })(S.in_loop, S.labels)
1065 var cond = parenthesised(), body = statement(), belse = null;
1066 if (is("keyword", "else")) {
1068 belse = statement();
1080 while (!is("punc", "}")) {
1081 if (is("eof")) unexpected();
1082 a.push(statement());
1088 function switch_body_() {
1090 var a = [], cur = null, branch = null, tmp;
1091 while (!is("punc", "}")) {
1092 if (is("eof")) unexpected();
1093 if (is("keyword", "case")) {
1094 if (branch) branch.end = prev();
1096 branch = new AST_Case({
1097 start : (tmp = S.token, next(), tmp),
1098 expression : expression(true),
1104 else if (is("keyword", "default")) {
1105 if (branch) branch.end = prev();
1107 branch = new AST_Default({
1108 start : (tmp = S.token, next(), expect(":"), tmp),
1114 if (!cur) unexpected();
1115 cur.push(statement());
1118 if (branch) branch.end = prev();
1124 var body = block_(), bcatch = null, bfinally = null;
1125 if (is("keyword", "catch")) {
1126 var start = S.token;
1129 var name = as_symbol(AST_SymbolCatch);
1131 bcatch = new AST_Catch({
1138 if (is("keyword", "finally")) {
1139 var start = S.token;
1141 bfinally = new AST_Finally({
1147 if (!bcatch && !bfinally)
1148 croak("Missing catch/finally blocks");
1149 return new AST_Try({
1156 function vardefs(no_in, in_const) {
1159 a.push(new AST_VarDef({
1161 name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
1162 value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
1165 if (!is("punc", ","))
1172 var var_ = function(no_in) {
1173 return new AST_Var({
1175 definitions : vardefs(no_in, false),
1180 var const_ = function() {
1181 return new AST_Const({
1183 definitions : vardefs(false, true),
1188 var new_ = function(allow_calls) {
1189 var start = S.token;
1190 expect_token("operator", "new");
1191 var newexp = expr_atom(false), args;
1192 if (is("punc", "(")) {
1194 args = expr_list(")");
1198 return subscripts(new AST_New({
1200 expression : newexp,
1206 function as_atom_node() {
1207 var tok = S.token, ret;
1211 ret = _make_symbol(AST_SymbolRef);
1214 ret = new AST_Number({ start: tok, end: tok, value: tok.value });
1217 ret = new AST_String({
1225 ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
1228 switch (tok.value) {
1230 ret = new AST_False({ start: tok, end: tok });
1233 ret = new AST_True({ start: tok, end: tok });
1236 ret = new AST_Null({ start: tok, end: tok });
1241 if (!is_identifier_string(tok.value)) {
1242 croak("Invalid getter/setter name: " + tok.value,
1243 tok.line, tok.col, tok.pos);
1245 ret = _make_symbol(AST_SymbolRef);
1252 var expr_atom = function(allow_calls) {
1253 if (is("operator", "new")) {
1254 return new_(allow_calls);
1256 var start = S.token;
1258 switch (start.value) {
1261 var ex = expression(true);
1265 return subscripts(ex, allow_calls);
1267 return subscripts(array_(), allow_calls);
1269 return subscripts(object_(), allow_calls);
1273 if (is("keyword", "function")) {
1275 var func = function_(AST_Function);
1278 return subscripts(func, allow_calls);
1280 if (ATOMIC_START_TOKEN[S.token.type]) {
1281 return subscripts(as_atom_node(), allow_calls);
1286 function expr_list(closing, allow_trailing_comma, allow_empty) {
1287 var first = true, a = [];
1288 while (!is("punc", closing)) {
1289 if (first) first = false; else expect(",");
1290 if (allow_trailing_comma && is("punc", closing)) break;
1291 if (is("punc", ",") && allow_empty) {
1292 a.push(new AST_Hole({ start: S.token, end: S.token }));
1294 a.push(expression(false));
1301 var array_ = embed_tokens(function() {
1303 return new AST_Array({
1304 elements: expr_list("]", !options.strict, true)
1308 var create_accessor = embed_tokens(function() {
1309 return function_(AST_Accessor);
1312 var object_ = embed_tokens(function() {
1314 var first = true, a = [];
1315 while (!is("punc", "}")) {
1316 if (first) first = false; else expect(",");
1317 if (!options.strict && is("punc", "}"))
1318 // allow trailing comma
1320 var start = S.token;
1321 var type = start.type;
1322 var name = as_property_name();
1323 if (type == "name" && !is("punc", ":")) {
1324 if (name == "get") {
1325 a.push(new AST_ObjectGetter({
1327 key : as_atom_node(),
1328 value : create_accessor(),
1333 if (name == "set") {
1334 a.push(new AST_ObjectSetter({
1336 key : as_atom_node(),
1337 value : create_accessor(),
1344 a.push(new AST_ObjectKeyVal({
1346 quote : start.quote,
1348 value : expression(false),
1353 return new AST_Object({ properties: a });
1356 function as_property_name() {
1372 function as_name() {
1386 function _make_symbol(type) {
1387 var name = S.token.value;
1388 return new (name == "this" ? AST_This : type)({
1389 name : String(name),
1395 function as_symbol(type, noerror) {
1397 if (!noerror) croak("Name expected");
1400 var sym = _make_symbol(type);
1405 var subscripts = function(expr, allow_calls) {
1406 var start = expr.start;
1407 if (is("punc", ".")) {
1409 return subscripts(new AST_Dot({
1412 property : as_name(),
1416 if (is("punc", "[")) {
1418 var prop = expression(true);
1420 return subscripts(new AST_Sub({
1427 if (allow_calls && is("punc", "(")) {
1429 return subscripts(new AST_Call({
1432 args : expr_list(")"),
1439 var maybe_unary = function(allow_calls) {
1440 var start = S.token;
1441 if (is("operator") && UNARY_PREFIX(start.value)) {
1444 var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
1449 var val = expr_atom(allow_calls);
1450 while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
1451 val = make_unary(AST_UnaryPostfix, S.token.value, val);
1459 function make_unary(ctor, op, expr) {
1460 if ((op == "++" || op == "--") && !is_assignable(expr))
1461 croak("Invalid use of " + op + " operator");
1462 return new ctor({ operator: op, expression: expr });
1465 var expr_op = function(left, min_prec, no_in) {
1466 var op = is("operator") ? S.token.value : null;
1467 if (op == "in" && no_in) op = null;
1468 var prec = op != null ? PRECEDENCE[op] : null;
1469 if (prec != null && prec > min_prec) {
1471 var right = expr_op(maybe_unary(true), prec, no_in);
1472 return expr_op(new AST_Binary({
1478 }), min_prec, no_in);
1483 function expr_ops(no_in) {
1484 return expr_op(maybe_unary(true), 0, no_in);
1487 var maybe_conditional = function(no_in) {
1488 var start = S.token;
1489 var expr = expr_ops(no_in);
1490 if (is("operator", "?")) {
1492 var yes = expression(false);
1494 return new AST_Conditional({
1498 alternative : expression(false, no_in),
1505 function is_assignable(expr) {
1506 if (!options.strict) return true;
1507 if (expr instanceof AST_This) return false;
1508 return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
1511 var maybe_assign = function(no_in) {
1512 var start = S.token;
1513 var left = maybe_conditional(no_in), val = S.token.value;
1514 if (is("operator") && ASSIGNMENT(val)) {
1515 if (is_assignable(left)) {
1517 return new AST_Assign({
1521 right : maybe_assign(no_in),
1525 croak("Invalid assignment");
1530 var expression = function(commas, no_in) {
1531 var start = S.token;
1532 var expr = maybe_assign(no_in);
1533 if (commas && is("punc", ",")) {
1535 return new AST_Seq({
1538 cdr : expression(true, no_in),
1545 function in_loop(cont) {
1552 if (options.expression) {
1553 return expression(true);
1557 var start = S.token;
1559 S.input.push_directives_stack();
1561 body.push(statement());
1562 S.input.pop_directives_stack();
1564 var toplevel = options.toplevel;
1566 toplevel.body = toplevel.body.concat(body);
1569 toplevel = new AST_Toplevel({ start: start, body: body, end: end });