mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
2070 lines
46 KiB
C
2070 lines
46 KiB
C
/* Copyright 2014 Samsung Electronics Co., Ltd.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "optimizer-passes.h"
|
|
#include "jerry-libc.h"
|
|
#include "lexer.h"
|
|
#include "parser.h"
|
|
#include "opcodes.h"
|
|
#include "serializer.h"
|
|
#include "interpreter.h"
|
|
|
|
#define MAX_OPCODES 10
|
|
#define MAX_NESTINGS 10
|
|
#define INVALID_VALUE 255
|
|
|
|
typedef enum
|
|
{
|
|
REWRITABLE_CONTINUE = 0,
|
|
REWRITABLE_BREAK,
|
|
REWRITABLE_OPCODES_COUNT
|
|
}
|
|
rewritable_opcode_type;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t size;
|
|
uint8_t head;
|
|
opcode_counter_t *oc_stack;
|
|
} __packed
|
|
rewritable_opcode;
|
|
|
|
#define NESTING_ITERATIONAL 1
|
|
#define NESTING_SWITCH 2
|
|
#define NESTING_FUNCTION 3
|
|
|
|
static token tok;
|
|
static OPCODE opcode;
|
|
static opcode_counter_t opcode_counter = 0;
|
|
static T_IDX temp_name_stack[MAX_OPCODES], temp_name_stack_head = 0, max_temp_name;
|
|
static uint8_t nestings[MAX_NESTINGS], nestings_head = 0;
|
|
|
|
static rewritable_opcode rewritable_opcodes[REWRITABLE_OPCODES_COUNT] =
|
|
{
|
|
[REWRITABLE_CONTINUE] = { .size = 0, .head = 0, .oc_stack = NULL },
|
|
[REWRITABLE_BREAK] = { .size = 0, .head = 0, .oc_stack = NULL }
|
|
};
|
|
|
|
static T_IDX parse_expression (void);
|
|
static void parse_statement (void);
|
|
static T_IDX parse_assignment_expression (void);
|
|
static void parse_source_element_list (void);
|
|
|
|
static T_IDX temp_name, min_temp_name;
|
|
|
|
static T_IDX
|
|
next_temp_name (void)
|
|
{
|
|
return temp_name++;
|
|
}
|
|
|
|
static void
|
|
start_new_scope (void)
|
|
{
|
|
JERRY_ASSERT (temp_name_stack_head < MAX_OPCODES);
|
|
temp_name_stack[temp_name_stack_head++] = temp_name;
|
|
max_temp_name = min_temp_name;
|
|
}
|
|
|
|
static void
|
|
finish_scope (void)
|
|
{
|
|
temp_name = temp_name_stack[--temp_name_stack_head];
|
|
}
|
|
|
|
static void
|
|
reset_temp_name (void)
|
|
{
|
|
if (max_temp_name < temp_name)
|
|
max_temp_name = temp_name;
|
|
temp_name = min_temp_name;
|
|
}
|
|
|
|
static void
|
|
push_nesting (uint8_t nesting_type)
|
|
{
|
|
JERRY_ASSERT (nestings_head < MAX_NESTINGS);
|
|
|
|
nestings[nestings_head++] = nesting_type;
|
|
}
|
|
|
|
static void
|
|
pop_nesting (uint8_t nesting_type)
|
|
{
|
|
JERRY_ASSERT (nestings_head > 0);
|
|
JERRY_ASSERT (nestings[nestings_head - 1] == nesting_type);
|
|
|
|
nestings_head--;
|
|
}
|
|
|
|
static void
|
|
must_be_inside_but_not_in (uint8_t inside[], uint8_t insides_count, uint8_t not_in)
|
|
{
|
|
int8_t i;
|
|
|
|
if (nestings_head == 0)
|
|
parser_fatal (ERR_PARSER);
|
|
|
|
for (i = (int8_t) (nestings_head - 1); i >= 0; i--)
|
|
{
|
|
int8_t j;
|
|
if (nestings[i] == not_in)
|
|
parser_fatal (ERR_PARSER);
|
|
|
|
for (j = 0; j < insides_count; j++)
|
|
{
|
|
if (nestings[i] == inside[j])
|
|
return;
|
|
}
|
|
}
|
|
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
|
|
static void
|
|
assert_keyword (keyword kw)
|
|
{
|
|
if (tok.type != TOK_KEYWORD || tok.data.kw != kw)
|
|
{
|
|
#ifdef __TARGET_HOST_x64
|
|
__printf ("assert_keyword: %d\n", kw);
|
|
#endif
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
static bool
|
|
is_keyword (keyword kw)
|
|
{
|
|
return tok.type == TOK_KEYWORD && tok.data.kw == kw;
|
|
}
|
|
|
|
static void
|
|
current_token_must_be(token_type tt)
|
|
{
|
|
if (tok.type != tt)
|
|
{
|
|
#ifdef __TARGET_HOST_x64
|
|
__printf ("current_token_must_be: %d\n", tt);
|
|
#endif
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
}
|
|
|
|
static void
|
|
skip_newlines (void)
|
|
{
|
|
tok = lexer_next_token ();
|
|
while (tok.type == TOK_NEWLINE)
|
|
tok = lexer_next_token ();
|
|
}
|
|
|
|
static void
|
|
next_token_must_be (token_type tt)
|
|
{
|
|
tok = lexer_next_token ();
|
|
if (tok.type != tt)
|
|
{
|
|
#ifdef __TARGET_HOST_x64
|
|
__printf ("next_token_must_be: %d\n", tt);
|
|
#endif
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
}
|
|
|
|
static void
|
|
token_after_newlines_must_be (token_type tt)
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type != tt)
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
|
|
static inline void
|
|
token_after_newlines_must_be_keyword (keyword kw)
|
|
{
|
|
skip_newlines ();
|
|
if (!is_keyword (kw))
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
insert_semicolon (void)
|
|
{
|
|
tok = lexer_next_token ();
|
|
if (tok.type != TOK_NEWLINE && tok.type != TOK_SEMICOLON)
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
#endif
|
|
|
|
#define NEXT(ID, TYPE) \
|
|
do { skip_newlines (); ID = parse_##TYPE (); } while (0)
|
|
|
|
#define DUMP_VOID_OPCODE(GETOP) \
|
|
do { \
|
|
opcode=getop_##GETOP (); \
|
|
serializer_dump_opcode (opcode); \
|
|
opcode_counter++; \
|
|
} while (0)
|
|
|
|
#define DUMP_OPCODE_1(GETOP, OP1) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1)); \
|
|
serializer_dump_opcode (opcode); \
|
|
opcode_counter++; \
|
|
} while (0)
|
|
|
|
#define DUMP_OPCODE_2(GETOP, OP1, OP2) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP2) == 1 || OP2 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1), (T_IDX) (OP2)); \
|
|
serializer_dump_opcode (opcode); \
|
|
opcode_counter++; \
|
|
} while (0)
|
|
|
|
#define DUMP_OPCODE_3(GETOP, OP1, OP2, OP3) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP2) == 1 || OP2 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP3) == 1 || OP3 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1), (T_IDX) (OP2), (T_IDX) (OP3)); \
|
|
serializer_dump_opcode (opcode); \
|
|
opcode_counter++; \
|
|
} while (0)
|
|
|
|
#define REWRITE_OPCODE_1(OC, GETOP, OP1) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1)); \
|
|
serializer_rewrite_opcode (OC, opcode); \
|
|
} while (0)
|
|
|
|
#define REWRITE_OPCODE_2(OC, GETOP, OP1, OP2) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP2) == 1 || OP2 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1), (T_IDX) (OP2)); \
|
|
serializer_rewrite_opcode (OC, opcode); \
|
|
} while (0)
|
|
|
|
#define REWRITE_OPCODE_3(OC, GETOP, OP1, OP2, OP3) \
|
|
do { \
|
|
JERRY_ASSERT (sizeof (OP1) == 1 || OP1 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP2) == 1 || OP2 <= 255); \
|
|
JERRY_ASSERT (sizeof (OP3) == 1 || OP3 <= 255); \
|
|
opcode=getop_##GETOP ((T_IDX) (OP1), (T_IDX) (OP2), (T_IDX) (OP3)); \
|
|
serializer_rewrite_opcode (OC, opcode); \
|
|
} while (0)
|
|
|
|
static T_IDX
|
|
integer_zero (void)
|
|
{
|
|
T_IDX lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_SMALLINT, 0);
|
|
return lhs;
|
|
}
|
|
|
|
static T_IDX
|
|
integer_one (void)
|
|
{
|
|
T_IDX lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_SMALLINT, 1);
|
|
return lhs;
|
|
}
|
|
|
|
static void
|
|
add_to_rewritable_opcodes (rewritable_opcode_type type, opcode_counter_t oc)
|
|
{
|
|
rewritable_opcode op = rewritable_opcodes[type];
|
|
if (op.oc_stack == NULL)
|
|
{
|
|
op.size = op.head = 1;
|
|
op.oc_stack = (opcode_counter_t *) mem_heap_alloc_block (sizeof (opcode_counter_t), MEM_HEAP_ALLOC_SHORT_TERM);
|
|
op.oc_stack[0] = oc;
|
|
return;
|
|
}
|
|
|
|
if (op.head == op.size)
|
|
{
|
|
opcode_counter_t *temp = (opcode_counter_t *) mem_heap_alloc_block (sizeof (opcode_counter_t) * op.size * 2,
|
|
MEM_HEAP_ALLOC_SHORT_TERM);
|
|
__memcpy (temp, op.oc_stack, op.size * sizeof (opcode_counter_t));
|
|
op.size = (uint8_t) (op.size * 2);
|
|
temp[op.head++] = oc;
|
|
mem_heap_free_block ((uint8_t *) op.oc_stack);
|
|
op.oc_stack = temp;
|
|
return;
|
|
}
|
|
|
|
op.oc_stack[op.head++] = oc;
|
|
}
|
|
|
|
static void
|
|
rewrite_rewritable_opcodes (rewritable_opcode_type type, opcode_counter_t oc)
|
|
{
|
|
uint8_t i;
|
|
rewritable_opcode op = rewritable_opcodes[type];
|
|
|
|
for (i = 0; i < op.head; i++)
|
|
{
|
|
switch (type)
|
|
{
|
|
case REWRITABLE_CONTINUE:
|
|
REWRITE_OPCODE_1 (op.oc_stack[i], jmp_up, oc);
|
|
break;
|
|
case REWRITABLE_BREAK:
|
|
REWRITE_OPCODE_1 (op.oc_stack[i], jmp_down, oc);
|
|
break;
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
if (op.oc_stack)
|
|
{
|
|
mem_heap_free_block ((uint8_t *) op.oc_stack);
|
|
}
|
|
op.oc_stack = NULL;
|
|
op.head = op.size = 0;
|
|
}
|
|
|
|
/* property_name
|
|
: Identifier
|
|
| StringLiteral
|
|
| NumericLiteral
|
|
; */
|
|
static T_IDX
|
|
parse_property_name (void)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_NAME:
|
|
case TOK_STRING:
|
|
case TOK_INT:
|
|
return tok.data.uid;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
/* property_name_and_value
|
|
: property_name LT!* ':' LT!* assignment_expression
|
|
; */
|
|
static T_IDX
|
|
parse_property_name_and_value (void)
|
|
{
|
|
T_IDX lhs, name, value;
|
|
|
|
lhs = next_temp_name ();
|
|
name = parse_property_name ();
|
|
token_after_newlines_must_be (TOK_COLON);
|
|
NEXT (value, assignment_expression);
|
|
|
|
DUMP_OPCODE_3 (prop, lhs, name, value);
|
|
|
|
return lhs;
|
|
}
|
|
|
|
/* property_assignment
|
|
: property_name_and_value
|
|
| get LT!* property_name LT!* '(' LT!* ')' LT!* '{' LT!* function_body LT!* '}'
|
|
| set LT!* property_name LT!* '(' identifier ')' LT!* '{' LT!* function_body LT!* '}'
|
|
; */
|
|
static T_IDX
|
|
parse_property_assignment (void)
|
|
{
|
|
T_IDX lhs, name, arg;
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
lhs = next_temp_name ();
|
|
|
|
if (!__strcmp ("get", lexer_get_string_by_id (tok.data.uid)))
|
|
{
|
|
NEXT (name, property_name);
|
|
|
|
token_after_newlines_must_be (TOK_OPEN_PAREN);
|
|
token_after_newlines_must_be (TOK_CLOSE_PAREN);
|
|
token_after_newlines_must_be (TOK_OPEN_BRACE);
|
|
|
|
DUMP_OPCODE_2 (prop_get_decl, lhs, name);
|
|
|
|
skip_newlines ();
|
|
parse_source_element_list ();
|
|
|
|
token_after_newlines_must_be (TOK_CLOSE_BRACE);
|
|
DUMP_VOID_OPCODE (ret);
|
|
|
|
return lhs;
|
|
}
|
|
else if (!__strcmp ("set", lexer_get_string_by_id (tok.data.uid)))
|
|
{
|
|
NEXT (name, property_name);
|
|
|
|
token_after_newlines_must_be (TOK_OPEN_PAREN);
|
|
token_after_newlines_must_be (TOK_NAME);
|
|
arg = tok.data.uid;
|
|
token_after_newlines_must_be (TOK_CLOSE_PAREN);
|
|
token_after_newlines_must_be (TOK_OPEN_BRACE);
|
|
|
|
DUMP_OPCODE_3 (prop_set_decl, lhs, name, arg);
|
|
|
|
skip_newlines ();
|
|
parse_source_element_list ();
|
|
|
|
token_after_newlines_must_be (TOK_CLOSE_BRACE);
|
|
DUMP_VOID_OPCODE (ret);
|
|
|
|
return lhs;
|
|
}
|
|
else
|
|
return parse_property_name_and_value ();
|
|
}
|
|
|
|
static void
|
|
dump_varg_3 (T_IDX current_param, T_IDX params[3])
|
|
{
|
|
if (current_param == 3)
|
|
{
|
|
DUMP_OPCODE_3 (varg_3, params[0], params[1], params[2]);
|
|
current_param = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dump_varg_end (T_IDX current_param, T_IDX params[3])
|
|
{
|
|
switch (current_param)
|
|
{
|
|
case 0:
|
|
DUMP_OPCODE_1 (varg_1_end, params[0]);
|
|
break;
|
|
|
|
case 1:
|
|
DUMP_OPCODE_2 (varg_2_end, params[0], params[1]);
|
|
break;
|
|
|
|
case 2:
|
|
DUMP_OPCODE_3 (varg_3_end, params[0], params[1], params[2]);
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
AL_FUNC_DECL,
|
|
AL_FUNC_EXPR,
|
|
AL_ARRAY_LIT,
|
|
AL_OBJECT_LIT,
|
|
AL_CONSTRUCT_EXPR,
|
|
AL_CALL_EXPR
|
|
}
|
|
argument_list_type;
|
|
|
|
/** Parse list of identifiers, assigment expressions or properties, splitted by comma.
|
|
For each ALT dumps appropriate bytecode. Uses OBJ during dump if neccesary.
|
|
Returns temp var if expression has lhs, or 0 otherwise. */
|
|
static T_IDX
|
|
parse_argument_list (argument_list_type alt, T_IDX obj)
|
|
{
|
|
token_type open_tt, close_tt;
|
|
T_IDX first_opcode_args_count,
|
|
lhs = 0,
|
|
args[3+1/* +1 for stack protector */],
|
|
current_arg = 0;
|
|
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
open_tt = TOK_OPEN_PAREN; // Openning token
|
|
close_tt = TOK_CLOSE_PAREN; // Ending token
|
|
first_opcode_args_count = 2; // Maximum number of arguments in first opcode
|
|
break;
|
|
|
|
case AL_FUNC_EXPR:
|
|
case AL_CONSTRUCT_EXPR:
|
|
case AL_CALL_EXPR:
|
|
open_tt = TOK_OPEN_PAREN;
|
|
close_tt = TOK_CLOSE_PAREN;
|
|
first_opcode_args_count = 1;
|
|
lhs = next_temp_name ();
|
|
break;
|
|
|
|
case AL_ARRAY_LIT:
|
|
open_tt = TOK_OPEN_SQUARE;
|
|
close_tt = TOK_CLOSE_SQUARE;
|
|
first_opcode_args_count = 2;
|
|
lhs = next_temp_name ();
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
open_tt = TOK_OPEN_BRACE;
|
|
close_tt = TOK_CLOSE_BRACE;
|
|
first_opcode_args_count = 2;
|
|
lhs = next_temp_name ();
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
|
|
current_token_must_be (open_tt);
|
|
|
|
skip_newlines ();
|
|
if (tok.type != close_tt)
|
|
{
|
|
bool is_first_opcode = true;
|
|
while (true)
|
|
{
|
|
if (is_first_opcode)
|
|
{
|
|
if (current_arg == first_opcode_args_count)
|
|
{
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
DUMP_OPCODE_3 (func_decl_n, obj, args[0], args[1]);
|
|
break;
|
|
|
|
case AL_FUNC_EXPR:
|
|
DUMP_OPCODE_3 (func_expr_n, lhs, obj, args[0]);
|
|
break;
|
|
|
|
case AL_ARRAY_LIT:
|
|
DUMP_OPCODE_3 (array_n, lhs, args[0], args[1]);
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
DUMP_OPCODE_3 (obj_n, lhs, args[0], args[1]);
|
|
break;
|
|
|
|
case AL_CONSTRUCT_EXPR:
|
|
DUMP_OPCODE_3 (construct_n, lhs, obj, args[0]);
|
|
break;
|
|
|
|
case AL_CALL_EXPR:
|
|
DUMP_OPCODE_3 (call_n, lhs, obj, args[0]);
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
current_arg = 0;
|
|
is_first_opcode = false;
|
|
}
|
|
}
|
|
else
|
|
dump_varg_3 (current_arg, args);
|
|
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
current_token_must_be (TOK_NAME);
|
|
args[current_arg] = tok.data.uid;
|
|
break;
|
|
|
|
case AL_FUNC_EXPR:
|
|
case AL_ARRAY_LIT:
|
|
case AL_CONSTRUCT_EXPR:
|
|
case AL_CALL_EXPR:
|
|
args[current_arg] = parse_assignment_expression ();
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
args[current_arg] = parse_property_assignment ();
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
|
|
skip_newlines ();
|
|
if (tok.type != TOK_COMMA)
|
|
{
|
|
current_token_must_be (close_tt);
|
|
break;
|
|
}
|
|
|
|
skip_newlines ();
|
|
current_arg++;
|
|
}
|
|
|
|
if (is_first_opcode)
|
|
{
|
|
if (current_arg == 0)
|
|
{
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
DUMP_OPCODE_2 (func_decl_1, obj, args[0]);
|
|
break;
|
|
|
|
case AL_FUNC_EXPR:
|
|
DUMP_OPCODE_3 (func_expr_1, lhs, obj, args[0]);
|
|
break;
|
|
|
|
case AL_ARRAY_LIT:
|
|
DUMP_OPCODE_2 (array_1, lhs, args[0]);
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
DUMP_OPCODE_2 (obj_1, lhs, args[0]);
|
|
break;
|
|
|
|
case AL_CONSTRUCT_EXPR:
|
|
DUMP_OPCODE_3 (construct_1, lhs, obj, args[0]);
|
|
break;
|
|
|
|
case AL_CALL_EXPR:
|
|
DUMP_OPCODE_3 (call_1, lhs, obj, args[0]);
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
else if (current_arg == 1)
|
|
{
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
DUMP_OPCODE_3 (func_decl_2, obj, args[0], args[1]);
|
|
break;
|
|
|
|
case AL_ARRAY_LIT:
|
|
DUMP_OPCODE_3 (array_2, lhs, args[0], args[1]);
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
DUMP_OPCODE_3 (obj_2, lhs, args[0], args[1]);
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
else
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
else
|
|
dump_varg_end (current_arg, args);
|
|
}
|
|
else
|
|
{
|
|
switch (alt)
|
|
{
|
|
case AL_FUNC_DECL:
|
|
DUMP_OPCODE_1 (func_decl_0, obj);
|
|
break;
|
|
|
|
case AL_FUNC_EXPR:
|
|
DUMP_OPCODE_2 (func_expr_0, lhs, obj);
|
|
break;
|
|
|
|
case AL_ARRAY_LIT:
|
|
DUMP_OPCODE_1 (array_0, lhs);
|
|
break;
|
|
|
|
case AL_OBJECT_LIT:
|
|
DUMP_OPCODE_1 (obj_0, lhs);
|
|
break;
|
|
|
|
case AL_CONSTRUCT_EXPR:
|
|
DUMP_OPCODE_2 (construct_0, lhs, obj);
|
|
break;
|
|
|
|
case AL_CALL_EXPR:
|
|
DUMP_OPCODE_2 (call_0, lhs, obj);
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
return lhs;
|
|
}
|
|
|
|
/* function_declaration
|
|
: 'function' LT!* Identifier LT!*
|
|
'(' (LT!* Identifier (LT!* ',' LT!* Identifier)* ) ? LT!* ')' LT!* function_body
|
|
;
|
|
|
|
function_body
|
|
: '{' LT!* sourceElements LT!* '}' */
|
|
static void
|
|
parse_function_declaration (void)
|
|
{
|
|
T_IDX name;
|
|
opcode_counter_t jmp_oc;
|
|
|
|
assert_keyword (KW_FUNCTION);
|
|
|
|
token_after_newlines_must_be (TOK_NAME);
|
|
|
|
name = tok.data.uid;
|
|
|
|
skip_newlines ();
|
|
parse_argument_list (AL_FUNC_DECL, name);
|
|
|
|
jmp_oc = opcode_counter;
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
|
|
token_after_newlines_must_be (TOK_OPEN_BRACE);
|
|
|
|
skip_newlines ();
|
|
push_nesting (NESTING_FUNCTION);
|
|
parse_source_element_list ();
|
|
pop_nesting (NESTING_FUNCTION);
|
|
|
|
next_token_must_be (TOK_CLOSE_BRACE);
|
|
|
|
DUMP_VOID_OPCODE (ret);
|
|
REWRITE_OPCODE_1 (jmp_oc, jmp_down, opcode_counter - jmp_oc);
|
|
}
|
|
|
|
/* function_expression
|
|
: 'function' LT!* Identifier? LT!* '(' formal_parameter_list? LT!* ')' LT!* function_body
|
|
; */
|
|
static T_IDX
|
|
parse_function_expression (void)
|
|
{
|
|
T_IDX name, lhs;
|
|
opcode_counter_t jmp_oc;
|
|
|
|
assert_keyword (KW_FUNCTION);
|
|
|
|
skip_newlines ();
|
|
if (tok.type == TOK_NAME)
|
|
name = tok.data.uid;
|
|
else
|
|
{
|
|
lexer_save_token (tok);
|
|
name = next_temp_name ();
|
|
}
|
|
|
|
skip_newlines ();
|
|
lhs = parse_argument_list (AL_FUNC_EXPR, name);
|
|
|
|
jmp_oc = opcode_counter;
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
|
|
token_after_newlines_must_be (TOK_OPEN_BRACE);
|
|
|
|
skip_newlines ();
|
|
push_nesting (NESTING_FUNCTION);
|
|
parse_source_element_list ();
|
|
pop_nesting (NESTING_FUNCTION);
|
|
|
|
token_after_newlines_must_be (TOK_CLOSE_BRACE);
|
|
|
|
DUMP_VOID_OPCODE (ret);
|
|
REWRITE_OPCODE_1 (jmp_oc, jmp_down, opcode_counter - jmp_oc);
|
|
|
|
return lhs;
|
|
}
|
|
|
|
/* array_literal
|
|
: '[' LT!* assignment_expression? (LT!* ',' (LT!* assignment_expression)?)* LT!* ']' LT!*
|
|
; */
|
|
static T_IDX
|
|
parse_array_literal (void)
|
|
{
|
|
return parse_argument_list (AL_ARRAY_LIT, 0);
|
|
}
|
|
|
|
/* object_literal
|
|
: '{' LT!* property_assignment (LT!* ',' LT!* property_assignment)* LT!* '}'
|
|
; */
|
|
static T_IDX
|
|
parse_object_literal (void)
|
|
{
|
|
return parse_argument_list (AL_OBJECT_LIT, 0);
|
|
}
|
|
|
|
static T_IDX
|
|
parse_literal (void)
|
|
{
|
|
T_IDX lhs;
|
|
|
|
switch (tok.type)
|
|
{
|
|
case TOK_NULL:
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_SIMPLE, ECMA_SIMPLE_VALUE_NULL);
|
|
return lhs;
|
|
|
|
case TOK_BOOL:
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_SIMPLE,
|
|
tok.data.uid ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE);
|
|
return lhs;
|
|
|
|
case TOK_INT:
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_NUMBER, tok.data.uid);
|
|
return lhs;
|
|
|
|
case TOK_STRING:
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_STRING, tok.data.uid);
|
|
return lhs;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
/* primary_expression
|
|
: 'this'
|
|
| Identifier
|
|
| literal
|
|
| '[' LT!* array_literal LT!* ']'
|
|
| '{' LT!* object_literal LT!* '}'
|
|
| '(' LT!* expression LT!* ')'
|
|
; */
|
|
static T_IDX
|
|
parse_primary_expression (void)
|
|
{
|
|
T_IDX lhs;
|
|
|
|
if (is_keyword (KW_THIS))
|
|
{
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_1 (this, lhs);
|
|
return lhs;
|
|
}
|
|
else if (tok.type == TOK_NAME)
|
|
return tok.data.uid;
|
|
else if (tok.type == TOK_NULL || tok.type == TOK_BOOL
|
|
|| tok.type == TOK_INT || tok.type == TOK_STRING)
|
|
return parse_literal ();
|
|
else if (tok.type == TOK_OPEN_SQUARE)
|
|
return parse_array_literal ();
|
|
else if (tok.type == TOK_OPEN_BRACE)
|
|
return parse_object_literal ();
|
|
else if (tok.type == TOK_OPEN_PAREN)
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type != TOK_CLOSE_PAREN)
|
|
{
|
|
lhs = parse_expression ();
|
|
token_after_newlines_must_be (TOK_CLOSE_PAREN);
|
|
return lhs;
|
|
}
|
|
}
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
|
|
/* member_expression
|
|
: (primary_expression | function_expression | 'new' LT!* member_expression (LT!* '(' LT!* arguments? LT!* ')')
|
|
(LT!* member_expression_suffix)*
|
|
;
|
|
|
|
arguments
|
|
: assignment_expression (LT!* ',' LT!* assignment_expression)*)?
|
|
;
|
|
|
|
member_expression_suffix
|
|
: index_suffix
|
|
| property_reference_suffix
|
|
;
|
|
|
|
index_suffix
|
|
: '[' LT!* expression LT!* ']'
|
|
;
|
|
|
|
property_reference_suffix
|
|
: '.' LT!* Identifier
|
|
; */
|
|
static T_IDX
|
|
parse_member_expression (void)
|
|
{
|
|
T_IDX lhs, obj, prop;
|
|
if (is_keyword (KW_FUNCTION))
|
|
obj = parse_function_expression ();
|
|
else if (is_keyword (KW_NEW))
|
|
{
|
|
T_IDX member;
|
|
|
|
NEXT (member, member_expression);
|
|
|
|
obj = parse_argument_list (AL_CONSTRUCT_EXPR, member);
|
|
}
|
|
else
|
|
obj = parse_primary_expression ();
|
|
|
|
skip_newlines ();
|
|
while (tok.type == TOK_OPEN_SQUARE || tok.type == TOK_DOT)
|
|
{
|
|
lhs = next_temp_name ();
|
|
|
|
if (tok.type == TOK_OPEN_SQUARE)
|
|
{
|
|
NEXT (prop, expression);
|
|
next_token_must_be (TOK_CLOSE_SQUARE);
|
|
}
|
|
else if (tok.type == TOK_DOT)
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type != TOK_NAME)
|
|
parser_fatal (ERR_PARSER);
|
|
prop = tok.data.uid;
|
|
}
|
|
else
|
|
JERRY_UNREACHABLE ();
|
|
|
|
DUMP_OPCODE_3 (prop_access, lhs, obj, prop);
|
|
obj = lhs;
|
|
skip_newlines ();
|
|
}
|
|
|
|
lexer_save_token (tok);
|
|
|
|
return obj;
|
|
}
|
|
|
|
/* call_expression
|
|
: member_expression LT!* arguments (LT!* call_expression_suffix)*
|
|
;
|
|
|
|
call_expression_suffix
|
|
: arguments
|
|
| index_suffix
|
|
| property_reference_suffix
|
|
;
|
|
|
|
arguments
|
|
: '(' LT!* assignment_expression LT!* ( ',' LT!* assignment_expression * LT!* )* ')'
|
|
; */
|
|
static T_IDX
|
|
parse_call_expression (void)
|
|
{
|
|
T_IDX lhs, obj, prop;
|
|
|
|
obj = parse_member_expression ();
|
|
|
|
skip_newlines ();
|
|
if (tok.type != TOK_OPEN_PAREN)
|
|
{
|
|
lexer_save_token (tok);
|
|
return obj;
|
|
}
|
|
|
|
lhs = parse_argument_list (AL_CALL_EXPR, obj);
|
|
obj = lhs;
|
|
|
|
skip_newlines ();
|
|
while (tok.type == TOK_OPEN_PAREN || tok.type == TOK_OPEN_SQUARE
|
|
|| tok.type == TOK_DOT)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_OPEN_PAREN:
|
|
lhs = parse_argument_list (AL_CALL_EXPR, obj);
|
|
skip_newlines ();
|
|
break;
|
|
|
|
case TOK_OPEN_SQUARE:
|
|
NEXT (prop, expression);
|
|
next_token_must_be (TOK_CLOSE_SQUARE);
|
|
|
|
DUMP_OPCODE_3 (prop_access, lhs, obj, prop);
|
|
obj = lhs;
|
|
skip_newlines ();
|
|
break;
|
|
|
|
case TOK_DOT:
|
|
token_after_newlines_must_be (TOK_NAME);
|
|
prop = tok.data.uid;
|
|
|
|
DUMP_OPCODE_3 (prop_access, lhs, obj, prop);
|
|
obj = lhs;
|
|
skip_newlines ();
|
|
break;
|
|
|
|
default:
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
lexer_save_token (tok);
|
|
|
|
return obj;
|
|
}
|
|
|
|
/* left_hand_side_expression
|
|
: call_expression
|
|
| new_expression
|
|
; */
|
|
static T_IDX
|
|
parse_left_hand_side_expression (void)
|
|
{
|
|
return parse_call_expression ();
|
|
}
|
|
|
|
/* postfix_expression
|
|
: left_hand_side_expression ('++' | '--')?
|
|
; */
|
|
static T_IDX
|
|
parse_postfix_expression (void)
|
|
{
|
|
T_IDX expr = parse_left_hand_side_expression (), lhs;
|
|
|
|
tok = lexer_next_token ();
|
|
if (tok.type == TOK_DOUBLE_PLUS)
|
|
{
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_2 (post_incr, lhs, expr);
|
|
}
|
|
else if (tok.type == TOK_DOUBLE_MINUS)
|
|
{
|
|
lhs = next_temp_name ();
|
|
DUMP_OPCODE_2 (post_decr, lhs, expr);
|
|
}
|
|
else
|
|
lexer_save_token (tok);
|
|
|
|
return expr;
|
|
}
|
|
|
|
/* unary_expression
|
|
: postfix_expression
|
|
| ('delete' | 'void' | 'typeof' | '++' | '--' | '+' | '-' | '~' | '!') unary_expression
|
|
; */
|
|
static T_IDX
|
|
parse_unary_expression (void)
|
|
{
|
|
T_IDX expr, lhs;
|
|
|
|
switch (tok.type)
|
|
{
|
|
case TOK_DOUBLE_PLUS:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (pre_incr, lhs, expr);
|
|
return expr;
|
|
|
|
case TOK_DOUBLE_MINUS:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (pre_decr, lhs, expr);
|
|
return expr;
|
|
|
|
case TOK_PLUS:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_3 (addition, lhs, integer_zero (), expr);
|
|
return lhs;
|
|
|
|
case TOK_MINUS:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_3 (substraction, lhs, integer_zero (), expr);
|
|
return lhs;
|
|
|
|
case TOK_COMPL:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (b_not, lhs, expr);
|
|
return lhs;
|
|
|
|
case TOK_NOT:
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (logical_not, lhs, expr);
|
|
return lhs;
|
|
|
|
case TOK_KEYWORD:
|
|
if (is_keyword (KW_DELETE))
|
|
{
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (delete, lhs, expr);
|
|
return lhs;
|
|
}
|
|
if (is_keyword (KW_VOID))
|
|
JERRY_UNIMPLEMENTED ();
|
|
if (is_keyword (KW_TYPEOF))
|
|
{
|
|
lhs = next_temp_name ();
|
|
NEXT (expr, unary_expression);
|
|
DUMP_OPCODE_2 (typeof, lhs, expr);
|
|
return lhs;
|
|
}
|
|
/* FALLTHRU. */
|
|
|
|
default:
|
|
return parse_postfix_expression ();
|
|
}
|
|
}
|
|
|
|
#define DUMP_OF(GETOP, EXPR) \
|
|
lhs = next_temp_name (); \
|
|
NEXT (expr2, EXPR);\
|
|
DUMP_OPCODE_3 (GETOP, lhs, expr1, expr2); \
|
|
expr1 = lhs; \
|
|
break;
|
|
|
|
/* multiplicative_expression
|
|
: unary_expression (LT!* ('*' | '/' | '%') LT!* unary_expression)*
|
|
; */
|
|
static T_IDX
|
|
parse_multiplicative_expression (void)
|
|
{
|
|
T_IDX lhs, expr1, expr2;
|
|
|
|
expr1 = parse_unary_expression ();
|
|
|
|
skip_newlines ();
|
|
while (true)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_MULT: DUMP_OF (multiplication, unary_expression)
|
|
case TOK_DIV: DUMP_OF (division, unary_expression)
|
|
case TOK_MOD: DUMP_OF (remainder, unary_expression)
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
return expr1;
|
|
}
|
|
|
|
skip_newlines ();
|
|
}
|
|
}
|
|
|
|
/* additive_expression
|
|
: multiplicative_expression (LT!* ('+' | '-') LT!* multiplicative_expression)*
|
|
; */
|
|
static T_IDX
|
|
parse_additive_expression (void)
|
|
{
|
|
T_IDX lhs, expr1, expr2;
|
|
|
|
expr1 = parse_multiplicative_expression ();
|
|
|
|
skip_newlines ();
|
|
while (true)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_PLUS: DUMP_OF (addition, multiplicative_expression);
|
|
case TOK_MINUS: DUMP_OF (substraction, multiplicative_expression);
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
return expr1;
|
|
}
|
|
|
|
skip_newlines ();
|
|
}
|
|
}
|
|
|
|
/* shift_expression
|
|
: additive_expression (LT!* ('<<' | '>>' | '>>>') LT!* additive_expression)*
|
|
; */
|
|
static T_IDX
|
|
parse_shift_expression (void)
|
|
{
|
|
T_IDX lhs, expr1, expr2;
|
|
|
|
expr1 = parse_additive_expression ();
|
|
|
|
skip_newlines ();
|
|
while (true)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_LSHIFT: DUMP_OF (b_shift_left, additive_expression)
|
|
case TOK_RSHIFT: DUMP_OF (b_shift_right, additive_expression)
|
|
case TOK_RSHIFT_EX: DUMP_OF (b_shift_uright, additive_expression)
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
return expr1;
|
|
}
|
|
|
|
skip_newlines ();
|
|
}
|
|
}
|
|
|
|
/* relational_expression
|
|
: shift_expression (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof' | 'in') LT!* shift_expression)*
|
|
; */
|
|
static T_IDX
|
|
parse_relational_expression (void)
|
|
{
|
|
T_IDX lhs, expr1, expr2;
|
|
|
|
expr1 = parse_shift_expression ();
|
|
|
|
skip_newlines ();
|
|
while (true)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_LESS: DUMP_OF (less_than, shift_expression)
|
|
case TOK_GREATER: DUMP_OF (greater_than, shift_expression)
|
|
case TOK_LESS_EQ: DUMP_OF (less_or_equal_than, shift_expression)
|
|
case TOK_GREATER_EQ: DUMP_OF (greater_or_equal_than, shift_expression)
|
|
case TOK_KEYWORD:
|
|
if (is_keyword (KW_INSTANCEOF))
|
|
{
|
|
DUMP_OF (instanceof, shift_expression)
|
|
}
|
|
else if (is_keyword (KW_IN))
|
|
{
|
|
DUMP_OF (in, shift_expression)
|
|
}
|
|
// FALLTHRU
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
return expr1;
|
|
}
|
|
|
|
skip_newlines ();
|
|
}
|
|
}
|
|
|
|
/* equality_expression
|
|
: relational_expression (LT!* ('==' | '!=' | '===' | '!==') LT!* relational_expression)*
|
|
; */
|
|
static T_IDX
|
|
parse_equality_expression (void)
|
|
{
|
|
T_IDX lhs, expr1, expr2;
|
|
|
|
expr1 = parse_relational_expression ();
|
|
|
|
skip_newlines ();
|
|
while (true)
|
|
{
|
|
switch (tok.type)
|
|
{
|
|
case TOK_DOUBLE_EQ: DUMP_OF (equal_value, relational_expression)
|
|
case TOK_NOT_EQ: DUMP_OF (not_equal_value, relational_expression)
|
|
case TOK_TRIPLE_EQ: DUMP_OF (equal_value_type, relational_expression)
|
|
case TOK_NOT_DOUBLE_EQ: DUMP_OF (not_equal_value_type, relational_expression)
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
return expr1;
|
|
}
|
|
|
|
skip_newlines ();
|
|
}
|
|
}
|
|
|
|
#define PARSE_OF(FUNC, EXPR, TOK_TYPE, GETOP) \
|
|
static T_IDX parse_##FUNC (void) { \
|
|
T_IDX lhs, expr1, expr2; \
|
|
expr1 = parse_##EXPR (); \
|
|
skip_newlines (); \
|
|
while (true) \
|
|
{ \
|
|
switch (tok.type) \
|
|
{ \
|
|
case TOK_##TOK_TYPE: DUMP_OF (GETOP, EXPR) \
|
|
default: lexer_save_token (tok); return expr1; \
|
|
} \
|
|
skip_newlines (); \
|
|
} \
|
|
}
|
|
|
|
/* bitwise_and_expression
|
|
: equality_expression (LT!* '&' LT!* equality_expression)*
|
|
; */
|
|
PARSE_OF (bitwise_and_expression, equality_expression, AND, b_and)
|
|
|
|
/* bitwise_xor_expression
|
|
: bitwise_and_expression (LT!* '^' LT!* bitwise_and_expression)*
|
|
; */
|
|
PARSE_OF (bitwise_xor_expression, bitwise_and_expression, XOR, b_xor)
|
|
|
|
/* bitwise_or_expression
|
|
: bitwise_xor_expression (LT!* '|' LT!* bitwise_xor_expression)*
|
|
; */
|
|
PARSE_OF (bitwise_or_expression, bitwise_xor_expression, OR, b_or)
|
|
|
|
/* logical_and_expression
|
|
: bitwise_or_expression (LT!* '&&' LT!* bitwise_or_expression)*
|
|
; */
|
|
PARSE_OF (logical_and_expression, bitwise_or_expression, DOUBLE_AND, logical_and)
|
|
|
|
/* logical_or_expression
|
|
: logical_and_expression (LT!* '||' LT!* logical_and_expression)*
|
|
; */
|
|
PARSE_OF (logical_or_expression, logical_and_expression, DOUBLE_OR, logical_or)
|
|
|
|
/* conditional_expression
|
|
: logical_or_expression (LT!* '?' LT!* assignment_expression LT!* ':' LT!* assignment_expression)?
|
|
; */
|
|
static T_IDX
|
|
parse_conditional_expression (bool *was_conditional)
|
|
{
|
|
T_IDX expr = parse_logical_or_expression ();
|
|
|
|
skip_newlines ();
|
|
if (tok.type == TOK_QUERY)
|
|
{
|
|
T_IDX lhs, res = next_temp_name ();
|
|
opcode_counter_t jmp_oc;
|
|
|
|
DUMP_OPCODE_2 (is_true_jmp, expr, opcode_counter + 2);
|
|
jmp_oc = opcode_counter;
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
|
|
NEXT (lhs, assignment_expression);
|
|
DUMP_OPCODE_3 (assignment, res, OPCODE_ARG_TYPE_VARIABLE, lhs);
|
|
token_after_newlines_must_be (TOK_COLON);
|
|
|
|
REWRITE_OPCODE_1 (jmp_oc, jmp_down, opcode_counter - jmp_oc);
|
|
jmp_oc = opcode_counter;
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
|
|
NEXT (lhs, assignment_expression);
|
|
DUMP_OPCODE_3 (assignment, res, OPCODE_ARG_TYPE_VARIABLE, lhs);
|
|
REWRITE_OPCODE_1 (jmp_oc, jmp_down, opcode_counter - jmp_oc);
|
|
|
|
*was_conditional = true;
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
lexer_save_token (tok);
|
|
return expr;
|
|
}
|
|
}
|
|
|
|
/* assignment_expression
|
|
: conditional_expression
|
|
| left_hand_side_expression LT!* assignment_operator LT!* assignment_expression
|
|
; */
|
|
static T_IDX
|
|
parse_assignment_expression (void)
|
|
{
|
|
T_IDX lhs, rhs;
|
|
bool was_conditional = false;
|
|
|
|
lhs = parse_conditional_expression (&was_conditional);
|
|
if (was_conditional)
|
|
{
|
|
return lhs;
|
|
}
|
|
|
|
skip_newlines ();
|
|
switch (tok.type)
|
|
{
|
|
case TOK_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (assignment, lhs, OPCODE_ARG_TYPE_VARIABLE, rhs);
|
|
break;
|
|
|
|
case TOK_MULT_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (multiplication, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_DIV_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (division, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_MOD_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (remainder, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_PLUS_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (addition, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_MINUS_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (substraction, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_LSHIFT_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_shift_left, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_RSHIFT_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_shift_right, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_RSHIFT_EX_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_shift_uright, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_AND_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_and, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_XOR_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_xor, lhs, lhs, rhs);
|
|
break;
|
|
|
|
case TOK_OR_EQ:
|
|
NEXT (rhs, assignment_expression);
|
|
DUMP_OPCODE_3 (b_or, lhs, lhs, rhs);
|
|
break;
|
|
|
|
default:
|
|
lexer_save_token (tok);
|
|
}
|
|
|
|
return lhs;
|
|
}
|
|
|
|
/* expression
|
|
: assignment_expression (LT!* ',' LT!* assignment_expression)*
|
|
;
|
|
*/
|
|
static T_IDX
|
|
parse_expression (void)
|
|
{
|
|
T_IDX expr = parse_assignment_expression ();
|
|
|
|
while (true)
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type == TOK_COMMA)
|
|
NEXT (expr, assignment_expression);
|
|
else
|
|
{
|
|
lexer_save_token (tok);
|
|
return expr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* variable_declaration
|
|
: Identifier LT!* initialiser?
|
|
;
|
|
initialiser
|
|
: '=' LT!* assignment_expression
|
|
; */
|
|
static void
|
|
parse_variable_declaration (void)
|
|
{
|
|
T_IDX name, expr;
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
name = tok.data.uid;
|
|
DUMP_OPCODE_1 (var_decl, name);
|
|
|
|
skip_newlines ();
|
|
if (tok.type == TOK_EQ)
|
|
{
|
|
NEXT (expr, assignment_expression);
|
|
DUMP_OPCODE_3 (assignment, name, OPCODE_ARG_TYPE_VARIABLE, expr);
|
|
}
|
|
else
|
|
lexer_save_token (tok);
|
|
}
|
|
|
|
/* variable_declaration_list
|
|
: variable_declaration
|
|
(LT!* ',' LT!* variable_declaration)*
|
|
; */
|
|
static void
|
|
parse_variable_declaration_list (bool *several_decls)
|
|
{
|
|
while (true)
|
|
{
|
|
parse_variable_declaration ();
|
|
|
|
skip_newlines ();
|
|
if (tok.type != TOK_COMMA)
|
|
{
|
|
lexer_save_token (tok);
|
|
return;
|
|
}
|
|
|
|
skip_newlines ();
|
|
if (several_decls)
|
|
*several_decls = true;
|
|
}
|
|
}
|
|
|
|
/* for_statement
|
|
: 'for' LT!* '(' (LT!* for_statement_initialiser_part)? LT!* ';'
|
|
(LT!* expression)? LT!* ';' (LT!* expression)? LT!* ')' LT!* statement
|
|
;
|
|
|
|
for_statement_initialiser_part
|
|
: expression
|
|
| 'var' LT!* variable_declaration_list
|
|
;
|
|
|
|
for_in_statement
|
|
: 'for' LT!* '(' LT!* for_in_statement_initialiser_part LT!* 'in'
|
|
LT!* expression LT!* ')' LT!* statement
|
|
;
|
|
|
|
for_in_statement_initialiser_part
|
|
: left_hand_side_expression
|
|
| 'var' LT!* variable_declaration
|
|
;*/
|
|
|
|
static void
|
|
parse_for_or_for_in_statement (void)
|
|
{
|
|
T_IDX stop;
|
|
opcode_counter_t cond_oc, body_oc, step_oc, end_oc;
|
|
|
|
assert_keyword (KW_FOR);
|
|
token_after_newlines_must_be (TOK_OPEN_PAREN);
|
|
|
|
skip_newlines ();
|
|
if (tok.type == TOK_SEMICOLON)
|
|
goto plain_for;
|
|
/* Both for_statement_initialiser_part and for_in_statement_initialiser_part
|
|
contains 'var'. Check it first. */
|
|
if (is_keyword (KW_VAR))
|
|
{
|
|
bool several_decls = false;
|
|
skip_newlines ();
|
|
parse_variable_declaration_list (&several_decls);
|
|
if (several_decls)
|
|
{
|
|
token_after_newlines_must_be (TOK_SEMICOLON);
|
|
goto plain_for;
|
|
}
|
|
else
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type == TOK_SEMICOLON)
|
|
goto plain_for;
|
|
else if (is_keyword (KW_IN))
|
|
goto for_in;
|
|
else
|
|
parser_fatal (ERR_PARSER);
|
|
}
|
|
}
|
|
|
|
/* expression contains left_hand_side_expression. */
|
|
parse_expression ();
|
|
|
|
skip_newlines ();
|
|
if (tok.type == TOK_SEMICOLON)
|
|
goto plain_for;
|
|
else if (is_keyword (KW_IN))
|
|
goto for_in;
|
|
else
|
|
parser_fatal (ERR_PARSER);
|
|
|
|
JERRY_UNREACHABLE ();
|
|
|
|
plain_for:
|
|
/* Represent loop like
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
body;
|
|
}
|
|
|
|
as
|
|
|
|
11 i = #0;
|
|
cond_oc:
|
|
12 tmp1 = #10;
|
|
13 tmp2 = i < tmp1;
|
|
end_oc:
|
|
14 is_false_jmp tmp2, 8 // end of loop
|
|
body_oc:
|
|
15 jmp_down 5 // body
|
|
step_oc:
|
|
16 tmp3 = #1;
|
|
17 tmp4 = i + 1;
|
|
18 i = tmp4;
|
|
19 jmp_up 7; // cond_oc
|
|
|
|
20 body
|
|
|
|
21 jmp_up 5; // step_oc;
|
|
22 ...
|
|
*/
|
|
cond_oc = opcode_counter;
|
|
skip_newlines ();
|
|
if (tok.type != TOK_SEMICOLON)
|
|
{
|
|
stop = parse_assignment_expression ();
|
|
next_token_must_be (TOK_SEMICOLON);
|
|
}
|
|
else
|
|
stop = integer_one ();
|
|
|
|
end_oc = opcode_counter;
|
|
DUMP_OPCODE_2 (is_false_jmp, stop, INVALID_VALUE);
|
|
|
|
body_oc = opcode_counter;
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
|
|
step_oc = opcode_counter;
|
|
skip_newlines ();
|
|
if (tok.type != TOK_CLOSE_PAREN)
|
|
{
|
|
parse_assignment_expression ();
|
|
next_token_must_be (TOK_CLOSE_PAREN);
|
|
}
|
|
DUMP_OPCODE_1 (jmp_up, opcode_counter - cond_oc);
|
|
REWRITE_OPCODE_1 (body_oc, jmp_down, opcode_counter - body_oc);
|
|
|
|
skip_newlines ();
|
|
push_nesting (NESTING_ITERATIONAL);
|
|
parse_statement ();
|
|
pop_nesting (NESTING_ITERATIONAL);
|
|
|
|
DUMP_OPCODE_1 (jmp_up, opcode_counter - step_oc);
|
|
REWRITE_OPCODE_2 (end_oc, is_false_jmp, stop, opcode_counter);
|
|
|
|
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, step_oc);
|
|
rewrite_rewritable_opcodes (REWRITABLE_BREAK, opcode_counter);
|
|
return;
|
|
|
|
for_in:
|
|
JERRY_UNIMPLEMENTED ();
|
|
|
|
}
|
|
|
|
static T_IDX
|
|
parse_expression_inside_parens (void)
|
|
{
|
|
T_IDX expr;
|
|
token_after_newlines_must_be (TOK_OPEN_PAREN);
|
|
NEXT (expr, expression);
|
|
token_after_newlines_must_be (TOK_CLOSE_PAREN);
|
|
return expr;
|
|
}
|
|
|
|
/* statement_list
|
|
: statement (LT!* statement)*
|
|
; */
|
|
static void
|
|
parse_statement_list (void)
|
|
{
|
|
while (true)
|
|
{
|
|
parse_statement ();
|
|
|
|
skip_newlines ();
|
|
while (tok.type == TOK_SEMICOLON)
|
|
{
|
|
skip_newlines ();
|
|
}
|
|
if (tok.type == TOK_CLOSE_BRACE)
|
|
{
|
|
lexer_save_token (tok);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if_statement
|
|
: 'if' LT!* '(' LT!* expression LT!* ')' LT!* statement (LT!* 'else' LT!* statement)?
|
|
; */
|
|
static void
|
|
parse_if_statement (void)
|
|
{
|
|
T_IDX cond;
|
|
opcode_counter_t cond_oc;
|
|
assert_keyword (KW_IF);
|
|
|
|
cond = parse_expression_inside_parens ();
|
|
cond_oc = opcode_counter;
|
|
DUMP_OPCODE_2 (is_false_jmp, cond, INVALID_VALUE);
|
|
|
|
skip_newlines ();
|
|
parse_statement ();
|
|
|
|
REWRITE_OPCODE_2 (cond_oc, is_false_jmp, cond, opcode_counter);
|
|
|
|
skip_newlines ();
|
|
if (is_keyword (KW_ELSE))
|
|
{
|
|
skip_newlines ();
|
|
parse_statement ();
|
|
}
|
|
else
|
|
lexer_save_token (tok);
|
|
}
|
|
|
|
/* do_while_statement
|
|
: 'do' LT!* statement LT!* 'while' LT!* '(' expression ')' (LT | ';')!
|
|
; */
|
|
static void
|
|
parse_do_while_statement (void)
|
|
{
|
|
T_IDX cond;
|
|
opcode_counter_t loop_oc;
|
|
|
|
assert_keyword (KW_DO);
|
|
|
|
loop_oc = opcode_counter;
|
|
|
|
skip_newlines ();
|
|
push_nesting (NESTING_ITERATIONAL);
|
|
parse_statement ();
|
|
pop_nesting (NESTING_ITERATIONAL);
|
|
|
|
token_after_newlines_must_be_keyword (KW_WHILE);
|
|
cond = parse_expression_inside_parens ();
|
|
DUMP_OPCODE_2 (is_true_jmp, cond, loop_oc);
|
|
|
|
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, loop_oc);
|
|
rewrite_rewritable_opcodes (REWRITABLE_BREAK, opcode_counter);
|
|
}
|
|
|
|
/* while_statement
|
|
: 'while' LT!* '(' LT!* expression LT!* ')' LT!* statement
|
|
; */
|
|
static void
|
|
parse_while_statement (void)
|
|
{
|
|
T_IDX cond;
|
|
opcode_counter_t cond_oc, jmp_oc;
|
|
|
|
assert_keyword (KW_WHILE);
|
|
|
|
cond_oc = opcode_counter;
|
|
cond = parse_expression_inside_parens ();
|
|
jmp_oc = opcode_counter;
|
|
DUMP_OPCODE_2 (is_false_jmp, cond, INVALID_VALUE);
|
|
|
|
skip_newlines ();
|
|
push_nesting (NESTING_ITERATIONAL);
|
|
parse_statement ();
|
|
pop_nesting (NESTING_ITERATIONAL);
|
|
|
|
DUMP_OPCODE_1 (jmp_up, opcode_counter - cond_oc);
|
|
REWRITE_OPCODE_2 (jmp_oc, is_false_jmp, cond, opcode_counter);
|
|
|
|
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, cond_oc);
|
|
rewrite_rewritable_opcodes (REWRITABLE_BREAK, opcode_counter);
|
|
}
|
|
|
|
/* with_statement
|
|
: 'with' LT!* '(' LT!* expression LT!* ')' LT!* statement
|
|
; */
|
|
static void
|
|
parse_with_statement (void)
|
|
{
|
|
T_IDX expr;
|
|
assert_keyword (KW_WITH);
|
|
expr = parse_expression_inside_parens ();
|
|
|
|
DUMP_OPCODE_1 (with, expr);
|
|
|
|
skip_newlines ();
|
|
parse_statement ();
|
|
|
|
DUMP_VOID_OPCODE (end_with);
|
|
}
|
|
|
|
/* switch_statement
|
|
: 'switch' LT!* '(' LT!* expression LT!* ')' LT!* '{' LT!* case_block LT!* '}'
|
|
; */
|
|
static void
|
|
parse_switch_statement (void)
|
|
{
|
|
JERRY_UNIMPLEMENTED ();
|
|
}
|
|
|
|
/* try_statement
|
|
: 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?)
|
|
;
|
|
|
|
catch_clause
|
|
: 'catch' LT!* '(' LT!* Identifier LT!* ')' LT!* '{' LT!* statement_list LT!* '}'
|
|
;
|
|
|
|
finally_clause
|
|
: 'finally' LT!* '{' LT!* statement_list LT!* '}'
|
|
;*/
|
|
static void
|
|
parse_try_statement (void)
|
|
{
|
|
JERRY_UNIMPLEMENTED ();
|
|
}
|
|
|
|
/* statement
|
|
: statement_block
|
|
| variable_statement
|
|
| empty_statement
|
|
| if_statement
|
|
| iteration_statement
|
|
| continue_statement
|
|
| break_statement
|
|
| return_statement
|
|
| with_statement
|
|
| labelled_statement
|
|
| switch_statement
|
|
| throw_statement
|
|
| try_statement
|
|
| expression_statement
|
|
;
|
|
|
|
statement_block
|
|
: '{' LT!* statement_list? LT!* '}'
|
|
;
|
|
|
|
variable_statement
|
|
: 'var' LT!* variable_declaration_list (LT | ';')!
|
|
;
|
|
|
|
empty_statement
|
|
: ';'
|
|
;
|
|
|
|
expression_statement
|
|
: expression (LT | ';')!
|
|
;
|
|
|
|
iteration_statement
|
|
: do_while_statement
|
|
| while_statement
|
|
| for_statement
|
|
| for_in_statement
|
|
;
|
|
|
|
continue_statement
|
|
: 'continue' Identifier? (LT | ';')!
|
|
;
|
|
|
|
break_statement
|
|
: 'break' Identifier? (LT | ';')!
|
|
;
|
|
|
|
return_statement
|
|
: 'return' expression? (LT | ';')!
|
|
;
|
|
|
|
switchStatement
|
|
: 'switch' LT!* '(' LT!* expression LT!* ')' LT!* caseBlock
|
|
;
|
|
|
|
throw_statement
|
|
: 'throw' expression (LT | ';')!
|
|
;
|
|
|
|
try_statement
|
|
: 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?)
|
|
;*/
|
|
static void
|
|
parse_statement (void)
|
|
{
|
|
reset_temp_name ();
|
|
|
|
if (tok.type == TOK_CLOSE_BRACE)
|
|
{
|
|
lexer_save_token (tok);
|
|
return;
|
|
}
|
|
if (tok.type == TOK_OPEN_BRACE)
|
|
{
|
|
skip_newlines ();
|
|
if (tok.type != TOK_CLOSE_BRACE)
|
|
{
|
|
parse_statement_list ();
|
|
next_token_must_be (TOK_CLOSE_BRACE);
|
|
}
|
|
return;
|
|
}
|
|
if (is_keyword (KW_VAR))
|
|
{
|
|
skip_newlines ();
|
|
parse_variable_declaration_list (NULL);
|
|
return;
|
|
}
|
|
if (tok.type == TOK_SEMICOLON)
|
|
{
|
|
return;
|
|
}
|
|
if (is_keyword (KW_IF))
|
|
{
|
|
parse_if_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_DO))
|
|
{
|
|
parse_do_while_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_WHILE))
|
|
{
|
|
parse_while_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_FOR))
|
|
{
|
|
parse_for_or_for_in_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_CONTINUE))
|
|
{
|
|
must_be_inside_but_not_in ((uint8_t[]){NESTING_ITERATIONAL, NESTING_SWITCH}, 2,
|
|
NESTING_FUNCTION);
|
|
add_to_rewritable_opcodes (REWRITABLE_CONTINUE, opcode_counter);
|
|
DUMP_OPCODE_1 (jmp_up, INVALID_VALUE);
|
|
return;
|
|
}
|
|
if (is_keyword (KW_BREAK))
|
|
{
|
|
must_be_inside_but_not_in ((uint8_t[]){NESTING_ITERATIONAL, NESTING_SWITCH}, 2,
|
|
NESTING_FUNCTION);
|
|
add_to_rewritable_opcodes (REWRITABLE_BREAK, opcode_counter);
|
|
DUMP_OPCODE_1 (jmp_down, INVALID_VALUE);
|
|
return;
|
|
}
|
|
if (is_keyword (KW_RETURN))
|
|
{
|
|
T_IDX expr;
|
|
tok = lexer_next_token ();
|
|
if (tok.type != TOK_SEMICOLON)
|
|
{
|
|
expr = parse_expression ();
|
|
DUMP_OPCODE_1 (retval, expr);
|
|
}
|
|
else
|
|
DUMP_VOID_OPCODE (ret);
|
|
return;
|
|
}
|
|
if (is_keyword (KW_WITH))
|
|
{
|
|
parse_with_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_SWITCH))
|
|
{
|
|
parse_switch_statement ();
|
|
return;
|
|
}
|
|
if (is_keyword (KW_THROW))
|
|
{
|
|
JERRY_UNIMPLEMENTED ();
|
|
}
|
|
if (is_keyword (KW_TRY))
|
|
{
|
|
parse_try_statement ();
|
|
return;
|
|
}
|
|
if (tok.type == TOK_NAME)
|
|
{
|
|
token saved = tok;
|
|
skip_newlines ();
|
|
if (tok.type == TOK_COLON)
|
|
{
|
|
// STMT_LABELLED;
|
|
JERRY_UNIMPLEMENTED ();
|
|
}
|
|
else
|
|
{
|
|
lexer_save_token (tok);
|
|
tok = saved;
|
|
parse_expression ();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parse_expression ();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* source_element
|
|
: function_declaration
|
|
| statement
|
|
; */
|
|
static void
|
|
parse_source_element (void)
|
|
{
|
|
if (is_keyword (KW_FUNCTION))
|
|
parse_function_declaration ();
|
|
else
|
|
parse_statement ();
|
|
}
|
|
|
|
/* source_element_list
|
|
: source_element (LT!* source_element)*
|
|
; */
|
|
static void
|
|
parse_source_element_list (void)
|
|
{
|
|
opcode_counter_t reg_var_decl_loc;
|
|
|
|
start_new_scope ();
|
|
reg_var_decl_loc = opcode_counter;
|
|
DUMP_OPCODE_2 (reg_var_decl, min_temp_name, INVALID_VALUE);
|
|
|
|
while (tok.type != TOK_EOF && tok.type != TOK_CLOSE_BRACE)
|
|
{
|
|
parse_source_element ();
|
|
skip_newlines ();
|
|
}
|
|
lexer_save_token (tok);
|
|
if (max_temp_name > min_temp_name)
|
|
REWRITE_OPCODE_2 (reg_var_decl_loc, reg_var_decl, min_temp_name, max_temp_name - 1);
|
|
else if (max_temp_name == min_temp_name)
|
|
REWRITE_OPCODE_2 (reg_var_decl_loc, reg_var_decl, min_temp_name, max_temp_name);
|
|
else
|
|
JERRY_UNREACHABLE ();
|
|
finish_scope ();
|
|
optimizer_reorder_scope ((uint16_t) (reg_var_decl_loc + 1), opcode_counter);
|
|
}
|
|
|
|
/* program
|
|
: LT!* source_element_list LT!* EOF!
|
|
; */
|
|
void
|
|
parser_parse_program (void)
|
|
{
|
|
skip_newlines ();
|
|
parse_source_element_list ();
|
|
|
|
skip_newlines ();
|
|
JERRY_ASSERT (tok.type == TOK_EOF);
|
|
DUMP_OPCODE_1 (exitval, 0);
|
|
}
|
|
|
|
void
|
|
parser_init (void)
|
|
{
|
|
max_temp_name = temp_name = min_temp_name = lexer_get_reserved_ids_count ();
|
|
}
|
|
|
|
void
|
|
parser_fatal (jerry_status_t code)
|
|
{
|
|
__printf ("FATAL: %d\n", code);
|
|
lexer_dump_buffer_state ();
|
|
|
|
jerry_exit( code);
|
|
}
|