/** Evaluate an expression in a given frame */ value_t eval_expr( heapptr_t expr, clos_t* clos, value_t* locals ) { //printf("eval_expr\n"); // Get the shape of the AST node // Note: AST nodes must match the shapes defined in init_parser, // otherwise this interpreter can't handle it shapeidx_t shape = get_shape(expr); // Variable or constant declaration (let/var) if (shape == SHAPE_AST_DECL) { ast_decl_t* decl = (ast_decl_t*)expr; // Let declarations should be initialized assert (decl->cst == false); return VAL_FALSE; } // Variable reference (read) if (shape == SHAPE_AST_REF) { ast_ref_t* ref = (ast_ref_t*)expr; assert (ref->decl != NULL); //printf("evaluating ref to %s\n", string_cstr(ref->name)); // If this is a variable from an outer function if (ref->decl->fun != clos->fun) { //printf("ref from outer fun\n"); assert (ref->idx < clos->fun->free_vars->len); cell_t* cell = clos->cells[ref->idx]; assert (cell != NULL); value_t value; value.word = cell->word; value.tag = cell->tag; return value; } // Check that the ref index is valid if (ref->idx > clos->fun->local_decls->len) { printf("invalid variable reference\n"); printf("ref->name=%s\n", string_cstr(ref->name)); printf("ref->idx=%d\n", ref->idx); printf("local_decls->len=%d\n", clos->fun->local_decls->len); exit(-1); } // If this an escaping variable (captured by a closure) if (ref->decl->esc) { // Free variables are stored in mutable cells // Pointers to the cells are found on the closure object cell_t* cell = locals[ref->idx].word.cell; value_t value; value.word = cell->word; value.tag = cell->tag; //printf("read value from cell\n"); return value; } /* printf("reading normal local\n"); string_print(ref->name); printf("\n"); */ // Read directly from the stack frame return locals[ref->idx]; } if (shape == SHAPE_AST_CONST) { //printf("constant\n"); ast_const_t* cst = (ast_const_t*)expr; return cst->val; } if (shape == SHAPE_STRING) { return value_from_heapptr(expr, TAG_STRING); } // Array literal expression if (shape == SHAPE_ARRAY) { array_t* array_expr = (array_t*)expr; // Array of values to be produced array_t* val_array = array_alloc(array_expr->len); for (size_t i = 0; i < array_expr->len; ++i) { heapptr_t expr = array_get(array_expr, i).word.heapptr; value_t value = eval_expr(expr, clos, locals); array_set(val_array, i, value); } return value_from_heapptr((heapptr_t)val_array, TAG_ARRAY); } // Object literal expression if (shape == SHAPE_AST_OBJ) { //printf("obj literal expr\n"); ast_obj_t* obj_expr = (ast_obj_t*)expr; object_t* obj = object_alloc(OBJ_MIN_CAP); // TODO: set prototype // Do this in object_alloc? for (size_t i = 0; i < obj_expr->name_strs->len; ++i) { string_t* prop_name = array_get(obj_expr->name_strs, i).word.string; heapptr_t val_expr = array_get(obj_expr->val_exprs, i).word.heapptr; value_t value = eval_expr(val_expr, clos, locals); object_set_prop( obj, prop_name, value, ATTR_DEFAULT ); } return value_from_obj((heapptr_t)obj); } // Binary operator (e.g. a + b) if (shape == SHAPE_AST_BINOP) { //printf("binop\n"); ast_binop_t* binop = (ast_binop_t*)expr; // Assignment if (binop->op == &OP_ASSIGN) { value_t val = eval_expr(binop->right_expr, clos, locals); return eval_assign( binop->left_expr, val, clos, locals ); } value_t v0 = eval_expr(binop->left_expr, clos, locals); value_t v1 = eval_expr(binop->right_expr, clos, locals); int64_t i0 = v0.word.int64; int64_t i1 = v1.word.int64; string_t* s0 = v0.word.string; string_t* s1 = v1.word.string; if (binop->op == &OP_MEMBER) return eval_get_prop(v0, v1); if (binop->op == &OP_INDEX) return eval_get_index(v0, v1); if (binop->op == &OP_ADD) return value_from_int64(i0 + i1); if (binop->op == &OP_SUB) return value_from_int64(i0 - i1); if (binop->op == &OP_MUL) return value_from_int64(i0 * i1); if (binop->op == &OP_DIV) return value_from_int64(i0 / i1); if (binop->op == &OP_MOD) return value_from_int64(i0 % i1); if (binop->op == &OP_LT) return (i0 < i1)? VAL_TRUE:VAL_FALSE; if (binop->op == &OP_LE) { if (v0.tag == TAG_STRING && v1.tag == TAG_STRING) return (strcmp(string_cstr(s0), string_cstr(s1)) <= 0)? VAL_TRUE:VAL_FALSE; if (v0.tag == TAG_INT64 && v1.tag == TAG_INT64) return (i0 <= i1)? VAL_TRUE:VAL_FALSE; assert (false); } if (binop->op == &OP_GT) return (i0 > i1)? VAL_TRUE:VAL_FALSE; if (binop->op == &OP_GE) { if (v0.tag == TAG_STRING && v1.tag == TAG_STRING) return (strcmp(string_cstr(s0), string_cstr(s1)) >= 0)? VAL_TRUE:VAL_FALSE; if (v0.tag == TAG_INT64 && v1.tag == TAG_INT64) return (i0 >= i1)? VAL_TRUE:VAL_FALSE; assert (false); } if (binop->op == &OP_EQ) return value_equals(v0, v1)? VAL_TRUE:VAL_FALSE; if (binop->op == &OP_NE) return value_equals(v0, v1)? VAL_FALSE:VAL_TRUE; printf("unimplemented binary operator: %s\n", binop->op->str); return VAL_FALSE; } // Unary operator (e.g.: -x, not a) if (shape == SHAPE_AST_UNOP) { ast_unop_t* unop = (ast_unop_t*)expr; value_t v0 = eval_expr(unop->expr, clos, locals); if (unop->op == &OP_NEG) return value_from_int64(-v0.word.int64); if (unop->op == &OP_NOT) return eval_truth(v0)? VAL_FALSE:VAL_TRUE; printf("unimplemented unary operator: %s\n", unop->op->str); return VAL_FALSE; } // Sequence/block expression if (shape == SHAPE_AST_SEQ) { ast_seq_t* seqexpr = (ast_seq_t*)expr; array_t* expr_list = seqexpr->expr_list; value_t value = VAL_TRUE; for (size_t i = 0; i < expr_list->len; ++i) { heapptr_t expr = array_get(expr_list, i).word.heapptr; value = eval_expr(expr, clos, locals); } // Return the value of the last expression return value; } // If expression if (shape == SHAPE_AST_IF) { ast_if_t* ifexpr = (ast_if_t*)expr; value_t t = eval_expr(ifexpr->test_expr, clos, locals); if (eval_truth(t)) return eval_expr(ifexpr->then_expr, clos, locals); else return eval_expr(ifexpr->else_expr, clos, locals); } // Function/closure expression if (shape == SHAPE_AST_FUN) { //printf("creating closure\n"); ast_fun_t* nested = (ast_fun_t*)expr; // Allocate a closure of the nested function clos_t* new_clos = clos_alloc(nested); // For each free (closure) variable of the nested function for (size_t i = 0; i < nested->free_vars->len; ++i) { ast_decl_t* decl = array_get(nested->free_vars, i).word.decl; // If the variable is from this function if (decl->fun == clos->fun) { new_clos->cells[i] = locals[decl->idx].word.cell; } else { uint32_t free_idx = array_indexof_ptr(clos->fun->free_vars, (heapptr_t)decl); assert (free_idx < clos->fun->free_vars->len); new_clos->cells[i] = clos->cells[free_idx]; } } assert (new_clos->fun == nested); return value_from_heapptr((heapptr_t)new_clos, TAG_CLOS); } // Call expression if (shape == SHAPE_AST_CALL) { //printf("evaluating call\n"); ast_call_t* callexpr = (ast_call_t*)expr; // Evaluate the closure expression value_t callee_clos = eval_expr(callexpr->fun_expr, clos, locals); if (callee_clos.tag == TAG_CLOS) { return eval_call( callee_clos.word.clos, callexpr->arg_exprs, clos, locals ); } if (callee_clos.tag == TAG_HOSTFN) { return eval_host_call( callee_clos.word.hostfn, callexpr->arg_exprs, clos, locals ); } printf("invalid callee in function call\n"); exit(-1); } printf("eval error, unknown expression type, shapeidx=%d\n", get_shape(expr)); exit(-1); }
bool equals(const Value& a, const Value& b) { static const Symbol equal_sign_symbol("="); return eval_truth(snow::call(a, get(a, equal_sign_symbol), b).value()); }