| @@ -59,9 +59,10 @@ struct l2_token { | |||
| #define l2_token_get_kind(tok) ((enum l2_token_kind)((tok)->v.flags & ~(1 << 7))) | |||
| #define l2_token_get_name(tok) (l2_token_kind_name(l2_token_get_kind(tok))) | |||
| #define l2_token_is_small(tok) ((tok)->v.flags & (1 << 7)) | |||
| #define l2_token_is_small(tok) ((tok)->v.flags & L2_TOK_SMALL) | |||
| void l2_token_free(struct l2_token *tok); | |||
| struct l2_token_value l2_token_extract_val(struct l2_token *tok); | |||
| const char *l2_token_get_str(struct l2_token *tok); | |||
| void l2_token_print(struct l2_token *tok, struct l2_io_writer *w); | |||
| struct l2_lexer { | |||
| @@ -90,6 +90,14 @@ struct l2_token_value l2_token_extract_val(struct l2_token *tok) { | |||
| return v; | |||
| } | |||
| const char *l2_token_get_str(struct l2_token *tok) { | |||
| if (l2_token_is_small(tok)) { | |||
| return tok->v.strbuf; | |||
| } else { | |||
| return tok->v.str; | |||
| } | |||
| } | |||
| void l2_lexer_init(struct l2_lexer *lexer, struct l2_io_reader *r) { | |||
| lexer->toks[0].v.flags = L2_TOK_EOF, | |||
| lexer->tokidx = 0; | |||
| @@ -13,12 +13,7 @@ static int tok_is_end(struct l2_token *tok) { | |||
| 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; | |||
| } | |||
| const char *str = l2_token_get_str(tok); | |||
| return | |||
| (str[0] == '$' && str[1] != '\0') || | |||
| @@ -58,7 +53,7 @@ static int parse_object_literal( | |||
| return -1; | |||
| } | |||
| l2_trace("key: '%s'", tok->v.str); | |||
| l2_trace("key: '%s'", l2_token_get_str(tok)); | |||
| struct l2_token_value key = l2_token_extract_val(tok); | |||
| l2_lexer_consume(lexer); // ident | |||
| @@ -258,11 +253,7 @@ static int parse_arg_level_expression_base( | |||
| l2_lexer_consume(lexer); // ')' | |||
| } else if (l2_token_get_kind(tok) == L2_TOK_IDENT) { | |||
| l2_trace_scope("ident"); | |||
| if (l2_token_is_small(tok)) { | |||
| l2_trace("ident '%s'", tok->v.strbuf); | |||
| } else { | |||
| l2_trace("ident '%s'", tok->v.str); | |||
| } | |||
| l2_trace("ident '%s'", l2_token_get_str(tok)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok); | |||
| l2_lexer_consume(lexer); // ident | |||
| @@ -280,7 +271,7 @@ static int parse_arg_level_expression_base( | |||
| l2_gen_number(gen, number); | |||
| } else if (l2_token_get_kind(tok) == L2_TOK_STRING) { | |||
| l2_trace_scope("string literal"); | |||
| l2_trace("string '%s'", tok->v.str); | |||
| l2_trace("string '%s'", l2_token_get_str(tok)); | |||
| struct l2_token_value str = l2_token_extract_val(tok); | |||
| l2_lexer_consume(lexer); // string | |||
| @@ -293,7 +284,7 @@ static int parse_arg_level_expression_base( | |||
| l2_token_get_kind(tok) == L2_TOK_QUOT && | |||
| l2_token_get_kind(tok2) == L2_TOK_IDENT) { | |||
| l2_trace_scope("atom literal"); | |||
| l2_trace("atom '%s'", tok->v.str); | |||
| l2_trace("atom '%s'", l2_token_get_str(tok2)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok2); | |||
| l2_lexer_consume(lexer); // "'" | |||
| l2_lexer_consume(lexer); // ident | |||
| @@ -334,10 +325,18 @@ static int parse_func_call_after_base( | |||
| // so we need to parse the operator, then the rhs | |||
| // Operator | |||
| if (parse_arg_level_expression(lexer, gen, err) < 0) { | |||
| int ret = parse_arg_level_expression(lexer, gen, err); | |||
| if (ret < 0) { | |||
| return -1; | |||
| } | |||
| // If the operator wasn't just the one base expression, | |||
| // abort; we're not doing the infix call | |||
| if (ret == 1) { | |||
| argc += 1; | |||
| break; | |||
| } | |||
| // RHS | |||
| if (parse_arg_level_expression(lexer, gen, err) < 0) { | |||
| return -1; | |||
| @@ -377,6 +376,7 @@ static int parse_arg_level_expression( | |||
| return -1; | |||
| } | |||
| int ret = 0; | |||
| while (1) { | |||
| struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||
| struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | |||
| @@ -407,11 +407,7 @@ static int parse_arg_level_expression( | |||
| l2_token_get_kind(tok2) == L2_TOK_IDENT && | |||
| l2_token_get_kind(tok3) == L2_TOK_EQUALS) { | |||
| l2_trace_scope("namespace assign"); | |||
| if (l2_token_is_small(tok2)) { | |||
| l2_trace("ident '%s'", tok2->v.strbuf); | |||
| } else { | |||
| l2_trace("ident '%s'", tok2->v.str); | |||
| } | |||
| l2_trace("ident '%s'", l2_token_get_str(tok2)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok2); | |||
| l2_lexer_consume(lexer); // '.' | |||
| l2_lexer_consume(lexer); // ident | |||
| @@ -432,11 +428,7 @@ static int parse_arg_level_expression( | |||
| l2_token_get_kind(tok) == L2_TOK_PERIOD && | |||
| l2_token_get_kind(tok2) == L2_TOK_IDENT) { | |||
| l2_trace_scope("namespace lookup"); | |||
| if (l2_token_is_small(tok2)) { | |||
| l2_trace("ident '%s'", tok2->v.strbuf); | |||
| } else { | |||
| l2_trace("ident '%s'", tok2->v.str); | |||
| } | |||
| l2_trace("ident '%s'", l2_token_get_str(tok2)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok2); | |||
| l2_lexer_consume(lexer); // '.' | |||
| l2_lexer_consume(lexer); // ident | |||
| @@ -498,9 +490,11 @@ static int parse_arg_level_expression( | |||
| } else { | |||
| break; | |||
| } | |||
| ret = 1; | |||
| } | |||
| return 0; | |||
| return ret; | |||
| } | |||
| static int parse_expression( | |||
| @@ -513,11 +507,7 @@ static int parse_expression( | |||
| l2_token_get_kind(tok) == L2_TOK_IDENT && | |||
| l2_token_get_kind(tok2) == L2_TOK_COLON_EQ) { | |||
| l2_trace_scope("assign expression"); | |||
| if (l2_token_is_small(tok)) { | |||
| l2_trace("ident '%s'", tok->v.strbuf); | |||
| } else { | |||
| l2_trace("ident '%s'", tok->v.str); | |||
| } | |||
| l2_trace("ident '%s'", l2_token_get_str(tok)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok); | |||
| l2_lexer_consume(lexer); // ident | |||
| l2_lexer_consume(lexer); // := | |||
| @@ -536,11 +526,7 @@ static int parse_expression( | |||
| l2_token_get_kind(tok) == L2_TOK_IDENT && | |||
| l2_token_get_kind(tok2) == L2_TOK_EQUALS) { | |||
| l2_trace_scope("replacement assign expression"); | |||
| if (l2_token_is_small(tok)) { | |||
| l2_trace("ident '%s'", tok->v.strbuf); | |||
| } else { | |||
| l2_trace("ident '%s'", tok->v.str); | |||
| } | |||
| l2_trace("ident '%s'", l2_token_get_str(tok)); | |||
| struct l2_token_value ident = l2_token_extract_val(tok); | |||
| l2_lexer_consume(lexer); // ident | |||
| l2_lexer_consume(lexer); // = | |||
| @@ -66,113 +66,39 @@ static void print_val(struct l2_vm *vm, struct l2_io_writer *out, struct l2_vm_v | |||
| } | |||
| } | |||
| l2_word l2_builtin_add(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
| if (argc < 1) { | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = 0; | |||
| return id; | |||
| } | |||
| struct l2_vm_value *val = &vm->values[argv[0]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| double sum = val->real; | |||
| for (l2_word i = 1; i < argc; ++i) { | |||
| val = &vm->values[argv[i]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| sum += val->real; | |||
| } | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = sum; | |||
| return id; | |||
| } | |||
| l2_word l2_builtin_sub(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
| if (argc < 1) { | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = 0; | |||
| return id; | |||
| } | |||
| struct l2_vm_value *val = &vm->values[argv[0]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| double sum = val->real; | |||
| for (l2_word i = 1; i < argc; ++i) { | |||
| val = &vm->values[argv[i]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| sum -= val->real; | |||
| } | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = sum; | |||
| return id; | |||
| } | |||
| l2_word l2_builtin_mul(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
| if (argc < 1) { | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = 1; | |||
| return id; | |||
| } | |||
| struct l2_vm_value *val = &vm->values[argv[0]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| double sum = val->real; | |||
| for (l2_word i = 1; i < argc; ++i) { | |||
| val = &vm->values[argv[i]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| sum *= val->real; | |||
| } | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = sum; | |||
| return id; | |||
| } | |||
| l2_word l2_builtin_div(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
| if (argc < 1) { | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = 1; | |||
| return id; | |||
| } | |||
| struct l2_vm_value *val = &vm->values[argv[0]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| double sum = val->real; | |||
| for (l2_word i = 1; i < argc; ++i) { | |||
| val = &vm->values[argv[i]]; | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { | |||
| return l2_vm_type_error(vm, val); | |||
| } | |||
| sum /= val->real; | |||
| } | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); | |||
| vm->values[id].real = sum; | |||
| return id; | |||
| #define X(name, identity, op) \ | |||
| l2_word name(struct l2_vm *vm, l2_word argc, l2_word *argv) { \ | |||
| if (argc == 0) { \ | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); \ | |||
| vm->values[id].real = identity; \ | |||
| return id; \ | |||
| } \ | |||
| struct l2_vm_value *first = &vm->values[argv[0]]; \ | |||
| if (l2_value_get_type(first) != L2_VAL_TYPE_REAL) { \ | |||
| return l2_vm_type_error(vm, first); \ | |||
| } \ | |||
| if (argc == 1) { \ | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); \ | |||
| vm->values[id].real = identity op first->real; \ | |||
| return id; \ | |||
| } \ | |||
| double sum = first->real; \ | |||
| for (l2_word i = 1; i < argc; ++i) { \ | |||
| struct l2_vm_value *val = &vm->values[argv[i]]; \ | |||
| if (l2_value_get_type(val) != L2_VAL_TYPE_REAL) { \ | |||
| return l2_vm_type_error(vm, val); \ | |||
| } \ | |||
| sum = sum op val->real; \ | |||
| } \ | |||
| l2_word id = l2_vm_alloc(vm, L2_VAL_TYPE_REAL, 0); \ | |||
| vm->values[id].real = sum; \ | |||
| return id; \ | |||
| } | |||
| X(l2_builtin_add, 0, +) | |||
| X(l2_builtin_sub, 0, -) | |||
| X(l2_builtin_mul, 1, *) | |||
| X(l2_builtin_div, 1, /) | |||
| #undef X | |||
| l2_word l2_builtin_eq(struct l2_vm *vm, l2_word argc, l2_word *argv) { | |||
| if (argc < 2) { | |||
| @@ -0,0 +1,73 @@ | |||
| print "+" | |||
| print +() | |||
| print +(10) | |||
| print 10 + 20 | |||
| print (+ 10 20 30) | |||
| print "\n-" | |||
| print -() -(10) | |||
| print 10 - 20 | |||
| print (- 10 20 30) | |||
| print "\n/" | |||
| print /() /(10) | |||
| print 10 / 2 | |||
| print (/ 10 2 2) | |||
| print "\n*" | |||
| print *() *(10) | |||
| print 10 * 2 | |||
| print (* 10 20 30) | |||
| print "\n==" | |||
| print ==() ==(10) | |||
| print 10 == 10 20 == 10 | |||
| print (== 10 20 30) | |||
| print (== 'a 'a 'a) | |||
| print "\n!=" | |||
| print !=() !=(10) | |||
| print 10 != 10 20 != 10 | |||
| print (!= 10 20 30) | |||
| print (!= 'a 'a 'a) | |||
| print "\n>" | |||
| print 10 > 20 | |||
| print 20 > 10 | |||
| print 10 > 10 | |||
| print (> 1 10 100 200) | |||
| print "\n>=" | |||
| print 10 >= 20 | |||
| print 20 >= 10 | |||
| print 10 >= 10 | |||
| print (>= 1 10 100 200) | |||
| print "\n<" | |||
| print 10 < 20 | |||
| print 20 < 10 | |||
| print 10 < 10 | |||
| print (< 1 10 100 200) | |||
| print "\n<=" | |||
| print 10 <= 20 | |||
| print 20 <= 10 | |||
| print 10 <= 10 | |||
| print (<= 1 10 100 200) | |||
| print "\nprint" | |||
| print none | |||
| print 'true 'false 'hello | |||
| print 100 | |||
| print 100.5 | |||
| print 0xff | |||
| print "Hello World" | |||
| print [none 'true 'false 'hello 100.1 "Nope" {} {0}] | |||
| print {} {a: 10} | |||
| print "\nlen" | |||
| print (len 10) | |||
| print (len []) | |||
| print (len [10 20]) | |||
| print (len "Hello") | |||
| print (len {a: 10; b: 20; c: 30}) | |||
| @@ -0,0 +1,73 @@ | |||
| + | |||
| 0 | |||
| 10 | |||
| 30 | |||
| 60 | |||
| - | |||
| 0 -10 | |||
| -10 | |||
| -40 | |||
| / | |||
| 1 0.1 | |||
| 5 | |||
| 2.5 | |||
| * | |||
| 1 10 | |||
| 20 | |||
| 6000 | |||
| == | |||
| (true) (true) | |||
| (true) (false) | |||
| (false) | |||
| (true) | |||
| != | |||
| (false) (false) | |||
| (false) (true) | |||
| (true) | |||
| (false) | |||
| > | |||
| (false) | |||
| (true) | |||
| (false) | |||
| (false) | |||
| >= | |||
| (false) | |||
| (true) | |||
| (true) | |||
| (false) | |||
| < | |||
| (true) | |||
| (false) | |||
| (false) | |||
| (true) | |||
| <= | |||
| (true) | |||
| (false) | |||
| (true) | |||
| (true) | |||
| (none) | |||
| (true) (false) (atom 20) | |||
| 100 | |||
| 100.5 | |||
| 255 | |||
| Hello World | |||
| [(none) (true) (false) (atom 20) 100.1 Nope (namespace) (function)] | |||
| (namespace) (namespace) | |||
| len | |||
| 0 | |||
| 0 | |||
| 2 | |||
| 5 | |||
| 3 | |||
| @@ -131,6 +131,7 @@ describe(exaples) { | |||
| check("arrays.l2"); | |||
| check("functions.l2"); | |||
| check("dynamic-lookups.l2"); | |||
| check("builtins.l2"); | |||
| if (error_message != NULL) { | |||
| free(error_message); | |||