UglifyJS/lib/parse.js

2234 lines
83 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-22 13:33:43 +00:00
!this.Cola && (this.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';
2014-05-19 09:58:17 +00:00
Cola.cKEYWORDS = Cola.KEYWORDS.replace(' void', '') + ' when clone isset 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';
2014-05-19 09:58:17 +00:00
Cola.cKEYWORDS_BEFORE_EXPRESSION = Cola.KEYWORDS_BEFORE_EXPRESSION += ' when';
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);
2014-05-19 09:58:17 +00:00
Cola.cKEYWORDS_BEFORE_EXPRESSION = Cola.makePredicate(Cola.cKEYWORDS_BEFORE_EXPRESSION);
2014-04-16 16:43:40 +00:00
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
"clone",
"isset",
"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 - 8);
2014-04-16 16:43:40 +00:00
Cola.OPERATORS.push('void');
Cola.OPERATORS = Cola.makePredicate(Cola.OPERATORS);
2012-05-27 11:09:01 +00:00
2014-05-16 12:27:51 +00:00
Cola.COMPARISON = Cola.makePredicate("< > <= >= == === != !==");
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
Cola.cPUNC_BEFORE_EXPRESSION = Cola.makePredicate(Cola.characters("[{(,.;:").concat(["::"]));
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-17 10:10:18 +00:00
Cola.is_identifier = function (name, is_js) {
return (is_js && !Cola.RESERVED_WORDS(name) || !is_js && !Cola.cRESERVED_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 18:39:27 +00:00
Cola.Tokenizer = function ($TEXT, filename, is_js, html5_comments) {
2014-04-16 16:43:40 +00:00
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,
comments_before : []
};
2014-05-16 12:27:51 +00:00
this.dumps = [];
this.dumpi = -1;
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
if(!is_js) this.S.string = {
at : [ new Cola.Tokenizer.StringInfo() ],
level : 0
};
2014-04-17 10:10:18 +00:00
if(is_js){
this.KEYWORDS = Cola.KEYWORDS;
this.KEYWORDS_ATOM = Cola.KEYWORDS_ATOM;
this.OPERATORS = Cola.OPERATORS;
this.UNARY_POSTFIX = Cola.UNARY_POSTFIX;
2014-05-19 09:58:17 +00:00
this.KEYWORDS_BEFORE_EXPRESSION = Cola.KEYWORDS_BEFORE_EXPRESSION;
this.PUNC_BEFORE_EXPRESSION = Cola.PUNC_BEFORE_EXPRESSION;
2014-04-17 10:10:18 +00:00
} else {
this.KEYWORDS = Cola.cKEYWORDS;
this.KEYWORDS_ATOM = Cola.cKEYWORDS_ATOM;
this.OPERATORS = Cola.cOPERATORS;
this.UNARY_POSTFIX = Cola.cUNARY_POSTFIX;
2014-05-19 09:58:17 +00:00
this.KEYWORDS_BEFORE_EXPRESSION = Cola.cKEYWORDS_BEFORE_EXPRESSION;
this.PUNC_BEFORE_EXPRESSION = Cola.cPUNC_BEFORE_EXPRESSION;
2014-04-17 10:10:18 +00:00
}
2014-04-16 18:39:27 +00:00
this.is_js = !!is_js;
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 18:39:27 +00:00
Cola.Tokenizer.StringInfo = function () {
2014-04-16 16:43:40 +00:00
this.quote = '';
this.inside = false;
this.after_at = false;
this.inside_at = false;
this.inside_braces = false;
this.balance = 0;
};
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.with_eof_error = function (eof_error, cont) {
2014-04-16 16:43:40 +00:00
return function(x) {
try {
2014-04-16 18:39:27 +00:00
return cont.call(this, x);
2014-04-16 16:43:40 +00:00
} 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-19 18:04:11 +00:00
Cola.Tokenizer.prototype.dumpS = function () {
2014-05-16 12:27:51 +00:00
this.dumps[++this.dumpi] = Cola.clone(this.S);
2014-04-19 18:04:11 +00:00
};
Cola.Tokenizer.prototype.restoreS = function () {
2014-05-16 12:27:51 +00:00
if(this.dumpi == -1) return;
this.S = this.dumps[this.dumpi];
delete this.dumps[this.dumpi--];
2014-04-19 18:04:11 +00:00
};
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.peek = function (offset) { return this.S.text.charAt(this.S.pos + (offset ? offset : 0)); };
2014-04-16 16:43:40 +00:00
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.next = function (signal_eof, in_string) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.forward = function (i) {
2014-04-16 16:43:40 +00:00
while (i-- > 0) this.next();
};
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.looking_at = function (str) {
2014-04-16 16:43:40 +00:00
return this.S.text.substr(this.S.pos, str.length) == str;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.find = function (what, signal_eof) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.start_token = function () {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.token = function (type, value, is_comment) {
this.S.regex_allowed = ((type == "operator" && !this.UNARY_POSTFIX(value)) ||
2014-05-19 09:58:17 +00:00
(type == "keyword" && this.KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && this.PUNC_BEFORE_EXPRESSION(value)));
this.prev_was_dot = (type == "punc" && value == ".") || (!this.is_js && type == "punc" && value == "::");
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.skip_whitespace = function () {
2014-04-16 16:43:40 +00:00
while (Cola.WHITESPACE_CHARS(this.peek()))
this.next();
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_while = function (pred) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.parse_error = function (err) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.read_num = function (prefix) {
2014-04-22 13:33:43 +00:00
var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", _this = this;
2014-04-16 16:43:40 +00:00
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): // .
2014-04-22 13:33:43 +00:00
if(!_this.is_js && _this.peek(1) == '.') return false;
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.read_escaped_char = function (in_string) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.hex_bytes = function (n) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.read_string = Cola.Tokenizer.with_eof_error("Unterminated string constant", function(raw){
2014-04-16 16:43:40 +00:00
var quote = this.next(), ret = "";
2014-04-16 18:39:27 +00:00
if (!this.is_js && !raw) {
2014-04-16 16:43:40 +00:00
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++;
2014-04-16 18:39:27 +00:00
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;
2014-04-28 08:20:12 +00:00
if (this.peek() == '@' || this.peek() == '{' && this.peek(1) == '{') return this.token("string", ret);
2014-04-16 16:43:40 +00:00
}
for (;;) {
var ch = this.next(true);
2014-04-16 18:39:27 +00:00
if ((!raw || this.is_js) && ch == "\\") {
2014-04-16 16:43:40 +00:00
// 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) {
2014-04-16 18:39:27 +00:00
if(!this.is_js && !raw){
2014-04-16 16:43:40 +00:00
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;
2014-04-16 18:39:27 +00:00
if (!this.is_js && !raw && (this.peek() == '@' || this.peek() == '{' && this.peek(1) == '{')) break;
2014-04-16 16:43:40 +00:00
}
2014-04-16 16:43:40 +00:00
return this.token("string", ret);
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_at = function (){
2014-04-16 16:43:40 +00:00
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-05-19 17:29:01 +00:00
} else return this.token("punc", "@");
2014-04-16 16:43:40 +00:00
this.parse_error('Unexpected character "@"');
}
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_braces = function (){
2014-04-16 16:43:40 +00:00
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", "{{");
}
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.skip_line_comment = function (type) {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.skip_multiline_comment = Cola.Tokenizer.with_eof_error("Unterminated multiline comment", function(){
2014-04-16 16:43:40 +00:00
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();
});
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_name = function () {
2014-04-16 16:43:40 +00:00
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
}
2014-04-17 10:10:18 +00:00
if (this.KEYWORDS(name) && escaped) {
2014-04-16 16:43:40 +00:00
hex = name.charCodeAt(0).toString(16).toUpperCase();
name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
}
2014-04-16 18:39:27 +00:00
if (!this.is_js && 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;
2014-04-16 16:43:40 +00:00
return name;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_regexp = Cola.Tokenizer.with_eof_error("Unterminated regular expression", function(regexp){
2014-04-16 16:43:40 +00:00
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();
2014-04-16 18:39:27 +00:00
return this.token("regexp", this.is_js ? new RegExp(regexp, mods) : "/" + regexp + "/" + mods);
2014-04-16 16:43:40 +00:00
});
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.read_operator = function (prefix) {
var _this = this;
2014-04-16 16:43:40 +00:00
function grow(op) {
2014-04-16 18:39:27 +00:00
if (!_this.peek()) return op;
var bigger = op + _this.peek();
2014-04-17 10:10:18 +00:00
if (_this.OPERATORS(bigger)) {
2014-04-16 18:39:27 +00:00
_this.next();
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.handle_slash = function () {
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.handle_dot = function () {
2014-04-16 16:43:40 +00:00
this.next();
2014-04-27 20:30:25 +00:00
if (!this.is_js && this.peek() == '.' && this.peek(1) != ".") return this.next(), this.token("punc", "..");
else if (!this.is_js && this.peek() == '.' && this.peek(1) == ".") return this.next(), this.next(), this.token("punc", "...");
2014-04-16 16:43:40 +00:00
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 18:39:27 +00:00
Cola.Tokenizer.prototype.read_word = function () {
2014-04-16 16:43:40 +00:00
var word = this.read_name();
if (this.prev_was_dot) return this.token("name", word);
2014-04-17 10:10:18 +00:00
return this.KEYWORDS_ATOM(word) ? this.token("atom", word)
: !this.KEYWORDS(word) ? this.token("name", word)
: this.OPERATORS(word) ? this.token("operator", word)
2014-04-16 16:43:40 +00:00
: this.token("keyword", word);
2012-05-27 11:09:01 +00:00
};
2014-04-16 18:39:27 +00:00
Cola.Tokenizer.prototype.next_token = function (force_regexp) {
2014-04-16 16:43:40 +00:00
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;
2014-04-16 18:39:27 +00:00
if (!this.is_js && 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) {
2014-04-16 16:43:40 +00:00
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);
2014-04-17 10:10:18 +00:00
if(!this.is_js && code == 96) return this.read_string();
switch (code) {
2014-04-16 18:39:27 +00:00
case 34: case 39: return this.read_string();
2014-04-16 16:43:40 +00:00
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.is_js && ch == ":" && this.peek(1) == ":") return this.next(), this.next(), this.token("punc", "::");
2014-04-16 18:39:27 +00:00
if (!this.is_js && 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)) {
2014-04-16 16:43:40 +00:00
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());
}
2014-04-20 18:56:47 +00:00
if (!this.is_js) {
if (ch == 'r' && (this.peek(1) == '"' || this.peek(1) == "'" || this.peek(1) == '`')) return this.next(), this.read_string(true);
if (ch + this.peek(1) == '=>') return this.next(), this.next(), this.token("punc", "=>");
if (ch == '@') return this.read_at();
}
2014-04-16 16:43:40 +00:00
if (Cola.OPERATOR_CHARS(ch)) return this.read_operator();
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 18:39:27 +00:00
Cola.Tokenizer.prototype.context = function(nc) {
2014-04-16 16:43:40 +00:00
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([
"clone",
"isset",
2012-05-27 11:09:01 +00:00
"typeof",
"delete",
"--",
"++",
"!",
"~",
"-",
"+"
]);
2014-04-16 16:43:40 +00:00
Cola.UNARY_POSTFIX = Cola.makePredicate([ "--", "++" ]);
Cola.cUNARY_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([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "?=" ]);
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
[
["||"],
["&&"],
["|"],
["^"],
["&"],
["==", "===", "!=", "!=="],
2014-05-16 12:27:51 +00:00
["<", ">", "<=", ">=", "in", "instanceof", "is", "isnt"],
2012-05-27 11:09:01 +00:00
[">>", "<<", ">>>"],
["+", "-"],
2014-05-16 12:27:51 +00:00
["*", "/", "%", "**", "%%"]
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-18 06:10:20 +00:00
Cola.parse = function ($TEXT, options) {
return (new Cola.Parser($TEXT, options)).parse();
2014-04-16 18:39:27 +00:00
};
2014-04-18 06:10:20 +00:00
Cola.Parser = function ($TEXT, options) {
2014-04-16 18:39:27 +00:00
var _this = this;
2014-04-16 18:39:27 +00:00
this.options = Cola.defaults(options, {
strict : false,
filename : null,
toplevel : null,
expression : false,
2014-04-18 06:10:20 +00:00
html5_comments : true,
is_js : false
});
2012-05-27 11:09:01 +00:00
this.is_js = !!this.options.is_js;
2014-04-18 06:10:20 +00:00
this.tokenizer = typeof $TEXT == "string" ? new Cola.Tokenizer($TEXT, this.options.filename, this.is_js, this.options.html5_comments) : $TEXT;
2014-04-16 16:43:40 +00:00
2014-04-16 18:39:27 +00:00
this.S = {
input : function(){ return _this.tokenizer.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 : []
};
2014-04-16 18:39:27 +00:00
this.S.input.context = function(){ return _this.tokenizer.context() };
this.S.token = this.next();
2014-05-16 12:27:51 +00:00
this.dumps = [];
this.dumpi = -1;
2014-04-17 10:10:18 +00:00
2014-04-18 06:10:20 +00:00
if(this.is_js){
2014-04-17 10:10:18 +00:00
this.UNARY_PREFIX = Cola.UNARY_PREFIX;
this.PRECEDENCE = Cola.PRECEDENCE;
this.ASSIGNMENT = Cola.ASSIGNMENT;
this.UNARY_POSTFIX = Cola.UNARY_POSTFIX;
2014-04-17 10:10:18 +00:00
} else {
this.UNARY_PREFIX = Cola.cUNARY_PREFIX;
this.PRECEDENCE = Cola.cPRECEDENCE;
this.ASSIGNMENT = Cola.cASSIGNMENT;
this.UNARY_POSTFIX = Cola.cUNARY_POSTFIX;
2014-04-17 10:10:18 +00:00
}
2014-04-16 18:39:27 +00:00
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.parse = function () {
var _this = this;
if (this.options.expression) {
return this.expression(true);
}
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
return (function(){
var start = _this.S.token;
var body = [];
while (!_this.is("eof"))
body.push(_this.statement());
var end = _this.prev();
var toplevel = _this.options.toplevel;
if (toplevel) {
toplevel.body = toplevel.body.concat(body);
toplevel.end = end;
2012-05-27 11:09:01 +00:00
} else {
2014-04-16 18:39:27 +00:00
toplevel = new Cola.AST_Toplevel({ start: start, body: body, end: end });
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
toplevel.language = _this.is_js ? 'js' : 'cola';
return toplevel;
})();
};
2012-05-27 11:09:01 +00:00
2014-04-19 18:04:11 +00:00
Cola.Parser.prototype.dumpS = function () {
2014-05-16 12:27:51 +00:00
this.dumps[++this.dumpi] = Cola.clone(this.S);
2014-04-19 18:04:11 +00:00
this.tokenizer.dumpS();
};
Cola.Parser.prototype.restoreS = function () {
2014-05-16 12:27:51 +00:00
if(this.dumpi == -1) return;
2014-04-19 18:04:11 +00:00
this.tokenizer.restoreS();
2014-05-16 12:27:51 +00:00
this.S = this.dumps[this.dumpi];
delete this.dumps[this.dumpi--];
2014-04-19 18:04:11 +00:00
};
Cola.Parser.prototype.next_until = function (until) {
while(true){
if(until(this.S.token)) break;
this.next();
}
return this.S.token;
}
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.is = function (type, value) {
return Cola.is_token(this.S.token, type, value);
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.peek = function () { return this.S.peeked || (this.S.peeked = this.S.input()); };
2012-05-27 11:09:01 +00:00
2014-05-16 12:27:51 +00:00
Cola.Parser.prototype.next_is = function (type, value) {
return Cola.is_token(this.peek(), type, value);
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.next = function () {
this.S.prev = this.S.token;
if (this.S.peeked) {
this.S.token = this.S.peeked;
this.S.peeked = null;
} else {
this.S.token = this.S.input();
}
this.S.in_directives = this.S.in_directives && (
this.S.token.type == "string" || this.is("punc", ";")
);
return this.S.token;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.prev = function () {
return this.S.prev;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.croak = function (msg, line, col, pos) {
2014-05-19 09:58:17 +00:00
var ctx = this.S ? this.S.input.context() : {};
2014-04-16 18:39:27 +00:00
Cola.js_error(msg,
ctx.filename,
line != null ? line : ctx.tokline,
col != null ? col : ctx.tokcol,
pos != null ? pos : ctx.tokpos);
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.token_error = function (token, msg) {
2014-05-19 09:58:17 +00:00
this.croak(msg, token.line, token.col, token.pos);
2014-04-16 18:39:27 +00:00
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.unexpected = function (token) {
if (token == null)
token = this.S.token;
this.token_error(token, "Unexpected token: " + token.type + " `" + token.value + "`");
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.expect_token = function (type, val) {
if (this.is(type, val)) {
return this.next();
}
this.token_error(this.S.token, "Unexpected token " + this.S.token.type + " `" + this.S.token.value + "`" + ", expected " + type + " `" + val + "`");
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.expect = function (punc) { return this.expect_token("punc", punc); };
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.can_insert_semicolon = function () {
2014-04-27 20:30:25 +00:00
if(!this.is_js) return false;
2014-04-16 18:39:27 +00:00
return !this.options.strict && (
this.S.token.nlb || this.is("eof") || this.is("punc", "}")
);
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.semicolon = function () {
if (this.is("punc", ";")) this.next();
else if (!this.can_insert_semicolon()) this.unexpected();
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.parenthesised = function () {
this.expect("(");
var exp = this.expression(true);
this.expect(")");
return exp;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.embed_tokens = function (parser) {
return function() {
var start = this.S.token;
2014-05-16 12:27:51 +00:00
var expr = parser.apply(this, arguments);
2014-04-16 18:39:27 +00:00
var end = this.prev();
expr.start = start;
expr.end = end;
return expr;
};
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.handle_regexp = function () {
if (this.is("operator", "/") || this.is("operator", "/=")) {
this.S.peeked = null;
this.S.token = this.S.input(this.S.token.value.substr(1)); // force regexp
}
};
Cola.Parser.prototype.statement = Cola.Parser.embed_tokens(function() {
var tmp, type, _this = this;
2014-04-16 18:39:27 +00:00
this.handle_regexp();
switch (this.S.token.type) {
case "string":
var dir = this.S.in_directives, stat = this.simple_statement();
// XXXv2: decide how to fix directives
if (dir && stat.body instanceof Cola.AST_String && !this.is("punc", ","))
return new Cola.AST_Directive({ value: stat.body.value });
return stat;
case "num":
case "regexp":
case "operator":
case "atom":
return this.simple_statement();
case "name":
2014-05-16 12:27:51 +00:00
if(!this.is_js && this.next_is("name")){
2014-04-19 18:04:11 +00:00
type = this.S.token.value, this.next();
2014-05-16 12:27:51 +00:00
if(this.next_is("punc", "(")) return this.function_(Cola.AST_Defun, type);
2014-04-19 18:04:11 +00:00
return tmp = this.var_(false, type), this.semicolon(), tmp;
}
if(!this.is_js){
var _this = this, balance = 1, isfun = false;
this.dumpS();
this.next();
if(this.is('punc', '(')){
this.next();
this.next_until(function(){
if(_this.is('punc', '(')) balance++;
else if(_this.is('punc', ')')) balance--;
return balance == 0 || _this.is('eof');
});
2014-04-20 15:38:58 +00:00
isfun = (this.next(), (this.is('punc','{') || this.is('punc','=>')));
2014-04-19 18:04:11 +00:00
}
this.restoreS();
if(isfun) return this.function_(Cola.AST_Defun);
}
2014-05-16 12:27:51 +00:00
return this.next_is("punc", ":")
2014-04-16 18:39:27 +00:00
? this.labeled_statement()
: this.simple_statement();
case "punc":
switch (this.S.token.value) {
2014-05-19 17:29:01 +00:00
case "@":
if (!this.is_js && this.next_is("name"))
return new Cola.AST_Command({
name : this.next().value,
2014-05-22 15:44:11 +00:00
args : (function(name){
2014-05-19 17:29:01 +00:00
var args = [];
2014-05-22 15:44:11 +00:00
if (name == 'require' || name == 'include') {
while (!this.peek().nlb && !this.next_is('eof')){
this.next();
if (!this.is("string")) this.unexpected();
args.push(this.S.token.value);
}
this.next();
} else
if (name == 'use') {
this.next();
if (!this.is("name")) this.unexpected();
args.push(this.S.token.value);
if (this.S.token.value == 'closure') {
2014-05-19 17:29:01 +00:00
this.next();
2014-05-22 15:44:11 +00:00
args.push(this.is("punc","{"), new Cola.AST_BlockStatement({
2014-05-19 17:29:01 +00:00
start : this.S.token,
2014-05-22 15:44:11 +00:00
body : this.block_(!this.is("punc","{")),
2014-05-19 17:29:01 +00:00
end : this.prev()
}));
} else
2014-05-22 15:44:11 +00:00
if (this.S.token.value != "asmjs" && this.S.token.value != "strict") this.unexpected();
} else this.unexpected();
2014-05-19 17:29:01 +00:00
return args;
2014-05-22 15:44:11 +00:00
}).call(this, this.S.token.value)
2014-05-19 17:29:01 +00:00
});
2014-04-16 18:39:27 +00:00
case "{":
2014-05-19 17:29:01 +00:00
if (this.is_js) return new Cola.AST_BlockStatement({
start : this.S.token,
body : this.block_(),
end : this.prev()
});
2014-05-16 12:27:51 +00:00
this.dumpS();
var balance = 0, is_object = false;
this.next_until(function(){
if(_this.is('punc', '{')) balance++;
else if(_this.is('punc', '}')) balance--;
return balance == 0 || _this.is('eof');
});
is_object = this.next_is("operator");
this.restoreS();
if (is_object) return this.simple_statement();
2014-04-16 18:39:27 +00:00
return new Cola.AST_BlockStatement({
start : this.S.token,
body : this.block_(),
end : this.prev()
});
2014-05-16 12:27:51 +00:00
2014-04-16 18:39:27 +00:00
case "[":
case "(":
return this.simple_statement();
case ";":
this.next();
return new Cola.AST_EmptyStatement();
default:
this.unexpected();
}
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "keyword":
switch (tmp = this.S.token.value, this.next(), tmp) {
case "break":
return this.break_cont(Cola.AST_Break);
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "continue":
return this.break_cont(Cola.AST_Continue);
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "debugger":
this.semicolon();
return new Cola.AST_Debugger();
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "do":
return new Cola.AST_Do({
2014-05-19 09:58:17 +00:00
body : this.in_loop(function(){ return this.statement() }),
2014-04-16 18:39:27 +00:00
condition : (this.expect_token("keyword", "while"), tmp = this.parenthesised(), this.semicolon(), tmp)
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "while":
return new Cola.AST_While({
condition : this.parenthesised(),
2014-05-19 09:58:17 +00:00
body : this.in_loop(function(){ return this.statement() })
2014-04-16 18:39:27 +00:00
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "for":
return this.for_();
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "function":
return this.function_(Cola.AST_Defun);
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "if":
return this.if_();
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "return":
if (this.S.in_function == 0)
this.croak("'return' outside of function");
return new Cola.AST_Return({
value: ( this.is("punc", ";")
? (this.next(), null)
: this.can_insert_semicolon()
? null
: (tmp = this.expression(true), this.semicolon(), tmp) )
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "switch":
return new Cola.AST_Switch({
2014-05-19 09:58:17 +00:00
expression : this.is('punc', '{') && !this.is_js ? new Cola.AST_Noop() : this.parenthesised(),
body : this.in_loop(this.switch_body_)
2012-05-27 11:09:01 +00:00
});
2014-04-16 18:39:27 +00:00
case "throw":
if (this.S.token.nlb)
this.croak("Illegal newline after 'throw'");
return new Cola.AST_Throw({
value: (tmp = this.expression(true), this.semicolon(), tmp)
2012-05-27 14:25:31 +00:00
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "try":
return this.try_();
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "var":
return tmp = this.var_(), this.semicolon(), tmp;
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "const":
return tmp = this.const_(), this.semicolon(), tmp;
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
case "with":
return new Cola.AST_With({
expression : this.parenthesised(),
body : this.statement()
});
default:
this.unexpected();
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
}
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.labeled_statement = function () {
var label = this.as_symbol(Cola.AST_Label), _this = this;
if (Cola.find_if(function(l){ return l.name == label.name }, this.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.
this.croak("Label " + label.name + " defined twice");
}
this.expect(":");
this.S.labels.push(label);
var stat = this.statement();
this.S.labels.pop();
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){
if (ref instanceof Cola.AST_Continue) {
ref = ref.label.start;
_this.croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
ref.line, ref.col, ref.pos);
}
2014-04-16 18:39:27 +00:00
});
}
return new Cola.AST_LabeledStatement({ body: stat, label: label });
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.simple_statement = function (tmp) {
return new Cola.AST_SimpleStatement({ body: (tmp = this.expression(true), this.semicolon(), tmp) });
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.break_cont = function (type) {
var label = null, ldef;
if (!this.can_insert_semicolon()) {
label = this.as_symbol(Cola.AST_LabelRef, true);
}
2014-04-16 18:39:27 +00:00
if (label != null) {
ldef = Cola.find_if(function(l){ return l.name == label.name }, this.S.labels);
if (!ldef)
this.croak("Undefined label " + label.name);
label.thedef = ldef;
}
else if (this.S.in_loop == 0)
this.croak(type.TYPE + " not inside a loop or switch");
this.semicolon();
var stat = new type({ label: label });
if (ldef) ldef.references.push(stat);
return stat;
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.for_ = function () {
this.expect("(");
var init = null;
if (!this.is("punc", ";")) {
init = this.is("keyword", "var")
? (this.next(), this.var_(true))
: this.expression(true, true);
if (this.is("operator", "in")) {
if (init instanceof Cola.AST_Var && init.definitions.length > 1)
this.croak("Only one variable declaration allowed in for..in loop");
this.next();
return this.for_in(init);
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
}
return this.regular_for(init);
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.regular_for = function (init) {
this.expect(";");
var test = this.is("punc", ";") ? null : this.expression(true);
this.expect(";");
var step = this.is("punc", ")") ? null : this.expression(true);
this.expect(")");
2014-04-17 10:10:18 +00:00
var _this = this;
2014-04-16 18:39:27 +00:00
return new Cola.AST_For({
init : init,
condition : test,
step : step,
2014-05-19 09:58:17 +00:00
body : this.in_loop(function(){ return this.statement() })
2014-04-16 18:39:27 +00:00
});
};
Cola.Parser.prototype.for_in = function (init) {
var lhs = init instanceof Cola.AST_Var ? init.definitions[0].name : null;
var obj = this.expression(true);
this.expect(")");
2014-04-17 10:10:18 +00:00
var _this = this;
2014-04-16 18:39:27 +00:00
return new Cola.AST_ForIn({
init : init,
name : lhs,
object : obj,
2014-05-19 09:58:17 +00:00
body : this.in_loop(function(){ return this.statement() })
2014-04-16 18:39:27 +00:00
});
};
2014-04-27 20:30:25 +00:00
Cola.Parser.prototype.as_funcarg = function(splatedexist) {
var name = this.as_symbol(Cola.AST_SymbolFunarg), type = name, argtype = 'positional', defval = new Cola.AST_Noop();
if(this.is("name")) name = this.as_symbol(Cola.AST_SymbolFunarg);
if(this.is("punc", "...")){
if(splatedexist) this.token_error(this.S, "Unexpected token: splated argument can be only one.");
if(type != name && type.name != "Array") this.token_error(this.S, "Unexpected token: splated argument must have `Array` type.");
this.next();
argtype = "splated";
} else if(this.is("operator", "=")){
this.next();
defval = this.expression(false);
} else if(this.is("punc",":")){
this.next();
argtype = "named";
if(!this.is("punc",",") && !this.is("punc",")")){
defval = this.expression(false);
}
}
2014-05-16 12:27:51 +00:00
return new Cola.AST_ArgDef({
2014-04-27 20:30:25 +00:00
name : name,
type : argtype == "splated" ? "Array" : type == name ? "dynamic" : type.name,
argtype : argtype,
defval : defval,
start : type.start,
end : this.prev()
});
};
2014-04-19 18:04:11 +00:00
Cola.Parser.prototype.function_ = function(ctor, type) {
!type && (type = "dynamic");
2014-04-27 20:30:25 +00:00
var in_statement = ctor === Cola.AST_Defun, _this = this, splatedexist = false;
2014-04-16 18:39:27 +00:00
var name = this.is("name") ? this.as_symbol(in_statement ? Cola.AST_SymbolDefun : Cola.AST_SymbolLambda) : null;
if (in_statement && !name)
this.unexpected();
this.expect("(");
return new ctor({
2014-04-19 18:04:11 +00:00
type: type,
2014-04-16 18:39:27 +00:00
name: name,
argnames: (function(first, a){
while (!_this.is("punc", ")")) {
if (first) first = false; else _this.expect(",");
2014-04-27 20:30:25 +00:00
a.push(_this.is_js ? _this.as_symbol(Cola.AST_SymbolFunarg) : _this.as_funcarg(splatedexist));
splatedexist = a[a.length - 1].argtype == "splated";
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
_this.next();
return a;
})(true, []),
body: (function(loop, labels){
++_this.S.in_function;
_this.S.in_directives = true;
_this.S.in_loop = 0;
_this.S.labels = [];
2014-04-20 15:38:58 +00:00
var tmp, a = !_this.is_js && _this.is("punc", "=>")
? (_this.next(), [new Cola.AST_Return({
start: new Cola.AST_Token({ nlb : false, type : 'keyword', value : 'return' }),
value: (function(){
tmp = _this.expression(true);
if ( ctor === Cola.AST_Defun ) {
_this.semicolon();
_this.next();
}
return tmp;
})(),
end : _this.prev()
})])
: _this.block_();
2014-04-16 18:39:27 +00:00
--_this.S.in_function;
_this.S.in_loop = loop;
_this.S.labels = labels;
return a;
})(this.S.in_loop, this.S.labels)
});
};
2014-05-21 18:01:32 +00:00
Cola.Parser.prototype.if_ = function (inline) {
var cond = this.parenthesised(), body = (inline && !this.is_js ? this.expression(true) : this.statement()), belse = null;
2014-04-16 18:39:27 +00:00
if (this.is("keyword", "else")) {
this.next();
2014-05-21 18:01:32 +00:00
if (inline && !this.is_js) belse = this.is("keyword", "if") ? (this.next(), this.if_(true)) : this.expression(true);
else belse = this.statement();
2014-04-16 18:39:27 +00:00
}
return new Cola.AST_If({
condition : cond,
body : body,
2014-05-21 18:01:32 +00:00
alternative : belse,
inline : inline
2014-04-16 18:39:27 +00:00
});
};
2014-05-22 15:44:11 +00:00
Cola.Parser.prototype.block_ = function (to_eof) {
if(this.is_js || !to_eof) this.expect("{");
2014-04-16 18:39:27 +00:00
var a = [];
2014-05-22 15:44:11 +00:00
if (this.is_js || !to_eof)
while (!this.is("punc", "}")) {
if (this.is("eof")) this.unexpected();
a.push(this.statement());
}
else
while (!this.is("eof")) a.push(this.statement());
2014-04-16 18:39:27 +00:00
this.next();
return a;
};
Cola.Parser.prototype.switch_body_ = function () {
this.expect("{");
var a = [], cur = null, branch = null, tmp;
while (!this.is("punc", "}")) {
if (this.is("eof")) this.unexpected();
2014-05-19 09:58:17 +00:00
if (this.is("keyword", "case") || !this.is_js && this.is("keyword", "when")) {
2014-04-16 18:39:27 +00:00
if (branch) branch.end = this.prev();
cur = [];
2014-05-19 09:58:17 +00:00
branch = new (!this.is_js && this.is("keyword", "when") ? Cola.AST_When : Cola.AST_Case)({
2014-04-16 18:39:27 +00:00
start : (tmp = this.S.token, this.next(), tmp),
expression : this.expression(true),
body : cur
});
a.push(branch);
this.expect(":");
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
else if (this.is("keyword", "default")) {
if (branch) branch.end = this.prev();
cur = [];
branch = new Cola.AST_Default({
start : (tmp = this.S.token, this.next(), this.expect(":"), tmp),
body : cur
});
a.push(branch);
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
else {
if (!cur) this.unexpected();
cur.push(this.statement());
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
}
if (branch) branch.end = this.prev();
this.next();
return a;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.try_ = function () {
var body = this.block_(), bcatch = null, bfinally = null;
if (this.is("keyword", "catch")) {
var start = this.S.token;
this.next();
this.expect("(");
var name = this.as_symbol(Cola.AST_SymbolCatch);
this.expect(")");
bcatch = new Cola.AST_Catch({
start : start,
argname : name,
body : this.block_(),
end : this.prev()
2012-05-27 11:09:01 +00:00
});
2014-04-16 18:39:27 +00:00
}
if (this.is("keyword", "finally")) {
var start = this.S.token;
this.next();
bfinally = new Cola.AST_Finally({
start : start,
body : this.block_(),
end : this.prev()
});
}
if (!bcatch && !bfinally)
this.croak("Missing catch/finally blocks");
return new Cola.AST_Try({
body : body,
bcatch : bcatch,
bfinally : bfinally
2012-05-27 14:25:31 +00:00
});
2014-04-16 18:39:27 +00:00
};
2012-05-27 11:09:01 +00:00
2014-05-16 12:27:51 +00:00
Cola.Parser.prototype.vardefs = function (no_in, in_const, type) {
var a = [], was_template = false;
2014-04-16 18:39:27 +00:00
for (;;) {
2014-05-16 12:27:51 +00:00
was_template = false;
2014-04-16 18:39:27 +00:00
a.push(new Cola.AST_VarDef({
start : this.S.token,
2014-05-16 12:27:51 +00:00
type : type,
name : (function(_this){
was_template = !_this.is_js && ( _this.is("punc","[") || _this.is("punc","{") );
if (!_this.is_js && _this.is("punc","[")) return _this.array_(true, true);
if (!_this.is_js && _this.is("punc","{")) return _this.object_(true, true);
return _this.as_symbol(in_const ? Cola.AST_SymbolConst : Cola.AST_SymbolVar);
})(this),
value : this.is("operator", "=") ? (this.next(), this.expression(false, no_in)) : ( was_template ? this.expect_token("operator","=") : null ),
2014-04-16 18:39:27 +00:00
end : this.prev()
}));
if (!this.is("punc", ","))
break;
this.next();
}
return a;
};
Cola.Parser.prototype.var_ = function(no_in, type) {
!type && (type = "dynamic");
2014-04-16 18:39:27 +00:00
return new Cola.AST_Var({
start : this.prev(),
2014-05-16 12:27:51 +00:00
definitions : this.vardefs(no_in, false, type),
type : type,
2014-04-16 18:39:27 +00:00
end : this.prev()
2012-05-27 11:09:01 +00:00
});
2014-04-16 18:39:27 +00:00
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.const_ = function() {
return new Cola.AST_Const({
start : this.prev(),
definitions : this.vardefs(false, true),
end : this.prev()
});
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.new_ = function() {
var start = this.S.token;
this.expect_token("operator", "new");
var newexp = this.expr_atom(false), args;
if (this.is("punc", "(")) {
this.next();
2014-04-28 08:20:12 +00:00
args = this.expr_list(")", false, false, !this.is_js);
2014-04-16 18:39:27 +00:00
} else {
args = [];
}
return this.subscripts(new Cola.AST_New({
start : start,
expression : newexp,
args : args,
end : this.prev()
}), true);
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.string_template_ = function (start_token) {
var body = [new Cola.AST_String({ start: start_token, end: start_token, value: start_token.value })];
this.next();
2014-04-16 18:39:27 +00:00
while (this.is('punc', '@') || this.is('punc', '@{') || this.is('punc', '{{') || this.is('string', null))
if (this.is('string', null)) {
body.push(new Cola.AST_String({ start: this.S.token, end: this.S.token, value: this.S.token.value }));
this.next();
} else
if (this.is('punc', '@')) {
this.next();
body.push(this._make_symbol(Cola.AST_SymbolRef));
this.next();
} else
if (this.is('punc', '@{')) {
this.next();
body.push(this.expression(true));
this.expect('}');
} else
if (this.is('punc', '{{')) {
this.next();
body.push(this.expression(true));
this.expect('}}');
}
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
var last = body[body.length - 1];
body[0].value = body[0].value.replace(/^[ \t]*[\n\r]/,'');
if (last instanceof Cola.AST_String) {
var offstr = last.value.match(/[\n\r][ \t]*$/);
if(offstr){
offstr = offstr[0];
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]$/,'');
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
}
if (body.length == 1) return body[0];
return new Cola.AST_StringTemplate({
start : start_token,
end : this.prev(),
body : body
});
}
Cola.Parser.prototype.as_atom_node = function () {
var tok = this.S.token, ret;
switch (tok.type) {
case "name":
case "keyword":
ret = this._make_symbol(Cola.AST_SymbolRef);
break;
case "num":
ret = new Cola.AST_Number({ start: tok, end: tok, value: tok.value });
break;
case "string":
if(this.is_js){
ret = new Cola.AST_String({ start: tok, end: tok, value: tok.value });
break;
} else return this.string_template_(tok);
case "regexp":
ret = new Cola.AST_RegExp({ start: tok, end: tok, value: tok.value });
break;
case "atom":
if(!this.is_js) switch (tok.value) {
case "off": case "no":
ret = new Cola.AST_False({ start: tok, end: tok });
break;
case "on": case "yes":
ret = new Cola.AST_True({ start: tok, end: tok });
break;
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
switch (tok.value) {
case "false":
ret = new Cola.AST_False({ start: tok, end: tok });
break;
case "true":
ret = new Cola.AST_True({ start: tok, end: tok });
break;
case "null":
ret = new Cola.AST_Null({ start: tok, end: tok });
break;
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
break;
}
this.next();
return ret;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.expr_atom = function(allow_calls) {
if (this.is("operator", "new")) {
return this.new_();
}
2014-04-27 20:30:25 +00:00
var type, start = this.S.token;
2014-04-16 18:39:27 +00:00
if (this.is("punc")) {
switch (start.value) {
case "(":
if(!this.is_js){
var _this = this, balance = 1, isfun = false;
this.dumpS();
this.next();
this.next_until(function(){
if(_this.is('punc', '(')) balance++;
else if(_this.is('punc', ')')) balance--;
return balance == 0 || _this.is('eof');
});
2014-04-20 15:38:58 +00:00
isfun = (this.next(), (this.is('punc','{') || this.is('punc','=>')));
this.restoreS();
if(isfun) return this.function_(Cola.AST_Function);
}
2014-04-16 18:39:27 +00:00
this.next();
var ex = this.expression(true);
2012-05-27 14:25:31 +00:00
ex.start = start;
2014-04-16 18:39:27 +00:00
ex.end = this.S.token;
this.expect(")");
return this.subscripts(ex, allow_calls);
case "[":
return this.subscripts(this.array_(), allow_calls);
case "{":
return this.subscripts(this.object_(), allow_calls);
2012-05-27 11:09:01 +00:00
}
2014-04-16 18:39:27 +00:00
this.unexpected();
}
2014-04-27 20:30:25 +00:00
if (!this.is_js && this.is("name")) {
2014-05-16 12:27:51 +00:00
if(this.next_is("name")){
2014-04-27 20:30:25 +00:00
type = this.S.token.value, this.next();
return this.function_(Cola.AST_Defun, type);
}
var _this = this, balance = 1, isfun = false;
this.dumpS();
this.next();
if(this.is('punc', '(')){
this.next();
this.next_until(function(){
if(_this.is('punc', '(')) balance++;
else if(_this.is('punc', ')')) balance--;
return balance == 0 || _this.is('eof');
});
isfun = (this.next(), (this.is('punc','{') || this.is('punc','=>')));
}
this.restoreS();
if(isfun) return this.function_(Cola.AST_Defun);
}
2014-04-16 18:39:27 +00:00
if (this.is("keyword", "function")) {
this.next();
var func = this.function_(Cola.AST_Function);
func.start = start;
func.end = this.prev();
return this.subscripts(func, allow_calls);
}
2014-05-21 18:01:32 +00:00
if (this.is("keyword", "if") && !this.is_js) {
this.next();
var f = this.if_(true), s = f;
/*while (true) {
if (s.body instanceof Cola.AST_BlockStatement && s.body.body.length != 1 ||
!(s.body instanceof Cola.AST_BlockStatement) && !(s.body instanceof Cola.AST_SimpleStatement)) this.unexpected(s.body.start);
if (s.alternative instanceof Cola.AST_If) s = f;
else if (s.alternative == null) break;
else {
if (s.alternative instanceof Cola.AST_BlockStatement && s.alternative.body.length != 1 ||
!(s.alternative instanceof Cola.AST_BlockStatement) && !(s.alternative instanceof Cola.AST_SimpleStatement)) this.unexpected(s.alternative.start);
break;
}
}*/
return f;
}
2014-05-19 09:58:17 +00:00
if (this.is("keyword", "switch") && !this.is_js) {
this.next();
var swtch = {
start : start,
expression : this.is('punc', '{') ? new Cola.AST_Noop() : this.parenthesised(),
body : this.in_loop(this.switch_body_),
end : this.prev()
}, _this = this;
swtch.body.forEach(function(branch){
2014-05-21 18:01:32 +00:00
if (branch.body.length != 1 || !(branch.body[0] instanceof Cola.AST_SimpleStatement)) _this.unexpected(branch.start);
2014-05-19 09:58:17 +00:00
});
2014-05-19 17:29:01 +00:00
2014-05-19 09:58:17 +00:00
return new Cola.AST_Switch(swtch);
}
2014-04-16 18:39:27 +00:00
if (Cola.ATOMIC_START_TOKEN[this.S.token.type]) {
return this.subscripts(this.as_atom_node(), allow_calls);
}
this.unexpected();
};
2014-04-27 20:30:25 +00:00
Cola.Parser.prototype.expr_list = function (closing, allow_trailing_comma, allow_empty, allow_named_args) {
var first = true, a = [], name;
2014-04-16 18:39:27 +00:00
while (!this.is("punc", closing)) {
if (first) first = false; else this.expect(",");
if (allow_trailing_comma && this.is("punc", closing)) break;
if (this.is("punc", ",") && allow_empty) {
a.push(new Cola.AST_Hole({ start: this.S.token, end: this.S.token }));
2014-04-27 20:30:25 +00:00
} else
if(this.is("name") && allow_named_args){
name = Cola.$_cola_clone(this.S.token);
this.dumpS();
this.next();
if(this.is("punc",":")){
this.next();
a.push(new Cola.AST_Namedarg({
name : name.value,
value : this.expression(false),
start : name,
end : this.prev()
}));
} else {
this.restoreS();
a.push(this.expression(false))
}
} else a.push(this.expression(false));
2014-04-16 18:39:27 +00:00
}
this.next();
return a;
};
2012-05-27 11:09:01 +00:00
2014-05-16 12:27:51 +00:00
Cola.Parser.prototype.array_ = Cola.Parser.embed_tokens(function(is_template, is_var) {
2014-04-16 18:39:27 +00:00
this.expect("[");
2014-05-16 12:27:51 +00:00
if(!this.is_js && !this.is("punc","]") && !this.is("punc",",") && !(this.is("name") && this.next_is("name"))){
2014-04-22 13:33:43 +00:00
this.dumpS();
2014-05-16 12:27:51 +00:00
var tmp, from = this.expression(false, false, true), triple;
if((this.is("punc","..") || this.is("punc","...")) && !this.next_is("punc", ",") && !this.next_is("punc", "]")){
triple = this.is("punc","...");
2014-04-22 13:33:43 +00:00
this.next();
return new Cola.AST_ArrayRange({
2014-05-16 12:27:51 +00:00
from : from,
to : (tmp = this.expression(true, false, true), this.expect("]"), tmp),
triple : triple,
start : from.start,
end : this.prev()
2014-04-22 13:33:43 +00:00
});
}
this.restoreS();
}
2014-05-16 12:27:51 +00:00
if(this.is_js) return new Cola.AST_Array({
2014-04-16 18:39:27 +00:00
elements: this.expr_list("]", !this.options.strict, true)
});
2014-05-16 12:27:51 +00:00
var is_array = ( is_template ? false : null ), vardef = false, first = true, a = [], val, skiped = false;
while (!this.is("punc", "]")) {
if (first) first = false; else this.expect(",");
if (this.is("punc", ",") || this.is("punc", "]")) {
a.push(new Cola.AST_Hole({ start: this.S.token, end: this.S.token }));
if (!this.options.strict && this.is("punc", "]")) break;
} else
if (this.is("punc", "...") && is_array !== true) {
if (skiped) this.unexpected();
this.next();
skiped = true;
is_array = false;
a.push(new Cola.AST_Noop());
} else
if (!is_var && this.is("name") && this.next_is("name") && !Cola.cKEYWORDS(this.peek().value)) {
if (is_array === true) this.unexpected();
is_array = false;
vardef = true;
a.push(new Cola.AST_VarDef({
start : this.S.token,
type : this.S.token.value,
name : (function(_this){
_this.next();
val = _this.as_symbol(Cola.AST_SymbolVar);
if (_this.is("punc", "...") && is_array !== true) {
if (skiped) _this.unexpected();
_this.next();
skiped = true;
is_array = false;
val.splated = true;
}
return val;
})(this),
value : null,
end : this.prev()
}))
} else {
if (is_array === false && this.is("punc","[")) val = this.array_(true, is_var);
else if (is_array === false && this.is("punc","{")) val = this.object_(true, is_var);
else val = this.expression(false);
if (val.vardef) vardef = true;
if (this.is("punc", "...") && is_array !== true) {
if (skiped) this.unexpected();
this.next();
skiped = true;
is_array = false;
val.splated = true;
}
if (val instanceof Cola.AST_ObjectTemplate || val instanceof Cola.AST_ArrayTemplate) {
if (is_array === true) this.unexpected();
is_array = false;
}
if (!(val instanceof Cola.AST_SymbolRef ||
val instanceof Cola.AST_ObjectTemplate || val instanceof Cola.AST_ArrayTemplate ||
!is_var && ( val instanceof Cola.AST_Dot || val instanceof Cola.AST_Sub ) ||
val instanceof Cola.AST_Object && val.template == true ||
val instanceof Cola.AST_Array && val.template == true)) {
if (is_array === false) this.unexpected();
is_array = true;
}
a.push(val);
}
}
this.next();
return is_array === true || is_array === null
? new Cola.AST_Array({ elements: a, template: is_array === null, vardef : vardef })
: new Cola.AST_ArrayTemplate({ elements: a, vardef : vardef });
2014-04-16 18:39:27 +00:00
});
2012-05-27 11:09:01 +00:00
2014-05-16 12:27:51 +00:00
Cola.Parser.prototype.object_ = Cola.Parser.embed_tokens(function(is_template, is_var) {
2014-04-16 18:39:27 +00:00
this.expect("{");
2014-05-16 12:27:51 +00:00
var first = true, a = [], ptype, is_object = ( is_template ? false : null ), vardef = false, val;
2014-04-16 18:39:27 +00:00
while (!this.is("punc", "}")) {
if (first) first = false; else this.expect(",");
if (!this.options.strict && this.is("punc", "}"))
// allow trailing comma
break;
var start = this.S.token;
var type = start.type;
var name = this.as_property_name();
2014-05-16 12:27:51 +00:00
if (!this.is_js && !is_var && this.is("name") && !this.next_is("punc", "(") && !Cola.cKEYWORDS(name)) {
vardef = true;
ptype = name;
name = this.as_name();
} else ptype = false;
2014-04-16 18:39:27 +00:00
if (type == "name" && !this.is("punc", ":")) {
if (name == "get") {
2014-05-16 12:27:51 +00:00
if (!this.is_js && is_object === false) this.unexpected();
is_object = true;
2014-04-16 18:39:27 +00:00
a.push(new Cola.AST_ObjectGetter({
start : start,
2014-05-16 12:27:51 +00:00
type : ptype,
2014-04-16 18:39:27 +00:00
key : this.as_atom_node(),
value : this.function_(Cola.AST_Accessor),
end : this.prev()
}));
continue;
}
if (name == "set") {
2014-05-16 12:27:51 +00:00
if (!this.is_js && is_object === false) this.unexpected();
is_object = true;
2014-04-16 18:39:27 +00:00
a.push(new Cola.AST_ObjectSetter({
start : start,
2014-05-16 12:27:51 +00:00
type : ptype,
2014-04-16 18:39:27 +00:00
key : this.as_atom_node(),
value : this.function_(Cola.AST_Accessor),
end : this.prev()
}));
continue;
}
2012-05-27 11:09:01 +00:00
}
2014-05-16 12:27:51 +00:00
if (!this.is_js && !this.is("punc",":")) {
2014-05-21 18:01:32 +00:00
//if (is_object === true) this.unexpected();
//is_object = false;
val = new Cola.AST_SymbolRef({ name : name });
2014-05-16 12:27:51 +00:00
} else {
this.expect(":");
if (is_object === false && this.is("punc","[")) val = this.array_(true, is_var);
else if (is_object === false && this.is("punc","{")) val = this.object_(true, is_var);
else val = this.expression(false);
if (ptype && !(val instanceof Cola.AST_SymbolRef)) this.unexpected(val.start);
if (val.vardef) vardef = true;
if (val instanceof Cola.AST_ObjectTemplate || val instanceof Cola.AST_ArrayTemplate) {
if (is_object === true || ptype) this.unexpected();
is_object = false;
}
if (!(val instanceof Cola.AST_SymbolRef ||
val instanceof Cola.AST_ObjectTemplate || val instanceof Cola.AST_ArrayTemplate ||
!is_var && ( val instanceof Cola.AST_Dot || val instanceof Cola.AST_Sub ) ||
val instanceof Cola.AST_Object && val.template == true ||
val instanceof Cola.AST_Array && val.template == true)) {
if (is_object === false) this.unexpected();
is_object = true;
}
}
2014-04-16 18:39:27 +00:00
a.push(new Cola.AST_ObjectKeyVal({
start : start,
2014-05-16 12:27:51 +00:00
type : ptype,
2014-04-16 18:39:27 +00:00
key : name,
2014-05-16 12:27:51 +00:00
value : val,
2014-04-16 18:39:27 +00:00
end : this.prev()
}));
}
this.next();
2014-05-16 12:27:51 +00:00
return is_object === true || is_object === null
? new Cola.AST_Object({ properties: a, template: is_object === null, vardef : vardef })
: new Cola.AST_ObjectTemplate({ properties: a, vardef : vardef });
2014-04-16 18:39:27 +00:00
});
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.as_property_name = function () {
var tmp = this.S.token;
this.next();
switch (tmp.type) {
case "num":
case "string":
case "name":
case "operator":
case "keyword":
case "atom":
return tmp.value;
default:
this.unexpected();
}
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.as_name = function () {
var tmp = this.S.token;
this.next();
switch (tmp.type) {
case "name":
case "operator":
case "keyword":
case "atom":
return tmp.value;
default:
this.unexpected();
}
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype._make_symbol = function (type) {
var name = this.S.token.value;
return new (name == "this" ? Cola.AST_This : type)({
name : String(name),
start : this.S.token,
end : this.S.token
});
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.as_symbol = function (type, noerror) {
if (!this.is("name")) {
if (!noerror) this.croak("Name expected");
return null;
}
var sym = this._make_symbol(type);
this.next();
return sym;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.subscripts = function(expr, allow_calls) {
var start = expr.start;
if (this.is("punc", ".")) {
this.next();
return this.subscripts(new Cola.AST_Dot({
start : start,
expression : expr,
property : this.as_name(),
end : this.prev()
}), allow_calls);
}
if (this.is("punc", "::") && !this.is_js) {
this.next();
return this.subscripts(new Cola.AST_Proto({
start : start,
expression : expr,
property : this.as_name(),
end : this.prev()
}), allow_calls);
}
2014-04-16 18:39:27 +00:00
if (this.is("punc", "[")) {
this.next();
2014-04-22 13:33:43 +00:00
2014-05-16 12:27:51 +00:00
var prop, triple;
2014-04-22 13:33:43 +00:00
if(this.is_js) prop = this.expression(true);
else if(this.is("punc","]")) prop = new Cola.AST_Noop();
else {
2014-05-16 12:27:51 +00:00
if(this.is("punc","..") || this.is("punc","...")) prop = new Cola.AST_Number({ value : 0 });
else prop = this.expression(true, false, true);
2014-04-22 13:33:43 +00:00
this.dumpS();
2014-05-16 12:27:51 +00:00
if(this.is("punc","..") || this.is("punc","...")){
triple = this.is("punc","...");
2014-04-22 13:33:43 +00:00
this.next();
prop = new Cola.AST_ArrayRange({
2014-05-16 12:27:51 +00:00
from : prop,
to : ( this.is("punc","]") ? new Cola.AST_Noop() : this.expression(true, false, true) ),
triple : triple,
start : prop.start,
end : this.prev()
2014-04-22 13:33:43 +00:00
});
} else this.restoreS();
}
2014-04-16 18:39:27 +00:00
this.expect("]");
return this.subscripts(new Cola.AST_Sub({
start : start,
expression : expr,
property : prop,
end : this.prev()
}), allow_calls);
}
if (allow_calls && this.is("punc", "(")) {
this.next();
return this.subscripts(new Cola.AST_Call({
start : start,
expression : expr,
2014-04-27 20:30:25 +00:00
args : this.expr_list(")", false, false, !this.is_js),
2014-04-16 18:39:27 +00:00
end : this.prev()
}), true);
}
return expr;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.maybe_unary = function(allow_calls) {
var start = this.S.token;
2014-04-17 10:10:18 +00:00
if (this.is("operator") && this.UNARY_PREFIX(start.value)) {
2014-04-16 18:39:27 +00:00
this.next();
this.handle_regexp();
var ex = this.make_unary(Cola.AST_UnaryPrefix, start.value, this.maybe_unary(allow_calls));
ex.start = start;
ex.end = this.prev();
return ex;
}
var val = this.expr_atom(allow_calls);
while (this.is("operator") && this.UNARY_POSTFIX(this.S.token.value) && !this.S.token.nlb) {
2014-04-16 18:39:27 +00:00
val = this.make_unary(Cola.AST_UnaryPostfix, this.S.token.value, val);
val.start = start;
val.end = this.S.token;
this.next();
}
return val;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.make_unary = function (ctor, op, expr) {
if ((op == "++" || op == "--") && !this.is_assignable(expr))
this.croak("Invalid use of " + op + " operator");
return new ctor({ operator: op, expression: expr });
};
2014-05-16 12:27:51 +00:00
/*
a <= b < c == d
as
(((a <= b) && (b < c)) && c == d)
*/
Cola.Parser.prototype.expr_op = function(left, min_prec, no_in, is_comp, rightest) {
var op = this.is("operator") ? this.S.token.value : null, cop = Cola.COMPARISON(op);
2014-04-16 18:39:27 +00:00
if (op == "in" && no_in) op = null;
2014-04-17 10:10:18 +00:00
var prec = op != null ? this.PRECEDENCE[op] : null;
2014-05-16 12:27:51 +00:00
if (!this.is_js && is_comp && cop) {
this.next();
var right = this.maybe_unary(true);
return this.expr_op(new Cola.AST_Binary({
start : left.start,
left : left,
operator : "&&",
right : new Cola.AST_Binary({
start : rightest.start,
left : rightest,
operator : op,
right : right,
end : right.end
}),
end : right.end
}), min_prec, no_in, true, right);
}
2014-04-16 18:39:27 +00:00
if (prec != null && prec > min_prec) {
this.next();
2014-05-16 12:27:51 +00:00
var right = !this.is_js && cop ? this.maybe_unary(true) : this.expr_op(this.maybe_unary(true), prec, no_in);
2014-04-16 18:39:27 +00:00
return this.expr_op(new Cola.AST_Binary({
start : left.start,
left : left,
operator : op,
right : right,
end : right.end
2014-05-16 12:27:51 +00:00
}), min_prec, no_in, cop, right);
}
2014-04-16 18:39:27 +00:00
return left;
};
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.expr_ops = function (no_in) {
return this.expr_op(this.maybe_unary(true), 0, no_in);
};
Cola.Parser.prototype.maybe_conditional = function(no_in) {
var start = this.S.token;
var expr = this.expr_ops(no_in);
if (this.is("operator", "?")) {
this.next();
var yes = this.expression(false);
2014-04-16 18:39:27 +00:00
return new Cola.AST_Conditional({
start : start,
condition : expr,
consequent : yes,
alternative : this.is_js || this.is('punc',':') ? (this.expect(":"), this.expression(false, no_in)) : new Cola.AST_Noop(),
2014-04-16 18:39:27 +00:00
end : this.prev()
});
}
return expr;
};
Cola.Parser.prototype.is_assignable = function (expr) {
if (!this.options.strict) return true;
if (expr instanceof Cola.AST_This) return false;
return (expr instanceof Cola.AST_PropAccess || expr instanceof Cola.AST_Symbol);
};
Cola.Parser.prototype.maybe_assign = function(no_in) {
var start = this.S.token;
var left = this.maybe_conditional(no_in), val = this.S.token.value;
2014-04-17 10:10:18 +00:00
if (this.is("operator") && this.ASSIGNMENT(val)) {
2014-04-16 18:39:27 +00:00
if (this.is_assignable(left)) {
this.next();
return new Cola.AST_Assign({
start : start,
left : left,
operator : val,
right : this.maybe_assign(no_in),
end : this.prev()
});
}
2014-04-16 18:39:27 +00:00
this.croak("Invalid assignment");
}
return left;
};
Cola.Parser.prototype.cascade = function(expr, start) {
var last, props = {
start : start,
expression : expr,
subexpressions : []
};
while (this.next()) {
if (this.is("name") || this.is("punc","[")) {
last = this.expression(false, false, true);
if (this.is("punc", ":")) {
last = this.cascade(last, last.start);
this.next();
}
props.subexpressions.push(last);
if (!( last instanceof Cola.AST_SymbolRef
|| last instanceof Cola.AST_Binary
|| last instanceof Cola.AST_Call
|| last instanceof Cola.AST_Sub
|| last instanceof Cola.AST_Dot
|| last instanceof Cola.AST_Array
|| last instanceof Cola.AST_Cascade
)) this.unexpected();
}
if (!this.is("punc", "..")) break;
}
props.end = this.S.token;
return new Cola.AST_Cascade(props);
};
Cola.Parser.prototype.expression = function(commas, no_in, in_dd) {
2014-04-16 18:39:27 +00:00
var start = this.S.token;
var expr = this.maybe_assign(no_in);
if (!in_dd && !this.is_js && this.is("punc", "..")) return this.cascade(expr, start);
2014-04-16 18:39:27 +00:00
if (commas && this.is("punc", ",")) {
2014-05-16 12:27:51 +00:00
if (expr instanceof Cola.AST_Assign && (expr.left instanceof Cola.AST_ArrayTemplate || expr.left instanceof Cola.AST_ObjectTemplate ||
(expr.left instanceof Cola.AST_Array || expr.left instanceof Cola.AST_Object) && expr.left.template) && expr.left.vardef) this.unexpected();
2014-04-16 18:39:27 +00:00
this.next();
return new Cola.AST_Seq({
start : start,
car : expr,
cdr : this.expression(true, no_in),
end : this.peek()
});
}
return expr;
};
2012-05-27 11:09:01 +00:00
2014-04-16 18:39:27 +00:00
Cola.Parser.prototype.in_loop = function (cont) {
++this.S.in_loop;
2014-05-19 09:58:17 +00:00
var ret = cont.call(this);
2014-04-16 18:39:27 +00:00
--this.S.in_loop;
return ret;
2012-05-27 11:09:01 +00:00
};