@@ -46,11 +46,14 @@ enum l2_opcode { | |||
/* | |||
* Call a function. | |||
* Pop <word> | |||
* Pop <argc> | |||
* Pop <func> | |||
* Pop argc times | |||
* Push <iptr> + 1 | |||
* Jump to <word> | |||
* Push array with args | |||
* Call <func> | |||
*/ | |||
L2_OP_CALL, | |||
L2_OP_FUNC_CALL, | |||
/* | |||
* Jump relative. | |||
@@ -59,13 +62,6 @@ enum l2_opcode { | |||
*/ | |||
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. | |||
* Pop <word> | |||
@@ -85,6 +81,7 @@ enum l2_opcode { | |||
/* | |||
* Return from a function. | |||
* NSPop | |||
* Pop (discard args array) | |||
* Pop <word> | |||
* Jump to <word> | |||
*/ |
@@ -23,14 +23,15 @@ void l2_gen_flush(struct l2_generator *gen); | |||
void l2_gen_free(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_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_assignment(struct l2_generator *gen, char **ident); | |||
void l2_gen_number(struct l2_generator *gen, double num); | |||
void l2_gen_string(struct l2_generator *gen, char **str); | |||
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_func_call(struct l2_generator *gen); | |||
#endif |
@@ -13,6 +13,7 @@ enum l2_token_kind { | |||
L2_TOK_COMMA, | |||
L2_TOK_PERIOD, | |||
L2_TOK_COLON_EQ, | |||
L2_TOK_EOL, | |||
L2_TOK_EOF, | |||
L2_TOK_NUMBER, | |||
L2_TOK_STRING, | |||
@@ -42,6 +43,7 @@ struct l2_lexer { | |||
int tokidx; | |||
int line; | |||
int ch; | |||
int parens; | |||
struct l2_bufio_reader reader; | |||
}; | |||
@@ -49,5 +51,6 @@ struct l2_lexer { | |||
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); | |||
void l2_lexer_consume(struct l2_lexer *lexer); | |||
void l2_lexer_skip_opt(struct l2_lexer *lexer, enum l2_token_kind kind); | |||
#endif |
@@ -6,6 +6,10 @@ | |||
#include "../bytecode.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 { | |||
enum l2_value_flags { | |||
L2_VAL_TYPE_NONE, | |||
@@ -15,6 +19,7 @@ struct l2_vm_value { | |||
L2_VAL_TYPE_ARRAY, | |||
L2_VAL_TYPE_NAMESPACE, | |||
L2_VAL_TYPE_FUNCTION, | |||
L2_VAL_TYPE_CFUNCTION, | |||
L2_VAL_MARKED = 1 << 7, | |||
L2_VAL_CONST = 1 << 8, | |||
} flags; | |||
@@ -25,6 +30,7 @@ struct l2_vm_value { | |||
l2_word pos; | |||
l2_word namespace; | |||
} func; | |||
l2_vm_cfunction cfunc; | |||
void *data; | |||
}; | |||
}; |
@@ -28,10 +28,6 @@ void l2_gen_halt(struct l2_generator *gen) { | |||
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) { | |||
put(gen, L2_OP_PUSH); | |||
put(gen, len); | |||
@@ -42,6 +38,11 @@ void l2_gen_pop(struct l2_generator *gen) { | |||
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) { | |||
put(gen, L2_OP_RET); | |||
} | |||
@@ -111,3 +112,7 @@ void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | |||
put(gen, atom_id); | |||
put(gen, L2_OP_STACK_FRAME_LOOKUP); | |||
} | |||
void l2_gen_func_call(struct l2_generator *gen) { | |||
put(gen, L2_OP_FUNC_CALL); | |||
} |
@@ -40,6 +40,8 @@ const char *l2_token_kind_name(enum l2_token_kind kind) { | |||
return "period"; | |||
case L2_TOK_COLON_EQ: | |||
return "period"; | |||
case L2_TOK_EOL: | |||
return "end-of-line"; | |||
case L2_TOK_EOF: | |||
return "end-of-file"; | |||
case L2_TOK_NUMBER: | |||
@@ -70,6 +72,7 @@ void l2_lexer_init(struct l2_lexer *lexer, struct l2_io_reader *r) { | |||
lexer->tokidx = 0; | |||
lexer->line = 1; | |||
lexer->ch = 1; | |||
lexer->parens = 0; | |||
l2_bufio_reader_init(&lexer->reader, r); | |||
} | |||
@@ -93,8 +96,16 @@ static int is_whitespace(int ch) { | |||
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) { | |||
@@ -213,21 +224,27 @@ static void read_ident(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->ch = lexer->ch; | |||
int nl = skip_whitespace(lexer); | |||
if (nl && lexer->parens == 0) { | |||
tok->kind = L2_TOK_EOL; | |||
return; | |||
} | |||
int ch = peek_ch(lexer); | |||
switch (ch) { | |||
case '(': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_OPEN_PAREN; | |||
lexer->parens += 1; | |||
break; | |||
case ')': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_CLOSE_PAREN; | |||
lexer->parens -= 1; | |||
break; | |||
case '{': | |||
@@ -317,3 +334,9 @@ void l2_lexer_consume(struct l2_lexer *lexer) { | |||
lexer->tokidx -= 1; | |||
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); | |||
} | |||
} |
@@ -1,16 +1,21 @@ | |||
#include "parse/parse.h" | |||
#include <stdbool.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( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | |||
static int parse_function_impl( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
l2_gen_stack_frame(gen); | |||
l2_lexer_consume(lexer); // { | |||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||
while (1) { | |||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||
@@ -25,6 +30,8 @@ static int parse_function_impl( | |||
if (parse_expression(lexer, gen, err) < 0) { | |||
return -1; | |||
} | |||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||
} | |||
l2_gen_ret(gen); | |||
@@ -71,22 +78,24 @@ static int parse_function( | |||
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_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) { | |||
free(ident); | |||
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; | |||
} else if (tok->kind == L2_TOK_NUMBER) { | |||
l2_gen_number(gen, tok->v.num); | |||
@@ -106,16 +115,52 @@ static int parse_expression( | |||
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; | |||
} | |||
int l2_parse_program( | |||
static int parse_expression( | |||
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) { | |||
struct l2_token *tok = l2_lexer_peek(lexer, 1); | |||
@@ -134,7 +179,9 @@ int l2_parse_program( | |||
return -1; | |||
} | |||
first = false; | |||
l2_lexer_skip_opt(lexer, L2_TOK_EOL); | |||
first = 0; | |||
} | |||
l2_gen_halt(gen); |
@@ -91,7 +91,11 @@ static l2_word get(struct l2_vm_namespace *ns, l2_word key) { | |||
l2_word hash = (key + i) & ns->mask; | |||
l2_word k = ns->data[hash]; | |||
if (k == 0) { | |||
return 0; | |||
if (ns->parent == NULL) { | |||
return 0; | |||
} else { | |||
return get(ns->parent->data, key); | |||
} | |||
} else if (k == key) { | |||
return ns->data[ns->size + hash]; | |||
} |
@@ -137,18 +137,14 @@ void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
printf("ADD\n"); | |||
break; | |||
case L2_OP_CALL: | |||
printf("CALL\n"); | |||
case L2_OP_FUNC_CALL: | |||
printf("FUNC_CALL\n"); | |||
break; | |||
case L2_OP_RJMP: | |||
printf("RJMP\n"); | |||
break; | |||
case L2_OP_GEN_STACK_FRAME: | |||
printf("GEN_STACK_FRAME\n"); | |||
break; | |||
case L2_OP_STACK_FRAME_LOOKUP: | |||
printf("STACK_FRAME_LOOKUP\n"); | |||
break; |
@@ -38,8 +38,12 @@ static void gc_mark_array(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) { | |||
printf("GC MARK %i\n", 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; | |||
int typ = l2_vm_value_type(val); | |||
@@ -77,6 +81,10 @@ static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val) { | |||
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) { | |||
@@ -101,6 +109,7 @@ static size_t gc_sweep(struct l2_vm *vm) { | |||
struct l2_vm_value *val = &vm->values[i]; | |||
if (!(val->flags & L2_VAL_MARKED)) { | |||
printf("GC FREE %zi\n", i); | |||
gc_free(vm, i); | |||
freed += 1; | |||
} else { | |||
@@ -125,6 +134,13 @@ void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
// variable ID 0 should be the only 'none' variable in the system | |||
l2_word none_id = alloc_val(vm); | |||
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) { | |||
@@ -192,10 +208,51 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->sptr -= 1; | |||
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; | |||
case L2_OP_RJMP: | |||
@@ -204,14 +261,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->iptr += word; | |||
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: | |||
{ | |||
l2_word key = vm->stack[vm->sptr - 1]; | |||
@@ -231,10 +280,14 @@ void l2_vm_step(struct l2_vm *vm) { | |||
break; | |||
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; | |||
case L2_OP_ALLOC_INTEGER_32: |
@@ -46,6 +46,9 @@ describe(lex) { | |||
asserteq(l2_lexer_peek(&lexer, 1)->v.num, 10); | |||
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)->v.str, "bar"); | |||
l2_lexer_consume(&lexer); |