@@ -1,67 +1,10 @@ | |||
#include "vm/vm.h" | |||
#include "vm/print.h" | |||
#include "bitset.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
void print_var(struct l2_vm_value *val) { | |||
switch (val->flags & 0x0f) { | |||
case L2_VAL_TYPE_NONE: | |||
printf("NONE\n"); | |||
break; | |||
case L2_VAL_TYPE_INTEGER: | |||
printf("INTEGER %zi\n", val->integer); | |||
break; | |||
case L2_VAL_TYPE_REAL: | |||
printf("REAL %f\n", val->real); | |||
break; | |||
case L2_VAL_TYPE_ARRAY: | |||
{ | |||
if (val->data == NULL) { | |||
printf("ARRAY, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_array *arr = (struct l2_vm_array *)val->data; | |||
printf("ARRAY, len %zu\n", arr->len); | |||
for (size_t i = 0; i < arr->len; ++i) { | |||
printf(" %zu: %u\n", i, arr->data[i]); | |||
} | |||
} | |||
break; | |||
case L2_VAL_TYPE_BUFFER: | |||
{ | |||
if (val->data == NULL) { | |||
printf("BUFFER, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_buffer *buf = (struct l2_vm_buffer *)val->data; | |||
printf("BUFFER, len %zu\n", buf->len); | |||
for (size_t i = 0; i < buf->len; ++i) { | |||
printf(" %zu: %c\n", i, buf->data[i]); | |||
} | |||
} | |||
break; | |||
case L2_VAL_TYPE_NAMESPACE: | |||
{ | |||
if (val->data == NULL) { | |||
printf("NAMESPACE, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data; | |||
printf("NAMESPACE, len %zu\n", ns->len); | |||
} | |||
break; | |||
} | |||
} | |||
int main() { | |||
l2_word ops[] = { | |||
L2_OP_PUSH, 100, | |||
@@ -88,28 +31,12 @@ int main() { | |||
l2_vm_run(&vm); | |||
printf("Stack:\n"); | |||
for (l2_word i = 0; i < vm.sptr; ++i) { | |||
printf(" %i: %i\n", i, vm.stack[i]); | |||
} | |||
printf("Heap:\n"); | |||
for (l2_word i = 0; i < vm.valuessize; ++i) { | |||
if (l2_bitset_get(&vm.valueset, i)) { | |||
printf(" %u: ", i); | |||
print_var(&vm.values[i]); | |||
} | |||
} | |||
l2_vm_print_state(&vm); | |||
l2_vm_gc(&vm); | |||
printf("Heap:\n"); | |||
for (l2_word i = 0; i < vm.valuessize; ++i) { | |||
if (l2_bitset_get(&vm.valueset, i)) { | |||
printf(" %u: ", i); | |||
print_var(&vm.values[i]); | |||
} | |||
} | |||
l2_vm_print_stack(&vm); | |||
l2_vm_free(&vm); | |||
} |
@@ -52,8 +52,32 @@ enum l2_opcode { | |||
*/ | |||
L2_OP_CALL, | |||
/* | |||
* 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> | |||
* Find <val> in stack frame using <word> | |||
* Push <val> | |||
*/ | |||
L2_OP_STACK_FRAME_LOOKUP, | |||
/* | |||
* Set a value in the current stack frame. | |||
* Pop <key> | |||
* Read <val> | |||
* Assign <val> to stack frame | |||
*/ | |||
L2_OP_STACK_FRAME_SET, | |||
/* | |||
* Return from a function. | |||
* NSPop | |||
* Pop <word> | |||
* Jump to <word> | |||
*/ | |||
@@ -86,9 +110,9 @@ enum l2_opcode { | |||
/* | |||
* Allocate a real from two words. | |||
* Pop <word1> | |||
* Pop <word2> | |||
* Alloc real <var> from <word1> << 32 | <word2> | |||
* Pop <high> | |||
* Pop <low> | |||
* Alloc real <var> from <high> << 32 | <low> | |||
* Push <var> | |||
*/ | |||
L2_OP_ALLOC_REAL_64, | |||
@@ -127,10 +151,9 @@ enum l2_opcode { | |||
/* | |||
* Set a namespace's name to a value. | |||
* Pop <key> | |||
* Pop <val> | |||
* Read <val> | |||
* Read <ns> | |||
* Assign <val> to <ns[<key>]> | |||
* Push <val> | |||
*/ | |||
L2_OP_NAMESPACE_SET, | |||
@@ -10,10 +10,14 @@ struct l2_generator { | |||
struct l2_bufio_writer writer; | |||
}; | |||
void l2_gen_init(struct l2_generator *gen); | |||
void l2_gen_init(struct l2_generator *gen, struct l2_io_writer *w); | |||
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_assignment(struct l2_generator *gen, char **ident); | |||
void l2_gen_number(struct l2_generator *gen, double num); | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident); | |||
#endif |
@@ -34,12 +34,12 @@ struct l2_token { | |||
}; | |||
void l2_token_free(struct l2_token *tok); | |||
struct l2_token l2_token_move(struct l2_token *tok); | |||
char *l2_token_extract_str(struct l2_token *tok); | |||
void l2_token_print(struct l2_token *tok, struct l2_io_writer *w); | |||
struct l2_lexer { | |||
struct l2_token currtok; | |||
struct l2_token toks[2]; | |||
struct l2_token toks[4]; | |||
int tokidx; | |||
int line; | |||
int ch; |
@@ -4,6 +4,16 @@ | |||
#include "lex.h" | |||
#include "gen/gen.h" | |||
int l2_parse_program(struct l2_lexer *lexer, struct l2_generator *gen); | |||
struct l2_parse_error { | |||
int line; | |||
int ch; | |||
char *message; | |||
}; | |||
void l2_parse_err(struct l2_parse_error *err, struct l2_token *tok, const char *fmt, ...); | |||
void l2_parse_error_free(struct l2_parse_error *err); | |||
int l2_parse_program( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err); | |||
#endif |
@@ -0,0 +1,15 @@ | |||
#ifndef L2_VM_PRINT_H | |||
#define L2_VM_PRINT_H | |||
#include "vm.h" | |||
void l2_vm_print_val(struct l2_vm_value *val); | |||
void l2_vm_print_state(struct l2_vm *vm); | |||
void l2_vm_print_heap(struct l2_vm *vm); | |||
void l2_vm_print_stack(struct l2_vm *vm); | |||
void l2_vm_print_nstack(struct l2_vm *vm); | |||
void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr); | |||
void l2_vm_print_bytecode(l2_word *ops, size_t opcount); | |||
#endif |
@@ -24,6 +24,8 @@ struct l2_vm_value { | |||
}; | |||
}; | |||
#define l2_vm_value_type(val) ((val).flags & 0x0f) | |||
struct l2_vm_buffer { | |||
size_t len; | |||
char data[]; | |||
@@ -49,15 +51,17 @@ l2_word l2_vm_namespace_get(struct l2_vm_value *ns, l2_word key); | |||
struct l2_vm { | |||
l2_word *ops; | |||
size_t opcount; | |||
l2_word iptr; | |||
struct l2_vm_value *values; | |||
size_t valuessize; | |||
struct l2_bitset valueset; | |||
l2_word stack[1024]; | |||
unsigned char stackflags[1024]; | |||
l2_word iptr; | |||
l2_word sptr; | |||
l2_word nstack[1024]; | |||
l2_word nsptr; | |||
}; | |||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); |
@@ -6,9 +6,14 @@ static void put(struct l2_generator *gen, l2_word word) { | |||
l2_bufio_put_n(&gen->writer, &word, sizeof(word)); | |||
} | |||
void l2_gen_init(struct l2_generator *gen) { | |||
void l2_gen_init(struct l2_generator *gen, struct l2_io_writer *w) { | |||
l2_strset_init(&gen->atoms); | |||
l2_strset_init(&gen->strings); | |||
l2_bufio_writer_init(&gen->writer, w); | |||
} | |||
void l2_gen_flush(struct l2_generator *gen) { | |||
l2_bufio_flush(&gen->writer); | |||
} | |||
void l2_gen_free(struct l2_generator *gen) { | |||
@@ -16,22 +21,47 @@ void l2_gen_free(struct l2_generator *gen) { | |||
l2_strset_free(&gen->strings); | |||
} | |||
// Postconditions: | |||
// * Execution is halted | |||
void l2_gen_halt(struct l2_generator *gen) { | |||
put(gen, L2_OP_HALT); | |||
} | |||
// Postconditions: | |||
// * NStack(0) is a namespace value | |||
void l2_gen_stack_frame(struct l2_generator *gen) { | |||
put(gen, L2_OP_ALLOC_NAMESPACE); | |||
put(gen, L2_OP_GEN_STACK_FRAME); | |||
} | |||
// Preconditions: | |||
// * Stack(0) is any value | |||
// Postconditions: | |||
// * The namespace contains the new value under key 'ident' | |||
// * Stack(0) is untouched | |||
void l2_gen_assignment(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atoms, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_NAMESPACE_SET); | |||
put(gen, L2_OP_STACK_FRAME_SET); | |||
} | |||
// Postconditions; | |||
// * Stack(0) is changed to a number value | |||
void l2_gen_number(struct l2_generator *gen, double num) { | |||
uint64_t n; | |||
memcpy(&n, &num, sizeof(num)); | |||
put(gen, L2_OP_PUSH_2); | |||
put(gen, n >> 32); | |||
put(gen, n); | |||
put(gen, n >> 32); | |||
put(gen, L2_OP_ALLOC_REAL_64); | |||
} | |||
// Postconditions: | |||
// * Stack(0) is any value | |||
void l2_gen_namespace_lookup(struct l2_generator *gen, char **ident) { | |||
size_t atom_id = l2_strset_put(&gen->atoms, ident); | |||
put(gen, L2_OP_PUSH); | |||
put(gen, atom_id); | |||
put(gen, L2_OP_STACK_FRAME_LOOKUP); | |||
} |
@@ -0,0 +1,36 @@ | |||
#include "parse/parse.h" | |||
#include <stdio.h> | |||
#include <stdarg.h> | |||
void l2_parse_err(struct l2_parse_error *err, struct l2_token *tok, const char *fmt, ...) { | |||
err->line = tok->line; | |||
err->ch = tok->ch; | |||
char buf[256]; | |||
va_list va; | |||
va_start(va, fmt); | |||
int n = vsnprintf(buf, sizeof(buf), fmt, va); | |||
if (n < 0) { | |||
const char *message = "Failed to generate error message!"; | |||
err->message = malloc(strlen(message) + 1); | |||
strcpy(err->message, message); | |||
va_end(va); | |||
return; | |||
} else if (n + 1 < sizeof(buf)) { | |||
err->message = malloc(n + 1); | |||
strcpy(err->message, buf); | |||
va_end(va); | |||
return; | |||
} | |||
// Need to allocate for this one | |||
err->message = malloc(n + 1); | |||
vsnprintf(err->message, n + 1, fmt, va); | |||
va_end(va); | |||
} | |||
void l2_parse_error_free(struct l2_parse_error *err) { | |||
free(err->message); | |||
} |
@@ -2,6 +2,23 @@ | |||
#include <stdlib.h> | |||
static int parse_number(const char *str, double *num) { | |||
size_t len = strlen(str); | |||
*num = 0; | |||
int power = 1; | |||
for (int i = (int)len - 1; i >= 0; --i) { | |||
char ch = str[i]; | |||
if (ch >= '0' && ch <= '9') { | |||
*num += (ch - '0') * power; | |||
power *= 10; | |||
} else { | |||
return -1; | |||
} | |||
} | |||
return 0; | |||
} | |||
const char *l2_token_kind_name(enum l2_token_kind kind) { | |||
switch (kind) { | |||
case L2_TOK_OPEN_PAREN: | |||
@@ -41,13 +58,10 @@ void l2_token_free(struct l2_token *tok) { | |||
} | |||
} | |||
struct l2_token l2_token_move(struct l2_token *tok) { | |||
struct l2_token dup = *tok; | |||
if (tok->kind == L2_TOK_STRING) { | |||
tok->v.str = NULL; | |||
} | |||
return dup; | |||
char *l2_token_extract_str(struct l2_token *tok) { | |||
char *str = tok->v.str; | |||
tok->v.str = NULL; | |||
return str; | |||
} | |||
void l2_lexer_init(struct l2_lexer *lexer, struct l2_io_reader *r) { | |||
@@ -201,41 +215,50 @@ static void read_tok(struct l2_lexer *lexer, struct l2_token *tok) { | |||
tok->line = lexer->line; | |||
tok->ch = lexer->ch; | |||
int ch = read_ch(lexer); | |||
int ch = peek_ch(lexer); | |||
switch (ch) { | |||
case '(': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_OPEN_PAREN; | |||
break; | |||
case ')': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_CLOSE_PAREN; | |||
break; | |||
case '{': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_OPEN_BRACE; | |||
break; | |||
case '}': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_CLOSE_BRACE; | |||
break; | |||
case '[': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_OPEN_BRACKET; | |||
break; | |||
case ']': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_CLOSE_BRACKET; | |||
break; | |||
case ',': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_COMMA; | |||
break; | |||
case '.': | |||
read_ch(lexer); | |||
tok->kind = L2_TOK_PERIOD; | |||
break; | |||
case ':': | |||
read_ch(lexer); | |||
{ | |||
ch = read_ch(lexer); | |||
switch (ch) { | |||
@@ -256,11 +279,22 @@ static void read_tok(struct l2_lexer *lexer, struct l2_token *tok) { | |||
break; | |||
case '"': | |||
read_ch(lexer); | |||
read_string(lexer, tok); | |||
break; | |||
default: | |||
read_ident(lexer, tok); | |||
if (tok->kind != L2_TOK_IDENT) { | |||
break; | |||
} | |||
double num; | |||
if (parse_number(tok->v.str, &num) >= 0) { | |||
free(tok->v.str); | |||
tok->kind = L2_TOK_NUMBER; | |||
tok->v.num = num; | |||
} | |||
break; | |||
} | |||
} |
@@ -2,23 +2,41 @@ | |||
#include "gen/gen.h" | |||
static int parse_expression(struct l2_lexer *lexer, struct l2_generator *gen) { | |||
static int parse_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) { | |||
parse_expression(lexer, gen); | |||
l2_gen_assignment(gen, &tok->v.str); | |||
char *ident = l2_token_extract_str(tok); | |||
l2_lexer_get(lexer); // ident | |||
l2_lexer_get(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_NUMBER) { | |||
l2_gen_number(gen, tok->v.num); | |||
l2_lexer_get(lexer); // number | |||
return 0; | |||
} else if (tok->kind == L2_TOK_IDENT) { | |||
char *ident = l2_token_extract_str(tok); | |||
l2_lexer_get(lexer); // ident | |||
l2_gen_namespace_lookup(gen, &ident); | |||
return 0; | |||
} | |||
l2_parse_err(err, tok, "In expression: Unexpected tokens %s, %s", | |||
l2_token_kind_name(tok->kind), l2_token_kind_name(tok2->kind)); | |||
return -1; | |||
} | |||
int l2_parse_program(struct l2_lexer *lexer, struct l2_generator *gen) { | |||
int l2_parse_program( | |||
struct l2_lexer *lexer, struct l2_generator *gen, struct l2_parse_error *err) { | |||
l2_gen_stack_frame(gen); | |||
while (1) { | |||
@@ -27,10 +45,14 @@ int l2_parse_program(struct l2_lexer *lexer, struct l2_generator *gen) { | |||
break; | |||
} | |||
if (parse_expression(lexer, gen) < 0) { | |||
if (parse_expression(lexer, gen, err) < 0) { | |||
l2_gen_halt(gen); | |||
l2_gen_flush(gen); | |||
return -1; | |||
} | |||
} | |||
l2_gen_halt(gen); | |||
l2_gen_flush(gen); | |||
return 0; | |||
} |
@@ -0,0 +1,202 @@ | |||
#include "vm/print.h" | |||
#include <stdio.h> | |||
void l2_vm_print_val(struct l2_vm_value *val) { | |||
switch (val->flags & 0x0f) { | |||
case L2_VAL_TYPE_NONE: | |||
printf("NONE\n"); | |||
break; | |||
case L2_VAL_TYPE_INTEGER: | |||
printf("INTEGER %zi\n", val->integer); | |||
break; | |||
case L2_VAL_TYPE_REAL: | |||
printf("REAL %f\n", val->real); | |||
break; | |||
case L2_VAL_TYPE_ARRAY: | |||
{ | |||
if (val->data == NULL) { | |||
printf("ARRAY, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_array *arr = (struct l2_vm_array *)val->data; | |||
printf("ARRAY, len %zu\n", arr->len); | |||
for (size_t i = 0; i < arr->len; ++i) { | |||
printf(" %zu: %u\n", i, arr->data[i]); | |||
} | |||
} | |||
break; | |||
case L2_VAL_TYPE_BUFFER: | |||
{ | |||
if (val->data == NULL) { | |||
printf("BUFFER, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_buffer *buf = (struct l2_vm_buffer *)val->data; | |||
printf("BUFFER, len %zu\n", buf->len); | |||
for (size_t i = 0; i < buf->len; ++i) { | |||
printf(" %zu: %c\n", i, buf->data[i]); | |||
} | |||
} | |||
break; | |||
case L2_VAL_TYPE_NAMESPACE: | |||
{ | |||
if (val->data == NULL) { | |||
printf("NAMESPACE, empty\n"); | |||
return; | |||
} | |||
struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data; | |||
printf("NAMESPACE, len %zu\n", ns->len); | |||
} | |||
break; | |||
} | |||
} | |||
void l2_vm_print_state(struct l2_vm *vm) { | |||
printf("Stack:\n"); | |||
l2_vm_print_stack(vm); | |||
printf("Heap:\n"); | |||
l2_vm_print_heap(vm); | |||
printf("NStack:\n"); | |||
l2_vm_print_nstack(vm); | |||
} | |||
void l2_vm_print_heap(struct l2_vm *vm) { | |||
for (l2_word i = 0; i < vm->valuessize; ++i) { | |||
if (l2_bitset_get(&vm->valueset, i)) { | |||
printf(" %u: ", i); | |||
l2_vm_print_val(&vm->values[i]); | |||
} | |||
} | |||
} | |||
void l2_vm_print_stack(struct l2_vm *vm) { | |||
for (l2_word i = 0; i < vm->sptr; ++i) { | |||
printf(" %i: %i\n", i, vm->stack[i]); | |||
} | |||
} | |||
void l2_vm_print_nstack(struct l2_vm *vm) { | |||
for (l2_word i = 0; i < vm->nsptr; ++i) { | |||
printf(" %i: %i\n", i, vm->nstack[i]); | |||
} | |||
} | |||
static void print_op_num(l2_word *ops, size_t opcount, size_t ptr) { | |||
if (ptr >= opcount) { | |||
printf("<EOF>"); | |||
} else { | |||
printf("%i", ops[ptr]); | |||
} | |||
} | |||
void l2_vm_print_op(l2_word *ops, size_t opcount, size_t *ptr) { | |||
enum l2_opcode opcode = (enum l2_opcode)ops[(*ptr)++]; | |||
switch (opcode) { | |||
case L2_OP_NOP: | |||
printf("NOP\n"); | |||
break; | |||
case L2_OP_PUSH: | |||
printf("PUSH "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf("\n"); | |||
break; | |||
case L2_OP_PUSH_2: | |||
printf("PUSH2 "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf(" "); | |||
print_op_num(ops, opcount, (*ptr)++); | |||
printf("\n"); | |||
break; | |||
case L2_OP_POP: | |||
printf("POP\n"); | |||
break; | |||
case L2_OP_DUP: | |||
printf("DUP\n"); | |||
break; | |||
case L2_OP_ADD: | |||
printf("ADD\n"); | |||
break; | |||
case L2_OP_CALL: | |||
printf("CALL\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; | |||
case L2_OP_STACK_FRAME_SET: | |||
printf("STACK_FRAME_SET\n"); | |||
break; | |||
case L2_OP_RET: | |||
printf("RET\n"); | |||
break; | |||
case L2_OP_ALLOC_INTEGER_32: | |||
printf("ALLOC_INTEGER_32\n"); | |||
break; | |||
case L2_OP_ALLOC_INTEGER_64: | |||
printf("ALLOC_INTEGER_64\n"); | |||
break; | |||
case L2_OP_ALLOC_REAL_32: | |||
printf("ALLOC_REAL_32\n"); | |||
break; | |||
case L2_OP_ALLOC_REAL_64: | |||
printf("ALLOC_REAL_64\n"); | |||
break; | |||
case L2_OP_ALLOC_BUFFER_STATIC: | |||
printf("ALLOC_BUFFER_STATIC\n"); | |||
break; | |||
case L2_OP_ALLOC_BUFFER_ZERO: | |||
printf("ALLOC_BUFFER_ZERO\n"); | |||
break; | |||
case L2_OP_ALLOC_ARRAY: | |||
printf("ALLOC_ARRAY\n"); | |||
break; | |||
case L2_OP_ALLOC_NAMESPACE: | |||
printf("ALLOC_NAMESPACE\n"); | |||
break; | |||
case L2_OP_NAMESPACE_SET: | |||
printf("NAMESPACE_SET\n"); | |||
break; | |||
case L2_OP_HALT: | |||
printf("HALT\n"); | |||
break; | |||
} | |||
} | |||
void l2_vm_print_bytecode(l2_word *ops, size_t opcount) { | |||
size_t ptr = 0; | |||
while (ptr < opcount) { | |||
l2_vm_print_op(ops, opcount, &ptr); | |||
} | |||
} |
@@ -34,35 +34,46 @@ static double u32s_to_double(uint32_t high, uint32_t low) { | |||
return d; | |||
} | |||
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]; | |||
val->flags |= L2_VAL_MARKED; | |||
int typ = val->flags & 0x0f; | |||
int typ = l2_vm_value_type(*val); | |||
if (typ == L2_VAL_TYPE_ARRAY) { | |||
if (val->data == NULL) { | |||
return; | |||
} | |||
struct l2_vm_array *arr = (struct l2_vm_array *)val->data; | |||
for (size_t i = 0; i < arr->len; ++i) { | |||
gc_mark(vm, arr->data[i]); | |||
} | |||
gc_mark_array(vm, val); | |||
} else if (typ == L2_VAL_TYPE_NAMESPACE) { | |||
if (val->data == NULL) { | |||
return; | |||
} | |||
gc_mark_namespace(vm, val); | |||
} | |||
} | |||
struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data; | |||
for (size_t i = 0; i < ns->size; ++i) { | |||
l2_word key = ns->data[i]; | |||
if (key == 0 || key == ~(l2_word)0) { | |||
continue; | |||
} | |||
static void gc_mark_array(struct l2_vm *vm, struct l2_vm_value *val) { | |||
if (val->data == NULL) { | |||
return; | |||
} | |||
gc_mark(vm, ns->data[ns->size + i]); | |||
struct l2_vm_array *arr = (struct l2_vm_array *)val->data; | |||
for (size_t i = 0; i < arr->len; ++i) { | |||
gc_mark(vm, arr->data[i]); | |||
} | |||
} | |||
static void gc_mark_namespace(struct l2_vm *vm, struct l2_vm_value *val) { | |||
if (val->data == NULL) { | |||
return; | |||
} | |||
struct l2_vm_namespace *ns = (struct l2_vm_namespace *)val->data; | |||
for (size_t i = 0; i < ns->size; ++i) { | |||
l2_word key = ns->data[i]; | |||
if (key == 0 || key == ~(l2_word)0) { | |||
continue; | |||
} | |||
gc_mark(vm, ns->data[ns->size + i]); | |||
} | |||
} | |||
@@ -71,7 +82,7 @@ static void gc_free(struct l2_vm *vm, l2_word id) { | |||
struct l2_vm_value *val = &vm->values[id]; | |||
l2_bitset_unset(&vm->valueset, id); | |||
int typ = val->flags & 0x0f; | |||
int typ = l2_vm_value_type(*val); | |||
if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER || typ == L2_VAL_TYPE_NAMESPACE) { | |||
free(val->data); | |||
// Don't need to do anything more; the next round of GC will free | |||
@@ -103,6 +114,7 @@ void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount) { | |||
vm->opcount = opcount; | |||
vm->iptr = 0; | |||
vm->sptr = 0; | |||
vm->nsptr = 0; | |||
vm->values = NULL; | |||
vm->valuessize = 0; | |||
@@ -129,10 +141,10 @@ void l2_vm_free(struct l2_vm *vm) { | |||
} | |||
size_t l2_vm_gc(struct l2_vm *vm) { | |||
for (l2_word sptr = 0; sptr < vm->sptr; ++sptr) { | |||
if (vm->stackflags[sptr]) { | |||
gc_mark(vm, vm->stack[sptr]); | |||
} | |||
for (l2_word nsptr = 0; nsptr < vm->nsptr; ++nsptr) { | |||
struct l2_vm_value *val = &vm->values[vm->nstack[nsptr]]; | |||
val->flags |= L2_VAL_MARKED; | |||
gc_mark_namespace(vm, val); | |||
} | |||
return gc_sweep(vm); | |||
@@ -154,7 +166,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_PUSH: | |||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | |||
vm->stackflags[vm->sptr] = 0; | |||
vm->sptr += 1; | |||
vm->iptr += 1; | |||
break; | |||
@@ -162,8 +173,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_PUSH_2: | |||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | |||
vm->stack[vm->sptr + 1] = vm->ops[vm->iptr + 1]; | |||
vm->stackflags[vm->sptr] = 0; | |||
vm->stackflags[vm->sptr + 1] = 0; | |||
vm->sptr += 2; | |||
vm->iptr += 2; | |||
break; | |||
@@ -174,26 +183,51 @@ void l2_vm_step(struct l2_vm *vm) { | |||
case L2_OP_DUP: | |||
vm->stack[vm->sptr] = vm->ops[vm->sptr - 1]; | |||
vm->stackflags[vm->sptr] = 0; | |||
vm->sptr += 1; | |||
break; | |||
case L2_OP_ADD: | |||
vm->stack[vm->sptr - 2] += vm->stack[vm->sptr - 1]; | |||
vm->stackflags[vm->sptr - 2] = 0; | |||
vm->sptr -= 1; | |||
break; | |||
case L2_OP_CALL: | |||
word = vm->stack[vm->sptr - 1]; | |||
vm->stack[vm->sptr - 1] = vm->iptr + 1; | |||
vm->stackflags[vm->sptr - 1] = 0; | |||
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]; | |||
struct l2_vm_value *ns = &vm->values[vm->nstack[vm->nsptr - 1]]; | |||
vm->stack[vm->sptr - 1] = l2_vm_namespace_get(ns, key); | |||
} | |||
break; | |||
case L2_OP_STACK_FRAME_SET: | |||
{ | |||
l2_word key = vm->stack[vm->sptr - 1]; | |||
l2_word val = vm->stack[vm->sptr - 2]; | |||
struct l2_vm_value *ns = &vm->values[vm->nstack[vm->nsptr - 1]]; | |||
l2_vm_namespace_set(ns, key, val); | |||
vm->sptr -= 1; | |||
} | |||
break; | |||
case L2_OP_RET: | |||
vm->nsptr -= 1; | |||
vm->iptr = vm->stack[vm->sptr - 1]; | |||
vm->sptr -= 1; | |||
vm->nsptr -= 1; | |||
break; | |||
case L2_OP_ALLOC_INTEGER_32: | |||
@@ -201,7 +235,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].flags = L2_VAL_TYPE_INTEGER; | |||
vm->values[word].integer = vm->stack[--vm->sptr]; | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -213,7 +246,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
(uint64_t)vm->stack[vm->sptr - 2]); | |||
vm->sptr -= 2; | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -222,7 +254,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].flags = L2_VAL_TYPE_REAL; | |||
vm->values[word].real = u32_to_float(vm->stack[--vm->sptr]); | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -232,7 +263,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].real = u32s_to_double(vm->stack[vm->sptr - 1], vm->stack[vm->sptr - 2]); | |||
vm->sptr -= 2; | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -248,7 +278,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
(unsigned char *)vm->values[word].data + sizeof(struct l2_vm_buffer), | |||
vm->ops + offset, length); | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
} | |||
break; | |||
@@ -261,7 +290,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].data = calloc(1, sizeof(struct l2_vm_buffer) + length); | |||
((struct l2_vm_buffer *)vm->values[word].data)->len = length; | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
} | |||
break; | |||
@@ -271,7 +299,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].flags = L2_VAL_TYPE_ARRAY; | |||
vm->values[word].data = NULL; // Will be allocated on first insert | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -280,7 +307,6 @@ void l2_vm_step(struct l2_vm *vm) { | |||
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; | |||
vm->values[word].data = NULL; // Will be allocated on first insert | |||
vm->stack[vm->sptr] = word; | |||
vm->stackflags[vm->sptr] = 1; | |||
vm->sptr += 1; | |||
break; | |||
@@ -1,5 +1,5 @@ | |||
files := src ../lib | |||
includes := snow ../include/lang2 | |||
defines := SNOW_ENABLED | |||
sanitizers := address | |||
cflags := -g -O3 | |||
#sanitizers := address undefined | |||
cflags := -g |
@@ -0,0 +1,50 @@ | |||
#include "parse/parse.h" | |||
#include "vm/vm.h" | |||
#include "vm/print.h" | |||
#include <stdio.h> | |||
#include <snow/snow.h> | |||
static struct l2_lexer lex; | |||
static struct l2_io_mem_reader r; | |||
static struct l2_generator gen; | |||
static struct l2_io_mem_writer w; | |||
static struct l2_vm vm; | |||
static struct l2_parse_error err; | |||
static struct l2_vm_value *var_lookup(const char *name) { | |||
l2_word atom_id = l2_strset_get(&gen.atoms, name); | |||
l2_word id = l2_vm_namespace_get(&vm.values[vm.nstack[0]], atom_id); | |||
return &vm.values[id]; | |||
} | |||
static void init(const char *str) { | |||
r.r.read = l2_io_mem_read; | |||
r.idx = 0; | |||
r.len = strlen(str); | |||
r.mem = str; | |||
l2_lexer_init(&lex, &r.r); | |||
w.w.write = l2_io_mem_write; | |||
w.len = 0; | |||
w.mem = NULL; | |||
l2_gen_init(&gen, (struct l2_io_writer *)&w); | |||
} | |||
static void done() { | |||
l2_gen_free(&gen); | |||
free(w.mem); | |||
} | |||
describe(parse) { | |||
test("parse program") { | |||
init("foo := 10"); | |||
defer(done()); | |||
assert(l2_parse_program(&lex, &gen, &err) >= 0); | |||
l2_vm_init(&vm, (l2_word *)w.mem, w.len / sizeof(l2_word)); | |||
l2_vm_run(&vm); | |||
assert(l2_vm_value_type(*var_lookup("foo")) == L2_VAL_TYPE_REAL); | |||
assert(var_lookup("foo")->real == 10); | |||
} | |||
} |