├── tests ├── hello.feeny ├── hello2.feeny ├── toplevel.feeny ├── hello9.feeny ├── hello3.feeny ├── hello4.feeny ├── hello8.feeny ├── hello5.feeny ├── hello7.feeny ├── hello6.feeny ├── hanoi.feeny ├── inheritance.feeny ├── fibonacci.feeny ├── bsearch.feeny ├── cplx.feeny ├── lists.feeny ├── vector.feeny └── sudoku.feeny ├── scripts ├── zip-feeny.sh └── make-feeny.sh ├── docs ├── Assignment1.pdf ├── Assignment2.pdf ├── Assignment3.pdf ├── Assignment4.pdf ├── Assignment5.pdf ├── Assignment6.pdf └── Assignment7.pdf ├── stanza.proj ├── src ├── utils.h ├── cfeeny.c ├── vm.h ├── main.stanza ├── bytecode.h ├── ast.h ├── utils.c ├── abscode.stanza ├── parser.stanza ├── compile.stanza ├── bytecode.stanza ├── bytecode.c ├── eval.stanza ├── ir.stanza ├── vm.stanza ├── main.c ├── ast.c └── vm.c └── readme.md /tests/hello.feeny: -------------------------------------------------------------------------------- 1 | defn main () : 2 | printf("Hello World\n") 3 | 4 | main() -------------------------------------------------------------------------------- /scripts/zip-feeny.sh: -------------------------------------------------------------------------------- 1 | zip -r feeny.zip readme.md scripts src stanza.proj tests docs 2 | -------------------------------------------------------------------------------- /docs/Assignment1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment1.pdf -------------------------------------------------------------------------------- /docs/Assignment2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment2.pdf -------------------------------------------------------------------------------- /docs/Assignment3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment3.pdf -------------------------------------------------------------------------------- /docs/Assignment4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment4.pdf -------------------------------------------------------------------------------- /docs/Assignment5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment5.pdf -------------------------------------------------------------------------------- /docs/Assignment6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment6.pdf -------------------------------------------------------------------------------- /docs/Assignment7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CuppoJava/Feeny/HEAD/docs/Assignment7.pdf -------------------------------------------------------------------------------- /tests/hello2.feeny: -------------------------------------------------------------------------------- 1 | defn main () : 2 | printf("Hello ") 3 | printf("World\n") 4 | 5 | main() -------------------------------------------------------------------------------- /tests/toplevel.feeny: -------------------------------------------------------------------------------- 1 | var x = 10 2 | 3 | if x > 0 : 4 | var y = 20 5 | x = x + y 6 | 7 | printf("x is ~\n", x) -------------------------------------------------------------------------------- /tests/hello9.feeny: -------------------------------------------------------------------------------- 1 | defn main () : 2 | var i = 0 3 | while i : 4 | printf("Do one iteration\n") 5 | i = null 6 | 7 | main() -------------------------------------------------------------------------------- /tests/hello3.feeny: -------------------------------------------------------------------------------- 1 | defn hello () : 2 | printf("Hello ") 3 | 4 | defn world () : 5 | printf("World\n") 6 | 7 | defn main () : 8 | hello() 9 | world() 10 | 11 | main() -------------------------------------------------------------------------------- /scripts/make-feeny.sh: -------------------------------------------------------------------------------- 1 | mkdir -p bin 2 | mkdir -p build 3 | stanza build feeny 4 | gcc -O3 src/cfeeny.c src/utils.c src/bytecode.c src/vm.c src/ast.c -o bin/cfeeny -Wno-int-to-void-pointer-cast 5 | -------------------------------------------------------------------------------- /tests/hello4.feeny: -------------------------------------------------------------------------------- 1 | defn hello (i) : 2 | printf("Hello ~ ", i) 3 | 4 | defn world (j, k) : 5 | printf("World ~ ~\n", j, k) 6 | 7 | defn main () : 8 | hello(3) 9 | world(4, 5) 10 | 11 | main() -------------------------------------------------------------------------------- /tests/hello8.feeny: -------------------------------------------------------------------------------- 1 | defn main () : 2 | if null : 3 | printf("Hello") 4 | else : 5 | printf("World") 6 | 7 | if 0 : 8 | printf("Hello\n") 9 | else : 10 | printf("World\n") 11 | 12 | main() -------------------------------------------------------------------------------- /tests/hello5.feeny: -------------------------------------------------------------------------------- 1 | defn three () : 2 | 3 3 | 4 | defn hello (i) : 5 | printf("Hello ~ ", i) 6 | 7 | defn world (j, k) : 8 | printf("World ~ ~\n", j, k) 9 | 10 | defn main () : 11 | hello(three()) 12 | world(4, three()) 13 | 14 | main() -------------------------------------------------------------------------------- /tests/hello7.feeny: -------------------------------------------------------------------------------- 1 | defn three () : 2 | 3 3 | 4 | defn hello (i) : 5 | printf("Hello ~ ", i) 6 | 7 | defn world (j, k) : 8 | printf("World ~ ~\n", j, k) 9 | 10 | defn main () : 11 | var i = three() 12 | hello(i) 13 | i = 10 14 | world(4, i) 15 | 16 | main() -------------------------------------------------------------------------------- /tests/hello6.feeny: -------------------------------------------------------------------------------- 1 | defn three () : 2 | 3 3 | 4 | defn hello (i) : 5 | printf("Hello ~ ", i) 6 | 7 | defn world (j, k) : 8 | printf("World ~ ~\n", j, k) 9 | 10 | defn main () : 11 | var i = three() 12 | hello(i) 13 | var j = 10 14 | world(4, j) 15 | 16 | main() -------------------------------------------------------------------------------- /stanza.proj: -------------------------------------------------------------------------------- 1 | package feeny/vm defined-in "src/vm.stanza" 2 | package feeny/bytecode defined-in "src/bytecode.stanza" 3 | package feeny/parser defined-in "src/parser.stanza" 4 | package feeny/eval defined-in "src/eval.stanza" 5 | package feeny/ir defined-in "src/ir.stanza" 6 | package feeny defined-in "src/main.stanza" 7 | package feeny/compile defined-in "src/compile.stanza" 8 | package feeny/abc defined-in "src/abscode.stanza" 9 | 10 | build feeny : 11 | inputs: feeny 12 | pkg: "build" 13 | o: "bin/feeny" 14 | optimize -------------------------------------------------------------------------------- /tests/hanoi.feeny: -------------------------------------------------------------------------------- 1 | defn towerOfHanoi (n, src, dst, buff) : 2 | if n == 1: 3 | move(src, dst, 1) 4 | else: 5 | towerOfHanoi(n - 1, src, buff, dst) 6 | move(src, dst, n) 7 | towerOfHanoi(n - 1, buff, dst, src) 8 | 9 | defn move (src, dst, n): 10 | printf("Move plate ~ from ", n) 11 | stackPrint(src) 12 | printf(" stack to ") 13 | stackPrint(dst) 14 | printf(" stack\n") 15 | 16 | defn stackPrint (n) : 17 | if n == 1: 18 | printf("A") 19 | else if n == 2: 20 | printf("B") 21 | else: 22 | printf("C") 23 | 24 | defn main () : 25 | towerOfHanoi(6, 1,2,3) 26 | 27 | main() -------------------------------------------------------------------------------- /tests/inheritance.feeny: -------------------------------------------------------------------------------- 1 | defn base () : 2 | object : 3 | method do-op (x, y) : 4 | printf("Do a binary operation on ~ and ~.\n", x, y) 5 | printf("Result = ~.\n", this.op(x, y)) 6 | 7 | defn adder () : 8 | object(base()) : 9 | method op (x, y) : 10 | x + y 11 | 12 | defn multiplier () : 13 | object(base()) : 14 | method op (x, y) : 15 | x * y 16 | 17 | var a = adder() 18 | var m = multiplier() 19 | a.do-op(11, 7) 20 | m.do-op(11, 7) 21 | 22 | 23 | 24 | ;============================================================ 25 | ;====================== OUTPUT ============================== 26 | ;============================================================ 27 | ; 28 | ;Do a binary operation on 11 and 7. 29 | ;Result = 18. 30 | ;Do a binary operation on 11 and 7. 31 | ;Result = 77. -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | int max (int a, int b); 5 | int min (int a, int b); 6 | void print_string (char* str); 7 | 8 | //============================================================ 9 | //===================== VECTORS ============================== 10 | //============================================================ 11 | 12 | typedef struct { 13 | int size; 14 | int capacity; 15 | void** array; 16 | } Vector; 17 | 18 | Vector* make_vector (); 19 | void vector_add (Vector* v, void* val); 20 | void* vector_pop (Vector* v); 21 | void* vector_peek (Vector* v); 22 | void vector_clear (Vector* v); 23 | void vector_free (Vector* v); 24 | void* vector_get (Vector* v, int i); 25 | void vector_set (Vector* v, int i, void* x); 26 | void vector_set_length (Vector* v, int len, void* x); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /tests/fibonacci.feeny: -------------------------------------------------------------------------------- 1 | defn fib (n) : 2 | if n == 0 : 3 | 1 4 | else if n == 1 : 5 | 1 6 | else : 7 | var a = 1 8 | var b = 1 9 | while n >= 2 : 10 | var c = a + b 11 | a = b 12 | b = c 13 | n = n - 1 14 | b 15 | 16 | defn main () : 17 | var i = 0 18 | while i < 20 : 19 | printf("Fib(~) = ~\n", i, fib(i)) 20 | i = i + 1 21 | 22 | main() 23 | 24 | 25 | 26 | ;============================================================ 27 | ;====================== OUTPUT ============================== 28 | ;============================================================ 29 | ; 30 | ;Fib(0) = 1 31 | ;Fib(1) = 1 32 | ;Fib(2) = 2 33 | ;Fib(3) = 3 34 | ;Fib(4) = 5 35 | ;Fib(5) = 8 36 | ;Fib(6) = 13 37 | ;Fib(7) = 21 38 | ;Fib(8) = 34 39 | ;Fib(9) = 55 40 | ;Fib(10) = 89 41 | ;Fib(11) = 144 42 | ;Fib(12) = 233 43 | ;Fib(13) = 377 44 | ;Fib(14) = 610 45 | ;Fib(15) = 987 46 | ;Fib(16) = 1597 47 | ;Fib(17) = 2584 48 | ;Fib(18) = 4181 49 | ;Fib(19) = 6765 -------------------------------------------------------------------------------- /src/cfeeny.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | #include "bytecode.h" 6 | #include "vm.h" 7 | #include "ast.h" 8 | 9 | void interpret_bc (char* filename) { 10 | Program* p = load_bytecode(filename); 11 | print_prog(p); 12 | printf("\n\n"); 13 | initvm(link_program(p)); 14 | runvm(); 15 | } 16 | 17 | void interpret_ast (char* filename) { 18 | ScopeStmt* s = read_ast(filename); 19 | print_scopestmt(s); 20 | printf("\n\n"); 21 | interpret(s); 22 | } 23 | 24 | //Usage: 25 | //cfeeny -ast bsearch.ast 26 | //cfeeny -bc bsearch.bc 27 | int main (int argc, char** argvs) { 28 | //Check number of arguments 29 | if(argc != 3){ 30 | printf("Expected 2 arguments to commandline.\n"); 31 | exit(-1); 32 | } 33 | if(strcmp(argvs[1], "-ast") == 0) 34 | interpret_ast(argvs[2]); 35 | else if(strcmp(argvs[1], "-bc") == 0) 36 | interpret_bc(argvs[2]); 37 | else{ 38 | printf("Unrecognized flag: %s\n", argvs[1]); 39 | exit(-1); 40 | } 41 | return 0; 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /tests/bsearch.feeny: -------------------------------------------------------------------------------- 1 | defn bshelper (xs, i, n, v) : 2 | if n == 1 : 3 | if xs[i] == v : i 4 | else : -1 5 | else : 6 | var n1 = n / 2 7 | var a = xs[i + n1 - 1] 8 | if a < v : bshelper(xs, i + n1, n - n1, v) 9 | else : bshelper(xs, i, n1, v) 10 | 11 | defn bs (xs, v) : 12 | if xs.length() == 0 : -1 13 | else : bshelper(xs, 0, xs.length(), v) 14 | 15 | 16 | var a = array(10, 0) 17 | a[0] = 4 18 | a[1] = 6 19 | a[2] = 9 20 | a[3] = 13 21 | a[4] = 15 22 | a[5] = 17 23 | a[6] = 18 24 | a[7] = 24 25 | a[8] = 29 26 | a[9] = 35 27 | 28 | defn find (v) : 29 | printf("The index of ~ in a is ~.\n", v, bs(a, v)) 30 | 31 | find(13) 32 | find(14) 33 | find(15) 34 | find(29) 35 | find(30) 36 | 37 | 38 | ;============================================================ 39 | ;====================== OUTPUT ============================== 40 | ;============================================================ 41 | ;The index of 13 in a is 3. 42 | ;The index of 14 in a is -1. 43 | ;The index of 15 in a is 4. 44 | ;The index of 29 in a is 8. 45 | ;The index of 30 in a is -1. 46 | -------------------------------------------------------------------------------- /tests/cplx.feeny: -------------------------------------------------------------------------------- 1 | defn cplx (r, i) : 2 | object : 3 | var real = r 4 | var imag = i 5 | method add (c) : 6 | cplx(this.real + c.real, 7 | this.imag + c.imag) 8 | method sub (c) : 9 | cplx(this.real - c.real, 10 | this.imag - c.imag) 11 | method mul (c) : 12 | cplx(this.real * c.real - this.imag * c.imag, 13 | this.real * c.imag + this.imag * c.real) 14 | method div (c) : 15 | var d = c.real * c.real + c.imag * c.imag 16 | cplx((this.real * c.real + this.imag * c.imag) / d, 17 | (this.imag * c.real - this.real * c.imag) / d) 18 | method print () : 19 | if this.imag < 0 : 20 | printf("~ - ~i", this.real, 0 - this.imag) 21 | else if this.imag == 0 : 22 | printf("~", this.real) 23 | else : 24 | printf("~ + ~i", this.real, this.imag) 25 | 26 | defn main () : 27 | var a = cplx(2, 5) 28 | printf("a = ") 29 | a.print() 30 | printf("\n") 31 | 32 | var b = cplx(-4, 2) 33 | printf("b = ") 34 | b.print() 35 | printf("\n") 36 | 37 | printf("a + b = ") 38 | (a + b).print() 39 | printf("\n") 40 | 41 | printf("a - b = ") 42 | (a - b).print() 43 | printf("\n") 44 | 45 | printf("a * b = ") 46 | (a * b).print() 47 | printf("\n") 48 | 49 | printf("a / b = ") 50 | (a / b).print() 51 | printf("\n") 52 | 53 | main() 54 | 55 | 56 | 57 | ;============================================================ 58 | ;====================== OUTPUT ============================== 59 | ;============================================================ 60 | ; 61 | ;a = 2 + 5i 62 | ;b = -4 + 2i 63 | ;a + b = -2 + 7i 64 | ;a - b = 6 + 3i 65 | ;a * b = -18 - 16i 66 | ;a / b = 0 - 1i -------------------------------------------------------------------------------- /tests/lists.feeny: -------------------------------------------------------------------------------- 1 | var nil = object : 2 | method nil? () : 3 | 1 4 | method print () : 5 | printf("()") 6 | method length () : 7 | 0 8 | method append (b) : 9 | b 10 | method reverse () : 11 | this 12 | 13 | defn cons (a, b) : 14 | object : 15 | var head = a 16 | var tail = b 17 | method nil? () : 18 | 0 19 | 20 | method print () : 21 | printf("(~", this.head) 22 | var rest = this.tail 23 | while rest.nil?() == 0 : 24 | printf(" ~", rest.head) 25 | rest = rest.tail 26 | printf(")") 27 | 28 | method length () : 29 | 1 + this.tail.length() 30 | 31 | method append (b) : 32 | cons(this.head, this.tail.append(b)) 33 | 34 | method reverse () : 35 | this.tail.reverse().append(cons(this.head, nil)) 36 | 37 | defn nl () : 38 | printf("\n") 39 | 40 | defn main () : 41 | printf("List A\n") 42 | var a = cons(1, cons(2, cons(3, cons(4, nil)))) 43 | a.print() 44 | nl() 45 | 46 | printf("List B\n") 47 | var b = cons(10, cons(9, cons(8, cons(7, cons(6, cons(5, nil)))))) 48 | b.print() 49 | nl() 50 | 51 | printf("a.length() = ~\n", a.length()) 52 | printf("b.length() = ~\n", b.length()) 53 | 54 | var c = a.append(b) 55 | printf("a.append(b) = ") 56 | c.print() 57 | nl() 58 | 59 | var ar = a.reverse() 60 | var br = b.reverse() 61 | printf("a reversed = ") 62 | ar.print() 63 | nl() 64 | printf("b reversed = ") 65 | br.print() 66 | nl() 67 | 68 | main() 69 | 70 | 71 | 72 | 73 | ;============================================================ 74 | ;====================== Output ============================== 75 | ;============================================================ 76 | ; 77 | ;List A 78 | ;(1 2 3 4) 79 | ;List B 80 | ;(10 9 8 7 6 5) 81 | ;a.length() = 4 82 | ;b.length() = 6 83 | ;a.append(b) = (1 2 3 4 10 9 8 7 6 5) 84 | ;a reversed = (4 3 2 1) 85 | ;b reversed = (5 6 7 8 9 10) -------------------------------------------------------------------------------- /src/vm.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_H 2 | #define VM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "utils.h" 8 | #include "bytecode.h" 9 | 10 | typedef enum { 11 | INT_INS, //0 12 | NULL_INS, //1 13 | PRINTF_INS, //2 14 | ARRAY_INS, //3 15 | OBJECT_INS, //4 16 | SLOT_INS, //5 17 | SET_SLOT_INS, //6 18 | CALL_SLOT_INS, //7 19 | CALL_INS, //8 20 | SET_LOCAL_INS, //9 21 | GET_LOCAL_INS, //a 22 | SET_GLOBAL_INS, //b 23 | GET_GLOBAL_INS, //c 24 | BRANCH_INS, //d 25 | GOTO_INS, //e 26 | RETURN_INS, //f 27 | DROP_INS, //10 28 | FRAME_INS //11 29 | } OpTag; 30 | 31 | //Int : 32 | // tag: char 33 | // value: int 34 | //Printf : 35 | // tag: char 36 | // arity: char 37 | // format: char* 38 | //Object : 39 | // tag: char 40 | // arity: char 41 | // class: short 42 | //Slot : 43 | // tag: char 44 | // name: char* 45 | //SetSlot : 46 | // tag: char 47 | // name: char* 48 | //CallSlot : 49 | // tag: char 50 | // arity: char 51 | // name: char* 52 | //Call : 53 | // tag: char 54 | // arity: char 55 | // code: void* 56 | //SetLocal : 57 | // tag: char 58 | // idx: short 59 | //GetLocal : 60 | // tag: char 61 | // idx: short 62 | //SetGlobal : 63 | // tag: char 64 | // idx: short 65 | //GetGlobal : 66 | // tag: char 67 | // idx: short 68 | //Branch : 69 | // tag: char 70 | // code: void* 71 | //Goto : 72 | // tag: char 73 | // code: void* 74 | //Frame : 75 | // tag: char 76 | // nargs: char 77 | // nlocals: short 78 | 79 | typedef enum { 80 | VAR_SLOT, 81 | CODE_SLOT 82 | } SlotTag; 83 | 84 | typedef struct { 85 | SlotTag tag; 86 | char* name; 87 | union { 88 | int idx; 89 | void* code; 90 | }; 91 | } LSlot; 92 | 93 | typedef struct { 94 | int nvars; 95 | int nslots; 96 | LSlot* slots; 97 | } LClass; 98 | 99 | char* link_program (Program* prog); 100 | void initvm (char* entry); 101 | void runvm (); 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /tests/vector.feeny: -------------------------------------------------------------------------------- 1 | defn copy-array (dst, src, i, n) : 2 | var j = 0 3 | while j < n : 4 | dst[i + j] = src[i + j] 5 | j = j + 1 6 | dst 7 | 8 | defn max (a, b) : 9 | if a < b : b 10 | else : a 11 | 12 | defn ensure-capacity (v, c) : 13 | var n = v.array.length() 14 | if n < c : 15 | var sz = max(c, n * 2) 16 | var a = array(sz, 0) 17 | copy-array(a, v.array, 0, v.size) 18 | v.array = a 19 | 20 | defn vector () : 21 | object : 22 | var array = array(4, 0) 23 | var size = 0 24 | method add (x) : 25 | ensure-capacity(this, this.size + 1) 26 | this.array[this.size] = x 27 | this.size = this.size + 1 28 | method get (i) : 29 | this.array[i] 30 | method set (i, x) : 31 | if i == this.size : 32 | this.add(x) 33 | else : 34 | this.array[i] = x 35 | method length () : 36 | this.size 37 | method print () : 38 | if this.size == 0 : 39 | printf("[]") 40 | else : 41 | printf("[~", this.array[0]) 42 | var i = 1 43 | while i < this.size : 44 | printf(", ~", this.array[i]) 45 | i = i + 1 46 | printf("]") 47 | 48 | defn main () : 49 | printf("Create empty vector.\n") 50 | var v = vector() 51 | v.print() 52 | printf("\n") 53 | 54 | printf("Add some elements.\n") 55 | v.add(2) 56 | v.add(10) 57 | v.add(22) 58 | v.add(17) 59 | v.add(23) 60 | v.add(2) 61 | v.add(7) 62 | v.print() 63 | printf("\n") 64 | 65 | printf("Retrieving some elements.\n") 66 | printf("v[~] = ~.\n", 2, v[2]) 67 | printf("v[~] = ~.\n", 4, v[4]) 68 | printf("v[~] = ~.\n", 1, v[1]) 69 | 70 | printf("Setting first 3 elements to 0.\n") 71 | v[0] = 0 72 | v[1] = 0 73 | v[2] = 0 74 | v.print() 75 | printf("\n") 76 | 77 | main() 78 | 79 | 80 | ;============================================================ 81 | ;====================== Output ============================== 82 | ;============================================================ 83 | ; 84 | ;Create empty vector. 85 | ;[] 86 | ;Add some elements. 87 | ;[2, 10, 22, 17, 23, 2, 7] 88 | ;Retrieving some elements. 89 | ;v[2] = 22. 90 | ;v[4] = 23. 91 | ;v[1] = 10. 92 | ;Setting first 3 elements to 0. 93 | ;[0, 0, 0, 17, 23, 2, 7] -------------------------------------------------------------------------------- /src/main.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny : 2 | import core 3 | import collections 4 | import parser 5 | import feeny/ir 6 | import feeny/parser 7 | import feeny/eval 8 | import feeny/compile 9 | import feeny/vm 10 | import feeny/bytecode 11 | 12 | defn parse-commandline () : 13 | val table = HashTable() 14 | val args = to-seq(command-line-arguments()) 15 | next(args) 16 | var flag = false 17 | for s in args do : 18 | if length(s) > 0 : 19 | if s[0] == '-' : 20 | flag = to-symbol(s[1 to false]) 21 | else if flag != false : 22 | val flag = flag as Symbol 23 | table[flag] = cons(s, get?(table, flag, List())) 24 | for k in keys(table) do : 25 | table[k] = reverse(table[k]) 26 | table 27 | 28 | defn main () : 29 | val args = parse-commandline() 30 | if key?(args, `e) : 31 | eval(args) 32 | else if key?(args, `o) : 33 | compile(args) 34 | else if key?(args, `oast) : 35 | save-ast(args) 36 | else if key?(args, `past) : 37 | print-ast(args) 38 | else if key?(args, `pb) : 39 | print-bytecode(args) 40 | else if key?(args, `rb) : 41 | run-bytecode(args) 42 | else : 43 | println("No option given.") 44 | 45 | defn print-bytecode (args:HashTable) : 46 | val infile:String = head(args[`pb]) 47 | val prog = load-bytecode(infile) 48 | println(prog) 49 | 50 | defn run-bytecode (args:HashTable) : 51 | val infile:String = head(args[`rb]) 52 | val prog = load-bytecode(infile) 53 | run(link(prog)) 54 | 55 | defn compile (args:HashTable) : 56 | val infile:String = head(args[`i]) 57 | val outfile:String = head(args[`o]) 58 | val parsed = parse-feeny-file(infile) 59 | val compiled = compile(parsed) 60 | save-bytecode(compiled, outfile) 61 | 62 | defn save-ast (args:HashTable) : 63 | val infile:String = head(args[`i]) 64 | val outfile:String = head(args[`oast]) 65 | val parsed = parse-feeny-file(infile) 66 | save-ast(parsed, outfile) 67 | 68 | defn print-ast (args:HashTable) : 69 | val infile:String = head(args[`past]) 70 | val ast = load-ast(infile) 71 | println(ast) 72 | 73 | defn eval (args:HashTable) : 74 | val infile:String = head(args[`e]) 75 | val parsed = parse-feeny-file(infile) 76 | eval(parsed) 77 | 78 | try : 79 | main() 80 | catch (e:IOException| 81 | NoMatchException| 82 | feeny/vm/EvalException| 83 | feeny/eval/EvalException| 84 | reader/LexerException) : 85 | println(e) 86 | exit(-1) -------------------------------------------------------------------------------- /src/bytecode.h: -------------------------------------------------------------------------------- 1 | #ifndef BYTECODE_H 2 | #define BYTECODE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "utils.h" 8 | 9 | typedef enum { 10 | INT_VAL, 11 | NULL_VAL, 12 | STRING_VAL, 13 | METHOD_VAL, 14 | SLOT_VAL, 15 | CLASS_VAL 16 | } ValTag; 17 | 18 | typedef enum { 19 | LABEL_OP, 20 | LIT_OP, 21 | PRINTF_OP, 22 | ARRAY_OP, 23 | OBJECT_OP, 24 | SLOT_OP, 25 | SET_SLOT_OP, 26 | CALL_SLOT_OP, 27 | CALL_OP, 28 | SET_LOCAL_OP, 29 | GET_LOCAL_OP, 30 | SET_GLOBAL_OP, 31 | GET_GLOBAL_OP, 32 | BRANCH_OP, 33 | GOTO_OP, 34 | RETURN_OP, 35 | DROP_OP 36 | } OpCode; 37 | 38 | typedef struct { 39 | ValTag tag; 40 | } Value; 41 | 42 | typedef struct { 43 | ValTag tag; 44 | int value; 45 | } IntValue; 46 | 47 | typedef struct { 48 | ValTag tag; 49 | char* value; 50 | } StringValue; 51 | 52 | typedef struct { 53 | ValTag tag; 54 | int name; 55 | int nargs; 56 | int nlocals; 57 | Vector* code; 58 | } MethodValue; 59 | 60 | typedef struct { 61 | ValTag tag; 62 | int name; 63 | } SlotValue; 64 | 65 | typedef struct { 66 | ValTag tag; 67 | Vector* slots; 68 | } ClassValue; 69 | 70 | typedef struct { 71 | OpCode tag; 72 | } ByteIns; 73 | 74 | typedef struct { 75 | OpCode tag; 76 | int name; 77 | } LabelIns; 78 | 79 | typedef struct { 80 | OpCode tag; 81 | int idx; 82 | } LitIns; 83 | 84 | typedef struct { 85 | OpCode tag; 86 | int format; 87 | int arity; 88 | } PrintfIns; 89 | 90 | typedef struct { 91 | OpCode tag; 92 | int class; 93 | } ObjectIns; 94 | 95 | typedef struct { 96 | OpCode tag; 97 | int name; 98 | } SlotIns; 99 | 100 | typedef struct { 101 | OpCode tag; 102 | int name; 103 | } SetSlotIns; 104 | 105 | typedef struct { 106 | OpCode tag; 107 | int name; 108 | int arity; 109 | } CallSlotIns; 110 | 111 | typedef struct { 112 | OpCode tag; 113 | int name; 114 | int arity; 115 | } CallIns; 116 | 117 | typedef struct { 118 | OpCode tag; 119 | int idx; 120 | } SetLocalIns; 121 | 122 | typedef struct { 123 | OpCode tag; 124 | int idx; 125 | } GetLocalIns; 126 | 127 | typedef struct { 128 | OpCode tag; 129 | int name; 130 | } SetGlobalIns; 131 | 132 | typedef struct { 133 | OpCode tag; 134 | int name; 135 | } GetGlobalIns; 136 | 137 | typedef struct { 138 | OpCode tag; 139 | int name; 140 | } BranchIns; 141 | 142 | typedef struct { 143 | OpCode tag; 144 | int name; 145 | } GotoIns; 146 | 147 | typedef struct { 148 | Vector* values; 149 | Vector* slots; 150 | int entry; 151 | } Program; 152 | 153 | Program* load_bytecode (char* filename); 154 | void print_ins (ByteIns* ins); 155 | void print_prog (Program* p); 156 | 157 | #endif 158 | 159 | 160 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # The Feeny Programming Language # 2 | 3 | Feeny is a small programming language designed to include the semantics of many popular dynamically-typed programming languages, such as Javascript, Ruby, and Python, and remain simple enough to implement in one semester. 4 | 5 | It was first introduced in the University of California, Berkeley graduate course *Virtual Machines and Managed Runtimes* taught by Mario Wolczko and Patrick S. Li. 6 | 7 | The course walks the class through a from-scratch implementation of Feeny, starting with a simple abstract syntax tree interpreter and ending with a just-in-time compiler. 8 | 9 | ## Installing Stanza 10 | 11 | There is an included Feeny parser and implementation written in L.B. Stanza. To compile the system from its sources, you will need to install Stanza. Please go to [lbstanza.org](http://lbstanza.org) to download it. Feeny has been verified to work with version `0.13.44` . Installation instructions for Stanza can be found [here](http://lbstanza.org/chapter1.html). 12 | 13 | ## Compiling the System 14 | 15 | Run `scripts/make-feeny.sh` to compile the system. It produces two files: `bin/feeny` and `bin/cfeeny`. 16 | 17 | ## Included Documentation 18 | 19 | The course teaches students to ultimately implement a just-in-time compiler for Feeny through a series of exercises. The exercises are kept in the `docs` folder. 20 | 21 | ## How to Run the Example System 22 | 23 | **Evaluating**: The following command interprets the included sudoku program. 24 | 25 | ``` 26 | bin/feeny -e tests/sudoku.feeny 27 | ``` 28 | 29 | **Parsing:** The following command reads in the included sudoku program, and outputs the parse tree as a `.ast` file. The included C files are able to read in `.ast` files into an in-memory AST datastructure. 30 | 31 | ``` 32 | bin/feeny -i tests/sudoku.feeny -oast sudoku.ast 33 | ``` 34 | 35 | **Compiling:** The following command reads in the included sudoku program, and compiles it to a bytecode `.bc` format. The included C files are able to read in `.bc` files. 36 | 37 | ``` 38 | bin/feeny -i tests/sudoku.feeny -o sudoku.bc 39 | ``` 40 | 41 | **Display an AST File:** The following command reads in a `.ast` file and prints it to the screen. 42 | 43 | ``` 44 | bin/feeny -past sudoku.ast 45 | ``` 46 | 47 | **Display a Bytecode File:** The following command reads in a `.bc` file and prints it to the screen. 48 | 49 | ``` 50 | bin/feeny -pb sudoku.bc 51 | ``` 52 | 53 | **Execute a Bytecode File:** The following command executes a `.bc` file. 54 | 55 | ``` 56 | bin/feeny -rb sudoku.bc 57 | ``` 58 | 59 | **Example AST Interpreter**: The example C implementation of the Feeny AST interpreter can be executed using the following command: 60 | 61 | ``` 62 | bin/cfeeny -ast sudoku.ast 63 | ``` 64 | 65 | **Example Bytecode Interpreter:** The example C implementation of the Feeny bytecode interpreter can be executed using the following command: 66 | 67 | ``` 68 | bin/cfeeny -bc sudoku.bc 69 | ``` 70 | 71 | -------------------------------------------------------------------------------- /src/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | typedef enum { 5 | INT_EXP, 6 | NULL_EXP, 7 | PRINTF_EXP, 8 | ARRAY_EXP, 9 | OBJECT_EXP, 10 | SLOT_EXP, 11 | SET_SLOT_EXP, 12 | CALL_SLOT_EXP, 13 | CALL_EXP, 14 | SET_EXP, 15 | IF_EXP, 16 | WHILE_EXP, 17 | REF_EXP, 18 | VAR_STMT, 19 | FN_STMT, 20 | SEQ_STMT, 21 | EXP_STMT 22 | } AstTag; 23 | 24 | typedef struct { 25 | AstTag tag; 26 | } Exp; 27 | 28 | typedef struct { 29 | AstTag tag; 30 | } SlotStmt; 31 | 32 | typedef struct { 33 | AstTag tag; 34 | } ScopeStmt; 35 | 36 | 37 | 38 | 39 | typedef struct { 40 | AstTag tag; 41 | int value; 42 | } IntExp; 43 | 44 | typedef struct { 45 | AstTag tag; 46 | char* format; 47 | int nexps; 48 | Exp** exps; 49 | } PrintfExp; 50 | 51 | typedef struct { 52 | AstTag tag; 53 | Exp* length; 54 | Exp* init; 55 | } ArrayExp; 56 | 57 | typedef struct { 58 | AstTag tag; 59 | Exp* parent; 60 | int nslots; 61 | SlotStmt** slots; 62 | } ObjectExp; 63 | 64 | typedef struct { 65 | AstTag tag; 66 | char* name; 67 | Exp* exp; 68 | } SlotExp; 69 | 70 | typedef struct { 71 | AstTag tag; 72 | char* name; 73 | Exp* exp; 74 | Exp* value; 75 | } SetSlotExp; 76 | 77 | typedef struct { 78 | AstTag tag; 79 | char* name; 80 | Exp* exp; 81 | int nargs; 82 | Exp** args; 83 | } CallSlotExp; 84 | 85 | typedef struct { 86 | AstTag tag; 87 | char* name; 88 | int nargs; 89 | Exp** args; 90 | } CallExp; 91 | 92 | typedef struct { 93 | AstTag tag; 94 | char* name; 95 | Exp* exp; 96 | } SetExp; 97 | 98 | typedef struct { 99 | AstTag tag; 100 | Exp* pred; 101 | ScopeStmt* conseq; 102 | ScopeStmt* alt; 103 | } IfExp; 104 | 105 | typedef struct { 106 | AstTag tag; 107 | Exp* pred; 108 | ScopeStmt* body; 109 | } WhileExp; 110 | 111 | typedef struct { 112 | AstTag tag; 113 | char* name; 114 | } RefExp; 115 | 116 | //Slot Statements 117 | typedef struct { 118 | AstTag tag; 119 | char* name; 120 | Exp* exp; 121 | } SlotVar; 122 | 123 | typedef struct { 124 | AstTag tag; 125 | char* name; 126 | int nargs; 127 | char** args; 128 | ScopeStmt* body; 129 | } SlotMethod; 130 | 131 | //Scope Statements 132 | typedef struct { 133 | AstTag tag; 134 | char* name; 135 | Exp* exp; 136 | } ScopeVar; 137 | 138 | typedef struct { 139 | AstTag tag; 140 | char* name; 141 | int nargs; 142 | char** args; 143 | ScopeStmt* body; 144 | } ScopeFn; 145 | 146 | typedef struct { 147 | AstTag tag; 148 | ScopeStmt* a; 149 | ScopeStmt* b; 150 | } ScopeSeq; 151 | 152 | typedef struct { 153 | AstTag tag; 154 | Exp* exp; 155 | } ScopeExp; 156 | 157 | void print_exp (Exp* e); 158 | void print_slotstmt (SlotStmt* s); 159 | void print_scopestmt (ScopeStmt* s); 160 | 161 | Exp* read_exp (); 162 | SlotStmt* read_slot (); 163 | ScopeStmt* read_scopestmt (); 164 | ScopeStmt* read_ast (char* filename); 165 | 166 | void interpret (ScopeStmt* stmt); 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | 6 | //============================================================ 7 | //===================== CONVENIENCE ========================== 8 | //============================================================ 9 | 10 | int max (int a, int b) { 11 | return a > b? a : b; 12 | } 13 | 14 | int min (int a, int b) { 15 | return a < b? a : b; 16 | } 17 | 18 | void print_string (char* str) { 19 | printf("\""); 20 | while(1){ 21 | char c = str[0]; 22 | str++; 23 | switch(c){ 24 | case '\n': 25 | printf("\\n"); 26 | break; 27 | case '\\': 28 | printf("\\\\"); 29 | break; 30 | case '"': 31 | printf("\\\""); 32 | break; 33 | case 0: 34 | printf("\""); 35 | return; 36 | default: 37 | printf("%c", c); 38 | break; 39 | } 40 | } 41 | } 42 | 43 | //============================================================ 44 | //===================== VECTORS ============================== 45 | //============================================================ 46 | 47 | Vector* make_vector () { 48 | Vector* v = (Vector*)malloc(sizeof(Vector)); 49 | v->size = 0; 50 | v->capacity = 8; 51 | v->array = malloc(sizeof(void*) * v->capacity); 52 | return v; 53 | } 54 | 55 | void vector_ensure_capacity (Vector* v, int c) { 56 | if(v->capacity < c){ 57 | int c2 = max(v->capacity * 2, c); 58 | void** a2 = malloc(sizeof(void*) * c2); 59 | memcpy(a2, v->array, sizeof(void*) * v->size); 60 | free(v->array); 61 | v->capacity = c2; 62 | v->array = a2; 63 | } 64 | } 65 | 66 | void vector_set_length (Vector* v, int len, void* x) { 67 | if(len < 0){ 68 | printf("Negative length given to vector.\n"); 69 | exit(-1); 70 | } 71 | if(len <= v->size){ 72 | v->size = len; 73 | }else{ 74 | while(v->size < len) 75 | vector_add(v, x); 76 | } 77 | } 78 | 79 | void vector_add (Vector* v, void* val) { 80 | vector_ensure_capacity(v, v->size + 1); 81 | v->array[v->size] = val; 82 | v->size++; 83 | } 84 | 85 | void* vector_pop (Vector* v) { 86 | if(v->size == 0){ 87 | printf("Pop from empty vector.\n"); 88 | exit(-1); 89 | } 90 | v->size--; 91 | return v->array[v->size]; 92 | } 93 | 94 | void* vector_peek (Vector* v) { 95 | if(v->size == 0){ 96 | printf("Peek from empty vector.\n"); 97 | exit(-1); 98 | } 99 | return v->array[v->size - 1]; 100 | } 101 | 102 | void vector_clear (Vector* v){ 103 | v->size = 0; 104 | } 105 | 106 | void vector_free (Vector* v){ 107 | free(v->array); 108 | free(v); 109 | } 110 | 111 | void* vector_get (Vector* v, int i){ 112 | if(i < 0 || i >= v->size){ 113 | printf("Index %d out of bounds.\n", i); 114 | exit(-1); 115 | } 116 | return v->array[i]; 117 | } 118 | 119 | void vector_set (Vector* v, int i, void* x){ 120 | if(i < 0 || i > v->size){ 121 | printf("Index %d out of bounds.\n", i); 122 | exit(-1); 123 | }else if(i == v->size){ 124 | vector_add(v, x); 125 | }else{ 126 | v->array[i] = x; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/abscode.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/abc : 2 | import core 3 | import collections 4 | 5 | ;============================================================ 6 | ;=================== Abstract Code ========================== 7 | ;============================================================ 8 | 9 | public deftype Const 10 | public defstruct IntConst <: Const : (value:Int) 11 | public defstruct NullConst <: Const 12 | public defstruct StringConst <: Const : (value:String) 13 | public defstruct MethodConst <: Const : (name:Int, nargs:Int, nlocals:Int, code:List) 14 | public defstruct SlotConst <: Const : (name:Int) 15 | public defstruct ClassConst <: Const : (slots:List) 16 | 17 | public deftype Ins 18 | public defstruct LabelIns <: Ins : (name:Int) 19 | public defstruct LitIns <: Ins : (idx:Int) 20 | public defstruct PrintfIns <: Ins : (format:Int, arity:Int) 21 | public defstruct ArrayIns <: Ins 22 | public defstruct ObjectIns <: Ins : (class:Int) 23 | public defstruct SlotIns <: Ins : (name:Int) 24 | public defstruct SetSlotIns <: Ins : (name:Int) 25 | public defstruct CallSlotIns <: Ins : (name:Int, arity:Int) 26 | public defstruct CallIns <: Ins : (name:Int, arity:Int) 27 | public defstruct SetLocalIns <: Ins : (idx:Int) 28 | public defstruct GetLocalIns <: Ins : (idx:Int) 29 | public defstruct SetGlobalIns <: Ins : (name:Int) 30 | public defstruct GetGlobalIns <: Ins : (name:Int) 31 | public defstruct BranchIns <: Ins : (name:Int) 32 | public defstruct GotoIns <: Ins : (name:Int) 33 | public defstruct ReturnIns <: Ins 34 | public defstruct DropIns <: Ins 35 | 36 | public defstruct Program : 37 | consts: List 38 | slots: List 39 | entry: Int 40 | 41 | ;============================================================ 42 | ;==================== Printer =============================== 43 | ;============================================================ 44 | defmethod print (o:OutputStream, c:Const) : 45 | match(c) : 46 | (c:IntConst) : print{o, _} $ "Int(%~)" % [value(c)] 47 | (c:NullConst) : print{o, _} $ "Null" 48 | (c:StringConst) : print{o, _} $ "String(%~)" % [value(c)] 49 | (c:SlotConst) : print{o, _} $ "Slot(#%~)" % [name(c)] 50 | (c:ClassConst) : 51 | val xs = for x in slots(c) seq : "#%~" % [x] 52 | print{o, _} $ "Class(%,)" % [xs] 53 | (c:MethodConst) : 54 | val io = IndentedStream(o) 55 | print(o, "Method(#%~, nargs:%~, nlocals:%~) :" % [name(c), nargs(c), nlocals(c)]) 56 | for i in code(c) do : 57 | print(io, "\n%~" % [i]) 58 | 59 | defmethod print (o:OutputStream, i:Ins) : 60 | print{o, _} $ match(i) : 61 | (i:LabelIns) : "label #%~" % [name(i)] 62 | (i:LitIns) : " lit #%~" % [idx(i)] 63 | (i:PrintfIns) : " printf #%~ %~" % [format(i), arity(i)] 64 | (i:ArrayIns) : " array" 65 | (i:ObjectIns) : " object #%~" % [class(i)] 66 | (i:SlotIns) : " slot #%~" % [name(i)] 67 | (i:SetSlotIns) : " set-slot #%~" % [name(i)] 68 | (i:CallSlotIns) : " call-slot #%~ %~" % [name(i), arity(i)] 69 | (i:CallIns) : " call #%~ %~" % [name(i), arity(i)] 70 | (i:SetLocalIns) : " set local %~" % [idx(i)] 71 | (i:GetLocalIns) : " get local %~" % [idx(i)] 72 | (i:SetGlobalIns) : " set global #%~" % [name(i)] 73 | (i:GetGlobalIns) : " get global #%~" % [name(i)] 74 | (i:BranchIns) : " branch #%~" % [name(i)] 75 | (i:GotoIns) : " goto #%~" % [name(i)] 76 | (i:ReturnIns) : " return" 77 | (i:DropIns) : " drop" 78 | 79 | defmethod print (o:OutputStream, p:Program) : 80 | val io = IndentedStream(o) 81 | print(o, "Constants :") 82 | for (c in consts(p), i in 0 to false) do : 83 | match(c) : 84 | (c:MethodConst) : 85 | print(io, "\n#%~ :" % [i]) 86 | print(IndentedStream(io), "\n%~" % [c]) 87 | (c) : 88 | print(io, "\n#%~ : %~" % [i, c]) 89 | print(o, "\nGlobals :") 90 | for s in slots(p) do : 91 | print(io, "\n#%~" % [s]) 92 | print(o, "\nEntry : #%~" % [entry(p)]) 93 | -------------------------------------------------------------------------------- /tests/sudoku.feeny: -------------------------------------------------------------------------------- 1 | defn and (a, b) : 2 | if a : b 3 | else : a 4 | 5 | defn or (a, b) : 6 | if a : a 7 | else : b 8 | 9 | defn not (x) : 10 | if x : null 11 | else : 0 12 | 13 | var check-array = array(10, 0) 14 | defn begin-check () : 15 | var i = 0 16 | while i < 10 : 17 | check-array[i] = 0 18 | i = i + 1 19 | defn check (x) : 20 | check-array[x] = check-array[x] + 1 21 | defn check-good? () : 22 | var good = 1 23 | var i = 1 24 | while i < 10 : 25 | good = and(good, check-array[i] <= 1) 26 | i = i + 1 27 | good 28 | 29 | defn solvehelper (b, i) : 30 | if i == 81 : 31 | 0 32 | else if b.pos(i) == 0 : 33 | var s = null 34 | var n = 1 35 | while n <= 9 : 36 | if not(s) : 37 | b.set-pos(i, n) 38 | if b.good?() : 39 | s = or(s, solvehelper(b, i + 1)) 40 | n = n + 1 41 | if s : 42 | s 43 | else : 44 | b.set-pos(i, 0) 45 | null 46 | else : 47 | solvehelper(b, i + 1) 48 | 49 | defn board () : 50 | object : 51 | var array = array(9 * 9, 0) 52 | method pos (i) : 53 | this.array[i] 54 | method set-pos (i, x) : 55 | this.array[i] = x 56 | method get (r, c) : 57 | this.array[r * 9 + c] 58 | method set (r, c, x) : 59 | this.array[r * 9 + c] = x 60 | method print () : 61 | var r = 0 62 | while r < 9 : 63 | var c = 0 64 | while c < 9 : 65 | if c > 0 : printf(" ") 66 | if this[r, c] == 0 : printf("_") 67 | else : printf("~", this[r,c]) 68 | c = c + 1 69 | printf("\n") 70 | r = r + 1 71 | method good? () : 72 | var good? = 1 73 | 74 | ;Rows 75 | var r = 0 76 | while r < 9 : 77 | begin-check() 78 | var c = 0 79 | while c < 9 : 80 | check(this[r,c]) 81 | c = c + 1 82 | good? = and(good?, check-good?()) 83 | r = r + 1 84 | 85 | ;Columns 86 | var c = 0 87 | while c < 9 : 88 | begin-check() 89 | var r = 0 90 | while r < 9 : 91 | check(this[r,c]) 92 | r = r + 1 93 | good? = and(good?, check-good?()) 94 | c = c + 1 95 | 96 | ;Cells 97 | var cell = 0 98 | while cell < 9 : 99 | var r = (cell / 3) * 3 100 | var c = (cell % 3) * 3 101 | begin-check() 102 | var ri = r 103 | while ri < r + 3 : 104 | var ci = c 105 | while ci < c + 3 : 106 | check(this[ri,ci]) 107 | ci = ci + 1 108 | ri = ri + 1 109 | good? = and(good?, check-good?()) 110 | cell = cell + 1 111 | 112 | good? 113 | method solve () : 114 | solvehelper(this, 0) 115 | 116 | defn main () : 117 | var b = board() 118 | b[0,0] = 8 119 | b[0,1] = 0 120 | b[0,2] = 0 121 | b[0,3] = 1 122 | b[0,4] = 0 123 | b[0,5] = 3 124 | b[0,6] = 4 125 | b[0,7] = 0 126 | b[0,8] = 0 127 | b[1,0] = 0 128 | b[1,1] = 3 129 | b[1,2] = 5 130 | b[1,3] = 7 131 | b[1,4] = 8 132 | b[1,5] = 0 133 | b[1,6] = 0 134 | b[1,7] = 6 135 | b[1,8] = 2 136 | b[2,0] = 4 137 | b[2,1] = 7 138 | b[2,2] = 0 139 | b[2,3] = 0 140 | b[2,4] = 0 141 | b[2,5] = 6 142 | b[2,6] = 0 143 | b[2,7] = 9 144 | b[2,8] = 0 145 | b[3,0] = 0 146 | b[3,1] = 0 147 | b[3,2] = 0 148 | b[3,3] = 0 149 | b[3,4] = 0 150 | b[3,5] = 0 151 | b[3,6] = 0 152 | b[3,7] = 2 153 | b[3,8] = 4 154 | b[4,0] = 0 155 | b[4,1] = 1 156 | b[4,2] = 0 157 | b[4,3] = 3 158 | b[4,4] = 0 159 | b[4,5] = 5 160 | b[4,6] = 0 161 | b[4,7] = 8 162 | b[4,8] = 0 163 | b[5,0] = 2 164 | b[5,1] = 8 165 | b[5,2] = 0 166 | b[5,3] = 0 167 | b[5,4] = 0 168 | b[5,5] = 0 169 | b[5,6] = 0 170 | b[5,7] = 0 171 | b[5,8] = 0 172 | b[6,0] = 0 173 | b[6,1] = 2 174 | b[6,2] = 0 175 | b[6,3] = 6 176 | b[6,4] = 0 177 | b[6,5] = 0 178 | b[6,6] = 0 179 | b[6,7] = 3 180 | b[6,8] = 9 181 | b[7,0] = 1 182 | b[7,1] = 9 183 | b[7,2] = 0 184 | b[7,3] = 0 185 | b[7,4] = 7 186 | b[7,5] = 2 187 | b[7,6] = 6 188 | b[7,7] = 4 189 | b[7,8] = 0 190 | b[8,0] = 0 191 | b[8,1] = 0 192 | b[8,2] = 8 193 | b[8,3] = 5 194 | b[8,4] = 0 195 | b[8,5] = 9 196 | b[8,6] = 0 197 | b[8,7] = 0 198 | b[8,8] = 1 199 | 200 | printf("=== Given Puzzle ===\n") 201 | b.print() 202 | 203 | printf("\n=== Solution ===\n") 204 | b.solve() 205 | b.print() 206 | 207 | main() 208 | 209 | 210 | ;============================================================ 211 | ;======================== OUTPUT ============================ 212 | ;============================================================ 213 | ; 214 | ;=== Given Puzzle === 215 | ;8 _ _ 1 _ 3 4 _ _ 216 | ;_ 3 5 7 8 _ _ 6 2 217 | ;4 7 _ _ _ 6 _ 9 _ 218 | ;_ _ _ _ _ _ _ 2 4 219 | ;_ 1 _ 3 _ 5 _ 8 _ 220 | ;2 8 _ _ _ _ _ _ _ 221 | ;_ 2 _ 6 _ _ _ 3 9 222 | ;1 9 _ _ 7 2 6 4 _ 223 | ;_ _ 8 5 _ 9 _ _ 1 224 | ; 225 | ;=== Solution === 226 | ;8 6 2 1 9 3 4 5 7 227 | ;9 3 5 7 8 4 1 6 2 228 | ;4 7 1 2 5 6 3 9 8 229 | ;3 5 6 9 1 8 7 2 4 230 | ;7 1 4 3 2 5 9 8 6 231 | ;2 8 9 4 6 7 5 1 3 232 | ;5 2 7 6 4 1 8 3 9 233 | ;1 9 3 8 7 2 6 4 5 234 | ;6 4 8 5 3 9 2 7 1 -------------------------------------------------------------------------------- /src/parser.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/parser : 2 | import core 3 | import collections 4 | import feeny/ir 5 | import reader 6 | 7 | defn* apply-suffix-ops (x:?T, fs:List<(T -> T)>) -> T : 8 | if empty?(fs) : x 9 | else : apply-suffix-ops(head(fs)(x), tail(fs)) 10 | 11 | defn ParseException (info:FileInfo|False, msg) : 12 | new Exception : 13 | defmethod print (o:OutputStream, this) : 14 | val info-str = ("%_: " % [info]) when info is-not False else "" 15 | print(o, "%_%_" % [info-str, msg]) 16 | 17 | defsyntax feeny : 18 | defproduction emt : False 19 | fail-if emt = (?x) : ParseException(closest-info(), "Unexpected token %_ found here." % [x]) 20 | defrule emt = () : false 21 | 22 | defproduction sym: Symbol 23 | defrule sym = (?x) when unwrap-token(x) is Symbol : unwrap-token(x) 24 | 25 | defproduction int: Int 26 | defrule int = (?x) when unwrap-token(x) is Int : unwrap-token(x) 27 | 28 | defproduction str: String 29 | defrule str = (?x) when unwrap-token(x) is String : unwrap-token(x) 30 | 31 | defproduction sym!: Symbol 32 | defrule sym! = (?x:#sym) : x 33 | fail-if sym! = () : ParseException(closest-info(), "Expected a symbol here.") 34 | 35 | defproduction int!: Int 36 | defrule int! = (?x:#int) : x 37 | fail-if int! = () : ParseException(closest-info(), "Expected an integer here.") 38 | 39 | defproduction str!: String 40 | defrule str! = (?x:#str) : x 41 | fail-if str! = () : ParseException(closest-info(), "Expected a string here.") 42 | 43 | defproduction e0 : Exp 44 | defproduction e1 : Exp 45 | defproduction e2 : Exp 46 | defproduction e3 : Exp 47 | defrule e0 = (?x:#e1 ?ys:#op0 ...) : apply-suffix-ops(x, ys) 48 | defrule e1 = (?x:#e2 ?ys:#op1 ...) : apply-suffix-ops(x, ys) 49 | defrule e2 = (?x:#e3 ?ys:#op2 ...) : apply-suffix-ops(x, ys) 50 | defrule e3 = (?x:#e4 ?ys:#op3 ...) : apply-suffix-ops(x, ys) 51 | 52 | defproduction op0 : Exp -> Exp 53 | defrule op0 = (< ?y:#e1) : (fn (x) : CallSlotExp(`lt, x, List(y))) 54 | defrule op0 = (> ?y:#e1) : (fn (x) : CallSlotExp(`gt, x, List(y))) 55 | defrule op0 = (<= ?y:#e1) : (fn (x) : CallSlotExp(`le, x, List(y))) 56 | defrule op0 = (>= ?y:#e1) : (fn (x) : CallSlotExp(`ge, x, List(y))) 57 | defrule op0 = (== ?y:#e1) : (fn (x) : CallSlotExp(`eq, x, List(y))) 58 | 59 | defproduction op1 : Exp -> Exp 60 | defrule op1 = (+ ?y:#e2) : (fn (x) : CallSlotExp(`add, x, List(y))) 61 | defrule op1 = (- ?y:#e2) : (fn (x) : CallSlotExp(`sub, x, List(y))) 62 | 63 | defproduction op2 : Exp -> Exp 64 | defrule op2 = (* ?y:#e3) : (fn (x) : CallSlotExp(`mul, x, List(y))) 65 | defrule op2 = (/ ?y:#e3) : (fn (x) : CallSlotExp(`div, x, List(y))) 66 | defrule op2 = (% ?y:#e3) : (fn (x) : CallSlotExp(`mod, x, List(y))) 67 | 68 | defproduction op3 : Exp -> Exp 69 | defrule op3 = (. ?f:#id = ?y:#e0) : (fn (x) : SetSlotExp(f, x, y)) 70 | defrule op3 = (. ?f:#id(?ys:#e0 ... #emt)) : (fn (x) : CallSlotExp(f, x, ys)) 71 | defrule op3 = (. ?f:#id) : (fn (x) : SlotExp(f, x)) 72 | defrule op3 = ((@get ?i:#e0 ... #emt) = ?y:#e0) : (fn (x) : CallSlotExp(`set, x, append(i, List(y)))) 73 | defrule op3 = ((@get ?i:#e0 ... #emt)) : (fn (x) : CallSlotExp(`get, x, i)) 74 | 75 | defproduction e4: Exp 76 | defrule e4 = (printf(?f:#str!, ?ys:#e0 ... #emt)) : PrintfExp(f, ys) 77 | defrule e4 = (array(?n:#e0, ?x:#e0)) : ArrayExp(n, x) 78 | defrule e4 = (object : (?ss:#slot ... #emt)) : ObjectExp(NullExp(), ss) 79 | defrule e4 = (object(?p:#e0) : (?ss:#slot ... #emt)) : ObjectExp(p, ss) 80 | defrule e4 = (?s:#ifexp) : s 81 | defrule e4 = (while ?p:#e0 : ?b:#sexp) : WhileExp(p, b) 82 | defrule e4 = (null) : NullExp() 83 | defrule e4 = (?x:#id(?ys:#e0 ... #emt)) : CallExp(x, ys) 84 | defrule e4 = (?x:#id = ?y:#e0) : SetExp(x, y) 85 | defrule e4 = (?x:#id) : RefExp(x) 86 | defrule e4 = (?x:#int) : IntExp(x) 87 | defrule e4 = ((?x:#e0)) : x 88 | 89 | defproduction id : Symbol 90 | defrule id != (@do) 91 | defrule id != (@get) 92 | defrule id != (@tuple) 93 | defrule id != (@afn) 94 | defrule id != (@do-afn) 95 | defrule id != (@of) 96 | defrule id != (@cap) 97 | defrule id != (@quote) 98 | defrule id = (?x:#sym) : x 99 | 100 | defproduction id! : Symbol 101 | defrule id! = (?x:#id) : x 102 | fail-if id! = () : ParseException(closest-info(), "Expected an identifier here!") 103 | 104 | defproduction ifexp: IfExp 105 | defrule ifexp = (if ?p:#e0 : ?c:#sexp else : ?a:#sexp) : IfExp(p, c, a) 106 | defrule ifexp = (if ?p:#e0 : ?c:#sexp else ?a:#ifexp) : IfExp(p, c, ScopeExp(a)) 107 | defrule ifexp = (if ?p:#e0 : ?c:#sexp) : IfExp(p, c, ScopeExp(NullExp())) 108 | 109 | defproduction slot: SlotStmt 110 | defrule slot = (var ?n:#id! = ?e:#e0) : SlotVar(n, e) 111 | defrule slot = (method ?n:#id! (?xs:#id! ... #emt) : ?b:#sexp) : SlotMethod(n, xs, b) 112 | 113 | defproduction sexp: ScopeStmt 114 | defrule sexp = (var ?n:#id! = ?e:#e0) : ScopeVar(n, e) 115 | defrule sexp = (?e:#e0) : ScopeExp(e) 116 | defrule sexp = (()) : ScopeBegin(List()) 117 | defrule sexp = ((?s:#sexp ?ss:#sexp ... #emt)) : ScopeBegin(cons(s,ss)) 118 | 119 | public defproduction texp: ScopeStmt 120 | defrule texp = (var ?n:#id! = ?e:#e0) : ScopeVar(n, e) 121 | defrule texp = (defn ?n:#id! (?xs:#id! ...) : ?b:#sexp) : ScopeFn(n, xs, b) 122 | defrule texp = (?e:#e0) : ScopeExp(e) 123 | defrule texp = (()) : ScopeBegin(List()) 124 | defrule texp = ((?s:#texp ?ss:#texp ... #emt)) : ScopeBegin(cons(s,ss)) 125 | 126 | defn begin-to-seq (s:ScopeStmt) : 127 | defn flatten (s:ScopeBegin) : 128 | val stmts = to-list $ generate : 129 | defn loop (s:ScopeStmt) : 130 | match(s) : 131 | (s:ScopeBegin) : do(loop, stmts(s)) 132 | (s) : yield(s) 133 | loop(s) 134 | if empty?(stmts) : 135 | throw $ ParseException(false, "Empty body.") 136 | if last(stmts) is-not ScopeExp : 137 | throw $ ParseException(false, "Last statement in scope is not an expression.") 138 | defn to-seq (ss:List) : 139 | val [h t] = [head(ss), tail(ss)] 140 | if empty?(t) : h 141 | else : ScopeSeq(h, to-seq(t)) 142 | to-seq(map(begin-to-seq, stmts)) 143 | 144 | match(s) : 145 | (s:ScopeBegin) : flatten(s) 146 | (s) : mapr(begin-to-seq, s) 147 | 148 | public defn parse-feeny (forms) -> ScopeStmt : 149 | begin-to-seq $ parse-syntax[feeny / #texp](List(forms)) 150 | 151 | public defn parse-feeny-file (filename:String) -> ScopeStmt : 152 | parse-feeny(read-file(filename)) -------------------------------------------------------------------------------- /src/compile.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/compile : 2 | import core 3 | import collections 4 | import feeny/ir 5 | import feeny/abc 6 | 7 | public defn compile (s:ScopeStmt) : 8 | ;Constant Pool 9 | val consts = Vector() 10 | val int-map = IntTable() 11 | val string-map = HashTable() 12 | var null-idx = false 13 | 14 | ;Constant Compiler 15 | defn new-const (c:Const) : 16 | add(consts, c) 17 | length(consts) - 1 18 | defn const-idx (s:String) : 19 | match(get?(string-map, s, false)) : 20 | (i:Int) : 21 | i 22 | (f:False) : 23 | val i = new-const(StringConst(s)) 24 | string-map[s] = i 25 | i 26 | defn const-idx (s:Symbol) : 27 | const-idx(to-string(s)) 28 | defn const-idx (x:Int) : 29 | match(get?(int-map, x, false)) : 30 | (i:Int) : 31 | i 32 | (f:False) : 33 | val i = new-const(IntConst(x)) 34 | int-map[x] = i 35 | i 36 | defn get-null-idx () : 37 | if null-idx == false : 38 | null-idx = new-const(NullConst()) 39 | null-idx as Int 40 | 41 | ;Instruction Accumulator 42 | var ins-accum = Vector() 43 | defn emit (i:Ins) : add(ins-accum, i) 44 | defn compile-ins (f: () -> ?) : 45 | let-var ins-accum = Vector() : 46 | f() 47 | to-list(ins-accum) 48 | 49 | ;Method Compiler 50 | defn compile-method (name:Symbol, args:List, s:ScopeStmt) : 51 | val n = length(args) 52 | var n2 53 | val env = map(KeyValue, args, 0 to n) 54 | val code = compile-ins $ fn () : 55 | n2 = cs(s, env, n) 56 | emit $ ReturnIns() 57 | MethodConst(const-idx(name), n, n2 - n, code) 58 | 59 | ;Class Compiler 60 | defn cclass (e:ObjectExp) -> Int : 61 | val slot-idxs = Vector() 62 | for s in slots(e) do : 63 | add{slot-idxs, new-const(_)} $ match(s) : 64 | (s:SlotVar) : SlotConst(const-idx(name(s))) 65 | (s:SlotMethod) : compile-method(name(s), cons(`this, args(s)), body(s)) 66 | new-const(ClassConst(to-list(slot-idxs))) 67 | 68 | ;Expression Compiler 69 | defn c (e:Exp, env:List>, si:Int) -> Int : 70 | ;Local storage tracker 71 | var max-si:Int = si 72 | defn ce (e:Exp) : 73 | max-si = max(max-si, c(e, env, si)) 74 | defn cb (s:ScopeStmt) : 75 | max-si = max(max-si, cs(s, env, si)) 76 | 77 | match(e) : 78 | (e:IntExp) : 79 | emit $ LitIns(const-idx(value(e))) 80 | (e:NullExp) : 81 | emit $ LitIns(get-null-idx()) 82 | (e:PrintfExp) : 83 | do(ce, exps(e)) 84 | emit $ PrintfIns(const-idx(format(e)), length(exps(e))) 85 | (e:ArrayExp) : 86 | ce(length(e)) 87 | ce(init(e)) 88 | emit $ ArrayIns() 89 | (e:ObjectExp) : 90 | ce(parent(e)) 91 | do(ce{exp(_)}, filter-by(slots(e))) 92 | emit $ ObjectIns(cclass(e)) 93 | (e:SlotExp) : 94 | ce(exp(e)) 95 | emit $ SlotIns(const-idx(name(e))) 96 | (e:SetSlotExp) : 97 | ce(exp(e)) 98 | ce(value(e)) 99 | emit $ SetSlotIns(const-idx(name(e))) 100 | (e:CallSlotExp) : 101 | ce(exp(e)) 102 | do(ce, args(e)) 103 | emit $ CallSlotIns(const-idx(name(e)), length(args(e)) + 1) 104 | (e:CallExp) : 105 | do(ce, args(e)) 106 | emit $ CallIns(const-idx(name(e)), length(args(e))) 107 | (e:RefExp) : 108 | emit $ match(lookup?(env, name(e))) : 109 | (n:Int) : GetLocalIns(n) 110 | (n:False) : GetGlobalIns(const-idx(name(e))) 111 | (e:SetExp) : 112 | ce(exp(e)) 113 | emit $ match(lookup?(env, name(e))) : 114 | (n:Int) : SetLocalIns(n) 115 | (n:False) : SetGlobalIns(const-idx(name(e))) 116 | (e:IfExp) : 117 | val cl = const-idx(gensym(`conseq)) 118 | val el = const-idx(gensym(`end)) 119 | ce(pred(e)) 120 | emit $ BranchIns(cl) 121 | cb(alt(e)) 122 | emit $ GotoIns(el) 123 | emit $ LabelIns(cl) 124 | cb(conseq(e)) 125 | emit $ LabelIns(el) 126 | (e:WhileExp) : 127 | val tl = const-idx(gensym(`test)) 128 | val ll = const-idx(gensym(`loop)) 129 | emit $ GotoIns(tl) 130 | emit $ LabelIns(ll) 131 | cb(body(e)) 132 | emit $ DropIns() 133 | emit $ LabelIns(tl) 134 | ce(pred(e)) 135 | emit $ BranchIns(ll) 136 | ce(NullExp()) 137 | 138 | ;Return maximum stack index 139 | max-si 140 | 141 | ;Statement Compiler 142 | defn cs (s:ScopeStmt, env:List>, si:Int) -> Int : 143 | match(s) : 144 | (s:ScopeExp) : 145 | c(exp(s), env, si) 146 | (s:ScopeSeq) : 147 | match(a(s)) : 148 | (v:ScopeVar) : 149 | val m1 = c(exp(v), env, si) 150 | emit $ SetLocalIns(si) 151 | emit $ DropIns() 152 | val m2 = cs(b(s), cons(name(v) => si, env), si + 1) 153 | max(m1, m2) 154 | (e:ScopeSeq) : 155 | cs(ScopeSeq(a(e), ScopeSeq(b(e), b(s))), env, si) 156 | (e:ScopeExp) : 157 | val m1 = cs(e, env, si) 158 | emit $ DropIns() 159 | val m2 = cs(b(s), env, si) 160 | max(m1, m2) 161 | 162 | ;Top Compiler 163 | val globals = Vector() 164 | 165 | defn compile-top-method (s:ScopeStmt) : 166 | var n2 167 | val code = compile-ins $ fn () : 168 | n2 = ctop(s) 169 | emit $ LitIns(get-null-idx()) 170 | emit $ ReturnIns() 171 | MethodConst(const-idx(gensym(`entry)), 0, n2, code) 172 | 173 | defn ctop (s:ScopeStmt) -> Int : 174 | match(s) : 175 | (s:ScopeVar) : 176 | val m = c(exp(s), List(), 0) 177 | emit $ SetGlobalIns(const-idx(name(s))) 178 | emit $ DropIns() 179 | add(globals, new-const(SlotConst(const-idx(name(s))))) 180 | m 181 | (s:ScopeFn) : 182 | add(globals, new-const(compile-method(name(s), args(s), body(s)))) 183 | 0 184 | (s:ScopeExp) : 185 | val m = c(exp(s), List(), 0) 186 | emit $ DropIns() 187 | m 188 | (s:ScopeSeq) : 189 | max(ctop(a(s)), ctop(b(s))) 190 | 191 | ;Driver 192 | val entry = new-const(compile-top-method(s)) 193 | Program(to-list(consts), to-list(globals), entry) 194 | -------------------------------------------------------------------------------- /src/bytecode.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/bytecode : 2 | import core 3 | import collections 4 | import feeny/abc 5 | 6 | ;================= DRIVER =================================== 7 | public defn save-bytecode (p:Program, filename:String) : 8 | let-var OUTFILE = FileOutputStream(filename) : 9 | try : 10 | emit(p) 11 | finally : 12 | close(OUTFILE as FileOutputStream) 13 | 14 | public defn load-bytecode (filename:String) -> Program : 15 | let-var INFILE = FileInputStream(filename) : 16 | try : 17 | read-program() 18 | finally : 19 | close(INFILE as FileInputStream) 20 | 21 | ;=================== TAGS =================================== 22 | val tag-counter = to-seq(0 to false) 23 | val INT-TAG = next(tag-counter) 24 | val NULL-TAG = next(tag-counter) 25 | val STRING-TAG = next(tag-counter) 26 | val METHOD-TAG = next(tag-counter) 27 | val SLOT-TAG = next(tag-counter) 28 | val CLASS-TAG = next(tag-counter) 29 | 30 | val opcode-counter = to-seq(0 to false) 31 | val LABEL-OP = next(opcode-counter) 32 | val LIT-OP = next(opcode-counter) 33 | val PRINTF-OP = next(opcode-counter) 34 | val ARRAY-OP = next(opcode-counter) 35 | val OBJECT-OP = next(opcode-counter) 36 | val SLOT-OP = next(opcode-counter) 37 | val SET-SLOT-OP = next(opcode-counter) 38 | val CALL-SLOT-OP = next(opcode-counter) 39 | val CALL-OP = next(opcode-counter) 40 | val SET-LOCAL-OP = next(opcode-counter) 41 | val GET-LOCAL-OP = next(opcode-counter) 42 | val SET-GLOBAL-OP = next(opcode-counter) 43 | val GET-GLOBAL-OP = next(opcode-counter) 44 | val BRANCH-OP = next(opcode-counter) 45 | val GOTO-OP = next(opcode-counter) 46 | val RETURN-OP = next(opcode-counter) 47 | val DROP-OP = next(opcode-counter) 48 | 49 | ;=================== EMITTING ================================ 50 | var OUTFILE: FileOutputStream|False = false 51 | defn emit-byte (i:Int) : 52 | put(OUTFILE as FileOutputStream, to-byte(i)) 53 | defn emit-short (i:Int) : 54 | put(OUTFILE as FileOutputStream, to-byte(i)) 55 | put(OUTFILE as FileOutputStream, to-byte(i >> 8)) 56 | defn emit-int (i:Int) : 57 | put(OUTFILE as FileOutputStream, to-byte(i)) 58 | put(OUTFILE as FileOutputStream, to-byte(i >> 8)) 59 | put(OUTFILE as FileOutputStream, to-byte(i >> 16)) 60 | put(OUTFILE as FileOutputStream, to-byte(i >> 24)) 61 | defn emit-string (s:String) : 62 | emit-int(length(s)) 63 | for c in s do : 64 | put(OUTFILE as FileOutputStream, c) 65 | 66 | ;================ INSTRUCTION ENCODING ======================= 67 | defn emit (i:Ins) -> False : 68 | match(i) : 69 | (i:LabelIns) : 70 | emit-byte(LABEL-OP) 71 | emit-short(name(i)) 72 | (i:LitIns) : 73 | emit-byte(LIT-OP) 74 | emit-short(idx(i)) 75 | (i:PrintfIns) : 76 | emit-byte(PRINTF-OP) 77 | emit-short(format(i)) 78 | emit-byte(arity(i)) 79 | (i:ArrayIns) : 80 | emit-byte(ARRAY-OP) 81 | (i:ObjectIns) : 82 | emit-byte(OBJECT-OP) 83 | emit-short(class(i)) 84 | (i:SlotIns) : 85 | emit-byte(SLOT-OP) 86 | emit-short(name(i)) 87 | (i:SetSlotIns) : 88 | emit-byte(SET-SLOT-OP) 89 | emit-short(name(i)) 90 | (i:CallSlotIns) : 91 | emit-byte(CALL-SLOT-OP) 92 | emit-short(name(i)) 93 | emit-byte(arity(i)) 94 | (i:CallIns) : 95 | emit-byte(CALL-OP) 96 | emit-short(name(i)) 97 | emit-byte(arity(i)) 98 | (i:SetLocalIns) : 99 | emit-byte(SET-LOCAL-OP) 100 | emit-short(idx(i)) 101 | (i:GetLocalIns) : 102 | emit-byte(GET-LOCAL-OP) 103 | emit-short(idx(i)) 104 | (i:SetGlobalIns) : 105 | emit-byte(SET-GLOBAL-OP) 106 | emit-short(name(i)) 107 | (i:GetGlobalIns) : 108 | emit-byte(GET-GLOBAL-OP) 109 | emit-short(name(i)) 110 | (i:BranchIns) : 111 | emit-byte(BRANCH-OP) 112 | emit-short(name(i)) 113 | (i:GotoIns) : 114 | emit-byte(GOTO-OP) 115 | emit-short(name(i)) 116 | (i:ReturnIns) : 117 | emit-byte(RETURN-OP) 118 | (i:DropIns) : 119 | emit-byte(DROP-OP) 120 | 121 | ;=============== CONSTANT ENCODING ========================= 122 | defn emit (c:Const) -> False : 123 | match(c) : 124 | (c:IntConst) : 125 | emit-byte(INT-TAG) 126 | emit-int(value(c)) 127 | (c:NullConst) : 128 | emit-byte(NULL-TAG) 129 | (c:StringConst) : 130 | emit-byte(STRING-TAG) 131 | emit-string(value(c)) 132 | (c:MethodConst) : 133 | emit-byte(METHOD-TAG) 134 | emit-short(name(c)) 135 | emit-byte(nargs(c)) 136 | emit-short(nlocals(c)) 137 | emit-int(length(code(c))) 138 | do(emit, code(c)) 139 | (c:SlotConst) : 140 | emit-byte(SLOT-TAG) 141 | emit-short(name(c)) 142 | (c:ClassConst) : 143 | emit-byte(CLASS-TAG) 144 | emit-short(length(slots(c))) 145 | do(emit-short, slots(c)) 146 | 147 | ;================ PROGRAM ENCODING ========================= 148 | defn emit (p:Program) -> False : 149 | emit-short(length(consts(p))) 150 | do(emit, consts(p)) 151 | emit-short(length(slots(p))) 152 | do(emit-short, slots(p)) 153 | emit-short(entry(p)) 154 | 155 | ;================ READING =================================== 156 | var INFILE: FileInputStream|False = false 157 | defn read-byte () : 158 | match(get-byte(INFILE as FileInputStream)) : 159 | (i:Byte) : to-int(i) 160 | (f:False) : fatal("Unexpected end of file.") 161 | defn read-short () : 162 | read-byte() + 163 | read-byte() << 8 164 | defn read-int () : 165 | read-byte() + 166 | read-byte() << 8 + 167 | read-byte() << 16 + 168 | read-byte() << 24 169 | defn read-string () : 170 | val l = read-int() 171 | String(for i in 0 to l seq : to-char(read-byte())) 172 | 173 | ;================ PROGRAM INPUT ============================= 174 | defn read-code () : 175 | val l = read-int() 176 | to-list $ seq(read-ins{}, 0 to l) 177 | 178 | defn read-slots () : 179 | val l = read-short() 180 | to-list $ seq(read-short{}, 0 to l) 181 | 182 | defn read-const () : 183 | val tag = read-byte() 184 | switch(tag) : 185 | INT-TAG : IntConst(read-int()) 186 | NULL-TAG : NullConst() 187 | STRING-TAG : StringConst(read-string()) 188 | METHOD-TAG : MethodConst(read-short(), read-byte(), read-short(), read-code()) 189 | SLOT-TAG : SlotConst(read-short()) 190 | CLASS-TAG : ClassConst(read-slots()) 191 | else : fatal("Not a Constant Tag: %~" % [tag]) 192 | 193 | defn read-ins () : 194 | val op = read-byte() 195 | switch(op) : 196 | LABEL-OP : LabelIns(read-short()) 197 | LIT-OP : LitIns(read-short()) 198 | PRINTF-OP : PrintfIns(read-short(), read-byte()) 199 | ARRAY-OP : ArrayIns() 200 | OBJECT-OP : ObjectIns(read-short()) 201 | SLOT-OP : SlotIns(read-short()) 202 | SET-SLOT-OP : SetSlotIns(read-short()) 203 | CALL-SLOT-OP : CallSlotIns(read-short(), read-byte()) 204 | CALL-OP : CallIns(read-short(), read-byte()) 205 | SET-LOCAL-OP : SetLocalIns(read-short()) 206 | GET-LOCAL-OP : GetLocalIns(read-short()) 207 | SET-GLOBAL-OP : SetGlobalIns(read-short()) 208 | GET-GLOBAL-OP : GetGlobalIns(read-short()) 209 | BRANCH-OP : BranchIns(read-short()) 210 | GOTO-OP : GotoIns(read-short()) 211 | RETURN-OP : ReturnIns() 212 | DROP-OP : DropIns() 213 | else : fatal("Invalid opcode: %~" % [op]) 214 | 215 | public defn read-program () : 216 | val numc = read-short() 217 | val consts = to-list $ seq(read-const{}, 0 to numc) 218 | val slots = read-slots() 219 | val entry = read-short() 220 | Program(consts, slots, entry) -------------------------------------------------------------------------------- /src/bytecode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | #include "bytecode.h" 6 | 7 | //============================================================ 8 | //==================== FILE READING ========================== 9 | //============================================================ 10 | 11 | Vector* read_slots (); 12 | Vector* read_values (); 13 | Vector* read_code (); 14 | 15 | static FILE* inputfile; 16 | 17 | static char read_byte () { 18 | int i = fgetc(inputfile); 19 | if(i < 0) { 20 | printf("Unexpected end of file.\n"); 21 | exit(-1); 22 | } 23 | return (char)i; 24 | } 25 | static int read_short () { 26 | unsigned char b1 = read_byte(); 27 | unsigned char b2 = read_byte(); 28 | return b1 + (b2 << 8); 29 | } 30 | static int read_int () { 31 | unsigned char b1 = read_byte(); 32 | unsigned char b2 = read_byte(); 33 | unsigned char b3 = read_byte(); 34 | unsigned char b4 = read_byte(); 35 | return (int)b1 + ((int)b2 << 8) + ((int)b3 << 16) + ((int)b4 << 24); 36 | } 37 | static char* read_string () { 38 | int len = read_int(); 39 | char* str = malloc(len + 1); 40 | for(int i=0; itag = op; \ 50 | return o; \ 51 | } 52 | #define RETURN_NEW_INS1(TAG, x, xv) \ 53 | { \ 54 | TAG* o = malloc(sizeof(TAG)); \ 55 | o->tag = op; \ 56 | o->x = xv; \ 57 | return (ByteIns*)o; \ 58 | } 59 | #define RETURN_NEW_INS2(TAG, x, xv, y, yv) \ 60 | { \ 61 | TAG* o = malloc(sizeof(TAG)); \ 62 | o->tag = op; \ 63 | o->x = xv; \ 64 | o->y = yv; \ 65 | return (ByteIns*)o; \ 66 | } 67 | 68 | ByteIns* read_ins () { 69 | char op = read_byte(); 70 | switch(op){ 71 | case LABEL_OP: 72 | RETURN_NEW_INS1(LabelIns, 73 | name, read_short()); 74 | case LIT_OP: 75 | RETURN_NEW_INS1(LitIns, 76 | idx, read_short()); 77 | case PRINTF_OP: 78 | RETURN_NEW_INS2(PrintfIns, 79 | format, read_short(), 80 | arity, read_byte()); 81 | case ARRAY_OP: 82 | RETURN_NEW_INS0(); 83 | case OBJECT_OP: 84 | RETURN_NEW_INS1(ObjectIns, 85 | class, read_short()); 86 | case SLOT_OP: 87 | RETURN_NEW_INS1(SlotIns, 88 | name, read_short()); 89 | case SET_SLOT_OP: 90 | RETURN_NEW_INS1(SetSlotIns, 91 | name, read_short()); 92 | case CALL_SLOT_OP: 93 | RETURN_NEW_INS2(CallSlotIns, 94 | name, read_short(), 95 | arity, read_byte()); 96 | case CALL_OP: 97 | RETURN_NEW_INS2(CallIns, 98 | name, read_short(), 99 | arity, read_byte()); 100 | case SET_LOCAL_OP: 101 | RETURN_NEW_INS1(SetLocalIns, 102 | idx, read_short()); 103 | case GET_LOCAL_OP: 104 | RETURN_NEW_INS1(GetLocalIns, 105 | idx, read_short()); 106 | case SET_GLOBAL_OP: 107 | RETURN_NEW_INS1(SetGlobalIns, 108 | name, read_short()); 109 | case GET_GLOBAL_OP: 110 | RETURN_NEW_INS1(GetGlobalIns, 111 | name, read_short()); 112 | case BRANCH_OP: 113 | RETURN_NEW_INS1(BranchIns, 114 | name, read_short()); 115 | case GOTO_OP: 116 | RETURN_NEW_INS1(GotoIns, 117 | name, read_short()); 118 | case RETURN_OP: 119 | RETURN_NEW_INS0(); 120 | case DROP_OP: 121 | RETURN_NEW_INS0(); 122 | default: 123 | printf("Unrecognized Opcode: %d\n", op); 124 | exit(-1); 125 | } 126 | } 127 | 128 | #define RETURN_NEW_VAL0() \ 129 | {\ 130 | Value* o = malloc(sizeof(Value));\ 131 | o->tag = tag;\ 132 | return o;\ 133 | } 134 | #define RETURN_NEW_VAL1(TYPE, x, xv) \ 135 | {\ 136 | TYPE* o = malloc(sizeof(TYPE));\ 137 | o->tag = tag;\ 138 | o->x = xv;\ 139 | return (Value*)o;\ 140 | } 141 | #define RETURN_NEW_VAL4(TYPE, w, wv, x, xv, y, yv, z, zv) \ 142 | {\ 143 | TYPE* o = malloc(sizeof(TYPE));\ 144 | o->tag = tag;\ 145 | o->w = wv;\ 146 | o->x = xv;\ 147 | o->y = yv;\ 148 | o->z = zv;\ 149 | return (Value*)o;\ 150 | } 151 | Value* read_value () { 152 | char tag = read_byte(); 153 | switch(tag){ 154 | case INT_VAL: 155 | RETURN_NEW_VAL1(IntValue, 156 | value, read_int()); 157 | case NULL_VAL: 158 | RETURN_NEW_VAL0(); 159 | case STRING_VAL: 160 | RETURN_NEW_VAL1(StringValue, 161 | value, read_string()); 162 | case METHOD_VAL: 163 | RETURN_NEW_VAL4(MethodValue, 164 | name, read_short(), 165 | nargs, read_byte(), 166 | nlocals, read_short(), 167 | code, read_code()); 168 | case SLOT_VAL: 169 | RETURN_NEW_VAL1(SlotValue, 170 | name, read_short()); 171 | case CLASS_VAL: 172 | RETURN_NEW_VAL1(ClassValue, 173 | slots, read_slots()); 174 | default: 175 | printf("Unrecognized value tag: %d\n", tag); 176 | exit(-1); 177 | } 178 | } 179 | 180 | Vector* read_slots () { 181 | Vector* v = make_vector(); 182 | int n = read_short(); 183 | for(int i=0; ivalues = read_values(); 207 | p->slots = read_slots(); 208 | p->entry = read_short(); 209 | return p; 210 | } 211 | 212 | Program* load_bytecode (char* filename) { 213 | inputfile = fopen(filename, "r"); 214 | if(!inputfile){ 215 | printf("Could not read file %s.\n", filename); 216 | exit(-1); 217 | } 218 | Program* p = read_program(); 219 | fclose(inputfile); 220 | return p; 221 | } 222 | 223 | //============================================================ 224 | //===================== PRINTING ============================= 225 | //============================================================ 226 | 227 | void print_value (Value* v); 228 | 229 | void print_value (Value* v) { 230 | switch(v->tag){ 231 | case INT_VAL:{ 232 | IntValue* v2 = (IntValue*)v; 233 | printf("Int(%d)", v2->value); 234 | break; 235 | } 236 | case NULL_VAL:{ 237 | printf("Null"); 238 | break; 239 | } 240 | case STRING_VAL:{ 241 | StringValue* v2 = (StringValue*)v; 242 | printf("String("); 243 | print_string(v2->value); 244 | printf(")"); 245 | break; 246 | } 247 | case METHOD_VAL:{ 248 | MethodValue* v2 = (MethodValue*)v; 249 | printf("Method(#%d, nargs:%d, nlocals:%d) :", v2->name, v2->nargs, v2->nlocals); 250 | for(int i=0; icode->size; i++){ 251 | printf("\n "); 252 | print_ins(vector_get(v2->code, i)); 253 | } 254 | break; 255 | } 256 | case SLOT_VAL:{ 257 | SlotValue* v2 = (SlotValue*)v; 258 | printf("Slot(#%d)", v2->name); 259 | break; 260 | } 261 | case CLASS_VAL:{ 262 | ClassValue* v2 = (ClassValue*)v; 263 | printf("Class("); 264 | for(int i=0; islots->size; i++){ 265 | if(i > 0) printf(", "); 266 | printf("#%d", (int)vector_get(v2->slots,i)); 267 | } 268 | printf(")"); 269 | break; 270 | } 271 | default: 272 | printf("Value with unknown tag: %d\n", v->tag); 273 | exit(-1); 274 | } 275 | } 276 | 277 | void print_ins (ByteIns* ins) { 278 | switch(ins->tag){ 279 | case LABEL_OP:{ 280 | LabelIns* i = (LabelIns*)ins; 281 | printf("label #%d", i->name); 282 | break; 283 | } 284 | case LIT_OP:{ 285 | LitIns* i = (LitIns*)ins; 286 | printf(" lit #%d", i->idx); 287 | break; 288 | } 289 | case PRINTF_OP:{ 290 | PrintfIns* i = (PrintfIns*)ins; 291 | printf(" printf #%d %d", i->format, i->arity); 292 | break; 293 | } 294 | case ARRAY_OP:{ 295 | printf(" array"); 296 | break; 297 | } 298 | case OBJECT_OP:{ 299 | ObjectIns* i = (ObjectIns*)ins; 300 | printf(" object #%d", i->class); 301 | break; 302 | } 303 | case SLOT_OP:{ 304 | SlotIns* i = (SlotIns*)ins; 305 | printf(" slot #%d", i->name); 306 | break; 307 | } 308 | case SET_SLOT_OP:{ 309 | SetSlotIns* i = (SetSlotIns*)ins; 310 | printf(" set-slot #%d", i->name); 311 | break; 312 | } 313 | case CALL_SLOT_OP:{ 314 | CallSlotIns* i = (CallSlotIns*)ins; 315 | printf(" call-slot #%d %d", i->name, i->arity); 316 | break; 317 | } 318 | case CALL_OP:{ 319 | CallIns* i = (CallIns*)ins; 320 | printf(" call #%d %d", i->name, i->arity); 321 | break; 322 | } 323 | case SET_LOCAL_OP:{ 324 | SetLocalIns* i = (SetLocalIns*)ins; 325 | printf(" set local %d", i->idx); 326 | break; 327 | } 328 | case GET_LOCAL_OP:{ 329 | GetLocalIns* i = (GetLocalIns*)ins; 330 | printf(" get local %d", i->idx); 331 | break; 332 | } 333 | case SET_GLOBAL_OP:{ 334 | SetGlobalIns* i = (SetGlobalIns*)ins; 335 | printf(" set global #%d", i->name); 336 | break; 337 | } 338 | case GET_GLOBAL_OP:{ 339 | GetGlobalIns* i = (GetGlobalIns*)ins; 340 | printf(" get global #%d", i->name); 341 | break; 342 | } 343 | case BRANCH_OP:{ 344 | BranchIns* i = (BranchIns*)ins; 345 | printf(" branch #%d", i->name); 346 | break; 347 | } 348 | case GOTO_OP:{ 349 | GotoIns* i = (GotoIns*)ins; 350 | printf(" goto #%d", i->name); 351 | break; 352 | } 353 | case RETURN_OP:{ 354 | printf(" return"); 355 | break; 356 | } 357 | case DROP_OP:{ 358 | printf(" drop"); 359 | break; 360 | } 361 | } 362 | } 363 | 364 | void print_prog (Program* p) { 365 | printf("Constants :"); 366 | Vector* vs = p->values; 367 | for(int i=0; isize; i++){ 368 | printf("\n #%d: ", i); 369 | print_value(vector_get(vs, i)); 370 | } 371 | printf("\nGlobals :"); 372 | for(int i=0; islots->size; i++) 373 | printf("\n #%d", (int)vector_get(p->slots, i)); 374 | printf("\nEntry : #%d", p->entry); 375 | } 376 | -------------------------------------------------------------------------------- /src/eval.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/eval : 2 | import core 3 | import collections 4 | import feeny/ir 5 | 6 | ;============================================================ 7 | ;====================== Driver ============================== 8 | ;============================================================ 9 | public defn eval (e:ScopeStmt) -> False : 10 | try : 11 | val genv = Env(Null()) 12 | eval(genv, genv, e) 13 | catch (e:EvalException) : 14 | println("ERROR: %~" % [e]) 15 | false 16 | 17 | ;============================================================ 18 | ;================== Evaluation Exception ==================== 19 | ;============================================================ 20 | 21 | public deftype EvalException <: Exception 22 | defn EvalException (msg) : 23 | new EvalException : 24 | defmethod print (o:OutputStream, this) : 25 | print(o, msg) 26 | 27 | ;============================================================ 28 | ;================= Environment Structures =================== 29 | ;============================================================ 30 | 31 | defstruct Null 32 | 33 | defstruct Env : 34 | entries: HashTable with: (init => HashTable()) 35 | parent: Env|Null 36 | 37 | deftype EnvValue 38 | defstruct VarValue <: EnvValue : 39 | value: Int|Array|Env|Null 40 | defstruct CodeValue <: EnvValue : 41 | args: List 42 | body: ScopeStmt 43 | 44 | defn add (e:Env, n:Symbol, v:EnvValue) : 45 | if key?(entries(e), n) : 46 | throw $ EvalException("Variable %~ already defined in environment." % [n]) 47 | else : 48 | entries(e)[n] = v 49 | 50 | defn get (e:Env, n:Symbol) : 51 | if key?(entries(e), n) : 52 | entries(e)[n] 53 | else if parent(e) is-not Null : 54 | (parent(e) as Env)[n] 55 | else : 56 | throw $ EvalException("Could not resolve variable %~ in environment." % [n]) 57 | 58 | defn set (e:Env, n:Symbol, v:EnvValue) : 59 | if key?(entries(e), n) : 60 | entries(e)[n] = v 61 | else if parent(e) is-not Null : 62 | (parent(e) as Env)[n] = v 63 | else : 64 | throw $ EvalException("Could not resolve variable %~ in environment." % [n]) 65 | 66 | defmethod print (o:OutputStream, e:Env) : 67 | print(o, "[OBJECT]") 68 | 69 | defmethod print (o:OutputStream, e:Null) : 70 | print(o, "Null") 71 | 72 | ;============================================================ 73 | ;===================== Evaluator ============================ 74 | ;============================================================ 75 | 76 | defn eval (genv:Env, env:Env, e:Exp) -> Int|Array|Env|Null : 77 | match(e) : 78 | (e:IntExp) : 79 | value(e) 80 | (e:NullExp) : 81 | Null() 82 | (e:PrintfExp) : 83 | val exps* = for e in exps(e) map : eval(genv, env, e) 84 | pprint(format(e), exps*) 85 | Null() 86 | (e:ArrayExp) : 87 | val len* = fint(eval(genv, env, length(e))) 88 | val init* = eval(genv, env, init(e)) 89 | Array(len*, init*) 90 | (e:ObjectExp) : 91 | val obj = match(eval(genv, env, parent(e))) : 92 | (v:Env|Null) : Env(v) 93 | (v:Int) : throw $ EvalException("Cannot extend base object of type int.") 94 | (v:Array) : throw $ EvalException("Cannot extend base object of type array.") 95 | for s in slots(e) do : 96 | match(s) : 97 | (s:SlotVar) : 98 | val v = eval(genv, env, exp(s)) 99 | add(obj, name(s), VarValue(v)) 100 | (s:SlotMethod) : 101 | add(obj, name(s), CodeValue(args(s), body(s))) 102 | obj 103 | (e:SlotExp) : 104 | val obj = eval(genv, env, exp(e)) 105 | get-slot(obj, name(e)) 106 | (e:SetSlotExp) : 107 | val obj = eval(genv, env, exp(e)) 108 | val value = eval(genv, env, value(e)) 109 | set-slot(obj, name(e), value) 110 | value 111 | (e:CallSlotExp) : 112 | val obj = eval(genv, env, exp(e)) 113 | val args* = for a in args(e) map : eval(genv, env, a) 114 | call-slot(genv, obj, name(e), args*) 115 | (e:CallExp) : 116 | val argvs = for a in args(e) map : eval(genv, env, a) 117 | match(genv[name(e)]) : 118 | (v:VarValue) : 119 | throw $ EvalException("Variable %~ cannot be called as function." % [name(e)]) 120 | (v:CodeValue) : 121 | if length(argvs) != length(args(v)) : 122 | throw $ EvalException("Incorrect number of arguments to function %~." % [name(e)]) 123 | val env* = Env(genv) 124 | for (a in args(v), ea in argvs) do : 125 | add(env*, a, VarValue(ea)) 126 | eval(genv, env*, body(v)) 127 | (e:SetExp) : 128 | val exp* = eval(genv, env, exp(e)) 129 | match(env[name(e)]) : 130 | (v:VarValue) : 131 | env[name(e)] = VarValue(exp*) 132 | exp* 133 | (v:CodeValue) : 134 | throw $ EvalException("Cannot assign to function %~." % [name(e)]) 135 | (e:IfExp) : 136 | val p = fbool(eval(genv, env, pred(e))) 137 | eval{genv, Env(env), _} $ 138 | if p : conseq(e) 139 | else : alt(e) 140 | (e:WhileExp) : 141 | while fbool(eval(genv, env, pred(e))) : 142 | eval(genv, Env(env), body(e)) 143 | Null() 144 | (e:RefExp) : 145 | match(env[name(e)]) : 146 | (v:VarValue) : 147 | value(v) 148 | (v:CodeValue) : 149 | throw $ EvalException("Cannot directly refer to function %~." % [name(e)]) 150 | 151 | defn* eval (genv:Env, env:Env, e:ScopeStmt) -> Int|Array|Env|Null : 152 | match(e) : 153 | (e:ScopeVar) : 154 | val v = eval(genv, env, exp(e)) 155 | add(env, name(e), VarValue(v)) 156 | Null() 157 | (e:ScopeFn) : 158 | add(env, name(e), CodeValue(args(e), body(e))) 159 | Null() 160 | (e:ScopeExp) : 161 | eval(genv, env, exp(e)) 162 | (e:ScopeSeq) : 163 | eval(genv, env, a(e)) 164 | eval(genv, env, b(e)) 165 | (e) : 166 | fatal("Not supported: %~" % [e]) 167 | 168 | defn fbool (v) : 169 | match(v) : 170 | (v:Null) : false 171 | (v) : true 172 | 173 | defn fint (v) : 174 | match(v) : 175 | (v:Int) : v 176 | (v:Array) : throw $ EvalException("Expected an int value but received an array.") 177 | (v:Env) : throw $ EvalException("Expected an int value but received an object.") 178 | (v:Null) : throw $ EvalException("Expected an int value but received null.") 179 | 180 | ;=== Pretty Printing === 181 | defn pprint (format:String, args:Seqable) : 182 | val arg-seq = to-seq(args) 183 | val n = length(format) 184 | let loop (i:Int = 0) : 185 | if i < n : 186 | switch(format[i]) : 187 | '~' : 188 | if empty?(arg-seq) : 189 | throw $ EvalException $ 190 | "Format string %~ is expecting more arguments." % [format] 191 | print(next(arg-seq)) 192 | loop(i + 1) 193 | else : 194 | print(format[i]) 195 | loop(i + 1) 196 | else : 197 | if not empty?(arg-seq) : 198 | throw $ EvalException $ 199 | "Unexpected end of format string %~. More arguments remaining." % [format] 200 | 201 | ;=== Method Interface === 202 | defmulti set-slot (o:Int|Array|Env|Null, name:Symbol, value:Int|Array|Env|Null) -> False 203 | defmulti get-slot (o:Int|Array|Env|Null, name:Symbol) -> Int|Array|Env|Null 204 | defmulti call-slot (genv:Env, o:Int|Array|Env|Null, name:Symbol, args:List) -> Int|Array|Env|Null 205 | 206 | ;=== Integer Methods === 207 | defmethod get-slot (o:Int, name:Symbol) : 208 | throw $ EvalException("No variable slot named %~ in integer %~." % [name, o]) 209 | 210 | defmethod get-slot (o:Int, name:Symbol) : 211 | throw $ EvalException("Cannot assign slot named %~ in integer %~." % [name, o]) 212 | 213 | defmethod call-slot (genv:Env, x:Int, name:Symbol, args:List) : 214 | defn to-fbool (b:True|False) : 215 | 0 when b else Null() 216 | if length(args) != 1 : 217 | throw $ EvalException("Integer method requires single integer argument.") 218 | if head(args) is-not Int : 219 | throw $ EvalException("Integer method requires single integer argument.") 220 | val y = head(args) as Int 221 | switch(name) : 222 | `eq : to-fbool(x == y) 223 | `lt : to-fbool(x < y) 224 | `le : to-fbool(x <= y) 225 | `gt : to-fbool(x > y) 226 | `ge : to-fbool(x >= y) 227 | `add : x + y 228 | `sub : x - y 229 | `mul : x * y 230 | `div : x / y 231 | `mod : x % y 232 | else : throw $ EvalException("No method %~ for integer." % [name]) 233 | 234 | ;=== Array Methods === 235 | defmethod get-slot (o:Array, name:Symbol) : 236 | throw $ EvalException("No variable slot named %~ in array." % [name, o]) 237 | 238 | defmethod get-slot (o:Array, name:Symbol) : 239 | throw $ EvalException("Cannot assign slot named %~ in array." % [name, o]) 240 | 241 | defmethod call-slot (genv:Env, a:Array, name:Symbol, args:List) : 242 | defn ensure-nargs (n:Int) : 243 | if length(args) != n : 244 | throw $ EvalException("Array method %~ requires %~ arguments." % [name, n]) 245 | defn ensure-index-arg (i:Int) : 246 | if args[i] is-not Int : 247 | throw $ EvalException("Array method %~ requires argument %~ to be an integer." % [name, i]) 248 | val x = args[i] as Int 249 | if x < 0 or x >= length(a) : 250 | throw $ EvalException("Index %~ out of bounds for array method %~." % [x, name]) 251 | 252 | switch(name) : 253 | `get : 254 | ensure-nargs(1) 255 | ensure-index-arg(0) 256 | a[args[0] as Int] 257 | `set : 258 | ensure-nargs(2) 259 | ensure-index-arg(0) 260 | a[args[0] as Int] = args[1] 261 | Null() 262 | `length : 263 | ensure-nargs(0) 264 | length(a) 265 | else : 266 | throw $ EvalException("No method %~ for array." % [name]) 267 | 268 | ;=== Null Methods === 269 | defmethod get-slot (n:Null, name:Symbol) : 270 | throw $ EvalException("No slot %~ for null." % [name]) 271 | 272 | defmethod set-slot (n:Null, name:Symbol, v:Int|Array|Env|Null) : 273 | throw $ EvalException("No slot %~ for null." % [name]) 274 | 275 | defmethod call-slot (genv:Env, n:Null, name:Symbol, args:List) : 276 | throw $ EvalException("No method %~ for null." % [name]) 277 | 278 | ;=== Object Methods === 279 | defmethod get-slot (o:Env, name:Symbol) : 280 | match(o[name]) : 281 | (v:VarValue) : 282 | value(v) 283 | (v:CodeValue) : 284 | throw $ EvalException("Cannot directly access method slot %~." % [name]) 285 | 286 | defmethod set-slot (o:Env, name:Symbol, v:Int|Array|Env|Null) : 287 | match(o[name]) : 288 | (e:VarValue) : 289 | o[name] = VarValue(v) 290 | (e:CodeValue) : 291 | throw $ EvalException("Cannot assign to method slot %~ in object." % [name]) 292 | 293 | defmethod call-slot (genv:Env, o:Env, name:Symbol, argvs:List) : 294 | match(o[name]) : 295 | (v:VarValue) : 296 | throw $ EvalException("Variable slot %~ in object cannot be called as method." % [name]) 297 | (v:CodeValue) : 298 | if length(args(v)) != length(argvs) : 299 | throw $ EvalException("Incorrect number of arguments to method %~ for object." % [name]) 300 | val env* = Env(genv) 301 | add(env*, `this, VarValue(o)) 302 | for (a in args(v), ea in argvs) do : 303 | add(env*, a, VarValue(ea)) 304 | eval(genv, env*, body(v)) 305 | -------------------------------------------------------------------------------- /src/ir.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/ir : 2 | import core 3 | import collections 4 | 5 | ;============================================================ 6 | ;=================== IR Definition ========================== 7 | ;============================================================ 8 | 9 | public deftype Exp 10 | public defstruct IntExp <: Exp : (value:Int) 11 | public defstruct NullExp <: Exp 12 | public defstruct PrintfExp <: Exp : (format:String, exps:List) 13 | public defstruct ArrayExp <: Exp : (length:Exp, init:Exp) 14 | public defstruct ObjectExp <: Exp : (parent:Exp, slots:List) 15 | public defstruct SlotExp <: Exp : (name:Symbol, exp:Exp) 16 | public defstruct SetSlotExp <: Exp : (name:Symbol, exp:Exp, value:Exp) 17 | public defstruct CallSlotExp <: Exp : (name:Symbol, exp:Exp, args:List) 18 | public defstruct CallExp <: Exp : (name:Symbol, args:List) 19 | public defstruct SetExp <: Exp : (name:Symbol, exp:Exp) 20 | public defstruct IfExp <: Exp : (pred:Exp, conseq:ScopeStmt, alt:ScopeStmt) 21 | public defstruct WhileExp <: Exp : (pred:Exp, body:ScopeStmt) 22 | public defstruct RefExp <: Exp : (name:Symbol) 23 | 24 | public deftype SlotStmt 25 | public defstruct SlotVar <: SlotStmt : (name:Symbol, exp:Exp) 26 | public defstruct SlotMethod <: SlotStmt : (name:Symbol, args:List, body:ScopeStmt) 27 | 28 | public deftype ScopeStmt 29 | public defstruct ScopeVar <: ScopeStmt : (name:Symbol, exp:Exp) 30 | public defstruct ScopeFn <: ScopeStmt : (name:Symbol, args:List, body:ScopeStmt) 31 | public defstruct ScopeBegin <: ScopeStmt : (stmts:List) 32 | public defstruct ScopeSeq <: ScopeStmt : (a:ScopeStmt, b:ScopeStmt) 33 | public defstruct ScopeExp <: ScopeStmt : (exp:Exp) 34 | 35 | ;============================================================ 36 | ;=================== IR Printer ============================= 37 | ;============================================================ 38 | 39 | defn written (x) : "%~" % [x] 40 | 41 | defmethod print (o:OutputStream, e:Exp) : 42 | print{o, _} $ match(e) : 43 | (e:IntExp) : value(e) 44 | (e:NullExp) : "null" 45 | (e:PrintfExp) : "printf(%,)" % [cons(written(format(e)), exps(e))] 46 | (e:ArrayExp) : "array(%~, %~)" % [length(e), init(e)] 47 | (e:ObjectExp) : "object(%_) : (%,)" % [parent(e), slots(e)] 48 | (e:SlotExp) : "%~.%~" % [exp(e), name(e)] 49 | (e:SetSlotExp) : "%~.%~ = %~" % [exp(e), name(e), value(e)] 50 | (e:CallSlotExp) : "%~.%~(%,)" % [exp(e), name(e), args(e)] 51 | (e:CallExp) : "%~(%,)" % [name(e), args(e)] 52 | (e:SetExp) : "%~ = %~" % [name(e), exp(e)] 53 | (e:IfExp) : "if %~ : %~ else : %~" % [pred(e), conseq(e), alt(e)] 54 | (e:WhileExp) : "while %~ : %~" % [pred(e), body(e)] 55 | (e:RefExp) : name(e) 56 | 57 | defmethod print (o:OutputStream, e:SlotStmt) : 58 | print{o, _} $ match(e) : 59 | (e:SlotVar) : "var %~ = %~" % [name(e), exp(e)] 60 | (e:SlotMethod) : "method %~ (%,) : %~" % [name(e), args(e), body(e)] 61 | 62 | defmethod print (o:OutputStream, e:ScopeStmt) : 63 | print{o, _} $ match(e) : 64 | (e:ScopeVar) : "var %~ = %~" % [name(e), exp(e)] 65 | (e:ScopeFn) : "defn %~ (%,) : %~" % [name(e), args(e), body(e)] 66 | (e:ScopeExp) : exp(e) 67 | (e:ScopeBegin) : "(%*)" % [join(stmts(e), " ")] 68 | (e:ScopeSeq) : "%~ ; %~" % [a(e), b(e)] 69 | 70 | ;============================================================ 71 | ;==================== Mappers =============================== 72 | ;============================================================ 73 | 74 | public defn map (f:Exp -> Exp, s:?T&ScopeStmt) -> T : 75 | {_ as T&ScopeStmt} $ match(s) : 76 | (s:ScopeVar) : ScopeVar(name(s), f(exp(s))) 77 | (s:ScopeFn) : s 78 | (s:ScopeBegin) : s 79 | (s:ScopeSeq) : s 80 | (s:ScopeExp) : ScopeExp(f(exp(s))) 81 | 82 | public defn map (f:ScopeStmt -> ScopeStmt, s:?T&ScopeStmt) -> T : 83 | {_ as T&ScopeStmt} $ match(s) : 84 | (s:ScopeVar) : s 85 | (s:ScopeFn) : ScopeFn(name(s), args(s), f(body(s))) 86 | (s:ScopeBegin) : ScopeBegin(map(f, stmts(s))) 87 | (s:ScopeSeq) : ScopeSeq(f(a(s)), f(b(s))) 88 | (s:ScopeExp) : s 89 | 90 | public defn map (f:Exp -> Exp, s:?T&SlotStmt) -> T : 91 | {_ as T&SlotStmt} $ match(s) : 92 | (s:SlotVar) : SlotVar(name(s), f(exp(s))) 93 | (s:SlotMethod) : s 94 | 95 | public defn map (f:ScopeStmt -> ScopeStmt, s:?T&SlotStmt) -> T : 96 | {_ as T&SlotStmt} $ match(s) : 97 | (s:SlotVar) : s 98 | (s:SlotMethod) : SlotMethod(name(s), args(s), f(body(s))) 99 | 100 | public defn map (f:Exp -> Exp, e:?T&Exp) -> T : 101 | {_ as T&Exp} $ match(e) : 102 | (e:IntExp) : e 103 | (e:NullExp) : e 104 | (e:PrintfExp) : PrintfExp(format(e), map(f,exps(e))) 105 | (e:ArrayExp) : ArrayExp(f(length(e)), f(init(e))) 106 | (e:ObjectExp) : ObjectExp(f(parent(e)), slots(e)) 107 | (e:SlotExp) : SlotExp(name(e), f(exp(e))) 108 | (e:SetSlotExp) : SetSlotExp(name(e), f(exp(e)), f(value(e))) 109 | (e:CallSlotExp) : CallSlotExp(name(e), f(exp(e)), map(f,args(e))) 110 | (e:CallExp) : CallExp(name(e), map(f,args(e))) 111 | (e:SetExp) : SetExp(name(e), f(exp(e))) 112 | (e:IfExp) : IfExp(f(pred(e)), conseq(e), alt(e)) 113 | (e:WhileExp) : WhileExp(f(pred(e)), body(e)) 114 | (e:RefExp) : e 115 | 116 | public defn map (f:ScopeStmt -> ScopeStmt, e:?T&Exp) -> T : 117 | {_ as T&Exp} $ match(e) : 118 | (e:IntExp) : e 119 | (e:NullExp) : e 120 | (e:PrintfExp) : e 121 | (e:ArrayExp) : e 122 | (e:ObjectExp) : e 123 | (e:SlotExp) : e 124 | (e:SetSlotExp) : e 125 | (e:CallSlotExp) : e 126 | (e:CallExp) : e 127 | (e:SetExp) : e 128 | (e:IfExp) : IfExp(pred(e), f(conseq(e)), f(alt(e))) 129 | (e:WhileExp) : WhileExp(pred(e), f(body(e))) 130 | (e:RefExp) : e 131 | 132 | public defn map (f:SlotStmt -> SlotStmt, e:?T&Exp) -> T : 133 | {_ as T&Exp} $ match(e) : 134 | (e:IntExp) : e 135 | (e:NullExp) : e 136 | (e:PrintfExp) : e 137 | (e:ArrayExp) : e 138 | (e:ObjectExp) : ObjectExp(parent(e), map(f,slots(e))) 139 | (e:SlotExp) : e 140 | (e:SetSlotExp) : e 141 | (e:CallSlotExp) : e 142 | (e:CallExp) : e 143 | (e:SetExp) : e 144 | (e:IfExp) : e 145 | (e:WhileExp) : e 146 | (e:RefExp) : e 147 | 148 | ;============================================================ 149 | ;================ Recursive Mappers ========================= 150 | ;============================================================ 151 | 152 | public defn mapr (f:ScopeStmt -> ScopeStmt, l:?T&SlotStmt) -> T : 153 | defn fe (e:Exp) : mapr(f, e) 154 | map(f, map(fe, l)) 155 | 156 | public defn mapr (f:ScopeStmt -> ScopeStmt, s:?T&Exp) -> T : 157 | defn fe (e:Exp) : mapr(f, e) 158 | defn fl (l:SlotStmt) : mapr(f, l) 159 | map(f, map(fl, map(fe, s))) 160 | 161 | public defn mapr (f:ScopeStmt -> ScopeStmt, s:?T&ScopeStmt) -> T : 162 | defn fe (e:Exp) : mapr(f, e) 163 | map(f, map(fe, s)) as T&ScopeStmt 164 | 165 | public defn mapr (f:Exp -> Exp, s:?T&ScopeStmt) -> T : 166 | defn fs (s:ScopeStmt) : mapr(f, s) 167 | map(f, map(fs, s)) as T&ScopeStmt 168 | 169 | ;============================================================ 170 | ;================ Serialization ============================= 171 | ;============================================================ 172 | 173 | ;Expression Tags 174 | val tag-counter = to-seq(0 to false) 175 | val INT-TAG = next(tag-counter) 176 | val NULL-TAG = next(tag-counter) 177 | val PRINTF-TAG = next(tag-counter) 178 | val ARRAY-TAG = next(tag-counter) 179 | val OBJECT-TAG = next(tag-counter) 180 | val SLOT-TAG = next(tag-counter) 181 | val SET-SLOT-TAG = next(tag-counter) 182 | val CALL-SLOT-TAG = next(tag-counter) 183 | val CALL-TAG = next(tag-counter) 184 | val SET-TAG = next(tag-counter) 185 | val IF-TAG = next(tag-counter) 186 | val WHILE-TAG = next(tag-counter) 187 | val REF-TAG = next(tag-counter) 188 | ;Statement Tags 189 | val SCOPE-VAR-TAG = next(tag-counter) 190 | val SCOPE-FN-TAG = next(tag-counter) 191 | val SCOPE-SEQ-TAG = next(tag-counter) 192 | val SCOPE-EXP-TAG = next(tag-counter) 193 | 194 | public defn save-ast (p:ScopeStmt, filename:String) : 195 | val file = FileOutputStream(filename) 196 | try : 197 | serialize(file, p) 198 | finally : 199 | close(file) 200 | 201 | defn serialize (o:FileOutputStream, program:ScopeStmt) : 202 | ;Tags 203 | defn tag (e:Exp) : 204 | match(e) : 205 | (e:IntExp) : INT-TAG 206 | (e:NullExp) : NULL-TAG 207 | (e:PrintfExp) : PRINTF-TAG 208 | (e:ArrayExp) : ARRAY-TAG 209 | (e:ObjectExp) : OBJECT-TAG 210 | (e:SlotExp) : SLOT-TAG 211 | (e:SetSlotExp) : SET-SLOT-TAG 212 | (e:CallSlotExp) : CALL-SLOT-TAG 213 | (e:CallExp) : CALL-TAG 214 | (e:SetExp) : SET-TAG 215 | (e:IfExp) : IF-TAG 216 | (e:WhileExp) : WHILE-TAG 217 | (e:RefExp) : REF-TAG 218 | defn tag (s:SlotStmt) : 219 | match(s) : 220 | (s:SlotVar) : SCOPE-VAR-TAG 221 | (s:SlotMethod) : SCOPE-FN-TAG 222 | defn tag (s:ScopeStmt) : 223 | match(s) : 224 | (s:ScopeVar) : SCOPE-VAR-TAG 225 | (s:ScopeFn) : SCOPE-FN-TAG 226 | (s:ScopeSeq) : SCOPE-SEQ-TAG 227 | (s:ScopeExp) : SCOPE-EXP-TAG 228 | 229 | ;Low Level 230 | defn emit (x:Byte|Char|Int) : 231 | put(o, x) 232 | defn emit (s:String) : 233 | emit(length(s)) 234 | do(emit, s) 235 | defn emit (s:Symbol) : 236 | emit(to-string(s)) 237 | defn emit (ss:List) : 238 | emit(length(ss)) 239 | do(emit, ss) 240 | 241 | ;High Level 242 | defn emit (s:ScopeStmt) : 243 | emit(tag(s)) 244 | match(s) : 245 | (s:ScopeVar) : 246 | emit(name(s)) 247 | emit(exp(s)) 248 | (s:ScopeFn) : 249 | emit(name(s)) 250 | emit(args(s)) 251 | emit(body(s)) 252 | (s:ScopeSeq) : 253 | emit(a(s)) 254 | emit(b(s)) 255 | (s:ScopeExp) : 256 | emit(exp(s)) 257 | (s) : 258 | fatal("Cannot serialize scope statement: %~." % [s]) 259 | defn emit (s:SlotStmt) : 260 | emit(tag(s)) 261 | match(s) : 262 | (s:SlotVar) : 263 | emit(name(s)) 264 | emit(exp(s)) 265 | (s:SlotMethod) : 266 | emit(name(s)) 267 | emit(args(s)) 268 | emit(body(s)) 269 | defn emit (e:Exp) : 270 | emit(tag(e)) 271 | match(e) : 272 | (e:IntExp) : 273 | emit(value(e)) 274 | (e:NullExp) : 275 | false 276 | (e:PrintfExp) : 277 | emit(format(e)) 278 | emit(exps(e)) 279 | (e:ArrayExp) : 280 | emit(length(e)) 281 | emit(init(e)) 282 | (e:ObjectExp) : 283 | emit(parent(e)) 284 | emit(slots(e)) 285 | (e:SlotExp) : 286 | emit(name(e)) 287 | emit(exp(e)) 288 | (e:SetSlotExp) : 289 | emit(name(e)) 290 | emit(exp(e)) 291 | emit(value(e)) 292 | (e:CallSlotExp) : 293 | emit(name(e)) 294 | emit(exp(e)) 295 | emit(args(e)) 296 | (e:CallExp) : 297 | emit(name(e)) 298 | emit(args(e)) 299 | (e:SetExp) : 300 | emit(name(e)) 301 | emit(exp(e)) 302 | (e:IfExp) : 303 | emit(pred(e)) 304 | emit(conseq(e)) 305 | emit(alt(e)) 306 | (e:WhileExp) : 307 | emit(pred(e)) 308 | emit(body(e)) 309 | (e:RefExp) : 310 | emit(name(e)) 311 | 312 | ;Driver 313 | emit(program) 314 | 315 | 316 | ;============================================================ 317 | ;=================== Deserialization ======================== 318 | ;============================================================ 319 | 320 | public defn load-ast (filename:String) : 321 | val f = FileInputStream(filename) 322 | try : 323 | read-ast(f) 324 | finally : 325 | close(f) 326 | 327 | defn read-ast (f:FileInputStream) : 328 | ;Errors 329 | defn read-error (x) : 330 | throw $ new Exception : 331 | defmethod print (o:OutputStream, this) : 332 | print(o, x) 333 | 334 | ;Low Level 335 | defn read-byte () : 336 | match(get-byte(f)) : 337 | (i:Byte) : i 338 | (f:False) : read-error("Unexpected end of file.") 339 | defn read-char () : 340 | match(get-char(f)) : 341 | (i:Char) : i 342 | (f:False) : read-error("Unexpected end of file.") 343 | defn read-int () : 344 | match(get-int(f)) : 345 | (i:Int) : i 346 | (f:False) : read-error("Unexpected end of file.") 347 | defn read-string () : 348 | val l = read-int() 349 | String(seq(read-char{}, 0 to l)) 350 | defn read-symbol () : 351 | to-symbol(read-string()) 352 | 353 | ;Lists 354 | defn read-list (f: () -> ?T) : 355 | val n = read-int() 356 | to-list $ seq(f{}, 0 to n) 357 | defn read-symbols () : read-list(read-symbol) 358 | defn read-exps () : read-list(read-exp) 359 | 360 | ;High Level 361 | defn read-slot () : 362 | val tag = read-int() 363 | switch(tag) : 364 | SCOPE-VAR-TAG : SlotVar(read-symbol(), read-exp()) 365 | SCOPE-FN-TAG : SlotMethod(read-symbol(), read-symbols(), read-stmt()) 366 | else : read-error("Unrecognized tag for slot statement: %~" % [tag]) 367 | 368 | defn read-stmt () : 369 | val tag = read-int() 370 | switch(tag) : 371 | SCOPE-VAR-TAG : ScopeVar(read-symbol(), read-exp()) 372 | SCOPE-FN-TAG : ScopeFn(read-symbol(), read-symbols(), read-stmt()) 373 | SCOPE-SEQ-TAG : ScopeSeq(read-stmt(), read-stmt()) 374 | SCOPE-EXP-TAG : ScopeExp(read-exp()) 375 | else : read-error("Unrecognized tag for scope statement: %~" % [tag]) 376 | 377 | defn read-exp () : 378 | val tag = read-int() 379 | switch(tag) : 380 | INT-TAG : IntExp(read-int()) 381 | NULL-TAG : NullExp() 382 | PRINTF-TAG : PrintfExp(read-string(), read-exps()) 383 | ARRAY-TAG : ArrayExp(read-exp(), read-exp()) 384 | OBJECT-TAG : ObjectExp(read-exp(), read-list(read-slot)) 385 | SLOT-TAG : SlotExp(read-symbol(), read-exp()) 386 | SET-SLOT-TAG : SetSlotExp(read-symbol(), read-exp(), read-exp()) 387 | CALL-SLOT-TAG : CallSlotExp(read-symbol(), read-exp(), read-exps()) 388 | CALL-TAG : CallExp(read-symbol(), read-exps()) 389 | SET-TAG : SetExp(read-symbol(), read-exp()) 390 | IF-TAG : IfExp(read-exp(), read-stmt(), read-stmt()) 391 | WHILE-TAG : WhileExp(read-exp(), read-stmt()) 392 | REF-TAG : RefExp(read-symbol()) 393 | else : read-error("Unrecognized tag for expression: %~" % [tag]) 394 | 395 | ;Driver 396 | read-stmt() -------------------------------------------------------------------------------- /src/vm.stanza: -------------------------------------------------------------------------------- 1 | defpackage feeny/vm : 2 | import core 3 | import collections 4 | import feeny/abc 5 | 6 | ;============================================================ 7 | ;================== Evaluation Exception ==================== 8 | ;============================================================ 9 | public deftype EvalException <: Exception 10 | defn EvalException (msg) : 11 | new EvalException : 12 | defmethod print (o:OutputStream, this) : 13 | print(o, msg) 14 | 15 | ;============================================================ 16 | ;====================== VM Objects ========================== 17 | ;============================================================ 18 | defstruct Null 19 | 20 | defstruct Object : 21 | class: Int 22 | parent: Object|Null 23 | slots: Array 24 | 25 | defmethod print (o:OutputStream, n:Null) : 26 | print(o, "null") 27 | 28 | defmethod print (o:OutputStream, obj:Object) : 29 | print(o, "object") 30 | 31 | ;============================================================ 32 | ;================== Linked Program ========================== 33 | ;============================================================ 34 | deftype LIns 35 | defstruct LInt <: LIns : (value:Int) 36 | defstruct LNull <: LIns 37 | defstruct LPrintf <: LIns : (format:String, arity:Int) 38 | defstruct LArray <: LIns 39 | defstruct LObject <: LIns : (class:Int, arity:Int) 40 | defstruct LSlot <: LIns : (name:Symbol) 41 | defstruct LSetSlot <: LIns : (name:Symbol) 42 | defstruct LCallSlot <: LIns : (name:Symbol, arity:Int) 43 | defstruct LCall <: LIns : (code:Int, arity:Int) 44 | defstruct LSetLocal <: LIns : (idx:Int) 45 | defstruct LGetLocal <: LIns : (idx:Int) 46 | defstruct LSetGlobal <: LIns : (idx:Int) 47 | defstruct LGetGlobal <: LIns : (idx:Int) 48 | defstruct LBranch <: LIns : (code:Int) 49 | defstruct LGoto <: LIns : (code:Int) 50 | defstruct LReturn <: LIns 51 | defstruct LDrop <: LIns 52 | defstruct LFrame <: LIns : (nargs:Int, nlocals:Int) 53 | defstruct LClass : (slots: Array) 54 | 55 | deftype ClassSlot 56 | defmulti name (c:ClassSlot) -> Symbol 57 | defstruct VarSlot <: ClassSlot : (name:Symbol with: (as-method => true), idx:Int) 58 | defstruct CodeSlot <: ClassSlot : (name:Symbol with: (as-method => true), code:Int) 59 | 60 | defstruct LProg : (ins: Array, classes: Array, num-globals:Int, entry:Int) 61 | 62 | defmethod print (o:OutputStream, i:LIns) : 63 | print{o, _} $ match(i) : 64 | (i:LInt) : "int %~" % [value(i)] 65 | (i:LNull) : "null" 66 | (i:LPrintf) : "printf %~ %~" % [format(i), arity(i)] 67 | (i:LArray) : "array" 68 | (i:LObject) : "object %~ %~" % [class(i), arity(i)] 69 | (i:LSlot) : "slot %~" % [name(i)] 70 | (i:LSetSlot) : "set-slot %~" % [name(i)] 71 | (i:LCallSlot) : "call-slot %~ %~" % [name(i), arity(i)] 72 | (i:LCall) : "call %~ %~" % [code(i), arity(i)] 73 | (i:LSetLocal) : "set local %~" % [idx(i)] 74 | (i:LGetLocal) : "get local %~" % [idx(i)] 75 | (i:LSetGlobal) : "set global %~" % [idx(i)] 76 | (i:LGetGlobal) : "get global %~" % [idx(i)] 77 | (i:LBranch) : "branch %~" % [code(i)] 78 | (i:LGoto) : "goto %~" % [code(i)] 79 | (i:LReturn) : "return" 80 | (i:LDrop) : "drop" 81 | (i:LFrame) : "frame %~ %~" % [nargs(i), nlocals(i)] 82 | 83 | defmethod print (o:OutputStream, c:LClass) : 84 | print{o, _} $ "class(%*)" % [join(slots(c), ", ")] 85 | 86 | defmethod print (o:OutputStream, s:ClassSlot) : 87 | print{o, _} $ match(s) : 88 | (s:VarSlot) : "slot %~ %~" % [name(s), idx(s)] 89 | (s:CodeSlot) : "method %~ %~" % [name(s), code(s)] 90 | 91 | defmethod print (o:OutputStream, p:LProg) : 92 | val io = IndentedStream(o) 93 | print(o, "Entry: %~" % [entry(p)]) 94 | print(o, "\nClasses:") 95 | for (c in classes(p), i in 0 to false) do : 96 | print(io, "\nClass %~ :" % [i]) 97 | for s in slots(c) do : 98 | print(io, "\n %~" % [s]) 99 | print(o, "\nInstructions:") 100 | for (c in ins(p), i in 0 to false) do : 101 | print(io, "\n%~ : %~" % [i, c]) 102 | 103 | ;============================================================ 104 | ;================== Program Linking ========================= 105 | ;============================================================ 106 | public defn link (p:Program) : 107 | ;Instructions and Labels 108 | val ins-vector = Vector() 109 | val label-map = HashTable() 110 | 111 | ;Constant pool 112 | val const-map = to-array(consts(p)) 113 | defn str-const (i:Int) : value(const-map[i] as StringConst) 114 | defn sym-const (i:Int) : to-symbol(str-const(i)) 115 | defn class-const (i:Int) : const-map[i] as ClassConst 116 | 117 | ;Classes 118 | val class-map = IntTable() 119 | val class-vector = Vector() 120 | 121 | ;Global Functions/Variables 122 | val function-map = HashTable() 123 | val var-map = HashTable() 124 | 125 | ;Code Patches 126 | val patch-vector = Vector<(() -> ?)>() 127 | defn add-patch (f: () -> LIns) : 128 | val p = length(ins-vector) 129 | add(ins-vector, LNull()) 130 | add{patch-vector, _} $ fn () : 131 | ins-vector[p] = f() 132 | defn run-patches () : 133 | for f in patch-vector do : f() 134 | 135 | ;Instruction Linking 136 | defn link-ins (ins:List) : 137 | for i in ins do : 138 | match(i) : 139 | (i:LabelIns) : 140 | val s = str-const(name(i)) 141 | label-map[s] = length(ins-vector) 142 | (i:LitIns) : 143 | add{ins-vector, _} $ match(const-map[idx(i)]) : 144 | (v:IntConst) : LInt(value(v)) 145 | (v:NullConst) : LNull() 146 | (i:PrintfIns) : 147 | add(ins-vector, LPrintf(str-const(format(i)), arity(i))) 148 | (i:ArrayIns) : 149 | add(ins-vector, LArray()) 150 | (i:ObjectIns) : 151 | add-patch $ fn () : 152 | val ci = class-map[class(i)] 153 | val c = class-vector[ci] 154 | val arity = count({_ is VarSlot}, slots(c)) 155 | LObject(ci, arity) 156 | (i:SlotIns) : 157 | add(ins-vector, LSlot(sym-const(name(i)))) 158 | (i:SetSlotIns) : 159 | add(ins-vector, LSetSlot(sym-const(name(i)))) 160 | (i:CallSlotIns) : 161 | add(ins-vector, LCallSlot(sym-const(name(i)), arity(i))) 162 | (i:CallIns) : 163 | add-patch $ fn () : 164 | val f = function-map[str-const(name(i))] 165 | LCall(f, arity(i)) 166 | (i:SetLocalIns) : 167 | add(ins-vector, LSetLocal(idx(i))) 168 | (i:GetLocalIns) : 169 | add(ins-vector, LGetLocal(idx(i))) 170 | (i:SetGlobalIns) : 171 | add-patch $ fn () : 172 | val s = str-const(name(i)) 173 | LSetGlobal(var-map[s]) 174 | (i:GetGlobalIns) : 175 | add-patch $ fn () : 176 | val s = str-const(name(i)) 177 | LGetGlobal(var-map[s]) 178 | (i:BranchIns) : 179 | add-patch $ fn () : 180 | val d = label-map[str-const(name(i))] 181 | LBranch(d) 182 | (i:GotoIns) : 183 | add-patch $ fn () : 184 | val d = label-map[str-const(name(i))] 185 | LGoto(d) 186 | (i:ReturnIns) : 187 | add(ins-vector, LReturn()) 188 | (i:DropIns) : 189 | add(ins-vector, LDrop()) 190 | 191 | ;Code Linking 192 | val code-map = IntTable() 193 | for (m in const-map, i in 0 to false) do : 194 | if m is MethodConst : 195 | val m = m as MethodConst 196 | code-map[i] = length(ins-vector) 197 | add(ins-vector, LFrame(nargs(m), nlocals(m))) 198 | link-ins(code(m)) 199 | 200 | ;Class Linking 201 | for (c in const-map, i in 0 to false) do : 202 | if c is ClassConst : 203 | val c = c as ClassConst 204 | class-map[i] = length(class-vector) 205 | val idx-counter = to-seq(0 to false) 206 | val class-slots = to-array $ for s in slots(c) seq : 207 | match(const-map[s]) : 208 | (v:SlotConst) : VarSlot(sym-const(name(v)), next(idx-counter)) 209 | (m:MethodConst) : CodeSlot(sym-const(name(m)), code-map[s]) 210 | add(class-vector, LClass(class-slots)) 211 | 212 | ;Global Functions/Variables 213 | val var-counter = to-seq(0 to false) 214 | for s in slots(p) do : 215 | match(const-map[s]) : 216 | (v:SlotConst) : var-map[str-const(name(v))] = next(var-counter) 217 | (m:MethodConst) : function-map[str-const(name(m))] = code-map[s] 218 | 219 | ;Patch Program 220 | run-patches() 221 | LProg(to-array(ins-vector), 222 | to-array(class-vector), 223 | next(var-counter), 224 | code-map[entry(p)]) 225 | 226 | ;============================================================ 227 | ;================== VM Evaluation =========================== 228 | ;============================================================ 229 | public defn run (p:LProg) -> False : 230 | ;Global state 231 | val ins = ins(p) 232 | val classes = classes(p) 233 | val null = Null() 234 | val globals = Array(num-globals(p), null) 235 | val vstack = Vector() 236 | val fstack = Vector() 237 | 238 | defn* step (ip:Int, fp:Int, n:Int) -> False : 239 | if ip >= 0 : 240 | ; println("Run %~: %~" % [ip, ins[ip]]) 241 | ; println(" Vstack: %~" % [vstack]) 242 | ; println(" Fstack: %~" % [fstack]) 243 | ; println(" fp = %~" % [fp]) 244 | match(ins[ip]) : 245 | (i:LInt) : 246 | add(vstack, value(i)) 247 | step(ip + 1, fp, 0) 248 | (i:LNull) : 249 | add(vstack, null) 250 | step(ip + 1, fp, 0) 251 | (i:LPrintf) : 252 | val args = reverse $ to-list $ 253 | for j in 0 to arity(i) seq : 254 | pop(vstack) 255 | pprint(format(i), args) 256 | add(vstack, null) 257 | step(ip + 1, fp, 0) 258 | (i:LArray) : 259 | val init = pop(vstack) 260 | val len = pop(vstack) 261 | ensure-int(len) 262 | add(vstack, Array(len as Int, init)) 263 | step(ip + 1, fp, 0) 264 | (i:LObject) : 265 | val slots = Array(arity(i)) 266 | for i in (arity(i) - 1) through 0 by -1 do : 267 | slots[i] = pop(vstack) 268 | val parent = pop(vstack) 269 | ensure-parent(parent) 270 | add(vstack, Object(class(i), parent as Object|Null, slots)) 271 | step(ip + 1, fp, 0) 272 | (i:LSlot) : 273 | match(pop(vstack)) : 274 | (v:Int) : no-slot(name(i), `Int) 275 | (v:Array) : no-slot(name(i), `Array) 276 | (v:Null) : no-slot(name(i), `Null) 277 | (v:Object) : add(vstack, get-obj-slot(v, name(i))) 278 | step(ip + 1, fp, 0) 279 | (i:LSetSlot) : 280 | val x = pop(vstack) 281 | val obj = pop(vstack) 282 | match(obj) : 283 | (obj:Int) : no-slot(name(i), `Int) 284 | (obj:Array) : no-slot(name(i), `Array) 285 | (obj:Null) : no-slot(name(i), `Null) 286 | (obj:Object) : set-obj-slot(obj, name(i), x) 287 | add(vstack, x) 288 | step(ip + 1, fp, 0) 289 | (i:LCallSlot) : 290 | val sp = length(vstack) 291 | val obj = vstack[sp - arity(i)] 292 | match(obj) : 293 | (obj:Int) : 294 | call-int-slot(name(i), arity(i)) 295 | step(ip + 1, fp, 0) 296 | (obj:Array) : 297 | call-array-slot(name(i), arity(i)) 298 | step(ip + 1, fp, 0) 299 | (obj:Null) : 300 | no-slot(name(i), `Null) 301 | (obj:Object) : 302 | val m = get-method(obj, name(i)) 303 | val newfp = length(fstack) 304 | add(fstack, ip + 1) 305 | add(fstack, fp) 306 | step(code(m), newfp, arity(i)) 307 | (i:LCall) : 308 | val newfp = length(fstack) 309 | add(fstack, ip + 1) 310 | add(fstack, fp) 311 | step(code(i), newfp, arity(i)) 312 | (i:LSetLocal) : 313 | fstack[fp + 2 + idx(i)] = peek(vstack) 314 | step(ip + 1, fp, 0) 315 | (i:LGetLocal) : 316 | add(vstack, fstack[fp + 2 + idx(i)]) 317 | step(ip + 1, fp, 0) 318 | (i:LSetGlobal) : 319 | globals[idx(i)] = peek(vstack) 320 | step(ip + 1, fp, 0) 321 | (i:LGetGlobal) : 322 | add(vstack, globals[idx(i)]) 323 | step(ip + 1, fp, 0) 324 | (i:LBranch) : 325 | if pop(vstack) is Null : step(ip + 1, fp, 0) 326 | else : step(code(i), fp, 0) 327 | (i:LGoto) : 328 | step(code(i), fp, 0) 329 | (i:LReturn) : 330 | val oldfp = fstack[fp + 1] as Int 331 | val ret = fstack[fp] as Int 332 | set-length(fstack, fp, null) 333 | step(ret, oldfp, 0) 334 | (i:LFrame) : 335 | ensure-arity(n, nargs(i)) 336 | set-length(fstack, fp + 2 + nargs(i) + nlocals(i), null) 337 | for i in (n - 1) through 0 by -1 do : 338 | fstack[fp + 2 + i] = pop(vstack) 339 | step(ip + 1, fp, 0) 340 | (i:LDrop) : 341 | pop(vstack) 342 | step(ip + 1, fp, 0) 343 | 344 | ;=== Type Specific Methods === 345 | defn get-obj-slot (o:Object|Null, slotname:Symbol) : 346 | match(o) : 347 | (o:Null) : 348 | no-slot(slotname, `Object) 349 | (o:Object) : 350 | val c = classes[class(o)] 351 | match(find({name(_) == slotname}, slots(c))) : 352 | (s:VarSlot) : slots(o)[idx(s)] 353 | (s:CodeSlot) : get-method-error(slotname) 354 | (s:False) : get-obj-slot(parent(o), slotname) 355 | 356 | defn set-obj-slot (o:Object|Null, slotname:Symbol, value:Int|Array|Object|Null) : 357 | match(o) : 358 | (o:Null) : 359 | no-slot(slotname, `Object) 360 | (o:Object) : 361 | val c = classes[class(o)] 362 | match(find({name(_) == slotname}, slots(c))) : 363 | (s:VarSlot) : slots(o)[idx(s)] = value 364 | (s:CodeSlot) : set-method-error(slotname) 365 | (s:False) : set-obj-slot(parent(o), slotname, value) 366 | 367 | defn call-int-slot (slotname:Symbol, n:Int) : 368 | ensure-arity(n, 2) 369 | val y? = pop(vstack) 370 | val x = pop(vstack) as Int 371 | ensure-int(y?) 372 | val y = y? as Int 373 | defn to-fbool (b:True|False) : 0 when b else null 374 | add{vstack, _} $ switch(slotname) : 375 | `eq : to-fbool(x == y) 376 | `lt : to-fbool(x < y) 377 | `le : to-fbool(x <= y) 378 | `gt : to-fbool(x > y) 379 | `ge : to-fbool(x >= y) 380 | `add : x + y 381 | `sub : x - y 382 | `mul : x * y 383 | `div : x / y 384 | `mod : x % y 385 | else : no-slot(slotname, `Int) 386 | 387 | defn call-array-slot (slotname:Symbol, n:Int) : 388 | add{vstack, _} $ switch(slotname) : 389 | `get : 390 | ensure-arity(n, 2) 391 | val i = pop(vstack) 392 | val a = pop(vstack) 393 | ensure-index(i, a) 394 | (a as Array)[i as Int] 395 | `set : 396 | ensure-arity(n, 3) 397 | val v = pop(vstack) 398 | val i = pop(vstack) 399 | val a = pop(vstack) 400 | ensure-index(i, a) 401 | (a as Array)[i as Int] = v 402 | null 403 | `length : 404 | ensure-arity(n, 1) 405 | val a = pop(vstack) 406 | length(a as Array) 407 | else : 408 | no-slot(slotname, `Array) 409 | 410 | defn get-method (o:Object|Null, slotname:Symbol) : 411 | match(o) : 412 | (o:Null) : 413 | no-slot(slotname, `Object) 414 | (o:Object) : 415 | val c = classes[class(o)] 416 | match(find({name(_) == slotname}, slots(c))) : 417 | (s:VarSlot) : call-var-error(slotname) 418 | (s:CodeSlot) : s 419 | (s:False) : get-method(parent(o), slotname) 420 | 421 | ;=== Pretty Printing === 422 | defn pprint (format:String, args:Seqable) : 423 | val arg-seq = to-seq(args) 424 | val n = length(format) 425 | let loop (i:Int = 0) : 426 | if i < n : 427 | switch(format[i]) : 428 | '~' : 429 | if empty?(arg-seq) : 430 | throw $ EvalException $ 431 | "Format string %~ is expecting more arguments." % [format] 432 | print(next(arg-seq)) 433 | loop(i + 1) 434 | else : 435 | print(format[i]) 436 | loop(i + 1) 437 | else : 438 | if not empty?(arg-seq) : 439 | throw $ EvalException $ 440 | "Unexpected end of format string %~. More arguments remaining." % [format] 441 | 442 | ;=== Error Reporting === 443 | defn ensure-arity (a:Int, da:Int) : 444 | if a != da : 445 | throw $ EvalException $ 446 | "Incorrect arity. Given %~ but expected %~ arguments." % [a, da] 447 | 448 | defn no-slot (name:Symbol, type:Symbol) : 449 | throw $ EvalException $ 450 | "No slot named %~ in object of type %~." % [name, type] 451 | 452 | defn get-method-error (name:Symbol) : 453 | throw $ EvalException $ 454 | "Cannot directly retrieve method slot %~." % [name] 455 | 456 | defn set-method-error (name:Symbol) : 457 | throw $ EvalException $ 458 | "Cannot directly assign to method slot %~." % [name] 459 | 460 | defn call-var-error (name:Symbol) : 461 | throw $ EvalException $ 462 | "Cannot call variable slot %~ as method." % [name] 463 | 464 | defn ensure-parent (o) : 465 | if o is-not Object|Null : 466 | throw $ EvalException $ 467 | "Expected either an object or null as parent." 468 | 469 | defn ensure-int (o) : 470 | if o is-not Int : 471 | throw $ EvalException $ 472 | "Expected an integer." 473 | 474 | defn ensure-index (i, a) : 475 | ensure-int(i) 476 | if (i < 0) or (i >= length(a)) : 477 | throw $ EvalException $ 478 | "Index (%~) out of bounds." % [i] 479 | 480 | ;=== Driver === 481 | add(fstack, -1) 482 | add(fstack, -1) 483 | step(entry(p), 0, 0) 484 | 485 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | 6 | //============================================================ 7 | //====================== LEXER =============================== 8 | //============================================================ 9 | 10 | typedef enum { 11 | LPAREN, 12 | RPAREN, 13 | INT, 14 | SYMBOL, 15 | NO_TOKEN 16 | } TokenTag; 17 | 18 | typedef struct { 19 | TokenTag tag; 20 | } Token; 21 | 22 | typedef struct { 23 | TokenTag tag; 24 | int value; 25 | } IntToken; 26 | 27 | typedef struct { 28 | TokenTag tag; 29 | char* name; 30 | } SymToken; 31 | 32 | Token* make_leftparen () { 33 | Token* ret = (Token*)malloc(sizeof(Token)); 34 | ret->tag = LPAREN; 35 | return ret; 36 | } 37 | 38 | Token* make_rightparen () { 39 | Token* ret = (Token*)malloc(sizeof(Token)); 40 | ret->tag = RPAREN; 41 | return ret; 42 | } 43 | 44 | SymToken* make_symtoken (char* name) { 45 | SymToken* ret = (SymToken*)malloc(sizeof(SymToken)); 46 | ret->tag = SYMBOL; 47 | ret->name = name; 48 | return ret; 49 | } 50 | 51 | IntToken* make_inttoken (int value) { 52 | IntToken* ret = (IntToken*)malloc(sizeof(IntToken)); 53 | ret->tag = INT; 54 | ret->value = value; 55 | return ret; 56 | } 57 | 58 | char sym_chars[256]; 59 | char op_chars[256]; 60 | char num_chars[256]; 61 | Vector* lexemes; 62 | void init_lex_tables () { 63 | //Reset 64 | lexemes = make_vector(); 65 | for(int i=0; i<256; i++){ 66 | sym_chars[i] = 0; 67 | op_chars[i] = 0; 68 | num_chars[i] = 0; 69 | } 70 | //Symbols 71 | for(char c = 'a'; c <= 'z'; c++) 72 | sym_chars[c] = 1; 73 | for(char c = 'A'; c <= 'Z'; c++) 74 | sym_chars[c] = 1; 75 | for(char c = '0'; c <= '9'; c++) 76 | sym_chars[c] = 1; 77 | sym_chars['_'] = 1; 78 | //Operators 79 | op_chars['-'] = 1; 80 | op_chars['+'] = 1; 81 | op_chars['*'] = 1; 82 | op_chars['/'] = 1; 83 | op_chars['='] = 1; 84 | //Numbers 85 | for(char c = '0'; c <= '9'; c++) 86 | num_chars[c] = 1; 87 | } 88 | 89 | char* lex_buffer; 90 | int lex_index; 91 | char peek_char (){ 92 | return lex_buffer[lex_index]; 93 | } 94 | 95 | char peek_char_i (int i){ 96 | return lex_buffer[lex_index + i]; 97 | } 98 | 99 | char* eat_nchars (int n) { 100 | char* cpy = (char*)malloc(sizeof(char) * n + 1); 101 | strncpy(cpy, lex_buffer + lex_index, n); 102 | cpy[n] = (char)0; 103 | lex_index += n; 104 | return cpy; 105 | } 106 | 107 | char* eat_chars (int start, char* table) { 108 | int n = start; 109 | while(table[lex_buffer[lex_index + n]]) 110 | n++; 111 | return eat_nchars(n); 112 | } 113 | 114 | int eat_symbol () { 115 | if(sym_chars[peek_char()]){ 116 | char* name = eat_chars(0, sym_chars); 117 | vector_add(lexemes, make_symtoken(name)); 118 | return 1; 119 | } 120 | return 0; 121 | } 122 | 123 | int eat_operator () { 124 | if(op_chars[peek_char()]){ 125 | char* name = eat_nchars(1); 126 | vector_add(lexemes, make_symtoken(name)); 127 | return 1; 128 | } 129 | return 0; 130 | } 131 | 132 | int parse_int (char* str) { 133 | int i; 134 | sscanf(str, "%d", &i); 135 | return i; 136 | } 137 | 138 | int eat_number () { 139 | if(peek_char() == '-' && num_chars[peek_char_i(1)]){ 140 | char* num = eat_chars(1, num_chars); 141 | int i = parse_int(num); 142 | vector_add(lexemes, make_inttoken(i)); 143 | return 1; 144 | } 145 | else if(num_chars[peek_char()]){ 146 | char* num = eat_chars(0, num_chars); 147 | int i = parse_int(num); 148 | vector_add(lexemes, make_inttoken(i)); 149 | return 1; 150 | } 151 | return 0; 152 | } 153 | 154 | int eat_paren () { 155 | if(peek_char() == '('){ 156 | lex_index++; 157 | vector_add(lexemes, make_leftparen()); 158 | return 1; 159 | } 160 | else if(peek_char() == ')'){ 161 | lex_index++; 162 | vector_add(lexemes, make_rightparen()); 163 | return 1; 164 | } 165 | return 0; 166 | } 167 | 168 | void eat_whitespace () { 169 | while(peek_char() == ' ' || peek_char() == '\n') 170 | lex_index++; 171 | } 172 | 173 | void lex (char* str) { 174 | lex_buffer = str; 175 | lex_index = 0; 176 | vector_clear(lexemes); 177 | while(peek_char() != 0){ 178 | int ate = eat_number() || eat_symbol() || eat_operator() || eat_paren(); 179 | if(!ate && peek_char() != 0){ 180 | printf("Lex Error: Unexpected character %c.\n", peek_char()); 181 | exit(-1); 182 | } 183 | eat_whitespace(); 184 | } 185 | } 186 | 187 | //============================================================ 188 | //===================== AST ================================== 189 | //============================================================ 190 | 191 | typedef enum { 192 | ADD_EXP, 193 | SUB_EXP, 194 | MUL_EXP, 195 | DIV_EXP, 196 | INT_EXP, 197 | VAR_EXP 198 | } ExpTag; 199 | 200 | typedef struct { 201 | ExpTag tag; 202 | } Exp; 203 | 204 | typedef struct { 205 | ExpTag tag; 206 | Exp* a; 207 | Exp* b; 208 | } AddExp; 209 | 210 | typedef struct { 211 | ExpTag tag; 212 | Exp* a; 213 | Exp* b; 214 | } SubExp; 215 | 216 | typedef struct { 217 | ExpTag tag; 218 | Exp* a; 219 | Exp* b; 220 | } MulExp; 221 | 222 | typedef struct { 223 | ExpTag tag; 224 | Exp* a; 225 | Exp* b; 226 | } DivExp; 227 | 228 | typedef struct { 229 | ExpTag tag; 230 | int value; 231 | } IntExp; 232 | 233 | typedef struct { 234 | ExpTag tag; 235 | char* name; 236 | } VarExp; 237 | 238 | typedef enum { 239 | CALC_STMT, 240 | LET_STMT 241 | } StmtTag; 242 | 243 | typedef struct { 244 | StmtTag tag; 245 | } Stmt; 246 | 247 | typedef struct { 248 | StmtTag tag; 249 | Exp* exp; 250 | } CalcStmt; 251 | 252 | typedef struct { 253 | StmtTag tag; 254 | char* name; 255 | Exp* exp; 256 | } LetStmt; 257 | 258 | AddExp* make_addexp(Exp* a, Exp* b){ 259 | AddExp* e = malloc(sizeof(AddExp)); 260 | e->tag = ADD_EXP; 261 | e->a = a; 262 | e->b = b; 263 | return e; 264 | } 265 | 266 | SubExp* make_subexp(Exp* a, Exp* b){ 267 | SubExp* e = malloc(sizeof(SubExp)); 268 | e->tag = SUB_EXP; 269 | e->a = a; 270 | e->b = b; 271 | return e; 272 | } 273 | 274 | MulExp* make_mulexp(Exp* a, Exp* b){ 275 | MulExp* e = malloc(sizeof(MulExp)); 276 | e->tag = MUL_EXP; 277 | e->a = a; 278 | e->b = b; 279 | return e; 280 | } 281 | 282 | DivExp* make_divexp(Exp* a, Exp* b){ 283 | DivExp* e = malloc(sizeof(DivExp)); 284 | e->tag = DIV_EXP; 285 | e->a = a; 286 | e->b = b; 287 | return e; 288 | } 289 | 290 | IntExp* make_intexp(int value){ 291 | IntExp* e = (IntExp*)malloc(sizeof(IntExp)); 292 | e->tag = INT_EXP; 293 | e->value = value; 294 | return e; 295 | } 296 | 297 | VarExp* make_varexp(char* name){ 298 | VarExp* e = (VarExp*)malloc(sizeof(VarExp)); 299 | e->tag = VAR_EXP; 300 | e->name = name; 301 | return e; 302 | } 303 | 304 | CalcStmt* make_calcstmt(Exp* e){ 305 | CalcStmt* s = (CalcStmt*)malloc(sizeof(CalcStmt)); 306 | s->tag = CALC_STMT; 307 | s->exp = e; 308 | return s; 309 | } 310 | 311 | LetStmt* make_letstmt(char* name, Exp* e){ 312 | LetStmt* s = (LetStmt*)malloc(sizeof(LetStmt)); 313 | s->tag = LET_STMT; 314 | s->name = name; 315 | s->exp = e; 316 | return s; 317 | } 318 | 319 | void print_exp (Exp* e){ 320 | switch(e->tag){ 321 | case ADD_EXP:{ 322 | AddExp* e2 = (AddExp*)e; 323 | printf("("); 324 | print_exp(e2->a); 325 | printf(" + "); 326 | print_exp(e2->b); 327 | printf(")"); 328 | break; 329 | } 330 | case SUB_EXP:{ 331 | SubExp* e2 = (SubExp*)e; 332 | printf("("); 333 | print_exp(e2->a); 334 | printf(" - "); 335 | print_exp(e2->b); 336 | printf(")"); 337 | break; 338 | } 339 | case MUL_EXP:{ 340 | MulExp* e2 = (MulExp*)e; 341 | printf("("); 342 | print_exp(e2->a); 343 | printf(" * "); 344 | print_exp(e2->b); 345 | printf(")"); 346 | break; 347 | } 348 | case DIV_EXP:{ 349 | DivExp* e2 = (DivExp*)e; 350 | printf("("); 351 | print_exp(e2->a); 352 | printf(" / "); 353 | print_exp(e2->b); 354 | printf(")"); 355 | break; 356 | } 357 | case INT_EXP:{ 358 | IntExp* e2 = (IntExp*)e; 359 | printf("%d", e2->value); 360 | break; 361 | } 362 | case VAR_EXP:{ 363 | VarExp* e2 = (VarExp*)e; 364 | printf("%s", e2->name); 365 | break; 366 | } 367 | default: 368 | printf("Expression with unknown tag %d\n", e->tag); 369 | exit(-1); 370 | } 371 | } 372 | 373 | void print_stmt (Stmt* s){ 374 | switch(s->tag){ 375 | case CALC_STMT:{ 376 | CalcStmt* s2 = (CalcStmt*)s; 377 | print_exp(s2->exp); 378 | break; 379 | } 380 | case LET_STMT:{ 381 | LetStmt* s2 = (LetStmt*)s; 382 | printf("let %s = ", s2->name); 383 | print_exp(s2->exp); 384 | break; 385 | } 386 | default: 387 | printf("Statement with unknown tag %d\n", s->tag); 388 | exit(-1); 389 | } 390 | } 391 | 392 | //============================================================ 393 | //==================== PARSER ================================ 394 | //============================================================ 395 | 396 | Exp* parse_exp (); 397 | Exp* parse_term (int p); 398 | Exp* parse_ops (Exp* a, int p); 399 | Exp* parse_atom (); 400 | 401 | int parse_index; 402 | int more_tokens () { 403 | return parse_index < lexemes->size; 404 | } 405 | int n_more_tokens (int n) { 406 | return parse_index + n - 1 < lexemes->size; 407 | } 408 | Token* peek_token () { 409 | return vector_get(lexemes, parse_index); 410 | } 411 | Token* peek_token_i (int i) { 412 | return vector_get(lexemes, parse_index + i); 413 | } 414 | Token* pop_token () { 415 | parse_index++; 416 | return vector_get(lexemes, parse_index-1); 417 | } 418 | TokenTag peek_tag () { 419 | if(more_tokens()) 420 | return peek_token()->tag; 421 | return NO_TOKEN; 422 | } 423 | 424 | int is_op(int p, char* n) { 425 | switch(p){ 426 | case 0: 427 | return strcmp(n,"+") == 0 || 428 | strcmp(n,"-") == 0; 429 | case 1: 430 | return strcmp(n,"*") == 0 || 431 | strcmp(n,"/") == 0; 432 | default: 433 | return 0; 434 | } 435 | } 436 | 437 | Exp* parse_exp () { 438 | return parse_term(0); 439 | } 440 | 441 | Exp* parse_term (int p) { 442 | if(p < 2) return parse_ops(parse_term(p + 1), p); 443 | else return parse_atom(); 444 | } 445 | 446 | Exp* parse_ops (Exp* a, int p) { 447 | switch(peek_tag()){ 448 | case SYMBOL:{ 449 | SymToken* t = (SymToken*)peek_token(); 450 | if(is_op(p, t->name)){ 451 | pop_token(); 452 | if(strcmp(t->name, "+") == 0) 453 | return parse_ops((Exp*)make_addexp(a, parse_term(p + 1)), p); 454 | else if(strcmp(t->name, "-") == 0) 455 | return parse_ops((Exp*)make_subexp(a, parse_term(p + 1)), p); 456 | else if(strcmp(t->name, "*") == 0) 457 | return parse_ops((Exp*)make_mulexp(a, parse_term(p + 1)), p); 458 | else if(strcmp(t->name, "/") == 0) 459 | return parse_ops((Exp*)make_divexp(a, parse_term(p + 1)), p); 460 | } 461 | else{ 462 | return a; 463 | } 464 | } 465 | default: 466 | return a; 467 | } 468 | } 469 | 470 | Exp* parse_atom () { 471 | if(!more_tokens()){ 472 | printf("Parse error: Unexpected end of input.\n"); 473 | exit(-1); 474 | } 475 | 476 | Token* t = pop_token(); 477 | switch(t->tag){ 478 | case INT:{ 479 | IntToken* t2 = (IntToken*)t; 480 | return (Exp*)make_intexp(t2->value); 481 | } 482 | case SYMBOL:{ 483 | SymToken* t2 = (SymToken*)t; 484 | if(strlen(t2->name) != 1){ 485 | printf("Invalid variable name: %s\n", t2->name); 486 | exit(-1); 487 | } 488 | char c = t2->name[0]; 489 | if(c < 'a' || c > 'z'){ 490 | printf("Invalid variable name: %s\n", t2->name); 491 | exit(-1); 492 | } 493 | return (Exp*)make_varexp(t2->name); 494 | } 495 | case LPAREN:{ 496 | Exp* e = parse_exp(); 497 | Token* t2 = pop_token(); 498 | if(t2->tag != RPAREN){ 499 | printf("Parse error: Unbalanced parenthesis.\n"); 500 | exit(-1); 501 | } 502 | return e; 503 | } 504 | default: 505 | printf("Parse error: Unexpected token with tag %d.\n", t->tag); 506 | exit(-1); 507 | } 508 | } 509 | 510 | int let_pending () { 511 | if(n_more_tokens(2)){ 512 | Token* x = peek_token(); 513 | if(x->tag != SYMBOL) 514 | return 0; 515 | Token* t = peek_token_i(1); 516 | if(t->tag == SYMBOL){ 517 | SymToken* t2 = (SymToken*)t; 518 | return strcmp(t2->name, "=") == 0; 519 | } 520 | } 521 | return 0; 522 | } 523 | 524 | Stmt* parse_stmt () { 525 | if(let_pending()){ 526 | SymToken* x = (SymToken*)pop_token(); 527 | pop_token(); 528 | return (Stmt*)make_letstmt(x->name, parse_exp()); 529 | } 530 | else{ 531 | return (Stmt*)make_calcstmt(parse_exp()); 532 | } 533 | } 534 | 535 | void ensure_no_more_tokens () { 536 | if(more_tokens()){ 537 | printf("Parse error: Unexpected token with tag %d.\n", peek_token()->tag); 538 | exit(-1); 539 | } 540 | } 541 | 542 | Stmt* parse_one_stmt (char* str){ 543 | lex(str); 544 | parse_index = 0; 545 | Stmt* s = parse_stmt(); 546 | ensure_no_more_tokens(); 547 | return s; 548 | } 549 | 550 | //============================================================ 551 | //==================== EVALUATOR ============================= 552 | //============================================================ 553 | 554 | int var_idx (char* name) { 555 | char c = name[0]; 556 | return c - 'a'; 557 | } 558 | 559 | int eval_exp (Exp* e, int* env) { 560 | switch(e->tag){ 561 | case ADD_EXP:{ 562 | AddExp* e2 = (AddExp*)e; 563 | int a = eval_exp(e2->a, env); 564 | int b = eval_exp(e2->b, env); 565 | return a + b; 566 | } 567 | case SUB_EXP:{ 568 | SubExp* e2 = (SubExp*)e; 569 | int a = eval_exp(e2->a, env); 570 | int b = eval_exp(e2->b, env); 571 | return a - b; 572 | } 573 | case MUL_EXP:{ 574 | MulExp* e2 = (MulExp*)e; 575 | int a = eval_exp(e2->a, env); 576 | int b = eval_exp(e2->b, env); 577 | return a * b; 578 | } 579 | case DIV_EXP:{ 580 | DivExp* e2 = (DivExp*)e; 581 | int a = eval_exp(e2->a, env); 582 | int b = eval_exp(e2->b, env); 583 | return a / b; 584 | } 585 | case INT_EXP:{ 586 | IntExp* e2 = (IntExp*)e; 587 | return e2->value; 588 | } 589 | case VAR_EXP:{ 590 | VarExp* e2 = (VarExp*)e; 591 | return env[var_idx(e2->name)]; 592 | } 593 | default: 594 | printf("Unsupported expression with tag %d\n", e->tag); 595 | exit(-1); 596 | } 597 | } 598 | 599 | void run_stmt (Stmt* s, int* env) { 600 | switch(s->tag){ 601 | case CALC_STMT:{ 602 | CalcStmt* s2 = (CalcStmt*)s; 603 | int r = eval_exp(s2->exp, env); 604 | print_stmt((Stmt*)s2); 605 | printf(" = "); 606 | printf("%d\n", r); 607 | break; 608 | } 609 | case LET_STMT:{ 610 | LetStmt* s2 = (LetStmt*)s; 611 | int r = eval_exp(s2->exp, env); 612 | env[var_idx(s2->name)] = r; 613 | print_stmt((Stmt*)s2); 614 | printf(" = "); 615 | printf("%d\n", r); 616 | break; 617 | } 618 | default: 619 | printf("Unsupported statement with tag: %d\n", s->tag); 620 | exit(-1); 621 | } 622 | } 623 | 624 | int* new_env () { 625 | return malloc(sizeof(int)*26); 626 | } 627 | 628 | //============================================================ 629 | //=================== BYTECODE FORMAT ======================== 630 | //============================================================ 631 | typedef enum { 632 | LIT_OP, 633 | ADD_OP, 634 | SUB_OP, 635 | MUL_OP, 636 | DIV_OP, 637 | GET_OP, 638 | SET_OP, 639 | END_OP 640 | } OpCode; 641 | 642 | typedef struct { 643 | char op; 644 | } Ins; 645 | 646 | typedef struct { 647 | char op; 648 | char b3; 649 | char b2; 650 | char b1; 651 | char b0; 652 | } LitIns; 653 | 654 | typedef struct { 655 | char op; 656 | char idx; 657 | } GetIns; 658 | 659 | typedef struct { 660 | char op; 661 | char idx; 662 | } SetIns; 663 | 664 | void print_ins (char* ins) { 665 | while(1){ 666 | switch(*ins){ 667 | case LIT_OP:{ 668 | LitIns* i = (LitIns*)ins; 669 | int v = (i->b3 << 3*8) + (i->b2 << 2*8) + (i->b1 << 1*8) + i->b0; 670 | printf(" LIT[%d]\n", v); 671 | ins += sizeof(LitIns); 672 | continue; 673 | } 674 | case GET_OP:{ 675 | GetIns* i = (GetIns*)ins; 676 | printf(" GET[%c]\n", 'a' + i->idx); 677 | ins += sizeof(GetIns); 678 | continue; 679 | } 680 | case SET_OP:{ 681 | SetIns* i = (SetIns*)ins; 682 | printf(" SET[%c]\n", 'a' + i->idx); 683 | ins += sizeof(SetIns); 684 | continue; 685 | } 686 | case ADD_OP: 687 | printf(" ADD\n"); 688 | ins++; 689 | continue; 690 | case SUB_OP: 691 | printf(" SUB\n"); 692 | ins++; 693 | continue; 694 | case MUL_OP: 695 | printf(" MUL\n"); 696 | ins++; 697 | continue; 698 | case DIV_OP: 699 | printf(" DIV\n"); 700 | ins++; 701 | continue; 702 | case END_OP: 703 | return; 704 | } 705 | } 706 | } 707 | 708 | //============================================================ 709 | //================= BYTECODE COMPILER ======================== 710 | //============================================================ 711 | char* push_ins (Exp* e, char* buffer) { 712 | switch(e->tag){ 713 | case ADD_EXP:{ 714 | AddExp* e2 = (AddExp*)e; 715 | buffer = push_ins(e2->a, buffer); 716 | buffer = push_ins(e2->b, buffer); 717 | buffer[0] = ADD_OP; 718 | return buffer+1; 719 | } 720 | case SUB_EXP:{ 721 | SubExp* e2 = (SubExp*)e; 722 | buffer = push_ins(e2->a, buffer); 723 | buffer = push_ins(e2->b, buffer); 724 | buffer[0] = SUB_OP; 725 | return buffer+1; 726 | } 727 | case MUL_EXP:{ 728 | MulExp* e2 = (MulExp*)e; 729 | buffer = push_ins(e2->a, buffer); 730 | buffer = push_ins(e2->b, buffer); 731 | buffer[0] = MUL_OP; 732 | return buffer+1; 733 | } 734 | case DIV_EXP:{ 735 | DivExp* e2 = (DivExp*)e; 736 | buffer = push_ins(e2->a, buffer); 737 | buffer = push_ins(e2->b, buffer); 738 | buffer[0] = DIV_OP; 739 | return buffer+1; 740 | } 741 | case INT_EXP:{ 742 | LitIns* i = (LitIns*)buffer; 743 | IntExp* e2 = (IntExp*)e; 744 | int v = e2->value; 745 | i->op = LIT_OP; 746 | i->b3 = (char)(v >> 3*8); 747 | i->b2 = (char)(v >> 2*8); 748 | i->b1 = (char)(v >> 1*8); 749 | i->b0 = (char)(v); 750 | return buffer + sizeof(LitIns); 751 | } 752 | case VAR_EXP:{ 753 | GetIns* i = (GetIns*)buffer; 754 | VarExp* e2 = (VarExp*)e; 755 | i->op = GET_OP; 756 | i->idx = (char)var_idx(e2->name); 757 | return buffer + sizeof(GetIns); 758 | } 759 | default: 760 | printf("Unknown expression with tag: %d\n", e->tag); 761 | exit(-1); 762 | } 763 | } 764 | 765 | char* compile (Stmt* s) { 766 | if(s->tag == CALC_STMT){ 767 | CalcStmt* s2 = (CalcStmt*)s; 768 | char* ins = malloc(1024 * 1024); 769 | char* end = push_ins(s2->exp, ins); 770 | end[0] = END_OP; 771 | return ins; 772 | } 773 | else if(s->tag == LET_STMT){ 774 | LetStmt* s2 = (LetStmt*)s; 775 | char* ins = malloc(1024 * 1024); 776 | char* end = push_ins(s2->exp, ins); 777 | //Set Ins 778 | SetIns* i = (SetIns*)end; 779 | i->op = SET_OP; 780 | i->idx = (char)var_idx(s2->name); 781 | end += sizeof(SetIns); 782 | //End Ins 783 | end[0] = END_OP; 784 | return ins; 785 | } 786 | else{ 787 | printf("Unknown statement with tag: %d\n", s->tag); 788 | exit(-1); 789 | } 790 | } 791 | 792 | //============================================================ 793 | //=============== BYTECODE INTERPRETER ======================= 794 | //============================================================ 795 | 796 | int run_ins (char* ins, int* env) { 797 | int* stack = malloc(sizeof(int) * 1024); 798 | int* sp = stack; 799 | int running = 1; 800 | while(running){ 801 | switch(*ins){ 802 | case LIT_OP:{ 803 | LitIns* i = (LitIns*)ins; 804 | int v = (i->b3 << 3*8) + (i->b2 << 2*8) + (i->b1 << 1*8) + i->b0; 805 | sp[0] = v; 806 | sp++; 807 | ins += sizeof(LitIns); 808 | continue; 809 | } 810 | case GET_OP:{ 811 | GetIns* i = (GetIns*)ins; 812 | sp[0] = env[i->idx]; 813 | sp++; 814 | ins += sizeof(GetIns); 815 | continue; 816 | } 817 | case SET_OP:{ 818 | SetIns* i = (SetIns*)ins; 819 | env[i->idx] = sp[-1]; 820 | ins += sizeof(SetIns); 821 | continue; 822 | } 823 | case ADD_OP:{ 824 | sp--; 825 | sp[-1] = sp[-1] + sp[0]; 826 | ins++; 827 | continue; 828 | } 829 | case SUB_OP: 830 | sp--; 831 | sp[-1] = sp[-1] - sp[0]; 832 | ins++; 833 | continue; 834 | case MUL_OP: 835 | sp--; 836 | sp[-1] = sp[-1] * sp[0]; 837 | ins++; 838 | continue; 839 | case DIV_OP: 840 | sp--; 841 | sp[-1] = sp[-1] / sp[0]; 842 | ins++; 843 | continue; 844 | case END_OP: 845 | running = 0; 846 | break; 847 | } 848 | } 849 | int v = sp[-1]; 850 | free(stack); 851 | return v; 852 | } 853 | 854 | void compile_run_stmt (Stmt* s, int* env) { 855 | char* ins = compile(s); 856 | int r = run_ins(ins, env); 857 | printf("Code:\n"); 858 | print_ins(ins); 859 | if(s->tag == CALC_STMT) 860 | printf("Result = %d\n", r); 861 | } 862 | 863 | //============================================================ 864 | //==================== DRIVERS =============================== 865 | //============================================================ 866 | 867 | void test_eater (char* s) { 868 | init_lex_tables(); 869 | lex(s); 870 | } 871 | 872 | void test_parse (char* str) { 873 | init_lex_tables(); 874 | Stmt* s = parse_one_stmt(str); 875 | printf("Parsed: "); 876 | print_stmt(s); 877 | printf("\n"); 878 | } 879 | 880 | char* read_line () { 881 | char* line; 882 | int size = 0; 883 | getline(&line, &size, stdin); 884 | return line; 885 | } 886 | 887 | void test_eval () { 888 | init_lex_tables(); 889 | int* env = new_env(); 890 | while(1){ 891 | printf("> "); 892 | fflush(stdout); 893 | char* line = read_line(); 894 | Stmt* s = parse_one_stmt(line); 895 | run_stmt(s, env); 896 | } 897 | } 898 | 899 | void test_compile () { 900 | init_lex_tables(); 901 | int* env = new_env(); 902 | while(1){ 903 | printf("> "); 904 | fflush(stdout); 905 | char* line = read_line(); 906 | Stmt* s = parse_one_stmt(line); 907 | compile_run_stmt(s, env); 908 | } 909 | } 910 | 911 | int main (int argc, char** argvs) { 912 | if(argc < 2){ 913 | printf("No flags given.\n"); 914 | printf("Use either -eval, or -compile\n"); 915 | exit(-1); 916 | } 917 | if(strcmp(argvs[1], "-eval") == 0) 918 | test_eval(); 919 | else if(strcmp(argvs[1], "-compile") == 0) 920 | test_compile(); 921 | } 922 | -------------------------------------------------------------------------------- /src/ast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | #include "ast.h" 6 | 7 | //============================================================ 8 | //================= CONSTRUCTORS ============================= 9 | //============================================================ 10 | 11 | Exp* make_IntExp (int value) { 12 | IntExp* e = malloc(sizeof(IntExp)); 13 | e->tag = INT_EXP; 14 | e->value = value; 15 | return (Exp*)e; 16 | } 17 | 18 | Exp* make_NullExp () { 19 | Exp* e = malloc(sizeof(Exp)); 20 | e->tag = NULL_EXP; 21 | return e; 22 | } 23 | 24 | Exp* make_PrintfExp (char* format, int nexps, Exp** exps) { 25 | PrintfExp* e = malloc(sizeof(PrintfExp)); 26 | e->tag = PRINTF_EXP; 27 | e->format = format; 28 | e->nexps = nexps; 29 | e->exps = exps; 30 | return (Exp*)e; 31 | } 32 | 33 | Exp* make_ArrayExp (Exp* length, Exp* init) { 34 | ArrayExp* e = malloc(sizeof(ArrayExp)); 35 | e->tag = ARRAY_EXP; 36 | e->length = length; 37 | e->init = init; 38 | return (Exp*)e; 39 | } 40 | 41 | Exp* make_ObjectExp (Exp* parent, int nslots, SlotStmt** slots) { 42 | ObjectExp* e = malloc(sizeof(ObjectExp)); 43 | e->tag = OBJECT_EXP; 44 | e->parent = parent; 45 | e->nslots = nslots; 46 | e->slots = slots; 47 | return (Exp*)e; 48 | } 49 | 50 | Exp* make_SlotExp (char* name, Exp* exp) { 51 | SlotExp* e = malloc(sizeof(SlotExp)); 52 | e->tag = SLOT_EXP; 53 | e->name = name; 54 | e->exp = exp; 55 | return (Exp*)e; 56 | } 57 | 58 | Exp* make_SetSlotExp (char* name, Exp* exp, Exp* value) { 59 | SetSlotExp* e = malloc(sizeof(SetSlotExp)); 60 | e->tag = SET_SLOT_EXP; 61 | e->name = name; 62 | e->exp = exp; 63 | e->value = value; 64 | return (Exp*)e; 65 | } 66 | 67 | Exp* make_CallSlotExp (char* name, Exp* exp, int nargs, Exp** args) { 68 | CallSlotExp* e = malloc(sizeof(CallSlotExp)); 69 | e->tag = CALL_SLOT_EXP; 70 | e->name = name; 71 | e->exp = exp; 72 | e->nargs = nargs; 73 | e->args = args; 74 | return (Exp*)e; 75 | } 76 | 77 | Exp* make_CallExp (char* name, int nargs, Exp** args) { 78 | CallExp* e = malloc(sizeof(CallExp)); 79 | e->tag = CALL_EXP; 80 | e->name = name; 81 | e->nargs = nargs; 82 | e->args = args; 83 | return (Exp*)e; 84 | } 85 | 86 | Exp* make_SetExp (char* name, Exp* exp) { 87 | SetExp* e = malloc(sizeof(SetExp)); 88 | e->tag = SET_EXP; 89 | e->name = name; 90 | e->exp = exp; 91 | return (Exp*)e; 92 | } 93 | 94 | Exp* make_IfExp (Exp* pred, ScopeStmt* conseq, ScopeStmt* alt) { 95 | IfExp* e = malloc(sizeof(IfExp)); 96 | e->tag = IF_EXP; 97 | e->pred = pred; 98 | e->conseq = conseq; 99 | e->alt = alt; 100 | return (Exp*)e; 101 | } 102 | 103 | Exp* make_WhileExp (Exp* pred, ScopeStmt* body) { 104 | WhileExp* e = malloc(sizeof(WhileExp)); 105 | e->tag = WHILE_EXP; 106 | e->pred = pred; 107 | e->body = body; 108 | return (Exp*)e; 109 | } 110 | 111 | Exp* make_RefExp (char* name) { 112 | RefExp* e = malloc(sizeof(RefExp)); 113 | e->tag = REF_EXP; 114 | e->name = name; 115 | return (Exp*)e; 116 | } 117 | 118 | SlotStmt* make_SlotVar (char* name, Exp* exp) { 119 | SlotVar* s = malloc(sizeof(SlotVar)); 120 | s->tag = VAR_STMT; 121 | s->name = name; 122 | s->exp = exp; 123 | return (SlotStmt*)s; 124 | } 125 | 126 | SlotStmt* make_SlotMethod (char* name, int nargs, char** args, ScopeStmt* body) { 127 | SlotMethod* s = malloc(sizeof(SlotMethod)); 128 | s->tag = FN_STMT; 129 | s->name = name; 130 | s->nargs = nargs; 131 | s->args = args; 132 | s->body = body; 133 | return (SlotStmt*)s; 134 | } 135 | 136 | ScopeStmt* make_ScopeVar (char* name, Exp* exp) { 137 | ScopeVar* s = malloc(sizeof(ScopeVar)); 138 | s->tag = VAR_STMT; 139 | s->name = name; 140 | s->exp = exp; 141 | return (ScopeStmt*)s; 142 | } 143 | 144 | ScopeStmt* make_ScopeFn (char* name, int nargs, char** args, ScopeStmt* body) { 145 | ScopeFn* s = malloc(sizeof(ScopeFn)); 146 | s->tag = FN_STMT; 147 | s->name = name; 148 | s->nargs = nargs; 149 | s->args = args; 150 | s->body = body; 151 | return (ScopeStmt*)s; 152 | } 153 | 154 | ScopeStmt* make_ScopeSeq (ScopeStmt* a, ScopeStmt* b) { 155 | ScopeSeq* s = malloc(sizeof(ScopeSeq)); 156 | s->tag = SEQ_STMT; 157 | s->a = a; 158 | s->b = b; 159 | return (ScopeStmt*)s; 160 | } 161 | 162 | ScopeStmt* make_ScopeExp (Exp* exp) { 163 | ScopeExp* s = malloc(sizeof(ScopeExp)); 164 | s->tag = EXP_STMT; 165 | s->exp = exp; 166 | return (ScopeStmt*)s; 167 | } 168 | 169 | //============================================================ 170 | //=================== PRINTING =============================== 171 | //============================================================ 172 | 173 | void print_exp (Exp* e) { 174 | switch(e->tag){ 175 | case INT_EXP:{ 176 | IntExp* e2 = (IntExp*)e; 177 | printf("%d", e2->value); 178 | break; 179 | } 180 | case NULL_EXP:{ 181 | printf("null"); 182 | break; 183 | } 184 | case PRINTF_EXP:{ 185 | PrintfExp* e2 = (PrintfExp*)e; 186 | printf("printf("); 187 | print_string(e2->format); 188 | for(int i=0; inexps; i++){ 189 | printf(", "); 190 | print_exp(e2->exps[i]); 191 | } 192 | printf(")"); 193 | break; 194 | } 195 | case ARRAY_EXP:{ 196 | ArrayExp* e2 = (ArrayExp*)e; 197 | printf("array("); 198 | print_exp(e2->length); 199 | printf(", "); 200 | print_exp(e2->init); 201 | printf(")"); 202 | break; 203 | } 204 | case OBJECT_EXP:{ 205 | ObjectExp* e2 = (ObjectExp*)e; 206 | printf("object : ("); 207 | for(int i=0; inslots; i++){ 208 | if(i > 0) printf(" "); 209 | print_slotstmt(e2->slots[i]); 210 | } 211 | printf(")"); 212 | break; 213 | } 214 | case SLOT_EXP:{ 215 | SlotExp* e2 = (SlotExp*)e; 216 | print_exp(e2->exp); 217 | printf(".%s", e2->name); 218 | break; 219 | } 220 | case SET_SLOT_EXP:{ 221 | SetSlotExp* e2 = (SetSlotExp*)e; 222 | print_exp(e2->exp); 223 | printf(".%s = ", e2->name); 224 | print_exp(e2->value); 225 | break; 226 | } 227 | case CALL_SLOT_EXP:{ 228 | CallSlotExp* e2 = (CallSlotExp*)e; 229 | print_exp(e2->exp); 230 | printf(".%s(", e2->name); 231 | for(int i=0; inargs; i++){ 232 | if(i > 0) printf(", "); 233 | print_exp(e2->args[i]); 234 | } 235 | printf(")"); 236 | break; 237 | } 238 | case CALL_EXP:{ 239 | CallExp* e2 = (CallExp*)e; 240 | printf("%s(", e2->name); 241 | for(int i=0; inargs; i++){ 242 | if(i > 0) printf(", "); 243 | print_exp(e2->args[i]); 244 | } 245 | printf(")"); 246 | break; 247 | } 248 | case SET_EXP:{ 249 | SetExp* e2 = (SetExp*)e; 250 | printf("%s = ", e2->name); 251 | print_exp(e2->exp); 252 | break; 253 | } 254 | case IF_EXP:{ 255 | IfExp* e2 = (IfExp*)e; 256 | printf("if "); 257 | print_exp(e2->pred); 258 | printf(" : ("); 259 | print_scopestmt(e2->conseq); 260 | printf(") else : ("); 261 | print_scopestmt(e2->alt); 262 | printf(")"); 263 | break; 264 | } 265 | case WHILE_EXP:{ 266 | WhileExp* e2 = (WhileExp*)e; 267 | printf("while "); 268 | print_exp(e2->pred); 269 | printf(" : ("); 270 | print_scopestmt(e2->body); 271 | printf(")"); 272 | break; 273 | } 274 | case REF_EXP:{ 275 | RefExp* e2 = (RefExp*)e; 276 | printf("%s", e2->name); 277 | break; 278 | } 279 | default: 280 | printf("Unrecognized Expression with tag %d\n", e->tag); 281 | exit(-1); 282 | } 283 | } 284 | 285 | void print_slotstmt (SlotStmt* s) { 286 | switch(s->tag){ 287 | case VAR_STMT:{ 288 | SlotVar* s2 = (SlotVar*)s; 289 | printf("var %s = ", s2->name); 290 | print_exp(s2->exp); 291 | break; 292 | } 293 | case FN_STMT:{ 294 | SlotMethod* s2 = (SlotMethod*)s; 295 | printf("method %s (", s2->name); 296 | for(int i=0; inargs; i++){ 297 | if(i>0) printf(", "); 298 | printf("%s", s2->args[i]); 299 | } 300 | printf(") : ("); 301 | print_scopestmt(s2->body); 302 | printf(")"); 303 | break; 304 | } 305 | default: 306 | printf("Unrecognized slot statement with tag %d\n", s->tag); 307 | exit(-1); 308 | } 309 | } 310 | 311 | void print_scopestmt (ScopeStmt* s) { 312 | switch(s->tag){ 313 | case VAR_STMT:{ 314 | ScopeVar* s2 = (ScopeVar*)s; 315 | printf("var %s = ", s2->name); 316 | print_exp(s2->exp); 317 | break; 318 | } 319 | case FN_STMT:{ 320 | ScopeFn* s2 = (ScopeFn*)s; 321 | printf("defn %s (", s2->name); 322 | for(int i=0; inargs; i++){ 323 | if(i>0) printf(", "); 324 | printf("%s", s2->args[i]); 325 | } 326 | printf(") : ("); 327 | print_scopestmt(s2->body); 328 | printf(")"); 329 | break; 330 | } 331 | case SEQ_STMT:{ 332 | ScopeSeq* s2 = (ScopeSeq*)s; 333 | print_scopestmt(s2->a); 334 | printf(" "); 335 | print_scopestmt(s2->b); 336 | break; 337 | } 338 | case EXP_STMT:{ 339 | ScopeExp* s2 = (ScopeExp*)s; 340 | print_exp(s2->exp); 341 | break; 342 | } 343 | default: 344 | printf("Unrecognized scope statement with tag %d\n", s->tag); 345 | exit(-1); 346 | } 347 | } 348 | 349 | //============================================================ 350 | //=================== LOADING ================================ 351 | //============================================================ 352 | 353 | static FILE* inputfile; 354 | 355 | static char read_byte () { 356 | int i = fgetc(inputfile); 357 | if(i < 0) { 358 | printf("Unexpected end of file.\n"); 359 | exit(-1); 360 | } 361 | return (char)i; 362 | } 363 | static int read_int () { 364 | unsigned char b1 = read_byte(); 365 | unsigned char b2 = read_byte(); 366 | unsigned char b3 = read_byte(); 367 | unsigned char b4 = read_byte(); 368 | return (int)b1 + ((int)b2 << 8) + ((int)b3 << 16) + ((int)b4 << 24); 369 | } 370 | static char* read_string () { 371 | int len = read_int(); 372 | char* str = malloc(len + 1); 373 | for(int i=0; itag = VAR_ENTRY; 596 | e->name = name; 597 | e->value = obj; 598 | return e; 599 | } 600 | 601 | EnvEntry* make_CodeEntry (char* name, int nargs, char** args, ScopeStmt* body) { 602 | EnvEntry* e = malloc(sizeof(EnvEntry)); 603 | e->tag = CODE_ENTRY; 604 | e->name = name; 605 | e->nargs = nargs; 606 | e->args = args; 607 | e->body = body; 608 | return e; 609 | } 610 | 611 | EvalObj* make_NullObj () { 612 | EvalObj* o = malloc(sizeof(EvalObj)); 613 | o->tag = NULL_OBJ; 614 | return o; 615 | } 616 | 617 | IntObj* make_IntObj (int value) { 618 | IntObj* e = malloc(sizeof(IntObj)); 619 | e->tag = INT_OBJ; 620 | e->value = value; 621 | return e; 622 | } 623 | 624 | ArrayObj* make_ArrayObj (int length, EvalObj* x) { 625 | ArrayObj* o = malloc(sizeof(ArrayObj)); 626 | o->tag = ARRAY_OBJ; 627 | o->length = length; 628 | o->items = malloc(sizeof(EvalObj*) * length); 629 | for(int i=0; iitems[i] = x; 631 | return o; 632 | } 633 | 634 | EnvObj* make_Env (EvalObj* parent) { 635 | EnvObj* e = malloc(sizeof(EnvObj)); 636 | e->tag = ENV_OBJ; 637 | e->parent = parent; 638 | e->entries = make_vector(); 639 | return e; 640 | } 641 | 642 | //============================================================ 643 | //================= PRINTER ================================== 644 | //============================================================ 645 | 646 | static void print_obj (EvalObj* o) { 647 | switch(o->tag){ 648 | case NULL_OBJ:{ 649 | printf("null"); 650 | break; 651 | } 652 | case INT_OBJ:{ 653 | IntObj* o2 = (IntObj*)o; 654 | printf("%d", o2->value); 655 | break; 656 | } 657 | case ARRAY_OBJ:{ 658 | ArrayObj* o2 = (ArrayObj*)o; 659 | printf("["); 660 | for(int i=0; ilength; i++){ 661 | if(i>0) printf(" "); 662 | print_obj(o2->items[i]); 663 | } 664 | printf("]"); 665 | break; 666 | } 667 | case ENV_OBJ:{ 668 | printf("[Object]"); 669 | break; 670 | } 671 | default:{ 672 | printf("Object with unknown tag: %d\n", o->tag); 673 | exit(-1); 674 | } 675 | } 676 | } 677 | 678 | //============================================================ 679 | //================= ENVIRONMENTS ============================= 680 | //============================================================ 681 | 682 | EnvEntry* lookup_frame (Vector* entries, char* name) { 683 | for(int i=0; isize; i++){ 684 | EnvEntry* e = vector_get(entries, i); 685 | if(strcmp(e->name, name) == 0) 686 | return e; 687 | } 688 | return 0; 689 | } 690 | 691 | EnvEntry* lookup_env (EnvObj* env, char* name) { 692 | if(env->tag == ENV_OBJ){ 693 | EnvEntry* e = lookup_frame(env->entries, name); 694 | if(e) return e; 695 | return lookup_env((EnvObj*)env->parent, name); 696 | } 697 | else{ 698 | printf("No entry for %s\n", name); 699 | exit(-1); 700 | } 701 | } 702 | 703 | EnvEntry* lookup_fn (EnvObj* env, char* name) { 704 | EnvEntry* e = lookup_env(env, name); 705 | if(e->tag != CODE_ENTRY){ 706 | printf("Entry %s is not a function.\n", name); 707 | exit(-1); 708 | } 709 | return e; 710 | } 711 | 712 | EnvEntry* lookup_var (EnvObj* env, char* name) { 713 | EnvEntry* e = lookup_env(env, name); 714 | if(e->tag != VAR_ENTRY){ 715 | printf("Entry %s is not a variable.\n", name); 716 | exit(-1); 717 | } 718 | return e; 719 | } 720 | 721 | //============================================================ 722 | //================== INTERPRETER ============================= 723 | //============================================================ 724 | 725 | // Interpreter Values 726 | EvalObj* nullobj; 727 | EvalObj* zeroobj; 728 | 729 | // Interpreter Routines 730 | EvalObj* eval_stmt (EnvObj* genv, EnvObj* env, ScopeStmt* stmt); 731 | EvalObj* eval_exp (EnvObj* genv, EnvObj* env, Exp* exp); 732 | 733 | static void init_eval () { 734 | nullobj = (EvalObj*)make_NullObj(); 735 | zeroobj = (EvalObj*)make_IntObj(0); 736 | } 737 | 738 | static void ensure_nargs (int nargs, int desired) { 739 | if(nargs != desired){ 740 | printf("Incorrect number of arguments. Expected %d but got %d\n", desired, nargs); 741 | exit(-1); 742 | } 743 | } 744 | 745 | static void ensure_int (IntObj* o) { 746 | if(o->tag != INT_OBJ){ 747 | printf("Not an integer.\n"); 748 | exit(-1); 749 | } 750 | } 751 | 752 | static void ensure_index (ArrayObj* o, IntObj* i) { 753 | ensure_int(i); 754 | if(i->value < 0 || i->value >= o->length){ 755 | printf("Index %d out of bounds.\n", i->value); 756 | exit(-1); 757 | } 758 | } 759 | 760 | static void ensure_parent (EvalObj* o) { 761 | if(o->tag != NULL_OBJ && o->tag != ENV_OBJ){ 762 | print_obj(o); 763 | printf(" is not a legal parent.\n"); 764 | exit(-1); 765 | } 766 | } 767 | 768 | static void ensure_env (EnvObj* o) { 769 | if(o->tag != ENV_OBJ){ 770 | print_obj((EvalObj*)o); 771 | printf(" is not an object.\n"); 772 | exit(-1); 773 | } 774 | } 775 | 776 | static EvalObj* bool_obj (int b) { 777 | if(b) return zeroobj; 778 | return nullobj; 779 | } 780 | 781 | static EvalObj* call_int_slot (EnvObj* genv, EnvObj* env, IntObj* obj, char* name, int nargs, Exp** args) { 782 | ensure_nargs(nargs, 1); 783 | IntObj* o = (IntObj*)eval_exp(genv, env, args[0]); 784 | ensure_int(o); 785 | if(strcmp(name, "eq") == 0) 786 | return bool_obj(obj->value == o->value); 787 | else if(strcmp(name, "lt") == 0) 788 | return bool_obj(obj->value < o->value); 789 | else if(strcmp(name, "le") == 0) 790 | return bool_obj(obj->value <= o->value); 791 | else if(strcmp(name, "gt") == 0) 792 | return bool_obj(obj->value > o->value); 793 | else if(strcmp(name, "ge") == 0) 794 | return bool_obj(obj->value >= o->value); 795 | else if(strcmp(name, "add") == 0) 796 | return (EvalObj*)make_IntObj(obj->value + o->value); 797 | else if(strcmp(name, "sub") == 0) 798 | return (EvalObj*)make_IntObj(obj->value - o->value); 799 | else if(strcmp(name, "mul") == 0) 800 | return (EvalObj*)make_IntObj(obj->value * o->value); 801 | else if(strcmp(name, "div") == 0) 802 | return (EvalObj*)make_IntObj(obj->value / o->value); 803 | else if(strcmp(name, "mod") == 0) 804 | return (EvalObj*)make_IntObj(obj->value % o->value); 805 | 806 | printf("No slot named %s for Ints.\n", name); 807 | exit(-1); 808 | } 809 | 810 | static EvalObj* call_array_slot (EnvObj* genv, EnvObj* env, ArrayObj* obj, char* name, int nargs, Exp** args) { 811 | if(strcmp(name, "get") == 0){ 812 | ensure_nargs(nargs, 1); 813 | IntObj* o = (IntObj*)eval_exp(genv, env, args[0]); 814 | ensure_index(obj, o); 815 | return obj->items[o->value]; 816 | } 817 | else if(strcmp(name, "set") == 0){ 818 | ensure_nargs(nargs, 2); 819 | IntObj* i = (IntObj*)eval_exp(genv, env, args[0]); 820 | EvalObj* x = eval_exp(genv, env, args[1]); 821 | ensure_index(obj, i); 822 | obj->items[i->value] = x; 823 | return nullobj; 824 | } 825 | else if(strcmp(name, "length") == 0){ 826 | ensure_nargs(nargs, 0); 827 | return (EvalObj*)make_IntObj(obj->length); 828 | } 829 | 830 | printf("No slot named %s for Array.\n", name); 831 | exit(-1); 832 | } 833 | 834 | EvalObj* call_slot (EnvObj* genv, EnvObj* env, EvalObj* obj, char* name, int nargs, Exp** args) { 835 | if(obj->tag == NULL_OBJ){ 836 | printf("No slot named %s for null.\n", name); 837 | exit(-1); 838 | } 839 | else if(obj->tag == INT_OBJ){ 840 | return call_int_slot(genv, env, (IntObj*)obj, name, nargs, args); 841 | } 842 | else if(obj->tag == ARRAY_OBJ){ 843 | return call_array_slot(genv, env, (ArrayObj*)obj, name, nargs, args); 844 | } 845 | else if(obj->tag == ENV_OBJ){ 846 | EnvEntry* m = lookup_fn((EnvObj*)obj, name); 847 | ensure_nargs(nargs, m->nargs); 848 | EnvObj* fenv = make_Env((EvalObj*)genv); 849 | vector_add(fenv->entries, make_VarEntry("this", obj)); 850 | for(int i=0; inargs; i++){ 851 | EnvEntry* e = make_VarEntry(m->args[i], eval_exp(genv, env, args[i])); 852 | vector_add(fenv->entries, e); 853 | } 854 | return eval_stmt(genv, fenv, m->body); 855 | } 856 | else{ 857 | printf("Unknown object with tag: %d\n", obj->tag); 858 | exit(-1); 859 | } 860 | } 861 | 862 | static void print_format (char* fmt, int nvs, EvalObj** vs) { 863 | int i = 0; 864 | while(1){ 865 | char c = fmt[0]; 866 | if(c == 0) { 867 | if(i != nvs){ 868 | printf("Incorrect number of arguments to printf statement.\n"); 869 | exit(-1); 870 | } 871 | return; 872 | }else if(c == '~'){ 873 | if(i >= nvs){ 874 | printf("Incorrect number of arguments to printf statement.\n"); 875 | exit(-1); 876 | } 877 | print_obj(vs[i]); 878 | i++; 879 | }else{ 880 | printf("%c", c); 881 | } 882 | fmt++; 883 | } 884 | } 885 | 886 | EvalObj* eval_exp (EnvObj* genv, EnvObj* env, Exp* exp) { 887 | switch(exp->tag){ 888 | case INT_EXP:{ 889 | IntExp* e = (IntExp*)exp; 890 | return (EvalObj*)make_IntObj(e->value); 891 | } 892 | 893 | case NULL_EXP:{ 894 | return nullobj; 895 | } 896 | 897 | case PRINTF_EXP:{ 898 | PrintfExp* e = (PrintfExp*)exp; 899 | EvalObj** vs = malloc(sizeof(EvalObj*) * e->nexps); 900 | for(int i=0; inexps; i++) 901 | vs[i] = eval_exp(genv, env, e->exps[i]); 902 | print_format(e->format, e->nexps, vs); 903 | return nullobj; 904 | } 905 | 906 | case ARRAY_EXP:{ 907 | ArrayExp* e = (ArrayExp*)exp; 908 | IntObj* n = (IntObj*)eval_exp(genv, env, e->length); 909 | EvalObj* x = eval_exp(genv, env, e->init); 910 | ensure_int(n); 911 | return (EvalObj*)make_ArrayObj(n->value, x); 912 | } 913 | 914 | case OBJECT_EXP:{ 915 | ObjectExp* e = (ObjectExp*)exp; 916 | EvalObj* p = eval_exp(genv, env, e->parent); 917 | ensure_parent(p); 918 | EnvObj* o = make_Env(p); 919 | for(int i=0; inslots; i++){ 920 | SlotStmt* s = e->slots[i]; 921 | if(s->tag == VAR_STMT){ 922 | SlotVar* s2 = (SlotVar*)s; 923 | EnvEntry* v = make_VarEntry(s2->name, eval_exp(genv, env, s2->exp)); 924 | vector_add(o->entries, v); 925 | }else if(s->tag == FN_STMT){ 926 | SlotMethod* s2 = (SlotMethod*)s; 927 | EnvEntry* m = make_CodeEntry(s2->name, s2->nargs, s2->args, s2->body); 928 | vector_add(o->entries, m); 929 | }else{ 930 | printf("Unrecognized slot type: %d\n", s->tag); 931 | exit(-1); 932 | } 933 | } 934 | return (EvalObj*)o; 935 | } 936 | 937 | case SLOT_EXP:{ 938 | SlotExp* e = (SlotExp*)exp; 939 | EnvObj* o = (EnvObj*)eval_exp(genv, env, e->exp); 940 | ensure_env(o); 941 | EnvEntry* v = lookup_var(o, e->name); 942 | return v->value; 943 | } 944 | 945 | case SET_SLOT_EXP:{ 946 | SetSlotExp* e = (SetSlotExp*)exp; 947 | EnvObj* o = (EnvObj*)eval_exp(genv, env, e->exp); 948 | ensure_env(o); 949 | EvalObj* v = eval_exp(genv, env, e->value); 950 | EnvEntry* var = lookup_var(o, e->name); 951 | var->value = v; 952 | return nullobj; 953 | } 954 | 955 | case CALL_SLOT_EXP:{ 956 | CallSlotExp* e = (CallSlotExp*)exp; 957 | EvalObj* o = eval_exp(genv, env, e->exp); 958 | return call_slot(genv, env, o, e->name, e->nargs, e->args); 959 | } 960 | 961 | case CALL_EXP:{ 962 | CallExp* e = (CallExp*)exp; 963 | EnvEntry* f = lookup_fn(env, e->name); 964 | ensure_nargs(e->nargs, f->nargs); 965 | EnvObj* fenv = make_Env((EvalObj*)genv); 966 | for(int i=0; inargs; i++){ 967 | EnvEntry* a = make_VarEntry(f->args[i], eval_exp(genv, env, e->args[i])); 968 | vector_add(fenv->entries, a); 969 | } 970 | return eval_stmt(genv, fenv, f->body); 971 | } 972 | 973 | case SET_EXP:{ 974 | SetExp* e = (SetExp*)exp; 975 | EvalObj* v = eval_exp(genv, env, e->exp); 976 | EnvEntry* var = lookup_var(env, e->name); 977 | var->value = v; 978 | return nullobj; 979 | } 980 | 981 | case IF_EXP:{ 982 | IfExp* e = (IfExp*)exp; 983 | EvalObj* p = eval_exp(genv, env, e->pred); 984 | EnvObj* benv = make_Env((EvalObj*)env); 985 | if(p->tag != NULL_OBJ) 986 | return eval_stmt(genv, benv, e->conseq); 987 | else 988 | return eval_stmt(genv, benv, e->alt); 989 | } 990 | 991 | case WHILE_EXP:{ 992 | WhileExp* e = (WhileExp*)exp; 993 | while(1){ 994 | EvalObj* p = eval_exp(genv, env, e->pred); 995 | if(p->tag == NULL_OBJ) 996 | break; 997 | EnvObj* benv = make_Env((EvalObj*)env); 998 | eval_stmt(genv, benv, e->body); 999 | } 1000 | return nullobj; 1001 | } 1002 | 1003 | case REF_EXP:{ 1004 | RefExp* e = (RefExp*)exp; 1005 | EnvEntry* v = lookup_var(env, e->name); 1006 | return v->value; 1007 | } 1008 | 1009 | default:{ 1010 | printf("Unrecognized expression with tag: %d\n", exp->tag); 1011 | exit(-1); 1012 | } 1013 | } 1014 | } 1015 | 1016 | EvalObj* eval_stmt (EnvObj* genv, EnvObj* env, ScopeStmt* stmt) { 1017 | switch(stmt->tag){ 1018 | case VAR_STMT:{ 1019 | ScopeVar* s = (ScopeVar*)stmt; 1020 | EvalObj* val = eval_exp(genv, env, s->exp); 1021 | EnvEntry* v = make_VarEntry(s->name, val); 1022 | vector_add(env->entries, v); 1023 | return nullobj; 1024 | } 1025 | case FN_STMT:{ 1026 | ScopeFn* s = (ScopeFn*)stmt; 1027 | EnvEntry* f = make_CodeEntry(s->name, s->nargs, s->args, s->body); 1028 | vector_add(env->entries, f); 1029 | return nullobj; 1030 | } 1031 | case SEQ_STMT:{ 1032 | ScopeSeq* s = (ScopeSeq*)stmt; 1033 | eval_stmt(genv, env, s->a); 1034 | return eval_stmt(genv, env, s->b); 1035 | } 1036 | case EXP_STMT:{ 1037 | ScopeExp* s = (ScopeExp*)stmt; 1038 | return eval_exp(genv, env, s->exp); 1039 | } 1040 | default: 1041 | printf("Unrecognized Statement with tag: %d\n", stmt->tag); 1042 | exit(-1); 1043 | } 1044 | } 1045 | 1046 | //============================================================ 1047 | //================= DRIVER =================================== 1048 | //============================================================ 1049 | 1050 | void interpret (ScopeStmt* stmt) { 1051 | init_eval(); 1052 | EnvObj* genv = make_Env(nullobj); 1053 | eval_stmt(genv, genv, stmt); 1054 | } 1055 | -------------------------------------------------------------------------------- /src/vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | #include "bytecode.h" 6 | #include "vm.h" 7 | 8 | //============================================================ 9 | //===================== LINKER =============================== 10 | //============================================================ 11 | 12 | //======== CODE BUFFER =============== 13 | char* code; 14 | char* codep; 15 | int code_cap; 16 | 17 | void init_codebuffer () { 18 | code_cap = 1024 * 1024; 19 | code = malloc(code_cap); 20 | codep = code; 21 | } 22 | 23 | void ensure_code_space () { 24 | int code_size = codep - code; 25 | if(code_size + 2*sizeof(long) > code_cap){ 26 | int new_cap = code_size * 2 + 2*sizeof(long); 27 | char* buf = malloc(new_cap); 28 | memcpy(buf, code, code_size); 29 | free(code); 30 | code = buf; 31 | code_cap = new_cap; 32 | codep = code + code_size; 33 | } 34 | } 35 | 36 | //=========== WRITER ============= 37 | void align_short () { 38 | codep = (char*)(((long)codep + 1)&(-2)); 39 | } 40 | void align_int () { 41 | codep = (char*)(((long)codep + 3)&(-4)); 42 | } 43 | void align_ptr () { 44 | codep = (char*)(((long)codep + 7)&(-8)); 45 | } 46 | 47 | void write_char (char c) { 48 | ensure_code_space(); 49 | codep[0] = c; 50 | codep++; 51 | } 52 | 53 | void write_short (short s) { 54 | ensure_code_space(); 55 | align_short(); 56 | ((short*)codep)[0] = s; 57 | codep += sizeof(short); 58 | } 59 | 60 | void write_int (int i) { 61 | ensure_code_space(); 62 | align_int(); 63 | ((int*)codep)[0] = i; 64 | codep += sizeof(int); 65 | } 66 | 67 | void write_ptr (void* ptr) { 68 | ensure_code_space(); 69 | align_ptr(); 70 | ((void**)codep)[0] = ptr; 71 | codep += sizeof(void*); 72 | } 73 | 74 | void write_frame (MethodValue* v) { 75 | ensure_code_space(); 76 | write_char(FRAME_INS); 77 | write_char(v->nargs); 78 | write_short(v->nlocals); 79 | } 80 | 81 | //======= PATCH BUFFER =============== 82 | typedef enum { 83 | LABEL_PATCH, 84 | FUNCTION_PATCH, 85 | CLASS_TAG_PATCH, 86 | CLASS_ARITY_PATCH, 87 | GLOBAL_IDX_PATCH, 88 | } PatchType; 89 | 90 | typedef struct { 91 | PatchType type; 92 | int pos; 93 | union{ 94 | char* name; 95 | int class; 96 | }; 97 | } Patch; 98 | 99 | Vector* patches; 100 | 101 | void init_patchbuffer () { 102 | patches = make_vector(); 103 | } 104 | 105 | void write_class_arity (int class) { 106 | Patch* p = malloc(sizeof(Patch)); 107 | p->type = CLASS_ARITY_PATCH; 108 | p->pos = codep - code; 109 | p->class = class; 110 | vector_add(patches, p); 111 | write_char(0); 112 | } 113 | 114 | void write_class_tag (int class) { 115 | align_short(); 116 | Patch* p = malloc(sizeof(Patch)); 117 | p->type = CLASS_TAG_PATCH; 118 | p->pos = codep - code; 119 | p->class = class; 120 | vector_add(patches, p); 121 | write_short(0); 122 | } 123 | 124 | void write_function_ptr (char* name) { 125 | align_ptr(); 126 | Patch* p = malloc(sizeof(Patch)); 127 | p->type = FUNCTION_PATCH; 128 | p->pos = codep - code; 129 | p->name = name; 130 | vector_add(patches, p); 131 | write_ptr(0); 132 | } 133 | 134 | void write_global_idx (char* name) { 135 | align_short(); 136 | Patch* p = malloc(sizeof(Patch)); 137 | p->type = GLOBAL_IDX_PATCH; 138 | p->pos = codep - code; 139 | p->name = name; 140 | vector_add(patches, p); 141 | write_short(0); 142 | } 143 | 144 | void write_label (char* name) { 145 | align_ptr(); 146 | Patch* p = malloc(sizeof(Patch)); 147 | p->type = LABEL_PATCH; 148 | p->pos = codep - code; 149 | p->name = name; 150 | vector_add(patches, p); 151 | write_ptr(0); 152 | } 153 | 154 | //======= TABLE BUFFER =============== 155 | typedef enum { 156 | LABEL_ENTRY, 157 | METHOD_ENTRY, 158 | FUNCTION_ENTRY, 159 | CLASS_ENTRY 160 | } EntryType; 161 | 162 | typedef struct { 163 | EntryType type; 164 | union { 165 | char* name; 166 | int idx; 167 | }; 168 | union { 169 | int codepos; 170 | int class; 171 | }; 172 | } TableEntry; 173 | 174 | Vector* entries; 175 | 176 | void init_tablebuffer () { 177 | entries = make_vector(); 178 | } 179 | 180 | void set_label (char* name) { 181 | TableEntry* e = malloc(sizeof(TableEntry)); 182 | e->type = LABEL_ENTRY; 183 | e->name = name; 184 | e->codepos = codep - code; 185 | vector_add(entries, e); 186 | } 187 | 188 | void* get_label (char* name) { 189 | for(int i=0; isize; i++){ 190 | TableEntry* e = vector_get(entries, i); 191 | if(e->type == LABEL_ENTRY) 192 | if(strcmp(e->name, name) == 0) 193 | return code + e->codepos; 194 | } 195 | printf("No label with name %s.\n", name); 196 | exit(-1); 197 | } 198 | 199 | void set_method_label (int idx) { 200 | TableEntry* e = malloc(sizeof(TableEntry)); 201 | e->type = METHOD_ENTRY; 202 | e->idx = idx; 203 | e->codepos = codep - code; 204 | vector_add(entries, e); 205 | } 206 | 207 | int get_method_pos (int idx) { 208 | for(int i=0; isize; i++){ 209 | TableEntry* e = vector_get(entries, i); 210 | if(e->type == METHOD_ENTRY) 211 | if(e->idx == idx) 212 | return e->codepos; 213 | } 214 | printf("No method with index %d.\n", idx); 215 | exit(-1); 216 | } 217 | 218 | void* get_method_label (int idx) { 219 | return code + get_method_pos(idx); 220 | } 221 | 222 | void set_function_label (char* name, int idx) { 223 | TableEntry* e = malloc(sizeof(TableEntry)); 224 | e->type = FUNCTION_ENTRY; 225 | e->name = name; 226 | e->codepos = get_method_pos(idx); 227 | vector_add(entries, e); 228 | } 229 | 230 | void* get_function_label (char* name) { 231 | for(int i=0; isize; i++){ 232 | TableEntry* e = vector_get(entries, i); 233 | if(e->type == FUNCTION_ENTRY) 234 | if(strcmp(e->name, name) == 0) 235 | return code + e->codepos; 236 | } 237 | printf("No function with name %s.\n", name); 238 | exit(-1); 239 | } 240 | 241 | void set_class_tag (int idx, int class) { 242 | TableEntry* e = malloc(sizeof(TableEntry)); 243 | e->type = CLASS_ENTRY; 244 | e->idx = idx; 245 | e->class = class; 246 | vector_add(entries, e); 247 | } 248 | 249 | int get_class_tag (int idx) { 250 | for(int i=0; isize; i++){ 251 | TableEntry* e = vector_get(entries, i); 252 | if(e->type == CLASS_ENTRY) 253 | if(e->idx == idx) 254 | return e->class; 255 | } 256 | printf("No class with index %d.\n", idx); 257 | exit(-1); 258 | } 259 | 260 | //========== GLOBALS =========== 261 | Vector* globals; 262 | 263 | void init_globals () { 264 | globals = make_vector(); 265 | } 266 | 267 | void new_global (char* name) { 268 | vector_add(globals, name); 269 | } 270 | 271 | int get_global_idx (char* name) { 272 | for(int i=0; isize; i++){ 273 | char* gname = vector_get(globals, i); 274 | if(strcmp(gname, name) == 0) 275 | return i; 276 | } 277 | printf("No global with name %s.\n", name); 278 | exit(-1); 279 | } 280 | 281 | //========== LINKER ============= 282 | char* link_str (Vector* values, int idx) { 283 | StringValue* v = vector_get(values, idx); 284 | return v->value; 285 | } 286 | 287 | void print_code_buffer () { 288 | for(long* cp = (long*)code; cp < (long*)(codep + 8); cp += 1){ 289 | printf("0x%lx: %016lx\n", (long)cp, cp[0]); 290 | } 291 | } 292 | 293 | void link_ins (Vector* values, ByteIns* ins) { 294 | switch(ins->tag){ 295 | case LABEL_OP:{ 296 | LabelIns* ins2 = (LabelIns*)ins; 297 | set_label(link_str(values, ins2->name)); 298 | break; 299 | } 300 | case LIT_OP:{ 301 | LitIns* ins2 = (LitIns*)ins; 302 | Value* v = vector_get(values, ins2->idx); 303 | if(v->tag == INT_VAL){ 304 | write_char(INT_INS); 305 | write_int(((IntValue*)v)->value); 306 | }else if(v->tag == NULL_VAL){ 307 | write_char(NULL_INS); 308 | }else{ 309 | printf("Unrecognized Literal: %d\n", v->tag); 310 | exit(-1); 311 | } 312 | break; 313 | } 314 | case PRINTF_OP:{ 315 | PrintfIns* ins2 = (PrintfIns*)ins; 316 | write_char(PRINTF_INS); 317 | write_char(ins2->arity); 318 | write_ptr(link_str(values, ins2->format)); 319 | break; 320 | } 321 | case ARRAY_OP:{ 322 | write_char(ARRAY_INS); 323 | break; 324 | } 325 | case OBJECT_OP:{ 326 | ObjectIns* ins2 = (ObjectIns*)ins; 327 | write_char(OBJECT_INS); 328 | write_class_arity(ins2->class); 329 | write_class_tag(ins2->class); 330 | break; 331 | } 332 | case SLOT_OP:{ 333 | SlotIns* ins2 = (SlotIns*)ins; 334 | write_char(SLOT_INS); 335 | write_ptr(link_str(values, ins2->name)); 336 | break; 337 | } 338 | case SET_SLOT_OP:{ 339 | SetSlotIns* ins2 = (SetSlotIns*)ins; 340 | write_char(SET_SLOT_INS); 341 | write_ptr(link_str(values, ins2->name)); 342 | break; 343 | } 344 | case CALL_SLOT_OP:{ 345 | CallSlotIns* ins2 = (CallSlotIns*)ins; 346 | write_char(CALL_SLOT_INS); 347 | write_char(ins2->arity); 348 | write_ptr(link_str(values, ins2->name)); 349 | break; 350 | } 351 | case CALL_OP:{ 352 | CallIns* ins2 = (CallIns*)ins; 353 | write_char(CALL_INS); 354 | write_char(ins2->arity); 355 | write_function_ptr(link_str(values, ins2->name)); 356 | break; 357 | } 358 | case SET_LOCAL_OP:{ 359 | SetLocalIns* ins2 = (SetLocalIns*)ins; 360 | write_char(SET_LOCAL_INS); 361 | write_short(ins2->idx); 362 | break; 363 | } 364 | case GET_LOCAL_OP:{ 365 | GetLocalIns* ins2 = (GetLocalIns*)ins; 366 | write_char(GET_LOCAL_INS); 367 | write_short(ins2->idx); 368 | break; 369 | } 370 | case SET_GLOBAL_OP:{ 371 | SetGlobalIns* ins2 = (SetGlobalIns*)ins; 372 | write_char(SET_GLOBAL_INS); 373 | write_global_idx(link_str(values, ins2->name)); 374 | break; 375 | } 376 | case GET_GLOBAL_OP:{ 377 | GetGlobalIns* ins2 = (GetGlobalIns*)ins; 378 | write_char(GET_GLOBAL_INS); 379 | write_global_idx(link_str(values, ins2->name)); 380 | break; 381 | } 382 | case BRANCH_OP:{ 383 | BranchIns* ins2 = (BranchIns*)ins; 384 | write_char(BRANCH_INS); 385 | write_label(link_str(values, ins2->name)); 386 | break; 387 | } 388 | case GOTO_OP:{ 389 | GotoIns* ins2 = (GotoIns*)ins; 390 | write_char(GOTO_INS); 391 | write_label(link_str(values, ins2->name)); 392 | break; 393 | } 394 | case RETURN_OP:{ 395 | write_char(RETURN_INS); 396 | break; 397 | } 398 | case DROP_OP:{ 399 | write_char(DROP_INS); 400 | break; 401 | } 402 | default: 403 | printf("Unknown instruction: %d\n", ins->tag); 404 | exit(-1); 405 | } 406 | } 407 | 408 | //=========== CLASSES ============= 409 | Vector* classes; 410 | int NULL_CLASS_TAG; 411 | int INT_CLASS_TAG; 412 | int ARRAY_CLASS_TAG; 413 | 414 | void init_classes () { 415 | classes = make_vector(); 416 | NULL_CLASS_TAG = classes->size; 417 | vector_add(classes, 0); 418 | INT_CLASS_TAG = classes->size; 419 | vector_add(classes, 0); 420 | ARRAY_CLASS_TAG = classes->size; 421 | vector_add(classes, 0); 422 | } 423 | 424 | int make_class (Vector* values, ClassValue* class) { 425 | //Link 426 | int nvars = 0; 427 | int nslots = class->slots->size; 428 | LSlot* slots = malloc(sizeof(LSlot) * nslots); 429 | for(int i=0; islots, i); 431 | Value* v = vector_get(values, vidx); 432 | switch(v->tag){ 433 | case SLOT_VAL:{ 434 | SlotValue* v2 = (SlotValue*)v; 435 | slots[i].tag = VAR_SLOT; 436 | slots[i].name = link_str(values, v2->name); 437 | slots[i].idx = nvars; 438 | nvars++; 439 | break; 440 | } 441 | case METHOD_VAL:{ 442 | MethodValue* v2 = (MethodValue*)v; 443 | slots[i].tag = CODE_SLOT; 444 | slots[i].name = link_str(values, v2->name); 445 | slots[i].code = get_method_label(vidx); 446 | break; 447 | } 448 | default:{ 449 | printf("Invalid class slot: %d\n", v->tag); 450 | exit(-1); 451 | } 452 | } 453 | } 454 | 455 | //Create Class 456 | LClass* c = malloc(sizeof(LClass)); 457 | c->nvars = nvars; 458 | c->nslots = nslots; 459 | c->slots = slots; 460 | vector_add(classes, c); 461 | 462 | //Return class index 463 | return classes->size - 1; 464 | } 465 | 466 | char* link_program (Program* prog) { 467 | init_codebuffer(); 468 | init_patchbuffer(); 469 | init_tablebuffer(); 470 | init_globals(); 471 | init_classes(); 472 | 473 | //Link code 474 | for(int i=0; ivalues->size; i++){ 475 | MethodValue* v = vector_get(prog->values, i); 476 | if(v->tag == METHOD_VAL){ 477 | set_method_label(i); 478 | write_frame(v); 479 | for(int i=0; icode->size; i++) 480 | link_ins(prog->values, vector_get(v->code, i)); 481 | } 482 | } 483 | 484 | //Link Classes 485 | for(int i=0; ivalues->size; i++){ 486 | ClassValue* v = vector_get(prog->values, i); 487 | if(v->tag == CLASS_VAL){ 488 | int tag = make_class(prog->values, v); 489 | set_class_tag(i, tag); 490 | } 491 | } 492 | 493 | //Link Globals 494 | for(int i=0; islots->size; i++){ 495 | int idx = (int)vector_get(prog->slots, i); 496 | Value* v = vector_get(prog->values, idx); 497 | if(v->tag == SLOT_VAL){ 498 | SlotValue* v2 = (SlotValue*)v; 499 | new_global(link_str(prog->values, v2->name)); 500 | }else if(v->tag == METHOD_VAL){ 501 | MethodValue* v2 = (MethodValue*)v; 502 | set_function_label(link_str(prog->values, v2->name), idx); 503 | }else{ 504 | printf("Not a global.\n"); 505 | exit(-1); 506 | } 507 | } 508 | 509 | //Run Patches 510 | for(int i=0; isize; i++){ 511 | Patch* p = vector_get(patches, i); 512 | switch(p->type){ 513 | case LABEL_PATCH:{ 514 | void* dst = get_label(p->name); 515 | ((void**)(code + p->pos))[0] = dst; 516 | break; 517 | } 518 | case FUNCTION_PATCH:{ 519 | void* dst = get_function_label(p->name); 520 | ((void**)(code + p->pos))[0] = dst; 521 | break; 522 | } 523 | case CLASS_TAG_PATCH:{ 524 | short tag = get_class_tag(p->class); 525 | ((short*)(code + p->pos))[0] = tag; 526 | break; 527 | } 528 | case CLASS_ARITY_PATCH:{ 529 | short tag = get_class_tag(p->class); 530 | LClass* c = vector_get(classes, tag); 531 | (code + p->pos)[0] = c->nvars; 532 | break; 533 | } 534 | case GLOBAL_IDX_PATCH:{ 535 | short idx = get_global_idx(p->name); 536 | ((short*)(code + p->pos))[0] = idx; 537 | break; 538 | } 539 | default: 540 | printf("Unrecognized patch type: %d\n", p->type); 541 | exit(-1); 542 | } 543 | } 544 | 545 | //Return Entry 546 | return get_method_label(prog->entry); 547 | } 548 | 549 | 550 | //============================================================ 551 | //===================== INTERPRETER ========================== 552 | //============================================================ 553 | 554 | typedef struct { 555 | long tag; 556 | long value; 557 | } VMInt; 558 | 559 | typedef struct { 560 | long tag; 561 | long scratch; 562 | } VMNull; 563 | 564 | typedef struct { 565 | long tag; 566 | void* parent; 567 | void* slots[]; 568 | } VMObj; 569 | 570 | typedef struct { 571 | long tag; 572 | long length; 573 | void* items[]; 574 | } VMArray; 575 | 576 | LSlot lookup_method (VMObj* obj, char* name); 577 | LSlot lookup_varslot (VMObj* obj, char* name); 578 | void call_array_slot (char* slotname, int n); 579 | void call_int_slot (char* slotname, int n); 580 | void run_gc (); 581 | void print_obj (VMObj* obj); 582 | 583 | int heap_sz; 584 | char* heap_mem; 585 | char* heap_top; 586 | char* heap_ptr; 587 | char* free_mem; 588 | 589 | char* ip; 590 | int fp; 591 | int n; 592 | Vector* vstack; 593 | Vector* fstack; 594 | void** genv; 595 | VMNull* nullobj; 596 | VMInt* zeroobj; 597 | 598 | void init_heap () { 599 | heap_sz = 1024 * 16; 600 | heap_mem = malloc(heap_sz); 601 | free_mem = malloc(heap_sz); 602 | heap_ptr = heap_mem; 603 | heap_top = heap_mem + heap_sz; 604 | } 605 | 606 | void* halloc (long tag, int sz) { 607 | if(heap_ptr + sz > heap_top){ 608 | run_gc(); 609 | if(heap_ptr + sz > heap_top){ 610 | printf("Out of Memory.\n"); 611 | exit(-1); 612 | } 613 | } 614 | long* obj = (long*)heap_ptr; 615 | obj[0] = tag; 616 | heap_ptr += sz; 617 | return obj; 618 | } 619 | 620 | VMNull* alloc_null () { 621 | return halloc(NULL_CLASS_TAG, sizeof(VMNull)); 622 | } 623 | 624 | VMInt* alloc_int (int value) { 625 | VMInt* i = halloc(INT_CLASS_TAG, sizeof(VMInt)); 626 | i->value = value; 627 | return i; 628 | } 629 | 630 | VMArray* alloc_empty_array (int length) { 631 | VMArray* o = halloc(ARRAY_CLASS_TAG, sizeof(VMArray) + sizeof(void*) * length); 632 | o->length = length; 633 | return o; 634 | } 635 | 636 | VMArray* alloc_array (int length, void* x) { 637 | VMArray* o = alloc_empty_array(length); 638 | for(int i=0; iitems[i] = x; 640 | return o; 641 | } 642 | 643 | VMObj* alloc_object (int class, int nslots) { 644 | return halloc(class, sizeof(VMObj) + sizeof(void*) * nslots); 645 | } 646 | 647 | //============================================================ 648 | //================ GARBAGE COLLECTOR ========================= 649 | //============================================================ 650 | 651 | typedef struct { 652 | long tag; 653 | void* forward; 654 | } BrokenHeart; 655 | 656 | void* make_forward (void* src, void* dst) { 657 | BrokenHeart* bh = (BrokenHeart*)src; 658 | bh->tag = -1; 659 | bh->forward = dst; 660 | return dst; 661 | } 662 | 663 | int sizeof_obj (VMObj* o) { 664 | if(o->tag == NULL_CLASS_TAG) 665 | return sizeof(VMNull); 666 | else if(o->tag == INT_CLASS_TAG) 667 | return sizeof(VMInt); 668 | else if(o->tag == ARRAY_CLASS_TAG){ 669 | VMArray* a = (VMArray*)o; 670 | return sizeof(VMArray) + sizeof(void*) * a->length; 671 | } 672 | else{ 673 | LClass* c = vector_get(classes, o->tag); 674 | return sizeof(VMObj) + sizeof(void*) * c->nvars; 675 | } 676 | } 677 | 678 | void* link_ptr (void* ptr) { 679 | long tag = ((long*)ptr)[0]; 680 | if(tag == -1){ 681 | BrokenHeart* bh = (BrokenHeart*)ptr; 682 | return bh->forward; 683 | }else{ 684 | void* dst = heap_ptr; 685 | int sz = sizeof_obj((VMObj*)ptr); 686 | memcpy(dst, ptr, sz); 687 | heap_ptr += sz; 688 | return make_forward(ptr, dst); 689 | } 690 | } 691 | 692 | void scan_obj (VMObj* o) { 693 | LClass* c = vector_get(classes, o->tag); 694 | o->parent = link_ptr(o->parent); 695 | for(int i=0; invars; i++) 696 | o->slots[i] = link_ptr(o->slots[i]); 697 | } 698 | 699 | void scan_array (VMArray* o) { 700 | for(int i=0; ilength; i++) 701 | o->items[i] = link_ptr(o->items[i]); 702 | } 703 | 704 | void* scan_next (char* ptr) { 705 | long tag = ((long*)ptr)[0]; 706 | if(tag == ARRAY_CLASS_TAG) 707 | scan_array((VMArray*)ptr); 708 | else if(tag != INT_CLASS_TAG && tag != NULL_CLASS_TAG) 709 | scan_obj((VMObj*)ptr); 710 | return ptr + sizeof_obj((VMObj*)ptr); 711 | } 712 | 713 | void scan_fstack () { 714 | int frame_top = fstack->size; 715 | int frame_bot = fp; 716 | while(frame_top > 0){ 717 | for(int i = frame_bot+2; isize; i++){ 728 | void* o = link_ptr(vector_get(vstack, i)); 729 | vector_set(vstack, i, o); 730 | } 731 | } 732 | 733 | void scan_globals () { 734 | for(int i=0; isize; i++) 735 | genv[i] = link_ptr(genv[i]); 736 | } 737 | 738 | void run_gc () { 739 | //Flip flop heap 740 | char* swap = heap_mem; 741 | heap_mem = free_mem; 742 | free_mem = swap; 743 | heap_ptr = heap_mem; 744 | heap_top = heap_ptr + heap_sz; 745 | 746 | //Scan roots 747 | scan_globals(); 748 | scan_fstack(); 749 | scan_vstack(); 750 | nullobj = link_ptr(nullobj); 751 | zeroobj = link_ptr(zeroobj); 752 | 753 | //Scan heap 754 | char* p = heap_mem; 755 | while(p < heap_ptr) 756 | p = scan_next(p); 757 | 758 | //printf("Garbage Collection\n"); 759 | //printf("Number of bytes used: %ld\n", heap_ptr - heap_mem); 760 | } 761 | 762 | //============================================================ 763 | //============================================================ 764 | 765 | 766 | unsigned char next_char () { 767 | unsigned char c = ip[0]; 768 | ip++; 769 | return c; 770 | } 771 | 772 | int next_short () { 773 | ip = (char*)(((long)ip + 1)&(-2)); 774 | int s = ((unsigned short*)ip)[0]; 775 | ip += 2; 776 | return s; 777 | } 778 | 779 | int next_int () { 780 | ip = (char*)(((long)ip + 3)&(-4)); 781 | int s = ((int*)ip)[0]; 782 | ip += 4; 783 | return s; 784 | } 785 | 786 | void* next_ptr () { 787 | ip = (char*)(((long)ip + 7)&(-8)); 788 | void* s = ((void**)ip)[0]; 789 | ip += 8; 790 | return s; 791 | } 792 | 793 | void initvm (char* entry) { 794 | //Initialize State 795 | ip = entry; 796 | fp = 0; 797 | n = 0; 798 | vstack = make_vector(); 799 | fstack = make_vector(); 800 | genv = malloc(sizeof(void*) * globals->size); 801 | init_heap(); 802 | nullobj = alloc_null(); 803 | zeroobj = alloc_int(0); 804 | 805 | //Initialize globals 806 | for(int i=0; isize; i++) 807 | genv[i] = nullobj; 808 | 809 | //Default Frame 810 | vector_add(fstack, 0); 811 | vector_add(fstack, 0); 812 | } 813 | 814 | void ensure_arity (int actual, int desired) { 815 | if(actual != desired){ 816 | printf("Incorrect arity: Expected %d but received %d.\n", desired, actual); 817 | exit(-1); 818 | } 819 | } 820 | 821 | void ensure_parent (VMObj* o) { 822 | if(o->tag == INT_CLASS_TAG){ 823 | printf("Int is not a legal parent.\n"); 824 | exit(-1); 825 | } 826 | else if(o->tag == ARRAY_CLASS_TAG){ 827 | printf("Array is not a legal parent.\n"); 828 | exit(-1); 829 | } 830 | } 831 | 832 | void ensure_int (VMInt* o) { 833 | if(o->tag != INT_CLASS_TAG){ 834 | printf("Not an integer!\n"); 835 | exit(-1); 836 | } 837 | } 838 | 839 | void ensure_index (VMInt* i, VMArray* a) { 840 | ensure_int(i); 841 | if(i->value < 0 || i->value >= a->length){ 842 | printf("Index %d is out of bounds.\n", (int)(i->value)); 843 | exit(-1); 844 | } 845 | } 846 | 847 | void print_vstack () { 848 | printf("["); 849 | for(int i=0; isize; i++){ 850 | VMObj* obj = vector_get(vstack, i); 851 | if(i > 0) printf(" "); 852 | printf("%d", (int)(obj->tag)); 853 | } 854 | printf("]\n"); 855 | } 856 | 857 | void print_obj (VMObj* obj) { 858 | if(obj->tag == NULL_CLASS_TAG){ 859 | printf("null"); 860 | }else if(obj->tag == INT_CLASS_TAG){ 861 | VMInt* o = (VMInt*)obj; 862 | printf("%d", (int)(o->value)); 863 | }else if(obj->tag == ARRAY_CLASS_TAG){ 864 | VMArray* o = (VMArray*)obj; 865 | printf("["); 866 | for(int i=0; ilength; i++){ 867 | if(i > 0) printf(" "); 868 | print_obj(o->items[i]); 869 | } 870 | printf("]"); 871 | }else{ 872 | printf("[Object %ld]", obj->tag); 873 | } 874 | } 875 | 876 | void print_format (char* format, int n) { 877 | int i = vstack->size - n; 878 | while(1){ 879 | char c = format[0]; 880 | if(c == 0) return; 881 | if(c == '~') { 882 | print_obj(vector_get(vstack, i)); 883 | i++; 884 | }else{ 885 | printf("%c", c); 886 | } 887 | format++; 888 | } 889 | } 890 | 891 | void runvm () { 892 | while(ip){ 893 | // printf("IP = 0x%lx\n", ip); 894 | // print_vstack(); 895 | 896 | char tag = next_char(); 897 | switch(tag){ 898 | case INT_INS : { 899 | int i = next_int(); 900 | //printf("Run Int(%d)\n", i); 901 | VMInt* v = alloc_int(i); 902 | vector_add(vstack, v); 903 | break; 904 | } 905 | case NULL_INS : { 906 | //printf("Run Null\n"); 907 | vector_add(vstack, nullobj); 908 | break; 909 | } 910 | case PRINTF_INS : { 911 | int n = next_char(); 912 | char* format = next_ptr(); 913 | //printf("Run Printf("); 914 | //print_string(format); 915 | //printf(", %d)\n", n); 916 | print_format(format, n); 917 | for(int i=0; isize - 2); 925 | ensure_int(len); 926 | VMArray* a = alloc_empty_array(len->value); 927 | void* init = vector_pop(vstack); 928 | vector_pop(vstack); 929 | for(int i=0; ivalue; i++) 930 | a->items[i] = init; 931 | vector_add(vstack, a); 932 | break; 933 | } 934 | case OBJECT_INS: { 935 | int arity = next_char(); 936 | int class = next_short(); 937 | //printf("Run Object(%d,%d)\n", class, arity); 938 | VMObj* o = alloc_object(class, arity); 939 | for(int i = arity-1; i>=0; i--) 940 | o->slots[i] = vector_pop(vstack); 941 | void* parent = vector_pop(vstack); 942 | ensure_parent(parent); 943 | o->parent = parent; 944 | vector_add(vstack, o); 945 | break; 946 | } 947 | case SLOT_INS: { 948 | char* name = next_ptr(); 949 | //printf("Run Slot(%s)\n", name); 950 | VMObj* o = vector_pop(vstack); 951 | if(o->tag == INT_CLASS_TAG || o->tag == NULL_CLASS_TAG || o->tag == ARRAY_CLASS_TAG){ 952 | printf("No variable slot %s for object ", name); 953 | print_obj(o); 954 | printf(".\n"); 955 | } 956 | LSlot slot = lookup_varslot(o, name); 957 | vector_add(vstack, o->slots[slot.idx]); 958 | break; 959 | } 960 | case SET_SLOT_INS: { 961 | char* name = next_ptr(); 962 | //printf("Run SetSlot(%s)\n", name); 963 | void* x = vector_pop(vstack); 964 | VMObj* o = vector_pop(vstack); 965 | if(o->tag == INT_CLASS_TAG || o->tag == NULL_CLASS_TAG || o->tag == ARRAY_CLASS_TAG){ 966 | printf("No variable slot %s for object ", name); 967 | print_obj(o); 968 | printf(".\n"); 969 | } 970 | LSlot slot = lookup_varslot(o, name); 971 | o->slots[slot.idx] = x; 972 | vector_add(vstack, x); 973 | break; 974 | } 975 | case CALL_SLOT_INS: { 976 | n = next_char(); 977 | char* name = next_ptr(); 978 | //printf("Run CallSlot(%s, %d)\n", name, n); 979 | int sp = vstack->size; 980 | VMObj* obj = vector_get(vstack, sp - n); 981 | if(obj->tag == INT_CLASS_TAG){ 982 | call_int_slot(name, n); 983 | break; 984 | } 985 | else if(obj->tag == ARRAY_CLASS_TAG){ 986 | call_array_slot(name, n); 987 | break; 988 | } 989 | else if(obj->tag == NULL_CLASS_TAG){ 990 | printf("No slot named %s for Null.\n", name); 991 | exit(-1); 992 | } 993 | else{ 994 | LSlot m = lookup_method(obj, name); 995 | int newfp = fstack->size; 996 | vector_add(fstack, ip); 997 | vector_add(fstack, (void*)fp); 998 | fp = newfp; 999 | ip = m.code; 1000 | break; 1001 | } 1002 | } 1003 | case CALL_INS : { 1004 | n = next_char(); 1005 | void* code = next_ptr(); 1006 | //printf("Run Call(0x%lx, %d)\n", code, n); 1007 | int newfp = fstack->size; 1008 | vector_add(fstack, ip); 1009 | vector_add(fstack, (void*)fp); 1010 | fp = newfp; 1011 | ip = code; 1012 | break; 1013 | } 1014 | case SET_LOCAL_INS : { 1015 | int idx = next_short(); 1016 | //printf("Run SetLocal(%d)\n", idx); 1017 | void* v = vector_peek(vstack); 1018 | vector_set(fstack, fp + 2 + idx, v); 1019 | break; 1020 | } 1021 | case GET_LOCAL_INS : { 1022 | int idx = next_short(); 1023 | //printf("Run GetLocal(%d)\n", idx); 1024 | void* v = vector_get(fstack, fp + 2 + idx); 1025 | vector_add(vstack, v); 1026 | break; 1027 | } 1028 | case SET_GLOBAL_INS : { 1029 | int idx = next_short(); 1030 | //printf("Run SetGlobal(%d)\n", idx); 1031 | genv[idx] = vector_peek(vstack); 1032 | break; 1033 | } 1034 | case GET_GLOBAL_INS : { 1035 | int idx = next_short(); 1036 | //printf("Run GetGlobal(%d)\n", idx); 1037 | vector_add(vstack, genv[idx]); 1038 | break; 1039 | } 1040 | case BRANCH_INS : { 1041 | void* code = next_ptr(); 1042 | //printf("Run Branch(0x%lx)\n", code); 1043 | VMObj* obj = vector_pop(vstack); 1044 | if(obj->tag != NULL_CLASS_TAG) 1045 | ip = code; 1046 | break; 1047 | } 1048 | case GOTO_INS : { 1049 | void* code = next_ptr(); 1050 | //printf("Run Goto(0x%lx)\n", code); 1051 | ip = code; 1052 | break; 1053 | } 1054 | case RETURN_INS : { 1055 | //printf("Run Return\n"); 1056 | int oldfp = (int)vector_get(fstack, fp + 1); 1057 | ip = vector_get(fstack, fp); 1058 | vector_set_length(fstack, fp, nullobj); 1059 | fp = oldfp; 1060 | break; 1061 | } 1062 | case DROP_INS : { 1063 | //printf("Run Drop\n"); 1064 | vector_pop(vstack); 1065 | break; 1066 | } 1067 | case FRAME_INS : { 1068 | int nargs = next_char(); 1069 | int nlocals = next_short(); 1070 | //printf("Run Frame(%d,%d)\n", nargs, nlocals); 1071 | ensure_arity(n, nargs); 1072 | vector_set_length(fstack, fp + 2 + nargs + nlocals, nullobj); 1073 | for(int i=n-1; i>=0; i--) 1074 | vector_set(fstack, fp + 2 + i, vector_pop(vstack)); 1075 | break; 1076 | } 1077 | default: 1078 | printf("Unknown tag: %d\n", tag); 1079 | exit(-1); 1080 | } 1081 | } 1082 | } 1083 | 1084 | void push_bool (int bool) { 1085 | if(bool) 1086 | vector_add(vstack, zeroobj); 1087 | else 1088 | vector_add(vstack, nullobj); 1089 | } 1090 | 1091 | void push_int (int r) { 1092 | vector_add(vstack, alloc_int(r)); 1093 | } 1094 | 1095 | void call_int_slot (char* slotname, int n) { 1096 | ensure_arity(n, 2); 1097 | VMInt* y = vector_pop(vstack); 1098 | VMInt* x = vector_pop(vstack); 1099 | ensure_int(y); 1100 | if(strcmp(slotname, "eq") == 0) 1101 | push_bool(x->value == y->value); 1102 | else if(strcmp(slotname, "lt") == 0) 1103 | push_bool(x->value < y->value); 1104 | else if(strcmp(slotname, "le") == 0) 1105 | push_bool(x->value <= y->value); 1106 | else if(strcmp(slotname, "gt") == 0) 1107 | push_bool(x->value > y->value); 1108 | else if(strcmp(slotname, "ge") == 0) 1109 | push_bool(x->value >= y->value); 1110 | else if(strcmp(slotname, "add") == 0) 1111 | push_int(x->value + y->value); 1112 | else if(strcmp(slotname, "sub") == 0) 1113 | push_int(x->value - y->value); 1114 | else if(strcmp(slotname, "mul") == 0) 1115 | push_int(x->value * y->value); 1116 | else if(strcmp(slotname, "div") == 0) 1117 | push_int(x->value / y->value); 1118 | else if(strcmp(slotname, "mod") == 0) 1119 | push_int(x->value % y->value); 1120 | else{ 1121 | printf("No slot named %s for Int.\n", slotname); 1122 | exit(-1); 1123 | } 1124 | } 1125 | 1126 | void call_array_slot (char* slotname, int n) { 1127 | if(strcmp(slotname, "get") == 0){ 1128 | ensure_arity(n, 2); 1129 | VMInt* i = vector_pop(vstack); 1130 | VMArray* a = vector_pop(vstack); 1131 | ensure_index(i, a); 1132 | vector_add(vstack, a->items[i->value]); 1133 | } 1134 | else if(strcmp(slotname, "set") == 0){ 1135 | ensure_arity(n, 3); 1136 | void* v = vector_pop(vstack); 1137 | VMInt* i = vector_pop(vstack); 1138 | VMArray* a = vector_pop(vstack); 1139 | ensure_index(i, a); 1140 | a->items[i->value] = v; 1141 | vector_add(vstack, nullobj); 1142 | } 1143 | else if(strcmp(slotname, "length") == 0){ 1144 | ensure_arity(n, 1); 1145 | VMArray* a = vector_pop(vstack); 1146 | push_int(a->length); 1147 | } 1148 | else{ 1149 | printf("No slot named %s for Array.\n", slotname); 1150 | exit(-1); 1151 | } 1152 | } 1153 | 1154 | LSlot lookup_slot (VMObj* obj, char* name) { 1155 | if(obj->tag == NULL_CLASS_TAG){ 1156 | printf("No slot %s for Null.\n", name); 1157 | exit(-1); 1158 | }else{ 1159 | LClass* c = vector_get(classes, obj->tag); 1160 | for(int i=0; inslots; i++){ 1161 | LSlot s = c->slots[i]; 1162 | if(strcmp(s.name, name) == 0) 1163 | return s; 1164 | } 1165 | return lookup_slot(obj->parent, name); 1166 | } 1167 | } 1168 | 1169 | LSlot lookup_method (VMObj* obj, char* name) { 1170 | LSlot s = lookup_slot(obj, name); 1171 | if(s.tag != CODE_SLOT){ 1172 | printf("Slot %s is not a method slot.\n", name); 1173 | exit(-1); 1174 | } 1175 | return s; 1176 | } 1177 | 1178 | LSlot lookup_varslot (VMObj* obj, char* name) { 1179 | LSlot s = lookup_slot(obj, name); 1180 | if(s.tag != VAR_SLOT){ 1181 | printf("Slot %s is not a variable slot.\n", name); 1182 | exit(-1); 1183 | } 1184 | return s; 1185 | } 1186 | --------------------------------------------------------------------------------