UglifyJS/lib/parse.js

1669 lines
60 KiB
JavaScript
Raw Normal View History

2012-08-22 18:28:59 +00:00
/***********************************************************************
A JavaScript tokenizer / parser / beautifier / compressor.
https://github.com/mishoo/UglifyJS2
2014-04-15 12:57:14 +00:00
Edited for parsing ColaScript.
2012-08-22 18:28:59 +00:00
-------------------------------- (C) ---------------------------------
Author: Mihai Bazon
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
2012-08-27 08:01:27 +00:00
Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
2014-04-15 12:57:14 +00:00
Copyright 2014 (c) TrigenSoftware <danon0404@gmail.com>
2012-08-22 18:28:59 +00:00
Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
***********************************************************************/
2012-10-02 09:45:31 +00:00
"use strict";
2014-04-16 16:43:40 +00:00
!window.Cola && (window.Cola = {});
2012-10-02 09:45:31 +00:00
2014-04-16 16:43:40 +00:00
Cola.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';
Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' is isnt class singleton injector';
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.KEYWORDS_ATOM = 'false null true';
Cola.cKEYWORDS_ATOM = Cola.KEYWORDS_ATOM + ' on yes off no';
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.RESERVED_WORDS = 'abstract boolean byte char 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';
Cola.cRESERVED_WORDS = Cola.RESERVED_WORDS.replace(' class', '') + " " + Cola.cKEYWORDS_ATOM + " " + Cola.cKEYWORDS;
Cola.RESERVED_WORDS += " " + Cola.KEYWORDS_ATOM + " " + Cola.KEYWORDS;
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.KEYWORDS = Cola.makePredicate(Cola.KEYWORDS);
Cola.cKEYWORDS = Cola.makePredicate(Cola.cKEYWORDS);
Cola.RESERVED_WORDS = Cola.makePredicate(Cola.RESERVED_WORDS);
Cola.cRESERVED_WORDS = Cola.makePredicate(Cola.cRESERVED_WORDS);
Cola.KEYWORDS_ATOM = Cola.makePredicate(Cola.KEYWORDS_ATOM);
Cola.cKEYWORDS_ATOM = Cola.makePredicate(Cola.cKEYWORDS_ATOM);
Cola.KEYWORDS_BEFORE_EXPRESSION = Cola.makePredicate(Cola.KEYWORDS_BEFORE_EXPRESSION);
Cola.OPERATOR_CHARS = Cola.makePredicate(Cola.characters("+-*&%=<>!?|~^"));
Cola.RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
Cola.RE_OCT_NUMBER = /^0[0-7]+$/;
Cola.RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
Cola.OPERATORS = [
2012-05-27 11:09:01 +00:00
"in",
"instanceof",
"typeof",
"new",
2014-04-16 16:43:40 +00:00
//"void",
2012-05-27 11:09:01 +00:00
"delete",
"++",
"--",
"+",
"-",
"!",
"~",
"&",
"|",
"^",
"*",
"/",
"%",
">>",
"<<",
">>>",
"<",
">",
"<=",
">=",
"==",
"===",
"!=",
"!==",
"?",
"=",
"+=",
"-=",
"/=",
"*=",
"%=",
">>=",
"<<=",
">>>=",
"|=",
"^=",
"&=",
"&&",
"||",
// ColaScript
"is",
"isnt",
"**",
"%%",
"?="
2014-04-16 16:43:40 +00:00
];
Cola.cOPERATORS = Cola.makePredicate(Cola.OPERATORS);
Cola.OPERATORS = Cola.OPERATORS.slice(0, Cola.OPERATORS.length - 5);
Cola.OPERATORS.push('void');
Cola.OPERATORS = Cola.makePredicate(Cola.OPERATORS);
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.WHITESPACE_CHARS = Cola.makePredicate(Cola.characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.PUNC_BEFORE_EXPRESSION = Cola.makePredicate(Cola.characters("[{(,.;:"));
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.PUNC_CHARS = Cola.makePredicate(Cola.characters("[]{}(),;:"));
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.REGEXP_MODIFIERS = Cola.makePredicate(Cola.characters("gmsiy"));
2012-05-27 11:09:01 +00:00
/* -----[ Tokenizer ]----- */
// regexps adapted from http://xregexp.com/plugins/#unicode
2014-04-16 16:43:40 +00:00
Cola.UNICODE = {
2012-05-27 11:09:01 +00:00
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-\\
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]"),
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]"),
connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
};
2014-04-16 16:43:40 +00:00
Cola.is_letter = function (code) {
return (code >= 97 && code <= 122)
|| (code >= 65 && code <= 90)
2014-04-16 16:43:40 +00:00
|| (code >= 0xaa && Cola.UNICODE.letter.test(String.fromCharCode(code)));
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_digit = function (code) {
return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_alphanumeric_char = function (code) {
return Cola.is_digit(code) || Cola.is_letter(code);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_unicode_combining_mark = function (ch) {
return Cola.UNICODE.non_spacing_mark.test(ch) || Cola.UNICODE.space_combining_mark.test(ch);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_unicode_connector_punctuation = function (ch) {
return Cola.UNICODE.connector_punctuation.test(ch);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_identifier = function (name) {
return !Cola.RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name);
};
2014-04-16 16:43:40 +00:00
Cola.is_identifier_start = function (code) {
return code == 36 || code == 95 || Cola.is_letter(code);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_identifier_char = function (ch) {
var code = ch.charCodeAt(0);
2014-04-16 16:43:40 +00:00
return Cola.is_identifier_start(code)
|| Cola.is_digit(code)
|| code == 8204 // \u200c: zero-width non-joiner <ZWNJ>
|| code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c)
2014-04-16 16:43:40 +00:00
|| Cola.is_unicode_combining_mark(ch)
|| Cola.is_unicode_connector_punctuation(ch)
2012-05-27 11:09:01 +00:00
;
};
2014-04-16 16:43:40 +00:00
Cola.is_identifier_string = function (str){
2013-05-08 19:37:48 +00:00
var i = str.length;
if (i == 0) return false;
2014-04-16 16:43:40 +00:00
if (!Cola.is_identifier_start(str.charCodeAt(0))) return false;
2013-05-08 19:37:48 +00:00
while (--i >= 0) {
2014-04-16 16:43:40 +00:00
if (!Cola.is_identifier_char(str.charAt(i)))
return false;
}
return true;
};
2014-04-16 16:43:40 +00:00
Cola.parse_js_number = function (num) {
if (Cola.RE_HEX_NUMBER.test(num)) {
2012-05-27 11:09:01 +00:00
return parseInt(num.substr(2), 16);
2014-04-16 16:43:40 +00:00
} else if (Cola.RE_OCT_NUMBER.test(num)) {
2012-05-27 11:09:01 +00:00
return parseInt(num.substr(1), 8);
2014-04-16 16:43:40 +00:00
} else if (Cola.RE_DEC_NUMBER.test(num)) {
2012-05-27 11:09:01 +00:00
return parseFloat(num);
}
};
2014-04-16 16:43:40 +00:00
Cola.JS_Parse_Error = function (message, line, col, pos) {
2012-05-27 11:09:01 +00:00
this.message = message;
2012-10-02 13:39:53 +00:00
this.line = line;
this.col = col;
2012-10-11 12:25:38 +00:00
this.pos = pos;
2012-05-27 11:09:01 +00:00
this.stack = new Error().stack;
};
2014-04-16 16:43:40 +00:00
Cola.JS_Parse_Error.prototype.toString = function() {
2012-05-27 11:09:01 +00:00
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
};
2014-04-16 16:43:40 +00:00
Cola.js_error = function (message, filename, line, col, pos) {
throw new Cola.JS_Parse_Error(message, line, col, pos);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.is_token = function (token, type, val) {
2012-05-27 11:09:01 +00:00
return token.type == type && (val == null || token.value == val);
};
2014-04-16 16:43:40 +00:00
Cola.EX_EOF = {};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer = function ($TEXT, filename, html5_comments) {
this.S = {
text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''),
filename : filename,
2013-07-15 08:59:23 +00:00
pos : 0,
tokpos : 0,
line : 1,
tokline : 0,
col : 0,
tokcol : 0,
2012-05-27 11:09:01 +00:00
newline_before : false,
regex_allowed : false,
string : {
2014-04-16 16:43:40 +00:00
at : [ new Cola.tokenizer.StringInfo() ],
level : 0
},
in_string : false,
string_quote : '',
2012-05-27 11:09:01 +00:00
comments_before : []
};
2014-04-16 16:43:40 +00:00
this.html5_comments = html5_comments;
this.prev_was_dot = false;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.StringInfo = function () {
this.quote = '';
this.inside = false;
this.after_at = false;
this.inside_at = false;
this.inside_braces = false;
this.balance = 0;
};
Cola.tokenizer.with_eof_error = function (eof_error, cont) {
return function(x) {
try {
return cont(x);
} catch(ex) {
if (ex === Cola.EX_EOF) this.parse_error(eof_error);
else throw ex;
2012-05-27 11:09:01 +00:00
}
};
2014-04-16 16:43:40 +00:00
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.peek = function (offset) { return this.S.text.charAt(this.S.pos + (offset ? offset : 0)); };
Cola.tokenizer.prototype.next = function (signal_eof, in_string) {
var ch = this.S.text.charAt(this.S.pos++);
if (signal_eof && !ch)
throw Cola.EX_EOF;
if (ch == "\n") {
this.S.newline_before = this.S.newline_before || !in_string;
++this.S.line;
this.S.col = 0;
} else {
++this.S.col;
}
return ch;
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.forward = function (i) {
while (i-- > 0) this.next();
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.looking_at = function (str) {
return this.S.text.substr(this.S.pos, str.length) == str;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.find = function (what, signal_eof) {
var pos = this.S.text.indexOf(what, this.S.pos);
if (signal_eof && pos == -1) throw Cola.EX_EOF;
return pos;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.start_token = function () {
this.S.tokline = this.S.line;
this.S.tokcol = this.S.col;
this.S.tokpos = this.S.pos;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.token = function (type, value, is_comment) {
this.S.regex_allowed = ((type == "operator" && !Cola.UNARY_POSTFIX(value)) ||
(type == "keyword" && Cola.KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && Cola.PUNC_BEFORE_EXPRESSION(value)));
this.prev_was_dot = (type == "punc" && value == ".");
var ret = {
type : type,
value : value,
line : this.S.tokline,
col : this.S.tokcol,
pos : this.S.tokpos,
endpos : this.S.pos,
nlb : this.S.newline_before,
file : this.S.filename
};
if (!is_comment) {
ret.comments_before = this.S.comments_before;
this.S.comments_before = [];
// make note of any newlines in the comments that came before
for (var i = 0, len = ret.comments_before.length; i < len; i++) {
ret.nlb = ret.nlb || ret.comments_before[i].nlb;
}
}
this.S.newline_before = false;
return new Cola.AST_Token(ret);
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.skip_whitespace = function () {
while (Cola.WHITESPACE_CHARS(this.peek()))
this.next();
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_while = function (pred) {
var ret = "", ch, i = 0;
while ((ch = this.peek()) && pred(ch, i++))
ret += this.next();
return ret;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.parse_error = function (err) {
Cola.js_error(err, this.S.filename, this.S.tokline, this.S.tokcol, this.S.tokpos);
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_num = function (prefix) {
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
var num = this.read_while(function(ch, i){
var code = ch.charCodeAt(0);
switch (code) {
case 120: case 88: // xX
return has_x ? false : (has_x = true);
case 101: case 69: // eE
return has_x ? true : has_e ? false : (has_e = after_e = true);
case 45: // -
return after_e || (i == 0 && !prefix);
case 43: // +
return after_e;
case (after_e = false, 46): // .
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
2012-05-27 11:09:01 +00:00
}
2014-04-16 16:43:40 +00:00
return Cola.is_alphanumeric_char(code);
});
if (prefix) num = prefix + num;
var valid = Cola.parse_js_number(num);
if (!isNaN(valid)) {
return this.token("num", valid);
} else {
this.parse_error("Invalid syntax: " + num);
}
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_escaped_char = function (in_string) {
var ch = this.next(true, in_string);
switch (ch.charCodeAt(0)) {
case 110 : return "\n";
case 114 : return "\r";
case 116 : return "\t";
case 98 : return "\b";
case 118 : return "\u000b"; // \v
case 102 : return "\f";
case 48 : return "\0";
case 120 : return String.fromCharCode(this.hex_bytes(2)); // \x
case 117 : return String.fromCharCode(this.hex_bytes(4)); // \u
case 10 : return ""; // newline
default : return ch;
}
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.hex_bytes = function (n) {
var num = 0;
for (; n > 0; --n) {
var digit = parseInt(this.next(true), 16);
if (isNaN(digit))
this.parse_error("Invalid hex-character pattern in string");
num = (num << 4) | digit;
}
return num;
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_string = Cola.tokenizer.with_eof_error("Unterminated string constant", function(raw){
var quote = this.next(), ret = "";
2014-04-16 16:43:40 +00:00
if (!raw) {
if (this.S.string.at[this.S.string.level].inside && (this.S.string.at[this.S.string.level].inside_at || this.S.string.at[this.S.string.level].inside_braces)) {
this.S.string.level++;
this.S.string.at[this.S.string.level] = new Cola.tokenizer.StringInfo();
}
2014-04-16 16:43:40 +00:00
this.S.string.at[this.S.string.level].inside = true;
2014-04-16 16:43:40 +00:00
if (quote != '"' && quote != "'" && quote != '`') {
ret = quote;
quote = this.S.string.at[this.S.string.level].quote;
} else
if (quote == this.S.string.at[this.S.string.level].quote){
this.S.string.at[this.S.string.level].inside = false;
this.S.string.at[this.S.string.level].quote = '';
if(this.S.string.level != 0){
delete this.S.string.at[this.S.string.level];
this.S.string.level--;
2012-05-27 11:09:01 +00:00
}
2014-04-16 16:43:40 +00:00
return this.next_token();
} else
this.S.string.at[this.S.string.level].quote = quote;
if (this.peek() == '@' || this.peek() == '{' && this.peek(1) == '{') return this.token("string", "");
}
for (;;) {
var ch = this.next(true);
if (!raw && ch == "\\") {
// read OctalEscapeSequence (XXX: deprecated if "strict mode")
// https://github.com/mishoo/UglifyJS/issues/178
var octal_len = 0, first = null;
ch = this.read_while(function(ch){
if (ch >= "0" && ch <= "7") {
if (!first) {
first = ch;
return ++octal_len;
}
2014-04-16 16:43:40 +00:00
else if (first <= "3" && octal_len <= 2) return ++octal_len;
else if (first >= "4" && octal_len <= 1) return ++octal_len;
}
2014-04-16 16:43:40 +00:00
return false;
});
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
else ch = this.read_escaped_char(true);
}
else if (ch == quote) {
if(!raw){
this.S.string.at[this.S.string.level].inside = false;
this.S.string.at[this.S.string.level].quote = '';
if(this.S.string.level != 0){
delete this.S.string.at[this.S.string.level];
this.S.string.level--;
}
}
break;
}
2014-04-16 16:43:40 +00:00
ret += ch;
if (!raw && (this.peek() == '@' || this.peek() == '{' && this.peek(1) == '{')) break;
}
2014-04-16 16:43:40 +00:00
return this.token("string", ret);
});
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_at = function (){
var at = this.next();
2014-04-16 16:43:40 +00:00
if (this.S.string.at[this.S.string.level].inside && Cola.is_identifier_char(this.peek())) {
this.S.string.at[this.S.string.level].after_at = true;
return this.token("punc", "@");
} else
2014-04-16 16:43:40 +00:00
if (this.S.string.at[this.S.string.level].inside && this.peek() == '{') {
this.S.string.at[this.S.string.level].inside_at = true;
this.S.string.at[this.S.string.level].balance = 1;
return this.token("punc", "@" + this.next());
}
2014-04-16 16:43:40 +00:00
this.parse_error('Unexpected character "@"');
}
Cola.tokenizer.prototype.read_braces = function (){
this.next(), this.next();
this.S.string.at[this.S.string.level].inside_braces = true;
this.S.string.at[this.S.string.level].balance = 1;
return this.token("punc", "{{");
}
Cola.tokenizer.prototype.skip_line_comment = function (type) {
var regex_allowed = this.S.regex_allowed;
var i = this.find("\n"), ret;
if (i == -1) {
ret = this.S.text.substr(this.S.pos);
this.S.pos = this.S.text.length;
} else {
ret = this.S.text.substring(this.S.pos, i);
this.S.pos = i;
}
2014-04-16 16:43:40 +00:00
this.S.comments_before.push(this.token(type, ret, true));
this.S.regex_allowed = regex_allowed;
return this.next_token();
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.skip_multiline_comment = Cola.tokenizer.with_eof_error("Unterminated multiline comment", function(){
var regex_allowed = this.S.regex_allowed;
var i = this.find("*/", true);
var text = this.S.text.substring(this.S.pos, i);
var a = text.split("\n"), n = a.length;
// update stream position
this.S.pos = i + 2;
this.S.line += n - 1;
if (n > 1) this.S.col = a[n - 1].length;
else this.S.col += a[n - 1].length;
this.S.col += 2;
var nlb = this.S.newline_before = this.S.newline_before || text.indexOf("\n") >= 0;
this.S.comments_before.push(this.token("comment2", text, true));
this.S.regex_allowed = regex_allowed;
this.S.newline_before = nlb;
return this.next_token();
});
Cola.tokenizer.prototype.read_name = function () {
var backslash = false, name = "", ch, escaped = false, hex;
while ((ch = this.peek()) != null) {
if (!backslash) {
if (ch == "\\") escaped = backslash = true, this.next();
else if (Cola.is_identifier_char(ch)) name += this.next();
else break;
2012-05-27 11:09:01 +00:00
}
2014-04-16 16:43:40 +00:00
else {
if (ch != "u") this.parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
ch = this.read_escaped_char();
if (!Cola.is_identifier_char(ch)) this.parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
name += ch;
backslash = false;
2012-05-27 11:09:01 +00:00
}
2014-04-16 16:43:40 +00:00
}
if (Cola.KEYWORDS(name) && escaped) {
hex = name.charCodeAt(0).toString(16).toUpperCase();
name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
}
if (this.S.string.at[this.S.string.level].inside && this.S.string.at[this.S.string.level].after_at) this.S.string.at[this.S.string.level].after_at = false;
return name;
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_regexp = Cola.tokenizer.with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false;
while ((ch = this.next(true))) if (prev_backslash) {
regexp += "\\" + ch;
prev_backslash = false;
} else if (ch == "[") {
in_class = true;
regexp += ch;
} else if (ch == "]" && in_class) {
in_class = false;
regexp += ch;
} else if (ch == "/" && !in_class) {
break;
} else if (ch == "\\") {
prev_backslash = true;
} else {
regexp += ch;
}
var mods = this.read_name();
return this.token("regexp", /* new RegExp(regexp, mods) ColaScript */ "/" + regexp + "/" + mods);
});
Cola.tokenizer.prototype.read_operator = function (prefix) {
function grow(op) {
if (!this.peek()) return op;
var bigger = op + this.peek();
if (Cola.OPERATORS(bigger)) {
this.next();
return grow(bigger);
} else {
2014-04-16 16:43:40 +00:00
return op;
}
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
return this.token("operator", grow(prefix || this.next()));
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.handle_slash = function () {
this.next();
switch (this.peek()) {
case "/":
this.next();
return this.skip_line_comment("comment1");
case "*":
this.next();
return this.skip_multiline_comment();
}
return this.S.regex_allowed ? this.read_regexp("") : this.read_operator("/");
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.handle_dot = function () {
this.next();
return Cola.is_digit(this.peek().charCodeAt(0))
? this.read_num(".")
: this.token("punc", ".");
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.read_word = function () {
var word = this.read_name();
if (this.prev_was_dot) return this.token("name", word);
return Cola.KEYWORDS_ATOM(word) ? this.token("atom", word)
: !Cola.KEYWORDS(word) ? this.token("name", word)
: Cola.OPERATORS(word) ? this.token("operator", word)
: this.token("keyword", word);
2012-05-27 11:09:01 +00:00
};
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.next_token = function (force_regexp) {
if (force_regexp != null)
return this.read_regexp(force_regexp);
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
var ch;
if (this.S.string.at[this.S.string.level].inside && !this.S.string.at[this.S.string.level].after_at && !this.S.string.at[this.S.string.level].inside_at && !this.S.string.at[this.S.string.level].inside_braces) {
ch = this.peek();
if (ch == '@') return this.read_at();
if (ch == '{' && this.peek(1) == '{') return this.read_braces();
return this.read_string();
}
2014-04-16 16:43:40 +00:00
this.skip_whitespace();
this.start_token();
if (this.html5_comments) {
if (this.looking_at("<!--")) {
this.forward(4);
return this.skip_line_comment("comment3");
}
2014-04-16 16:43:40 +00:00
if (this.looking_at("-->") && this.S.newline_before) {
this.forward(3);
return this.skip_line_comment("comment4");
}
2014-04-16 16:43:40 +00:00
}
ch = this.peek();
if (!ch) return this.token("eof");
var code = ch.charCodeAt(0);
switch (code) {
case 34: case 39: /* ColaScript */ case 96: return this.read_string();
case 46: return this.handle_dot();
case 47: return this.handle_slash();
}
if (Cola.is_digit(code)) return this.read_num();
if (Cola.PUNC_CHARS(ch)){
if (this.S.string.at[this.S.string.level].inside && (this.S.string.at[this.S.string.level].inside_at || this.S.string.at[this.S.string.level].inside_braces)) {
if (ch == '{') this.S.string.at[this.S.string.level].balance++;
else if (ch == '}') this.S.string.at[this.S.string.level].balance--;
if (this.S.string.at[this.S.string.level].balance == 0)
if (this.S.string.at[this.S.string.level].inside_at) this.S.string.at[this.S.string.level].inside_at = false;
else {
this.S.string.at[this.S.string.level].inside_braces = false;
return this.next(), this.next(), this.token("punc", "}}");
}
}
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
return this.token("punc", this.next());
}
if (ch == '@') return this.read_at();
if (Cola.OPERATOR_CHARS(ch)) return this.read_operator();
if (ch == 'r' && (this.peek(1) == '"' || this.peek(1) == "'" || this.peek(1) == '`')) return this.next(), this.read_string(true);
if (code == 92 || Cola.is_identifier_start(code)) return this.read_word();
this.parse_error("Unexpected character '" + ch + "'");
};
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.tokenizer.prototype.next_token.context = function(nc) {
if (nc) this.S = nc;
return this.S;
2012-05-27 11:09:01 +00:00
};
/* -----[ Parser (constants) ]----- */
2014-04-16 16:43:40 +00:00
Cola.UNARY_PREFIX = Cola.makePredicate([
"typeof",
"void",
"delete",
"--",
"++",
"!",
"~",
"-",
"+"
]);
Cola.cUNARY_PREFIX = Cola.makePredicate([
2012-05-27 11:09:01 +00:00
"typeof",
"delete",
"--",
"++",
"!",
"~",
"-",
"+"
]);
2014-04-16 16:43:40 +00:00
Cola.UNARY_POSTFIX = Cola.makePredicate([ "--", "++" ]);
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.ASSIGNMENT = Cola.makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
Cola.cASSIGNMENT = Cola.makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="/* ColaScript */, "?=" ]);
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.mergeTokens = function (a, ret) {
2013-10-30 07:10:56 +00:00
for (var i = 0; i < a.length; ++i) {
2012-05-27 11:09:01 +00:00
var b = a[i];
for (var j = 0; j < b.length; ++j) {
2013-10-30 07:10:56 +00:00
ret[b[j]] = i + 1;
2012-05-27 11:09:01 +00:00
}
}
return ret;
2014-04-16 16:43:40 +00:00
};
Cola.PRECEDENCE = Cola.mergeTokens(
[
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "===", "!=", "!=="],
["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"],
["+", "-"],
["*", "/", "%"]
],
{}
);
Cola.cPRECEDENCE = Cola.mergeTokens(
2012-05-27 11:09:01 +00:00
[
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "===", "!=", "!=="],
["<", ">", "<=", ">=", "in", "instanceof"],
[">>", "<<", ">>>"],
["+", "-"],
["*", "/", "%"],
["is", "isnt", "**", "%%"]
2012-05-27 11:09:01 +00:00
],
{}
);
2014-04-16 16:43:40 +00:00
Cola.STATEMENTS_WITH_LABELS = Cola.array_to_hash([ "for", "do", "while", "switch" ]);
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
Cola.ATOMIC_START_TOKEN = Cola.array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
2012-05-27 11:09:01 +00:00
/* -----[ Parser ]----- */
2014-04-16 16:43:40 +00:00
Cola.parse = function ($TEXT, options) {
2014-04-16 16:43:40 +00:00
options = Cola.defaults(options, {
strict : false,
filename : null,
toplevel : null,
expression : false,
html5_comments : true
});
2012-05-27 11:09:01 +00:00
2014-04-16 16:43:40 +00:00
var tzr = typeof $TEXT == "string" ? new Cola.tokenizer($TEXT, options.filename, options.html5_comments) : $TEXT;
2012-05-27 11:09:01 +00:00
var S = {
2014-04-16 16:43:40 +00:00
input : function(){ return tzr.next_token() },
2012-05-27 11:09:01 +00:00
token : null,
prev : null,
peeked : null,
in_function : 0,
in_directives : true,
in_loop : 0,
labels : []
};
S.token = next();
function is(type, value) {
2014-04-16 16:43:40 +00:00
return Cola.is_token(S.token, type, value);
2012-05-27 11:09:01 +00:00
};
function peek() { return S.peeked || (S.peeked = S.input()); };
function next() {
S.prev = S.token;
if (S.peeked) {
S.token = S.peeked;
S.peeked = null;
} else {
S.token = S.input();
}
S.in_directives = S.in_directives && (
S.token.type == "string" || is("punc", ";")
);
return S.token;
};
function prev() {
return S.prev;
};
function croak(msg, line, col, pos) {
var ctx = S.input.context();
2014-04-16 16:43:40 +00:00
Cola.js_error(msg,
ctx.filename,
2012-05-27 11:09:01 +00:00
line != null ? line : ctx.tokline,
col != null ? col : ctx.tokcol,
pos != null ? pos : ctx.tokpos);
};
function token_error(token, msg) {
croak(msg, token.line, token.col);
};
function unexpected(token) {
if (token == null)
token = S.token;
token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
};
function expect_token(type, val) {
if (is(type, val)) {
return next();
}
token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
2012-05-27 11:09:01 +00:00
};
function expect(punc) { return expect_token("punc", punc); };
function can_insert_semicolon() {
return false; // ColaScript
return !options.strict && (
2012-05-27 11:09:01 +00:00
S.token.nlb || is("eof") || is("punc", "}")
);
};
function semicolon() {
if (is("punc", ";")) next();
else if (!can_insert_semicolon()) unexpected();
};
function parenthesised() {
expect("(");
var exp = expression(true);
expect(")");
return exp;
2012-05-27 11:09:01 +00:00
};
function embed_tokens(parser) {
return function() {
var start = S.token;
var expr = parser();
2012-05-27 11:09:01 +00:00
var end = prev();
expr.start = start;
expr.end = end;
return expr;
};
};
function handle_regexp() {
2012-05-27 11:09:01 +00:00
if (is("operator", "/") || is("operator", "/=")) {
S.peeked = null;
S.token = S.input(S.token.value.substr(1)); // force regexp
}
};
var statement = embed_tokens(function() {
var tmp;
handle_regexp();
2012-05-27 11:09:01 +00:00
switch (S.token.type) {
case "string":
var dir = S.in_directives, stat = simple_statement();
// XXXv2: decide how to fix directives
2014-04-16 16:43:40 +00:00
if (dir && stat.body instanceof Cola.AST_String && !is("punc", ","))
return new Cola.AST_Directive({ value: stat.body.value });
2012-05-27 11:09:01 +00:00
return stat;
case "num":
case "regexp":
case "operator":
case "atom":
return simple_statement();
case "name":
2014-04-16 16:43:40 +00:00
return Cola.is_token(peek(), "punc", ":")
2012-05-27 11:09:01 +00:00
? labeled_statement()
: simple_statement();
case "punc":
switch (S.token.value) {
case "{":
2014-04-16 16:43:40 +00:00
return new Cola.AST_BlockStatement({
start : S.token,
body : block_(),
end : prev()
});
2012-05-27 11:09:01 +00:00
case "[":
case "(":
return simple_statement();
case ";":
next();
2014-04-16 16:43:40 +00:00
return new Cola.AST_EmptyStatement();
2012-05-27 11:09:01 +00:00
default:
unexpected();
}
case "keyword":
switch (tmp = S.token.value, next(), tmp) {
2012-05-27 11:09:01 +00:00
case "break":
2014-04-16 16:43:40 +00:00
return break_cont(Cola.AST_Break);
2012-05-27 11:09:01 +00:00
case "continue":
2014-04-16 16:43:40 +00:00
return break_cont(Cola.AST_Continue);
2012-05-27 11:09:01 +00:00
case "debugger":
semicolon();
2014-04-16 16:43:40 +00:00
return new Cola.AST_Debugger();
2012-05-27 11:09:01 +00:00
case "do":
2014-04-16 16:43:40 +00:00
return new Cola.AST_Do({
2012-05-27 11:09:01 +00:00
body : in_loop(statement),
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
2012-05-27 11:09:01 +00:00
});
case "while":
2014-04-16 16:43:40 +00:00
return new Cola.AST_While({
2012-05-27 11:09:01 +00:00
condition : parenthesised(),
body : in_loop(statement)
});
case "for":
return for_();
case "function":
2014-04-16 16:43:40 +00:00
return function_(Cola.AST_Defun);
2012-05-27 11:09:01 +00:00
case "if":
return if_();
case "return":
if (S.in_function == 0)
croak("'return' outside of function");
2014-04-16 16:43:40 +00:00
return new Cola.AST_Return({
2012-05-27 11:09:01 +00:00
value: ( is("punc", ";")
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
2012-05-27 11:09:01 +00:00
});
case "switch":
2014-04-16 16:43:40 +00:00
return new Cola.AST_Switch({
2012-05-27 11:09:01 +00:00
expression : parenthesised(),
body : in_loop(switch_body_)
2012-05-27 11:09:01 +00:00
});
case "throw":
if (S.token.nlb)
croak("Illegal newline after 'throw'");
2014-04-16 16:43:40 +00:00
return new Cola.AST_Throw({
value: (tmp = expression(true), semicolon(), tmp)
2012-05-27 11:09:01 +00:00
});
case "try":
return try_();
case "var":
return tmp = var_(), semicolon(), tmp;
2012-05-27 11:09:01 +00:00
case "const":
return tmp = const_(), semicolon(), tmp;
2012-05-27 11:09:01 +00:00
case "with":
2014-04-16 16:43:40 +00:00
return new Cola.AST_With({
2012-05-27 11:09:01 +00:00
expression : parenthesised(),
body : statement()
});
default:
unexpected();
}
}
});
function labeled_statement() {
2014-04-16 16:43:40 +00:00
var label = as_symbol(Cola.AST_Label);
if (Cola.find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a
// LabelledStatement with the same Identifier as label.
croak("Label " + label.name + " defined twice");
}
2012-05-27 11:09:01 +00:00
expect(":");
S.labels.push(label);
2012-10-04 05:49:18 +00:00
var stat = statement();
2012-05-27 11:09:01 +00:00
S.labels.pop();
2014-04-16 16:43:40 +00:00
if (!(stat instanceof Cola.AST_IterationStatement)) {
// check for `continue` that refers to this label.
// those should be reported as syntax errors.
// https://github.com/mishoo/UglifyJS2/issues/287
label.references.forEach(function(ref){
2014-04-16 16:43:40 +00:00
if (ref instanceof Cola.AST_Continue) {
ref = ref.label.start;
2013-09-02 16:38:00 +00:00
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
ref.line, ref.col, ref.pos);
}
});
}
2014-04-16 16:43:40 +00:00
return new Cola.AST_LabeledStatement({ body: stat, label: label });
2012-05-27 11:09:01 +00:00
};
function simple_statement(tmp) {
2014-04-16 16:43:40 +00:00
return new Cola.AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) });
2012-05-27 11:09:01 +00:00
};
function break_cont(type) {
var label = null, ldef;
2012-05-27 11:09:01 +00:00
if (!can_insert_semicolon()) {
2014-04-16 16:43:40 +00:00
label = as_symbol(Cola.AST_LabelRef, true);
2012-05-27 11:09:01 +00:00
}
if (label != null) {
2014-04-16 16:43:40 +00:00
ldef = Cola.find_if(function(l){ return l.name == label.name }, S.labels);
if (!ldef)
croak("Undefined label " + label.name);
label.thedef = ldef;
2012-05-27 11:09:01 +00:00
}
else if (S.in_loop == 0)
croak(type.TYPE + " not inside a loop or switch");
semicolon();
var stat = new type({ label: label });
if (ldef) ldef.references.push(stat);
return stat;
2012-05-27 11:09:01 +00:00
};
function for_() {
expect("(");
var init = null;
if (!is("punc", ";")) {
init = is("keyword", "var")
? (next(), var_(true))
: expression(true, true);
if (is("operator", "in")) {
2014-04-16 16:43:40 +00:00
if (init instanceof Cola.AST_Var && init.definitions.length > 1)
2012-05-27 11:09:01 +00:00
croak("Only one variable declaration allowed in for..in loop");
next();
return for_in(init);
}
}
return regular_for(init);
};
function regular_for(init) {
expect(";");
var test = is("punc", ";") ? null : expression(true);
2012-05-27 11:09:01 +00:00
expect(";");
var step = is("punc", ")") ? null : expression(true);
2012-05-27 11:09:01 +00:00
expect(")");
2014-04-16 16:43:40 +00:00
return new Cola.AST_For({
2012-05-27 11:09:01 +00:00
init : init,
condition : test,
step : step,
body : in_loop(statement)
});
};
function for_in(init) {
2014-04-16 16:43:40 +00:00
var lhs = init instanceof Cola.AST_Var ? init.definitions[0].name : null;
var obj = expression(true);
2012-05-27 11:09:01 +00:00
expect(")");
2014-04-16 16:43:40 +00:00
return new Cola.AST_ForIn({
2012-05-27 11:09:01 +00:00
init : init,
2012-05-27 14:25:31 +00:00
name : lhs,
2012-05-27 11:09:01 +00:00
object : obj,
body : in_loop(statement)
});
};
var function_ = function(ctor) {
2014-04-16 16:43:40 +00:00
var in_statement = ctor === Cola.AST_Defun;
var name = is("name") ? as_symbol(in_statement ? Cola.AST_SymbolDefun : Cola.AST_SymbolLambda) : null;
2012-05-27 11:09:01 +00:00
if (in_statement && !name)
unexpected();
expect("(");
return new ctor({
name: name,
argnames: (function(first, a){
while (!is("punc", ")")) {
if (first) first = false; else expect(",");
2014-04-16 16:43:40 +00:00
a.push(as_symbol(Cola.AST_SymbolFunarg));
2012-05-27 11:09:01 +00:00
}
next();
return a;
})(true, []),
body: (function(loop, labels){
++S.in_function;
S.in_directives = true;
S.in_loop = 0;
S.labels = [];
var a = block_();
--S.in_function;
S.in_loop = loop;
S.labels = labels;
return a;
})(S.in_loop, S.labels)
2012-05-27 11:09:01 +00:00
});
};
function if_() {
var cond = parenthesised(), body = statement(), belse = null;
if (is("keyword", "else")) {
next();
belse = statement();
}
2014-04-16 16:43:40 +00:00
return new Cola.AST_If({
2012-05-27 11:09:01 +00:00
condition : cond,
2012-09-03 09:11:44 +00:00
body : body,
2012-05-27 11:09:01 +00:00
alternative : belse
});
};
function block_() {
2012-05-27 11:09:01 +00:00
expect("{");
var a = [];
while (!is("punc", "}")) {
if (is("eof")) unexpected();
a.push(statement());
}
next();
return a;
2012-05-27 11:09:01 +00:00
};
function switch_body_() {
2012-05-27 11:09:01 +00:00
expect("{");
var a = [], cur = null, branch = null, tmp;
2012-05-27 11:09:01 +00:00
while (!is("punc", "}")) {
if (is("eof")) unexpected();
if (is("keyword", "case")) {
2012-05-27 14:25:31 +00:00
if (branch) branch.end = prev();
2012-05-27 11:09:01 +00:00
cur = [];
2014-04-16 16:43:40 +00:00
branch = new Cola.AST_Case({
start : (tmp = S.token, next(), tmp),
expression : expression(true),
2012-05-27 14:25:31 +00:00
body : cur
});
a.push(branch);
2012-05-27 11:09:01 +00:00
expect(":");
}
else if (is("keyword", "default")) {
2012-05-27 14:25:31 +00:00
if (branch) branch.end = prev();
2012-05-27 11:09:01 +00:00
cur = [];
2014-04-16 16:43:40 +00:00
branch = new Cola.AST_Default({
start : (tmp = S.token, next(), expect(":"), tmp),
2012-05-27 14:25:31 +00:00
body : cur
});
2012-05-27 14:25:31 +00:00
a.push(branch);
2012-05-27 11:09:01 +00:00
}
else {
if (!cur) unexpected();
cur.push(statement());
}
}
2012-05-27 14:25:31 +00:00
if (branch) branch.end = prev();
2012-05-27 11:09:01 +00:00
next();
2012-10-03 12:52:01 +00:00
return a;
};
2012-05-27 11:09:01 +00:00
function try_() {
var body = block_(), bcatch = null, bfinally = null;
2012-05-27 11:09:01 +00:00
if (is("keyword", "catch")) {
2012-05-27 14:25:31 +00:00
var start = S.token;
2012-05-27 11:09:01 +00:00
next();
expect("(");
2014-04-16 16:43:40 +00:00
var name = as_symbol(Cola.AST_SymbolCatch);
2012-05-27 11:09:01 +00:00
expect(")");
2014-04-16 16:43:40 +00:00
bcatch = new Cola.AST_Catch({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
argname : name,
body : block_(),
2012-05-27 14:25:31 +00:00
end : prev()
2012-05-27 11:09:01 +00:00
});
}
if (is("keyword", "finally")) {
2012-05-27 14:25:31 +00:00
var start = S.token;
2012-05-27 11:09:01 +00:00
next();
2014-04-16 16:43:40 +00:00
bfinally = new Cola.AST_Finally({
2012-05-27 14:25:31 +00:00
start : start,
body : block_(),
2012-05-27 14:25:31 +00:00
end : prev()
});
2012-05-27 11:09:01 +00:00
}
if (!bcatch && !bfinally)
croak("Missing catch/finally blocks");
2014-04-16 16:43:40 +00:00
return new Cola.AST_Try({
body : body,
2012-05-27 11:09:01 +00:00
bcatch : bcatch,
bfinally : bfinally
});
};
function vardefs(no_in, in_const) {
2012-05-27 11:09:01 +00:00
var a = [];
for (;;) {
2014-04-16 16:43:40 +00:00
a.push(new Cola.AST_VarDef({
2012-05-27 11:09:01 +00:00
start : S.token,
2014-04-16 16:43:40 +00:00
name : as_symbol(in_const ? Cola.AST_SymbolConst : Cola.AST_SymbolVar),
2012-05-27 11:09:01 +00:00
value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
end : prev()
}));
if (!is("punc", ","))
break;
next();
}
return a;
};
2012-05-27 14:25:31 +00:00
var var_ = function(no_in) {
2014-04-16 16:43:40 +00:00
return new Cola.AST_Var({
2012-05-27 14:25:31 +00:00
start : prev(),
definitions : vardefs(no_in, false),
2012-05-27 14:25:31 +00:00
end : prev()
2012-05-27 11:09:01 +00:00
});
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var const_ = function() {
2014-04-16 16:43:40 +00:00
return new Cola.AST_Const({
2012-05-27 14:25:31 +00:00
start : prev(),
definitions : vardefs(false, true),
2012-05-27 14:25:31 +00:00
end : prev()
2012-05-27 11:09:01 +00:00
});
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var new_ = function() {
var start = S.token;
expect_token("operator", "new");
2012-05-27 11:09:01 +00:00
var newexp = expr_atom(false), args;
if (is("punc", "(")) {
next();
args = expr_list(")");
} else {
args = [];
}
2014-04-16 16:43:40 +00:00
return subscripts(new Cola.AST_New({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
expression : newexp,
2012-05-27 14:25:31 +00:00
args : args,
end : prev()
2012-05-27 11:09:01 +00:00
}), true);
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
function string_template_(start_token) {
2014-04-16 16:43:40 +00:00
var body = [new Cola.AST_String({ start: start_token, end: start_token, value: start_token.value })];
next();
//if (!is('punc', '@') && !is('punc', '@{')) return body[0];
while (is('punc', '@') || is('punc', '@{') || is('punc', '{{') || is('string', null))
if (is('string', null)) {
2014-04-16 16:43:40 +00:00
body.push(new Cola.AST_String({ start: S.token, end: S.token, value: S.token.value }));
next();
} else
if (is('punc', '@')) {
next();
2014-04-16 16:43:40 +00:00
body.push(_make_symbol(Cola.AST_SymbolRef));
next();
} else
if (is('punc', '@{')) {
next();
body.push(expression(true));
expect('}');
} else
if (is('punc', '{{')) {
next();
body.push(expression(true));
expect('}}');
}
var last = body[body.length - 1];
body[0].value = body[0].value.replace(/^[ \t]*[\n\r]/,'');
2014-04-16 16:43:40 +00:00
if (last instanceof Cola.AST_String) {
var offstr = last.value.match(/[\n\r][ \t]*$/);
if(offstr){
offstr = offstr[0];
2014-04-16 16:43:40 +00:00
for(var i in body) if(body[i] instanceof Cola.AST_String){
body[i].value = body[i].value.replace(new RegExp(offstr, 'g'), '\n');
body[i].value = body[i].value.replace(/\n/g, '\n');
}
last.value = last.value.replace(/[\n\r]$/,'');
}
}
if (body.length == 1) return body[0];
2014-04-16 16:43:40 +00:00
return new Cola.AST_StringTemplate({
start : start_token,
end : prev(),
body : body
});
}
2012-05-27 11:09:01 +00:00
function as_atom_node() {
var tok = S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
2014-04-16 16:43:40 +00:00
ret = _make_symbol(Cola.AST_SymbolRef);
break;
2012-05-27 11:09:01 +00:00
case "num":
2014-04-16 16:43:40 +00:00
ret = new Cola.AST_Number({ start: tok, end: tok, value: tok.value });
2012-05-27 11:09:01 +00:00
break;
case "string":
2014-04-16 16:43:40 +00:00
return string_template_(tok);//new Cola.AST_String({ start: tok, end: tok, value: tok.value });
//break;
2012-05-27 11:09:01 +00:00
case "regexp":
2014-04-16 16:43:40 +00:00
ret = new Cola.AST_RegExp({ start: tok, end: tok, value: tok.value });
2012-05-27 11:09:01 +00:00
break;
case "atom":
switch (tok.value) {
case "false": /* ColaScript */ case "off": case "no":
2014-04-16 16:43:40 +00:00
ret = new Cola.AST_False({ start: tok, end: tok });
2012-05-27 11:09:01 +00:00
break;
case "true": /* ColaScript */ case "on": case "yes":
2014-04-16 16:43:40 +00:00
ret = new Cola.AST_True({ start: tok, end: tok });
2012-05-27 11:09:01 +00:00
break;
case "null":
2014-04-16 16:43:40 +00:00
ret = new Cola.AST_Null({ start: tok, end: tok });
2012-05-27 11:09:01 +00:00
break;
}
break;
}
next();
return ret;
};
var expr_atom = function(allow_calls) {
if (is("operator", "new")) {
return new_();
}
2012-05-27 14:25:31 +00:00
var start = S.token;
2012-05-27 11:09:01 +00:00
if (is("punc")) {
2012-05-27 14:25:31 +00:00
switch (start.value) {
2012-05-27 11:09:01 +00:00
case "(":
next();
var ex = expression(true);
2012-05-27 14:25:31 +00:00
ex.start = start;
ex.end = S.token;
expect(")");
return subscripts(ex, allow_calls);
2012-05-27 11:09:01 +00:00
case "[":
return subscripts(array_(), allow_calls);
case "{":
return subscripts(object_(), allow_calls);
}
unexpected();
}
if (is("keyword", "function")) {
next();
2014-04-16 16:43:40 +00:00
var func = function_(Cola.AST_Function);
2012-05-27 11:09:01 +00:00
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
}
2014-04-16 16:43:40 +00:00
if (Cola.ATOMIC_START_TOKEN[S.token.type]) {
2012-05-27 11:09:01 +00:00
return subscripts(as_atom_node(), allow_calls);
}
unexpected();
};
function expr_list(closing, allow_trailing_comma, allow_empty) {
var first = true, a = [];
while (!is("punc", closing)) {
if (first) first = false; else expect(",");
if (allow_trailing_comma && is("punc", closing)) break;
if (is("punc", ",") && allow_empty) {
2014-04-16 16:43:40 +00:00
a.push(new Cola.AST_Hole({ start: S.token, end: S.token }));
2012-05-27 11:09:01 +00:00
} else {
a.push(expression(false));
}
}
next();
return a;
};
2012-05-27 14:25:31 +00:00
var array_ = embed_tokens(function() {
expect("[");
2014-04-16 16:43:40 +00:00
return new Cola.AST_Array({
elements: expr_list("]", !options.strict, true)
2012-05-27 11:09:01 +00:00
});
2012-05-27 14:25:31 +00:00
});
2012-05-27 11:09:01 +00:00
var object_ = embed_tokens(function() {
2012-05-27 14:25:31 +00:00
expect("{");
2012-05-27 11:09:01 +00:00
var first = true, a = [];
while (!is("punc", "}")) {
if (first) first = false; else expect(",");
if (!options.strict && is("punc", "}"))
2012-05-27 11:09:01 +00:00
// allow trailing comma
break;
var start = S.token;
var type = start.type;
var name = as_property_name();
if (type == "name" && !is("punc", ":")) {
if (name == "get") {
2014-04-16 16:43:40 +00:00
a.push(new Cola.AST_ObjectGetter({
2012-05-27 11:09:01 +00:00
start : start,
key : as_atom_node(),
2014-04-16 16:43:40 +00:00
value : function_(Cola.AST_Accessor),
2012-05-27 11:09:01 +00:00
end : prev()
}));
continue;
}
if (name == "set") {
2014-04-16 16:43:40 +00:00
a.push(new Cola.AST_ObjectSetter({
2012-05-27 11:09:01 +00:00
start : start,
key : as_atom_node(),
2014-04-16 16:43:40 +00:00
value : function_(Cola.AST_Accessor),
2012-05-27 11:09:01 +00:00
end : prev()
}));
continue;
}
}
expect(":");
2014-04-16 16:43:40 +00:00
a.push(new Cola.AST_ObjectKeyVal({
2012-05-27 11:09:01 +00:00
start : start,
key : name,
value : expression(false),
end : prev()
}));
}
next();
2014-04-16 16:43:40 +00:00
return new Cola.AST_Object({ properties: a });
2012-05-27 11:09:01 +00:00
});
function as_property_name() {
2012-10-13 09:42:01 +00:00
var tmp = S.token;
next();
switch (tmp.type) {
2012-05-27 11:09:01 +00:00
case "num":
case "string":
2012-05-27 14:25:31 +00:00
case "name":
case "operator":
case "keyword":
case "atom":
2012-10-13 09:42:01 +00:00
return tmp.value;
2012-05-27 14:25:31 +00:00
default:
unexpected();
2012-05-27 11:09:01 +00:00
}
};
function as_name() {
2012-10-13 09:42:01 +00:00
var tmp = S.token;
next();
switch (tmp.type) {
2012-05-27 11:09:01 +00:00
case "name":
case "operator":
case "keyword":
case "atom":
2012-10-13 09:42:01 +00:00
return tmp.value;
2012-05-27 11:09:01 +00:00
default:
unexpected();
}
};
function _make_symbol(type) {
var name = S.token.value;
2014-04-16 16:43:40 +00:00
return new (name == "this" ? Cola.AST_This : type)({
name : String(name),
start : S.token,
end : S.token
});
};
function as_symbol(type, noerror) {
if (!is("name")) {
if (!noerror) croak("Name expected");
return null;
}
var sym = _make_symbol(type);
2012-05-27 11:09:01 +00:00
next();
return sym;
};
2012-05-27 14:25:31 +00:00
var subscripts = function(expr, allow_calls) {
var start = expr.start;
2012-05-27 11:09:01 +00:00
if (is("punc", ".")) {
next();
2014-04-16 16:43:40 +00:00
return subscripts(new Cola.AST_Dot({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
expression : expr,
2012-05-27 14:25:31 +00:00
property : as_name(),
end : prev()
2012-05-27 11:09:01 +00:00
}), allow_calls);
}
if (is("punc", "[")) {
next();
var prop = expression(true);
2012-05-27 14:25:31 +00:00
expect("]");
2014-04-16 16:43:40 +00:00
return subscripts(new Cola.AST_Sub({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
expression : expr,
2012-05-27 14:25:31 +00:00
property : prop,
end : prev()
2012-05-27 11:09:01 +00:00
}), allow_calls);
}
if (allow_calls && is("punc", "(")) {
next();
2014-04-16 16:43:40 +00:00
return subscripts(new Cola.AST_Call({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
expression : expr,
2012-05-27 14:25:31 +00:00
args : expr_list(")"),
end : prev()
2012-05-27 11:09:01 +00:00
}), true);
}
return expr;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var maybe_unary = function(allow_calls) {
2012-10-13 09:42:01 +00:00
var start = S.token;
2014-04-16 16:43:40 +00:00
if (is("operator") && Cola.UNARY_PREFIX(start.value)) {
2012-10-13 09:42:01 +00:00
next();
handle_regexp();
2014-04-16 16:43:40 +00:00
var ex = make_unary(Cola.AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
2012-05-27 14:25:31 +00:00
ex.start = start;
ex.end = prev();
return ex;
2012-05-27 11:09:01 +00:00
}
var val = expr_atom(allow_calls);
2014-04-16 16:43:40 +00:00
while (is("operator") && Cola.UNARY_POSTFIX(S.token.value) && !S.token.nlb) {
val = make_unary(Cola.AST_UnaryPostfix, S.token.value, val);
2012-05-27 14:25:31 +00:00
val.start = start;
val.end = S.token;
2012-05-27 11:09:01 +00:00
next();
}
return val;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
function make_unary(ctor, op, expr) {
if ((op == "++" || op == "--") && !is_assignable(expr))
croak("Invalid use of " + op + " operator");
return new ctor({ operator: op, expression: expr });
};
2012-05-27 14:25:31 +00:00
var expr_op = function(left, min_prec, no_in) {
2012-05-27 11:09:01 +00:00
var op = is("operator") ? S.token.value : null;
if (op == "in" && no_in) op = null;
2014-04-16 16:43:40 +00:00
var prec = op != null ? Cola.PRECEDENCE[op] : null;
2012-05-27 11:09:01 +00:00
if (prec != null && prec > min_prec) {
next();
var right = expr_op(maybe_unary(true), prec, no_in);
2014-04-16 16:43:40 +00:00
return expr_op(new Cola.AST_Binary({
2012-05-27 14:25:31 +00:00
start : left.start,
2012-05-27 11:09:01 +00:00
left : left,
operator : op,
2012-05-27 14:25:31 +00:00
right : right,
end : right.end
2012-05-27 11:09:01 +00:00
}), min_prec, no_in);
}
return left;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
function expr_ops(no_in) {
return expr_op(maybe_unary(true), 0, no_in);
};
2012-05-27 14:25:31 +00:00
var maybe_conditional = function(no_in) {
var start = S.token;
2012-05-27 11:09:01 +00:00
var expr = expr_ops(no_in);
if (is("operator", "?")) {
next();
var yes = expression(false);
expect(":");
2014-04-16 16:43:40 +00:00
return new Cola.AST_Conditional({
2012-05-27 14:25:31 +00:00
start : start,
condition : expr,
consequent : yes,
alternative : expression(false, no_in),
end : prev()
2012-05-27 11:09:01 +00:00
});
}
return expr;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
function is_assignable(expr) {
if (!options.strict) return true;
2014-04-16 16:43:40 +00:00
if (expr instanceof Cola.AST_This) return false;
return (expr instanceof Cola.AST_PropAccess || expr instanceof Cola.AST_Symbol);
2012-05-27 11:09:01 +00:00
};
2012-05-27 14:25:31 +00:00
var maybe_assign = function(no_in) {
var start = S.token;
2012-05-27 11:09:01 +00:00
var left = maybe_conditional(no_in), val = S.token.value;
2014-04-16 16:43:40 +00:00
if (is("operator") && Cola.ASSIGNMENT(val)) {
2012-05-27 11:09:01 +00:00
if (is_assignable(left)) {
next();
2014-04-16 16:43:40 +00:00
return new Cola.AST_Assign({
2012-05-27 14:25:31 +00:00
start : start,
2012-05-27 11:09:01 +00:00
left : left,
operator : val,
2012-05-27 14:25:31 +00:00
right : maybe_assign(no_in),
2013-01-26 12:24:54 +00:00
end : prev()
2012-05-27 11:09:01 +00:00
});
}
croak("Invalid assignment");
}
return left;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
2012-05-27 14:25:31 +00:00
var expression = function(commas, no_in) {
var start = S.token;
2012-05-27 11:09:01 +00:00
var expr = maybe_assign(no_in);
if (commas && is("punc", ",")) {
next();
2014-04-16 16:43:40 +00:00
return new Cola.AST_Seq({
2012-05-27 14:25:31 +00:00
start : start,
car : expr,
cdr : expression(true, no_in),
2012-05-27 14:25:31 +00:00
end : peek()
2012-05-27 11:09:01 +00:00
});
}
return expr;
2012-05-27 14:25:31 +00:00
};
2012-05-27 11:09:01 +00:00
function in_loop(cont) {
++S.in_loop;
var ret = cont();
--S.in_loop;
return ret;
};
if (options.expression) {
return expression(true);
}
return (function(){
var start = S.token;
var body = [];
while (!is("eof"))
body.push(statement());
var end = prev();
var toplevel = options.toplevel;
if (toplevel) {
toplevel.body = toplevel.body.concat(body);
toplevel.end = end;
} else {
2014-04-16 16:43:40 +00:00
toplevel = new Cola.AST_Toplevel({ start: start, body: body, end: end });
}
return toplevel;
})();
2012-05-27 11:09:01 +00:00
};