diff options
| author | Rose Hogenson <rhogenson@posteo.net> | 2023-02-11 09:41:54 -0800 |
|---|---|---|
| committer | Rose Hogenson <rhogenson@posteo.net> | 2023-02-11 09:41:54 -0800 |
| commit | 4c3b2fec322feb8afd23703866d81f1dda51b6ae (patch) | |
| tree | cae4d14c66f639c13198a4bfafc9425cb8aa70bf /bytecode/encoding.c | |
| parent | ddb69212f03a82e980144594d809a072b50e7f19 (diff) | |
| download | sml-4c3b2fec322feb8afd23703866d81f1dda51b6ae.tar.zst | |
Add a bytecode interpreter.
I wrote it in C because I actually like programming in C. Turns out
setting up a Makefile is really easy too.
Diffstat (limited to 'bytecode/encoding.c')
| -rw-r--r-- | bytecode/encoding.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/bytecode/encoding.c b/bytecode/encoding.c new file mode 100644 index 0000000..62c9762 --- /dev/null +++ b/bytecode/encoding.c @@ -0,0 +1,118 @@ +#include <stdint.h> +#include <stdlib.h> + +#include "panic.h" +#include "slice.h" +#include "value.h" + +#include "encoding.h" + +struct reader { + struct slice data; + int n; +}; + +static void advance(struct reader *r, int size) +{ + r->data = slice1(r->data, size); + r->n += size; +} + +static uint8_t parse_byte(struct reader *r) +{ + if (r->data.size < 1) { + panicf("read byte: no data!\n"); + } + uint8_t res = r->data.buf[0]; + advance(r, 1); + return res; +} + +static local parse_local(struct reader *r) +{ + if (r->data.size < 1) { + panicf("local: no data!\n"); + } + local res = r->data.buf[0]; + if (res > 7) { + panicf("Invalid local (out of range): %d\n", res); + } + advance(r, 1); + return res; +} + +static value parse_value(struct reader *r) +{ + if (r->data.size < 8) { + panicf("value: no data!\n"); + } + uint64_t res = *(uint64_t *) r->data.buf; + advance(r, 8); + return res; +} + +static struct arg parse_arg(struct reader *r, bool is_const) +{ + if (is_const) { + return (struct arg) { .constant = parse_value(r) }; + } + return (struct arg) { .local = parse_local(r) }; +} + +static uint8_t parse_offset(struct reader *r) +{ + if (r->data.size < 1) { + panicf("offset: no data!\n"); + } + uint8_t res = r->data.buf[0]; + advance(r, 1); + return res; +} + +struct result parse(struct slice data) +{ + struct reader r = { .data = data, .n = 0 }; + + uint8_t codeByte = parse_byte(&r); + enum opcode code = codeByte >> 2; + bool arg1_const = codeByte & 0x2; + // bool arg2_const = codeByte & 0x1; + + struct op out = { + .code = code, + }; + + switch (code) { + case OALLOC: + out.alloc.out = parse_local(&r); + out.alloc.size = parse_arg(&r, arg1_const); + break; + case OCALL: + // No arguments. + break; + case OPOKE: + out.poke.offset = parse_offset(&r); + out.poke.pointer = parse_local(&r); + out.poke.value = parse_arg(&r, arg1_const); + break; + case OPEEK: + out.peek.out = parse_local(&r); + out.peek.offset = parse_offset(&r); + out.peek.value = parse_arg(&r, arg1_const); + break; + case OSHUF: + out.shuf.out = parse_local(&r); + out.shuf.value = parse_arg(&r, arg1_const); + break; + case OEXIT: + out.exit.value = parse_arg(&r, arg1_const); + break; + default: + panicf("Invalid code %d\n", code); + } + + return (struct result) { + .op = out, + .n = r.n, + }; +} |
