| @@ -51,6 +51,16 @@ enum l2_opcode { | |||
| */ | |||
| L2_OP_FUNC_CALL, | |||
| /* | |||
| * Call an infix function | |||
| * Pop <rhs> | |||
| * Pop <func> | |||
| * Pop <lhs> | |||
| * Call <func> | |||
| * (Before returning, the function will push a return value onto the stack) | |||
| */ | |||
| L2_OP_FUNC_CALL_INFIX, | |||
| /* | |||
| * Jump relative; rjmp <count> | |||
| * Jump <count> words forwards | |||
| @@ -51,5 +51,6 @@ void l2_gen_stack_frame_set_copy(struct l2_generator *gen, char *ident); | |||
| void l2_gen_stack_frame_replace(struct l2_generator *gen, char **ident); | |||
| void l2_gen_stack_frame_replace_copy(struct l2_generator *gen, char *ident); | |||
| void l2_gen_func_call(struct l2_generator *gen, l2_word argc); | |||
| void l2_gen_func_call_infix(struct l2_generator *gen); | |||
| #endif | |||
| @@ -221,3 +221,7 @@ void l2_gen_func_call(struct l2_generator *gen, l2_word argc) { | |||
| put(gen, L2_OP_FUNC_CALL); | |||
| put(gen, argc); | |||
| } | |||
| void l2_gen_func_call_infix(struct l2_generator *gen) { | |||
| put(gen, L2_OP_FUNC_CALL_INFIX); | |||
| } | |||
| @@ -98,6 +98,11 @@ static int peek_ch(struct l2_lexer *lexer) { | |||
| return ch; | |||
| } | |||
| static int peek_ch_n(struct l2_lexer *lexer, int n) { | |||
| int ch = l2_bufio_peek(&lexer->reader, n); | |||
| return ch; | |||
| } | |||
| static int read_ch(struct l2_lexer *lexer) { | |||
| int ch = l2_bufio_get(&lexer->reader); | |||
| lexer->ch += 1; | |||
| @@ -568,7 +573,9 @@ static void read_tok(struct l2_lexer *lexer, struct l2_token *tok) { | |||
| break; | |||
| default: | |||
| if (is_numeric(ch) || ch == '-') { | |||
| if ( | |||
| is_numeric(ch) || | |||
| (ch == '-' && is_numeric(peek_ch_n(lexer, 2)))) { | |||
| read_number(lexer, tok); | |||
| break; | |||
| } | |||
| @@ -11,6 +11,23 @@ static int tok_is_end(struct l2_token *tok) { | |||
| kind == L2_TOK_EOL; | |||
| } | |||
| static int tok_is_infix(struct l2_token *tok) { | |||
| if (l2_token_get_kind(tok) != L2_TOK_IDENT) return 0; | |||
| char *str; | |||
| if (l2_token_is_small(tok)) { | |||
| str = tok->v.strbuf; | |||
| } else { | |||
| str = tok->v.str; | |||
| } | |||
| return | |||
| (str[0] == '$' && str[1] != '\0') || | |||
| strcmp(str, "+") == 0 || | |||
| strcmp(str, "-") == 0 || | |||
| strcmp(str, "*") == 0 || | |||
| strcmp(str, "/") == 0; | |||
| } | |||
| static int parse_expression( | |||
| struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | |||
| @@ -85,9 +102,8 @@ static int parse_function_literal_impl( | |||
| // '{' and EOL already skipped by parse_object_or_function_literal | |||
| // The arguments array will be at the top of the stack | |||
| char *ident = malloc(2); | |||
| ident[0] = '$'; ident[1] = '\0'; | |||
| l2_gen_stack_frame_set(gen, &ident); | |||
| char *ident = "$"; | |||
| l2_gen_stack_frame_set_copy(gen, ident); | |||
| int first = 1; | |||
| while (1) { | |||
| @@ -474,6 +490,19 @@ static int parse_expression( | |||
| return -1; | |||
| } | |||
| if (tok_is_infix(l2_lexer_peek(lexer, 1))) { | |||
| if (parse_arg_level_expression(lexer, gen, err) < 0) { | |||
| return -1; | |||
| } | |||
| if (parse_arg_level_expression(lexer, gen, err) < 0) { | |||
| return -1; | |||
| } | |||
| l2_gen_func_call_infix(gen); | |||
| } | |||
| if (!tok_is_end(l2_lexer_peek(lexer, 1))) { | |||
| if (parse_func_call_after_base(lexer, gen, err) < 0) { | |||
| return -1; | |||
| @@ -138,6 +138,10 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
| printf("FUNC_CALL %08x\n", ops[(*ptr)++]); | |||
| return; | |||
| case L2_OP_FUNC_CALL_INFIX: | |||
| printf("FUNC_CALL_INFIX\n"); | |||
| return; | |||
| case L2_OP_RJMP: | |||
| printf("RJMP %08x\n", ops[(*ptr)++]); | |||
| return; | |||
| @@ -319,7 +319,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
| struct l2_vm_value *func = &vm->values[func_id]; | |||
| l2_word stack_base = vm->sptr; | |||
| enum l2_value_type typ = l2_vm_value_type(func); | |||
| // C functions are called differently from language functions | |||
| @@ -568,6 +567,56 @@ void l2_vm_step(struct l2_vm *vm) { | |||
| } | |||
| break; | |||
| case L2_OP_FUNC_CALL_INFIX: | |||
| { | |||
| l2_word rhs = vm->stack[--vm->sptr]; | |||
| l2_word func_id = vm->stack[--vm->sptr]; | |||
| l2_word lhs = vm->stack[--vm->sptr]; | |||
| struct l2_vm_value *func = &vm->values[func_id]; | |||
| l2_word stack_base = vm->sptr; | |||
| enum l2_value_type typ = l2_vm_value_type(func); | |||
| // C functions are called differently from language functions | |||
| if (typ == L2_VAL_TYPE_CFUNCTION) { | |||
| l2_word argv[] = { lhs, rhs }; | |||
| vm->stack[vm->sptr++] = func->cfunc(vm, 2, argv); | |||
| break; | |||
| } | |||
| // Don't interpret a non-function as a function | |||
| if (typ != L2_VAL_TYPE_FUNCTION) { | |||
| vm->stack[vm->sptr++] = l2_vm_error(vm, "Attempt to call non-function"); | |||
| break; | |||
| } | |||
| l2_word arr_id = alloc_val(vm); | |||
| vm->values[arr_id].flags = L2_VAL_TYPE_ARRAY; | |||
| vm->values[arr_id].array = malloc( | |||
| sizeof(struct l2_vm_array) + sizeof(l2_word) * 2); | |||
| struct l2_vm_array *arr = vm->values[arr_id].array; | |||
| arr->len = 2; | |||
| arr->size = 2; | |||
| arr->data[0] = lhs; | |||
| arr->data[1] = rhs; | |||
| vm->stack[vm->sptr++] = arr_id; | |||
| l2_word ns_id = alloc_val(vm); | |||
| func = &vm->values[func_id]; // func might be stale after alloc | |||
| vm->values[ns_id].extra.ns_parent = func->func.ns; | |||
| vm->values[ns_id].ns = NULL; | |||
| vm->values[ns_id].flags = L2_VAL_TYPE_NAMESPACE; | |||
| vm->fstack[vm->fsptr].ns = ns_id; | |||
| vm->fstack[vm->fsptr].retptr = vm->iptr; | |||
| vm->fstack[vm->fsptr].sptr = stack_base; | |||
| vm->fsptr += 1; | |||
| vm->iptr = func->func.pos; | |||
| } | |||
| break; | |||
| case L2_OP_HALT: | |||
| vm->halted = 1; | |||
| break; | |||