mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
2. implement assigment complex cases( +=, -=...) 3. implement update expressions (++, --) 4. implement if, for statement 5. implement RopeString 6. implement throw statement this patch passes bitops-bitwise-and.js, bitops-3bit-bits-in-byte.js Signed-off-by: seonghyun kim <sh8281.kim@samsung.com>
6119 lines
243 KiB
C++
6119 lines
243 KiB
C++
#include "Escargot.h"
|
|
#include "esprima.h"
|
|
#include "interpreter/ByteCode.h"
|
|
#include "parser/ast/AST.h"
|
|
#include "parser/CodeBlock.h"
|
|
#include "double-conversion.h"
|
|
#include "ieee.h"
|
|
|
|
namespace Escargot {
|
|
|
|
namespace esprima {
|
|
|
|
enum Token {
|
|
BooleanLiteralToken = 1,
|
|
EOFToken = 2,
|
|
IdentifierToken = 3,
|
|
KeywordToken = 4,
|
|
NullLiteralToken = 5,
|
|
NumericLiteralToken = 6,
|
|
PunctuatorToken = 7,
|
|
StringLiteralToken = 8,
|
|
RegularExpressionToken = 9,
|
|
TemplateToken = 10
|
|
};
|
|
|
|
enum PlaceHolders {
|
|
ArrowParameterPlaceHolder
|
|
};
|
|
|
|
enum PunctuatorsKind {
|
|
LeftParenthesis,
|
|
RightParenthesis,
|
|
LeftBrace,
|
|
RightBrace,
|
|
Period,
|
|
PeriodPeriodPeriod,
|
|
Comma,
|
|
Colon,
|
|
SemiColon,
|
|
LeftSquareBracket,
|
|
RightSquareBracket,
|
|
GuessMark,
|
|
Wave,
|
|
UnsignedRightShift,
|
|
RightShift,
|
|
LeftShift,
|
|
Plus,
|
|
Minus,
|
|
Multiply,
|
|
Divide,
|
|
Mod,
|
|
ExclamationMark,
|
|
StrictEqual,
|
|
NotStrictEqual,
|
|
Equal,
|
|
NotEqual,
|
|
LogicalAnd,
|
|
LogicalOr,
|
|
PlusPlus,
|
|
MinusMinus,
|
|
BitwiseAnd,
|
|
BitwiseOr,
|
|
BitwiseXor,
|
|
LeftInequality,
|
|
RightInequality,
|
|
InPunctuator,
|
|
InstanceOfPunctuator,
|
|
|
|
Substitution,
|
|
UnsignedRightShiftEqual,
|
|
RightShiftEqual,
|
|
LeftShiftEqual,
|
|
PlusEqual,
|
|
MinusEqual,
|
|
MultiplyEqual,
|
|
DivideEqual,
|
|
ModEqual,
|
|
// ExclamationMarkEqual,
|
|
BitwiseAndEqual,
|
|
BitwiseOrEqual,
|
|
BitwiseXorEqual,
|
|
LeftInequalityEqual,
|
|
RightInequalityEqual,
|
|
SubstitutionEnd,
|
|
|
|
Arrow,
|
|
PunctuatorsKindEnd,
|
|
};
|
|
|
|
enum KeywordKind {
|
|
NotKeyword,
|
|
If,
|
|
In,
|
|
Do,
|
|
Of,
|
|
Var,
|
|
For,
|
|
New,
|
|
Try,
|
|
This,
|
|
Else,
|
|
Case,
|
|
Void,
|
|
With,
|
|
Enum,
|
|
Await,
|
|
While,
|
|
Break,
|
|
Catch,
|
|
Throw,
|
|
Const,
|
|
Class,
|
|
Super,
|
|
Return,
|
|
Typeof,
|
|
Delete,
|
|
Switch,
|
|
Export,
|
|
Import,
|
|
Default,
|
|
Finally,
|
|
Extends,
|
|
Function,
|
|
Continue,
|
|
Debugger,
|
|
InstanceofKeyword,
|
|
StrictModeReservedWord,
|
|
Implements,
|
|
Interface,
|
|
Package,
|
|
Private,
|
|
Protected,
|
|
Public,
|
|
Static,
|
|
Yield,
|
|
Let,
|
|
KeywordKindEnd
|
|
};
|
|
|
|
ALWAYS_INLINE bool isDecimalDigit(char16_t ch)
|
|
{
|
|
return (ch >= '0' && ch <= '9'); // 0..9
|
|
}
|
|
|
|
ALWAYS_INLINE bool isHexDigit(char16_t ch)
|
|
{
|
|
return isDecimalDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
|
|
}
|
|
|
|
ALWAYS_INLINE bool isOctalDigit(char16_t ch)
|
|
{
|
|
return (ch >= '0' && ch < '8'); // 0..7
|
|
}
|
|
|
|
ALWAYS_INLINE bool octalValue(char16_t ch)
|
|
{
|
|
ASSERT(isOctalDigit(ch));
|
|
return ch - '0';
|
|
}
|
|
|
|
ALWAYS_INLINE uint8_t toHexNumericValue(char16_t ch)
|
|
{
|
|
return ch < 'A' ? ch - '0' : (ch - 'A' + 10) & 0xF;
|
|
}
|
|
|
|
// ECMA-262 11.2 White Space
|
|
ALWAYS_INLINE bool isWhiteSpace(char16_t ch)
|
|
{
|
|
return (ch == 0x20) || (ch == 0x09) || (ch == 0x0B) || (ch == 0x0C) || (ch == 0xA0)
|
|
|| (ch >= 0x1680 && (ch == 0x1680 || ch == 0x180E || ch == 0x2000 || ch == 0x2001
|
|
|| ch == 0x2002 || ch == 0x2003 || ch == 0x2004 || ch == 0x2005 || ch == 0x2006
|
|
|| ch == 0x2007 || ch == 0x2008 || ch == 0x2009 || ch == 0x200A || ch == 0x202F
|
|
|| ch == 0x205F || ch == 0x3000 || ch == 0xFEFF));
|
|
}
|
|
|
|
// ECMA-262 11.3 Line Terminators
|
|
ALWAYS_INLINE bool isLineTerminator(char16_t ch)
|
|
{
|
|
return (ch == 0x0A) || (ch == 0x0D) || (ch == 0x2028) || (ch == 0x2029);
|
|
}
|
|
|
|
struct ParserCharPiece {
|
|
char16_t data[3];
|
|
size_t length;
|
|
|
|
ParserCharPiece(const char16_t a)
|
|
{
|
|
data[0] = a;
|
|
data[1] = 0;
|
|
length = 1;
|
|
}
|
|
|
|
ParserCharPiece(const char32_t a)
|
|
{
|
|
if (a < 0x10000) {
|
|
data[0] = a;
|
|
data[1] = 0;
|
|
length = 1;
|
|
} else {
|
|
data[0] = (char16_t)(0xD800 + ((a - 0x10000) >> 10));
|
|
data[1] = (char16_t)(0xDC00 + ((a - 0x10000) & 1023));
|
|
data[2] = 0;
|
|
length = 2;
|
|
}
|
|
}
|
|
|
|
ParserCharPiece(const char16_t a, const char16_t b)
|
|
{
|
|
data[0] = a;
|
|
data[1] = b;
|
|
data[2] = 0;
|
|
length = 2;
|
|
}
|
|
};
|
|
|
|
// ECMA-262 11.6 Identifier Names and Identifiers
|
|
ALWAYS_INLINE ParserCharPiece fromCodePoint(char32_t cp)
|
|
{
|
|
if (cp < 0x10000) {
|
|
return ParserCharPiece((char16_t)cp);
|
|
} else {
|
|
return ParserCharPiece((char16_t)(0xD800 + ((cp - 0x10000) >> 10)), (char16_t)(0xDC00 + ((cp - 0x10000) & 1023)));
|
|
}
|
|
}
|
|
|
|
bool isIdentifierPartSlow(char32_t ch)
|
|
{
|
|
return (ch == 0xAA) || (ch == 0xB5) || (ch == 0xB7) || (ch == 0xBA) || (0xC0 <= ch && ch <= 0xD6) || (0xD8 <= ch && ch <= 0xF6) || (0xF8 <= ch && ch <= 0x02C1) || (0x02C6 <= ch && ch <= 0x02D1) || (0x02E0 <= ch && ch <= 0x02E4) || (ch == 0x02EC) || (ch == 0x02EE) || (0x0300 <= ch && ch <= 0x0374) || (ch == 0x0376) || (ch == 0x0377) || (0x037A <= ch && ch <= 0x037D) || (ch == 0x037F) || (0x0386 <= ch && ch <= 0x038A) || (ch == 0x038C) || (0x038E <= ch && ch <= 0x03A1) || (0x03A3 <= ch && ch <= 0x03F5) || (0x03F7 <= ch && ch <= 0x0481) || (0x0483 <= ch && ch <= 0x0487) || (0x048A <= ch && ch <= 0x052F) || (0x0531 <= ch && ch <= 0x0556) || (ch == 0x0559) || (0x0561 <= ch && ch <= 0x0587) || (0x0591 <= ch && ch <= 0x05BD) || (ch == 0x05BF) || (ch == 0x05C1) || (ch == 0x05C2) || (ch == 0x05C4) || (ch == 0x05C5) || (ch == 0x05C7) || (0x05D0 <= ch && ch <= 0x05EA) || (0x05F0 <= ch && ch <= 0x05F2) || (0x0610 <= ch && ch <= 0x061A) || (0x0620 <= ch && ch <= 0x0669) || (0x066E <= ch && ch <= 0x06D3) || (0x06D5 <= ch && ch <= 0x06DC) || (0x06DF <= ch && ch <= 0x06E8) || (0x06EA <= ch && ch <= 0x06FC) || (ch == 0x06FF) || (0x0710 <= ch && ch <= 0x074A) || (0x074D <= ch && ch <= 0x07B1) || (0x07C0 <= ch && ch <= 0x07F5) || (ch == 0x07FA) || (0x0800 <= ch && ch <= 0x082D) || (0x0840 <= ch && ch <= 0x085B) || (0x08A0 <= ch && ch <= 0x08B2) || (0x08E4 <= ch && ch <= 0x0963) || (0x0966 <= ch && ch <= 0x096F) || (0x0971 <= ch && ch <= 0x0983) || (0x0985 <= ch && ch <= 0x098C) || (ch == 0x098F) || (ch == 0x0990) || (0x0993 <= ch && ch <= 0x09A8) || (0x09AA <= ch && ch <= 0x09B0) || (ch == 0x09B2) || (0x09B6 <= ch && ch <= 0x09B9) || (0x09BC <= ch && ch <= 0x09C4) || (ch == 0x09C7) || (ch == 0x09C8) || (0x09CB <= ch && ch <= 0x09CE) || (ch == 0x09D7) || (ch == 0x09DC) || (ch == 0x09DD) || (0x09DF <= ch && ch <= 0x09E3) || (0x09E6 <= ch && ch <= 0x09F1) || (0x0A01 <= ch && ch <= 0x0A03) || (0x0A05 <= ch && ch <= 0x0A0A) || (ch == 0x0A0F) || (ch == 0x0A10) || (0x0A13 <= ch && ch <= 0x0A28) || (0x0A2A <= ch && ch <= 0x0A30) || (ch == 0x0A32) || (ch == 0x0A33) || (ch == 0x0A35) || (ch == 0x0A36) || (ch == 0x0A38) || (ch == 0x0A39) || (ch == 0x0A3C) || (0x0A3E <= ch && ch <= 0x0A42) || (ch == 0x0A47) || (ch == 0x0A48) || (0x0A4B <= ch && ch <= 0x0A4D) || (ch == 0x0A51) || (0x0A59 <= ch && ch <= 0x0A5C) || (ch == 0x0A5E) || (0x0A66 <= ch && ch <= 0x0A75) || (0x0A81 <= ch && ch <= 0x0A83) || (0x0A85 <= ch && ch <= 0x0A8D) || (0x0A8F <= ch && ch <= 0x0A91) || (0x0A93 <= ch && ch <= 0x0AA8) || (0x0AAA <= ch && ch <= 0x0AB0) || (ch == 0x0AB2) || (ch == 0x0AB3) || (0x0AB5 <= ch && ch <= 0x0AB9) || (0x0ABC <= ch && ch <= 0x0AC5) || (0x0AC7 <= ch && ch <= 0x0AC9) || (0x0ACB <= ch && ch <= 0x0ACD) || (ch == 0x0AD0) || (0x0AE0 <= ch && ch <= 0x0AE3) || (0x0AE6 <= ch && ch <= 0x0AEF) || (0x0B01 <= ch && ch <= 0x0B03) || (0x0B05 <= ch && ch <= 0x0B0C) || (ch == 0x0B0F) || (ch == 0x0B10) || (0x0B13 <= ch && ch <= 0x0B28) || (0x0B2A <= ch && ch <= 0x0B30) || (ch == 0x0B32) || (ch == 0x0B33) || (0x0B35 <= ch && ch <= 0x0B39) || (0x0B3C <= ch && ch <= 0x0B44) || (ch == 0x0B47) || (ch == 0x0B48) || (0x0B4B <= ch && ch <= 0x0B4D) || (ch == 0x0B56) || (ch == 0x0B57) || (ch == 0x0B5C) || (ch == 0x0B5D) || (0x0B5F <= ch && ch <= 0x0B63) || (0x0B66 <= ch && ch <= 0x0B6F) || (ch == 0x0B71) || (ch == 0x0B82) || (ch == 0x0B83) || (0x0B85 <= ch && ch <= 0x0B8A) || (0x0B8E <= ch && ch <= 0x0B90) || (0x0B92 <= ch && ch <= 0x0B95) || (ch == 0x0B99) || (ch == 0x0B9A) || (ch == 0x0B9C) || (ch == 0x0B9E) || (ch == 0x0B9F) || (ch == 0x0BA3) || (ch == 0x0BA4) || (0x0BA8 <= ch && ch <= 0x0BAA) || (0x0BAE <= ch && ch <= 0x0BB9) || (0x0BBE <= ch && ch <= 0x0BC2) || (0x0BC6 <= ch && ch <= 0x0BC8) || (0x0BCA <= ch && ch <= 0x0BCD) || (ch == 0x0BD0) || (ch == 0x0BD7) || (0x0BE6 <= ch && ch <= 0x0BEF) || (0x0C00 <= ch && ch <= 0x0C03) || (0x0C05 <= ch && ch <= 0x0C0C) || (0x0C0E <= ch && ch <= 0x0C10) || (0x0C12 <= ch && ch <= 0x0C28) || (0x0C2A <= ch && ch <= 0x0C39) || (0x0C3D <= ch && ch <= 0x0C44) || (0x0C46 <= ch && ch <= 0x0C48) || (0x0C4A <= ch && ch <= 0x0C4D) || (ch == 0x0C55) || (ch == 0x0C56) || (ch == 0x0C58) || (ch == 0x0C59) || (0x0C60 <= ch && ch <= 0x0C63) || (0x0C66 <= ch && ch <= 0x0C6F) || (0x0C81 <= ch && ch <= 0x0C83) || (0x0C85 <= ch && ch <= 0x0C8C) || (0x0C8E <= ch && ch <= 0x0C90) || (0x0C92 <= ch && ch <= 0x0CA8) || (0x0CAA <= ch && ch <= 0x0CB3) || (0x0CB5 <= ch && ch <= 0x0CB9) || (0x0CBC <= ch && ch <= 0x0CC4) || (0x0CC6 <= ch && ch <= 0x0CC8) || (0x0CCA <= ch && ch <= 0x0CCD) || (ch == 0x0CD5) || (ch == 0x0CD6) || (ch == 0x0CDE) || (0x0CE0 <= ch && ch <= 0x0CE3) || (0x0CE6 <= ch && ch <= 0x0CEF) || (ch == 0x0CF1) || (ch == 0x0CF2) || (0x0D01 <= ch && ch <= 0x0D03) || (0x0D05 <= ch && ch <= 0x0D0C) || (0x0D0E <= ch && ch <= 0x0D10) || (0x0D12 <= ch && ch <= 0x0D3A) || (0x0D3D <= ch && ch <= 0x0D44) || (0x0D46 <= ch && ch <= 0x0D48) || (0x0D4A <= ch && ch <= 0x0D4E) || (ch == 0x0D57) || (0x0D60 <= ch && ch <= 0x0D63) || (0x0D66 <= ch && ch <= 0x0D6F) || (0x0D7A <= ch && ch <= 0x0D7F) || (ch == 0x0D82) || (ch == 0x0D83) || (0x0D85 <= ch && ch <= 0x0D96) || (0x0D9A <= ch && ch <= 0x0DB1) || (0x0DB3 <= ch && ch <= 0x0DBB) || (ch == 0x0DBD) || (0x0DC0 <= ch && ch <= 0x0DC6) || (ch == 0x0DCA) || (0x0DCF <= ch && ch <= 0x0DD4) || (ch == 0x0DD6) || (0x0DD8 <= ch && ch <= 0x0DDF) || (0x0DE6 <= ch && ch <= 0x0DEF) || (ch == 0x0DF2) || (ch == 0x0DF3) || (0x0E01 <= ch && ch <= 0x0E3A) || (0x0E40 <= ch && ch <= 0x0E4E) || (0x0E50 <= ch && ch <= 0x0E59) || (ch == 0x0E81) || (ch == 0x0E82) || (ch == 0x0E84) || (ch == 0x0E87) || (ch == 0x0E88) || (ch == 0x0E8A) || (ch == 0x0E8D) || (0x0E94 <= ch && ch <= 0x0E97) || (0x0E99 <= ch && ch <= 0x0E9F) || (0x0EA1 <= ch && ch <= 0x0EA3) || (ch == 0x0EA5) || (ch == 0x0EA7) || (ch == 0x0EAA) || (ch == 0x0EAB) || (0x0EAD <= ch && ch <= 0x0EB9) || (0x0EBB <= ch && ch <= 0x0EBD) || (0x0EC0 <= ch && ch <= 0x0EC4) || (ch == 0x0EC6) || (0x0EC8 <= ch && ch <= 0x0ECD) || (0x0ED0 <= ch && ch <= 0x0ED9) || (0x0EDC <= ch && ch <= 0x0EDF) || (ch == 0x0F00) || (ch == 0x0F18) || (ch == 0x0F19) || (0x0F20 <= ch && ch <= 0x0F29) || (ch == 0x0F35) || (ch == 0x0F37) || (ch == 0x0F39) || (0x0F3E <= ch && ch <= 0x0F47) || (0x0F49 <= ch && ch <= 0x0F6C) || (0x0F71 <= ch && ch <= 0x0F84) || (0x0F86 <= ch && ch <= 0x0F97) || (0x0F99 <= ch && ch <= 0x0FBC) || (ch == 0x0FC6) || (0x1000 <= ch && ch <= 0x1049) || (0x1050 <= ch && ch <= 0x109D) || (0x10A0 <= ch && ch <= 0x10C5) || (ch == 0x10C7) || (ch == 0x10CD) || (0x10D0 <= ch && ch <= 0x10FA) || (0x10FC <= ch && ch <= 0x1248) || (0x124A <= ch && ch <= 0x124D) || (0x1250 <= ch && ch <= 0x1256) || (ch == 0x1258) || (0x125A <= ch && ch <= 0x125D) || (0x1260 <= ch && ch <= 0x1288) || (0x128A <= ch && ch <= 0x128D) || (0x1290 <= ch && ch <= 0x12B0) || (0x12B2 <= ch && ch <= 0x12B5) || (0x12B8 <= ch && ch <= 0x12BE) || (ch == 0x12C0) || (0x12C2 <= ch && ch <= 0x12C5) || (0x12C8 <= ch && ch <= 0x12D6) || (0x12D8 <= ch && ch <= 0x1310) || (0x1312 <= ch && ch <= 0x1315) || (0x1318 <= ch && ch <= 0x135A) || (0x135D <= ch && ch <= 0x135F) || (0x1369 <= ch && ch <= 0x1371) || (0x1380 <= ch && ch <= 0x138F) || (0x13A0 <= ch && ch <= 0x13F4) || (0x1401 <= ch && ch <= 0x166C) || (0x166F <= ch && ch <= 0x167F) || (0x1681 <= ch && ch <= 0x169A) || (0x16A0 <= ch && ch <= 0x16EA) || (0x16EE <= ch && ch <= 0x16F8) || (0x1700 <= ch && ch <= 0x170C) || (0x170E <= ch && ch <= 0x1714) || (0x1720 <= ch && ch <= 0x1734) || (0x1740 <= ch && ch <= 0x1753) || (0x1760 <= ch && ch <= 0x176C) || (0x176E <= ch && ch <= 0x1770) || (ch == 0x1772) || (ch == 0x1773) || (0x1780 <= ch && ch <= 0x17D3) || (ch == 0x17D7) || (ch == 0x17DC) || (ch == 0x17DD) || (0x17E0 <= ch && ch <= 0x17E9) || (0x180B <= ch && ch <= 0x180D) || (0x1810 <= ch && ch <= 0x1819) || (0x1820 <= ch && ch <= 0x1877) || (0x1880 <= ch && ch <= 0x18AA) || (0x18B0 <= ch && ch <= 0x18F5) || (0x1900 <= ch && ch <= 0x191E) || (0x1920 <= ch && ch <= 0x192B) || (0x1930 <= ch && ch <= 0x193B) || (0x1946 <= ch && ch <= 0x196D) || (0x1970 <= ch && ch <= 0x1974) || (0x1980 <= ch && ch <= 0x19AB) || (0x19B0 <= ch && ch <= 0x19C9) || (0x19D0 <= ch && ch <= 0x19DA) || (0x1A00 <= ch && ch <= 0x1A1B) || (0x1A20 <= ch && ch <= 0x1A5E) || (0x1A60 <= ch && ch <= 0x1A7C) || (0x1A7F <= ch && ch <= 0x1A89) || (0x1A90 <= ch && ch <= 0x1A99) || (ch == 0x1AA7) || (0x1AB0 <= ch && ch <= 0x1ABD) || (0x1B00 <= ch && ch <= 0x1B4B) || (0x1B50 <= ch && ch <= 0x1B59) || (0x1B6B <= ch && ch <= 0x1B73) || (0x1B80 <= ch && ch <= 0x1BF3) || (0x1C00 <= ch && ch <= 0x1C37) || (0x1C40 <= ch && ch <= 0x1C49) || (0x1C4D <= ch && ch <= 0x1C7D) || (0x1CD0 <= ch && ch <= 0x1CD2) || (0x1CD4 <= ch && ch <= 0x1CF6) || (ch == 0x1CF8) || (ch == 0x1CF9) || (0x1D00 <= ch && ch <= 0x1DF5) || (0x1DFC <= ch && ch <= 0x1F15) || (0x1F18 <= ch && ch <= 0x1F1D) || (0x1F20 <= ch && ch <= 0x1F45) || (0x1F48 <= ch && ch <= 0x1F4D) || (0x1F50 <= ch && ch <= 0x1F57) || (ch == 0x1F59) || (ch == 0x1F5B) || (ch == 0x1F5D) || (0x1F5F <= ch && ch <= 0x1F7D) || (0x1F80 <= ch && ch <= 0x1FB4) || (0x1FB6 <= ch && ch <= 0x1FBC) || (ch == 0x1FBE) || (0x1FC2 <= ch && ch <= 0x1FC4) || (0x1FC6 <= ch && ch <= 0x1FCC) || (0x1FD0 <= ch && ch <= 0x1FD3) || (0x1FD6 <= ch && ch <= 0x1FDB) || (0x1FE0 <= ch && ch <= 0x1FEC) || (0x1FF2 <= ch && ch <= 0x1FF4) || (0x1FF6 <= ch && ch <= 0x1FFC) || (ch == 0x200C) || (ch == 0x200D) || (ch == 0x203F) || (ch == 0x2040) || (ch == 0x2054) || (ch == 0x2071) || (ch == 0x207F) || (0x2090 <= ch && ch <= 0x209C) || (0x20D0 <= ch && ch <= 0x20DC) || (ch == 0x20E1) || (0x20E5 <= ch && ch <= 0x20F0) || (ch == 0x2102) || (ch == 0x2107) || (0x210A <= ch && ch <= 0x2113) || (ch == 0x2115) || (0x2118 <= ch && ch <= 0x211D) || (ch == 0x2124) || (ch == 0x2126) || (ch == 0x2128) || (0x212A <= ch && ch <= 0x2139) || (0x213C <= ch && ch <= 0x213F) || (0x2145 <= ch && ch <= 0x2149) || (ch == 0x214E) || (0x2160 <= ch && ch <= 0x2188) || (0x2C00 <= ch && ch <= 0x2C2E) || (0x2C30 <= ch && ch <= 0x2C5E) || (0x2C60 <= ch && ch <= 0x2CE4) || (0x2CEB <= ch && ch <= 0x2CF3) || (0x2D00 <= ch && ch <= 0x2D25) || (ch == 0x2D27) || (ch == 0x2D2D) || (0x2D30 <= ch && ch <= 0x2D67) || (ch == 0x2D6F) || (0x2D7F <= ch && ch <= 0x2D96) || (0x2DA0 <= ch && ch <= 0x2DA6) || (0x2DA8 <= ch && ch <= 0x2DAE) || (0x2DB0 <= ch && ch <= 0x2DB6) || (0x2DB8 <= ch && ch <= 0x2DBE) || (0x2DC0 <= ch && ch <= 0x2DC6) || (0x2DC8 <= ch && ch <= 0x2DCE) || (0x2DD0 <= ch && ch <= 0x2DD6) || (0x2DD8 <= ch && ch <= 0x2DDE) || (0x2DE0 <= ch && ch <= 0x2DFF) || (0x3005 <= ch && ch <= 0x3007) || (0x3021 <= ch && ch <= 0x302F) || (0x3031 <= ch && ch <= 0x3035) || (0x3038 <= ch && ch <= 0x303C) || (0x3041 <= ch && ch <= 0x3096) || (0x3099 <= ch && ch <= 0x309F) || (0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x3105 <= ch && ch <= 0x312D) || (0x3131 <= ch && ch <= 0x318E) || (0x31A0 <= ch && ch <= 0x31BA) || (0x31F0 <= ch && ch <= 0x31FF) || (0x3400 <= ch && ch <= 0x4DB5) || (0x4E00 <= ch && ch <= 0x9FCC) || (0xA000 <= ch && ch <= 0xA48C) || (0xA4D0 <= ch && ch <= 0xA4FD) || (0xA500 <= ch && ch <= 0xA60C) || (0xA610 <= ch && ch <= 0xA62B) || (0xA640 <= ch && ch <= 0xA66F) || (0xA674 <= ch && ch <= 0xA67D) || (0xA67F <= ch && ch <= 0xA69D) || (0xA69F <= ch && ch <= 0xA6F1) || (0xA717 <= ch && ch <= 0xA71F) || (0xA722 <= ch && ch <= 0xA788) || (0xA78B <= ch && ch <= 0xA78E) || (0xA790 <= ch && ch <= 0xA7AD) || (ch == 0xA7B0) || (ch == 0xA7B1) || (0xA7F7 <= ch && ch <= 0xA827) || (0xA840 <= ch && ch <= 0xA873) || (0xA880 <= ch && ch <= 0xA8C4) || (0xA8D0 <= ch && ch <= 0xA8D9) || (0xA8E0 <= ch && ch <= 0xA8F7) || (ch == 0xA8FB) || (0xA900 <= ch && ch <= 0xA92D) || (0xA930 <= ch && ch <= 0xA953) || (0xA960 <= ch && ch <= 0xA97C) || (0xA980 <= ch && ch <= 0xA9C0) || (0xA9CF <= ch && ch <= 0xA9D9) || (0xA9E0 <= ch && ch <= 0xA9FE) || (0xAA00 <= ch && ch <= 0xAA36) || (0xAA40 <= ch && ch <= 0xAA4D) || (0xAA50 <= ch && ch <= 0xAA59) || (0xAA60 <= ch && ch <= 0xAA76) || (0xAA7A <= ch && ch <= 0xAAC2) || (0xAADB <= ch && ch <= 0xAADD) || (0xAAE0 <= ch && ch <= 0xAAEF) || (0xAAF2 <= ch && ch <= 0xAAF6) || (0xAB01 <= ch && ch <= 0xAB06) || (0xAB09 <= ch && ch <= 0xAB0E) || (0xAB11 <= ch && ch <= 0xAB16) || (0xAB20 <= ch && ch <= 0xAB26) || (0xAB28 <= ch && ch <= 0xAB2E) || (0xAB30 <= ch && ch <= 0xAB5A) || (0xAB5C <= ch && ch <= 0xAB5F) || (ch == 0xAB64) || (ch == 0xAB65) || (0xABC0 <= ch && ch <= 0xABEA) || (ch == 0xABEC) || (ch == 0xABED) || (0xABF0 <= ch && ch <= 0xABF9) || (0xAC00 <= ch && ch <= 0xD7A3) || (0xD7B0 <= ch && ch <= 0xD7C6) || (0xD7CB <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFA6D) || (0xFA70 <= ch && ch <= 0xFAD9) || (0xFB00 <= ch && ch <= 0xFB06) || (0xFB13 <= ch && ch <= 0xFB17) || (0xFB1D <= ch && ch <= 0xFB28) || (0xFB2A <= ch && ch <= 0xFB36) || (0xFB38 <= ch && ch <= 0xFB3C) || (ch == 0xFB3E) || (ch == 0xFB40) || (ch == 0xFB41) || (ch == 0xFB43) || (ch == 0xFB44) || (0xFB46 <= ch && ch <= 0xFBB1) || (0xFBD3 <= ch && ch <= 0xFD3D) || (0xFD50 <= ch && ch <= 0xFD8F) || (0xFD92 <= ch && ch <= 0xFDC7) || (0xFDF0 <= ch && ch <= 0xFDFB) || (0xFE00 <= ch && ch <= 0xFE0F) || (0xFE20 <= ch && ch <= 0xFE2D) || (ch == 0xFE33) || (ch == 0xFE34) || (0xFE4D <= ch && ch <= 0xFE4F) || (0xFE70 <= ch && ch <= 0xFE74) || (0xFE76 <= ch && ch <= 0xFEFC) || (0xFF10 <= ch && ch <= 0xFF19) || (0xFF21 <= ch && ch <= 0xFF3A) || (ch == 0xFF3F) || (0xFF41 <= ch && ch <= 0xFF5A) || (0xFF66 <= ch && ch <= 0xFFBE) || (0xFFC2 <= ch && ch <= 0xFFC7) || (0xFFCA <= ch && ch <= 0xFFCF) || (0xFFD2 <= ch && ch <= 0xFFD7) || (0xFFDA <= ch && ch <= 0xFFDC);
|
|
}
|
|
|
|
ALWAYS_INLINE bool isIdentifierPart(char32_t ch)
|
|
{
|
|
return (ch >= 97 && ch <= 122) // a..z
|
|
|| (ch >= 65 && ch <= 90) // A..Z
|
|
|| (ch >= 48 && ch <= 57) // 0..9
|
|
|| (ch == 36) || (ch == 95) // $ (dollar) and _ (underscore)
|
|
|| (ch == 92) // \ (backslash)
|
|
|| isIdentifierPartSlow(ch);
|
|
}
|
|
|
|
bool isIdentifierStartSlow(char32_t ch)
|
|
{
|
|
return (ch == 0xAA) || (ch == 0xB5) || (ch == 0xB7) || (ch == 0xBA) || (0xC0 <= ch && ch <= 0xD6) || (0xD8 <= ch && ch <= 0xF6) || (0xF8 <= ch && ch <= 0x02C1) || (0x02C6 <= ch && ch <= 0x02D1) || (0x02E0 <= ch && ch <= 0x02E4) || (ch == 0x02EC) || (ch == 0x02EE) || (0x0300 <= ch && ch <= 0x0374) || (ch == 0x0376) || (ch == 0x0377) || (0x037A <= ch && ch <= 0x037D) || (ch == 0x037F) || (0x0386 <= ch && ch <= 0x038A) || (ch == 0x038C) || (0x038E <= ch && ch <= 0x03A1) || (0x03A3 <= ch && ch <= 0x03F5) || (0x03F7 <= ch && ch <= 0x0481) || (0x0483 <= ch && ch <= 0x0487) || (0x048A <= ch && ch <= 0x052F) || (0x0531 <= ch && ch <= 0x0556) || (ch == 0x0559) || (0x0561 <= ch && ch <= 0x0587) || (0x0591 <= ch && ch <= 0x05BD) || (ch == 0x05BF) || (ch == 0x05C1) || (ch == 0x05C2) || (ch == 0x05C4) || (ch == 0x05C5) || (ch == 0x05C7) || (0x05D0 <= ch && ch <= 0x05EA) || (0x05F0 <= ch && ch <= 0x05F2) || (0x0610 <= ch && ch <= 0x061A) || (0x0620 <= ch && ch <= 0x0669) || (0x066E <= ch && ch <= 0x06D3) || (0x06D5 <= ch && ch <= 0x06DC) || (0x06DF <= ch && ch <= 0x06E8) || (0x06EA <= ch && ch <= 0x06FC) || (ch == 0x06FF) || (0x0710 <= ch && ch <= 0x074A) || (0x074D <= ch && ch <= 0x07B1) || (0x07C0 <= ch && ch <= 0x07F5) || (ch == 0x07FA) || (0x0800 <= ch && ch <= 0x082D) || (0x0840 <= ch && ch <= 0x085B) || (0x08A0 <= ch && ch <= 0x08B2) || (0x08E4 <= ch && ch <= 0x0963) || (0x0966 <= ch && ch <= 0x096F) || (0x0971 <= ch && ch <= 0x0983) || (0x0985 <= ch && ch <= 0x098C) || (ch == 0x098F) || (ch == 0x0990) || (0x0993 <= ch && ch <= 0x09A8) || (0x09AA <= ch && ch <= 0x09B0) || (ch == 0x09B2) || (0x09B6 <= ch && ch <= 0x09B9) || (0x09BC <= ch && ch <= 0x09C4) || (ch == 0x09C7) || (ch == 0x09C8) || (0x09CB <= ch && ch <= 0x09CE) || (ch == 0x09D7) || (ch == 0x09DC) || (ch == 0x09DD) || (0x09DF <= ch && ch <= 0x09E3) || (0x09E6 <= ch && ch <= 0x09F1) || (0x0A01 <= ch && ch <= 0x0A03) || (0x0A05 <= ch && ch <= 0x0A0A) || (ch == 0x0A0F) || (ch == 0x0A10) || (0x0A13 <= ch && ch <= 0x0A28) || (0x0A2A <= ch && ch <= 0x0A30) || (ch == 0x0A32) || (ch == 0x0A33) || (ch == 0x0A35) || (ch == 0x0A36) || (ch == 0x0A38) || (ch == 0x0A39) || (ch == 0x0A3C) || (0x0A3E <= ch && ch <= 0x0A42) || (ch == 0x0A47) || (ch == 0x0A48) || (0x0A4B <= ch && ch <= 0x0A4D) || (ch == 0x0A51) || (0x0A59 <= ch && ch <= 0x0A5C) || (ch == 0x0A5E) || (0x0A66 <= ch && ch <= 0x0A75) || (0x0A81 <= ch && ch <= 0x0A83) || (0x0A85 <= ch && ch <= 0x0A8D) || (0x0A8F <= ch && ch <= 0x0A91) || (0x0A93 <= ch && ch <= 0x0AA8) || (0x0AAA <= ch && ch <= 0x0AB0) || (ch == 0x0AB2) || (ch == 0x0AB3) || (0x0AB5 <= ch && ch <= 0x0AB9) || (0x0ABC <= ch && ch <= 0x0AC5) || (0x0AC7 <= ch && ch <= 0x0AC9) || (0x0ACB <= ch && ch <= 0x0ACD) || (ch == 0x0AD0) || (0x0AE0 <= ch && ch <= 0x0AE3) || (0x0AE6 <= ch && ch <= 0x0AEF) || (0x0B01 <= ch && ch <= 0x0B03) || (0x0B05 <= ch && ch <= 0x0B0C) || (ch == 0x0B0F) || (ch == 0x0B10) || (0x0B13 <= ch && ch <= 0x0B28) || (0x0B2A <= ch && ch <= 0x0B30) || (ch == 0x0B32) || (ch == 0x0B33) || (0x0B35 <= ch && ch <= 0x0B39) || (0x0B3C <= ch && ch <= 0x0B44) || (ch == 0x0B47) || (ch == 0x0B48) || (0x0B4B <= ch && ch <= 0x0B4D) || (ch == 0x0B56) || (ch == 0x0B57) || (ch == 0x0B5C) || (ch == 0x0B5D) || (0x0B5F <= ch && ch <= 0x0B63) || (0x0B66 <= ch && ch <= 0x0B6F) || (ch == 0x0B71) || (ch == 0x0B82) || (ch == 0x0B83) || (0x0B85 <= ch && ch <= 0x0B8A) || (0x0B8E <= ch && ch <= 0x0B90) || (0x0B92 <= ch && ch <= 0x0B95) || (ch == 0x0B99) || (ch == 0x0B9A) || (ch == 0x0B9C) || (ch == 0x0B9E) || (ch == 0x0B9F) || (ch == 0x0BA3) || (ch == 0x0BA4) || (0x0BA8 <= ch && ch <= 0x0BAA) || (0x0BAE <= ch && ch <= 0x0BB9) || (0x0BBE <= ch && ch <= 0x0BC2) || (0x0BC6 <= ch && ch <= 0x0BC8) || (0x0BCA <= ch && ch <= 0x0BCD) || (ch == 0x0BD0) || (ch == 0x0BD7) || (0x0BE6 <= ch && ch <= 0x0BEF) || (0x0C00 <= ch && ch <= 0x0C03) || (0x0C05 <= ch && ch <= 0x0C0C) || (0x0C0E <= ch && ch <= 0x0C10) || (0x0C12 <= ch && ch <= 0x0C28) || (0x0C2A <= ch && ch <= 0x0C39) || (0x0C3D <= ch && ch <= 0x0C44) || (0x0C46 <= ch && ch <= 0x0C48) || (0x0C4A <= ch && ch <= 0x0C4D) || (ch == 0x0C55) || (ch == 0x0C56) || (ch == 0x0C58) || (ch == 0x0C59) || (0x0C60 <= ch && ch <= 0x0C63) || (0x0C66 <= ch && ch <= 0x0C6F) || (0x0C81 <= ch && ch <= 0x0C83) || (0x0C85 <= ch && ch <= 0x0C8C) || (0x0C8E <= ch && ch <= 0x0C90) || (0x0C92 <= ch && ch <= 0x0CA8) || (0x0CAA <= ch && ch <= 0x0CB3) || (0x0CB5 <= ch && ch <= 0x0CB9) || (0x0CBC <= ch && ch <= 0x0CC4) || (0x0CC6 <= ch && ch <= 0x0CC8) || (0x0CCA <= ch && ch <= 0x0CCD) || (ch == 0x0CD5) || (ch == 0x0CD6) || (ch == 0x0CDE) || (0x0CE0 <= ch && ch <= 0x0CE3) || (0x0CE6 <= ch && ch <= 0x0CEF) || (ch == 0x0CF1) || (ch == 0x0CF2) || (0x0D01 <= ch && ch <= 0x0D03) || (0x0D05 <= ch && ch <= 0x0D0C) || (0x0D0E <= ch && ch <= 0x0D10) || (0x0D12 <= ch && ch <= 0x0D3A) || (0x0D3D <= ch && ch <= 0x0D44) || (0x0D46 <= ch && ch <= 0x0D48) || (0x0D4A <= ch && ch <= 0x0D4E) || (ch == 0x0D57) || (0x0D60 <= ch && ch <= 0x0D63) || (0x0D66 <= ch && ch <= 0x0D6F) || (0x0D7A <= ch && ch <= 0x0D7F) || (ch == 0x0D82) || (ch == 0x0D83) || (0x0D85 <= ch && ch <= 0x0D96) || (0x0D9A <= ch && ch <= 0x0DB1) || (0x0DB3 <= ch && ch <= 0x0DBB) || (ch == 0x0DBD) || (0x0DC0 <= ch && ch <= 0x0DC6) || (ch == 0x0DCA) || (0x0DCF <= ch && ch <= 0x0DD4) || (ch == 0x0DD6) || (0x0DD8 <= ch && ch <= 0x0DDF) || (0x0DE6 <= ch && ch <= 0x0DEF) || (ch == 0x0DF2) || (ch == 0x0DF3) || (0x0E01 <= ch && ch <= 0x0E3A) || (0x0E40 <= ch && ch <= 0x0E4E) || (0x0E50 <= ch && ch <= 0x0E59) || (ch == 0x0E81) || (ch == 0x0E82) || (ch == 0x0E84) || (ch == 0x0E87) || (ch == 0x0E88) || (ch == 0x0E8A) || (ch == 0x0E8D) || (0x0E94 <= ch && ch <= 0x0E97) || (0x0E99 <= ch && ch <= 0x0E9F) || (0x0EA1 <= ch && ch <= 0x0EA3) || (ch == 0x0EA5) || (ch == 0x0EA7) || (ch == 0x0EAA) || (ch == 0x0EAB) || (0x0EAD <= ch && ch <= 0x0EB9) || (0x0EBB <= ch && ch <= 0x0EBD) || (0x0EC0 <= ch && ch <= 0x0EC4) || (ch == 0x0EC6) || (0x0EC8 <= ch && ch <= 0x0ECD) || (0x0ED0 <= ch && ch <= 0x0ED9) || (0x0EDC <= ch && ch <= 0x0EDF) || (ch == 0x0F00) || (ch == 0x0F18) || (ch == 0x0F19) || (0x0F20 <= ch && ch <= 0x0F29) || (ch == 0x0F35) || (ch == 0x0F37) || (ch == 0x0F39) || (0x0F3E <= ch && ch <= 0x0F47) || (0x0F49 <= ch && ch <= 0x0F6C) || (0x0F71 <= ch && ch <= 0x0F84) || (0x0F86 <= ch && ch <= 0x0F97) || (0x0F99 <= ch && ch <= 0x0FBC) || (ch == 0x0FC6) || (0x1000 <= ch && ch <= 0x1049) || (0x1050 <= ch && ch <= 0x109D) || (0x10A0 <= ch && ch <= 0x10C5) || (ch == 0x10C7) || (ch == 0x10CD) || (0x10D0 <= ch && ch <= 0x10FA) || (0x10FC <= ch && ch <= 0x1248) || (0x124A <= ch && ch <= 0x124D) || (0x1250 <= ch && ch <= 0x1256) || (ch == 0x1258) || (0x125A <= ch && ch <= 0x125D) || (0x1260 <= ch && ch <= 0x1288) || (0x128A <= ch && ch <= 0x128D) || (0x1290 <= ch && ch <= 0x12B0) || (0x12B2 <= ch && ch <= 0x12B5) || (0x12B8 <= ch && ch <= 0x12BE) || (ch == 0x12C0) || (0x12C2 <= ch && ch <= 0x12C5) || (0x12C8 <= ch && ch <= 0x12D6) || (0x12D8 <= ch && ch <= 0x1310) || (0x1312 <= ch && ch <= 0x1315) || (0x1318 <= ch && ch <= 0x135A) || (0x135D <= ch && ch <= 0x135F) || (0x1369 <= ch && ch <= 0x1371) || (0x1380 <= ch && ch <= 0x138F) || (0x13A0 <= ch && ch <= 0x13F4) || (0x1401 <= ch && ch <= 0x166C) || (0x166F <= ch && ch <= 0x167F) || (0x1681 <= ch && ch <= 0x169A) || (0x16A0 <= ch && ch <= 0x16EA) || (0x16EE <= ch && ch <= 0x16F8) || (0x1700 <= ch && ch <= 0x170C) || (0x170E <= ch && ch <= 0x1714) || (0x1720 <= ch && ch <= 0x1734) || (0x1740 <= ch && ch <= 0x1753) || (0x1760 <= ch && ch <= 0x176C) || (0x176E <= ch && ch <= 0x1770) || (ch == 0x1772) || (ch == 0x1773) || (0x1780 <= ch && ch <= 0x17D3) || (ch == 0x17D7) || (ch == 0x17DC) || (ch == 0x17DD) || (0x17E0 <= ch && ch <= 0x17E9) || (0x180B <= ch && ch <= 0x180D) || (0x1810 <= ch && ch <= 0x1819) || (0x1820 <= ch && ch <= 0x1877) || (0x1880 <= ch && ch <= 0x18AA) || (0x18B0 <= ch && ch <= 0x18F5) || (0x1900 <= ch && ch <= 0x191E) || (0x1920 <= ch && ch <= 0x192B) || (0x1930 <= ch && ch <= 0x193B) || (0x1946 <= ch && ch <= 0x196D) || (0x1970 <= ch && ch <= 0x1974) || (0x1980 <= ch && ch <= 0x19AB) || (0x19B0 <= ch && ch <= 0x19C9) || (0x19D0 <= ch && ch <= 0x19DA) || (0x1A00 <= ch && ch <= 0x1A1B) || (0x1A20 <= ch && ch <= 0x1A5E) || (0x1A60 <= ch && ch <= 0x1A7C) || (0x1A7F <= ch && ch <= 0x1A89) || (0x1A90 <= ch && ch <= 0x1A99) || (ch == 0x1AA7) || (0x1AB0 <= ch && ch <= 0x1ABD) || (0x1B00 <= ch && ch <= 0x1B4B) || (0x1B50 <= ch && ch <= 0x1B59) || (0x1B6B <= ch && ch <= 0x1B73) || (0x1B80 <= ch && ch <= 0x1BF3) || (0x1C00 <= ch && ch <= 0x1C37) || (0x1C40 <= ch && ch <= 0x1C49) || (0x1C4D <= ch && ch <= 0x1C7D) || (0x1CD0 <= ch && ch <= 0x1CD2) || (0x1CD4 <= ch && ch <= 0x1CF6) || (ch == 0x1CF8) || (ch == 0x1CF9) || (0x1D00 <= ch && ch <= 0x1DF5) || (0x1DFC <= ch && ch <= 0x1F15) || (0x1F18 <= ch && ch <= 0x1F1D) || (0x1F20 <= ch && ch <= 0x1F45) || (0x1F48 <= ch && ch <= 0x1F4D) || (0x1F50 <= ch && ch <= 0x1F57) || (ch == 0x1F59) || (ch == 0x1F5B) || (ch == 0x1F5D) || (0x1F5F <= ch && ch <= 0x1F7D) || (0x1F80 <= ch && ch <= 0x1FB4) || (0x1FB6 <= ch && ch <= 0x1FBC) || (ch == 0x1FBE) || (0x1FC2 <= ch && ch <= 0x1FC4) || (0x1FC6 <= ch && ch <= 0x1FCC) || (0x1FD0 <= ch && ch <= 0x1FD3) || (0x1FD6 <= ch && ch <= 0x1FDB) || (0x1FE0 <= ch && ch <= 0x1FEC) || (0x1FF2 <= ch && ch <= 0x1FF4) || (0x1FF6 <= ch && ch <= 0x1FFC) || (ch == 0x200C) || (ch == 0x200D) || (ch == 0x203F) || (ch == 0x2040) || (ch == 0x2054) || (ch == 0x2071) || (ch == 0x207F) || (0x2090 <= ch && ch <= 0x209C) || (0x20D0 <= ch && ch <= 0x20DC) || (ch == 0x20E1) || (0x20E5 <= ch && ch <= 0x20F0) || (ch == 0x2102) || (ch == 0x2107) || (0x210A <= ch && ch <= 0x2113) || (ch == 0x2115) || (0x2118 <= ch && ch <= 0x211D) || (ch == 0x2124) || (ch == 0x2126) || (ch == 0x2128) || (0x212A <= ch && ch <= 0x2139) || (0x213C <= ch && ch <= 0x213F) || (0x2145 <= ch && ch <= 0x2149) || (ch == 0x214E) || (0x2160 <= ch && ch <= 0x2188) || (0x2C00 <= ch && ch <= 0x2C2E) || (0x2C30 <= ch && ch <= 0x2C5E) || (0x2C60 <= ch && ch <= 0x2CE4) || (0x2CEB <= ch && ch <= 0x2CF3) || (0x2D00 <= ch && ch <= 0x2D25) || (ch == 0x2D27) || (ch == 0x2D2D) || (0x2D30 <= ch && ch <= 0x2D67) || (ch == 0x2D6F) || (0x2D7F <= ch && ch <= 0x2D96) || (0x2DA0 <= ch && ch <= 0x2DA6) || (0x2DA8 <= ch && ch <= 0x2DAE) || (0x2DB0 <= ch && ch <= 0x2DB6) || (0x2DB8 <= ch && ch <= 0x2DBE) || (0x2DC0 <= ch && ch <= 0x2DC6) || (0x2DC8 <= ch && ch <= 0x2DCE) || (0x2DD0 <= ch && ch <= 0x2DD6) || (0x2DD8 <= ch && ch <= 0x2DDE) || (0x2DE0 <= ch && ch <= 0x2DFF) || (0x3005 <= ch && ch <= 0x3007) || (0x3021 <= ch && ch <= 0x302F) || (0x3031 <= ch && ch <= 0x3035) || (0x3038 <= ch && ch <= 0x303C) || (0x3041 <= ch && ch <= 0x3096) || (0x3099 <= ch && ch <= 0x309F) || (0x30A1 <= ch && ch <= 0x30FA) || (0x30FC <= ch && ch <= 0x30FF) || (0x3105 <= ch && ch <= 0x312D) || (0x3131 <= ch && ch <= 0x318E) || (0x31A0 <= ch && ch <= 0x31BA) || (0x31F0 <= ch && ch <= 0x31FF) || (0x3400 <= ch && ch <= 0x4DB5) || (0x4E00 <= ch && ch <= 0x9FCC) || (0xA000 <= ch && ch <= 0xA48C) || (0xA4D0 <= ch && ch <= 0xA4FD) || (0xA500 <= ch && ch <= 0xA60C) || (0xA610 <= ch && ch <= 0xA62B) || (0xA640 <= ch && ch <= 0xA66F) || (0xA674 <= ch && ch <= 0xA67D) || (0xA67F <= ch && ch <= 0xA69D) || (0xA69F <= ch && ch <= 0xA6F1) || (0xA717 <= ch && ch <= 0xA71F) || (0xA722 <= ch && ch <= 0xA788) || (0xA78B <= ch && ch <= 0xA78E) || (0xA790 <= ch && ch <= 0xA7AD) || (ch == 0xA7B0) || (ch == 0xA7B1) || (0xA7F7 <= ch && ch <= 0xA827) || (0xA840 <= ch && ch <= 0xA873) || (0xA880 <= ch && ch <= 0xA8C4) || (0xA8D0 <= ch && ch <= 0xA8D9) || (0xA8E0 <= ch && ch <= 0xA8F7) || (ch == 0xA8FB) || (0xA900 <= ch && ch <= 0xA92D) || (0xA930 <= ch && ch <= 0xA953) || (0xA960 <= ch && ch <= 0xA97C) || (0xA980 <= ch && ch <= 0xA9C0) || (0xA9CF <= ch && ch <= 0xA9D9) || (0xA9E0 <= ch && ch <= 0xA9FE) || (0xAA00 <= ch && ch <= 0xAA36) || (0xAA40 <= ch && ch <= 0xAA4D) || (0xAA50 <= ch && ch <= 0xAA59) || (0xAA60 <= ch && ch <= 0xAA76) || (0xAA7A <= ch && ch <= 0xAAC2) || (0xAADB <= ch && ch <= 0xAADD) || (0xAAE0 <= ch && ch <= 0xAAEF) || (0xAAF2 <= ch && ch <= 0xAAF6) || (0xAB01 <= ch && ch <= 0xAB06) || (0xAB09 <= ch && ch <= 0xAB0E) || (0xAB11 <= ch && ch <= 0xAB16) || (0xAB20 <= ch && ch <= 0xAB26) || (0xAB28 <= ch && ch <= 0xAB2E) || (0xAB30 <= ch && ch <= 0xAB5A) || (0xAB5C <= ch && ch <= 0xAB5F) || (ch == 0xAB64) || (ch == 0xAB65) || (0xABC0 <= ch && ch <= 0xABEA) || (ch == 0xABEC) || (ch == 0xABED) || (0xABF0 <= ch && ch <= 0xABF9) || (0xAC00 <= ch && ch <= 0xD7A3) || (0xD7B0 <= ch && ch <= 0xD7C6) || (0xD7CB <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFA6D) || (0xFA70 <= ch && ch <= 0xFAD9) || (0xFB00 <= ch && ch <= 0xFB06) || (0xFB13 <= ch && ch <= 0xFB17) || (0xFB1D <= ch && ch <= 0xFB28) || (0xFB2A <= ch && ch <= 0xFB36) || (0xFB38 <= ch && ch <= 0xFB3C) || (ch == 0xFB3E) || (ch == 0xFB40) || (ch == 0xFB41) || (ch == 0xFB43) || (ch == 0xFB44) || (0xFB46 <= ch && ch <= 0xFBB1) || (0xFBD3 <= ch && ch <= 0xFD3D) || (0xFD50 <= ch && ch <= 0xFD8F) || (0xFD92 <= ch && ch <= 0xFDC7) || (0xFDF0 <= ch && ch <= 0xFDFB) || (0xFE00 <= ch && ch <= 0xFE0F) || (0xFE20 <= ch && ch <= 0xFE2D) || (ch == 0xFE33) || (ch == 0xFE34) || (0xFE4D <= ch && ch <= 0xFE4F) || (0xFE70 <= ch && ch <= 0xFE74) || (0xFE76 <= ch && ch <= 0xFEFC) || (0xFF10 <= ch && ch <= 0xFF19) || (0xFF21 <= ch && ch <= 0xFF3A) || (ch == 0xFF3F) || (0xFF41 <= ch && ch <= 0xFF5A) || (0xFF66 <= ch && ch <= 0xFFBE) || (0xFFC2 <= ch && ch <= 0xFFC7) || (0xFFCA <= ch && ch <= 0xFFCF) || (0xFFD2 <= ch && ch <= 0xFFD7) || (0xFFDA <= ch && ch <= 0xFFDC);
|
|
}
|
|
|
|
ALWAYS_INLINE bool isIdentifierStart(char32_t ch)
|
|
{
|
|
return (ch >= 97 && ch <= 122) // a..z
|
|
|| (ch >= 65 && ch <= 90) // A..Z
|
|
|| (ch == 36) || (ch == 95) // $ (dollar) and _ (underscore)
|
|
|| (ch == 92) // \ (backslash)
|
|
|| isIdentifierStartSlow(ch);
|
|
}
|
|
|
|
|
|
struct Curly {
|
|
char m_curly[4];
|
|
Curly() { }
|
|
Curly(const char curly[4])
|
|
{
|
|
m_curly[0] = curly[0];
|
|
m_curly[1] = curly[1];
|
|
m_curly[2] = curly[2];
|
|
m_curly[3] = curly[3];
|
|
}
|
|
};
|
|
|
|
namespace Messages {
|
|
const char* UnexpectedToken = "Unexpected token %s";
|
|
const char* UnexpectedTokenIllegal = "Unexpected token ILLEGAL";
|
|
const char* UnexpectedNumber = "Unexpected number";
|
|
const char* UnexpectedString = "Unexpected string";
|
|
const char* UnexpectedIdentifier = "Unexpected identifier";
|
|
const char* UnexpectedReserved = "Unexpected reserved word";
|
|
const char* UnexpectedTemplate = "Unexpected quasi %s";
|
|
const char* UnexpectedEOS = "Unexpected end of input";
|
|
const char* NewlineAfterThrow = "Illegal newline after throw";
|
|
const char* InvalidRegExp = "Invalid regular expression";
|
|
const char* UnterminatedRegExp = "Invalid regular expression: missing /";
|
|
const char* InvalidLHSInAssignment = "Invalid left-hand side in assignment";
|
|
const char* InvalidLHSInForIn = "Invalid left-hand side in for-in";
|
|
const char* InvalidLHSInForLoop = "Invalid left-hand side in for-loop";
|
|
const char* MultipleDefaultsInSwitch = "More than one default clause in switch statement";
|
|
const char* NoCatchOrFinally = "Missing catch or finally after try";
|
|
const char* UnknownLabel = "Undefined label \'%s\'";
|
|
const char* Redeclaration = "%s \'%s\' has already been declared";
|
|
const char* IllegalContinue = "Illegal continue statement";
|
|
const char* IllegalBreak = "Illegal break statement";
|
|
const char* IllegalReturn = "Illegal return statement";
|
|
const char* StrictModeWith = "Strict mode code may not include a with statement";
|
|
const char* StrictCatchVariable = "Catch variable may not be eval or arguments in strict mode";
|
|
const char* StrictVarName = "Variable name may not be eval or arguments in strict mode";
|
|
const char* StrictParamName = "Parameter name eval or arguments is not allowed in strict mode";
|
|
const char* StrictParamDupe = "Strict mode function may not have duplicate parameter names";
|
|
const char* StrictFunctionName = "Function name may not be eval or arguments in strict mode";
|
|
const char* StrictOctalLiteral = "Octal literals are not allowed in strict mode.";
|
|
const char* StrictDelete = "Delete of an unqualified identifier in strict mode.";
|
|
const char* StrictLHSAssignment = "Assignment to eval or arguments is not allowed in strict mode";
|
|
const char* StrictLHSPostfix = "Postfix increment/decrement may not have eval or arguments operand in strict mode";
|
|
const char* StrictLHSPrefix = "Prefix increment/decrement may not have eval or arguments operand in strict mode";
|
|
const char* StrictReservedWord = "Use of future reserved word in strict mode";
|
|
const char* TemplateOctalLiteral = "Octal literals are not allowed in template strings.";
|
|
const char* ParameterAfterRestParameter = "Rest parameter must be last formal parameter";
|
|
const char* DefaultRestParameter = "Unexpected token =";
|
|
const char* ObjectPatternAsRestParameter = "Unexpected token {";
|
|
const char* DuplicateProtoProperty = "Duplicate __proto__ fields are not allowed in object literals";
|
|
const char* ConstructorSpecialMethod = "Class constructor may not be an accessor";
|
|
const char* DuplicateConstructor = "A class may only have one constructor";
|
|
const char* StaticPrototype = "Classes may not have static property named prototype";
|
|
const char* MissingFromClause = "Unexpected token";
|
|
const char* NoAsAfterImportNamespace = "Unexpected token";
|
|
const char* InvalidModuleSpecifier = "Unexpected token";
|
|
const char* IllegalImportDeclaration = "Unexpected token";
|
|
const char* IllegalExportDeclaration = "Unexpected token";
|
|
const char* DuplicateBinding = "Duplicate binding %s";
|
|
const char* ForInOfLoopInitializer = "%s loop variable declaration may not have an initializer";
|
|
}
|
|
|
|
/*
|
|
export interface Comment {
|
|
multiLine: boolean;
|
|
slice: number[];
|
|
range: number[];
|
|
loc: any;
|
|
}
|
|
*/
|
|
|
|
struct ParserError : public gc {
|
|
String* description;
|
|
size_t index;
|
|
size_t line;
|
|
size_t col;
|
|
|
|
ParserError(size_t index, size_t line, size_t col, String* description)
|
|
{
|
|
this->index = index;
|
|
this->line = line;
|
|
this->col = col;
|
|
this->description = description;
|
|
}
|
|
|
|
ParserError(size_t index, size_t line, size_t col, const char* description)
|
|
: ParserError(index, line, col, new ASCIIString(description))
|
|
{
|
|
}
|
|
};
|
|
|
|
struct ScanTemplteResult {
|
|
UTF16StringData valueCooked;
|
|
StringView raw;
|
|
size_t head;
|
|
size_t tail;
|
|
};
|
|
|
|
struct ScanRegExpResult {
|
|
String* body;
|
|
String* flags;
|
|
};
|
|
|
|
struct ScannerResult : public gc {
|
|
Token type;
|
|
bool octal;
|
|
bool head;
|
|
|
|
int prec;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
size_t start;
|
|
size_t end;
|
|
size_t index;
|
|
|
|
StringView valueString;
|
|
KeywordKind valueKeywordKind;
|
|
union {
|
|
PunctuatorsKind valuePunctuatorsKind;
|
|
double valueNumber;
|
|
ScanTemplteResult valueTemplate;
|
|
ScanRegExpResult valueRegexp;
|
|
};
|
|
|
|
ScannerResult(Token type, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->type = type;
|
|
this->octal = false;
|
|
this->head = false;
|
|
this->prec = -1;
|
|
this->valueKeywordKind = NotKeyword;
|
|
this->valueString = valueString;
|
|
this->lineNumber = lineNumber;
|
|
this->valueNumber = 0;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = this->index = end;
|
|
}
|
|
|
|
ScannerResult(Token type, StringView valueString, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->type = type;
|
|
this->octal = false;
|
|
this->head = false;
|
|
this->prec = -1;
|
|
this->valueKeywordKind = NotKeyword;
|
|
this->valueString = valueString;
|
|
this->lineNumber = lineNumber;
|
|
this->valueNumber = 0;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = this->index = end;
|
|
}
|
|
|
|
ScannerResult(Token type, double value, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->type = type;
|
|
this->octal = false;
|
|
this->head = false;
|
|
this->valueKeywordKind = NotKeyword;
|
|
this->valueNumber = value;
|
|
this->lineNumber = lineNumber;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = this->index = end;
|
|
if (end != SIZE_MAX) {
|
|
this->end = end;
|
|
}
|
|
}
|
|
|
|
ScannerResult(Token type, ScanTemplteResult value, size_t lineNumber, size_t lineStart, size_t start, size_t end)
|
|
{
|
|
this->type = type;
|
|
this->octal = false;
|
|
this->head = false;
|
|
this->prec = -1;
|
|
this->valueKeywordKind = NotKeyword;
|
|
this->valueNumber = 0;
|
|
this->valueTemplate = value;
|
|
this->lineNumber = lineNumber;
|
|
this->lineStart = lineStart;
|
|
this->start = start;
|
|
this->end = this->index = end;
|
|
if (end != SIZE_MAX) {
|
|
this->end = end;
|
|
}
|
|
}
|
|
};
|
|
|
|
class ErrorHandler : public gc {
|
|
public:
|
|
// errors: Error[];
|
|
// tolerant: boolean;
|
|
|
|
ErrorHandler() {
|
|
// this->errors = [];
|
|
// this->tolerant = false;
|
|
}
|
|
|
|
// recordError(error: Error): void {
|
|
// this->errors.push(error);
|
|
// };
|
|
|
|
void tolerate(Error* error)
|
|
{
|
|
/*
|
|
if (this->tolerant) {
|
|
this->recordError(error);
|
|
} else {
|
|
throw error;
|
|
}*/
|
|
throw error;
|
|
}
|
|
|
|
Error* constructError(String* msg, size_t column)
|
|
{
|
|
Error* error = new Error(msg);
|
|
error->column = column;
|
|
return error;
|
|
// try {
|
|
// throw error;
|
|
// } catch (base) {
|
|
/* istanbul ignore else */
|
|
// if (Object.create && Object.defineProperty) {
|
|
// error = Object.create(base);
|
|
// Object.defineProperty(error, 'column', { value: column });
|
|
// }
|
|
// } finally {
|
|
// return error;
|
|
// }
|
|
}
|
|
|
|
Error* createError(size_t index, size_t line, size_t col, String* description)
|
|
{
|
|
UTF16StringDataNonGCStd msg = u"Line ";
|
|
std::string lineString = std::to_string(line);
|
|
msg += UTF16StringDataNonGCStd(lineString.begin(), lineString.end());
|
|
msg += u": ";
|
|
if (description->length()) {
|
|
msg += UTF16StringDataNonGCStd(description->toUTF16StringData().data());
|
|
}
|
|
Error* error = constructError(new UTF16String(msg.data(), msg.length()), col);
|
|
error->index = index;
|
|
error->lineNumber = line;
|
|
error->description = description;
|
|
return error;
|
|
};
|
|
|
|
void throwError(size_t index, size_t line, size_t col, String* description)
|
|
{
|
|
throw this->createError(index, line, col, description);
|
|
}
|
|
|
|
void tolerateError(size_t index, size_t line, size_t col, String* description)
|
|
{
|
|
Error* error = this->createError(index, line, col, description);
|
|
/*
|
|
if (this->tolerant) {
|
|
this->recordError(error);
|
|
} else {
|
|
throw error;
|
|
}*/
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
|
|
class Scanner {
|
|
public:
|
|
StringView source;
|
|
ErrorHandler* errorHandler;
|
|
// trackComment: boolean;
|
|
|
|
size_t length;
|
|
size_t index;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
std::vector<Curly> curlyStack;
|
|
|
|
Scanner(StringView code, ErrorHandler* handler, size_t startLine = 0, size_t startColumn = 0)
|
|
{
|
|
source = code;
|
|
errorHandler = handler;
|
|
// trackComment = false;
|
|
|
|
length = code.end();
|
|
index = 0;
|
|
lineNumber = ((length > 0) ? 1 : 0) + startLine;
|
|
lineStart = startColumn;
|
|
}
|
|
|
|
bool eof()
|
|
{
|
|
return index >= length;
|
|
}
|
|
|
|
void throwUnexpectedToken(const char *message = Messages::UnexpectedTokenIllegal)
|
|
{
|
|
this->errorHandler->throwError(this->index, this->lineNumber, this->index - this->lineStart + 1, new ASCIIString(message));
|
|
}
|
|
/*
|
|
tolerateUnexpectedToken() {
|
|
this->errorHandler.tolerateError(this->index, this->lineNumber,
|
|
this->index - this->lineStart + 1, Messages.UnexpectedTokenIllegal);
|
|
};
|
|
*/
|
|
void tolerateUnexpectedToken()
|
|
{
|
|
throwUnexpectedToken();
|
|
}
|
|
// ECMA-262 11.4 Comments
|
|
|
|
// skipSingleLineComment(offset: number): Comment[] {
|
|
void skipSingleLineComment(size_t /*offset*/)
|
|
{
|
|
// let comments: Comment[];
|
|
// size_t start, loc;
|
|
|
|
/*
|
|
if (this->trackComment) {
|
|
comments = [];
|
|
start = this->index - offset;
|
|
loc = {
|
|
start: {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - offset
|
|
},
|
|
end: {}
|
|
};
|
|
}*/
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
++this->index;
|
|
if (isLineTerminator(ch)) {
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - 1
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: false,
|
|
slice: [start + offset, this->index - 1],
|
|
range: [start, this->index - 1],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
if (ch == 13 && this->source.charAt(this->index) == 10) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
this->lineStart = this->index;
|
|
// return comments;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: false,
|
|
slice: [start + offset, this->index],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
|
|
// return comments;
|
|
return;
|
|
}
|
|
|
|
// skipMultiLineComment(): Comment[] {
|
|
void skipMultiLineComment()
|
|
{
|
|
// let comments: Comment[];
|
|
// size_t start, loc;
|
|
/*
|
|
if (this->trackComment) {
|
|
comments = [];
|
|
start = this->index - 2;
|
|
loc = {
|
|
start: {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart - 2
|
|
},
|
|
end: {}
|
|
};
|
|
}
|
|
*/
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
if (isLineTerminator(ch)) {
|
|
if (ch == 0x0D && this->source.charAt(this->index + 1) == 0x0A) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
++this->index;
|
|
this->lineStart = this->index;
|
|
} else if (ch == 0x2A) {
|
|
// Block comment ends with '*/'.
|
|
if (this->source.charAt(this->index + 1) == 0x2F) {
|
|
this->index += 2;
|
|
/*
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: true,
|
|
slice: [start + 2, this->index - 2],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}
|
|
return comments;
|
|
*/
|
|
return;
|
|
}
|
|
++this->index;
|
|
} else {
|
|
++this->index;
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Ran off the end of the file - the whole thing is a comment
|
|
if (this->trackComment) {
|
|
loc.end = {
|
|
line: this->lineNumber,
|
|
column: this->index - this->lineStart
|
|
};
|
|
const entry: Comment = {
|
|
multiLine: true,
|
|
slice: [start + 2, this->index],
|
|
range: [start, this->index],
|
|
loc: loc
|
|
};
|
|
comments.push(entry);
|
|
}*/
|
|
|
|
tolerateUnexpectedToken();
|
|
// return comments;
|
|
return;
|
|
}
|
|
|
|
void scanComments()
|
|
{
|
|
/*
|
|
let comments;
|
|
if (this->trackComment) {
|
|
comments = [];
|
|
}*/
|
|
|
|
bool start = (this->index == 0);
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
|
|
if (isWhiteSpace(ch)) {
|
|
++this->index;
|
|
} else if (isLineTerminator(ch)) {
|
|
++this->index;
|
|
if (ch == 0x0D && this->source.charAt(this->index) == 0x0A) {
|
|
++this->index;
|
|
}
|
|
++this->lineNumber;
|
|
this->lineStart = this->index;
|
|
start = true;
|
|
} else if (ch == 0x2F) { // U+002F is '/'
|
|
ch = this->source.charAt(this->index + 1);
|
|
if (ch == 0x2F) {
|
|
this->index += 2;
|
|
/*
|
|
const comment = this->skipSingleLineComment(2);
|
|
if (this->trackComment) {
|
|
comments = comments.concat(comment);
|
|
}
|
|
*/
|
|
this->skipSingleLineComment(2);
|
|
start = true;
|
|
} else if (ch == 0x2A) { // U+002A is '*'
|
|
this->index += 2;
|
|
/*
|
|
const comment = this->skipMultiLineComment();
|
|
if (this->trackComment) {
|
|
comments = comments.concat(comment);
|
|
}*/
|
|
this->skipMultiLineComment();
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (start && ch == 0x2D) { // U+002D is '-'
|
|
// U+003E is '>'
|
|
if ((this->source.charAt(this->index + 1) == 0x2D) && (this->source.charAt(this->index + 2) == 0x3E)) {
|
|
// '-->' is a single-line comment
|
|
this->index += 3;
|
|
/*
|
|
const comment = this->skipSingleLineComment(3);
|
|
if (this->trackComment) {
|
|
comments = comments.concat(comment);
|
|
}*/
|
|
this->skipSingleLineComment(3);
|
|
} else {
|
|
break;
|
|
}
|
|
} else if (ch == 0x3C) { // U+003C is '<'
|
|
// if (this->source.slice(this->index + 1, this->index + 4) == '!--') {
|
|
if (this->source.length() > this->index + 4) {
|
|
StringView sv(this->source, this->index + 1, this->index + 4);
|
|
if (sv == "!--") {
|
|
this->index += 4; // `<!--`
|
|
/*
|
|
const comment = this->skipSingleLineComment(4);
|
|
if (this->trackComment) {
|
|
comments = comments.concat(comment);
|
|
}*/
|
|
this->skipSingleLineComment(4);
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// return comments;
|
|
return;
|
|
}
|
|
|
|
// ECMA-262 11.6.2.2 Future Reserved Words
|
|
|
|
bool isFutureReservedWord(const StringView& id)
|
|
{
|
|
if (id.length() == 4 || id.length() == 5) {
|
|
if (id == "enum" || id == "export" || id == "import" || id == "super") {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool isStrictModeReservedWord(const T& id)
|
|
{
|
|
if (id == "let") {
|
|
return true;
|
|
} else if (id == "yield") {
|
|
return true;
|
|
} else if (id == "static") {
|
|
return true;
|
|
} else if (id == "public") {
|
|
return true;
|
|
} else if (id == "protected") {
|
|
return true;
|
|
} else if (id == "private") {
|
|
return true;
|
|
} else if (id == "package") {
|
|
return true;
|
|
} else if (id == "implements") {
|
|
return true;
|
|
} else if (id == "interface") {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
bool isRestrictedWord(const T& id)
|
|
{
|
|
return id == "eval" || id == "arguments";
|
|
}
|
|
|
|
// ECMA-262 11.6.2.1 Keywords
|
|
ALWAYS_INLINE KeywordKind isKeyword(const StringView& id)
|
|
{
|
|
// 'const' is specialized as Keyword in V8.
|
|
// 'yield' and 'let' are for compatibility with SpiderMonkey and ES.next.
|
|
// Some others are from future reserved words.
|
|
|
|
char16_t first = id[0];
|
|
|
|
switch (id.length()) {
|
|
case 2:
|
|
if (first == 'i') {
|
|
if (id == "if") {
|
|
return If;
|
|
} else if (id == "in") {
|
|
return In;
|
|
}
|
|
|
|
} else if (first == 'd' && id == "do") {
|
|
return Do;
|
|
} else if (first == 'o' && id == "of") {
|
|
return Of;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (first == 'v' && id == "var") {
|
|
return Var;
|
|
} else if (first == 'f' && id == "for") {
|
|
return For;
|
|
} else if (first == 'n' && id == "new") {
|
|
return New;
|
|
} else if (first == 't' && id == "try") {
|
|
return Try;
|
|
/*
|
|
} else if (first == 'l' && id == "let") {
|
|
return Let;
|
|
*/
|
|
}
|
|
|
|
break;
|
|
case 4:
|
|
if (first == 't' && id == "this") {
|
|
return This;
|
|
} else if (first == 'e' && id == "else") {
|
|
return Else;
|
|
} else if (first == 'c' && id == "case") {
|
|
return Case;
|
|
} else if (first == 'v' && id == "void") {
|
|
return Void;
|
|
} else if (first == 'w' && id == "with") {
|
|
return With;
|
|
} else if (first == 'e' && id == "enum") {
|
|
return Enum;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (first == 'w' && id == "while") {
|
|
return While;
|
|
} else if (first == 'b' && id == "break") {
|
|
return Break;
|
|
} else if (first == 'c') {
|
|
if (id == "catch") {
|
|
return Catch;
|
|
} else if (id == "const") {
|
|
return Const;
|
|
} else if (id == "class") {
|
|
return Class;
|
|
}
|
|
} else if (first == 't' && id == "throw") {
|
|
return Throw;
|
|
/*
|
|
} else if (first == 'y' && id == "yield") {
|
|
return Yield;
|
|
*/
|
|
} else if (first == 's' && id == "super") {
|
|
return Super;
|
|
/*
|
|
} else if (first == 'a' && id == "await") {
|
|
return Await;
|
|
*/
|
|
}
|
|
break;
|
|
case 6:
|
|
if (first == 'r' && id == "return") {
|
|
return Return;
|
|
} else if (first == 't' && id == "typeof") {
|
|
return Typeof;
|
|
} else if (first == 'd' && id == "delete") {
|
|
return Delete;
|
|
} else if (first == 's' && id == "switch") {
|
|
return Switch;
|
|
} else if (first == 'e' && id == "export") {
|
|
return Export;
|
|
} else if (first == 'i' && id == "import") {
|
|
return Import;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (first == 'd' && id == "default") {
|
|
return Default;
|
|
} else if (first == 'f' && id == "finally") {
|
|
return Finally;
|
|
} else if (first == 'e' && id == "extends") {
|
|
return Extends;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (first == 'f' && id == "function") {
|
|
return Function;
|
|
} else if (first == 'c' && id == "continue") {
|
|
return Continue;
|
|
} else if (first == 'd' && id == "debugger") {
|
|
return Debugger;
|
|
}
|
|
break;
|
|
case 10:
|
|
if (first == 'i' && id == "instanceof") {
|
|
return InstanceofKeyword;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return NotKeyword;
|
|
}
|
|
|
|
char32_t codePointAt(size_t i)
|
|
{
|
|
char32_t cp, first, second;
|
|
cp = this->source.charAt(i);
|
|
if (cp >= 0xD800 && cp <= 0xDBFF) {
|
|
second = this->source.charAt(i + 1);
|
|
if (second >= 0xDC00 && second <= 0xDFFF) {
|
|
first = cp;
|
|
cp = (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
|
}
|
|
}
|
|
|
|
return cp;
|
|
}
|
|
|
|
int hexValue(char16_t ch)
|
|
{
|
|
int c = 0;
|
|
if (ch >= '0' && ch <= '9') {
|
|
c = ch - '0';
|
|
} else if (ch >= 'a' && ch <= 'f') {
|
|
c = ch - 'a' + 10;
|
|
} else if (ch >= 'A' && ch <= 'F') {
|
|
c = ch - 'A' + 10;
|
|
} else {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
return c;
|
|
}
|
|
|
|
char32_t scanHexEscape(char prefix)
|
|
{
|
|
size_t len = (prefix == 'u') ? 4 : 2;
|
|
char32_t code = 0;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if (!this->eof() && isHexDigit(this->source.charAt(this->index))) {
|
|
code = code * 16 + hexValue(this->source.charAt(this->index++));
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
char32_t scanUnicodeCodePointEscape()
|
|
{
|
|
char16_t ch = this->source.charAt(this->index);
|
|
char32_t code = 0;
|
|
|
|
// At least, one hex digit is required.
|
|
if (ch == '}') {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
while (!this->eof()) {
|
|
ch = this->source.charAt(this->index++);
|
|
if (!isHexDigit(ch)) {
|
|
break;
|
|
}
|
|
code = code * 16 + hexValue(ch);
|
|
}
|
|
|
|
if (code > 0x10FFFF || ch != '}') {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
return code;
|
|
};
|
|
|
|
StringView getIdentifier()
|
|
{
|
|
const size_t start = this->index++;
|
|
while (!this->eof()) {
|
|
const char16_t ch = this->source.charAt(this->index);
|
|
if (ch == 0x5C) {
|
|
// Blackslash (U+005C) marks Unicode escape sequence.
|
|
this->index = start;
|
|
return this->getComplexIdentifier();
|
|
} else if (ch >= 0xD800 && ch < 0xDFFF) {
|
|
// Need to handle surrogate pairs.
|
|
this->index = start;
|
|
return this->getComplexIdentifier();
|
|
}
|
|
if (isIdentifierPart(ch)) {
|
|
++this->index;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return StringView(this->source, start, this->index);
|
|
}
|
|
|
|
StringView getComplexIdentifier()
|
|
{
|
|
char32_t cp = this->codePointAt(this->index);
|
|
ParserCharPiece piece = ParserCharPiece(cp);
|
|
UTF16StringDataNonGCStd id(piece.data);
|
|
this->index += id.length();
|
|
|
|
// '\u' (U+005C, U+0075) denotes an escaped character.
|
|
char32_t ch;
|
|
if (cp == 0x5C) {
|
|
if (this->source.charAt(this->index) != 0x75) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
++this->index;
|
|
if (this->source.charAt(this->index) == '{') {
|
|
++this->index;
|
|
ch = this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
ch = this->scanHexEscape('u');
|
|
cp = ch;
|
|
if (!ch || ch == '\\' || !isIdentifierStart(cp)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
id = ch;
|
|
}
|
|
|
|
while (!this->eof()) {
|
|
cp = this->codePointAt(this->index);
|
|
if (!isIdentifierPart(cp)) {
|
|
break;
|
|
}
|
|
// ch = Character.fromCodePoint(cp);
|
|
ch = cp;
|
|
piece = ParserCharPiece(ch);
|
|
id += piece.data;
|
|
this->index += piece.length;
|
|
|
|
// '\u' (U+005C, U+0075) denotes an escaped character.
|
|
if (cp == 0x5C) {
|
|
// id = id.substr(0, id.length - 1);
|
|
id.pop_back();
|
|
|
|
if (this->source.charAt(this->index) != 0x75) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
++this->index;
|
|
if (this->source.charAt(this->index) == '{') {
|
|
++this->index;
|
|
ch = this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
ch = this->scanHexEscape('u');
|
|
cp = ch;
|
|
if (!ch || ch == '\\' || !isIdentifierPart(cp)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
piece = ParserCharPiece(ch);
|
|
id += piece.data;
|
|
}
|
|
}
|
|
|
|
String* str = new UTF16String(id.data(), id.length());
|
|
return StringView(str, 0, str->length());
|
|
}
|
|
|
|
struct OctalToDecimalResult {
|
|
char16_t code;
|
|
bool octal;
|
|
|
|
OctalToDecimalResult(char16_t code, bool octal)
|
|
{
|
|
this->code = code;
|
|
this->octal = octal;
|
|
}
|
|
};
|
|
OctalToDecimalResult octalToDecimal(char16_t ch)
|
|
{
|
|
// \0 is not octal escape sequence
|
|
bool octal = (ch != '0');
|
|
char16_t code = octalValue(ch);
|
|
|
|
if (!this->eof() && isOctalDigit(this->source.charAt(this->index))) {
|
|
octal = true;
|
|
code = code * 8 + octalValue(this->source.charAt(this->index++));
|
|
|
|
// 3 digits are only allowed when string starts
|
|
// with 0, 1, 2, 3
|
|
// if ('0123'.indexOf(ch) >= 0 && !this->eof() && Character.isOctalDigit(this->source.charCodeAt(this->index))) {
|
|
if ((ch >= '0' && ch <= '3') && !this->eof() && isOctalDigit(this->source.charAt(this->index))) {
|
|
code = code * 8 + octalValue(this->source.charAt(this->index++));
|
|
}
|
|
}
|
|
|
|
return OctalToDecimalResult(code, octal);
|
|
};
|
|
|
|
// ECMA-262 11.6 Names and Keywords
|
|
|
|
ScannerResult* scanIdentifier()
|
|
{
|
|
Token type;
|
|
const size_t start = this->index;
|
|
|
|
// Backslash (U+005C) starts an escaped character.
|
|
StringView id;
|
|
if (this->source.charAt(start) == 0x5C)
|
|
id = this->getComplexIdentifier();
|
|
else
|
|
id = this->getIdentifier();
|
|
|
|
// There is no keyword or literal with only one character.
|
|
// Thus, it must be an identifier.
|
|
KeywordKind keywordKind = NotKeyword;
|
|
if (id.length() == 1) {
|
|
type = Token::IdentifierToken;
|
|
} else if ((keywordKind = this->isKeyword(id))) {
|
|
type = Token::KeywordToken;
|
|
} else if (id == "null") {
|
|
type = Token::NullLiteralToken;
|
|
} else if (id == "true" || id == "false") {
|
|
type = Token::BooleanLiteralToken;
|
|
} else {
|
|
type = Token::IdentifierToken;
|
|
}
|
|
|
|
if (keywordKind) {
|
|
ScannerResult* r = new ScannerResult(type, id, this->lineNumber, this->lineStart, start, this->index);
|
|
r->valueKeywordKind = keywordKind;
|
|
return r;
|
|
} else {
|
|
return new ScannerResult(type, id, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
}
|
|
|
|
// ECMA-262 11.7 Punctuators
|
|
ScannerResult* scanPunctuator()
|
|
{
|
|
ScannerResult* token = new ScannerResult(Token::PunctuatorToken, StringView(), this->lineNumber, this->lineStart, this->index, this->index);
|
|
|
|
PunctuatorsKind kind;
|
|
// Check for most common single-character punctuators.
|
|
char16_t ch0 = this->source.charAt(this->index);
|
|
char ch1, ch2, ch3;
|
|
switch (ch0) {
|
|
case '(':
|
|
++this->index;
|
|
kind = LeftParenthesis;
|
|
break;
|
|
|
|
case '{':
|
|
this->curlyStack.push_back(Curly("{\0\0"));
|
|
++this->index;
|
|
kind = LeftBrace;
|
|
break;
|
|
|
|
case '.':
|
|
++this->index;
|
|
kind = Period;
|
|
if (this->source.charAt(this->index) == '.' && this->source.charAt(this->index + 1) == '.') {
|
|
// Spread operator: ...
|
|
this->index += 2;
|
|
// resultStr = "...";
|
|
kind = PeriodPeriodPeriod;
|
|
}
|
|
break;
|
|
|
|
case '}':
|
|
++this->index;
|
|
this->curlyStack.pop_back();
|
|
kind = RightBrace;
|
|
break;
|
|
case ')':
|
|
kind = RightParenthesis;
|
|
++this->index;
|
|
break;
|
|
case ';':
|
|
kind = SemiColon;
|
|
++this->index;
|
|
break;
|
|
case ',':
|
|
kind = Comma;
|
|
++this->index;
|
|
break;
|
|
case '[':
|
|
kind = LeftSquareBracket;
|
|
++this->index;
|
|
break;
|
|
case ']':
|
|
kind = RightSquareBracket;
|
|
++this->index;
|
|
break;
|
|
case ':':
|
|
kind = Colon;
|
|
++this->index;
|
|
break;
|
|
case '?':
|
|
kind = GuessMark;
|
|
++this->index;
|
|
break;
|
|
case '~':
|
|
kind = Wave;
|
|
++this->index;
|
|
break;
|
|
|
|
case '>':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '>') {
|
|
ch2 = this->source.charAt(this->index + 2);
|
|
if (ch2 == '>') {
|
|
ch3 = this->source.charAt(this->index + 3);
|
|
if (ch3 == '=') {
|
|
this->index += 4;
|
|
kind = UnsignedRightShiftEqual;
|
|
} else {
|
|
kind = UnsignedRightShift;
|
|
this->index += 3;
|
|
}
|
|
} else if (ch2 == '=') {
|
|
kind = RightShiftEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = RightShift;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '=') {
|
|
kind = RightInequalityEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = RightInequality;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '<':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '<') {
|
|
ch2 = this->source.charAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = LeftShiftEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = LeftShift;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '=') {
|
|
kind = LeftInequalityEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = LeftInequality;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '=':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
ch2 = this->source.charAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = StrictEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = Equal;
|
|
this->index += 2;
|
|
}
|
|
} else if (ch1 == '>') {
|
|
kind = Arrow;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Substitution;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '!':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
ch2 = this->source.charAt(this->index + 2);
|
|
if (ch2 == '=') {
|
|
kind = NotStrictEqual;
|
|
this->index += 3;
|
|
} else {
|
|
kind = NotEqual;
|
|
this->index += 2;
|
|
}
|
|
} else {
|
|
kind = ExclamationMark;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '&':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '&') {
|
|
kind = LogicalAnd;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = BitwiseAndEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseAnd;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '|':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '|') {
|
|
kind = LogicalOr;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = BitwiseOrEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseOr;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '^':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = BitwiseXorEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = BitwiseXor;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '+':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '+') {
|
|
kind = PlusPlus;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = PlusEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Plus;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '-':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '-') {
|
|
kind = MinusMinus;
|
|
this->index += 2;
|
|
} else if (ch1 == '=') {
|
|
kind = MinusEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Minus;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '*':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = MultiplyEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Multiply;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '/':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = DivideEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Divide;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
case '%':
|
|
ch1 = this->source.charAt(this->index + 1);
|
|
if (ch1 == '=') {
|
|
kind = ModEqual;
|
|
this->index += 2;
|
|
} else {
|
|
kind = Mod;
|
|
this->index += 1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (this->index == token->start) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
token->end = this->index;
|
|
token->valuePunctuatorsKind = kind;
|
|
return token;
|
|
}
|
|
|
|
// ECMA-262 11.8.3 Numeric Literals
|
|
|
|
ScannerResult* scanHexLiteral(size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
double numberDouble = 0.0;
|
|
bool shouldUseDouble = false;
|
|
bool scanned = false;
|
|
|
|
size_t shiftCount = 0;
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
if (!isHexDigit(ch)) {
|
|
break;
|
|
}
|
|
if (shouldUseDouble) {
|
|
numberDouble = numberDouble * 16 + toHexNumericValue(ch);
|
|
} else {
|
|
number = (number << 4) + toHexNumericValue(ch);
|
|
if (++shiftCount >= 16) {
|
|
shouldUseDouble = true;
|
|
numberDouble = number;
|
|
number = 0;
|
|
}
|
|
}
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!scanned) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.charAt(this->index))) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (shouldUseDouble) {
|
|
ASSERT(number == 0);
|
|
return new ScannerResult(Token::NumericLiteralToken, numberDouble, this->lineNumber, this->lineStart, start, this->index);
|
|
} else {
|
|
ASSERT(numberDouble == 0.0);
|
|
return new ScannerResult(Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
}
|
|
|
|
ScannerResult* scanBinaryLiteral(size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
bool scanned = false;
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
if (ch != '0' && ch != '1') {
|
|
break;
|
|
}
|
|
number = (number << 1) + ch - '0';
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!scanned) {
|
|
// only 0b or 0B
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
/* istanbul ignore else */
|
|
if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
|
|
return new ScannerResult(Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
|
|
ScannerResult* scanOctalLiteral(char16_t prefix, size_t start)
|
|
{
|
|
uint64_t number = 0;
|
|
bool scanned = false;
|
|
bool octal = isOctalDigit(prefix);
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
if (!isOctalDigit(ch)) {
|
|
break;
|
|
}
|
|
number = (number << 3) + ch - '0';
|
|
this->index++;
|
|
scanned = true;
|
|
}
|
|
|
|
if (!octal && !scanned) {
|
|
// only 0o or 0O
|
|
throwUnexpectedToken();
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.charAt(this->index)) || isDecimalDigit(this->source.charAt(this->index))) {
|
|
throwUnexpectedToken();
|
|
}
|
|
return new ScannerResult(Token::NumericLiteralToken, number, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
|
|
bool isImplicitOctalLiteral()
|
|
{
|
|
// Implicit octal, unless there is a non-octal digit.
|
|
// (Annex B.1.1 on Numeric Literals)
|
|
for (size_t i = this->index + 1; i < this->length; ++i) {
|
|
const char16_t ch = this->source.charAt(i);
|
|
if (ch == '8' || ch == '9') {
|
|
return false;
|
|
}
|
|
if (!isOctalDigit(ch)) {
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ScannerResult* scanNumericLiteral()
|
|
{
|
|
const size_t start = this->index;
|
|
char16_t ch = this->source.charAt(start);
|
|
ASSERT(isDecimalDigit(ch) || (ch == '.'));
|
|
// 'Numeric literal must start with a decimal digit or a decimal point');
|
|
|
|
std::string number;
|
|
number.reserve(32);
|
|
|
|
if (ch != '.') {
|
|
number = this->source.charAt(this->index++);
|
|
ch = this->source.charAt(this->index);
|
|
|
|
// Hex number starts with '0x'.
|
|
// Octal number starts with '0'.
|
|
// Octal number in ES6 starts with '0o'.
|
|
// Binary number in ES6 starts with '0b'.
|
|
if (number == "0") {
|
|
if (ch == 'x' || ch == 'X') {
|
|
++this->index;
|
|
return this->scanHexLiteral(start);
|
|
}
|
|
if (ch == 'b' || ch == 'B') {
|
|
++this->index;
|
|
return this->scanBinaryLiteral(start);
|
|
}
|
|
if (ch == 'o' || ch == 'O') {
|
|
return this->scanOctalLiteral(ch, start);
|
|
}
|
|
|
|
if (ch && isOctalDigit(ch)) {
|
|
if (this->isImplicitOctalLiteral()) {
|
|
return this->scanOctalLiteral(ch, start);
|
|
}
|
|
}
|
|
}
|
|
|
|
while (isDecimalDigit(this->source.charAt(this->index))) {
|
|
number += this->source.charAt(this->index++);
|
|
}
|
|
ch = this->source.charAt(this->index);
|
|
}
|
|
|
|
if (ch == '.') {
|
|
number += this->source.charAt(this->index++);
|
|
while (isDecimalDigit(this->source.charAt(this->index))) {
|
|
number += this->source.charAt(this->index++);
|
|
}
|
|
ch = this->source.charAt(this->index);
|
|
}
|
|
|
|
if (ch == 'e' || ch == 'E') {
|
|
number += this->source.charAt(this->index++);
|
|
|
|
ch = this->source.charAt(this->index);
|
|
if (ch == '+' || ch == '-') {
|
|
number += this->source.charAt(this->index++);
|
|
}
|
|
if (isDecimalDigit(this->source.charAt(this->index))) {
|
|
while (isDecimalDigit(this->source.charAt(this->index))) {
|
|
number += this->source.charAt(this->index++);
|
|
}
|
|
} else {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
}
|
|
|
|
if (isIdentifierStart(this->source.charAt(this->index))) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
int length = number.length();
|
|
int length_dummy;
|
|
double_conversion::StringToDoubleConverter converter(double_conversion::StringToDoubleConverter::ALLOW_HEX
|
|
| double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES
|
|
| double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES, 0.0, double_conversion::Double::NaN(),
|
|
"Infinity", "NaN");
|
|
double ll = converter.StringToDouble(number.data(), length, &length_dummy);
|
|
|
|
return new ScannerResult(Token::NumericLiteralToken, ll, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
|
|
// ECMA-262 11.8.4 String Literals
|
|
|
|
ScannerResult* scanStringLiteral()
|
|
{
|
|
// TODO apply rope-string
|
|
const size_t start = this->index;
|
|
char16_t quote = this->source.charAt(start);
|
|
ASSERT((quote == '\'' || quote == '"'));
|
|
// 'String literal must starts with a quote');
|
|
|
|
++this->index;
|
|
bool octal = false;
|
|
|
|
bool isPlainCase = true;
|
|
|
|
UTF16StringDataNonGCStd stringUTF16;
|
|
StringView str;
|
|
|
|
#define CONVERT_UNPLAIN_CASE_IF_NEEDED() \
|
|
if (isPlainCase) { \
|
|
auto temp = str.toUTF16StringData(); \
|
|
stringUTF16 = UTF16StringDataNonGCStd(temp.data(), temp.length()); \
|
|
isPlainCase = false; \
|
|
} \
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index++);
|
|
|
|
if (ch == quote) {
|
|
quote = '\0';
|
|
break;
|
|
} else if (ch == '\\') {
|
|
ch = this->source.charAt(this->index++);
|
|
if (!ch || !isLineTerminator(ch)) {
|
|
CONVERT_UNPLAIN_CASE_IF_NEEDED()
|
|
switch (ch) {
|
|
case 'u':
|
|
case 'x':
|
|
if (this->source.charAt(this->index) == '{') {
|
|
++this->index;
|
|
ParserCharPiece piece(this->scanUnicodeCodePointEscape());
|
|
stringUTF16 += piece.data;
|
|
} else {
|
|
const char32_t unescaped = this->scanHexEscape(ch);
|
|
if (!unescaped) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
ParserCharPiece piece(unescaped);
|
|
stringUTF16 += piece.data;
|
|
}
|
|
break;
|
|
case 'n':
|
|
stringUTF16 += '\n';
|
|
break;
|
|
case 'r':
|
|
stringUTF16 += '\r';
|
|
break;
|
|
case 't':
|
|
stringUTF16 += '\t';
|
|
break;
|
|
case 'b':
|
|
stringUTF16 += '\b';
|
|
break;
|
|
case 'f':
|
|
stringUTF16 += '\f';
|
|
break;
|
|
case 'v':
|
|
stringUTF16 += '\x0B';
|
|
break;
|
|
case '8':
|
|
case '9':
|
|
stringUTF16 += ch;
|
|
this->tolerateUnexpectedToken();
|
|
break;
|
|
|
|
default:
|
|
if (ch && isOctalDigit(ch)) {
|
|
OctalToDecimalResult octToDec = this->octalToDecimal(ch);
|
|
|
|
octal = octToDec.octal || octal;
|
|
stringUTF16 += octToDec.code;
|
|
} else {
|
|
stringUTF16 += ch;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.charAt(this->index) == '\n') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
}
|
|
} else if (isLineTerminator(ch)) {
|
|
break;
|
|
} else {
|
|
if (isPlainCase) {
|
|
str = StringView(this->source, start + 1, start + 1 + str.length() + 1);
|
|
} else {
|
|
stringUTF16 += ch;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (quote != '\0') {
|
|
this->index = start;
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
ScannerResult* ret;
|
|
if (isPlainCase) {
|
|
ret = new ScannerResult(Token::StringLiteralToken, str, /*octal, */this->lineNumber, this->lineStart, start, this->index);
|
|
} else {
|
|
String* newStr = new UTF16String(stringUTF16.data(), stringUTF16.length());
|
|
ret = new ScannerResult(Token::StringLiteralToken, StringView(newStr, 0, newStr->length()), /*octal, */this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
ret->octal = octal;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ECMA-262 11.8.6 Template Literal Lexical Components
|
|
|
|
ScannerResult* scanTemplate()
|
|
{
|
|
// TODO apply rope-string
|
|
UTF16StringDataNonGCStd cooked;
|
|
bool terminated = false;
|
|
size_t start = this->index;
|
|
|
|
bool head = (this->source.charAt(start) == '`');
|
|
bool tail = false;
|
|
size_t rawOffset = 2;
|
|
|
|
++this->index;
|
|
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index++);
|
|
if (ch == '`') {
|
|
rawOffset = 1;
|
|
tail = true;
|
|
terminated = true;
|
|
break;
|
|
} else if (ch == '$') {
|
|
if (this->source.charAt(this->index) == '{') {
|
|
this->curlyStack.push_back(Curly("${\0"));
|
|
++this->index;
|
|
terminated = true;
|
|
break;
|
|
}
|
|
cooked += ch;
|
|
} else if (ch == '\\') {
|
|
ch = this->source.charAt(this->index++);
|
|
if (!isLineTerminator(ch)) {
|
|
switch (ch) {
|
|
case 'n':
|
|
cooked += '\n';
|
|
break;
|
|
case 'r':
|
|
cooked += '\r';
|
|
break;
|
|
case 't':
|
|
cooked += '\t';
|
|
break;
|
|
case 'u':
|
|
case 'x':
|
|
if (this->source.charAt(this->index) == '{') {
|
|
++this->index;
|
|
cooked += this->scanUnicodeCodePointEscape();
|
|
} else {
|
|
const size_t restore = this->index;
|
|
const char32_t unescaped = this->scanHexEscape(ch);
|
|
if (unescaped) {
|
|
ParserCharPiece piece(unescaped);
|
|
cooked += piece.data;
|
|
} else {
|
|
this->index = restore;
|
|
cooked += ch;
|
|
}
|
|
}
|
|
break;
|
|
case 'b':
|
|
cooked += '\b';
|
|
break;
|
|
case 'f':
|
|
cooked += '\f';
|
|
break;
|
|
case 'v':
|
|
cooked += '\v';
|
|
break;
|
|
|
|
default:
|
|
if (ch == '0') {
|
|
if (isDecimalDigit(this->source.charAt(this->index))) {
|
|
// Illegal: \01 \02 and so on
|
|
this->throwUnexpectedToken(Messages::TemplateOctalLiteral);
|
|
}
|
|
cooked += (char16_t)'\0';
|
|
} else if (isOctalDigit(ch)) {
|
|
// Illegal: \1 \2
|
|
this->throwUnexpectedToken(Messages::TemplateOctalLiteral);
|
|
} else {
|
|
cooked += ch;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.charAt(this->index) == '\n') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
}
|
|
} else if (isLineTerminator(ch)) {
|
|
++this->lineNumber;
|
|
if (ch == '\r' && this->source.charAt(this->index) == '\n') {
|
|
++this->index;
|
|
}
|
|
this->lineStart = this->index;
|
|
cooked += '\n';
|
|
} else {
|
|
cooked += ch;
|
|
}
|
|
}
|
|
|
|
if (!terminated) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
if (!head) {
|
|
this->curlyStack.pop_back();
|
|
}
|
|
|
|
ScanTemplteResult result;
|
|
result.head = head;
|
|
result.tail = tail;
|
|
// TODO when parsing global code, we should generate new string not string view of code
|
|
result.raw = StringView(this->source, start + 1, this->index - rawOffset);
|
|
result.valueCooked = UTF16StringData(cooked.data(), cooked.length());
|
|
|
|
return new ScannerResult(Token::TemplateToken, result, this->lineNumber, this->lineStart, start, this->index);
|
|
}
|
|
|
|
// ECMA-262 11.8.5 Regular Expression Literals
|
|
/*
|
|
testRegExp(pattern: string, flags: string) {
|
|
// The BMP character to use as a replacement for astral symbols when
|
|
// translating an ES6 "u"-flagged pattern to an ES5-compatible
|
|
// approximation.
|
|
// Note: replacing with '\uFFFF' enables false positives in unlikely
|
|
// scenarios. For example, `[\u{1044f}-\u{10440}]` is an invalid
|
|
// pattern that would not be detected by this substitution.
|
|
const astralSubstitute = '\uFFFF';
|
|
let tmp = pattern;
|
|
let self = this;
|
|
|
|
if (flags.indexOf('u') >= 0) {
|
|
tmp = tmp
|
|
// Replace every Unicode escape sequence with the equivalent
|
|
// BMP character or a constant ASCII code point in the case of
|
|
// astral symbols. (See the above note on `astralSubstitute`
|
|
// for more information.)
|
|
.replace(/\\u\{([0-9a-fA-F]+)\}|\\u([a-fA-F0-9]{4})/g, function($0, $1, $2) {
|
|
const codePoint = parseInt($1 || $2, 16);
|
|
if (codePoint > 0x10FFFF) {
|
|
self.throwUnexpectedToken(Messages.InvalidRegExp);
|
|
}
|
|
if (codePoint <= 0xFFFF) {
|
|
return String.fromCharCode(codePoint);
|
|
}
|
|
return astralSubstitute;
|
|
})
|
|
// Replace each paired surrogate with a single ASCII symbol to
|
|
// avoid throwing on regular expressions that are only valid in
|
|
// combination with the "u" flag.
|
|
.replace(
|
|
/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
|
astralSubstitute
|
|
);
|
|
}
|
|
|
|
// First, detect invalid regular expressions.
|
|
try {
|
|
RegExp(tmp);
|
|
} catch (e) {
|
|
this->throwUnexpectedToken(Messages.InvalidRegExp);
|
|
}
|
|
|
|
// Return a regular expression object for this pattern-flag pair, or
|
|
// `null` in case the current environment doesn't support the flags it
|
|
// uses.
|
|
try {
|
|
return new RegExp(pattern, flags);
|
|
} catch (exception) {
|
|
return null;
|
|
}
|
|
};
|
|
*/
|
|
|
|
String* scanRegExpBody()
|
|
{
|
|
char16_t ch = this->source.charAt(this->index);
|
|
ASSERT(ch == '/');
|
|
// assert(ch == '/', 'Regular expression literal must start with a slash');
|
|
|
|
// TODO apply rope-string
|
|
char16_t ch0 = this->source.charAt(this->index++);
|
|
UTF16StringDataNonGCStd str(&ch0, 1);
|
|
bool classMarker = false;
|
|
bool terminated = false;
|
|
|
|
while (!this->eof()) {
|
|
ch = this->source.charAt(this->index++);
|
|
str += ch;
|
|
if (ch == '\\') {
|
|
ch = this->source.charAt(this->index++);
|
|
// ECMA-262 7.8.5
|
|
if (isLineTerminator(ch)) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
}
|
|
str += ch;
|
|
} else if (isLineTerminator(ch)) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
} else if (classMarker) {
|
|
if (ch == ']') {
|
|
classMarker = false;
|
|
}
|
|
} else {
|
|
if (ch == '/') {
|
|
terminated = true;
|
|
break;
|
|
} else if (ch == '[') {
|
|
classMarker = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!terminated) {
|
|
this->throwUnexpectedToken(Messages::UnterminatedRegExp);
|
|
}
|
|
|
|
// Exclude leading and trailing slash.
|
|
str = str.substr(1, str.length() - 2);
|
|
/*
|
|
return {
|
|
value: body,
|
|
literal: str
|
|
};*/
|
|
return new UTF16String(str.data(), str.length());
|
|
}
|
|
|
|
String* scanRegExpFlags()
|
|
{
|
|
// UTF16StringData str = '';
|
|
UTF16StringDataNonGCStd flags;
|
|
while (!this->eof()) {
|
|
char16_t ch = this->source.charAt(this->index);
|
|
if (!isIdentifierPart(ch)) {
|
|
break;
|
|
}
|
|
|
|
++this->index;
|
|
if (ch == '\\' && !this->eof()) {
|
|
ch = this->source.charAt(this->index);
|
|
if (ch == 'u') {
|
|
++this->index;
|
|
const size_t restore = this->index;
|
|
char32_t ch32;
|
|
ch32 = this->scanHexEscape('u');
|
|
if (ch32) {
|
|
ParserCharPiece piece(ch32);
|
|
flags += piece.data;
|
|
/*
|
|
for (str += '\\u'; restore < this->index; ++restore) {
|
|
str += this->source[restore];
|
|
}*/
|
|
} else {
|
|
this->index = restore;
|
|
flags += 'u';
|
|
// str += '\\u';
|
|
}
|
|
this->tolerateUnexpectedToken();
|
|
} else {
|
|
// str += '\\';
|
|
this->tolerateUnexpectedToken();
|
|
}
|
|
} else {
|
|
flags += ch;
|
|
// str += ch;
|
|
}
|
|
}
|
|
|
|
/*
|
|
return {
|
|
value: flags,
|
|
literal: str
|
|
};
|
|
*/
|
|
return new UTF16String(flags.data(), flags.length());
|
|
}
|
|
|
|
ScannerResult* scanRegExp()
|
|
{
|
|
const size_t start = this->index;
|
|
|
|
String* body = this->scanRegExpBody();
|
|
String* flags = this->scanRegExpFlags();
|
|
// const value = this->testRegExp(body.value, flags.value);
|
|
|
|
ScanRegExpResult result;
|
|
result.body = body;
|
|
result.flags = flags;
|
|
ScannerResult* res = new ScannerResult(Token::RegularExpressionToken, StringView(), this->lineNumber, this->lineStart, start, this->index);
|
|
res->valueRegexp = result;
|
|
return res;
|
|
};
|
|
|
|
ScannerResult* lex()
|
|
{
|
|
if (this->eof()) {
|
|
return new ScannerResult(Token::EOFToken, this->lineNumber, this->lineStart, this->index, this->index);
|
|
}
|
|
|
|
const char16_t cp = this->source.charAt(this->index);
|
|
|
|
if (isIdentifierStart(cp)) {
|
|
return this->scanIdentifier();
|
|
}
|
|
|
|
// Very common: ( and ) and ;
|
|
if (cp == 0x28 || cp == 0x29 || cp == 0x3B) {
|
|
return this->scanPunctuator();
|
|
}
|
|
|
|
// String literal starts with single quote (U+0027) or double quote (U+0022).
|
|
if (cp == 0x27 || cp == 0x22) {
|
|
return this->scanStringLiteral();
|
|
}
|
|
|
|
// Dot (.) U+002E can also start a floating-point number, hence the need
|
|
// to check the next character.
|
|
if (cp == 0x2E) {
|
|
if (isDecimalDigit(this->source.charAt(this->index + 1))) {
|
|
return this->scanNumericLiteral();
|
|
}
|
|
return this->scanPunctuator();
|
|
}
|
|
|
|
if (isDecimalDigit(cp)) {
|
|
return this->scanNumericLiteral();
|
|
}
|
|
|
|
// Template literals start with ` (U+0060) for template head
|
|
// or } (U+007D) for template middle or template tail.
|
|
if (cp == 0x60 || (cp == 0x7D && (strcmp(this->curlyStack.back().m_curly, "${") == 0))) {
|
|
return this->scanTemplate();
|
|
}
|
|
|
|
// Possible identifier start in a surrogate pair.
|
|
if (cp >= 0xD800 && cp < 0xDFFF) {
|
|
if (isIdentifierStart(this->codePointAt(this->index))) {
|
|
return this->scanIdentifier();
|
|
}
|
|
}
|
|
return this->scanPunctuator();
|
|
}
|
|
|
|
};
|
|
|
|
struct Config : public gc {
|
|
bool range;
|
|
bool loc;
|
|
// String* source;
|
|
bool tokens;
|
|
bool comment;
|
|
bool tolerant;
|
|
bool parseSingleFunction;
|
|
};
|
|
|
|
|
|
struct Context : public gc {
|
|
bool allowIn;
|
|
bool allowYield;
|
|
ScannerResult* firstCoverInitializedNameError;
|
|
bool isAssignmentTarget;
|
|
bool isBindingElement;
|
|
bool inFunctionBody;
|
|
bool inIteration;
|
|
bool inSwitch;
|
|
std::vector<std::pair<String*, bool>> labelSet;
|
|
bool strict;
|
|
};
|
|
|
|
struct Marker : public gc {
|
|
size_t index;
|
|
size_t lineNumber;
|
|
size_t lineStart;
|
|
};
|
|
|
|
struct MetaNode : public gc {
|
|
size_t index;
|
|
size_t line;
|
|
size_t column;
|
|
};
|
|
|
|
|
|
struct ArrowParameterPlaceHolderNode : public gc {
|
|
String* type;
|
|
ExpressionNodeVector params;
|
|
};
|
|
|
|
struct DeclarationOptions : public gc {
|
|
bool inFor;
|
|
};
|
|
|
|
class Parser : public gc {
|
|
public:
|
|
::Escargot::Context* escargotContext;
|
|
Config config;
|
|
ParserASTNodeHandler delegate;
|
|
ErrorHandler* errorHandler;
|
|
std::unique_ptr<Scanner> scanner;
|
|
std::unordered_map<IdentifierNode*, ScannerResult*,
|
|
std::hash<IdentifierNode*>, std::equal_to<IdentifierNode*>, gc_malloc_ignore_off_page_allocator<std::pair<IdentifierNode*, ScannerResult*>>> nodeExtraInfo;
|
|
|
|
enum SourceType {
|
|
Script, Module
|
|
};
|
|
SourceType sourceType;
|
|
ScannerResult* lookahead;
|
|
bool hasLineTerminator;
|
|
|
|
Context* context;
|
|
std::vector<ScannerResult*, gc_allocator_ignore_off_page<ScannerResult*>> tokens;
|
|
Marker startMarker;
|
|
Marker lastMarker;
|
|
|
|
Vector<ASTScopeContext*, gc_allocator_ignore_off_page<ASTScopeContext*>> scopeContexts;
|
|
bool trackUsingNames;
|
|
|
|
ASTScopeContext* popScopeContext(const MetaNode& node)
|
|
{
|
|
auto ret = scopeContexts.back();
|
|
ret->m_nodeStartIndex = node.index;
|
|
scopeContexts.pop_back();
|
|
return ret;
|
|
}
|
|
|
|
void extractNamesFromFunctionParams(const PatternNodeVector& vector)
|
|
{
|
|
for (size_t i = 0 ; i < vector.size(); i ++) {
|
|
ASSERT(vector[i]->isIdentifier());
|
|
IdentifierNode* id = (IdentifierNode*)vector[i];
|
|
scopeContexts.back()->insertName(id->name());
|
|
}
|
|
}
|
|
|
|
void pushScopeContext(const PatternNodeVector& params, AtomicString functionName)
|
|
{
|
|
auto parentContext = scopeContexts.back();
|
|
scopeContexts.push_back(new ASTScopeContext(this->context->strict, scopeContexts.back()));
|
|
scopeContexts.back()->m_functionName = functionName;
|
|
for (size_t i = 0 ; i < params.size(); i ++) {
|
|
ASSERT(params[i]->isIdentifier());
|
|
IdentifierNode* id = (IdentifierNode*)params[i];
|
|
scopeContexts.back()->m_parameters.pushBack(id->name());
|
|
}
|
|
if (parentContext) {
|
|
parentContext->m_childScopes.push_back(scopeContexts.back());
|
|
}
|
|
}
|
|
|
|
Parser(::Escargot::Context* escargotContext, StringView code, ParserASTNodeHandler delegate, size_t startLine = 0, size_t startColumn = 0/*, options: any = {}, delegate*/)
|
|
{
|
|
this->escargotContext = escargotContext;
|
|
trackUsingNames = true;
|
|
config.range = false;
|
|
config.loc = false;
|
|
// config.source = String::emptyString;
|
|
config.tokens = false;
|
|
config.comment = false;
|
|
config.tolerant = false;
|
|
config.parseSingleFunction = false;
|
|
/*
|
|
this->config = {
|
|
range: (typeof options.range == 'boolean') && options.range,
|
|
loc: (typeof options.loc == 'boolean') && options.loc,
|
|
source: null,
|
|
tokens: (typeof options.tokens == 'boolean') && options.tokens,
|
|
comment: (typeof options.comment == 'boolean') && options.comment,
|
|
tolerant: (typeof options.tolerant == 'boolean') && options.tolerant
|
|
};
|
|
if (this->config.loc && options.source && options.source !== null) {
|
|
this->config.source = String(options.source);
|
|
}*/
|
|
|
|
this->delegate = delegate;
|
|
|
|
this->errorHandler = new ErrorHandler();
|
|
|
|
this->scanner = std::unique_ptr<Scanner>(new Scanner(code, this->errorHandler, startLine, startColumn));
|
|
|
|
// this->sourceType = (options && options.sourceType == 'module') ? 'module' : 'script';
|
|
this->sourceType = Script;
|
|
this->lookahead = nullptr;
|
|
this->hasLineTerminator = false;
|
|
|
|
this->context = new Context();
|
|
this->context->allowIn = true;
|
|
this->context->allowYield = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
this->context->inFunctionBody = true;
|
|
this->context->inIteration = true;
|
|
this->context->inSwitch = true;
|
|
this->context->strict = this->sourceType == Module;
|
|
|
|
this->startMarker.index = 0;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = 0;
|
|
|
|
this->lastMarker.index = 0;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = 0;
|
|
|
|
this->nextToken();
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
}
|
|
|
|
void throwError(const char* messageFormat, String* arg0 = String::emptyString, String* arg1 = String::emptyString)
|
|
{
|
|
UTF16StringDataNonGCStd msg;
|
|
if (arg0->length() && arg1->length()) {
|
|
char message[512];
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
UTF8StringData d2 = arg1->toUTF8StringData();
|
|
snprintf(message, 512, messageFormat, d1.data(), d2.data());
|
|
auto temp = utf8StringToUTF16String(message, strlen(message));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
} else if (arg0->length()) {
|
|
char message[512];
|
|
UTF8StringData d1 = arg0->toUTF8StringData();
|
|
snprintf(message, 512, messageFormat, d1.data());
|
|
auto temp = utf8StringToUTF16String(message, strlen(message));
|
|
msg = UTF16StringDataNonGCStd(temp.data(), temp.length());
|
|
} else {
|
|
msg.assign(messageFormat, &messageFormat[strlen(messageFormat)]);
|
|
}
|
|
|
|
size_t index = this->lastMarker.index;
|
|
size_t line = this->lastMarker.lineNumber;
|
|
size_t column = this->lastMarker.index - this->lastMarker.lineStart + 1;
|
|
throw this->errorHandler->createError(index, line, column, new UTF16String(msg.data(), msg.length()));
|
|
}
|
|
|
|
void tolerateError(const char* messageFormat, String* arg0 = String::emptyString, String* arg1 = String::emptyString)
|
|
{
|
|
throwError(messageFormat, arg0, arg1);
|
|
}
|
|
|
|
void replaceAll(UTF16StringDataNonGCStd& str, const UTF16StringDataNonGCStd& from, const UTF16StringDataNonGCStd& to)
|
|
{
|
|
if(from.empty())
|
|
return;
|
|
size_t start_pos = 0;
|
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length();
|
|
}
|
|
}
|
|
|
|
// Throw an exception because of the token.
|
|
Error* unexpectedTokenError(ScannerResult* token = nullptr, const char* message = nullptr)
|
|
{
|
|
const char* msg;
|
|
if (message)
|
|
msg = message;
|
|
else
|
|
msg = Messages::UnexpectedToken;
|
|
|
|
String* value;
|
|
if (token) {
|
|
if (!msg) {
|
|
msg = (token->type == Token::EOFToken) ? Messages::UnexpectedEOS :
|
|
(token->type == Token::IdentifierToken) ? Messages::UnexpectedIdentifier :
|
|
(token->type == Token::NumericLiteralToken) ? Messages::UnexpectedNumber :
|
|
(token->type == Token::StringLiteralToken) ? Messages::UnexpectedString :
|
|
(token->type == Token::TemplateToken) ? Messages::UnexpectedTemplate :
|
|
Messages::UnexpectedToken;
|
|
|
|
if (token->type == Token::KeywordToken) {
|
|
if (this->scanner->isFutureReservedWord(token->valueString)) {
|
|
msg = Messages::UnexpectedReserved;
|
|
} else if (this->context->strict && this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
msg = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
value = new StringView((token->type == Token::TemplateToken) ? token->valueTemplate.raw : token->valueString);
|
|
} else {
|
|
value = new ASCIIString("ILLEGAL");
|
|
}
|
|
|
|
// msg = msg.replace('%0', value);
|
|
UTF16StringDataNonGCStd msgData;
|
|
msgData.assign(msg, &msg[strlen(msg)]);
|
|
UTF16StringDataNonGCStd valueData = UTF16StringDataNonGCStd(value->toUTF16StringData().data());
|
|
replaceAll(msgData, UTF16StringDataNonGCStd(u"%s"), valueData);
|
|
|
|
// if (token && typeof token.lineNumber == 'number') {
|
|
if (token) {
|
|
const size_t index = token->start;
|
|
const size_t line = token->lineNumber;
|
|
const size_t column = token->start - this->lastMarker.lineStart + 1;
|
|
return this->errorHandler->createError(index, line, column, new UTF16String(msgData.data(), msgData.length()));
|
|
} else {
|
|
const size_t index = this->lastMarker.index;
|
|
const size_t line = this->lastMarker.lineNumber;
|
|
const size_t column = index - this->lastMarker.lineStart + 1;
|
|
return this->errorHandler->createError(index, line, column, new UTF16String(msgData.data(), msgData.length()));
|
|
}
|
|
}
|
|
|
|
void throwUnexpectedToken(ScannerResult* token, const char* message = nullptr)
|
|
{
|
|
throw this->unexpectedTokenError(token, message = nullptr);
|
|
}
|
|
|
|
void tolerateUnexpectedToken(ScannerResult* token, const char* message = nullptr)
|
|
{
|
|
// this->errorHandler.tolerate(this->unexpectedTokenError(token, message));
|
|
throwUnexpectedToken(token, message);
|
|
}
|
|
|
|
void collectComments()
|
|
{
|
|
this->scanner->scanComments();
|
|
/*
|
|
if (!this->config.comment) {
|
|
this->scanner->scanComments();
|
|
} else {
|
|
const comments: Comment[] = this->scanner->scanComments();
|
|
if (comments.length > 0 && this->delegate) {
|
|
for (let i = 0; i < comments.length; ++i) {
|
|
const e: Comment = comments[i];
|
|
let node;
|
|
node = {
|
|
type: e.multiLine ? 'BlockComment' : 'LineComment',
|
|
value: this->scanner->source.slice(e.slice[0], e.slice[1])
|
|
};
|
|
if (this->config.range) {
|
|
node.range = e.range;
|
|
}
|
|
if (this->config.loc) {
|
|
node.loc = e.loc;
|
|
}
|
|
const metadata = {
|
|
start: {
|
|
line: e.loc.start.line,
|
|
column: e.loc.start.column,
|
|
offset: e.range[0]
|
|
},
|
|
end: {
|
|
line: e.loc.end.line,
|
|
column: e.loc.end.column,
|
|
offset: e.range[1]
|
|
}
|
|
};
|
|
this->delegate(node, metadata);
|
|
}
|
|
}
|
|
}*/
|
|
}
|
|
|
|
ScannerResult* nextToken()
|
|
{
|
|
ScannerResult* token = this->lookahead;
|
|
|
|
this->lastMarker.index = this->scanner->index;
|
|
this->lastMarker.lineNumber = this->scanner->lineNumber;
|
|
this->lastMarker.lineStart = this->scanner->lineStart;
|
|
|
|
this->collectComments();
|
|
|
|
this->startMarker.index = this->scanner->index;
|
|
this->startMarker.lineNumber = this->scanner->lineNumber;
|
|
this->startMarker.lineStart = this->scanner->lineStart;
|
|
|
|
ScannerResult* next;
|
|
next = this->scanner->lex();
|
|
this->hasLineTerminator = (token && next) ? (token->lineNumber != next->lineNumber) : false;
|
|
|
|
if (next && this->context->strict && next->type == Token::IdentifierToken) {
|
|
if (this->scanner->isStrictModeReservedWord(next->valueString)) {
|
|
next->type = Token::KeywordToken;
|
|
}
|
|
}
|
|
this->lookahead = next;
|
|
|
|
/*
|
|
if (this->config.tokens && next.type !== Token.EOF) {
|
|
this->tokens.push(this->convertToken(next));
|
|
}
|
|
*/
|
|
|
|
return token;
|
|
}
|
|
|
|
ScannerResult* nextRegexToken()
|
|
{
|
|
this->collectComments();
|
|
|
|
ScannerResult* token = this->scanner->scanRegExp();
|
|
/*
|
|
if (this->config.tokens) {
|
|
// Pop the previous token, '/' or '/='
|
|
// This is added from the lookahead token.
|
|
this->tokens.pop();
|
|
|
|
this->tokens.push(this->convertToken(token));
|
|
}*/
|
|
|
|
// Prime the next lookahead.
|
|
this->lookahead = token;
|
|
this->nextToken();
|
|
|
|
return token;
|
|
}
|
|
|
|
MetaNode createNode()
|
|
{
|
|
MetaNode n;
|
|
n.index = this->startMarker.index;
|
|
n.line = this->startMarker.lineNumber;
|
|
n.column = this->startMarker.index - this->startMarker.lineStart;
|
|
return n;
|
|
}
|
|
|
|
MetaNode startNode(ScannerResult* token)
|
|
{
|
|
MetaNode n;
|
|
n.index = token->start;
|
|
n.line = token->lineNumber;
|
|
n.column = token->start - token->lineStart;
|
|
return n;
|
|
}
|
|
|
|
template <typename T>
|
|
T* finalize(MetaNode meta, T* node)
|
|
{
|
|
/*
|
|
if (this->config.range) {
|
|
node.range = [meta.index, this->lastMarker.index];
|
|
}
|
|
|
|
if (this->config.loc) {
|
|
node.loc = {
|
|
start: {
|
|
line: meta.line,
|
|
column: meta.column
|
|
},
|
|
end: {
|
|
line: this->lastMarker.lineNumber,
|
|
column: this->lastMarker.index - this->lastMarker.lineStart
|
|
}
|
|
};
|
|
if (this->config.source) {
|
|
node.loc.source = this->config.source;
|
|
}
|
|
}*/
|
|
auto type = node->type();
|
|
if (type == CallExpression) {
|
|
CallExpressionNode* c = (CallExpressionNode*)node;
|
|
if (c->callee()->isIdentifier()) {
|
|
if (((IdentifierNode*)c->callee())->name().string()->equals("eval")){
|
|
scopeContexts.back()->m_hasEval = true;
|
|
}
|
|
}
|
|
} else if (type == WithStatement) {
|
|
scopeContexts.back()->m_hasWith = true;
|
|
} else if (type == YieldExpression) {
|
|
scopeContexts.back()->m_hasYield = true;
|
|
}
|
|
|
|
node->m_loc = NodeLOC(meta.line, meta.column, meta.index);
|
|
if (this->delegate) {
|
|
this->delegate(node, NodeLOC(meta.line, meta.column, meta.index),
|
|
NodeLOC(this->lastMarker.lineNumber, this->lastMarker.index - this->lastMarker.lineStart, this->lastMarker.index));
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
// Expect the next token to match the specified punctuator.
|
|
// If not, an exception will be thrown.
|
|
|
|
void expect(PunctuatorsKind value)
|
|
{
|
|
ScannerResult* token = this->nextToken();
|
|
if (token->type != Token::PunctuatorToken || token->valuePunctuatorsKind != value) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
// Quietly expect a comma when in tolerant mode, otherwise delegates to expect().
|
|
|
|
void expectCommaSeparator()
|
|
{
|
|
/*
|
|
if (this->config.tolerant) {
|
|
let token = this->lookahead;
|
|
if (token.type == Token.Punctuator && token.value == ',') {
|
|
this->nextToken();
|
|
} else if (token.type == Token.Punctuator && token.value == ';') {
|
|
this->nextToken();
|
|
this->tolerateUnexpectedToken(token);
|
|
} else {
|
|
this->tolerateUnexpectedToken(token, Messages.UnexpectedToken);
|
|
}
|
|
} else {
|
|
this->expect(',');
|
|
}*/
|
|
this->expect(PunctuatorsKind::Comma);
|
|
}
|
|
|
|
// Expect the next token to match the specified keyword.
|
|
// If not, an exception will be thrown.
|
|
|
|
void expectKeyword(KeywordKind keyword)
|
|
{
|
|
ScannerResult* token = this->nextToken();
|
|
if (token->type != Token::KeywordToken || token->valueKeywordKind != keyword) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
|
|
// Return true if the next token matches the specified punctuator.
|
|
|
|
bool match(PunctuatorsKind value)
|
|
{
|
|
return this->lookahead->type == Token::PunctuatorToken && this->lookahead->valuePunctuatorsKind == value;
|
|
}
|
|
|
|
// Return true if the next token matches the specified keyword
|
|
|
|
bool matchKeyword(KeywordKind keyword)
|
|
{
|
|
return this->lookahead->type == Token::KeywordToken && this->lookahead->valueKeywordKind == keyword;
|
|
}
|
|
|
|
// Return true if the next token matches the specified contextual keyword
|
|
// (where an identifier is sometimes a keyword depending on the context)
|
|
|
|
bool matchContextualKeyword(KeywordKind keyword)
|
|
{
|
|
return this->lookahead->type == Token::IdentifierToken && this->lookahead->valueKeywordKind == keyword;
|
|
}
|
|
|
|
// Return true if the next token is an assignment operator
|
|
|
|
bool matchAssign()
|
|
{
|
|
if (this->lookahead->type != Token::PunctuatorToken) {
|
|
return false;
|
|
}
|
|
PunctuatorsKind op = this->lookahead->valuePunctuatorsKind;
|
|
|
|
if (op >= Substitution && op < SubstitutionEnd) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Cover grammar support.
|
|
//
|
|
// When an assignment expression position starts with an left parenthesis, the determination of the type
|
|
// of the syntax is to be deferred arbitrarily long until the end of the parentheses pair (plus a lookahead)
|
|
// or the first comma. This situation also defers the determination of all the expressions nested in the pair.
|
|
//
|
|
// There are three productions that can be parsed in a parentheses pair that needs to be determined
|
|
// after the outermost pair is closed. They are:
|
|
//
|
|
// 1. AssignmentExpression
|
|
// 2. BindingElements
|
|
// 3. AssignmentTargets
|
|
//
|
|
// In order to avoid exponential backtracking, we use two flags to denote if the production can be
|
|
// binding element or assignment target.
|
|
//
|
|
// The three productions have the relationship:
|
|
//
|
|
// BindingElements ⊆ AssignmentTargets ⊆ AssignmentExpression
|
|
//
|
|
// with a single exception that CoverInitializedName when used directly in an Expression, generates
|
|
// an early error. Therefore, we need the third state, firstCoverInitializedNameError, to track the
|
|
// first usage of CoverInitializedName and report it when we reached the end of the parentheses pair.
|
|
//
|
|
// isolateCoverGrammar function runs the given parser function with a new cover grammar context, and it does not
|
|
// effect the current flags. This means the production the parser parses is only used as an expression. Therefore
|
|
// the CoverInitializedName check is conducted.
|
|
//
|
|
// inheritCoverGrammar function runs the given parse function with a new cover grammar context, and it propagates
|
|
// the flags outside of the parser. This means the production the parser parses is used as a part of a potential
|
|
// pattern. The CoverInitializedName check is deferred.
|
|
|
|
typedef Node* (Parser::*ParseFunction)();
|
|
|
|
template <typename T>
|
|
Node* isolateCoverGrammar(T parseFunction)
|
|
{
|
|
const bool previousIsBindingElement = this->context->isBindingElement;
|
|
const bool previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
ScannerResult* previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
|
|
|
|
Node* result = (this->*parseFunction)();
|
|
if (this->context->firstCoverInitializedNameError != nullptr) {
|
|
this->throwUnexpectedToken(this->context->firstCoverInitializedNameError);
|
|
}
|
|
|
|
this->context->isBindingElement = previousIsBindingElement;
|
|
this->context->isAssignmentTarget = previousIsAssignmentTarget;
|
|
this->context->firstCoverInitializedNameError = previousFirstCoverInitializedNameError;
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
Node* inheritCoverGrammar(T parseFunction)
|
|
{
|
|
const bool previousIsBindingElement = this->context->isBindingElement;
|
|
const bool previousIsAssignmentTarget = this->context->isAssignmentTarget;
|
|
ScannerResult* previousFirstCoverInitializedNameError = this->context->firstCoverInitializedNameError;
|
|
|
|
this->context->isBindingElement = true;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
|
|
Node* result = (this->*parseFunction)();
|
|
|
|
this->context->isBindingElement = this->context->isBindingElement && previousIsBindingElement;
|
|
this->context->isAssignmentTarget = this->context->isAssignmentTarget && previousIsAssignmentTarget;
|
|
if (previousFirstCoverInitializedNameError)
|
|
this->context->firstCoverInitializedNameError = previousFirstCoverInitializedNameError;
|
|
|
|
return result;
|
|
}
|
|
|
|
void consumeSemicolon()
|
|
{
|
|
if (this->match(PunctuatorsKind::SemiColon)) {
|
|
this->nextToken();
|
|
} else if (!this->hasLineTerminator) {
|
|
if (this->lookahead->type != Token::EOFToken && !this->match(PunctuatorsKind::RightBrace)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
this->lastMarker.index = this->startMarker.index;
|
|
this->lastMarker.lineNumber = this->startMarker.lineNumber;
|
|
this->lastMarker.lineStart = this->startMarker.lineStart;
|
|
}
|
|
}
|
|
|
|
IdentifierNode* finishIdentifier(ScannerResult* token, bool isScopeVariableName)
|
|
{
|
|
auto ret = new IdentifierNode(AtomicString(this->escargotContext, token->valueString));
|
|
nodeExtraInfo.insert(std::make_pair(ret, token));
|
|
if (trackUsingNames)
|
|
scopeContexts.back()->insertUsingName(ret->name());
|
|
return ret;
|
|
}
|
|
|
|
#define DEFINE_AS_NODE(TypeName) \
|
|
TypeName##Node* as##TypeName##Node(Node* n) \
|
|
{ \
|
|
if (!n) return (TypeName##Node*)n; \
|
|
ASSERT(n->is##TypeName##Node()); \
|
|
return (TypeName##Node*)n; \
|
|
} \
|
|
|
|
DEFINE_AS_NODE(Expression);
|
|
DEFINE_AS_NODE(Statement);
|
|
|
|
// ECMA-262 12.2 Primary Expressions
|
|
|
|
Node* parsePrimaryExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
Node* expr;
|
|
// let value, token, raw;
|
|
ScannerResult* token;
|
|
|
|
switch (this->lookahead->type) {
|
|
case Token::IdentifierToken:
|
|
if (this->sourceType == SourceType::Module && this->lookahead->valueKeywordKind == KeywordKind::Await) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
}
|
|
expr = this->finalize(node, finishIdentifier(this->nextToken(), true));
|
|
break;
|
|
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && this->lookahead->octal) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::StrictOctalLiteral);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// raw = this->getTokenRaw(token);
|
|
if (token->type == Token::NumericLiteralToken)
|
|
expr = this->finalize(node, new LiteralNode(Value(token->valueNumber)));
|
|
else
|
|
expr = this->finalize(node, new LiteralNode(Value(new StringView(token->valueString))));
|
|
break;
|
|
|
|
case Token::BooleanLiteralToken:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// token.value = (token.value === 'true');
|
|
// raw = this->getTokenRaw(token);
|
|
{
|
|
bool value = token->valueString == "true";
|
|
expr = this->finalize(node, new LiteralNode(Value(value)));
|
|
}
|
|
break;
|
|
|
|
case Token::NullLiteralToken:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
token = this->nextToken();
|
|
// token.value = null;
|
|
// raw = this->getTokenRaw(token);
|
|
expr = this->finalize(node, new LiteralNode(Value(Value::Null)));
|
|
break;
|
|
|
|
case Token::TemplateToken:
|
|
expr = this->parseTemplateLiteral();
|
|
break;
|
|
|
|
case Token::PunctuatorToken:
|
|
{
|
|
PunctuatorsKind value = this->lookahead->valuePunctuatorsKind;
|
|
switch (value) {
|
|
case LeftParenthesis:
|
|
this->context->isBindingElement = false;
|
|
expr = this->inheritCoverGrammar(&Parser::parseGroupExpression);
|
|
break;
|
|
case LeftSquareBracket:
|
|
expr = this->inheritCoverGrammar(&Parser::parseArrayInitializer);
|
|
break;
|
|
case LeftBrace:
|
|
expr = this->inheritCoverGrammar(&Parser::parseObjectInitializer);
|
|
break;
|
|
case Divide:
|
|
case DivideEqual:
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->scanner->index = this->startMarker.index;
|
|
token = this->nextRegexToken();
|
|
// raw = this->getTokenRaw(token);
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = this->finalize(node, new Node.RegexLiteral(token.value, raw, token.regex));
|
|
break;
|
|
default:
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Token::KeywordToken:
|
|
if (!this->context->strict && this->context->allowYield && this->matchKeyword(KeywordKind::Yield)) {
|
|
expr = this->parseIdentifierName();
|
|
} else if (!this->context->strict && this->matchKeyword(KeywordKind::Let)) {
|
|
expr = this->finalize(node, finishIdentifier(this->nextToken(), true));
|
|
} else {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
if (this->matchKeyword(KeywordKind::Function)) {
|
|
expr = this->parseFunctionExpression();
|
|
} else if (this->matchKeyword(KeywordKind::This)) {
|
|
this->nextToken();
|
|
expr = this->finalize(node, new ThisExpressionNode());
|
|
} else if (this->matchKeyword(KeywordKind::Class)) {
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = this->parseClassExpression();
|
|
} else {
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
struct ParseParameterOptions {
|
|
PatternNodeVector params;
|
|
std::vector<AtomicString, gc_allocator<AtomicString>> paramSet;
|
|
ScannerResult* stricted;
|
|
const char* message;
|
|
ScannerResult* firstRestricted;
|
|
ParseParameterOptions()
|
|
{
|
|
firstRestricted = nullptr;
|
|
stricted = nullptr;
|
|
message = nullptr;
|
|
}
|
|
};
|
|
|
|
|
|
void validateParam(ParseParameterOptions& options, ScannerResult* param, AtomicString name)
|
|
{
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamName;
|
|
}
|
|
if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
} else if (!options.firstRestricted) {
|
|
if (this->scanner->isRestrictedWord(name)) {
|
|
options.firstRestricted = param;
|
|
options.message = Messages::StrictParamName;
|
|
} else if (this->scanner->isStrictModeReservedWord(name)) {
|
|
options.firstRestricted = param;
|
|
options.message = Messages::StrictReservedWord;
|
|
} else if (std::find(options.paramSet.begin(), options.paramSet.end(), name) != options.paramSet.end()) {
|
|
options.stricted = param;
|
|
options.message = Messages::StrictParamDupe;
|
|
}
|
|
}
|
|
options.paramSet.push_back(name);
|
|
}
|
|
|
|
RestElementNode* parseRestElement(std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>>& params)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->nextToken();
|
|
if (this->match(LeftBrace)) {
|
|
this->throwError(Messages::ObjectPatternAsRestParameter);
|
|
}
|
|
params.push_back(this->lookahead);
|
|
|
|
IdentifierNode* param = this->parseVariableIdentifier();
|
|
if (this->match(Equal)) {
|
|
this->throwError(Messages::DefaultRestParameter);
|
|
}
|
|
if (!this->match(RightParenthesis)) {
|
|
this->throwError(Messages::ParameterAfterRestParameter);
|
|
}
|
|
|
|
return this->finalize(node, new RestElementNode(param));
|
|
}
|
|
|
|
Node* parsePattern(std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>>& params, String* kind = String::emptyString)
|
|
{
|
|
Node* pattern;
|
|
|
|
if (this->match(LeftSquareBracket)) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// pattern = this->parseArrayPattern(params, kind);
|
|
} else if (this->match(LeftBrace)) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// pattern = this->parseObjectPattern(params, kind);
|
|
} else {
|
|
if (this->matchKeyword(KeywordKind::Let) && (kind->equals("const") || kind->equals("let"))) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages::UnexpectedToken);
|
|
}
|
|
params.push_back(this->lookahead);
|
|
pattern = this->parseVariableIdentifier(kind);
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
Node* parsePatternWithDefault(std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>>& params, String* kind = String::emptyString)
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
|
|
Node* pattern = this->parsePattern(params, kind);
|
|
if (this->match(PunctuatorsKind::Substitution)) {
|
|
this->nextToken();
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = true;
|
|
Node* right = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowYield = previousAllowYield;
|
|
pattern = this->finalize(this->startNode(startToken), new AssignmentExpressionSimpleNode(pattern, right));
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
bool parseFormalParameter(ParseParameterOptions& options)
|
|
{
|
|
Node* param;
|
|
trackUsingNames = false;
|
|
std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>> params;
|
|
ScannerResult* token = this->lookahead;
|
|
if (token->type == Token::PunctuatorToken && token->valuePunctuatorsKind == PunctuatorsKind::PeriodPeriodPeriod) {
|
|
RestElementNode* param = this->parseRestElement(params);
|
|
this->validateParam(options, params.back(), param->argument()->name());
|
|
options.params.push_back(param);
|
|
trackUsingNames = true;
|
|
return false;
|
|
}
|
|
|
|
param = this->parsePatternWithDefault(params);
|
|
for (size_t i = 0; i < params.size(); i++) {
|
|
UTF16StringData data = params[i]->valueString.toUTF16StringData();
|
|
AtomicString as(this->escargotContext, data.data(), data.length());
|
|
this->validateParam(options, params[i], as);
|
|
}
|
|
options.params.push_back(param);
|
|
trackUsingNames = true;
|
|
return !this->match(PunctuatorsKind::RightParenthesis);
|
|
}
|
|
|
|
struct ParseFormalParametersResult {
|
|
PatternNodeVector params;
|
|
ScannerResult* stricted;
|
|
ScannerResult* firstRestricted;
|
|
const char* message;
|
|
ParseFormalParametersResult(PatternNodeVector params, ScannerResult* stricted, ScannerResult* firstRestricted, const char* message)
|
|
{
|
|
this->params = std::move(params);
|
|
this->stricted = stricted;
|
|
this->firstRestricted = firstRestricted;
|
|
this->message = nullptr;
|
|
}
|
|
};
|
|
|
|
ParseFormalParametersResult parseFormalParameters(ScannerResult* firstRestricted = nullptr)
|
|
{
|
|
ParseParameterOptions options;
|
|
|
|
options.firstRestricted = firstRestricted;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (!this->match(RightParenthesis)) {
|
|
options.paramSet.clear();
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->parseFormalParameter(options)) {
|
|
break;
|
|
}
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return ParseFormalParametersResult(options.params, options.stricted, options.firstRestricted, options.message);
|
|
}
|
|
|
|
// ECMA-262 12.2.5 Array Initializer
|
|
|
|
SpreadElementNode* parseSpreadElement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(PunctuatorsKind::PeriodPeriodPeriod);
|
|
Node* arg = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
return this->finalize(node, new SpreadElementNode(arg));
|
|
}
|
|
|
|
Node* parseArrayInitializer()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
// const elements: Node.ArrayExpressionElement[] = [];
|
|
ExpressionNodeVector elements;
|
|
|
|
this->expect(LeftSquareBracket);
|
|
while (!this->match(RightSquareBracket)) {
|
|
if (this->match(Comma)) {
|
|
this->nextToken();
|
|
elements.push_back(nullptr);
|
|
} else if (this->match(PeriodPeriodPeriod)) {
|
|
SpreadElementNode* element = this->parseSpreadElement();
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->expect(Comma);
|
|
}
|
|
elements.push_back(element);
|
|
} else {
|
|
elements.push_back(this->inheritCoverGrammar(&Parser::parseAssignmentExpression));
|
|
if (!this->match(RightSquareBracket)) {
|
|
this->expect(Comma);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightSquareBracket);
|
|
|
|
return this->finalize(node, new ArrayExpressionNode(std::move(elements)));
|
|
}
|
|
|
|
// ECMA-262 12.2.6 Object Initializer
|
|
|
|
Node* parsePropertyMethod(ParseFormalParametersResult& params)
|
|
{
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
pushScopeContext(params.params, AtomicString());
|
|
extractNamesFromFunctionParams(params.params);
|
|
const bool previousStrict = this->context->strict;
|
|
Node* body = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
|
|
if (this->context->strict && params.firstRestricted) {
|
|
this->tolerateUnexpectedToken(params.firstRestricted, params.message);
|
|
}
|
|
if (this->context->strict && params.stricted) {
|
|
this->tolerateUnexpectedToken(params.stricted, params.message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
|
|
return body;
|
|
}
|
|
|
|
FunctionExpressionNode* parsePropertyMethodFunction()
|
|
{
|
|
const bool isGenerator = false;
|
|
MetaNode node = this->createNode();
|
|
|
|
const bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
ParseFormalParametersResult params = this->parseFormalParameters();
|
|
Node* method = this->parsePropertyMethod(params);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method, popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
Node* parseObjectPropertyKey()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ScannerResult* token = this->nextToken();
|
|
|
|
Node* key = nullptr;
|
|
switch (token->type) {
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
if (this->context->strict && token->octal) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictOctalLiteral);
|
|
}
|
|
// const raw = this->getTokenRaw(token);
|
|
{
|
|
Value v;
|
|
if (token->type == Token::NumericLiteralToken) {
|
|
v = Value(token->valueNumber);
|
|
} else {
|
|
v = Value(new StringView(token->valueString));
|
|
}
|
|
key = this->finalize(node, new LiteralNode(v));
|
|
}
|
|
break;
|
|
|
|
case Token::IdentifierToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::KeywordToken:
|
|
key = this->finalize(node, finishIdentifier(token, false));
|
|
break;
|
|
|
|
case Token::PunctuatorToken:
|
|
if (token->valuePunctuatorsKind == LeftSquareBracket) {
|
|
key = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->expect(RightSquareBracket);
|
|
} else {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
bool qualifiedPropertyName(ScannerResult* token)
|
|
{
|
|
switch (token->type) {
|
|
case Token::IdentifierToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::KeywordToken:
|
|
return true;
|
|
case Token::PunctuatorToken:
|
|
return token->valuePunctuatorsKind == LeftSquareBracket;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool isPropertyKey(Node* key, const char* value)
|
|
{
|
|
if (key->type() == Identifier) {
|
|
return ((IdentifierNode*)key)->name() == value;
|
|
} else if (key->type() == Literal) {
|
|
if (((LiteralNode*)key)->value().isString()) {
|
|
return ((LiteralNode*)key)->value().asString()->equals(value);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
PropertyNode* parseObjectProperty(bool& hasProto)//: Node.Property
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ScannerResult* token = this->lookahead;
|
|
|
|
PropertyNode::Kind kind;
|
|
Node* key; //'': Node.PropertyKey;
|
|
Node* value; //: Node.PropertyValue;
|
|
|
|
bool computed = false;
|
|
bool method = false;
|
|
bool shorthand = false;
|
|
|
|
if (token->type == Token::IdentifierToken) {
|
|
this->nextToken();
|
|
key = this->finalize(node, finishIdentifier(token, true));
|
|
} else if (this->match(PunctuatorsKind::Multiply)) {
|
|
this->nextToken();
|
|
} else {
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
}
|
|
|
|
bool lookaheadPropertyKey = this->qualifiedPropertyName(this->lookahead);
|
|
if (token->type == Token::IdentifierToken && token->valueString == "get" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Get;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
this->context->allowYield = false;
|
|
value = this->parseGetterMethod();
|
|
|
|
} else if (token->type == Token::IdentifierToken && token->valueString == "set" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Set;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
value = this->parseSetterMethod();
|
|
|
|
} else if (token->type == Token::PunctuatorToken && token->valueString == "*" && lookaheadPropertyKey) {
|
|
kind = PropertyNode::Kind::Init;
|
|
computed = this->match(LeftSquareBracket);
|
|
key = this->parseObjectPropertyKey();
|
|
value = this->parseGeneratorMethod();
|
|
method = true;
|
|
|
|
} else {
|
|
if (!key) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
kind = PropertyNode::Kind::Init;
|
|
if (this->match(PunctuatorsKind::Colon)) {
|
|
if (!computed && this->isPropertyKey(key, "__proto__")) {
|
|
if (hasProto) {
|
|
this->tolerateError(Messages::DuplicateProtoProperty);
|
|
}
|
|
hasProto = true;
|
|
}
|
|
this->nextToken();
|
|
value = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
} else if (this->match(LeftParenthesis)) {
|
|
value = this->parsePropertyMethodFunction();
|
|
method = true;
|
|
|
|
} else if (token->type == Token::IdentifierToken) {
|
|
Node* id = this->finalize(node, finishIdentifier(token, true));
|
|
if (this->match(Substitution)) {
|
|
this->context->firstCoverInitializedNameError = this->lookahead;
|
|
this->nextToken();
|
|
shorthand = true;
|
|
Node* init = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
value = this->finalize(node, new AssignmentExpressionSimpleNode(id, init));
|
|
} else {
|
|
shorthand = true;
|
|
value = id;
|
|
}
|
|
} else {
|
|
this->throwUnexpectedToken(this->nextToken());
|
|
}
|
|
}
|
|
|
|
// return this->finalize(node, new PropertyNode(kind, key, computed, value, method, shorthand));
|
|
return this->finalize(node, new PropertyNode(key, value, kind));
|
|
}
|
|
|
|
Node* parseObjectInitializer()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
PropertiesNodeVector properties;
|
|
bool hasProto = false;
|
|
while (!this->match(RightBrace)) {
|
|
properties.push_back(this->parseObjectProperty(hasProto));
|
|
if (!this->match(RightBrace)) {
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, new ObjectExpressionNode(std::move(properties)));
|
|
}
|
|
|
|
// ECMA-262 12.2.9 Template Literals
|
|
Node* parseTemplateLiteral()
|
|
{
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
/*
|
|
parseTemplateHead(): Node.TemplateElement {
|
|
assert(this->lookahead.head, 'Template literal must start with a template head');
|
|
|
|
const node = this->createNode();
|
|
const token = this->nextToken();
|
|
const value = {
|
|
raw: token.value.raw,
|
|
cooked: token.value.cooked
|
|
};
|
|
|
|
return this->finalize(node, new Node.TemplateElement(value, token.tail));
|
|
}
|
|
|
|
parseTemplateElement(): Node.TemplateElement {
|
|
if (this->lookahead.type !== Token.Template) {
|
|
this->throwUnexpectedToken();
|
|
}
|
|
|
|
const node = this->createNode();
|
|
const token = this->nextToken();
|
|
const value = {
|
|
raw: token.value.raw,
|
|
cooked: token.value.cooked
|
|
};
|
|
|
|
return this->finalize(node, new Node.TemplateElement(value, token.tail));
|
|
}
|
|
|
|
parseTemplateLiteral(): Node.TemplateLiteral {
|
|
const node = this->createNode();
|
|
|
|
const expressions = [];
|
|
const quasis = [];
|
|
|
|
let quasi = this->parseTemplateHead();
|
|
quasis.push(quasi);
|
|
while (!quasi.tail) {
|
|
expressions.push(this->parseExpression());
|
|
quasi = this->parseTemplateElement();
|
|
quasis.push(quasi);
|
|
}
|
|
|
|
return this->finalize(node, new Node.TemplateLiteral(quasis, expressions));
|
|
}
|
|
|
|
// ECMA-262 12.2.10 The Grouping Operator
|
|
|
|
reinterpretExpressionAsPattern(expr) {
|
|
switch (expr.type) {
|
|
case Syntax.Identifier:
|
|
case Syntax.MemberExpression:
|
|
case Syntax.RestElement:
|
|
case Syntax.AssignmentPattern:
|
|
break;
|
|
case Syntax.SpreadElement:
|
|
expr.type = Syntax.RestElement;
|
|
this->reinterpretExpressionAsPattern(expr.argument);
|
|
break;
|
|
case Syntax.ArrayExpression:
|
|
expr.type = Syntax.ArrayPattern;
|
|
for (let i = 0; i < expr.elements.length; i++) {
|
|
if (expr.elements[i] !== null) {
|
|
this->reinterpretExpressionAsPattern(expr.elements[i]);
|
|
}
|
|
}
|
|
break;
|
|
case Syntax.ObjectExpression:
|
|
expr.type = Syntax.ObjectPattern;
|
|
for (let i = 0; i < expr.properties.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expr.properties[i].value);
|
|
}
|
|
break;
|
|
case Syntax.AssignmentExpression:
|
|
expr.type = Syntax.AssignmentPattern;
|
|
delete expr.operator;
|
|
this->reinterpretExpressionAsPattern(expr.left);
|
|
break;
|
|
default:
|
|
// Allow other node type for tolerant parsing.
|
|
break;
|
|
}
|
|
}
|
|
|
|
parseGroupExpression(): ArrowParameterPlaceHolderNode | Node.Expression {
|
|
let expr;
|
|
|
|
this->expect('(');
|
|
if (this->match(')')) {
|
|
this->nextToken();
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: []
|
|
};
|
|
} else {
|
|
const startToken = this->lookahead;
|
|
let params = [];
|
|
if (this->match('...')) {
|
|
expr = this->parseRestElement(params);
|
|
this->expect(')');
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: [expr]
|
|
};
|
|
} else {
|
|
let arrow = false;
|
|
this->context.isBindingElement = true;
|
|
expr = this->inheritCoverGrammar(this->parseAssignmentExpression);
|
|
|
|
if (this->match(',')) {
|
|
const expressions = [];
|
|
|
|
this->context.isAssignmentTarget = false;
|
|
expressions.push(expr);
|
|
while (this->startMarker.index < this->scanner.length) {
|
|
if (!this->match(',')) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
|
|
if (this->match('...')) {
|
|
if (!this->context.isBindingElement) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
expressions.push(this->parseRestElement(params));
|
|
this->expect(')');
|
|
if (!this->match('=>')) {
|
|
this->expect('=>');
|
|
}
|
|
this->context.isBindingElement = false;
|
|
for (let i = 0; i < expressions.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expressions[i]);
|
|
}
|
|
arrow = true;
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: expressions
|
|
};
|
|
} else {
|
|
expressions.push(this->inheritCoverGrammar(this->parseAssignmentExpression));
|
|
}
|
|
if (arrow) {
|
|
break;
|
|
}
|
|
}
|
|
if (!arrow) {
|
|
expr = this->finalize(this->startNode(startToken), new Node.SequenceExpression(expressions));
|
|
}
|
|
}
|
|
|
|
if (!arrow) {
|
|
this->expect(')');
|
|
if (this->match('=>')) {
|
|
if (expr.type === Syntax.Identifier && expr.name === 'yield') {
|
|
arrow = true;
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: [expr]
|
|
};
|
|
}
|
|
if (!arrow) {
|
|
if (!this->context.isBindingElement) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
if (expr.type === Syntax.SequenceExpression) {
|
|
for (let i = 0; i < expr.expressions.length; i++) {
|
|
this->reinterpretExpressionAsPattern(expr.expressions[i]);
|
|
}
|
|
} else {
|
|
this->reinterpretExpressionAsPattern(expr);
|
|
}
|
|
|
|
const params = (expr.type === Syntax.SequenceExpression ? expr.expressions : [expr]);
|
|
expr = {
|
|
type: ArrowParameterPlaceHolder,
|
|
params: params
|
|
};
|
|
}
|
|
}
|
|
this->context.isBindingElement = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
*/
|
|
Node* parseGroupExpression()
|
|
{
|
|
Node* expr;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
ScannerResult* startToken = this->lookahead;
|
|
this->context->isBindingElement = true;
|
|
expr = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector expressions;
|
|
|
|
this->context->isAssignmentTarget = false;
|
|
expressions.push_back(expr);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
|
|
expressions.push_back(this->inheritCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
expr = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return expr;
|
|
}
|
|
// ECMA-262 12.3 Left-Hand-Side Expressions
|
|
|
|
ArgumentVector parseArguments()
|
|
{
|
|
this->expect(LeftParenthesis);
|
|
ArgumentVector args;
|
|
if (!this->match(RightParenthesis)) {
|
|
while (true) {
|
|
Node* expr = this->match(PeriodPeriodPeriod) ? this->parseSpreadElement() :
|
|
this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
args.push_back(expr);
|
|
if (this->match(RightParenthesis)) {
|
|
break;
|
|
}
|
|
this->expectCommaSeparator();
|
|
}
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
return args;
|
|
}
|
|
|
|
bool isIdentifierName(ScannerResult* token)
|
|
{
|
|
return token->type == Token::IdentifierToken ||
|
|
token->type == Token::KeywordToken ||
|
|
token->type == Token::BooleanLiteralToken ||
|
|
token->type == Token::NullLiteralToken;
|
|
}
|
|
|
|
IdentifierNode* parseIdentifierName()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
ScannerResult* token = this->nextToken();
|
|
if (!this->isIdentifierName(token)) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
return this->finalize(node, finishIdentifier(token, true));
|
|
}
|
|
|
|
Node* parseNewExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
IdentifierNode* id = this->parseIdentifierName();
|
|
// assert(id.name === 'new', 'New expression must start with `new`');
|
|
|
|
Node* expr;
|
|
if (this->match(Period)) {
|
|
this->nextToken();
|
|
if (this->lookahead->type == Token::IdentifierToken && this->context->inFunctionBody && this->lookahead->valueString == "target") {
|
|
// TODO
|
|
IdentifierNode* property = this->parseIdentifierName();
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = new Node.MetaProperty(id, property);
|
|
} else {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
} else {
|
|
Node* callee = this->isolateCoverGrammar(&Parser::parseLeftHandSideExpression);
|
|
ArgumentVector args;
|
|
if (this->match(LeftParenthesis))
|
|
args = this->parseArguments();
|
|
expr = new NewExpressionNode(callee, std::move(args));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
}
|
|
|
|
return this->finalize(node, expr);
|
|
}
|
|
|
|
Node* parseLeftHandSideExpressionAllowCall()
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
|
|
Node* expr;
|
|
if (this->matchKeyword(Super) && this->context->inFunctionBody) {
|
|
MetaNode node = this->createNode();
|
|
this->nextToken();
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// expr = this->finalize(node, new Node.Super());
|
|
if (!this->match(LeftParenthesis) && !this->match(Period) && !this->match(LeftSquareBracket)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
} else {
|
|
expr = this->inheritCoverGrammar(this->matchKeyword(New) ? &Parser::parseNewExpression : &Parser::parsePrimaryExpression);
|
|
}
|
|
|
|
while (true) {
|
|
if (this->match(Period)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(Period);
|
|
IdentifierNode* property = this->parseIdentifierName();
|
|
expr = this->finalize(this->startNode(startToken), new MemberExpressionNode(expr, property, true));
|
|
|
|
} else if (this->match(LeftParenthesis)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = false;
|
|
ArgumentVector args = this->parseArguments();
|
|
expr = this->finalize(this->startNode(startToken), new CallExpressionNode(expr, std::move(args)));
|
|
|
|
} else if (this->match(LeftSquareBracket)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(RightSquareBracket);
|
|
Node* property = this->isolateCoverGrammar(&Parser::parseExpression);
|
|
this->expect(RightSquareBracket);
|
|
expr = this->finalize(this->startNode(startToken), new MemberExpressionNode(expr, property, false));
|
|
|
|
} else if (this->lookahead->type == Token::TemplateToken && this->lookahead->head) {
|
|
Node* quasi = this->parseTemplateLiteral();
|
|
// expr = this->finalize(this->startNode(startToken), new Node.TaggedTemplateExpression(expr, quasi));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
return expr;
|
|
}
|
|
|
|
Node* parseSuper()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expectKeyword(Super);
|
|
if (!this->match(LeftSquareBracket) && !this->match(Period)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// return this->finalize(node, new Node.Super());
|
|
return nullptr;
|
|
}
|
|
|
|
Node* parseLeftHandSideExpression()
|
|
{
|
|
// assert(this->context->allowIn, 'callee of new expression always allow in keyword.');
|
|
ASSERT(this->context->allowIn);
|
|
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
Node* expr = (this->matchKeyword(Super) && this->context->inFunctionBody) ? this->parseSuper() :
|
|
this->inheritCoverGrammar(this->matchKeyword(New) ? &Parser::parseNewExpression : &Parser::parsePrimaryExpression);
|
|
|
|
while (true) {
|
|
if (this->match(LeftSquareBracket)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(LeftSquareBracket);
|
|
Node* property = this->isolateCoverGrammar(&Parser::parseExpression);
|
|
this->expect(RightSquareBracket);
|
|
expr = this->finalize(node, new MemberExpressionNode(expr, property, false));
|
|
|
|
} else if (this->match(Period)) {
|
|
this->context->isBindingElement = false;
|
|
this->context->isAssignmentTarget = true;
|
|
this->expect(Period);
|
|
IdentifierNode* property = this->parseIdentifierName();
|
|
expr = this->finalize(node, new MemberExpressionNode(expr, property, true));
|
|
|
|
} else if (this->lookahead->type == Token::TemplateToken && this->lookahead->head) {
|
|
Node* quasi = this->parseTemplateLiteral();
|
|
// TODO
|
|
// expr = this->finalize(node, new Node.TaggedTemplateExpression(expr, quasi));
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.4 Update Expressions
|
|
|
|
Node* parseUpdateExpression()
|
|
{
|
|
Node* expr;
|
|
ScannerResult* startToken = this->lookahead;
|
|
|
|
if (this->match(PlusPlus) || this->match(MinusMinus)) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
MetaNode node = this->startNode(startToken);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
if (this->context->strict && expr->type() == Identifier && this->scanner->isRestrictedWord(((IdentifierNode*)expr)->name())) {
|
|
this->tolerateError(Messages::StrictLHSPrefix);
|
|
}
|
|
if (!this->context->isAssignmentTarget) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
bool prefix = true;
|
|
|
|
if (isPlus) {
|
|
expr = this->finalize(node, new UpdateExpressionIncrementPrefixNode(expr));
|
|
} else {
|
|
expr = this->finalize(node, new UpdateExpressionDecrementPrefixNode(expr));
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
expr = this->inheritCoverGrammar(&Parser::parseLeftHandSideExpressionAllowCall);
|
|
if (!this->hasLineTerminator && this->lookahead->type == Token::PunctuatorToken) {
|
|
if (this->match(PlusPlus) || this->match(MinusMinus)) {
|
|
bool isPlus = this->match(PlusPlus);
|
|
if (this->context->strict && expr->isIdentifier() && this->scanner->isRestrictedWord(((IdentifierNode*)expr)->name())) {
|
|
this->tolerateError(Messages::StrictLHSPostfix);
|
|
}
|
|
if (!this->context->isAssignmentTarget) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
this->nextToken();
|
|
if (isPlus) {
|
|
expr = this->finalize(this->startNode(startToken), new UpdateExpressionIncrementPostfixNode(expr));
|
|
} else {
|
|
expr = this->finalize(this->startNode(startToken), new UpdateExpressionDecrementPostfixNode(expr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.5 Unary Operators
|
|
|
|
Node* parseUnaryExpression()
|
|
{
|
|
Node* expr;
|
|
|
|
if (this->match(Plus)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionPlusNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(Minus)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionMinusNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(Wave)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionBitwiseNotNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->match(ExclamationMark)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionLogicalNotNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->matchKeyword(Delete)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
Node* exprOld = expr;
|
|
expr = this->finalize(node, new UnaryExpressionDeleteNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
if (exprOld->isIdentifier()) {
|
|
this->tolerateError(Messages::StrictDelete);
|
|
}
|
|
} else if (this->matchKeyword(Void)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionVoidNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else if (this->matchKeyword(Typeof)) {
|
|
MetaNode node = this->startNode(this->lookahead);
|
|
ScannerResult* token = this->nextToken();
|
|
expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
expr = this->finalize(node, new UnaryExpressionTypeOfNode(expr));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
expr = this->parseUpdateExpression();
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Node* parseExponentiationExpression()
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
Node* expr = this->inheritCoverGrammar(&Parser::parseUnaryExpression);
|
|
// TODO
|
|
/*
|
|
if (expr->type != Syntax.UnaryExpression && this->match('**')) {
|
|
this->nextToken();
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
const left = expr;
|
|
const right = this->isolateCoverGrammar(this->parseExponentiationExpression);
|
|
expr = this->finalize(this->startNode(startToken), new Node.BinaryExpression('**', left, right));
|
|
}*/
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.6 Exponentiation Operators
|
|
// ECMA-262 12.7 Multiplicative Operators
|
|
// ECMA-262 12.8 Additive Operators
|
|
// ECMA-262 12.9 Bitwise Shift Operators
|
|
// ECMA-262 12.10 Relational Operators
|
|
// ECMA-262 12.11 Equality Operators
|
|
// ECMA-262 12.12 Binary Bitwise Operators
|
|
// ECMA-262 12.13 Binary Logical Operators
|
|
|
|
int binaryPrecedence(ScannerResult* token)
|
|
{
|
|
if (token->type == Token::PunctuatorToken) {
|
|
if (token->valuePunctuatorsKind == Substitution) {
|
|
return 0;
|
|
} else if (token->valuePunctuatorsKind == LogicalOr) {
|
|
return 1;
|
|
} else if (token->valuePunctuatorsKind == LogicalAnd) {
|
|
return 2;
|
|
} else if (token->valuePunctuatorsKind == BitwiseOr) {
|
|
return 3;
|
|
} else if (token->valuePunctuatorsKind == BitwiseXor) {
|
|
return 4;
|
|
} else if (token->valuePunctuatorsKind == BitwiseAnd) {
|
|
return 5;
|
|
} else if (token->valuePunctuatorsKind == Equal) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == NotEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == StrictEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == NotStrictEqual) {
|
|
return 6;
|
|
} else if (token->valuePunctuatorsKind == RightInequality) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftInequality) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == RightInequalityEqual) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftInequalityEqual) {
|
|
return 7;
|
|
} else if (token->valuePunctuatorsKind == LeftShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == RightShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == UnsignedRightShift) {
|
|
return 8;
|
|
} else if (token->valuePunctuatorsKind == Plus) {
|
|
return 9;
|
|
} else if (token->valuePunctuatorsKind == Minus) {
|
|
return 9;
|
|
} else if (token->valuePunctuatorsKind == Multiply) {
|
|
return 11;
|
|
} else if (token->valuePunctuatorsKind == Divide) {
|
|
return 11;
|
|
} else if (token->valuePunctuatorsKind == Mod) {
|
|
return 11;
|
|
}
|
|
return 0;
|
|
} else if (token->type == Token::KeywordToken) {
|
|
if (token->valueKeywordKind == In) {
|
|
return this->context->allowIn ? 7 : 0;
|
|
} else if (token->valueKeywordKind == InstanceofKeyword) {
|
|
return 7;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Node* parseBinaryExpression()
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
|
|
Node* expr = this->inheritCoverGrammar(&Parser::parseExponentiationExpression);
|
|
|
|
ScannerResult* token = this->lookahead;
|
|
int prec = this->binaryPrecedence(token);
|
|
if (prec > 0) {
|
|
this->nextToken();
|
|
|
|
token->prec = prec;
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
|
|
std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>> markers;
|
|
markers.push_back(startToken);
|
|
markers.push_back(this->lookahead);
|
|
Node* left = expr;
|
|
Node* right = this->isolateCoverGrammar(&Parser::parseExponentiationExpression);
|
|
|
|
std::vector<void*, gc_malloc_ignore_off_page_allocator<void*>> stack;
|
|
stack.push_back(left);
|
|
stack.push_back(token);
|
|
stack.push_back(right);
|
|
|
|
while (true) {
|
|
prec = this->binaryPrecedence(this->lookahead);
|
|
if (prec <= 0) {
|
|
break;
|
|
}
|
|
|
|
// Reduce: make a binary expression from the three topmost entries.
|
|
while ((stack.size() > 2) && (prec <= ((ScannerResult*)stack[stack.size() - 2])->prec)) {
|
|
right = (Node*)stack.back();
|
|
stack.pop_back();
|
|
PunctuatorsKind operator_ = ((ScannerResult*)stack.back())->valuePunctuatorsKind;
|
|
stack.pop_back();
|
|
left = (Node*)stack.back();
|
|
stack.pop_back();
|
|
markers.pop_back();
|
|
MetaNode node = this->startNode(markers.back());
|
|
stack.push_back(this->finalize(node, finishBinaryExpression(left, right, operator_)));
|
|
}
|
|
|
|
// Shift.
|
|
token = this->nextToken();
|
|
token->prec = prec;
|
|
stack.push_back(token);
|
|
markers.push_back(this->lookahead);
|
|
stack.push_back(this->isolateCoverGrammar(&Parser::parseExponentiationExpression));
|
|
}
|
|
|
|
// Final reduce to clean-up the stack.
|
|
size_t i = stack.size() - 1;
|
|
expr = (Node*)stack[i];
|
|
markers.pop_back();
|
|
while (i > 1) {
|
|
MetaNode node = this->startNode(markers.back());
|
|
expr = this->finalize(node, finishBinaryExpression((Node*)stack[i - 2], expr, ((ScannerResult*)stack[i - 1])->valuePunctuatorsKind));
|
|
i -= 2;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
Node* finishBinaryExpression(Node* left, Node* right, PunctuatorsKind oper)
|
|
{
|
|
// Additive Operators
|
|
Node* nd;
|
|
if (oper == Plus)
|
|
nd = new BinaryExpressionPlusNode(left, right);
|
|
else if (oper == Minus)
|
|
nd = new BinaryExpressionMinusNode(left, right);
|
|
|
|
// Bitwise Shift Operators
|
|
else if (oper == LeftShift)
|
|
nd = new BinaryExpressionLeftShiftNode(left, right);
|
|
else if (oper == RightShift)
|
|
nd = new BinaryExpressionSignedRightShiftNode(left, right);
|
|
else if (oper == UnsignedRightShift)
|
|
nd = new BinaryExpressionUnsignedRightShiftNode(left, right);
|
|
|
|
// Multiplicative Operators
|
|
else if (oper == Multiply)
|
|
nd = new BinaryExpressionMultiplyNode(left, right);
|
|
else if (oper == Divide)
|
|
nd = new BinaryExpressionDivisionNode(left, right);
|
|
else if (oper == Mod)
|
|
nd = new BinaryExpressionModNode(left, right);
|
|
|
|
// Relational Operators
|
|
else if (oper == LeftInequality)
|
|
nd = new BinaryExpressionLessThanNode(left, right);
|
|
else if (oper == RightInequality)
|
|
nd = new BinaryExpressionGreaterThanNode(left, right);
|
|
else if (oper == LeftInequalityEqual)
|
|
nd = new BinaryExpressionLessThanOrEqualNode(left, right);
|
|
else if (oper == RightInequalityEqual)
|
|
nd = new BinaryExpressionGreaterThanOrEqualNode(left, right);
|
|
|
|
// Equality Operators
|
|
else if (oper == Equal)
|
|
nd = new BinaryExpressionEqualNode(left, right);
|
|
else if (oper == NotEqual)
|
|
nd = new BinaryExpressionNotEqualNode(left, right);
|
|
else if (oper == StrictEqual)
|
|
nd = new BinaryExpressionStrictEqualNode(left, right);
|
|
else if (oper == NotStrictEqual)
|
|
nd = new BinaryExpressionNotStrictEqualNode(left, right);
|
|
|
|
// Binary Bitwise Operator
|
|
else if (oper == BitwiseAnd)
|
|
nd = new BinaryExpressionBitwiseAndNode(left, right);
|
|
else if (oper == BitwiseXor)
|
|
nd = new BinaryExpressionBitwiseXorNode(left, right);
|
|
else if (oper == BitwiseOr)
|
|
nd = new BinaryExpressionBitwiseOrNode(left, right);
|
|
else if (oper == LogicalOr)
|
|
nd = new BinaryExpressionLogicalOrNode(left, right);
|
|
else if (oper == LogicalAnd)
|
|
nd = new BinaryExpressionLogicalAndNode(left, right);
|
|
else if (oper == InPunctuator)
|
|
nd = new BinaryExpressionInNode(left, right);
|
|
else if (oper == InstanceOfPunctuator)
|
|
nd = new BinaryExpressionInstanceOfNode(left, right);
|
|
// TODO
|
|
else
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
|
|
return nd;
|
|
}
|
|
|
|
// ECMA-262 12.14 Conditional Operator
|
|
|
|
Node* parseConditionalExpression()
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
|
|
Node* expr = this->inheritCoverGrammar(&Parser::parseBinaryExpression);
|
|
if (this->match(GuessMark)) {
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = true;
|
|
Node* consequent = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
this->expect(Colon);
|
|
Node* alternate = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
expr = this->finalize(this->startNode(startToken), new ConditionalExpressionNode(expr, consequent, alternate));
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.15 Assignment Operators
|
|
|
|
void checkPatternParam(ParseParameterOptions options, Node* param)
|
|
{
|
|
switch (param->type()) {
|
|
case Identifier:
|
|
this->validateParam(options, nodeExtraInfo.find(((IdentifierNode*)param))->second, ((IdentifierNode*)param)->name());
|
|
break;
|
|
case RestElement:
|
|
this->checkPatternParam(options, ((RestElementNode*)param)->argument());
|
|
break;
|
|
// case AssignmentPattern:
|
|
case AssignmentExpression:
|
|
case AssignmentExpressionBitwiseAnd:
|
|
case AssignmentExpressionBitwiseOr:
|
|
case AssignmentExpressionBitwiseXor:
|
|
case AssignmentExpressionDivision:
|
|
case AssignmentExpressionLeftShift:
|
|
case AssignmentExpressionMinus:
|
|
case AssignmentExpressionMod:
|
|
case AssignmentExpressionMultiply:
|
|
case AssignmentExpressionPlus:
|
|
case AssignmentExpressionSignedRightShift:
|
|
case AssignmentExpressionUnsignedRightShift:
|
|
case AssignmentExpressionSimple:
|
|
this->checkPatternParam(options, ((AssignmentExpressionSimpleNode*)param)->left());
|
|
break;
|
|
default:
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// TODO
|
|
/*
|
|
case Syntax.ArrayPattern:
|
|
for (let i = 0; i < param.elements.length; i++) {
|
|
if (param.elements[i] !== null) {
|
|
this->checkPatternParam(options, param.elements[i]);
|
|
}
|
|
}
|
|
break;
|
|
case Syntax.YieldExpression:
|
|
break;
|
|
default:
|
|
RELEASE_ASSERT(param->type() == Object)
|
|
assert(param.type === Syntax.ObjectPattern, 'Invalid type');
|
|
for (let i = 0; i < param.properties.length; i++) {
|
|
this->checkPatternParam(options, param.properties[i].value);
|
|
}
|
|
break;
|
|
*/
|
|
}
|
|
}
|
|
/*
|
|
reinterpretAsCoverFormalsList(expr) {
|
|
let params = [expr];
|
|
let options;
|
|
|
|
switch (expr.type) {
|
|
case Syntax.Identifier:
|
|
break;
|
|
case ArrowParameterPlaceHolder:
|
|
params = expr.params;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
options = {
|
|
paramSet: {}
|
|
};
|
|
|
|
for (let i = 0; i < params.length; ++i) {
|
|
const param = params[i];
|
|
if (param.type === Syntax.AssignmentPattern) {
|
|
if (param.right.type === Syntax.YieldExpression) {
|
|
if (param.right.argument) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
param.right.type = Syntax.Identifier;
|
|
param.right.name = 'yield';
|
|
delete param.right.argument;
|
|
delete param.right.delegate;
|
|
}
|
|
}
|
|
this->checkPatternParam(options, param);
|
|
params[i] = param;
|
|
}
|
|
|
|
if (this->context.strict || !this->context.allowYield) {
|
|
for (let i = 0; i < params.length; ++i) {
|
|
const param = params[i];
|
|
if (param.type === Syntax.YieldExpression) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (options.message === Messages.StrictParamDupe) {
|
|
const token = this->context.strict ? options.stricted : options.firstRestricted;
|
|
this->throwUnexpectedToken(token, options.message);
|
|
}
|
|
|
|
return {
|
|
params: params,
|
|
stricted: options.stricted,
|
|
firstRestricted: options.firstRestricted,
|
|
message: options.message
|
|
};
|
|
}
|
|
*/
|
|
Node* parseAssignmentExpression()
|
|
{
|
|
Node* expr;
|
|
|
|
if (!this->context->allowYield && this->matchKeyword(Yield)) {
|
|
expr = this->parseYieldExpression();
|
|
} else {
|
|
ScannerResult* startToken = this->lookahead;
|
|
ScannerResult* token = startToken;
|
|
expr = this->parseConditionalExpression();
|
|
/*
|
|
if (expr->type() == ArrowParameterPlaceHolder || this->match('=>')) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// ECMA-262 14.2 Arrow Function Definitions
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
const list = this->reinterpretAsCoverFormalsList(expr);
|
|
|
|
if (list) {
|
|
if (this->hasLineTerminator) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
}
|
|
this->context->firstCoverInitializedNameError = null;
|
|
|
|
const previousStrict = this->context->strict;
|
|
const previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = true;
|
|
|
|
const node = this->startNode(startToken);
|
|
this->expect('=>');
|
|
const body = this->match('{') ? this->parseFunctionSourceElements() :
|
|
this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
const expression = body.type !== Syntax.BlockStatement;
|
|
|
|
if (this->context->strict && list.firstRestricted) {
|
|
this->throwUnexpectedToken(list.firstRestricted, list.message);
|
|
}
|
|
if (this->context->strict && list.stricted) {
|
|
this->tolerateUnexpectedToken(list.stricted, list.message);
|
|
}
|
|
expr = this->finalize(node, new Node.ArrowFunctionExpression(list.params, body, expression));
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
}
|
|
|
|
} else {
|
|
if (this->matchAssign()) {
|
|
if (!this->context->isAssignmentTarget) {
|
|
this->tolerateError(Messages.InvalidLHSInAssignment);
|
|
}
|
|
|
|
if (this->context->strict && expr.type === Syntax.Identifier) {
|
|
const id = <Node.Identifier>(expr);
|
|
if (this->scanner.isRestrictedWord(id.name)) {
|
|
this->tolerateUnexpectedToken(token, Messages.StrictLHSAssignment);
|
|
}
|
|
if (this->scanner.isStrictModeReservedWord(id.name)) {
|
|
this->tolerateUnexpectedToken(token, Messages.StrictReservedWord);
|
|
}
|
|
}
|
|
|
|
if (!this->match('=')) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
this->reinterpretExpressionAsPattern(expr);
|
|
}
|
|
|
|
token = this->nextToken();
|
|
const right = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
expr = this->finalize(this->startNode(startToken), new Node.AssignmentExpression(token.value, expr, right));
|
|
this->context->firstCoverInitializedNameError = null;
|
|
}
|
|
}*/
|
|
if (this->matchAssign()) {
|
|
if (!this->context->isAssignmentTarget) {
|
|
this->tolerateError(Messages::InvalidLHSInAssignment);
|
|
}
|
|
|
|
if (this->context->strict && expr->type() == Identifier) {
|
|
IdentifierNode* id = (IdentifierNode*)(expr);
|
|
if (this->scanner->isRestrictedWord(id->name())) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictLHSAssignment);
|
|
}
|
|
if (this->scanner->isStrictModeReservedWord(id->name())) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}
|
|
}
|
|
|
|
if (!this->match(Substitution)) {
|
|
this->context->isAssignmentTarget = false;
|
|
this->context->isBindingElement = false;
|
|
} else {
|
|
// TODO
|
|
// this->reinterpretExpressionAsPattern(expr);
|
|
}
|
|
|
|
token = this->nextToken();
|
|
Node* right = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
// Node* expr;
|
|
if (token->valuePunctuatorsKind == Substitution) {
|
|
expr = new AssignmentExpressionSimpleNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == PlusEqual) {
|
|
expr = new AssignmentExpressionPlusNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == MinusEqual) {
|
|
expr = new AssignmentExpressionMinusNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == MultiplyEqual) {
|
|
expr = new AssignmentExpressionMultiplyNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == DivideEqual) {
|
|
expr = new AssignmentExpressionDivisionNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == ModEqual) {
|
|
expr = new AssignmentExpressionModNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == LeftShiftEqual) {
|
|
expr = new AssignmentExpressionLeftShiftNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == RightShiftEqual) {
|
|
expr = new AssignmentExpressionSignedRightShiftNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == UnsignedRightShiftEqual) {
|
|
expr = new AssignmentExpressionUnsignedShiftNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == BitwiseXorEqual) {
|
|
expr = new AssignmentExpressionBitwiseXorNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == BitwiseAndEqual) {
|
|
expr = new AssignmentExpressionBitwiseAndNode(expr, right);
|
|
} else if (token->valuePunctuatorsKind == BitwiseOrEqual) {
|
|
expr = new AssignmentExpressionBitwiseOrNode(expr, right);
|
|
} else {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
expr = this->finalize(this->startNode(startToken), expr);
|
|
this->context->firstCoverInitializedNameError = nullptr;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 12.16 Comma Operator
|
|
|
|
Node* parseExpression()
|
|
{
|
|
ScannerResult* startToken = this->lookahead;
|
|
Node* expr = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector expressions;
|
|
expressions.push_back(expr);
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (!this->match(Comma)) {
|
|
break;
|
|
}
|
|
this->nextToken();
|
|
expressions.push_back(this->isolateCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
|
|
expr = this->finalize(this->startNode(startToken), new SequenceExpressionNode(std::move(expressions)));
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// ECMA-262 13.2 Block
|
|
|
|
StatementNode* parseStatementListItem()
|
|
{
|
|
StatementNode* statement = nullptr;
|
|
this->context->isAssignmentTarget = true;
|
|
this->context->isBindingElement = true;
|
|
if (this->lookahead->type == KeywordToken) {
|
|
switch (this->lookahead->valueKeywordKind) {
|
|
case Function:
|
|
statement = this->parseFunctionDeclaration();
|
|
break;
|
|
default:
|
|
statement = this->parseStatement();
|
|
break;
|
|
}
|
|
} else {
|
|
statement = this->parseStatement();
|
|
}
|
|
/*
|
|
if (this->lookahead.type === Token.Keyword) {
|
|
switch (this->lookahead.value) {
|
|
case 'export':
|
|
if (this->sourceType !== 'module') {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.IllegalExportDeclaration);
|
|
}
|
|
statement = this->parseExportDeclaration();
|
|
break;
|
|
case 'import':
|
|
if (this->sourceType !== 'module') {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.IllegalImportDeclaration);
|
|
}
|
|
statement = this->parseImportDeclaration();
|
|
break;
|
|
case 'const':
|
|
statement = this->parseLexicalDeclaration({ inFor: false });
|
|
break;
|
|
case 'function':
|
|
statement = this->parseFunctionDeclaration();
|
|
break;
|
|
case 'class':
|
|
statement = this->parseClassDeclaration();
|
|
break;
|
|
case 'let':
|
|
statement = this->isLexicalDeclaration() ? this->parseLexicalDeclaration({ inFor: false }) : this->parseStatement();
|
|
break;
|
|
default:
|
|
statement = this->parseStatement();
|
|
break;
|
|
}
|
|
} else {
|
|
statement = this->parseStatement();
|
|
}*/
|
|
|
|
return statement;
|
|
}
|
|
|
|
BlockStatementNode* parseBlock()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
StatementNodeVector block;
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
block.push_back(this->parseStatementListItem());
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
return this->finalize(node, new BlockStatementNode(std::move(block)));
|
|
}
|
|
/*
|
|
// ECMA-262 13.3.1 Let and Const Declarations
|
|
|
|
parseLexicalBinding(kind: string, options): Node.VariableDeclarator {
|
|
const node = this->createNode();
|
|
let params = [];
|
|
const id = this->parsePattern(params, kind);
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context.strict && id.type === Syntax.Identifier) {
|
|
if (this->scanner.isRestrictedWord((<Node.Identifier>(id)).name)) {
|
|
this->tolerateError(Messages.StrictVarName);
|
|
}
|
|
}
|
|
|
|
let init = null;
|
|
if (kind === 'const') {
|
|
if (!this->matchKeyword('in') && !this->matchContextualKeyword('of')) {
|
|
this->expect('=');
|
|
init = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
}
|
|
} else if ((!options.inFor && id.type !== Syntax.Identifier) || this->match('=')) {
|
|
this->expect('=');
|
|
init = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
}
|
|
|
|
return this->finalize(node, new Node.VariableDeclarator(id, init));
|
|
}
|
|
|
|
parseBindingList(kind: string, options): Node.VariableDeclarator[] {
|
|
let list = [this->parseLexicalBinding(kind, options)];
|
|
|
|
while (this->match(',')) {
|
|
this->nextToken();
|
|
list.push(this->parseLexicalBinding(kind, options));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
isLexicalDeclaration(): boolean {
|
|
const previousIndex = this->scanner.index;
|
|
const previousLineNumber = this->scanner.lineNumber;
|
|
const previousLineStart = this->scanner.lineStart;
|
|
this->collectComments();
|
|
const next = <any>this->scanner.lex();
|
|
this->scanner.index = previousIndex;
|
|
this->scanner.lineNumber = previousLineNumber;
|
|
this->scanner.lineStart = previousLineStart;
|
|
|
|
return (next.type === Token.Identifier) ||
|
|
(next.type === Token.Punctuator && next.value === '[') ||
|
|
(next.type === Token.Punctuator && next.value === '{') ||
|
|
(next.type === Token.Keyword && next.value === 'let') ||
|
|
(next.type === Token.Keyword && next.value === 'yield');
|
|
}
|
|
|
|
parseLexicalDeclaration(options): Node.VariableDeclaration {
|
|
const node = this->createNode();
|
|
const kind = this->nextToken().value;
|
|
assert(kind === 'let' || kind === 'const', 'Lexical declaration must be either let or const');
|
|
|
|
const declarations = this->parseBindingList(kind, options);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new Node.VariableDeclaration(declarations, kind));
|
|
}
|
|
|
|
// ECMA-262 13.3.3 Destructuring Binding Patterns
|
|
|
|
parseBindingRestElement(params, kind: string): Node.RestElement {
|
|
const node = this->createNode();
|
|
this->expect('...');
|
|
params.push(this->lookahead);
|
|
const arg = this->parseVariableIdentifier(kind);
|
|
return this->finalize(node, new Node.RestElement(arg));
|
|
}
|
|
|
|
|
|
parseArrayPattern(params, kind: string): Node.ArrayPattern {
|
|
const node = this->createNode();
|
|
|
|
this->expect('[');
|
|
const elements: Node.ArrayPatternElement[] = [];
|
|
while (!this->match(']')) {
|
|
if (this->match(',')) {
|
|
this->nextToken();
|
|
elements.push(null);
|
|
} else {
|
|
if (this->match('...')) {
|
|
elements.push(this->parseBindingRestElement(params, kind));
|
|
break;
|
|
} else {
|
|
elements.push(this->parsePatternWithDefault(params, kind));
|
|
}
|
|
if (!this->match(']')) {
|
|
this->expect(',');
|
|
}
|
|
}
|
|
|
|
}
|
|
this->expect(']');
|
|
|
|
return this->finalize(node, new Node.ArrayPattern(elements));
|
|
}
|
|
|
|
parsePropertyPattern(params, kind: string): Node.Property {
|
|
const node = this->createNode();
|
|
|
|
let computed = false;
|
|
let shorthand = false;
|
|
const method = false;
|
|
|
|
let key: Node.PropertyKey;
|
|
let value: Node.PropertyValue;
|
|
|
|
if (this->lookahead.type === Token.Identifier) {
|
|
const keyToken = this->lookahead;
|
|
key = this->parseVariableIdentifier();
|
|
const init = this->finalize(node, new Node.Identifier(keyToken.value));
|
|
if (this->match('=')) {
|
|
params.push(keyToken);
|
|
shorthand = true;
|
|
this->nextToken();
|
|
const expr = this->parseAssignmentExpression();
|
|
value = this->finalize(this->startNode(keyToken), new Node.AssignmentPattern(init, expr));
|
|
} else if (!this->match(':')) {
|
|
params.push(keyToken);
|
|
shorthand = true;
|
|
value = init;
|
|
} else {
|
|
this->expect(':');
|
|
value = this->parsePatternWithDefault(params, kind);
|
|
}
|
|
} else {
|
|
computed = this->match('[');
|
|
key = this->parseObjectPropertyKey();
|
|
this->expect(':');
|
|
value = this->parsePatternWithDefault(params, kind);
|
|
}
|
|
|
|
return this->finalize(node, new Node.Property('init', key, computed, value, method, shorthand));
|
|
}
|
|
|
|
parseObjectPattern(params, kind: string): Node.ObjectPattern {
|
|
const node = this->createNode();
|
|
const properties: Node.Property[] = [];
|
|
|
|
this->expect('{');
|
|
while (!this->match('}')) {
|
|
properties.push(this->parsePropertyPattern(params, kind));
|
|
if (!this->match('}')) {
|
|
this->expect(',');
|
|
}
|
|
}
|
|
this->expect('}');
|
|
|
|
return this->finalize(node, new Node.ObjectPattern(properties));
|
|
}
|
|
|
|
parsePattern(params, kind?: string): Node.BindingIdentifier | Node.BindingPattern {
|
|
let pattern;
|
|
|
|
if (this->match('[')) {
|
|
pattern = this->parseArrayPattern(params, kind);
|
|
} else if (this->match('{')) {
|
|
pattern = this->parseObjectPattern(params, kind);
|
|
} else {
|
|
if (this->matchKeyword('let') && (kind === 'const' || kind === 'let')) {
|
|
this->tolerateUnexpectedToken(this->lookahead, Messages.UnexpectedToken);
|
|
}
|
|
params.push(this->lookahead);
|
|
pattern = this->parseVariableIdentifier(kind);
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
|
|
parsePatternWithDefault(params, kind?: string): Node.AssignmentPattern | Node.BindingIdentifier | Node.BindingPattern {
|
|
const startToken = this->lookahead;
|
|
|
|
let pattern = this->parsePattern(params, kind);
|
|
if (this->match('=')) {
|
|
this->nextToken();
|
|
const previousAllowYield = this->context.allowYield;
|
|
this->context.allowYield = true;
|
|
const right = this->isolateCoverGrammar(this->parseAssignmentExpression);
|
|
this->context.allowYield = previousAllowYield;
|
|
pattern = this->finalize(this->startNode(startToken), new Node.AssignmentPattern(pattern, right));
|
|
}
|
|
|
|
return pattern;
|
|
}
|
|
*/
|
|
|
|
// ECMA-262 13.3.2 Variable Statement
|
|
IdentifierNode* parseVariableIdentifier(String* kind = String::emptyString)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ScannerResult* token = this->nextToken();
|
|
if (token->type == Token::KeywordToken && token->valueKeywordKind == Yield) {
|
|
if (this->context->strict) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
}if (!this->context->allowYield) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
} else if (token->type != Token::IdentifierToken) {
|
|
if (this->context->strict && token->type == Token::KeywordToken && this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictReservedWord);
|
|
} else {
|
|
if (this->context->strict || token->valueString != "let" || (!kind->equals("var"))) {
|
|
this->throwUnexpectedToken(token);
|
|
}
|
|
}
|
|
} else if (this->sourceType == Module && token->type == Token::IdentifierToken && token->valueString == "Await") {
|
|
this->tolerateUnexpectedToken(token);
|
|
}
|
|
|
|
return this->finalize(node, finishIdentifier(token, true));
|
|
}
|
|
|
|
struct DeclarationOptions {
|
|
bool inFor;
|
|
};
|
|
|
|
VariableDeclaratorNode* parseVariableDeclaration(DeclarationOptions& options)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>> params;
|
|
Node* id = this->parsePattern(params, new ASCIIString("var"));
|
|
|
|
// ECMA-262 12.2.1
|
|
if (this->context->strict && id->type() == Identifier) {
|
|
if (this->scanner->isRestrictedWord(((IdentifierNode*)id)->name())) {
|
|
this->tolerateError(Messages::StrictVarName);
|
|
}
|
|
}
|
|
|
|
if (id->type() == Identifier) {
|
|
this->scopeContexts.back()->insertName(((IdentifierNode*)id)->name());
|
|
}
|
|
|
|
Node* init = nullptr;
|
|
if (this->match(Substitution)) {
|
|
this->nextToken();
|
|
init = this->isolateCoverGrammar(&Parser::parseAssignmentExpression);
|
|
} else if (id->type() != Identifier && !options.inFor) {
|
|
this->expect(Substitution);
|
|
}
|
|
|
|
return this->finalize(node, new VariableDeclaratorNode(id, init));
|
|
}
|
|
|
|
VariableDeclaratorVector parseVariableDeclarationList(DeclarationOptions& options)
|
|
{
|
|
DeclarationOptions opt;
|
|
opt.inFor = options.inFor;
|
|
|
|
VariableDeclaratorVector list;
|
|
list.push_back(this->parseVariableDeclaration(opt));
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
list.push_back(this->parseVariableDeclaration(opt));
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
VariableDeclarationNode* parseVariableStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Var);
|
|
DeclarationOptions opt;
|
|
opt.inFor = false;
|
|
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new VariableDeclarationNode(std::move(declarations)/*, 'var'*/));
|
|
}
|
|
|
|
// ECMA-262 13.4 Empty Statement
|
|
|
|
EmptyStatementNode* parseEmptyStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(SemiColon);
|
|
return this->finalize(node, new EmptyStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.5 Expression Statement
|
|
|
|
ExpressionStatementNode* parseExpressionStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
Node* expr = this->parseExpression();
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, new ExpressionStatementNode(expr));
|
|
}
|
|
|
|
// ECMA-262 13.6 If statement
|
|
|
|
IfStatementNode* parseIfStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
Node* consequent = nullptr;
|
|
Node* alternate = nullptr;
|
|
|
|
this->expectKeyword(If);
|
|
this->expect(LeftParenthesis);
|
|
Node* test = this->parseExpression();
|
|
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
consequent = this->finalize(this->createNode(), new Node.EmptyStatement());
|
|
*/
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
consequent = this->parseStatement();
|
|
if (this->matchKeyword(Else)) {
|
|
this->nextToken();
|
|
alternate = this->parseStatement();
|
|
}
|
|
}
|
|
|
|
return this->finalize(node, new IfStatementNode(test, consequent, alternate));
|
|
}
|
|
|
|
// ECMA-262 13.7.2 The do-while Statement
|
|
|
|
DoWhileStatementNode* parseDoWhileStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Do);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
Node* body = this->parseStatement();
|
|
this->context->inIteration = previousInIteration;
|
|
|
|
this->expectKeyword(While);
|
|
this->expect(LeftParenthesis);
|
|
Node* test = this->parseExpression();
|
|
this->expect(RightParenthesis);
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
}
|
|
|
|
return this->finalize(node, new DoWhileStatementNode(test, body));
|
|
}
|
|
|
|
// ECMA-262 13.7.3 The while Statement
|
|
|
|
WhileStatementNode* parseWhileStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
Node* body;
|
|
|
|
this->expectKeyword(While);
|
|
this->expect(LeftParenthesis);
|
|
Node* test = this->parseExpression();
|
|
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
body = this->finalize(this->createNode(), new EmptyStatementNode());
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
body = this->parseStatement();
|
|
this->context->inIteration = previousInIteration;
|
|
}
|
|
|
|
return this->finalize(node, new WhileStatementNode(test, body));
|
|
}
|
|
|
|
// ECMA-262 13.7.4 The for Statement
|
|
// ECMA-262 13.7.5 The for-in and for-of Statements
|
|
|
|
StatementNode* parseForStatement()
|
|
{
|
|
Node* init = nullptr;
|
|
Node* test = nullptr;
|
|
Node* update = nullptr;
|
|
bool forIn = true;
|
|
Node* left = nullptr;
|
|
Node* right = nullptr;
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(For);
|
|
this->expect(LeftParenthesis);
|
|
|
|
if (this->match(SemiColon)) {
|
|
this->nextToken();
|
|
} else {
|
|
if (this->matchKeyword(Var)) {
|
|
MetaNode metaInit = this->createNode();
|
|
this->nextToken();
|
|
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
DeclarationOptions opt;
|
|
opt.inFor = true;
|
|
VariableDeclaratorVector declarations = this->parseVariableDeclarationList(opt);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarations.size() == 1 && this->matchKeyword(In)) {
|
|
VariableDeclaratorNode* decl = declarations[0];
|
|
// if (decl->init() && (decl.id.type === Syntax.ArrayPattern || decl.id.type === Syntax.ObjectPattern || this->context->strict)) {
|
|
if (decl->init() && (decl->id()->type() == ArrayExpression || decl->id()->type() == ObjectExpression || this->context->strict)) {
|
|
this->tolerateError(Messages::ForInOfLoopInitializer, new ASCIIString("for-in"));
|
|
}
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations)/*, 'var'*/));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = nullptr;
|
|
} else if (declarations.size() == 1 && declarations[0]->init() == nullptr && this->matchContextualKeyword(Of)) {
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations)/*, 'var'*/));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = nullptr;
|
|
forIn = false;
|
|
} else {
|
|
init = this->finalize(metaInit, new VariableDeclarationNode(std::move(declarations)/*, 'var'*/));
|
|
this->expect(SemiColon);
|
|
}
|
|
} else if (this->matchKeyword(Const) || this->matchKeyword(Let)) {
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
init = this->createNode();
|
|
const kind = this->nextToken().value;
|
|
|
|
if (!this->context->strict && this->lookahead.value === 'in') {
|
|
init = this->finalize(init, new Node.Identifier(kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = null;
|
|
} else {
|
|
const previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
const declarations = this->parseBindingList(kind, { inFor: true });
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (declarations.length === 1 && declarations[0].init === null && this->matchKeyword('in')) {
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = null;
|
|
} else if (declarations.length === 1 && declarations[0].init === null && this->matchContextualKeyword('of')) {
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
this->nextToken();
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = null;
|
|
forIn = false;
|
|
} else {
|
|
this->consumeSemicolon();
|
|
init = this->finalize(init, new Node.VariableDeclaration(declarations, kind));
|
|
}
|
|
}
|
|
*/
|
|
} else {
|
|
ScannerResult* initStartToken = this->lookahead;
|
|
bool previousAllowIn = this->context->allowIn;
|
|
this->context->allowIn = false;
|
|
init = this->inheritCoverGrammar(&Parser::parseAssignmentExpression);
|
|
this->context->allowIn = previousAllowIn;
|
|
|
|
if (this->matchKeyword(In)) {
|
|
if (!this->context->isAssignmentTarget || init->type() == AssignmentExpression) {
|
|
this->tolerateError(Messages::InvalidLHSInForIn);
|
|
}
|
|
|
|
this->nextToken();
|
|
// this->reinterpretExpressionAsPattern(init);
|
|
left = init;
|
|
right = this->parseExpression();
|
|
init = nullptr;
|
|
} else if (this->matchContextualKeyword(Of)) {
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
if (!this->context->isAssignmentTarget || init.type === Syntax.AssignmentExpression) {
|
|
this->tolerateError(Messages.InvalidLHSInForLoop);
|
|
}
|
|
|
|
this->nextToken();
|
|
this->reinterpretExpressionAsPattern(init);
|
|
left = init;
|
|
right = this->parseAssignmentExpression();
|
|
init = null;
|
|
forIn = false;
|
|
*/
|
|
} else {
|
|
if (this->match(Comma)) {
|
|
ExpressionNodeVector initSeq;
|
|
initSeq.push_back(init);
|
|
while (this->match(Comma)) {
|
|
this->nextToken();
|
|
initSeq.push_back(this->isolateCoverGrammar(&Parser::parseAssignmentExpression));
|
|
}
|
|
init = this->finalize(this->startNode(initStartToken), new SequenceExpressionNode(std::move(initSeq)));
|
|
}
|
|
this->expect(SemiColon);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (left == nullptr) {
|
|
if (!this->match(SemiColon)) {
|
|
test = this->parseExpression();
|
|
}
|
|
this->expect(SemiColon);
|
|
if (!this->match(RightParenthesis)) {
|
|
update = this->parseExpression();
|
|
}
|
|
}
|
|
|
|
Node* body = nullptr;
|
|
if (!this->match(RightParenthesis) && this->config.tolerant) {
|
|
this->tolerateUnexpectedToken(this->nextToken());
|
|
body = this->finalize(this->createNode(), new EmptyStatementNode());
|
|
} else {
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInIteration = this->context->inIteration;
|
|
this->context->inIteration = true;
|
|
body = this->isolateCoverGrammar(&Parser::parseStatement);
|
|
this->context->inIteration = previousInIteration;
|
|
}
|
|
|
|
if (left == nullptr) {
|
|
return this->finalize(node, new ForStatementNode(init, test, update, body));
|
|
} else {
|
|
if (forIn) {
|
|
return this->finalize(node, new ForInStatementNode(left, right, body, false));
|
|
} else {
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
// return this->finalize(node, new Node.ForOfStatement(left, right, body));
|
|
}
|
|
}
|
|
/*
|
|
return (typeof left === 'undefined') ?
|
|
this->finalize(node, new Node.ForStatement(init, test, update, body)) :
|
|
forIn ? this->finalize(node, new Node.ForInStatement(left, right, body)) :
|
|
this->finalize(node, new Node.ForOfStatement(left, right, body));
|
|
|
|
*/
|
|
}
|
|
|
|
void removeLabel(String* label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i ++) {
|
|
if (this->context->labelSet[i].first->equals(label)) {
|
|
this->context->labelSet.erase(this->context->labelSet.begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
}
|
|
|
|
bool hasLabel(String* label)
|
|
{
|
|
for (size_t i = 0; i < this->context->labelSet.size(); i ++) {
|
|
if (this->context->labelSet[i].first->equals(label)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// ECMA-262 13.8 The continue statement
|
|
|
|
Node* parseContinueStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Continue);
|
|
|
|
IdentifierNode* label = nullptr;
|
|
if (this->lookahead->type == IdentifierToken && !this->hasLineTerminator) {
|
|
label = this->parseVariableIdentifier();
|
|
|
|
if (!hasLabel(label->name().string())) {
|
|
this->throwError(Messages::UnknownLabel, label->name().string());
|
|
}
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
if (label == nullptr && !this->context->inIteration) {
|
|
this->throwError(Messages::IllegalContinue);
|
|
}
|
|
|
|
if (label)
|
|
return this->finalize(node, new ContinueLabelStatementNode(label->name().string()));
|
|
else
|
|
return this->finalize(node, new ContinueStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.9 The break statement
|
|
|
|
Node* parseBreakStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Break);
|
|
|
|
IdentifierNode* label = nullptr;
|
|
if (this->lookahead->type == IdentifierToken && !this->hasLineTerminator) {
|
|
label = this->parseVariableIdentifier();
|
|
|
|
if (!hasLabel(label->name().string())) {
|
|
this->throwError(Messages::UnknownLabel, label->name().string());
|
|
}
|
|
|
|
}
|
|
|
|
this->consumeSemicolon();
|
|
if (label == nullptr && !this->context->inIteration && !this->context->inSwitch) {
|
|
this->throwError(Messages::IllegalBreak);
|
|
}
|
|
|
|
if (label)
|
|
return this->finalize(node, new BreakLabelStatementNode(label->name().string()));
|
|
else
|
|
return this->finalize(node, new BreakStatementNode());
|
|
}
|
|
|
|
// ECMA-262 13.10 The return statement
|
|
|
|
Node* parseReturnStatement()
|
|
{
|
|
if (!this->context->inFunctionBody) {
|
|
this->tolerateError(Messages::IllegalReturn);
|
|
}
|
|
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Return);
|
|
|
|
bool hasArgument = !this->match(SemiColon) && !this->match(RightBrace) &&
|
|
!this->hasLineTerminator && this->lookahead->type != EOFToken;
|
|
Node* argument = nullptr;
|
|
if (hasArgument) {
|
|
argument = this->parseExpression();
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new ReturnStatmentNode(argument));
|
|
}
|
|
|
|
// ECMA-262 13.11 The with statement
|
|
|
|
Node* parseWithStatement()
|
|
{
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
if (this->context->strict) {
|
|
this->tolerateError(Messages::StrictModeWith);
|
|
}
|
|
|
|
const node = this->createNode();
|
|
this->expectKeyword('with');
|
|
this->expect('(');
|
|
const object = this->parseExpression();
|
|
this->expect(')');
|
|
const body = this->parseStatement();
|
|
|
|
return this->finalize(node, new Node.WithStatement(object, body));
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 13.12 The switch statement
|
|
|
|
SwitchCaseNode* parseSwitchCase()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
Node* test;
|
|
if (this->matchKeyword(Default)) {
|
|
this->nextToken();
|
|
test = nullptr;
|
|
} else {
|
|
this->expectKeyword(Case);
|
|
test = this->parseExpression();
|
|
}
|
|
this->expect(Colon);
|
|
|
|
StatementNodeVector consequent;
|
|
while (true) {
|
|
if (this->match(RightBrace) || this->matchKeyword(Default) || this->matchKeyword(Case)) {
|
|
break;
|
|
}
|
|
consequent.push_back(this->parseStatementListItem());
|
|
}
|
|
|
|
return this->finalize(node, new SwitchCaseNode(test, std::move(consequent)));
|
|
}
|
|
|
|
SwitchStatementNode* parseSwitchStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Switch);
|
|
|
|
this->expect(LeftParenthesis);
|
|
Node* discriminant = this->parseExpression();
|
|
this->expect(RightParenthesis);
|
|
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
this->context->inSwitch = true;
|
|
|
|
StatementNodeVector casesA, casesB;
|
|
Node* deflt = nullptr;
|
|
bool defaultFound = false;
|
|
this->expect(LeftBrace);
|
|
while (true) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
SwitchCaseNode* clause = this->parseSwitchCase();
|
|
if (clause->isDefaultNode()) {
|
|
if (defaultFound) {
|
|
this->throwError(Messages::MultipleDefaultsInSwitch);
|
|
}
|
|
deflt = clause;
|
|
defaultFound = true;
|
|
} else {
|
|
if (defaultFound) {
|
|
casesA.push_back(clause);
|
|
} else {
|
|
casesB.push_back(clause);
|
|
}
|
|
}
|
|
}
|
|
this->expect(RightBrace);
|
|
|
|
this->context->inSwitch = previousInSwitch;
|
|
|
|
return this->finalize(node, new SwitchStatementNode(discriminant, std::move(casesA), deflt, std::move(casesB), false));
|
|
}
|
|
|
|
// ECMA-262 13.13 Labelled Statements
|
|
|
|
Node* parseLabelledStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
Node* expr = this->parseExpression();
|
|
|
|
StatementNode* statement;
|
|
if ((expr->type() == Identifier) && this->match(Colon)) {
|
|
this->nextToken();
|
|
|
|
IdentifierNode* id = (IdentifierNode*)expr;
|
|
if (hasLabel(id->name().string())) {
|
|
this->throwError(Messages::Redeclaration, new ASCIIString("Label"), id->name().string());
|
|
}
|
|
|
|
this->context->labelSet.push_back(std::make_pair(id->name().string(), true));
|
|
StatementNode* labeledBody = this->parseStatement();
|
|
removeLabel(id->name().string());
|
|
|
|
statement = new LabeledStatementNode(labeledBody, id->name().string());
|
|
} else {
|
|
this->consumeSemicolon();
|
|
statement = new ExpressionStatementNode(expr);
|
|
}
|
|
|
|
return this->finalize(node, statement);
|
|
}
|
|
|
|
// ECMA-262 13.14 The throw statement
|
|
|
|
Node* parseThrowStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Throw);
|
|
|
|
if (this->hasLineTerminator) {
|
|
this->throwError(Messages::NewlineAfterThrow);
|
|
}
|
|
|
|
Node* argument = this->parseExpression();
|
|
this->consumeSemicolon();
|
|
|
|
return this->finalize(node, new ThrowStatementNode(argument));
|
|
}
|
|
|
|
// ECMA-262 13.15 The try statement
|
|
|
|
CatchClauseNode* parseCatchClause()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
this->expectKeyword(Catch);
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
std::vector<ScannerResult*, gc_malloc_ignore_off_page_allocator<ScannerResult*>> params;
|
|
Node* param = this->parsePattern(params);
|
|
|
|
std::vector<String*, gc_malloc_ignore_off_page_allocator<String*>> paramMap;
|
|
for (size_t i = 0; i < params.size(); i ++) {
|
|
|
|
bool has = false;
|
|
for (size_t j = 0; j < paramMap.size(); j ++) {
|
|
if (paramMap[j]->equals(¶ms[i]->valueString)) {
|
|
has = true;
|
|
}
|
|
}
|
|
if (has) {
|
|
this->tolerateError(Messages::DuplicateBinding, ¶ms[i]->valueString);
|
|
} else {
|
|
paramMap.push_back(new StringView(params[i]->valueString));
|
|
}
|
|
}
|
|
|
|
if (this->context->strict && param->type() == Identifier) {
|
|
IdentifierNode* id = (IdentifierNode*)param;
|
|
if (this->scanner->isRestrictedWord(id->name())) {
|
|
this->tolerateError(Messages::StrictCatchVariable);
|
|
}
|
|
}
|
|
|
|
this->expect(RightParenthesis);
|
|
Node* body = this->parseBlock();
|
|
|
|
return this->finalize(node, new CatchClauseNode(param, nullptr, body));
|
|
}
|
|
|
|
BlockStatementNode* parseFinallyClause()
|
|
{
|
|
this->expectKeyword(Finally);
|
|
return this->parseBlock();
|
|
}
|
|
|
|
TryStatementNode* parseTryStatement()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Try);
|
|
|
|
BlockStatementNode* block = this->parseBlock();
|
|
CatchClauseNode* handler = this->matchKeyword(Catch) ? this->parseCatchClause() : nullptr;
|
|
BlockStatementNode* finalizer = this->matchKeyword(Finally) ? this->parseFinallyClause() : nullptr;
|
|
|
|
if (!handler && !finalizer) {
|
|
this->throwError(Messages::NoCatchOrFinally);
|
|
}
|
|
|
|
return this->finalize(node, new TryStatementNode(block, handler, CatchClauseNodeVector(), finalizer));
|
|
}
|
|
|
|
// ECMA-262 13.16 The debugger statement
|
|
|
|
StatementNode* parseDebuggerStatement()
|
|
{
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
const node = this->createNode();
|
|
this->expectKeyword('debugger');
|
|
this->consumeSemicolon();
|
|
return this->finalize(node, new Node.DebuggerStatement());
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 13 Statements
|
|
|
|
StatementNode* parseStatement()
|
|
{
|
|
StatementNode* statement = nullptr;
|
|
switch (this->lookahead->type) {
|
|
case Token::BooleanLiteralToken:
|
|
case Token::NullLiteralToken:
|
|
case Token::NumericLiteralToken:
|
|
case Token::StringLiteralToken:
|
|
case Token::TemplateToken:
|
|
case Token::RegularExpressionToken:
|
|
statement = this->parseExpressionStatement();
|
|
break;
|
|
|
|
case Token::PunctuatorToken:
|
|
{
|
|
PunctuatorsKind value = this->lookahead->valuePunctuatorsKind;
|
|
if (value == LeftBrace) {
|
|
statement = this->parseBlock();
|
|
} else if (value == LeftParenthesis) {
|
|
statement = this->parseExpressionStatement();
|
|
} else if (value == SemiColon) {
|
|
statement = this->parseEmptyStatement();
|
|
} else {
|
|
statement = this->parseExpressionStatement();
|
|
}
|
|
break;
|
|
}
|
|
case Token::IdentifierToken:
|
|
statement = asStatementNode(this->parseLabelledStatement());
|
|
break;
|
|
|
|
case Token::KeywordToken:
|
|
switch (this->lookahead->valueKeywordKind) {
|
|
case Break:
|
|
statement = asStatementNode(this->parseBreakStatement());
|
|
break;
|
|
case Continue:
|
|
statement = asStatementNode(this->parseContinueStatement());
|
|
break;
|
|
case Debugger:
|
|
statement = asStatementNode(this->parseDebuggerStatement());
|
|
break;
|
|
case Do:
|
|
statement = asStatementNode(this->parseDoWhileStatement());
|
|
break;
|
|
case For:
|
|
statement = asStatementNode(this->parseForStatement());
|
|
break;
|
|
case Function:
|
|
statement = asStatementNode(this->parseFunctionDeclaration());
|
|
break;
|
|
case If:
|
|
statement = asStatementNode(this->parseIfStatement());
|
|
break;
|
|
case Return:
|
|
statement = asStatementNode(this->parseReturnStatement());
|
|
break;
|
|
case Switch:
|
|
statement = asStatementNode(this->parseSwitchStatement());
|
|
break;
|
|
case Throw:
|
|
statement = asStatementNode(this->parseThrowStatement());
|
|
break;
|
|
case Try:
|
|
statement = asStatementNode(this->parseTryStatement());
|
|
break;
|
|
case Var:
|
|
statement = asStatementNode(this->parseVariableStatement());
|
|
break;
|
|
case While:
|
|
statement = asStatementNode(this->parseWhileStatement());
|
|
break;
|
|
case With:
|
|
statement = asStatementNode(this->parseWithStatement());
|
|
break;
|
|
default:
|
|
statement = asStatementNode(this->parseExpressionStatement());
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
this->throwUnexpectedToken(this->lookahead);
|
|
}
|
|
|
|
return statement;
|
|
}
|
|
|
|
// ECMA-262 14.1 Function Definition
|
|
|
|
BlockStatementNode* parseFunctionSourceElements()
|
|
{
|
|
MetaNode nodeStart = this->createNode();
|
|
|
|
this->expect(LeftBrace);
|
|
StatementNodeVector body = this->parseDirectivePrologues();
|
|
|
|
auto previousLabelSet = this->context->labelSet;
|
|
bool previousInIteration = this->context->inIteration;
|
|
bool previousInSwitch = this->context->inSwitch;
|
|
bool previousInFunctionBody = this->context->inFunctionBody;
|
|
|
|
this->context->labelSet.clear();
|
|
this->context->inIteration = false;
|
|
this->context->inSwitch = false;
|
|
this->context->inFunctionBody = true;
|
|
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
if (this->match(RightBrace)) {
|
|
break;
|
|
}
|
|
body.push_back(this->parseStatementListItem());
|
|
}
|
|
|
|
this->expect(RightBrace);
|
|
MetaNode nodeEnd = this->createNode();
|
|
|
|
this->context->labelSet = previousLabelSet;
|
|
this->context->inIteration = previousInIteration;
|
|
this->context->inSwitch = previousInSwitch;
|
|
this->context->inFunctionBody = previousInFunctionBody;
|
|
|
|
scopeContexts.back()->m_locStart.line = nodeStart.line;
|
|
scopeContexts.back()->m_locStart.column = nodeStart.column;
|
|
scopeContexts.back()->m_locStart.index = nodeStart.index;
|
|
|
|
scopeContexts.back()->m_locEnd.line = nodeEnd.line;
|
|
scopeContexts.back()->m_locEnd.column = nodeEnd.column;
|
|
scopeContexts.back()->m_locEnd.index = nodeEnd.index;
|
|
|
|
return this->finalize(nodeStart, new BlockStatementNode(std::move(body)));
|
|
}
|
|
|
|
FunctionDeclarationNode* parseFunctionDeclaration(bool identifierIsOptional = false)
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Function);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message = nullptr;
|
|
IdentifierNode* id = nullptr;
|
|
ScannerResult* firstRestricted = nullptr;
|
|
|
|
if (!identifierIsOptional || !this->match(LeftParenthesis)) {
|
|
ScannerResult* token = this->lookahead;
|
|
id = this->parseVariableIdentifier();
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = !isGenerator;
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(firstRestricted);
|
|
PatternNodeVector params = std::move(formalParameters.params);
|
|
ScannerResult* stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
this->scopeContexts.back()->insertName(id->name());
|
|
|
|
pushScopeContext(params, id->name());
|
|
extractNamesFromFunctionParams(params);
|
|
scopeContexts.back()->insertName(id->name());
|
|
|
|
bool previousStrict = this->context->strict;
|
|
BlockStatementNode* body = this->parseFunctionSourceElements();
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->tolerateUnexpectedToken(stricted, message);
|
|
}
|
|
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
FunctionDeclarationNode* fd = this->finalize(node, new FunctionDeclarationNode(id->name(), std::move(params), body, popScopeContext(node), isGenerator));
|
|
|
|
return fd;
|
|
}
|
|
|
|
FunctionExpressionNode* parseFunctionExpression()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expectKeyword(Function);
|
|
|
|
bool isGenerator = this->match(Multiply);
|
|
if (isGenerator) {
|
|
this->nextToken();
|
|
}
|
|
|
|
const char* message;
|
|
IdentifierNode* id = nullptr;
|
|
ScannerResult* firstRestricted;
|
|
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = !isGenerator;
|
|
|
|
if (!this->match(LeftParenthesis)) {
|
|
ScannerResult* token = this->lookahead;
|
|
id = (!this->context->strict && !isGenerator && this->matchKeyword(Yield)) ? this->parseIdentifierName() : this->parseVariableIdentifier();
|
|
if (this->context->strict) {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
this->tolerateUnexpectedToken(token, Messages::StrictFunctionName);
|
|
}
|
|
} else {
|
|
if (this->scanner->isRestrictedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictFunctionName;
|
|
} else if (this->scanner->isStrictModeReservedWord(token->valueString)) {
|
|
firstRestricted = token;
|
|
message = Messages::StrictReservedWord;
|
|
}
|
|
}
|
|
}
|
|
|
|
ParseFormalParametersResult formalParameters = this->parseFormalParameters(firstRestricted);
|
|
PatternNodeVector params = std::move(formalParameters.params);
|
|
ScannerResult* stricted = formalParameters.stricted;
|
|
firstRestricted = formalParameters.firstRestricted;
|
|
if (formalParameters.message) {
|
|
message = formalParameters.message;
|
|
}
|
|
|
|
pushScopeContext(params, id->name());
|
|
extractNamesFromFunctionParams(params);
|
|
scopeContexts.back()->insertName(id->name());
|
|
|
|
bool previousStrict = this->context->strict;
|
|
BlockStatementNode* body = this->parseFunctionSourceElements();
|
|
if (this->context->strict && firstRestricted) {
|
|
this->throwUnexpectedToken(firstRestricted, message);
|
|
}
|
|
if (this->context->strict && stricted) {
|
|
this->tolerateUnexpectedToken(stricted, message);
|
|
}
|
|
this->context->strict = previousStrict;
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new FunctionExpressionNode(id->name(), std::move(params), body, popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
// ECMA-262 14.1.1 Directive Prologues
|
|
|
|
Node* parseDirective()
|
|
{
|
|
ScannerResult* token = this->lookahead;
|
|
StringView directiveValue;
|
|
bool isLiteral = false;
|
|
|
|
MetaNode node = this->createNode();
|
|
Node* expr = this->parseExpression();
|
|
if (expr->type() == Literal) {
|
|
isLiteral = true;
|
|
size_t newStart = token->valueString.start() + 1;
|
|
size_t newEnd = token->valueString.end() - 1;
|
|
if (newStart <= newEnd && newEnd < token->valueString.end()) {
|
|
directiveValue = StringView(token->valueString.string(), newStart, newEnd);
|
|
}
|
|
}
|
|
this->consumeSemicolon();
|
|
|
|
if (isLiteral) {
|
|
return this->finalize(node, new DirectiveNode(asExpressionNode(expr), directiveValue));
|
|
} else {
|
|
return this->finalize(node, new ExpressionStatementNode(expr));
|
|
}
|
|
}
|
|
|
|
StatementNodeVector parseDirectivePrologues()
|
|
{
|
|
ScannerResult* firstRestricted = nullptr;
|
|
|
|
StatementNodeVector body;
|
|
while (true) {
|
|
ScannerResult* token = this->lookahead;
|
|
if (token->type != StringLiteralToken) {
|
|
break;
|
|
}
|
|
|
|
Node* statement = this->parseDirective();
|
|
body.push_back(statement);
|
|
|
|
if (statement->type() != Directive) {
|
|
break;
|
|
}
|
|
|
|
DirectiveNode* directive = (DirectiveNode*)statement;
|
|
if (directive->value().equals("use strict")) {
|
|
this->context->strict = true;
|
|
if (firstRestricted) {
|
|
this->tolerateUnexpectedToken(firstRestricted, Messages::StrictOctalLiteral);
|
|
}
|
|
} else {
|
|
if (!firstRestricted && token->octal) {
|
|
firstRestricted = token;
|
|
}
|
|
}
|
|
}
|
|
|
|
return body;
|
|
}
|
|
|
|
// ECMA-262 14.3 Method Definitions
|
|
|
|
FunctionExpressionNode* parseGetterMethod()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
this->expect(LeftParenthesis);
|
|
this->expect(RightParenthesis);
|
|
|
|
bool isGenerator = false;
|
|
// std::vector<Node*, gc_allocator<Node*>> params, ScannerResult* stricted, ScannerResult* firstRestricted, const char* message
|
|
ParseFormalParametersResult params(PatternNodeVector(), nullptr, nullptr, nullptr);
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
Node* method = this->parsePropertyMethod(params);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
extractNamesFromFunctionParams(params.params);
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(params.params), method, popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
FunctionExpressionNode* parseSetterMethod()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
|
|
ParseParameterOptions options;
|
|
|
|
bool isGenerator = false;
|
|
bool previousAllowYield = this->context->allowYield;
|
|
this->context->allowYield = false;
|
|
|
|
this->expect(LeftParenthesis);
|
|
if (this->match(RightParenthesis)) {
|
|
this->tolerateUnexpectedToken(this->lookahead);
|
|
} else {
|
|
this->parseFormalParameter(options);
|
|
}
|
|
this->expect(RightParenthesis);
|
|
|
|
ParseFormalParametersResult options2(std::move(options.params), options.stricted, options.firstRestricted, options.message);
|
|
Node* method = this->parsePropertyMethod(options2);
|
|
this->context->allowYield = previousAllowYield;
|
|
|
|
extractNamesFromFunctionParams(options.params);
|
|
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(options.params), method, popScopeContext(node), isGenerator));
|
|
}
|
|
|
|
FunctionExpressionNode* parseGeneratorMethod()
|
|
{
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
MetaNode node = this->createNode();
|
|
const isGenerator = true;
|
|
const previousAllowYield = this->context.allowYield;
|
|
|
|
this->context.allowYield = true;
|
|
const params = this->parseFormalParameters();
|
|
this->context.allowYield = false;
|
|
const method = this->parsePropertyMethod(params);
|
|
this->context.allowYield = previousAllowYield;
|
|
|
|
return this->finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator));
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 14.4 Generator Function Definitions
|
|
|
|
Node* parseYieldExpression()
|
|
{
|
|
// TODO
|
|
RELEASE_ASSERT_NOT_REACHED();
|
|
/*
|
|
const node = this->createNode();
|
|
this->expectKeyword('yield');
|
|
|
|
let argument = null;
|
|
let delegate = false;
|
|
if (!this->hasLineTerminator) {
|
|
const previousAllowYield = this->context.allowYield;
|
|
this->context.allowYield = false;
|
|
delegate = this->match('*');
|
|
if (delegate) {
|
|
this->nextToken();
|
|
argument = this->parseAssignmentExpression();
|
|
} else {
|
|
if (!this->match(';') && !this->match('}') && !this->match(')') && this->lookahead.type !== Token.EOF) {
|
|
argument = this->parseAssignmentExpression();
|
|
}
|
|
}
|
|
this->context.allowYield = previousAllowYield;
|
|
}
|
|
|
|
return this->finalize(node, new Node.YieldExpression(argument, delegate));
|
|
*/
|
|
}
|
|
|
|
// ECMA-262 14.5 Class Definitions
|
|
/*
|
|
parseClassElement(hasConstructor): Node.Property {
|
|
let token = this.lookahead;
|
|
let node = this.createNode();
|
|
|
|
let kind: string;
|
|
let key: Node.PropertyKey;
|
|
let value: Node.FunctionExpression;
|
|
let computed = false;
|
|
let method = false;
|
|
let isStatic = false;
|
|
|
|
if (this.match('*')) {
|
|
this.nextToken();
|
|
} else {
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
const id = <Node.Identifier>key;
|
|
if (id.name === 'static' && (this.qualifiedPropertyName(this.lookahead) || this.match('*'))) {
|
|
token = this.lookahead;
|
|
isStatic = true;
|
|
computed = this.match('[');
|
|
if (this.match('*')) {
|
|
this.nextToken();
|
|
} else {
|
|
key = this.parseObjectPropertyKey();
|
|
}
|
|
}
|
|
}
|
|
|
|
const lookaheadPropertyKey = this.qualifiedPropertyName(this.lookahead);
|
|
if (token.type === Token.Identifier) {
|
|
if (token.value === 'get' && lookaheadPropertyKey) {
|
|
kind = 'get';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
this.context.allowYield = false;
|
|
value = this.parseGetterMethod();
|
|
} else if (token.value === 'set' && lookaheadPropertyKey) {
|
|
kind = 'set';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
value = this.parseSetterMethod();
|
|
}
|
|
} else if (token.type === Token.Punctuator && token.value === '*' && lookaheadPropertyKey) {
|
|
kind = 'init';
|
|
computed = this.match('[');
|
|
key = this.parseObjectPropertyKey();
|
|
value = this.parseGeneratorMethod();
|
|
method = true;
|
|
}
|
|
|
|
if (!kind && key && this.match('(')) {
|
|
kind = 'init';
|
|
value = this.parsePropertyMethodFunction();
|
|
method = true;
|
|
}
|
|
|
|
if (!kind) {
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
|
|
if (kind === 'init') {
|
|
kind = 'method';
|
|
}
|
|
|
|
if (!computed) {
|
|
if (isStatic && this.isPropertyKey(key, 'prototype')) {
|
|
this.throwUnexpectedToken(token, Messages.StaticPrototype);
|
|
}
|
|
if (!isStatic && this.isPropertyKey(key, 'constructor')) {
|
|
if (kind !== 'method' || !method || value.generator) {
|
|
this.throwUnexpectedToken(token, Messages.ConstructorSpecialMethod);
|
|
}
|
|
if (hasConstructor.value) {
|
|
this.throwUnexpectedToken(token, Messages.DuplicateConstructor);
|
|
} else {
|
|
hasConstructor.value = true;
|
|
}
|
|
kind = 'constructor';
|
|
}
|
|
}
|
|
|
|
|
|
return this.finalize(node, new Node.MethodDefinition(key, computed, value, kind, isStatic));
|
|
}
|
|
|
|
parseClassElementList(): Node.Property[] {
|
|
const body = [];
|
|
let hasConstructor = { value: false };
|
|
|
|
this.expect('{');
|
|
while (!this.match('}')) {
|
|
if (this.match(';')) {
|
|
this.nextToken();
|
|
} else {
|
|
body.push(this.parseClassElement(hasConstructor));
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
return body;
|
|
}
|
|
|
|
parseClassBody(): Node.ClassBody {
|
|
const node = this.createNode();
|
|
const elementList = this.parseClassElementList();
|
|
|
|
return this.finalize(node, new Node.ClassBody(elementList));
|
|
}
|
|
|
|
parseClassDeclaration(identifierIsOptional?: boolean): Node.ClassDeclaration {
|
|
const node = this.createNode();
|
|
|
|
const previousStrict = this.context.strict;
|
|
this.context.strict = true;
|
|
this.expectKeyword('class');
|
|
|
|
const id = (identifierIsOptional && (this.lookahead.type !== Token.Identifier)) ? null : this.parseVariableIdentifier();
|
|
let superClass = null;
|
|
if (this.matchKeyword('extends')) {
|
|
this.nextToken();
|
|
superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall);
|
|
}
|
|
const classBody = this.parseClassBody();
|
|
this.context.strict = previousStrict;
|
|
|
|
return this.finalize(node, new Node.ClassDeclaration(id, superClass, classBody));
|
|
}
|
|
|
|
parseClassExpression(): Node.ClassExpression {
|
|
const node = this.createNode();
|
|
|
|
const previousStrict = this.context.strict;
|
|
this.context.strict = true;
|
|
this.expectKeyword('class');
|
|
const id = (this.lookahead.type === Token.Identifier) ? this.parseVariableIdentifier() : null;
|
|
let superClass = null;
|
|
if (this.matchKeyword('extends')) {
|
|
this.nextToken();
|
|
superClass = this.isolateCoverGrammar(this.parseLeftHandSideExpressionAllowCall);
|
|
}
|
|
const classBody = this.parseClassBody();
|
|
this.context.strict = previousStrict;
|
|
|
|
return this.finalize(node, new Node.ClassExpression(id, superClass, classBody));
|
|
}
|
|
*/
|
|
|
|
// ECMA-262 15.1 Scripts
|
|
// ECMA-262 15.2 Modules
|
|
|
|
ProgramNode* parseProgram()
|
|
{
|
|
MetaNode node = this->createNode();
|
|
StatementNodeVector body = this->parseDirectivePrologues();
|
|
scopeContexts.push_back(new ASTScopeContext(this->context->strict, nullptr));
|
|
while (this->startMarker.index < this->scanner->length) {
|
|
body.push_back(this->parseStatementListItem());
|
|
}
|
|
scopeContexts.back()->m_locStart.line = node.line;
|
|
scopeContexts.back()->m_locStart.column = node.column;
|
|
scopeContexts.back()->m_locStart.index = node.index;
|
|
|
|
MetaNode endNode = this->createNode();
|
|
scopeContexts.back()->m_locEnd.line = endNode.line;
|
|
scopeContexts.back()->m_locEnd.column = endNode.column;
|
|
scopeContexts.back()->m_locEnd.index = endNode.index;
|
|
return this->finalize(node, new ProgramNode(std::move(body), scopeContexts.back()/*, this->sourceType*/));
|
|
}
|
|
|
|
// ECMA-262 15.2.2 Imports
|
|
/*
|
|
parseModuleSpecifier(): Node.Literal {
|
|
const node = this.createNode();
|
|
|
|
if (this.lookahead.type !== Token.StringLiteral) {
|
|
this.throwError(Messages.InvalidModuleSpecifier);
|
|
}
|
|
|
|
const token = this.nextToken();
|
|
const raw = this.getTokenRaw(token);
|
|
return this.finalize(node, new Node.Literal(token.value, raw));
|
|
}
|
|
|
|
// import {<foo as bar>} ...;
|
|
parseImportSpecifier(): Node.ImportSpecifier {
|
|
const node = this.createNode();
|
|
|
|
let local;
|
|
const imported = this.parseIdentifierName();
|
|
if (this.matchContextualKeyword('as')) {
|
|
this.nextToken();
|
|
local = this.parseVariableIdentifier();
|
|
} else {
|
|
local = imported;
|
|
}
|
|
|
|
return this.finalize(node, new Node.ImportSpecifier(local, imported));
|
|
}
|
|
|
|
// {foo, bar as bas}
|
|
parseNamedImports(): Node.ImportSpecifier[] {
|
|
this.expect('{');
|
|
const specifiers: Node.ImportSpecifier[] = [];
|
|
while (!this.match('}')) {
|
|
specifiers.push(this.parseImportSpecifier());
|
|
if (!this.match('}')) {
|
|
this.expect(',');
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
return specifiers;
|
|
}
|
|
|
|
// import <foo> ...;
|
|
parseImportDefaultSpecifier(): Node.ImportDefaultSpecifier {
|
|
const node = this.createNode();
|
|
const local = this.parseIdentifierName();
|
|
return this.finalize(node, new Node.ImportDefaultSpecifier(local));
|
|
}
|
|
|
|
// import <* as foo> ...;
|
|
parseImportNamespaceSpecifier(): Node.ImportNamespaceSpecifier {
|
|
const node = this.createNode();
|
|
|
|
this.expect('*');
|
|
if (!this.matchContextualKeyword('as')) {
|
|
this.throwError(Messages.NoAsAfterImportNamespace);
|
|
}
|
|
this.nextToken();
|
|
const local = this.parseIdentifierName();
|
|
|
|
return this.finalize(node, new Node.ImportNamespaceSpecifier(local));
|
|
}
|
|
|
|
parseImportDeclaration(): Node.ImportDeclaration {
|
|
if (this.context.inFunctionBody) {
|
|
this.throwError(Messages.IllegalImportDeclaration);
|
|
}
|
|
|
|
const node = this.createNode();
|
|
this.expectKeyword('import');
|
|
|
|
let src: Node.Literal;
|
|
let specifiers: Node.ImportDeclarationSpecifier[] = [];
|
|
if (this.lookahead.type === Token.StringLiteral) {
|
|
// import 'foo';
|
|
src = this.parseModuleSpecifier();
|
|
} else {
|
|
if (this.match('{')) {
|
|
// import {bar}
|
|
specifiers = specifiers.concat(this.parseNamedImports());
|
|
} else if (this.match('*')) {
|
|
// import * as foo
|
|
specifiers.push(this.parseImportNamespaceSpecifier());
|
|
} else if (this.isIdentifierName(this.lookahead) && !this.matchKeyword('default')) {
|
|
// import foo
|
|
specifiers.push(this.parseImportDefaultSpecifier());
|
|
if (this.match(',')) {
|
|
this.nextToken();
|
|
if (this.match('*')) {
|
|
// import foo, * as foo
|
|
specifiers.push(this.parseImportNamespaceSpecifier());
|
|
} else if (this.match('{')) {
|
|
// import foo, {bar}
|
|
specifiers = specifiers.concat(this.parseNamedImports());
|
|
} else {
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
}
|
|
} else {
|
|
this.throwUnexpectedToken(this.nextToken());
|
|
}
|
|
|
|
if (!this.matchContextualKeyword('from')) {
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
}
|
|
this.nextToken();
|
|
src = this.parseModuleSpecifier();
|
|
}
|
|
this.consumeSemicolon();
|
|
|
|
return this.finalize(node, new Node.ImportDeclaration(specifiers, src));
|
|
}
|
|
|
|
// ECMA-262 15.2.3 Exports
|
|
|
|
parseExportSpecifier(): Node.ExportSpecifier {
|
|
const node = this.createNode();
|
|
|
|
const local = this.parseIdentifierName();
|
|
let exported = local;
|
|
if (this.matchContextualKeyword('as')) {
|
|
this.nextToken();
|
|
exported = this.parseIdentifierName();
|
|
}
|
|
|
|
return this.finalize(node, new Node.ExportSpecifier(local, exported));
|
|
}
|
|
|
|
parseExportDeclaration(): Node.ExportDeclaration {
|
|
if (this.context.inFunctionBody) {
|
|
this.throwError(Messages.IllegalExportDeclaration);
|
|
}
|
|
|
|
const node = this.createNode();
|
|
this.expectKeyword('export');
|
|
|
|
let exportDeclaration;
|
|
if (this.matchKeyword('default')) {
|
|
// export default ...
|
|
this.nextToken();
|
|
if (this.matchKeyword('function')) {
|
|
// export default function foo () {}
|
|
// export default function () {}
|
|
const declaration = this.parseFunctionDeclaration(true);
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
} else if (this.matchKeyword('class')) {
|
|
// export default class foo {}
|
|
const declaration = this.parseClassDeclaration(true);
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
} else {
|
|
if (this.matchContextualKeyword('from')) {
|
|
this.throwError(Messages.UnexpectedToken, this.lookahead.value);
|
|
}
|
|
// export default {};
|
|
// export default [];
|
|
// export default (1 + 2);
|
|
const declaration = this.match('{') ? this.parseObjectInitializer() :
|
|
this.match('[') ? this.parseArrayInitializer() : this.parseAssignmentExpression();
|
|
this.consumeSemicolon();
|
|
exportDeclaration = this.finalize(node, new Node.ExportDefaultDeclaration(declaration));
|
|
}
|
|
|
|
} else if (this.match('*')) {
|
|
// export * from 'foo';
|
|
this.nextToken();
|
|
if (!this.matchContextualKeyword('from')) {
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
}
|
|
this.nextToken();
|
|
const src = this.parseModuleSpecifier();
|
|
this.consumeSemicolon();
|
|
exportDeclaration = this.finalize(node, new Node.ExportAllDeclaration(src));
|
|
|
|
} else if (this.lookahead.type === Token.Keyword) {
|
|
// export var f = 1;
|
|
let declaration;
|
|
switch (this.lookahead.value) {
|
|
case 'let':
|
|
case 'const':
|
|
declaration = this.parseLexicalDeclaration({ inFor: false });
|
|
break;
|
|
case 'var':
|
|
case 'class':
|
|
case 'function':
|
|
declaration = this.parseStatementListItem();
|
|
break;
|
|
default:
|
|
this.throwUnexpectedToken(this.lookahead);
|
|
}
|
|
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(declaration, [], null));
|
|
|
|
} else {
|
|
const specifiers = [];
|
|
let source = null;
|
|
let isExportFromIdentifier = false;
|
|
|
|
this.expect('{');
|
|
while (!this.match('}')) {
|
|
isExportFromIdentifier = isExportFromIdentifier || this.matchKeyword('default');
|
|
specifiers.push(this.parseExportSpecifier());
|
|
if (!this.match('}')) {
|
|
this.expect(',');
|
|
}
|
|
}
|
|
this.expect('}');
|
|
|
|
if (this.matchContextualKeyword('from')) {
|
|
// export {default} from 'foo';
|
|
// export {foo} from 'foo';
|
|
this.nextToken();
|
|
source = this.parseModuleSpecifier();
|
|
this.consumeSemicolon();
|
|
} else if (isExportFromIdentifier) {
|
|
// export {default}; // missing fromClause
|
|
const message = this.lookahead.value ? Messages.UnexpectedToken : Messages.MissingFromClause;
|
|
this.throwError(message, this.lookahead.value);
|
|
} else {
|
|
// export {foo};
|
|
this.consumeSemicolon();
|
|
}
|
|
exportDeclaration = this.finalize(node, new Node.ExportNamedDeclaration(null, specifiers, source));
|
|
}
|
|
|
|
return exportDeclaration;
|
|
}
|
|
*/
|
|
};
|
|
|
|
|
|
|
|
ProgramNode* parseProgram(::Escargot::Context* ctx, StringView source, ParserASTNodeHandler handler)
|
|
{
|
|
Parser parser(ctx, source, handler);
|
|
return parser.parseProgram();
|
|
}
|
|
|
|
Node* parseSingleFunction(::Escargot::Context* ctx, CodeBlock* codeBlock)
|
|
{
|
|
Parser parser(ctx, codeBlock->src(), nullptr, codeBlock->sourceElementStart().line, codeBlock->sourceElementStart().column);
|
|
parser.config.parseSingleFunction = true;
|
|
parser.scopeContexts.pushBack(new ASTScopeContext(codeBlock->isStrict(), nullptr));
|
|
return parser.parseFunctionSourceElements();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|