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 { | ||||
struct l2_token currtok; | |||||
struct l2_token toks[4]; | struct l2_token toks[4]; | ||||
int tokidx; | int tokidx; | ||||
int line; | int line; | ||||
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); | ||||
struct l2_token *l2_lexer_peek(struct l2_lexer *lexer, int count); | struct l2_token *l2_lexer_peek(struct l2_lexer *lexer, int count); | ||||
struct l2_token *l2_lexer_get(struct l2_lexer *lexer); | |||||
void l2_lexer_consume(struct l2_lexer *lexer); | |||||
#endif | #endif |
#include <stdlib.h> | #include <stdlib.h> | ||||
static int parse_number(const char *str, double *num) { | static int parse_number(const char *str, double *num) { | ||||
// TODO: Floats | |||||
size_t len = strlen(str); | size_t len = strlen(str); | ||||
*num = 0; | *num = 0; | ||||
int power = 1; | int power = 1; | ||||
} | } | ||||
void l2_token_free(struct l2_token *tok) { | void l2_token_free(struct l2_token *tok) { | ||||
if (tok->kind == L2_TOK_STRING) { | |||||
if (tok->kind == L2_TOK_STRING || tok->kind == L2_TOK_IDENT) { | |||||
free(tok->v.str); | free(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->currtok.kind = L2_TOK_EOF, | |||||
lexer->toks[0].kind = L2_TOK_EOF, | |||||
lexer->tokidx = 0; | lexer->tokidx = 0; | ||||
lexer->line = 1; | lexer->line = 1; | ||||
lexer->ch = 1; | lexer->ch = 1; | ||||
return &lexer->toks[offset]; | return &lexer->toks[offset]; | ||||
} | } | ||||
struct l2_token *l2_lexer_get(struct l2_lexer *lexer) { | |||||
l2_token_free(&lexer->currtok); | |||||
if (lexer->tokidx == 0) { | |||||
read_tok(lexer, &lexer->currtok); | |||||
} else { | |||||
memmove(lexer->toks, lexer->toks + 1, lexer->tokidx - 1); | |||||
lexer->tokidx -= 1; | |||||
} | |||||
return &lexer->currtok; | |||||
void l2_lexer_consume(struct l2_lexer *lexer) { | |||||
l2_token_free(&lexer->toks[0]); | |||||
lexer->tokidx -= 1; | |||||
memmove(lexer->toks, lexer->toks + 1, lexer->tokidx * sizeof(*lexer->toks)); | |||||
} | } |
if (tok->kind == L2_TOK_IDENT && tok2->kind == L2_TOK_COLON_EQ) { | if (tok->kind == L2_TOK_IDENT && tok2->kind == L2_TOK_COLON_EQ) { | ||||
char *ident = l2_token_extract_str(tok); | char *ident = l2_token_extract_str(tok); | ||||
l2_lexer_get(lexer); // ident | |||||
l2_lexer_get(lexer); // := | |||||
l2_lexer_consume(lexer); // ident | |||||
l2_lexer_consume(lexer); // := | |||||
if (parse_expression(lexer, gen, err) < 0) { | if (parse_expression(lexer, gen, err) < 0) { | ||||
free(ident); | free(ident); | ||||
return 0; | return 0; | ||||
} else if (tok->kind == L2_TOK_NUMBER) { | } else if (tok->kind == L2_TOK_NUMBER) { | ||||
l2_gen_number(gen, tok->v.num); | l2_gen_number(gen, tok->v.num); | ||||
l2_lexer_get(lexer); // number | |||||
l2_lexer_consume(lexer); // number | |||||
return 0; | return 0; | ||||
} else if (tok->kind == L2_TOK_IDENT) { | } else if (tok->kind == L2_TOK_IDENT) { | ||||
char *ident = l2_token_extract_str(tok); | char *ident = l2_token_extract_str(tok); | ||||
l2_lexer_get(lexer); // ident | |||||
l2_lexer_consume(lexer); // ident | |||||
l2_gen_namespace_lookup(gen, &ident); | l2_gen_namespace_lookup(gen, &ident); | ||||
return 0; | return 0; | ||||
} | } |
return &vm.values[id]; | return &vm.values[id]; | ||||
} | } | ||||
static int exec(const char *str) { | |||||
static int eval(const char *str) { | |||||
r.r.read = l2_io_mem_read; | r.r.read = l2_io_mem_read; | ||||
r.idx = 0; | r.idx = 0; | ||||
r.len = strlen(str); | r.len = strlen(str); | ||||
return 0; | return 0; | ||||
} | } | ||||
describe(exec) { | |||||
test("exec assignment") { | |||||
exec("foo := 10"); | |||||
describe(eval) { | |||||
test("eval assignment") { | |||||
eval("foo := 10"); | |||||
defer(l2_vm_free(&vm)); | defer(l2_vm_free(&vm)); | ||||
defer(l2_gen_free(&gen)); | defer(l2_gen_free(&gen)); | ||||
assert(l2_vm_value_type(var_lookup("foo")) == L2_VAL_TYPE_REAL); | assert(l2_vm_value_type(var_lookup("foo")) == L2_VAL_TYPE_REAL); | ||||
assert(var_lookup("foo")->real == 10); | assert(var_lookup("foo")->real == 10); | ||||
} | } | ||||
test("eval var deref assignment") { | |||||
eval("foo := 10\nbar := foo"); | |||||
defer(l2_vm_free(&vm)); | |||||
defer(l2_gen_free(&gen)); | |||||
l2_vm_print_state(&vm); | |||||
} | |||||
} | } |
#include "parse/lex.h" | |||||
#include <stdio.h> | |||||
#include <snow/snow.h> | |||||
static struct l2_lexer lexer; | |||||
static struct l2_io_mem_reader r; | |||||
static void lex(const char *str) { | |||||
r.r.read = l2_io_mem_read; | |||||
r.idx = 0; | |||||
r.len = strlen(str); | |||||
r.mem = str; | |||||
l2_lexer_init(&lexer, &r.r); | |||||
} | |||||
describe(lex) { | |||||
test("lex assignment") { | |||||
lex("foo := 10"); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "foo"); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_COLON_EQ); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_NUMBER); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_EOF); | |||||
} | |||||
test("lex var deref assignment") { | |||||
lex("foo := 10\nbar := foo"); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "foo"); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_COLON_EQ); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_NUMBER); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "bar"); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_COLON_EQ); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "foo"); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_EOF); | |||||
} | |||||
test("lex peek multiple") { | |||||
lex("foo := 10"); | |||||
l2_lexer_peek(&lexer, 3); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "foo"); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_COLON_EQ); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_NUMBER); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_EOF); | |||||
} | |||||
} |