* 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. */
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!");
}
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);
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);
/* &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)");
}
});
}
-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,
});
}
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);
IR_E_CONTEXT,
IR_E_FUNC,
IR_E_CALL,
- IR_E_SEND,
+ IR_E_DOT,
IR_E_LIST,
IR_E_HASH,
} sbIrExprType;
} list;
struct {
struct sbIrExpr *target;
- struct sbIrExpr *message;
- } send;
+ struct sbIrExpr *param;
+ } dot;
};
} sbIrExpr;
void sbIrProgram_print(hIrProgram ir);
+void sbIr_print_expr(sbIrExpr *e);
+
#endif
#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*);
}
}
+void sbIr_print_expr(sbIrExpr *e) {
+ print_expr(e);
+}
+
/* --- */
static void print_var(sbIrVariable *v) {
}
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) {
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
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;
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);
}
}
+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;
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);