| X("-", l2_builtin_sub) | X("-", l2_builtin_sub) | ||||
| X("*", l2_builtin_mul) | X("*", l2_builtin_mul) | ||||
| X("/", l2_builtin_div) | X("/", l2_builtin_div) | ||||
| X("==", l2_builtin_eq) | |||||
| X("!=", l2_builtin_neq) | |||||
| X("print", l2_builtin_print) | X("print", l2_builtin_print) | ||||
| X("len", l2_builtin_len) | X("len", l2_builtin_len) | ||||
| X("if", l2_builtin_if) | X("if", l2_builtin_if) |
| return ch >= '0' && ch <= '9'; | return ch >= '0' && ch <= '9'; | ||||
| } | } | ||||
| static int is_ident(int ch) { | |||||
| return !is_whitespace(ch) && ch != EOF && | |||||
| ch != '(' && ch != ')' && | |||||
| ch != '{' && ch != '}' && | |||||
| ch != '[' && ch != ']' && | |||||
| ch != '\'' && | |||||
| ch != ',' && ch != '.' && | |||||
| ch != ':' && ch != ';'; | |||||
| } | |||||
| static int skip_whitespace(struct l2_lexer *lexer) { | static int skip_whitespace(struct l2_lexer *lexer) { | ||||
| int nl = 0; | int nl = 0; | ||||
| while (1) { | while (1) { | ||||
| while (1) { | while (1) { | ||||
| int ch = peek_ch(lexer); | int ch = peek_ch(lexer); | ||||
| if (is_whitespace(ch)) { | |||||
| dest[idx] = '\0'; | |||||
| return; | |||||
| } | |||||
| switch (ch) { | |||||
| case '(': | |||||
| case ')': | |||||
| case '{': | |||||
| case '}': | |||||
| case '[': | |||||
| case ']': | |||||
| case '\'': | |||||
| case ',': | |||||
| case '.': | |||||
| case ':': | |||||
| case '=': | |||||
| case ';': | |||||
| case EOF: | |||||
| if (!is_ident(ch)) { | |||||
| dest[idx] = '\0'; | dest[idx] = '\0'; | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| case '=': | |||||
| read_ch(lexer); | |||||
| tok->v.flags = L2_TOK_EQUALS; | |||||
| break; | |||||
| case EOF: | case EOF: | ||||
| tok->v.flags = L2_TOK_EOF; | tok->v.flags = L2_TOK_EOF; | ||||
| break; | break; | ||||
| break; | break; | ||||
| default: | default: | ||||
| if ( | |||||
| is_numeric(ch) || | |||||
| (ch == '-' && is_numeric(peek_ch_n(lexer, 2)))) { | |||||
| read_number(lexer, tok); | |||||
| break; | |||||
| } | |||||
| { | |||||
| int ch2 = peek_ch_n(lexer, 2); | |||||
| read_ident(lexer, tok); | |||||
| if ( | |||||
| is_numeric(ch) || | |||||
| (ch == '-' && is_numeric(ch2))) { | |||||
| read_number(lexer, tok); | |||||
| break; | |||||
| } | |||||
| tok->v.flags = L2_TOK_IDENT; | |||||
| read_ident(lexer, tok); | |||||
| if (l2_token_is_small(tok) && strcmp(tok->v.strbuf, "=") == 0) { | |||||
| tok->v.flags = L2_TOK_EQUALS; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| strcmp(str, "+") == 0 || | strcmp(str, "+") == 0 || | ||||
| strcmp(str, "-") == 0 || | strcmp(str, "-") == 0 || | ||||
| strcmp(str, "*") == 0 || | strcmp(str, "*") == 0 || | ||||
| strcmp(str, "/") == 0; | |||||
| strcmp(str, "/") == 0 || | |||||
| strcmp(str, "==") == 0 || | |||||
| strcmp(str, "!=") == 0; | |||||
| } | } | ||||
| static int parse_expression( | static int parse_expression( |
| return id; | return id; | ||||
| } | } | ||||
| l2_word l2_builtin_eq(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||||
| if (argc < 2) { | |||||
| return vm->ktrue; | |||||
| } | |||||
| for (l2_word i = 1; i < argc; ++i) { | |||||
| if (argv[i - 1] == argv[i]) continue; | |||||
| struct l2_vm_value *a = &vm->values[argv[i - 1]]; | |||||
| struct l2_vm_value *b = &vm->values[argv[i]]; | |||||
| if (a->flags != b->flags) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| enum l2_value_type typ = l2_vm_value_type(a); | |||||
| if (typ == L2_VAL_TYPE_ATOM) { | |||||
| if (a->atom != b->atom) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| } else if (typ == L2_VAL_TYPE_REAL) { | |||||
| if (a->real != b->real) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| } else if (typ == L2_VAL_TYPE_BUFFER) { | |||||
| if (a->buffer == NULL && b->buffer == NULL) continue; | |||||
| if (a->buffer == NULL || b->buffer == NULL) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| if (a->buffer->len != b->buffer->len) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| if (memcmp(a->buffer->data, b->buffer->data, a->buffer->len) != 0) { | |||||
| return vm->kfalse; | |||||
| } | |||||
| } else { | |||||
| return vm->kfalse; | |||||
| } | |||||
| } | |||||
| return vm->ktrue; | |||||
| } | |||||
| l2_word l2_builtin_neq(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||||
| l2_word ret_id = l2_builtin_eq(vm, argc, argv); | |||||
| if (ret_id == vm->ktrue) { | |||||
| return vm->kfalse; | |||||
| } else if (ret_id == vm->kfalse) { | |||||
| return vm->ktrue; | |||||
| } else { | |||||
| return ret_id; | |||||
| } | |||||
| } | |||||
| l2_word l2_builtin_print(struct l2_vm *vm, l2_word argc, l2_word *argv) { | l2_word l2_builtin_print(struct l2_vm *vm, l2_word argc, l2_word *argv) { | ||||
| for (size_t i = 0; i < argc; ++i) { | for (size_t i = 0; i < argc; ++i) { | ||||
| if (i != 0) { | if (i != 0) { |
| #define Y(name, k) \ | #define Y(name, k) \ | ||||
| if (strcmp(#k, "knone") == 0) { \ | if (strcmp(#k, "knone") == 0) { \ | ||||
| id = 0; \ | id = 0; \ | ||||
| l2_vm_namespace_set(&vm->values[builtins], key, id); \ | |||||
| } else { \ | } else { \ | ||||
| id = alloc_val(vm); \ | id = alloc_val(vm); \ | ||||
| vm->values[id].flags = L2_VAL_TYPE_ATOM | L2_VAL_CONST; \ | vm->values[id].flags = L2_VAL_TYPE_ATOM | L2_VAL_CONST; \ | ||||
| vm->values[id].atom = key; \ | vm->values[id].atom = key; \ | ||||
| } \ | } \ | ||||
| vm->k = id; \ | vm->k = id; \ | ||||
| l2_vm_namespace_set(&vm->values[builtins], key++, id); | |||||
| key += 1; | |||||
| #define X(name, f) \ | #define X(name, f) \ | ||||
| id = alloc_val(vm); \ | id = alloc_val(vm); \ | ||||
| vm->values[id].flags = L2_VAL_TYPE_CFUNCTION | L2_VAL_CONST; \ | vm->values[id].flags = L2_VAL_TYPE_CFUNCTION | L2_VAL_CONST; \ | ||||
| vm->values[id].cfunc = f; \ | vm->values[id].cfunc = f; \ | ||||
| l2_vm_namespace_set(&vm->values[builtins], key++, id); | l2_vm_namespace_set(&vm->values[builtins], key++, id); | ||||
| #include "builtins.x.h" | #include "builtins.x.h" | ||||
| #undef Y | |||||
| #undef X | #undef X | ||||
| } | } | ||||