UNPKG

58.9 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15 Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
16
17 Redistribution and use in source and binary forms, with or without
18 modification, are permitted provided that the following conditions
19 are met:
20
21 * Redistributions of source code must retain the above
22 copyright notice, this list of conditions and the following
23 disclaimer.
24
25 * Redistributions in binary form must reproduce the above
26 copyright notice, this list of conditions and the following
27 disclaimer in the documentation and/or other materials
28 provided with the distribution.
29
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
34 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
37 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
39 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
40 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 SUCH DAMAGE.
42
43 ***********************************************************************/
44
45"use strict";
46
47var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
48var KEYWORDS_ATOM = 'false null true';
49var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield'
50 + " " + KEYWORDS_ATOM + " " + KEYWORDS;
51var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
52
53KEYWORDS = makePredicate(KEYWORDS);
54RESERVED_WORDS = makePredicate(RESERVED_WORDS);
55KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION);
56KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
57
58var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
59
60var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
61var RE_OCT_NUMBER = /^0[0-7]+$/;
62
63var OPERATORS = makePredicate([
64 "in",
65 "instanceof",
66 "typeof",
67 "new",
68 "void",
69 "delete",
70 "++",
71 "--",
72 "+",
73 "-",
74 "!",
75 "~",
76 "&",
77 "|",
78 "^",
79 "*",
80 "/",
81 "%",
82 ">>",
83 "<<",
84 ">>>",
85 "<",
86 ">",
87 "<=",
88 ">=",
89 "==",
90 "===",
91 "!=",
92 "!==",
93 "?",
94 "=",
95 "+=",
96 "-=",
97 "/=",
98 "*=",
99 "%=",
100 ">>=",
101 "<<=",
102 ">>>=",
103 "|=",
104 "^=",
105 "&=",
106 "&&",
107 "||"
108]);
109
110var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF"));
111
112var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029"));
113
114var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:"));
115
116var PUNC_CHARS = makePredicate(characters("[]{}(),;:"));
117
118/* -----[ Tokenizer ]----- */
119
120// regexps adapted from http://xregexp.com/plugins/#unicode
121var UNICODE = {
122 letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
123 digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"),
124 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]"),
125 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]"),
126 connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
127};
128
129function is_letter(code) {
130 return (code >= 97 && code <= 122)
131 || (code >= 65 && code <= 90)
132 || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code)));
133}
134
135function is_surrogate_pair_head(code) {
136 return code >= 0xd800 && code <= 0xdbff;
137}
138
139function is_surrogate_pair_tail(code) {
140 return code >= 0xdc00 && code <= 0xdfff;
141}
142
143function is_digit(code) {
144 return code >= 48 && code <= 57;
145}
146
147function is_alphanumeric_char(code) {
148 return is_digit(code) || is_letter(code);
149}
150
151function is_unicode_digit(code) {
152 return UNICODE.digit.test(String.fromCharCode(code));
153}
154
155function is_unicode_combining_mark(ch) {
156 return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
157}
158
159function is_unicode_connector_punctuation(ch) {
160 return UNICODE.connector_punctuation.test(ch);
161}
162
163function is_identifier_start(code) {
164 return code == 36 || code == 95 || is_letter(code);
165}
166
167function is_identifier_char(ch) {
168 var code = ch.charCodeAt(0);
169 return is_identifier_start(code)
170 || is_digit(code)
171 || code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
172 || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
173 || is_unicode_combining_mark(ch)
174 || is_unicode_connector_punctuation(ch)
175 || is_unicode_digit(code)
176 ;
177}
178
179function is_identifier_string(str) {
180 return /^[a-z_$][a-z0-9_$]*$/i.test(str);
181}
182
183function parse_js_number(num) {
184 if (RE_HEX_NUMBER.test(num)) {
185 return parseInt(num.substr(2), 16);
186 } else if (RE_OCT_NUMBER.test(num)) {
187 return parseInt(num.substr(1), 8);
188 } else {
189 var val = parseFloat(num);
190 if (val == num) return val;
191 }
192}
193
194function JS_Parse_Error(message, filename, line, col, pos) {
195 this.message = message;
196 this.filename = filename;
197 this.line = line;
198 this.col = col;
199 this.pos = pos;
200}
201JS_Parse_Error.prototype = Object.create(Error.prototype);
202JS_Parse_Error.prototype.constructor = JS_Parse_Error;
203JS_Parse_Error.prototype.name = "SyntaxError";
204configure_error_stack(JS_Parse_Error);
205
206function js_error(message, filename, line, col, pos) {
207 throw new JS_Parse_Error(message, filename, line, col, pos);
208}
209
210function is_token(token, type, val) {
211 return token.type == type && (val == null || token.value == val);
212}
213
214var EX_EOF = {};
215
216function tokenizer($TEXT, filename, html5_comments, shebang) {
217
218 var S = {
219 text : $TEXT,
220 filename : filename,
221 pos : 0,
222 tokpos : 0,
223 line : 1,
224 tokline : 0,
225 col : 0,
226 tokcol : 0,
227 newline_before : false,
228 regex_allowed : false,
229 comments_before : [],
230 directives : {},
231 directive_stack : []
232 };
233 var prev_was_dot = false;
234
235 function peek() {
236 return S.text.charAt(S.pos);
237 }
238
239 function next(signal_eof, in_string) {
240 var ch = S.text.charAt(S.pos++);
241 if (signal_eof && !ch)
242 throw EX_EOF;
243 if (NEWLINE_CHARS[ch]) {
244 S.newline_before = S.newline_before || !in_string;
245 ++S.line;
246 S.col = 0;
247 if (!in_string && ch == "\r" && peek() == "\n") {
248 // treat a \r\n sequence as a single \n
249 ++S.pos;
250 ch = "\n";
251 }
252 } else {
253 ++S.col;
254 }
255 return ch;
256 }
257
258 function forward(i) {
259 while (i-- > 0) next();
260 }
261
262 function looking_at(str) {
263 return S.text.substr(S.pos, str.length) == str;
264 }
265
266 function find_eol() {
267 var text = S.text;
268 for (var i = S.pos; i < S.text.length; ++i) {
269 if (NEWLINE_CHARS[text[i]]) return i;
270 }
271 return -1;
272 }
273
274 function find(what, signal_eof) {
275 var pos = S.text.indexOf(what, S.pos);
276 if (signal_eof && pos == -1) throw EX_EOF;
277 return pos;
278 }
279
280 function start_token() {
281 S.tokline = S.line;
282 S.tokcol = S.col;
283 S.tokpos = S.pos;
284 }
285
286 function token(type, value, is_comment) {
287 S.regex_allowed = type == "operator" && !UNARY_POSTFIX[value]
288 || type == "keyword" && KEYWORDS_BEFORE_EXPRESSION[value]
289 || type == "punc" && PUNC_BEFORE_EXPRESSION[value];
290 if (type == "punc" && value == ".") prev_was_dot = true;
291 else if (!is_comment) prev_was_dot = false;
292 var ret = {
293 type : type,
294 value : value,
295 line : S.tokline,
296 col : S.tokcol,
297 pos : S.tokpos,
298 endline : S.line,
299 endcol : S.col,
300 endpos : S.pos,
301 nlb : S.newline_before,
302 file : filename
303 };
304 if (/^(?:num|string|regexp)$/i.test(type)) {
305 ret.raw = $TEXT.substring(ret.pos, ret.endpos);
306 }
307 if (!is_comment) {
308 ret.comments_before = S.comments_before;
309 ret.comments_after = S.comments_before = [];
310 }
311 S.newline_before = false;
312 return new AST_Token(ret);
313 }
314
315 function skip_whitespace() {
316 while (WHITESPACE_CHARS[peek()])
317 next();
318 }
319
320 function read_while(pred) {
321 var ret = "", ch, i = 0;
322 while ((ch = peek()) && pred(ch, i++))
323 ret += next();
324 return ret;
325 }
326
327 function parse_error(err) {
328 js_error(err, filename, S.tokline, S.tokcol, S.tokpos);
329 }
330
331 function read_num(prefix) {
332 var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
333 var num = read_while(function(ch, i) {
334 var code = ch.charCodeAt(0);
335 switch (code) {
336 case 120: case 88: // xX
337 return has_x ? false : (has_x = true);
338 case 101: case 69: // eE
339 return has_x ? true : has_e ? false : (has_e = after_e = true);
340 case 45: // -
341 return after_e || (i == 0 && !prefix);
342 case 43: // +
343 return after_e;
344 case (after_e = false, 46): // .
345 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
346 }
347 return is_alphanumeric_char(code);
348 });
349 if (prefix) num = prefix + num;
350 if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
351 parse_error("Legacy octal literals are not allowed in strict mode");
352 }
353 var valid = parse_js_number(num);
354 if (!isNaN(valid)) return token("num", valid);
355 parse_error("Invalid syntax: " + num);
356 }
357
358 function read_escaped_char(in_string) {
359 var ch = next(true, in_string);
360 switch (ch.charCodeAt(0)) {
361 case 110 : return "\n";
362 case 114 : return "\r";
363 case 116 : return "\t";
364 case 98 : return "\b";
365 case 118 : return "\u000b"; // \v
366 case 102 : return "\f";
367 case 120 : return String.fromCharCode(hex_bytes(2)); // \x
368 case 117 : return String.fromCharCode(hex_bytes(4)); // \u
369 case 10 : return ""; // newline
370 case 13 : // \r
371 if (peek() == "\n") { // DOS newline
372 next(true, in_string);
373 return "";
374 }
375 }
376 if (ch >= "0" && ch <= "7")
377 return read_octal_escape_sequence(ch);
378 return ch;
379 }
380
381 function read_octal_escape_sequence(ch) {
382 // Read
383 var p = peek();
384 if (p >= "0" && p <= "7") {
385 ch += next(true);
386 if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7")
387 ch += next(true);
388 }
389
390 // Parse
391 if (ch === "0") return "\0";
392 if (ch.length > 0 && next_token.has_directive("use strict"))
393 parse_error("Legacy octal escape sequences are not allowed in strict mode");
394 return String.fromCharCode(parseInt(ch, 8));
395 }
396
397 function hex_bytes(n) {
398 var num = 0;
399 for (; n > 0; --n) {
400 var digit = parseInt(next(true), 16);
401 if (isNaN(digit))
402 parse_error("Invalid hex-character pattern in string");
403 num = (num << 4) | digit;
404 }
405 return num;
406 }
407
408 var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
409 var quote = next(), ret = "";
410 for (;;) {
411 var ch = next(true, true);
412 if (ch == "\\") ch = read_escaped_char(true);
413 else if (NEWLINE_CHARS[ch]) parse_error("Unterminated string constant");
414 else if (ch == quote) break;
415 ret += ch;
416 }
417 var tok = token("string", ret);
418 tok.quote = quote_char;
419 return tok;
420 });
421
422 function skip_line_comment(type) {
423 var regex_allowed = S.regex_allowed;
424 var i = find_eol(), ret;
425 if (i == -1) {
426 ret = S.text.substr(S.pos);
427 S.pos = S.text.length;
428 } else {
429 ret = S.text.substring(S.pos, i);
430 S.pos = i;
431 }
432 S.col = S.tokcol + (S.pos - S.tokpos);
433 S.comments_before.push(token(type, ret, true));
434 S.regex_allowed = regex_allowed;
435 return next_token;
436 }
437
438 var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function() {
439 var regex_allowed = S.regex_allowed;
440 var i = find("*/", true);
441 var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
442 // update stream position
443 forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2);
444 S.comments_before.push(token("comment2", text, true));
445 S.regex_allowed = regex_allowed;
446 return next_token;
447 });
448
449 function read_name() {
450 var backslash = false, name = "", ch, escaped = false, hex;
451 while ((ch = peek()) != null) {
452 if (!backslash) {
453 if (ch == "\\") escaped = backslash = true, next();
454 else if (is_identifier_char(ch)) name += next();
455 else break;
456 } else {
457 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
458 ch = read_escaped_char();
459 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
460 name += ch;
461 backslash = false;
462 }
463 }
464 if (KEYWORDS[name] && escaped) {
465 hex = name.charCodeAt(0).toString(16).toUpperCase();
466 name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
467 }
468 return name;
469 }
470
471 var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
472 var prev_backslash = false, ch, in_class = false;
473 while ((ch = next(true))) if (NEWLINE_CHARS[ch]) {
474 parse_error("Unexpected line terminator");
475 } else if (prev_backslash) {
476 source += "\\" + ch;
477 prev_backslash = false;
478 } else if (ch == "[") {
479 in_class = true;
480 source += ch;
481 } else if (ch == "]" && in_class) {
482 in_class = false;
483 source += ch;
484 } else if (ch == "/" && !in_class) {
485 break;
486 } else if (ch == "\\") {
487 prev_backslash = true;
488 } else {
489 source += ch;
490 }
491 var mods = read_name();
492 try {
493 var regexp = new RegExp(source, mods);
494 regexp.raw_source = source;
495 return token("regexp", regexp);
496 } catch (e) {
497 parse_error(e.message);
498 }
499 });
500
501 function read_operator(prefix) {
502 function grow(op) {
503 if (!peek()) return op;
504 var bigger = op + peek();
505 if (OPERATORS[bigger]) {
506 next();
507 return grow(bigger);
508 } else {
509 return op;
510 }
511 }
512 return token("operator", grow(prefix || next()));
513 }
514
515 function handle_slash() {
516 next();
517 switch (peek()) {
518 case "/":
519 next();
520 return skip_line_comment("comment1");
521 case "*":
522 next();
523 return skip_multiline_comment();
524 }
525 return S.regex_allowed ? read_regexp("") : read_operator("/");
526 }
527
528 function handle_dot() {
529 next();
530 return is_digit(peek().charCodeAt(0)) ? read_num(".") : token("punc", ".");
531 }
532
533 function read_word() {
534 var word = read_name();
535 if (prev_was_dot) return token("name", word);
536 return KEYWORDS_ATOM[word] ? token("atom", word)
537 : !KEYWORDS[word] ? token("name", word)
538 : OPERATORS[word] ? token("operator", word)
539 : token("keyword", word);
540 }
541
542 function with_eof_error(eof_error, cont) {
543 return function(x) {
544 try {
545 return cont(x);
546 } catch (ex) {
547 if (ex === EX_EOF) parse_error(eof_error);
548 else throw ex;
549 }
550 };
551 }
552
553 function next_token(force_regexp) {
554 if (force_regexp != null)
555 return read_regexp(force_regexp);
556 if (shebang && S.pos == 0 && looking_at("#!")) {
557 start_token();
558 forward(2);
559 skip_line_comment("comment5");
560 }
561 for (;;) {
562 skip_whitespace();
563 start_token();
564 if (html5_comments) {
565 if (looking_at("<!--")) {
566 forward(4);
567 skip_line_comment("comment3");
568 continue;
569 }
570 if (looking_at("-->") && S.newline_before) {
571 forward(3);
572 skip_line_comment("comment4");
573 continue;
574 }
575 }
576 var ch = peek();
577 if (!ch) return token("eof");
578 var code = ch.charCodeAt(0);
579 switch (code) {
580 case 34: case 39: return read_string(ch);
581 case 46: return handle_dot();
582 case 47:
583 var tok = handle_slash();
584 if (tok === next_token) continue;
585 return tok;
586 }
587 if (is_digit(code)) return read_num();
588 if (PUNC_CHARS[ch]) return token("punc", next());
589 if (OPERATOR_CHARS[ch]) return read_operator();
590 if (code == 92 || is_identifier_start(code)) return read_word();
591 break;
592 }
593 parse_error("Unexpected character '" + ch + "'");
594 }
595
596 next_token.context = function(nc) {
597 if (nc) S = nc;
598 return S;
599 };
600
601 next_token.add_directive = function(directive) {
602 S.directive_stack[S.directive_stack.length - 1].push(directive);
603 if (S.directives[directive]) S.directives[directive]++;
604 else S.directives[directive] = 1;
605 }
606
607 next_token.push_directives_stack = function() {
608 S.directive_stack.push([]);
609 }
610
611 next_token.pop_directives_stack = function() {
612 var directives = S.directive_stack.pop();
613 for (var i = directives.length; --i >= 0;) {
614 S.directives[directives[i]]--;
615 }
616 }
617
618 next_token.has_directive = function(directive) {
619 return S.directives[directive] > 0;
620 }
621
622 return next_token;
623}
624
625/* -----[ Parser (constants) ]----- */
626
627var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");
628
629var UNARY_POSTFIX = makePredicate("-- ++");
630
631var ASSIGNMENT = makePredicate("= += -= /= *= %= >>= <<= >>>= |= ^= &=");
632
633var PRECEDENCE = function(a, ret) {
634 for (var i = 0; i < a.length;) {
635 var b = a[i++];
636 for (var j = 0; j < b.length; j++) {
637 ret[b[j]] = i;
638 }
639 }
640 return ret;
641}([
642 ["||"],
643 ["&&"],
644 ["|"],
645 ["^"],
646 ["&"],
647 ["==", "===", "!=", "!=="],
648 ["<", ">", "<=", ">=", "in", "instanceof"],
649 [">>", "<<", ">>>"],
650 ["+", "-"],
651 ["*", "/", "%"]
652], {});
653
654var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
655
656/* -----[ Parser ]----- */
657
658function parse($TEXT, options) {
659 options = defaults(options, {
660 bare_returns : false,
661 expression : false,
662 filename : null,
663 html5_comments : true,
664 shebang : true,
665 strict : false,
666 toplevel : null,
667 }, true);
668
669 var S = {
670 input : typeof $TEXT == "string"
671 ? tokenizer($TEXT, options.filename, options.html5_comments, options.shebang)
672 : $TEXT,
673 token : null,
674 prev : null,
675 peeked : null,
676 in_function : 0,
677 in_directives : true,
678 in_loop : 0,
679 labels : []
680 };
681
682 S.token = next();
683
684 function is(type, value) {
685 return is_token(S.token, type, value);
686 }
687
688 function peek() {
689 return S.peeked || (S.peeked = S.input());
690 }
691
692 function next() {
693 S.prev = S.token;
694 if (S.peeked) {
695 S.token = S.peeked;
696 S.peeked = null;
697 } else {
698 S.token = S.input();
699 }
700 S.in_directives = S.in_directives && (
701 S.token.type == "string" || is("punc", ";")
702 );
703 return S.token;
704 }
705
706 function prev() {
707 return S.prev;
708 }
709
710 function croak(msg, line, col, pos) {
711 var ctx = S.input.context();
712 js_error(msg,
713 ctx.filename,
714 line != null ? line : ctx.tokline,
715 col != null ? col : ctx.tokcol,
716 pos != null ? pos : ctx.tokpos);
717 }
718
719 function token_error(token, msg) {
720 croak(msg, token.line, token.col);
721 }
722
723 function token_to_string(type, value) {
724 return type + (value === undefined ? "" : " «" + value + "»");
725 }
726
727 function unexpected(token) {
728 if (token == null) token = S.token;
729 token_error(token, "Unexpected token: " + token_to_string(token.type, token.value));
730 }
731
732 function expect_token(type, val) {
733 if (is(type, val)) return next();
734 token_error(S.token, "Unexpected token: " + token_to_string(S.token.type, S.token.value) + ", expected: " + token_to_string(type, val));
735 }
736
737 function expect(punc) {
738 return expect_token("punc", punc);
739 }
740
741 function has_newline_before(token) {
742 return token.nlb || !all(token.comments_before, function(comment) {
743 return !comment.nlb;
744 });
745 }
746
747 function can_insert_semicolon() {
748 return !options.strict
749 && (is("eof") || is("punc", "}") || has_newline_before(S.token));
750 }
751
752 function semicolon(optional) {
753 if (is("punc", ";")) next();
754 else if (!optional && !can_insert_semicolon()) expect_token("punc", ";");
755 }
756
757 function parenthesised() {
758 expect("(");
759 var exp = expression(true);
760 expect(")");
761 return exp;
762 }
763
764 function embed_tokens(parser) {
765 return function() {
766 var start = S.token;
767 var expr = parser.apply(null, arguments);
768 var end = prev();
769 expr.start = start;
770 expr.end = end;
771 return expr;
772 };
773 }
774
775 function handle_regexp() {
776 if (is("operator", "/") || is("operator", "/=")) {
777 S.peeked = null;
778 S.token = S.input(S.token.value.substr(1)); // force regexp
779 }
780 }
781
782 var statement = embed_tokens(function(strict_defun) {
783 handle_regexp();
784 switch (S.token.type) {
785 case "string":
786 var dir = S.in_directives;
787 var body = expression(true);
788 if (dir) {
789 if (body instanceof AST_String) {
790 var value = body.start.raw.slice(1, -1);
791 S.input.add_directive(value);
792 body.value = value;
793 } else {
794 S.in_directives = dir = false;
795 }
796 }
797 semicolon();
798 return dir ? new AST_Directive(body) : new AST_SimpleStatement({ body: body });
799 case "num":
800 case "regexp":
801 case "operator":
802 case "atom":
803 return simple_statement();
804
805 case "name":
806 return is_token(peek(), "punc", ":")
807 ? labeled_statement()
808 : simple_statement();
809
810 case "punc":
811 switch (S.token.value) {
812 case "{":
813 return new AST_BlockStatement({
814 start : S.token,
815 body : block_(),
816 end : prev()
817 });
818 case "[":
819 case "(":
820 return simple_statement();
821 case ";":
822 S.in_directives = false;
823 next();
824 return new AST_EmptyStatement();
825 default:
826 unexpected();
827 }
828
829 case "keyword":
830 switch (S.token.value) {
831 case "break":
832 next();
833 return break_cont(AST_Break);
834
835 case "continue":
836 next();
837 return break_cont(AST_Continue);
838
839 case "debugger":
840 next();
841 semicolon();
842 return new AST_Debugger();
843
844 case "do":
845 next();
846 var body = in_loop(statement);
847 expect_token("keyword", "while");
848 var condition = parenthesised();
849 semicolon(true);
850 return new AST_Do({
851 body : body,
852 condition : condition
853 });
854
855 case "while":
856 next();
857 return new AST_While({
858 condition : parenthesised(),
859 body : in_loop(statement)
860 });
861
862 case "for":
863 next();
864 return for_();
865
866 case "function":
867 if (!strict_defun && S.input.has_directive("use strict")) {
868 croak("In strict mode code, functions can only be declared at top level or immediately within another function.");
869 }
870 next();
871 return function_(AST_Defun);
872
873 case "if":
874 next();
875 return if_();
876
877 case "return":
878 if (S.in_function == 0 && !options.bare_returns)
879 croak("'return' outside of function");
880 next();
881 var value = null;
882 if (is("punc", ";")) {
883 next();
884 } else if (!can_insert_semicolon()) {
885 value = expression(true);
886 semicolon();
887 }
888 return new AST_Return({
889 value: value
890 });
891
892 case "switch":
893 next();
894 return new AST_Switch({
895 expression : parenthesised(),
896 body : in_loop(switch_body_)
897 });
898
899 case "throw":
900 next();
901 if (has_newline_before(S.token))
902 croak("Illegal newline after 'throw'");
903 var value = expression(true);
904 semicolon();
905 return new AST_Throw({
906 value: value
907 });
908
909 case "try":
910 next();
911 return try_();
912
913 case "var":
914 next();
915 var node = var_();
916 semicolon();
917 return node;
918
919 case "with":
920 if (S.input.has_directive("use strict")) {
921 croak("Strict mode may not include a with statement");
922 }
923 next();
924 return new AST_With({
925 expression : parenthesised(),
926 body : statement()
927 });
928 }
929 }
930 unexpected();
931 });
932
933 function labeled_statement() {
934 var label = as_symbol(AST_Label);
935 if (!all(S.labels, function(l) {
936 return l.name != label.name;
937 })) {
938 // ECMA-262, 12.12: An ECMAScript program is considered
939 // syntactically incorrect if it contains a
940 // LabelledStatement that is enclosed by a
941 // LabelledStatement with the same Identifier as label.
942 croak("Label " + label.name + " defined twice");
943 }
944 expect(":");
945 S.labels.push(label);
946 var stat = statement();
947 S.labels.pop();
948 if (!(stat instanceof AST_IterationStatement)) {
949 // check for `continue` that refers to this label.
950 // those should be reported as syntax errors.
951 // https://github.com/mishoo/UglifyJS2/issues/287
952 label.references.forEach(function(ref) {
953 if (ref instanceof AST_Continue) {
954 ref = ref.label.start;
955 croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
956 ref.line, ref.col, ref.pos);
957 }
958 });
959 }
960 return new AST_LabeledStatement({ body: stat, label: label });
961 }
962
963 function simple_statement() {
964 var body = expression(true);
965 semicolon();
966 return new AST_SimpleStatement({ body: body });
967 }
968
969 function break_cont(type) {
970 var label = null, ldef;
971 if (!can_insert_semicolon()) {
972 label = as_symbol(AST_LabelRef, true);
973 }
974 if (label != null) {
975 ldef = find_if(function(l) {
976 return l.name == label.name;
977 }, S.labels);
978 if (!ldef) croak("Undefined label " + label.name);
979 label.thedef = ldef;
980 } else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
981 semicolon();
982 var stat = new type({ label: label });
983 if (ldef) ldef.references.push(stat);
984 return stat;
985 }
986
987 function for_() {
988 expect("(");
989 var init = null;
990 if (!is("punc", ";")) {
991 init = is("keyword", "var")
992 ? (next(), var_(true))
993 : expression(true, true);
994 if (is("operator", "in")) {
995 if (init instanceof AST_Var) {
996 if (init.definitions.length > 1)
997 croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
998 } else if (!is_assignable(init)) {
999 croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
1000 }
1001 next();
1002 return for_in(init);
1003 }
1004 }
1005 return regular_for(init);
1006 }
1007
1008 function regular_for(init) {
1009 expect(";");
1010 var test = is("punc", ";") ? null : expression(true);
1011 expect(";");
1012 var step = is("punc", ")") ? null : expression(true);
1013 expect(")");
1014 return new AST_For({
1015 init : init,
1016 condition : test,
1017 step : step,
1018 body : in_loop(statement)
1019 });
1020 }
1021
1022 function for_in(init) {
1023 var obj = expression(true);
1024 expect(")");
1025 return new AST_ForIn({
1026 init : init,
1027 object : obj,
1028 body : in_loop(statement)
1029 });
1030 }
1031
1032 var function_ = function(ctor) {
1033 var in_statement = ctor === AST_Defun;
1034 var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null;
1035 if (in_statement && !name)
1036 expect_token("name");
1037 if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
1038 unexpected(prev());
1039 expect("(");
1040 var argnames = [];
1041 for (var first = true; !is("punc", ")");) {
1042 if (first) first = false; else expect(",");
1043 argnames.push(as_symbol(AST_SymbolFunarg));
1044 }
1045 next();
1046 var loop = S.in_loop;
1047 var labels = S.labels;
1048 ++S.in_function;
1049 S.in_directives = true;
1050 S.input.push_directives_stack();
1051 S.in_loop = 0;
1052 S.labels = [];
1053 var body = block_(true);
1054 if (S.input.has_directive("use strict")) {
1055 if (name) strict_verify_symbol(name);
1056 argnames.forEach(strict_verify_symbol);
1057 }
1058 S.input.pop_directives_stack();
1059 --S.in_function;
1060 S.in_loop = loop;
1061 S.labels = labels;
1062 return new ctor({
1063 name: name,
1064 argnames: argnames,
1065 body: body
1066 });
1067 };
1068
1069 function if_() {
1070 var cond = parenthesised(), body = statement(), belse = null;
1071 if (is("keyword", "else")) {
1072 next();
1073 belse = statement();
1074 }
1075 return new AST_If({
1076 condition : cond,
1077 body : body,
1078 alternative : belse
1079 });
1080 }
1081
1082 function block_(strict_defun) {
1083 expect("{");
1084 var a = [];
1085 while (!is("punc", "}")) {
1086 if (is("eof")) expect_token("punc", "}");
1087 a.push(statement(strict_defun));
1088 }
1089 next();
1090 return a;
1091 }
1092
1093 function switch_body_() {
1094 expect("{");
1095 var a = [], cur = null, branch = null, tmp;
1096 while (!is("punc", "}")) {
1097 if (is("eof")) expect_token("punc", "}");
1098 if (is("keyword", "case")) {
1099 if (branch) branch.end = prev();
1100 cur = [];
1101 branch = new AST_Case({
1102 start : (tmp = S.token, next(), tmp),
1103 expression : expression(true),
1104 body : cur
1105 });
1106 a.push(branch);
1107 expect(":");
1108 } else if (is("keyword", "default")) {
1109 if (branch) branch.end = prev();
1110 cur = [];
1111 branch = new AST_Default({
1112 start : (tmp = S.token, next(), expect(":"), tmp),
1113 body : cur
1114 });
1115 a.push(branch);
1116 } else {
1117 if (!cur) unexpected();
1118 cur.push(statement());
1119 }
1120 }
1121 if (branch) branch.end = prev();
1122 next();
1123 return a;
1124 }
1125
1126 function try_() {
1127 var body = block_(), bcatch = null, bfinally = null;
1128 if (is("keyword", "catch")) {
1129 var start = S.token;
1130 next();
1131 expect("(");
1132 var name = as_symbol(AST_SymbolCatch);
1133 expect(")");
1134 bcatch = new AST_Catch({
1135 start : start,
1136 argname : name,
1137 body : block_(),
1138 end : prev()
1139 });
1140 }
1141 if (is("keyword", "finally")) {
1142 var start = S.token;
1143 next();
1144 bfinally = new AST_Finally({
1145 start : start,
1146 body : block_(),
1147 end : prev()
1148 });
1149 }
1150 if (!bcatch && !bfinally)
1151 croak("Missing catch/finally blocks");
1152 return new AST_Try({
1153 body : body,
1154 bcatch : bcatch,
1155 bfinally : bfinally
1156 });
1157 }
1158
1159 function vardefs(no_in) {
1160 var a = [];
1161 for (;;) {
1162 a.push(new AST_VarDef({
1163 start : S.token,
1164 name : as_symbol(AST_SymbolVar),
1165 value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
1166 end : prev()
1167 }));
1168 if (!is("punc", ","))
1169 break;
1170 next();
1171 }
1172 return a;
1173 }
1174
1175 var var_ = function(no_in) {
1176 return new AST_Var({
1177 start : prev(),
1178 definitions : vardefs(no_in),
1179 end : prev()
1180 });
1181 };
1182
1183 var new_ = function(allow_calls) {
1184 var start = S.token;
1185 expect_token("operator", "new");
1186 var newexp = expr_atom(false), args;
1187 if (is("punc", "(")) {
1188 next();
1189 args = expr_list(")");
1190 } else {
1191 args = [];
1192 }
1193 var call = new AST_New({
1194 start : start,
1195 expression : newexp,
1196 args : args,
1197 end : prev()
1198 });
1199 mark_pure(call);
1200 return subscripts(call, allow_calls);
1201 };
1202
1203 function as_atom_node() {
1204 var tok = S.token, ret;
1205 switch (tok.type) {
1206 case "name":
1207 ret = _make_symbol(AST_SymbolRef);
1208 break;
1209 case "num":
1210 ret = new AST_Number({ start: tok, end: tok, value: tok.value });
1211 break;
1212 case "string":
1213 ret = new AST_String({
1214 start : tok,
1215 end : tok,
1216 value : tok.value,
1217 quote : tok.quote
1218 });
1219 break;
1220 case "regexp":
1221 ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
1222 break;
1223 case "atom":
1224 switch (tok.value) {
1225 case "false":
1226 ret = new AST_False({ start: tok, end: tok });
1227 break;
1228 case "true":
1229 ret = new AST_True({ start: tok, end: tok });
1230 break;
1231 case "null":
1232 ret = new AST_Null({ start: tok, end: tok });
1233 break;
1234 }
1235 break;
1236 }
1237 next();
1238 return ret;
1239 }
1240
1241 var expr_atom = function(allow_calls) {
1242 if (is("operator", "new")) {
1243 return new_(allow_calls);
1244 }
1245 var start = S.token;
1246 if (is("punc")) {
1247 switch (start.value) {
1248 case "(":
1249 next();
1250 var ex = expression(true);
1251 var len = start.comments_before.length;
1252 [].unshift.apply(ex.start.comments_before, start.comments_before);
1253 start.comments_before.length = 0;
1254 start.comments_before = ex.start.comments_before;
1255 start.comments_before_length = len;
1256 if (len == 0 && start.comments_before.length > 0) {
1257 var comment = start.comments_before[0];
1258 if (!comment.nlb) {
1259 comment.nlb = start.nlb;
1260 start.nlb = false;
1261 }
1262 }
1263 start.comments_after = ex.start.comments_after;
1264 ex.start = start;
1265 expect(")");
1266 var end = prev();
1267 end.comments_before = ex.end.comments_before;
1268 [].push.apply(ex.end.comments_after, end.comments_after);
1269 end.comments_after.length = 0;
1270 end.comments_after = ex.end.comments_after;
1271 ex.end = end;
1272 if (ex instanceof AST_Call) mark_pure(ex);
1273 return subscripts(ex, allow_calls);
1274 case "[":
1275 return subscripts(array_(), allow_calls);
1276 case "{":
1277 return subscripts(object_(), allow_calls);
1278 }
1279 unexpected();
1280 }
1281 if (is("keyword", "function")) {
1282 next();
1283 var func = function_(AST_Function);
1284 func.start = start;
1285 func.end = prev();
1286 return subscripts(func, allow_calls);
1287 }
1288 if (ATOMIC_START_TOKEN[S.token.type]) {
1289 return subscripts(as_atom_node(), allow_calls);
1290 }
1291 unexpected();
1292 };
1293
1294 function expr_list(closing, allow_trailing_comma, allow_empty) {
1295 var first = true, a = [];
1296 while (!is("punc", closing)) {
1297 if (first) first = false; else expect(",");
1298 if (allow_trailing_comma && is("punc", closing)) break;
1299 if (is("punc", ",") && allow_empty) {
1300 a.push(new AST_Hole({ start: S.token, end: S.token }));
1301 } else {
1302 a.push(expression(false));
1303 }
1304 }
1305 next();
1306 return a;
1307 }
1308
1309 var array_ = embed_tokens(function() {
1310 expect("[");
1311 return new AST_Array({
1312 elements: expr_list("]", !options.strict, true)
1313 });
1314 });
1315
1316 var create_accessor = embed_tokens(function() {
1317 return function_(AST_Accessor);
1318 });
1319
1320 var object_ = embed_tokens(function() {
1321 expect("{");
1322 var first = true, a = [];
1323 while (!is("punc", "}")) {
1324 if (first) first = false; else expect(",");
1325 if (!options.strict && is("punc", "}"))
1326 // allow trailing comma
1327 break;
1328 var start = S.token;
1329 var type = start.type;
1330 var name = as_property_name();
1331 if (type == "name" && !is("punc", ":")) {
1332 var key = new AST_SymbolAccessor({
1333 start: S.token,
1334 name: "" + as_property_name(),
1335 end: prev()
1336 });
1337 if (name == "get") {
1338 a.push(new AST_ObjectGetter({
1339 start : start,
1340 key : key,
1341 value : create_accessor(),
1342 end : prev()
1343 }));
1344 continue;
1345 }
1346 if (name == "set") {
1347 a.push(new AST_ObjectSetter({
1348 start : start,
1349 key : key,
1350 value : create_accessor(),
1351 end : prev()
1352 }));
1353 continue;
1354 }
1355 }
1356 expect(":");
1357 a.push(new AST_ObjectKeyVal({
1358 start : start,
1359 quote : start.quote,
1360 key : "" + name,
1361 value : expression(false),
1362 end : prev()
1363 }));
1364 }
1365 next();
1366 return new AST_Object({ properties: a });
1367 });
1368
1369 function as_property_name() {
1370 var tmp = S.token;
1371 switch (tmp.type) {
1372 case "operator":
1373 if (!KEYWORDS[tmp.value]) unexpected();
1374 case "num":
1375 case "string":
1376 case "name":
1377 case "keyword":
1378 case "atom":
1379 next();
1380 return tmp.value;
1381 default:
1382 unexpected();
1383 }
1384 }
1385
1386 function as_name() {
1387 if (!is("name")) expect_token("name");
1388 var name = S.token.value;
1389 next();
1390 return name;
1391 }
1392
1393 function _make_symbol(type) {
1394 var name = S.token.value;
1395 return new (name == "this" ? AST_This : type)({
1396 name : String(name),
1397 start : S.token,
1398 end : S.token
1399 });
1400 }
1401
1402 function strict_verify_symbol(sym) {
1403 if (sym.name == "arguments" || sym.name == "eval")
1404 croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
1405 }
1406
1407 function as_symbol(type, noerror) {
1408 if (!is("name")) {
1409 if (!noerror) croak("Name expected");
1410 return null;
1411 }
1412 var sym = _make_symbol(type);
1413 if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
1414 strict_verify_symbol(sym);
1415 }
1416 next();
1417 return sym;
1418 }
1419
1420 function mark_pure(call) {
1421 var start = call.start;
1422 var comments = start.comments_before;
1423 var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
1424 while (--i >= 0) {
1425 var comment = comments[i];
1426 if (/[@#]__PURE__/.test(comment.value)) {
1427 call.pure = comment;
1428 break;
1429 }
1430 }
1431 }
1432
1433 var subscripts = function(expr, allow_calls) {
1434 var start = expr.start;
1435 if (is("punc", ".")) {
1436 next();
1437 return subscripts(new AST_Dot({
1438 start : start,
1439 expression : expr,
1440 property : as_name(),
1441 end : prev()
1442 }), allow_calls);
1443 }
1444 if (is("punc", "[")) {
1445 next();
1446 var prop = expression(true);
1447 expect("]");
1448 return subscripts(new AST_Sub({
1449 start : start,
1450 expression : expr,
1451 property : prop,
1452 end : prev()
1453 }), allow_calls);
1454 }
1455 if (allow_calls && is("punc", "(")) {
1456 next();
1457 var call = new AST_Call({
1458 start : start,
1459 expression : expr,
1460 args : expr_list(")"),
1461 end : prev()
1462 });
1463 mark_pure(call);
1464 return subscripts(call, true);
1465 }
1466 return expr;
1467 };
1468
1469 var maybe_unary = function(allow_calls) {
1470 var start = S.token;
1471 if (is("operator") && UNARY_PREFIX[start.value]) {
1472 next();
1473 handle_regexp();
1474 var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls));
1475 ex.start = start;
1476 ex.end = prev();
1477 return ex;
1478 }
1479 var val = expr_atom(allow_calls);
1480 while (is("operator") && UNARY_POSTFIX[S.token.value] && !has_newline_before(S.token)) {
1481 val = make_unary(AST_UnaryPostfix, S.token, val);
1482 val.start = start;
1483 val.end = S.token;
1484 next();
1485 }
1486 return val;
1487 };
1488
1489 function make_unary(ctor, token, expr) {
1490 var op = token.value;
1491 switch (op) {
1492 case "++":
1493 case "--":
1494 if (!is_assignable(expr))
1495 croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
1496 break;
1497 case "delete":
1498 if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
1499 croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
1500 break;
1501 }
1502 return new ctor({ operator: op, expression: expr });
1503 }
1504
1505 var expr_op = function(left, min_prec, no_in) {
1506 var op = is("operator") ? S.token.value : null;
1507 if (op == "in" && no_in) op = null;
1508 var prec = op != null ? PRECEDENCE[op] : null;
1509 if (prec != null && prec > min_prec) {
1510 next();
1511 var right = expr_op(maybe_unary(true), prec, no_in);
1512 return expr_op(new AST_Binary({
1513 start : left.start,
1514 left : left,
1515 operator : op,
1516 right : right,
1517 end : right.end
1518 }), min_prec, no_in);
1519 }
1520 return left;
1521 };
1522
1523 function expr_ops(no_in) {
1524 return expr_op(maybe_unary(true), 0, no_in);
1525 }
1526
1527 var maybe_conditional = function(no_in) {
1528 var start = S.token;
1529 var expr = expr_ops(no_in);
1530 if (is("operator", "?")) {
1531 next();
1532 var yes = expression(false);
1533 expect(":");
1534 return new AST_Conditional({
1535 start : start,
1536 condition : expr,
1537 consequent : yes,
1538 alternative : expression(false, no_in),
1539 end : prev()
1540 });
1541 }
1542 return expr;
1543 };
1544
1545 function is_assignable(expr) {
1546 return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
1547 }
1548
1549 var maybe_assign = function(no_in) {
1550 var start = S.token;
1551 var left = maybe_conditional(no_in), val = S.token.value;
1552 if (is("operator") && ASSIGNMENT[val]) {
1553 if (is_assignable(left)) {
1554 next();
1555 return new AST_Assign({
1556 start : start,
1557 left : left,
1558 operator : val,
1559 right : maybe_assign(no_in),
1560 end : prev()
1561 });
1562 }
1563 croak("Invalid assignment");
1564 }
1565 return left;
1566 };
1567
1568 var expression = function(commas, no_in) {
1569 var start = S.token;
1570 var exprs = [];
1571 while (true) {
1572 exprs.push(maybe_assign(no_in));
1573 if (!commas || !is("punc", ",")) break;
1574 next();
1575 commas = true;
1576 }
1577 return exprs.length == 1 ? exprs[0] : new AST_Sequence({
1578 start : start,
1579 expressions : exprs,
1580 end : peek()
1581 });
1582 };
1583
1584 function in_loop(cont) {
1585 ++S.in_loop;
1586 var ret = cont();
1587 --S.in_loop;
1588 return ret;
1589 }
1590
1591 if (options.expression) {
1592 handle_regexp();
1593 return expression(true);
1594 }
1595
1596 return function() {
1597 var start = S.token;
1598 var body = [];
1599 S.input.push_directives_stack();
1600 while (!is("eof"))
1601 body.push(statement(true));
1602 S.input.pop_directives_stack();
1603 var end = prev();
1604 var toplevel = options.toplevel;
1605 if (toplevel) {
1606 toplevel.body = toplevel.body.concat(body);
1607 toplevel.end = end;
1608 } else {
1609 toplevel = new AST_Toplevel({ start: start, body: body, end: end });
1610 }
1611 return toplevel;
1612 }();
1613}