// number = { digit } static enum v7_err parse_num(struct v7 *v7) { double value = 0; int is_negative = 0; if (*v7->cursor == '-') { is_negative = 1; TRY(match(v7, *v7->cursor)); } CHECK(is_digit(*v7->cursor), V7_SYNTAX_ERROR); v7->tok = v7->cursor; while (is_digit(*v7->cursor)) { value *= 10; value += *v7->cursor++ - '0'; } if (is_negative) { value = -value; } v7->tok_len = (unsigned long) (v7->cursor - v7->tok); skip_whitespaces_and_comments(v7); if (!v7->no_exec) { TRY(v7_make_and_push(v7, V7_NUM)); v7_top(v7)[-1]->v.num = value; } return V7_OK; }
V7_PRIVATE enum v7_err Str_match(struct v7_c_func_arg *cfa) { #define v7 (cfa->v7) /* Needed for TRY() macro below */ struct v7_val *arg = cfa->args[0]; struct Resub sub; struct v7_val *arr = NULL; unsigned long shift = 0; if (cfa->num_args > 0) { TRY(check_str_re_conv(v7, &arg, 1)); TRY(regex_check_prog(arg)); do { if (!re_exec(arg->v.str.prog, arg->fl.fl, cfa->this_obj->v.str.buf + shift, &sub)) { if (NULL == arr) { arr = v7_push_new_object(v7); v7_set_class(arr, V7_CLASS_ARRAY); } shift = sub.sub[0].end - cfa->this_obj->v.str.buf; v7_append(v7, arr, v7_mkv(v7, V7_TYPE_STR, sub.sub[0].start, sub.sub[0].end - sub.sub[0].start, 1)); } } while (arg->fl.fl.re_g && shift < cfa->this_obj->v.str.len); } if (0 == shift) TRY(v7_make_and_push(v7, V7_TYPE_NULL)); return V7_OK; #undef v7 }
// factor = number | string_literal | "(" expression ")" | // variable | "this" | "null" | "true" | "false" | // "{" object_literal "}" | // "[" array_literal "]" | // function_definition | // function_call static enum v7_err parse_factor(struct v7 *v7) { int old_sp = v7_sp(v7); if (*v7->cursor == '(') { TRY(match(v7, '(')); TRY(parse_expression(v7)); TRY(match(v7, ')')); } else if (*v7->cursor == '\'' || *v7->cursor == '"') { TRY(parse_string_literal(v7)); } else if (*v7->cursor == '{') { TRY(parse_object_literal(v7)); } else if (is_alpha(*v7->cursor) || *v7->cursor == '_') { TRY(parse_identifier(v7)); if (test_token(v7, "this", 4)) { inc_stack(v7, 1); v7_top(v7)[-1] = &v7->scopes[v7->current_scope]; } else if (test_token(v7, "null", 4)) { TRY(v7_make_and_push(v7, V7_NULL)); } else if (test_token(v7, "true", 4)) { TRY(v7_make_and_push(v7, V7_BOOL)); v7_top(v7)[-1]->v.num = 1; } else if (test_token(v7, "false", 5)) { TRY(v7_make_and_push(v7, V7_BOOL)); v7_top(v7)[-1]->v.num = 0; } else if (test_token(v7, "function", 8)) { TRY(parse_function_definition(v7, NULL, 0)); } else if (test_token(v7, "delete", 6)) { TRY(parse_delete(v7)); } else { TRY(parse_variable(v7)); } } else { TRY(parse_num(v7)); } if (*v7->cursor == '(') { TRY(parse_function_call(v7)); } // Don't leave anything on stack if no execution flag is set if (v7->no_exec) { inc_stack(v7, old_sp - v7->sp); } return V7_OK; }
static enum v7_err parse_return_statement(struct v7 *v7) { if (*v7->cursor == ';' || *v7->cursor == '}') { if (!v7->no_exec) TRY(v7_make_and_push(v7, V7_UNDEF)); } else { TRY(parse_expression(v7)); } return V7_OK; }
static enum v7_err v7_make_and_push_string(struct v7 *v7, const char *s, unsigned long len, int do_copy) { struct v7_val **v = v7_top(v7); TRY(v7_make_and_push(v7, V7_STR)); v[0]->v.str.len = len; v[0]->v.str.buf = do_copy ? v7_strdup(s, len) : (char *) s; v[0]->flags = do_copy ? 0 : STR_RO; return V7_OK; }
// function_defition = "function" "(" func_params ")" "{" func_body "}" static enum v7_err parse_function_definition(struct v7 *v7, struct v7_val **v, int num_params) { int i = 0, old_no_exec = v7->no_exec, old_sp = v7->sp; const char *src = v7->cursor; // If 'v' (func to call) is NULL, that means we're just parsing function // definition to save it's body. v7->no_exec = v == NULL; TRY(match(v7, '(')); // Initialize new scope if (!v7->no_exec) { v7->current_scope++; CHECK(v7->current_scope < (int) ARRAY_SIZE(v7->scopes), V7_RECURSION_TOO_DEEP); CHECK(v7->scopes[v7->current_scope].v.props == NULL, V7_INTERNAL_ERROR); CHECK(v7->scopes[v7->current_scope].type == V7_OBJ, V7_INTERNAL_ERROR); } while (*v7->cursor != ')') { TRY(parse_identifier(v7)); if (!v7->no_exec) { struct v7_val *key = v7_mkval_str(v7, v7->tok, v7->tok_len); struct v7_val *val = i < num_params ? v[i + 1] : v7_mkval(v7, V7_UNDEF); v7_set(v7, &v7->scopes[v7->current_scope], key, val); } i++; if (!test_and_skip_char(v7, ',')) break; } TRY(match(v7, ')')); TRY(match(v7, '{')); while (*v7->cursor != '}') { int is_return_statement = 0; inc_stack(v7, old_sp - v7->sp); // Clean up the stack from prev stmt TRY(parse_statement(v7, &is_return_statement)); if (is_return_statement) break; // Leave statement value on stack } if (v7->no_exec) { TRY(v7_make_and_push(v7, V7_FUNC)); v7_top(v7)[-1]->v.func = v7_strdup(src, (v7->cursor + 1) - src); } TRY(match(v7, '}')); // Deinitialize scope if (!v7->no_exec) { v7->scopes[v7->current_scope].ref_count = 1; // Force free_val() below free_val(v7, &v7->scopes[v7->current_scope]); v7->current_scope--; assert(v7->current_scope >= 0); } v7->no_exec = old_no_exec; return V7_OK; }
static void call_handler(struct ns_connection *nc, const char *name) { struct v7_val *js_srv = (struct v7_val *) nc->server->server_data; struct v7_val *v, *options = v7_lookup(js_srv, "options"); if ((v = v7_lookup(options, name)) != NULL) { v7_push(s_v7, v); v7_make_and_push(s_v7, V7_OBJ); make_js_conn(v7_top(s_v7)[-1], nc); v7_call(s_v7, 1); } }
// declaration = "var" identifier [ "=" expression ] [ "," { i [ "=" e ] } ] static enum v7_err parse_declaration(struct v7 *v7) { int sp = v7_sp(v7); do { inc_stack(v7, sp - v7_sp(v7)); // Clean up the stack after prev decl TRY(parse_identifier(v7)); if (*v7->cursor == '=') { if (!v7->no_exec) v7_make_and_push(v7, V7_UNDEF); TRY(parse_assignment(v7, &v7->scopes[v7->current_scope])); } } while (test_and_skip_char(v7, ',')); return V7_OK; }
// variable = identifier { '.' identifier | '[' expression ']' } static enum v7_err parse_variable(struct v7 *v7) { struct v7_val **v = NULL, key = str_to_val(v7->tok, v7->tok_len); struct v7_val *ns = find(v7, &key), ro_prop; if (!v7->no_exec) { TRY(v7_make_and_push(v7, V7_UNDEF)); v = v7_top(v7); } while (*v7->cursor == '.' || *v7->cursor == '[') { int ch = *v7->cursor; TRY(match(v7, ch)); CHECK(v7->no_exec || ns != NULL, V7_SYNTAX_ERROR); v7->cur_obj = ns; if (ch == '.') { TRY(parse_identifier(v7)); if (!v7->no_exec) { key = str_to_val(v7->tok, v7->tok_len); ns = get2(ns, &key); if (ns != NULL && ns->type == V7_RO_PROP) { ns->v.prop_func(v7->cur_obj, &ro_prop); ns = &ro_prop; } } } else { TRY(parse_expression(v7)); TRY(match(v7, ']')); if (!v7->no_exec) { ns = get2(ns, v7_top(v7)[-1]); if (ns != NULL && ns->type == V7_RO_PROP) { ns->v.prop_func(v7->cur_obj, &ro_prop); ns = &ro_prop; } TRY(inc_stack(v7, -1)); } } } if (v != NULL && ns != NULL) { free_val(v7, v[-1]); v[-1] = ns; v[-1]->ref_count++; } return V7_OK; }
static enum v7_err parse_object_literal(struct v7 *v7) { TRY(v7_make_and_push(v7, V7_OBJ)); TRY(match(v7, '{')); while (*v7->cursor != '}') { if (*v7->cursor == '\'' || *v7->cursor == '"') { TRY(parse_string_literal(v7)); } else { TRY(parse_identifier(v7)); TRY(v7_make_and_push_string(v7, v7->tok, v7->tok_len, 1)); } TRY(match(v7, ':')); TRY(parse_expression(v7)); if (!v7->no_exec) { struct v7_val **v = v7_top(v7) - 3; CHECK(v[0]->type == V7_OBJ, V7_TYPE_MISMATCH); v7_set(v7, v[0], v[1], v[2]); inc_stack(v7, -2); } test_and_skip_char(v7, ','); } TRY(match(v7, '}')); return V7_OK; }
static enum v7_err do_arithmetic_op(struct v7 *v7, int op) { struct v7_val **v = v7_top(v7) - 2; double a, b, res = 0; if (v7->no_exec) return V7_OK; CHECK(v7->sp >= 2, V7_STACK_UNDERFLOW); CHECK(v[0]->type == V7_NUM && v[1]->type == V7_NUM, V7_TYPE_MISMATCH); a = v[0]->v.num; b = v[1]->v.num; switch (op) { case '+': res = a + b; break; case '-': res = a - b; break; case '*': res = a * b; break; case '/': res = a / b; break; } TRY(inc_stack(v7, -2)); TRY(v7_make_and_push(v7, V7_NUM)); v7_top(v7)[-1]->v.num = res; return V7_OK; }
static enum v7_err parse_string_literal(struct v7 *v7) { char buf[MAX_STRING_LITERAL_LENGTH]; const char *begin = v7->cursor++; struct v7_val *v; size_t i = 0; TRY(v7_make_and_push(v7, V7_STR)); v = v7_top(v7)[-1]; // Scan string literal into the buffer, handle escape sequences while (*v7->cursor != *begin && *v7->cursor != '\0') { switch (*v7->cursor) { case '\\': v7->cursor++; switch (*v7->cursor) { case 'n': buf[i++] = '\n'; break; case 't': buf[i++] = '\t'; break; case '\\': buf[i++] = '\\'; break; default: if (*v7->cursor == *begin) buf[i++] = *begin; break; } break; default: buf[i++] = *v7->cursor; break; } if (i >= sizeof(buf) - 1) i = sizeof(buf) - 1; v7->cursor++; } v->v.str.len = v7->no_exec ? 0 : i; v->v.str.buf = v7->no_exec ? NULL : v7_strdup(buf, v->v.str.len); TRY(match(v7, *begin)); skip_whitespaces_and_comments(v7); return V7_OK; }