summaryrefslogtreecommitdiffstats
path: root/bytecode/oper.c
diff options
context:
space:
mode:
authorRose Hogenson <rhogenson@posteo.net>2023-02-11 09:41:54 -0800
committerRose Hogenson <rhogenson@posteo.net>2023-02-11 09:41:54 -0800
commit4c3b2fec322feb8afd23703866d81f1dda51b6ae (patch)
treecae4d14c66f639c13198a4bfafc9425cb8aa70bf /bytecode/oper.c
parentddb69212f03a82e980144594d809a072b50e7f19 (diff)
downloadsml-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/oper.c')
-rw-r--r--bytecode/oper.c93
1 files changed, 93 insertions, 0 deletions
diff --git a/bytecode/oper.c b/bytecode/oper.c
new file mode 100644
index 0000000..ed4a681
--- /dev/null
+++ b/bytecode/oper.c
@@ -0,0 +1,93 @@
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "encoding.h"
+#include "panic.h"
+#include "slice.h"
+#include "value.h"
+
+#include "oper.h"
+
+struct st {
+ size_t i;
+ value locals[8];
+};
+
+static value read_arg(struct st *s, struct arg arg)
+{
+ if (arg.constant) {
+ return arg.constant;
+ }
+ return s->locals[arg.local];
+}
+
+static void alloc(struct st *s, struct op oper)
+{
+ s->locals[oper.alloc.out] = from_pointer(calloc(to_int(read_arg(s, oper.alloc.size)), sizeof(value)));
+}
+
+static void call(struct st *s)
+{
+ s->i = to_int(s->locals[0]);
+}
+
+static void poke(struct st *s, struct op oper)
+{
+ value *p = to_pointer(s->locals[oper.poke.pointer]);
+ p[oper.poke.offset] = read_arg(s, oper.poke.value);
+}
+
+static void peek(struct st *s, struct op oper)
+{
+ value *p = to_pointer(read_arg(s, oper.peek.value));
+ s->locals[oper.peek.out] = p[oper.peek.offset];
+}
+
+static void shuf(struct st *s, struct op oper)
+{
+ s->locals[oper.shuf.out] = read_arg(s, oper.shuf.value);
+}
+
+static void oexit(struct st *s, struct op oper)
+{
+ exit(to_int(read_arg(s, oper.exit.value)));
+}
+
+static void op(struct st *s, struct op oper)
+{
+ switch (oper.code) {
+ case OALLOC:
+ alloc(s, oper);
+ break;
+ case OCALL:
+ call(s);
+ break;
+ case OPOKE:
+ poke(s, oper);
+ break;
+ case OPEEK:
+ peek(s, oper);
+ break;
+ case OSHUF:
+ shuf(s, oper);
+ break;
+ case OEXIT:
+ oexit(s, oper);
+ break;
+ default:
+ panicf("Invalid op: %x\n", oper.code);
+ }
+}
+
+void run(struct slice prog)
+{
+ struct st s = {
+ .i = 0,
+ .locals = { 0 },
+ };
+ while (true) {
+ struct result oper = parse(slice1(prog, s.i));
+ s.i += oper.n;
+ op(&s, oper.op);
+ }
+}