enum v7_err v7_call(struct v7 *v7, int num_args) { struct v7_val **top = v7_top(v7), **v = top - (num_args + 1), *f = v[0]; if (v7->no_exec) return V7_OK; CHECK(v7->sp > num_args, V7_INTERNAL_ERROR); CHECK(f->type == V7_FUNC || f->type == V7_C_FUNC, V7_CALLED_NON_FUNCTION); // Return value will substitute function objest on a stack v[0] = v7_mkval(v7, V7_UNDEF); // Set return value to 'undefined' v[0]->ref_count++; if (f->type == V7_FUNC) { const char *src = v7->cursor; v7->cursor = f->v.func; // Move control flow to function body TRY(parse_function_definition(v7, v, num_args)); // Execute function body v7->cursor = src; // Return control flow if (v7_top(v7) > top) { // If function body pushed some value on stack, free_val(v7, v[0]); v[0] = top[0]; // use that value as return value v[0]->ref_count++; } } else if (f->type == V7_C_FUNC) { f->v.c_func(v7, v7->cur_obj, v[0], v + 1, num_args); } free_val(v7, f); TRY(inc_stack(v7, - (int) (v7_top(v7) - (v + 1)))); // Clean up stack return V7_OK; }
V7_PRIVATE enum v7_err do_exec(struct v7 *v7, const char *file_name, const char *source_code, int sp) { int has_ret = 0; struct v7_pstate old_pstate = v7->pstate; enum v7_err err = V7_OK; v7->pstate.source_code = v7->pstate.pc = source_code; v7->pstate.file_name = file_name; v7->pstate.line_no = 1; // Prior calls to v7_exec() may have left current_scope modified, reset now // TODO(lsm): free scope chain v7->this_obj = &v7->root_scope; next_tok(v7); while ((err == V7_OK) && (v7->cur_tok != TOK_END_OF_INPUT)) { // Reset stack on each statement if ((err = inc_stack(v7, sp - v7->sp)) == V7_OK) { err = parse_statement(v7, &has_ret); } } //printf("%s: [%s] %d %d\n", __func__, file_name, v7->pstate.line_no, err); assert(v7->root_scope.proto == &s_global); v7->pstate = old_pstate; return err; }
// 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; }
// code = { statement } enum v7_err v7_exec(struct v7 *v7, const char *source_code) { v7->source_code = v7->cursor = source_code; skip_whitespaces_and_comments(v7); // The following code may raise an exception and jump to the previous line, // returning non-zero from the setjmp() call // Prior calls to v7_exec() may have left current_scope modified, reset now v7->current_scope = 0; // XXX free up higher scopes? inc_stack(v7, -v7->sp); while (*v7->cursor != '\0') { inc_stack(v7, -v7->sp); // Reset stack on each statement TRY(parse_statement(v7, 0)); // Leave the result of last expr on stack } 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; }
void v7_destroy(struct v7 **v7) { size_t i; if (v7 == NULL || v7[0] == NULL) return; assert(v7[0]->sp >= 0); inc_stack(v7[0], -v7[0]->sp); for (i = 0; i < ARRAY_SIZE(v7[0]->scopes); i++) { v7[0]->scopes[i].ref_count = 1; // Force free_val() below to free memory free_val(v7[0], &v7[0]->scopes[i]); } free(v7[0]); v7[0] = NULL; }
V7_PRIVATE enum v7_err check_str_re_conv(struct v7 *v7, struct v7_val **arg, int re_fl) { /* If argument is not (RegExp + re_fl) or string, do type conversion */ if (!is_string(*arg) && !(re_fl && instanceof(*arg, &s_constructors[V7_CLASS_REGEXP]))) { TRY(toString(v7, *arg)); *arg = v7_top_val(v7); INC_REF_COUNT(*arg); TRY(inc_stack(v7, -2)); TRY(v7_push(v7, *arg)); } return V7_OK; }
// 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; }
static enum v7_err parse_compound_statement(struct v7 *v7) { if (*v7->cursor == '{') { int old_sp = v7->sp; match(v7, '{'); while (*v7->cursor != '}') { if (v7->sp > old_sp) inc_stack(v7, old_sp - v7->sp); TRY(parse_statement(v7, NULL)); } match(v7, '}'); } else { TRY(parse_statement(v7, NULL)); } 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 do_logical_op(struct v7 *v7, int op) { struct v7_val **v = v7_top(v7) - 2; if (v7->no_exec) return V7_OK; CHECK(v[0]->type == V7_NUM && v[1]->type == V7_NUM, V7_TYPE_MISMATCH); switch (op) { case OP_GT: v[0]->v.num = v[0]->v.num > v[1]->v.num ? 1.0 : 0.0; break; case OP_GE: v[0]->v.num = v[0]->v.num >= v[1]->v.num ? 1.0 : 0.0; break; case OP_LT: v[0]->v.num = v[0]->v.num < v[1]->v.num ? 1.0 : 0.0; break; case OP_LE: v[0]->v.num = v[0]->v.num <= v[1]->v.num ? 1.0 : 0.0; break; case OP_EQ: v[0]->v.num = v[0]->v.num == v[1]->v.num ? 1.0 : 0.0; break; } TRY(inc_stack(v7, -1)); return V7_OK; }
static enum v7_err parse_assignment(struct v7 *v7, struct v7_val *obj) { const char *tok = v7->tok; unsigned long tok_len = v7->tok_len; struct v7_val **top = v7_top(v7); TRY(match(v7, '=')); TRY(parse_expression(v7)); if (!v7->no_exec) { TRY(v7_make_and_push_string(v7, tok, tok_len, 1)); v7_set(v7, obj, top[1], top[0]); CHECK(v7_top(v7) - top > 1, V7_INTERNAL_ERROR); CHECK(v7->sp > 1, V7_STACK_UNDERFLOW); free_val(v7, top[-1]); top[-1] = top[0]; top[-1]->ref_count++; inc_stack(v7, (int) (top - v7_top(v7))); } 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; }
V7_PRIVATE enum v7_err Str_replace(struct v7_c_func_arg *cfa) { #define v7 (cfa->v7) /* Needed for TRY() macro below */ struct v7_val *result = v7_push_new_object(v7); const char *out_str = cfa->this_obj->v.str.buf; uint8_t own = 1; size_t out_len = cfa->this_obj->v.str.len; int old_sp = v7->sp; if (cfa->num_args > 1) { const char *const str_end = cfa->this_obj->v.str.buf + cfa->this_obj->v.str.len; char *p = cfa->this_obj->v.str.buf; uint32_t out_sub_num = 0; struct v7_val *re = cfa->args[0], *str_func = cfa->args[1], *arr = NULL; struct re_tok out_sub[V7_RE_MAX_REPL_SUB], *ptok = out_sub; struct Resub loot; TRY(check_str_re_conv(v7, &re, 1)); TRY(regex_check_prog(re)); if (v7_is_class(str_func, V7_CLASS_FUNCTION)) { arr = v7_push_new_object(v7); v7_set_class(arr, V7_CLASS_ARRAY); TRY(v7_push(v7, str_func)); } else TRY(check_str_re_conv(v7, &str_func, 0)); out_len = 0; do { int i; if (re_exec(re->v.str.prog, re->fl.fl, p, &loot)) break; if (p != loot.sub->start) { ptok->start = p; ptok->end = loot.sub->start; ptok++; out_len += loot.sub->start - p; out_sub_num++; } if (NULL != arr) { /* replace function */ Rune rune; int old_sp = v7->sp, utf_shift = 0; struct v7_val *rez_str; for (i = 0; i < loot.subexpr_num; i++) v7_push_string(v7, loot.sub[i].start, loot.sub[i].end - loot.sub[i].start, 1); for (i = 0; p + i < loot.sub[0].start; i += chartorune(&rune, p + i), utf_shift++) ; TRY(push_number(v7, utf_shift)); TRY(v7_push(v7, cfa->this_obj)); rez_str = v7_call(v7, cfa->this_obj, loot.subexpr_num + 2); TRY(check_str_re_conv(v7, &rez_str, 0)); if (rez_str->v.str.len) { ptok->start = rez_str->v.str.buf; ptok->end = rez_str->v.str.buf + rez_str->v.str.len; ptok++; out_len += rez_str->v.str.len; out_sub_num++; v7_append(v7, arr, rez_str); } TRY(inc_stack(v7, old_sp - v7->sp)); } else { /* replace string */ struct Resub newsub; re_rplc(&loot, p, str_func->v.str.buf, &newsub); for (i = 0; i < newsub.subexpr_num; i++) { ptok->start = newsub.sub[i].start; ptok->end = newsub.sub[i].end; ptok++; out_len += newsub.sub[i].end - newsub.sub[i].start; out_sub_num++; } } p = (char *)loot.sub->end; } while (re->fl.fl.re_g && p < str_end); if (p < str_end) { ptok->start = p; ptok->end = str_end; ptok++; out_len += str_end - p; out_sub_num++; } out_str = malloc(out_len + 1); CHECK(out_str, V7_OUT_OF_MEMORY); ptok = out_sub; p = (char *)out_str; do { size_t ln = ptok->end - ptok->start; memcpy(p, ptok->start, ln); p += ln; ptok++; } while (--out_sub_num); *p = '\0'; own = 0; } TRY(inc_stack(v7, old_sp - v7->sp)); v7_init_str(result, out_str, out_len, own); result->fl.fl.str_alloc = 1; return V7_OK; #undef v7 }
V7_PRIVATE enum v7_err v7_push(struct v7 *v7, struct v7_val *v) { inc_ref_count(v); TRY(inc_stack(v7, 1)); v7->stack[v7->sp - 1] = v; return V7_OK; }
enum v7_err v7_push(struct v7 *v7, struct v7_val *v) { v->ref_count++; TRY(inc_stack(v7, 1)); v7->stack[v7->sp - 1] = v; return V7_OK; }