2020-03-11 18:27:43 +00:00
/*
2021-05-29 10:38:28 +00:00
* Copyright ( c ) 2020 , Stephan Unverwerth < s . unverwerth @ serenityos . org >
2021-05-10 10:56:08 +00:00
* Copyright ( c ) 2020 - 2021 , Linus Groh < linusg @ serenityos . org >
2020-03-11 18:27:43 +00:00
*
2021-04-22 08:24:48 +00:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-03-11 18:27:43 +00:00
*/
# include "Parser.h"
2021-06-16 22:57:01 +00:00
# include <AK/Array.h>
2021-06-13 08:47:09 +00:00
# include <AK/CharacterTypes.h>
2021-05-10 11:01:38 +00:00
# include <AK/HashTable.h>
2020-04-13 14:42:54 +00:00
# include <AK/ScopeGuard.h>
2020-03-11 18:27:43 +00:00
# include <AK/StdLibExtras.h>
2020-10-15 18:46:52 +00:00
# include <AK/TemporaryChange.h>
2020-03-11 18:27:43 +00:00
namespace JS {
2020-03-12 22:02:41 +00:00
2020-11-01 21:49:25 +00:00
static bool statement_is_use_strict_directive ( NonnullRefPtr < Statement > statement )
{
2021-01-01 18:34:07 +00:00
if ( ! is < ExpressionStatement > ( * statement ) )
2020-11-01 21:49:25 +00:00
return false ;
auto & expression_statement = static_cast < ExpressionStatement & > ( * statement ) ;
auto & expression = expression_statement . expression ( ) ;
2021-01-01 18:34:07 +00:00
if ( ! is < StringLiteral > ( expression ) )
2020-11-01 21:49:25 +00:00
return false ;
return static_cast < const StringLiteral & > ( expression ) . is_use_strict_directive ( ) ;
}
2020-04-13 14:42:54 +00:00
class ScopePusher {
public :
enum Type {
Var = 1 ,
Let = 2 ,
} ;
2021-07-04 01:15:52 +00:00
ScopePusher ( Parser & parser , unsigned mask , Parser : : Scope : : Type scope_type )
2020-04-13 14:42:54 +00:00
: m_parser ( parser )
, m_mask ( mask )
{
if ( m_mask & Var )
2021-06-19 12:43:09 +00:00
m_parser . m_state . var_scopes . append ( NonnullRefPtrVector < VariableDeclaration > ( ) ) ;
2020-04-13 14:42:54 +00:00
if ( m_mask & Let )
2021-06-19 12:43:09 +00:00
m_parser . m_state . let_scopes . append ( NonnullRefPtrVector < VariableDeclaration > ( ) ) ;
2021-07-04 01:15:52 +00:00
m_parser . m_state . current_scope = create < Parser : : Scope > ( scope_type , m_parser . m_state . current_scope ) ;
2020-04-13 14:42:54 +00:00
}
~ ScopePusher ( )
{
if ( m_mask & Var )
2021-06-19 12:43:09 +00:00
m_parser . m_state . var_scopes . take_last ( ) ;
2020-04-13 14:42:54 +00:00
if ( m_mask & Let )
2021-06-19 12:43:09 +00:00
m_parser . m_state . let_scopes . take_last ( ) ;
2021-07-04 01:15:52 +00:00
auto & popped = m_parser . m_state . current_scope ;
2021-07-05 19:45:34 +00:00
// Manual clear required to resolve circular references
popped - > hoisted_function_declarations . clear ( ) ;
2021-07-04 01:15:52 +00:00
m_parser . m_state . current_scope = popped - > parent ;
}
void add_to_scope_node ( NonnullRefPtr < ScopeNode > scope_node )
{
if ( m_mask & Var )
scope_node - > add_variables ( m_parser . m_state . var_scopes . last ( ) ) ;
if ( m_mask & Let )
scope_node - > add_variables ( m_parser . m_state . let_scopes . last ( ) ) ;
2021-07-05 19:45:34 +00:00
auto & scope = m_parser . m_state . current_scope ;
scope_node - > add_functions ( scope - > function_declarations ) ;
for ( auto & hoistable_function : scope - > hoisted_function_declarations ) {
if ( is_hoistable ( hoistable_function ) ) {
scope_node - > add_hoisted_function ( hoistable_function . declaration ) ;
}
}
}
static bool is_hoistable ( Parser : : Scope : : HoistableDeclaration & declaration )
{
auto & name = declaration . declaration - > name ( ) ;
// See if we find any conflicting lexical declaration on the way up
for ( RefPtr < Parser : : Scope > scope = declaration . scope ; ! scope . is_null ( ) ; scope = scope - > parent ) {
if ( scope - > lexical_declarations . contains ( name ) ) {
return false ;
}
}
return true ;
2020-04-13 14:42:54 +00:00
}
Parser & m_parser ;
unsigned m_mask { 0 } ;
} ;
2020-08-18 16:46:36 +00:00
class OperatorPrecedenceTable {
public :
constexpr OperatorPrecedenceTable ( )
: m_token_precedence ( )
{
for ( size_t i = 0 ; i < array_size ( m_operator_precedence ) ; + + i ) {
auto & op = m_operator_precedence [ i ] ;
m_token_precedence [ static_cast < size_t > ( op . token ) ] = op . precedence ;
}
}
2020-03-30 13:24:43 +00:00
2020-08-18 16:46:36 +00:00
constexpr int get ( TokenType token ) const
{
int p = m_token_precedence [ static_cast < size_t > ( token ) ] ;
if ( p = = 0 ) {
2020-12-06 16:55:19 +00:00
warnln ( " Internal Error: No precedence for operator {} " , Token : : name ( token ) ) ;
2021-02-23 19:42:32 +00:00
VERIFY_NOT_REACHED ( ) ;
2020-08-18 16:46:36 +00:00
return - 1 ;
}
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
return p ;
}
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
private :
int m_token_precedence [ cs_num_of_js_tokens ] ;
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
struct OperatorPrecedence {
TokenType token ;
int precedence ;
} ;
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
static constexpr const OperatorPrecedence m_operator_precedence [ ] = {
{ TokenType : : Period , 20 } ,
{ TokenType : : BracketOpen , 20 } ,
{ TokenType : : ParenOpen , 20 } ,
{ TokenType : : QuestionMarkPeriod , 20 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : New , 19 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : PlusPlus , 18 } ,
{ TokenType : : MinusMinus , 18 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : ExclamationMark , 17 } ,
{ TokenType : : Tilde , 17 } ,
{ TokenType : : Typeof , 17 } ,
{ TokenType : : Void , 17 } ,
{ TokenType : : Delete , 17 } ,
{ TokenType : : Await , 17 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : DoubleAsterisk , 16 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : Asterisk , 15 } ,
{ TokenType : : Slash , 15 } ,
{ TokenType : : Percent , 15 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : Plus , 14 } ,
{ TokenType : : Minus , 14 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : ShiftLeft , 13 } ,
{ TokenType : : ShiftRight , 13 } ,
{ TokenType : : UnsignedShiftRight , 13 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : LessThan , 12 } ,
{ TokenType : : LessThanEquals , 12 } ,
{ TokenType : : GreaterThan , 12 } ,
{ TokenType : : GreaterThanEquals , 12 } ,
{ TokenType : : In , 12 } ,
{ TokenType : : Instanceof , 12 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : EqualsEquals , 11 } ,
{ TokenType : : ExclamationMarkEquals , 11 } ,
{ TokenType : : EqualsEqualsEquals , 11 } ,
{ TokenType : : ExclamationMarkEqualsEquals , 11 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : Ampersand , 10 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : Caret , 9 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : Pipe , 8 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : DoubleQuestionMark , 7 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : DoubleAmpersand , 6 } ,
2020-03-12 22:02:41 +00:00
2020-08-18 16:46:36 +00:00
{ TokenType : : DoublePipe , 5 } ,
{ TokenType : : QuestionMark , 4 } ,
{ TokenType : : Equals , 3 } ,
{ TokenType : : PlusEquals , 3 } ,
{ TokenType : : MinusEquals , 3 } ,
{ TokenType : : DoubleAsteriskEquals , 3 } ,
{ TokenType : : AsteriskEquals , 3 } ,
{ TokenType : : SlashEquals , 3 } ,
{ TokenType : : PercentEquals , 3 } ,
{ TokenType : : ShiftLeftEquals , 3 } ,
{ TokenType : : ShiftRightEquals , 3 } ,
{ TokenType : : UnsignedShiftRightEquals , 3 } ,
{ TokenType : : AmpersandEquals , 3 } ,
{ TokenType : : CaretEquals , 3 } ,
2020-10-05 15:49:43 +00:00
{ TokenType : : PipeEquals , 3 } ,
{ TokenType : : DoubleAmpersandEquals , 3 } ,
{ TokenType : : DoublePipeEquals , 3 } ,
{ TokenType : : DoubleQuestionMarkEquals , 3 } ,
2020-08-18 16:46:36 +00:00
{ TokenType : : Yield , 2 } ,
{ TokenType : : Comma , 1 } ,
} ;
} ;
constexpr OperatorPrecedenceTable g_operator_precedence ;
2020-03-12 22:02:41 +00:00
2021-06-19 12:43:09 +00:00
Parser : : ParserState : : ParserState ( Lexer l )
: lexer ( move ( l ) )
, current_token ( lexer . next ( ) )
2020-03-12 22:02:41 +00:00
{
2020-08-18 16:46:36 +00:00
}
2020-03-12 22:02:41 +00:00
2021-07-04 01:15:52 +00:00
Parser : : Scope : : Scope ( Parser : : Scope : : Type type , RefPtr < Parser : : Scope > parent_scope )
: type ( type )
, parent ( move ( parent_scope ) )
{
}
RefPtr < Parser : : Scope > Parser : : Scope : : get_current_function_scope ( )
{
if ( this - > type = = Parser : : Scope : : Function ) {
return * this ;
}
auto result = this - > parent ;
while ( result - > type ! = Parser : : Scope : : Function ) {
result = result - > parent ;
}
return result ;
}
2020-08-18 16:46:36 +00:00
Parser : : Parser ( Lexer lexer )
2021-06-19 12:43:09 +00:00
: m_state ( move ( lexer ) )
2020-08-18 16:46:36 +00:00
{
2020-03-12 22:02:41 +00:00
}
Associativity Parser : : operator_associativity ( TokenType type ) const
{
switch ( type ) {
case TokenType : : Period :
case TokenType : : BracketOpen :
case TokenType : : ParenOpen :
case TokenType : : QuestionMarkPeriod :
case TokenType : : Asterisk :
case TokenType : : Slash :
case TokenType : : Percent :
case TokenType : : Plus :
case TokenType : : Minus :
case TokenType : : ShiftLeft :
case TokenType : : ShiftRight :
case TokenType : : UnsignedShiftRight :
case TokenType : : LessThan :
case TokenType : : LessThanEquals :
case TokenType : : GreaterThan :
case TokenType : : GreaterThanEquals :
case TokenType : : In :
case TokenType : : Instanceof :
case TokenType : : EqualsEquals :
case TokenType : : ExclamationMarkEquals :
case TokenType : : EqualsEqualsEquals :
case TokenType : : ExclamationMarkEqualsEquals :
2020-03-28 10:48:52 +00:00
case TokenType : : Typeof :
2020-04-15 16:55:03 +00:00
case TokenType : : Void :
2020-04-26 11:53:40 +00:00
case TokenType : : Delete :
2020-03-12 22:02:41 +00:00
case TokenType : : Ampersand :
case TokenType : : Caret :
case TokenType : : Pipe :
case TokenType : : DoubleQuestionMark :
case TokenType : : DoubleAmpersand :
case TokenType : : DoublePipe :
case TokenType : : Comma :
return Associativity : : Left ;
default :
return Associativity : : Right ;
}
2020-03-11 18:27:43 +00:00
}
2021-06-20 03:13:53 +00:00
NonnullRefPtr < Program > Parser : : parse_program ( bool starts_in_strict_mode )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-07-04 01:15:52 +00:00
ScopePusher scope ( * this , ScopePusher : : Var | ScopePusher : : Let , Scope : : Function ) ;
2021-04-23 14:46:57 +00:00
auto program = adopt_ref ( * new Program ( { m_filename , rule_start . position ( ) , position ( ) } ) ) ;
2021-06-20 03:13:53 +00:00
if ( starts_in_strict_mode ) {
program - > set_strict_mode ( ) ;
m_state . strict_mode = true ;
}
2020-05-28 05:22:08 +00:00
bool first = true ;
2020-03-11 18:27:43 +00:00
while ( ! done ( ) ) {
2020-10-22 22:30:07 +00:00
if ( match_declaration ( ) ) {
program - > append ( parse_declaration ( ) ) ;
} else if ( match_statement ( ) ) {
2020-11-01 21:49:25 +00:00
auto statement = parse_statement ( ) ;
program - > append ( statement ) ;
if ( statement_is_use_strict_directive ( statement ) ) {
if ( first ) {
2020-05-28 05:22:08 +00:00
program - > set_strict_mode ( ) ;
2021-06-19 12:43:09 +00:00
m_state . strict_mode = true ;
2020-05-28 05:22:08 +00:00
}
2021-06-19 12:43:09 +00:00
if ( m_state . string_legacy_octal_escape_sequence_in_scope )
2020-11-01 21:49:25 +00:00
syntax_error ( " Octal escape sequence in string literal not allowed in strict mode " ) ;
2020-05-28 05:22:08 +00:00
}
2020-03-11 18:27:43 +00:00
} else {
2020-10-22 22:30:07 +00:00
expected ( " statement or declaration " ) ;
2020-03-11 18:27:43 +00:00
consume ( ) ;
}
2020-11-01 21:49:25 +00:00
first = false ;
2020-03-11 18:27:43 +00:00
}
2021-06-19 12:43:09 +00:00
if ( m_state . var_scopes . size ( ) = = 1 ) {
2021-07-04 01:15:52 +00:00
scope . add_to_scope_node ( program ) ;
2020-06-01 14:08:34 +00:00
} else {
2021-06-22 13:42:44 +00:00
syntax_error ( " Unclosed lexical_environment " ) ;
2020-06-01 14:08:34 +00:00
}
2020-12-28 17:15:22 +00:00
program - > source_range ( ) . end = position ( ) ;
2020-03-11 18:27:43 +00:00
return program ;
}
2020-10-22 22:30:07 +00:00
NonnullRefPtr < Declaration > Parser : : parse_declaration ( )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-06-08 18:31:21 +00:00
case TokenType : : Class :
2020-08-29 11:25:37 +00:00
return parse_class_declaration ( ) ;
2020-06-04 12:48:36 +00:00
case TokenType : : Function : {
auto declaration = parse_function_node < FunctionDeclaration > ( ) ;
2021-07-04 01:15:52 +00:00
m_state . current_scope - > function_declarations . append ( declaration ) ;
2021-07-05 19:45:34 +00:00
auto hoisting_target = m_state . current_scope - > get_current_function_scope ( ) ;
hoisting_target - > hoisted_function_declarations . append ( { declaration , * m_state . current_scope } ) ;
2020-06-04 12:48:36 +00:00
return declaration ;
}
2020-10-22 22:30:07 +00:00
case TokenType : : Let :
case TokenType : : Const :
return parse_variable_declaration ( ) ;
default :
expected ( " declaration " ) ;
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ErrorDeclaration > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-10-22 22:30:07 +00:00
}
}
NonnullRefPtr < Statement > Parser : : parse_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-03-11 18:27:43 +00:00
case TokenType : : CurlyOpen :
return parse_block_statement ( ) ;
case TokenType : : Return :
return parse_return_statement ( ) ;
case TokenType : : Var :
return parse_variable_declaration ( ) ;
2020-03-12 12:12:12 +00:00
case TokenType : : For :
return parse_for_statement ( ) ;
2020-03-21 17:40:17 +00:00
case TokenType : : If :
return parse_if_statement ( ) ;
2020-03-24 21:03:50 +00:00
case TokenType : : Throw :
return parse_throw_statement ( ) ;
2020-03-24 13:03:55 +00:00
case TokenType : : Try :
return parse_try_statement ( ) ;
2020-03-29 11:09:54 +00:00
case TokenType : : Break :
return parse_break_statement ( ) ;
2020-04-04 22:22:42 +00:00
case TokenType : : Continue :
2020-08-29 11:25:37 +00:00
return parse_continue_statement ( ) ;
2020-03-29 11:09:54 +00:00
case TokenType : : Switch :
return parse_switch_statement ( ) ;
2020-04-04 19:29:23 +00:00
case TokenType : : Do :
return parse_do_while_statement ( ) ;
2020-04-21 18:27:57 +00:00
case TokenType : : While :
return parse_while_statement ( ) ;
2020-11-28 14:05:57 +00:00
case TokenType : : With :
2021-06-19 12:43:09 +00:00
if ( m_state . strict_mode )
2020-11-28 19:17:33 +00:00
syntax_error ( " 'with' statement not allowed in strict mode " ) ;
2020-11-28 14:05:57 +00:00
return parse_with_statement ( ) ;
2020-04-30 16:26:27 +00:00
case TokenType : : Debugger :
2020-08-29 11:25:37 +00:00
return parse_debugger_statement ( ) ;
2020-05-03 09:59:00 +00:00
case TokenType : : Semicolon :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < EmptyStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-03-11 18:27:43 +00:00
default :
2020-05-28 20:36:59 +00:00
if ( match ( TokenType : : Identifier ) ) {
auto result = try_parse_labelled_statement ( ) ;
if ( ! result . is_null ( ) )
return result . release_nonnull ( ) ;
}
2020-04-17 13:05:58 +00:00
if ( match_expression ( ) ) {
2020-10-22 22:30:07 +00:00
if ( match ( TokenType : : Function ) )
syntax_error ( " Function declaration not allowed in single-statement context " ) ;
2020-04-17 13:05:58 +00:00
auto expr = parse_expression ( 0 ) ;
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ExpressionStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expr ) ) ;
2020-04-17 13:05:58 +00:00
}
2020-10-22 22:30:07 +00:00
expected ( " statement " ) ;
2020-03-11 18:27:43 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ErrorStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-08-29 11:25:37 +00:00
}
2020-03-11 18:27:43 +00:00
}
2020-03-30 13:26:09 +00:00
RefPtr < FunctionExpression > Parser : : try_parse_arrow_function_expression ( bool expect_parens )
{
save_state ( ) ;
2020-12-29 05:12:02 +00:00
auto rule_start = push_start ( ) ;
2020-04-13 14:42:54 +00:00
ArmedScopeGuard state_rollback_guard = [ & ] {
load_state ( ) ;
} ;
2020-03-30 13:26:09 +00:00
2020-05-02 18:46:39 +00:00
Vector < FunctionNode : : Parameter > parameters ;
2020-05-06 03:02:14 +00:00
i32 function_length = - 1 ;
2020-10-18 23:26:41 +00:00
if ( expect_parens ) {
// We have parens around the function parameters and can re-use the same parsing
// logic used for regular functions: multiple parameters, default values, rest
// parameter, maybe a trailing comma. If we have a new syntax error afterwards we
2020-10-25 11:14:04 +00:00
// check if it's about a wrong token (something like duplicate parameter name must
// not abort), know parsing failed and rollback the parser state.
2021-06-19 12:43:09 +00:00
auto previous_syntax_errors = m_state . errors . size ( ) ;
2021-05-29 11:33:19 +00:00
parameters = parse_formal_parameters ( function_length , FunctionNodeParseOptions : : IsArrowFunction ) ;
2021-06-19 12:43:09 +00:00
if ( m_state . errors . size ( ) > previous_syntax_errors & & m_state . errors [ previous_syntax_errors ] . message . starts_with ( " Unexpected token " ) )
2020-10-18 23:26:41 +00:00
return nullptr ;
if ( ! match ( TokenType : : ParenClose ) )
return nullptr ;
consume ( ) ;
} else {
// No parens - this must be an identifier followed by arrow. That's it.
2021-07-11 11:04:55 +00:00
if ( ! match_identifier ( ) & & ! match ( TokenType : : Yield ) & & ! match ( TokenType : : Await ) )
2020-10-18 23:26:41 +00:00
return nullptr ;
2021-07-11 11:04:55 +00:00
auto token = consume_identifier_reference ( ) ;
if ( m_state . strict_mode & & token . value ( ) . is_one_of ( " arguments " sv , " eval " sv ) )
syntax_error ( " BindingIdentifier may not be 'arguments' or 'eval' in strict mode " ) ;
parameters . append ( { FlyString { token . value ( ) } , { } } ) ;
2020-03-30 13:26:09 +00:00
}
2020-10-18 23:29:17 +00:00
// If there's a newline between the closing paren and arrow it's not a valid arrow function,
// ASI should kick in instead (it'll then fail with "Unexpected token Arrow")
2021-06-19 12:43:09 +00:00
if ( m_state . current_token . trivia_contains_line_terminator ( ) )
2020-10-18 23:29:17 +00:00
return nullptr ;
2020-10-18 23:26:41 +00:00
if ( ! match ( TokenType : : Arrow ) )
2020-03-30 13:26:09 +00:00
return nullptr ;
2020-10-18 23:26:41 +00:00
consume ( ) ;
2020-03-30 13:26:09 +00:00
2020-05-11 16:27:31 +00:00
if ( function_length = = - 1 )
2020-05-06 03:02:14 +00:00
function_length = parameters . size ( ) ;
2021-06-19 12:43:09 +00:00
m_state . function_parameters . append ( parameters ) ;
2021-06-14 07:30:43 +00:00
2021-06-19 12:43:09 +00:00
auto old_labels_in_scope = move ( m_state . labels_in_scope ) ;
2020-10-08 17:49:08 +00:00
ScopeGuard guard ( [ & ] ( ) {
2021-06-19 12:43:09 +00:00
m_state . labels_in_scope = move ( old_labels_in_scope ) ;
2020-10-08 17:49:08 +00:00
} ) ;
2020-10-04 00:02:43 +00:00
bool is_strict = false ;
auto function_body_result = [ & ] ( ) - > RefPtr < BlockStatement > {
2021-06-19 12:43:09 +00:00
TemporaryChange change ( m_state . in_arrow_function_context , true ) ;
2020-03-30 13:26:09 +00:00
if ( match ( TokenType : : CurlyOpen ) ) {
// Parse a function body with statements
2021-07-04 01:15:52 +00:00
ScopePusher scope ( * this , ScopePusher : : Var , Scope : : Function ) ;
auto body = parse_block_statement ( is_strict ) ;
scope . add_to_scope_node ( body ) ;
return body ;
2020-03-30 13:26:09 +00:00
}
if ( match_expression ( ) ) {
// Parse a function body which returns a single expression
// FIXME: We synthesize a block with a return statement
// for arrow function bodies which are a single expression.
// Esprima generates a single "ArrowFunctionExpression"
// with a "body" property.
2020-05-29 20:03:47 +00:00
auto return_expression = parse_expression ( 2 ) ;
2021-06-19 12:43:09 +00:00
auto return_block = create_ast_node < BlockStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2021-02-28 09:42:34 +00:00
return_block - > append < ReturnStatement > ( { m_filename , rule_start . position ( ) , position ( ) } , move ( return_expression ) ) ;
2020-03-30 13:26:09 +00:00
return return_block ;
}
// Invalid arrow function body
return nullptr ;
} ( ) ;
2021-06-19 12:43:09 +00:00
m_state . function_parameters . take_last ( ) ;
2021-06-14 07:30:43 +00:00
2020-03-30 13:26:09 +00:00
if ( ! function_body_result . is_null ( ) ) {
2020-04-13 14:42:54 +00:00
state_rollback_guard . disarm ( ) ;
2020-12-29 13:17:39 +00:00
discard_saved_state ( ) ;
2020-03-30 13:26:09 +00:00
auto body = function_body_result . release_nonnull ( ) ;
2021-06-10 23:08:05 +00:00
return create_ast_node < FunctionExpression > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , " " , move ( body ) ,
2021-07-04 01:12:27 +00:00
move ( parameters ) , function_length , FunctionKind : : Regular , is_strict , true ) ;
2020-03-30 13:26:09 +00:00
}
return nullptr ;
}
2020-05-28 18:09:19 +00:00
RefPtr < Statement > Parser : : try_parse_labelled_statement ( )
{
save_state ( ) ;
2020-12-29 05:12:02 +00:00
auto rule_start = push_start ( ) ;
2020-05-28 18:09:19 +00:00
ArmedScopeGuard state_rollback_guard = [ & ] {
load_state ( ) ;
} ;
2021-07-11 11:04:55 +00:00
auto identifier = consume_identifier_reference ( ) . value ( ) ;
2020-05-28 18:09:19 +00:00
if ( ! match ( TokenType : : Colon ) )
return { } ;
consume ( TokenType : : Colon ) ;
if ( ! match_statement ( ) )
return { } ;
2021-06-19 12:43:09 +00:00
m_state . labels_in_scope . set ( identifier ) ;
2020-05-28 18:09:19 +00:00
auto statement = parse_statement ( ) ;
2021-06-19 12:43:09 +00:00
m_state . labels_in_scope . remove ( identifier ) ;
2020-05-28 18:09:19 +00:00
statement - > set_label ( identifier ) ;
state_rollback_guard . disarm ( ) ;
2020-12-29 13:17:39 +00:00
discard_saved_state ( ) ;
2020-05-28 18:09:19 +00:00
return statement ;
}
2020-11-02 21:27:42 +00:00
RefPtr < MetaProperty > Parser : : try_parse_new_target_expression ( )
{
save_state ( ) ;
2020-12-29 05:12:02 +00:00
auto rule_start = push_start ( ) ;
2020-11-02 21:27:42 +00:00
ArmedScopeGuard state_rollback_guard = [ & ] {
load_state ( ) ;
} ;
consume ( TokenType : : New ) ;
if ( ! match ( TokenType : : Period ) )
return { } ;
consume ( ) ;
if ( ! match ( TokenType : : Identifier ) )
return { } ;
if ( consume ( ) . value ( ) ! = " target " )
return { } ;
state_rollback_guard . disarm ( ) ;
2020-12-29 13:17:39 +00:00
discard_saved_state ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < MetaProperty > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , MetaProperty : : Type : : NewTarget ) ;
2020-11-02 21:27:42 +00:00
}
2020-06-08 18:31:21 +00:00
NonnullRefPtr < ClassDeclaration > Parser : : parse_class_declaration ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ClassDeclaration > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , parse_class_expression ( true ) ) ;
2020-06-08 18:31:21 +00:00
}
NonnullRefPtr < ClassExpression > Parser : : parse_class_expression ( bool expect_class_name )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-06-08 18:31:21 +00:00
// Classes are always in strict mode.
2021-06-19 12:43:09 +00:00
TemporaryChange strict_mode_rollback ( m_state . strict_mode , true ) ;
2020-06-08 18:31:21 +00:00
consume ( TokenType : : Class ) ;
NonnullRefPtrVector < ClassMethod > methods ;
RefPtr < Expression > super_class ;
RefPtr < FunctionExpression > constructor ;
2021-07-11 11:04:55 +00:00
String class_name = expect_class_name | | match_identifier ( ) | | match ( TokenType : : Yield ) | | match ( TokenType : : Await )
? consume_identifier_reference ( ) . value ( ) . to_string ( )
: " " ;
2020-06-08 18:31:21 +00:00
if ( match ( TokenType : : Extends ) ) {
consume ( ) ;
2021-06-14 11:16:41 +00:00
auto [ expression , should_continue_parsing ] = parse_primary_expression ( ) ;
super_class = move ( expression ) ;
( void ) should_continue_parsing ;
2020-06-08 18:31:21 +00:00
}
consume ( TokenType : : CurlyOpen ) ;
while ( ! done ( ) & & ! match ( TokenType : : CurlyClose ) ) {
RefPtr < Expression > property_key ;
bool is_static = false ;
bool is_constructor = false ;
2021-07-02 10:07:00 +00:00
bool is_generator = false ;
2020-06-08 18:31:21 +00:00
auto method_kind = ClassMethod : : Kind : : Method ;
if ( match ( TokenType : : Semicolon ) ) {
consume ( ) ;
continue ;
}
2021-07-02 10:07:00 +00:00
if ( match ( TokenType : : Asterisk ) ) {
consume ( ) ;
is_generator = true ;
}
2020-06-08 18:31:21 +00:00
if ( match_property_key ( ) ) {
StringView name ;
2021-07-02 10:07:00 +00:00
if ( ! is_generator & & m_state . current_token . value ( ) = = " static " sv ) {
if ( match ( TokenType : : Identifier ) ) {
consume ( ) ;
is_static = true ;
if ( match ( TokenType : : Asterisk ) ) {
consume ( ) ;
is_generator = true ;
}
}
2020-06-08 18:31:21 +00:00
}
if ( match ( TokenType : : Identifier ) ) {
2021-06-19 12:43:09 +00:00
auto identifier_name = m_state . current_token . value ( ) ;
2020-06-08 18:31:21 +00:00
if ( identifier_name = = " get " ) {
method_kind = ClassMethod : : Kind : : Getter ;
consume ( ) ;
} else if ( identifier_name = = " set " ) {
method_kind = ClassMethod : : Kind : : Setter ;
consume ( ) ;
}
}
if ( match_property_key ( ) ) {
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-06-08 18:31:21 +00:00
case TokenType : : Identifier :
name = consume ( ) . value ( ) ;
2021-06-19 12:43:09 +00:00
property_key = create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , name ) ;
2020-06-08 18:31:21 +00:00
break ;
case TokenType : : StringLiteral : {
auto string_literal = parse_string_literal ( consume ( ) ) ;
name = string_literal - > value ( ) ;
property_key = move ( string_literal ) ;
break ;
}
default :
property_key = parse_property_key ( ) ;
break ;
}
} else {
expected ( " property key " ) ;
}
// Constructor may be a StringLiteral or an Identifier.
if ( ! is_static & & name = = " constructor " ) {
if ( method_kind ! = ClassMethod : : Kind : : Method )
syntax_error ( " Class constructor may not be an accessor " ) ;
if ( ! constructor . is_null ( ) )
syntax_error ( " Classes may not have more than one constructor " ) ;
2021-07-02 10:07:00 +00:00
if ( is_generator )
syntax_error ( " Class constructor may not be a generator " ) ;
2020-06-08 18:31:21 +00:00
is_constructor = true ;
}
}
if ( match ( TokenType : : ParenOpen ) ) {
2020-10-20 16:56:49 +00:00
u8 parse_options = FunctionNodeParseOptions : : AllowSuperPropertyLookup ;
if ( ! super_class . is_null ( ) )
parse_options | = FunctionNodeParseOptions : : AllowSuperConstructorCall ;
2020-10-20 17:32:51 +00:00
if ( method_kind = = ClassMethod : : Kind : : Getter )
parse_options | = FunctionNodeParseOptions : : IsGetterFunction ;
if ( method_kind = = ClassMethod : : Kind : : Setter )
parse_options | = FunctionNodeParseOptions : : IsSetterFunction ;
2021-07-02 10:07:00 +00:00
if ( is_generator )
parse_options | = FunctionNodeParseOptions : : IsGeneratorFunction ;
2020-10-20 16:56:49 +00:00
auto function = parse_function_node < FunctionExpression > ( parse_options ) ;
2020-06-08 18:31:21 +00:00
if ( is_constructor ) {
constructor = move ( function ) ;
} else if ( ! property_key . is_null ( ) ) {
2021-06-19 12:43:09 +00:00
methods . append ( create_ast_node < ClassMethod > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , property_key . release_nonnull ( ) , move ( function ) , method_kind , is_static ) ) ;
2020-06-08 18:31:21 +00:00
} else {
syntax_error ( " No key for class method " ) ;
}
} else {
expected ( " ParenOpen " ) ;
consume ( ) ;
}
}
consume ( TokenType : : CurlyClose ) ;
if ( constructor . is_null ( ) ) {
2021-06-19 12:43:09 +00:00
auto constructor_body = create_ast_node < BlockStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-06-08 18:31:21 +00:00
if ( ! super_class . is_null ( ) ) {
// Set constructor to the result of parsing the source text
// constructor(... args){ super (...args);}
2021-07-02 17:30:38 +00:00
auto super_call = create_ast_node < SuperCall > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
Vector { CallExpression : : Argument { create_ast_node < Identifier > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , " args " ) , true } } ) ;
constructor_body - > append ( create_ast_node < ExpressionStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( super_call ) ) ) ;
constructor_body - > add_variables ( m_state . var_scopes . last ( ) ) ;
2020-06-08 18:31:21 +00:00
2021-06-10 23:08:05 +00:00
constructor = create_ast_node < FunctionExpression > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , class_name , move ( constructor_body ) ,
2021-07-04 01:12:27 +00:00
Vector { FunctionNode : : Parameter { FlyString { " args " } , nullptr , true } } , 0 , FunctionKind : : Regular , true ) ;
2020-06-08 18:31:21 +00:00
} else {
2021-06-10 23:08:05 +00:00
constructor = create_ast_node < FunctionExpression > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , class_name , move ( constructor_body ) ,
2021-07-04 01:12:27 +00:00
Vector < FunctionNode : : Parameter > { } , 0 , FunctionKind : : Regular , true ) ;
2020-06-08 18:31:21 +00:00
}
}
2021-06-19 12:43:09 +00:00
return create_ast_node < ClassExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( class_name ) , move ( constructor ) , move ( super_class ) , move ( methods ) ) ;
2020-06-08 18:31:21 +00:00
}
2021-06-14 11:16:41 +00:00
Parser : : PrimaryExpressionParseResult Parser : : parse_primary_expression ( )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-25 08:51:54 +00:00
if ( match_unary_prefixed_expression ( ) )
2021-06-14 11:16:41 +00:00
return { parse_unary_prefixed_expression ( ) } ;
2020-03-25 08:51:54 +00:00
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-03-11 18:27:43 +00:00
case TokenType : : ParenOpen : {
2021-04-11 20:41:51 +00:00
auto paren_position = position ( ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : ParenOpen ) ;
2021-07-11 11:04:55 +00:00
if ( ( match ( TokenType : : ParenClose ) | | match_identifier ( ) | | match ( TokenType : : TripleDot ) | | match ( TokenType : : CurlyOpen ) | | match ( TokenType : : BracketOpen ) )
2021-07-02 10:51:46 +00:00
& & ! try_parse_arrow_function_expression_failed_at_position ( paren_position ) ) {
2020-03-30 13:26:09 +00:00
auto arrow_function_result = try_parse_arrow_function_expression ( true ) ;
2020-11-02 21:27:42 +00:00
if ( ! arrow_function_result . is_null ( ) )
2021-06-14 11:16:41 +00:00
return { arrow_function_result . release_nonnull ( ) } ;
2021-04-11 20:41:51 +00:00
set_try_parse_arrow_function_expression_failed_at_position ( paren_position , true ) ;
2020-03-30 13:26:09 +00:00
}
2020-03-12 22:02:41 +00:00
auto expression = parse_expression ( 0 ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : ParenClose ) ;
2021-03-22 11:44:07 +00:00
if ( is < FunctionExpression > ( * expression ) ) {
static_cast < FunctionExpression & > ( * expression ) . set_cannot_auto_rename ( ) ;
}
2021-06-14 11:16:41 +00:00
return { move ( expression ) } ;
2020-03-11 18:27:43 +00:00
}
2020-04-12 22:42:14 +00:00
case TokenType : : This :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return { create_ast_node < ThisExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) } ;
2020-06-08 18:31:21 +00:00
case TokenType : : Class :
2021-06-14 11:16:41 +00:00
return { parse_class_expression ( false ) } ;
2020-06-08 18:31:21 +00:00
case TokenType : : Super :
consume ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . allow_super_property_lookup )
2020-06-08 18:31:21 +00:00
syntax_error ( " 'super' keyword unexpected here " ) ;
2021-06-19 12:43:09 +00:00
return { create_ast_node < SuperExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) } ;
2020-03-30 13:26:09 +00:00
case TokenType : : Identifier : {
2021-06-10 21:08:30 +00:00
read_as_identifier : ;
2021-04-11 20:41:51 +00:00
if ( ! try_parse_arrow_function_expression_failed_at_position ( position ( ) ) ) {
auto arrow_function_result = try_parse_arrow_function_expression ( false ) ;
if ( ! arrow_function_result . is_null ( ) )
2021-06-14 11:16:41 +00:00
return { arrow_function_result . release_nonnull ( ) } ;
2021-04-11 20:41:51 +00:00
set_try_parse_arrow_function_expression_failed_at_position ( position ( ) , true ) ;
}
2021-06-14 07:30:43 +00:00
auto string = consume ( ) . value ( ) ;
2021-06-22 20:20:17 +00:00
return { create_ast_node < Identifier > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , string ) } ;
2020-03-30 13:26:09 +00:00
}
2020-03-11 18:27:43 +00:00
case TokenType : : NumericLiteral :
2021-06-19 12:43:09 +00:00
return { create_ast_node < NumericLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume_and_validate_numeric_literal ( ) . double_value ( ) ) } ;
2020-06-06 00:14:10 +00:00
case TokenType : : BigIntLiteral :
2021-06-19 12:43:09 +00:00
return { create_ast_node < BigIntLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . value ( ) ) } ;
2020-03-11 18:27:43 +00:00
case TokenType : : BoolLiteral :
2021-06-19 12:43:09 +00:00
return { create_ast_node < BooleanLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . bool_value ( ) ) } ;
2020-03-12 12:05:06 +00:00
case TokenType : : StringLiteral :
2021-06-14 11:16:41 +00:00
return { parse_string_literal ( consume ( ) ) } ;
2020-03-15 21:32:34 +00:00
case TokenType : : NullLiteral :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return { create_ast_node < NullLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) } ;
2020-03-11 18:27:43 +00:00
case TokenType : : CurlyOpen :
2021-06-14 11:16:41 +00:00
return { parse_object_expression ( ) } ;
2020-03-19 10:52:56 +00:00
case TokenType : : Function :
2021-06-14 11:16:41 +00:00
return { parse_function_node < FunctionExpression > ( ) } ;
2020-03-20 19:29:57 +00:00
case TokenType : : BracketOpen :
2021-06-14 11:16:41 +00:00
return { parse_array_expression ( ) } ;
2020-06-03 23:05:49 +00:00
case TokenType : : RegexLiteral :
2021-06-14 11:16:41 +00:00
return { parse_regexp_literal ( ) } ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
case TokenType : : TemplateLiteralStart :
2021-06-14 11:16:41 +00:00
return { parse_template_literal ( false ) } ;
2020-11-02 21:27:42 +00:00
case TokenType : : New : {
auto new_start = position ( ) ;
auto new_target_result = try_parse_new_target_expression ( ) ;
if ( ! new_target_result . is_null ( ) ) {
2021-06-19 12:43:09 +00:00
if ( ! m_state . in_function_context )
2020-11-02 21:27:42 +00:00
syntax_error ( " 'new.target' not allowed outside of a function " , new_start ) ;
2021-06-14 11:16:41 +00:00
return { new_target_result . release_nonnull ( ) } ;
2020-11-02 21:27:42 +00:00
}
2021-06-14 11:16:41 +00:00
return { parse_new_expression ( ) } ;
2020-11-02 21:27:42 +00:00
}
2021-06-10 21:08:30 +00:00
case TokenType : : Yield :
2021-06-19 12:43:09 +00:00
if ( ! m_state . in_generator_function_context )
2021-06-10 21:08:30 +00:00
goto read_as_identifier ;
2021-06-14 11:16:41 +00:00
return { parse_yield_expression ( ) , false } ;
2020-03-11 18:27:43 +00:00
default :
2021-07-11 11:04:55 +00:00
if ( match_identifier_name ( ) )
goto read_as_identifier ;
2020-10-22 22:30:07 +00:00
expected ( " primary expression " ) ;
2020-03-11 18:27:43 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return { create_ast_node < ErrorExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) } ;
2020-03-11 18:27:43 +00:00
}
}
2020-06-03 23:05:49 +00:00
NonnullRefPtr < RegExpLiteral > Parser : : parse_regexp_literal ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-05-10 10:56:08 +00:00
auto pattern = consume ( ) . value ( ) ;
// Remove leading and trailing slash.
pattern = pattern . substring_view ( 1 , pattern . length ( ) - 2 ) ;
2021-05-10 11:01:38 +00:00
auto flags = String : : empty ( ) ;
if ( match ( TokenType : : RegexFlags ) ) {
auto flags_start = position ( ) ;
flags = consume ( ) . value ( ) ;
HashTable < char > seen_flags ;
for ( size_t i = 0 ; i < flags . length ( ) ; + + i ) {
auto flag = flags . substring_view ( i , 1 ) ;
2021-07-09 20:10:17 +00:00
if ( ! flag . is_one_of ( " d " , " g " , " i " , " m " , " s " , " u " , " y " ) )
2021-05-10 11:01:38 +00:00
syntax_error ( String : : formatted ( " Invalid RegExp flag '{}' " , flag ) , Position { flags_start . line , flags_start . column + i } ) ;
if ( seen_flags . contains ( * flag . characters_without_null_termination ( ) ) )
syntax_error ( String : : formatted ( " Repeated RegExp flag '{}' " , flag ) , Position { flags_start . line , flags_start . column + i } ) ;
seen_flags . set ( * flag . characters_without_null_termination ( ) ) ;
}
}
2021-06-19 12:43:09 +00:00
return create_ast_node < RegExpLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , pattern , flags ) ;
2020-06-03 23:05:49 +00:00
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < Expression > Parser : : parse_unary_prefixed_expression ( )
2020-03-14 18:45:51 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
auto precedence = g_operator_precedence . get ( m_state . current_token . type ( ) ) ;
auto associativity = operator_associativity ( m_state . current_token . type ( ) ) ;
switch ( m_state . current_token . type ( ) ) {
2020-04-29 20:39:00 +00:00
case TokenType : : PlusPlus : {
consume ( ) ;
2020-11-02 21:03:19 +00:00
auto rhs_start = position ( ) ;
2020-04-29 20:39:00 +00:00
auto rhs = parse_expression ( precedence , associativity ) ;
2020-09-16 19:03:06 +00:00
// FIXME: Apparently for functions this should also not be enforced on a parser level,
// other engines throw ReferenceError for ++foo()
2021-01-01 18:34:07 +00:00
if ( ! is < Identifier > ( * rhs ) & & ! is < MemberExpression > ( * rhs ) )
2020-11-02 21:03:19 +00:00
syntax_error ( String : : formatted ( " Right-hand side of prefix increment operator must be identifier or member expression, got {} " , rhs - > class_name ( ) ) , rhs_start ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UpdateExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UpdateOp : : Increment , move ( rhs ) , true ) ;
2020-04-29 20:39:00 +00:00
}
case TokenType : : MinusMinus : {
consume ( ) ;
2020-11-02 21:03:19 +00:00
auto rhs_start = position ( ) ;
2020-04-29 20:39:00 +00:00
auto rhs = parse_expression ( precedence , associativity ) ;
2020-09-16 19:03:06 +00:00
// FIXME: Apparently for functions this should also not be enforced on a parser level,
// other engines throw ReferenceError for --foo()
2021-01-01 18:34:07 +00:00
if ( ! is < Identifier > ( * rhs ) & & ! is < MemberExpression > ( * rhs ) )
2020-11-02 21:03:19 +00:00
syntax_error ( String : : formatted ( " Right-hand side of prefix decrement operator must be identifier or member expression, got {} " , rhs - > class_name ( ) ) , rhs_start ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UpdateExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UpdateOp : : Decrement , move ( rhs ) , true ) ;
2020-04-29 20:39:00 +00:00
}
2020-03-14 18:45:51 +00:00
case TokenType : : ExclamationMark :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Not , parse_expression ( precedence , associativity ) ) ;
2020-03-14 18:45:51 +00:00
case TokenType : : Tilde :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : BitwiseNot , parse_expression ( precedence , associativity ) ) ;
2020-04-02 16:58:39 +00:00
case TokenType : : Plus :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Plus , parse_expression ( precedence , associativity ) ) ;
2020-04-02 16:58:39 +00:00
case TokenType : : Minus :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Minus , parse_expression ( precedence , associativity ) ) ;
2020-03-17 19:33:32 +00:00
case TokenType : : Typeof :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Typeof , parse_expression ( precedence , associativity ) ) ;
2020-04-15 16:55:03 +00:00
case TokenType : : Void :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Void , parse_expression ( precedence , associativity ) ) ;
2020-04-26 11:53:40 +00:00
case TokenType : : Delete :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UnaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UnaryOp : : Delete , parse_expression ( precedence , associativity ) ) ;
2020-03-14 18:45:51 +00:00
default :
2020-10-22 22:30:07 +00:00
expected ( " primary expression " ) ;
2020-03-14 18:45:51 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ErrorExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-03-14 18:45:51 +00:00
}
}
2020-06-08 18:31:21 +00:00
NonnullRefPtr < Expression > Parser : : parse_property_key ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-06-08 18:31:21 +00:00
if ( match ( TokenType : : StringLiteral ) ) {
return parse_string_literal ( consume ( ) ) ;
} else if ( match ( TokenType : : NumericLiteral ) ) {
2021-06-19 12:43:09 +00:00
return create_ast_node < NumericLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . double_value ( ) ) ;
2020-06-08 18:31:21 +00:00
} else if ( match ( TokenType : : BigIntLiteral ) ) {
2021-06-19 12:43:09 +00:00
return create_ast_node < BigIntLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . value ( ) ) ;
2020-06-08 18:31:21 +00:00
} else if ( match ( TokenType : : BracketOpen ) ) {
consume ( TokenType : : BracketOpen ) ;
2021-06-11 15:43:28 +00:00
auto result = parse_expression ( 2 ) ;
2020-06-08 18:31:21 +00:00
consume ( TokenType : : BracketClose ) ;
return result ;
} else {
if ( ! match_identifier_name ( ) )
expected ( " IdentifierName " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . value ( ) ) ;
2020-06-08 18:31:21 +00:00
}
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < ObjectExpression > Parser : : parse_object_expression ( )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : CurlyOpen ) ;
2020-03-21 00:29:00 +00:00
2020-05-29 05:50:06 +00:00
NonnullRefPtrVector < ObjectProperty > properties ;
ObjectProperty : : Type property_type ;
2021-07-11 10:18:30 +00:00
Optional < SourceRange > invalid_object_literal_property_range ;
2020-05-29 05:50:06 +00:00
auto skip_to_next_property = [ & ] {
while ( ! done ( ) & & ! match ( TokenType : : Comma ) & & ! match ( TokenType : : CurlyOpen ) )
consume ( ) ;
} ;
2020-05-22 00:28:28 +00:00
2020-04-07 17:29:37 +00:00
while ( ! done ( ) & & ! match ( TokenType : : CurlyClose ) ) {
2020-05-29 05:50:06 +00:00
property_type = ObjectProperty : : Type : : KeyValue ;
RefPtr < Expression > property_name ;
2020-04-23 18:37:53 +00:00
RefPtr < Expression > property_value ;
2021-06-14 10:22:59 +00:00
FunctionKind function_kind { FunctionKind : : Regular } ;
2020-04-28 04:52:47 +00:00
2020-05-29 05:50:06 +00:00
if ( match ( TokenType : : TripleDot ) ) {
consume ( ) ;
property_name = parse_expression ( 4 ) ;
2021-06-19 12:43:09 +00:00
properties . append ( create_ast_node < ObjectProperty > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , * property_name , nullptr , ObjectProperty : : Type : : Spread , false ) ) ;
2020-05-29 05:50:06 +00:00
if ( ! match ( TokenType : : Comma ) )
break ;
consume ( TokenType : : Comma ) ;
continue ;
}
2021-06-14 10:22:59 +00:00
if ( match ( TokenType : : Asterisk ) ) {
consume ( ) ;
property_type = ObjectProperty : : Type : : KeyValue ;
property_name = parse_property_key ( ) ;
function_kind = FunctionKind : : Generator ;
} else if ( match ( TokenType : : Identifier ) ) {
2020-04-23 18:37:53 +00:00
auto identifier = consume ( ) . value ( ) ;
2020-05-29 05:50:06 +00:00
if ( identifier = = " get " & & match_property_key ( ) ) {
property_type = ObjectProperty : : Type : : Getter ;
property_name = parse_property_key ( ) ;
} else if ( identifier = = " set " & & match_property_key ( ) ) {
property_type = ObjectProperty : : Type : : Setter ;
property_name = parse_property_key ( ) ;
} else {
2021-06-19 12:43:09 +00:00
property_name = create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , identifier ) ;
property_value = create_ast_node < Identifier > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , identifier ) ;
2020-05-22 00:28:28 +00:00
}
2020-04-06 20:17:05 +00:00
} else {
2020-05-29 05:50:06 +00:00
property_name = parse_property_key ( ) ;
}
if ( property_type = = ObjectProperty : : Type : : Getter | | property_type = = ObjectProperty : : Type : : Setter ) {
if ( ! match ( TokenType : : ParenOpen ) ) {
2021-07-11 19:00:55 +00:00
expected ( " '(' for object getter or setter property " ) ;
2020-05-29 05:50:06 +00:00
skip_to_next_property ( ) ;
2020-05-22 00:28:28 +00:00
continue ;
}
2020-04-06 20:17:05 +00:00
}
2021-07-11 10:18:30 +00:00
if ( match ( TokenType : : Equals ) ) {
// Not a valid object literal, but a valid assignment target
consume ( ) ;
// Parse the expression and throw it away
auto expression = parse_expression ( 2 ) ;
if ( ! invalid_object_literal_property_range . has_value ( ) )
invalid_object_literal_property_range = expression - > source_range ( ) ;
} else if ( match ( TokenType : : ParenOpen ) ) {
2021-02-23 19:42:32 +00:00
VERIFY ( property_name ) ;
2020-10-20 17:32:51 +00:00
u8 parse_options = FunctionNodeParseOptions : : AllowSuperPropertyLookup ;
if ( property_type = = ObjectProperty : : Type : : Getter )
parse_options | = FunctionNodeParseOptions : : IsGetterFunction ;
if ( property_type = = ObjectProperty : : Type : : Setter )
parse_options | = FunctionNodeParseOptions : : IsSetterFunction ;
2021-06-14 10:22:59 +00:00
if ( function_kind = = FunctionKind : : Generator )
parse_options | = FunctionNodeParseOptions : : IsGeneratorFunction ;
2020-10-20 17:32:51 +00:00
auto function = parse_function_node < FunctionExpression > ( parse_options ) ;
2021-06-19 12:43:09 +00:00
properties . append ( create_ast_node < ObjectProperty > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , * property_name , function , property_type , true ) ) ;
2020-05-29 05:50:06 +00:00
} else if ( match ( TokenType : : Colon ) ) {
2020-06-01 14:08:34 +00:00
if ( ! property_name ) {
2021-07-11 19:00:55 +00:00
expected ( " a property name " ) ;
2020-06-01 14:08:34 +00:00
skip_to_next_property ( ) ;
continue ;
}
2020-05-29 05:50:06 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
properties . append ( create_ast_node < ObjectProperty > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , * property_name , parse_expression ( 2 ) , property_type , false ) ) ;
2020-06-01 14:08:34 +00:00
} else if ( property_name & & property_value ) {
2021-06-19 12:43:09 +00:00
properties . append ( create_ast_node < ObjectProperty > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , * property_name , * property_value , property_type , false ) ) ;
2020-06-01 14:08:34 +00:00
} else {
2021-07-11 19:00:55 +00:00
expected ( " a property " ) ;
2020-06-01 14:08:34 +00:00
skip_to_next_property ( ) ;
continue ;
2020-03-21 00:29:00 +00:00
}
if ( ! match ( TokenType : : Comma ) )
break ;
consume ( TokenType : : Comma ) ;
}
2020-03-11 18:27:43 +00:00
consume ( TokenType : : CurlyClose ) ;
2021-07-11 10:18:30 +00:00
return create_ast_node < ObjectExpression > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
move ( properties ) ,
move ( invalid_object_literal_property_range ) ) ;
2020-03-11 18:27:43 +00:00
}
2020-03-20 19:29:57 +00:00
NonnullRefPtr < ArrayExpression > Parser : : parse_array_expression ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-20 19:29:57 +00:00
consume ( TokenType : : BracketOpen ) ;
2020-04-15 19:09:06 +00:00
Vector < RefPtr < Expression > > elements ;
2020-04-27 06:05:37 +00:00
while ( match_expression ( ) | | match ( TokenType : : TripleDot ) | | match ( TokenType : : Comma ) ) {
2020-04-15 19:09:06 +00:00
RefPtr < Expression > expression ;
2020-04-27 06:05:37 +00:00
if ( match ( TokenType : : TripleDot ) ) {
consume ( TokenType : : TripleDot ) ;
2021-06-19 12:43:09 +00:00
expression = create_ast_node < SpreadExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , parse_expression ( 2 ) ) ;
2020-04-27 06:05:37 +00:00
} else if ( match_expression ( ) ) {
2020-05-11 16:27:31 +00:00
expression = parse_expression ( 2 ) ;
2020-04-27 06:05:37 +00:00
}
2020-04-15 19:09:06 +00:00
elements . append ( expression ) ;
2020-03-20 19:29:57 +00:00
if ( ! match ( TokenType : : Comma ) )
break ;
consume ( TokenType : : Comma ) ;
}
consume ( TokenType : : BracketClose ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ArrayExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( elements ) ) ;
2020-03-20 19:29:57 +00:00
}
2021-02-24 08:58:24 +00:00
NonnullRefPtr < StringLiteral > Parser : : parse_string_literal ( const Token & token , bool in_template_literal )
2020-05-17 06:27:25 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-05-17 06:27:25 +00:00
auto status = Token : : StringValueStatus : : Ok ;
auto string = token . string_value ( status ) ;
if ( status ! = Token : : StringValueStatus : : Ok ) {
String message ;
2020-10-24 12:30:57 +00:00
if ( status = = Token : : StringValueStatus : : LegacyOctalEscapeSequence ) {
2021-06-19 12:43:09 +00:00
m_state . string_legacy_octal_escape_sequence_in_scope = true ;
2020-10-24 12:30:57 +00:00
if ( in_template_literal )
message = " Octal escape sequence not allowed in template literal " ;
2021-06-19 12:43:09 +00:00
else if ( m_state . strict_mode )
2020-10-24 12:30:57 +00:00
message = " Octal escape sequence in string literal not allowed in strict mode " ;
} else if ( status = = Token : : StringValueStatus : : MalformedHexEscape | | status = = Token : : StringValueStatus : : MalformedUnicodeEscape ) {
2020-05-17 06:27:25 +00:00
auto type = status = = Token : : StringValueStatus : : MalformedUnicodeEscape ? " unicode " : " hexadecimal " ;
2020-10-04 13:53:15 +00:00
message = String : : formatted ( " Malformed {} escape sequence " , type ) ;
2020-05-17 06:27:25 +00:00
} else if ( status = = Token : : StringValueStatus : : UnicodeEscapeOverflow ) {
2020-08-05 20:31:20 +00:00
message = " Unicode code_point must not be greater than 0x10ffff in escape sequence " ;
2020-10-24 12:30:57 +00:00
} else {
2021-02-23 19:42:32 +00:00
VERIFY_NOT_REACHED ( ) ;
2020-05-17 06:27:25 +00:00
}
2020-10-24 11:48:25 +00:00
if ( ! message . is_empty ( ) )
2020-11-02 21:03:19 +00:00
syntax_error ( message , Position { token . line_number ( ) , token . line_column ( ) } ) ;
2020-05-17 06:27:25 +00:00
}
2020-05-28 05:22:08 +00:00
2020-11-01 21:49:25 +00:00
auto is_use_strict_directive = ! in_template_literal & & ( token . value ( ) = = " 'use strict' " | | token . value ( ) = = " \" use strict \" " ) ;
2020-10-24 12:30:57 +00:00
2021-06-19 12:43:09 +00:00
return create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , string , is_use_strict_directive ) ;
2020-05-17 06:27:25 +00:00
}
2020-05-06 23:34:14 +00:00
NonnullRefPtr < TemplateLiteral > Parser : : parse_template_literal ( bool is_tagged )
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
consume ( TokenType : : TemplateLiteralStart ) ;
NonnullRefPtrVector < Expression > expressions ;
2020-05-06 23:34:14 +00:00
NonnullRefPtrVector < Expression > raw_strings ;
2020-12-28 17:15:22 +00:00
auto append_empty_string = [ this , & rule_start , & expressions , & raw_strings , is_tagged ] ( ) {
2021-06-19 12:43:09 +00:00
auto string_literal = create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , " " ) ;
2020-05-06 23:34:14 +00:00
expressions . append ( string_literal ) ;
if ( is_tagged )
raw_strings . append ( string_literal ) ;
} ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
2020-05-06 09:17:35 +00:00
if ( ! match ( TokenType : : TemplateLiteralString ) )
2020-05-06 23:34:14 +00:00
append_empty_string ( ) ;
2020-05-06 09:17:35 +00:00
2020-06-03 22:36:25 +00:00
while ( ! done ( ) & & ! match ( TokenType : : TemplateLiteralEnd ) & & ! match ( TokenType : : UnterminatedTemplateLiteral ) ) {
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
if ( match ( TokenType : : TemplateLiteralString ) ) {
2020-05-06 23:34:14 +00:00
auto token = consume ( ) ;
2020-10-24 12:30:57 +00:00
expressions . append ( parse_string_literal ( token , true ) ) ;
2020-05-06 23:34:14 +00:00
if ( is_tagged )
2021-06-19 12:43:09 +00:00
raw_strings . append ( create_ast_node < StringLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , token . value ( ) ) ) ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
} else if ( match ( TokenType : : TemplateLiteralExprStart ) ) {
consume ( TokenType : : TemplateLiteralExprStart ) ;
if ( match ( TokenType : : TemplateLiteralExprEnd ) ) {
syntax_error ( " Empty template literal expression block " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < TemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , expressions ) ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
}
expressions . append ( parse_expression ( 0 ) ) ;
if ( match ( TokenType : : UnterminatedTemplateLiteral ) ) {
syntax_error ( " Unterminated template literal " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < TemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , expressions ) ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
}
consume ( TokenType : : TemplateLiteralExprEnd ) ;
2020-05-06 09:17:35 +00:00
if ( ! match ( TokenType : : TemplateLiteralString ) )
2020-05-06 23:34:14 +00:00
append_empty_string ( ) ;
2020-06-03 22:36:25 +00:00
} else {
expected ( " Template literal string or expression " ) ;
break ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
}
}
if ( match ( TokenType : : UnterminatedTemplateLiteral ) ) {
syntax_error ( " Unterminated template literal " ) ;
} else {
consume ( TokenType : : TemplateLiteralEnd ) ;
}
2020-05-06 23:34:14 +00:00
if ( is_tagged )
2021-06-19 12:43:09 +00:00
return create_ast_node < TemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , expressions , raw_strings ) ;
return create_ast_node < TemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , expressions ) ;
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
}
2021-02-24 08:58:24 +00:00
NonnullRefPtr < Expression > Parser : : parse_expression ( int min_precedence , Associativity associativity , const Vector < TokenType > & forbidden )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-14 11:16:41 +00:00
auto [ expression , should_continue_parsing ] = parse_primary_expression ( ) ;
2021-07-11 10:18:30 +00:00
auto check_for_invalid_object_property = [ & ] ( auto & expression ) {
if ( is < ObjectExpression > ( * expression ) ) {
if ( auto range = static_cast < ObjectExpression & > ( * expression ) . invalid_property_range ( ) ; range . has_value ( ) )
syntax_error ( " Invalid property in object literal " , range - > start ) ;
}
} ;
2020-05-06 09:17:35 +00:00
while ( match ( TokenType : : TemplateLiteralStart ) ) {
2020-05-06 23:34:14 +00:00
auto template_literal = parse_template_literal ( true ) ;
2021-06-19 12:43:09 +00:00
expression = create_ast_node < TaggedTemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expression ) , move ( template_literal ) ) ;
2020-05-06 09:17:35 +00:00
}
2021-06-14 11:16:41 +00:00
if ( should_continue_parsing ) {
while ( match_secondary_expression ( forbidden ) ) {
2021-06-19 12:43:09 +00:00
int new_precedence = g_operator_precedence . get ( m_state . current_token . type ( ) ) ;
2021-06-14 11:16:41 +00:00
if ( new_precedence < min_precedence )
break ;
if ( new_precedence = = min_precedence & & associativity = = Associativity : : Left )
break ;
2021-07-11 10:18:30 +00:00
check_for_invalid_object_property ( expression ) ;
2020-03-12 22:02:41 +00:00
2021-06-19 12:43:09 +00:00
Associativity new_associativity = operator_associativity ( m_state . current_token . type ( ) ) ;
2021-06-14 11:16:41 +00:00
expression = parse_secondary_expression ( move ( expression ) , new_precedence , new_associativity ) ;
while ( match ( TokenType : : TemplateLiteralStart ) ) {
auto template_literal = parse_template_literal ( true ) ;
2021-06-19 12:43:09 +00:00
expression = create_ast_node < TaggedTemplateLiteral > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expression ) , move ( template_literal ) ) ;
2021-06-14 11:16:41 +00:00
}
2020-05-06 09:17:35 +00:00
}
2020-03-11 18:27:43 +00:00
}
2021-07-11 10:18:30 +00:00
check_for_invalid_object_property ( expression ) ;
2020-05-11 16:27:31 +00:00
if ( match ( TokenType : : Comma ) & & min_precedence < = 1 ) {
NonnullRefPtrVector < Expression > expressions ;
expressions . append ( expression ) ;
while ( match ( TokenType : : Comma ) ) {
consume ( ) ;
expressions . append ( parse_expression ( 2 ) ) ;
}
2021-06-19 12:43:09 +00:00
expression = create_ast_node < SequenceExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expressions ) ) ;
2020-05-11 16:27:31 +00:00
}
2020-03-11 18:27:43 +00:00
return expression ;
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < Expression > Parser : : parse_secondary_expression ( NonnullRefPtr < Expression > lhs , int min_precedence , Associativity associativity )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-03-11 18:27:43 +00:00
case TokenType : : Plus :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Addition , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:09:15 +00:00
case TokenType : : PlusEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : AdditionAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-11 18:27:43 +00:00
case TokenType : : Minus :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Subtraction , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:09:15 +00:00
case TokenType : : MinusEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : SubtractionAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-12 12:04:52 +00:00
case TokenType : : Asterisk :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Multiplication , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:09:15 +00:00
case TokenType : : AsteriskEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : MultiplicationAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-12 12:04:52 +00:00
case TokenType : : Slash :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Division , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:09:15 +00:00
case TokenType : : SlashEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : DivisionAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-04 19:17:34 +00:00
case TokenType : : Percent :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Modulo , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-05-04 22:07:05 +00:00
case TokenType : : PercentEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : ModuloAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-12 22:42:14 +00:00
case TokenType : : DoubleAsterisk :
2020-04-05 12:40:00 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : Exponentiation , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-05-04 22:03:35 +00:00
case TokenType : : DoubleAsteriskEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : ExponentiationAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-12 12:10:27 +00:00
case TokenType : : GreaterThan :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : GreaterThan , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:07:08 +00:00
case TokenType : : GreaterThanEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : GreaterThanEquals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:10:27 +00:00
case TokenType : : LessThan :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : LessThan , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:07:08 +00:00
case TokenType : : LessThanEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : LessThanEquals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:11:33 +00:00
case TokenType : : EqualsEqualsEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : TypedEquals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-12 12:11:33 +00:00
case TokenType : : ExclamationMarkEqualsEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : TypedInequals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-15 22:23:38 +00:00
case TokenType : : EqualsEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : AbstractEquals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-15 22:23:38 +00:00
case TokenType : : ExclamationMarkEquals :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : AbstractInequals , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-04-23 15:06:01 +00:00
case TokenType : : In :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : In , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-03-28 15:56:54 +00:00
case TokenType : : Instanceof :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : InstanceOf , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-04-03 12:02:31 +00:00
case TokenType : : Ampersand :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : BitwiseAnd , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-05-04 21:34:45 +00:00
case TokenType : : AmpersandEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : BitwiseAndAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-03 12:02:31 +00:00
case TokenType : : Pipe :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : BitwiseOr , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-05-04 21:34:45 +00:00
case TokenType : : PipeEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : BitwiseOrAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-03 12:02:31 +00:00
case TokenType : : Caret :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : BitwiseXor , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-05-04 21:34:45 +00:00
case TokenType : : CaretEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : BitwiseXorAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-23 12:36:14 +00:00
case TokenType : : ShiftLeft :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : LeftShift , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-04-23 12:36:14 +00:00
case TokenType : : ShiftLeftEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : LeftShiftAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-23 12:45:19 +00:00
case TokenType : : ShiftRight :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : RightShift , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-04-23 12:45:19 +00:00
case TokenType : : ShiftRightEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : RightShiftAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-23 14:43:10 +00:00
case TokenType : : UnsignedShiftRight :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BinaryExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , BinaryOp : : UnsignedRightShift , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-04-23 14:43:10 +00:00
case TokenType : : UnsignedShiftRightEquals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : UnsignedRightShiftAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-11 18:27:43 +00:00
case TokenType : : ParenOpen :
return parse_call_expression ( move ( lhs ) ) ;
case TokenType : : Equals :
2020-10-04 22:58:57 +00:00
return parse_assignment_expression ( AssignmentOp : : Assignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-12 12:05:57 +00:00
case TokenType : : Period :
consume ( ) ;
2020-04-18 18:31:27 +00:00
if ( ! match_identifier_name ( ) )
expected ( " IdentifierName " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < MemberExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( lhs ) , create_ast_node < Identifier > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , consume ( ) . value ( ) ) ) ;
2020-03-20 19:51:03 +00:00
case TokenType : : BracketOpen : {
consume ( TokenType : : BracketOpen ) ;
2021-06-19 12:43:09 +00:00
auto expression = create_ast_node < MemberExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( lhs ) , parse_expression ( 0 ) , true ) ;
2020-03-20 19:51:03 +00:00
consume ( TokenType : : BracketClose ) ;
return expression ;
}
2020-03-12 11:45:45 +00:00
case TokenType : : PlusPlus :
2020-09-16 19:03:06 +00:00
// FIXME: Apparently for functions this should also not be enforced on a parser level,
// other engines throw ReferenceError for foo()++
2021-01-01 18:34:07 +00:00
if ( ! is < Identifier > ( * lhs ) & & ! is < MemberExpression > ( * lhs ) )
2020-10-04 13:53:15 +00:00
syntax_error ( String : : formatted ( " Left-hand side of postfix increment operator must be identifier or member expression, got {} " , lhs - > class_name ( ) ) ) ;
2020-03-12 11:45:45 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UpdateExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UpdateOp : : Increment , move ( lhs ) ) ;
2020-03-12 11:45:45 +00:00
case TokenType : : MinusMinus :
2020-09-16 19:03:06 +00:00
// FIXME: Apparently for functions this should also not be enforced on a parser level,
// other engines throw ReferenceError for foo()--
2021-01-01 18:34:07 +00:00
if ( ! is < Identifier > ( * lhs ) & & ! is < MemberExpression > ( * lhs ) )
2020-10-04 13:53:15 +00:00
syntax_error ( String : : formatted ( " Left-hand side of postfix increment operator must be identifier or member expression, got {} " , lhs - > class_name ( ) ) ) ;
2020-03-12 11:45:45 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < UpdateExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , UpdateOp : : Decrement , move ( lhs ) ) ;
2020-03-15 21:35:22 +00:00
case TokenType : : DoubleAmpersand :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < LogicalExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , LogicalOp : : And , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-10-05 15:49:43 +00:00
case TokenType : : DoubleAmpersandEquals :
return parse_assignment_expression ( AssignmentOp : : AndAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-03-15 21:35:22 +00:00
case TokenType : : DoublePipe :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < LogicalExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , LogicalOp : : Or , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-10-05 15:49:43 +00:00
case TokenType : : DoublePipeEquals :
return parse_assignment_expression ( AssignmentOp : : OrAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-17 23:49:11 +00:00
case TokenType : : DoubleQuestionMark :
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < LogicalExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , LogicalOp : : NullishCoalescing , move ( lhs ) , parse_expression ( min_precedence , associativity ) ) ;
2020-10-05 15:49:43 +00:00
case TokenType : : DoubleQuestionMarkEquals :
return parse_assignment_expression ( AssignmentOp : : NullishAssignment , move ( lhs ) , min_precedence , associativity ) ;
2020-04-03 10:14:28 +00:00
case TokenType : : QuestionMark :
return parse_conditional_expression ( move ( lhs ) ) ;
2020-03-11 18:27:43 +00:00
default :
2020-10-22 22:30:07 +00:00
expected ( " secondary expression " ) ;
2020-03-11 18:27:43 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ErrorExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-03-11 18:27:43 +00:00
}
}
2020-10-04 22:58:57 +00:00
NonnullRefPtr < AssignmentExpression > Parser : : parse_assignment_expression ( AssignmentOp assignment_op , NonnullRefPtr < Expression > lhs , int min_precedence , Associativity associativity )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-02-23 19:42:32 +00:00
VERIFY ( match ( TokenType : : Equals )
2020-10-04 22:58:57 +00:00
| | match ( TokenType : : PlusEquals )
| | match ( TokenType : : MinusEquals )
| | match ( TokenType : : AsteriskEquals )
| | match ( TokenType : : SlashEquals )
| | match ( TokenType : : PercentEquals )
| | match ( TokenType : : DoubleAsteriskEquals )
| | match ( TokenType : : AmpersandEquals )
| | match ( TokenType : : PipeEquals )
| | match ( TokenType : : CaretEquals )
| | match ( TokenType : : ShiftLeftEquals )
| | match ( TokenType : : ShiftRightEquals )
2020-10-05 15:49:43 +00:00
| | match ( TokenType : : UnsignedShiftRightEquals )
| | match ( TokenType : : DoubleAmpersandEquals )
| | match ( TokenType : : DoublePipeEquals )
| | match ( TokenType : : DoubleQuestionMarkEquals ) ) ;
2020-10-04 22:58:57 +00:00
consume ( ) ;
2021-07-10 20:46:17 +00:00
if ( assignment_op = = AssignmentOp : : Assignment ) {
auto synthesize_binding_pattern = [ this ] ( Expression const & expression ) - > RefPtr < BindingPattern > {
// Clear any syntax error that has occurred in the range that 'expression' spans.
m_state . errors . remove_all_matching ( [ range = expression . source_range ( ) ] ( auto const & error ) {
return error . position . has_value ( ) & & range . contains ( * error . position ) ;
} ) ;
// Make a parser and parse the source for this expression as a binding pattern.
auto source = m_state . lexer . source ( ) . substring_view ( expression . source_range ( ) . start . offset - 2 , expression . source_range ( ) . end . offset - expression . source_range ( ) . start . offset ) ;
Lexer lexer { source , m_state . lexer . filename ( ) , expression . source_range ( ) . start . line , expression . source_range ( ) . start . column } ;
Parser parser { lexer } ;
2021-07-11 11:04:55 +00:00
parser . m_state . strict_mode = m_state . strict_mode ;
parser . m_state . allow_super_property_lookup = m_state . allow_super_property_lookup ;
parser . m_state . allow_super_constructor_call = m_state . allow_super_constructor_call ;
parser . m_state . in_function_context = m_state . in_function_context ;
parser . m_state . in_generator_function_context = m_state . in_generator_function_context ;
parser . m_state . in_arrow_function_context = m_state . in_arrow_function_context ;
parser . m_state . in_break_context = m_state . in_break_context ;
parser . m_state . in_continue_context = m_state . in_continue_context ;
parser . m_state . string_legacy_octal_escape_sequence_in_scope = m_state . string_legacy_octal_escape_sequence_in_scope ;
2021-07-10 20:46:17 +00:00
auto result = parser . parse_binding_pattern ( ) ;
2021-07-11 11:04:55 +00:00
if ( parser . has_errors ( ) )
m_state . errors . extend ( parser . errors ( ) ) ;
2021-07-10 20:46:17 +00:00
return result ;
} ;
if ( is < ArrayExpression > ( * lhs ) | | is < ObjectExpression > ( * lhs ) ) {
auto binding_pattern = synthesize_binding_pattern ( * lhs ) ;
if ( binding_pattern ) {
auto rhs = parse_expression ( min_precedence , associativity ) ;
return create_ast_node < AssignmentExpression > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
assignment_op ,
binding_pattern . release_nonnull ( ) ,
move ( rhs ) ) ;
}
}
}
2021-01-01 18:34:07 +00:00
if ( ! is < Identifier > ( * lhs ) & & ! is < MemberExpression > ( * lhs ) & & ! is < CallExpression > ( * lhs ) ) {
2020-10-04 22:58:57 +00:00
syntax_error ( " Invalid left-hand side in assignment " ) ;
2021-06-19 12:43:09 +00:00
} else if ( m_state . strict_mode & & is < Identifier > ( * lhs ) ) {
2020-10-04 22:58:57 +00:00
auto name = static_cast < const Identifier & > ( * lhs ) . string ( ) ;
if ( name = = " eval " | | name = = " arguments " )
syntax_error ( String : : formatted ( " '{}' cannot be assigned to in strict mode code " , name ) ) ;
2021-06-19 12:43:09 +00:00
} else if ( m_state . strict_mode & & is < CallExpression > ( * lhs ) ) {
2020-10-04 23:00:10 +00:00
syntax_error ( " Cannot assign to function call " ) ;
2020-10-04 22:58:57 +00:00
}
2021-03-22 11:44:07 +00:00
auto rhs = parse_expression ( min_precedence , associativity ) ;
if ( assignment_op = = AssignmentOp : : Assignment & & is < FunctionExpression > ( * rhs ) ) {
auto ident = lhs ;
if ( is < MemberExpression > ( * lhs ) ) {
ident = static_cast < MemberExpression & > ( * lhs ) . property ( ) ;
}
if ( is < Identifier > ( * ident ) )
static_cast < FunctionExpression & > ( * rhs ) . set_name_if_possible ( static_cast < Identifier & > ( * ident ) . string ( ) ) ;
}
2021-06-19 12:43:09 +00:00
return create_ast_node < AssignmentExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , assignment_op , move ( lhs ) , move ( rhs ) ) ;
2020-10-04 22:58:57 +00:00
}
2021-06-13 01:04:28 +00:00
NonnullRefPtr < Identifier > Parser : : parse_identifier ( )
{
auto identifier_start = position ( ) ;
2021-07-11 11:04:55 +00:00
auto token = consume_identifier ( ) ;
2021-06-13 01:04:28 +00:00
return create_ast_node < Identifier > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , identifier_start , position ( ) } ,
2021-06-13 01:04:28 +00:00
token . value ( ) ) ;
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < CallExpression > Parser : : parse_call_expression ( NonnullRefPtr < Expression > lhs )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . allow_super_constructor_call & & is < SuperExpression > ( * lhs ) )
2020-06-08 18:31:21 +00:00
syntax_error ( " 'super' keyword unexpected here " ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : ParenOpen ) ;
2020-03-12 18:35:23 +00:00
2020-05-06 05:36:24 +00:00
Vector < CallExpression : : Argument > arguments ;
2020-03-12 18:35:23 +00:00
2020-05-06 05:36:24 +00:00
while ( match_expression ( ) | | match ( TokenType : : TripleDot ) ) {
if ( match ( TokenType : : TripleDot ) ) {
consume ( ) ;
2020-05-11 16:27:31 +00:00
arguments . append ( { parse_expression ( 2 ) , true } ) ;
2020-05-06 05:36:24 +00:00
} else {
2020-05-11 16:27:31 +00:00
arguments . append ( { parse_expression ( 2 ) , false } ) ;
2020-05-06 05:36:24 +00:00
}
2020-03-12 19:03:12 +00:00
if ( ! match ( TokenType : : Comma ) )
break ;
consume ( ) ;
2020-03-12 18:35:23 +00:00
}
2020-03-11 18:27:43 +00:00
consume ( TokenType : : ParenClose ) ;
2021-07-02 17:30:38 +00:00
if ( is < SuperExpression > ( * lhs ) )
return create_ast_node < SuperCall > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( arguments ) ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < CallExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( lhs ) , move ( arguments ) ) ;
2020-03-11 18:27:43 +00:00
}
2020-03-28 15:33:52 +00:00
NonnullRefPtr < NewExpression > Parser : : parse_new_expression ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-28 15:33:52 +00:00
consume ( TokenType : : New ) ;
2020-08-18 16:46:36 +00:00
auto callee = parse_expression ( g_operator_precedence . get ( TokenType : : New ) , Associativity : : Right , { TokenType : : ParenOpen } ) ;
2020-03-28 15:33:52 +00:00
2020-05-06 05:36:24 +00:00
Vector < CallExpression : : Argument > arguments ;
2020-03-28 15:33:52 +00:00
if ( match ( TokenType : : ParenOpen ) ) {
consume ( TokenType : : ParenOpen ) ;
2020-05-06 05:36:24 +00:00
while ( match_expression ( ) | | match ( TokenType : : TripleDot ) ) {
if ( match ( TokenType : : TripleDot ) ) {
consume ( ) ;
2020-05-11 16:27:31 +00:00
arguments . append ( { parse_expression ( 2 ) , true } ) ;
2020-05-06 05:36:24 +00:00
} else {
2020-05-11 16:27:31 +00:00
arguments . append ( { parse_expression ( 2 ) , false } ) ;
2020-05-06 05:36:24 +00:00
}
2020-03-28 15:33:52 +00:00
if ( ! match ( TokenType : : Comma ) )
break ;
consume ( ) ;
}
consume ( TokenType : : ParenClose ) ;
}
2021-06-19 12:43:09 +00:00
return create_ast_node < NewExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( callee ) , move ( arguments ) ) ;
2020-03-28 15:33:52 +00:00
}
2021-06-10 21:08:30 +00:00
NonnullRefPtr < YieldExpression > Parser : : parse_yield_expression ( )
{
auto rule_start = push_start ( ) ;
consume ( TokenType : : Yield ) ;
RefPtr < Expression > argument ;
2021-06-14 11:16:41 +00:00
bool yield_from = false ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . current_token . trivia_contains_line_terminator ( ) ) {
2021-06-14 11:16:41 +00:00
if ( match ( TokenType : : Asterisk ) ) {
consume ( ) ;
yield_from = true ;
}
if ( yield_from | | match_expression ( ) )
argument = parse_expression ( 0 ) ;
}
2021-06-19 12:43:09 +00:00
return create_ast_node < YieldExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( argument ) , yield_from ) ;
2021-06-10 21:08:30 +00:00
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < ReturnStatement > Parser : : parse_return_statement ( )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . in_function_context & & ! m_state . in_arrow_function_context )
2020-10-07 18:33:48 +00:00
syntax_error ( " 'return' not allowed outside of a function " ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : Return ) ;
2020-04-17 13:05:58 +00:00
// Automatic semicolon insertion: terminate statement when return is followed by newline
2021-06-19 12:43:09 +00:00
if ( m_state . current_token . trivia_contains_line_terminator ( ) )
return create_ast_node < ReturnStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , nullptr ) ;
2020-04-17 13:05:58 +00:00
2020-03-11 18:27:43 +00:00
if ( match_expression ( ) ) {
2020-04-18 12:00:43 +00:00
auto expression = parse_expression ( 0 ) ;
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ReturnStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expression ) ) ;
2020-03-11 18:27:43 +00:00
}
2020-04-18 12:00:43 +00:00
2020-04-17 13:05:58 +00:00
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ReturnStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , nullptr ) ;
2020-03-11 18:27:43 +00:00
}
2020-03-18 10:23:53 +00:00
NonnullRefPtr < BlockStatement > Parser : : parse_block_statement ( )
2020-10-04 00:02:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-10-04 00:02:43 +00:00
bool dummy = false ;
return parse_block_statement ( dummy ) ;
}
NonnullRefPtr < BlockStatement > Parser : : parse_block_statement ( bool & is_strict )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-07-04 01:15:52 +00:00
ScopePusher scope ( * this , ScopePusher : : Let , Parser : : Scope : : Block ) ;
2021-06-19 12:43:09 +00:00
auto block = create_ast_node < BlockStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : CurlyOpen ) ;
2020-05-28 05:22:08 +00:00
bool first = true ;
2021-06-19 12:43:09 +00:00
bool initial_strict_mode_state = m_state . strict_mode ;
2020-11-01 21:49:25 +00:00
if ( initial_strict_mode_state )
2020-10-04 00:02:43 +00:00
is_strict = true ;
2020-05-28 05:22:08 +00:00
2020-03-11 18:27:43 +00:00
while ( ! done ( ) & & ! match ( TokenType : : CurlyClose ) ) {
2020-10-22 22:30:07 +00:00
if ( match_declaration ( ) ) {
block - > append ( parse_declaration ( ) ) ;
2020-03-11 18:27:43 +00:00
} else if ( match_statement ( ) ) {
2020-11-01 21:49:25 +00:00
auto statement = parse_statement ( ) ;
block - > append ( statement ) ;
if ( statement_is_use_strict_directive ( statement ) ) {
if ( first & & ! initial_strict_mode_state ) {
2020-10-04 00:02:43 +00:00
is_strict = true ;
2021-06-19 12:43:09 +00:00
m_state . strict_mode = true ;
2020-05-28 05:22:08 +00:00
}
2021-06-19 12:43:09 +00:00
if ( m_state . string_legacy_octal_escape_sequence_in_scope )
2020-11-01 21:49:25 +00:00
syntax_error ( " Octal escape sequence in string literal not allowed in strict mode " ) ;
2020-05-28 05:22:08 +00:00
}
2020-03-11 18:27:43 +00:00
} else {
2020-10-22 22:30:07 +00:00
expected ( " statement or declaration " ) ;
2020-03-11 18:27:43 +00:00
consume ( ) ;
}
2020-05-28 05:22:08 +00:00
first = false ;
2020-03-11 18:27:43 +00:00
}
2021-06-19 12:43:09 +00:00
m_state . strict_mode = initial_strict_mode_state ;
m_state . string_legacy_octal_escape_sequence_in_scope = false ;
2020-03-11 18:27:43 +00:00
consume ( TokenType : : CurlyClose ) ;
2021-07-04 01:15:52 +00:00
scope . add_to_scope_node ( block ) ;
2020-03-11 18:27:43 +00:00
return block ;
}
2020-03-19 10:52:56 +00:00
template < typename FunctionNodeType >
2020-10-20 16:56:49 +00:00
NonnullRefPtr < FunctionNodeType > Parser : : parse_function_node ( u8 parse_options )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-02-23 19:42:32 +00:00
VERIFY ( ! ( parse_options & FunctionNodeParseOptions : : IsGetterFunction & & parse_options & FunctionNodeParseOptions : : IsSetterFunction ) ) ;
2020-10-20 17:32:51 +00:00
2021-06-19 12:43:09 +00:00
TemporaryChange super_property_access_rollback ( m_state . allow_super_property_lookup , ! ! ( parse_options & FunctionNodeParseOptions : : AllowSuperPropertyLookup ) ) ;
TemporaryChange super_constructor_call_rollback ( m_state . allow_super_constructor_call , ! ! ( parse_options & FunctionNodeParseOptions : : AllowSuperConstructorCall ) ) ;
2020-06-08 18:31:21 +00:00
2021-07-04 01:15:52 +00:00
ScopePusher scope ( * this , ScopePusher : : Var , Parser : : Scope : : Function ) ;
2020-04-13 14:42:54 +00:00
2021-07-02 10:41:11 +00:00
constexpr auto is_function_expression = IsSame < FunctionNodeType , FunctionExpression > ;
2021-06-14 10:22:59 +00:00
auto is_generator = ( parse_options & FunctionNodeParseOptions : : IsGeneratorFunction ) ! = 0 ;
2020-03-19 10:52:56 +00:00
String name ;
2020-10-20 16:56:49 +00:00
if ( parse_options & FunctionNodeParseOptions : : CheckForFunctionAndName ) {
consume ( TokenType : : Function ) ;
2021-06-14 10:22:59 +00:00
if ( ! is_generator ) {
is_generator = match ( TokenType : : Asterisk ) ;
2021-06-17 17:14:45 +00:00
if ( is_generator ) {
2021-06-14 10:22:59 +00:00
consume ( TokenType : : Asterisk ) ;
2021-06-17 17:14:45 +00:00
parse_options = parse_options | FunctionNodeParseOptions : : IsGeneratorFunction ;
}
2021-06-14 10:22:59 +00:00
}
2021-06-10 21:08:30 +00:00
2021-07-11 11:04:55 +00:00
if ( FunctionNodeType : : must_have_name ( ) | | match_identifier ( ) )
name = consume_identifier ( ) . value ( ) ;
2021-07-02 10:41:11 +00:00
else if ( is_function_expression & & ( match ( TokenType : : Yield ) | | match ( TokenType : : Await ) ) )
name = consume ( ) . value ( ) ;
2020-03-19 10:52:56 +00:00
}
2020-03-11 18:27:43 +00:00
consume ( TokenType : : ParenOpen ) ;
2020-05-06 03:02:14 +00:00
i32 function_length = - 1 ;
2021-05-29 11:33:19 +00:00
auto parameters = parse_formal_parameters ( function_length , parse_options ) ;
2020-10-18 23:26:41 +00:00
consume ( TokenType : : ParenClose ) ;
if ( function_length = = - 1 )
function_length = parameters . size ( ) ;
2021-06-19 12:43:09 +00:00
TemporaryChange change ( m_state . in_function_context , true ) ;
TemporaryChange generator_change ( m_state . in_generator_function_context , m_state . in_generator_function_context | | is_generator ) ;
auto old_labels_in_scope = move ( m_state . labels_in_scope ) ;
2020-10-18 23:26:41 +00:00
ScopeGuard guard ( [ & ] ( ) {
2021-06-19 12:43:09 +00:00
m_state . labels_in_scope = move ( old_labels_in_scope ) ;
2020-10-18 23:26:41 +00:00
} ) ;
2021-06-19 12:43:09 +00:00
m_state . function_parameters . append ( parameters ) ;
2021-06-14 07:30:43 +00:00
2020-10-18 23:26:41 +00:00
bool is_strict = false ;
auto body = parse_block_statement ( is_strict ) ;
2021-06-14 07:30:43 +00:00
2021-06-19 12:43:09 +00:00
m_state . function_parameters . take_last ( ) ;
2021-06-14 07:30:43 +00:00
2021-07-04 01:15:52 +00:00
scope . add_to_scope_node ( body ) ;
2021-06-10 23:08:05 +00:00
return create_ast_node < FunctionNodeType > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
2021-07-04 01:12:27 +00:00
name , move ( body ) , move ( parameters ) , function_length ,
2021-06-10 23:08:05 +00:00
is_generator ? FunctionKind : : Generator : FunctionKind : : Regular , is_strict ) ;
2020-10-18 23:26:41 +00:00
}
2021-05-29 11:33:19 +00:00
Vector < FunctionNode : : Parameter > Parser : : parse_formal_parameters ( int & function_length , u8 parse_options )
2020-10-18 23:26:41 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-10-25 11:14:04 +00:00
bool has_default_parameter = false ;
bool has_rest_parameter = false ;
2020-10-18 23:26:41 +00:00
Vector < FunctionNode : : Parameter > parameters ;
2020-10-25 11:14:04 +00:00
2021-05-29 11:33:19 +00:00
auto consume_identifier_or_binding_pattern = [ & ] ( ) - > Variant < FlyString , NonnullRefPtr < BindingPattern > > {
if ( auto pattern = parse_binding_pattern ( ) )
return pattern . release_nonnull ( ) ;
2021-07-11 11:04:55 +00:00
auto token = consume_identifier ( ) ;
2020-10-25 11:14:04 +00:00
auto parameter_name = token . value ( ) ;
for ( auto & parameter : parameters ) {
2021-05-29 11:33:19 +00:00
if ( auto * ptr = parameter . binding . get_pointer < FlyString > ( ) ; ! ptr | | parameter_name ! = * ptr )
2020-10-25 11:14:04 +00:00
continue ;
String message ;
if ( parse_options & FunctionNodeParseOptions : : IsArrowFunction )
message = String : : formatted ( " Duplicate parameter '{}' not allowed in arrow function " , parameter_name ) ;
2021-06-19 12:43:09 +00:00
else if ( m_state . strict_mode )
2020-10-25 11:14:04 +00:00
message = String : : formatted ( " Duplicate parameter '{}' not allowed in strict mode " , parameter_name ) ;
else if ( has_default_parameter | | match ( TokenType : : Equals ) )
message = String : : formatted ( " Duplicate parameter '{}' not allowed in function with default parameter " , parameter_name ) ;
else if ( has_rest_parameter )
message = String : : formatted ( " Duplicate parameter '{}' not allowed in function with rest parameter " , parameter_name ) ;
if ( ! message . is_empty ( ) )
2020-11-02 21:03:19 +00:00
syntax_error ( message , Position { token . line_number ( ) , token . line_column ( ) } ) ;
2020-10-25 11:14:04 +00:00
break ;
}
2021-05-29 11:33:19 +00:00
return FlyString { token . value ( ) } ;
2020-10-25 11:14:04 +00:00
} ;
2021-07-11 11:04:55 +00:00
while ( match ( TokenType : : CurlyOpen ) | | match ( TokenType : : BracketOpen ) | | match_identifier ( ) | | match ( TokenType : : TripleDot ) ) {
2020-10-20 17:32:51 +00:00
if ( parse_options & FunctionNodeParseOptions : : IsGetterFunction )
syntax_error ( " Getter function must have no arguments " ) ;
2020-10-20 17:43:58 +00:00
if ( parse_options & FunctionNodeParseOptions : : IsSetterFunction & & ( parameters . size ( ) > = 1 | | match ( TokenType : : TripleDot ) ) )
2020-10-20 17:32:51 +00:00
syntax_error ( " Setter function must have one argument " ) ;
2021-05-29 11:33:19 +00:00
auto is_rest = false ;
2020-05-04 15:05:13 +00:00
if ( match ( TokenType : : TripleDot ) ) {
consume ( ) ;
2020-10-25 11:14:04 +00:00
has_rest_parameter = true ;
2020-05-06 03:02:14 +00:00
function_length = parameters . size ( ) ;
2021-05-29 11:33:19 +00:00
is_rest = true ;
2020-05-04 15:05:13 +00:00
}
2021-05-29 11:33:19 +00:00
auto parameter = consume_identifier_or_binding_pattern ( ) ;
2020-05-02 18:46:39 +00:00
RefPtr < Expression > default_value ;
if ( match ( TokenType : : Equals ) ) {
2020-10-25 11:14:04 +00:00
consume ( ) ;
2021-07-10 18:18:25 +00:00
TemporaryChange change ( m_state . in_function_context , true ) ;
2020-10-25 11:14:04 +00:00
has_default_parameter = true ;
2020-05-06 03:02:14 +00:00
function_length = parameters . size ( ) ;
2020-05-11 16:27:31 +00:00
default_value = parse_expression ( 2 ) ;
2021-06-17 17:14:45 +00:00
bool is_generator = parse_options & FunctionNodeParseOptions : : IsGeneratorFunction ;
if ( is_generator & & default_value & & default_value - > fast_is < Identifier > ( ) & & static_cast < Identifier & > ( * default_value ) . string ( ) = = " yield " sv )
syntax_error ( " Generator function parameter initializer cannot contain a reference to an identifier named \" yield \" " ) ;
2020-05-02 18:46:39 +00:00
}
2021-05-29 11:33:19 +00:00
parameters . append ( { move ( parameter ) , default_value , is_rest } ) ;
2020-05-04 15:05:13 +00:00
if ( match ( TokenType : : ParenClose ) )
2020-03-11 18:27:43 +00:00
break ;
consume ( TokenType : : Comma ) ;
2021-05-29 11:33:19 +00:00
if ( is_rest )
break ;
2020-03-11 18:27:43 +00:00
}
2020-10-20 17:32:51 +00:00
if ( parse_options & FunctionNodeParseOptions : : IsSetterFunction & & parameters . is_empty ( ) )
syntax_error ( " Setter function must have one argument " ) ;
2020-10-18 23:26:41 +00:00
return parameters ;
2020-03-11 18:27:43 +00:00
}
2021-07-11 11:04:55 +00:00
static constexpr AK : : Array < StringView , 36 > s_reserved_words = { " break " , " case " , " catch " , " class " , " const " , " continue " , " debugger " , " default " , " delete " , " do " , " else " , " enum " , " export " , " extends " , " false " , " finally " , " for " , " function " , " if " , " import " , " in " , " instanceof " , " new " , " null " , " return " , " super " , " switch " , " this " , " throw " , " true " , " try " , " typeof " , " var " , " void " , " while " , " with " } ;
2021-05-29 11:33:19 +00:00
RefPtr < BindingPattern > Parser : : parse_binding_pattern ( )
{
auto rule_start = push_start ( ) ;
TokenType closing_token ;
2021-06-13 01:04:28 +00:00
bool is_object = true ;
2021-05-29 11:33:19 +00:00
if ( match ( TokenType : : BracketOpen ) ) {
consume ( ) ;
closing_token = TokenType : : BracketClose ;
2021-06-13 01:04:28 +00:00
is_object = false ;
2021-05-29 11:33:19 +00:00
} else if ( match ( TokenType : : CurlyOpen ) ) {
consume ( ) ;
closing_token = TokenType : : CurlyClose ;
} else {
return { } ;
}
2021-06-13 01:04:28 +00:00
Vector < BindingPattern : : BindingEntry > entries ;
2021-05-29 11:33:19 +00:00
while ( ! match ( closing_token ) ) {
2021-06-13 01:04:28 +00:00
if ( ! is_object & & match ( TokenType : : Comma ) ) {
2021-05-29 11:33:19 +00:00
consume ( ) ;
2021-06-13 01:04:28 +00:00
entries . append ( BindingPattern : : BindingEntry { } ) ;
continue ;
}
2021-05-29 11:33:19 +00:00
auto is_rest = false ;
if ( match ( TokenType : : TripleDot ) ) {
consume ( ) ;
is_rest = true ;
}
2021-06-13 01:04:28 +00:00
decltype ( BindingPattern : : BindingEntry : : name ) name = Empty { } ;
decltype ( BindingPattern : : BindingEntry : : alias ) alias = Empty { } ;
RefPtr < Expression > initializer = { } ;
2021-05-29 11:33:19 +00:00
2021-06-13 01:04:28 +00:00
if ( is_object ) {
2021-07-11 11:04:55 +00:00
if ( match_identifier_name ( ) ) {
name = create_ast_node < Identifier > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
consume ( ) . value ( ) ) ;
2021-06-13 01:04:28 +00:00
} else if ( match ( TokenType : : BracketOpen ) ) {
2021-05-29 11:33:19 +00:00
consume ( ) ;
2021-06-13 01:04:28 +00:00
name = parse_expression ( 0 ) ;
2021-07-11 10:19:42 +00:00
consume ( TokenType : : BracketClose ) ;
2021-06-13 01:04:28 +00:00
} else {
2021-07-11 19:00:55 +00:00
expected ( " identifier or computed property name " ) ;
2021-06-13 01:04:28 +00:00
return { } ;
2021-05-29 11:33:19 +00:00
}
2021-06-13 01:04:28 +00:00
if ( ! is_rest & & match ( TokenType : : Colon ) ) {
2021-05-29 11:33:19 +00:00
consume ( ) ;
2021-06-13 01:04:28 +00:00
if ( match ( TokenType : : CurlyOpen ) | | match ( TokenType : : BracketOpen ) ) {
auto binding_pattern = parse_binding_pattern ( ) ;
if ( ! binding_pattern )
return { } ;
alias = binding_pattern . release_nonnull ( ) ;
} else if ( match_identifier_name ( ) ) {
2021-07-11 11:04:55 +00:00
alias = create_ast_node < Identifier > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
consume ( ) . value ( ) ) ;
2021-06-13 01:04:28 +00:00
} else {
2021-07-11 19:00:55 +00:00
expected ( " identifier or binding pattern " ) ;
2021-06-13 01:04:28 +00:00
return { } ;
}
}
} else {
2021-07-11 11:04:55 +00:00
if ( match_identifier_name ( ) ) {
2021-06-13 01:04:28 +00:00
// BindingElement must always have an Empty name field
2021-07-11 11:04:55 +00:00
alias = create_ast_node < Identifier > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
consume ( ) . value ( ) ) ;
2021-06-13 01:04:28 +00:00
} else if ( match ( TokenType : : BracketOpen ) | | match ( TokenType : : CurlyOpen ) ) {
auto pattern = parse_binding_pattern ( ) ;
if ( ! pattern ) {
2021-07-11 19:00:55 +00:00
expected ( " binding pattern " ) ;
2021-06-13 01:04:28 +00:00
return { } ;
}
alias = pattern . release_nonnull ( ) ;
} else {
2021-07-11 19:00:55 +00:00
expected ( " identifier or binding pattern " ) ;
2021-06-13 01:04:28 +00:00
return { } ;
2021-05-29 11:33:19 +00:00
}
}
2021-06-13 01:04:28 +00:00
if ( match ( TokenType : : Equals ) ) {
if ( is_rest ) {
syntax_error ( " Unexpected initializer after rest element " ) ;
return { } ;
2021-05-29 11:33:19 +00:00
}
2021-06-13 01:04:28 +00:00
consume ( ) ;
initializer = parse_expression ( 2 ) ;
if ( ! initializer ) {
2021-07-11 19:00:55 +00:00
expected ( " initialization expression " ) ;
2021-06-13 01:04:28 +00:00
return { } ;
}
2021-05-29 11:33:19 +00:00
}
2021-06-13 01:04:28 +00:00
entries . append ( BindingPattern : : BindingEntry { move ( name ) , move ( alias ) , move ( initializer ) , is_rest } ) ;
if ( match ( TokenType : : Comma ) ) {
if ( is_rest ) {
syntax_error ( " Rest element may not be followed by a comma " ) ;
return { } ;
}
consume ( ) ;
}
2021-05-29 11:33:19 +00:00
}
2021-06-13 01:04:28 +00:00
while ( ! is_object & & match ( TokenType : : Comma ) )
2021-05-29 11:33:19 +00:00
consume ( ) ;
consume ( closing_token ) ;
2021-06-13 01:04:28 +00:00
auto kind = is_object ? BindingPattern : : Kind : : Object : BindingPattern : : Kind : : Array ;
auto pattern = adopt_ref ( * new BindingPattern ) ;
pattern - > entries = move ( entries ) ;
pattern - > kind = kind ;
2021-07-11 11:04:55 +00:00
pattern - > for_each_bound_name ( [ this ] ( auto & name ) { check_identifier_name_for_assignment_validity ( name ) ; } ) ;
2021-05-29 11:33:19 +00:00
return pattern ;
}
2020-10-30 19:08:45 +00:00
NonnullRefPtr < VariableDeclaration > Parser : : parse_variable_declaration ( bool for_loop_variable_declaration )
2020-03-11 18:27:43 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-08 09:59:18 +00:00
DeclarationKind declaration_kind ;
2020-03-12 11:16:48 +00:00
2021-06-19 12:43:09 +00:00
switch ( m_state . current_token . type ( ) ) {
2020-03-12 11:16:48 +00:00
case TokenType : : Var :
2020-04-08 09:59:18 +00:00
declaration_kind = DeclarationKind : : Var ;
2020-03-12 11:16:48 +00:00
break ;
case TokenType : : Let :
2020-04-08 09:59:18 +00:00
declaration_kind = DeclarationKind : : Let ;
2020-03-12 11:16:48 +00:00
break ;
2020-03-12 12:24:34 +00:00
case TokenType : : Const :
2020-04-08 09:59:18 +00:00
declaration_kind = DeclarationKind : : Const ;
2020-03-12 12:24:34 +00:00
break ;
2020-03-12 11:16:48 +00:00
default :
2021-02-23 19:42:32 +00:00
VERIFY_NOT_REACHED ( ) ;
2020-03-12 11:16:48 +00:00
}
2020-10-30 19:08:45 +00:00
consume ( ) ;
2020-04-04 19:46:25 +00:00
NonnullRefPtrVector < VariableDeclarator > declarations ;
for ( ; ; ) {
2021-05-29 11:33:19 +00:00
Variant < NonnullRefPtr < Identifier > , NonnullRefPtr < BindingPattern > , Empty > target { Empty ( ) } ;
2021-07-11 11:04:55 +00:00
if ( match_identifier ( ) ) {
auto name = consume_identifier ( ) . value ( ) ;
2021-05-29 11:33:19 +00:00
target = create_ast_node < Identifier > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
2021-07-11 11:04:55 +00:00
name ) ;
if ( ( declaration_kind = = DeclarationKind : : Let | | declaration_kind = = DeclarationKind : : Const ) & & name = = " let " sv )
syntax_error ( " Lexical binding may not be called 'let' " ) ;
2021-05-29 11:33:19 +00:00
} else if ( auto pattern = parse_binding_pattern ( ) ) {
target = pattern . release_nonnull ( ) ;
2021-07-11 11:04:55 +00:00
if ( ( declaration_kind = = DeclarationKind : : Let | | declaration_kind = = DeclarationKind : : Const ) ) {
target . get < NonnullRefPtr < BindingPattern > > ( ) - > for_each_bound_name ( [ this ] ( auto & name ) {
if ( name = = " let " sv )
syntax_error ( " Lexical binding may not be called 'let' " ) ;
} ) ;
}
2021-07-02 10:25:34 +00:00
} else if ( ! m_state . in_generator_function_context & & match ( TokenType : : Yield ) ) {
target = create_ast_node < Identifier > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
consume ( ) . value ( ) ) ;
2021-05-29 11:33:19 +00:00
}
if ( target . has < Empty > ( ) ) {
2021-07-11 19:00:55 +00:00
expected ( " identifier or a binding pattern " ) ;
2021-05-29 11:33:19 +00:00
if ( match ( TokenType : : Comma ) ) {
consume ( ) ;
continue ;
}
break ;
}
2020-04-04 19:46:25 +00:00
RefPtr < Expression > init ;
if ( match ( TokenType : : Equals ) ) {
consume ( ) ;
2020-05-11 16:27:31 +00:00
init = parse_expression ( 2 ) ;
2020-10-30 19:08:45 +00:00
} else if ( ! for_loop_variable_declaration & & declaration_kind = = DeclarationKind : : Const ) {
syntax_error ( " Missing initializer in 'const' variable declaration " ) ;
2021-07-02 10:25:53 +00:00
} else if ( ! for_loop_variable_declaration & & target . has < NonnullRefPtr < BindingPattern > > ( ) ) {
2021-05-29 11:33:19 +00:00
syntax_error ( " Missing initializer in destructuring assignment " ) ;
2020-04-04 19:46:25 +00:00
}
2021-05-29 11:33:19 +00:00
if ( init & & is < FunctionExpression > ( * init ) & & target . has < NonnullRefPtr < Identifier > > ( ) ) {
static_cast < FunctionExpression & > ( * init ) . set_name_if_possible ( target . get < NonnullRefPtr < Identifier > > ( ) - > string ( ) ) ;
2021-03-22 11:44:07 +00:00
}
2021-05-29 11:33:19 +00:00
declarations . append ( create_ast_node < VariableDeclarator > (
2021-06-19 12:43:09 +00:00
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
2021-06-26 19:22:49 +00:00
move ( target ) . downcast < NonnullRefPtr < Identifier > , NonnullRefPtr < BindingPattern > > ( ) ,
2021-05-29 11:33:19 +00:00
move ( init ) ) ) ;
2020-04-04 19:46:25 +00:00
if ( match ( TokenType : : Comma ) ) {
consume ( ) ;
continue ;
}
break ;
2020-03-11 18:27:43 +00:00
}
2020-10-30 19:08:45 +00:00
if ( ! for_loop_variable_declaration )
2020-04-21 18:21:26 +00:00
consume_or_insert_semicolon ( ) ;
2020-04-17 13:05:58 +00:00
2021-06-19 12:43:09 +00:00
auto declaration = create_ast_node < VariableDeclaration > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , declaration_kind , move ( declarations ) ) ;
2021-07-05 19:45:34 +00:00
if ( declaration_kind = = DeclarationKind : : Var ) {
2021-06-19 12:43:09 +00:00
m_state . var_scopes . last ( ) . append ( declaration ) ;
2021-07-05 19:45:34 +00:00
} else {
2021-06-19 12:43:09 +00:00
m_state . let_scopes . last ( ) . append ( declaration ) ;
2021-07-05 19:45:34 +00:00
for ( auto & declarator : declaration - > declarations ( ) ) {
declarator . target ( ) . visit (
[ & ] ( const NonnullRefPtr < Identifier > & id ) {
m_state . current_scope - > lexical_declarations . set ( id - > string ( ) ) ;
} ,
[ & ] ( const NonnullRefPtr < BindingPattern > & binding ) {
binding - > for_each_bound_name ( [ & ] ( const auto & name ) {
m_state . current_scope - > lexical_declarations . set ( name ) ;
} ) ;
} ) ;
}
}
2020-04-13 14:42:54 +00:00
return declaration ;
2020-03-11 18:27:43 +00:00
}
2020-03-24 21:03:50 +00:00
NonnullRefPtr < ThrowStatement > Parser : : parse_throw_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-24 21:03:50 +00:00
consume ( TokenType : : Throw ) ;
2020-04-17 13:05:58 +00:00
// Automatic semicolon insertion: terminate statement when throw is followed by newline
2021-06-19 12:43:09 +00:00
if ( m_state . current_token . trivia_contains_line_terminator ( ) ) {
2020-04-29 20:37:51 +00:00
syntax_error ( " No line break is allowed between 'throw' and its expression " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ThrowStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , create_ast_node < ErrorExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ) ;
2020-04-17 13:05:58 +00:00
}
auto expression = parse_expression ( 0 ) ;
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ThrowStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( expression ) ) ;
2020-03-24 21:03:50 +00:00
}
2020-03-29 11:09:54 +00:00
NonnullRefPtr < BreakStatement > Parser : : parse_break_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-29 11:09:54 +00:00
consume ( TokenType : : Break ) ;
2020-05-28 19:02:32 +00:00
FlyString target_label ;
if ( match ( TokenType : : Semicolon ) ) {
consume ( ) ;
2020-10-07 19:21:15 +00:00
} else {
2021-06-19 12:43:09 +00:00
if ( match ( TokenType : : Identifier ) & & ! m_state . current_token . trivia_contains_line_terminator ( ) ) {
2020-10-07 19:21:15 +00:00
target_label = consume ( ) . value ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . labels_in_scope . contains ( target_label ) )
2020-10-08 17:49:08 +00:00
syntax_error ( String : : formatted ( " Label '{}' not found " , target_label ) ) ;
}
2020-10-07 19:21:15 +00:00
consume_or_insert_semicolon ( ) ;
2020-05-28 19:02:32 +00:00
}
2020-10-07 19:21:15 +00:00
2021-06-19 12:43:09 +00:00
if ( target_label . is_null ( ) & & ! m_state . in_break_context )
2020-10-07 19:21:15 +00:00
syntax_error ( " Unlabeled 'break' not allowed outside of a loop or switch statement " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < BreakStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , target_label ) ;
2020-03-29 11:09:54 +00:00
}
2020-04-04 22:22:42 +00:00
NonnullRefPtr < ContinueStatement > Parser : : parse_continue_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . in_continue_context )
2020-10-07 19:21:15 +00:00
syntax_error ( " 'continue' not allow outside of a loop " ) ;
2020-04-04 22:22:42 +00:00
consume ( TokenType : : Continue ) ;
2020-05-28 19:02:32 +00:00
FlyString target_label ;
if ( match ( TokenType : : Semicolon ) ) {
consume ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ContinueStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , target_label ) ;
2020-05-28 19:02:32 +00:00
}
2021-06-19 12:43:09 +00:00
if ( match ( TokenType : : Identifier ) & & ! m_state . current_token . trivia_contains_line_terminator ( ) ) {
2020-05-28 19:02:32 +00:00
target_label = consume ( ) . value ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . labels_in_scope . contains ( target_label ) )
2020-10-08 17:49:08 +00:00
syntax_error ( String : : formatted ( " Label '{}' not found " , target_label ) ) ;
}
2020-04-17 13:05:58 +00:00
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ContinueStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , target_label ) ;
2020-04-04 22:22:42 +00:00
}
2020-04-03 10:14:28 +00:00
NonnullRefPtr < ConditionalExpression > Parser : : parse_conditional_expression ( NonnullRefPtr < Expression > test )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-03 10:14:28 +00:00
consume ( TokenType : : QuestionMark ) ;
2020-05-28 21:42:20 +00:00
auto consequent = parse_expression ( 2 ) ;
2020-04-03 10:14:28 +00:00
consume ( TokenType : : Colon ) ;
2020-05-28 21:42:20 +00:00
auto alternate = parse_expression ( 2 ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < ConditionalExpression > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( test ) , move ( consequent ) , move ( alternate ) ) ;
2020-04-03 10:14:28 +00:00
}
2020-03-24 13:03:55 +00:00
NonnullRefPtr < TryStatement > Parser : : parse_try_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-24 13:03:55 +00:00
consume ( TokenType : : Try ) ;
auto block = parse_block_statement ( ) ;
2020-10-23 19:54:00 +00:00
RefPtr < CatchClause > handler ;
if ( match ( TokenType : : Catch ) )
handler = parse_catch_clause ( ) ;
2020-03-24 13:03:55 +00:00
RefPtr < BlockStatement > finalizer ;
if ( match ( TokenType : : Finally ) ) {
consume ( ) ;
finalizer = parse_block_statement ( ) ;
}
2020-10-23 19:54:00 +00:00
if ( ! handler & & ! finalizer )
syntax_error ( " try statement must have a 'catch' or 'finally' clause " ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < TryStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( block ) , move ( handler ) , move ( finalizer ) ) ;
2020-03-24 13:03:55 +00:00
}
2020-04-04 19:29:23 +00:00
NonnullRefPtr < DoWhileStatement > Parser : : parse_do_while_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-04 19:29:23 +00:00
consume ( TokenType : : Do ) ;
2020-04-05 14:57:01 +00:00
2020-10-07 19:21:15 +00:00
auto body = [ & ] ( ) - > NonnullRefPtr < Statement > {
2021-06-19 12:43:09 +00:00
TemporaryChange break_change ( m_state . in_break_context , true ) ;
TemporaryChange continue_change ( m_state . in_continue_context , true ) ;
2020-10-07 19:21:15 +00:00
return parse_statement ( ) ;
} ( ) ;
2020-04-05 14:57:01 +00:00
2020-04-04 19:29:23 +00:00
consume ( TokenType : : While ) ;
2020-04-05 14:57:01 +00:00
consume ( TokenType : : ParenOpen ) ;
2020-04-04 19:29:23 +00:00
auto test = parse_expression ( 0 ) ;
2020-04-05 14:57:01 +00:00
consume ( TokenType : : ParenClose ) ;
2020-10-27 19:16:23 +00:00
// Since ES 2015 a missing semicolon is inserted here, despite the regular ASI rules not applying
if ( match ( TokenType : : Semicolon ) )
consume ( ) ;
2020-04-05 14:57:01 +00:00
2021-06-19 12:43:09 +00:00
return create_ast_node < DoWhileStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( test ) , move ( body ) ) ;
2020-04-04 19:29:23 +00:00
}
2020-04-21 18:27:57 +00:00
NonnullRefPtr < WhileStatement > Parser : : parse_while_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-21 18:27:57 +00:00
consume ( TokenType : : While ) ;
consume ( TokenType : : ParenOpen ) ;
auto test = parse_expression ( 0 ) ;
consume ( TokenType : : ParenClose ) ;
2021-06-19 12:43:09 +00:00
TemporaryChange break_change ( m_state . in_break_context , true ) ;
TemporaryChange continue_change ( m_state . in_continue_context , true ) ;
2020-04-21 18:27:57 +00:00
auto body = parse_statement ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < WhileStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( test ) , move ( body ) ) ;
2020-04-21 18:27:57 +00:00
}
2020-03-29 11:09:54 +00:00
NonnullRefPtr < SwitchStatement > Parser : : parse_switch_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-29 11:09:54 +00:00
consume ( TokenType : : Switch ) ;
consume ( TokenType : : ParenOpen ) ;
auto determinant = parse_expression ( 0 ) ;
consume ( TokenType : : ParenClose ) ;
consume ( TokenType : : CurlyOpen ) ;
NonnullRefPtrVector < SwitchCase > cases ;
2020-10-18 22:12:27 +00:00
auto has_default = false ;
while ( match ( TokenType : : Case ) | | match ( TokenType : : Default ) ) {
if ( match ( TokenType : : Default ) ) {
if ( has_default )
syntax_error ( " Multiple 'default' clauses in switch statement " ) ;
has_default = true ;
}
2020-03-29 11:09:54 +00:00
cases . append ( parse_switch_case ( ) ) ;
2020-10-18 22:12:27 +00:00
}
2020-03-29 11:09:54 +00:00
consume ( TokenType : : CurlyClose ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < SwitchStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( determinant ) , move ( cases ) ) ;
2020-03-29 11:09:54 +00:00
}
2020-11-28 14:05:57 +00:00
NonnullRefPtr < WithStatement > Parser : : parse_with_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-11-28 14:05:57 +00:00
consume ( TokenType : : With ) ;
consume ( TokenType : : ParenOpen ) ;
auto object = parse_expression ( 0 ) ;
consume ( TokenType : : ParenClose ) ;
auto body = parse_statement ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < WithStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( object ) , move ( body ) ) ;
2020-11-28 14:05:57 +00:00
}
2020-03-29 11:09:54 +00:00
NonnullRefPtr < SwitchCase > Parser : : parse_switch_case ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-29 11:09:54 +00:00
RefPtr < Expression > test ;
if ( consume ( ) . type ( ) = = TokenType : : Case ) {
test = parse_expression ( 0 ) ;
}
consume ( TokenType : : Colon ) ;
NonnullRefPtrVector < Statement > consequent ;
2021-06-19 12:43:09 +00:00
TemporaryChange break_change ( m_state . in_break_context , true ) ;
2020-10-22 22:30:07 +00:00
for ( ; ; ) {
if ( match_declaration ( ) )
consequent . append ( parse_declaration ( ) ) ;
else if ( match_statement ( ) )
consequent . append ( parse_statement ( ) ) ;
else
break ;
}
2020-03-29 11:09:54 +00:00
2021-06-19 12:43:09 +00:00
return create_ast_node < SwitchCase > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( test ) , move ( consequent ) ) ;
2020-03-29 11:09:54 +00:00
}
2020-03-24 13:03:55 +00:00
NonnullRefPtr < CatchClause > Parser : : parse_catch_clause ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-03-24 13:03:55 +00:00
consume ( TokenType : : Catch ) ;
2021-07-11 10:45:38 +00:00
FlyString parameter ;
RefPtr < BindingPattern > pattern_parameter ;
auto should_expect_parameter = false ;
2020-03-24 13:03:55 +00:00
if ( match ( TokenType : : ParenOpen ) ) {
2021-07-11 10:45:38 +00:00
should_expect_parameter = true ;
2020-03-24 13:03:55 +00:00
consume ( ) ;
2021-07-11 10:45:38 +00:00
if ( match_identifier_name ( ) )
2021-07-11 11:04:55 +00:00
parameter = consume ( ) . value ( ) ;
2021-07-11 10:45:38 +00:00
else
pattern_parameter = parse_binding_pattern ( ) ;
2020-03-24 13:03:55 +00:00
consume ( TokenType : : ParenClose ) ;
}
2021-07-11 10:45:38 +00:00
if ( should_expect_parameter & & parameter . is_empty ( ) & & ! pattern_parameter )
2021-07-11 19:00:55 +00:00
expected ( " an identifier or a binding pattern " ) ;
2021-07-11 10:45:38 +00:00
2021-07-11 11:04:55 +00:00
if ( pattern_parameter )
pattern_parameter - > for_each_bound_name ( [ this ] ( auto & name ) { check_identifier_name_for_assignment_validity ( name ) ; } ) ;
if ( ! parameter . is_empty ( ) )
check_identifier_name_for_assignment_validity ( parameter ) ;
2020-03-24 13:03:55 +00:00
auto body = parse_block_statement ( ) ;
2021-07-11 10:45:38 +00:00
if ( pattern_parameter ) {
return create_ast_node < CatchClause > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
pattern_parameter . release_nonnull ( ) ,
move ( body ) ) ;
}
return create_ast_node < CatchClause > (
{ m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ,
move ( parameter ) ,
move ( body ) ) ;
2020-03-24 13:03:55 +00:00
}
2020-03-21 17:40:17 +00:00
NonnullRefPtr < IfStatement > Parser : : parse_if_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
auto parse_function_declaration_as_block_statement = [ & ] {
// https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
// Code matching this production is processed as if each matching occurrence of
// FunctionDeclaration[?Yield, ?Await, ~Default] was the sole StatementListItem
// of a BlockStatement occupying that position in the source code.
2021-07-04 01:15:52 +00:00
ScopePusher scope ( * this , ScopePusher : : Let , Parser : : Scope : : Block ) ;
2021-06-19 12:43:09 +00:00
auto block = create_ast_node < BlockStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
block - > append ( parse_declaration ( ) ) ;
2021-07-04 01:15:52 +00:00
scope . add_to_scope_node ( block ) ;
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
return block ;
} ;
2020-03-21 17:40:17 +00:00
consume ( TokenType : : If ) ;
consume ( TokenType : : ParenOpen ) ;
auto predicate = parse_expression ( 0 ) ;
consume ( TokenType : : ParenClose ) ;
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
RefPtr < Statement > consequent ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . strict_mode & & match ( TokenType : : Function ) )
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
consequent = parse_function_declaration_as_block_statement ( ) ;
else
consequent = parse_statement ( ) ;
2020-03-23 15:46:41 +00:00
RefPtr < Statement > alternate ;
if ( match ( TokenType : : Else ) ) {
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
consume ( ) ;
2021-06-19 12:43:09 +00:00
if ( ! m_state . strict_mode & & match ( TokenType : : Function ) )
LibJS: Function declarations in if statement clauses
https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses
B.3.4 FunctionDeclarations in IfStatement Statement Clauses
The following augments the IfStatement production in 13.6:
IfStatement[Yield, Await, Return] :
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else Statement[?Yield, ?Await, ?Return]
if ( Expression[+In, ?Yield, ?Await] ) Statement[?Yield, ?Await, ?Return] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default] else FunctionDeclaration[?Yield, ?Await, ~Default]
if ( Expression[+In, ?Yield, ?Await] ) FunctionDeclaration[?Yield, ?Await, ~Default]
This production only applies when parsing non-strict code. Code matching
this production is processed as if each matching occurrence of
FunctionDeclaration[?Yield, ?Await, ~Default] was the sole
StatementListItem of a BlockStatement occupying that position in the
source code. The semantics of such a synthetic BlockStatement includes
the web legacy compatibility semantics specified in B.3.3.
2020-10-31 13:37:09 +00:00
alternate = parse_function_declaration_as_block_statement ( ) ;
else
alternate = parse_statement ( ) ;
2020-03-23 15:46:41 +00:00
}
2021-06-19 12:43:09 +00:00
return create_ast_node < IfStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( predicate ) , move ( * consequent ) , move ( alternate ) ) ;
2020-03-21 17:40:17 +00:00
}
2020-04-21 18:21:26 +00:00
NonnullRefPtr < Statement > Parser : : parse_for_statement ( )
2020-03-12 12:12:12 +00:00
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-21 18:21:26 +00:00
auto match_for_in_of = [ & ] ( ) {
2021-06-19 12:43:09 +00:00
return match ( TokenType : : In ) | | ( match ( TokenType : : Identifier ) & & m_state . current_token . value ( ) = = " of " ) ;
2020-04-21 18:21:26 +00:00
} ;
2020-03-12 12:12:12 +00:00
consume ( TokenType : : For ) ;
consume ( TokenType : : ParenOpen ) ;
2020-05-04 22:58:53 +00:00
bool in_scope = false ;
2021-07-05 19:31:51 +00:00
ScopeGuard guard ( [ & ] ( ) {
if ( in_scope )
m_state . let_scopes . take_last ( ) ;
} ) ;
2020-03-23 18:08:32 +00:00
RefPtr < ASTNode > init ;
2020-04-21 18:21:26 +00:00
if ( ! match ( TokenType : : Semicolon ) ) {
2021-07-11 11:04:55 +00:00
if ( match_variable_declaration ( ) ) {
2020-04-21 18:21:26 +00:00
if ( ! match ( TokenType : : Var ) ) {
2021-06-19 12:43:09 +00:00
m_state . let_scopes . append ( NonnullRefPtrVector < VariableDeclaration > ( ) ) ;
2020-05-04 22:58:53 +00:00
in_scope = true ;
}
2020-10-30 19:08:45 +00:00
init = parse_variable_declaration ( true ) ;
2020-04-21 18:21:26 +00:00
if ( match_for_in_of ( ) )
return parse_for_in_of_statement ( * init ) ;
2020-10-30 19:08:45 +00:00
if ( static_cast < VariableDeclaration & > ( * init ) . declaration_kind ( ) = = DeclarationKind : : Const ) {
for ( auto & declaration : static_cast < VariableDeclaration & > ( * init ) . declarations ( ) ) {
if ( ! declaration . init ( ) )
syntax_error ( " Missing initializer in 'const' variable declaration " ) ;
}
}
2021-07-11 11:04:55 +00:00
} else if ( match_expression ( ) ) {
init = parse_expression ( 0 , Associativity : : Right , { TokenType : : In } ) ;
if ( match_for_in_of ( ) )
return parse_for_in_of_statement ( * init ) ;
2020-04-17 13:05:58 +00:00
} else {
2020-06-01 14:08:34 +00:00
syntax_error ( " Unexpected token in for loop " ) ;
2020-04-17 13:05:58 +00:00
}
2020-03-12 12:12:12 +00:00
}
2020-04-21 18:21:26 +00:00
consume ( TokenType : : Semicolon ) ;
2020-03-12 12:12:12 +00:00
2020-03-18 10:23:53 +00:00
RefPtr < Expression > test ;
2020-04-21 18:21:26 +00:00
if ( ! match ( TokenType : : Semicolon ) )
2020-03-12 22:02:41 +00:00
test = parse_expression ( 0 ) ;
2020-03-12 12:12:12 +00:00
consume ( TokenType : : Semicolon ) ;
2020-03-18 10:23:53 +00:00
RefPtr < Expression > update ;
2020-04-21 18:21:26 +00:00
if ( ! match ( TokenType : : ParenClose ) )
2020-03-12 22:02:41 +00:00
update = parse_expression ( 0 ) ;
2020-03-12 12:12:12 +00:00
consume ( TokenType : : ParenClose ) ;
2021-06-19 12:43:09 +00:00
TemporaryChange break_change ( m_state . in_break_context , true ) ;
TemporaryChange continue_change ( m_state . in_continue_context , true ) ;
2020-04-04 19:09:06 +00:00
auto body = parse_statement ( ) ;
2020-03-12 12:12:12 +00:00
2021-06-19 12:43:09 +00:00
return create_ast_node < ForStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( init ) , move ( test ) , move ( update ) , move ( body ) ) ;
2020-03-12 12:12:12 +00:00
}
2020-04-21 18:21:26 +00:00
NonnullRefPtr < Statement > Parser : : parse_for_in_of_statement ( NonnullRefPtr < ASTNode > lhs )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2021-01-01 18:34:07 +00:00
if ( is < VariableDeclaration > ( * lhs ) ) {
2020-10-30 19:08:45 +00:00
auto declarations = static_cast < VariableDeclaration & > ( * lhs ) . declarations ( ) ;
if ( declarations . size ( ) > 1 )
2020-04-21 18:21:26 +00:00
syntax_error ( " multiple declarations not allowed in for..in/of " ) ;
2021-05-30 06:04:52 +00:00
if ( declarations . size ( ) < 1 )
syntax_error ( " need exactly one variable declaration in for..in/of " ) ;
else if ( declarations . first ( ) . init ( ) ! = nullptr )
2020-04-21 18:21:26 +00:00
syntax_error ( " variable initializer not allowed in for..in/of " ) ;
}
auto in_or_of = consume ( ) ;
auto rhs = parse_expression ( 0 ) ;
consume ( TokenType : : ParenClose ) ;
2020-10-07 19:21:15 +00:00
2021-06-19 12:43:09 +00:00
TemporaryChange break_change ( m_state . in_break_context , true ) ;
TemporaryChange continue_change ( m_state . in_continue_context , true ) ;
2020-04-21 18:21:26 +00:00
auto body = parse_statement ( ) ;
if ( in_or_of . type ( ) = = TokenType : : In )
2021-06-19 12:43:09 +00:00
return create_ast_node < ForInStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( lhs ) , move ( rhs ) , move ( body ) ) ;
return create_ast_node < ForOfStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } , move ( lhs ) , move ( rhs ) , move ( body ) ) ;
2020-04-21 18:21:26 +00:00
}
2020-04-30 16:26:27 +00:00
NonnullRefPtr < DebuggerStatement > Parser : : parse_debugger_statement ( )
{
2020-12-28 17:15:22 +00:00
auto rule_start = push_start ( ) ;
2020-04-30 16:26:27 +00:00
consume ( TokenType : : Debugger ) ;
consume_or_insert_semicolon ( ) ;
2021-06-19 12:43:09 +00:00
return create_ast_node < DebuggerStatement > ( { m_state . current_token . filename ( ) , rule_start . position ( ) , position ( ) } ) ;
2020-04-30 16:26:27 +00:00
}
2020-03-11 18:27:43 +00:00
bool Parser : : match ( TokenType type ) const
{
2021-06-19 12:43:09 +00:00
return m_state . current_token . type ( ) = = type ;
2020-03-11 18:27:43 +00:00
}
bool Parser : : match_expression ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-03-11 18:27:43 +00:00
return type = = TokenType : : BoolLiteral
| | type = = TokenType : : NumericLiteral
2020-06-06 00:14:10 +00:00
| | type = = TokenType : : BigIntLiteral
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : StringLiteral
LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.
When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.
When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.
The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).
TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):
> `foo
> bar`
'foo
bar'
2020-05-03 22:41:14 +00:00
| | type = = TokenType : : TemplateLiteralStart
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : NullLiteral
2021-07-11 11:04:55 +00:00
| | match_identifier ( )
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : New
| | type = = TokenType : : CurlyOpen
| | type = = TokenType : : BracketOpen
2020-03-14 18:45:51 +00:00
| | type = = TokenType : : ParenOpen
2020-03-19 10:52:56 +00:00
| | type = = TokenType : : Function
2020-04-12 22:42:14 +00:00
| | type = = TokenType : : This
2020-06-08 18:31:21 +00:00
| | type = = TokenType : : Super
2020-06-03 23:05:49 +00:00
| | type = = TokenType : : RegexLiteral
2021-06-10 21:08:30 +00:00
| | type = = TokenType : : Yield
2020-03-14 18:45:51 +00:00
| | match_unary_prefixed_expression ( ) ;
}
bool Parser : : match_unary_prefixed_expression ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-03-14 18:45:51 +00:00
return type = = TokenType : : PlusPlus
| | type = = TokenType : : MinusMinus
| | type = = TokenType : : ExclamationMark
2020-03-17 19:33:32 +00:00
| | type = = TokenType : : Tilde
2020-04-02 16:58:39 +00:00
| | type = = TokenType : : Plus
| | type = = TokenType : : Minus
2020-04-15 16:55:03 +00:00
| | type = = TokenType : : Typeof
2020-04-26 11:53:40 +00:00
| | type = = TokenType : : Void
| | type = = TokenType : : Delete ;
2020-03-11 18:27:43 +00:00
}
2021-02-24 08:58:24 +00:00
bool Parser : : match_secondary_expression ( const Vector < TokenType > & forbidden ) const
2020-03-11 18:27:43 +00:00
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-04-21 18:21:26 +00:00
if ( forbidden . contains_slow ( type ) )
return false ;
2020-03-11 18:27:43 +00:00
return type = = TokenType : : Plus
2020-03-12 12:09:15 +00:00
| | type = = TokenType : : PlusEquals
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Minus
2020-03-12 12:09:15 +00:00
| | type = = TokenType : : MinusEquals
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Asterisk
2020-03-12 12:09:15 +00:00
| | type = = TokenType : : AsteriskEquals
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Slash
2020-03-12 12:09:15 +00:00
| | type = = TokenType : : SlashEquals
2020-04-04 19:17:34 +00:00
| | type = = TokenType : : Percent
2020-05-04 22:07:05 +00:00
| | type = = TokenType : : PercentEquals
2020-04-05 12:40:00 +00:00
| | type = = TokenType : : DoubleAsterisk
2020-05-04 22:03:35 +00:00
| | type = = TokenType : : DoubleAsteriskEquals
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Equals
2020-03-12 12:11:33 +00:00
| | type = = TokenType : : EqualsEqualsEquals
| | type = = TokenType : : ExclamationMarkEqualsEquals
2020-03-15 22:23:38 +00:00
| | type = = TokenType : : EqualsEquals
| | type = = TokenType : : ExclamationMarkEquals
2020-03-12 12:10:27 +00:00
| | type = = TokenType : : GreaterThan
2020-03-12 12:07:08 +00:00
| | type = = TokenType : : GreaterThanEquals
2020-03-12 12:10:27 +00:00
| | type = = TokenType : : LessThan
2020-03-12 12:07:08 +00:00
| | type = = TokenType : : LessThanEquals
2020-03-12 12:05:57 +00:00
| | type = = TokenType : : ParenOpen
2020-03-12 11:45:45 +00:00
| | type = = TokenType : : Period
2020-03-20 19:51:03 +00:00
| | type = = TokenType : : BracketOpen
2020-03-12 11:45:45 +00:00
| | type = = TokenType : : PlusPlus
2020-03-28 15:56:54 +00:00
| | type = = TokenType : : MinusMinus
2020-04-23 15:06:01 +00:00
| | type = = TokenType : : In
2020-04-03 10:14:28 +00:00
| | type = = TokenType : : Instanceof
2020-04-03 12:02:31 +00:00
| | type = = TokenType : : QuestionMark
| | type = = TokenType : : Ampersand
2020-05-04 21:34:45 +00:00
| | type = = TokenType : : AmpersandEquals
2020-04-03 12:02:31 +00:00
| | type = = TokenType : : Pipe
2020-05-04 21:34:45 +00:00
| | type = = TokenType : : PipeEquals
2020-04-03 13:33:28 +00:00
| | type = = TokenType : : Caret
2020-05-04 21:34:45 +00:00
| | type = = TokenType : : CaretEquals
2020-04-23 12:36:14 +00:00
| | type = = TokenType : : ShiftLeft
| | type = = TokenType : : ShiftLeftEquals
2020-04-23 12:45:19 +00:00
| | type = = TokenType : : ShiftRight
| | type = = TokenType : : ShiftRightEquals
2020-04-23 14:43:10 +00:00
| | type = = TokenType : : UnsignedShiftRight
| | type = = TokenType : : UnsignedShiftRightEquals
2020-04-03 13:33:28 +00:00
| | type = = TokenType : : DoubleAmpersand
2020-10-05 15:49:43 +00:00
| | type = = TokenType : : DoubleAmpersandEquals
2020-04-17 23:49:11 +00:00
| | type = = TokenType : : DoublePipe
2020-10-05 15:49:43 +00:00
| | type = = TokenType : : DoublePipeEquals
| | type = = TokenType : : DoubleQuestionMark
| | type = = TokenType : : DoubleQuestionMarkEquals ;
2020-03-11 18:27:43 +00:00
}
bool Parser : : match_statement ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-03-11 18:27:43 +00:00
return match_expression ( )
| | type = = TokenType : : Return
2021-06-10 21:08:30 +00:00
| | type = = TokenType : : Yield
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Do
| | type = = TokenType : : If
2020-03-24 21:03:50 +00:00
| | type = = TokenType : : Throw
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : Try
| | type = = TokenType : : While
2020-11-28 14:05:57 +00:00
| | type = = TokenType : : With
2020-03-12 12:12:12 +00:00
| | type = = TokenType : : For
2020-03-11 18:27:43 +00:00
| | type = = TokenType : : CurlyOpen
2020-03-29 11:09:54 +00:00
| | type = = TokenType : : Switch
| | type = = TokenType : : Break
2020-04-04 22:22:42 +00:00
| | type = = TokenType : : Continue
2020-04-30 16:26:27 +00:00
| | type = = TokenType : : Var
2020-05-03 09:59:00 +00:00
| | type = = TokenType : : Debugger
| | type = = TokenType : : Semicolon ;
2020-03-11 18:27:43 +00:00
}
2020-10-22 22:30:07 +00:00
bool Parser : : match_declaration ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-10-22 22:30:07 +00:00
return type = = TokenType : : Function
| | type = = TokenType : : Class
| | type = = TokenType : : Const
| | type = = TokenType : : Let ;
}
bool Parser : : match_variable_declaration ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-10-22 22:30:07 +00:00
return type = = TokenType : : Var
| | type = = TokenType : : Let
| | type = = TokenType : : Const ;
}
2021-07-11 11:04:55 +00:00
bool Parser : : match_identifier ( ) const
{
return m_state . current_token . type ( ) = = TokenType : : Identifier
| | m_state . current_token . type ( ) = = TokenType : : Let ; // See note in Parser::parse_identifier().
}
2020-04-18 18:31:27 +00:00
bool Parser : : match_identifier_name ( ) const
{
2021-06-19 12:43:09 +00:00
return m_state . current_token . is_identifier_name ( ) ;
2020-04-18 18:31:27 +00:00
}
2020-06-08 18:31:21 +00:00
bool Parser : : match_property_key ( ) const
{
2021-06-19 12:43:09 +00:00
auto type = m_state . current_token . type ( ) ;
2020-06-08 18:31:21 +00:00
return match_identifier_name ( )
| | type = = TokenType : : BracketOpen
| | type = = TokenType : : StringLiteral
| | type = = TokenType : : NumericLiteral
| | type = = TokenType : : BigIntLiteral ;
}
2020-03-11 18:27:43 +00:00
bool Parser : : done ( ) const
{
return match ( TokenType : : Eof ) ;
}
Token Parser : : consume ( )
{
2021-06-19 12:43:09 +00:00
auto old_token = m_state . current_token ;
m_state . current_token = m_state . lexer . next ( ) ;
2020-03-12 22:02:41 +00:00
return old_token ;
2020-03-11 18:27:43 +00:00
}
2020-04-17 13:05:58 +00:00
void Parser : : consume_or_insert_semicolon ( )
2020-03-11 18:27:43 +00:00
{
2020-04-17 13:05:58 +00:00
// Semicolon was found and will be consumed
if ( match ( TokenType : : Semicolon ) ) {
consume ( ) ;
return ;
}
// Insert semicolon if...
2020-10-02 13:59:28 +00:00
// ...token is preceded by one or more newlines
2021-06-19 12:43:09 +00:00
if ( m_state . current_token . trivia_contains_line_terminator ( ) )
2020-04-17 13:05:58 +00:00
return ;
2020-04-17 13:27:51 +00:00
// ...token is a closing curly brace
if ( match ( TokenType : : CurlyClose ) )
2020-04-17 13:05:58 +00:00
return ;
// ...token is eof
if ( match ( TokenType : : Eof ) )
return ;
// No rule for semicolon insertion applies -> syntax error
expected ( " Semicolon " ) ;
}
2021-06-16 22:57:01 +00:00
static constexpr AK : : Array < StringView , 9 > strict_reserved_words = { " implements " , " interface " , " let " , " package " , " private " , " protected " , " public " , " static " , " yield " } ;
2021-07-11 11:04:55 +00:00
Token Parser : : consume_identifier ( )
{
if ( match ( TokenType : : Identifier ) )
return consume ( TokenType : : Identifier ) ;
// Note that 'let' is not a reserved keyword, but our lexer considers it such
// As it's pretty nice to have that (for syntax highlighting and such), we'll
// special-case it here instead.
if ( match ( TokenType : : Let ) ) {
if ( m_state . strict_mode )
syntax_error ( " 'let' is not allowed as an identifier in strict mode " ) ;
return consume ( ) ;
}
expected ( " Identifier " ) ;
return consume ( ) ;
}
// https://tc39.es/ecma262/#prod-IdentifierReference
Token Parser : : consume_identifier_reference ( )
{
if ( match ( TokenType : : Identifier ) )
return consume ( TokenType : : Identifier ) ;
// See note in Parser::parse_identifier().
if ( match ( TokenType : : Let ) ) {
if ( m_state . strict_mode )
syntax_error ( " 'let' is not allowed as an identifier in strict mode " ) ;
return consume ( ) ;
}
if ( match ( TokenType : : Yield ) ) {
if ( m_state . strict_mode )
syntax_error ( " Identifier reference may not be 'yield' in strict mode " ) ;
return consume ( ) ;
}
if ( match ( TokenType : : Await ) ) {
syntax_error ( " Identifier reference may not be 'await' " ) ;
return consume ( ) ;
}
expected ( Token : : name ( TokenType : : Identifier ) ) ;
return consume ( ) ;
}
2020-04-17 13:05:58 +00:00
Token Parser : : consume ( TokenType expected_type )
{
2020-04-21 18:21:26 +00:00
if ( ! match ( expected_type ) ) {
2020-04-17 13:05:58 +00:00
expected ( Token : : name ( expected_type ) ) ;
2020-03-11 18:27:43 +00:00
}
2021-06-16 22:57:01 +00:00
auto token = consume ( ) ;
if ( expected_type = = TokenType : : Identifier ) {
2021-06-19 12:43:09 +00:00
if ( m_state . strict_mode & & any_of ( strict_reserved_words . begin ( ) , strict_reserved_words . end ( ) , [ & ] ( auto const & word ) { return word = = token . value ( ) ; } ) )
2021-06-16 22:57:01 +00:00
syntax_error ( " Identifier must not be a class-related reserved word in strict mode " ) ;
}
return token ;
2020-03-11 18:27:43 +00:00
}
2020-10-19 17:01:28 +00:00
Token Parser : : consume_and_validate_numeric_literal ( )
{
auto is_unprefixed_octal_number = [ ] ( const StringView & value ) {
2021-06-13 08:47:09 +00:00
return value . length ( ) > 1 & & value [ 0 ] = = ' 0 ' & & is_ascii_digit ( value [ 1 ] ) ;
2020-10-19 17:01:28 +00:00
} ;
2020-11-02 21:03:19 +00:00
auto literal_start = position ( ) ;
2020-10-19 17:01:28 +00:00
auto token = consume ( TokenType : : NumericLiteral ) ;
2021-06-19 12:43:09 +00:00
if ( m_state . strict_mode & & is_unprefixed_octal_number ( token . value ( ) ) )
2020-11-02 21:03:19 +00:00
syntax_error ( " Unprefixed octal number not allowed in strict mode " , literal_start ) ;
2021-06-19 12:43:09 +00:00
if ( match_identifier_name ( ) & & m_state . current_token . trivia ( ) . is_empty ( ) )
2020-10-22 21:18:31 +00:00
syntax_error ( " Numeric literal must not be immediately followed by identifier " ) ;
2020-10-19 17:01:28 +00:00
return token ;
}
2020-03-11 18:27:43 +00:00
void Parser : : expected ( const char * what )
2020-04-29 20:37:51 +00:00
{
2021-06-19 12:43:09 +00:00
auto message = m_state . current_token . message ( ) ;
2020-10-26 20:19:36 +00:00
if ( message . is_empty ( ) )
2021-06-19 12:43:09 +00:00
message = String : : formatted ( " Unexpected token {}. Expected {} " , m_state . current_token . name ( ) , what ) ;
2020-10-26 20:19:36 +00:00
syntax_error ( message ) ;
2020-04-29 20:37:51 +00:00
}
2020-12-28 17:15:22 +00:00
Position Parser : : position ( ) const
2020-03-11 18:27:43 +00:00
{
2020-11-02 21:03:19 +00:00
return {
2021-06-19 12:43:09 +00:00
m_state . current_token . line_number ( ) ,
2021-07-10 20:46:17 +00:00
m_state . current_token . line_column ( ) ,
m_state . current_token . offset ( ) ,
2020-11-02 21:03:19 +00:00
} ;
}
2021-04-11 20:41:51 +00:00
bool Parser : : try_parse_arrow_function_expression_failed_at_position ( const Position & position ) const
{
auto it = m_token_memoizations . find ( position ) ;
if ( it = = m_token_memoizations . end ( ) )
return false ;
return ( * it ) . value . try_parse_arrow_function_expression_failed ;
}
void Parser : : set_try_parse_arrow_function_expression_failed_at_position ( const Position & position , bool failed )
{
m_token_memoizations . set ( position , { failed } ) ;
}
2020-11-02 21:03:19 +00:00
void Parser : : syntax_error ( const String & message , Optional < Position > position )
{
if ( ! position . has_value ( ) )
position = this - > position ( ) ;
2021-06-19 12:43:09 +00:00
m_state . errors . append ( { message , position } ) ;
2020-03-11 18:27:43 +00:00
}
2020-03-30 13:24:43 +00:00
void Parser : : save_state ( )
{
2021-06-19 12:43:09 +00:00
m_saved_state . append ( m_state ) ;
2020-03-30 13:24:43 +00:00
}
void Parser : : load_state ( )
{
2021-02-23 19:42:32 +00:00
VERIFY ( ! m_saved_state . is_empty ( ) ) ;
2021-06-19 12:43:09 +00:00
m_state = m_saved_state . take_last ( ) ;
2020-03-30 13:24:43 +00:00
}
2020-04-29 20:37:51 +00:00
2020-12-29 13:17:39 +00:00
void Parser : : discard_saved_state ( )
{
m_saved_state . take_last ( ) ;
}
2021-07-11 11:04:55 +00:00
void Parser : : check_identifier_name_for_assignment_validity ( StringView name )
{
if ( any_of ( s_reserved_words . begin ( ) , s_reserved_words . end ( ) , [ & ] ( auto & value ) { return name = = value ; } ) ) {
syntax_error ( " Binding pattern target may not be a reserved word " ) ;
} else if ( m_state . strict_mode ) {
if ( name . is_one_of ( " arguments " sv , " eval " sv ) )
syntax_error ( " Binding pattern target may not be called 'arguments' or 'eval' in strict mode " ) ;
else if ( name = = " yield " sv )
syntax_error ( " Binding pattern target may not be called 'yield' in strict mode " ) ;
}
}
2020-03-11 18:27:43 +00:00
}