From: cassowarii <2374677+cassowarii@users.noreply.github.com> Date: Thu, 23 Jul 2026 01:17:50 +0000 (-0700) Subject: an object field with '.' should return a reference X-Git-Url: https://www.git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=a42f601bbaf679c2f8a61ede932e8da2028ed1bf;p=sarabande.git an object field with '.' should return a reference --- diff --git a/src/compile/emit.c b/src/compile/emit.c index 2c61541..bfe0728 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -210,7 +210,7 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { * then we assign through the reference we receive, i.e. * a[b] = c * really means - * *(a{op::refindex}(b)) = c + * *((&a)->.[op::index]->(b)) = c * The reason for this has to do with assigning through nested data structures. * Doing it this way just makes the whole semantics feel more natural. It's similar * to returning a reference from indexing in C++, I guess. */ @@ -218,6 +218,15 @@ void compile_stmt(sbVmCompiler *cm, sbIrStmt *stmt) { compile_expr(cm, stmt->assign.where->op.right); /* index we are using */ EMIT(BC_OP_INDEXLREF); EMIT(BC_REF_PUT); + } else if (stmt->assign.where->type == IR_E_DOT) { + /* same as op::index, essentially. the dot returns a reference to the inside of the + * thing, then we can assign to that reference */ + compile_expr(cm, stmt->assign.where->op.right); + EMIT(BC_LD_IMM); + EARG(1); + compile_ref(cm, stmt->assign.where->op.left, TRUE); + EMIT(BC_CALL_IND); + EMIT(BC_REF_PUT); } else { PANIC("This type of assignment operation is not supported!"); } @@ -363,6 +372,8 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { switch(expr->type) { case IR_E_OP: if (expr->op.type == AST_OP_REF) { + /* in expr context, the & operator generally prevents 'collapsing' + * of references to values when applied to a[b] or a.b or a::b */ compile_ref(cm, expr->op.left, FALSE); } else { compile_expr(cm, expr->op.left); @@ -375,13 +386,30 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { case IR_E_CALL: /* calling convention: store argument count on stack */ compile_list(cm, expr->call.param); - compile_expr(cm, expr->call.func); - EMIT(BC_CALL); + if (expr->call.func->type == IR_E_DOT + || (expr->call.func->type == IR_E_OP && expr->call.func->op.type == AST_OP_INDEX)) { + /* for expressions with reference type, call 'through' the reference */ + compile_ref(cm, expr->call.func, TRUE); + EMIT(BC_CALL_IND); + } else if (expr->call.func->type == IR_E_OP && expr->call.func->op.type == AST_OP_DEREF) { + /* for something like (*a)(...), call 'through' the reference as well + * (that is, evaluate the thing inside the * and call indirectly, instead + * of dereferencing it first) */ + compile_expr(cm, expr->call.func->op.left); + EMIT(BC_CALL_IND); + } else { + compile_expr(cm, expr->call.func); + EMIT(BC_CALL); + } break; - case IR_E_SEND: - compile_list(cm, expr->send.message); - compile_expr(cm, expr->send.target); - EMIT(BC_SEND); + case IR_E_DOT: + compile_expr(cm, expr->dot.param); + /* TODO dot should always take 1 parameter, so this is not needed */ + EMIT(BC_LD_IMM); + EARG(1); + compile_expr(cm, expr->dot.target); + EMIT(BC_DOT); + EMIT(BC_OP_DEREF); break; case IR_E_CONTEXT: EMIT(BC_LD_CTX); @@ -478,6 +506,12 @@ void compile_ref(sbVmCompiler *cm, sbIrExpr *expr, flag is_lref) { /* &a[0] / &a::b */ EMIT(BC_OP_INDEXRREF); } + } else if (expr->type == IR_E_DOT) { + compile_expr(cm, expr->dot.param); + EMIT(BC_LD_IMM); + EARG(1); + compile_ref(cm, expr->dot.target, is_lref); + EMIT(BC_DOT); } else { PANIC("cannot & non-variable-name! (todo)"); } diff --git a/src/compile/ir.c b/src/compile/ir.c index 8f5fc27..d7f4813 100644 --- a/src/compile/ir.c +++ b/src/compile/ir.c @@ -361,11 +361,11 @@ static sbIrExpr *expr_call(hIrChunk ck, sbIrExpr *to_call, sbIrExpr *param) { }); } -static sbIrExpr *expr_send(hIrChunk ck, sbIrExpr *target, sbIrExpr *message) { +static sbIrExpr *expr_dot(hIrChunk ck, sbIrExpr *target, sbIrExpr *param) { return new_expr(ck, &(sbIrExpr) { - .type = IR_E_SEND, - .send.target = target, - .send.message = message, + .type = IR_E_DOT, + .dot.target = target, + .dot.param = param, }); } @@ -998,22 +998,13 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node, flag list_context) { if (node == NO_NODE) return NULL; if (node->type == AST_NODE_FUNCCALL) { - if (node->seq.left->type == AST_NODE_DOT) { - /* For a.b(c,d,e), we can optimize a bit to avoid allocating closures - * for built-in types that get methods called on them */ - sbIrExpr *called = compile_ast_expr(ck, node->seq.left->seq.left, FALSE); - sbIrExpr *method = expr_list(ck, compile_ast_expr(ck, node->seq.left->seq.right, FALSE)); - sbIrExpr *params = compile_ast_list_after(ck, node->seq.right, method); - return expr_send(ck, called, params); - } else { - sbIrExpr *called = compile_ast_expr(ck, node->seq.left, FALSE); - sbIrExpr *params = compile_ast_list(ck, node->seq.right); - return expr_call(ck, called, params); - } + sbIrExpr *called = compile_ast_expr(ck, node->seq.left, FALSE); + sbIrExpr *params = compile_ast_list(ck, node->seq.right); + return expr_call(ck, called, params); } else if (node->type == AST_NODE_DOT) { sbIrExpr *target = compile_ast_expr(ck, node->seq.left, FALSE); - sbIrExpr *param = expr_list(ck, compile_ast_expr(ck, node->seq.right, FALSE)); - return expr_call(ck, target, param); + sbIrExpr *param = compile_ast_expr(ck, node->seq.right, FALSE); + return expr_dot(ck, target, param); } else if (node->type == AST_VAL_FUNC) { sbIrChunk *func = compile_ast_function(ck->program, node->seq.left, node->seq.right); return expr_func(ck, func); diff --git a/src/compile/ir.h b/src/compile/ir.h index bbba211..2f3bbbe 100644 --- a/src/compile/ir.h +++ b/src/compile/ir.h @@ -47,7 +47,7 @@ typedef enum sbIrExprType { IR_E_CONTEXT, IR_E_FUNC, IR_E_CALL, - IR_E_SEND, + IR_E_DOT, IR_E_LIST, IR_E_HASH, } sbIrExprType; @@ -107,8 +107,8 @@ typedef struct sbIrExpr { } list; struct { struct sbIrExpr *target; - struct sbIrExpr *message; - } send; + struct sbIrExpr *param; + } dot; }; } sbIrExpr; @@ -177,4 +177,6 @@ void sbIrProgram_compile_ast(hIrProgram ir, sbAst ast); void sbIrProgram_print(hIrProgram ir); +void sbIr_print_expr(sbIrExpr *e); + #endif diff --git a/src/compile/print_ir.c b/src/compile/print_ir.c index 9f3ef8b..070b0c1 100644 --- a/src/compile/print_ir.c +++ b/src/compile/print_ir.c @@ -3,6 +3,7 @@ #include "data/symbol.h" static void print_stmt(sbIrStmt *s); +static void print_expr(sbIrExpr *e); void sbIrProgram_print(hIrProgram ir) { usize nchunks = ir->chunks.size / sizeof(sbIrChunk*); @@ -25,6 +26,10 @@ void sbIrProgram_print(hIrProgram ir) { } } +void sbIr_print_expr(sbIrExpr *e) { + print_expr(e); +} + /* --- */ static void print_var(sbIrVariable *v) { @@ -85,12 +90,12 @@ static void print_expr(sbIrExpr *e) { } debug(")"); break; - case IR_E_SEND: - debug("SEND: "); - print_expr(e->send.target); - debug(" <~( "); - print_expr(e->send.message); - debug(" )"); + case IR_E_DOT: + debug("("); + print_expr(e->dot.target); + debug(" . "); + print_expr(e->dot.param); + debug(")"); break; case IR_E_LIST: if (e->list.next) { diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index 364aa9d..c246edd 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -24,6 +24,8 @@ typedef enum sbOpcode { BC_NPOP, // pop N values from stack given by top number BC_SWAP, // swap top two values on stack BC_CALL, // push return address and jump to new block + BC_CALL_IND, // 'a->(...)' indirect call that can return a value + BC_DOT, // 'a.b' indirect call that should return a reference BC_RET, // return to calling block BC_NUMARG, // check number of function arguments = this BC_MINARG, // check number of function arguments > this diff --git a/src/vm/exec.c b/src/vm/exec.c index abdeafc..f68f119 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -461,6 +461,15 @@ void execute_instruction(hVm vm) { v = pop_stack(vm); sbVm_call_func(vm, v); break; + case BC_DOT: + case BC_CALL_IND: + v = pop_stack(vm); + if (v->type != IT_REF) { + PANIC("object illegally returned non-reference value"); + } + vars = sbVar_deref(v); + sbVm_call_func(vm, sbVar_get_value_ptr(vars)); + break; case BC_SEND: sbLib_resolve_method(vm); break; @@ -598,6 +607,12 @@ void execute_instruction(hVm vm) { case BC_OP_INDEXVAL: sbV_index_value(vm); break; + case BC_OP_INDEXLREF: + sbV_index_lref(vm); + break; + case BC_OP_INDEXRREF: + sbV_index_rref(vm); + break; case BC_OP_RANGEINDEX: v = peek_stack(vm, 2); w = peek_stack(vm, 1); diff --git a/src/vm/operations.c b/src/vm/operations.c index fddb958..fcd4e33 100644 --- a/src/vm/operations.c +++ b/src/vm/operations.c @@ -148,6 +148,62 @@ void sbV_index_value(hVm vm) { } } +void sbV_index_lref(hVm vm) { + hVal *a = sbVm_peek(vm, 1); + hVal *b = sbVm_peek(vm, 0); + if (a->type != IT_REF) { + PANIC("Object illegally returned non-reference!"); + } + a = sbVar_get_value_ptr(sbVar_deref(a)); + + if (a->type == IT_LIST && b->type == IT_INTEGER) { + sbVm_npop(vm, 2); + hVal result = sbList_index_lvalue_ref(a->list, b->integer); + sbVm_push(vm, &result); + } else if (a->type == IT_HASH) { + sbVm_npop(vm, 2); + hVal result = sbHash_find_lvalue_ref(a->hash, b); + sbVm_push(vm, &result); + } else if (a->type == IT_MODULE && b->type == IT_SYMBOL) { + PANIC("TODO"); + } else { + sbVm_swap(vm); /* b a */ + sbVm_push_immediate(vm, &HVSYM(S_OP_INDEX)); /* b a op::index */ + sbVm_swap(vm); /* b op::index a */ + sbVm_push_immediate(vm, &HVINT(2)); /* b op::index a 2 */ + sbVm_swap(vm); /* b op::index 2 a */ + sbLib_resolve_method(vm); + } +} + +void sbV_index_rref(hVm vm) { + hVal *a = sbVm_peek(vm, 1); + hVal *b = sbVm_peek(vm, 0); + if (a->type != IT_REF) { + PANIC("Object illegally returned non-reference!"); + } + a = sbVar_get_value_ptr(sbVar_deref(a)); + + if (a->type == IT_LIST && b->type == IT_INTEGER) { + sbVm_npop(vm, 2); + hVal result = sbList_index_rvalue_ref(a->list, b->integer); + sbVm_push(vm, &result); + } else if (a->type == IT_HASH) { + sbVm_npop(vm, 2); + hVal result = sbHash_find_rvalue_ref(a->hash, b); + sbVm_push(vm, &result); + } else if (a->type == IT_MODULE && b->type == IT_SYMBOL) { + PANIC("TODO"); + } else { + sbVm_swap(vm); /* b a */ + sbVm_push_immediate(vm, &HVSYM(S_OP_INDEX)); /* b a op::index */ + sbVm_swap(vm); /* b op::index a */ + sbVm_push_immediate(vm, &HVINT(2)); /* b op::index a 2 */ + sbVm_swap(vm); /* b op::index 2 a */ + sbLib_resolve_method(vm); + } +} + hVal sbV_rangeindex(hVal *a, hVal *b, hVal *c) { if (b->type == IT_INTEGER && c->type == IT_INTEGER) { usize length; diff --git a/src/vm/operations.h b/src/vm/operations.h index ddccd00..12270e9 100644 --- a/src/vm/operations.h +++ b/src/vm/operations.h @@ -26,6 +26,10 @@ hVal sbV_append(hVal *a, hVal *b); void sbV_index_value(hVm vm); +void sbV_index_lref(hVm vm); + +void sbV_index_rref(hVm vm); + hVal sbV_rangeindex(hVal *a, hVal *b, hVal *c); void sbV_index_set(hVal *obj, hVal *key, hVal *value);