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 njs_token_t njs_parser_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { njs_token_t next; njs_parser_node_t *node; njs_vmcode_operation_t operation; switch (token) { case NJS_TOKEN_INCREMENT: operation = njs_vmcode_increment; break; case NJS_TOKEN_DECREMENT: operation = njs_vmcode_decrement; break; default: return njs_parser_post_inc_dec_expression(vm, parser, token); } next = njs_parser_token(parser); if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) { return next; } next = njs_parser_call_expression(vm, parser, next); if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) { return next; } if (parser->node->lvalue == NJS_LVALUE_NONE) { nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required"); return NJS_TOKEN_ILLEGAL; } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = token; node->u.operation = operation; node->left = parser->node; parser->node = node; parser->code_size += (parser->node->token == NJS_TOKEN_NAME) ? sizeof(njs_vmcode_3addr_t): sizeof(njs_vmcode_prop_get_t) + sizeof(njs_vmcode_3addr_t) + sizeof(njs_vmcode_prop_set_t); return next; }
static njs_token_t njs_parser_property_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { njs_token_t next; njs_parser_node_t *node; for ( ;; ) { if (token != NJS_TOKEN_DOT && token != NJS_TOKEN_OPEN_BRACKET) { return token; } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = NJS_TOKEN_PROPERTY; node->lvalue = NJS_LVALUE_ENABLED; node->u.operation = njs_vmcode_property_get; node->left = parser->node; next = njs_parser_token(parser); if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) { return next; } if (token == NJS_TOKEN_DOT) { if (next != NJS_TOKEN_NAME) { return NJS_TOKEN_ILLEGAL; } token = njs_parser_property_name(vm, parser, next); } else { token = njs_parser_property_brackets(vm, parser, next); } if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node->right = parser->node; parser->node = node; parser->code_size += sizeof(njs_vmcode_prop_get_t); } }
njs_token_t njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *parent) { njs_token_t token; njs_index_t index; njs_parser_node_t *node; index = NJS_SCOPE_CALLEE_ARGUMENTS; do { token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (token == NJS_TOKEN_CLOSE_PARENTHESIS) { break; } token = njs_parser_assignment_expression(vm, parser, NULL, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = NJS_TOKEN_ARGUMENT; node->index = index; index += sizeof(njs_value_t); node->left = parser->node; parser->node->dest = node; parent->right = node; parent = node; parser->code_size += sizeof(njs_vmcode_move_t); } while (token == NJS_TOKEN_COMMA); if (nxt_slow_path(token != NJS_TOKEN_CLOSE_PARENTHESIS)) { return NJS_TOKEN_ILLEGAL; } return token; }
static njs_token_t njs_parser_property_brackets(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { token = njs_parser_expression(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (nxt_slow_path(token != NJS_TOKEN_CLOSE_BRACKET)) { return NJS_TOKEN_ERROR; } return njs_parser_token(parser); }
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_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; }
njs_token_t njs_parser_property_name(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { nxt_int_t ret; njs_parser_node_t *node; node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = NJS_TOKEN_STRING; ret = njs_parser_string_create(vm, &node->u.value); if (nxt_slow_path(ret != NXT_OK)) { return NJS_TOKEN_ERROR; } parser->node = node; return njs_parser_token(parser); }
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; }
static njs_token_t njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { nxt_bool_t ctor; njs_parser_node_t *func, *node; ctor = 0; if (token == NJS_TOKEN_NEW) { token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } ctor = 1; } token = njs_parser_terminal(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } for ( ;; ) { token = njs_parser_property_expression(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node = parser->node; if (token != NJS_TOKEN_OPEN_PARENTHESIS) { /* TODO: var o = new Object; */ node->ctor = ctor; return token; } switch (node->token) { case NJS_TOKEN_NAME: func = node; func->token = NJS_TOKEN_FUNCTION_CALL; parser->code_size += sizeof(njs_vmcode_function_frame_t) + sizeof(njs_vmcode_function_call_t); break; case NJS_TOKEN_FUNCTION_EXPRESSION: func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; } func->token = NJS_TOKEN_FUNCTION_CALL; func->left = node; func->index = node->index; parser->code_size += sizeof(njs_vmcode_function_frame_t) + sizeof(njs_vmcode_function_call_t); break; case NJS_TOKEN_PROPERTY: func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; } func->token = NJS_TOKEN_METHOD_CALL; func->left = node; parser->code_size += sizeof(njs_vmcode_method_frame_t) + sizeof(njs_vmcode_function_call_t); break; default: /* * NJS_TOKEN_OPEN_PARENTHESIS, * NJS_TOKEN_OBJECT_CONSTRUCTOR, * NJS_TOKEN_ARRAY_CONSTRUCTOR, * NJS_TOKEN_BOOLEAN_CONSTRUCTOR, * NJS_TOKEN_NUMBER_CONSTRUCTOR, * NJS_TOKEN_STRING_CONSTRUCTOR, * NJS_TOKEN_FUNCTION_CONSTRUCTOR, * NJS_TOKEN_REGEXP_CONSTRUCTOR, * NJS_TOKEN_EVAL. */ func = njs_parser_node_alloc(vm); if (nxt_slow_path(func == NULL)) { return NJS_TOKEN_ERROR; } func->token = NJS_TOKEN_FUNCTION_CALL; func->left = node; parser->code_size += sizeof(njs_vmcode_function_frame_t) + sizeof(njs_vmcode_function_call_t); break; } token = njs_parser_arguments(vm, parser, func); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } parser->node = func; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } } }
static njs_token_t njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser, const njs_parser_expression_t *expr, njs_token_t token) { double num; njs_token_t next; njs_parser_node_t *node; njs_vmcode_operation_t operation; switch (token) { case NJS_TOKEN_ADDITION: token = NJS_TOKEN_UNARY_PLUS; operation = njs_vmcode_unary_plus; break; case NJS_TOKEN_SUBSTRACTION: token = NJS_TOKEN_UNARY_NEGATION; operation = njs_vmcode_unary_negation; break; case NJS_TOKEN_LOGICAL_NOT: operation = njs_vmcode_logical_not; break; case NJS_TOKEN_BITWISE_NOT: operation = njs_vmcode_bitwise_not; break; case NJS_TOKEN_TYPEOF: operation = njs_vmcode_typeof; break; case NJS_TOKEN_VOID: operation = njs_vmcode_void; break; case NJS_TOKEN_DELETE: operation = njs_vmcode_delete; break; default: return njs_parser_inc_dec_expression(vm, parser, token); } next = njs_parser_token(parser); if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) { return next; } next = njs_parser_unary_expression(vm, parser, NULL, next); if (nxt_slow_path(next <= NJS_TOKEN_ILLEGAL)) { return next; } if (token == NJS_TOKEN_UNARY_PLUS && parser->node->token == NJS_TOKEN_NUMBER) { /* Skip the unary plus of number. */ return next; } if (token == NJS_TOKEN_UNARY_NEGATION && 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); return next; } if (token == NJS_TOKEN_TYPEOF && parser->node->token == NJS_TOKEN_NAME) { parser->node->state = NJS_VARIABLE_TYPEOF; } else if (token == NJS_TOKEN_DELETE && parser->node->token == NJS_TOKEN_PROPERTY) { parser->node->token = NJS_TOKEN_PROPERTY_DELETE; parser->node->u.operation = njs_vmcode_property_delete; parser->code_size += sizeof(njs_vmcode_3addr_t); return next; } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = token; node->u.operation = operation; node->left = parser->node; node->left->dest = node; parser->node = node; parser->code_size += sizeof(njs_vmcode_2addr_t); return next; }
static njs_token_t njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser, const njs_parser_expression_t *expr, njs_token_t token) { nxt_int_t n; njs_parser_node_t *node; const njs_parser_operation_t *op; token = expr->next(vm, parser, expr->expression, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } for ( ;; ) { n = expr->count; op = expr->op; do { if (op->token == token) { goto found; } op++; n--; } while (n != 0); if (token == NJS_TOKEN_LINE_END) { token = njs_lexer_token(parser->lexer); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (njs_parser_expression_operator(token)) { continue; } } return token; found: node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = token; node->u.operation = op->operation; node->left = parser->node; node->left->dest = node; parser->code_size += op->size; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } token = expr->next(vm, parser, expr->expression, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node->right = parser->node; node->right->dest = node; parser->node = node; } }
njs_token_t njs_parser_conditional_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { njs_parser_node_t *node, *cond; token = njs_parser_binary_expression(vm, parser, &njs_parser_logical_or_expression, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } for ( ;; ) { if (token != NJS_TOKEN_CONDITIONAL) { return token; } parser->branch = 1; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } cond = njs_parser_node_alloc(vm); if (nxt_slow_path(cond == NULL)) { return NJS_TOKEN_ERROR; } cond->token = NJS_TOKEN_CONDITIONAL; cond->left = parser->node; node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } cond->right = node; node->token = NJS_TOKEN_BRANCHING; token = njs_parser_assignment_expression(vm, parser, NULL, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (nxt_slow_path(token != NJS_TOKEN_COLON)) { return NJS_TOKEN_ILLEGAL; } node->left = parser->node; node->left->dest = cond; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } token = njs_parser_assignment_expression(vm, parser, NULL, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node->right = parser->node; node->right->dest = cond; parser->node = cond; parser->code_size += sizeof(njs_vmcode_cond_jump_t) + sizeof(njs_vmcode_move_t) + sizeof(njs_vmcode_jump_t) + sizeof(njs_vmcode_move_t); } }
static njs_token_t njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser, const njs_parser_expression_t *expr, njs_token_t token) { size_t size; njs_parser_node_t *node, *pending; njs_vmcode_operation_t operation; token = njs_parser_conditional_expression(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } for ( ;; ) { switch (token) { case NJS_TOKEN_ASSIGNMENT: nxt_thread_log_debug("JS: ="); operation = njs_vmcode_move; break; case NJS_TOKEN_ADDITION_ASSIGNMENT: nxt_thread_log_debug("JS: +="); operation = njs_vmcode_addition; break; case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT: nxt_thread_log_debug("JS: -="); operation = njs_vmcode_substraction; break; case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: nxt_thread_log_debug("JS: *="); operation = njs_vmcode_multiplication; break; case NJS_TOKEN_DIVISION_ASSIGNMENT: nxt_thread_log_debug("JS: /="); operation = njs_vmcode_division; break; case NJS_TOKEN_REMAINDER_ASSIGNMENT: nxt_thread_log_debug("JS: %="); operation = njs_vmcode_remainder; break; case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT: nxt_thread_log_debug("JS: <<="); operation = njs_vmcode_left_shift; break; case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT: nxt_thread_log_debug("JS: >>="); operation = njs_vmcode_right_shift; break; case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: nxt_thread_log_debug("JS: >>="); operation = njs_vmcode_unsigned_right_shift; break; case NJS_TOKEN_BITWISE_AND_ASSIGNMENT: nxt_thread_log_debug("JS: &="); operation = njs_vmcode_bitwise_and; break; case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT: nxt_thread_log_debug("JS: ^="); operation = njs_vmcode_bitwise_xor; break; case NJS_TOKEN_BITWISE_OR_ASSIGNMENT: nxt_thread_log_debug("JS: |="); operation = njs_vmcode_bitwise_or; break; case NJS_TOKEN_LINE_END: token = njs_lexer_token(parser->lexer); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (njs_parser_expression_operator(token)) { continue; } /* Fall through. */ default: return token; } node = parser->node; if (node->lvalue == NJS_LVALUE_NONE) { nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required"); return NJS_TOKEN_ILLEGAL; } pending = NULL; if (node->token == NJS_TOKEN_NAME) { if (token == NJS_TOKEN_ASSIGNMENT) { node->state = NJS_VARIABLE_ASSIGNMENT; if (node->u.variable->state == NJS_VARIABLE_PENDING) { pending = node; } } else if (node->u.variable->state == NJS_VARIABLE_PENDING) { node->u.variable->state = NJS_VARIABLE_USED; } } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = token; node->u.operation = operation; node->left = parser->node; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } token = njs_parser_assignment_expression(vm, parser, NULL, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node->right = parser->node; parser->node = node; if (node->left->token == NJS_TOKEN_NAME) { if (node->token == NJS_TOKEN_ASSIGNMENT) { size = sizeof(njs_vmcode_move_t); } else { if (njs_parser_has_side_effect(node->right)) { size = sizeof(njs_vmcode_move_t) + sizeof(njs_vmcode_3addr_t); } else { size = sizeof(njs_vmcode_3addr_t); } } } else { if (node->token == NJS_TOKEN_ASSIGNMENT) { size = sizeof(njs_vmcode_prop_set_t); if (njs_parser_has_side_effect(node->right)) { size += 2 * sizeof(njs_vmcode_move_t); } } else { size = sizeof(njs_vmcode_prop_get_t) + sizeof(njs_vmcode_3addr_t) + sizeof(njs_vmcode_prop_set_t); } } parser->code_size += size; if (pending != NULL && pending->u.variable->state == NJS_VARIABLE_PENDING) { pending->u.variable->state = NJS_VARIABLE_SET; if (!parser->branch) { pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT; } } } }
njs_token_t njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token) { size_t size; njs_parser_node_t *node, *pending; njs_vmcode_operation_t operation; token = njs_parser_conditional_expression(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } for ( ;; ) { size = sizeof(njs_vmcode_3addr_t); switch (token) { case NJS_TOKEN_ASSIGNMENT: nxt_thread_log_debug("JS: ="); operation = njs_vmcode_move; size = sizeof(njs_vmcode_move_t); break; case NJS_TOKEN_LINE_END: token = njs_lexer_token(parser->lexer); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } if (njs_parser_expression_operator(token)) { continue; } /* Fall through. */ default: return token; } node = parser->node; if (node->lvalue == NJS_LVALUE_NONE) { nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required"); return NJS_TOKEN_ILLEGAL; } pending = NULL; if (node->token == NJS_TOKEN_NAME) { node->state = NJS_VARIABLE_ASSIGNMENT; if (node->u.variable->state == NJS_VARIABLE_PENDING) { pending = node; } } node = njs_parser_node_alloc(vm); if (nxt_slow_path(node == NULL)) { return NJS_TOKEN_ERROR; } node->token = token; node->u.operation = operation; node->left = parser->node; parser->code_size += size; token = njs_parser_token(parser); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } token = njs_parser_var_expression(vm, parser, token); if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) { return token; } node->right = parser->node; parser->node = node; if (pending != NULL && pending->u.variable->state == NJS_VARIABLE_PENDING) { pending->u.variable->state = NJS_VARIABLE_SET; parser->code_size -= sizeof(njs_vmcode_1addr_t); if (!parser->branch) { pending->state = NJS_VARIABLE_FIRST_ASSIGNMENT; } } } }