1/*********************************************************************** 2 3 A JavaScript tokenizer / parser / beautifier / compressor. 4 https://github.com/mishoo/UglifyJS2 5 6 -------------------------------- (C) --------------------------------- 7 8 Author: Mihai Bazon 9 <mihai.bazon@gmail.com> 10 http://mihai.bazon.net/blog 11 12 Distributed under the BSD license: 13 14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com> 15 Parser based on parse-js (http://marijn.haverbeke.nl/parse-js/). 16 17 Redistribution and use in source and binary forms, with or without 18 modification, are permitted provided that the following conditions 19 are met: 20 21 * Redistributions of source code must retain the above 22 copyright notice, this list of conditions and the following 23 disclaimer. 24 25 * Redistributions in binary form must reproduce the above 26 copyright notice, this list of conditions and the following 27 disclaimer in the documentation and/or other materials 28 provided with the distribution. 29 30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 31 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 34 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 35 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 36 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 37 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 39 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 40 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41 SUCH DAMAGE. 42 43 ***********************************************************************/ 44 45"use strict"; 46 47var 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'; 48var KEYWORDS_ATOM = 'false null true'; 49var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield' 50 + " " + KEYWORDS_ATOM + " " + KEYWORDS; 51var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case'; 52 53KEYWORDS = makePredicate(KEYWORDS); 54RESERVED_WORDS = makePredicate(RESERVED_WORDS); 55KEYWORDS_BEFORE_EXPRESSION = makePredicate(KEYWORDS_BEFORE_EXPRESSION); 56KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM); 57 58var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^")); 59 60var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i; 61var RE_OCT_NUMBER = /^0[0-7]+$/; 62 63var OPERATORS = makePredicate([ 64 "in", 65 "instanceof", 66 "typeof", 67 "new", 68 "void", 69 "delete", 70 "++", 71 "--", 72 "+", 73 "-", 74 "!", 75 "~", 76 "&", 77 "|", 78 "^", 79 "*", 80 "/", 81 "%", 82 ">>", 83 "<<", 84 ">>>", 85 "<", 86 ">", 87 "<=", 88 ">=", 89 "==", 90 "===", 91 "!=", 92 "!==", 93 "?", 94 "=", 95 "+=", 96 "-=", 97 "/=", 98 "*=", 99 "%=", 100 ">>=", 101 "<<=", 102 ">>>=", 103 "|=", 104 "^=", 105 "&=", 106 "&&", 107 "||" 108]); 109 110var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000\uFEFF")); 111 112var NEWLINE_CHARS = makePredicate(characters("\n\r\u2028\u2029")); 113 114var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,;:")); 115 116var PUNC_CHARS = makePredicate(characters("[]{}(),;:")); 117 118var REGEXP_MODIFIERS = makePredicate(characters("gmsiy")); 119 120/* -----[ Tokenizer ]----- */ 121 122// regexps adapted from http://xregexp.com/plugins/#unicode 123var UNICODE = { 124 letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"), 125 digit: new RegExp("[\\u0030-\\u0039\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0BE6-\\u0BEF\\u0C66-\\u0C6F\\u0CE6-\\u0CEF\\u0D66-\\u0D6F\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F29\\u1040-\\u1049\\u1090-\\u1099\\u17E0-\\u17E9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19D9\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\uA620-\\uA629\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19]"), 126 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]"), 127 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]"), 128 connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]") 129}; 130 131function is_letter(code) { 132 return (code >= 97 && code <= 122) 133 || (code >= 65 && code <= 90) 134 || (code >= 0xaa && UNICODE.letter.test(String.fromCharCode(code))); 135}; 136 137function is_digit(code) { 138 return code >= 48 && code <= 57; 139}; 140 141function is_alphanumeric_char(code) { 142 return is_digit(code) || is_letter(code); 143}; 144 145function is_unicode_digit(code) { 146 return UNICODE.digit.test(String.fromCharCode(code)); 147} 148 149function is_unicode_combining_mark(ch) { 150 return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch); 151}; 152 153function is_unicode_connector_punctuation(ch) { 154 return UNICODE.connector_punctuation.test(ch); 155}; 156 157function is_identifier(name) { 158 return !RESERVED_WORDS(name) && /^[a-z_$][a-z0-9_$]*$/i.test(name); 159}; 160 161function is_identifier_start(code) { 162 return code == 36 || code == 95 || is_letter(code); 163}; 164 165function is_identifier_char(ch) { 166 var code = ch.charCodeAt(0); 167 return is_identifier_start(code) 168 || is_digit(code) 169 || code == 8204 // \u200c: zero-width non-joiner <ZWNJ> 170 || code == 8205 // \u200d: zero-width joiner <ZWJ> (in my ECMA-262 PDF, this is also 200c) 171 || is_unicode_combining_mark(ch) 172 || is_unicode_connector_punctuation(ch) 173 || is_unicode_digit(code) 174 ; 175}; 176 177function is_identifier_string(str){ 178 return /^[a-z_$][a-z0-9_$]*$/i.test(str); 179}; 180 181function parse_js_number(num) { 182 if (RE_HEX_NUMBER.test(num)) { 183 return parseInt(num.substr(2), 16); 184 } else if (RE_OCT_NUMBER.test(num)) { 185 return parseInt(num.substr(1), 8); 186 } else { 187 var val = parseFloat(num); 188 if (val == num) return val; 189 } 190}; 191 192function JS_Parse_Error(message, filename, line, col, pos) { 193 this.message = message; 194 this.filename = filename; 195 this.line = line; 196 this.col = col; 197 this.pos = pos; 198}; 199JS_Parse_Error.prototype = Object.create(Error.prototype); 200JS_Parse_Error.prototype.constructor = JS_Parse_Error; 201JS_Parse_Error.prototype.name = "SyntaxError"; 202configure_error_stack(JS_Parse_Error); 203 204function js_error(message, filename, line, col, pos) { 205 throw new JS_Parse_Error(message, filename, line, col, pos); 206}; 207 208function is_token(token, type, val) { 209 return token.type == type && (val == null || token.value == val); 210}; 211 212var EX_EOF = {}; 213 214function tokenizer($TEXT, filename, html5_comments, shebang) { 215 216 var S = { 217 text : $TEXT, 218 filename : filename, 219 pos : 0, 220 tokpos : 0, 221 line : 1, 222 tokline : 0, 223 col : 0, 224 tokcol : 0, 225 newline_before : false, 226 regex_allowed : false, 227 comments_before : [], 228 directives : {}, 229 directive_stack : [] 230 }; 231 232 function peek() { return S.text.charAt(S.pos); }; 233 234 function next(signal_eof, in_string) { 235 var ch = S.text.charAt(S.pos++); 236 if (signal_eof && !ch) 237 throw EX_EOF; 238 if (NEWLINE_CHARS(ch)) { 239 S.newline_before = S.newline_before || !in_string; 240 ++S.line; 241 S.col = 0; 242 if (!in_string && ch == "\r" && peek() == "\n") { 243 // treat a \r\n sequence as a single \n 244 ++S.pos; 245 ch = "\n"; 246 } 247 } else { 248 ++S.col; 249 } 250 return ch; 251 }; 252 253 function forward(i) { 254 while (i-- > 0) next(); 255 }; 256 257 function looking_at(str) { 258 return S.text.substr(S.pos, str.length) == str; 259 }; 260 261 function find_eol() { 262 var text = S.text; 263 for (var i = S.pos, n = S.text.length; i < n; ++i) { 264 var ch = text[i]; 265 if (NEWLINE_CHARS(ch)) 266 return i; 267 } 268 return -1; 269 }; 270 271 function find(what, signal_eof) { 272 var pos = S.text.indexOf(what, S.pos); 273 if (signal_eof && pos == -1) throw EX_EOF; 274 return pos; 275 }; 276 277 function start_token() { 278 S.tokline = S.line; 279 S.tokcol = S.col; 280 S.tokpos = S.pos; 281 }; 282 283 var prev_was_dot = false; 284 function token(type, value, is_comment) { 285 S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || 286 (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || 287 (type == "punc" && PUNC_BEFORE_EXPRESSION(value))); 288 if (type == "punc" && value == ".") { 289 prev_was_dot = true; 290 } else if (!is_comment) { 291 prev_was_dot = false; 292 } 293 var ret = { 294 type : type, 295 value : value, 296 line : S.tokline, 297 col : S.tokcol, 298 pos : S.tokpos, 299 endline : S.line, 300 endcol : S.col, 301 endpos : S.pos, 302 nlb : S.newline_before, 303 file : filename 304 }; 305 if (/^(?:num|string|regexp)$/i.test(type)) { 306 ret.raw = $TEXT.substring(ret.pos, ret.endpos); 307 } 308 if (!is_comment) { 309 ret.comments_before = S.comments_before; 310 S.comments_before = []; 311 // make note of any newlines in the comments that came before 312 for (var i = 0, len = ret.comments_before.length; i < len; i++) { 313 ret.nlb = ret.nlb || ret.comments_before[i].nlb; 314 } 315 } 316 S.newline_before = false; 317 return new AST_Token(ret); 318 }; 319 320 function skip_whitespace() { 321 while (WHITESPACE_CHARS(peek())) 322 next(); 323 }; 324 325 function read_while(pred) { 326 var ret = "", ch, i = 0; 327 while ((ch = peek()) && pred(ch, i++)) 328 ret += next(); 329 return ret; 330 }; 331 332 function parse_error(err) { 333 js_error(err, filename, S.tokline, S.tokcol, S.tokpos); 334 }; 335 336 function read_num(prefix) { 337 var has_e = false, after_e = false, has_x = false, has_dot = prefix == "."; 338 var num = read_while(function(ch, i){ 339 var code = ch.charCodeAt(0); 340 switch (code) { 341 case 120: case 88: // xX 342 return has_x ? false : (has_x = true); 343 case 101: case 69: // eE 344 return has_x ? true : has_e ? false : (has_e = after_e = true); 345 case 45: // - 346 return after_e || (i == 0 && !prefix); 347 case 43: // + 348 return after_e; 349 case (after_e = false, 46): // . 350 return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false; 351 } 352 return is_alphanumeric_char(code); 353 }); 354 if (prefix) num = prefix + num; 355 if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { 356 parse_error("Legacy octal literals are not allowed in strict mode"); 357 } 358 var valid = parse_js_number(num); 359 if (!isNaN(valid)) { 360 return token("num", valid); 361 } else { 362 parse_error("Invalid syntax: " + num); 363 } 364 }; 365 366 function read_escaped_char(in_string) { 367 var ch = next(true, in_string); 368 switch (ch.charCodeAt(0)) { 369 case 110 : return "\n"; 370 case 114 : return "\r"; 371 case 116 : return "\t"; 372 case 98 : return "\b"; 373 case 118 : return "\u000b"; // \v 374 case 102 : return "\f"; 375 case 120 : return String.fromCharCode(hex_bytes(2)); // \x 376 case 117 : return String.fromCharCode(hex_bytes(4)); // \u 377 case 10 : return ""; // newline 378 case 13 : // \r 379 if (peek() == "\n") { // DOS newline 380 next(true, in_string); 381 return ""; 382 } 383 } 384 if (ch >= "0" && ch <= "7") 385 return read_octal_escape_sequence(ch); 386 return ch; 387 }; 388 389 function read_octal_escape_sequence(ch) { 390 // Read 391 var p = peek(); 392 if (p >= "0" && p <= "7") { 393 ch += next(true); 394 if (ch[0] <= "3" && (p = peek()) >= "0" && p <= "7") 395 ch += next(true); 396 } 397 398 // Parse 399 if (ch === "0") return "\0"; 400 if (ch.length > 0 && next_token.has_directive("use strict")) 401 parse_error("Legacy octal escape sequences are not allowed in strict mode"); 402 return String.fromCharCode(parseInt(ch, 8)); 403 } 404 405 function hex_bytes(n) { 406 var num = 0; 407 for (; n > 0; --n) { 408 var digit = parseInt(next(true), 16); 409 if (isNaN(digit)) 410 parse_error("Invalid hex-character pattern in string"); 411 num = (num << 4) | digit; 412 } 413 return num; 414 }; 415 416 var read_string = with_eof_error("Unterminated string constant", function(quote_char){ 417 var quote = next(), ret = ""; 418 for (;;) { 419 var ch = next(true, true); 420 if (ch == "\\") ch = read_escaped_char(true); 421 else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant"); 422 else if (ch == quote) break; 423 ret += ch; 424 } 425 var tok = token("string", ret); 426 tok.quote = quote_char; 427 return tok; 428 }); 429 430 function skip_line_comment(type) { 431 var regex_allowed = S.regex_allowed; 432 var i = find_eol(), ret; 433 if (i == -1) { 434 ret = S.text.substr(S.pos); 435 S.pos = S.text.length; 436 } else { 437 ret = S.text.substring(S.pos, i); 438 S.pos = i; 439 } 440 S.col = S.tokcol + (S.pos - S.tokpos); 441 S.comments_before.push(token(type, ret, true)); 442 S.regex_allowed = regex_allowed; 443 return next_token; 444 }; 445 446 var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){ 447 var regex_allowed = S.regex_allowed; 448 var i = find("*/", true); 449 var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); 450 // update stream position 451 forward(text.length /* doesn't count \r\n as 2 char while S.pos - i does */ + 2); 452 S.comments_before.push(token("comment2", text, true)); 453 S.regex_allowed = regex_allowed; 454 return next_token; 455 }); 456 457 function read_name() { 458 var backslash = false, name = "", ch, escaped = false, hex; 459 while ((ch = peek()) != null) { 460 if (!backslash) { 461 if (ch == "\\") escaped = backslash = true, next(); 462 else if (is_identifier_char(ch)) name += next(); 463 else break; 464 } 465 else { 466 if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX"); 467 ch = read_escaped_char(); 468 if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); 469 name += ch; 470 backslash = false; 471 } 472 } 473 if (KEYWORDS(name) && escaped) { 474 hex = name.charCodeAt(0).toString(16).toUpperCase(); 475 name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1); 476 } 477 return name; 478 }; 479 480 var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){ 481 var prev_backslash = false, ch, in_class = false; 482 while ((ch = next(true))) if (NEWLINE_CHARS(ch)) { 483 parse_error("Unexpected line terminator"); 484 } else if (prev_backslash) { 485 regexp += "\\" + ch; 486 prev_backslash = false; 487 } else if (ch == "[") { 488 in_class = true; 489 regexp += ch; 490 } else if (ch == "]" && in_class) { 491 in_class = false; 492 regexp += ch; 493 } else if (ch == "/" && !in_class) { 494 break; 495 } else if (ch == "\\") { 496 prev_backslash = true; 497 } else { 498 regexp += ch; 499 } 500 var mods = read_name(); 501 try { 502 return token("regexp", new RegExp(regexp, mods)); 503 } catch(e) { 504 parse_error(e.message); 505 } 506 }); 507 508 function read_operator(prefix) { 509 function grow(op) { 510 if (!peek()) return op; 511 var bigger = op + peek(); 512 if (OPERATORS(bigger)) { 513 next(); 514 return grow(bigger); 515 } else { 516 return op; 517 } 518 }; 519 return token("operator", grow(prefix || next())); 520 }; 521 522 function handle_slash() { 523 next(); 524 switch (peek()) { 525 case "/": 526 next(); 527 return skip_line_comment("comment1"); 528 case "*": 529 next(); 530 return skip_multiline_comment(); 531 } 532 return S.regex_allowed ? read_regexp("") : read_operator("/"); 533 }; 534 535 function handle_dot() { 536 next(); 537 return is_digit(peek().charCodeAt(0)) 538 ? read_num(".") 539 : token("punc", "."); 540 }; 541 542 function read_word() { 543 var word = read_name(); 544 if (prev_was_dot) return token("name", word); 545 return KEYWORDS_ATOM(word) ? token("atom", word) 546 : !KEYWORDS(word) ? token("name", word) 547 : OPERATORS(word) ? token("operator", word) 548 : token("keyword", word); 549 }; 550 551 function with_eof_error(eof_error, cont) { 552 return function(x) { 553 try { 554 return cont(x); 555 } catch(ex) { 556 if (ex === EX_EOF) parse_error(eof_error); 557 else throw ex; 558 } 559 }; 560 }; 561 562 function next_token(force_regexp) { 563 if (force_regexp != null) 564 return read_regexp(force_regexp); 565 if (shebang && S.pos == 0 && looking_at("#!")) { 566 start_token(); 567 forward(2); 568 skip_line_comment("comment5"); 569 } 570 for (;;) { 571 skip_whitespace(); 572 start_token(); 573 if (html5_comments) { 574 if (looking_at("<!--")) { 575 forward(4); 576 skip_line_comment("comment3"); 577 continue; 578 } 579 if (looking_at("-->") && S.newline_before) { 580 forward(3); 581 skip_line_comment("comment4"); 582 continue; 583 } 584 } 585 var ch = peek(); 586 if (!ch) return token("eof"); 587 var code = ch.charCodeAt(0); 588 switch (code) { 589 case 34: case 39: return read_string(ch); 590 case 46: return handle_dot(); 591 case 47: { 592 var tok = handle_slash(); 593 if (tok === next_token) continue; 594 return tok; 595 } 596 } 597 if (is_digit(code)) return read_num(); 598 if (PUNC_CHARS(ch)) return token("punc", next()); 599 if (OPERATOR_CHARS(ch)) return read_operator(); 600 if (code == 92 || is_identifier_start(code)) return read_word(); 601 break; 602 } 603 parse_error("Unexpected character '" + ch + "'"); 604 }; 605 606 next_token.context = function(nc) { 607 if (nc) S = nc; 608 return S; 609 }; 610 611 next_token.add_directive = function(directive) { 612 S.directive_stack[S.directive_stack.length - 1].push(directive); 613 614 if (S.directives[directive] === undefined) { 615 S.directives[directive] = 1; 616 } else { 617 S.directives[directive]++; 618 } 619 } 620 621 next_token.push_directives_stack = function() { 622 S.directive_stack.push([]); 623 } 624 625 next_token.pop_directives_stack = function() { 626 var directives = S.directive_stack[S.directive_stack.length - 1]; 627 628 for (var i = 0; i < directives.length; i++) { 629 S.directives[directives[i]]--; 630 } 631 632 S.directive_stack.pop(); 633 } 634 635 next_token.has_directive = function(directive) { 636 return S.directives[directive] !== undefined && 637 S.directives[directive] > 0; 638 } 639 640 return next_token; 641 642}; 643 644/* -----[ Parser (constants) ]----- */ 645 646var UNARY_PREFIX = makePredicate([ 647 "typeof", 648 "void", 649 "delete", 650 "--", 651 "++", 652 "!", 653 "~", 654 "-", 655 "+" 656]); 657 658var UNARY_POSTFIX = makePredicate([ "--", "++" ]); 659 660var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]); 661 662var PRECEDENCE = (function(a, ret){ 663 for (var i = 0; i < a.length; ++i) { 664 var b = a[i]; 665 for (var j = 0; j < b.length; ++j) { 666 ret[b[j]] = i + 1; 667 } 668 } 669 return ret; 670})( 671 [ 672 ["||"], 673 ["&&"], 674 ["|"], 675 ["^"], 676 ["&"], 677 ["==", "===", "!=", "!=="], 678 ["<", ">", "<=", ">=", "in", "instanceof"], 679 [">>", "<<", ">>>"], 680 ["+", "-"], 681 ["*", "/", "%"] 682 ], 683 {} 684); 685 686var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]); 687 688var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]); 689 690/* -----[ Parser ]----- */ 691 692function parse($TEXT, options) { 693 694 options = defaults(options, { 695 bare_returns : false, 696 cli : false, 697 expression : false, 698 filename : null, 699 html5_comments : true, 700 shebang : true, 701 strict : false, 702 toplevel : null, 703 }); 704 705 var S = { 706 input : (typeof $TEXT == "string" 707 ? tokenizer($TEXT, options.filename, 708 options.html5_comments, options.shebang) 709 : $TEXT), 710 token : null, 711 prev : null, 712 peeked : null, 713 in_function : 0, 714 in_directives : true, 715 in_loop : 0, 716 labels : [] 717 }; 718 719 S.token = next(); 720 721 function is(type, value) { 722 return is_token(S.token, type, value); 723 }; 724 725 function peek() { return S.peeked || (S.peeked = S.input()); }; 726 727 function next() { 728 S.prev = S.token; 729 if (S.peeked) { 730 S.token = S.peeked; 731 S.peeked = null; 732 } else { 733 S.token = S.input(); 734 } 735 S.in_directives = S.in_directives && ( 736 S.token.type == "string" || is("punc", ";") 737 ); 738 return S.token; 739 }; 740 741 function prev() { 742 return S.prev; 743 }; 744 745 function croak(msg, line, col, pos) { 746 var ctx = S.input.context(); 747 js_error(msg, 748 ctx.filename, 749 line != null ? line : ctx.tokline, 750 col != null ? col : ctx.tokcol, 751 pos != null ? pos : ctx.tokpos); 752 }; 753 754 function token_error(token, msg) { 755 croak(msg, token.line, token.col); 756 }; 757 758 function unexpected(token) { 759 if (token == null) 760 token = S.token; 761 token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")"); 762 }; 763 764 function expect_token(type, val) { 765 if (is(type, val)) { 766 return next(); 767 } 768 token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); 769 }; 770 771 function expect(punc) { return expect_token("punc", punc); }; 772 773 function can_insert_semicolon() { 774 return !options.strict && ( 775 S.token.nlb || is("eof") || is("punc", "}") 776 ); 777 }; 778 779 function semicolon(optional) { 780 if (is("punc", ";")) next(); 781 else if (!optional && !can_insert_semicolon()) unexpected(); 782 }; 783 784 function parenthesised() { 785 expect("("); 786 var exp = expression(true); 787 expect(")"); 788 return exp; 789 }; 790 791 function embed_tokens(parser) { 792 return function() { 793 var start = S.token; 794 var expr = parser(); 795 var end = prev(); 796 expr.start = start; 797 expr.end = end; 798 return expr; 799 }; 800 }; 801 802 function handle_regexp() { 803 if (is("operator", "/") || is("operator", "/=")) { 804 S.peeked = null; 805 S.token = S.input(S.token.value.substr(1)); // force regexp 806 } 807 }; 808 809 var statement = embed_tokens(function() { 810 handle_regexp(); 811 switch (S.token.type) { 812 case "string": 813 if (S.in_directives) { 814 var token = peek(); 815 if (S.token.raw.indexOf("\\") == -1 816 && (token.nlb 817 || is_token(token, "eof") 818 || is_token(token, "punc", ";") 819 || is_token(token, "punc", "}"))) { 820 S.input.add_directive(S.token.value); 821 } else { 822 S.in_directives = false; 823 } 824 } 825 var dir = S.in_directives, stat = simple_statement(); 826 return dir ? new AST_Directive(stat.body) : stat; 827 case "num": 828 case "regexp": 829 case "operator": 830 case "atom": 831 return simple_statement(); 832 833 case "name": 834 return is_token(peek(), "punc", ":") 835 ? labeled_statement() 836 : simple_statement(); 837 838 case "punc": 839 switch (S.token.value) { 840 case "{": 841 return new AST_BlockStatement({ 842 start : S.token, 843 body : block_(), 844 end : prev() 845 }); 846 case "[": 847 case "(": 848 return simple_statement(); 849 case ";": 850 S.in_directives = false; 851 next(); 852 return new AST_EmptyStatement(); 853 default: 854 unexpected(); 855 } 856 857 case "keyword": 858 switch (S.token.value) { 859 case "break": 860 next(); 861 return break_cont(AST_Break); 862 863 case "continue": 864 next(); 865 return break_cont(AST_Continue); 866 867 case "debugger": 868 next(); 869 semicolon(); 870 return new AST_Debugger(); 871 872 case "do": 873 next(); 874 var body = in_loop(statement); 875 expect_token("keyword", "while"); 876 var condition = parenthesised(); 877 semicolon(true); 878 return new AST_Do({ 879 body : body, 880 condition : condition 881 }); 882 883 case "while": 884 next(); 885 return new AST_While({ 886 condition : parenthesised(), 887 body : in_loop(statement) 888 }); 889 890 case "for": 891 next(); 892 return for_(); 893 894 case "function": 895 next(); 896 return function_(AST_Defun); 897 898 case "if": 899 next(); 900 return if_(); 901 902 case "return": 903 if (S.in_function == 0 && !options.bare_returns) 904 croak("'return' outside of function"); 905 next(); 906 var value = null; 907 if (is("punc", ";")) { 908 next(); 909 } else if (!can_insert_semicolon()) { 910 value = expression(true); 911 semicolon(); 912 } 913 return new AST_Return({ 914 value: value 915 }); 916 917 case "switch": 918 next(); 919 return new AST_Switch({ 920 expression : parenthesised(), 921 body : in_loop(switch_body_) 922 }); 923 924 case "throw": 925 next(); 926 if (S.token.nlb) 927 croak("Illegal newline after 'throw'"); 928 var value = expression(true); 929 semicolon(); 930 return new AST_Throw({ 931 value: value 932 }); 933 934 case "try": 935 next(); 936 return try_(); 937 938 case "var": 939 next(); 940 var node = var_(); 941 semicolon(); 942 return node; 943 944 case "const": 945 next(); 946 var node = const_(); 947 semicolon(); 948 return node; 949 950 case "with": 951 if (S.input.has_directive("use strict")) { 952 croak("Strict mode may not include a with statement"); 953 } 954 next(); 955 return new AST_With({ 956 expression : parenthesised(), 957 body : statement() 958 }); 959 } 960 } 961 unexpected(); 962 }); 963 964 function labeled_statement() { 965 var label = as_symbol(AST_Label); 966 if (find_if(function(l){ return l.name == label.name }, S.labels)) { 967 // ECMA-262, 12.12: An ECMAScript program is considered 968 // syntactically incorrect if it contains a 969 // LabelledStatement that is enclosed by a 970 // LabelledStatement with the same Identifier as label. 971 croak("Label " + label.name + " defined twice"); 972 } 973 expect(":"); 974 S.labels.push(label); 975 var stat = statement(); 976 S.labels.pop(); 977 if (!(stat instanceof AST_IterationStatement)) { 978 // check for `continue` that refers to this label. 979 // those should be reported as syntax errors. 980 // https://github.com/mishoo/UglifyJS2/issues/287 981 label.references.forEach(function(ref){ 982 if (ref instanceof AST_Continue) { 983 ref = ref.label.start; 984 croak("Continue label `" + label.name + "` refers to non-IterationStatement.", 985 ref.line, ref.col, ref.pos); 986 } 987 }); 988 } 989 return new AST_LabeledStatement({ body: stat, label: label }); 990 }; 991 992 function simple_statement(tmp) { 993 return new AST_SimpleStatement({ body: (tmp = expression(true), semicolon(), tmp) }); 994 }; 995 996 function break_cont(type) { 997 var label = null, ldef; 998 if (!can_insert_semicolon()) { 999 label = as_symbol(AST_LabelRef, true); 1000 } 1001 if (label != null) { 1002 ldef = find_if(function(l){ return l.name == label.name }, S.labels); 1003 if (!ldef) 1004 croak("Undefined label " + label.name); 1005 label.thedef = ldef; 1006 } 1007 else if (S.in_loop == 0) 1008 croak(type.TYPE + " not inside a loop or switch"); 1009 semicolon(); 1010 var stat = new type({ label: label }); 1011 if (ldef) ldef.references.push(stat); 1012 return stat; 1013 }; 1014 1015 function for_() { 1016 expect("("); 1017 var init = null; 1018 if (!is("punc", ";")) { 1019 init = is("keyword", "var") 1020 ? (next(), var_(true)) 1021 : expression(true, true); 1022 if (is("operator", "in")) { 1023 if (init instanceof AST_Var && init.definitions.length > 1) 1024 croak("Only one variable declaration allowed in for..in loop"); 1025 next(); 1026 return for_in(init); 1027 } 1028 } 1029 return regular_for(init); 1030 }; 1031 1032 function regular_for(init) { 1033 expect(";"); 1034 var test = is("punc", ";") ? null : expression(true); 1035 expect(";"); 1036 var step = is("punc", ")") ? null : expression(true); 1037 expect(")"); 1038 return new AST_For({ 1039 init : init, 1040 condition : test, 1041 step : step, 1042 body : in_loop(statement) 1043 }); 1044 }; 1045 1046 function for_in(init) { 1047 var lhs = init instanceof AST_Var ? init.definitions[0].name : null; 1048 var obj = expression(true); 1049 expect(")"); 1050 return new AST_ForIn({ 1051 init : init, 1052 name : lhs, 1053 object : obj, 1054 body : in_loop(statement) 1055 }); 1056 }; 1057 1058 var function_ = function(ctor) { 1059 var in_statement = ctor === AST_Defun; 1060 var name = is("name") ? as_symbol(in_statement ? AST_SymbolDefun : AST_SymbolLambda) : null; 1061 if (in_statement && !name) 1062 unexpected(); 1063 expect("("); 1064 return new ctor({ 1065 name: name, 1066 argnames: (function(first, a){ 1067 while (!is("punc", ")")) { 1068 if (first) first = false; else expect(","); 1069 a.push(as_symbol(AST_SymbolFunarg)); 1070 } 1071 next(); 1072 return a; 1073 })(true, []), 1074 body: (function(loop, labels){ 1075 ++S.in_function; 1076 S.in_directives = true; 1077 S.input.push_directives_stack(); 1078 S.in_loop = 0; 1079 S.labels = []; 1080 var a = block_(); 1081 S.input.pop_directives_stack(); 1082 --S.in_function; 1083 S.in_loop = loop; 1084 S.labels = labels; 1085 return a; 1086 })(S.in_loop, S.labels) 1087 }); 1088 }; 1089 1090 function if_() { 1091 var cond = parenthesised(), body = statement(), belse = null; 1092 if (is("keyword", "else")) { 1093 next(); 1094 belse = statement(); 1095 } 1096 return new AST_If({ 1097 condition : cond, 1098 body : body, 1099 alternative : belse 1100 }); 1101 }; 1102 1103 function block_() { 1104 expect("{"); 1105 var a = []; 1106 while (!is("punc", "}")) { 1107 if (is("eof")) unexpected(); 1108 a.push(statement()); 1109 } 1110 next(); 1111 return a; 1112 }; 1113 1114 function switch_body_() { 1115 expect("{"); 1116 var a = [], cur = null, branch = null, tmp; 1117 while (!is("punc", "}")) { 1118 if (is("eof")) unexpected(); 1119 if (is("keyword", "case")) { 1120 if (branch) branch.end = prev(); 1121 cur = []; 1122 branch = new AST_Case({ 1123 start : (tmp = S.token, next(), tmp), 1124 expression : expression(true), 1125 body : cur 1126 }); 1127 a.push(branch); 1128 expect(":"); 1129 } 1130 else if (is("keyword", "default")) { 1131 if (branch) branch.end = prev(); 1132 cur = []; 1133 branch = new AST_Default({ 1134 start : (tmp = S.token, next(), expect(":"), tmp), 1135 body : cur 1136 }); 1137 a.push(branch); 1138 } 1139 else { 1140 if (!cur) unexpected(); 1141 cur.push(statement()); 1142 } 1143 } 1144 if (branch) branch.end = prev(); 1145 next(); 1146 return a; 1147 }; 1148 1149 function try_() { 1150 var body = block_(), bcatch = null, bfinally = null; 1151 if (is("keyword", "catch")) { 1152 var start = S.token; 1153 next(); 1154 expect("("); 1155 var name = as_symbol(AST_SymbolCatch); 1156 expect(")"); 1157 bcatch = new AST_Catch({ 1158 start : start, 1159 argname : name, 1160 body : block_(), 1161 end : prev() 1162 }); 1163 } 1164 if (is("keyword", "finally")) { 1165 var start = S.token; 1166 next(); 1167 bfinally = new AST_Finally({ 1168 start : start, 1169 body : block_(), 1170 end : prev() 1171 }); 1172 } 1173 if (!bcatch && !bfinally) 1174 croak("Missing catch/finally blocks"); 1175 return new AST_Try({ 1176 body : body, 1177 bcatch : bcatch, 1178 bfinally : bfinally 1179 }); 1180 }; 1181 1182 function vardefs(no_in, in_const) { 1183 var a = []; 1184 for (;;) { 1185 a.push(new AST_VarDef({ 1186 start : S.token, 1187 name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), 1188 value : is("operator", "=") ? (next(), expression(false, no_in)) : null, 1189 end : prev() 1190 })); 1191 if (!is("punc", ",")) 1192 break; 1193 next(); 1194 } 1195 return a; 1196 }; 1197 1198 var var_ = function(no_in) { 1199 return new AST_Var({ 1200 start : prev(), 1201 definitions : vardefs(no_in, false), 1202 end : prev() 1203 }); 1204 }; 1205 1206 var const_ = function() { 1207 return new AST_Const({ 1208 start : prev(), 1209 definitions : vardefs(false, true), 1210 end : prev() 1211 }); 1212 }; 1213 1214 var new_ = function(allow_calls) { 1215 var start = S.token; 1216 expect_token("operator", "new"); 1217 var newexp = expr_atom(false), args; 1218 if (is("punc", "(")) { 1219 next(); 1220 args = expr_list(")"); 1221 } else { 1222 args = []; 1223 } 1224 return subscripts(new AST_New({ 1225 start : start, 1226 expression : newexp, 1227 args : args, 1228 end : prev() 1229 }), allow_calls); 1230 }; 1231 1232 function as_atom_node() { 1233 var tok = S.token, ret; 1234 switch (tok.type) { 1235 case "name": 1236 case "keyword": 1237 ret = _make_symbol(AST_SymbolRef); 1238 break; 1239 case "num": 1240 ret = new AST_Number({ start: tok, end: tok, value: tok.value }); 1241 break; 1242 case "string": 1243 ret = new AST_String({ 1244 start : tok, 1245 end : tok, 1246 value : tok.value, 1247 quote : tok.quote 1248 }); 1249 break; 1250 case "regexp": 1251 ret = new AST_RegExp({ start: tok, end: tok, value: tok.value }); 1252 break; 1253 case "atom": 1254 switch (tok.value) { 1255 case "false": 1256 ret = new AST_False({ start: tok, end: tok }); 1257 break; 1258 case "true": 1259 ret = new AST_True({ start: tok, end: tok }); 1260 break; 1261 case "null": 1262 ret = new AST_Null({ start: tok, end: tok }); 1263 break; 1264 } 1265 break; 1266 case "operator": 1267 if (!is_identifier_string(tok.value)) { 1268 croak("Invalid getter/setter name: " + tok.value, 1269 tok.line, tok.col, tok.pos); 1270 } 1271 ret = _make_symbol(AST_SymbolRef); 1272 break; 1273 } 1274 next(); 1275 return ret; 1276 }; 1277 1278 var expr_atom = function(allow_calls) { 1279 if (is("operator", "new")) { 1280 return new_(allow_calls); 1281 } 1282 var start = S.token; 1283 if (is("punc")) { 1284 switch (start.value) { 1285 case "(": 1286 next(); 1287 var ex = expression(true); 1288 ex.start = start; 1289 ex.end = S.token; 1290 expect(")"); 1291 return subscripts(ex, allow_calls); 1292 case "[": 1293 return subscripts(array_(), allow_calls); 1294 case "{": 1295 return subscripts(object_(), allow_calls); 1296 } 1297 unexpected(); 1298 } 1299 if (is("keyword", "function")) { 1300 next(); 1301 var func = function_(AST_Function); 1302 func.start = start; 1303 func.end = prev(); 1304 return subscripts(func, allow_calls); 1305 } 1306 if (ATOMIC_START_TOKEN[S.token.type]) { 1307 return subscripts(as_atom_node(), allow_calls); 1308 } 1309 unexpected(); 1310 }; 1311 1312 function expr_list(closing, allow_trailing_comma, allow_empty) { 1313 var first = true, a = []; 1314 while (!is("punc", closing)) { 1315 if (first) first = false; else expect(","); 1316 if (allow_trailing_comma && is("punc", closing)) break; 1317 if (is("punc", ",") && allow_empty) { 1318 a.push(new AST_Hole({ start: S.token, end: S.token })); 1319 } else { 1320 a.push(expression(false)); 1321 } 1322 } 1323 next(); 1324 return a; 1325 }; 1326 1327 var array_ = embed_tokens(function() { 1328 expect("["); 1329 return new AST_Array({ 1330 elements: expr_list("]", !options.strict, true) 1331 }); 1332 }); 1333 1334 var create_accessor = embed_tokens(function() { 1335 return function_(AST_Accessor); 1336 }); 1337 1338 var object_ = embed_tokens(function() { 1339 expect("{"); 1340 var first = true, a = []; 1341 while (!is("punc", "}")) { 1342 if (first) first = false; else expect(","); 1343 if (!options.strict && is("punc", "}")) 1344 // allow trailing comma 1345 break; 1346 var start = S.token; 1347 var type = start.type; 1348 var name = as_property_name(); 1349 if (type == "name" && !is("punc", ":")) { 1350 var key = new AST_SymbolAccessor({ 1351 start: S.token, 1352 name: as_property_name(), 1353 end: prev() 1354 }); 1355 if (name == "get") { 1356 a.push(new AST_ObjectGetter({ 1357 start : start, 1358 key : key, 1359 value : create_accessor(), 1360 end : prev() 1361 })); 1362 continue; 1363 } 1364 if (name == "set") { 1365 a.push(new AST_ObjectSetter({ 1366 start : start, 1367 key : key, 1368 value : create_accessor(), 1369 end : prev() 1370 })); 1371 continue; 1372 } 1373 } 1374 expect(":"); 1375 a.push(new AST_ObjectKeyVal({ 1376 start : start, 1377 quote : start.quote, 1378 key : name, 1379 value : expression(false), 1380 end : prev() 1381 })); 1382 } 1383 next(); 1384 return new AST_Object({ properties: a }); 1385 }); 1386 1387 function as_property_name() { 1388 var tmp = S.token; 1389 switch (tmp.type) { 1390 case "operator": 1391 if (!KEYWORDS(tmp.value)) unexpected(); 1392 case "num": 1393 case "string": 1394 case "name": 1395 case "keyword": 1396 case "atom": 1397 next(); 1398 return tmp.value; 1399 default: 1400 unexpected(); 1401 } 1402 }; 1403 1404 function as_name() { 1405 var tmp = S.token; 1406 if (tmp.type != "name") unexpected(); 1407 next(); 1408 return tmp.value; 1409 }; 1410 1411 function _make_symbol(type) { 1412 var name = S.token.value; 1413 return new (name == "this" ? AST_This : type)({ 1414 name : String(name), 1415 start : S.token, 1416 end : S.token 1417 }); 1418 }; 1419 1420 function as_symbol(type, noerror) { 1421 if (!is("name")) { 1422 if (!noerror) croak("Name expected"); 1423 return null; 1424 } 1425 var sym = _make_symbol(type); 1426 next(); 1427 return sym; 1428 }; 1429 1430 var subscripts = function(expr, allow_calls) { 1431 var start = expr.start; 1432 if (is("punc", ".")) { 1433 next(); 1434 return subscripts(new AST_Dot({ 1435 start : start, 1436 expression : expr, 1437 property : as_name(), 1438 end : prev() 1439 }), allow_calls); 1440 } 1441 if (is("punc", "[")) { 1442 next(); 1443 var prop = expression(true); 1444 expect("]"); 1445 return subscripts(new AST_Sub({ 1446 start : start, 1447 expression : expr, 1448 property : prop, 1449 end : prev() 1450 }), allow_calls); 1451 } 1452 if (allow_calls && is("punc", "(")) { 1453 next(); 1454 return subscripts(new AST_Call({ 1455 start : start, 1456 expression : expr, 1457 args : expr_list(")"), 1458 end : prev() 1459 }), true); 1460 } 1461 return expr; 1462 }; 1463 1464 var maybe_unary = function(allow_calls) { 1465 var start = S.token; 1466 if (is("operator") && UNARY_PREFIX(start.value)) { 1467 next(); 1468 handle_regexp(); 1469 var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(allow_calls)); 1470 ex.start = start; 1471 ex.end = prev(); 1472 return ex; 1473 } 1474 var val = expr_atom(allow_calls); 1475 while (is("operator") && UNARY_POSTFIX(S.token.value) && !S.token.nlb) { 1476 val = make_unary(AST_UnaryPostfix, S.token, val); 1477 val.start = start; 1478 val.end = S.token; 1479 next(); 1480 } 1481 return val; 1482 }; 1483 1484 function make_unary(ctor, token, expr) { 1485 var op = token.value; 1486 if ((op == "++" || op == "--") && !is_assignable(expr)) 1487 croak("Invalid use of " + op + " operator", token.line, token.col, token.pos); 1488 return new ctor({ operator: op, expression: expr }); 1489 }; 1490 1491 var expr_op = function(left, min_prec, no_in) { 1492 var op = is("operator") ? S.token.value : null; 1493 if (op == "in" && no_in) op = null; 1494 var prec = op != null ? PRECEDENCE[op] : null; 1495 if (prec != null && prec > min_prec) { 1496 next(); 1497 var right = expr_op(maybe_unary(true), prec, no_in); 1498 return expr_op(new AST_Binary({ 1499 start : left.start, 1500 left : left, 1501 operator : op, 1502 right : right, 1503 end : right.end 1504 }), min_prec, no_in); 1505 } 1506 return left; 1507 }; 1508 1509 function expr_ops(no_in) { 1510 return expr_op(maybe_unary(true), 0, no_in); 1511 }; 1512 1513 var maybe_conditional = function(no_in) { 1514 var start = S.token; 1515 var expr = expr_ops(no_in); 1516 if (is("operator", "?")) { 1517 next(); 1518 var yes = expression(false); 1519 expect(":"); 1520 return new AST_Conditional({ 1521 start : start, 1522 condition : expr, 1523 consequent : yes, 1524 alternative : expression(false, no_in), 1525 end : prev() 1526 }); 1527 } 1528 return expr; 1529 }; 1530 1531 function is_assignable(expr) { 1532 if (options.cli) return true; 1533 return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef; 1534 }; 1535 1536 var maybe_assign = function(no_in) { 1537 var start = S.token; 1538 var left = maybe_conditional(no_in), val = S.token.value; 1539 if (is("operator") && ASSIGNMENT(val)) { 1540 if (is_assignable(left)) { 1541 next(); 1542 return new AST_Assign({ 1543 start : start, 1544 left : left, 1545 operator : val, 1546 right : maybe_assign(no_in), 1547 end : prev() 1548 }); 1549 } 1550 croak("Invalid assignment"); 1551 } 1552 return left; 1553 }; 1554 1555 var expression = function(commas, no_in) { 1556 var start = S.token; 1557 var expr = maybe_assign(no_in); 1558 if (commas && is("punc", ",")) { 1559 next(); 1560 return new AST_Seq({ 1561 start : start, 1562 car : expr, 1563 cdr : expression(true, no_in), 1564 end : peek() 1565 }); 1566 } 1567 return expr; 1568 }; 1569 1570 function in_loop(cont) { 1571 ++S.in_loop; 1572 var ret = cont(); 1573 --S.in_loop; 1574 return ret; 1575 }; 1576 1577 if (options.expression) { 1578 return expression(true); 1579 } 1580 1581 return (function(){ 1582 var start = S.token; 1583 var body = []; 1584 S.input.push_directives_stack(); 1585 while (!is("eof")) 1586 body.push(statement()); 1587 S.input.pop_directives_stack(); 1588 var end = prev(); 1589 var toplevel = options.toplevel; 1590 if (toplevel) { 1591 toplevel.body = toplevel.body.concat(body); 1592 toplevel.end = end; 1593 } else { 1594 toplevel = new AST_Toplevel({ start: start, body: body, end: end }); 1595 } 1596 return toplevel; 1597 })(); 1598 1599}; 1600