/* | /* | ||||
* Call a function. | * Call a function. | ||||
* Pop <word> | |||||
* Pop <argc> | |||||
* Pop <func> | |||||
* Pop argc times | |||||
* Push <iptr> + 1 | * Push <iptr> + 1 | ||||
* Jump to <word> | |||||
* Push array with args | |||||
* Call <func> | |||||
*/ | */ | ||||
L2_OP_CALL, | |||||
L2_OP_FUNC_CALL, | |||||
/* | /* | ||||
* Jump relative. | * Jump relative. | ||||
*/ | */ | ||||
L2_OP_RJMP, | L2_OP_RJMP, | ||||
/* | |||||
* Generate a stack frame. | |||||
* Alloc namespace <var> | |||||
* NSPush <var> | |||||
*/ | |||||
L2_OP_GEN_STACK_FRAME, | |||||
/* | /* | ||||
* Look up a value from the current stack frame. | * Look up a value from the current stack frame. | ||||
* Pop <word> | * Pop <word> | ||||
/* | /* | ||||
* Return from a function. | * Return from a function. | ||||
* NSPop | * NSPop | ||||
* Pop (discard args array) | |||||
* Pop <word> | * Pop <word> | ||||
* Jump to <word> | * Jump to <word> | ||||
*/ | */ |
void l2_gen_free(struct l2_generator *gen); | void l2_gen_free(struct l2_generator *gen); | ||||
void l2_gen_halt(struct l2_generator *gen); | void l2_gen_halt(struct l2_generator *gen); | ||||
void l2_gen_stack_frame(struct l2_generator *gen); | |||||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | void l2_gen_rjmp(struct l2_generator *gen, l2_word len); | ||||
void l2_gen_pop(struct l2_generator *gen); | void l2_gen_pop(struct l2_generator *gen); | ||||
void l2_gen_push(struct l2_generator *gen, l2_word word); | |||||
void l2_gen_ret(struct l2_generator *gen); | void l2_gen_ret(struct l2_generator *gen); | ||||
void l2_gen_assignment(struct l2_generator *gen, char **ident); | void l2_gen_assignment(struct l2_generator *gen, char **ident); | ||||
void l2_gen_number(struct l2_generator *gen, double num); | void l2_gen_number(struct l2_generator *gen, double num); | ||||
void l2_gen_string(struct l2_generator *gen, char **str); | void l2_gen_string(struct l2_generator *gen, char **str); | ||||
void l2_gen_function(struct l2_generator *gen, l2_word pos); | void l2_gen_function(struct l2_generator *gen, l2_word pos); | ||||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | ||||
void l2_gen_func_call(struct l2_generator *gen); | |||||
#endif | #endif |
L2_TOK_COMMA, | L2_TOK_COMMA, | ||||
L2_TOK_PERIOD, | L2_TOK_PERIOD, | ||||
L2_TOK_COLON_EQ, | L2_TOK_COLON_EQ, | ||||
L2_TOK_EOL, | |||||
L2_TOK_EOF, | L2_TOK_EOF, | ||||
L2_TOK_NUMBER, | L2_TOK_NUMBER, | ||||
L2_TOK_STRING, | L2_TOK_STRING, | ||||
int tokidx; | int tokidx; | ||||
int line; | int line; | ||||
int ch; | int ch; | ||||
int parens; | |||||
struct l2_bufio_reader reader; | struct l2_bufio_reader reader; | ||||
}; | }; | ||||
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); | ||||
void l2_lexer_consume(struct l2_lexer *lexer); | void l2_lexer_consume(struct l2_lexer *lexer); | ||||
void l2_lexer_skip_opt(struct l2_lexer *lexer, enum l2_token_kind kind); | |||||
#endif | #endif |
#include "../bytecode.h" | #include "../bytecode.h" | ||||
#include "../bitset.h" | #include "../bitset.h" | ||||
struct l2_vm; | |||||
struct l2_vm_array; | |||||
typedef l2_word (*l2_vm_cfunction)(struct l2_vm *vm, struct l2_vm_array *args); | |||||
struct l2_vm_value { | struct l2_vm_value { | ||||
enum l2_value_flags { | enum l2_value_flags { | ||||
L2_VAL_TYPE_NONE, | L2_VAL_TYPE_NONE, | ||||
L2_VAL_TYPE_ARRAY, | L2_VAL_TYPE_ARRAY, | ||||
L2_VAL_TYPE_NAMESPACE, | L2_VAL_TYPE_NAMESPACE, | ||||
L2_VAL_TYPE_FUNCTION, | L2_VAL_TYPE_FUNCTION, | ||||
L2_VAL_TYPE_CFUNCTION, | |||||
L2_VAL_MARKED = 1 << 7, | L2_VAL_MARKED = 1 << 7, | ||||
L2_VAL_CONST = 1 << 8, | L2_VAL_CONST = 1 << 8, | ||||
} flags; | } flags; | ||||
l2_word pos; | l2_word pos; | ||||
l2_word namespace; | l2_word namespace; | ||||
} func; | } func; | ||||
l2_vm_cfunction cfunc; | |||||
void *data; | void *data; | ||||
}; | }; | ||||
}; | }; |
put(gen, L2_OP_HALT); | put(gen, L2_OP_HALT); | ||||
} | } | ||||
void l2_gen_stack_frame(struct l2_generator *gen) { | |||||
put(gen, L2_OP_GEN_STACK_FRAME); | |||||
} | |||||
void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | void l2_gen_rjmp(struct l2_generator *gen, l2_word len) { | ||||
put(gen, L2_OP_PUSH); | put(gen, L2_OP_PUSH); | ||||
put(gen, len); | put(gen, len); | ||||
put(gen, L2_OP_POP); | put(gen, L2_OP_POP); | ||||
} | } | ||||
void l2_gen_push(struct l2_generator *gen, l2_word word) { | |||||
put(gen, L2_OP_PUSH); | |||||
put(gen, word); | |||||
} | |||||
void l2_gen_ret(struct l2_generator *gen) { | void l2_gen_ret(struct l2_generator *gen) { | ||||
put(gen, L2_OP_RET); | put(gen, L2_OP_RET); | ||||
} | } | ||||
put(gen, atom_id); | put(gen, atom_id); | ||||
put(gen, L2_OP_STACK_FRAME_LOOKUP); | put(gen, L2_OP_STACK_FRAME_LOOKUP); | ||||
} | } | ||||
void l2_gen_func_call(struct l2_generator *gen) { | |||||
put(gen, L2_OP_FUNC_CALL); | |||||
} |
return "period"; | return "period"; | ||||
case L2_TOK_COLON_EQ: | case L2_TOK_COLON_EQ: | ||||
return "period"; | return "period"; | ||||
case L2_TOK_EOL: | |||||
return "end-of-line"; | |||||
case L2_TOK_EOF: | case L2_TOK_EOF: | ||||
return "end-of-file"; | return "end-of-file"; | ||||
case L2_TOK_NUMBER: | case L2_TOK_NUMBER: | ||||
lexer->tokidx = 0; | lexer->tokidx = 0; | ||||
lexer->line = 1; | lexer->line = 1; | ||||
lexer->ch = 1; | lexer->ch = 1; | ||||
lexer->parens = 0; | |||||
l2_bufio_reader_init(&lexer->reader, r); | l2_bufio_reader_init(&lexer->reader, r); | ||||
} | } | ||||
return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'; | return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'; | ||||
} | } | ||||
static void skip_whitespace(struct l2_lexer *lexer) { | |||||
while (is_whitespace(l2_bufio_peek(&lexer->reader, 1))) read_ch(lexer); | |||||
static int skip_whitespace(struct l2_lexer *lexer) { | |||||
int nl = 0; | |||||
while (is_whitespace(l2_bufio_peek(&lexer->reader, 1))) { | |||||
int ch = read_ch(lexer); | |||||
if (ch == '\n') { | |||||
nl = 1; | |||||
} | |||||
} | |||||
return nl; | |||||
} | } | ||||
static void read_string(struct l2_lexer *lexer, struct l2_token *tok) { | static void read_string(struct l2_lexer *lexer, struct l2_token *tok) { | ||||
} | } | ||||
static void read_tok(struct l2_lexer *lexer, struct l2_token *tok) { | static void read_tok(struct l2_lexer *lexer, struct l2_token *tok) { | ||||
skip_whitespace(lexer); | |||||
tok->line = lexer->line; | tok->line = lexer->line; | ||||
tok->ch = lexer->ch; | tok->ch = lexer->ch; | ||||
int nl = skip_whitespace(lexer); | |||||
if (nl && lexer->parens == 0) { | |||||
tok->kind = L2_TOK_EOL; | |||||
return; | |||||
} | |||||
int ch = peek_ch(lexer); | int ch = peek_ch(lexer); | ||||
switch (ch) { | switch (ch) { | ||||
case '(': | case '(': | ||||
read_ch(lexer); | read_ch(lexer); | ||||
tok->kind = L2_TOK_OPEN_PAREN; | tok->kind = L2_TOK_OPEN_PAREN; | ||||
lexer->parens += 1; | |||||
break; | break; | ||||
case ')': | case ')': | ||||
read_ch(lexer); | read_ch(lexer); | ||||
tok->kind = L2_TOK_CLOSE_PAREN; | tok->kind = L2_TOK_CLOSE_PAREN; | ||||
lexer->parens -= 1; | |||||
break; | break; | ||||
case '{': | case '{': | ||||
lexer->tokidx -= 1; | lexer->tokidx -= 1; | ||||
memmove(lexer->toks, lexer->toks + 1, lexer->tokidx * sizeof(*lexer->toks)); | memmove(lexer->toks, lexer->toks + 1, lexer->tokidx * sizeof(*lexer->toks)); | ||||
} | } | ||||
void l2_lexer_skip_opt(struct l2_lexer *lexer, enum l2_token_kind kind) { | |||||
if (l2_lexer_peek(lexer, 1)->kind == kind) { | |||||
l2_lexer_consume(lexer); | |||||
} | |||||
} |
#include "parse/parse.h" | #include "parse/parse.h" | ||||
#include <stdbool.h> | |||||
#include "gen/gen.h" | #include "gen/gen.h" | ||||
static int is_end_tok(struct l2_token *tok) { | |||||
return | |||||
tok->kind == L2_TOK_CLOSE_PAREN || tok->kind == L2_TOK_CLOSE_BRACE || | |||||
tok->kind == L2_TOK_CLOSE_BRACKET || tok->kind == L2_TOK_EOL || | |||||
tok->kind == L2_TOK_EOF; | |||||
} | |||||
static int parse_expression( | static int parse_expression( | ||||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | ||||
static int parse_function_impl( | static int parse_function_impl( | ||||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | ||||
l2_gen_stack_frame(gen); | |||||
l2_lexer_consume(lexer); // { | l2_lexer_consume(lexer); // { | ||||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||||
while (1) { | while (1) { | ||||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | struct l2_token *tok = l2_lexer_peek(lexer, 1); | ||||
if (parse_expression(lexer, gen, err) < 0) { | if (parse_expression(lexer, gen, err) < 0) { | ||||
return -1; | return -1; | ||||
} | } | ||||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||||
} | } | ||||
l2_gen_ret(gen); | l2_gen_ret(gen); | ||||
return 0; | return 0; | ||||
} | } | ||||
static int parse_expression( | |||||
static int parse_sub_expression( | |||||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | ||||
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); | |||||
if (tok->kind == L2_TOK_IDENT && tok2->kind == L2_TOK_COLON_EQ) { | |||||
char *ident = l2_token_extract_str(tok); | |||||
l2_lexer_consume(lexer); // ident | |||||
l2_lexer_consume(lexer); // := | |||||
if (tok->kind == L2_TOK_OPEN_PAREN) { | |||||
l2_lexer_consume(lexer); // ( | |||||
if (parse_expression(lexer, gen, err) < 0) { | if (parse_expression(lexer, gen, err) < 0) { | ||||
free(ident); | |||||
return -1; | return -1; | ||||
} | } | ||||
l2_gen_assignment(gen, &ident); | |||||
tok = l2_lexer_peek(lexer, 1); | |||||
if (tok->kind != L2_TOK_CLOSE_PAREN) { | |||||
l2_parse_err(err, tok, "In paren expression: Expected close paren, got %s", | |||||
l2_token_kind_name(tok->kind)); | |||||
return -1; | |||||
} | |||||
l2_lexer_consume(lexer); // ) | |||||
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); | ||||
return parse_function(lexer, gen, err); | return parse_function(lexer, gen, err); | ||||
} | } | ||||
l2_parse_err(err, tok, "In expression: Unexpected tokens %s, %s", | |||||
l2_token_kind_name(tok->kind), l2_token_kind_name(tok2->kind)); | |||||
l2_parse_err(err, tok, "In expression: Unexpected token %s", | |||||
l2_token_kind_name(tok->kind)); | |||||
return -1; | return -1; | ||||
} | } | ||||
int l2_parse_program( | |||||
static int parse_expression( | |||||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | ||||
l2_gen_stack_frame(gen); | |||||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||||
struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | |||||
if (tok->kind == L2_TOK_IDENT && tok2->kind == L2_TOK_COLON_EQ) { | |||||
char *ident = l2_token_extract_str(tok); | |||||
l2_lexer_consume(lexer); // ident | |||||
l2_lexer_consume(lexer); // := | |||||
if (parse_expression(lexer, gen, err) < 0) { | |||||
free(ident); | |||||
return -1; | |||||
} | |||||
l2_gen_assignment(gen, &ident); | |||||
return 0; | |||||
} else if (tok->kind == L2_TOK_IDENT && !is_end_tok(tok2)) { | |||||
char *ident = l2_token_extract_str(tok); | |||||
l2_lexer_consume(lexer); | |||||
l2_word count = 0; | |||||
while (!is_end_tok(l2_lexer_peek(lexer, 1))) { | |||||
count += 1; | |||||
if (parse_sub_expression(lexer, gen, err) < 0) { | |||||
return -1; | |||||
} | |||||
} | |||||
bool first = true; | |||||
l2_gen_push(gen, count); | |||||
l2_gen_namespace_lookup(gen, &ident); | |||||
l2_gen_func_call(gen); | |||||
return 0; | |||||
} | |||||
return parse_sub_expression(lexer, gen, err); | |||||
} | |||||
int l2_parse_program( | |||||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||||
int first = 1; | |||||
while (1) { | while (1) { | ||||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | struct l2_token *tok = l2_lexer_peek(lexer, 1); | ||||
return -1; | return -1; | ||||
} | } | ||||
first = false; | |||||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||||
first = 0; | |||||
} | } | ||||
l2_gen_halt(gen); | l2_gen_halt(gen); |
l2_word hash = (key + i) & ns->mask; | l2_word hash = (key + i) & ns->mask; | ||||
l2_word k = ns->data[hash]; | l2_word k = ns->data[hash]; | ||||
if (k == 0) { | if (k == 0) { | ||||
return 0; | |||||
if (ns->parent == NULL) { | |||||
return 0; | |||||
} else { | |||||
return get(ns->parent->data, key); | |||||
} | |||||
} else if (k == key) { | } else if (k == key) { | ||||
return ns->data[ns->size + hash]; | return ns->data[ns->size + hash]; | ||||
} | } |
printf("ADD\n"); | printf("ADD\n"); | ||||
break; | break; | ||||
case L2_OP_CALL: | |||||
printf("CALL\n"); | |||||
case L2_OP_FUNC_CALL: | |||||
printf("FUNC_CALL\n"); | |||||
break; | break; | ||||
case L2_OP_RJMP: | case L2_OP_RJMP: | ||||
printf("RJMP\n"); | printf("RJMP\n"); | ||||
break; | break; | ||||
case L2_OP_GEN_STACK_FRAME: | |||||
printf("GEN_STACK_FRAME\n"); | |||||
break; | |||||
case L2_OP_STACK_FRAME_LOOKUP: | case L2_OP_STACK_FRAME_LOOKUP: | ||||
printf("STACK_FRAME_LOOKUP\n"); | printf("STACK_FRAME_LOOKUP\n"); | ||||
break; | break; |
static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val); | static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val); | ||||
static void gc_mark(struct l2_vm *vm, l2_word id) { | static void gc_mark(struct l2_vm *vm, l2_word id) { | ||||
printf("GC MARK %i\n", id); | |||||
struct l2_vm_value *val = &vm->values[id]; | struct l2_vm_value *val = &vm->values[id]; | ||||
if (val->flags & L2_VAL_MARKED) { | |||||
return; | |||||
} | |||||
printf("GC MARK %i\n", id); | |||||
val->flags |= L2_VAL_MARKED; | val->flags |= L2_VAL_MARKED; | ||||
int typ = l2_vm_value_type(val); | int typ = l2_vm_value_type(val); | ||||
gc_mark(vm, ns->data[ns->size + i]); | gc_mark(vm, ns->data[ns->size + i]); | ||||
} | } | ||||
if (ns->parent != NULL) { | |||||
gc_mark_namespace(vm, ns->parent); | |||||
} | |||||
} | } | ||||
static void gc_free(struct l2_vm *vm, l2_word id) { | static void gc_free(struct l2_vm *vm, l2_word id) { | ||||
struct l2_vm_value *val = &vm->values[i]; | struct l2_vm_value *val = &vm->values[i]; | ||||
if (!(val->flags & L2_VAL_MARKED)) { | if (!(val->flags & L2_VAL_MARKED)) { | ||||
printf("GC FREE %zi\n", i); | |||||
gc_free(vm, i); | gc_free(vm, i); | ||||
freed += 1; | freed += 1; | ||||
} else { | } else { | ||||
// variable ID 0 should be the only 'none' variable in the system | // variable ID 0 should be the only 'none' variable in the system | ||||
l2_word none_id = alloc_val(vm); | l2_word none_id = alloc_val(vm); | ||||
vm->values[none_id].flags = L2_VAL_TYPE_NONE | L2_VAL_CONST; | vm->values[none_id].flags = L2_VAL_TYPE_NONE | L2_VAL_CONST; | ||||
// Need to allocate a root namespace | |||||
l2_word root = alloc_val(vm); | |||||
vm->values[root].flags = L2_VAL_TYPE_NAMESPACE; | |||||
vm->values[root].data = NULL; // Will be allocated on first insert | |||||
vm->nstack[vm->nsptr] = root; | |||||
vm->nsptr += 1; | |||||
} | } | ||||
void l2_vm_free(struct l2_vm *vm) { | void l2_vm_free(struct l2_vm *vm) { | ||||
vm->sptr -= 1; | vm->sptr -= 1; | ||||
break; | break; | ||||
case L2_OP_CALL: | |||||
word = vm->stack[vm->sptr - 1]; | |||||
vm->stack[vm->sptr - 1] = vm->iptr + 1; | |||||
vm->iptr = word; | |||||
case L2_OP_FUNC_CALL: | |||||
{ | |||||
l2_word func_id = vm->stack[--vm->sptr]; | |||||
l2_word argc = vm->stack[--vm->sptr]; | |||||
struct l2_vm_value *func = &vm->values[func_id]; | |||||
l2_word arr_id = alloc_val(vm); | |||||
vm->values[arr_id].flags = L2_VAL_TYPE_ARRAY; | |||||
vm->values[arr_id].data = malloc( | |||||
sizeof(struct l2_vm_array) + sizeof(l2_word) * argc); | |||||
struct l2_vm_array *arr = vm->values[arr_id].data; | |||||
arr->len = argc; | |||||
arr->size = argc; | |||||
vm->sptr -= argc; | |||||
for (l2_word i = 0; i < argc; ++i) { | |||||
arr->data[i] = vm->stack[vm->sptr + i]; | |||||
} | |||||
enum l2_value_flags typ = l2_vm_value_type(func); | |||||
// C functions are called differently from language functions | |||||
if (typ == L2_VAL_TYPE_CFUNCTION) { | |||||
vm->stack[vm->sptr++] = func->cfunc(vm, arr); | |||||
break; | |||||
} | |||||
// Don't interpret a non-function as a function | |||||
if (typ != L2_VAL_TYPE_FUNCTION) { | |||||
// TODO: Error mechanism | |||||
break; | |||||
} | |||||
vm->stack[vm->sptr++] = vm->iptr; | |||||
vm->stack[vm->sptr++] = arr_id; | |||||
l2_word ns_id = alloc_val(vm); | |||||
vm->values[ns_id].flags = L2_VAL_TYPE_NAMESPACE; | |||||
vm->values[ns_id].data = calloc(1, sizeof(struct l2_vm_namespace)); | |||||
struct l2_vm_namespace *ns = vm->values[ns_id].data; | |||||
ns->parent = &vm->values[func->func.namespace]; // TODO: This won't work if values is realloc'd | |||||
vm->nstack[vm->nsptr++] = ns_id; | |||||
vm->iptr = func->func.pos; | |||||
} | |||||
break; | break; | ||||
case L2_OP_RJMP: | case L2_OP_RJMP: | ||||
vm->iptr += word; | vm->iptr += word; | ||||
break; | break; | ||||
case L2_OP_GEN_STACK_FRAME: | |||||
word = alloc_val(vm); | |||||
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; | |||||
vm->values[word].data = NULL; // Will be allocated on first insert | |||||
vm->nstack[vm->nsptr] = word; | |||||
vm->nsptr += 1; | |||||
break; | |||||
case L2_OP_STACK_FRAME_LOOKUP: | case L2_OP_STACK_FRAME_LOOKUP: | ||||
{ | { | ||||
l2_word key = vm->stack[vm->sptr - 1]; | l2_word key = vm->stack[vm->sptr - 1]; | ||||
break; | break; | ||||
case L2_OP_RET: | case L2_OP_RET: | ||||
vm->nsptr -= 1; | |||||
vm->iptr = vm->stack[vm->sptr - 1]; | |||||
vm->sptr -= 1; | |||||
vm->nsptr -= 1; | |||||
{ | |||||
l2_word retval = vm->stack[--vm->sptr]; | |||||
vm->sptr -= 1; // Discard arguments array | |||||
l2_word retaddr = vm->stack[--vm->sptr]; | |||||
vm->stack[vm->sptr++] = retval; | |||||
vm->nsptr -= 1; // Pop function stack frame | |||||
vm->iptr = retaddr; | |||||
} | |||||
break; | break; | ||||
case L2_OP_ALLOC_INTEGER_32: | case L2_OP_ALLOC_INTEGER_32: |
asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | ||||
l2_lexer_consume(&lexer); | l2_lexer_consume(&lexer); | ||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_EOL); | |||||
l2_lexer_consume(&lexer); | |||||
asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | asserteq(l2_lexer_peek(&lexer, 1)->kind, L2_TOK_IDENT); | ||||
asserteq(l2_lexer_peek(&lexer, 1)->v.str, "bar"); | asserteq(l2_lexer_peek(&lexer, 1)->v.str, "bar"); | ||||
l2_lexer_consume(&lexer); | l2_lexer_consume(&lexer); |