summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--com.c55
-rw-r--r--dis.c48
-rw-r--r--val.c6
-rw-r--r--val.h4
-rw-r--r--vm.c25
-rw-r--r--vm.h4
6 files changed, 116 insertions, 26 deletions
diff --git a/com.c b/com.c
index 3d891f7..fef2dab 100644
--- a/com.c
+++ b/com.c
@@ -7,6 +7,19 @@
#include "read.h"
#include "state.h"
+static size_t placeholder(State *S, Chunk *ch) {
+ size_t old_ix = ch->bc.len;
+ chunk_wbc(S, ch, 0x00);
+ chunk_wbc(S, ch, 0x00);
+ return old_ix;
+}
+
+static void patch(State *S, Chunk *ch, size_t addr, uint16_t val) {
+ ch->bc.d[addr] = val & 0xff;
+ ch->bc.d[addr+1] = (val & 0xff00) >> 8;
+}
+
+
static void compile_node(State *S, Chunk *ch, AstNode a) {
switch (a.ty) {
case AST_IDENT:;
@@ -17,8 +30,7 @@ static void compile_node(State *S, Chunk *ch, AstNode a) {
else if (0 == strcmp(ident, "nil")) chunk_wbc(S, ch, OP_NIL);
else {
// global read
- size_t len = strlen(a.as.str);
- ObjString *o = objstring_copy(S, a.as.str, len);
+ ObjString *o = objstring_copy_cstr(S, a.as.str);
chunk_wbc(S, ch, OP_GETGLOBAL);
chunk_wbc(S, ch, chunk_wconst(S, ch, VAL_OBJ(o)));
}
@@ -28,8 +40,7 @@ static void compile_node(State *S, Chunk *ch, AstNode a) {
chunk_wbc(S, ch, chunk_wconst(S, ch, VAL_NUM(a.as.num)));
break;
case AST_STRING: {
- size_t len = strlen(a.as.str);
- ObjString *o = objstring_copy(S, a.as.str, len);
+ ObjString *o = objstring_copy_cstr(S, a.as.str);
chunk_wbc(S, ch, OP_LOADK);
chunk_wbc(S, ch, chunk_wconst(S, ch, VAL_OBJ(o)));
break;
@@ -37,29 +48,53 @@ static void compile_node(State *S, Chunk *ch, AstNode a) {
case AST_LIST: {
AstVec l = a.as.list;
#define CK(cond, msg) if (!(cond)) { puts(msg); exit(1); }
+ CK(l.len > 0, "can't handle empty list");
CK(l.vals[0].ty == AST_IDENT, "can only call ops");
- if (0 == strcmp(l.vals[0].as.str, "print")) {
+ char *name = l.vals[0].as.str;
+
+ if (0 == strcmp(name, "print")) {
CK(l.len == 2, "print requires exactly 1 argument");
compile_node(S, ch, l.vals[1]);
chunk_wbc(S, ch, OP_PRINT);
- } else if (0 == strcmp(l.vals[0].as.str, "set")) {
+ } else if (0 == strcmp(name, "set")) {
CK(l.len == 3, "set requires exactly 2 arguments");
AstNode ident = l.vals[1];
CK(ident.ty == AST_IDENT, "set's first argument must be identifier");
- size_t len = strlen(ident.as.str);
- ObjString *o = objstring_copy(S, ident.as.str, len);
-
+ ObjString *o = objstring_copy_cstr(S, ident.as.str);
compile_node(S, ch, l.vals[2]);
chunk_wbc(S, ch, OP_SETGLOBAL);
chunk_wbc(S, ch, chunk_wconst(S, ch, VAL_OBJ(o)));
- } else if (0 == strcmp(l.vals[0].as.str, "do")) {
+ } else if (0 == strcmp(name, "do")) {
for (int i = 1; i < l.len - 1; i++) {
compile_node(S, ch, l.vals[i]);
chunk_wbc(S, ch, OP_DROP);
}
compile_node(S, ch, l.vals[l.len - 1]);
+ } else if (0 == strcmp(name, "if")) {
+ CK(l.len == 4, "if requires exactly 3 arguments");
+ // (if cond if-true if-false)
+ // cond
+ // 0branch ->A
+ // if-true
+ // skip ->B
+ // A: if-false
+ // B:
+ compile_node(S, ch, l.vals[1]);
+ chunk_wbc(S, ch, OP_0BRANCH);
+ size_t ph1 = placeholder(S, ch);
+ compile_node(S, ch, l.vals[2]);
+ chunk_wbc(S, ch, OP_SKIP);
+ size_t ph2 = placeholder(S, ch);
+ size_t dest1 = ch->bc.len;
+ compile_node(S, ch, l.vals[3]);
+ size_t dest2 = ch->bc.len;
+
+ patch(S, ch, ph1, dest1 - ph1 - 2);
+ patch(S, ch, ph2, dest2 - ph2 - 2);
+
+
} else {
CK(l.len == 3, "can only compile binary ops");
diff --git a/dis.c b/dis.c
index c4492df..9033bff 100644
--- a/dis.c
+++ b/dis.c
@@ -3,41 +3,61 @@
#include <stdio.h>
#include <stdint.h>
-
+static void print_const(Chunk *ch, uint8_t ix) {
+ Val k = ch->consts.d[ix];
+ printf("%-4s : ", typename_str(k));
+ println_val(k);
+}
void disasm_chunk(Chunk *ch) {
for (size_t ip = 0; ip < ch->bc.len; ) {
uint8_t instr = ch->bc.d[ip];
printf("%04zd\t",ip);
ip ++;
-#define SIMPLE_INSTR(opcode, str) \
- case opcode: puts(str); break;
switch (instr) {
- SIMPLE_INSTR(OP_RET, "ret")
case OP_LOADK: {
uint8_t ix = ch->bc.d[ip++];
printf("loadk #%d\t; ",ix);
- Val k = ch->consts.d[ix];
- printf("%-4s : ",typename_str(k));
- println_val(k);
+ print_const(ch, ix);
break;
}
case OP_GETGLOBAL: {
uint8_t ix = ch->bc.d[ip++];
printf("getglobal #%d\t; ",ix);
- Val k = ch->consts.d[ix];
- printf("%-4s : ",typename_str(k));
- println_val(k);
+ print_const(ch, ix);
break;
}
case OP_SETGLOBAL: {
uint8_t ix = ch->bc.d[ip++];
printf("setglobal #%d\t; ",ix);
- Val k = ch->consts.d[ix];
- printf("%-4s : ",typename_str(k));
- println_val(k);
+ print_const(ch, ix);
+ break;
+ }
+ #define RSHORT() (uint16_t)( ch->bc.d[ip-2] | ch->bc.d[ip-1] << 8 )
+ case OP_SKIP: {
+ ip += 2;
+ uint16_t offset = RSHORT();
+ printf("skip +%5hu\t; -> %04zd\n", offset, ip + offset);
+ break;
+ }
+ case OP_0BRANCH: {
+ ip += 2;
+ uint16_t offset = RSHORT();
+ printf("0branch +%5hu\t; -> %04zd\n", offset, ip + offset);
break;
}
+ case OP_REDO: {
+ ip += 2;
+ uint16_t offset = RSHORT();
+ printf("0branch -%5hu\t; -> %04zd\n", offset, ip - offset);
+ break;
+ }
+ #undef RSHORT
+
+
+#define SIMPLE_INSTR(opcode, str) \
+ case opcode: puts(str); break;
+ SIMPLE_INSTR(OP_RET, "ret")
SIMPLE_INSTR(OP_PRINT, "print")
SIMPLE_INSTR(OP_DROP, "drop")
SIMPLE_INSTR(OP_ADD, "add")
@@ -47,10 +67,10 @@ void disasm_chunk(Chunk *ch) {
SIMPLE_INSTR(OP_NIL, "nil")
SIMPLE_INSTR(OP_TRUE, "true")
SIMPLE_INSTR(OP_FALSE, "false")
+#undef SIMPLE_INSTR
}
}
-#undef SIMPLE_INSTR
}
diff --git a/val.c b/val.c
index 6f1dd9e..19f96a0 100644
--- a/val.c
+++ b/val.c
@@ -75,6 +75,12 @@ void println_val(Val v) {
putchar('\n');
}
+bool is_truthy(Val v) {
+ if (IS_NIL(v) || ( IS_BOOL(v) && AS_BOOL(v) == false))
+ return false;
+ return true;
+}
+
const char *typename_str(Val v) {
switch(v.ty) {
diff --git a/val.h b/val.h
index db0a789..aa20b48 100644
--- a/val.h
+++ b/val.h
@@ -30,6 +30,8 @@ void print_val(Val v);
void println_val(Val v);
const char *typename_str(Val v);
+bool is_truthy(Val v);
+
typedef enum {
OTY_STRING,
@@ -55,7 +57,7 @@ ObjString *objstring_copy_cstr(State *s, char *str);
ObjString *objstring_take(State *S, char *src, size_t len);
-#define IS_NIL(x) (x.ty == NIL)
+#define IS_NIL(x) (x.ty == TY_NIL)
#define IS_NUM(x) (x.ty == TY_NUM)
#define IS_BOOL(x) (x.ty == TY_BOOL)
#define IS_OBJ(x) (x.ty == TY_OBJ)
diff --git a/vm.c b/vm.c
index 205e0fd..70aca3d 100644
--- a/vm.c
+++ b/vm.c
@@ -55,6 +55,7 @@ int runvm(State *S) {
disasm_chunk(ch);
#define RBYTE() (ch->bc.d[th->ip++])
+#define RSHORT() (th->ip += 2, (uint16_t)( ch->bc.d[th->ip-2] | ch->bc.d[th->ip-1] << 8 ))
#define PUSH(v) th->stack[th->sp++] = v;
#define POP() (th->stack[--th->sp])
@@ -85,6 +86,24 @@ int runvm(State *S) {
--th->sp;
break;
+ case OP_SKIP: {
+ uint16_t offset = RSHORT();
+ th->ip += offset;
+ break;
+ }
+ case OP_REDO: {
+ uint16_t offset = RSHORT();
+ th->ip -= offset;
+ break;
+ }
+ case OP_0BRANCH: {
+ uint16_t offset = RSHORT();
+ bool cond = is_truthy(POP());
+ if (!cond)
+ th->ip += offset;
+ break;
+ }
+
case OP_GETGLOBAL: {
uint8_t cix = RBYTE();
Val varname = ch->consts.d[cix];
@@ -134,12 +153,16 @@ int runvm(State *S) {
case OP_TRUE: PUSH(VAL_TRUE); break;
case OP_FALSE: PUSH(VAL_FALSE); break;
-
}
}
done:;
return status;
}
+#undef RBYTE
+#undef RSHORT
+#undef PUSH
+#undef POP
+#undef PEEK
diff --git a/vm.h b/vm.h
index e184426..81abea3 100644
--- a/vm.h
+++ b/vm.h
@@ -53,6 +53,10 @@ typedef enum {
OP_TRUE,
OP_FALSE,
OP_NIL,
+
+ OP_0BRANCH,
+ OP_SKIP,
+ OP_REDO,
} Op;
int runvm(State *S);