diff --git a/src/jitters.c b/src/jitters.c index 12a5e9e..ef84e73 100644 --- a/src/jitters.c +++ b/src/jitters.c @@ -13,6 +13,7 @@ #include "jit/jitter.h" #include "parse/parse-jitters.h" #include "print/printer.h" +#include "vm/vm.h" const char *argp_program_version = PACKAGE_STRING; const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; @@ -23,6 +24,7 @@ static char doc[] = static struct argp_option options[] = { {"evaluate", 'e', 0, 0, "Evaluate input by walking the tree", 0 }, + {"virtual", 'v', 0, 0, "Compile input bytecode and interpret", 0 }, {"compile", 'c', 0, 0, "Compile input to assembly", 0 }, {"jit", 'j', 0, 0, "JIT-compile input and evaluate", 0 }, {"print", 'p', 0, 0, "Print parsed expression", 0 }, @@ -36,6 +38,7 @@ struct arguments bool debug; // Whether to activate Bison using debug trace bool compile; // Whether to compile the input bool jit; // Whether to JIT the input + bool virtual; // Whether to run on VM bool evaluate; // Whether to evaluate the input bool print; // Whether to print the input const char *output_file; // Where to output @@ -65,6 +68,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 'o': arguments->output_file = arg; break; + case 'v': + arguments->virtual = true; + break; case ARGP_KEY_ARG: argp_usage(state); break; @@ -101,6 +107,8 @@ int main(int argc, char *argv[]) fprintf(output, "%d\n", jit_eval_ast(ast)); if (arguments.evaluate) fprintf(output, "%d\n", evaluate_ast(ast)); + if (arguments.virtual) + fprintf(output, "%d\n", bytecompile_eval_ast(ast)); if (arguments.print) print_ast(ast, output); } diff --git a/src/local.am b/src/local.am index 6248cf8..e5bed21 100644 --- a/src/local.am +++ b/src/local.am @@ -20,3 +20,4 @@ include %D%/eval/local.am include %D%/jit/local.am include %D%/parse/local.am include %D%/print/local.am +include %D%/vm/local.am diff --git a/src/vm/bytecode.c b/src/vm/bytecode.c new file mode 100644 index 0000000..1a1c121 --- /dev/null +++ b/src/vm/bytecode.c @@ -0,0 +1,102 @@ +#include "bytecode.h" + +#include +#include +#include + +static void *xmalloc(size_t size) +{ + void *ret = malloc(size); + + if (ret == NULL) + err(1, NULL); + + return ret; +} + +static void *xrealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + + if (ret == NULL) + { + free(ptr); + err(1, NULL); + } + + return ret; +} + +struct stack *init_stack(void) +{ + struct stack *ret = xmalloc(sizeof(*ret)); + + size_t init_cap = sizeof(struct bytecode_num); + + ret->buf = xmalloc(init_cap); + ret->capacity = init_cap; + ret->size = 0; + + return ret; +} + +void destroy_stack(struct stack *stack) +{ + free(stack->buf); + free(stack); +} + +static void resize_stack(struct stack *stack) +{ + stack->buf = xrealloc(stack->buf, stack->capacity * 2); + + void *old_op = get_op(stack); + stack->capacity *= 2; + void *new_op = get_op(stack); + + memmove(new_op, old_op, stack->size); +} + +static void *get_end_op(struct stack *stack) +{ + char *tmp = stack->buf; + return tmp + stack->capacity - stack->size - sizeof(struct bytecode); +} + +static void *get_end_num(struct stack *stack) +{ + char *tmp = stack->buf; + return tmp + stack->capacity - stack->size - sizeof(struct bytecode_num); +} + +void push_op(struct stack *stack, enum bytecode_kind kind) +{ + if (stack->capacity - stack->size < sizeof(struct bytecode)) + resize_stack(stack); + + struct bytecode *op = get_end_op(stack); + + op->kind = kind; + + stack->size += sizeof(*op); +} + +void push_num(struct stack *stack, int val) +{ + if (stack->capacity - stack->size < sizeof(struct bytecode_num)) + resize_stack(stack); + + struct bytecode_num *num = get_end_num(stack); + + num->header.kind = NUM_CODE; + num->num = val; + + stack->size += sizeof(*num); +} + +struct bytecode *get_op(const struct stack *stack) +{ + char *tmp = stack->buf; + void *op = tmp + stack->capacity - stack->size; + return op; +} diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h new file mode 100644 index 0000000..79614b1 --- /dev/null +++ b/src/vm/bytecode.h @@ -0,0 +1,37 @@ +#ifndef BYTECODE_H +#define BYTECODE_H + +#include + +struct bytecode +{ + enum bytecode_kind + { + NUM_CODE, + PLUS_CODE, + MINUS_CODE, + TIMES_CODE, + DIVIDE_CODE, + } kind; +}; + +struct bytecode_num +{ + struct bytecode header; + int num; +}; + +struct stack +{ + void *buf; + size_t size; + size_t capacity; +}; + +struct stack *init_stack(void); +void destroy_stack(struct stack *stack); +void push_op(struct stack *stack, enum bytecode_kind kind); +void push_num(struct stack *stack, int val); +struct bytecode *get_op(const struct stack *stack); + +#endif /* !BYTECODE_H */ diff --git a/src/vm/local.am b/src/vm/local.am new file mode 100644 index 0000000..5f6f5ae --- /dev/null +++ b/src/vm/local.am @@ -0,0 +1,14 @@ +jitters_LDADD += \ + libvm.a \ + $(NULL) + +noinst_LIBRARIES += \ + libvm.a \ + $(NULL) + +libvm_a_SOURCES = \ + %D%/bytecode.c \ + %D%/bytecode.h \ + %D%/vm.c \ + %D%/vm.h \ + $(NULL) diff --git a/src/vm/vm.c b/src/vm/vm.c new file mode 100644 index 0000000..4ea50de --- /dev/null +++ b/src/vm/vm.c @@ -0,0 +1,92 @@ +#include "vm.h" + +#include "bytecode.h" + +static enum bytecode_kind binop_to_code(enum binop_kind op) +{ + switch (op) + { + case PLUS: + return PLUS_CODE; + case MINUS: + return MINUS_CODE; + case TIMES: + return TIMES_CODE; + case DIVIDE: + return DIVIDE_CODE; + } + return PLUS_CODE; // To avoid errors... +} + +static void bytecompile(struct stack *stack, const struct ast_node *ast) +{ + switch (ast->kind) + { + case NUM: + push_num(stack, ast->val.num); + break; + case UNOP: + if (ast->val.un_op.op == IDENTITY) + bytecompile(stack, ast->val.un_op.rhs); + else + { + bytecompile(stack, ast->val.un_op.rhs); + push_num(stack, 0); + push_op(stack, MINUS_CODE); + } + break; + case BINOP: + bytecompile(stack, ast->val.bin_op.rhs); + bytecompile(stack, ast->val.bin_op.lhs); + push_op(stack, binop_to_code(ast->val.bin_op.op)); + break; + } +} + +static int get_num(const void *op) +{ + const struct bytecode_num *num = op; + return num->num; +} + + +static int interpret(struct stack *stack) +{ + const struct bytecode *op = get_op(stack); + switch (op->kind) + { + case NUM_CODE: + stack->size -= sizeof(struct bytecode_num); + return get_num(op); + default: + break; + } + + stack->size -= sizeof(struct bytecode); + int lhs = interpret(stack); + int rhs = interpret(stack); + switch (op->kind) + { + case PLUS_CODE: + return lhs + rhs; + case MINUS_CODE: + return lhs - rhs; + case TIMES_CODE: + return lhs * rhs; + case DIVIDE_CODE: + return lhs / rhs; + default: + return 0; // Should not happen + } +} + +int bytecompile_eval_ast(const struct ast_node *ast) +{ + struct stack *stack = init_stack(); + + bytecompile(stack, ast); + int ret = interpret(stack); + destroy_stack(stack); + + return ret; +} diff --git a/src/vm/vm.h b/src/vm/vm.h new file mode 100644 index 0000000..97bdd9b --- /dev/null +++ b/src/vm/vm.h @@ -0,0 +1,8 @@ +#ifndef VM_H +#define VM_H + +#include "ast/ast.h" + +int bytecompile_eval_ast(const struct ast_node *ast); + +#endif /* !VM_H */