From f331192861d8ba02af7fd47f2e0c6d6db7515007 Mon Sep 17 00:00:00 2001 From: ubq323 Date: Thu, 20 Jun 2024 23:26:18 +0100 Subject: add (if cond if-true if-false) builtin --- com.c | 55 +++++++++++++++++++++++++++++++++++++++++++++---------- dis.c | 48 ++++++++++++++++++++++++++++++++++-------------- val.c | 6 ++++++ val.h | 4 +++- vm.c | 25 ++++++++++++++++++++++++- vm.h | 4 ++++ 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 #include - +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); -- cgit v1.2.3