Export 0.2.1
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / grunt / node_modules / nodeunit / node_modules / tap / node_modules / runforcover / node_modules / bunker / node_modules / burrito / node_modules / uglify-js / lib / 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         "default",
69         "delete",
70         "do",
71         "else",
72         "finally",
73         "for",
74         "function",
75         "if",
76         "in",
77         "instanceof",
78         "new",
79         "return",
80         "switch",
81         "throw",
82         "try",
83         "typeof",
84         "var",
85         "void",
86         "while",
87         "with"
88 ]);
89
90 var RESERVED_WORDS = array_to_hash([
91         "abstract",
92         "boolean",
93         "byte",
94         "char",
95         "class",
96         "debugger",
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 // regexps adapted from http://xregexp.com/plugins/#unicode
203 var UNICODE = {
204         letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\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]"),
205         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]"),
206         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]"),
207         connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
208 };
209
210 function is_letter(ch) {
211         return UNICODE.letter.test(ch);
212 };
213
214 function is_digit(ch) {
215         ch = ch.charCodeAt(0);
216         return ch >= 48 && ch <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
217 };
218
219 function is_alphanumeric_char(ch) {
220         return is_digit(ch) || is_letter(ch);
221 };
222
223 function is_unicode_combining_mark(ch) {
224         return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
225 };
226
227 function is_unicode_connector_punctuation(ch) {
228         return UNICODE.connector_punctuation.test(ch);
229 };
230
231 function is_identifier_start(ch) {
232         return ch == "$" || ch == "_" || is_letter(ch);
233 };
234
235 function is_identifier_char(ch) {
236         return is_identifier_start(ch)
237                 || is_unicode_combining_mark(ch)
238                 || is_digit(ch)
239                 || is_unicode_connector_punctuation(ch)
240                 || ch == "\u200c" // zero-width non-joiner <ZWNJ>
241                 || ch == "\u200d" // zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
242         ;
243 };
244
245 function parse_js_number(num) {
246         if (RE_HEX_NUMBER.test(num)) {
247                 return parseInt(num.substr(2), 16);
248         } else if (RE_OCT_NUMBER.test(num)) {
249                 return parseInt(num.substr(1), 8);
250         } else if (RE_DEC_NUMBER.test(num)) {
251                 return parseFloat(num);
252         }
253 };
254
255 function JS_Parse_Error(message, line, col, pos) {
256         this.message = message;
257         this.line = line;
258         this.col = col;
259         this.pos = pos;
260         this.stack = new Error().stack;
261 };
262
263 JS_Parse_Error.prototype.toString = function() {
264         return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
265 };
266
267 function js_error(message, line, col, pos) {
268         throw new JS_Parse_Error(message, line, col, pos);
269 };
270
271 function is_token(token, type, val) {
272         return token.type == type && (val == null || token.value == val);
273 };
274
275 var EX_EOF = {};
276
277 function tokenizer($TEXT) {
278
279         var S = {
280                 text            : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
281                 pos             : 0,
282                 tokpos          : 0,
283                 line            : 0,
284                 tokline         : 0,
285                 col             : 0,
286                 tokcol          : 0,
287                 newline_before  : false,
288                 regex_allowed   : false,
289                 comments_before : []
290         };
291
292         function peek() { return S.text.charAt(S.pos); };
293
294         function next(signal_eof, in_string) {
295                 var ch = S.text.charAt(S.pos++);
296                 if (signal_eof && !ch)
297                         throw EX_EOF;
298                 if (ch == "\n") {
299                         S.newline_before = S.newline_before || !in_string;
300                         ++S.line;
301                         S.col = 0;
302                 } else {
303                         ++S.col;
304                 }
305                 return ch;
306         };
307
308         function eof() {
309                 return !S.peek();
310         };
311
312         function find(what, signal_eof) {
313                 var pos = S.text.indexOf(what, S.pos);
314                 if (signal_eof && pos == -1) throw EX_EOF;
315                 return pos;
316         };
317
318         function start_token() {
319                 S.tokline = S.line;
320                 S.tokcol = S.col;
321                 S.tokpos = S.pos;
322         };
323
324         function token(type, value, is_comment) {
325                 S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
326                                    (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
327                                    (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
328                 var ret = {
329                         type  : type,
330                         value : value,
331                         line  : S.tokline,
332                         col   : S.tokcol,
333                         pos   : S.tokpos,
334                         nlb   : S.newline_before
335                 };
336                 if (!is_comment) {
337                         ret.comments_before = S.comments_before;
338                         S.comments_before = [];
339                 }
340                 S.newline_before = false;
341                 return ret;
342         };
343
344         function skip_whitespace() {
345                 while (HOP(WHITESPACE_CHARS, peek()))
346                         next();
347         };
348
349         function read_while(pred) {
350                 var ret = "", ch = peek(), i = 0;
351                 while (ch && pred(ch, i++)) {
352                         ret += next();
353                         ch = peek();
354                 }
355                 return ret;
356         };
357
358         function parse_error(err) {
359                 js_error(err, S.tokline, S.tokcol, S.tokpos);
360         };
361
362         function read_num(prefix) {
363                 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
364                 var num = read_while(function(ch, i){
365                         if (ch == "x" || ch == "X") {
366                                 if (has_x) return false;
367                                 return has_x = true;
368                         }
369                         if (!has_x && (ch == "E" || ch == "e")) {
370                                 if (has_e) return false;
371                                 return has_e = after_e = true;
372                         }
373                         if (ch == "-") {
374                                 if (after_e || (i == 0 && !prefix)) return true;
375                                 return false;
376                         }
377                         if (ch == "+") return after_e;
378                         after_e = false;
379                         if (ch == ".") {
380                                 if (!has_dot && !has_x)
381                                         return has_dot = true;
382                                 return false;
383                         }
384                         return is_alphanumeric_char(ch);
385                 });
386                 if (prefix)
387                         num = prefix + num;
388                 var valid = parse_js_number(num);
389                 if (!isNaN(valid)) {
390                         return token("num", valid);
391                 } else {
392                         parse_error("Invalid syntax: " + num);
393                 }
394         };
395
396         function read_escaped_char(in_string) {
397                 var ch = next(true, in_string);
398                 switch (ch) {
399                     case "n" : return "\n";
400                     case "r" : return "\r";
401                     case "t" : return "\t";
402                     case "b" : return "\b";
403                     case "v" : return "\u000b";
404                     case "f" : return "\f";
405                     case "0" : return "\0";
406                     case "x" : return String.fromCharCode(hex_bytes(2));
407                     case "u" : return String.fromCharCode(hex_bytes(4));
408                     case "\n": return "";
409                     default  : return ch;
410                 }
411         };
412
413         function hex_bytes(n) {
414                 var num = 0;
415                 for (; n > 0; --n) {
416                         var digit = parseInt(next(true), 16);
417                         if (isNaN(digit))
418                                 parse_error("Invalid hex-character pattern in string");
419                         num = (num << 4) | digit;
420                 }
421                 return num;
422         };
423
424         function read_string() {
425                 return with_eof_error("Unterminated string constant", function(){
426                         var quote = next(), ret = "";
427                         for (;;) {
428                                 var ch = next(true);
429                                 if (ch == "\\") {
430                                         // read OctalEscapeSequence (XXX: deprecated if "strict mode")
431                                         // https://github.com/mishoo/UglifyJS/issues/178
432                                         var octal_len = 0, first = null;
433                                         ch = read_while(function(ch){
434                                                 if (ch >= "0" && ch <= "7") {
435                                                         if (!first) {
436                                                                 first = ch;
437                                                                 return ++octal_len;
438                                                         }
439                                                         else if (first <= "3" && octal_len <= 2) return ++octal_len;
440                                                         else if (first >= "4" && octal_len <= 1) return ++octal_len;
441                                                 }
442                                                 return false;
443                                         });
444                                         if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
445                                         else ch = read_escaped_char(true);
446                                 }
447                                 else if (ch == quote) break;
448                                 ret += ch;
449                         }
450                         return token("string", ret);
451                 });
452         };
453
454         function read_line_comment() {
455                 next();
456                 var i = find("\n"), ret;
457                 if (i == -1) {
458                         ret = S.text.substr(S.pos);
459                         S.pos = S.text.length;
460                 } else {
461                         ret = S.text.substring(S.pos, i);
462                         S.pos = i;
463                 }
464                 return token("comment1", ret, true);
465         };
466
467         function read_multiline_comment() {
468                 next();
469                 return with_eof_error("Unterminated multiline comment", function(){
470                         var i = find("*/", true),
471                             text = S.text.substring(S.pos, i),
472                             tok = token("comment2", text, true);
473                         S.pos = i + 2;
474                         S.line += text.split("\n").length - 1;
475                         S.newline_before = text.indexOf("\n") >= 0;
476
477                         // https://github.com/mishoo/UglifyJS/issues/#issue/100
478                         if (/^@cc_on/i.test(text)) {
479                                 warn("WARNING: at line " + S.line);
480                                 warn("*** Found \"conditional comment\": " + text);
481                                 warn("*** UglifyJS DISCARDS ALL COMMENTS.  This means your code might no longer work properly in Internet Explorer.");
482                         }
483
484                         return tok;
485                 });
486         };
487
488         function read_name() {
489                 var backslash = false, name = "", ch;
490                 while ((ch = peek()) != null) {
491                         if (!backslash) {
492                                 if (ch == "\\") backslash = true, next();
493                                 else if (is_identifier_char(ch)) name += next();
494                                 else break;
495                         }
496                         else {
497                                 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
498                                 ch = read_escaped_char();
499                                 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
500                                 name += ch;
501                                 backslash = false;
502                         }
503                 }
504                 return name;
505         };
506
507         function read_regexp(regexp) {
508                 return with_eof_error("Unterminated regular expression", function(){
509                         var prev_backslash = false, ch, in_class = false;
510                         while ((ch = next(true))) if (prev_backslash) {
511                                 regexp += "\\" + ch;
512                                 prev_backslash = false;
513                         } else if (ch == "[") {
514                                 in_class = true;
515                                 regexp += ch;
516                         } else if (ch == "]" && in_class) {
517                                 in_class = false;
518                                 regexp += ch;
519                         } else if (ch == "/" && !in_class) {
520                                 break;
521                         } else if (ch == "\\") {
522                                 prev_backslash = true;
523                         } else {
524                                 regexp += ch;
525                         }
526                         var mods = read_name();
527                         return token("regexp", [ regexp, mods ]);
528                 });
529         };
530
531         function read_operator(prefix) {
532                 function grow(op) {
533                         if (!peek()) return op;
534                         var bigger = op + peek();
535                         if (HOP(OPERATORS, bigger)) {
536                                 next();
537                                 return grow(bigger);
538                         } else {
539                                 return op;
540                         }
541                 };
542                 return token("operator", grow(prefix || next()));
543         };
544
545         function handle_slash() {
546                 next();
547                 var regex_allowed = S.regex_allowed;
548                 switch (peek()) {
549                     case "/":
550                         S.comments_before.push(read_line_comment());
551                         S.regex_allowed = regex_allowed;
552                         return next_token();
553                     case "*":
554                         S.comments_before.push(read_multiline_comment());
555                         S.regex_allowed = regex_allowed;
556                         return next_token();
557                 }
558                 return S.regex_allowed ? read_regexp("") : read_operator("/");
559         };
560
561         function handle_dot() {
562                 next();
563                 return is_digit(peek())
564                         ? read_num(".")
565                         : token("punc", ".");
566         };
567
568         function read_word() {
569                 var word = read_name();
570                 return !HOP(KEYWORDS, word)
571                         ? token("name", word)
572                         : HOP(OPERATORS, word)
573                         ? token("operator", word)
574                         : HOP(KEYWORDS_ATOM, word)
575                         ? token("atom", word)
576                         : token("keyword", word);
577         };
578
579         function with_eof_error(eof_error, cont) {
580                 try {
581                         return cont();
582                 } catch(ex) {
583                         if (ex === EX_EOF) parse_error(eof_error);
584                         else throw ex;
585                 }
586         };
587
588         function next_token(force_regexp) {
589                 if (force_regexp != null)
590                         return read_regexp(force_regexp);
591                 skip_whitespace();
592                 start_token();
593                 var ch = peek();
594                 if (!ch) return token("eof");
595                 if (is_digit(ch)) return read_num();
596                 if (ch == '"' || ch == "'") return read_string();
597                 if (HOP(PUNC_CHARS, ch)) return token("punc", next());
598                 if (ch == ".") return handle_dot();
599                 if (ch == "/") return handle_slash();
600                 if (HOP(OPERATOR_CHARS, ch)) return read_operator();
601                 if (ch == "\\" || is_identifier_start(ch)) return read_word();
602                 parse_error("Unexpected character '" + ch + "'");
603         };
604
605         next_token.context = function(nc) {
606                 if (nc) S = nc;
607                 return S;
608         };
609
610         return next_token;
611
612 };
613
614 /* -----[ Parser (constants) ]----- */
615
616 var UNARY_PREFIX = array_to_hash([
617         "typeof",
618         "void",
619         "delete",
620         "--",
621         "++",
622         "!",
623         "~",
624         "-",
625         "+"
626 ]);
627
628 var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
629
630 var ASSIGNMENT = (function(a, ret, i){
631         while (i < a.length) {
632                 ret[a[i]] = a[i].substr(0, a[i].length - 1);
633                 i++;
634         }
635         return ret;
636 })(
637         ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
638         { "=": true },
639         0
640 );
641
642 var PRECEDENCE = (function(a, ret){
643         for (var i = 0, n = 1; i < a.length; ++i, ++n) {
644                 var b = a[i];
645                 for (var j = 0; j < b.length; ++j) {
646                         ret[b[j]] = n;
647                 }
648         }
649         return ret;
650 })(
651         [
652                 ["||"],
653                 ["&&"],
654                 ["|"],
655                 ["^"],
656                 ["&"],
657                 ["==", "===", "!=", "!=="],
658                 ["<", ">", "<=", ">=", "in", "instanceof"],
659                 [">>", "<<", ">>>"],
660                 ["+", "-"],
661                 ["*", "/", "%"]
662         ],
663         {}
664 );
665
666 var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
667
668 var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
669
670 /* -----[ Parser ]----- */
671
672 function NodeWithToken(str, start, end) {
673         this.name = str;
674         this.start = start;
675         this.end = end;
676 };
677
678 NodeWithToken.prototype.toString = function() { return this.name; };
679
680 function parse($TEXT, exigent_mode, embed_tokens) {
681
682         var S = {
683                 input       : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
684                 token       : null,
685                 prev        : null,
686                 peeked      : null,
687                 in_function : 0,
688                 in_loop     : 0,
689                 labels      : []
690         };
691
692         S.token = next();
693
694         function is(type, value) {
695                 return is_token(S.token, type, value);
696         };
697
698         function peek() { return S.peeked || (S.peeked = S.input()); };
699
700         function next() {
701                 S.prev = S.token;
702                 if (S.peeked) {
703                         S.token = S.peeked;
704                         S.peeked = null;
705                 } else {
706                         S.token = S.input();
707                 }
708                 return S.token;
709         };
710
711         function prev() {
712                 return S.prev;
713         };
714
715         function croak(msg, line, col, pos) {
716                 var ctx = S.input.context();
717                 js_error(msg,
718                          line != null ? line : ctx.tokline,
719                          col != null ? col : ctx.tokcol,
720                          pos != null ? pos : ctx.tokpos);
721         };
722
723         function token_error(token, msg) {
724                 croak(msg, token.line, token.col);
725         };
726
727         function unexpected(token) {
728                 if (token == null)
729                         token = S.token;
730                 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
731         };
732
733         function expect_token(type, val) {
734                 if (is(type, val)) {
735                         return next();
736                 }
737                 token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
738         };
739
740         function expect(punc) { return expect_token("punc", punc); };
741
742         function can_insert_semicolon() {
743                 return !exigent_mode && (
744                         S.token.nlb || is("eof") || is("punc", "}")
745                 );
746         };
747
748         function semicolon() {
749                 if (is("punc", ";")) next();
750                 else if (!can_insert_semicolon()) unexpected();
751         };
752
753         function as() {
754                 return slice(arguments);
755         };
756
757         function parenthesised() {
758                 expect("(");
759                 var ex = expression();
760                 expect(")");
761                 return ex;
762         };
763
764         function add_tokens(str, start, end) {
765                 return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
766         };
767
768         function maybe_embed_tokens(parser) {
769                 if (embed_tokens) return function() {
770                         var start = S.token;
771                         var ast = parser.apply(this, arguments);
772                         ast[0] = add_tokens(ast[0], start, prev());
773                         return ast;
774                 };
775                 else return parser;
776         };
777
778         var statement = maybe_embed_tokens(function() {
779                 if (is("operator", "/") || is("operator", "/=")) {
780                         S.peeked = null;
781                         S.token = S.input(S.token.value.substr(1)); // force regexp
782                 }
783                 switch (S.token.type) {
784                     case "num":
785                     case "string":
786                     case "regexp":
787                     case "operator":
788                     case "atom":
789                         return simple_statement();
790
791                     case "name":
792                         return is_token(peek(), "punc", ":")
793                                 ? labeled_statement(prog1(S.token.value, next, next))
794                                 : simple_statement();
795
796                     case "punc":
797                         switch (S.token.value) {
798                             case "{":
799                                 return as("block", block_());
800                             case "[":
801                             case "(":
802                                 return simple_statement();
803                             case ";":
804                                 next();
805                                 return as("block");
806                             default:
807                                 unexpected();
808                         }
809
810                     case "keyword":
811                         switch (prog1(S.token.value, next)) {
812                             case "break":
813                                 return break_cont("break");
814
815                             case "continue":
816                                 return break_cont("continue");
817
818                             case "debugger":
819                                 semicolon();
820                                 return as("debugger");
821
822                             case "do":
823                                 return (function(body){
824                                         expect_token("keyword", "while");
825                                         return as("do", prog1(parenthesised, semicolon), body);
826                                 })(in_loop(statement));
827
828                             case "for":
829                                 return for_();
830
831                             case "function":
832                                 return function_(true);
833
834                             case "if":
835                                 return if_();
836
837                             case "return":
838                                 if (S.in_function == 0)
839                                         croak("'return' outside of function");
840                                 return as("return",
841                                           is("punc", ";")
842                                           ? (next(), null)
843                                           : can_insert_semicolon()
844                                           ? null
845                                           : prog1(expression, semicolon));
846
847                             case "switch":
848                                 return as("switch", parenthesised(), switch_block_());
849
850                             case "throw":
851                                 if (S.token.nlb)
852                                         croak("Illegal newline after 'throw'");
853                                 return as("throw", prog1(expression, semicolon));
854
855                             case "try":
856                                 return try_();
857
858                             case "var":
859                                 return prog1(var_, semicolon);
860
861                             case "const":
862                                 return prog1(const_, semicolon);
863
864                             case "while":
865                                 return as("while", parenthesised(), in_loop(statement));
866
867                             case "with":
868                                 return as("with", parenthesised(), statement());
869
870                             default:
871                                 unexpected();
872                         }
873                 }
874         });
875
876         function labeled_statement(label) {
877                 S.labels.push(label);
878                 var start = S.token, stat = statement();
879                 if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
880                         unexpected(start);
881                 S.labels.pop();
882                 return as("label", label, stat);
883         };
884
885         function simple_statement() {
886                 return as("stat", prog1(expression, semicolon));
887         };
888
889         function break_cont(type) {
890                 var name;
891                 if (!can_insert_semicolon()) {
892                         name = is("name") ? S.token.value : null;
893                 }
894                 if (name != null) {
895                         next();
896                         if (!member(name, S.labels))
897                                 croak("Label " + name + " without matching loop or statement");
898                 }
899                 else if (S.in_loop == 0)
900                         croak(type + " not inside a loop or switch");
901                 semicolon();
902                 return as(type, name);
903         };
904
905         function for_() {
906                 expect("(");
907                 var init = null;
908                 if (!is("punc", ";")) {
909                         init = is("keyword", "var")
910                                 ? (next(), var_(true))
911                                 : expression(true, true);
912                         if (is("operator", "in"))
913                                 return for_in(init);
914                 }
915                 return regular_for(init);
916         };
917
918         function regular_for(init) {
919                 expect(";");
920                 var test = is("punc", ";") ? null : expression();
921                 expect(";");
922                 var step = is("punc", ")") ? null : expression();
923                 expect(")");
924                 return as("for", init, test, step, in_loop(statement));
925         };
926
927         function for_in(init) {
928                 var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
929                 next();
930                 var obj = expression();
931                 expect(")");
932                 return as("for-in", init, lhs, obj, in_loop(statement));
933         };
934
935         var function_ = maybe_embed_tokens(function(in_statement) {
936                 var name = is("name") ? prog1(S.token.value, next) : null;
937                 if (in_statement && !name)
938                         unexpected();
939                 expect("(");
940                 return as(in_statement ? "defun" : "function",
941                           name,
942                           // arguments
943                           (function(first, a){
944                                   while (!is("punc", ")")) {
945                                           if (first) first = false; else expect(",");
946                                           if (!is("name")) unexpected();
947                                           a.push(S.token.value);
948                                           next();
949                                   }
950                                   next();
951                                   return a;
952                           })(true, []),
953                           // body
954                           (function(){
955                                   ++S.in_function;
956                                   var loop = S.in_loop;
957                                   S.in_loop = 0;
958                                   var a = block_();
959                                   --S.in_function;
960                                   S.in_loop = loop;
961                                   return a;
962                           })());
963         });
964
965         function if_() {
966                 var cond = parenthesised(), body = statement(), belse;
967                 if (is("keyword", "else")) {
968                         next();
969                         belse = statement();
970                 }
971                 return as("if", cond, body, belse);
972         };
973
974         function block_() {
975                 expect("{");
976                 var a = [];
977                 while (!is("punc", "}")) {
978                         if (is("eof")) unexpected();
979                         a.push(statement());
980                 }
981                 next();
982                 return a;
983         };
984
985         var switch_block_ = curry(in_loop, function(){
986                 expect("{");
987                 var a = [], cur = null;
988                 while (!is("punc", "}")) {
989                         if (is("eof")) unexpected();
990                         if (is("keyword", "case")) {
991                                 next();
992                                 cur = [];
993                                 a.push([ expression(), cur ]);
994                                 expect(":");
995                         }
996                         else if (is("keyword", "default")) {
997                                 next();
998                                 expect(":");
999                                 cur = [];
1000                                 a.push([ null, cur ]);
1001                         }
1002                         else {
1003                                 if (!cur) unexpected();
1004                                 cur.push(statement());
1005                         }
1006                 }
1007                 next();
1008                 return a;
1009         });
1010
1011         function try_() {
1012                 var body = block_(), bcatch, bfinally;
1013                 if (is("keyword", "catch")) {
1014                         next();
1015                         expect("(");
1016                         if (!is("name"))
1017                                 croak("Name expected");
1018                         var name = S.token.value;
1019                         next();
1020                         expect(")");
1021                         bcatch = [ name, block_() ];
1022                 }
1023                 if (is("keyword", "finally")) {
1024                         next();
1025                         bfinally = block_();
1026                 }
1027                 if (!bcatch && !bfinally)
1028                         croak("Missing catch/finally blocks");
1029                 return as("try", body, bcatch, bfinally);
1030         };
1031
1032         function vardefs(no_in) {
1033                 var a = [];
1034                 for (;;) {
1035                         if (!is("name"))
1036                                 unexpected();
1037                         var name = S.token.value;
1038                         next();
1039                         if (is("operator", "=")) {
1040                                 next();
1041                                 a.push([ name, expression(false, no_in) ]);
1042                         } else {
1043                                 a.push([ name ]);
1044                         }
1045                         if (!is("punc", ","))
1046                                 break;
1047                         next();
1048                 }
1049                 return a;
1050         };
1051
1052         function var_(no_in) {
1053                 return as("var", vardefs(no_in));
1054         };
1055
1056         function const_() {
1057                 return as("const", vardefs());
1058         };
1059
1060         function new_() {
1061                 var newexp = expr_atom(false), args;
1062                 if (is("punc", "(")) {
1063                         next();
1064                         args = expr_list(")");
1065                 } else {
1066                         args = [];
1067                 }
1068                 return subscripts(as("new", newexp, args), true);
1069         };
1070
1071         var expr_atom = maybe_embed_tokens(function(allow_calls) {
1072                 if (is("operator", "new")) {
1073                         next();
1074                         return new_();
1075                 }
1076                 if (is("punc")) {
1077                         switch (S.token.value) {
1078                             case "(":
1079                                 next();
1080                                 return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
1081                             case "[":
1082                                 next();
1083                                 return subscripts(array_(), allow_calls);
1084                             case "{":
1085                                 next();
1086                                 return subscripts(object_(), allow_calls);
1087                         }
1088                         unexpected();
1089                 }
1090                 if (is("keyword", "function")) {
1091                         next();
1092                         return subscripts(function_(false), allow_calls);
1093                 }
1094                 if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
1095                         var atom = S.token.type == "regexp"
1096                                 ? as("regexp", S.token.value[0], S.token.value[1])
1097                                 : as(S.token.type, S.token.value);
1098                         return subscripts(prog1(atom, next), allow_calls);
1099                 }
1100                 unexpected();
1101         });
1102
1103         function expr_list(closing, allow_trailing_comma, allow_empty) {
1104                 var first = true, a = [];
1105                 while (!is("punc", closing)) {
1106                         if (first) first = false; else expect(",");
1107                         if (allow_trailing_comma && is("punc", closing)) break;
1108                         if (is("punc", ",") && allow_empty) {
1109                                 a.push([ "atom", "undefined" ]);
1110                         } else {
1111                                 a.push(expression(false));
1112                         }
1113                 }
1114                 next();
1115                 return a;
1116         };
1117
1118         function array_() {
1119                 return as("array", expr_list("]", !exigent_mode, true));
1120         };
1121
1122         function object_() {
1123                 var first = true, a = [];
1124                 while (!is("punc", "}")) {
1125                         if (first) first = false; else expect(",");
1126                         if (!exigent_mode && is("punc", "}"))
1127                                 // allow trailing comma
1128                                 break;
1129                         var type = S.token.type;
1130                         var name = as_property_name();
1131                         if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
1132                                 a.push([ as_name(), function_(false), name ]);
1133                         } else {
1134                                 expect(":");
1135                                 a.push([ name, expression(false) ]);
1136                         }
1137                 }
1138                 next();
1139                 return as("object", a);
1140         };
1141
1142         function as_property_name() {
1143                 switch (S.token.type) {
1144                     case "num":
1145                     case "string":
1146                         return prog1(S.token.value, next);
1147                 }
1148                 return as_name();
1149         };
1150
1151         function as_name() {
1152                 switch (S.token.type) {
1153                     case "name":
1154                     case "operator":
1155                     case "keyword":
1156                     case "atom":
1157                         return prog1(S.token.value, next);
1158                     default:
1159                         unexpected();
1160                 }
1161         };
1162
1163         function subscripts(expr, allow_calls) {
1164                 if (is("punc", ".")) {
1165                         next();
1166                         return subscripts(as("dot", expr, as_name()), allow_calls);
1167                 }
1168                 if (is("punc", "[")) {
1169                         next();
1170                         return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
1171                 }
1172                 if (allow_calls && is("punc", "(")) {
1173                         next();
1174                         return subscripts(as("call", expr, expr_list(")")), true);
1175                 }
1176                 return expr;
1177         };
1178
1179         function maybe_unary(allow_calls) {
1180                 if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
1181                         return make_unary("unary-prefix",
1182                                           prog1(S.token.value, next),
1183                                           maybe_unary(allow_calls));
1184                 }
1185                 var val = expr_atom(allow_calls);
1186                 while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
1187                         val = make_unary("unary-postfix", S.token.value, val);
1188                         next();
1189                 }
1190                 return val;
1191         };
1192
1193         function make_unary(tag, op, expr) {
1194                 if ((op == "++" || op == "--") && !is_assignable(expr))
1195                         croak("Invalid use of " + op + " operator");
1196                 return as(tag, op, expr);
1197         };
1198
1199         function expr_op(left, min_prec, no_in) {
1200                 var op = is("operator") ? S.token.value : null;
1201                 if (op && op == "in" && no_in) op = null;
1202                 var prec = op != null ? PRECEDENCE[op] : null;
1203                 if (prec != null && prec > min_prec) {
1204                         next();
1205                         var right = expr_op(maybe_unary(true), prec, no_in);
1206                         return expr_op(as("binary", op, left, right), min_prec, no_in);
1207                 }
1208                 return left;
1209         };
1210
1211         function expr_ops(no_in) {
1212                 return expr_op(maybe_unary(true), 0, no_in);
1213         };
1214
1215         function maybe_conditional(no_in) {
1216                 var expr = expr_ops(no_in);
1217                 if (is("operator", "?")) {
1218                         next();
1219                         var yes = expression(false);
1220                         expect(":");
1221                         return as("conditional", expr, yes, expression(false, no_in));
1222                 }
1223                 return expr;
1224         };
1225
1226         function is_assignable(expr) {
1227                 if (!exigent_mode) return true;
1228                 switch (expr[0]+"") {
1229                     case "dot":
1230                     case "sub":
1231                     case "new":
1232                     case "call":
1233                         return true;
1234                     case "name":
1235                         return expr[1] != "this";
1236                 }
1237         };
1238
1239         function maybe_assign(no_in) {
1240                 var left = maybe_conditional(no_in), val = S.token.value;
1241                 if (is("operator") && HOP(ASSIGNMENT, val)) {
1242                         if (is_assignable(left)) {
1243                                 next();
1244                                 return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
1245                         }
1246                         croak("Invalid assignment");
1247                 }
1248                 return left;
1249         };
1250
1251         var expression = maybe_embed_tokens(function(commas, no_in) {
1252                 if (arguments.length == 0)
1253                         commas = true;
1254                 var expr = maybe_assign(no_in);
1255                 if (commas && is("punc", ",")) {
1256                         next();
1257                         return as("seq", expr, expression(true, no_in));
1258                 }
1259                 return expr;
1260         });
1261
1262         function in_loop(cont) {
1263                 try {
1264                         ++S.in_loop;
1265                         return cont();
1266                 } finally {
1267                         --S.in_loop;
1268                 }
1269         };
1270
1271         return as("toplevel", (function(a){
1272                 while (!is("eof"))
1273                         a.push(statement());
1274                 return a;
1275         })([]));
1276
1277 };
1278
1279 /* -----[ Utilities ]----- */
1280
1281 function curry(f) {
1282         var args = slice(arguments, 1);
1283         return function() { return f.apply(this, args.concat(slice(arguments))); };
1284 };
1285
1286 function prog1(ret) {
1287         if (ret instanceof Function)
1288                 ret = ret();
1289         for (var i = 1, n = arguments.length; --n > 0; ++i)
1290                 arguments[i]();
1291         return ret;
1292 };
1293
1294 function array_to_hash(a) {
1295         var ret = {};
1296         for (var i = 0; i < a.length; ++i)
1297                 ret[a[i]] = true;
1298         return ret;
1299 };
1300
1301 function slice(a, start) {
1302         return Array.prototype.slice.call(a, start || 0);
1303 };
1304
1305 function characters(str) {
1306         return str.split("");
1307 };
1308
1309 function member(name, array) {
1310         for (var i = array.length; --i >= 0;)
1311                 if (array[i] === name)
1312                         return true;
1313         return false;
1314 };
1315
1316 function HOP(obj, prop) {
1317         return Object.prototype.hasOwnProperty.call(obj, prop);
1318 };
1319
1320 var warn = function() {};
1321
1322 /* -----[ Exports ]----- */
1323
1324 exports.tokenizer = tokenizer;
1325 exports.parse = parse;
1326 exports.slice = slice;
1327 exports.curry = curry;
1328 exports.member = member;
1329 exports.array_to_hash = array_to_hash;
1330 exports.PRECEDENCE = PRECEDENCE;
1331 exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
1332 exports.RESERVED_WORDS = RESERVED_WORDS;
1333 exports.KEYWORDS = KEYWORDS;
1334 exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
1335 exports.OPERATORS = OPERATORS;
1336 exports.is_alphanumeric_char = is_alphanumeric_char;
1337 exports.set_logger = function(logger) {
1338         warn = logger;
1339 };