1/***********************************************************************
2
3  A JavaScript tokenizer / parser / beautifier / compressor.
4  https://github.com/mishoo/UglifyJS2
5
6  -------------------------------- (C) ---------------------------------
7
8                           Author: Mihai Bazon
9                         <mihai.bazon@gmail.com>
10                       http://mihai.bazon.net/blog
11
12  Distributed under the BSD license:
13
14    Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15    Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
16
17    Redistribution and use in source and binary forms, with or without
18    modification, are permitted provided that the following conditions
19    are met:
20
21        * Redistributions of source code must retain the above
22          copyright notice, this list of conditions and the following
23          disclaimer.
24
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.
29
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
41    SUCH DAMAGE.
42
43 ***********************************************************************/
44
45"use strict";
46
47var 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';
48var KEYWORDS_ATOM = 'false null true';
49var 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;
51var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
52
53KEYWORDS = makePredicate(KEYWORDS);
54RESERVED_WORDS = makePredicate(RESERVED_WORDS);
55KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
56KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
57
58var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
59
60var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
61var RE_OCT_NUMBER = /^0[0-7]+$/;
62
63var OPERATORS = makePredicate([
64    "in",
65    "instanceof",
66    "typeof",
67    "new",
68    "void",
69    "delete",
70    "++",
71    "--",
72    "+",
73    "-",
74    "!",
75    "~",
76    "&",
77    "|",
78    "^",
79    "*",
80    "/",
81    "%",
82    ">>",
83    "<<",
84    ">>>",
85    "<",
86    ">",
87    "<=",
88    ">=",
89    "==",
90    "===",
91    "!=",
92    "!==",
93    "?",
94    "=",
95    "+=",
96    "-=",
97    "/=",
98    "*=",
99    "%=",
100    ">>=",
101    "<<=",
102    ">>>=",
103    "|=",
104    "^=",
105    "&=",
106    "&&",
107    "||"
108]);
109
110var 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"));
111
112var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
113
114var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
115
116var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
117
118var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
119
120/* -----[ Tokenizer ]----- */
121
122// regexps adapted from http://xregexp.com/plugins/#unicode
123var 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]")
129};
130
131function is_letter(code) {
132    return (code >= 97 && code <= 122)
133        || (code >= 65 && code <= 90)
134        || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
135};
136
137function is_digit(code) {
138    return code >= 48 && code <= 57;
139};
140
141function is_alphanumeric_char(code) {
142    return is_digit(code) || is_letter(code);
143};
144
145function is_unicode_digit(code) {
146    return UNICODE.digit.test(String.fromCharCode(code));
147}
148
149function is_unicode_combining_mark(ch) {
150    return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
151};
152
153function is_unicode_connector_punctuation(ch) {
154    return UNICODE.connector_punctuation.test(ch);
155};
156
157function is_identifier(name) {
158    return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
159};
160
161function is_identifier_start(code) {
162    return code == 36 || code == 95 || is_letter(code);
163};
164
165function is_identifier_char(ch) {
166    var code = ch.charCodeAt(0);
167    return is_identifier_start(code)
168        || is_digit(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)
174    ;
175};
176
177function is_identifier_string(str){
178    return /^[a-z_$][a-z0-9_$]*$/i.test(str);
179};
180
181function 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);
186    } else {
187        var val = parseFloat(num);
188        if (val == num) return val;
189    }
190};
191
192function JS_Parse_Error(message, filename, line, col, pos) {
193    this.message = message;
194    this.filename = filename;
195    this.line = line;
196    this.col = col;
197    this.pos = pos;
198};
199JS_Parse_Error.prototype = Object.create(Error.prototype);
200JS_Parse_Error.prototype.constructor = JS_Parse_Error;
201JS_Parse_Error.prototype.name = "SyntaxError";
202configure_error_stack(JS_Parse_Error);
203
204function js_error(message, filename, line, col, pos) {
205    throw new JS_Parse_Error(message, filename, line, col, pos);
206};
207
208function is_token(token, type, val) {
209    return token.type == type && (val == null || token.value == val);
210};
211
212var EX_EOF = {};
213
214function tokenizer($TEXT, filename, html5_comments, shebang) {
215
216    var S = {
217        text            : $TEXT,
218        filename        : filename,
219        pos             : 0,
220        tokpos          : 0,
221        line            : 1,
222        tokline         : 0,
223        col             : 0,
224        tokcol          : 0,
225        newline_before  : false,
226        regex_allowed   : false,
227        comments_before : [],
228        directives      : {},
229        directive_stack : []
230    };
231
232    function peek() { return S.text.charAt(S.pos); };
233
234    function next(signal_eof, in_string) {
235        var ch = S.text.charAt(S.pos++);
236        if (signal_eof && !ch)
237            throw EX_EOF;
238        if (NEWLINE_CHARS(ch)) {
239            S.newline_before = S.newline_before || !in_string;
240            ++S.line;
241            S.col = 0;
242            if (!in_string && ch == "\r" && peek() == "\n") {
243                // treat a \r\n sequence as a single \n
244                ++S.pos;
245                ch = "\n";
246            }
247        } else {
248            ++S.col;
249        }
250        return ch;
251    };
252
253    function forward(i) {
254        while (i-- > 0) next();
255    };
256
257    function looking_at(str) {
258        return S.text.substr(S.pos, str.length) == str;
259    };
260
261    function find_eol() {
262        var text = S.text;
263        for (var i = S.pos, n = S.text.length; i < n; ++i) {
264            var ch = text[i];
265            if (NEWLINE_CHARS(ch))
266                return i;
267        }
268        return -1;
269    };
270
271    function find(what, signal_eof) {
272        var pos = S.text.indexOf(what, S.pos);
273        if (signal_eof && pos == -1) throw EX_EOF;
274        return pos;
275    };
276
277    function start_token() {
278        S.tokline = S.line;
279        S.tokcol = S.col;
280        S.tokpos = S.pos;
281    };
282
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        if (type == "punc" && value == ".") {
289            prev_was_dot = true;
290        } else if (!is_comment) {
291            prev_was_dot = false;
292        }
293        var ret = {
294            type    : type,
295            value   : value,
296            line    : S.tokline,
297            col     : S.tokcol,
298            pos     : S.tokpos,
299            endline : S.line,
300            endcol  : S.col,
301            endpos  : S.pos,
302            nlb     : S.newline_before,
303            file    : filename
304        };
305        if (/^(?:num|string|regexp)$/i.test(type)) {
306            ret.raw = $TEXT.substring(ret.pos, ret.endpos);
307        }
308        if (!is_comment) {
309            ret.comments_before = S.comments_before;
310            S.comments_before = [];
311            // make note of any newlines in the comments that came before
312            for (var i = 0, len = ret.comments_before.length; i < len; i++) {
313                ret.nlb = ret.nlb || ret.comments_before[i].nlb;
314            }
315        }
316        S.newline_before = false;
317        return new AST_Token(ret);
318    };
319
320    function skip_whitespace() {
321        while (WHITESPACE_CHARS(peek()))
322            next();
323    };
324
325    function read_while(pred) {
326        var ret = "", ch, i = 0;
327        while ((ch = peek()) && pred(ch, i++))
328            ret += next();
329        return ret;
330    };
331
332    function parse_error(err) {
333        js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
334    };
335
336    function read_num(prefix) {
337        var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
338        var num = read_while(function(ch, i){
339            var code = ch.charCodeAt(0);
340            switch (code) {
341              case 120: case 88: // xX
342                return has_x ? false : (has_x = true);
343              case 101: case 69: // eE
344                return has_x ? true : has_e ? false : (has_e = after_e = true);
345              case 45: // -
346                return after_e || (i == 0 && !prefix);
347              case 43: // +
348                return after_e;
349              case (after_e = false, 46): // .
350                return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
351            }
352            return is_alphanumeric_char(code);
353        });
354        if (prefix) num = prefix + num;
355        if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
356            parse_error("Legacy octal literals are not allowed in strict mode");
357        }
358        var valid = parse_js_number(num);
359        if (!isNaN(valid)) {
360            return token("num", valid);
361        } else {
362            parse_error("Invalid syntax: " + num);
363        }
364    };
365
366    function read_escaped_char(in_string) {
367        var ch = next(true, in_string);
368        switch (ch.charCodeAt(0)) {
369          case 110 : return "\n";
370          case 114 : return "\r";
371          case 116 : return "\t";
372          case 98  : return "\b";
373          case 118 : return "\u000b"; // \v
374          case 102 : return "\f";
375          case 120 : return String.fromCharCode(hex_bytes(2)); // \x
376          case 117 : return String.fromCharCode(hex_bytes(4)); // \u
377          case 10  : return ""; // newline
378          case 13  :            // \r
379            if (peek() == "\n") { // DOS newline
380                next(true, in_string);
381                return "";
382            }
383        }
384        if (ch >= "0" && ch <= "7")
385            return read_octal_escape_sequence(ch);
386        return ch;
387    };
388
389    function read_octal_escape_sequence(ch) {
390        // Read
391        var p = peek();
392        if (p >= "0" && p <= "7") {
393            ch += next(true);
394            if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
395                ch += next(true);
396        }
397
398        // Parse
399        if (ch === "0") return "\0";
400        if (ch.length > 0 && next_token.has_directive("use strict"))
401            parse_error("Legacy octal escape sequences are not allowed in strict mode");
402        return String.fromCharCode(parseInt(ch, 8));
403    }
404
405    function hex_bytes(n) {
406        var num = 0;
407        for (; n > 0; --n) {
408            var digit = parseInt(next(true), 16);
409            if (isNaN(digit))
410                parse_error("Invalid hex-character pattern in string");
411            num = (num << 4) | digit;
412        }
413        return num;
414    };
415
416    var read_string = with_eof_error("Unterminated string constant", function(quote_char){
417        var quote = next(), ret = "";
418        for (;;) {
419            var ch = next(true, true);
420            if (ch == "\\") ch = read_escaped_char(true);
421            else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
422            else if (ch == quote) break;
423            ret += ch;
424        }
425        var tok = token("string", ret);
426        tok.quote = quote_char;
427        return tok;
428    });
429
430    function skip_line_comment(type) {
431        var regex_allowed = S.regex_allowed;
432        var i = find_eol(), ret;
433        if (i == -1) {
434            ret = S.text.substr(S.pos);
435            S.pos = S.text.length;
436        } else {
437            ret = S.text.substring(S.pos, i);
438            S.pos = i;
439        }
440        S.col = S.tokcol + (S.pos - S.tokpos);
441        S.comments_before.push(token(type, ret, true));
442        S.regex_allowed = regex_allowed;
443        return next_token;
444    };
445
446    var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
447        var regex_allowed = S.regex_allowed;
448        var i = find("*/", true);
449        var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
450        // update stream position
451        forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
452        S.comments_before.push(token("comment2", text, true));
453        S.regex_allowed = regex_allowed;
454        return next_token;
455    });
456
457    function read_name() {
458        var backslash = false, name = "", ch, escaped = false, hex;
459        while ((ch = peek()) != null) {
460            if (!backslash) {
461                if (ch == "\\") escaped = backslash = true, next();
462                else if (is_identifier_char(ch)) name += next();
463                else break;
464            }
465            else {
466                if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
467                ch = read_escaped_char();
468                if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
469                name += ch;
470                backslash = false;
471            }
472        }
473        if (KEYWORDS(name) && escaped) {
474            hex = name.charCodeAt(0).toString(16).toUpperCase();
475            name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
476        }
477        return name;
478    };
479
480    var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
481        var prev_backslash = false, ch, in_class = false;
482        while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
483            parse_error("Unexpected line terminator");
484        } else if (prev_backslash) {
485            regexp += "\\" + ch;
486            prev_backslash = false;
487        } else if (ch == "[") {
488            in_class = true;
489            regexp += ch;
490        } else if (ch == "]" && in_class) {
491            in_class = false;
492            regexp += ch;
493        } else if (ch == "/" && !in_class) {
494            break;
495        } else if (ch == "\\") {
496            prev_backslash = true;
497        } else {
498            regexp += ch;
499        }
500        var mods = read_name();
501        try {
502          return token("regexp", new RegExp(regexp, mods));
503        } catch(e) {
504          parse_error(e.message);
505        }
506    });
507
508    function read_operator(prefix) {
509        function grow(op) {
510            if (!peek()) return op;
511            var bigger = op + peek();
512            if (OPERATORS(bigger)) {
513                next();
514                return grow(bigger);
515            } else {
516                return op;
517            }
518        };
519        return token("operator", grow(prefix || next()));
520    };
521
522    function handle_slash() {
523        next();
524        switch (peek()) {
525          case "/":
526            next();
527            return skip_line_comment("comment1");
528          case "*":
529            next();
530            return skip_multiline_comment();
531        }
532        return S.regex_allowed ? read_regexp("") : read_operator("/");
533    };
534
535    function handle_dot() {
536        next();
537        return is_digit(peek().charCodeAt(0))
538            ? read_num(".")
539            : token("punc", ".");
540    };
541
542    function read_word() {
543        var word = read_name();
544        if (prev_was_dot) return token("name", word);
545        return KEYWORDS_ATOM(word) ? token("atom", word)
546            : !KEYWORDS(word) ? token("name", word)
547            : OPERATORS(word) ? token("operator", word)
548            : token("keyword", word);
549    };
550
551    function with_eof_error(eof_error, cont) {
552        return function(x) {
553            try {
554                return cont(x);
555            } catch(ex) {
556                if (ex === EX_EOF) parse_error(eof_error);
557                else throw ex;
558            }
559        };
560    };
561
562    function next_token(force_regexp) {
563        if (force_regexp != null)
564            return read_regexp(force_regexp);
565        if (shebang && S.pos == 0 && looking_at("#!")) {
566            start_token();
567            forward(2);
568            skip_line_comment("comment5");
569        }
570        for (;;) {
571            skip_whitespace();
572            start_token();
573            if (html5_comments) {
574                if (looking_at("<!--")) {
575                    forward(4);
576                    skip_line_comment("comment3");
577                    continue;
578                }
579                if (looking_at("-->") && S.newline_before) {
580                    forward(3);
581                    skip_line_comment("comment4");
582                    continue;
583                }
584            }
585            var ch = peek();
586            if (!ch) return token("eof");
587            var code = ch.charCodeAt(0);
588            switch (code) {
589              case 34: case 39: return read_string(ch);
590              case 46: return handle_dot();
591              case 47: {
592                  var tok = handle_slash();
593                  if (tok === next_token) continue;
594                  return tok;
595              }
596            }
597            if (is_digit(code)) return read_num();
598            if (PUNC_CHARS(ch)) return token("punc", next());
599            if (OPERATOR_CHARS(ch)) return read_operator();
600            if (code == 92 || is_identifier_start(code)) return read_word();
601            break;
602        }
603        parse_error("Unexpected character '" + ch + "'");
604    };
605
606    next_token.context = function(nc) {
607        if (nc) S = nc;
608        return S;
609    };
610
611    next_token.add_directive = function(directive) {
612        S.directive_stack[S.directive_stack.length - 1].push(directive);
613
614        if (S.directives[directive] === undefined) {
615            S.directives[directive] = 1;
616        } else {
617            S.directives[directive]++;
618        }
619    }
620
621    next_token.push_directives_stack = function() {
622        S.directive_stack.push([]);
623    }
624
625    next_token.pop_directives_stack = function() {
626        var directives = S.directive_stack[S.directive_stack.length - 1];
627
628        for (var i = 0; i < directives.length; i++) {
629            S.directives[directives[i]]--;
630        }
631
632        S.directive_stack.pop();
633    }
634
635    next_token.has_directive = function(directive) {
636        return S.directives[directive] !== undefined &&
637            S.directives[directive] > 0;
638    }
639
640    return next_token;
641
642};
643
644/* -----[ Parser (constants) ]----- */
645
646var UNARY_PREFIX = makePredicate([
647    "typeof",
648    "void",
649    "delete",
650    "--",
651    "++",
652    "!",
653    "~",
654    "-",
655    "+"
656]);
657
658var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
659
660var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
661
662var PRECEDENCE = (function(a, ret){
663    for (var i = 0; i < a.length; ++i) {
664        var b = a[i];
665        for (var j = 0; j < b.length; ++j) {
666            ret[b[j]] = i + 1;
667        }
668    }
669    return ret;
670})(
671    [
672        ["||"],
673        ["&&"],
674        ["|"],
675        ["^"],
676        ["&"],
677        ["==", "===", "!=", "!=="],
678        ["<", ">", "<=", ">=", "in", "instanceof"],
679        [">>", "<<", ">>>"],
680        ["+", "-"],
681        ["*", "/", "%"]
682    ],
683    {}
684);
685
686var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
687
688var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
689
690/* -----[ Parser ]----- */
691
692function parse($TEXT, options) {
693
694    options = defaults(options, {
695        bare_returns   : false,
696        cli            : false,
697        expression     : false,
698        filename       : null,
699        html5_comments : true,
700        shebang        : true,
701        strict         : false,
702        toplevel       : null,
703    });
704
705    var S = {
706        input         : (typeof $TEXT == "string"
707                         ? tokenizer($TEXT, options.filename,
708                                     options.html5_comments, options.shebang)
709                         : $TEXT),
710        token         : null,
711        prev          : null,
712        peeked        : null,
713        in_function   : 0,
714        in_directives : true,
715        in_loop       : 0,
716        labels        : []
717    };
718
719    S.token = next();
720
721    function is(type, value) {
722        return is_token(S.token, type, value);
723    };
724
725    function peek() { return S.peeked || (S.peeked = S.input()); };
726
727    function next() {
728        S.prev = S.token;
729        if (S.peeked) {
730            S.token = S.peeked;
731            S.peeked = null;
732        } else {
733            S.token = S.input();
734        }
735        S.in_directives = S.in_directives && (
736            S.token.type == "string" || is("punc", ";")
737        );
738        return S.token;
739    };
740
741    function prev() {
742        return S.prev;
743    };
744
745    function croak(msg, line, col, pos) {
746        var ctx = S.input.context();
747        js_error(msg,
748                 ctx.filename,
749                 line != null ? line : ctx.tokline,
750                 col != null ? col : ctx.tokcol,
751                 pos != null ? pos : ctx.tokpos);
752    };
753
754    function token_error(token, msg) {
755        croak(msg, token.line, token.col);
756    };
757
758    function unexpected(token) {
759        if (token == null)
760            token = S.token;
761        token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
762    };
763
764    function expect_token(type, val) {
765        if (is(type, val)) {
766            return next();
767        }
768        token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
769    };
770
771    function expect(punc) { return expect_token("punc", punc); };
772
773    function can_insert_semicolon() {
774        return !options.strict && (
775            S.token.nlb || is("eof") || is("punc", "}")
776        );
777    };
778
779    function semicolon(optional) {
780        if (is("punc", ";")) next();
781        else if (!optional && !can_insert_semicolon()) unexpected();
782    };
783
784    function parenthesised() {
785        expect("(");
786        var exp = expression(true);
787        expect(")");
788        return exp;
789    };
790
791    function embed_tokens(parser) {
792        return function() {
793            var start = S.token;
794            var expr = parser();
795            var end = prev();
796            expr.start = start;
797            expr.end = end;
798            return expr;
799        };
800    };
801
802    function handle_regexp() {
803        if (is("operator", "/") || is("operator", "/=")) {
804            S.peeked = null;
805            S.token = S.input(S.token.value.substr(1)); // force regexp
806        }
807    };
808
809    var statement = embed_tokens(function() {
810        handle_regexp();
811        switch (S.token.type) {
812          case "string":
813            if (S.in_directives) {
814                var token = peek();
815                if (S.token.raw.indexOf("\\") == -1
816                    && (token.nlb
817                        || is_token(token, "eof")
818                        || is_token(token, "punc", ";")
819                        || is_token(token, "punc", "}"))) {
820                    S.input.add_directive(S.token.value);
821                } else {
822                    S.in_directives = false;
823                }
824            }
825            var dir = S.in_directives, stat = simple_statement();
826            return dir ? new AST_Directive(stat.body) : stat;
827          case "num":
828          case "regexp":
829          case "operator":
830          case "atom":
831            return simple_statement();
832
833          case "name":
834            return is_token(peek(), "punc", ":")
835                ? labeled_statement()
836                : simple_statement();
837
838          case "punc":
839            switch (S.token.value) {
840              case "{":
841                return new AST_BlockStatement({
842                    start : S.token,
843                    body  : block_(),
844                    end   : prev()
845                });
846              case "[":
847              case "(":
848                return simple_statement();
849              case ";":
850                S.in_directives = false;
851                next();
852                return new AST_EmptyStatement();
853              default:
854                unexpected();
855            }
856
857          case "keyword":
858            switch (S.token.value) {
859              case "break":
860                next();
861                return break_cont(AST_Break);
862
863              case "continue":
864                next();
865                return break_cont(AST_Continue);
866
867              case "debugger":
868                next();
869                semicolon();
870                return new AST_Debugger();
871
872              case "do":
873                next();
874                var body = in_loop(statement);
875                expect_token("keyword", "while");
876                var condition = parenthesised();
877                semicolon(true);
878                return new AST_Do({
879                    body      : body,
880                    condition : condition
881                });
882
883              case "while":
884                next();
885                return new AST_While({
886                    condition : parenthesised(),
887                    body      : in_loop(statement)
888                });
889
890              case "for":
891                next();
892                return for_();
893
894              case "function":
895                next();
896                return function_(AST_Defun);
897
898              case "if":
899                next();
900                return if_();
901
902              case "return":
903                if (S.in_function == 0 && !options.bare_returns)
904                    croak("'return' outside of function");
905                next();
906                var value = null;
907                if (is("punc", ";")) {
908                    next();
909                } else if (!can_insert_semicolon()) {
910                    value = expression(true);
911                    semicolon();
912                }
913                return new AST_Return({
914                    value: value
915                });
916
917              case "switch":
918                next();
919                return new AST_Switch({
920                    expression : parenthesised(),
921                    body       : in_loop(switch_body_)
922                });
923
924              case "throw":
925                next();
926                if (S.token.nlb)
927                    croak("Illegal newline after 'throw'");
928                var value = expression(true);
929                semicolon();
930                return new AST_Throw({
931                    value: value
932                });
933
934              case "try":
935                next();
936                return try_();
937
938              case "var":
939                next();
940                var node = var_();
941                semicolon();
942                return node;
943
944              case "const":
945                next();
946                var node = const_();
947                semicolon();
948                return node;
949
950              case "with":
951                if (S.input.has_directive("use strict")) {
952                    croak("Strict mode may not include a with statement");
953                }
954                next();
955                return new AST_With({
956                    expression : parenthesised(),
957                    body       : statement()
958                });
959            }
960        }
961        unexpected();
962    });
963
964    function labeled_statement() {
965        var label = as_symbol(AST_Label);
966        if (find_if(function(l){ return l.name == label.name }, S.labels)) {
967            // ECMA-262, 12.12: An ECMAScript program is considered
968            // syntactically incorrect if it contains a
969            // LabelledStatement that is enclosed by a
970            // LabelledStatement with the same Identifier as label.
971            croak("Label " + label.name + " defined twice");
972        }
973        expect(":");
974        S.labels.push(label);
975        var stat = statement();
976        S.labels.pop();
977        if (!(stat instanceof AST_IterationStatement)) {
978            // check for `continue` that refers to this label.
979            // those should be reported as syntax errors.
980            // https://github.com/mishoo/UglifyJS2/issues/287
981            label.references.forEach(function(ref){
982                if (ref instanceof AST_Continue) {
983                    ref = ref.label.start;
984                    croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
985                          ref.line, ref.col, ref.pos);
986                }
987            });
988        }
989        return new AST_LabeledStatement({ body: stat, label: label });
990    };
991
992    function simple_statement(tmp) {
993        return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
994    };
995
996    function break_cont(type) {
997        var label = null, ldef;
998        if (!can_insert_semicolon()) {
999            label = as_symbol(AST_LabelRef, true);
1000        }
1001        if (label != null) {
1002            ldef = find_if(function(l){ return l.name == label.name }, S.labels);
1003            if (!ldef)
1004                croak("Undefined label " + label.name);
1005            label.thedef = ldef;
1006        }
1007        else if (S.in_loop == 0)
1008            croak(type.TYPE + " not inside a loop or switch");
1009        semicolon();
1010        var stat = new type({ label: label });
1011        if (ldef) ldef.references.push(stat);
1012        return stat;
1013    };
1014
1015    function for_() {
1016        expect("(");
1017        var init = null;
1018        if (!is("punc", ";")) {
1019            init = is("keyword", "var")
1020                ? (next(), var_(true))
1021                : expression(true, true);
1022            if (is("operator", "in")) {
1023                if (init instanceof AST_Var && init.definitions.length > 1)
1024                    croak("Only one variable declaration allowed in for..in loop");
1025                next();
1026                return for_in(init);
1027            }
1028        }
1029        return regular_for(init);
1030    };
1031
1032    function regular_for(init) {
1033        expect(";");
1034        var test = is("punc", ";") ? null : expression(true);
1035        expect(";");
1036        var step = is("punc", ")") ? null : expression(true);
1037        expect(")");
1038        return new AST_For({
1039            init      : init,
1040            condition : test,
1041            step      : step,
1042            body      : in_loop(statement)
1043        });
1044    };
1045
1046    function for_in(init) {
1047        var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
1048        var obj = expression(true);
1049        expect(")");
1050        return new AST_ForIn({
1051            init   : init,
1052            name   : lhs,
1053            object : obj,
1054            body   : in_loop(statement)
1055        });
1056    };
1057
1058    var function_ = function(ctor) {
1059        var in_statement = ctor === AST_Defun;
1060        var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
1061        if (in_statement && !name)
1062            unexpected();
1063        expect("(");
1064        return new ctor({
1065            name: name,
1066            argnames: (function(first, a){
1067                while (!is("punc", ")")) {
1068                    if (first) first = false; else expect(",");
1069                    a.push(as_symbol(AST_SymbolFunarg));
1070                }
1071                next();
1072                return a;
1073            })(true, []),
1074            body: (function(loop, labels){
1075                ++S.in_function;
1076                S.in_directives = true;
1077                S.input.push_directives_stack();
1078                S.in_loop = 0;
1079                S.labels = [];
1080                var a = block_();
1081                S.input.pop_directives_stack();
1082                --S.in_function;
1083                S.in_loop = loop;
1084                S.labels = labels;
1085                return a;
1086            })(S.in_loop, S.labels)
1087        });
1088    };
1089
1090    function if_() {
1091        var cond = parenthesised(), body = statement(), belse = null;
1092        if (is("keyword", "else")) {
1093            next();
1094            belse = statement();
1095        }
1096        return new AST_If({
1097            condition   : cond,
1098            body        : body,
1099            alternative : belse
1100        });
1101    };
1102
1103    function block_() {
1104        expect("{");
1105        var a = [];
1106        while (!is("punc", "}")) {
1107            if (is("eof")) unexpected();
1108            a.push(statement());
1109        }
1110        next();
1111        return a;
1112    };
1113
1114    function switch_body_() {
1115        expect("{");
1116        var a = [], cur = null, branch = null, tmp;
1117        while (!is("punc", "}")) {
1118            if (is("eof")) unexpected();
1119            if (is("keyword", "case")) {
1120                if (branch) branch.end = prev();
1121                cur = [];
1122                branch = new AST_Case({
1123                    start      : (tmp = S.token, next(), tmp),
1124                    expression : expression(true),
1125                    body       : cur
1126                });
1127                a.push(branch);
1128                expect(":");
1129            }
1130            else if (is("keyword", "default")) {
1131                if (branch) branch.end = prev();
1132                cur = [];
1133                branch = new AST_Default({
1134                    start : (tmp = S.token, next(), expect(":"), tmp),
1135                    body  : cur
1136                });
1137                a.push(branch);
1138            }
1139            else {
1140                if (!cur) unexpected();
1141                cur.push(statement());
1142            }
1143        }
1144        if (branch) branch.end = prev();
1145        next();
1146        return a;
1147    };
1148
1149    function try_() {
1150        var body = block_(), bcatch = null, bfinally = null;
1151        if (is("keyword", "catch")) {
1152            var start = S.token;
1153            next();
1154            expect("(");
1155            var name = as_symbol(AST_SymbolCatch);
1156            expect(")");
1157            bcatch = new AST_Catch({
1158                start   : start,
1159                argname : name,
1160                body    : block_(),
1161                end     : prev()
1162            });
1163        }
1164        if (is("keyword", "finally")) {
1165            var start = S.token;
1166            next();
1167            bfinally = new AST_Finally({
1168                start : start,
1169                body  : block_(),
1170                end   : prev()
1171            });
1172        }
1173        if (!bcatch && !bfinally)
1174            croak("Missing catch/finally blocks");
1175        return new AST_Try({
1176            body     : body,
1177            bcatch   : bcatch,
1178            bfinally : bfinally
1179        });
1180    };
1181
1182    function vardefs(no_in, in_const) {
1183        var a = [];
1184        for (;;) {
1185            a.push(new AST_VarDef({
1186                start : S.token,
1187                name  : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
1188                value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
1189                end   : prev()
1190            }));
1191            if (!is("punc", ","))
1192                break;
1193            next();
1194        }
1195        return a;
1196    };
1197
1198    var var_ = function(no_in) {
1199        return new AST_Var({
1200            start       : prev(),
1201            definitions : vardefs(no_in, false),
1202            end         : prev()
1203        });
1204    };
1205
1206    var const_ = function() {
1207        return new AST_Const({
1208            start       : prev(),
1209            definitions : vardefs(false, true),
1210            end         : prev()
1211        });
1212    };
1213
1214    var new_ = function(allow_calls) {
1215        var start = S.token;
1216        expect_token("operator", "new");
1217        var newexp = expr_atom(false), args;
1218        if (is("punc", "(")) {
1219            next();
1220            args = expr_list(")");
1221        } else {
1222            args = [];
1223        }
1224        return subscripts(new AST_New({
1225            start      : start,
1226            expression : newexp,
1227            args       : args,
1228            end        : prev()
1229        }), allow_calls);
1230    };
1231
1232    function as_atom_node() {
1233        var tok = S.token, ret;
1234        switch (tok.type) {
1235          case "name":
1236          case "keyword":
1237            ret = _make_symbol(AST_SymbolRef);
1238            break;
1239          case "num":
1240            ret = new AST_Number({ start: tok, end: tok, value: tok.value });
1241            break;
1242          case "string":
1243            ret = new AST_String({
1244                start : tok,
1245                end   : tok,
1246                value : tok.value,
1247                quote : tok.quote
1248            });
1249            break;
1250          case "regexp":
1251            ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
1252            break;
1253          case "atom":
1254            switch (tok.value) {
1255              case "false":
1256                ret = new AST_False({ start: tok, end: tok });
1257                break;
1258              case "true":
1259                ret = new AST_True({ start: tok, end: tok });
1260                break;
1261              case "null":
1262                ret = new AST_Null({ start: tok, end: tok });
1263                break;
1264            }
1265            break;
1266          case "operator":
1267            if (!is_identifier_string(tok.value)) {
1268                croak("Invalid getter/setter name: " + tok.value,
1269                    tok.line, tok.col, tok.pos);
1270            }
1271            ret = _make_symbol(AST_SymbolRef);
1272            break;
1273        }
1274        next();
1275        return ret;
1276    };
1277
1278    var expr_atom = function(allow_calls) {
1279        if (is("operator", "new")) {
1280            return new_(allow_calls);
1281        }
1282        var start = S.token;
1283        if (is("punc")) {
1284            switch (start.value) {
1285              case "(":
1286                next();
1287                var ex = expression(true);
1288                ex.start = start;
1289                ex.end = S.token;
1290                expect(")");
1291                return subscripts(ex, allow_calls);
1292              case "[":
1293                return subscripts(array_(), allow_calls);
1294              case "{":
1295                return subscripts(object_(), allow_calls);
1296            }
1297            unexpected();
1298        }
1299        if (is("keyword", "function")) {
1300            next();
1301            var func = function_(AST_Function);
1302            func.start = start;
1303            func.end = prev();
1304            return subscripts(func, allow_calls);
1305        }
1306        if (ATOMIC_START_TOKEN[S.token.type]) {
1307            return subscripts(as_atom_node(), allow_calls);
1308        }
1309        unexpected();
1310    };
1311
1312    function expr_list(closing, allow_trailing_comma, allow_empty) {
1313        var first = true, a = [];
1314        while (!is("punc", closing)) {
1315            if (first) first = false; else expect(",");
1316            if (allow_trailing_comma && is("punc", closing)) break;
1317            if (is("punc", ",") && allow_empty) {
1318                a.push(new AST_Hole({ start: S.token, end: S.token }));
1319            } else {
1320                a.push(expression(false));
1321            }
1322        }
1323        next();
1324        return a;
1325    };
1326
1327    var array_ = embed_tokens(function() {
1328        expect("[");
1329        return new AST_Array({
1330            elements: expr_list("]", !options.strict, true)
1331        });
1332    });
1333
1334    var create_accessor = embed_tokens(function() {
1335        return function_(AST_Accessor);
1336    });
1337
1338    var object_ = embed_tokens(function() {
1339        expect("{");
1340        var first = true, a = [];
1341        while (!is("punc", "}")) {
1342            if (first) first = false; else expect(",");
1343            if (!options.strict && is("punc", "}"))
1344                // allow trailing comma
1345                break;
1346            var start = S.token;
1347            var type = start.type;
1348            var name = as_property_name();
1349            if (type == "name" && !is("punc", ":")) {
1350                var key = new AST_SymbolAccessor({
1351                    start: S.token,
1352                    name: as_property_name(),
1353                    end: prev()
1354                });
1355                if (name == "get") {
1356                    a.push(new AST_ObjectGetter({
1357                        start : start,
1358                        key   : key,
1359                        value : create_accessor(),
1360                        end   : prev()
1361                    }));
1362                    continue;
1363                }
1364                if (name == "set") {
1365                    a.push(new AST_ObjectSetter({
1366                        start : start,
1367                        key   : key,
1368                        value : create_accessor(),
1369                        end   : prev()
1370                    }));
1371                    continue;
1372                }
1373            }
1374            expect(":");
1375            a.push(new AST_ObjectKeyVal({
1376                start : start,
1377                quote : start.quote,
1378                key   : name,
1379                value : expression(false),
1380                end   : prev()
1381            }));
1382        }
1383        next();
1384        return new AST_Object({ properties: a });
1385    });
1386
1387    function as_property_name() {
1388        var tmp = S.token;
1389        switch (tmp.type) {
1390          case "operator":
1391            if (!KEYWORDS(tmp.value)) unexpected();
1392          case "num":
1393          case "string":
1394          case "name":
1395          case "keyword":
1396          case "atom":
1397            next();
1398            return tmp.value;
1399          default:
1400            unexpected();
1401        }
1402    };
1403
1404    function as_name() {
1405        var tmp = S.token;
1406        if (tmp.type != "name") unexpected();
1407        next();
1408        return tmp.value;
1409    };
1410
1411    function _make_symbol(type) {
1412        var name = S.token.value;
1413        return new (name == "this" ? AST_This : type)({
1414            name  : String(name),
1415            start : S.token,
1416            end   : S.token
1417        });
1418    };
1419
1420    function as_symbol(type, noerror) {
1421        if (!is("name")) {
1422            if (!noerror) croak("Name expected");
1423            return null;
1424        }
1425        var sym = _make_symbol(type);
1426        next();
1427        return sym;
1428    };
1429
1430    var subscripts = function(expr, allow_calls) {
1431        var start = expr.start;
1432        if (is("punc", ".")) {
1433            next();
1434            return subscripts(new AST_Dot({
1435                start      : start,
1436                expression : expr,
1437                property   : as_name(),
1438                end        : prev()
1439            }), allow_calls);
1440        }
1441        if (is("punc", "[")) {
1442            next();
1443            var prop = expression(true);
1444            expect("]");
1445            return subscripts(new AST_Sub({
1446                start      : start,
1447                expression : expr,
1448                property   : prop,
1449                end        : prev()
1450            }), allow_calls);
1451        }
1452        if (allow_calls && is("punc", "(")) {
1453            next();
1454            return subscripts(new AST_Call({
1455                start      : start,
1456                expression : expr,
1457                args       : expr_list(")"),
1458                end        : prev()
1459            }), true);
1460        }
1461        return expr;
1462    };
1463
1464    var maybe_unary = function(allow_calls) {
1465        var start = S.token;
1466        if (is("operator") && UNARY_PREFIX(start.value)) {
1467            next();
1468            handle_regexp();
1469            var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls));
1470            ex.start = start;
1471            ex.end = prev();
1472            return ex;
1473        }
1474        var val = expr_atom(allow_calls);
1475        while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
1476            val = make_unary(AST_UnaryPostfix, S.token, val);
1477            val.start = start;
1478            val.end = S.token;
1479            next();
1480        }
1481        return val;
1482    };
1483
1484    function make_unary(ctor, token, expr) {
1485        var op = token.value;
1486        if ((op == "++" || op == "--") && !is_assignable(expr))
1487            croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
1488        return new ctor({ operator: op, expression: expr });
1489    };
1490
1491    var expr_op = function(left, min_prec, no_in) {
1492        var op = is("operator") ? S.token.value : null;
1493        if (op == "in" && no_in) op = null;
1494        var prec = op != null ? PRECEDENCE[op] : null;
1495        if (prec != null && prec > min_prec) {
1496            next();
1497            var right = expr_op(maybe_unary(true), prec, no_in);
1498            return expr_op(new AST_Binary({
1499                start    : left.start,
1500                left     : left,
1501                operator : op,
1502                right    : right,
1503                end      : right.end
1504            }), min_prec, no_in);
1505        }
1506        return left;
1507    };
1508
1509    function expr_ops(no_in) {
1510        return expr_op(maybe_unary(true), 0, no_in);
1511    };
1512
1513    var maybe_conditional = function(no_in) {
1514        var start = S.token;
1515        var expr = expr_ops(no_in);
1516        if (is("operator", "?")) {
1517            next();
1518            var yes = expression(false);
1519            expect(":");
1520            return new AST_Conditional({
1521                start       : start,
1522                condition   : expr,
1523                consequent  : yes,
1524                alternative : expression(false, no_in),
1525                end         : prev()
1526            });
1527        }
1528        return expr;
1529    };
1530
1531    function is_assignable(expr) {
1532        if (options.cli) return true;
1533        return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
1534    };
1535
1536    var maybe_assign = function(no_in) {
1537        var start = S.token;
1538        var left = maybe_conditional(no_in), val = S.token.value;
1539        if (is("operator") && ASSIGNMENT(val)) {
1540            if (is_assignable(left)) {
1541                next();
1542                return new AST_Assign({
1543                    start    : start,
1544                    left     : left,
1545                    operator : val,
1546                    right    : maybe_assign(no_in),
1547                    end      : prev()
1548                });
1549            }
1550            croak("Invalid assignment");
1551        }
1552        return left;
1553    };
1554
1555    var expression = function(commas, no_in) {
1556        var start = S.token;
1557        var expr = maybe_assign(no_in);
1558        if (commas && is("punc", ",")) {
1559            next();
1560            return new AST_Seq({
1561                start  : start,
1562                car    : expr,
1563                cdr    : expression(true, no_in),
1564                end    : peek()
1565            });
1566        }
1567        return expr;
1568    };
1569
1570    function in_loop(cont) {
1571        ++S.in_loop;
1572        var ret = cont();
1573        --S.in_loop;
1574        return ret;
1575    };
1576
1577    if (options.expression) {
1578        return expression(true);
1579    }
1580
1581    return (function(){
1582        var start = S.token;
1583        var body = [];
1584        S.input.push_directives_stack();
1585        while (!is("eof"))
1586            body.push(statement());
1587        S.input.pop_directives_stack();
1588        var end = prev();
1589        var toplevel = options.toplevel;
1590        if (toplevel) {
1591            toplevel.body = toplevel.body.concat(body);
1592            toplevel.end = end;
1593        } else {
1594            toplevel = new AST_Toplevel({ start: start, body: body, end: end });
1595        }
1596        return toplevel;
1597    })();
1598
1599};
1600