/*********************************************************************** A JavaScript tokenizer / parser / beautifier / compressor. https://github.com/mishoo/UglifyJS2 Edited for parsing ColaScript. -------------------------------- (C) --------------------------------- Author: Mihai Bazon http://mihai.bazon.net/blog Distributed under the BSD license: Copyright 2012 (c) Mihai Bazon Copyright 2014 (c) TrigenSoftware 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. ***********************************************************************/ "use strict"; !this.Cola && (this.Cola = {}); 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', '') + ' clone isset is isnt class singleton injector'; Cola.KEYWORDS_ATOM = 'false null true'; Cola.cKEYWORDS_ATOM = Cola.KEYWORDS_ATOM + ' on yes off no'; 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; Cola.KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; 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 = [ "in", "instanceof", "typeof", "new", //"void", "delete", "++", "--", "+", "-", "!", "~", "&", "|", "^", "*", "/", "%", ">>", "<<", ">>>", "<", ">", "<=", ">=", "==", "===", "!=", "!==", "?", "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "&&", "||", // ColaScript "clone", "isset", "is", "isnt", "**", "%%", "?=", "??" ]; Cola.cOPERATORS = Cola.makePredicate(Cola.OPERATORS); Cola.OPERATORS = Cola.OPERATORS.slice(0, Cola.OPERATORS.length - 8); Cola.OPERATORS.push('void'); Cola.OPERATORS = Cola.makePredicate(Cola.OPERATORS); 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")); Cola.PUNC_BEFORE_EXPRESSION = Cola.makePredicate(Cola.characters("[{(,.;:")); Cola.PUNC_CHARS = Cola.makePredicate(Cola.characters("[]{}(),;:")); Cola.REGEXP_MODIFIERS = Cola.makePredicate(Cola.characters("gmsiy")); /* -----[ Tokenizer ]----- */ // regexps adapted from http://xregexp.com/plugins/#unicode Cola.UNICODE = { letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), 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]") }; Cola.is_letter = function (code) { return (code >= 97 && code <= 122) || (code >= 65 && code <= 90) || (code >= 0xaa && Cola.UNICODE.letter.test(String.fromCharCode(code))); }; Cola.is_digit = function (code) { return code >= 48 && code <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9 }; Cola.is_alphanumeric_char = function (code) { return Cola.is_digit(code) || Cola.is_letter(code); }; Cola.is_unicode_combining_mark = function (ch) { return Cola.UNICODE.non_spacing_mark.test(ch) || Cola.UNICODE.space_combining_mark.test(ch); }; Cola.is_unicode_connector_punctuation = function (ch) { return Cola.UNICODE.connector_punctuation.test(ch); }; 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); }; Cola.is_identifier_start = function (code) { return code == 36 || code == 95 || Cola.is_letter(code); }; Cola.is_identifier_char = function (ch) { var code = ch.charCodeAt(0); return Cola.is_identifier_start(code) || Cola.is_digit(code) || code == 8204 // \u200c: zero-width non-joiner || code == 8205 // \u200d: zero-width joiner (in my ECMA-262 PDF, this is also 200c) || Cola.is_unicode_combining_mark(ch) || Cola.is_unicode_connector_punctuation(ch) ; }; Cola.is_identifier_string = function (str){ var i = str.length; if (i == 0) return false; if (!Cola.is_identifier_start(str.charCodeAt(0))) return false; while (--i >= 0) { if (!Cola.is_identifier_char(str.charAt(i))) return false; } return true; }; Cola.parse_js_number = function (num) { if (Cola.RE_HEX_NUMBER.test(num)) { return parseInt(num.substr(2), 16); } else if (Cola.RE_OCT_NUMBER.test(num)) { return parseInt(num.substr(1), 8); } else if (Cola.RE_DEC_NUMBER.test(num)) { return parseFloat(num); } }; Cola.JS_Parse_Error = function (message, line, col, pos) { this.message = message; this.line = line; this.col = col; this.pos = pos; this.stack = new Error().stack; }; Cola.JS_Parse_Error.prototype.toString = function() { return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack; }; Cola.js_error = function (message, filename, line, col, pos) { throw new Cola.JS_Parse_Error(message, line, col, pos); }; Cola.is_token = function (token, type, val) { return token.type == type && (val == null || token.value == val); }; Cola.EX_EOF = {}; Cola.Tokenizer = function ($TEXT, filename, is_js, html5_comments) { this.S = { text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''), filename : filename, pos : 0, tokpos : 0, line : 1, tokline : 0, col : 0, tokcol : 0, newline_before : false, regex_allowed : false, comments_before : [] }; this.dumps = {}; if(!is_js) this.S.string = { at : [ new Cola.Tokenizer.StringInfo() ], level : 0 }; if(is_js){ this.KEYWORDS = Cola.KEYWORDS; this.KEYWORDS_ATOM = Cola.KEYWORDS_ATOM; this.OPERATORS = Cola.OPERATORS; this.UNARY_POSTFIX = Cola.UNARY_POSTFIX; } else { this.KEYWORDS = Cola.cKEYWORDS; this.KEYWORDS_ATOM = Cola.cKEYWORDS_ATOM; this.OPERATORS = Cola.cOPERATORS; this.UNARY_POSTFIX = Cola.cUNARY_POSTFIX; } this.is_js = !!is_js; this.html5_comments = html5_comments; this.prev_was_dot = false; }; 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.call(this, x); } catch(ex) { if (ex === Cola.EX_EOF) this.parse_error(eof_error); else throw ex; } }; }; Cola.Tokenizer.prototype.dumpS = function () { this.dumps = Cola.clone(this.S); }; Cola.Tokenizer.prototype.restoreS = function () { this.S = this.dumps; }; 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; }; Cola.Tokenizer.prototype.forward = function (i) { while (i-- > 0) this.next(); }; Cola.Tokenizer.prototype.looking_at = function (str) { return this.S.text.substr(this.S.pos, str.length) == str; }; 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; }; Cola.Tokenizer.prototype.start_token = function () { this.S.tokline = this.S.line; this.S.tokcol = this.S.col; this.S.tokpos = this.S.pos; }; Cola.Tokenizer.prototype.token = function (type, value, is_comment) { this.S.regex_allowed = ((type == "operator" && !this.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); }; Cola.Tokenizer.prototype.skip_whitespace = function () { while (Cola.WHITESPACE_CHARS(this.peek())) this.next(); }; Cola.Tokenizer.prototype.read_while = function (pred) { var ret = "", ch, i = 0; while ((ch = this.peek()) && pred(ch, i++)) ret += this.next(); return ret; }; Cola.Tokenizer.prototype.parse_error = function (err) { Cola.js_error(err, this.S.filename, this.S.tokline, this.S.tokcol, this.S.tokpos); }; Cola.Tokenizer.prototype.read_num = function (prefix) { var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".", _this = this; 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): // . if(!_this.is_js && _this.peek(1) == '.') return false; return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; } 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); } }; 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; } }; 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; }; Cola.Tokenizer.prototype.read_string = Cola.Tokenizer.with_eof_error("Unterminated string constant", function(raw){ var quote = this.next(), ret = ""; if (!this.is_js && !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(); } this.S.string.at[this.S.string.level].inside = true; 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--; } 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 || this.is_js) && 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; } else if (first <= "3" && octal_len <= 2) return ++octal_len; else if (first >= "4" && octal_len <= 1) return ++octal_len; } return false; }); if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8)); else ch = this.read_escaped_char(true); } else if (ch == quote) { if(!this.is_js && !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; } ret += ch; if (!this.is_js && !raw && (this.peek() == '@' || this.peek() == '{' && this.peek(1) == '{')) break; } return this.token("string", ret); }); Cola.Tokenizer.prototype.read_at = function (){ var at = this.next(); 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 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()); } 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; } this.S.comments_before.push(this.token(type, ret, true)); this.S.regex_allowed = regex_allowed; return this.next_token(); }; 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; } 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; } } if (this.KEYWORDS(name) && escaped) { hex = name.charCodeAt(0).toString(16).toUpperCase(); name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); } 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; return name; }; 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", this.is_js ? new RegExp(regexp, mods) : "/" + regexp + "/" + mods); }); Cola.Tokenizer.prototype.read_operator = function (prefix) { var _this = this; function grow(op) { if (!_this.peek()) return op; var bigger = op + _this.peek(); if (_this.OPERATORS(bigger)) { _this.next(); return grow(bigger); } else { return op; } }; return this.token("operator", grow(prefix || this.next())); }; 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("/"); }; Cola.Tokenizer.prototype.handle_dot = function () { this.next(); if (!this.is_js && this.peek() == '.') return this.next(), this.token("punc", ".."); return Cola.is_digit(this.peek().charCodeAt(0)) ? this.read_num(".") : this.token("punc", "."); }; Cola.Tokenizer.prototype.read_word = function () { var word = this.read_name(); if (this.prev_was_dot) return this.token("name", word); return this.KEYWORDS_ATOM(word) ? this.token("atom", word) : !this.KEYWORDS(word) ? this.token("name", word) : this.OPERATORS(word) ? this.token("operator", word) : this.token("keyword", word); }; Cola.Tokenizer.prototype.next_token = function (force_regexp) { if (force_regexp != null) return this.read_regexp(force_regexp); var ch; 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) { ch = this.peek(); if (ch == '@') return this.read_at(); if (ch == '{' && this.peek(1) == '{') return this.read_braces(); return this.read_string(); } this.skip_whitespace(); this.start_token(); if (this.html5_comments) { if (this.looking_at("") && this.S.newline_before) { this.forward(3); return this.skip_line_comment("comment4"); } } ch = this.peek(); if (!ch) return this.token("eof"); var code = ch.charCodeAt(0); if(!this.is_js && code == 96) return this.read_string(); switch (code) { case 34: case 39: 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.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)) { 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", "}}"); } } return this.token("punc", this.next()); } 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(); } 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 + "'"); }; Cola.Tokenizer.prototype.context = function(nc) { if (nc) this.S = nc; return this.S; }; /* -----[ Parser (constants) ]----- */ Cola.UNARY_PREFIX = Cola.makePredicate([ "typeof", "void", "delete", "--", "++", "!", "~", "-", "+" ]); Cola.cUNARY_PREFIX = Cola.makePredicate([ "clone", "isset", "typeof", "delete", "--", "++", "!", "~", "-", "+" ]); Cola.UNARY_POSTFIX = Cola.makePredicate([ "--", "++" ]); Cola.cUNARY_POSTFIX = Cola.makePredicate([ "--", "++", "??" ]); Cola.ASSIGNMENT = Cola.makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); Cola.cASSIGNMENT = Cola.makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=", "?=" ]); Cola.mergeTokens = function (a, ret) { for (var i = 0; i < a.length; ++i) { var b = a[i]; for (var j = 0; j < b.length; ++j) { ret[b[j]] = i + 1; } } return ret; }; Cola.PRECEDENCE = Cola.mergeTokens( [ ["||"], ["&&"], ["|"], ["^"], ["&"], ["==", "===", "!=", "!=="], ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"] ], {} ); Cola.cPRECEDENCE = Cola.mergeTokens( [ ["||"], ["&&"], ["|"], ["^"], ["&"], ["==", "===", "!=", "!=="], ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"], ["is", "isnt", "**", "%%"] ], {} ); Cola.STATEMENTS_WITH_LABELS = Cola.array_to_hash([ "for", "do", "while", "switch" ]); Cola.ATOMIC_START_TOKEN = Cola.array_to_hash([ "atom", "num", "string", "regexp", "name" ]); /* -----[ Parser ]----- */ Cola.parse = function ($TEXT, options) { return (new Cola.Parser($TEXT, options)).parse(); }; Cola.Parser = function ($TEXT, options) { var _this = this; this.options = Cola.defaults(options, { strict : false, filename : null, toplevel : null, expression : false, html5_comments : true, is_js : false }); this.is_js = !!this.options.is_js; this.tokenizer = typeof $TEXT == "string" ? new Cola.Tokenizer($TEXT, this.options.filename, this.is_js, this.options.html5_comments) : $TEXT; this.S = { input : function(){ return _this.tokenizer.next_token() }, token : null, prev : null, peeked : null, in_function : 0, in_directives : true, in_loop : 0, labels : [] }; this.S.input.context = function(){ return _this.tokenizer.context() }; this.S.token = this.next(); this.dumps = {}; if(this.is_js){ this.UNARY_PREFIX = Cola.UNARY_PREFIX; this.PRECEDENCE = Cola.PRECEDENCE; this.ASSIGNMENT = Cola.ASSIGNMENT; this.UNARY_POSTFIX = Cola.UNARY_POSTFIX; } else { this.UNARY_PREFIX = Cola.cUNARY_PREFIX; this.PRECEDENCE = Cola.cPRECEDENCE; this.ASSIGNMENT = Cola.cASSIGNMENT; this.UNARY_POSTFIX = Cola.cUNARY_POSTFIX; } }; Cola.Parser.prototype.parse = function () { var _this = this; if (this.options.expression) { return this.expression(true); } 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; } else { toplevel = new Cola.AST_Toplevel({ start: start, body: body, end: end }); } toplevel.language = _this.is_js ? 'js' : 'cola'; return toplevel; })(); }; Cola.Parser.prototype.dumpS = function () { this.dumps = Cola.clone(this.S); this.tokenizer.dumpS(); }; Cola.Parser.prototype.restoreS = function () { this.tokenizer.restoreS(); this.S = this.dumps; }; Cola.Parser.prototype.next_until = function (until) { while(true){ if(until(this.S.token)) break; this.next(); } return this.S.token; } Cola.Parser.prototype.is = function (type, value) { return Cola.is_token(this.S.token, type, value); }; Cola.Parser.prototype.peek = function () { return this.S.peeked || (this.S.peeked = this.S.input()); }; 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; }; Cola.Parser.prototype.prev = function () { return this.S.prev; }; Cola.Parser.prototype.croak = function (msg, line, col, pos) { var ctx = this.S.input.context(); Cola.js_error(msg, ctx.filename, line != null ? line : ctx.tokline, col != null ? col : ctx.tokcol, pos != null ? pos : ctx.tokpos); }; Cola.Parser.prototype.token_error = function (token, msg) { this.croak(msg, token.line, token.col); }; Cola.Parser.prototype.unexpected = function (token) { if (token == null) token = this.S.token; this.token_error(token, "Unexpected token: " + token.type + " `" + token.value + "`"); }; 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 + "`"); }; Cola.Parser.prototype.expect = function (punc) { return this.expect_token("punc", punc); }; Cola.Parser.prototype.can_insert_semicolon = function () { if(!this.is_js) return false; // ColaScript return !this.options.strict && ( this.S.token.nlb || this.is("eof") || this.is("punc", "}") ); }; Cola.Parser.prototype.semicolon = function () { if (this.is("punc", ";")) this.next(); else if (!this.can_insert_semicolon()) this.unexpected(); }; Cola.Parser.prototype.parenthesised = function () { this.expect("("); var exp = this.expression(true); this.expect(")"); return exp; }; Cola.Parser.embed_tokens = function (parser) { return function() { var start = this.S.token; var expr = parser.call(this); var end = this.prev(); expr.start = start; expr.end = end; return expr; }; }; 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; 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": if(!this.is_js && Cola.is_token(this.peek(), "name")){ type = this.S.token.value, this.next(); if(Cola.is_token(this.peek(), "punc", "(")) return this.function_(Cola.AST_Defun, type); 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'); }); isfun = (this.next(), (this.is('punc','{') || this.is('punc','=>'))); } this.restoreS(); if(isfun) return this.function_(Cola.AST_Defun); } return Cola.is_token(this.peek(), "punc", ":") ? this.labeled_statement() : this.simple_statement(); case "punc": switch (this.S.token.value) { case "{": return new Cola.AST_BlockStatement({ start : this.S.token, body : this.block_(), end : this.prev() }); case "[": case "(": return this.simple_statement(); case ";": this.next(); return new Cola.AST_EmptyStatement(); default: this.unexpected(); } case "keyword": switch (tmp = this.S.token.value, this.next(), tmp) { case "break": return this.break_cont(Cola.AST_Break); case "continue": return this.break_cont(Cola.AST_Continue); case "debugger": this.semicolon(); return new Cola.AST_Debugger(); case "do": return new Cola.AST_Do({ body : this.in_loop(function(){ return _this.statement() }), condition : (this.expect_token("keyword", "while"), tmp = this.parenthesised(), this.semicolon(), tmp) }); case "while": return new Cola.AST_While({ condition : this.parenthesised(), body : this.in_loop(function(){ return _this.statement() }) }); case "for": return this.for_(); case "function": return this.function_(Cola.AST_Defun); case "if": return this.if_(); 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) ) }); case "switch": return new Cola.AST_Switch({ expression : this.parenthesised(), body : this.in_loop(switch_body_) }); 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) }); case "try": return this.try_(); case "var": return tmp = this.var_(), this.semicolon(), tmp; case "const": return tmp = this.const_(), this.semicolon(), tmp; case "with": return new Cola.AST_With({ expression : this.parenthesised(), body : this.statement() }); default: this.unexpected(); } } }); 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); } }); } return new Cola.AST_LabeledStatement({ body: stat, label: label }); }; Cola.Parser.prototype.simple_statement = function (tmp) { return new Cola.AST_SimpleStatement({ body: (tmp = this.expression(true), this.semicolon(), tmp) }); }; Cola.Parser.prototype.break_cont = function (type) { var label = null, ldef; if (!this.can_insert_semicolon()) { label = this.as_symbol(Cola.AST_LabelRef, true); } 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; }; 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); } } return this.regular_for(init); }; 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(")"); var _this = this; return new Cola.AST_For({ init : init, condition : test, step : step, body : this.in_loop(function(){ return _this.statement() }) }); }; 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(")"); var _this = this; return new Cola.AST_ForIn({ init : init, name : lhs, object : obj, body : this.in_loop(function(){ return _this.statement() }) }); }; Cola.Parser.prototype.function_ = function(ctor, type) { !type && (type = "dynamic"); var in_statement = ctor === Cola.AST_Defun, _this = this; 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({ type: type, name: name, argnames: (function(first, a){ while (!_this.is("punc", ")")) { if (first) first = false; else _this.expect(","); a.push(_this.as_symbol(Cola.AST_SymbolFunarg)); } _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 = []; 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_(); --_this.S.in_function; _this.S.in_loop = loop; _this.S.labels = labels; return a; })(this.S.in_loop, this.S.labels) }); }; Cola.Parser.prototype.if_ = function () { var cond = this.parenthesised(), body = this.statement(), belse = null; if (this.is("keyword", "else")) { this.next(); belse = this.statement(); } return new Cola.AST_If({ condition : cond, body : body, alternative : belse }); }; Cola.Parser.prototype.block_ = function () { this.expect("{"); var a = []; while (!this.is("punc", "}")) { if (this.is("eof")) this.unexpected(); a.push(this.statement()); } 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(); if (this.is("keyword", "case")) { if (branch) branch.end = this.prev(); cur = []; branch = new Cola.AST_Case({ start : (tmp = this.S.token, this.next(), tmp), expression : this.expression(true), body : cur }); a.push(branch); this.expect(":"); } 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); } else { if (!cur) this.unexpected(); cur.push(this.statement()); } } if (branch) branch.end = this.prev(); this.next(); return a; }; 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() }); } 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 }); }; Cola.Parser.prototype.vardefs = function (no_in, in_const) { var a = []; for (;;) { a.push(new Cola.AST_VarDef({ start : this.S.token, name : this.as_symbol(in_const ? Cola.AST_SymbolConst : Cola.AST_SymbolVar), value : this.is("operator", "=") ? (this.next(), this.expression(false, no_in)) : null, end : this.prev() })); if (!this.is("punc", ",")) break; this.next(); } return a; }; Cola.Parser.prototype.var_ = function(no_in, type) { !type && (type = "dynamic"); return new Cola.AST_Var({ start : this.prev(), definitions : this.vardefs(no_in, false), type : type, end : this.prev() }); }; Cola.Parser.prototype.const_ = function() { return new Cola.AST_Const({ start : this.prev(), definitions : this.vardefs(false, true), end : this.prev() }); }; 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(); args = this.expr_list(")"); } else { args = []; } return this.subscripts(new Cola.AST_New({ start : start, expression : newexp, args : args, end : this.prev() }), true); }; 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(); 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('}}'); } 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]$/,''); } } 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; } 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; } break; } this.next(); return ret; }; Cola.Parser.prototype.expr_atom = function(allow_calls) { if (this.is("operator", "new")) { return this.new_(); } var start = this.S.token; 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'); }); isfun = (this.next(), (this.is('punc','{') || this.is('punc','=>'))); this.restoreS(); if(isfun) return this.function_(Cola.AST_Function); } this.next(); var ex = this.expression(true); ex.start = start; 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); } this.unexpected(); } 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); } if (Cola.ATOMIC_START_TOKEN[this.S.token.type]) { return this.subscripts(this.as_atom_node(), allow_calls); } this.unexpected(); }; Cola.Parser.prototype.expr_list = function (closing, allow_trailing_comma, allow_empty) { var first = true, a = []; 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 })); } else { a.push(this.expression(false)); } } this.next(); return a; }; Cola.Parser.prototype.array_ = Cola.Parser.embed_tokens(function() { this.expect("["); if(!this.is_js && !this.is("punc","]")){ this.dumpS(); var tmp, from = this.expression(true); if(this.is("punc","..")){ this.next(); return new Cola.AST_ArrayRange({ from : from, to : (tmp = this.expression(true), this.expect("]"), tmp), start : from.start, end : this.prev() }); } this.restoreS(); } return new Cola.AST_Array({ elements: this.expr_list("]", !this.options.strict, true) }); }); Cola.Parser.prototype.object_ = Cola.Parser.embed_tokens(function() { this.expect("{"); var first = true, a = []; 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(); if (type == "name" && !this.is("punc", ":")) { if (name == "get") { a.push(new Cola.AST_ObjectGetter({ start : start, key : this.as_atom_node(), value : this.function_(Cola.AST_Accessor), end : this.prev() })); continue; } if (name == "set") { a.push(new Cola.AST_ObjectSetter({ start : start, key : this.as_atom_node(), value : this.function_(Cola.AST_Accessor), end : this.prev() })); continue; } } this.expect(":"); a.push(new Cola.AST_ObjectKeyVal({ start : start, key : name, value : this.expression(false), end : this.prev() })); } this.next(); return new Cola.AST_Object({ properties: a }); }); 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(); } }; 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(); } }; 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 }); }; 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; }; 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.next(); var prop; if(this.is_js) prop = this.expression(true); else if(this.is("punc","]")) prop = new Cola.AST_Noop(); else { prop = this.expression(true); this.dumpS(); if(this.is("punc","..")){ this.next(); prop = new Cola.AST_ArrayRange({ from : prop, to : this.expression(true), start : prop.start, end : this.prev() }); } else this.restoreS(); } 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, args : this.expr_list(")"), end : this.prev() }), true); } return expr; }; Cola.Parser.prototype.maybe_unary = function(allow_calls) { var start = this.S.token; if (this.is("operator") && this.UNARY_PREFIX(start.value)) { 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) { val = this.make_unary(Cola.AST_UnaryPostfix, this.S.token.value, val); val.start = start; val.end = this.S.token; this.next(); } return val; }; 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 }); }; Cola.Parser.prototype.expr_op = function(left, min_prec, no_in) { var op = this.is("operator") ? this.S.token.value : null; if (op == "in" && no_in) op = null; var prec = op != null ? this.PRECEDENCE[op] : null; if (prec != null && prec > min_prec) { this.next(); var right = this.expr_op(this.maybe_unary(true), prec, no_in); return this.expr_op(new Cola.AST_Binary({ start : left.start, left : left, operator : op, right : right, end : right.end }), min_prec, no_in); } return left; }; 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); 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(), 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; if (this.is("operator") && this.ASSIGNMENT(val)) { 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() }); } this.croak("Invalid assignment"); } return left; }; Cola.Parser.prototype.expression = function(commas, no_in) { var start = this.S.token; var expr = this.maybe_assign(no_in); if (commas && this.is("punc", ",")) { this.next(); return new Cola.AST_Seq({ start : start, car : expr, cdr : this.expression(true, no_in), end : this.peek() }); } return expr; }; Cola.Parser.prototype.in_loop = function (cont) { ++this.S.in_loop; var ret = cont(); --this.S.in_loop; return ret; };