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