Fix: merge conflict
[myslice.git] / third-party / codemirror-3.15 / test / lint / parse-js.js
1 /***********************************************************************
2
3   A JavaScript tokenizer / parser / beautifier / compressor.
4
5   This version is suitable for Node.js.  With minimal changes (the
6   exports stuff) it should work on any JS platform.
7
8   This file contains the tokenizer/parser.  It is a port to JavaScript
9   of parse-js [1], a JavaScript parser library written in Common Lisp
10   by Marijn Haverbeke.  Thank you Marijn!
11
12   [1] http://marijn.haverbeke.nl/parse-js/
13
14   Exported functions:
15
16     - tokenizer(code) -- returns a function.  Call the returned
17       function to fetch the next token.
18
19     - parse(code) -- returns an AST of the given JavaScript code.
20
21   -------------------------------- (C) ---------------------------------
22
23                            Author: Mihai Bazon
24                          <mihai.bazon@gmail.com>
25                        http://mihai.bazon.net/blog
26
27   Distributed under the BSD license:
28
29     Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
30     Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
31
32     Redistribution and use in source and binary forms, with or without
33     modification, are permitted provided that the following conditions
34     are met:
35
36         * Redistributions of source code must retain the above
37           copyright notice, this list of conditions and the following
38           disclaimer.
39
40         * Redistributions in binary form must reproduce the above
41           copyright notice, this list of conditions and the following
42           disclaimer in the documentation and/or other materials
43           provided with the distribution.
44
45     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
46     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
49     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
50     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
54     TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
55     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56     SUCH DAMAGE.
57
58  ***********************************************************************/
59
60 /* -----[ Tokenizer (constants) ]----- */
61
62 var KEYWORDS = array_to_hash([
63         "break",
64         "case",
65         "catch",
66         "const",
67         "continue",
68         "debugger",
69         "default",
70         "delete",
71         "do",
72         "else",
73         "finally",
74         "for",
75         "function",
76         "if",
77         "in",
78         "instanceof",
79         "new",
80         "return",
81         "switch",
82         "throw",
83         "try",
84         "typeof",
85         "var",
86         "void",
87         "while",
88         "with"
89 ]);
90
91 var RESERVED_WORDS = array_to_hash([
92         "abstract",
93         "boolean",
94         "byte",
95         "char",
96         "class",
97         "double",
98         "enum",
99         "export",
100         "extends",
101         "final",
102         "float",
103         "goto",
104         "implements",
105         "import",
106         "int",
107         "interface",
108         "long",
109         "native",
110         "package",
111         "private",
112         "protected",
113         "public",
114         "short",
115         "static",
116         "super",
117         "synchronized",
118         "throws",
119         "transient",
120         "volatile"
121 ]);
122
123 var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
124         "return",
125         "new",
126         "delete",
127         "throw",
128         "else",
129         "case"
130 ]);
131
132 var KEYWORDS_ATOM = array_to_hash([
133         "false",
134         "null",
135         "true",
136         "undefined"
137 ]);
138
139 var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
140
141 var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
142 var RE_OCT_NUMBER = /^0[0-7]+$/;
143 var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
144
145 var OPERATORS = array_to_hash([
146         "in",
147         "instanceof",
148         "typeof",
149         "new",
150         "void",
151         "delete",
152         "++",
153         "--",
154         "+",
155         "-",
156         "!",
157         "~",
158         "&",
159         "|",
160         "^",
161         "*",
162         "/",
163         "%",
164         ">>",
165         "<<",
166         ">>>",
167         "<",
168         ">",
169         "<=",
170         ">=",
171         "==",
172         "===",
173         "!=",
174         "!==",
175         "?",
176         "=",
177         "+=",
178         "-=",
179         "/=",
180         "*=",
181         "%=",
182         ">>=",
183         "<<=",
184         ">>>=",
185         "|=",
186         "^=",
187         "&=",
188         "&&",
189         "||"
190 ]);
191
192 var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
193
194 var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
195
196 var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
197
198 var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
199
200 /* -----[ Tokenizer ]----- */
201
202 var UNICODE = {  // Unicode 6.1
203         letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0527\\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\\u08A2-\\u08AC\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0977\\u0979-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\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-\\u16F0\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191C\\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-\\uA697\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA793\\uA7A0-\\uA7AA\\uA7F8-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA80-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\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]"),
204         combining_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065F\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0859-\\u085B\\u08E4-\\u08FE\\u0900-\\u0903\\u093A-\\u093C\\u093E-\\u094F\\u0951-\\u0957\\u0962\\u0963\\u0981-\\u0983\\u09BC\\u09BE-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CD\\u09D7\\u09E2\\u09E3\\u0A01-\\u0A03\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81-\\u0A83\\u0ABC\\u0ABE-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AE2\\u0AE3\\u0B01-\\u0B03\\u0B3C\\u0B3E-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B62\\u0B63\\u0B82\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD7\\u0C01-\\u0C03\\u0C3E-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0C82\\u0C83\\u0CBC\\u0CBE-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CE2\\u0CE3\\u0D02\\u0D03\\u0D3E-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4D\\u0D57\\u0D62\\u0D63\\u0D82\\u0D83\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F3E\\u0F3F\\u0F71-\\u0F84\\u0F86\\u0F87\\u0F8D-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102B-\\u103E\\u1056-\\u1059\\u105E-\\u1060\\u1062-\\u1064\\u1067-\\u106D\\u1071-\\u1074\\u1082-\\u108D\\u108F\\u109A-\\u109D\\u135D-\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B4-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u192B\\u1930-\\u193B\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A17-\\u1A1B\\u1A55-\\u1A5E\\u1A60-\\u1A7C\\u1A7F\\u1B00-\\u1B04\\u1B34-\\u1B44\\u1B6B-\\u1B73\\u1B80-\\u1B82\\u1BA1-\\u1BAD\\u1BE6-\\u1BF3\\u1C24-\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE8\\u1CED\\u1CF2-\\u1CF4\\u1DC0-\\u1DE6\\u1DFC-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2D7F\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA674-\\uA67D\\uA69F\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA823-\\uA827\\uA880\\uA881\\uA8B4-\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA953\\uA980-\\uA983\\uA9B3-\\uA9C0\\uAA29-\\uAA36\\uAA43\\uAA4C\\uAA4D\\uAA7B\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uAAEB-\\uAAEF\\uAAF5\\uAAF6\\uABE3-\\uABEA\\uABEC\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
205         connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]"),
206         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\\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\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]")
207 };
208
209 function is_letter(ch) {
210         return UNICODE.letter.test(ch);
211 };
212
213 function is_digit(ch) {
214         ch = ch.charCodeAt(0);
215         return ch >= 48 && ch <= 57;
216 };
217
218 function is_unicode_digit(ch) {
219         return UNICODE.digit.test(ch);
220 }
221
222 function is_alphanumeric_char(ch) {
223         return is_digit(ch) || is_letter(ch);
224 };
225
226 function is_unicode_combining_mark(ch) {
227         return UNICODE.combining_mark.test(ch);
228 };
229
230 function is_unicode_connector_punctuation(ch) {
231         return UNICODE.connector_punctuation.test(ch);
232 };
233
234 function is_identifier_start(ch) {
235         return ch == "$" || ch == "_" || is_letter(ch);
236 };
237
238 function is_identifier_char(ch) {
239         return is_identifier_start(ch)
240                 || is_unicode_combining_mark(ch)
241                 || is_unicode_digit(ch)
242                 || is_unicode_connector_punctuation(ch)
243                 || ch == "\u200c" // zero-width non-joiner <ZWNJ>
244                 || ch == "\u200d" // zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
245         ;
246 };
247
248 function parse_js_number(num) {
249         if (RE_HEX_NUMBER.test(num)) {
250                 return parseInt(num.substr(2), 16);
251         } else if (RE_OCT_NUMBER.test(num)) {
252                 return parseInt(num.substr(1), 8);
253         } else if (RE_DEC_NUMBER.test(num)) {
254                 return parseFloat(num);
255         }
256 };
257
258 function JS_Parse_Error(message, line, col, pos) {
259         this.message = message;
260         this.line = line + 1;
261         this.col = col + 1;
262         this.pos = pos + 1;
263         this.stack = new Error().stack;
264 };
265
266 JS_Parse_Error.prototype.toString = function() {
267         return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
268 };
269
270 function js_error(message, line, col, pos) {
271         throw new JS_Parse_Error(message, line, col, pos);
272 };
273
274 function is_token(token, type, val) {
275         return token.type == type && (val == null || token.value == val);
276 };
277
278 var EX_EOF = {};
279
280 function tokenizer($TEXT) {
281
282         var S = {
283                 text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
284                 pos             : 0,
285                 tokpos          : 0,
286                 line            : 0,
287                 tokline         : 0,
288                 col             : 0,
289                 tokcol          : 0,
290                 newline_before  : false,
291                 regex_allowed   : false,
292                 comments_before : []
293         };
294
295         function peek() { return S.text.charAt(S.pos); };
296
297         function next(signal_eof, in_string) {
298                 var ch = S.text.charAt(S.pos++);
299                 if (signal_eof && !ch)
300                         throw EX_EOF;
301                 if (ch == "\n") {
302                         S.newline_before = S.newline_before || !in_string;
303                         ++S.line;
304                         S.col = 0;
305                 } else {
306                         ++S.col;
307                 }
308                 return ch;
309         };
310
311         function eof() {
312                 return !S.peek();
313         };
314
315         function find(what, signal_eof) {
316                 var pos = S.text.indexOf(what, S.pos);
317                 if (signal_eof && pos == -1) throw EX_EOF;
318                 return pos;
319         };
320
321         function start_token() {
322                 S.tokline = S.line;
323                 S.tokcol = S.col;
324                 S.tokpos = S.pos;
325         };
326
327         function token(type, value, is_comment) {
328                 S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
329                                    (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
330                                    (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
331                 var ret = {
332                         type   : type,
333                         value  : value,
334                         line   : S.tokline,
335                         col    : S.tokcol,
336                         pos    : S.tokpos,
337                         endpos : S.pos,
338                         nlb    : S.newline_before
339                 };
340                 if (!is_comment) {
341                         ret.comments_before = S.comments_before;
342                         S.comments_before = [];
343                         // make note of any newlines in the comments that came before
344                         for (var i = 0, len = ret.comments_before.length; i < len; i++) {
345                                 ret.nlb = ret.nlb || ret.comments_before[i].nlb;
346                         }
347                 }
348                 S.newline_before = false;
349                 return ret;
350         };
351
352         function skip_whitespace() {
353                 while (HOP(WHITESPACE_CHARS, peek()))
354                         next();
355         };
356
357         function read_while(pred) {
358                 var ret = "", ch = peek(), i = 0;
359                 while (ch && pred(ch, i++)) {
360                         ret += next();
361                         ch = peek();
362                 }
363                 return ret;
364         };
365
366         function parse_error(err) {
367                 js_error(err, S.tokline, S.tokcol, S.tokpos);
368         };
369
370         function read_num(prefix) {
371                 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
372                 var num = read_while(function(ch, i){
373                         if (ch == "x" || ch == "X") {
374                                 if (has_x) return false;
375                                 return has_x = true;
376                         }
377                         if (!has_x && (ch == "E" || ch == "e")) {
378                                 if (has_e) return false;
379                                 return has_e = after_e = true;
380                         }
381                         if (ch == "-") {
382                                 if (after_e || (i == 0 && !prefix)) return true;
383                                 return false;
384                         }
385                         if (ch == "+") return after_e;
386                         after_e = false;
387                         if (ch == ".") {
388                                 if (!has_dot && !has_x && !has_e)
389                                         return has_dot = true;
390                                 return false;
391                         }
392                         return is_alphanumeric_char(ch);
393                 });
394                 if (prefix)
395                         num = prefix + num;
396                 var valid = parse_js_number(num);
397                 if (!isNaN(valid)) {
398                         return token("num", valid);
399                 } else {
400                         parse_error("Invalid syntax: " + num);
401                 }
402         };
403
404         function read_escaped_char(in_string) {
405                 var ch = next(true, in_string);
406                 switch (ch) {
407                     case "n" : return "\n";
408                     case "r" : return "\r";
409                     case "t" : return "\t";
410                     case "b" : return "\b";
411                     case "v" : return "\u000b";
412                     case "f" : return "\f";
413                     case "0" : return "\0";
414                     case "x" : return String.fromCharCode(hex_bytes(2));
415                     case "u" : return String.fromCharCode(hex_bytes(4));
416                     case "\n": return "";
417                     default  : return ch;
418                 }
419         };
420
421         function hex_bytes(n) {
422                 var num = 0;
423                 for (; n > 0; --n) {
424                         var digit = parseInt(next(true), 16);
425                         if (isNaN(digit))
426                                 parse_error("Invalid hex-character pattern in string");
427                         num = (num << 4) | digit;
428                 }
429                 return num;
430         };
431
432         function read_string() {
433                 return with_eof_error("Unterminated string constant", function(){
434                         var quote = next(), ret = "";
435                         for (;;) {
436                                 var ch = next(true);
437                                 if (ch == "\\") {
438                                         // read OctalEscapeSequence (XXX: deprecated if "strict mode")
439                                         // https://github.com/mishoo/UglifyJS/issues/178
440                                         var octal_len = 0, first = null;
441                                         ch = read_while(function(ch){
442                                                 if (ch >= "0" && ch <= "7") {
443                                                         if (!first) {
444                                                                 first = ch;
445                                                                 return ++octal_len;
446                                                         }
447                                                         else if (first <= "3" && octal_len <= 2) return ++octal_len;
448                                                         else if (first >= "4" && octal_len <= 1) return ++octal_len;
449                                                 }
450                                                 return false;
451                                         });
452                                         if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
453                                         else ch = read_escaped_char(true);
454                                 }
455                                 else if (ch == quote) break;
456                                 ret += ch;
457                         }
458                         return token("string", ret);
459                 });
460         };
461
462         function read_line_comment() {
463                 next();
464                 var i = find("\n"), ret;
465                 if (i == -1) {
466                         ret = S.text.substr(S.pos);
467                         S.pos = S.text.length;
468                 } else {
469                         ret = S.text.substring(S.pos, i);
470                         S.pos = i;
471                 }
472                 return token("comment1", ret, true);
473         };
474
475         function read_multiline_comment() {
476                 next();
477                 return with_eof_error("Unterminated multiline comment", function(){
478                         var i = find("*/", true),
479                             text = S.text.substring(S.pos, i);
480                         S.pos = i + 2;
481                         S.line += text.split("\n").length - 1;
482                         S.newline_before = S.newline_before || text.indexOf("\n") >= 0;
483
484                         // https://github.com/mishoo/UglifyJS/issues/#issue/100
485                         if (/^@cc_on/i.test(text)) {
486                                 warn("WARNING: at line " + S.line);
487                                 warn("*** Found \"conditional comment\": " + text);
488                                 warn("*** UglifyJS DISCARDS ALL COMMENTS.  This means your code might no longer work properly in Internet Explorer.");
489                         }
490
491                         return token("comment2", text, true);
492                 });
493         };
494
495         function read_name() {
496                 var backslash = false, name = "", ch, escaped = false, hex;
497                 while ((ch = peek()) != null) {
498                         if (!backslash) {
499                                 if (ch == "\\") escaped = backslash = true, next();
500                                 else if (is_identifier_char(ch)) name += next();
501                                 else break;
502                         }
503                         else {
504                                 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
505                                 ch = read_escaped_char();
506                                 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
507                                 name += ch;
508                                 backslash = false;
509                         }
510                 }
511                 if (HOP(KEYWORDS, name) && escaped) {
512                         hex = name.charCodeAt(0).toString(16).toUpperCase();
513                         name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
514                 }
515                 return name;
516         };
517
518         function read_regexp(regexp) {
519                 return with_eof_error("Unterminated regular expression", function(){
520                         var prev_backslash = false, ch, in_class = false;
521                         while ((ch = next(true))) if (prev_backslash) {
522                                 regexp += "\\" + ch;
523                                 prev_backslash = false;
524                         } else if (ch == "[") {
525                                 in_class = true;
526                                 regexp += ch;
527                         } else if (ch == "]" && in_class) {
528                                 in_class = false;
529                                 regexp += ch;
530                         } else if (ch == "/" && !in_class) {
531                                 break;
532                         } else if (ch == "\\") {
533                                 prev_backslash = true;
534                         } else {
535                                 regexp += ch;
536                         }
537                         var mods = read_name();
538                         return token("regexp", [ regexp, mods ]);
539                 });
540         };
541
542         function read_operator(prefix) {
543                 function grow(op) {
544                         if (!peek()) return op;
545                         var bigger = op + peek();
546                         if (HOP(OPERATORS, bigger)) {
547                                 next();
548                                 return grow(bigger);
549                         } else {
550                                 return op;
551                         }
552                 };
553                 return token("operator", grow(prefix || next()));
554         };
555
556         function handle_slash() {
557                 next();
558                 var regex_allowed = S.regex_allowed;
559                 switch (peek()) {
560                     case "/":
561                         S.comments_before.push(read_line_comment());
562                         S.regex_allowed = regex_allowed;
563                         return next_token();
564                     case "*":
565                         S.comments_before.push(read_multiline_comment());
566                         S.regex_allowed = regex_allowed;
567                         return next_token();
568                 }
569                 return S.regex_allowed ? read_regexp("") : read_operator("/");
570         };
571
572         function handle_dot() {
573                 next();
574                 return is_digit(peek())
575                         ? read_num(".")
576                         : token("punc", ".");
577         };
578
579         function read_word() {
580                 var word = read_name();
581                 return !HOP(KEYWORDS, word)
582                         ? token("name", word)
583                         : HOP(OPERATORS, word)
584                         ? token("operator", word)
585                         : HOP(KEYWORDS_ATOM, word)
586                         ? token("atom", word)
587                         : token("keyword", word);
588         };
589
590         function with_eof_error(eof_error, cont) {
591                 try {
592                         return cont();
593                 } catch(ex) {
594                         if (ex === EX_EOF) parse_error(eof_error);
595                         else throw ex;
596                 }
597         };
598
599         function next_token(force_regexp) {
600                 if (force_regexp != null)
601                         return read_regexp(force_regexp);
602                 skip_whitespace();
603                 start_token();
604                 var ch = peek();
605                 if (!ch) return token("eof");
606                 if (is_digit(ch)) return read_num();
607                 if (ch == '"' || ch == "'") return read_string();
608                 if (HOP(PUNC_CHARS, ch)) return token("punc", next());
609                 if (ch == ".") return handle_dot();
610                 if (ch == "/") return handle_slash();
611                 if (HOP(OPERATOR_CHARS, ch)) return read_operator();
612                 if (ch == "\\" || is_identifier_start(ch)) return read_word();
613                 parse_error("Unexpected character '" + ch + "'");
614         };
615
616         next_token.context = function(nc) {
617                 if (nc) S = nc;
618                 return S;
619         };
620
621         return next_token;
622
623 };
624
625 /* -----[ Parser (constants) ]----- */
626
627 var UNARY_PREFIX = array_to_hash([
628         "typeof",
629         "void",
630         "delete",
631         "--",
632         "++",
633         "!",
634         "~",
635         "-",
636         "+"
637 ]);
638
639 var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
640
641 var ASSIGNMENT = (function(a, ret, i){
642         while (i < a.length) {
643                 ret[a[i]] = a[i].substr(0, a[i].length - 1);
644                 i++;
645         }
646         return ret;
647 })(
648         ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
649         { "=": true },
650         0
651 );
652
653 var PRECEDENCE = (function(a, ret){
654         for (var i = 0, n = 1; i < a.length; ++i, ++n) {
655                 var b = a[i];
656                 for (var j = 0; j < b.length; ++j) {
657                         ret[b[j]] = n;
658                 }
659         }
660         return ret;
661 })(
662         [
663                 ["||"],
664                 ["&&"],
665                 ["|"],
666                 ["^"],
667                 ["&"],
668                 ["==", "===", "!=", "!=="],
669                 ["<", ">", "<=", ">=", "in", "instanceof"],
670                 [">>", "<<", ">>>"],
671                 ["+", "-"],
672                 ["*", "/", "%"]
673         ],
674         {}
675 );
676
677 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
678
679 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
680
681 /* -----[ Parser ]----- */
682
683 function NodeWithToken(str, start, end) {
684         this.name = str;
685         this.start = start;
686         this.end = end;
687 };
688
689 NodeWithToken.prototype.toString = function() { return this.name; };
690
691 function parse($TEXT, exigent_mode, embed_tokens) {
692
693         var S = {
694                 input         : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
695                 token         : null,
696                 prev          : null,
697                 peeked        : null,
698                 in_function   : 0,
699                 in_directives : true,
700                 in_loop       : 0,
701                 labels        : []
702         };
703
704         S.token = next();
705
706         function is(type, value) {
707                 return is_token(S.token, type, value);
708         };
709
710         function peek() { return S.peeked || (S.peeked = S.input()); };
711
712         function next() {
713                 S.prev = S.token;
714                 if (S.peeked) {
715                         S.token = S.peeked;
716                         S.peeked = null;
717                 } else {
718                         S.token = S.input();
719                 }
720                 S.in_directives = S.in_directives && (
721                         S.token.type == "string" || is("punc", ";")
722                 );
723                 return S.token;
724         };
725
726         function prev() {
727                 return S.prev;
728         };
729
730         function croak(msg, line, col, pos) {
731                 var ctx = S.input.context();
732                 js_error(msg,
733                          line != null ? line : ctx.tokline,
734                          col != null ? col : ctx.tokcol,
735                          pos != null ? pos : ctx.tokpos);
736         };
737
738         function token_error(token, msg) {
739                 croak(msg, token.line, token.col);
740         };
741
742         function unexpected(token) {
743                 if (token == null)
744                         token = S.token;
745                 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
746         };
747
748         function expect_token(type, val) {
749                 if (is(type, val)) {
750                         return next();
751                 }
752                 token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
753         };
754
755         function expect(punc) { return expect_token("punc", punc); };
756
757         function can_insert_semicolon() {
758                 return !exigent_mode && (
759                         S.token.nlb || is("eof") || is("punc", "}")
760                 );
761         };
762
763         function semicolon() {
764                 if (is("punc", ";")) next();
765                 else if (!can_insert_semicolon()) unexpected();
766         };
767
768         function as() {
769                 return slice(arguments);
770         };
771
772         function parenthesised() {
773                 expect("(");
774                 var ex = expression();
775                 expect(")");
776                 return ex;
777         };
778
779         function add_tokens(str, start, end) {
780                 return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
781         };
782
783         function maybe_embed_tokens(parser) {
784                 if (embed_tokens) return function() {
785                         var start = S.token;
786                         var ast = parser.apply(this, arguments);
787                         ast[0] = add_tokens(ast[0], start, prev());
788                         return ast;
789                 };
790                 else return parser;
791         };
792
793         var statement = maybe_embed_tokens(function() {
794                 if (is("operator", "/") || is("operator", "/=")) {
795                         S.peeked = null;
796                         S.token = S.input(S.token.value.substr(1)); // force regexp
797                 }
798                 switch (S.token.type) {
799                     case "string":
800                         var dir = S.in_directives, stat = simple_statement();
801                         if (dir && stat[1][0] == "string" && !is("punc", ","))
802                             return as("directive", stat[1][1]);
803                         return stat;
804                     case "num":
805                     case "regexp":
806                     case "operator":
807                     case "atom":
808                         return simple_statement();
809
810                     case "name":
811                         return is_token(peek(), "punc", ":")
812                                 ? labeled_statement(prog1(S.token.value, next, next))
813                                 : simple_statement();
814
815                     case "punc":
816                         switch (S.token.value) {
817                             case "{":
818                                 return as("block", block_());
819                             case "[":
820                             case "(":
821                                 return simple_statement();
822                             case ";":
823                                 next();
824                                 return as("block");
825                             default:
826                                 unexpected();
827                         }
828
829                     case "keyword":
830                         switch (prog1(S.token.value, next)) {
831                             case "break":
832                                 return break_cont("break");
833
834                             case "continue":
835                                 return break_cont("continue");
836
837                             case "debugger":
838                                 semicolon();
839                                 return as("debugger");
840
841                             case "do":
842                                 return (function(body){
843                                         expect_token("keyword", "while");
844                                         return as("do", prog1(parenthesised, semicolon), body);
845                                 })(in_loop(statement));
846
847                             case "for":
848                                 return for_();
849
850                             case "function":
851                                 return function_(true);
852
853                             case "if":
854                                 return if_();
855
856                             case "return":
857                                 if (S.in_function == 0)
858                                         croak("'return' outside of function");
859                                 return as("return",
860                                           is("punc", ";")
861                                           ? (next(), null)
862                                           : can_insert_semicolon()
863                                           ? null
864                                           : prog1(expression, semicolon));
865
866                             case "switch":
867                                 return as("switch", parenthesised(), switch_block_());
868
869                             case "throw":
870                                 if (S.token.nlb)
871                                         croak("Illegal newline after 'throw'");
872                                 return as("throw", prog1(expression, semicolon));
873
874                             case "try":
875                                 return try_();
876
877                             case "var":
878                                 return prog1(var_, semicolon);
879
880                             case "const":
881                                 return prog1(const_, semicolon);
882
883                             case "while":
884                                 return as("while", parenthesised(), in_loop(statement));
885
886                             case "with":
887                                 return as("with", parenthesised(), statement());
888
889                             default:
890                                 unexpected();
891                         }
892                 }
893         });
894
895         function labeled_statement(label) {
896                 S.labels.push(label);
897                 var start = S.token, stat = statement();
898                 if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
899                         unexpected(start);
900                 S.labels.pop();
901                 return as("label", label, stat);
902         };
903
904         function simple_statement() {
905                 return as("stat", prog1(expression, semicolon));
906         };
907
908         function break_cont(type) {
909                 var name;
910                 if (!can_insert_semicolon()) {
911                         name = is("name") ? S.token.value : null;
912                 }
913                 if (name != null) {
914                         next();
915                         if (!member(name, S.labels))
916                                 croak("Label " + name + " without matching loop or statement");
917                 }
918                 else if (S.in_loop == 0)
919                         croak(type + " not inside a loop or switch");
920                 semicolon();
921                 return as(type, name);
922         };
923
924         function for_() {
925                 expect("(");
926                 var init = null;
927                 if (!is("punc", ";")) {
928                         init = is("keyword", "var")
929                                 ? (next(), var_(true))
930                                 : expression(true, true);
931                         if (is("operator", "in")) {
932                                 if (init[0] == "var" && init[1].length > 1)
933                                         croak("Only one variable declaration allowed in for..in loop");
934                                 return for_in(init);
935                         }
936                 }
937                 return regular_for(init);
938         };
939
940         function regular_for(init) {
941                 expect(";");
942                 var test = is("punc", ";") ? null : expression();
943                 expect(";");
944                 var step = is("punc", ")") ? null : expression();
945                 expect(")");
946                 return as("for", init, test, step, in_loop(statement));
947         };
948
949         function for_in(init) {
950                 var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
951                 next();
952                 var obj = expression();
953                 expect(")");
954                 return as("for-in", init, lhs, obj, in_loop(statement));
955         };
956
957         var function_ = function(in_statement) {
958                 var name = is("name") ? prog1(S.token.value, next) : null;
959                 if (in_statement && !name)
960                         unexpected();
961                 expect("(");
962                 return as(in_statement ? "defun" : "function",
963                           name,
964                           // arguments
965                           (function(first, a){
966                                   while (!is("punc", ")")) {
967                                           if (first) first = false; else expect(",");
968                                           if (!is("name")) unexpected();
969                                           a.push(S.token.value);
970                                           next();
971                                   }
972                                   next();
973                                   return a;
974                           })(true, []),
975                           // body
976                           (function(){
977                                   ++S.in_function;
978                                   var loop = S.in_loop;
979                                   S.in_directives = true;
980                                   S.in_loop = 0;
981                                   var a = block_();
982                                   --S.in_function;
983                                   S.in_loop = loop;
984                                   return a;
985                           })());
986         };
987
988         function if_() {
989                 var cond = parenthesised(), body = statement(), belse;
990                 if (is("keyword", "else")) {
991                         next();
992                         belse = statement();
993                 }
994                 return as("if", cond, body, belse);
995         };
996
997         function block_() {
998                 expect("{");
999                 var a = [];
1000                 while (!is("punc", "}")) {
1001                         if (is("eof")) unexpected();
1002                         a.push(statement());
1003                 }
1004                 next();
1005                 return a;
1006         };
1007
1008         var switch_block_ = curry(in_loop, function(){
1009                 expect("{");
1010                 var a = [], cur = null;
1011                 while (!is("punc", "}")) {
1012                         if (is("eof")) unexpected();
1013                         if (is("keyword", "case")) {
1014                                 next();
1015                                 cur = [];
1016                                 a.push([ expression(), cur ]);
1017                                 expect(":");
1018                         }
1019                         else if (is("keyword", "default")) {
1020                                 next();
1021                                 expect(":");
1022                                 cur = [];
1023                                 a.push([ null, cur ]);
1024                         }
1025                         else {
1026                                 if (!cur) unexpected();
1027                                 cur.push(statement());
1028                         }
1029                 }
1030                 next();
1031                 return a;
1032         });
1033
1034         function try_() {
1035                 var body = block_(), bcatch, bfinally;
1036                 if (is("keyword", "catch")) {
1037                         next();
1038                         expect("(");
1039                         if (!is("name"))
1040                                 croak("Name expected");
1041                         var name = S.token.value;
1042                         next();
1043                         expect(")");
1044                         bcatch = [ name, block_() ];
1045                 }
1046                 if (is("keyword", "finally")) {
1047                         next();
1048                         bfinally = block_();
1049                 }
1050                 if (!bcatch && !bfinally)
1051                         croak("Missing catch/finally blocks");
1052                 return as("try", body, bcatch, bfinally);
1053         };
1054
1055         function vardefs(no_in) {
1056                 var a = [];
1057                 for (;;) {
1058                         if (!is("name"))
1059                                 unexpected();
1060                         var name = S.token.value;
1061                         next();
1062                         if (is("operator", "=")) {
1063                                 next();
1064                                 a.push([ name, expression(false, no_in) ]);
1065                         } else {
1066                                 a.push([ name ]);
1067                         }
1068                         if (!is("punc", ","))
1069                                 break;
1070                         next();
1071                 }
1072                 return a;
1073         };
1074
1075         function var_(no_in) {
1076                 return as("var", vardefs(no_in));
1077         };
1078
1079         function const_() {
1080                 return as("const", vardefs());
1081         };
1082
1083         function new_() {
1084                 var newexp = expr_atom(false), args;
1085                 if (is("punc", "(")) {
1086                         next();
1087                         args = expr_list(")");
1088                 } else {
1089                         args = [];
1090                 }
1091                 return subscripts(as("new", newexp, args), true);
1092         };
1093
1094         var expr_atom = maybe_embed_tokens(function(allow_calls) {
1095                 if (is("operator", "new")) {
1096                         next();
1097                         return new_();
1098                 }
1099                 if (is("punc")) {
1100                         switch (S.token.value) {
1101                             case "(":
1102                                 next();
1103                                 return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
1104                             case "[":
1105                                 next();
1106                                 return subscripts(array_(), allow_calls);
1107                             case "{":
1108                                 next();
1109                                 return subscripts(object_(), allow_calls);
1110                         }
1111                         unexpected();
1112                 }
1113                 if (is("keyword", "function")) {
1114                         next();
1115                         return subscripts(function_(false), allow_calls);
1116                 }
1117                 if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
1118                         var atom = S.token.type == "regexp"
1119                                 ? as("regexp", S.token.value[0], S.token.value[1])
1120                                 : as(S.token.type, S.token.value);
1121                         return subscripts(prog1(atom, next), allow_calls);
1122                 }
1123                 unexpected();
1124         });
1125
1126         function expr_list(closing, allow_trailing_comma, allow_empty) {
1127                 var first = true, a = [];
1128                 while (!is("punc", closing)) {
1129                         if (first) first = false; else expect(",");
1130                         if (allow_trailing_comma && is("punc", closing)) break;
1131                         if (is("punc", ",") && allow_empty) {
1132                                 a.push([ "atom", "undefined" ]);
1133                         } else {
1134                                 a.push(expression(false));
1135                         }
1136                 }
1137                 next();
1138                 return a;
1139         };
1140
1141         function array_() {
1142                 return as("array", expr_list("]", !exigent_mode, true));
1143         };
1144
1145         function object_() {
1146                 var first = true, a = [];
1147                 while (!is("punc", "}")) {
1148                         if (first) first = false; else expect(",");
1149                         if (!exigent_mode && is("punc", "}"))
1150                                 // allow trailing comma
1151                                 break;
1152                         var type = S.token.type;
1153                         var name = as_property_name();
1154                         if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
1155                                 a.push([ as_name(), function_(false), name ]);
1156                         } else {
1157                                 expect(":");
1158                                 a.push([ name, expression(false) ]);
1159                         }
1160                         // FIXME [!!] Line not in original parse-js,
1161                         // added to be able to warn about unquoted
1162                         // keyword properties
1163                         a[a.length - 1].type = type;
1164                 }
1165                 next();
1166                 return as("object", a);
1167         };
1168
1169         function as_property_name() {
1170                 switch (S.token.type) {
1171                     case "num":
1172                     case "string":
1173                         return prog1(S.token.value, next);
1174                 }
1175                 return as_name();
1176         };
1177
1178         function as_name() {
1179                 switch (S.token.type) {
1180                     case "name":
1181                     case "operator":
1182                     case "keyword":
1183                     case "atom":
1184                         return prog1(S.token.value, next);
1185                     default:
1186                         unexpected();
1187                 }
1188         };
1189
1190         function subscripts(expr, allow_calls) {
1191                 if (is("punc", ".")) {
1192                         next();
1193                         return subscripts(as("dot", expr, as_name()), allow_calls);
1194                 }
1195                 if (is("punc", "[")) {
1196                         next();
1197                         return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
1198                 }
1199                 if (allow_calls && is("punc", "(")) {
1200                         next();
1201                         return subscripts(as("call", expr, expr_list(")")), true);
1202                 }
1203                 return expr;
1204         };
1205
1206         function maybe_unary(allow_calls) {
1207                 if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
1208                         return make_unary("unary-prefix",
1209                                           prog1(S.token.value, next),
1210                                           maybe_unary(allow_calls));
1211                 }
1212                 var val = expr_atom(allow_calls);
1213                 while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
1214                         val = make_unary("unary-postfix", S.token.value, val);
1215                         next();
1216                 }
1217                 return val;
1218         };
1219
1220         function make_unary(tag, op, expr) {
1221                 if ((op == "++" || op == "--") && !is_assignable(expr))
1222                         croak("Invalid use of " + op + " operator");
1223                 return as(tag, op, expr);
1224         };
1225
1226         function expr_op(left, min_prec, no_in) {
1227                 var op = is("operator") ? S.token.value : null;
1228                 if (op && op == "in" && no_in) op = null;
1229                 var prec = op != null ? PRECEDENCE[op] : null;
1230                 if (prec != null && prec > min_prec) {
1231                         next();
1232                         var right = expr_op(maybe_unary(true), prec, no_in);
1233                         return expr_op(as("binary", op, left, right), min_prec, no_in);
1234                 }
1235                 return left;
1236         };
1237
1238         function expr_ops(no_in) {
1239                 return expr_op(maybe_unary(true), 0, no_in);
1240         };
1241
1242         function maybe_conditional(no_in) {
1243                 var expr = expr_ops(no_in);
1244                 if (is("operator", "?")) {
1245                         next();
1246                         var yes = expression(false);
1247                         expect(":");
1248                         return as("conditional", expr, yes, expression(false, no_in));
1249                 }
1250                 return expr;
1251         };
1252
1253         function is_assignable(expr) {
1254                 if (!exigent_mode) return true;
1255                 switch (expr[0]+"") {
1256                     case "dot":
1257                     case "sub":
1258                     case "new":
1259                     case "call":
1260                         return true;
1261                     case "name":
1262                         return expr[1] != "this";
1263                 }
1264         };
1265
1266         function maybe_assign(no_in) {
1267                 var left = maybe_conditional(no_in), val = S.token.value;
1268                 if (is("operator") && HOP(ASSIGNMENT, val)) {
1269                         if (is_assignable(left)) {
1270                                 next();
1271                                 return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
1272                         }
1273                         croak("Invalid assignment");
1274                 }
1275                 return left;
1276         };
1277
1278         var expression = maybe_embed_tokens(function(commas, no_in) {
1279                 if (arguments.length == 0)
1280                         commas = true;
1281                 var expr = maybe_assign(no_in);
1282                 if (commas && is("punc", ",")) {
1283                         next();
1284                         return as("seq", expr, expression(true, no_in));
1285                 }
1286                 return expr;
1287         });
1288
1289         function in_loop(cont) {
1290                 try {
1291                         ++S.in_loop;
1292                         return cont();
1293                 } finally {
1294                         --S.in_loop;
1295                 }
1296         };
1297
1298         return as("toplevel", (function(a){
1299                 while (!is("eof"))
1300                         a.push(statement());
1301                 return a;
1302         })([]));
1303
1304 };
1305
1306 /* -----[ Utilities ]----- */
1307
1308 function curry(f) {
1309         var args = slice(arguments, 1);
1310         return function() { return f.apply(this, args.concat(slice(arguments))); };
1311 };
1312
1313 function prog1(ret) {
1314         if (ret instanceof Function)
1315                 ret = ret();
1316         for (var i = 1, n = arguments.length; --n > 0; ++i)
1317                 arguments[i]();
1318         return ret;
1319 };
1320
1321 function array_to_hash(a) {
1322         var ret = {};
1323         for (var i = 0; i < a.length; ++i)
1324                 ret[a[i]] = true;
1325         return ret;
1326 };
1327
1328 function slice(a, start) {
1329         return Array.prototype.slice.call(a, start || 0);
1330 };
1331
1332 function characters(str) {
1333         return str.split("");
1334 };
1335
1336 function member(name, array) {
1337         for (var i = array.length; --i >= 0;)
1338                 if (array[i] == name)
1339                         return true;
1340         return false;
1341 };
1342
1343 function HOP(obj, prop) {
1344         return Object.prototype.hasOwnProperty.call(obj, prop);
1345 };
1346
1347 var warn = function() {};
1348
1349 /* -----[ Exports ]----- */
1350
1351 exports.tokenizer = tokenizer;
1352 exports.parse = parse;
1353 exports.slice = slice;
1354 exports.curry = curry;
1355 exports.member = member;
1356 exports.array_to_hash = array_to_hash;
1357 exports.PRECEDENCE = PRECEDENCE;
1358 exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
1359 exports.RESERVED_WORDS = RESERVED_WORDS;
1360 exports.KEYWORDS = KEYWORDS;
1361 exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
1362 exports.OPERATORS = OPERATORS;
1363 exports.is_alphanumeric_char = is_alphanumeric_char;
1364 exports.is_identifier_start = is_identifier_start;
1365 exports.is_identifier_char = is_identifier_char;
1366 exports.set_logger = function(logger) {
1367         warn = logger;
1368 };
1369
1370 // Local variables:
1371 // js-indent-level: 8
1372 // End: