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