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