blob: 7e91fa3d7c78c70cb490e3b5e00068601ea99ff2 [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
* Copyright (C) NGINX, Inc.
*/
#include <nxt_auto_config.h>
#include <nxt_types.h>
#include <nxt_clang.h>
#include <nxt_stub.h>
#include <nxt_array.h>
#include <nxt_lvlhsh.h>
#include <nxt_random.h>
#include <nxt_mem_cache_pool.h>
#include <njscript.h>
#include <njs_vm.h>
#include <njs_number.h>
#include <njs_object.h>
#include <njs_variable.h>
#include <njs_parser.h>
#include <njs_regexp.h>
#include <string.h>
typedef nxt_int_t (*njs_parser_operation_t)(njs_vm_t *vm,
njs_parser_t *parser, njs_token_t token, const void *data);
typedef nxt_int_t (*njs_parser_stack_operation_t)(njs_vm_t *vm,
njs_parser_t *parser, const void *data);
#define NJS_TOKEN_ANY NJS_TOKEN_ILLEGAL
#define NJS_PARSER_NODE ((void *) -1)
#define NJS_PARSER_VOID ((void *) -2)
typedef struct {
njs_token_t token;
njs_parser_operation_t operation;
const void *data;
const void *primed;
} njs_parser_terminal_t;
#define NJS_PARSER_IGNORE_LINE_END 0
#define NJS_PARSER_TEST_LINE_END 1
typedef struct {
uint8_t take_line_end; /* 1 bit */
uint8_t count;
#if (NXT_SUNC)
/*
* SunC supports C99 flexible array members but does not allow
* static struct's initialization with arbitrary number of members.
*/
const njs_parser_terminal_t terminal[9];
#else
const njs_parser_terminal_t terminal[];
#endif
} njs_parser_switch_t;
njs_token_t njs_parser_token(njs_parser_t *parser);
static void *njs_parser_stack_pop(njs_parser_t *parser);
static nxt_int_t njs_parser_stack_push(njs_vm_t *vm, njs_parser_t *parser,
const void *data);
static const void *const njs_parser_statement[];
static const void *const njs_parser_expression0[];
/* STUB */
static nxt_int_t top = -1;
static void *stack[1024];
/**/
njs_parser_node_t *
njs_nonrecursive_parser(njs_vm_t *vm, njs_parser_t *parser)
{
nxt_int_t ret;
njs_token_t token;
njs_parser_stack_operation_t operation;
if (top < 0) {
njs_parser_stack_push(vm, parser, njs_parser_statement);
}
token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
/* TODO: NJS_TOKEN_AGAIN */
return NULL;
}
do {
operation = (njs_parser_stack_operation_t) njs_parser_stack_pop(parser);
if (operation == NULL) {
if (parser->lexer->token == NJS_TOKEN_END) {
return parser->node;
}
break;
}
ret = operation(vm, parser, njs_parser_stack_pop(parser));
} while (ret == NXT_OK);
nxt_thread_log_error(NXT_LOG_ERR, "unexpected token");
return NULL;
}
njs_token_t
njs_parser_token(njs_parser_t *parser)
{
njs_token_t token;
do {
token = njs_lexer_token(parser->lexer);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
return token;
}
} while (nxt_slow_path(token == NJS_TOKEN_LINE_END));
return token;
}
static void *
njs_parser_stack_pop(njs_parser_t *parser)
{
if (top < 0) {
return NULL;
}
return stack[top--];
}
static nxt_int_t
njs_parser_stack_push(njs_vm_t *vm, njs_parser_t *parser, const void *data)
{
void *const *next;
next = data;
if (next != NULL) {
do {
top++;
if (*next != NJS_PARSER_NODE) {
stack[top] = *next;
} else {
stack[top] = parser->node;
}
next++;
} while (*next != NULL);
}
return NXT_OK;
}
static nxt_int_t
njs_parser_switch(njs_vm_t *vm, njs_parser_t *parser, void *data)
{
nxt_int_t ret;
nxt_uint_t n;
njs_token_t token;
njs_parser_switch_t *swtch;
const njs_parser_terminal_t *term;
swtch = data;
token = parser->lexer->token;
n = swtch->count;
term = swtch->terminal;
do {
if (token == term->token || term->token == NJS_TOKEN_ANY) {
ret = term->operation(vm, parser, token, term->data);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
ret = njs_parser_stack_push(vm, parser, term->primed);
if (nxt_slow_path(ret != NXT_OK)) {
return NXT_ERROR;
}
if (term->token != NJS_TOKEN_ANY) {
token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
/* TODO: NJS_TOKEN_AGAIN */
return NXT_ERROR;
}
}
return NXT_OK;
}
term++;
n--;
} while (n != 0);
return NXT_OK;
}
static nxt_int_t
njs_parser_statement_semicolon(njs_vm_t *vm, njs_parser_t *parser,
void *data)
{
njs_token_t token;
njs_parser_node_t *node;
node = data;
switch (parser->lexer->token) {
case NJS_TOKEN_SEMICOLON:
token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
/* TODO: NJS_TOKEN_AGAIN */
return NXT_ERROR;
}
/* Fall through. */
case NJS_TOKEN_END:
node->right = parser->node;
parser->node = node;
return NXT_OK;
default:
break;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_test_token(njs_vm_t *vm, njs_parser_t *parser, void *data)
{
njs_token_t token;
token = (njs_token_t) data;
if (parser->lexer->token == token) {
token = njs_parser_token(parser);
if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
/* TODO: NJS_TOKEN_AGAIN */
return NXT_ERROR;
}
return NXT_OK;
}
vm->exception = &njs_exception_syntax_error;
return NXT_ERROR;
}
static nxt_int_t
njs_parser_node(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
const void *data)
{
njs_parser_node_t *node;
token = (njs_token_t) data;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->left = parser->node;
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_noop(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
const void *data)
{
return NXT_OK;
}
static nxt_int_t
njs_parser_link_left(njs_vm_t *vm, njs_parser_t *parser, const void *data)
{
njs_parser_node_t *node;
node = (njs_parser_node_t *) data;
node->left = parser->node;
parser->node = node;
return NXT_OK;
}
static nxt_int_t
njs_parser_link_right(njs_vm_t *vm, njs_parser_t *parser, const void *data)
{
njs_parser_node_t *node;
node = (njs_parser_node_t *) data;
node->right = parser->node;
parser->node = node;
return NXT_OK;
}
static nxt_int_t
njs_parser_condition_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->left = parser->node;
parser->node = node;
parser->code_size += sizeof(njs_vmcode_cond_jump_t)
+ sizeof(njs_vmcode_move_t)
+ sizeof(njs_vmcode_jump_t)
+ sizeof(njs_vmcode_move_t);
parser->branch = 1;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
njs_vmcode_operation_t operation;
operation = (njs_vmcode_operation_t) data;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.operation = operation;
node->left = parser->node;
parser->node = node;
parser->code_size += sizeof(njs_vmcode_3addr_t);
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_post_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
njs_vmcode_operation_t operation;
operation = (njs_vmcode_operation_t) data;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.operation = operation;
node->left = parser->node;
parser->node = node;
parser->code_size += sizeof(njs_vmcode_3addr_t);
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_unary_expression0(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
njs_vmcode_operation_t operation;
operation = (njs_vmcode_operation_t) data;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.operation = operation;
parser->node = node;
parser->code_size += sizeof(njs_vmcode_3addr_t);
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
njs_vmcode_operation_t operation;
operation = (njs_vmcode_operation_t) data;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.operation = operation;
parser->node = node;
parser->code_size += sizeof(njs_vmcode_2addr_t);
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_unary_plus_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
return njs_parser_unary_expression(vm, parser,
NJS_TOKEN_UNARY_PLUS, data);
}
static nxt_int_t
njs_parser_unary_plus_link(njs_vm_t *vm, njs_parser_t *parser,
const void *data)
{
njs_parser_node_t *node;
node = (njs_parser_node_t *) data;
/* Skip the unary plus of number. */
if (parser->node->token != NJS_TOKEN_NUMBER) {
node->left = parser->node;
parser->node = node;
}
return NXT_OK;
}
static nxt_int_t
njs_parser_unary_negation_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
return njs_parser_unary_expression(vm, parser,
NJS_TOKEN_UNARY_NEGATION, data);
}
static nxt_int_t
njs_parser_unary_negative_link(njs_vm_t *vm, njs_parser_t *parser,
void *data)
{
double num;
njs_parser_node_t *node;
node = data;
if (parser->node->token == NJS_TOKEN_NUMBER) {
/* Optimization of common negative number. */
node = parser->node;
num = -node->u.value.data.u.number;
node->u.value.data.u.number = num;
node->u.value.data.truth = njs_is_number_true(num);
} else {
node->left = parser->node;
parser->node = node;
}
return NXT_OK;
}
static nxt_int_t
njs_parser_name_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
nxt_uint_t level;
njs_extern_t *ext;
njs_variable_t *var;
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
ext = njs_parser_external(vm, parser);
if (ext != NULL) {
node->token = NJS_TOKEN_EXTERNAL;
node->u.value.type = NJS_EXTERNAL;
node->u.value.data.truth = 1;
node->index = (njs_index_t) ext;
} else {
node->token = token;
var = njs_parser_variable(vm, parser, &level);
if (nxt_slow_path(var == NULL)) {
return NJS_TOKEN_ERROR;
}
switch (var->state) {
case NJS_VARIABLE_CREATED:
var->state = NJS_VARIABLE_PENDING;
parser->code_size += sizeof(njs_vmcode_1addr_t);
break;
case NJS_VARIABLE_PENDING:
var->state = NJS_VARIABLE_USED;
parser->code_size += sizeof(njs_vmcode_1addr_t);
break;
case NJS_VARIABLE_USED:
parser->code_size += sizeof(njs_vmcode_1addr_t);
break;
case NJS_VARIABLE_SET:
case NJS_VARIABLE_DECLARED:
break;
}
node->lvalue = NJS_LVALUE_ENABLED;
node->u.variable = var;
}
}
parser->node = node;
return NXT_OK;
}
static nxt_int_t
njs_parser_var_name(njs_vm_t *vm, njs_parser_t *parser, void *data)
{
/* TODO disable NJS_TOKEN_EXTERNAL */
return njs_parser_name_expression(vm, parser, NJS_TOKEN_NAME, data);
}
static nxt_int_t
njs_parser_this_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->index = NJS_INDEX_THIS;
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_string_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
nxt_int_t ret;
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
ret = njs_parser_string_create(vm, &node->u.value);
if (nxt_slow_path(ret != NXT_OK)) {
return NJS_TOKEN_ERROR;
}
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_number_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
double num;
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
num = parser->lexer->number;
node->u.value.data.u.number = num;
node->u.value.type = NJS_NUMBER;
node->u.value.data.truth = njs_is_number_true(num);
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_boolean_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.value = (parser->lexer->number == 0.0) ? njs_value_false:
njs_value_true;
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_undefined_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.value = njs_value_void;
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_null_expression(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
njs_parser_node_t *node;
node = njs_parser_node_alloc(vm);
if (nxt_fast_path(node != NULL)) {
node->token = token;
node->u.value = njs_value_null;
parser->node = node;
return NXT_OK;
}
return NXT_ERROR;
}
static nxt_int_t
njs_parser_syntax_error(njs_vm_t *vm, njs_parser_t *parser,
njs_token_t token, const void *data)
{
vm->exception = &njs_exception_syntax_error;
return NXT_ERROR;
}
/*
* The variables and literal values.
*
* VALUE = "(" EXPRESSION ")"
* [ NAME create_node ]
* [ "this" create_node ]
* [ STRING create_node ]
* [ NUMBER create_node ]
* [ BOOLEAN create_node ]
* [ "undefined" create_node ]
* [ "null" create_node ]
* ERROR
*/
static const void *const njs_parser_grouping_expression[] = {
(void *) NJS_TOKEN_CLOSE_PARENTHESIS, (void *) njs_parser_test_token,
&njs_parser_expression0, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_value_expression_switch = {
NJS_PARSER_IGNORE_LINE_END,
9, {
{ NJS_TOKEN_OPEN_PARENTHESIS, njs_parser_noop, NULL,
&njs_parser_grouping_expression },
{ NJS_TOKEN_NAME, njs_parser_name_expression, NULL, NULL },
{ NJS_TOKEN_THIS, njs_parser_this_expression, NULL, NULL },
{ NJS_TOKEN_STRING, njs_parser_string_expression, NULL, NULL },
{ NJS_TOKEN_NUMBER, njs_parser_number_expression, NULL, NULL },
{ NJS_TOKEN_BOOLEAN, njs_parser_boolean_expression, NULL, NULL },
{ NJS_TOKEN_UNDEFINED,
njs_parser_undefined_expression, NULL, NULL },
{ NJS_TOKEN_NULL, njs_parser_null_expression, NULL, NULL },
{ NJS_TOKEN_ANY, njs_parser_syntax_error, NULL, NULL },
}
};
#if 0
static const void *const njs_parser_value_expression[] = {
&njs_parser_value_expression_switch, (void *) njs_parser_switch,
NULL,
};
#endif
/*
* The postfix increment and decrement operations.
*
* POSTFIX_INC_DEC = VALUE [ "++" create_node ]
* VALUE [ "--" create_node ]
* <>
*/
static const njs_parser_switch_t njs_parser_post_inc_dec_expression_switch = {
NJS_PARSER_IGNORE_LINE_END,
2, {
{ NJS_TOKEN_INCREMENT,
njs_parser_post_unary_expression,
(void *) njs_vmcode_post_increment, NULL },
{ NJS_TOKEN_DECREMENT,
njs_parser_post_unary_expression,
(void *) njs_vmcode_post_decrement, NULL },
}
};
static const void *const njs_parser_post_inc_dec_expression[] = {
&njs_parser_post_inc_dec_expression_switch, (void *) njs_parser_switch,
&njs_parser_value_expression_switch, (void *) njs_parser_switch,
NULL,
};
/*
* The prefix increment and decrement operations.
*
* PREFIX_INC_DEC = [ "++" create_node ] POSTFIX_INC_DEC link_left
* [ "--" create_node ] POSTFIX_INC_DEC link_left
* <> POSTFIX_INC_DEC
*/
static const void *const njs_parser_inc_dec_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_link_left,
&njs_parser_value_expression_switch, (void *) njs_parser_switch,
NULL,
};
static const njs_parser_switch_t njs_parser_inc_dec_expression_switch = {
NJS_PARSER_IGNORE_LINE_END,
3, {
{ NJS_TOKEN_INCREMENT,
njs_parser_unary_expression0, (void *) njs_vmcode_increment,
njs_parser_inc_dec_expression_primed },
{ NJS_TOKEN_DECREMENT,
njs_parser_unary_expression0, (void *) njs_vmcode_decrement,
njs_parser_inc_dec_expression_primed },
{ NJS_TOKEN_ANY, njs_parser_noop, NULL,
njs_parser_post_inc_dec_expression },
}
};
static const void *const njs_parser_inc_dec_expression[] = {
&njs_parser_inc_dec_expression_switch, (void *) njs_parser_switch,
NULL,
};
/*
* The unary operations.
*
* UNARY = [ "+" create_node ] UNARY plus_link_left
* [ "-" create_node ] UNARY negation_link_left
* [ "!" create_node ] UNARY link_left
* [ "~" create_node ] UNARY link_left
* [ "typeof" create_node ] UNARY link_left
* [ "void" create_node ] UNARY link_left
* [ "delete" create_node ] UNARY link_left
* <> INC_DEC
*/
static const njs_parser_switch_t njs_parser_unary_expression_switch;
static const void *const njs_parser_unary_plus_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_unary_plus_link,
&njs_parser_unary_expression_switch, (void *) njs_parser_switch,
NULL,
};
static const void *const njs_parser_unary_negative_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_unary_negative_link,
&njs_parser_unary_expression_switch, (void *) njs_parser_switch,
NULL,
};
static const void *const njs_parser_unary_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_link_left,
&njs_parser_unary_expression_switch, (void *) njs_parser_switch,
NULL,
};
static const njs_parser_switch_t njs_parser_unary_expression_switch = {
NJS_PARSER_TEST_LINE_END,
8, {
{ NJS_TOKEN_ADDITION,
njs_parser_unary_plus_expression, (void *) njs_vmcode_unary_plus,
njs_parser_unary_plus_expression_primed },
{ NJS_TOKEN_SUBSTRACTION,
njs_parser_unary_negation_expression,
(void *) njs_vmcode_unary_negation,
njs_parser_unary_negative_expression_primed },
{ NJS_TOKEN_LOGICAL_NOT,
njs_parser_unary_expression, (void *) njs_vmcode_logical_not,
njs_parser_unary_expression_primed },
{ NJS_TOKEN_BITWISE_NOT,
njs_parser_unary_expression, (void *) njs_vmcode_bitwise_not,
njs_parser_unary_expression_primed },
{ NJS_TOKEN_TYPEOF,
njs_parser_unary_expression, (void *) njs_vmcode_typeof,
njs_parser_unary_expression_primed },
{ NJS_TOKEN_VOID,
njs_parser_unary_expression, (void *) njs_vmcode_void,
njs_parser_unary_expression_primed },
{ NJS_TOKEN_DELETE,
njs_parser_unary_expression, (void *) njs_vmcode_delete,
njs_parser_unary_expression_primed },
{ NJS_TOKEN_ANY, njs_parser_noop, NULL,
njs_parser_inc_dec_expression },
}
};
/*
* The left associative multiplication, division and remainder operations.
*
* MULTIPLICATION = UNARY MULTIPLICATION'
* MULTIPLICATION' = [ "*" create_node ] UNARY link_right MULTIPLICATION'
* [ "/" create_node ] UNARY link_right MULTIPLICATION'
* [ "%" create_node ] UNARY link_right MULTIPLICATION'
* <>
*/
static const njs_parser_switch_t njs_parser_multiplicative_expression_switch;
static const void *const njs_parser_multiplicative_expression_primed[] = {
&njs_parser_multiplicative_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_unary_expression_switch, (void *) njs_parser_switch,
NULL,
};
static const njs_parser_switch_t
njs_parser_multiplicative_expression_switch =
{
NJS_PARSER_TEST_LINE_END,
3, {
{ NJS_TOKEN_MULTIPLICATION,
njs_parser_binary_expression, (void *) njs_vmcode_multiplication,
njs_parser_multiplicative_expression_primed },
{ NJS_TOKEN_DIVISION,
njs_parser_binary_expression, (void *) njs_vmcode_division,
njs_parser_multiplicative_expression_primed },
{ NJS_TOKEN_REMAINDER,
njs_parser_binary_expression, (void *) njs_vmcode_remainder,
njs_parser_multiplicative_expression_primed },
}
};
static const void *const njs_parser_multiplicative_expression[] = {
&njs_parser_multiplicative_expression_switch, (void *) njs_parser_switch,
&njs_parser_unary_expression_switch, (void *) njs_parser_switch,
NULL,
};
/*
* The left associative addition and substraction operations.
*
* ADDITION = MULTIPLICATION ADDITION'
* ADDITION' = [ "+" create_node ] MULTIPLICATION link_right ADDITION'
* [ "-" create_node ] MULTIPLICATION link_right ADDITION'
* <>
*/
static const njs_parser_switch_t njs_parser_additive_expression_switch;
static const void *const njs_parser_additive_expression_primed[] = {
&njs_parser_additive_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
njs_parser_multiplicative_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_additive_expression_switch = {
NJS_PARSER_TEST_LINE_END,
2, {
{ NJS_TOKEN_ADDITION,
njs_parser_binary_expression, (void *) njs_vmcode_addition,
njs_parser_additive_expression_primed },
{ NJS_TOKEN_SUBSTRACTION,
njs_parser_binary_expression, (void *) njs_vmcode_substraction,
njs_parser_additive_expression_primed },
}
};
static const void *const njs_parser_additive_expression[] = {
&njs_parser_additive_expression_switch, (void *) njs_parser_switch,
njs_parser_multiplicative_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative bitwise shift operations.
*
* BITWISE_SHIFT = ADDITION BITWISE_SHIFT'
* BITWISE_SHIFT' = [ "<<" create_node ] ADDITION link_right BITWISE_SHIFT'
* [ ">>" create_node ] ADDITION link_right BITWISE_SHIFT'
* [ ">>>" create_node ] ADDITION link_right BITWISE_SHIFT'
* <>
*/
static const njs_parser_switch_t njs_parser_bitwise_shift_expression_switch;
static const void *const njs_parser_bitwise_shift_expression_primed[] = {
&njs_parser_bitwise_shift_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_additive_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_bitwise_shift_expression_switch = {
NJS_PARSER_TEST_LINE_END,
3, {
{ NJS_TOKEN_LEFT_SHIFT,
njs_parser_binary_expression, (void *) njs_vmcode_left_shift,
njs_parser_bitwise_shift_expression_primed },
{ NJS_TOKEN_RIGHT_SHIFT,
njs_parser_binary_expression, (void *) njs_vmcode_right_shift,
njs_parser_bitwise_shift_expression_primed },
{ NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,
njs_parser_binary_expression,
(void *) njs_vmcode_unsigned_right_shift,
njs_parser_bitwise_shift_expression_primed },
}
};
static const void *const njs_parser_bitwise_shift_expression[] = {
&njs_parser_bitwise_shift_expression_switch, (void *) njs_parser_switch,
njs_parser_additive_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative relational operations.
*
* RELATIONAL = BITWISE_SHIFT RELATIONAL'
* RELATIONAL' = [ "< create_node ] BITWISE_SHIFT link_right RELATIONAL'
* [ "<=" create_node ] BITWISE_SHIFT link_right RELATIONAL'
* [ ">" create_node ] BITWISE_SHIFT link_right RELATIONAL'
* [ ">=" create_node ] BITWISE_SHIFT link_right RELATIONAL'
* [ "in" create_node ] BITWISE_SHIFT link_right RELATIONAL'
* <>
*/
static const njs_parser_switch_t njs_parser_relational_expression_switch;
static const void *const njs_parser_relational_expression_primed[] = {
&njs_parser_relational_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_relational_expression_switch = {
NJS_PARSER_TEST_LINE_END,
6, {
{ NJS_TOKEN_LESS,
njs_parser_binary_expression, (void *) njs_vmcode_less,
njs_parser_relational_expression_primed },
{ NJS_TOKEN_LESS_OR_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_less_or_equal,
njs_parser_relational_expression_primed },
{ NJS_TOKEN_GREATER,
njs_parser_binary_expression, (void *) njs_vmcode_greater,
njs_parser_relational_expression_primed },
{ NJS_TOKEN_GREATER_OR_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_greater_or_equal,
njs_parser_relational_expression_primed },
{ NJS_TOKEN_IN,
njs_parser_binary_expression, (void *) njs_vmcode_property_in,
njs_parser_relational_expression_primed },
{ NJS_TOKEN_INSTANCEOF,
njs_parser_binary_expression, (void *) njs_vmcode_instance_of,
njs_parser_relational_expression_primed },
}
};
static const void *const njs_parser_relational_expression[] = {
&njs_parser_relational_expression_switch, (void *) njs_parser_switch,
njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative equality operations.
*
* EQUALITY = RELATION EQUALITY'
* EQUALITY' = [ "==" create_node ] RELATION link_right EQUALITY'
* [ "!=" create_node ] RELATION link_right EQUALITY'
* [ "===" create_node ] RELATION link_right EQUALITY'
* [ "!==" create_node ] RELATION link_right EQUALITY'
* <>
*/
static const njs_parser_switch_t njs_parser_equality_expression_switch;
static const void *const njs_parser_equality_expression_primed[] = {
&njs_parser_equality_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_relational_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_equality_expression_switch = {
NJS_PARSER_TEST_LINE_END,
4, {
{ NJS_TOKEN_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_equal,
njs_parser_equality_expression_primed },
{ NJS_TOKEN_NOT_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_not_equal,
njs_parser_equality_expression_primed },
{ NJS_TOKEN_STRICT_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_strict_equal,
njs_parser_equality_expression_primed },
{ NJS_TOKEN_STRICT_NOT_EQUAL,
njs_parser_binary_expression, (void *) njs_vmcode_strict_not_equal,
njs_parser_equality_expression_primed },
}
};
static const void *const njs_parser_equality_expression[] = {
&njs_parser_equality_expression_switch, (void *) njs_parser_switch,
njs_parser_relational_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative bitwise AND.
*
* BITWISE_AND = EQUALITY BITWISE_AND'
* BITWISE_AND' = [ "&" create_node ] EQUALITY link_right BITWISE_AND'
* <>
*/
static const njs_parser_switch_t njs_parser_bitwise_and_expression_switch;
static const void *const njs_parser_bitwise_and_expression_primed[] = {
&njs_parser_bitwise_and_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_bitwise_shift_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_bitwise_and_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_BITWISE_AND,
njs_parser_binary_expression, (void *) njs_vmcode_bitwise_and,
njs_parser_bitwise_and_expression_primed },
}
};
static const void *const njs_parser_bitwise_and_expression[] = {
&njs_parser_bitwise_and_expression_switch, (void *) njs_parser_switch,
njs_parser_equality_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative bitwise XOR.
*
* BITWISE_XOR = BITWISE_AND BITWISE_XOR'
* BITWISE_XOR' = [ "^" create_node ] BITWISE_AND link_right BITWISE_XOR'
* <>
*/
static const njs_parser_switch_t njs_parser_bitwise_xor_expression_switch;
static const void *const njs_parser_bitwise_xor_expression_primed[] = {
&njs_parser_bitwise_xor_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_bitwise_and_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_bitwise_xor_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_BITWISE_XOR,
njs_parser_binary_expression, (void *) njs_vmcode_bitwise_xor,
njs_parser_bitwise_xor_expression_primed },
}
};
static const void *const njs_parser_bitwise_xor_expression[] = {
&njs_parser_bitwise_xor_expression_switch, (void *) njs_parser_switch,
njs_parser_bitwise_and_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative bitwise OR.
*
* BITWISE_OR = BITWISE_XOR BITWISE_OR'
* BITWISE_OR' = [ "|" create_node ] BITWISE_XOR link_right BITWISE_OR'
* <>
*/
static const njs_parser_switch_t njs_parser_bitwise_or_expression_switch;
static const void *const njs_parser_bitwise_or_expression_primed[] = {
&njs_parser_bitwise_or_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_bitwise_xor_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_bitwise_or_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_BITWISE_OR,
njs_parser_binary_expression, (void *) njs_vmcode_bitwise_or,
njs_parser_bitwise_or_expression_primed },
}
};
static const void *const njs_parser_bitwise_or_expression[] = {
&njs_parser_bitwise_or_expression_switch, (void *) njs_parser_switch,
njs_parser_bitwise_xor_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative logical AND.
*
* LOGICAL_AND = BITWISE_OR LOGICAL_AND'
* LOGICAL_AND' = [ "&&" create_node ] BITWISE_OR link_right LOGICAL_AND'
* <>
*/
static const njs_parser_switch_t njs_parser_logical_and_expression_switch;
static const void *const njs_parser_logical_and_expression_primed[] = {
&njs_parser_logical_and_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_bitwise_or_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_logical_and_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_LOGICAL_AND,
njs_parser_binary_expression, (void *) njs_vmcode_test_if_false,
njs_parser_logical_and_expression_primed },
}
};
static const void *const njs_parser_logical_and_expression[] = {
&njs_parser_logical_and_expression_switch, (void *) njs_parser_switch,
njs_parser_bitwise_or_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative logical OR.
*
* LOGICAL_OR = LOGICAL_AND LOGICAL_OR'
* LOGICAL_OR' = [ "||" create_node ] LOGICAL_AND link_right LOGICAL_OR'
* <>
*/
static const njs_parser_switch_t njs_parser_logical_or_expression_switch;
static const void *const njs_parser_logical_or_expression_primed[] = {
&njs_parser_logical_or_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_logical_and_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_logical_or_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_LOGICAL_OR,
njs_parser_binary_expression, (void *) njs_vmcode_test_if_true,
njs_parser_logical_or_expression_primed },
}
};
static const void *const njs_parser_logical_or_expression[] = {
&njs_parser_logical_or_expression_switch, (void *) njs_parser_switch,
njs_parser_logical_and_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The right associative condition operation.
*
* CONDITION = LOGICAL_OR CONDITION'
* CONDITION' = [ "?" create_node ] ASSIGNMENT COLON link_right
* <>
* COLON = [ ":" create_node ] ASSIGNMENT link_right
* ERROR
*/
static const void *const njs_parser_assignment_expression[];
static const void *const njs_parser_colon_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_colon_expression_switch = {
NJS_PARSER_IGNORE_LINE_END,
2, {
{ NJS_TOKEN_COLON, njs_parser_node, (void *) NJS_TOKEN_ELSE,
njs_parser_colon_expression_primed },
{ NJS_TOKEN_ANY, njs_parser_syntax_error, NULL, NULL },
}
};
static const void *const njs_parser_conditional_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_colon_expression_switch, (void *) njs_parser_switch,
&njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_conditional_expression_switch = {
NJS_PARSER_IGNORE_LINE_END,
1, {
{ NJS_TOKEN_CONDITIONAL,
njs_parser_condition_expression, (void *) NJS_TOKEN_CONDITIONAL,
njs_parser_conditional_expression_primed },
}
};
static const void *const njs_parser_conditional_expression0[] = {
&njs_parser_conditional_expression_switch, (void *) njs_parser_switch,
njs_parser_logical_or_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The right associative assignment operations.
*
* ASSIGNMENT = CONDITION ASSIGNMENT'
* ASSIGNMENT' = [ "=" create_node ] CONDITION ASSIGNMENT' link_right
* <>
*/
static const void *const njs_parser_assignment_expression_primed[] = {
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_assignment_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_ASSIGNMENT,
/* STUB */ njs_parser_binary_expression, (void *) njs_vmcode_move,
njs_parser_assignment_expression_primed },
}
};
static const void *const njs_parser_assignment_expression[] = {
&njs_parser_assignment_expression_switch, (void *) njs_parser_switch,
njs_parser_conditional_expression0, (void *) njs_parser_stack_push,
NULL,
};
/*
* The left associative comma.
*
* EXPRESSION = ASSIGNMENT EXPRESSION'
* EXPRESSION' = [ "," create_node ] ASSIGNMENT link_right EXPRESSION'
* <>
*/
static const njs_parser_switch_t njs_parser_comma_expression_switch;
static const void *const njs_parser_expression_primed[] = {
&njs_parser_comma_expression_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_comma_expression_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_COMMA,
njs_parser_binary_expression, NULL, njs_parser_expression_primed },
}
};
static const void *const njs_parser_expression0[] = {
&njs_parser_comma_expression_switch, (void *) njs_parser_switch,
njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
/*
* The variable declarations and initializations.
*
* VAR = [ NAME create_node ] VAR_INIT
* VAR_INIT = "=" ASSIGNMENT [ link_right ] VAR_NEXT
* "," VAR
* <>
* VAR_NEXT = "," VAR
* <>
*/
static const void *const njs_parser_var_statement[];
static const njs_parser_switch_t njs_parser_var_next_switch = {
NJS_PARSER_TEST_LINE_END,
1, {
{ NJS_TOKEN_COMMA,
njs_parser_noop, NULL, njs_parser_var_statement },
}
};
static const void *const njs_parser_var_init_expression[] = {
&njs_parser_var_next_switch, (void *) njs_parser_switch,
NJS_PARSER_NODE, (void *) njs_parser_link_right,
&njs_parser_assignment_expression, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_var_init_switch = {
NJS_PARSER_TEST_LINE_END,
2, {
{ NJS_TOKEN_ASSIGNMENT,
/* TODO */ njs_parser_binary_expression, (void *) njs_vmcode_move,
njs_parser_var_init_expression },
{ NJS_TOKEN_COMMA, /* TODO: free node */
njs_parser_noop, NULL, njs_parser_var_statement },
}
};
static const void *const njs_parser_var_statement[] = {
&njs_parser_var_init_switch, (void *) njs_parser_switch,
NJS_PARSER_VOID, (void *) njs_parser_var_name,
NULL,
};
/*
* The statements.
*
* STATEMENT = <END>
* "var" VAR
* ";" STATEMENT
* <*> create_node EXPRESSION [ ";" link_right ] STATEMENT
*/
static const void *const njs_parser_statement[];
static const void *const njs_parser_expression_statement[] = {
&njs_parser_statement, (void *) njs_parser_stack_push,
NJS_PARSER_NODE, (void *) njs_parser_statement_semicolon,
&njs_parser_expression0, (void *) njs_parser_stack_push,
NULL,
};
static const njs_parser_switch_t njs_parser_statement_switch = {
NJS_PARSER_IGNORE_LINE_END,
// 4, {
3, {
{ NJS_TOKEN_VAR, njs_parser_noop, NULL, njs_parser_var_statement },
{ NJS_TOKEN_END, njs_parser_noop, NULL, NULL },
#if 0
{ NJS_TOKEN_SEMICOLON,
njs_parser_node, (void *) NJS_TOKEN_STATEMENT, NULL },
#endif
{ NJS_TOKEN_ANY, njs_parser_node, (void *) NJS_TOKEN_STATEMENT,
njs_parser_expression_statement },
}
};
static const void *const njs_parser_statement[] = {
&njs_parser_statement_switch, (void *) njs_parser_switch,
NULL,
};