#define l2_token_get_kind(tok) ((enum l2_token_kind)((tok)->v.flags & ~(1 << 7))) | #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_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); | void l2_token_free(struct l2_token *tok); | ||||
struct l2_token_value l2_token_extract_val(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); | void l2_token_print(struct l2_token *tok, struct l2_io_writer *w); | ||||
struct l2_lexer { | struct l2_lexer { |
return v; | 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) { | void l2_lexer_init(struct l2_lexer *lexer, struct l2_io_reader *r) { | ||||
lexer->toks[0].v.flags = L2_TOK_EOF, | lexer->toks[0].v.flags = L2_TOK_EOF, | ||||
lexer->tokidx = 0; | lexer->tokidx = 0; |
static int tok_is_infix(struct l2_token *tok) { | static int tok_is_infix(struct l2_token *tok) { | ||||
if (l2_token_get_kind(tok) != L2_TOK_IDENT) return 0; | 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 | return | ||||
(str[0] == '$' && str[1] != '\0') || | (str[0] == '$' && str[1] != '\0') || | ||||
return -1; | 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); | struct l2_token_value key = l2_token_extract_val(tok); | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_lexer_consume(lexer); // ')' | l2_lexer_consume(lexer); // ')' | ||||
} else if (l2_token_get_kind(tok) == L2_TOK_IDENT) { | } else if (l2_token_get_kind(tok) == L2_TOK_IDENT) { | ||||
l2_trace_scope("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); | struct l2_token_value ident = l2_token_extract_val(tok); | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_gen_number(gen, number); | l2_gen_number(gen, number); | ||||
} else if (l2_token_get_kind(tok) == L2_TOK_STRING) { | } else if (l2_token_get_kind(tok) == L2_TOK_STRING) { | ||||
l2_trace_scope("string literal"); | 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); | struct l2_token_value str = l2_token_extract_val(tok); | ||||
l2_lexer_consume(lexer); // string | l2_lexer_consume(lexer); // string | ||||
l2_token_get_kind(tok) == L2_TOK_QUOT && | l2_token_get_kind(tok) == L2_TOK_QUOT && | ||||
l2_token_get_kind(tok2) == L2_TOK_IDENT) { | l2_token_get_kind(tok2) == L2_TOK_IDENT) { | ||||
l2_trace_scope("atom literal"); | 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); | struct l2_token_value ident = l2_token_extract_val(tok2); | ||||
l2_lexer_consume(lexer); // "'" | l2_lexer_consume(lexer); // "'" | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
// so we need to parse the operator, then the rhs | // so we need to parse the operator, then the rhs | ||||
// Operator | // Operator | ||||
if (parse_arg_level_expression(lexer, gen, err) < 0) { | |||||
int ret = parse_arg_level_expression(lexer, gen, err); | |||||
if (ret < 0) { | |||||
return -1; | 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 | // RHS | ||||
if (parse_arg_level_expression(lexer, gen, err) < 0) { | if (parse_arg_level_expression(lexer, gen, err) < 0) { | ||||
return -1; | return -1; | ||||
return -1; | return -1; | ||||
} | } | ||||
int ret = 0; | |||||
while (1) { | while (1) { | ||||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | struct l2_token *tok = l2_lexer_peek(lexer, 1); | ||||
struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | ||||
l2_token_get_kind(tok2) == L2_TOK_IDENT && | l2_token_get_kind(tok2) == L2_TOK_IDENT && | ||||
l2_token_get_kind(tok3) == L2_TOK_EQUALS) { | l2_token_get_kind(tok3) == L2_TOK_EQUALS) { | ||||
l2_trace_scope("namespace assign"); | 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); | struct l2_token_value ident = l2_token_extract_val(tok2); | ||||
l2_lexer_consume(lexer); // '.' | l2_lexer_consume(lexer); // '.' | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_token_get_kind(tok) == L2_TOK_PERIOD && | l2_token_get_kind(tok) == L2_TOK_PERIOD && | ||||
l2_token_get_kind(tok2) == L2_TOK_IDENT) { | l2_token_get_kind(tok2) == L2_TOK_IDENT) { | ||||
l2_trace_scope("namespace lookup"); | 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); | struct l2_token_value ident = l2_token_extract_val(tok2); | ||||
l2_lexer_consume(lexer); // '.' | l2_lexer_consume(lexer); // '.' | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
} else { | } else { | ||||
break; | break; | ||||
} | } | ||||
ret = 1; | |||||
} | } | ||||
return 0; | |||||
return ret; | |||||
} | } | ||||
static int parse_expression( | static int parse_expression( | ||||
l2_token_get_kind(tok) == L2_TOK_IDENT && | l2_token_get_kind(tok) == L2_TOK_IDENT && | ||||
l2_token_get_kind(tok2) == L2_TOK_COLON_EQ) { | l2_token_get_kind(tok2) == L2_TOK_COLON_EQ) { | ||||
l2_trace_scope("assign expression"); | 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); | struct l2_token_value ident = l2_token_extract_val(tok); | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_lexer_consume(lexer); // := | l2_lexer_consume(lexer); // := | ||||
l2_token_get_kind(tok) == L2_TOK_IDENT && | l2_token_get_kind(tok) == L2_TOK_IDENT && | ||||
l2_token_get_kind(tok2) == L2_TOK_EQUALS) { | l2_token_get_kind(tok2) == L2_TOK_EQUALS) { | ||||
l2_trace_scope("replacement assign expression"); | 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); | struct l2_token_value ident = l2_token_extract_val(tok); | ||||
l2_lexer_consume(lexer); // ident | l2_lexer_consume(lexer); // ident | ||||
l2_lexer_consume(lexer); // = | l2_lexer_consume(lexer); // = |
} | } | ||||
} | } | ||||
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) { | l2_word l2_builtin_eq(struct l2_vm *vm, l2_word argc, l2_word *argv) { | ||||
if (argc < 2) { | if (argc < 2) { |
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 | |||||
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 |
check("arrays.l2"); | check("arrays.l2"); | ||||
check("functions.l2"); | check("functions.l2"); | ||||
check("dynamic-lookups.l2"); | check("dynamic-lookups.l2"); | ||||
check("builtins.l2"); | |||||
if (error_message != NULL) { | if (error_message != NULL) { | ||||
free(error_message); | free(error_message); |