Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / handlebars / node_modules / uglify-js / lib / parse.js
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
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';
52
53 KEYWORDS = makePredicate(KEYWORDS);
54 RESERVED_WORDS = makePredicate(RESERVED_WORDS);
55 KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
56 KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
57
58 var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
59
60 var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
61 var RE_OCT_NUMBER = /^0[0-7]+$/;
62
63 var 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
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"));
111
112 var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
113
114 var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
115
116 var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
117
118 var REGEXP_MODIFIERS = makePredicate(characters("gmsiy"));
119
120 /* -----[ Tokenizer ]----- */
121
122 // regexps adapted from http://xregexp.com/plugins/#unicode
123 var 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
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)));
135 };
136
137 function is_digit(code) {
138     return code >= 48 && code <= 57;
139 };
140
141 function is_alphanumeric_char(code) {
142     return is_digit(code) || is_letter(code);
143 };
144
145 function is_unicode_digit(code) {
146     return UNICODE.digit.test(String.fromCharCode(code));
147 }
148
149 function is_unicode_combining_mark(ch) {
150     return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
151 };
152
153 function is_unicode_connector_punctuation(ch) {
154     return UNICODE.connector_punctuation.test(ch);
155 };
156
157 function is_identifier(name) {
158     return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
159 };
160
161 function is_identifier_start(code) {
162     return code == 36 || code == 95 || is_letter(code);
163 };
164
165 function 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
177 function is_identifier_string(str){
178     return /^[a-z_$][a-z0-9_$]*$/i.test(str);
179 };
180
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);
186     } else {
187         var val = parseFloat(num);
188         if (val == num) return val;
189     }
190 };
191
192 function 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 };
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);
203
204 function js_error(message, filename, line, col, pos) {
205     throw new JS_Parse_Error(message, filename, line, col, pos);
206 };
207
208 function is_token(token, type, val) {
209     return token.type == type && (val == null || token.value == val);
210 };
211
212 var EX_EOF = {};
213
214 function 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         prev_was_dot = (type == "punc" && value == ".");
289         var ret = {
290             type    : type,
291             value   : value,
292             line    : S.tokline,
293             col     : S.tokcol,
294             pos     : S.tokpos,
295             endline : S.line,
296             endcol  : S.col,
297             endpos  : S.pos,
298             nlb     : S.newline_before,
299             file    : filename
300         };
301         if (/^(?:num|string|regexp)$/i.test(type)) {
302             ret.raw = $TEXT.substring(ret.pos, ret.endpos);
303         }
304         if (!is_comment) {
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;
310             }
311         }
312         S.newline_before = false;
313         return new AST_Token(ret);
314     };
315
316     function skip_whitespace() {
317         while (WHITESPACE_CHARS(peek()))
318             next();
319     };
320
321     function read_while(pred) {
322         var ret = "", ch, i = 0;
323         while ((ch = peek()) && pred(ch, i++))
324             ret += next();
325         return ret;
326     };
327
328     function parse_error(err) {
329         js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
330     };
331
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);
336             switch (code) {
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);
341               case 45: // -
342                 return after_e || (i == 0 && !prefix);
343               case 43: // +
344                 return after_e;
345               case (after_e = false, 46): // .
346                 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
347             }
348             return is_alphanumeric_char(code);
349         });
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");
353         }
354         var valid = parse_js_number(num);
355         if (!isNaN(valid)) {
356             return token("num", valid);
357         } else {
358             parse_error("Invalid syntax: " + num);
359         }
360     };
361
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
374           case 13  :            // \r
375             if (peek() == "\n") { // DOS newline
376                 next(true, in_string);
377                 return "";
378             }
379         }
380         if (ch >= "0" && ch <= "7")
381             return read_octal_escape_sequence(ch);
382         return ch;
383     };
384
385     function read_octal_escape_sequence(ch) {
386         // Read
387         var p = peek();
388         if (p >= "0" && p <= "7") {
389             ch += next(true);
390             if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
391                 ch += next(true);
392         }
393
394         // Parse
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));
399     }
400
401     function hex_bytes(n) {
402         var num = 0;
403         for (; n > 0; --n) {
404             var digit = parseInt(next(true), 16);
405             if (isNaN(digit))
406                 parse_error("Invalid hex-character pattern in string");
407             num = (num << 4) | digit;
408         }
409         return num;
410     };
411
412     var read_string = with_eof_error("Unterminated string constant", function(quote_char){
413         var quote = next(), ret = "";
414         for (;;) {
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;
419             ret += ch;
420         }
421         var tok = token("string", ret);
422         tok.quote = quote_char;
423         return tok;
424     });
425
426     function skip_line_comment(type) {
427         var regex_allowed = S.regex_allowed;
428         var i = find_eol(), ret;
429         if (i == -1) {
430             ret = S.text.substr(S.pos);
431             S.pos = S.text.length;
432         } else {
433             ret = S.text.substring(S.pos, i);
434             S.pos = i;
435         }
436         S.col = S.tokcol + (S.pos - S.tokpos);
437         S.comments_before.push(token(type, ret, true));
438         S.regex_allowed = regex_allowed;
439         return next_token;
440     };
441
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;
450         return next_token;
451     });
452
453     function read_name() {
454         var backslash = false, name = "", ch, escaped = false, hex;
455         while ((ch = peek()) != null) {
456             if (!backslash) {
457                 if (ch == "\\") escaped = backslash = true, next();
458                 else if (is_identifier_char(ch)) name += next();
459                 else break;
460             }
461             else {
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");
465                 name += ch;
466                 backslash = false;
467             }
468         }
469         if (KEYWORDS(name) && escaped) {
470             hex = name.charCodeAt(0).toString(16).toUpperCase();
471             name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
472         }
473         return name;
474     };
475
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) {
481             regexp += "\\" + ch;
482             prev_backslash = false;
483         } else if (ch == "[") {
484             in_class = true;
485             regexp += ch;
486         } else if (ch == "]" && in_class) {
487             in_class = false;
488             regexp += ch;
489         } else if (ch == "/" && !in_class) {
490             break;
491         } else if (ch == "\\") {
492             prev_backslash = true;
493         } else {
494             regexp += ch;
495         }
496         var mods = read_name();
497         try {
498           return token("regexp", new RegExp(regexp, mods));
499         } catch(e) {
500           parse_error(e.message);
501         }
502     });
503
504     function read_operator(prefix) {
505         function grow(op) {
506             if (!peek()) return op;
507             var bigger = op + peek();
508             if (OPERATORS(bigger)) {
509                 next();
510                 return grow(bigger);
511             } else {
512                 return op;
513             }
514         };
515         return token("operator", grow(prefix || next()));
516     };
517
518     function handle_slash() {
519         next();
520         switch (peek()) {
521           case "/":
522             next();
523             return skip_line_comment("comment1");
524           case "*":
525             next();
526             return skip_multiline_comment();
527         }
528         return S.regex_allowed ? read_regexp("") : read_operator("/");
529     };
530
531     function handle_dot() {
532         next();
533         return is_digit(peek().charCodeAt(0))
534             ? read_num(".")
535             : token("punc", ".");
536     };
537
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);
545     };
546
547     function with_eof_error(eof_error, cont) {
548         return function(x) {
549             try {
550                 return cont(x);
551             } catch(ex) {
552                 if (ex === EX_EOF) parse_error(eof_error);
553                 else throw ex;
554             }
555         };
556     };
557
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("#!")) {
562             start_token();
563             forward(2);
564             skip_line_comment("comment5");
565         }
566         for (;;) {
567             skip_whitespace();
568             start_token();
569             if (html5_comments) {
570                 if (looking_at("<!--")) {
571                     forward(4);
572                     skip_line_comment("comment3");
573                     continue;
574                 }
575                 if (looking_at("-->") && S.newline_before) {
576                     forward(3);
577                     skip_line_comment("comment4");
578                     continue;
579                 }
580             }
581             var ch = peek();
582             if (!ch) return token("eof");
583             var code = ch.charCodeAt(0);
584             switch (code) {
585               case 34: case 39: return read_string(ch);
586               case 46: return handle_dot();
587               case 47: {
588                   var tok = handle_slash();
589                   if (tok === next_token) continue;
590                   return tok;
591               }
592             }
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();
597             break;
598         }
599         parse_error("Unexpected character '" + ch + "'");
600     };
601
602     next_token.context = function(nc) {
603         if (nc) S = nc;
604         return S;
605     };
606
607     next_token.add_directive = function(directive) {
608         S.directive_stack[S.directive_stack.length - 1].push(directive);
609
610         if (S.directives[directive] === undefined) {
611             S.directives[directive] = 1;
612         } else {
613             S.directives[directive]++;
614         }
615     }
616
617     next_token.push_directives_stack = function() {
618         S.directive_stack.push([]);
619     }
620
621     next_token.pop_directives_stack = function() {
622         var directives = S.directive_stack[S.directive_stack.length - 1];
623
624         for (var i = 0; i < directives.length; i++) {
625             S.directives[directives[i]]--;
626         }
627
628         S.directive_stack.pop();
629     }
630
631     next_token.has_directive = function(directive) {
632         return S.directives[directive] !== undefined &&
633             S.directives[directive] > 0;
634     }
635
636     return next_token;
637
638 };
639
640 /* -----[ Parser (constants) ]----- */
641
642 var UNARY_PREFIX = makePredicate([
643     "typeof",
644     "void",
645     "delete",
646     "--",
647     "++",
648     "!",
649     "~",
650     "-",
651     "+"
652 ]);
653
654 var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
655
656 var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
657
658 var PRECEDENCE = (function(a, ret){
659     for (var i = 0; i < a.length; ++i) {
660         var b = a[i];
661         for (var j = 0; j < b.length; ++j) {
662             ret[b[j]] = i + 1;
663         }
664     }
665     return ret;
666 })(
667     [
668         ["||"],
669         ["&&"],
670         ["|"],
671         ["^"],
672         ["&"],
673         ["==", "===", "!=", "!=="],
674         ["<", ">", "<=", ">=", "in", "instanceof"],
675         [">>", "<<", ">>>"],
676         ["+", "-"],
677         ["*", "/", "%"]
678     ],
679     {}
680 );
681
682 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
683
684 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
685
686 /* -----[ Parser ]----- */
687
688 function parse($TEXT, options) {
689
690     options = defaults(options, {
691         strict         : false,
692         filename       : null,
693         toplevel       : null,
694         expression     : false,
695         html5_comments : true,
696         bare_returns   : false,
697         shebang        : true,
698     });
699
700     var S = {
701         input         : (typeof $TEXT == "string"
702                          ? tokenizer($TEXT, options.filename,
703                                      options.html5_comments, options.shebang)
704                          : $TEXT),
705         token         : null,
706         prev          : null,
707         peeked        : null,
708         in_function   : 0,
709         in_directives : true,
710         in_loop       : 0,
711         labels        : []
712     };
713
714     S.token = next();
715
716     function is(type, value) {
717         return is_token(S.token, type, value);
718     };
719
720     function peek() { return S.peeked || (S.peeked = S.input()); };
721
722     function next() {
723         S.prev = S.token;
724         if (S.peeked) {
725             S.token = S.peeked;
726             S.peeked = null;
727         } else {
728             S.token = S.input();
729         }
730         S.in_directives = S.in_directives && (
731             S.token.type == "string" || is("punc", ";")
732         );
733         return S.token;
734     };
735
736     function prev() {
737         return S.prev;
738     };
739
740     function croak(msg, line, col, pos) {
741         var ctx = S.input.context();
742         js_error(msg,
743                  ctx.filename,
744                  line != null ? line : ctx.tokline,
745                  col != null ? col : ctx.tokcol,
746                  pos != null ? pos : ctx.tokpos);
747     };
748
749     function token_error(token, msg) {
750         croak(msg, token.line, token.col);
751     };
752
753     function unexpected(token) {
754         if (token == null)
755             token = S.token;
756         token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
757     };
758
759     function expect_token(type, val) {
760         if (is(type, val)) {
761             return next();
762         }
763         token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
764     };
765
766     function expect(punc) { return expect_token("punc", punc); };
767
768     function can_insert_semicolon() {
769         return !options.strict && (
770             S.token.nlb || is("eof") || is("punc", "}")
771         );
772     };
773
774     function semicolon(optional) {
775         if (is("punc", ";")) next();
776         else if (!optional && !can_insert_semicolon()) unexpected();
777     };
778
779     function parenthesised() {
780         expect("(");
781         var exp = expression(true);
782         expect(")");
783         return exp;
784     };
785
786     function embed_tokens(parser) {
787         return function() {
788             var start = S.token;
789             var expr = parser();
790             var end = prev();
791             expr.start = start;
792             expr.end = end;
793             return expr;
794         };
795     };
796
797     function handle_regexp() {
798         if (is("operator", "/") || is("operator", "/=")) {
799             S.peeked = null;
800             S.token = S.input(S.token.value.substr(1)); // force regexp
801         }
802     };
803
804     var statement = embed_tokens(function() {
805         var tmp;
806         handle_regexp();
807         switch (S.token.type) {
808           case "string":
809             var dir = false;
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);
813                 } else {
814                     S.in_directives = false;
815                 }
816             }
817             var dir = S.in_directives, stat = simple_statement();
818             if (dir) {
819                 return new AST_Directive({
820                     start : stat.body.start,
821                     end   : stat.body.end,
822                     quote : stat.body.quote,
823                     value : stat.body.value,
824                 });
825             }
826             return 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 (tmp = S.token.value, next(), tmp) {
859               case "break":
860                 return break_cont(AST_Break);
861
862               case "continue":
863                 return break_cont(AST_Continue);
864
865               case "debugger":
866                 semicolon();
867                 return new AST_Debugger();
868
869               case "do":
870                 return new AST_Do({
871                     body      : in_loop(statement),
872                     condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
873                 });
874
875               case "while":
876                 return new AST_While({
877                     condition : parenthesised(),
878                     body      : in_loop(statement)
879                 });
880
881               case "for":
882                 return for_();
883
884               case "function":
885                 return function_(AST_Defun);
886
887               case "if":
888                 return if_();
889
890               case "return":
891                 if (S.in_function == 0 && !options.bare_returns)
892                     croak("'return' outside of function");
893                 return new AST_Return({
894                     value: ( is("punc", ";")
895                              ? (next(), null)
896                              : can_insert_semicolon()
897                              ? null
898                              : (tmp = expression(true), semicolon(), tmp) )
899                 });
900
901               case "switch":
902                 return new AST_Switch({
903                     expression : parenthesised(),
904                     body       : in_loop(switch_body_)
905                 });
906
907               case "throw":
908                 if (S.token.nlb)
909                     croak("Illegal newline after 'throw'");
910                 return new AST_Throw({
911                     value: (tmp = expression(true), semicolon(), tmp)
912                 });
913
914               case "try":
915                 return try_();
916
917               case "var":
918                 return tmp = var_(), semicolon(), tmp;
919
920               case "const":
921                 return tmp = const_(), semicolon(), tmp;
922
923               case "with":
924                 if (S.input.has_directive("use strict")) {
925                     croak("Strict mode may not include a with statement");
926                 }
927                 return new AST_With({
928                     expression : parenthesised(),
929                     body       : statement()
930                 });
931
932               default:
933                 unexpected();
934             }
935         }
936     });
937
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");
946         }
947         expect(":");
948         S.labels.push(label);
949         var stat = statement();
950         S.labels.pop();
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);
960                 }
961             });
962         }
963         return new AST_LabeledStatement({ body: stat, label: label });
964     };
965
966     function simple_statement(tmp) {
967         return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
968     };
969
970     function break_cont(type) {
971         var label = null, ldef;
972         if (!can_insert_semicolon()) {
973             label = as_symbol(AST_LabelRef, true);
974         }
975         if (label != null) {
976             ldef = find_if(function(l){ return l.name == label.name }, S.labels);
977             if (!ldef)
978                 croak("Undefined label " + label.name);
979             label.thedef = ldef;
980         }
981         else if (S.in_loop == 0)
982             croak(type.TYPE + " not inside a loop or switch");
983         semicolon();
984         var stat = new type({ label: label });
985         if (ldef) ldef.references.push(stat);
986         return stat;
987     };
988
989     function for_() {
990         expect("(");
991         var init = null;
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");
999                 next();
1000                 return for_in(init);
1001             }
1002         }
1003         return regular_for(init);
1004     };
1005
1006     function regular_for(init) {
1007         expect(";");
1008         var test = is("punc", ";") ? null : expression(true);
1009         expect(";");
1010         var step = is("punc", ")") ? null : expression(true);
1011         expect(")");
1012         return new AST_For({
1013             init      : init,
1014             condition : test,
1015             step      : step,
1016             body      : in_loop(statement)
1017         });
1018     };
1019
1020     function for_in(init) {
1021         var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
1022         var obj = expression(true);
1023         expect(")");
1024         return new AST_ForIn({
1025             init   : init,
1026             name   : lhs,
1027             object : obj,
1028             body   : in_loop(statement)
1029         });
1030     };
1031
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)
1036             unexpected();
1037         expect("(");
1038         return new ctor({
1039             name: name,
1040             argnames: (function(first, a){
1041                 while (!is("punc", ")")) {
1042                     if (first) first = false; else expect(",");
1043                     a.push(as_symbol(AST_SymbolFunarg));
1044                 }
1045                 next();
1046                 return a;
1047             })(true, []),
1048             body: (function(loop, labels){
1049                 ++S.in_function;
1050                 S.in_directives = true;
1051                 S.input.push_directives_stack();
1052                 S.in_loop = 0;
1053                 S.labels = [];
1054                 var a = block_();
1055                 S.input.pop_directives_stack();
1056                 --S.in_function;
1057                 S.in_loop = loop;
1058                 S.labels = labels;
1059                 return a;
1060             })(S.in_loop, S.labels)
1061         });
1062     };
1063
1064     function if_() {
1065         var cond = parenthesised(), body = statement(), belse = null;
1066         if (is("keyword", "else")) {
1067             next();
1068             belse = statement();
1069         }
1070         return new AST_If({
1071             condition   : cond,
1072             body        : body,
1073             alternative : belse
1074         });
1075     };
1076
1077     function block_() {
1078         expect("{");
1079         var a = [];
1080         while (!is("punc", "}")) {
1081             if (is("eof")) unexpected();
1082             a.push(statement());
1083         }
1084         next();
1085         return a;
1086     };
1087
1088     function switch_body_() {
1089         expect("{");
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();
1095                 cur = [];
1096                 branch = new AST_Case({
1097                     start      : (tmp = S.token, next(), tmp),
1098                     expression : expression(true),
1099                     body       : cur
1100                 });
1101                 a.push(branch);
1102                 expect(":");
1103             }
1104             else if (is("keyword", "default")) {
1105                 if (branch) branch.end = prev();
1106                 cur = [];
1107                 branch = new AST_Default({
1108                     start : (tmp = S.token, next(), expect(":"), tmp),
1109                     body  : cur
1110                 });
1111                 a.push(branch);
1112             }
1113             else {
1114                 if (!cur) unexpected();
1115                 cur.push(statement());
1116             }
1117         }
1118         if (branch) branch.end = prev();
1119         next();
1120         return a;
1121     };
1122
1123     function try_() {
1124         var body = block_(), bcatch = null, bfinally = null;
1125         if (is("keyword", "catch")) {
1126             var start = S.token;
1127             next();
1128             expect("(");
1129             var name = as_symbol(AST_SymbolCatch);
1130             expect(")");
1131             bcatch = new AST_Catch({
1132                 start   : start,
1133                 argname : name,
1134                 body    : block_(),
1135                 end     : prev()
1136             });
1137         }
1138         if (is("keyword", "finally")) {
1139             var start = S.token;
1140             next();
1141             bfinally = new AST_Finally({
1142                 start : start,
1143                 body  : block_(),
1144                 end   : prev()
1145             });
1146         }
1147         if (!bcatch && !bfinally)
1148             croak("Missing catch/finally blocks");
1149         return new AST_Try({
1150             body     : body,
1151             bcatch   : bcatch,
1152             bfinally : bfinally
1153         });
1154     };
1155
1156     function vardefs(no_in, in_const) {
1157         var a = [];
1158         for (;;) {
1159             a.push(new AST_VarDef({
1160                 start : S.token,
1161                 name  : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
1162                 value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
1163                 end   : prev()
1164             }));
1165             if (!is("punc", ","))
1166                 break;
1167             next();
1168         }
1169         return a;
1170     };
1171
1172     var var_ = function(no_in) {
1173         return new AST_Var({
1174             start       : prev(),
1175             definitions : vardefs(no_in, false),
1176             end         : prev()
1177         });
1178     };
1179
1180     var const_ = function() {
1181         return new AST_Const({
1182             start       : prev(),
1183             definitions : vardefs(false, true),
1184             end         : prev()
1185         });
1186     };
1187
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", "(")) {
1193             next();
1194             args = expr_list(")");
1195         } else {
1196             args = [];
1197         }
1198         return subscripts(new AST_New({
1199             start      : start,
1200             expression : newexp,
1201             args       : args,
1202             end        : prev()
1203         }), allow_calls);
1204     };
1205
1206     function as_atom_node() {
1207         var tok = S.token, ret;
1208         switch (tok.type) {
1209           case "name":
1210           case "keyword":
1211             ret = _make_symbol(AST_SymbolRef);
1212             break;
1213           case "num":
1214             ret = new AST_Number({ start: tok, end: tok, value: tok.value });
1215             break;
1216           case "string":
1217             ret = new AST_String({
1218                 start : tok,
1219                 end   : tok,
1220                 value : tok.value,
1221                 quote : tok.quote
1222             });
1223             break;
1224           case "regexp":
1225             ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
1226             break;
1227           case "atom":
1228             switch (tok.value) {
1229               case "false":
1230                 ret = new AST_False({ start: tok, end: tok });
1231                 break;
1232               case "true":
1233                 ret = new AST_True({ start: tok, end: tok });
1234                 break;
1235               case "null":
1236                 ret = new AST_Null({ start: tok, end: tok });
1237                 break;
1238             }
1239             break;
1240           case "operator":
1241             if (!is_identifier_string(tok.value)) {
1242                 croak("Invalid getter/setter name: " + tok.value,
1243                     tok.line, tok.col, tok.pos);
1244             }
1245             ret = _make_symbol(AST_SymbolRef);
1246             break;
1247         }
1248         next();
1249         return ret;
1250     };
1251
1252     var expr_atom = function(allow_calls) {
1253         if (is("operator", "new")) {
1254             return new_(allow_calls);
1255         }
1256         var start = S.token;
1257         if (is("punc")) {
1258             switch (start.value) {
1259               case "(":
1260                 next();
1261                 var ex = expression(true);
1262                 ex.start = start;
1263                 ex.end = S.token;
1264                 expect(")");
1265                 return subscripts(ex, allow_calls);
1266               case "[":
1267                 return subscripts(array_(), allow_calls);
1268               case "{":
1269                 return subscripts(object_(), allow_calls);
1270             }
1271             unexpected();
1272         }
1273         if (is("keyword", "function")) {
1274             next();
1275             var func = function_(AST_Function);
1276             func.start = start;
1277             func.end = prev();
1278             return subscripts(func, allow_calls);
1279         }
1280         if (ATOMIC_START_TOKEN[S.token.type]) {
1281             return subscripts(as_atom_node(), allow_calls);
1282         }
1283         unexpected();
1284     };
1285
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 }));
1293             } else {
1294                 a.push(expression(false));
1295             }
1296         }
1297         next();
1298         return a;
1299     };
1300
1301     var array_ = embed_tokens(function() {
1302         expect("[");
1303         return new AST_Array({
1304             elements: expr_list("]", !options.strict, true)
1305         });
1306     });
1307
1308     var create_accessor = embed_tokens(function() {
1309         return function_(AST_Accessor);
1310     });
1311
1312     var object_ = embed_tokens(function() {
1313         expect("{");
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
1319                 break;
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({
1326                         start : start,
1327                         key   : as_atom_node(),
1328                         value : create_accessor(),
1329                         end   : prev()
1330                     }));
1331                     continue;
1332                 }
1333                 if (name == "set") {
1334                     a.push(new AST_ObjectSetter({
1335                         start : start,
1336                         key   : as_atom_node(),
1337                         value : create_accessor(),
1338                         end   : prev()
1339                     }));
1340                     continue;
1341                 }
1342             }
1343             expect(":");
1344             a.push(new AST_ObjectKeyVal({
1345                 start : start,
1346                 quote : start.quote,
1347                 key   : name,
1348                 value : expression(false),
1349                 end   : prev()
1350             }));
1351         }
1352         next();
1353         return new AST_Object({ properties: a });
1354     });
1355
1356     function as_property_name() {
1357         var tmp = S.token;
1358         next();
1359         switch (tmp.type) {
1360           case "num":
1361           case "string":
1362           case "name":
1363           case "operator":
1364           case "keyword":
1365           case "atom":
1366             return tmp.value;
1367           default:
1368             unexpected();
1369         }
1370     };
1371
1372     function as_name() {
1373         var tmp = S.token;
1374         next();
1375         switch (tmp.type) {
1376           case "name":
1377           case "operator":
1378           case "keyword":
1379           case "atom":
1380             return tmp.value;
1381           default:
1382             unexpected();
1383         }
1384     };
1385
1386     function _make_symbol(type) {
1387         var name = S.token.value;
1388         return new (name == "this" ? AST_This : type)({
1389             name  : String(name),
1390             start : S.token,
1391             end   : S.token
1392         });
1393     };
1394
1395     function as_symbol(type, noerror) {
1396         if (!is("name")) {
1397             if (!noerror) croak("Name expected");
1398             return null;
1399         }
1400         var sym = _make_symbol(type);
1401         next();
1402         return sym;
1403     };
1404
1405     var subscripts = function(expr, allow_calls) {
1406         var start = expr.start;
1407         if (is("punc", ".")) {
1408             next();
1409             return subscripts(new AST_Dot({
1410                 start      : start,
1411                 expression : expr,
1412                 property   : as_name(),
1413                 end        : prev()
1414             }), allow_calls);
1415         }
1416         if (is("punc", "[")) {
1417             next();
1418             var prop = expression(true);
1419             expect("]");
1420             return subscripts(new AST_Sub({
1421                 start      : start,
1422                 expression : expr,
1423                 property   : prop,
1424                 end        : prev()
1425             }), allow_calls);
1426         }
1427         if (allow_calls && is("punc", "(")) {
1428             next();
1429             return subscripts(new AST_Call({
1430                 start      : start,
1431                 expression : expr,
1432                 args       : expr_list(")"),
1433                 end        : prev()
1434             }), true);
1435         }
1436         return expr;
1437     };
1438
1439     var maybe_unary = function(allow_calls) {
1440         var start = S.token;
1441         if (is("operator") && UNARY_PREFIX(start.value)) {
1442             next();
1443             handle_regexp();
1444             var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
1445             ex.start = start;
1446             ex.end = prev();
1447             return ex;
1448         }
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);
1452             val.start = start;
1453             val.end = S.token;
1454             next();
1455         }
1456         return val;
1457     };
1458
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 });
1463     };
1464
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) {
1470             next();
1471             var right = expr_op(maybe_unary(true), prec, no_in);
1472             return expr_op(new AST_Binary({
1473                 start    : left.start,
1474                 left     : left,
1475                 operator : op,
1476                 right    : right,
1477                 end      : right.end
1478             }), min_prec, no_in);
1479         }
1480         return left;
1481     };
1482
1483     function expr_ops(no_in) {
1484         return expr_op(maybe_unary(true), 0, no_in);
1485     };
1486
1487     var maybe_conditional = function(no_in) {
1488         var start = S.token;
1489         var expr = expr_ops(no_in);
1490         if (is("operator", "?")) {
1491             next();
1492             var yes = expression(false);
1493             expect(":");
1494             return new AST_Conditional({
1495                 start       : start,
1496                 condition   : expr,
1497                 consequent  : yes,
1498                 alternative : expression(false, no_in),
1499                 end         : prev()
1500             });
1501         }
1502         return expr;
1503     };
1504
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);
1509     };
1510
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)) {
1516                 next();
1517                 return new AST_Assign({
1518                     start    : start,
1519                     left     : left,
1520                     operator : val,
1521                     right    : maybe_assign(no_in),
1522                     end      : prev()
1523                 });
1524             }
1525             croak("Invalid assignment");
1526         }
1527         return left;
1528     };
1529
1530     var expression = function(commas, no_in) {
1531         var start = S.token;
1532         var expr = maybe_assign(no_in);
1533         if (commas && is("punc", ",")) {
1534             next();
1535             return new AST_Seq({
1536                 start  : start,
1537                 car    : expr,
1538                 cdr    : expression(true, no_in),
1539                 end    : peek()
1540             });
1541         }
1542         return expr;
1543     };
1544
1545     function in_loop(cont) {
1546         ++S.in_loop;
1547         var ret = cont();
1548         --S.in_loop;
1549         return ret;
1550     };
1551
1552     if (options.expression) {
1553         return expression(true);
1554     }
1555
1556     return (function(){
1557         var start = S.token;
1558         var body = [];
1559         S.input.push_directives_stack();
1560         while (!is("eof"))
1561             body.push(statement());
1562         S.input.pop_directives_stack();
1563         var end = prev();
1564         var toplevel = options.toplevel;
1565         if (toplevel) {
1566             toplevel.body = toplevel.body.concat(body);
1567             toplevel.end = end;
1568         } else {
1569             toplevel = new AST_Toplevel({ start: start, body: body, end: end });
1570         }
1571         return toplevel;
1572     })();
1573
1574 };