#include "vm/vm.h" | #include "vm/vm.h" | ||||
#include "vm/print.h" | |||||
#include "bitset.h" | #include "bitset.h" | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <string.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() { | int main() { | ||||
l2_word ops[] = { | l2_word ops[] = { | ||||
L2_OP_PUSH, 100, | L2_OP_PUSH, 100, | ||||
l2_vm_run(&vm); | 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); | l2_vm_gc(&vm); | ||||
printf("Heap:\n"); | 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); | l2_vm_free(&vm); | ||||
} | } |
*/ | */ | ||||
L2_OP_CALL, | 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. | * Return from a function. | ||||
* NSPop | |||||
* Pop <word> | * Pop <word> | ||||
* Jump to <word> | * Jump to <word> | ||||
*/ | */ | ||||
/* | /* | ||||
* Allocate a real from two words. | * 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> | * Push <var> | ||||
*/ | */ | ||||
L2_OP_ALLOC_REAL_64, | L2_OP_ALLOC_REAL_64, | ||||
/* | /* | ||||
* Set a namespace's name to a value. | * Set a namespace's name to a value. | ||||
* Pop <key> | * Pop <key> | ||||
* Pop <val> | |||||
* Read <val> | |||||
* Read <ns> | * Read <ns> | ||||
* Assign <val> to <ns[<key>]> | * Assign <val> to <ns[<key>]> | ||||
* Push <val> | |||||
*/ | */ | ||||
L2_OP_NAMESPACE_SET, | L2_OP_NAMESPACE_SET, | ||||
struct l2_bufio_writer writer; | 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_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_stack_frame(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_namespace_lookup(struct l2_generator *gen, char **ident); | |||||
#endif | #endif |
}; | }; | ||||
void l2_token_free(struct l2_token *tok); | 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); | 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 currtok; | ||||
struct l2_token toks[2]; | |||||
struct l2_token toks[4]; | |||||
int tokidx; | int tokidx; | ||||
int line; | int line; | ||||
int ch; | int ch; |
#include "lex.h" | #include "lex.h" | ||||
#include "gen/gen.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 | #endif |
#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 |
}; | }; | ||||
}; | }; | ||||
#define l2_vm_value_type(val) ((val).flags & 0x0f) | |||||
struct l2_vm_buffer { | struct l2_vm_buffer { | ||||
size_t len; | size_t len; | ||||
char data[]; | char data[]; | ||||
struct l2_vm { | struct l2_vm { | ||||
l2_word *ops; | l2_word *ops; | ||||
size_t opcount; | size_t opcount; | ||||
l2_word iptr; | |||||
struct l2_vm_value *values; | struct l2_vm_value *values; | ||||
size_t valuessize; | size_t valuessize; | ||||
struct l2_bitset valueset; | struct l2_bitset valueset; | ||||
l2_word stack[1024]; | l2_word stack[1024]; | ||||
unsigned char stackflags[1024]; | |||||
l2_word iptr; | |||||
l2_word sptr; | l2_word sptr; | ||||
l2_word nstack[1024]; | |||||
l2_word nsptr; | |||||
}; | }; | ||||
void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); | void l2_vm_init(struct l2_vm *vm, l2_word *ops, size_t opcount); |
l2_bufio_put_n(&gen->writer, &word, sizeof(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->atoms); | ||||
l2_strset_init(&gen->strings); | 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) { | void l2_gen_free(struct l2_generator *gen) { | ||||
l2_strset_free(&gen->strings); | 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) { | 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) { | void l2_gen_assignment(struct l2_generator *gen, char **ident) { | ||||
size_t atom_id = l2_strset_put(&gen->atoms, ident); | size_t atom_id = l2_strset_put(&gen->atoms, ident); | ||||
put(gen, L2_OP_PUSH); | put(gen, L2_OP_PUSH); | ||||
put(gen, atom_id); | 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) { | void l2_gen_number(struct l2_generator *gen, double num) { | ||||
uint64_t n; | uint64_t n; | ||||
memcpy(&n, &num, sizeof(num)); | memcpy(&n, &num, sizeof(num)); | ||||
put(gen, L2_OP_PUSH_2); | put(gen, L2_OP_PUSH_2); | ||||
put(gen, n >> 32); | |||||
put(gen, n); | put(gen, n); | ||||
put(gen, n >> 32); | |||||
put(gen, L2_OP_ALLOC_REAL_64); | 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); | |||||
} |
#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); | |||||
} |
#include <stdlib.h> | #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) { | const char *l2_token_kind_name(enum l2_token_kind kind) { | ||||
switch (kind) { | switch (kind) { | ||||
case L2_TOK_OPEN_PAREN: | case L2_TOK_OPEN_PAREN: | ||||
} | } | ||||
} | } | ||||
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) { | void l2_lexer_init(struct l2_lexer *lexer, struct l2_io_reader *r) { | ||||
tok->line = lexer->line; | tok->line = lexer->line; | ||||
tok->ch = lexer->ch; | tok->ch = lexer->ch; | ||||
int ch = read_ch(lexer); | |||||
int ch = peek_ch(lexer); | |||||
switch (ch) { | switch (ch) { | ||||
case '(': | case '(': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_OPEN_PAREN; | tok->kind = L2_TOK_OPEN_PAREN; | ||||
break; | break; | ||||
case ')': | case ')': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_CLOSE_PAREN; | tok->kind = L2_TOK_CLOSE_PAREN; | ||||
break; | break; | ||||
case '{': | case '{': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_OPEN_BRACE; | tok->kind = L2_TOK_OPEN_BRACE; | ||||
break; | break; | ||||
case '}': | case '}': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_CLOSE_BRACE; | tok->kind = L2_TOK_CLOSE_BRACE; | ||||
break; | break; | ||||
case '[': | case '[': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_OPEN_BRACKET; | tok->kind = L2_TOK_OPEN_BRACKET; | ||||
break; | break; | ||||
case ']': | case ']': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_CLOSE_BRACKET; | tok->kind = L2_TOK_CLOSE_BRACKET; | ||||
break; | break; | ||||
case ',': | case ',': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_COMMA; | tok->kind = L2_TOK_COMMA; | ||||
break; | break; | ||||
case '.': | case '.': | ||||
read_ch(lexer); | |||||
tok->kind = L2_TOK_PERIOD; | tok->kind = L2_TOK_PERIOD; | ||||
break; | break; | ||||
case ':': | case ':': | ||||
read_ch(lexer); | |||||
{ | { | ||||
ch = read_ch(lexer); | ch = read_ch(lexer); | ||||
switch (ch) { | switch (ch) { | ||||
break; | break; | ||||
case '"': | case '"': | ||||
read_ch(lexer); | |||||
read_string(lexer, tok); | read_string(lexer, tok); | ||||
break; | break; | ||||
default: | default: | ||||
read_ident(lexer, tok); | 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; | break; | ||||
} | } | ||||
} | } |
#include "gen/gen.h" | #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 *tok = l2_lexer_peek(lexer, 1); | ||||
struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | struct l2_token *tok2 = l2_lexer_peek(lexer, 2); | ||||
if (tok->kind == L2_TOK_IDENT && tok2->kind == L2_TOK_COLON_EQ) { | 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; | 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 | |||||
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; | 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; | 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); | l2_gen_stack_frame(gen); | ||||
while (1) { | while (1) { | ||||
break; | break; | ||||
} | } | ||||
if (parse_expression(lexer, gen) < 0) { | |||||
if (parse_expression(lexer, gen, err) < 0) { | |||||
l2_gen_halt(gen); | |||||
l2_gen_flush(gen); | |||||
return -1; | return -1; | ||||
} | } | ||||
} | } | ||||
l2_gen_halt(gen); | |||||
l2_gen_flush(gen); | |||||
return 0; | return 0; | ||||
} | } |
#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); | |||||
} | |||||
} |
return d; | 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) { | static void gc_mark(struct l2_vm *vm, l2_word id) { | ||||
printf("GC MARK %i\n", id); | printf("GC MARK %i\n", id); | ||||
struct l2_vm_value *val = &vm->values[id]; | struct l2_vm_value *val = &vm->values[id]; | ||||
val->flags |= L2_VAL_MARKED; | val->flags |= L2_VAL_MARKED; | ||||
int typ = val->flags & 0x0f; | |||||
int typ = l2_vm_value_type(*val); | |||||
if (typ == L2_VAL_TYPE_ARRAY) { | 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) { | } 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]); | |||||
} | } | ||||
} | } | ||||
struct l2_vm_value *val = &vm->values[id]; | struct l2_vm_value *val = &vm->values[id]; | ||||
l2_bitset_unset(&vm->valueset, 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) { | if (typ == L2_VAL_TYPE_ARRAY || typ == L2_VAL_TYPE_BUFFER || typ == L2_VAL_TYPE_NAMESPACE) { | ||||
free(val->data); | free(val->data); | ||||
// Don't need to do anything more; the next round of GC will free | // Don't need to do anything more; the next round of GC will free | ||||
vm->opcount = opcount; | vm->opcount = opcount; | ||||
vm->iptr = 0; | vm->iptr = 0; | ||||
vm->sptr = 0; | vm->sptr = 0; | ||||
vm->nsptr = 0; | |||||
vm->values = NULL; | vm->values = NULL; | ||||
vm->valuessize = 0; | vm->valuessize = 0; | ||||
} | } | ||||
size_t l2_vm_gc(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); | return gc_sweep(vm); | ||||
case L2_OP_PUSH: | case L2_OP_PUSH: | ||||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | vm->stack[vm->sptr] = vm->ops[vm->iptr]; | ||||
vm->stackflags[vm->sptr] = 0; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
vm->iptr += 1; | vm->iptr += 1; | ||||
break; | break; | ||||
case L2_OP_PUSH_2: | case L2_OP_PUSH_2: | ||||
vm->stack[vm->sptr] = vm->ops[vm->iptr]; | vm->stack[vm->sptr] = vm->ops[vm->iptr]; | ||||
vm->stack[vm->sptr + 1] = vm->ops[vm->iptr + 1]; | 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->sptr += 2; | ||||
vm->iptr += 2; | vm->iptr += 2; | ||||
break; | break; | ||||
case L2_OP_DUP: | case L2_OP_DUP: | ||||
vm->stack[vm->sptr] = vm->ops[vm->sptr - 1]; | vm->stack[vm->sptr] = vm->ops[vm->sptr - 1]; | ||||
vm->stackflags[vm->sptr] = 0; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
case L2_OP_ADD: | case L2_OP_ADD: | ||||
vm->stack[vm->sptr - 2] += vm->stack[vm->sptr - 1]; | vm->stack[vm->sptr - 2] += vm->stack[vm->sptr - 1]; | ||||
vm->stackflags[vm->sptr - 2] = 0; | |||||
vm->sptr -= 1; | vm->sptr -= 1; | ||||
break; | break; | ||||
case L2_OP_CALL: | case L2_OP_CALL: | ||||
word = vm->stack[vm->sptr - 1]; | word = vm->stack[vm->sptr - 1]; | ||||
vm->stack[vm->sptr - 1] = vm->iptr + 1; | vm->stack[vm->sptr - 1] = vm->iptr + 1; | ||||
vm->stackflags[vm->sptr - 1] = 0; | |||||
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: | |||||
{ | |||||
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: | case L2_OP_RET: | ||||
vm->nsptr -= 1; | |||||
vm->iptr = vm->stack[vm->sptr - 1]; | vm->iptr = vm->stack[vm->sptr - 1]; | ||||
vm->sptr -= 1; | vm->sptr -= 1; | ||||
vm->nsptr -= 1; | |||||
break; | break; | ||||
case L2_OP_ALLOC_INTEGER_32: | case L2_OP_ALLOC_INTEGER_32: | ||||
vm->values[word].flags = L2_VAL_TYPE_INTEGER; | vm->values[word].flags = L2_VAL_TYPE_INTEGER; | ||||
vm->values[word].integer = vm->stack[--vm->sptr]; | vm->values[word].integer = vm->stack[--vm->sptr]; | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
(uint64_t)vm->stack[vm->sptr - 2]); | (uint64_t)vm->stack[vm->sptr - 2]); | ||||
vm->sptr -= 2; | vm->sptr -= 2; | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
vm->values[word].flags = L2_VAL_TYPE_REAL; | vm->values[word].flags = L2_VAL_TYPE_REAL; | ||||
vm->values[word].real = u32_to_float(vm->stack[--vm->sptr]); | vm->values[word].real = u32_to_float(vm->stack[--vm->sptr]); | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
vm->values[word].real = u32s_to_double(vm->stack[vm->sptr - 1], vm->stack[vm->sptr - 2]); | vm->values[word].real = u32s_to_double(vm->stack[vm->sptr - 1], vm->stack[vm->sptr - 2]); | ||||
vm->sptr -= 2; | vm->sptr -= 2; | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
(unsigned char *)vm->values[word].data + sizeof(struct l2_vm_buffer), | (unsigned char *)vm->values[word].data + sizeof(struct l2_vm_buffer), | ||||
vm->ops + offset, length); | vm->ops + offset, length); | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
} | } | ||||
break; | break; | ||||
vm->values[word].data = calloc(1, sizeof(struct l2_vm_buffer) + length); | vm->values[word].data = calloc(1, sizeof(struct l2_vm_buffer) + length); | ||||
((struct l2_vm_buffer *)vm->values[word].data)->len = length; | ((struct l2_vm_buffer *)vm->values[word].data)->len = length; | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
} | } | ||||
break; | break; | ||||
vm->values[word].flags = L2_VAL_TYPE_ARRAY; | vm->values[word].flags = L2_VAL_TYPE_ARRAY; | ||||
vm->values[word].data = NULL; // Will be allocated on first insert | vm->values[word].data = NULL; // Will be allocated on first insert | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; | vm->values[word].flags = L2_VAL_TYPE_NAMESPACE; | ||||
vm->values[word].data = NULL; // Will be allocated on first insert | vm->values[word].data = NULL; // Will be allocated on first insert | ||||
vm->stack[vm->sptr] = word; | vm->stack[vm->sptr] = word; | ||||
vm->stackflags[vm->sptr] = 1; | |||||
vm->sptr += 1; | vm->sptr += 1; | ||||
break; | break; | ||||
files := src ../lib | files := src ../lib | ||||
includes := snow ../include/lang2 | includes := snow ../include/lang2 | ||||
defines := SNOW_ENABLED | defines := SNOW_ENABLED | ||||
sanitizers := address | |||||
cflags := -g -O3 | |||||
#sanitizers := address undefined | |||||
cflags := -g |
#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); | |||||
} | |||||
} |