@@ -13,4 +13,5 @@ X("*", l2_builtin_mul) | |||
X("/", l2_builtin_div) | |||
X("print", l2_builtin_print) | |||
X("len", l2_builtin_len) | |||
X("if", l2_builtin_if) | |||
#endif |
@@ -9,13 +9,8 @@ | |||
struct l2_vm; | |||
struct l2_vm_array; | |||
struct l2_vm_args; | |||
typedef l2_word (*l2_vm_cfunction)(struct l2_vm *vm, l2_word argc, l2_word *argv); | |||
struct l2_vm_args { | |||
l2_word argc; | |||
}; | |||
enum l2_value_type { | |||
L2_VAL_TYPE_NONE, | |||
L2_VAL_TYPE_ATOM, | |||
@@ -25,6 +20,7 @@ enum l2_value_type { | |||
L2_VAL_TYPE_NAMESPACE, | |||
L2_VAL_TYPE_FUNCTION, | |||
L2_VAL_TYPE_CFUNCTION, | |||
L2_VAL_TYPE_CONTINUATION, | |||
L2_VAL_TYPE_ERROR, | |||
}; | |||
@@ -60,6 +56,10 @@ struct l2_vm_value { | |||
l2_word ns; | |||
} func; | |||
l2_vm_cfunction cfunc; | |||
struct { | |||
l2_word call; | |||
l2_word arg; | |||
} cont; | |||
char *error; | |||
}; | |||
}; |
@@ -57,6 +57,10 @@ static void print_val(struct l2_vm *vm, struct l2_io_writer *out, struct l2_vm_v | |||
case L2_VAL_TYPE_ERROR: | |||
l2_io_printf(out, "(error: %s)", val->error); | |||
break; | |||
case L2_VAL_TYPE_CONTINUATION: | |||
l2_io_printf(out, "(continuation)"); | |||
break; | |||
} | |||
} | |||
@@ -175,8 +179,8 @@ l2_word l2_builtin_print(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
} | |||
l2_word l2_builtin_len(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
if (argc < 1) { | |||
return l2_vm_error(vm, "Expected at least 1 argument"); | |||
if (argc != 1) { | |||
return l2_vm_error(vm, "Expected 1 argument"); | |||
} | |||
l2_word ret_id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
@@ -191,6 +195,7 @@ l2_word l2_builtin_len(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
case L2_VAL_TYPE_FUNCTION: | |||
case L2_VAL_TYPE_CFUNCTION: | |||
case L2_VAL_TYPE_ERROR: | |||
case L2_VAL_TYPE_CONTINUATION: | |||
break; | |||
case L2_VAL_TYPE_BUFFER: | |||
@@ -213,3 +218,29 @@ l2_word l2_builtin_len(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
return ret_id; | |||
} | |||
l2_word l2_builtin_if(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
if (argc != 2 && argc != 3) { | |||
return l2_vm_error(vm, "Expected 2 or 3 arguments"); | |||
} | |||
struct l2_vm_value *cond = &vm->values[argv[0]]; | |||
if ( | |||
l2_vm_value_type(cond) == L2_VAL_TYPE_ATOM && | |||
cond->atom == vm->values[vm->ktrue].atom) { | |||
l2_word ret_id = l2_vm_alloc(vm, L2_VAL_TYPE_CONTINUATION, 0); | |||
struct l2_vm_value *ret = &vm->values[ret_id]; | |||
ret->cont.call = argv[1]; | |||
ret->cont.arg = 0; | |||
return ret_id; | |||
} else if (argc == 3) { | |||
l2_word ret_id = l2_vm_alloc(vm, L2_VAL_TYPE_CONTINUATION, 0); | |||
struct l2_vm_value *ret = &vm->values[ret_id]; | |||
ret->cont.call = argv[2]; | |||
ret->cont.arg = 0; | |||
return ret_id; | |||
} else { | |||
return 0; | |||
} | |||
} |
@@ -76,6 +76,10 @@ void l2_vm_print_val(struct l2_vm_value *val) { | |||
case L2_VAL_TYPE_ERROR: | |||
printf("ERROR, %s\n", val->error); | |||
break; | |||
case L2_VAL_TYPE_CONTINUATION: | |||
printf("CONTINUATION, call %u, arg %u\n", val->cont.call, val->cont.arg); | |||
break; | |||
} | |||
} | |||
@@ -137,6 +137,7 @@ const char *l2_value_type_name(enum l2_value_type typ) { | |||
case L2_VAL_TYPE_FUNCTION: return "FUNCTION"; | |||
case L2_VAL_TYPE_CFUNCTION: return "CFUNCTION"; | |||
case L2_VAL_TYPE_ERROR: return "ERROR"; | |||
case L2_VAL_TYPE_CONTINUATION: return "CONTINUATION"; | |||
} | |||
return "(unknown)"; | |||
@@ -297,7 +298,35 @@ static void call_func( | |||
// C functions are called differently from language functions | |||
if (typ == L2_VAL_TYPE_CFUNCTION) { | |||
// Make this a while loop, because using call_func would | |||
// make the call stack depth unbounded | |||
vm->stack[vm->sptr++] = func->cfunc(vm, argc, argv); | |||
while (1) { | |||
struct l2_vm_value *val = &vm->values[vm->stack[vm->sptr - 1]]; | |||
if (l2_vm_value_type(val) != L2_VAL_TYPE_CONTINUATION) { | |||
break; | |||
} | |||
l2_word cont_id = val->cont.call; | |||
struct l2_vm_value *cont = &vm->values[cont_id]; | |||
l2_word new_argc; | |||
l2_word new_argv[1]; | |||
if (val->cont.arg == 0) { | |||
new_argc = 0; | |||
} else { | |||
new_argc = 1; | |||
new_argv[0] = val->cont.arg; | |||
} | |||
if (l2_vm_value_type(cont) == L2_VAL_TYPE_CFUNCTION) { | |||
vm->stack[vm->sptr - 1] = cont->cfunc(vm, new_argc, new_argv); | |||
} else { | |||
vm->sptr -= 1; | |||
call_func(vm, cont_id, new_argc, new_argv); | |||
break; | |||
} | |||
} | |||
return; | |||
} | |||