├── codecov.yml ├── bors.toml ├── .gitignore ├── compiler ├── testdata │ ├── packages-private-type │ │ ├── vendor │ │ │ └── sub │ │ │ │ └── sub.go │ │ └── main.go │ ├── multi-file-package │ │ ├── bar.go │ │ └── main.go │ ├── hello-world.go │ ├── asm_print.go │ ├── const_no_modify.go │ ├── packages-private │ │ ├── vendor │ │ │ └── sub │ │ │ │ └── sub.go │ │ └── main.go │ ├── init.go │ ├── func-call.go │ ├── import-paren.go │ ├── alloc-pointer-nil.go │ ├── array-compiletime-out-of-range.go │ ├── string-substring-end-out-of-bounds.go │ ├── string-substring-start-out-of-bounds.go │ ├── alloc-pointer-use.go │ ├── for-continue.go │ ├── for-break.go │ ├── slice-len.go │ ├── tour-swap.go │ ├── val-zero.go │ ├── alloc-multi-val.go │ ├── for-3-part.go │ ├── infix.go │ ├── fib.go │ ├── func-multi-ret.go │ ├── return-value.go │ ├── struct-func.go │ ├── vars-scope.go │ ├── increment-decrement.go │ ├── multi-package │ │ ├── vendor │ │ │ └── sub │ │ │ │ └── sub.go │ │ └── main.go │ ├── slice.go │ ├── string-substring-1-arg.go │ ├── func-as-value-from-func.go │ ├── int64-method.go │ ├── func-call-args.go │ ├── string-func.go │ ├── type-cast.go │ ├── var-swap.go │ ├── struct-method-pointer-receiver.go │ ├── arith-correct-order.go │ ├── bitwise.go │ ├── array-inline-init.go │ ├── uint8.go │ ├── slice-init.go │ ├── pkg-var.go │ ├── pointer.go │ ├── tour-for.go │ ├── int-type-detect.go │ ├── string-concat.go │ ├── array.go │ ├── array-range.go │ ├── slice-in-slice.go │ ├── struct-in-struct.go │ ├── struct-method.go │ ├── interface-slice.go │ ├── negative-number.go │ ├── package-vars.go │ ├── bool.go │ ├── struct-init-with-vals.go │ ├── func-as-value.go │ ├── interface.go │ ├── variable.go │ ├── const.go │ ├── tour-named-return.go │ ├── interface-pointer-receiver.go │ ├── interface-slice-consts.go │ ├── func-variadic.go │ ├── slice-range.go │ ├── switch.go │ ├── interface-assign.go │ ├── func-parameter.go │ ├── vars-heap.go │ ├── interface-append-test.go │ ├── string-substring-2-args.go │ ├── struct.go │ ├── interface-methods.go │ ├── slice-append-assign.go │ ├── condition.go │ ├── pointer-pointer.go │ └── slice-append.go ├── lexer │ ├── escape.go │ ├── keywords.go │ ├── lexer_test.go │ └── lexer.go ├── parser │ ├── debug.go │ ├── range.go │ ├── import.go │ ├── infix_sorter.go │ ├── for.go │ ├── switch.go │ ├── var_alloc_test.go │ ├── multi_assign_test.go │ ├── walk.go │ ├── node_types.go │ └── parser_test.go ├── compiler │ ├── internal │ │ ├── pointer │ │ │ └── llvm_pointer.go │ │ ├── slice.go │ │ ├── var.go │ │ └── string.go │ ├── io.go │ ├── name │ │ └── block.go │ ├── syscall │ │ ├── syscall.go │ │ └── print.go │ ├── func_cap.go │ ├── bool.go │ ├── types │ │ ├── builtin.go │ │ ├── package.go │ │ ├── interface.go │ │ └── type.go │ ├── value │ │ └── value.go │ ├── package.go │ ├── strings │ │ └── strings.go │ ├── func_len.go │ ├── switch.go │ ├── pointers.go │ ├── constants.go │ ├── interface.go │ ├── external_funcs.go │ ├── struct.go │ ├── for.go │ ├── array.go │ ├── alloc.go │ ├── types.go │ ├── condition.go │ └── compiler.go ├── passes │ ├── const_iota │ │ ├── iota.go │ │ └── iota_test.go │ └── escape │ │ ├── escape.go │ │ └── escape_test.go └── compiler_test.go ├── hello.tre ├── .github └── dependabot.yml ├── pkg └── fmt │ └── print.tre ├── go.mod ├── .circleci ├── images │ └── goclang │ │ └── Dockerfile └── config.yml ├── LICENSE ├── cmd └── tre │ ├── main.go │ └── build │ └── build.go ├── README.md └── go.sum /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | 4 | comment: off -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 'ci/circleci: tests-%' ] 2 | delete_merged_branches = true 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.bak 3 | 4 | # Ignore binaries 5 | /cmd/tre/tre 6 | /hello 7 | /tre 8 | -------------------------------------------------------------------------------- /compiler/testdata/packages-private-type/vendor/sub/sub.go: -------------------------------------------------------------------------------- 1 | package sub 2 | 3 | type Public string 4 | type private string -------------------------------------------------------------------------------- /hello.tre: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | external.Printf("Hello World!\n") 7 | } 8 | -------------------------------------------------------------------------------- /compiler/lexer/escape.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | var escapeSequences = map[string]string{ 4 | `\"`: "\"", 5 | `\n`: "\n", 6 | } 7 | -------------------------------------------------------------------------------- /compiler/testdata/multi-file-package/bar.go: -------------------------------------------------------------------------------- 1 | package main import "external" 2 | 3 | func bar() { 4 | external.Printf("bar\n") 5 | } 6 | -------------------------------------------------------------------------------- /compiler/testdata/hello-world.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // hello world 7 | fmt.Println("hello world") 8 | } 9 | -------------------------------------------------------------------------------- /compiler/testdata/asm_print.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // aaaa 5 | f := "aaaa\n" 6 | print(f) 7 | 8 | // bbbb 9 | print("bbbb\n") 10 | } 11 | -------------------------------------------------------------------------------- /compiler/testdata/multi-file-package/main.go: -------------------------------------------------------------------------------- 1 | package main import "external" 2 | 3 | // foo 4 | // bar 5 | 6 | func main() { 7 | external.Printf("foo\n") 8 | bar() 9 | } 10 | -------------------------------------------------------------------------------- /compiler/testdata/const_no_modify.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const ( 4 | c = 12 5 | ) 6 | 7 | func main() { 8 | // compile panic: Can only assign to variable 9 | c = 40 10 | } 11 | -------------------------------------------------------------------------------- /compiler/testdata/packages-private/vendor/sub/sub.go: -------------------------------------------------------------------------------- 1 | package sub 2 | 3 | func Public() string { 4 | return "public" 5 | } 6 | 7 | func private() string { 8 | return "private" 9 | } 10 | -------------------------------------------------------------------------------- /compiler/testdata/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func init() { 4 | print("1\n") // 1 5 | } 6 | 7 | func init() { 8 | print("2\n") // 2 9 | } 10 | 11 | func main() { 12 | print("3\n") // 3 13 | } 14 | -------------------------------------------------------------------------------- /compiler/parser/debug.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func (p *parser) printInput() { 8 | for i, p := range p.input { 9 | log.Printf("%d - %+v\n", i, p) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /compiler/testdata/func-call.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func otherfunc() { 6 | // in other func 7 | fmt.Println("in other func\n") 8 | } 9 | 10 | func main() { 11 | otherfunc() 12 | } 13 | -------------------------------------------------------------------------------- /compiler/testdata/import-paren.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | // hey 10 | // 123 11 | fmt.Println("hey") 12 | external.Printf("%d\n", 123) 13 | } -------------------------------------------------------------------------------- /compiler/testdata/alloc-pointer-nil.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type mytype struct{ 6 | a int 7 | } 8 | 9 | func main() { 10 | var foo *mytype 11 | // Expected: runtime crash 12 | foo.a = 100 13 | } -------------------------------------------------------------------------------- /compiler/testdata/array-compiletime-out-of-range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var array [4]int 7 | 8 | // compile panic: index out of range 9 | external.Printf("%d\n", array[10]) 10 | } 11 | -------------------------------------------------------------------------------- /compiler/testdata/string-substring-end-out-of-bounds.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | mystr := "hello" 7 | // runtime panic: substring out of bounds 8 | external.Printf("%s\n", mystr[1:6]) 9 | } 10 | -------------------------------------------------------------------------------- /compiler/testdata/string-substring-start-out-of-bounds.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | mystr := "hello" 7 | // runtime panic: substring out of bounds 8 | external.Printf("%s\n", mystr[6:10]) 9 | } 10 | -------------------------------------------------------------------------------- /compiler/testdata/packages-private-type/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sub" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | // compile panic: Can't use private from outside of sub 10 | var s1 sub.Public 11 | var s2 sub.private 12 | } 13 | -------------------------------------------------------------------------------- /compiler/testdata/alloc-pointer-use.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type mytype struct{ 6 | a int 7 | } 8 | 9 | func main() { 10 | var foo *mytype 11 | foo = &mytype{} 12 | foo.a = 100 13 | external.Printf("%d\n", foo.a) // 100 14 | } -------------------------------------------------------------------------------- /compiler/testdata/packages-private/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sub" 5 | "fmt" 6 | ) 7 | 8 | func main() { 9 | // compile panic: Can't use private from outside of sub 10 | fmt.Println(sub.Public()) 11 | fmt.Println(sub.private()) 12 | } 13 | -------------------------------------------------------------------------------- /compiler/testdata/for-continue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | // 0 6 | // 1 7 | // 3 8 | // 4 9 | 10 | func main() { 11 | for i := 0; i < 5; i = i + 1 { 12 | if i == 2 { 13 | continue 14 | } 15 | external.Printf("%d\n", i) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /compiler/testdata/for-break.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | // 0 6 | // 1 7 | // 2 8 | // 3 9 | // 4 10 | 11 | func main() { 12 | for i := 0; i < 10; i = i + 1 { 13 | external.Printf("%d\n", i) 14 | if i == 4 { 15 | break 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /compiler/compiler/internal/pointer/llvm_pointer.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | import ( 4 | "github.com/llir/llvm/ir/types" 5 | "github.com/llir/llvm/ir/value" 6 | ) 7 | 8 | func ElemType(src value.Value) types.Type { 9 | return src.Type().(*types.PointerType).ElemType 10 | } 11 | -------------------------------------------------------------------------------- /compiler/testdata/slice-len.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var arr [10]int 7 | 8 | for i := 0; i < 10; i = i + 1 { 9 | arr[i] = i 10 | } 11 | 12 | slice := arr[2:5] 13 | 14 | // 3 15 | external.Printf("%d\n", len(slice)) 16 | } 17 | -------------------------------------------------------------------------------- /compiler/testdata/tour-swap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func swap(x string, y string) (string, string) { 6 | return y, x 7 | } 8 | 9 | func main() { 10 | // world hello 11 | a, b := swap("hello", "world") 12 | external.Printf("%s %s\n", a, b) 13 | } 14 | -------------------------------------------------------------------------------- /compiler/testdata/val-zero.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var b bool 7 | external.Printf("%d\n", b) // 0 8 | 9 | var i8 int8 10 | external.Printf("%d\n", i8) // 0 11 | 12 | var i32 int 13 | external.Printf("%d\n", i32) // 0 14 | } 15 | -------------------------------------------------------------------------------- /compiler/testdata/alloc-multi-val.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | // 10 20 7 | a1, b1 := 10, 20 8 | external.Printf("%d %d\n", a1, b1) 9 | 10 | // 10 20 30 11 | a2, b2, c2 := 10, 20, 30 12 | external.Printf("%d %d %d\n", a2, b2, c2) 13 | } 14 | -------------------------------------------------------------------------------- /compiler/testdata/for-3-part.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | // 0 6 | // 1 7 | // 2 8 | // 3 9 | // 4 10 | // 5 11 | // 6 12 | // 7 13 | // 8 14 | // 9 15 | 16 | func main() { 17 | for i := 0; i < 10; i = i + 1 { 18 | external.Printf("%d\n", i) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /compiler/testdata/infix.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | ) 6 | 7 | func main() { 8 | external.Printf("%d\n", 1+2*3) // 7 9 | external.Printf("%d\n", 1*2+3) // 5 10 | external.Printf("%d\n", (1+2)*3) // 9 11 | external.Printf("%d\n", 1+(2*3)) // 7 12 | } 13 | -------------------------------------------------------------------------------- /compiler/testdata/fib.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func fib(num int) int { 6 | if num < 2 { 7 | return num 8 | } 9 | 10 | return fib(num-2) + fib(num-1) 11 | } 12 | 13 | func main() { 14 | // fib = 5702887 15 | external.Printf("fib = %d\n", fib(34)) 16 | } 17 | -------------------------------------------------------------------------------- /compiler/testdata/func-multi-ret.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func otherfunc() (int, int) { 6 | return 100, 200 7 | } 8 | 9 | func main() { 10 | a, b := otherfunc() 11 | external.Printf("a: %d\n", a) // a: 100 12 | external.Printf("b: %d\n", b) // b: 200 13 | } 14 | -------------------------------------------------------------------------------- /compiler/testdata/return-value.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | import "fmt" 5 | 6 | func otherfunc() int { 7 | // in other func 8 | fmt.Println("in other func") 9 | return 100 10 | } 11 | 12 | func main() { 13 | // 100 14 | external.Printf("%d\n", otherfunc()) 15 | } 16 | -------------------------------------------------------------------------------- /compiler/testdata/struct-func.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type a struct { 6 | fn func(int) int 7 | } 8 | 9 | func main() { 10 | v := a{ 11 | fn: func(a int) int { 12 | return a + 1 13 | }, 14 | } 15 | 16 | // 3 17 | external.Printf("%d\n", v.fn(2)) 18 | } 19 | -------------------------------------------------------------------------------- /compiler/testdata/vars-scope.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := 100 7 | 8 | if a == 100 { 9 | external.Printf("%d\n", a) // 100 10 | a := 200 11 | external.Printf("%d\n", a) // 200 12 | } 13 | 14 | external.Printf("%d\n", a) // 100 15 | } 16 | -------------------------------------------------------------------------------- /compiler/testdata/increment-decrement.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | i := 0 7 | i++ 8 | external.Printf("%d\n", i) // 1 9 | external.Printf("%d\n", i++) // 2 10 | external.Printf("%d\n", i--) // 1 11 | i-- 12 | external.Printf("%d\n", i) // 0 13 | } 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "11:00" 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - zegl 11 | ignore: 12 | - dependency-name: github.com/llir/llvm 13 | versions: 14 | - 0.3.2 15 | -------------------------------------------------------------------------------- /compiler/testdata/multi-package/vendor/sub/sub.go: -------------------------------------------------------------------------------- 1 | package sub 2 | 3 | type AnotherInt int64 4 | 5 | func (ai AnotherInt) Plus5() int64 { 6 | return ai + 5 7 | } 8 | 9 | var PackageVar string 10 | 11 | func GetPackageVar() string { 12 | return PackageVar 13 | } 14 | 15 | func World() string { 16 | return "World" 17 | } 18 | -------------------------------------------------------------------------------- /compiler/testdata/slice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var arr [10]int 7 | 8 | for i := 0; i < 10; i = i + 1 { 9 | arr[i] = i 10 | } 11 | 12 | slice := arr[2:4] 13 | 14 | // 2 15 | external.Printf("%d\n", slice[0]) 16 | 17 | // 3 18 | external.Printf("%d\n", slice[1]) 19 | } 20 | -------------------------------------------------------------------------------- /compiler/testdata/string-substring-1-arg.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | mystr := "hello" 7 | 8 | // h 9 | external.Printf("%c\n", mystr[0]) 10 | 11 | // o 12 | external.Printf("%c\n", mystr[4]) 13 | 14 | // runtime panic: index out of range 15 | external.Printf("%s\n", mystr[5]) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/fmt/print.tre: -------------------------------------------------------------------------------- 1 | package fmt 2 | 3 | import "external" 4 | 5 | func Println(a string) { 6 | external.Printf("%s\n", a) 7 | } 8 | 9 | func Printf(format string, a ...interface{}) { 10 | // This does not work 11 | // TODO: Figure out how to convert tre-interfaces to vararg C-style calls 12 | external.Printf(format, a...) 13 | } 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zegl/tre 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/llir/llvm v0.3.0 8 | github.com/spf13/pflag v1.0.5 9 | github.com/stretchr/testify v1.7.0 10 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 11 | gopkg.in/yaml.v2 v2.2.4 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /compiler/testdata/func-as-value-from-func.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func f1() int { 6 | return 100 7 | } 8 | 9 | func f2(a int, b int) int { 10 | return a + b 11 | } 12 | 13 | func main() { 14 | fn := f1 15 | external.Printf("%d\n", fn()) // 100 16 | 17 | ff := f2 18 | external.Printf("%d\n", ff(5, 6)) // 11 19 | } 20 | -------------------------------------------------------------------------------- /compiler/testdata/int64-method.go: -------------------------------------------------------------------------------- 1 | // yoloyolo = 100 2 | 3 | package main 4 | 5 | import "external" 6 | 7 | type myint int64 8 | 9 | func (m myint) Yolo() { 10 | external.Printf("yoloyolo = %d\n", m) 11 | } 12 | 13 | func main() { 14 | var abc myint 15 | abc = 100 16 | abc.Yolo() 17 | 18 | f1 := func() int { 19 | return 100 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /compiler/testdata/func-call-args.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func otherfunc(num int) int { 6 | // in other func = 100 7 | external.Printf("in other func = %d\n", num) 8 | return num + 10 9 | } 10 | 11 | func main() { 12 | res := otherfunc(100) 13 | // in main func = 110 14 | external.Printf("in main func = %d\n", res) 15 | } 16 | -------------------------------------------------------------------------------- /compiler/parser/range.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type RangeNode struct { 8 | baseNode 9 | Item Node 10 | } 11 | 12 | func (r RangeNode) String() string { 13 | return fmt.Sprintf("range %s", r.Item) 14 | } 15 | 16 | func (p *parser) parseRange() *RangeNode { 17 | p.i++ 18 | return &RangeNode{Item: p.parseOne(true)} 19 | } 20 | -------------------------------------------------------------------------------- /compiler/testdata/string-func.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | // a 6 | // 1 7 | // bb 8 | // 2 9 | // ccc 10 | // 3 11 | 12 | func myfunc(yolo string) int64 { 13 | external.Printf("%s\n", yolo) 14 | external.Printf("%d\n", len(yolo)) 15 | return 0 16 | } 17 | 18 | func main() { 19 | myfunc("a") 20 | myfunc("bb") 21 | myfunc("ccc") 22 | } 23 | -------------------------------------------------------------------------------- /compiler/testdata/type-cast.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func otherfunc(num int32) int32 { 6 | if num > int32(50) { 7 | return int32(500) 8 | } 9 | 10 | return int32(num) 11 | } 12 | 13 | func main() { 14 | // 20 15 | external.Printf("%d\n", otherfunc(int32(20))) 16 | // 500 17 | external.Printf("%d\n", otherfunc(int32(100))) 18 | } 19 | -------------------------------------------------------------------------------- /compiler/testdata/var-swap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := 100 7 | b := 200 8 | 9 | // 100 200 10 | external.Printf("%d %d\n", a, b) 11 | 12 | b, a = a, b 13 | 14 | // 200 100 15 | external.Printf("%d %d\n", a, b) 16 | 17 | c := 111 18 | d := 222 19 | a, b = c, d 20 | 21 | // 111 222 22 | external.Printf("%d %d\n", a, b) 23 | } 24 | -------------------------------------------------------------------------------- /compiler/compiler/internal/slice.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/llir/llvm/ir/types" 5 | ) 6 | 7 | func Slice(itemType types.Type) *types.StructType { 8 | return types.NewStruct( 9 | types.I32, // Len 10 | types.I32, // Cap 11 | types.I32, // Array Offset 12 | types.NewPointer(itemType), // Content 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /compiler/testdata/struct-method-pointer-receiver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type myint struct { 6 | A int 7 | } 8 | 9 | func (m *myint) Yolo() { 10 | external.Printf("yolo = %d\n", m.A) 11 | m.A = 200 12 | } 13 | 14 | func main() { 15 | var abc myint 16 | abc.A = 100 17 | abc.Yolo() // yolo = 100 18 | abc.Yolo() // yolo = 200 19 | abc.Yolo() // yolo = 200 20 | } 21 | -------------------------------------------------------------------------------- /compiler/testdata/arith-correct-order.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type f struct { 6 | a int64 7 | } 8 | 9 | func main() { 10 | // 56 11 | external.Printf("%d\n", 100 / 3 / 4 * 7) 12 | 13 | // 56 14 | f1 := f {a : 3} 15 | external.Printf("%d\n", 100 / f1.a / 4 * 7) 16 | 17 | // 10 18 | external.Printf("%d\n", 2 * 3 + 4) 19 | 20 | // 14 21 | external.Printf("%d\n", 2 + 3 * 4) 22 | } -------------------------------------------------------------------------------- /compiler/testdata/bitwise.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | // 9 7 | external.Printf("%d\n", 77&155) 8 | 9 | // 223 10 | external.Printf("%d\n", 77|155) 11 | 12 | // 214 13 | external.Printf("%d\n", 77^155) 14 | 15 | // 68 16 | external.Printf("%d\n", 77&^155) 17 | 18 | // 8 19 | external.Printf("%d\n", 1<<3) 20 | 21 | // 205 22 | external.Printf("%d\n", 822>>2) 23 | } 24 | -------------------------------------------------------------------------------- /.circleci/images/goclang/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.11-stretch 2 | 3 | # Install clang 4 | RUN apt-get update -q && \ 5 | apt-get install -y -q clang-3.9 clang-tidy-3.9 clang-format-3.9 && \ 6 | ln -s /usr/bin/clang-3.9 /usr/local/bin/clang && \ 7 | ln -s /usr/bin/clang-tidy-3.9 /usr/local/bin/clang-tidy && \ 8 | ln -s /usr/bin/clang-format-3.9 /usr/local/bin/clang-format && \ 9 | rm -rf /var/lib/apt/lists/* 10 | 11 | -------------------------------------------------------------------------------- /compiler/testdata/array-inline-init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | // 0 0 0 0 7 | a := [4]int{} 8 | external.Printf("%d %d %d %d\n", a[0], a[1], a[2], a[3]) 9 | 10 | // 10 20 30 40 11 | b := [4]int{10, 20, 30, 40} 12 | external.Printf("%d %d %d %d\n", b[0], b[1], b[2], b[3]) 13 | 14 | // 100 0 0 0 15 | c := [4]int{100} 16 | external.Printf("%d %d %d %d\n", c[0], c[1], c[2], c[3]) 17 | } 18 | -------------------------------------------------------------------------------- /compiler/testdata/uint8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | ) 6 | 7 | func main() { 8 | var u uint8 9 | var i int8 10 | 11 | 12 | u = 127 13 | i = 127 14 | 15 | // 127 127 16 | external.Printf("%hhi %hhu\n", i, u) 17 | 18 | i++ 19 | u++ 20 | 21 | // -128 128 22 | external.Printf("%hhi %hhu\n", i, u) 23 | 24 | i = 0 25 | u = 0 26 | i-- 27 | u-- 28 | 29 | // -1 255 30 | external.Printf("%hhi %hhu\n", i, u) 31 | } 32 | -------------------------------------------------------------------------------- /compiler/testdata/slice-init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | foo := []int{1, 2, 3} 7 | external.Printf("%d\n", foo[0]) // 1 8 | external.Printf("%d\n", foo[1]) // 2 9 | external.Printf("%d\n", foo[2]) // 3 10 | 11 | external.Printf("%d\n", len(foo)) // 3 12 | external.Printf("%d\n", cap(foo)) // 3 13 | 14 | bar := []int{} 15 | external.Printf("%d\n", len(bar)) // 0 16 | external.Printf("%d\n", cap(bar)) // 2 17 | } 18 | -------------------------------------------------------------------------------- /compiler/compiler/internal/var.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | llvmValue "github.com/llir/llvm/ir/value" 6 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 7 | "github.com/zegl/tre/compiler/compiler/value" 8 | ) 9 | 10 | func LoadIfVariable(block *ir.Block, val value.Value) llvmValue.Value { 11 | if val.IsVariable { 12 | return block.NewLoad(pointer.ElemType(val.Value), val.Value) 13 | } 14 | return val.Value 15 | } 16 | -------------------------------------------------------------------------------- /compiler/testdata/pkg-var.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type e struct { 8 | s string 9 | } 10 | 11 | var a string = "froop" 12 | var b = "hey" 13 | var c string 14 | var d1, d2 = "d1", "d2" 15 | var e1 = e{s: "fff"} 16 | 17 | func main() { 18 | fmt.Println(a) // froop 19 | fmt.Println(b) // hey 20 | c = "bar" 21 | fmt.Println(c) // bar 22 | fmt.Println(d1) // d1 23 | fmt.Println(d2) // d2 24 | fmt.Println(e1.s) // fff 25 | } 26 | -------------------------------------------------------------------------------- /compiler/testdata/pointer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | i := 100 7 | iptr := &i 8 | 9 | // 100 10 | // 100 11 | external.Printf("%d\n", i) 12 | external.Printf("%d\n", *iptr) 13 | 14 | *iptr = 200 15 | 16 | // 200 17 | // 200 18 | external.Printf("%d\n", i) 19 | external.Printf("%d\n", *iptr) 20 | 21 | i = 300 22 | 23 | // 300 24 | // 300 25 | external.Printf("%d\n", i) 26 | external.Printf("%d\n", *iptr) 27 | } 28 | -------------------------------------------------------------------------------- /compiler/testdata/tour-for.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | ) 6 | 7 | func main() { 8 | sum := 0 9 | for i := 0; i < 10; i++ { 10 | sum += i 11 | } 12 | // 45 13 | external.Printf("%d\n", sum) 14 | 15 | sum = 400 16 | for i := 0; i < 14; i++ { 17 | sum -= i 18 | } 19 | // 309 20 | external.Printf("%d\n", sum) 21 | 22 | sum = 4 23 | for i := 1; i < 6; i++ { 24 | sum *= i 25 | } 26 | // 480 27 | external.Printf("%d\n", sum) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/compiler/io.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/zegl/tre/compiler/compiler/syscall" 5 | "github.com/zegl/tre/compiler/compiler/types" 6 | "github.com/zegl/tre/compiler/compiler/value" 7 | "github.com/zegl/tre/compiler/parser" 8 | ) 9 | 10 | func (c *Compiler) printFuncCall(v *parser.CallNode) value.Value { 11 | arg := c.compileValue(v.Arguments[0]) 12 | syscall.Print(c.contextBlock, arg, c.GOOS) 13 | return value.Value{Type: types.Void} 14 | } 15 | -------------------------------------------------------------------------------- /compiler/testdata/int-type-detect.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var a int8 7 | a = 100 8 | external.Printf("%d\n", a) // 100 9 | 10 | var b int16 11 | b = 200 12 | external.Printf("%d\n", b) // 200 13 | 14 | var c int32 15 | c = 300 16 | external.Printf("%d\n", c) // 300 17 | 18 | var d int64 19 | d = 400 20 | external.Printf("%d\n", d) // 400 21 | 22 | var e int 23 | e = 500 24 | external.Printf("%d\n", e) // 500 25 | } 26 | -------------------------------------------------------------------------------- /compiler/testdata/string-concat.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | // 3 - foo 7 | foo := "foo" 8 | external.Printf("%d - %s\n", len(foo), foo) 9 | 10 | // 10 - fooyollllo 11 | foo = foo + "yollllo" 12 | external.Printf("%d - %s\n", len(foo), foo) 13 | 14 | // 16 - fooyolllloh11100 15 | foo = foo + "h11100" 16 | external.Printf("%d - %s\n", len(foo), foo) 17 | 18 | // abbcccdddd 19 | external.Printf("%s\n", "a"+"bb"+"ccc"+"dddd") 20 | } 21 | -------------------------------------------------------------------------------- /compiler/testdata/array.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var array [4]int 7 | 8 | array[0] = 100 9 | array[1] = 200 10 | array[2] = 300 11 | array[3] = 400 12 | 13 | // 100 14 | // 200 15 | // 300 16 | // 400 17 | external.Printf("%d\n", array[0]) 18 | external.Printf("%d\n", array[1]) 19 | external.Printf("%d\n", array[2]) 20 | external.Printf("%d\n", array[3]) 21 | 22 | // len = 4 23 | external.Printf("len = %d\n", len(array)) 24 | } 25 | -------------------------------------------------------------------------------- /compiler/testdata/array-range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | 6 | func ff() [2]int{ 7 | var s [2]int 8 | s[0] = 40 9 | s[1] = 50 10 | return s 11 | } 12 | 13 | func main() { 14 | var s [3]int 15 | s[0] = 10 16 | s[1] = 20 17 | s[2] = 30 18 | 19 | // 0 10 20 | // 1 20 21 | // 2 30 22 | for k, v := range s { 23 | external.Printf("%d %d\n", k, v) 24 | } 25 | 26 | // 0 40 27 | // 1 50 28 | for k, v := range ff() { 29 | external.Printf("%d %d\n", k, v) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /compiler/testdata/slice-in-slice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := [][]int{} 7 | external.Printf("%d\n", len(a)) // 0 8 | 9 | a = append(a, []int{100}) 10 | external.Printf("%d\n", len(a)) // 1 11 | external.Printf("%d\n", a[0][0]) // 100 12 | 13 | a = append(a, []int{200, 201}) 14 | external.Printf("%d\n", len(a)) // 2 15 | external.Printf("%d\n", a[0][0]) // 100 16 | external.Printf("%d\n", a[1][0]) // 200 17 | external.Printf("%d\n", a[1][1]) // 201 18 | } 19 | -------------------------------------------------------------------------------- /compiler/testdata/struct-in-struct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type Book struct { 6 | book_id int 7 | } 8 | 9 | type Bookshelf struct { 10 | book0 Book 11 | book1 Book 12 | } 13 | 14 | func main() { 15 | var shelf Bookshelf 16 | var b0 Book 17 | 18 | shelf.book0 = b0 19 | shelf.book0.book_id = 1000 20 | 21 | shelf.book1.book_id = 2000 22 | 23 | // 1000 24 | external.Printf("%d\n", shelf.book0.book_id) 25 | 26 | // 2000 27 | external.Printf("%d\n", shelf.book1.book_id) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/testdata/struct-method.go: -------------------------------------------------------------------------------- 1 | // yoloyolo = 100 2 | // yoloyolo = 100 3 | // witharg = 500 4 | 5 | package main 6 | 7 | import "external" 8 | 9 | type myint struct { 10 | A int 11 | } 12 | 13 | func (m myint) Yolo() { 14 | external.Printf("yoloyolo = %d\n", m.A) 15 | m.A = 200 16 | } 17 | 18 | func (m myint) WithArg(arg int) { 19 | external.Printf("witharg = %d\n", arg) 20 | } 21 | 22 | func main() { 23 | var abc myint 24 | abc.A = 100 25 | abc.Yolo() 26 | abc.Yolo() 27 | abc.WithArg(500) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/compiler/name/block.go: -------------------------------------------------------------------------------- 1 | package name 2 | 3 | import "fmt" 4 | 5 | var blockIndex uint64 6 | var anonFuncIndex uint64 7 | 8 | func Block() string { 9 | name := fmt.Sprintf("block-%d", blockIndex) 10 | blockIndex++ 11 | return name 12 | } 13 | 14 | func AnonFunc() string { 15 | name := fmt.Sprintf("fn-%d", anonFuncIndex) 16 | anonFuncIndex++ 17 | return name 18 | } 19 | 20 | func Var(prefix string) string { 21 | name := fmt.Sprintf("%s-%d", prefix, blockIndex) 22 | blockIndex++ 23 | return name 24 | } 25 | -------------------------------------------------------------------------------- /compiler/testdata/multi-package/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | "sub" 6 | ) 7 | 8 | var PackageVar string 9 | 10 | func main() { 11 | external.Printf("%s\n", sub.World()) // World 12 | 13 | var a sub.AnotherInt 14 | a = 10 15 | 16 | external.Printf("%d\n", a.Plus5()) // 15 17 | 18 | sub.PackageVar = "inAnotherPkg" 19 | PackageVar = "thisPackageVar" 20 | 21 | external.Printf("%s\n", sub.GetPackageVar()) // inAnotherPkg 22 | external.Printf("%s\n", PackageVar) // thisPackageVar 23 | } 24 | -------------------------------------------------------------------------------- /compiler/testdata/interface-slice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a1 := 100 7 | a2 := 200 8 | a := []interface{}{a1, a2} 9 | 10 | // 100 11 | fromSlice0, ok := a[0].(int64) 12 | if ok { 13 | external.Printf("%d\n", fromSlice0) 14 | } 15 | 16 | // 200 17 | fromSlice1, ok := a[1].(int64) 18 | if ok { 19 | external.Printf("%d\n", fromSlice1) 20 | } 21 | 22 | // 100 23 | // 200 24 | for k, v := range a { 25 | fromV, ok := v.(int64) 26 | if ok { 27 | external.Printf("%d\n", fromV) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /compiler/testdata/negative-number.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | 6 | func baz() int32 { 7 | return int32(-18) 8 | } 9 | 10 | 11 | func main() { 12 | foo := -4 13 | // foo is = -4 14 | // -foo is = 4 15 | external.Printf("foo is = %d\n", foo) 16 | external.Printf("-foo is = %d\n", -foo) 17 | 18 | // bar is = -16 19 | a := 16 20 | bar := -a 21 | external.Printf("bar is = %d\n", bar) 22 | 23 | // baz is = -18 24 | external.Printf("baz is = %d\n", baz()) 25 | 26 | // -baz is = 18 27 | external.Printf("-baz is = %d\n", -baz()) 28 | } 29 | -------------------------------------------------------------------------------- /compiler/compiler/internal/string.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | "github.com/llir/llvm/ir/types" 6 | ) 7 | 8 | func String() *types.StructType { 9 | return types.NewStruct( 10 | types.I64, // String length 11 | types.NewPointer(types.I8), // Content 12 | ) 13 | } 14 | 15 | func StringLen(stringType types.Type) *ir.Func { 16 | param := ir.NewParam("input", stringType) 17 | res := ir.NewFunc("string_len", types.I64, param) 18 | block := res.NewBlock("entry") 19 | block.NewRet(block.NewExtractValue(param, 0)) 20 | return res 21 | } 22 | -------------------------------------------------------------------------------- /compiler/lexer/keywords.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | var keywords = map[string]struct{}{ 4 | "if": {}, 5 | "else": {}, 6 | "func": {}, 7 | "return": {}, 8 | "type": {}, 9 | "struct": {}, 10 | "var": {}, 11 | "const": {}, 12 | "package": {}, 13 | "for": {}, 14 | "break": {}, 15 | "continue": {}, 16 | "import": {}, 17 | "true": {}, 18 | "false": {}, 19 | "interface": {}, 20 | "range": {}, 21 | "switch": {}, 22 | "case": {}, 23 | "fallthrough": {}, 24 | "default": {}, 25 | } 26 | -------------------------------------------------------------------------------- /compiler/testdata/package-vars.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "external" 5 | ) 6 | 7 | var foo string 8 | var num int 9 | var sli []int 10 | 11 | func main() { 12 | external.Printf("foo: '%s'\n", foo) // foo: '' 13 | external.Printf("num: %d\n", num) // num: 0 14 | external.Printf("sli: %d\n", len(sli)) // sli: 0 15 | 16 | foo = "abc" 17 | num = 3 18 | sli = append(sli, 1) 19 | sli = append(sli, 2) 20 | sli = append(sli, 3) 21 | 22 | external.Printf("foo: '%s'\n", foo) // foo: 'abc' 23 | external.Printf("num: %d\n", num) // num: 3 24 | external.Printf("sli: %d\n", len(sli)) // sli: 3 25 | } 26 | -------------------------------------------------------------------------------- /compiler/compiler/syscall/syscall.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | // https://opensource.apple.com/source/xnu/xnu-2782.20.48/bsd/kern/syscalls.master 4 | type fn string 5 | 6 | const ( 7 | EXIT fn = "EXIT" 8 | WRITE fn = "WRITE" 9 | ) 10 | 11 | var convDarwin = map[fn]int64{ 12 | EXIT: 0x2000001, 13 | WRITE: 0x2000004, 14 | } 15 | 16 | var convLinux = map[fn]int64{ 17 | EXIT: 60, 18 | WRITE: 1, 19 | } 20 | 21 | func Convert(f fn, goos string) int64 { 22 | switch goos { 23 | case "darwin": 24 | return convDarwin[f] 25 | case "linux": 26 | return convLinux[f] 27 | default: 28 | panic("unknown goos") 29 | } 30 | } -------------------------------------------------------------------------------- /compiler/testdata/bool.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | import "fmt" 5 | 6 | func main() { 7 | // 1 8 | // 0 9 | // a was true 10 | // b was not true 11 | // 0 12 | 13 | a := true 14 | external.Printf("%d\n", a) 15 | 16 | a = false 17 | external.Printf("%d\n", a) 18 | 19 | a = true 20 | if a { 21 | fmt.Println("a was true") 22 | } 23 | if !a { 24 | fmt.Println("a was not true") 25 | } 26 | 27 | b := false 28 | if b { 29 | fmt.Println("b was true") 30 | } 31 | if !b { 32 | fmt.Println("b was not true") 33 | } 34 | 35 | var c bool 36 | c = false 37 | external.Printf("%d\n", c) 38 | } 39 | -------------------------------------------------------------------------------- /compiler/testdata/struct-init-with-vals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type MyStruct struct { 6 | Foo string 7 | Bar int 8 | } 9 | 10 | func main() { 11 | bar := MyStruct{ 12 | Foo: "hello bar", 13 | Bar: 1234, 14 | } 15 | external.Printf("%s\n", bar.Foo) // hello bar 16 | external.Printf("%d\n", bar.Bar) // 1234 17 | 18 | baz := MyStruct{Foo: "hello baz", Bar: 5000} 19 | external.Printf("%s\n", baz.Foo) // hello baz 20 | external.Printf("%d\n", baz.Bar) // 5000 21 | 22 | onlyOne := MyStruct{Foo: "hello only one"} 23 | external.Printf("%s\n", onlyOne.Foo) // hello only one 24 | external.Printf("%d\n", onlyOne.Bar) // 0 25 | } 26 | -------------------------------------------------------------------------------- /compiler/testdata/func-as-value.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | f1 := func() int { 7 | return 100 8 | } 9 | 10 | external.Printf("%d\n", f1()) // 100 11 | 12 | f2 := func(a int) int { 13 | return 200 + a 14 | } 15 | external.Printf("%d\n", f2(2)) // 202 16 | 17 | func() { 18 | external.Printf("inside\n") // inside 19 | }() 20 | 21 | var f3 func(int) int 22 | 23 | f3 = func(a int) int { 24 | return 300 + a 25 | } 26 | 27 | b := f3(2) 28 | external.Printf("%d\n", b) // 302 29 | external.Printf("%d\n", f3(3)) // 303 30 | 31 | f3 = func(a int) int { 32 | return 400 + a 33 | } 34 | 35 | external.Printf("%d\n", f3(5)) // 405 36 | } 37 | -------------------------------------------------------------------------------- /compiler/testdata/interface.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func foo(bar interface{}) { 6 | realInt64, ok := bar.(int64) 7 | if ok { 8 | external.Printf("is int64: %d\n", realInt64) 9 | } 10 | 11 | realString, ok := bar.(string) 12 | if ok { 13 | external.Printf("is string: %s\n", realString) 14 | } 15 | 16 | external.Printf("alwaysstring: \"%s\"\n", realString) 17 | } 18 | 19 | func main() { 20 | // is string: foostring 21 | // alwaysstring: "foostring" 22 | foo("foostring") 23 | 24 | // is int64: 123 25 | // alwaysstring: "" 26 | foo(123) 27 | 28 | // alwaysstring: "" 29 | foo(false) 30 | 31 | // is string: barstring 32 | // alwaysstring: "barstring" 33 | foo("barstring") 34 | } 35 | -------------------------------------------------------------------------------- /compiler/compiler/func_cap.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 5 | "github.com/zegl/tre/compiler/compiler/value" 6 | "github.com/zegl/tre/compiler/parser" 7 | ) 8 | 9 | func (c *Compiler) capFuncCall(v *parser.CallNode) value.Value { 10 | arg := c.compileValue(v.Arguments[0]) 11 | 12 | if arg.Type.Name() == "slice" { 13 | val := arg.Value 14 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 15 | 16 | return value.Value{ 17 | Value: c.contextBlock.NewExtractValue(val, 1), 18 | Type: i64, 19 | IsVariable: false, 20 | } 21 | } 22 | 23 | c.panic(c.contextBlock, "Can not call cap on "+arg.Type.Name()) 24 | return value.Value{} 25 | } 26 | -------------------------------------------------------------------------------- /compiler/compiler/bool.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/llir/llvm/ir/constant" 5 | llvmTypes "github.com/llir/llvm/ir/types" 6 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 7 | "github.com/zegl/tre/compiler/compiler/types" 8 | "github.com/zegl/tre/compiler/compiler/value" 9 | "github.com/zegl/tre/compiler/parser" 10 | ) 11 | 12 | func (c *Compiler) compileNegateBoolNode(v *parser.NegateNode) value.Value { 13 | val := c.compileValue(v.Item) 14 | loadedVal := c.contextBlock.NewLoad(pointer.ElemType(val.Value), val.Value) 15 | 16 | return value.Value{ 17 | Type: types.Bool, 18 | Value: c.contextBlock.NewXor(loadedVal, constant.NewInt(llvmTypes.I1, 1)), 19 | IsVariable: false, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /compiler/testdata/variable.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var ( 7 | d int 8 | e, f int = 30, 40 9 | g, h = 50, 60 10 | ) 11 | 12 | external.Printf("d is = %d\n", d) // d is = 0 13 | external.Printf("e is = %d\n", e) // e is = 30 14 | external.Printf("f is = %d\n", f) // f is = 40 15 | external.Printf("g is = %d\n", g) // g is = 50 16 | external.Printf("h is = %d\n", h) // h is = 60 17 | 18 | foo := 4 19 | foo = foo + 5 20 | foo = foo + 6 + 7 + 8 21 | external.Printf("foo is = %d\n", foo) // foo is = 30 22 | 23 | var a int 24 | external.Printf("a is = %d\n", a) // a is = 0 25 | 26 | var b int = 20 27 | external.Printf("b is = %d\n", b) // b is = 20 28 | 29 | var c = 21 30 | external.Printf("c is = %d\n", c) // c is = 21 31 | 32 | _ = c 33 | } 34 | -------------------------------------------------------------------------------- /compiler/testdata/const.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | const ( 6 | a = 10 7 | c = 12 8 | 9 | big = 100000000 10 | 11 | a1 = iota 12 | a2 = iota 13 | a3 = iota 14 | ) 15 | 16 | const ( 17 | b1 = iota 18 | b2 = iota 19 | ) 20 | 21 | func main() { 22 | external.Printf("%d\n", a) // 10 23 | 24 | var b uint8 25 | b = 5 26 | external.Printf("%d\n", b+a) // 15 27 | external.Printf("%d\n", a+b) // 15 28 | 29 | external.Printf("%d\n", a+c) // 22 30 | 31 | var b32 int32 32 | b32 = 222288822 33 | external.Printf("%d\n", b32+a) // 222288832 34 | external.Printf("%d\n", a+b32) // 222288832 35 | 36 | external.Printf("%d\n", a1) // 3 37 | external.Printf("%d\n", a2) // 4 38 | external.Printf("%d\n", a3) // 5 39 | 40 | external.Printf("%d\n", b1) // 0 41 | external.Printf("%d\n", b2) // 1 42 | } 43 | -------------------------------------------------------------------------------- /compiler/testdata/tour-named-return.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func multiNamedReturn(inp int) (x int, y int) { 6 | x = inp * 2 7 | y = inp / 2 8 | return 9 | } 10 | 11 | func singleNamedReturn(inp int) (res int) { 12 | res = inp * 3 13 | return 14 | } 15 | 16 | func namedReturnNotUsed(inp int) (res int) { 17 | res = inp * 3 18 | return 500 19 | } 20 | 21 | func multiNamedReturnRef(inp int) (x int, y int) { 22 | x = inp * 2 23 | y = x + 2 24 | return 25 | } 26 | 27 | func main() { 28 | // 34 8 29 | a, b := multiNamedReturn(17) 30 | external.Printf("%d %d\n", a, b) 31 | 32 | // 54 33 | external.Printf("%d\n", singleNamedReturn(18)) 34 | 35 | // 500 36 | external.Printf("%d\n", namedReturnNotUsed(18)) 37 | 38 | // 36 38 39 | c, d := multiNamedReturnRef(18) 40 | external.Printf("%d %d\n", c, d) 41 | } 42 | -------------------------------------------------------------------------------- /compiler/testdata/interface-pointer-receiver.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type TestFace interface { 6 | Add(int) int 7 | } 8 | 9 | type FaceImpl struct { 10 | sum int 11 | } 12 | 13 | func (f *FaceImpl) Add(val int) int { 14 | external.Printf("val: %d\n", val) 15 | 16 | f.sum = f.sum + val 17 | 18 | return f.sum 19 | } 20 | 21 | func main() { 22 | var face TestFace 23 | 24 | impl := FaceImpl{} 25 | face = impl 26 | 27 | // val: 3 28 | // res: 3 29 | res := face.Add(3) 30 | external.Printf("res: %d\n", res) 31 | 32 | // val: 4 33 | // res: 7 34 | res = face.Add(4) 35 | external.Printf("res: %d\n", res) 36 | 37 | // val: 1 38 | // res: 8 39 | res = face.Add(1) 40 | external.Printf("res: %d\n", res) 41 | 42 | // val: 5 43 | // res: 13 44 | res = face.Add(5) 45 | external.Printf("res: %d\n", res) 46 | } 47 | -------------------------------------------------------------------------------- /compiler/testdata/interface-slice-consts.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := []interface{}{111, 222} 7 | 8 | // 111 9 | fromSlice0, ok := a[0].(int64) 10 | if ok { 11 | external.Printf("%d\n", fromSlice0) 12 | } 13 | 14 | // 222 15 | fromSlice1, ok := a[1].(int64) 16 | if ok { 17 | external.Printf("%d\n", fromSlice1) 18 | } 19 | 20 | // 111 21 | // 222 22 | for k, v := range a { 23 | fromV, ok := v.(int64) 24 | if ok { 25 | external.Printf("%d\n", fromV) 26 | } 27 | } 28 | 29 | // 10 30 | // 11 31 | // 12 32 | // 13 33 | // 14 34 | // 15 35 | // 16 36 | // 17 37 | // 18 38 | // 19 39 | large := []interface{}{10, 11, 12, 13, 14, 15, 16, 17, 18, 19} 40 | for k, v := range large { 41 | fromV, ok := v.(int64) 42 | if ok { 43 | external.Printf("%d\n", fromV) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /compiler/testdata/func-variadic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func variadic(num ...int) int { 6 | external.Printf("len = %d\n", len(num)) 7 | return 123 8 | } 9 | 10 | func variadicWithOtherArgs(preArg int, num ...int) int { 11 | external.Printf("pre = %d + len = %d\n", preArg, len(num)) 12 | return 123 13 | } 14 | 15 | func main() { 16 | variadic() // len = 0 17 | variadic(1) // len = 1 18 | variadic(1, 2) // len = 2 19 | variadic(1, 2, 3) // len = 3 20 | 21 | variadicWithOtherArgs(100) // pre = 100 + len = 0 22 | variadicWithOtherArgs(100, 2, 3) // pre = 100 + len = 2 23 | variadicWithOtherArgs(100, 2, 3, 4, 5, 6) // pre = 100 + len = 5 24 | 25 | fromSlice := []int{100, 200, 300} 26 | variadic(fromSlice...) // len = 3 27 | 28 | variadic([]int{100, 200, 300, 400, 500, 600, 700}...) // len = 7 29 | } 30 | -------------------------------------------------------------------------------- /compiler/testdata/slice-range.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | 6 | func ff() []int { 7 | return []int{40, 50} 8 | } 9 | 10 | func main() { 11 | s := []int{10, 20, 30} 12 | // 0 10 13 | // 1 20 14 | // 2 30 15 | for k, v := range s { 16 | external.Printf("%d %d\n", k, v) 17 | } 18 | 19 | // _, 10 20 | // _, 20 21 | // _, 30 22 | for _, v := range s { 23 | external.Printf("_, %d\n", v) 24 | } 25 | 26 | // 0 27 | // 1 28 | // 2 29 | for k := range s { 30 | external.Printf("%d\n", k) 31 | } 32 | 33 | // AAA 34 | // AAA 35 | // AAA 36 | for range s { 37 | external.Printf("%s\n", "AAA") 38 | } 39 | 40 | // 0 40 41 | // 1 50 42 | f := ff() 43 | for k, v := range f { 44 | external.Printf("%d %d\n", k, v) 45 | } 46 | 47 | // 0 40 48 | // 1 50 49 | for k, v := range ff() { 50 | external.Printf("%d %d\n", k, v) 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /compiler/compiler/types/builtin.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "github.com/llir/llvm/ir/types" 4 | 5 | var I8 = &Int{Type: types.I8, TypeName: "int8", TypeSize: 8 / 8, Signed: true} 6 | var U8 = &Int{Type: types.I8, TypeName: "uint8", TypeSize: 8 / 8} 7 | var I16 = &Int{Type: types.I16, TypeName: "int16", TypeSize: 18 / 8, Signed: true} 8 | var U16 = &Int{Type: types.I16, TypeName: "uint16", TypeSize: 18 / 8} 9 | var I32 = &Int{Type: types.I32, TypeName: "int32", TypeSize: 32 / 8, Signed: true} 10 | var U32 = &Int{Type: types.I32, TypeName: "uint32", TypeSize: 32 / 8} 11 | var I64 = &Int{Type: types.I64, TypeName: "int64", TypeSize: 64 / 8, Signed: true} 12 | var U64 = &Int{Type: types.I64, TypeName: "uint64", TypeSize: 64 / 8} 13 | var Uintptr = &Int{Type: types.I64, TypeName: "uintptr", TypeSize: 64 / 8} 14 | 15 | var Void = &VoidType{} 16 | var String = &StringType{} 17 | var Bool = &BoolType{} 18 | -------------------------------------------------------------------------------- /compiler/compiler/types/package.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/llir/llvm/ir/types" 5 | ) 6 | 7 | type PackageInstance struct { 8 | backingType 9 | name string 10 | funcs map[string]*Function 11 | } 12 | 13 | func (p *PackageInstance) SetName(name string) { 14 | p.name = name 15 | } 16 | 17 | func (p *PackageInstance) SetFunc(name string, val *Function) { 18 | if p.funcs == nil { 19 | p.funcs = make(map[string]*Function) 20 | } 21 | p.funcs[name] = val 22 | } 23 | 24 | func (p *PackageInstance) GetFunc(name string) (*Function, bool) { 25 | v, ok := p.funcs[name] 26 | return v, ok 27 | } 28 | 29 | func (PackageInstance) LLVM() types.Type { 30 | // TODO: Packages are not values, and should be represented some other way 31 | // Maybe via LLVM IR modules? 32 | panic("Package does not have LLVM defined") 33 | } 34 | 35 | func (p PackageInstance) Name() string { 36 | return p.name 37 | } 38 | -------------------------------------------------------------------------------- /compiler/testdata/switch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func runIntSwitch(a int) { 8 | switch a { 9 | case 3: 10 | fmt.Println("three") 11 | case 6, 7, 8: 12 | fmt.Println("six, seven, eight") 13 | case 4: 14 | fmt.Println("four") 15 | fallthrough 16 | case 5: 17 | fmt.Println("five") 18 | default: 19 | fmt.Println("default") 20 | } 21 | } 22 | 23 | func runBoolSwitch(a bool) { 24 | switch a { 25 | case false: 26 | fmt.Println("false") 27 | case true: 28 | fmt.Println("true") 29 | } 30 | } 31 | 32 | func main() { 33 | a := 5 34 | runIntSwitch(a) // five 35 | 36 | runIntSwitch(100) // default 37 | 38 | // four 39 | // five 40 | runIntSwitch(4) 41 | 42 | runIntSwitch(6) // six, seven, eight 43 | runIntSwitch(7) // six, seven, eight 44 | runIntSwitch(8) // six, seven, eight 45 | 46 | runBoolSwitch(false) // false 47 | runBoolSwitch(true) // true 48 | } 49 | -------------------------------------------------------------------------------- /compiler/testdata/interface-assign.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type myInterfaceType struct { 6 | A interface{} 7 | } 8 | 9 | func main() { 10 | var target interface{} 11 | 12 | target = 123 13 | realInt, ok := target.(int64) 14 | external.Printf("%d %d\n", ok, realInt) // 1 123 15 | 16 | target = "hello" 17 | realString, ok := target.(string) 18 | external.Printf("%d %s\n", ok, realString) // 1 hello 19 | 20 | foo := 456 21 | target = foo 22 | realVarInt, ok := target.(int64) 23 | external.Printf("%d %d\n", ok, realVarInt) // 1 456 24 | 25 | var targetSlice []interface{} 26 | targetSlice = append(targetSlice, 789) 27 | realSliceInt, ok := targetSlice[0].(int64) 28 | external.Printf("%d %d\n", ok, realSliceInt) // 1 789 29 | 30 | var targetStruct myInterfaceType 31 | targetStruct.A = 654 32 | realStructTarget, ok := targetStruct.A.(int64) 33 | external.Printf("%d %d\n", ok, realStructTarget) // 1 654 34 | } 35 | -------------------------------------------------------------------------------- /compiler/compiler/syscall/print.go: -------------------------------------------------------------------------------- 1 | package syscall 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | "github.com/llir/llvm/ir/constant" 6 | llvmTypes "github.com/llir/llvm/ir/types" 7 | 8 | "github.com/zegl/tre/compiler/compiler/strings" 9 | "github.com/zegl/tre/compiler/compiler/types" 10 | "github.com/zegl/tre/compiler/compiler/value" 11 | ) 12 | 13 | func Print(block *ir.Block, value value.Value, goos string) { 14 | asmFunc := ir.NewInlineAsm(llvmTypes.NewPointer(llvmTypes.NewFunc(types.I64.LLVM())), "syscall", "=r,{rax},{rdi},{rsi},{rdx}") 15 | asmFunc.SideEffect = true 16 | 17 | strPtr := strings.TreToI8Ptr(block, value.Value) 18 | strLen := strings.Len(block, value.Value) 19 | 20 | block.NewCall(asmFunc, 21 | constant.NewInt(types.I64.Type, Convert(WRITE, goos)), // rax 22 | constant.NewInt(types.I64.Type, 1), // rdi, stdout 23 | strPtr, // rsi 24 | strLen, // rdx 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /compiler/testdata/func-parameter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | import "fmt" 5 | 6 | func add(a int, b int) int { 7 | fmt.Println("exec add") 8 | return a + b 9 | } 10 | 11 | func addMultiRet(a int, b int) (int, int, int) { 12 | fmt.Println("exec addMultiRet") 13 | return a + b, a + b, a + b 14 | } 15 | 16 | func addNoRet(a int, b int) { 17 | fmt.Println("exec addNoRet") 18 | } 19 | 20 | func singleret(a int, b int, fn func(int, int) int) int { 21 | return fn(a, b) 22 | } 23 | 24 | func multiret(a int, b int, fn func(int, int) (int, int, int)) int { 25 | fn(a, b) 26 | return 200 27 | } 28 | 29 | func noret(a int, b int, fn func(int, int)) int { 30 | fn(a, b) 31 | return 300 32 | } 33 | 34 | func main() { 35 | // exec add 36 | // 4 37 | external.Printf("%d\n", singleret(1, 3, add)) 38 | 39 | 40 | // exec addMultiRet 41 | // 200 42 | external.Printf("%d\n", multiret(1, 3, addMultiRet)) 43 | 44 | // exec addNoRet 45 | // 300 46 | external.Printf("%d\n", noret(1, 3, addNoRet)) 47 | } 48 | -------------------------------------------------------------------------------- /compiler/testdata/vars-heap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type Bar struct { 6 | num int64 7 | } 8 | 9 | type Foo struct { 10 | num int64 11 | bar *Bar 12 | } 13 | 14 | func GetFooPtr() *Foo { 15 | f := Foo{ 16 | num: 300, 17 | bar: &Bar{num: 400}, 18 | } 19 | 20 | return &f 21 | } 22 | 23 | func main() { 24 | foo := &Foo{ 25 | num: 100, 26 | bar: &Bar{num: 200}, 27 | } 28 | 29 | external.Printf("foo.bar.num: %d\n", foo.bar.num) // foo.bar.num: 200 30 | external.Printf("foo.num: %d\n", foo.num) // foo.num: 100 31 | external.Printf("foo.bar.num: %d\n", foo.bar.num) // foo.bar.num: 200 32 | external.Printf("foo.num: %d\n", foo.num) // foo.num: 100 33 | 34 | foo2 := GetFooPtr() 35 | external.Printf("foo2.bar.num: %d\n", foo2.bar.num) // foo2.bar.num: 400 36 | external.Printf("foo2.num: %d\n", foo2.num) // foo2.num: 300 37 | external.Printf("foo2.bar.num: %d\n", foo2.bar.num) // foo2.bar.num: 400 38 | external.Printf("foo2.num: %d\n", foo2.num) // foo2.num: 300 39 | } 40 | -------------------------------------------------------------------------------- /compiler/testdata/interface-append-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | 5 | package main 6 | 7 | import "external" 8 | 9 | func main() { 10 | a1 := 100 11 | a2 := 200 12 | a := []interface{}{a1, a2} 13 | 14 | // 100 15 | fromSlice0, ok := a[0].(int64) 16 | if ok { 17 | external.Printf("%d\n", fromSlice0) 18 | } 19 | 20 | // 200 21 | fromSlice1, ok := a[1].(int64) 22 | if ok { 23 | external.Printf("%d\n", fromSlice1) 24 | } 25 | 26 | // 100 27 | // 200 28 | for k, v := range a { 29 | fromV, ok := v.(int64) 30 | if ok { 31 | external.Printf("%d\n", fromV) 32 | } 33 | } 34 | 35 | a = append(a, 300) 36 | 37 | // 100 38 | // 200 39 | // 300 40 | for k, v := range a { 41 | fromV, ok := v.(int64) 42 | if ok { 43 | external.Printf("%d\n", fromV) 44 | } 45 | } 46 | 47 | var b interface{} 48 | b = 400 49 | a = append(a, b) 50 | // 100 51 | // 200 52 | // 300 53 | // 400 54 | for k, v := range a { 55 | fromV, ok := v.(int64) 56 | if ok { 57 | external.Printf("%d\n", fromV) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /compiler/testdata/string-substring-2-args.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | mystr := "hello" 7 | 8 | // hel 9 | external.Printf("%s\n", mystr[0:3]) 10 | // he 11 | external.Printf("%s\n", mystr[0:2]) 12 | // l 13 | external.Printf("%s\n", mystr[2:3]) 14 | // llo 15 | external.Printf("%s\n", mystr[2:5]) 16 | // empty: 17 | external.Printf("empty:%s\n", mystr[3:3]) 18 | // ello 19 | external.Printf("%s\n", mystr[1:5]) 20 | 21 | external.Printf("%d\n", len(mystr[0:1])) // 1 22 | external.Printf("%d\n", len(mystr[0:2])) // 2 23 | external.Printf("%d\n", len(mystr[0:3])) // 3 24 | external.Printf("%d\n", len(mystr[0:4])) // 4 25 | external.Printf("%d\n", len(mystr[2:2])) // 0 26 | external.Printf("%d\n", len(mystr[2:5])) // 3 27 | 28 | start := 1 29 | end := 3 30 | external.Printf("%s\n", mystr[start:end]) // el 31 | 32 | external.Printf("%s\n", mystr[1:]) // ello 33 | external.Printf("%s\n", mystr[3:]) // lo 34 | 35 | external.Printf("%d\n", len(mystr[1:])) // 4 36 | external.Printf("%d\n", len(mystr[3:])) // 2 37 | } 38 | -------------------------------------------------------------------------------- /compiler/parser/import.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zegl/tre/compiler/lexer" 6 | ) 7 | 8 | func (p *parser) parseImport() *ImportNode { 9 | p.i++ 10 | 11 | 12 | // Single import statement 13 | expectPathString := p.lookAhead(0) 14 | if expectPathString.Type == lexer.STRING { 15 | return &ImportNode{ 16 | PackagePaths: []string{expectPathString.Val}, 17 | } 18 | } 19 | 20 | // Multiple imports 21 | p.expect(lexer.Item{Type: lexer.OPERATOR, Val: "("}, p.lookAhead(0)) 22 | p.i++ 23 | 24 | var imports []string 25 | 26 | for { 27 | checkIfEndParen := p.lookAhead(0) 28 | if checkIfEndParen.Type == lexer.OPERATOR && checkIfEndParen.Val == ")" { 29 | break 30 | } 31 | if checkIfEndParen.Type == lexer.EOL { 32 | p.i++ 33 | continue 34 | } 35 | 36 | if checkIfEndParen.Type == lexer.STRING { 37 | imports = append(imports, checkIfEndParen.Val) 38 | p.i++ 39 | continue 40 | } 41 | 42 | panic(fmt.Sprintf("Failed to parse import: %+v", checkIfEndParen)) 43 | } 44 | 45 | return &ImportNode{ 46 | PackagePaths: imports, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /compiler/testdata/struct.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type Book struct { 6 | book_id1 int 7 | book_id2 int 8 | } 9 | 10 | // main id1: 1111 11 | // main id2: 2222 12 | // info id1: 1111 13 | // info id2: 2222 14 | // info id1: 3333 15 | // main id1: 3333 16 | // info id1: 1111 17 | // info id2: 2222 18 | // info id1: 3333 19 | // res id1: 3333 20 | // res id2: 2222 21 | 22 | func bookInfo(book Book) Book { 23 | external.Printf("info id1: %d\n", book.book_id1) 24 | external.Printf("info id2: %d\n", book.book_id2) 25 | 26 | book.book_id1 = 3333 27 | 28 | external.Printf("info id1: %d\n", book.book_id1) 29 | 30 | return book 31 | } 32 | 33 | func main() { 34 | var bookiboy Book 35 | bookiboy.book_id1 = 1111 36 | bookiboy.book_id2 = 2222 37 | 38 | external.Printf("main id1: %d\n", bookiboy.book_id1) 39 | external.Printf("main id2: %d\n", bookiboy.book_id2) 40 | 41 | external.Printf("main id1: %d\n", bookInfo(bookiboy).book_id1) 42 | res := bookInfo(bookiboy) 43 | 44 | external.Printf("res id1: %d\n", res.book_id1) 45 | external.Printf("res id2: %d\n", res.book_id2) 46 | } 47 | -------------------------------------------------------------------------------- /compiler/testdata/interface-methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | type TestFace interface { 6 | Foo1(int) int 7 | Foo2(int) 8 | } 9 | 10 | type FaceImpl1 struct{} 11 | 12 | func (f FaceImpl1) Foo1(a int) int { 13 | external.Printf("FaceImpl1: foo1=%d\n", a) 14 | return 100 15 | } 16 | 17 | func (f FaceImpl1) Foo2(a int) { 18 | external.Printf("FaceImpl1: foo2=%d\n", a) 19 | } 20 | 21 | type FaceImpl2 struct{} 22 | 23 | func (f FaceImpl2) Foo1(a int) int { 24 | external.Printf("FaceImpl2: foo1=%d\n", a) 25 | return 200 26 | } 27 | 28 | func (f FaceImpl2) Foo2(a int) { 29 | external.Printf("FaceImpl2: foo2=%d\n", a) 30 | } 31 | 32 | func main() { 33 | var face TestFace 34 | face = FaceImpl1{} 35 | res1 := face.Foo1(500) // FaceImpl1: foo1=500 36 | external.Printf("res1: %d\n", res1) // res1: 100 37 | face.Foo2(505) // FaceImpl1: foo2=505 38 | 39 | face = FaceImpl2{} 40 | res2 := face.Foo1(600) // FaceImpl2: foo1=600 41 | external.Printf("res2: %d\n", res2) // res2: 200 42 | face.Foo2(606) // FaceImpl2: foo2=606 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Gustav Westling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /compiler/testdata/slice-append-assign.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := []int64{1, 2, 3} 7 | 8 | // a = len(3) cap(3) 1 2 3 9 | external.Printf("a = len(%d) cap(%d) %d %d %d\n", len(a), cap(a), a[0], a[1], a[2]) 10 | 11 | // a = len(4) cap(6) 1 2 3 4 12 | a = append(a, 4) 13 | external.Printf("a = len(%d) cap(%d) %d %d %d %d\n", len(a), cap(a), a[0], a[1], a[2], a[3]) 14 | 15 | // a = len(5) cap(6) 1 2 3 4 5000 16 | a = append(a, 5000) 17 | external.Printf("a = len(%d) cap(%d) %d %d %d %d %d\n", len(a), cap(a), a[0], a[1], a[2], a[3], a[4]) 18 | 19 | // b = len(6) cap(12) 1 2 3 4 5000 6000 20 | b := append(a, 6000) 21 | external.Printf("b = len(%d) cap(%d) %d %d %d %d %d %d\n", len(b), cap(b), b[0], b[1], b[2], b[3], b[4], b[5]) 22 | 23 | // a = len(6) cap(6) 1 2 3 4 5000 7000 24 | a = append(a, 7000) 25 | external.Printf("a = len(%d) cap(%d) %d %d %d %d %d %d\n", len(a), cap(a), a[0], a[1], a[2], a[3], a[4], a[5]) 26 | 27 | // b = len(7) cap(12) 1 2 3 4 5000 6000 8000 28 | b = append(b, 8000) 29 | external.Printf("b = len(%d) cap(%d) %d %d %d %d %d %d %d\n", len(b), cap(b), b[0], b[1], b[2], b[3], b[4], b[5], b[6]) 30 | } 31 | -------------------------------------------------------------------------------- /compiler/compiler/value/value.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import ( 4 | "github.com/llir/llvm/ir/constant" 5 | llvmValue "github.com/llir/llvm/ir/value" 6 | 7 | "github.com/zegl/tre/compiler/compiler/types" 8 | ) 9 | 10 | type Value struct { 11 | Type types.Type 12 | Value llvmValue.Value 13 | 14 | // Is true when Value points to an LLVM Allocated variable, and is false 15 | // when the value is a constant. 16 | // This is used to know if a "load" instruction is necessary or not. 17 | // Pointers are not considered "variables" in this context. 18 | IsVariable bool 19 | 20 | // Is used when returning multiple types from a function 21 | // Type is set to MultiValue when this is case, and will also contain the 22 | // type information 23 | MultiValues []Value 24 | } 25 | 26 | func UntypedConstAs(val Value, context Value) Value { 27 | switch val.Type.(type) { 28 | case *types.UntypedConstantNumber: 29 | if contextInt, ok := context.Type.(*types.Int); ok { 30 | return Value{ 31 | Type: contextInt, 32 | Value: &constant.Int{ 33 | Typ: contextInt.Type, 34 | X: val.Value.(*constant.Int).X, 35 | }, 36 | } 37 | } 38 | panic("unexpected type in UntypedConstAs") 39 | default: 40 | panic("unexpected type in UntypedConstAs") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /compiler/testdata/condition.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | a := 100 7 | 8 | // a is 100 9 | if a == 100 { 10 | external.Printf("a is 100\n") 11 | } else { 12 | external.Printf("a is not 100\n") 13 | } 14 | 15 | // a is not 200 16 | if a == 200 { 17 | external.Printf("a is 200\n") 18 | } else { 19 | external.Printf("a is not 200\n") 20 | } 21 | 22 | // a is larger than 50 23 | if a == 200 { 24 | external.Printf("a is 200\n") 25 | } else if a > 300 { 26 | external.Printf("a is larger than 300\n") 27 | } else if a > 50 { 28 | external.Printf("a is larger than 50\n") 29 | } else { 30 | external.Printf("a is not 200\n") 31 | } 32 | 33 | // a is larger than 30 34 | if a == 200 { 35 | external.Printf("a is 200\n") 36 | } else if a > 30 { 37 | external.Printf("a is larger than 30\n") 38 | 39 | // and a is 100 40 | if a == 100 { 41 | external.Printf("and a is 100\n") 42 | } else { 43 | external.Printf("and a is not 100\n") 44 | } 45 | 46 | // and a is not 200 47 | if a == 200 { 48 | external.Printf("and a is 200\n") 49 | } else { 50 | external.Printf("and a is not 200\n") 51 | } 52 | 53 | } else if a > 50 { 54 | external.Printf("a is larger than 50\n") 55 | } else { 56 | external.Printf("a is not 200\n") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /compiler/compiler/package.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "unicode" 6 | 7 | "github.com/zegl/tre/compiler/compiler/types" 8 | "github.com/zegl/tre/compiler/compiler/value" 9 | ) 10 | 11 | // Representation of a Go package 12 | type pkg struct { 13 | name string 14 | vars map[string]value.Value 15 | types map[string]types.Type 16 | } 17 | 18 | func NewPkg(name string) *pkg { 19 | return &pkg{ 20 | name: name, 21 | vars: make(map[string]value.Value), 22 | types: make(map[string]types.Type), 23 | } 24 | } 25 | 26 | func (p *pkg) DefinePkgVar(name string, val value.Value) { 27 | p.vars[name] = val 28 | } 29 | 30 | func (p *pkg) GetPkgVar(name string, inSamePackage bool) (value.Value, bool) { 31 | if unicode.IsLower([]rune(name)[0]) && !inSamePackage { 32 | compilePanic(fmt.Sprintf("Can't use %s from outside of %s", name, p.name)) 33 | } 34 | 35 | v, ok := p.vars[name] 36 | return v, ok 37 | } 38 | 39 | func (p *pkg) DefinePkgType(name string, ty types.Type) { 40 | p.types[name] = ty 41 | } 42 | 43 | func (p *pkg) GetPkgType(name string, inSamePackage bool) (types.Type, bool) { 44 | if unicode.IsLower([]rune(name)[0]) && !inSamePackage { 45 | compilePanic(fmt.Sprintf("Can't use %s from outside of %s", name, p.name)) 46 | } 47 | 48 | v, ok := p.types[name] 49 | return v, ok 50 | } 51 | -------------------------------------------------------------------------------- /cmd/tre/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | flag "github.com/spf13/pflag" 11 | "github.com/zegl/tre/cmd/tre/build" 12 | ) 13 | 14 | var ( 15 | debug bool 16 | optimize bool 17 | output string 18 | ) 19 | 20 | func init() { 21 | flag.Usage = func() { 22 | fmt.Fprintf(os.Stderr, "Usage: %s [options] \n", os.Args[0]) 23 | flag.PrintDefaults() 24 | } 25 | 26 | flag.BoolVarP(&debug, "debug", "d", false, "Emit debug information during compile time") 27 | flag.BoolVarP(&optimize, "optimize", "O", false, "Enable clang optimization") 28 | flag.StringVarP(&output, "output", "o", "", "Output binary filename") 29 | } 30 | 31 | func main() { 32 | flag.Parse() 33 | 34 | if len(flag.Args()) < 1 { 35 | log.Printf("No file specified. Usage: %s path/to/file.tre", os.Args[0]) 36 | os.Exit(1) 37 | } 38 | 39 | // "GOROOT" (treroot?) detection based on the binary path 40 | treBinaryPath, _ := os.Executable() 41 | goroot := filepath.Clean(treBinaryPath + "/../pkg/") 42 | 43 | if output == "" { 44 | basename := filepath.Base(flag.Arg(0)) 45 | output = strings.TrimSuffix(basename, filepath.Ext(basename)) 46 | } 47 | 48 | err := build.Build(flag.Arg(0), goroot, output, debug, optimize) 49 | if err != nil { 50 | log.Println(err) 51 | os.Exit(1) 52 | } 53 | 54 | os.Exit(0) 55 | } 56 | -------------------------------------------------------------------------------- /compiler/compiler/strings/strings.go: -------------------------------------------------------------------------------- 1 | package strings 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/llir/llvm/ir/types" 7 | 8 | "github.com/llir/llvm/ir" 9 | "github.com/llir/llvm/ir/constant" 10 | "github.com/llir/llvm/ir/value" 11 | 12 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 13 | ) 14 | 15 | func Constant(in string) *constant.CharArray { 16 | return constant.NewCharArray(append([]byte(in), 0)) 17 | } 18 | 19 | func Toi8Ptr(block *ir.Block, src value.Value) *ir.InstGetElementPtr { 20 | return block.NewGetElementPtr(pointer.ElemType(src), src, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 0)) 21 | } 22 | 23 | func Len(block *ir.Block, src value.Value) value.Value { 24 | if _, ok := src.Type().(*types.PointerType); ok { 25 | l := block.NewGetElementPtr(pointer.ElemType(src), src, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 0)) 26 | return block.NewLoad(pointer.ElemType(l), l) 27 | } 28 | return block.NewExtractValue(src, 0) 29 | } 30 | 31 | func TreToI8Ptr(block *ir.Block, src value.Value) value.Value { 32 | if _, ok := src.Type().(*types.PointerType); ok { 33 | l := block.NewGetElementPtr(pointer.ElemType(src), src, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 1)) 34 | return block.NewLoad(pointer.ElemType(l), l) 35 | } 36 | return block.NewExtractValue(src, 1) 37 | } 38 | 39 | var globalStringCounter uint 40 | 41 | func NextStringName() string { 42 | name := fmt.Sprintf("str.%d", globalStringCounter) 43 | globalStringCounter++ 44 | return name 45 | } 46 | -------------------------------------------------------------------------------- /compiler/testdata/pointer-pointer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | i := 100 7 | iptr1 := &i 8 | iptr2 := &iptr1 9 | iptr3 := &iptr2 10 | 11 | external.Printf("i: %d\n", i) // i: 100 12 | external.Printf("*iptr1: %d\n", *iptr1) // *iptr1: 100 13 | external.Printf("**iptr2: %d\n", **iptr2) // **iptr2: 100 14 | external.Printf("***iptr3: %d\n", ***iptr3) // ***iptr3: 100 15 | 16 | i = 200 17 | external.Printf("i: %d\n", i) // i: 200 18 | external.Printf("*iptr1: %d\n", *iptr1) // *iptr1: 200 19 | external.Printf("**iptr2: %d\n", **iptr2) // **iptr2: 200 20 | external.Printf("***iptr3: %d\n", ***iptr3) // ***iptr3: 200 21 | 22 | *iptr1 = 300 23 | external.Printf("i: %d\n", i) // i: 300 24 | external.Printf("*iptr1: %d\n", *iptr1) // *iptr1: 300 25 | external.Printf("**iptr2: %d\n", **iptr2) // **iptr2: 300 26 | external.Printf("***iptr3: %d\n", ***iptr3) // ***iptr3: 300 27 | 28 | **iptr2 = 400 29 | external.Printf("i: %d\n", i) // i: 400 30 | external.Printf("*iptr1: %d\n", *iptr1) // *iptr1: 400 31 | external.Printf("**iptr2: %d\n", **iptr2) // **iptr2: 400 32 | external.Printf("***iptr3: %d\n", ***iptr3) // ***iptr3: 400 33 | 34 | ***iptr3 = 500 35 | external.Printf("i: %d\n", i) // i: 500 36 | external.Printf("*iptr1: %d\n", *iptr1) // *iptr1: 500 37 | external.Printf("**iptr2: %d\n", **iptr2) // **iptr2: 500 38 | external.Printf("***iptr3: %d\n", ***iptr3) // ***iptr3: 500 39 | 40 | } 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tre 2 | 3 | > A LLVM backed Go compiler. 4 | 5 | `tre` is built in Go and can compile a subset of Go code to LLVM IR. Clang is 6 | used to compile the IR to an executable. 7 | 8 | ## Building 9 | 10 | ```bash 11 | # Build tre and run a test program 12 | go build -i github.com/zegl/tre/cmd/tre && ./tre tests/tests/fib.tre && ./output-binary 13 | ``` 14 | 15 | ## Example 16 | 17 | Example program that calculates the fibonacci sequence. 18 | 19 | ```go 20 | func fib(num int) int { 21 | if num < 2 { 22 | return num 23 | } 24 | 25 | return fib(num-2) + fib(num-1) 26 | } 27 | 28 | func main() { 29 | printf("%d\n", fib(34)) 30 | } 31 | ``` 32 | 33 | More examples of what's possible can be found in the [compiler testdata](https://github.com/zegl/tre/tree/master/compiler/testdata). 34 | 35 | ## Features 36 | 37 | ### Types 38 | 39 | - [x] int 40 | - [x] string 41 | - [x] struct 42 | - [x] array 43 | - [x] slice 44 | - [ ] [map](https://github.com/zegl/tre/issues/34) 45 | - [x] bool 46 | - [x] func 47 | - [ ] [chan](https://github.com/zegl/tre/issues/78) 48 | 49 | ### Language features 50 | 51 | - [ ] [first class func](https://github.com/zegl/tre/issues/36) 52 | - [ ] packages 53 | - [x] methods 54 | - [x] pointers 55 | - [x] interfaces 56 | - [ ] [chan](https://github.com/zegl/tre/issues/78) 57 | - [ ] [goroutines](https://github.com/zegl/tre/issues/77) 58 | - [x] if/else if/else 59 | - [ ] switch 60 | 61 | ## Dependencies 62 | 63 | * [clang](https://clang.llvm.org/) - Supports 8.0 and 9.0 64 | * [llir/llvm](https://github.com/llir/llvm) 65 | -------------------------------------------------------------------------------- /compiler/parser/infix_sorter.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | /* 4 | https://golang.org/ref/spec#Operator_precedence 5 | 6 | Precedence Operator 7 | 5 * / % << >> & &^ 8 | 4 + - | ^ 9 | 3 == != < <= > >= 10 | 2 && 11 | 1 || 12 | */ 13 | var infixPrioMap = map[Operator]int{ 14 | OP_MUL: 5, 15 | OP_DIV: 5, 16 | OP_REMAINDER: 5, 17 | OP_LEFT_SHIFT: 5, 18 | OP_RIGHT_SHIFT: 5, 19 | OP_BIT_AND: 5, 20 | OP_BIT_CLEAR: 5, 21 | 22 | OP_ADD: 4, 23 | OP_SUB: 4, 24 | OP_BIT_OR: 4, 25 | OP_BIT_XOR: 4, 26 | 27 | OP_EQ: 3, 28 | OP_NEQ: 3, 29 | OP_LT: 3, 30 | OP_LTEQ: 3, 31 | OP_GT: 3, 32 | OP_GTEQ: 3, 33 | 34 | OP_LOGICAL_AND: 2, 35 | 36 | OP_LOGICAL_OR: 1, 37 | } 38 | 39 | func infixPrio(input Operator) int { 40 | if prio, ok := infixPrioMap[input]; ok { 41 | return prio 42 | } 43 | 44 | panic("unknown infixPrio: " + string(input)) 45 | } 46 | 47 | // sortInfix maxes sure that the infix operators are applied in the correct order 48 | // 49 | // Example: (2 * (3 + 4) gets corrected to ((2 * 3) + 4) 50 | // Example: ((1 + 2) * 3) gets corrected to (1 + (2 * 3) 51 | func sortInfix(outer *OperatorNode) *OperatorNode { 52 | 53 | // outer: OP(OP(1 + 2) * 3) 54 | // outerOp: * 55 | // leftOp: + 56 | // left: OP(1 + 2) 57 | // res: OP(1 + OP(2 * 3) 58 | 59 | if left, ok := outer.Left.(*OperatorNode); ok { 60 | if infixPrio(outer.Operator) > infixPrio(left.Operator) { 61 | res := &OperatorNode{ 62 | Operator: left.Operator, 63 | Left: left.Left, 64 | Right: &OperatorNode{ 65 | Operator: outer.Operator, 66 | Left: left.Right, 67 | Right: outer.Right, 68 | }, 69 | } 70 | return res 71 | } 72 | } 73 | 74 | return outer 75 | } 76 | -------------------------------------------------------------------------------- /compiler/passes/const_iota/iota.go: -------------------------------------------------------------------------------- 1 | package const_iota 2 | 3 | import ( 4 | "github.com/zegl/tre/compiler/parser" 5 | ) 6 | 7 | func Iota(root *parser.FileNode) *parser.FileNode { 8 | parser.Walk(&iotaVisitor{}, root) 9 | return root 10 | } 11 | 12 | type iotaVisitor struct{} 13 | 14 | func (i *iotaVisitor) Visit(node parser.Node) (n parser.Node, w parser.Visitor) { 15 | w = i 16 | n = node 17 | if _, ok := node.(*parser.AllocGroup); ok { 18 | w = &iotaAllocGroupVisitor{} 19 | } 20 | return 21 | } 22 | 23 | type iotaAllocGroupVisitor struct { 24 | count int64 25 | } 26 | 27 | func (i *iotaAllocGroupVisitor) Visit(node parser.Node) (n parser.Node, w parser.Visitor) { 28 | w = i 29 | n = node 30 | 31 | if a, ok := node.(*parser.AllocNode); ok { 32 | i.count++ 33 | 34 | if len(a.Val) == 0 { 35 | a.Val = []parser.Node{ 36 | &parser.ConstantNode{ 37 | Type: parser.NUMBER, 38 | Value: i.count - 1, 39 | }, 40 | } 41 | return a, nil // nil to not scan deeper 42 | } 43 | } 44 | 45 | if a, ok := node.(*parser.NameNode); ok { 46 | if a.Name == "iota" { 47 | n = &parser.ConstantNode{ 48 | Type: parser.NUMBER, 49 | Value: i.count - 1, 50 | } 51 | } 52 | } 53 | 54 | /* 55 | if a, ok := node.(*parser.AllocNode); ok { 56 | if !a.IsConst { 57 | return 58 | } 59 | 60 | if len(a.Val) == 0 && i.count > 0 { 61 | a.Val = []parser.Node{&parser.ConstantNode{ 62 | Type: parser.NUMBER, 63 | Value: i.count, 64 | }} 65 | 66 | i.count++ 67 | return 68 | } 69 | 70 | if len(a.Val) == 1 { 71 | if n, ok := a.Val[0].(*parser.NameNode); ok { 72 | if n.Name == "iota" { 73 | a.Val[0] = &parser.ConstantNode{ 74 | Type: parser.NUMBER, 75 | Value: i.count, 76 | } 77 | i.count++ 78 | } 79 | } 80 | } 81 | }*/ 82 | 83 | return 84 | } 85 | -------------------------------------------------------------------------------- /compiler/passes/escape/escape.go: -------------------------------------------------------------------------------- 1 | package escape 2 | 3 | import ( 4 | "github.com/zegl/tre/compiler/parser" 5 | ) 6 | 7 | // Escape performs variable escape analysis on variables allocated in functions 8 | func Escape(input *parser.FileNode) *parser.FileNode { 9 | for _, ins := range input.Instructions { 10 | if defFunc, ok := ins.(*parser.DefineFuncNode); ok { 11 | 12 | // Name of the var mapped to their allocNode instruction index 13 | allocatedVars := map[string]int{} 14 | escapingVars := map[string]struct{}{} 15 | 16 | for insIndex, ins := range defFunc.Body { 17 | 18 | // Find all variables allocated in this function 19 | if allocIns, ok := ins.(*parser.AllocNode); ok { 20 | for _, name := range allocIns.Name { 21 | allocatedVars[name] = insIndex 22 | } 23 | } 24 | 25 | // Find all variables returned from this function 26 | if retIns, ok := ins.(*parser.ReturnNode); ok { 27 | for _, val := range retIns.Vals { 28 | findEscaping(escapingVars, val) 29 | } 30 | } 31 | } 32 | 33 | // Mark as escaping in the AST 34 | for escapingName := range escapingVars { 35 | if allocIndex, ok := allocatedVars[escapingName]; ok { 36 | allocIns := defFunc.Body[allocIndex].(*parser.AllocNode) 37 | allocIns.Escapes = true 38 | defFunc.Body[allocIndex] = allocIns 39 | } 40 | } 41 | } 42 | } 43 | 44 | return input 45 | } 46 | 47 | func findEscaping(escapingVars map[string]struct{}, ins parser.Node) { 48 | if retVariable, ok := ins.(*parser.NameNode); ok { 49 | escapingVars[retVariable.Name] = struct{}{} 50 | return 51 | } 52 | 53 | if retPtr, ok := ins.(*parser.GetReferenceNode); ok { 54 | findEscaping(escapingVars, retPtr.Item) 55 | return 56 | } 57 | 58 | /*if initStruct, ok := ins.(*parser.InitializeStructNode); ok { 59 | // initStruct.Items 60 | }*/ 61 | } 62 | -------------------------------------------------------------------------------- /compiler/compiler/func_len.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/llir/llvm/ir/constant" 7 | llvmTypes "github.com/llir/llvm/ir/types" 8 | llvmValue "github.com/llir/llvm/ir/value" 9 | 10 | "github.com/zegl/tre/compiler/compiler/internal" 11 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 12 | "github.com/zegl/tre/compiler/compiler/value" 13 | "github.com/zegl/tre/compiler/parser" 14 | ) 15 | 16 | func (c *Compiler) lenFuncCall(v *parser.CallNode) value.Value { 17 | arg := c.compileValue(v.Arguments[0]) 18 | 19 | if arg.Type.Name() == "string" { 20 | f, ok := c.packages["global"].GetPkgVar("len_string", true) 21 | if !ok { 22 | panic("could not find len_string func") 23 | } 24 | val := internal.LoadIfVariable(c.contextBlock, arg) 25 | 26 | return value.Value{ 27 | Value: c.contextBlock.NewCall(f.Value.(llvmValue.Named), val), 28 | Type: f.Type, 29 | IsVariable: false, 30 | } 31 | } 32 | 33 | if arg.Type.Name() == "array" { 34 | if ptrType, ok := arg.Value.Type().(*llvmTypes.PointerType); ok { 35 | if arrayType, ok := ptrType.ElemType.(*llvmTypes.ArrayType); ok { 36 | return value.Value{ 37 | Value: constant.NewInt(llvmTypes.I32, int64(arrayType.Len)), 38 | Type: i32, 39 | IsVariable: false, 40 | } 41 | } 42 | } 43 | } 44 | 45 | if arg.Type.Name() == "slice" { 46 | val := arg.Value 47 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 48 | 49 | if _, ok := val.Type().(*llvmTypes.PointerType); ok { 50 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 51 | } 52 | 53 | return value.Value{ 54 | Value: c.contextBlock.NewExtractValue(val, 0), 55 | Type: i32, 56 | IsVariable: false, 57 | } 58 | } 59 | 60 | panic(fmt.Sprintf("Can not call len() on type %s (%+v)", arg.Type.Name(), v.Arguments[0])) 61 | } 62 | -------------------------------------------------------------------------------- /compiler/parser/for.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zegl/tre/compiler/lexer" 6 | ) 7 | 8 | // ForNode creates a new for-loop 9 | type ForNode struct { 10 | baseNode 11 | 12 | BeforeLoop Node 13 | Condition *OperatorNode 14 | AfterIteration Node 15 | Block []Node 16 | 17 | IsThreeTypeFor bool 18 | } 19 | 20 | func (f ForNode) String() string { 21 | return fmt.Sprintf("For(%s; %s; %s) {\n\t%s\n}", f.BeforeLoop, f.Condition, f.AfterIteration, f.Block) 22 | } 23 | 24 | func (p *parser) parseFor() *ForNode { 25 | res := &ForNode{} 26 | 27 | p.i++ 28 | beforeLoop, reachedItem := p.parseUntilEither([]lexer.Item{ 29 | {Type: lexer.OPERATOR, Val: ";"}, // three type for 30 | {Type: lexer.OPERATOR, Val: "{"}, // range type for 31 | }) 32 | 33 | if len(beforeLoop) != 1 { 34 | panic("Expected only one beforeLoop in for loop") 35 | } 36 | 37 | isThreeTypeFor := false 38 | if reachedItem.Val == ";" { 39 | isThreeTypeFor = true 40 | res.IsThreeTypeFor = true 41 | } 42 | 43 | res.BeforeLoop = beforeLoop[0] 44 | 45 | if isThreeTypeFor { 46 | p.i++ 47 | loopCondition := p.parseUntil(lexer.Item{Type: lexer.OPERATOR, Val: ";"}) 48 | if len(loopCondition) != 1 { 49 | panic("Expected only one condition in for loop") 50 | } 51 | 52 | if conditionNode, ok := loopCondition[0].(*OperatorNode); ok { 53 | res.Condition = conditionNode 54 | } else { 55 | panic(fmt.Sprintf("Expected OperatorNode in for loop. Got: %T: %+v", loopCondition[0], loopCondition[0])) 56 | } 57 | 58 | p.i++ 59 | afterIteration := p.parseUntil(lexer.Item{Type: lexer.OPERATOR, Val: "{"}) 60 | if len(afterIteration) != 1 { 61 | panic("Expected only one afterIteration in for loop") 62 | } 63 | res.AfterIteration = afterIteration[0] 64 | } 65 | 66 | p.i++ 67 | res.Block = p.parseUntil(lexer.Item{Type: lexer.OPERATOR, Val: "}"}) 68 | 69 | return res 70 | } 71 | -------------------------------------------------------------------------------- /compiler/compiler/switch.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | "github.com/llir/llvm/ir/constant" 6 | 7 | "github.com/zegl/tre/compiler/compiler/internal" 8 | "github.com/zegl/tre/compiler/compiler/name" 9 | "github.com/zegl/tre/compiler/parser" 10 | ) 11 | 12 | func (c *Compiler) compileSwitchNode(v *parser.SwitchNode) { 13 | switchItem := c.compileValue(v.Item) 14 | 15 | var cases []*ir.Case 16 | caseBlocks := make([]*ir.Block, len(v.Cases)) 17 | 18 | afterSwitch := c.contextBlock.Parent.NewBlock(name.Block() + "after-switch") 19 | 20 | // build default case 21 | defaultCase := c.contextBlock.Parent.NewBlock(name.Block() + "switch-default") 22 | if v.DefaultBody != nil { 23 | preDefaultBlock := c.contextBlock 24 | c.contextBlock = defaultCase 25 | c.compile(v.DefaultBody) 26 | c.contextBlock = preDefaultBlock 27 | } 28 | defaultCase.NewBr(afterSwitch) 29 | 30 | // Parse all cases 31 | for caseIndex, parseCase := range v.Cases { 32 | preCaseBlock := c.contextBlock 33 | caseBlock := c.contextBlock.Parent.NewBlock(name.Block() + "case") 34 | c.contextBlock = caseBlock 35 | c.compile(parseCase.Body) 36 | c.contextBlock = preCaseBlock 37 | 38 | caseBlocks[caseIndex] = caseBlock 39 | 40 | for _, cond := range parseCase.Conditions { 41 | item := c.compileValue(cond) 42 | cases = append(cases, ir.NewCase(item.Value.(constant.Constant), caseBlock)) 43 | } 44 | } 45 | 46 | for caseIndex, parseCase := range v.Cases { 47 | if parseCase.Fallthrough { 48 | // Jump to the next case body 49 | caseBlocks[caseIndex].Term = ir.NewBr(caseBlocks[caseIndex+1]) 50 | } else { 51 | // Jump to after switch 52 | caseBlocks[caseIndex].Term = ir.NewBr(afterSwitch) 53 | } 54 | } 55 | 56 | val := internal.LoadIfVariable(c.contextBlock, switchItem) 57 | c.contextBlock.Term = c.contextBlock.NewSwitch(val, defaultCase, cases...) 58 | 59 | c.contextBlock = afterSwitch 60 | } 61 | -------------------------------------------------------------------------------- /compiler/compiler/pointers.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 6 | "github.com/zegl/tre/compiler/compiler/name" 7 | 8 | "github.com/zegl/tre/compiler/compiler/types" 9 | "github.com/zegl/tre/compiler/compiler/value" 10 | "github.com/zegl/tre/compiler/parser" 11 | ) 12 | 13 | func (c *Compiler) compileGetReferenceNode(v *parser.GetReferenceNode) value.Value { 14 | val := c.compileValue(v.Item) 15 | 16 | // Case where allocation is not necessary, as all LLVM values are pointers by default 17 | if val.IsVariable { 18 | if _, ok := val.Type.(*types.Pointer); !ok { 19 | return value.Value{ 20 | Type: &types.Pointer{ 21 | Type: val.Type, 22 | IsNonAllocDereference: true, 23 | }, 24 | Value: val.Value, 25 | IsVariable: false, 26 | } 27 | } 28 | 29 | if structType, ok := val.Type.(*types.Struct); ok && structType.IsHeapAllocated { 30 | return value.Value{ 31 | Type: &types.Pointer{ 32 | Type: val.Type, 33 | IsNonAllocDereference: true, 34 | }, 35 | Value: val.Value, 36 | IsVariable: false, 37 | } 38 | } 39 | } 40 | 41 | // One extra allocation is neccesary 42 | newSrc := c.contextBlock.NewAlloca(val.Type.LLVM()) 43 | newSrc.SetName(name.Var("reference-alloca")) 44 | c.contextBlock.NewStore(val.Value, newSrc) 45 | 46 | return value.Value{ 47 | Type: &types.Pointer{ 48 | Type: val.Type, 49 | LlvmType: newSrc.Type(), 50 | }, 51 | Value: newSrc, 52 | IsVariable: true, 53 | } 54 | } 55 | 56 | func (c *Compiler) compileDereferenceNode(v *parser.DereferenceNode) value.Value { 57 | val := c.compileValue(v.Item) 58 | 59 | if ptrVal, ok := val.Type.(*types.Pointer); ok { 60 | if ptrVal.IsNonAllocDereference { 61 | return value.Value{ 62 | Value: val.Value, 63 | Type: ptrVal.Type, 64 | IsVariable: true, 65 | } 66 | } 67 | 68 | return value.Value{ 69 | Value: c.contextBlock.NewLoad(pointer.ElemType(val.Value), val.Value), 70 | Type: ptrVal.Type, 71 | IsVariable: false, 72 | } 73 | } 74 | 75 | panic(fmt.Sprintf("invalid indirect of TODO (type %s)", val.Type)) 76 | } 77 | -------------------------------------------------------------------------------- /compiler/testdata/slice-append.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "external" 4 | 5 | func main() { 6 | var arr []int 7 | 8 | external.Printf("%d\n", len(arr)) // 0 9 | external.Printf("%d\n", cap(arr)) // 2 10 | 11 | arr = append(arr, 100) 12 | 13 | external.Printf("%d\n", len(arr)) // 1 14 | external.Printf("%d\n", cap(arr)) // 2 15 | 16 | external.Printf("%d\n", arr[0]) // 100 17 | 18 | arr = append(arr, 200) 19 | 20 | external.Printf("%d\n", len(arr)) // 2 21 | external.Printf("%d\n", cap(arr)) // 2 22 | 23 | external.Printf("%d\n", arr[0]) // 100 24 | external.Printf("%d\n", arr[1]) // 200 25 | 26 | arr = append(arr, 300) 27 | 28 | external.Printf("%d\n", len(arr)) // 3 29 | external.Printf("%d\n", cap(arr)) // 4 30 | 31 | external.Printf("%d\n", arr[0]) // 100 32 | external.Printf("%d\n", arr[1]) // 200 33 | external.Printf("%d\n", arr[2]) // 300 34 | 35 | arr = append(arr, 400) 36 | 37 | external.Printf("%d\n", len(arr)) // 4 38 | external.Printf("%d\n", cap(arr)) // 4 39 | 40 | external.Printf("%d\n", arr[0]) // 100 41 | external.Printf("%d\n", arr[1]) // 200 42 | external.Printf("%d\n", arr[2]) // 300 43 | external.Printf("%d\n", arr[3]) // 400 44 | 45 | arr = append(arr, 500) 46 | 47 | external.Printf("%d\n", len(arr)) // 5 48 | external.Printf("%d\n", cap(arr)) // 8 49 | 50 | external.Printf("%d\n", arr[0]) // 100 51 | external.Printf("%d\n", arr[1]) // 200 52 | external.Printf("%d\n", arr[2]) // 300 53 | external.Printf("%d\n", arr[3]) // 400 54 | external.Printf("%d\n", arr[4]) // 500 55 | 56 | arr = append(arr, 600) 57 | arr = append(arr, 700) 58 | arr = append(arr, 800) 59 | arr = append(arr, 900) 60 | arr = append(arr, 1000) 61 | 62 | external.Printf("%d\n", len(arr)) // 10 63 | external.Printf("%d\n", cap(arr)) // 16 64 | 65 | external.Printf("%d\n", arr[0]) // 100 66 | external.Printf("%d\n", arr[1]) // 200 67 | external.Printf("%d\n", arr[2]) // 300 68 | external.Printf("%d\n", arr[3]) // 400 69 | external.Printf("%d\n", arr[4]) // 500 70 | external.Printf("%d\n", arr[5]) // 600 71 | external.Printf("%d\n", arr[6]) // 700 72 | external.Printf("%d\n", arr[7]) // 800 73 | external.Printf("%d\n", arr[8]) // 900 74 | external.Printf("%d\n", arr[9]) // 1000 75 | } 76 | -------------------------------------------------------------------------------- /compiler/compiler/types/interface.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "sort" 6 | 7 | "github.com/llir/llvm/ir/types" 8 | llvmValue "github.com/llir/llvm/ir/value" 9 | ) 10 | 11 | type Interface struct { 12 | backingType 13 | 14 | SourceName string 15 | RequiredMethods map[string]InterfaceMethod 16 | } 17 | 18 | func (i Interface) Name() string { 19 | return fmt.Sprintf("interface(%s)", i.SourceName) 20 | } 21 | 22 | // SortedRequiredMethods returns a sorted slice of all method names 23 | // The returned order is the order the methods will be layed out in the JumpTable 24 | func (i Interface) SortedRequiredMethods() []string { 25 | var orderedMethods []string 26 | for methodName := range i.RequiredMethods { 27 | orderedMethods = append(orderedMethods, methodName) 28 | } 29 | sort.Strings(orderedMethods) 30 | return orderedMethods 31 | } 32 | 33 | func (i Interface) JumpTable() *types.StructType { 34 | orderedMethods := i.SortedRequiredMethods() 35 | 36 | var ifaceTableMethods []types.Type 37 | 38 | for _, methodName := range orderedMethods { 39 | methodSignature := i.RequiredMethods[methodName] 40 | 41 | var retType types.Type = types.Void 42 | if len(methodSignature.ReturnTypes) > 0 { 43 | retType = methodSignature.ReturnTypes[0].LLVM() 44 | } 45 | 46 | paramTypes := []types.Type{types.NewPointer(types.I8)} 47 | for _, argType := range methodSignature.ArgumentTypes { 48 | paramTypes = append(paramTypes, argType.LLVM()) 49 | } 50 | 51 | ifaceTableMethods = append(ifaceTableMethods, types.NewPointer(types.NewFunc(retType, paramTypes...))) 52 | } 53 | 54 | return types.NewStruct(ifaceTableMethods...) 55 | } 56 | 57 | func (i Interface) LLVM() types.Type { 58 | return types.NewStruct( 59 | // Pointer to the backing data 60 | types.NewPointer(types.I8), 61 | 62 | // Backing data type 63 | types.I32, 64 | 65 | // Interface table 66 | // Used for method resolving 67 | types.NewPointer(i.JumpTable()), 68 | ) 69 | } 70 | 71 | func (Interface) Size() int64 { 72 | return 64/8 * 3 73 | } 74 | 75 | type InterfaceMethod struct { 76 | backingType 77 | 78 | LlvmJumpFunction llvmValue.Named 79 | 80 | ArgumentTypes []Type 81 | ReturnTypes []Type 82 | } 83 | 84 | func (InterfaceMethod) LLVM() types.Type { 85 | panic("InterfaceMethod has no LLVM value") 86 | } 87 | 88 | func (InterfaceMethod) Name() string { 89 | return "InterfaceMethod" 90 | } 91 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | # This testsuite runs the tests in convermode, and uploads the results to codecov 4 | tests-stretch-clang-8-0-0: 5 | docker: 6 | - image: golang:1.13-stretch 7 | working_directory: /src/tre 8 | steps: 9 | - run: apt-get update -q && 10 | apt-get install -y wget xz-utils && 11 | mkdir -p /src/llvm && cd /src/llvm && 12 | wget http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -O llvm.tar.xz && 13 | tar -xf llvm.tar.xz --strip-components 1 && 14 | ln -s /src/llvm/bin/clang /usr/local/bin/clang && 15 | cd /src/tre 16 | - checkout 17 | - run: go test -v github.com/zegl/tre/compiler/... github.com/zegl/tre/cmd/... 18 | - run: go test -v --cover --coverpkg="github.com/zegl/tre/..." --coverprofile=coverage.txt --covermode=count github.com/zegl/tre/compiler 19 | - run: bash <(curl -s https://codecov.io/bash) 20 | 21 | tests-buster-clang-9-0-0: 22 | docker: 23 | - image: golang:1.13-buster 24 | working_directory: /src/tre 25 | steps: 26 | - run: apt-get update -q && 27 | apt-get install -y wget xz-utils libtinfo5 && 28 | mkdir -p /src/llvm && cd /src/llvm && 29 | wget http://releases.llvm.org/9.0.0/clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -O llvm.tar.xz && 30 | tar -xf llvm.tar.xz --strip-components 1 && 31 | ln -s /src/llvm/bin/clang /usr/local/bin/clang && 32 | cd /src/tre 33 | - checkout 34 | - run: go test -v github.com/zegl/tre/compiler/... github.com/zegl/tre/cmd/... 35 | - run: go test -v github.com/zegl/tre/compiler 36 | 37 | tests-buster-clang-10-0-0: 38 | docker: 39 | - image: golang:1.13-buster 40 | working_directory: /src/tre 41 | steps: 42 | - run: apt-get update -q && 43 | apt-get install -y wget xz-utils libtinfo5 && 44 | mkdir -p /src/llvm && cd /src/llvm && 45 | wget https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz -O llvm.tar.xz && 46 | tar -xf llvm.tar.xz --strip-components 1 && 47 | ln -s /src/llvm/bin/clang /usr/local/bin/clang && 48 | cd /src/tre 49 | - checkout 50 | - run: go test -v github.com/zegl/tre/compiler/... github.com/zegl/tre/cmd/... 51 | - run: go test -v github.com/zegl/tre/compiler 52 | 53 | workflows: 54 | version: 2 55 | build: 56 | jobs: 57 | - "tests-stretch-clang-8-0-0" 58 | - "tests-buster-clang-9-0-0" 59 | - "tests-buster-clang-10-0-0" 60 | -------------------------------------------------------------------------------- /compiler/compiler/constants.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 5 | "github.com/zegl/tre/compiler/compiler/strings" 6 | "github.com/zegl/tre/compiler/compiler/types" 7 | "github.com/zegl/tre/compiler/compiler/value" 8 | "github.com/zegl/tre/compiler/parser" 9 | 10 | "github.com/llir/llvm/ir" 11 | "github.com/llir/llvm/ir/constant" 12 | llvmTypes "github.com/llir/llvm/ir/types" 13 | ) 14 | 15 | func (c *Compiler) compileConstantNode(v *parser.ConstantNode) value.Value { 16 | switch v.Type { 17 | case parser.NUMBER: 18 | var intType *types.Int = i64 19 | 20 | // Use context to detect which type that should be returned 21 | // Is used to detect if a number should be i32 or i64 etc... 22 | var wantedType types.Type 23 | if len(c.contextAssignDest) > 0 { 24 | wantedType = c.contextAssignDest[len(c.contextAssignDest)-1].Type 25 | } 26 | 27 | // Create the correct type of int based on context 28 | if t, ok := wantedType.(*types.Int); ok { 29 | intType = t 30 | } 31 | 32 | return value.Value{ 33 | Value: constant.NewInt(intType.Type, v.Value), 34 | Type: intType, 35 | IsVariable: false, 36 | } 37 | 38 | case parser.STRING: 39 | var constString *ir.Global 40 | 41 | // Reuse the *ir.Global if it has already been created 42 | if reusedConst, ok := c.stringConstants[v.ValueStr]; ok { 43 | constString = reusedConst 44 | } else { 45 | constString = c.module.NewGlobalDef(strings.NextStringName(), strings.Constant(v.ValueStr)) 46 | constString.Immutable = true 47 | c.stringConstants[v.ValueStr] = constString 48 | } 49 | 50 | sType, ok := c.packages["global"].GetPkgType("string", true) 51 | if !ok { 52 | panic("string type not found") 53 | } 54 | alloc := c.contextBlock.NewAlloca(sType.LLVM()) 55 | 56 | // Save length of the string 57 | lenItem := c.contextBlock.NewGetElementPtr(pointer.ElemType(alloc), alloc, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 0)) 58 | c.contextBlock.NewStore(constant.NewInt(llvmTypes.I64, int64(len(v.ValueStr))), lenItem) 59 | 60 | // Save i8* version of string 61 | strItem := c.contextBlock.NewGetElementPtr(pointer.ElemType(alloc), alloc, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 1)) 62 | c.contextBlock.NewStore(strings.Toi8Ptr(c.contextBlock, constString), strItem) 63 | 64 | return value.Value{ 65 | Value: c.contextBlock.NewLoad(pointer.ElemType(alloc), alloc), 66 | Type: types.String, 67 | IsVariable: false, 68 | } 69 | 70 | case parser.BOOL: 71 | return value.Value{ 72 | Value: constant.NewInt(llvmTypes.I1, v.Value), 73 | Type: types.Bool, 74 | IsVariable: false, 75 | } 76 | 77 | default: 78 | panic("Unknown constant Type") 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /compiler/compiler_test.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "regexp" 11 | "runtime" 12 | "strings" 13 | "testing" 14 | 15 | "github.com/zegl/tre/cmd/tre/build" 16 | ) 17 | 18 | func TestAllPrograms(t *testing.T) { 19 | files, _ := ioutil.ReadDir("testdata") 20 | if len(files) == 0 { 21 | t.Error("No test files found") 22 | } 23 | 24 | for _, file := range files { 25 | for _, withOptimize := range []bool{false, true} { 26 | t.Run(fmt.Sprintf("%s/optimize:%v/", file.Name(), withOptimize), func(t *testing.T) { 27 | if err := buildRunAndCheck(t, "testdata/"+file.Name(), withOptimize); err != nil { 28 | t.Error("failed: " + err.Error()) 29 | } 30 | }) 31 | } 32 | } 33 | } 34 | 35 | func buildRunAndCheck(t *testing.T, path string, withOptimize bool) (err error) { 36 | defer func() { 37 | if r := recover(); r != nil { 38 | err = fmt.Errorf("Recovered in buildRunAndCheck: %+v", r) 39 | } 40 | }() 41 | 42 | fp, err := os.Stat(path) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | mainPath := path 48 | if fp.IsDir() { 49 | mainPath = path + "/main.go" 50 | } 51 | 52 | content, err := ioutil.ReadFile(mainPath) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | expect := "" 58 | 59 | re, _ := regexp.Compile(`(?m)// (.*?)$`) 60 | for _, str := range re.FindAllString(string(content), -1) { 61 | expect += strings.Replace(str, "// ", "", -1) + "\n" 62 | } 63 | 64 | // Normalize newlines 65 | expect = strings.Replace(expect, "\r\n", "\n", -1) 66 | expect = strings.TrimSpace(expect) 67 | 68 | runProgram := true 69 | var output string 70 | 71 | outputBinaryPath := os.TempDir() + "/exec" 72 | 73 | // "GOROOT" (treroot?) detection 74 | _, testFilePath, _, _ := runtime.Caller(0) 75 | goroot := filepath.Clean(testFilePath + "/../../pkg/") 76 | 77 | err = build.Build(path, goroot, outputBinaryPath, false, withOptimize) 78 | if err != nil { 79 | output = strings.TrimSpace(err.Error()) 80 | runProgram = false 81 | } 82 | 83 | // Run program output 84 | if runProgram { 85 | cmd := exec.Command(outputBinaryPath) 86 | stdout, err := cmd.CombinedOutput() 87 | if err != nil && err.Error() != "exit status 1" { 88 | 89 | // The test is only asserting that the program should not run successfully 90 | // We're currently getting different errors from runtime depending on the clang optimization level 91 | if expect == "Expected: runtime crash" { 92 | return nil 93 | } 94 | 95 | output = output + strings.TrimSpace(err.Error()) 96 | } else { 97 | output = output + strings.TrimSpace(string(stdout)) 98 | } 99 | } 100 | 101 | if expect == output { 102 | return nil 103 | } 104 | 105 | t.Logf("Expected:\n---\n'%s'\n---\nResult:\n---\n'%s'\n---\n", expect, output) 106 | 107 | return errors.New("Unexpected result") 108 | } 109 | -------------------------------------------------------------------------------- /compiler/parser/switch.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zegl/tre/compiler/lexer" 7 | ) 8 | 9 | type SwitchNode struct { 10 | baseNode 11 | Item Node 12 | Cases []*SwitchCaseNode 13 | DefaultBody []Node // can be null 14 | } 15 | 16 | type SwitchCaseNode struct { 17 | baseNode 18 | Conditions []Node 19 | Body []Node 20 | Fallthrough bool 21 | } 22 | 23 | func (s SwitchNode) String() string { 24 | return fmt.Sprintf("switch %s", s.Item) 25 | } 26 | 27 | func (s SwitchCaseNode) String() string { 28 | return fmt.Sprintf("case %+v", s.Conditions) 29 | } 30 | 31 | func (p *parser) parseSwitch() *SwitchNode { 32 | p.i++ 33 | 34 | s := &SwitchNode{ 35 | Item: p.parseOne(true), 36 | Cases: make([]*SwitchCaseNode, 0), 37 | } 38 | 39 | p.i++ 40 | p.expect(p.lookAhead(0), lexer.Item{Type: lexer.OPERATOR, Val: "{"}) 41 | p.i++ 42 | 43 | for { 44 | next := p.lookAhead(0) 45 | 46 | if next.Type == lexer.EOL { 47 | p.i++ 48 | continue 49 | } 50 | 51 | if next.Type == lexer.KEYWORD && next.Val == "case" { 52 | p.i++ 53 | switchCase := SwitchCaseNode{ 54 | Conditions: []Node{p.parseOne(true)}, 55 | } 56 | 57 | p.i++ 58 | 59 | for { 60 | curr := p.lookAhead(0) 61 | if curr.Type == lexer.OPERATOR && curr.Val == ":" { 62 | p.i++ 63 | break 64 | } 65 | 66 | if curr.Type == lexer.OPERATOR && curr.Val == "," { 67 | p.i++ 68 | switchCase.Conditions = append(append(switchCase.Conditions, 69 | p.parseOne(true), 70 | )) 71 | p.i++ 72 | continue 73 | } 74 | 75 | panic(fmt.Sprintf("Expected : or , in case. Got %+v", curr)) 76 | } 77 | 78 | var reached lexer.Item 79 | switchCase.Body, reached = p.parseUntilEither( 80 | []lexer.Item{ 81 | {Type: lexer.OPERATOR, Val: "}"}, 82 | {Type: lexer.KEYWORD, Val: "case"}, 83 | {Type: lexer.KEYWORD, Val: "default"}, 84 | {Type: lexer.KEYWORD, Val: "fallthrough"}, 85 | }, 86 | ) 87 | 88 | if reached.Type == lexer.KEYWORD && reached.Val == "fallthrough" { 89 | switchCase.Fallthrough = true 90 | p.i++ 91 | } 92 | 93 | s.Cases = append(s.Cases, &switchCase) 94 | 95 | // reached end of switch 96 | if reached.Type == lexer.OPERATOR && reached.Val == "}" { 97 | break 98 | } 99 | } 100 | 101 | if next.Type == lexer.KEYWORD && next.Val == "default" { 102 | p.i++ 103 | p.expect(p.lookAhead(0), lexer.Item{Type: lexer.OPERATOR, Val: ":"}) 104 | p.i++ 105 | 106 | body, reached := p.parseUntilEither( 107 | []lexer.Item{ 108 | {Type: lexer.OPERATOR, Val: "}"}, 109 | {Type: lexer.KEYWORD, Val: "case"}, 110 | }, 111 | ) 112 | 113 | s.DefaultBody = body 114 | 115 | // reached end of switch 116 | if reached.Type == lexer.OPERATOR && reached.Val == "}" { 117 | break 118 | } 119 | } 120 | } 121 | 122 | return s 123 | } 124 | -------------------------------------------------------------------------------- /compiler/passes/escape/escape_test.go: -------------------------------------------------------------------------------- 1 | package escape 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/zegl/tre/compiler/lexer" 8 | "github.com/zegl/tre/compiler/parser" 9 | ) 10 | 11 | func escapeTest(t *testing.T, input string, expected map[string]bool) { 12 | lexed := lexer.Lex(input) 13 | parsed := parser.Parse(lexed, false) 14 | parsed = Escape(parsed) 15 | 16 | var allocsChecked []string 17 | 18 | for _, ins := range parsed.Instructions { 19 | if defFuncNode, ok := ins.(*parser.DefineFuncNode); ok { 20 | for _, ins := range defFuncNode.Body { 21 | if allocNode, ok := ins.(*parser.AllocNode); ok { 22 | allocsChecked = append(allocsChecked, allocNode.Name[0]) 23 | assert.Equal(t, expected[allocNode.Name[0]], allocNode.Escapes, allocNode.Name) 24 | } 25 | } 26 | } 27 | } 28 | 29 | assert.Equal(t, len(allocsChecked), len(expected)) 30 | } 31 | 32 | func TestNoEscape(t *testing.T) { 33 | escapeTest(t, `package main 34 | 35 | func main() { 36 | a := 100 37 | b := 200 38 | } 39 | `, map[string]bool{ 40 | "a": false, 41 | "b": false, 42 | }) 43 | } 44 | 45 | func TestEscapes(t *testing.T) { 46 | escapeTest(t, `package main 47 | 48 | func main() { 49 | a := 100 50 | b := 200 51 | return b 52 | } 53 | `, map[string]bool{ 54 | "a": false, 55 | "b": true, 56 | }) 57 | } 58 | 59 | func TestEscapesPointer(t *testing.T) { 60 | escapeTest(t, `package main 61 | 62 | func main() *int { 63 | a := 100 64 | b := 200 65 | return &b 66 | } 67 | `, map[string]bool{ 68 | "a": false, 69 | "b": true, 70 | }) 71 | } 72 | 73 | func TestEscapesStructPointer(t *testing.T) { 74 | escapeTest(t, `package main 75 | 76 | type mytype struct { 77 | a int 78 | b int 79 | } 80 | 81 | func main() *int { 82 | a := 100 83 | b := mytype{ 84 | a: 100, 85 | b: 200, 86 | } 87 | return &b 88 | } 89 | `, map[string]bool{ 90 | "a": false, 91 | "b": true, 92 | }) 93 | } 94 | 95 | func TestEscapeNestedStruct(t *testing.T) { 96 | escapeTest(t, `package main 97 | 98 | type Bar struct { 99 | num int64 100 | } 101 | 102 | type Foo struct { 103 | num int64 104 | bar *Bar 105 | } 106 | 107 | func GetFooPtr() *Foo { 108 | f := Foo{ 109 | num: 300, 110 | bar: &Bar{num: 400}, 111 | } 112 | 113 | return &f 114 | }`, 115 | map[string]bool{ 116 | "f": true, 117 | }) 118 | } 119 | 120 | /* 121 | TODO: Implement feature so that this case can pass 122 | f can be stack allocated, but f.bar needs to allocqated on the heap 123 | func TestNoEscapeNestedStruct(t *testing.T) { 124 | escapeTest(t, `package main 125 | 126 | type Bar struct { 127 | num int64 128 | } 129 | 130 | type Foo struct { 131 | num int64 132 | bar *Bar 133 | } 134 | 135 | func GetFooPtr() Foo { 136 | f := Foo{ 137 | num: 300, 138 | bar: &Bar{num: 400}, 139 | } 140 | 141 | return f 142 | }`, 143 | map[string]bool{ 144 | "f": false, 145 | }) 146 | } 147 | */ 148 | -------------------------------------------------------------------------------- /compiler/compiler/interface.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/llir/llvm/ir" 7 | "github.com/llir/llvm/ir/constant" 8 | llvmTypes "github.com/llir/llvm/ir/types" 9 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 10 | "github.com/zegl/tre/compiler/compiler/types" 11 | "github.com/zegl/tre/compiler/compiler/value" 12 | ) 13 | 14 | func (c *Compiler) valueToInterfaceValue(v value.Value, targetType types.Type) value.Value { 15 | 16 | // Don't do anything if the target is not an interface 17 | iface, isInterface := targetType.(*types.Interface) 18 | if !isInterface { 19 | return v 20 | } 21 | 22 | // Don't do anything if the src already is an interface 23 | if _, sourceIsInterface := v.Type.(*types.Interface); sourceIsInterface { 24 | return v 25 | } 26 | 27 | val := v.Value 28 | 29 | // Convert to pointer variable 30 | if !v.IsVariable { 31 | ptrAlloca := c.contextBlock.NewAlloca(v.Type.LLVM()) 32 | c.contextBlock.NewStore(val, ptrAlloca) 33 | val = ptrAlloca 34 | } 35 | 36 | ifaceStruct := c.contextBlock.NewAlloca(targetType.LLVM()) 37 | 38 | dataPtr := c.contextBlock.NewGetElementPtr(pointer.ElemType(ifaceStruct), ifaceStruct, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 0)) 39 | bitcastedVal := c.contextBlock.NewBitCast(val, llvmTypes.NewPointer(llvmTypes.I8)) 40 | c.contextBlock.NewStore(bitcastedVal, dataPtr) 41 | 42 | dataTypePtr := c.contextBlock.NewGetElementPtr(pointer.ElemType(ifaceStruct), ifaceStruct, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 1)) 43 | 44 | backingTypID := getTypeID(v.Type.Name()) 45 | c.contextBlock.NewStore(constant.NewInt(llvmTypes.I32, backingTypID), dataTypePtr) 46 | 47 | // Create interface jump table if needed 48 | var funcTableAlloca *ir.InstAlloca 49 | if len(iface.RequiredMethods) > 0 { 50 | funcTablePtr := c.contextBlock.NewGetElementPtr(pointer.ElemType(ifaceStruct), ifaceStruct, 51 | constant.NewInt(llvmTypes.I32, 0), 52 | constant.NewInt(llvmTypes.I32, 2), 53 | ) 54 | funcTableAlloca = c.contextBlock.NewAlloca(iface.JumpTable()) 55 | c.contextBlock.NewStore(funcTableAlloca, funcTablePtr) 56 | } 57 | 58 | useFuncsFromType := v.Type 59 | 60 | // Pointer receiver methods are added to their "parent" type 61 | if ptrType, ok := useFuncsFromType.(*types.Pointer); ok { 62 | useFuncsFromType = ptrType.Type 63 | } 64 | 65 | // Add methods to the iface table 66 | for methodIndex, methodName := range iface.SortedRequiredMethods() { 67 | functionPointer := c.contextBlock.NewGetElementPtr(pointer.ElemType(funcTableAlloca), funcTableAlloca, 68 | constant.NewInt(llvmTypes.I32, 0), 69 | constant.NewInt(llvmTypes.I32, int64(methodIndex)), 70 | ) 71 | 72 | m, ok := useFuncsFromType.GetMethod(methodName) 73 | if !ok { 74 | panic(fmt.Sprintf("%s can not be used as %s, is missing %s method", useFuncsFromType.Name(), targetType.Name(), methodName)) 75 | } 76 | c.contextBlock.NewStore(m.Function.JumpFunction, functionPointer) 77 | } 78 | 79 | return value.Value{ 80 | Type: targetType, 81 | Value: ifaceStruct, 82 | IsVariable: true, 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /compiler/compiler/external_funcs.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | llvmTypes "github.com/llir/llvm/ir/types" 6 | 7 | "github.com/zegl/tre/compiler/compiler/types" 8 | "github.com/zegl/tre/compiler/compiler/value" 9 | ) 10 | 11 | // ExternalFuncs and the "external" package contains a mapping to glibc functions. 12 | // These are used to make bootstrapping of the language easier. The end goal is to not depend on glibc. 13 | type ExternalFuncs struct { 14 | Printf value.Value 15 | Malloc value.Value 16 | Realloc value.Value 17 | Memcpy value.Value 18 | Strcat value.Value 19 | Strcpy value.Value 20 | Strncpy value.Value 21 | Strndup value.Value 22 | Exit value.Value 23 | } 24 | 25 | func (c *Compiler) createExternalPackage() { 26 | external := NewPkg("external") 27 | 28 | setExternal := func(internalName string, fn *ir.Func, variadic bool) value.Value { 29 | fn.Sig.Variadic = variadic 30 | val := value.Value{ 31 | Type: &types.Function{ 32 | LlvmReturnType: types.Void, 33 | FuncType: fn.Type(), 34 | IsExternal: true, 35 | }, 36 | Value: fn, 37 | } 38 | external.DefinePkgVar(internalName, val) 39 | return val 40 | } 41 | 42 | c.externalFuncs.Printf = setExternal("Printf", c.module.NewFunc("printf", 43 | i32.LLVM(), 44 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 45 | ), true) 46 | 47 | c.externalFuncs.Malloc = setExternal("malloc", c.module.NewFunc("malloc", 48 | llvmTypes.NewPointer(i8.LLVM()), 49 | ir.NewParam("", i64.LLVM()), 50 | ), false) 51 | 52 | c.externalFuncs.Realloc = setExternal("realloc", c.module.NewFunc("realloc", 53 | llvmTypes.NewPointer(i8.LLVM()), 54 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 55 | ir.NewParam("", i64.LLVM()), 56 | ), false) 57 | 58 | c.externalFuncs.Memcpy = setExternal("memcpy", c.module.NewFunc("memcpy", 59 | llvmTypes.NewPointer(i8.LLVM()), 60 | ir.NewParam("dest", llvmTypes.NewPointer(i8.LLVM())), 61 | ir.NewParam("src", llvmTypes.NewPointer(i8.LLVM())), 62 | ir.NewParam("n", i64.LLVM()), 63 | ), false) 64 | 65 | c.externalFuncs.Strcat = setExternal("strcat", c.module.NewFunc("strcat", 66 | llvmTypes.NewPointer(i8.LLVM()), 67 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 68 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 69 | ), false) 70 | 71 | c.externalFuncs.Strcpy = setExternal("strcpy", c.module.NewFunc("strcpy", 72 | llvmTypes.NewPointer(i8.LLVM()), 73 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 74 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 75 | ), false) 76 | 77 | c.externalFuncs.Strncpy = setExternal("strncpy", c.module.NewFunc("strncpy", 78 | llvmTypes.NewPointer(i8.LLVM()), 79 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 80 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 81 | ir.NewParam("", i64.LLVM()), 82 | ), false) 83 | 84 | c.externalFuncs.Strndup = setExternal("strndup", c.module.NewFunc("strndup", 85 | llvmTypes.NewPointer(i8.LLVM()), 86 | ir.NewParam("", llvmTypes.NewPointer(i8.LLVM())), 87 | ir.NewParam("", i64.LLVM()), 88 | ), false) 89 | 90 | c.externalFuncs.Exit = setExternal("exit", c.module.NewFunc("exit", 91 | llvmTypes.Void, 92 | ir.NewParam("", i32.LLVM()), 93 | ), false) 94 | 95 | c.packages["external"] = external 96 | } 97 | -------------------------------------------------------------------------------- /compiler/parser/var_alloc_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/zegl/tre/compiler/lexer" 9 | ) 10 | 11 | func TestAllocType(t *testing.T) { 12 | lexed := lexer.Lex(`var a int`) 13 | 14 | expected := &FileNode{ 15 | Instructions: []Node{ 16 | &AllocNode{ 17 | Name: []string{"a"}, 18 | Type: &SingleTypeNode{TypeName: "int"}, 19 | }, 20 | }, 21 | } 22 | 23 | assert.Equal(t, expected, Parse(lexed, false)) 24 | } 25 | 26 | func TestAllocTypeWithValue(t *testing.T) { 27 | lexed := lexer.Lex(`var a int = 10`) 28 | 29 | expected := &FileNode{ 30 | Instructions: []Node{ 31 | &AllocNode{ 32 | Name: []string{"a"}, 33 | Type: &SingleTypeNode{TypeName: "int"}, 34 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 10}}, 35 | }, 36 | }, 37 | } 38 | 39 | assert.Equal(t, expected, Parse(lexed, false)) 40 | } 41 | 42 | func TestAllocImplicitTypeValue(t *testing.T) { 43 | lexed := lexer.Lex(`var a = 10`) 44 | 45 | expected := &FileNode{ 46 | Instructions: []Node{ 47 | &AllocNode{ 48 | Name: []string{"a"}, 49 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 10}}, 50 | }, 51 | }, 52 | } 53 | 54 | assert.Equal(t, expected, Parse(lexed, false)) 55 | } 56 | 57 | func TestAllocMultiWithType(t *testing.T) { 58 | lexed := lexer.Lex(`var a, b int`) 59 | 60 | expected := &FileNode{ 61 | Instructions: []Node{ 62 | &AllocNode{ 63 | Name: []string{"a", "b"}, 64 | Type: &SingleTypeNode{TypeName: "int"}, 65 | }, 66 | }, 67 | } 68 | 69 | assert.Equal(t, expected, Parse(lexed, false)) 70 | } 71 | 72 | func TestAllocGroup(t *testing.T) { 73 | lexed := lexer.Lex(`var ( 74 | a int 75 | b, c uint8 76 | d = 10 77 | )`) 78 | 79 | expected := &FileNode{ 80 | Instructions: []Node{ 81 | &AllocGroup{ 82 | Allocs: []*AllocNode{ 83 | { 84 | Name: []string{"a"}, 85 | Type: &SingleTypeNode{TypeName: "int"}, 86 | }, 87 | { 88 | Name: []string{"b", "c"}, 89 | Type: &SingleTypeNode{TypeName: "uint8"}, 90 | }, 91 | { 92 | Name: []string{"d"}, 93 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 10}}, 94 | }, 95 | }, 96 | }, 97 | }, 98 | } 99 | 100 | assert.Equal(t, expected, Parse(lexed, false)) 101 | } 102 | 103 | func TestConstAlloc(t *testing.T) { 104 | lexed := lexer.Lex(`const a = 30`) 105 | expected := &FileNode{ 106 | Instructions: []Node{ 107 | &AllocNode{ 108 | IsConst: true, 109 | Name: []string{"a"}, 110 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 30}}, 111 | }, 112 | }, 113 | } 114 | 115 | assert.Equal(t, expected, Parse(lexed, false)) 116 | } 117 | 118 | func TestAllocConstGroup(t *testing.T) { 119 | lexed := lexer.Lex(`const ( 120 | a = 10 121 | b, c = "bbb", "ccc" 122 | d = 20 123 | )`) 124 | 125 | expected := &FileNode{ 126 | Instructions: []Node{ 127 | &AllocGroup{ 128 | Allocs: []*AllocNode{ 129 | { 130 | IsConst: true, 131 | Name: []string{"a"}, 132 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 10}}, 133 | }, 134 | { 135 | Name: []string{"b", "c"}, 136 | Val: []Node{&ConstantNode{Type: STRING, ValueStr: "bbb"}, &ConstantNode{Type: STRING, ValueStr: "ccc"}}, 137 | IsConst: true, 138 | }, 139 | { 140 | Name: []string{"d"}, 141 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 20}}, 142 | IsConst: true, 143 | }, 144 | }, 145 | }, 146 | }, 147 | } 148 | 149 | assert.Equal(t, expected, Parse(lexed, false)) 150 | } 151 | -------------------------------------------------------------------------------- /compiler/lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestLexerSimpleAdd(t *testing.T) { 10 | r := Lex("aa + b") 11 | 12 | expected := []Item{ 13 | {Type: IDENTIFIER, Val: "aa", Line: 1}, 14 | {Type: OPERATOR, Val: "+", Line: 1}, 15 | {Type: IDENTIFIER, Val: "b", Line: 1}, 16 | {Type: EOL}, 17 | {Type: EOF}, 18 | } 19 | 20 | assert.Equal(t, expected, r) 21 | } 22 | 23 | func TestLexerSimpleAddWithNewlines(t *testing.T) { 24 | r := Lex("aa + b\naa + b") 25 | 26 | expected := []Item{ 27 | {Type: IDENTIFIER, Val: "aa", Line: 1}, 28 | {Type: OPERATOR, Val: "+", Line: 1}, 29 | {Type: IDENTIFIER, Val: "b", Line: 1}, 30 | 31 | {Type: EOL, Val: "", Line: 1}, 32 | 33 | {Type: IDENTIFIER, Val: "aa", Line: 2}, 34 | {Type: OPERATOR, Val: "+", Line: 2}, 35 | {Type: IDENTIFIER, Val: "b", Line: 2}, 36 | 37 | {Type: EOL}, 38 | {Type: EOF}, 39 | } 40 | 41 | assert.Equal(t, expected, r) 42 | } 43 | 44 | func TestLexerSimpleAddNumber(t *testing.T) { 45 | r := Lex("aa + 14") 46 | 47 | expected := []Item{ 48 | {Type: IDENTIFIER, Val: "aa", Line: 1}, 49 | {Type: OPERATOR, Val: "+", Line: 1}, 50 | {Type: NUMBER, Val: "14", Line: 1}, 51 | 52 | {Type: EOL}, 53 | {Type: EOF}, 54 | } 55 | 56 | assert.Equal(t, expected, r) 57 | } 58 | 59 | func TestLexerSimpleCall(t *testing.T) { 60 | r := Lex("foo(bar)") 61 | 62 | expected := []Item{ 63 | {Type: IDENTIFIER, Val: "foo", Line: 1}, 64 | {Type: OPERATOR, Val: "(", Line: 1}, 65 | {Type: IDENTIFIER, Val: "bar", Line: 1}, 66 | {Type: OPERATOR, Val: ")", Line: 1}, 67 | 68 | {Type: EOL}, 69 | {Type: EOF}, 70 | } 71 | 72 | assert.Equal(t, expected, r) 73 | } 74 | 75 | func TestLexerSimpleCallWithString(t *testing.T) { 76 | r := Lex("foo(\"bar\")") 77 | 78 | expected := []Item{ 79 | {Type: IDENTIFIER, Val: "foo", Line: 1}, 80 | {Type: OPERATOR, Val: "(", Line: 1}, 81 | {Type: STRING, Val: "bar", Line: 1}, 82 | {Type: OPERATOR, Val: ")", Line: 1}, 83 | 84 | {Type: EOL}, 85 | {Type: EOF}, 86 | } 87 | 88 | assert.Equal(t, expected, r) 89 | } 90 | 91 | func TestString(t *testing.T) { 92 | r := Lex(`"bar"`) 93 | 94 | expected := []Item{ 95 | {Type: STRING, Val: "bar", Line: 1}, 96 | {Type: EOL}, 97 | {Type: EOF}, 98 | } 99 | 100 | assert.Equal(t, expected, r) 101 | } 102 | 103 | func TestEscapedString(t *testing.T) { 104 | r := Lex(`"bar\""`) 105 | 106 | expected := []Item{ 107 | {Type: STRING, Val: "bar\"", Line: 1}, 108 | {Type: EOL}, 109 | {Type: EOF}, 110 | } 111 | 112 | assert.Equal(t, expected, r) 113 | } 114 | 115 | func TestLexerSimpleCallWithTwoStrings(t *testing.T) { 116 | r := Lex(`foo("bar", "baz")`) 117 | 118 | expected := []Item{ 119 | {Type: IDENTIFIER, Val: "foo", Line: 1}, 120 | {Type: OPERATOR, Val: "(", Line: 1}, 121 | {Type: STRING, Val: "bar", Line: 1}, 122 | {Type: OPERATOR, Val: ",", Line: 1}, 123 | {Type: STRING, Val: "baz", Line: 1}, 124 | {Type: OPERATOR, Val: ")", Line: 1}, 125 | {Type: EOL}, 126 | {Type: EOF}, 127 | } 128 | 129 | assert.Equal(t, expected, r) 130 | } 131 | 132 | func TestLexerSimpleCallWithStringNum(t *testing.T) { 133 | r := Lex(`printf("%d\n", 123)`) 134 | 135 | expected := []Item{ 136 | {Type: IDENTIFIER, Val: "printf", Line: 1}, 137 | {Type: OPERATOR, Val: "(", Line: 1}, 138 | {Type: STRING, Val: "%d\n", Line: 1}, 139 | {Type: OPERATOR, Val: ",", Line: 1}, 140 | {Type: NUMBER, Val: "123", Line: 1}, 141 | {Type: OPERATOR, Val: ")", Line: 1}, 142 | {Type: EOL}, 143 | {Type: EOF}, 144 | } 145 | 146 | assert.Equal(t, expected, r) 147 | } 148 | -------------------------------------------------------------------------------- /compiler/parser/multi_assign_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/zegl/tre/compiler/lexer" 9 | ) 10 | 11 | func TestMultiAssignVar(t *testing.T) { 12 | input := []lexer.Item{ 13 | {Type: lexer.KEYWORD, Val: "package", Line: 1}, 14 | {Type: lexer.IDENTIFIER, Val: "main", Line: 1}, 15 | {Type: lexer.EOL, Val: "", Line: 1}, 16 | {Type: lexer.EOL, Val: "", Line: 2}, 17 | {Type: lexer.KEYWORD, Val: "func", Line: 3}, 18 | {Type: lexer.IDENTIFIER, Val: "main", Line: 3}, 19 | {Type: lexer.OPERATOR, Val: "(", Line: 3}, 20 | {Type: lexer.OPERATOR, Val: ")", Line: 3}, 21 | {Type: lexer.OPERATOR, Val: "{", Line: 3}, 22 | {Type: lexer.EOL, Val: "", Line: 3}, 23 | {Type: lexer.IDENTIFIER, Val: "a", Line: 4}, 24 | {Type: lexer.OPERATOR, Val: ":=", Line: 4}, 25 | {Type: lexer.NUMBER, Val: "100", Line: 4}, 26 | {Type: lexer.EOL, Val: "", Line: 4}, 27 | {Type: lexer.IDENTIFIER, Val: "b", Line: 5}, 28 | {Type: lexer.OPERATOR, Val: ":=", Line: 5}, 29 | {Type: lexer.NUMBER, Val: "200", Line: 5}, 30 | {Type: lexer.EOL, Val: "", Line: 5}, 31 | {Type: lexer.IDENTIFIER, Val: "a", Line: 6}, 32 | {Type: lexer.OPERATOR, Val: ",", Line: 6}, 33 | {Type: lexer.IDENTIFIER, Val: "b", Line: 6}, 34 | {Type: lexer.OPERATOR, Val: "=", Line: 6}, 35 | {Type: lexer.NUMBER, Val: "300", Line: 6}, 36 | {Type: lexer.OPERATOR, Val: ",", Line: 6}, 37 | {Type: lexer.NUMBER, Val: "400", Line: 6}, 38 | {Type: lexer.EOL, Val: "", Line: 6}, 39 | {Type: lexer.OPERATOR, Val: "}", Line: 7}, 40 | {Type: lexer.EOL, Val: "", Line: 7}, 41 | {Type: lexer.EOF, Val: "", Line: 0}, 42 | } 43 | 44 | expected := &FileNode{ 45 | Instructions: []Node{ 46 | &DeclarePackageNode{PackageName: "main"}, 47 | &DefineFuncNode{Name: "main", IsNamed: true, 48 | Body: []Node{ 49 | &AllocNode{Name: []string{"a"}, Val: []Node{&ConstantNode{Type: NUMBER, Value: 100}}}, 50 | &AllocNode{Name: []string{"b"}, Val: []Node{&ConstantNode{Type: NUMBER, Value: 200}}}, 51 | &AssignNode{ 52 | Target: []Node{&NameNode{Name: "a"}, &NameNode{Name: "b"}}, 53 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 300}, &ConstantNode{Type: NUMBER, Value: 400}}, 54 | }, 55 | }, 56 | }, 57 | }, 58 | } 59 | 60 | assert.Equal(t, expected, Parse(input, false)) 61 | } 62 | func TestMultiAllocVar(t *testing.T) { 63 | input := []lexer.Item{ 64 | {Type: lexer.KEYWORD, Val: "package", Line: 1}, 65 | {Type: lexer.IDENTIFIER, Val: "main", Line: 1}, 66 | {Type: lexer.EOL, Val: "", Line: 1}, 67 | {Type: lexer.EOL, Val: "", Line: 2}, 68 | {Type: lexer.KEYWORD, Val: "func", Line: 3}, 69 | {Type: lexer.IDENTIFIER, Val: "main", Line: 3}, 70 | {Type: lexer.OPERATOR, Val: "(", Line: 3}, 71 | {Type: lexer.OPERATOR, Val: ")", Line: 3}, 72 | {Type: lexer.OPERATOR, Val: "{", Line: 3}, 73 | {Type: lexer.EOL, Val: "", Line: 3}, 74 | {Type: lexer.IDENTIFIER, Val: "a", Line: 6}, 75 | {Type: lexer.OPERATOR, Val: ",", Line: 6}, 76 | {Type: lexer.IDENTIFIER, Val: "b", Line: 6}, 77 | {Type: lexer.OPERATOR, Val: ":=", Line: 6}, 78 | {Type: lexer.NUMBER, Val: "300", Line: 6}, 79 | {Type: lexer.OPERATOR, Val: ",", Line: 6}, 80 | {Type: lexer.NUMBER, Val: "400", Line: 6}, 81 | {Type: lexer.EOL, Val: "", Line: 6}, 82 | {Type: lexer.OPERATOR, Val: "}", Line: 7}, 83 | {Type: lexer.EOL, Val: "", Line: 7}, 84 | {Type: lexer.EOF, Val: "", Line: 0}, 85 | } 86 | 87 | expected := &FileNode{ 88 | Instructions: []Node{ 89 | &DeclarePackageNode{PackageName: "main"}, 90 | &DefineFuncNode{Name: "main", IsNamed: true, 91 | Body: []Node{ 92 | &AllocNode{ 93 | Name: []string{"a", "b"}, 94 | Val: []Node{&ConstantNode{Type: NUMBER, Value: 300}, &ConstantNode{Type: NUMBER, Value: 400}}, 95 | }, 96 | }, 97 | }, 98 | }, 99 | } 100 | 101 | assert.Equal(t, expected, Parse(input, false)) 102 | } 103 | -------------------------------------------------------------------------------- /compiler/parser/walk.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Visitor interface { 8 | Visit(node Node) (n Node, w Visitor) 9 | } 10 | 11 | func Walk(v Visitor, node Node) (r Node) { 12 | if node, v = v.Visit(node); v == nil { 13 | return node 14 | } 15 | r = node 16 | 17 | if node == nil { 18 | return 19 | } 20 | 21 | switch n := node.(type) { 22 | case *FileNode: 23 | for i, a := range n.Instructions { 24 | n.Instructions[i] = Walk(v, a) 25 | } 26 | case *CallNode: 27 | for i, a := range n.Arguments { 28 | n.Arguments[i] = Walk(v, a) 29 | } 30 | n.Function = Walk(v, n.Function) 31 | case *OperatorNode: 32 | n.Left = Walk(v, n.Left) 33 | n.Right = Walk(v, n.Right) 34 | case *ConstantNode: 35 | // nothing to do 36 | case *ConditionNode: 37 | n.Cond = Walk(v, n.Cond).(*OperatorNode) 38 | for i, a := range n.True { 39 | n.True[i] = Walk(v, a) 40 | } 41 | for i, a := range n.False { 42 | n.False[i] = Walk(v, a) 43 | } 44 | case *DefineFuncNode: 45 | for i, a := range n.Body { 46 | n.Body[i] = Walk(v, a) 47 | } 48 | case *NameNode: 49 | // nothing to do 50 | case *MultiNameNode: 51 | // nothing to do 52 | case *ReturnNode: 53 | for i, a := range n.Vals { 54 | n.Vals[i] = Walk(v, a) 55 | } 56 | case *AllocNode: 57 | for i, a := range n.Val { 58 | n.Val[i] = Walk(v, a) 59 | } 60 | case *AllocGroup: 61 | for i, a := range n.Allocs { 62 | n.Allocs[i] = Walk(v, a).(*AllocNode) 63 | } 64 | case *TypeCastNode: 65 | // nothing to do 66 | case *DefineTypeNode: 67 | // nothing to do 68 | case *StructLoadElementNode: 69 | // nothing to do 70 | case *LoadArrayElement: 71 | n.Array = Walk(v, n.Array) 72 | n.Pos = Walk(v, n.Pos) 73 | case *SliceArrayNode: 74 | n.Start = Walk(v, n.Start) 75 | n.End = Walk(v, n.End) 76 | n.Val = Walk(v, n.Val) 77 | case *DeclarePackageNode: 78 | // nothing to do 79 | case *BreakNode: 80 | // nothing to do 81 | case *ContinueNode: 82 | // nothing to do 83 | case *GetReferenceNode: 84 | n.Item = Walk(v, n.Item) 85 | case *DereferenceNode: 86 | n.Item = Walk(v, n.Item) 87 | case *ImportNode: 88 | // nothing to do 89 | case *NegateNode: 90 | n.Item = Walk(v, n.Item) 91 | case *SubNode: 92 | n.Item = Walk(v, n.Item) 93 | case *InitializeSliceNode: 94 | for i, a := range n.Items { 95 | n.Items[i] = Walk(v, a) 96 | } 97 | case *InitializeArrayNode: 98 | for i, a := range n.Items { 99 | n.Items[i] = Walk(v, a) 100 | } 101 | case *DeVariadicSliceNode: 102 | n.Item = Walk(v, n.Item) 103 | case *TypeCastInterfaceNode: 104 | n.Item = Walk(v, n.Item) 105 | case *DecrementNode: 106 | n.Item = Walk(v, n.Item) 107 | case *IncrementNode: 108 | n.Item = Walk(v, n.Item) 109 | case *GroupNode: 110 | n.Item = Walk(v, n.Item) 111 | case *ForNode: 112 | n.BeforeLoop = Walk(v, n.BeforeLoop) 113 | if n.Condition != nil { 114 | if cond, ok := Walk(v, n.Condition).(*OperatorNode); ok { 115 | n.Condition = cond 116 | } 117 | } 118 | n.AfterIteration = Walk(v, n.AfterIteration) 119 | for i, a := range n.Block { 120 | n.Block[i] = Walk(v, a) 121 | } 122 | case *RangeNode: 123 | n.Item = Walk(v, n.Item) 124 | case *SwitchNode: 125 | n.Item = Walk(v, n.Item) 126 | for i, a := range n.Cases { 127 | n.Cases[i] = Walk(v, a).(*SwitchCaseNode) 128 | } 129 | for i, a := range n.DefaultBody { 130 | n.DefaultBody[i] = Walk(v, a) 131 | } 132 | case *SwitchCaseNode: 133 | for i, a := range n.Conditions { 134 | n.Conditions[i] = Walk(v, a) 135 | } 136 | for i, a := range n.Body { 137 | n.Body[i] = Walk(v, a) 138 | } 139 | case *AssignNode: 140 | for i, a := range n.Target { 141 | n.Target[i] = Walk(v, a) 142 | } 143 | for i, a := range n.Val { 144 | n.Val[i] = Walk(v, a) 145 | } 146 | case *InitializeStructNode: 147 | for i, a := range n.Items { 148 | n.Items[i] = Walk(v, a) 149 | } 150 | default: 151 | panic(fmt.Sprintf("unexpected type in Walk(): %T", node)) 152 | } 153 | return 154 | } 155 | -------------------------------------------------------------------------------- /compiler/passes/const_iota/iota_test.go: -------------------------------------------------------------------------------- 1 | package const_iota 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/zegl/tre/compiler/lexer" 8 | "github.com/zegl/tre/compiler/parser" 9 | ) 10 | 11 | func TestIota(t *testing.T) { 12 | // Run input code through the lexer. A list of tokens is returned. 13 | lexed := lexer.Lex(` 14 | package main 15 | 16 | const ( 17 | a = iota 18 | b = iota 19 | c = iota 20 | d = 19 21 | e = iota 22 | ) 23 | 24 | `) 25 | 26 | parsed := parser.Parse(lexed, false) 27 | res := Iota(parsed) 28 | 29 | expected := &parser.FileNode{ 30 | Instructions: []parser.Node{ 31 | &parser.DeclarePackageNode{PackageName: "main"}, 32 | &parser.AllocGroup{ 33 | Allocs: []*parser.AllocNode{ 34 | &parser.AllocNode{ 35 | Name: []string{"a"}, 36 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 0}}, 37 | IsConst: true, 38 | }, 39 | &parser.AllocNode{ 40 | Name: []string{"b"}, 41 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 1}}, 42 | IsConst: true, 43 | }, 44 | &parser.AllocNode{ 45 | Name: []string{"c"}, 46 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 2}}, 47 | IsConst: true, 48 | }, 49 | &parser.AllocNode{ 50 | Name: []string{"d"}, 51 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 19}}, 52 | IsConst: true, 53 | }, 54 | &parser.AllocNode{ 55 | Name: []string{"e"}, 56 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 4}}, 57 | IsConst: true, 58 | }, 59 | }, 60 | }, 61 | }, 62 | } 63 | 64 | assert.Equal(t, expected, res) 65 | } 66 | 67 | func TestIotaShortSyntax(t *testing.T) { 68 | // Run input code through the lexer. A list of tokens is returned. 69 | lexed := lexer.Lex(` 70 | package main 71 | 72 | const ( 73 | a = iota 74 | b 75 | c 76 | ) 77 | 78 | `) 79 | 80 | parsed := parser.Parse(lexed, false) 81 | res := Iota(parsed) 82 | 83 | expected := &parser.FileNode{ 84 | Instructions: []parser.Node{ 85 | &parser.DeclarePackageNode{PackageName: "main"}, 86 | &parser.AllocGroup{ 87 | Allocs: []*parser.AllocNode{ 88 | &parser.AllocNode{ 89 | Name: []string{"a"}, 90 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 0}}, 91 | IsConst: true, 92 | }, 93 | &parser.AllocNode{ 94 | Name: []string{"b"}, 95 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 1}}, 96 | IsConst: true, 97 | }, 98 | &parser.AllocNode{ 99 | Name: []string{"c"}, 100 | Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 2}}, 101 | IsConst: true, 102 | }, 103 | }, 104 | }, 105 | }, 106 | } 107 | 108 | assert.Equal(t, expected, res) 109 | } 110 | 111 | func TestIotaInOp(t *testing.T) { 112 | // Run input code through the lexer. A list of tokens is returned. 113 | lexed := lexer.Lex(` 114 | package main 115 | 116 | const ( 117 | a = iota * 2 118 | b = iota * 2 119 | c = 5 * iota 120 | ) 121 | 122 | `) 123 | 124 | parsed := parser.Parse(lexed, false) 125 | res := Iota(parsed) 126 | 127 | expected := &parser.FileNode{ 128 | Instructions: []parser.Node{ 129 | &parser.DeclarePackageNode{PackageName: "main"}, 130 | &parser.AllocGroup{ 131 | Allocs: []*parser.AllocNode{ 132 | &parser.AllocNode{ 133 | Name: []string{"a"}, 134 | Val: []parser.Node{ 135 | &parser.OperatorNode{ 136 | Left: &parser.ConstantNode{Type: parser.NUMBER, Value: 0}, 137 | Operator: parser.OP_MUL, 138 | Right: &parser.ConstantNode{Type: parser.NUMBER, Value: 2}, 139 | }, 140 | }, 141 | IsConst: true, 142 | }, 143 | &parser.AllocNode{ 144 | Name: []string{"b"}, 145 | Val: []parser.Node{ 146 | &parser.OperatorNode{ 147 | Left: &parser.ConstantNode{Type: parser.NUMBER, Value: 1}, 148 | Operator: parser.OP_MUL, 149 | Right: &parser.ConstantNode{Type: parser.NUMBER, Value: 2}, 150 | }, 151 | }, 152 | IsConst: true, 153 | }, 154 | &parser.AllocNode{ 155 | Name: []string{"c"}, 156 | Val: []parser.Node{ 157 | &parser.OperatorNode{ 158 | Left: &parser.ConstantNode{Type: parser.NUMBER, Value: 5}, 159 | Operator: parser.OP_MUL, 160 | Right: &parser.ConstantNode{Type: parser.NUMBER, Value: 2}, 161 | }, 162 | }, 163 | IsConst: true, 164 | }, 165 | }, 166 | }, 167 | }, 168 | } 169 | 170 | assert.Equal(t, expected, res) 171 | } 172 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= 5 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 6 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 7 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 8 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 9 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 11 | github.com/llir/ll v0.0.0-20191229032745-05be70ade156 h1:tQ3EaDUy4rHvIyvuI2Yf1X2xPMVi4orBwX3H1NSWp74= 12 | github.com/llir/ll v0.0.0-20191229032745-05be70ade156/go.mod h1:8W5HJz80PitAyPZUpOcljQxTu6LD5YKW1URTo+OjVoc= 13 | github.com/llir/llvm v0.3.0 h1:A4lHTPerMXrHYWJZF80XsnerraV8PKEqhBG+4wedSHg= 14 | github.com/llir/llvm v0.3.0/go.mod h1:yjHRZjO9xc1uLUS69qO2qYjS95XFdfd2odSdSnufS10= 15 | github.com/mewmew/float v0.0.0-20191226120903-16bbe2fdd85e h1:KCD7E/8LKwDsC5ymlEWJ3xCiSPaCywrS/psToBMOBH4= 16 | github.com/mewmew/float v0.0.0-20191226120903-16bbe2fdd85e/go.mod h1:O+xb+8ycBNHzJicFVs7GRWtruD4tVZI0huVnw5TM01E= 17 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 18 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 19 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 20 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 21 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 22 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 23 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 24 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 25 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 26 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 27 | github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 28 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 29 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 30 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 31 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 34 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 35 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 36 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 37 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 38 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 39 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 41 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4 h1:Toz2IK7k8rbltAXwNAxKcn9OzqyNfMUhUNjz3sL0NMk= 42 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 43 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 44 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 45 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 46 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 47 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 48 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 49 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 50 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 51 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 52 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 53 | -------------------------------------------------------------------------------- /cmd/tre/build/build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | 12 | "github.com/zegl/tre/compiler/compiler" 13 | "github.com/zegl/tre/compiler/lexer" 14 | "github.com/zegl/tre/compiler/parser" 15 | "github.com/zegl/tre/compiler/passes/const_iota" 16 | "github.com/zegl/tre/compiler/passes/escape" 17 | ) 18 | 19 | var debug bool 20 | 21 | func Build(path, goroot, outputBinaryPath string, setDebug bool, optimize bool) error { 22 | c := compiler.NewCompiler() 23 | debug = setDebug 24 | 25 | err := compilePackage(c, path, goroot, "main") 26 | if err != nil { 27 | return err 28 | } 29 | 30 | compiled := c.GetIR() 31 | 32 | if debug { 33 | fmt.Println(compiled) 34 | } 35 | 36 | // Get dir to save temporary dirs in 37 | tmpDir, err := ioutil.TempDir("", "tre") 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | // Write LLVM IR to disk 43 | err = ioutil.WriteFile(tmpDir+"/main.ll", []byte(compiled), 0666) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | if outputBinaryPath == "" { 49 | outputBinaryPath = "output-binary" 50 | } 51 | 52 | clangArgs := []string{ 53 | "-Wno-override-module", // Disable override target triple warnings 54 | tmpDir + "/main.ll", // Path to LLVM IR 55 | "-o", outputBinaryPath, // Output path 56 | } 57 | 58 | if optimize { 59 | clangArgs = append(clangArgs, "-O3") 60 | } 61 | 62 | // Invoke clang compiler to compile LLVM IR to a binary executable 63 | cmd := exec.Command("clang", clangArgs...) 64 | output, err := cmd.CombinedOutput() 65 | if err != nil { 66 | fmt.Println(string(output)) 67 | return err 68 | } 69 | 70 | if len(output) > 0 { 71 | fmt.Println(string(output)) 72 | return errors.New("Clang failure") 73 | } 74 | 75 | return nil 76 | } 77 | 78 | func compilePackage(c *compiler.Compiler, path, goroot, name string) error { 79 | f, err := os.Stat(path) 80 | if err != nil { 81 | return err 82 | } 83 | 84 | var parsedFiles []parser.FileNode 85 | 86 | // Parse all files in the folder 87 | if f.IsDir() { 88 | files, err := ioutil.ReadDir(path) 89 | if err != nil { 90 | panic(path + ": " + err.Error()) 91 | } 92 | 93 | for _, file := range files { 94 | if !file.IsDir() { 95 | // Tre files doesn't have to contain valid Go code, and is used to prevent issues 96 | // with some of the go tools (like vgo) 97 | if strings.HasSuffix(file.Name(), ".go") || strings.HasSuffix(file.Name(), ".tre") { 98 | parsedFiles = append(parsedFiles, parseFile(path+"/"+file.Name())) 99 | } 100 | } 101 | } 102 | } else { 103 | // Parse a single file 104 | parsedFiles = append(parsedFiles, parseFile(path)) 105 | } 106 | 107 | // Scan for ImportNodes 108 | // Use importNodes to import more packages 109 | for _, file := range parsedFiles { 110 | for _, i := range file.Instructions { 111 | if _, ok := i.(*parser.DeclarePackageNode); ok { 112 | continue 113 | } 114 | 115 | if importNode, ok := i.(*parser.ImportNode); ok { 116 | 117 | for _, packagePath := range importNode.PackagePaths { 118 | 119 | // Is built in to the compiler 120 | if packagePath == "external" { 121 | continue 122 | } 123 | 124 | searchPaths := []string{ 125 | path + "/vendor/" + packagePath, 126 | goroot + "/" + packagePath, 127 | } 128 | 129 | importSuccessful := false 130 | 131 | for _, sp := range searchPaths { 132 | fp, err := os.Stat(sp) 133 | if err != nil || !fp.IsDir() { 134 | continue 135 | } 136 | 137 | if debug { 138 | log.Printf("Loading %s from %s", packagePath, sp) 139 | } 140 | 141 | err = compilePackage(c, sp, goroot, packagePath) 142 | if err != nil { 143 | return err 144 | } 145 | 146 | importSuccessful = true 147 | } 148 | 149 | if !importSuccessful { 150 | return fmt.Errorf("Unable to import: %s", packagePath) 151 | } 152 | } 153 | 154 | continue 155 | } 156 | 157 | break 158 | } 159 | } 160 | 161 | return c.Compile(parser.PackageNode{ 162 | Files: parsedFiles, 163 | Name: name, 164 | }) 165 | } 166 | 167 | func parseFile(path string) parser.FileNode { 168 | // Read specified input file 169 | fileContents, err := ioutil.ReadFile(path) 170 | if err != nil { 171 | panic(err) 172 | } 173 | 174 | // Run input code through the lexer. A list of tokens is returned. 175 | lexed := lexer.Lex(string(fileContents)) 176 | 177 | // Run lexed source through the parser. A syntax tree is returned. 178 | parsed := parser.Parse(lexed, debug) 179 | 180 | // List of passes to run on the AST 181 | passes := []func(*parser.FileNode) *parser.FileNode{ 182 | const_iota.Iota, 183 | escape.Escape, 184 | } 185 | for _, pass := range passes { 186 | parsed = pass(parsed) 187 | } 188 | 189 | return *parsed 190 | } 191 | -------------------------------------------------------------------------------- /compiler/lexer/lexer.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type lexType uint8 9 | 10 | const ( 11 | IDENTIFIER lexType = iota 12 | KEYWORD 13 | NUMBER 14 | STRING 15 | OPERATOR 16 | EOF 17 | EOL 18 | ) 19 | 20 | type Item struct { 21 | Type lexType 22 | Val string 23 | Line int 24 | } 25 | 26 | func (i Item) String() string { 27 | var t string 28 | switch i.Type { 29 | case IDENTIFIER: 30 | t = "IDENTIFIER" 31 | case KEYWORD: 32 | t = "KEYWORD" 33 | case NUMBER: 34 | t = "NUMBER" 35 | case STRING: 36 | t = "STRING" 37 | case OPERATOR: 38 | t = "OPERATOR" 39 | case EOF: 40 | t = "EOF" 41 | case EOL: 42 | t = "EOL" 43 | } 44 | 45 | return fmt.Sprintf("{Type:%s, Val:%s, Line:%d}", t, i.Val, i.Line) 46 | } 47 | 48 | // https://golang.org/ref/spec#Operators_and_punctuation 49 | var operations = map[string]struct{}{ 50 | "+": {}, 51 | "&": {}, 52 | "+=": {}, 53 | "&=": {}, 54 | "&&": {}, 55 | "==": {}, 56 | "!=": {}, 57 | "(": {}, 58 | ")": {}, 59 | "-": {}, 60 | "|": {}, 61 | "-=": {}, 62 | "|=": {}, 63 | "||": {}, 64 | "<": {}, 65 | "<=": {}, 66 | "[": {}, 67 | "]": {}, 68 | "*": {}, 69 | "^": {}, 70 | "*=": {}, 71 | "^=": {}, 72 | "<-": {}, 73 | ">": {}, 74 | ">=": {}, 75 | "{": {}, 76 | "}": {}, 77 | "/": {}, 78 | "<<": {}, 79 | "/=": {}, 80 | "<<=": {}, 81 | "++": {}, 82 | "=": {}, 83 | ":=": {}, 84 | ",": {}, 85 | ";": {}, 86 | "%": {}, 87 | ">>": {}, 88 | "%=": {}, 89 | ">>=": {}, 90 | "--": {}, 91 | "!": {}, 92 | "...": {}, 93 | ".": {}, 94 | ":": {}, 95 | "&^": {}, 96 | "&^=": {}, 97 | 98 | // TODO: Remove 99 | "..": {}, // is not a real operation. Is there so that ... can be found. 100 | } 101 | 102 | func Lex(inputFullSource string) []Item { 103 | var res []Item 104 | 105 | for line, input := range strings.Split(inputFullSource, "\n") { 106 | if line > 0 { 107 | res = append(res, Item{Type: EOL, Line: line}) 108 | } 109 | 110 | // Lines starts at 1 111 | line = line + 1 112 | 113 | i := 0 114 | 115 | for i < len(input) { 116 | 117 | // Comment, until end of line or end of file 118 | if input[i] == '/' && input[i+1] == '/' { 119 | break 120 | } 121 | 122 | if _, ok := operations[string(input[i])]; ok { 123 | 124 | operator := string(input[i]) 125 | 126 | // Parse till end of the operator 127 | // Can be up to 3 characters long 128 | for { 129 | if len(input) == i+1 { 130 | break 131 | } 132 | 133 | checkIfOperator := operator + string(input[i+1]) 134 | if _, ok := operations[checkIfOperator]; ok { 135 | operator = checkIfOperator 136 | i++ 137 | continue 138 | } else { 139 | break 140 | } 141 | } 142 | 143 | res = append(res, Item{Type: OPERATOR, Val: operator, Line: line}) 144 | i++ 145 | continue 146 | } 147 | 148 | if input[i] == '"' { 149 | // String continues until next unescaped " 150 | var str string 151 | 152 | i++ 153 | 154 | for i < len(input) { 155 | if input[i] == '"' { 156 | break 157 | } 158 | 159 | // parse escape sequences 160 | if input[i] == '\\' { 161 | if esc, ok := escapeSequences[string(input[i])+string(input[i+1])]; ok { 162 | str += esc 163 | i += 2 164 | continue 165 | } 166 | } 167 | 168 | str += string(input[i]) 169 | i++ 170 | } 171 | 172 | i++ 173 | res = append(res, Item{Type: STRING, Val: str, Line: line}) 174 | continue 175 | } 176 | 177 | // NAME 178 | // Consists of a-z, parse until the last allowed char 179 | if (input[i] >= 'a' && input[i] <= 'z') || (input[i] >= 'A' && input[i] <= 'Z') || input[i] == '_' { 180 | name := "" 181 | 182 | for i < len(input) && ((input[i] >= 'a' && input[i] <= 'z') || 183 | (input[i] >= 'A' && input[i] <= 'Z') || 184 | (input[i] >= '0' && input[i] <= '9') || 185 | input[i] == '_') { 186 | name += string(input[i]) 187 | i++ 188 | } 189 | 190 | if _, ok := keywords[name]; ok { 191 | res = append(res, Item{Type: KEYWORD, Val: name, Line: line}) 192 | } else { 193 | res = append(res, Item{Type: IDENTIFIER, Val: name, Line: line}) 194 | } 195 | 196 | continue 197 | } 198 | 199 | // NUMBER 200 | // 0-9 201 | if input[i] >= '0' && input[i] <= '9' { 202 | val := "" 203 | for i < len(input) && input[i] >= '0' && input[i] <= '9' { 204 | val += string(input[i]) 205 | i++ 206 | } 207 | res = append(res, Item{Type: NUMBER, Val: val, Line: line}) 208 | continue 209 | } 210 | 211 | // Whitespace (ignore) 212 | if len(strings.TrimSpace(string(input[i]))) == 0 { 213 | i++ 214 | continue 215 | } 216 | 217 | panic("Unexpected char in Lexer: " + string(input[i])) 218 | } 219 | } 220 | 221 | res = append(res, Item{Type: EOL}, Item{Type: EOF}) 222 | 223 | return res 224 | } 225 | -------------------------------------------------------------------------------- /compiler/compiler/struct.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/llir/llvm/ir/constant" 7 | llvmTypes "github.com/llir/llvm/ir/types" 8 | llvmValue "github.com/llir/llvm/ir/value" 9 | 10 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 11 | "github.com/zegl/tre/compiler/compiler/name" 12 | "github.com/zegl/tre/compiler/compiler/types" 13 | "github.com/zegl/tre/compiler/compiler/value" 14 | "github.com/zegl/tre/compiler/parser" 15 | ) 16 | 17 | func (c *Compiler) compileStructLoadElementNode(v *parser.StructLoadElementNode) value.Value { 18 | src := c.compileValue(v.Struct) 19 | 20 | // Use this type, or the type behind the pointer 21 | targetType := src.Type 22 | var isPointer bool 23 | var isPointerNonAllocDereference bool 24 | if pointerType, ok := src.Type.(*types.Pointer); ok { 25 | targetType = pointerType.Type 26 | isPointerNonAllocDereference = pointerType.IsNonAllocDereference 27 | isPointer = true 28 | } 29 | 30 | if !src.IsVariable && !isPointer { 31 | // GetElementPtr only works on pointer types, and we don't have a pointer to our object. 32 | // Allocate it and use the pointer instead 33 | dst := c.contextBlock.NewAlloca(src.Type.LLVM()) 34 | c.contextBlock.NewStore(src.Value, dst) 35 | src = value.Value{ 36 | Value: dst, 37 | Type: src.Type, 38 | IsVariable: true, 39 | } 40 | } 41 | 42 | // Check if it is a struct member 43 | if structType, ok := targetType.(*types.Struct); ok { 44 | if memberIndex, ok := structType.MemberIndexes[v.ElementName]; ok { 45 | val := src.Value 46 | 47 | if isPointer && !isPointerNonAllocDereference && src.IsVariable { 48 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 49 | } 50 | 51 | retVal := c.contextBlock.NewGetElementPtr(pointer.ElemType(val), val, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, int64(memberIndex))) 52 | 53 | return value.Value{ 54 | Type: structType.Members[v.ElementName], 55 | Value: retVal, 56 | IsVariable: true, 57 | } 58 | } 59 | } 60 | 61 | // Check if it's a method 62 | if method, ok := targetType.GetMethod(v.ElementName); ok { 63 | return value.Value{ 64 | Type: method, 65 | Value: src.Value, 66 | IsVariable: false, 67 | } 68 | } 69 | 70 | // Check if it's a interface method 71 | if iface, ok := src.Type.(*types.Interface); ok { 72 | if ifaceMethod, ok := iface.RequiredMethods[v.ElementName]; ok { 73 | // Find method index 74 | // TODO: This can be much smarter 75 | var methodIndex int64 76 | for i, name := range iface.SortedRequiredMethods() { 77 | if name == v.ElementName { 78 | methodIndex = int64(i) 79 | break 80 | } 81 | } 82 | 83 | // Load jump function 84 | jumpTable := c.contextBlock.NewGetElementPtr(pointer.ElemType(src.Value), src.Value, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 2)) 85 | jumpLoad := c.contextBlock.NewLoad(pointer.ElemType(jumpTable), jumpTable) 86 | jumpFunc := c.contextBlock.NewGetElementPtr(pointer.ElemType(jumpLoad), jumpLoad, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, methodIndex)) 87 | jumpFuncLoad := c.contextBlock.NewLoad(pointer.ElemType(jumpFunc), jumpFunc) 88 | 89 | // Set jump function 90 | ifaceMethod.LlvmJumpFunction = jumpFuncLoad 91 | 92 | return value.Value{ 93 | Type: &ifaceMethod, 94 | Value: src.Value, 95 | IsVariable: false, 96 | } 97 | } 98 | } 99 | 100 | panic(fmt.Sprintf("%T internal error: no such type map indexing: %s", src, v.ElementName)) 101 | } 102 | 103 | func (c *Compiler) compileInitStructWithValues(v *parser.InitializeStructNode) value.Value { 104 | treType := c.parserTypeToType(v.Type) 105 | 106 | structType, ok := treType.(*types.Struct) 107 | if !ok { 108 | panic("Expected struct type in compileInitStructWithValues") 109 | } 110 | 111 | var alloc llvmValue.Value 112 | 113 | // Allocate on the heap or on the stack 114 | if len(c.contextAlloc) > 0 && c.contextAlloc[len(c.contextAlloc)-1].Escapes { 115 | mallocatedSpaceRaw := c.contextBlock.NewCall(c.externalFuncs.Malloc.Value.(llvmValue.Named), constant.NewInt(llvmTypes.I64, structType.Size())) 116 | alloc = c.contextBlock.NewBitCast(mallocatedSpaceRaw, llvmTypes.NewPointer(structType.LLVM())) 117 | structType.IsHeapAllocated = true 118 | } else { 119 | alloc = c.contextBlock.NewAlloca(structType.LLVM()) 120 | } 121 | 122 | treType.Zero(c.contextBlock, alloc) 123 | 124 | for key, val := range v.Items { 125 | keyIndex, ok := structType.MemberIndexes[key] 126 | if !ok { 127 | panic("Unknown struct key: " + key) 128 | } 129 | 130 | itemPtr := c.contextBlock.NewGetElementPtr(pointer.ElemType(alloc), alloc, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, int64(keyIndex))) 131 | itemPtr.SetName(name.Var(key)) 132 | 133 | compiledVal := c.compileValue(val) 134 | 135 | c.contextBlock.NewStore(compiledVal.Value, itemPtr) 136 | } 137 | 138 | return value.Value{ 139 | Type: structType, 140 | Value: alloc, 141 | IsVariable: true, 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /compiler/parser/node_types.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // TypeNode is an interface for different ways of creating new types or referring to existing ones 8 | type TypeNode interface { 9 | Node() // must also implement the Node interface 10 | Type() string 11 | String() string 12 | Variadic() bool 13 | SetName(string) 14 | GetName() string 15 | } 16 | 17 | // SingleTypeNode refers to an existing type. Such as "string". 18 | type SingleTypeNode struct { 19 | baseNode 20 | 21 | PackageName string 22 | SourceName string 23 | TypeName string 24 | IsVariadic bool 25 | } 26 | 27 | func (stn SingleTypeNode) Type() string { 28 | return stn.TypeName 29 | } 30 | 31 | func (stn SingleTypeNode) String() string { 32 | return fmt.Sprintf("type(%s.%s)", stn.PackageName, stn.Type()) 33 | } 34 | 35 | func (stn SingleTypeNode) Variadic() bool { 36 | return stn.IsVariadic 37 | } 38 | 39 | func (stn SingleTypeNode) SetName(name string) { 40 | stn.SourceName = name 41 | } 42 | 43 | func (stn SingleTypeNode) GetName() string { 44 | return stn.SourceName 45 | } 46 | 47 | // StructTypeNode refers to a struct type 48 | type StructTypeNode struct { 49 | baseNode 50 | 51 | SourceName string 52 | Types []TypeNode 53 | Names map[string]int 54 | IsVariadic bool 55 | } 56 | 57 | func (stn StructTypeNode) Type() string { 58 | return fmt.Sprintf("%+v", stn.Types) 59 | } 60 | 61 | func (stn StructTypeNode) String() string { 62 | return fmt.Sprintf("StructTypeNode(%+v)", stn.Types) 63 | } 64 | 65 | func (stn StructTypeNode) Variadic() bool { 66 | return stn.IsVariadic 67 | } 68 | 69 | func (stn StructTypeNode) SetName(name string) { 70 | stn.SourceName = name 71 | } 72 | 73 | func (stn StructTypeNode) GetName() string { 74 | return stn.SourceName 75 | } 76 | 77 | // ArrayTypeNode refers to an array 78 | type ArrayTypeNode struct { 79 | baseNode 80 | 81 | SourceName string 82 | ItemType TypeNode 83 | Len int64 84 | IsVariadic bool 85 | } 86 | 87 | func (atn ArrayTypeNode) Type() string { 88 | return fmt.Sprintf("[%d]%+v", atn.Len, atn.ItemType) 89 | } 90 | 91 | func (atn ArrayTypeNode) String() string { 92 | return atn.Type() 93 | } 94 | 95 | func (atn ArrayTypeNode) Variadic() bool { 96 | return atn.IsVariadic 97 | } 98 | 99 | func (atn ArrayTypeNode) SetName(name string) { 100 | atn.SourceName = name 101 | } 102 | 103 | func (atn ArrayTypeNode) GetName() string { 104 | return atn.SourceName 105 | } 106 | 107 | type SliceTypeNode struct { 108 | baseNode 109 | 110 | SourceName string 111 | ItemType TypeNode 112 | IsVariadic bool 113 | } 114 | 115 | func (stn SliceTypeNode) Type() string { 116 | return fmt.Sprintf("[]%+v", stn.ItemType) 117 | } 118 | 119 | func (stn SliceTypeNode) String() string { 120 | return stn.Type() 121 | } 122 | 123 | func (stn SliceTypeNode) Variadic() bool { 124 | return stn.IsVariadic 125 | } 126 | 127 | func (stn SliceTypeNode) SetName(name string) { 128 | stn.SourceName = name 129 | } 130 | 131 | func (stn SliceTypeNode) GetName() string { 132 | return stn.SourceName 133 | } 134 | 135 | type InterfaceTypeNode struct { 136 | baseNode 137 | 138 | SourceName string 139 | Methods map[string]InterfaceMethod 140 | IsVariadic bool 141 | } 142 | 143 | func (itn InterfaceTypeNode) Type() string { 144 | return fmt.Sprintf("interface{%+v}", itn.Methods) 145 | } 146 | 147 | func (itn InterfaceTypeNode) String() string { 148 | return itn.Type() 149 | } 150 | 151 | func (itn InterfaceTypeNode) Variadic() bool { 152 | return itn.IsVariadic 153 | } 154 | 155 | func (itn InterfaceTypeNode) SetName(name string) { 156 | itn.SourceName = name 157 | } 158 | 159 | func (itn InterfaceTypeNode) GetName() string { 160 | return itn.SourceName 161 | } 162 | 163 | type InterfaceMethod struct { 164 | ArgumentTypes []TypeNode 165 | ReturnTypes []TypeNode 166 | } 167 | 168 | type PointerTypeNode struct { 169 | baseNode 170 | 171 | SourceName string 172 | IsVariadic bool 173 | ValueType TypeNode 174 | } 175 | 176 | func (ptn PointerTypeNode) Type() string { 177 | return fmt.Sprintf("pointer(%+v)", ptn.ValueType.Type()) 178 | } 179 | 180 | func (ptn PointerTypeNode) String() string { 181 | return ptn.Type() 182 | } 183 | 184 | func (ptn PointerTypeNode) SetName(name string) { 185 | ptn.SourceName = name 186 | } 187 | 188 | func (ptn PointerTypeNode) GetName() string { 189 | return ptn.SourceName 190 | } 191 | 192 | func (ptn PointerTypeNode) Variadic() bool { 193 | return ptn.IsVariadic 194 | } 195 | 196 | type FuncTypeNode struct { 197 | baseNode 198 | 199 | ArgTypes []TypeNode 200 | RetTypes []TypeNode 201 | 202 | SourceName string 203 | IsVariadic bool 204 | } 205 | 206 | func (ftn FuncTypeNode) Type() string { 207 | return fmt.Sprintf("func(%+v)(%+v)", ftn.ArgTypes, ftn.RetTypes) 208 | } 209 | 210 | func (ftn FuncTypeNode) String() string { 211 | return ftn.Type() 212 | } 213 | 214 | func (ftn FuncTypeNode) SetName(name string) { 215 | ftn.SourceName = name 216 | } 217 | 218 | func (ftn FuncTypeNode) GetName() string { 219 | return ftn.SourceName 220 | } 221 | 222 | func (ftn FuncTypeNode) Variadic() bool { 223 | return ftn.IsVariadic 224 | } 225 | -------------------------------------------------------------------------------- /compiler/compiler/for.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zegl/tre/compiler/compiler/name" 6 | "github.com/zegl/tre/compiler/parser" 7 | ) 8 | 9 | func (c *Compiler) compileForNode(v *parser.ForNode) { 10 | if v.IsThreeTypeFor { 11 | c.compileForThreeType(v) 12 | return 13 | } 14 | 15 | c.compileForRange(v) 16 | } 17 | 18 | func (c *Compiler) compileForThreeType(v *parser.ForNode) { 19 | // TODO: create a new context-block for code running inside the for loop 20 | c.compile([]parser.Node{ 21 | v.BeforeLoop, 22 | }) 23 | 24 | // Check condition block 25 | checkCondBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-cond") 26 | 27 | // Loop body block 28 | loopBodyBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-body") 29 | loopAfterBodyBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after-body") 30 | 31 | // After loop block 32 | afterLoopBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after") 33 | 34 | // Push the break and continue stacks 35 | c.contextLoopBreak = append(c.contextLoopBreak, afterLoopBlock) 36 | c.contextLoopContinue = append(c.contextLoopContinue, loopAfterBodyBlock) 37 | 38 | // Jump from BeforeLoop to checkCond 39 | c.contextBlock.NewBr(checkCondBlock) 40 | 41 | // Compile condition block 42 | c.contextBlock = checkCondBlock 43 | cmp := c.compileOperatorNode(v.Condition) 44 | c.contextBlock.NewCondBr(cmp.Value, loopBodyBlock, afterLoopBlock) 45 | 46 | // Compiler loop body 47 | c.contextBlock = loopBodyBlock 48 | c.compile(v.Block) 49 | c.contextBlock.NewBr(loopAfterBodyBlock) // Jump to after body 50 | 51 | // After body block 52 | c.contextBlock = loopAfterBodyBlock 53 | c.compile([]parser.Node{v.AfterIteration}) 54 | c.contextBlock.NewBr(checkCondBlock) 55 | 56 | // Set context to the new block after the loop 57 | c.contextBlock = afterLoopBlock 58 | 59 | // Pop break and continue 60 | c.contextLoopBreak = c.contextLoopBreak[0 : len(c.contextLoopBreak)-1] 61 | c.contextLoopContinue = c.contextLoopContinue[0 : len(c.contextLoopContinue)-1] 62 | } 63 | 64 | func (c *Compiler) compileForRange(v *parser.ForNode) { 65 | // A for range that iterates over a slice is just syntactic sugar 66 | // for k, v := range a 67 | // for k := 0; k < len(a); k++ { v := a[k] } 68 | var modifiedBlock []parser.Node 69 | 70 | // The node/value that we're iterating over 71 | var rangeItem parser.Node 72 | 73 | if forAlloc, ok := v.BeforeLoop.(*parser.AllocNode); ok { 74 | forAllocRange := forAlloc.Val[0].(*parser.RangeNode) 75 | rangeItem = forAllocRange.Item 76 | } else if forRange, ok := v.BeforeLoop.(*parser.RangeNode); ok { 77 | rangeItem = forRange.Item 78 | } else { 79 | panic("unexpected for/range beforeLoop type: " + fmt.Sprintf("%T %+v", v.BeforeLoop, v.BeforeLoop)) 80 | } 81 | 82 | // Alloc the value of rangeItem and save it in a variable 83 | rangeItemName := name.Var("range-item") 84 | c.compileAllocNode(&parser.AllocNode{ 85 | Name: []string{rangeItemName}, 86 | Val: []parser.Node{rangeItem}, 87 | }) 88 | 89 | // Call and alloc len() and save it in a variable 90 | forItemLenName := name.Var("range-item-len") 91 | c.compileAllocNode(&parser.AllocNode{ 92 | Name: []string{forItemLenName}, 93 | Val: []parser.Node{&parser.CallNode{ 94 | Function: &parser.NameNode{Name: "len"}, 95 | Arguments: []parser.Node{&parser.NameNode{Name: rangeItemName}}, 96 | }}, 97 | }) 98 | 99 | forKeyName := name.Var("for-key") 100 | 101 | // Ranges that use the key and value 102 | if forAlloc, ok := v.BeforeLoop.(*parser.AllocNode); ok { 103 | keyName := forAlloc.Name[0] 104 | 105 | // Assignment of key 106 | modifiedBlock = append(modifiedBlock, &parser.AllocNode{ 107 | Name: []string{keyName}, 108 | Val: []parser.Node{&parser.NameNode{Name: forKeyName}}, 109 | }) 110 | 111 | // Assignment of value 112 | 113 | if len(forAlloc.Name) >= 2 { 114 | modifiedBlock = append(modifiedBlock, &parser.AllocNode{ 115 | Name: []string{forAlloc.Name[1]}, 116 | Val: []parser.Node{&parser.LoadArrayElement{Array: &parser.NameNode{Name: rangeItemName}, Pos: &parser.NameNode{Name: forKeyName}}}, 117 | }) 118 | } 119 | } 120 | modifiedBlock = append(modifiedBlock, v.Block...) 121 | 122 | typeCastedKey := &parser.TypeCastNode{ 123 | Type: &parser.SingleTypeNode{SourceName: "int32", TypeName: "int32"}, 124 | Val: &parser.NameNode{Name: forKeyName}, 125 | } 126 | 127 | c.compileForThreeType(&parser.ForNode{ 128 | BeforeLoop: &parser.AllocNode{Name: []string{forKeyName}, Val: []parser.Node{&parser.ConstantNode{Type: parser.NUMBER, Value: 0}}}, 129 | 130 | Condition: &parser.OperatorNode{ 131 | Left: typeCastedKey, 132 | Operator: parser.OP_LT, 133 | Right: &parser.NameNode{ 134 | Name: forItemLenName, 135 | }, 136 | }, 137 | 138 | AfterIteration: &parser.AssignNode{ 139 | Target: []parser.Node{&parser.NameNode{Name: forKeyName}}, 140 | Val: []parser.Node{&parser.OperatorNode{ 141 | Left: &parser.NameNode{Name: forKeyName}, 142 | Operator: parser.OP_ADD, 143 | Right: &parser.ConstantNode{Type: parser.NUMBER, Value: 1}, 144 | }}, 145 | }, 146 | 147 | Block: modifiedBlock, 148 | }) 149 | } 150 | 151 | func (c *Compiler) compileBreakNode(v *parser.BreakNode) { 152 | c.contextBlock.NewBr(c.contextLoopBreak[len(c.contextLoopBreak)-1]) 153 | } 154 | 155 | func (c *Compiler) compileContinueNode(v *parser.ContinueNode) { 156 | c.contextBlock.NewBr(c.contextLoopContinue[len(c.contextLoopContinue)-1]) 157 | } 158 | -------------------------------------------------------------------------------- /compiler/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/zegl/tre/compiler/lexer" 9 | ) 10 | 11 | func TestCall(t *testing.T) { 12 | input := []lexer.Item{ 13 | {Type: lexer.IDENTIFIER, Val: "printf"}, 14 | {Type: lexer.OPERATOR, Val: "("}, 15 | {Type: lexer.NUMBER, Val: "1"}, 16 | {Type: lexer.OPERATOR, Val: ")"}, 17 | {Type: lexer.EOF, Val: ""}, 18 | } 19 | 20 | expected := &FileNode{ 21 | Instructions: []Node{ 22 | &CallNode{ 23 | Function: &NameNode{Name: "printf"}, 24 | Arguments: []Node{&ConstantNode{Type: NUMBER, Value: 1}}, 25 | }, 26 | }, 27 | } 28 | 29 | assert.Equal(t, expected, Parse(input, false)) 30 | } 31 | 32 | func TestAdd(t *testing.T) { 33 | input := []lexer.Item{ 34 | {Type: lexer.NUMBER, Val: "1"}, 35 | {Type: lexer.OPERATOR, Val: "+"}, 36 | {Type: lexer.NUMBER, Val: "2"}, 37 | {Type: lexer.EOF, Val: ""}, 38 | } 39 | 40 | expected := &FileNode{ 41 | Instructions: []Node{ 42 | &OperatorNode{ 43 | Operator: OP_ADD, 44 | Left: &ConstantNode{ 45 | Type: NUMBER, 46 | Value: 1, 47 | }, 48 | Right: &ConstantNode{ 49 | Type: NUMBER, 50 | Value: 2, 51 | }, 52 | }, 53 | }, 54 | } 55 | 56 | assert.Equal(t, expected, Parse(input, false)) 57 | } 58 | 59 | func TestInfixPriority(t *testing.T) { 60 | input := []lexer.Item{ 61 | {Type: lexer.NUMBER, Val: "1"}, 62 | {Type: lexer.OPERATOR, Val: "+"}, 63 | {Type: lexer.NUMBER, Val: "2"}, 64 | {Type: lexer.OPERATOR, Val: "*"}, 65 | {Type: lexer.NUMBER, Val: "3"}, 66 | {Type: lexer.EOF, Val: ""}, 67 | } 68 | 69 | expected := &FileNode{ 70 | Instructions: []Node{ 71 | &OperatorNode{ 72 | Operator: OP_ADD, 73 | Left: &ConstantNode{ 74 | Type: NUMBER, 75 | Value: 1, 76 | }, 77 | Right: &OperatorNode{ 78 | Operator: OP_MUL, 79 | Left: &ConstantNode{ 80 | Type: NUMBER, 81 | Value: 2, 82 | }, 83 | Right: &ConstantNode{ 84 | Type: NUMBER, 85 | Value: 3, 86 | }, 87 | }, 88 | }, 89 | }, 90 | } 91 | 92 | assert.Equal(t, expected, Parse(input, false)) 93 | } 94 | 95 | func TestInfixPriority2(t *testing.T) { 96 | input := []lexer.Item{ 97 | {Type: lexer.NUMBER, Val: "1"}, 98 | {Type: lexer.OPERATOR, Val: "*"}, 99 | {Type: lexer.NUMBER, Val: "2"}, 100 | {Type: lexer.OPERATOR, Val: "+"}, 101 | {Type: lexer.NUMBER, Val: "3"}, 102 | {Type: lexer.EOF, Val: ""}, 103 | } 104 | 105 | expected := &FileNode{ 106 | Instructions: []Node{ 107 | &OperatorNode{ 108 | Operator: OP_ADD, 109 | Left: &OperatorNode{ 110 | Operator: OP_MUL, 111 | Left: &ConstantNode{ 112 | Type: NUMBER, 113 | Value: 1, 114 | }, 115 | Right: &ConstantNode{ 116 | Type: NUMBER, 117 | Value: 2, 118 | }, 119 | }, 120 | Right: &ConstantNode{ 121 | Type: NUMBER, 122 | Value: 3, 123 | }, 124 | }, 125 | }, 126 | } 127 | 128 | assert.Equal(t, expected, Parse(input, false)) 129 | } 130 | 131 | func TestInfixPriority3(t *testing.T) { 132 | input := []lexer.Item{ 133 | {Type: lexer.NUMBER, Val: "1"}, 134 | {Type: lexer.OPERATOR, Val: "*"}, 135 | {Type: lexer.NUMBER, Val: "2"}, 136 | {Type: lexer.OPERATOR, Val: "+"}, 137 | {Type: lexer.NUMBER, Val: "3"}, 138 | {Type: lexer.OPERATOR, Val: "*"}, 139 | {Type: lexer.NUMBER, Val: "4"}, 140 | {Type: lexer.EOF, Val: ""}, 141 | } 142 | 143 | expected := &FileNode{ 144 | Instructions: []Node{ 145 | &OperatorNode{ 146 | Operator: OP_ADD, 147 | Left: &OperatorNode{ 148 | Operator: OP_MUL, 149 | Left: &ConstantNode{ 150 | Type: NUMBER, 151 | Value: 1, 152 | }, 153 | Right: &ConstantNode{ 154 | Type: NUMBER, 155 | Value: 2, 156 | }, 157 | }, 158 | Right: &OperatorNode{ 159 | Operator: OP_MUL, 160 | Left: &ConstantNode{ 161 | Type: NUMBER, 162 | Value: 3, 163 | }, 164 | Right: &ConstantNode{ 165 | Type: NUMBER, 166 | Value: 4, 167 | }, 168 | }, 169 | }, 170 | }, 171 | } 172 | 173 | assert.Equal(t, expected, Parse(input, false)) 174 | } 175 | 176 | func TestInfixPriority4(t *testing.T) { 177 | input := []lexer.Item{ 178 | {Type: lexer.NUMBER, Val: "100"}, 179 | {Type: lexer.OPERATOR, Val: "/"}, 180 | {Type: lexer.NUMBER, Val: "3"}, 181 | {Type: lexer.OPERATOR, Val: "/"}, 182 | {Type: lexer.NUMBER, Val: "4"}, 183 | {Type: lexer.OPERATOR, Val: "*"}, 184 | {Type: lexer.NUMBER, Val: "7"}, 185 | {Type: lexer.EOF, Val: ""}, 186 | } 187 | 188 | /* 189 | OP(OP(OP(100/300)/4) * 7) 190 | 191 | */ 192 | 193 | expected := &FileNode{ 194 | Instructions: []Node{ 195 | &OperatorNode{ 196 | Operator: OP_MUL, 197 | 198 | Left: &OperatorNode{ 199 | Operator: OP_DIV, 200 | Left: &OperatorNode{ 201 | Operator: OP_DIV, 202 | Left: &ConstantNode{Type: NUMBER, Value: 100}, 203 | Right: &ConstantNode{Type: NUMBER, Value: 3}, 204 | }, 205 | Right: &ConstantNode{Type: NUMBER, Value: 4}, 206 | }, 207 | 208 | Right: &ConstantNode{Type: NUMBER, Value: 7}, 209 | }, 210 | }, 211 | } 212 | 213 | assert.Equal(t, expected, Parse(input, false)) 214 | } 215 | 216 | func TestInfixPriority4Load(t *testing.T) { 217 | input := []lexer.Item{ 218 | {Type: lexer.NUMBER, Val: "100"}, 219 | {Type: lexer.OPERATOR, Val: "/"}, 220 | {Type: lexer.IDENTIFIER, Val: "f"}, 221 | {Type: lexer.OPERATOR, Val: "."}, 222 | {Type: lexer.IDENTIFIER, Val: "a"}, 223 | {Type: lexer.OPERATOR, Val: "/"}, 224 | {Type: lexer.NUMBER, Val: "4"}, 225 | {Type: lexer.OPERATOR, Val: "*"}, 226 | {Type: lexer.NUMBER, Val: "7"}, 227 | {Type: lexer.EOF, Val: ""}, 228 | } 229 | 230 | /* 231 | OP(OP(OP(100/f.a)/4) * 7) 232 | */ 233 | 234 | expected := &FileNode{ 235 | Instructions: []Node{ 236 | &OperatorNode{ 237 | Operator: OP_MUL, 238 | 239 | Left: &OperatorNode{ 240 | Operator: OP_DIV, 241 | Left: &OperatorNode{ 242 | Operator: OP_DIV, 243 | Left: &ConstantNode{Type: NUMBER, Value: 100}, 244 | Right: &StructLoadElementNode{ 245 | Struct: &NameNode{Name: "f"}, 246 | ElementName: "a", 247 | }, 248 | }, 249 | Right: &ConstantNode{Type: NUMBER, Value: 4}, 250 | }, 251 | 252 | Right: &ConstantNode{Type: NUMBER, Value: 7}, 253 | }, 254 | }, 255 | } 256 | 257 | assert.Equal(t, expected, Parse(input, false)) 258 | } 259 | -------------------------------------------------------------------------------- /compiler/compiler/array.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | "github.com/llir/llvm/ir/constant" 6 | "github.com/llir/llvm/ir/enum" 7 | llvmTypes "github.com/llir/llvm/ir/types" 8 | llvmValue "github.com/llir/llvm/ir/value" 9 | 10 | "github.com/zegl/tre/compiler/compiler/internal" 11 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 12 | "github.com/zegl/tre/compiler/compiler/name" 13 | "github.com/zegl/tre/compiler/compiler/types" 14 | "github.com/zegl/tre/compiler/compiler/value" 15 | "github.com/zegl/tre/compiler/parser" 16 | ) 17 | 18 | func (c *Compiler) compileInitializeArrayNode(v *parser.InitializeArrayNode) value.Value { 19 | itemType := c.parserTypeToType(v.Type) 20 | 21 | var values []value.Value 22 | 23 | // Add items 24 | for _, val := range v.Items { 25 | // Push assigng type stack 26 | c.contextAssignDest = append(c.contextAssignDest, value.Value{Type: itemType}) 27 | 28 | values = append(values, c.compileValue(val)) 29 | 30 | // Pop assigng type stack 31 | c.contextAssignDest = c.contextAssignDest[0 : len(c.contextAssignDest)-1] 32 | } 33 | 34 | return c.compileInitializeArrayWithValues(uint64(v.Size), itemType, values...) 35 | } 36 | 37 | func (c *Compiler) compileInitializeArrayWithValues(len uint64, itemType types.Type, values ...value.Value) value.Value { 38 | arrayType := &types.Array{ 39 | Type: itemType, 40 | Len: len, 41 | LlvmType: llvmTypes.NewArray(len, itemType.LLVM()), 42 | } 43 | 44 | allocArray := c.contextBlock.NewAlloca(arrayType.LLVM()) 45 | arrayType.Zero(c.contextBlock, allocArray) 46 | 47 | for i, val := range values { 48 | dst := c.contextBlock.NewGetElementPtr(pointer.ElemType(allocArray), allocArray, constant.NewInt(llvmTypes.I64, 0), constant.NewInt(llvmTypes.I64, int64(i))) 49 | dst.SetName(name.Var("init-arr-value")) 50 | c.contextBlock.NewStore( 51 | val.Value, 52 | dst) 53 | } 54 | 55 | return value.Value{ 56 | Value: allocArray, 57 | Type: arrayType, 58 | IsVariable: true, 59 | } 60 | } 61 | 62 | func (c *Compiler) compileLoadArrayElement(v *parser.LoadArrayElement) value.Value { 63 | arr := c.compileValue(v.Array) 64 | arrayValue := arr.Value 65 | 66 | index := c.compileValue(v.Pos) 67 | indexVal := internal.LoadIfVariable(c.contextBlock, index) 68 | 69 | var runtimeLength llvmValue.Value 70 | var compileTimeLength uint64 71 | lengthKnownAtCompileTime := false 72 | lengthKnownAtRunTime := false 73 | isLlvmArrayBased := false 74 | var retType types.Type 75 | 76 | // Length of array 77 | if arr, ok := arr.Type.(*types.Array); ok { 78 | if arrayType, ok := arr.LlvmType.(*llvmTypes.ArrayType); ok { 79 | compileTimeLength = arrayType.Len 80 | lengthKnownAtCompileTime = true 81 | retType = arr.Type 82 | isLlvmArrayBased = true 83 | } 84 | } 85 | 86 | // Length of slice 87 | if slice, ok := arr.Type.(*types.Slice); ok { 88 | lengthKnownAtCompileTime = false 89 | lengthKnownAtRunTime = true 90 | 91 | // When the slice is a slice window into an array 92 | var isSliceOfArray bool 93 | 94 | retType = slice.Type 95 | 96 | arrayValue = c.contextBlock.NewLoad(pointer.ElemType(arrayValue), arrayValue) 97 | 98 | // One more load is needed when the slice is a window into a LLVM array 99 | if _, ok := arrayValue.Type().(*llvmTypes.PointerType); ok { 100 | isSliceOfArray = true 101 | } 102 | 103 | if isSliceOfArray { 104 | arrayValue = c.contextBlock.NewLoad(pointer.ElemType(arrayValue), arrayValue) 105 | } 106 | 107 | indexVal = c.contextBlock.NewTrunc(indexVal, i32.LLVM()) 108 | 109 | sliceValue := arrayValue 110 | 111 | // Length of the slice 112 | runtimeLength = c.contextBlock.NewExtractValue(sliceValue, 0) 113 | 114 | // Add offset to indexVal 115 | backingArrayOffset := c.contextBlock.NewExtractValue(sliceValue, 2) 116 | indexVal = c.contextBlock.NewAdd(indexVal, backingArrayOffset) 117 | 118 | // Add offset to runtimeLength 119 | runtimeLength = c.contextBlock.NewAdd(runtimeLength, backingArrayOffset) 120 | 121 | // Backing array 122 | arrayValue = c.contextBlock.NewExtractValue(sliceValue, 3) 123 | } 124 | 125 | // Length of string 126 | if !lengthKnownAtCompileTime { 127 | // Get backing array from string type 128 | if arr.Type.Name() == "string" { 129 | if arr.IsVariable { 130 | arrayValue = c.contextBlock.NewLoad(pointer.ElemType(arrayValue), arrayValue) 131 | } 132 | 133 | runtimeLength = c.contextBlock.NewExtractValue(arrayValue, 0) 134 | // Get backing array 135 | arrayValue = c.contextBlock.NewExtractValue(arrayValue, 1) 136 | lengthKnownAtRunTime = true 137 | retType = types.I8 138 | isLlvmArrayBased = false 139 | } 140 | } 141 | 142 | if !lengthKnownAtCompileTime && !lengthKnownAtRunTime { 143 | panic("unable to LoadArrayElement: could not calculate max length") 144 | } 145 | 146 | isCheckedAtCompileTime := false 147 | 148 | if lengthKnownAtCompileTime { 149 | if compileTimeLength < 0 { 150 | compilePanic("index out of range") 151 | } 152 | 153 | if intType, ok := index.Value.(*constant.Int); ok { 154 | if intType.X.IsInt64() { 155 | isCheckedAtCompileTime = true 156 | 157 | if intType.X.Uint64() > compileTimeLength { 158 | compilePanic("index out of range") 159 | } 160 | } 161 | } 162 | } 163 | 164 | if !isCheckedAtCompileTime { 165 | outsideOfLengthBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-array-index-out-of-range") 166 | c.panic(outsideOfLengthBlock, "index out of range") 167 | outsideOfLengthBlock.NewUnreachable() 168 | 169 | safeBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after-array-index-check") 170 | 171 | var runtimeOrCompiletimeCmp *ir.InstICmp 172 | if lengthKnownAtCompileTime { 173 | runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, constant.NewInt(llvmTypes.I32, int64(compileTimeLength))) 174 | } else { 175 | runtimeOrCompiletimeCmp = c.contextBlock.NewICmp(enum.IPredSGE, indexVal, runtimeLength) 176 | } 177 | 178 | outOfRangeCmp := c.contextBlock.NewOr( 179 | c.contextBlock.NewICmp(enum.IPredSLT, indexVal, constant.NewInt(llvmTypes.I64, 0)), 180 | runtimeOrCompiletimeCmp, 181 | ) 182 | 183 | c.contextBlock.NewCondBr(outOfRangeCmp, outsideOfLengthBlock, safeBlock) 184 | 185 | c.contextBlock = safeBlock 186 | } 187 | 188 | var indicies []llvmValue.Value 189 | if isLlvmArrayBased { 190 | indicies = append(indicies, constant.NewInt(llvmTypes.I64, 0)) 191 | } 192 | indicies = append(indicies, indexVal) 193 | 194 | return value.Value{ 195 | Value: c.contextBlock.NewGetElementPtr(pointer.ElemType(arrayValue), arrayValue, indicies...), 196 | Type: retType, 197 | IsVariable: true, 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /compiler/compiler/alloc.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/llir/llvm/ir" 5 | "github.com/llir/llvm/ir/constant" 6 | irTypes "github.com/llir/llvm/ir/types" 7 | llvmValue "github.com/llir/llvm/ir/value" 8 | 9 | "github.com/zegl/tre/compiler/compiler/internal" 10 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 11 | "github.com/zegl/tre/compiler/compiler/name" 12 | "github.com/zegl/tre/compiler/compiler/types" 13 | "github.com/zegl/tre/compiler/compiler/value" 14 | "github.com/zegl/tre/compiler/parser" 15 | ) 16 | 17 | func (c *Compiler) compileAllocNode(v *parser.AllocNode) { 18 | // Push and pop alloc stack 19 | c.contextAlloc = append(c.contextAlloc, v) 20 | defer func() { 21 | c.contextAlloc = c.contextAlloc[0 : len(c.contextAlloc)-1] 22 | }() 23 | 24 | if v.IsConst { 25 | c.compileAllocConstNode(v) 26 | return 27 | } 28 | 29 | // Allocate from type 30 | if len(v.Val) == 0 && v.Type != nil { 31 | treType := c.parserTypeToType(v.Type) 32 | 33 | var val llvmValue.Value 34 | var block *ir.Block 35 | 36 | // Package level variables 37 | if c.contextBlock == nil { 38 | globType := treType.LLVM() 39 | glob := c.module.NewGlobal(name.Var(v.Name[0]), globType) 40 | glob.Init = constant.NewZeroInitializer(globType) 41 | val = glob 42 | block = c.initGlobalsFunc.Blocks[0] 43 | 44 | c.currentPackage.DefinePkgVar(v.Name[0], value.Value{ 45 | Value: glob, 46 | Type: treType, 47 | IsVariable: true, 48 | }) 49 | } else { 50 | alloc := c.contextBlock.NewAlloca(treType.LLVM()) 51 | alloc.SetName(name.Var(v.Name[0])) 52 | val = alloc 53 | block = c.contextBlock 54 | 55 | c.setVar(v.Name[0], value.Value{ 56 | Value: alloc, 57 | Type: treType, 58 | IsVariable: true, 59 | }) 60 | } 61 | 62 | // Set to zero values 63 | // TODO: Make slices less special 64 | if sliceType, ok := treType.(*types.Slice); ok { 65 | sliceType.SliceZero(block, c.externalFuncs.Malloc.Value.(llvmValue.Named), 2, val) 66 | } else { 67 | treType.Zero(block, val) 68 | } 69 | 70 | return 71 | } 72 | 73 | for valIndex, valNode := range v.Val { 74 | // When allocating a package var (no context block set), 75 | // temporarily use the initGlobalFunc as the context block. 76 | // The context block is reset below after the initialization is done. 77 | allocPackageVar := c.contextBlock == nil 78 | if allocPackageVar { 79 | c.contextBlock = c.initGlobalsFunc.Blocks[0] 80 | } 81 | 82 | // Allocate from value 83 | val := c.compileValue(valNode) 84 | 85 | if _, ok := val.Type.(*types.MultiValue); ok { 86 | if len(v.Name) != len(val.MultiValues) { 87 | panic("Variable count on left and right side does not match") 88 | } 89 | if c.contextBlock == nil { 90 | panic("Multi alloc pkg vars is not yet supported") 91 | } 92 | 93 | // Is currently expecting that the variables are already allocated in this block. 94 | // Will only add the vars to the map of variables 95 | for i, multiVal := range val.MultiValues { 96 | c.setVar(v.Name[i], multiVal) 97 | } 98 | 99 | return 100 | } 101 | 102 | // Single variable allocation 103 | llvmVal := val.Value 104 | 105 | // Non-allocation needed pointers 106 | if ptrVal, ok := val.Type.(*types.Pointer); ok && ptrVal.IsNonAllocDereference { 107 | c.setVar(v.Name[valIndex], value.Value{ 108 | Type: val.Type, 109 | Value: llvmVal, 110 | IsVariable: false, 111 | }) 112 | return 113 | } 114 | 115 | // Non-allocation needed structs 116 | if structVal, ok := val.Type.(*types.Struct); ok && structVal.IsHeapAllocated { 117 | c.setVar(v.Name[valIndex], value.Value{ 118 | Type: val.Type, 119 | Value: llvmVal, 120 | IsVariable: true, 121 | }) 122 | return 123 | } 124 | 125 | if val.IsVariable { 126 | llvmVal = c.contextBlock.NewLoad(pointer.ElemType(llvmVal), llvmVal) 127 | } 128 | 129 | var allVal llvmValue.Value 130 | if allocPackageVar { 131 | glob := c.module.NewGlobal(name.Var(v.Name[0]), llvmVal.Type()) 132 | glob.Init = constant.NewZeroInitializer(llvmVal.Type()) 133 | allVal = glob 134 | } else { 135 | alloc := c.contextBlock.NewAlloca(llvmVal.Type()) 136 | alloc.SetName(name.Var(v.Name[valIndex])) 137 | allVal = alloc 138 | } 139 | 140 | c.contextBlock.NewStore(llvmVal, allVal) 141 | 142 | allocVal := value.Value{ 143 | Type: val.Type, 144 | Value: allVal, 145 | IsVariable: true, 146 | } 147 | 148 | if allocPackageVar { 149 | c.contextBlock = nil 150 | c.currentPackage.DefinePkgVar(v.Name[valIndex], allocVal) 151 | } else { 152 | c.setVar(v.Name[valIndex], allocVal) 153 | } 154 | } 155 | 156 | return 157 | } 158 | 159 | func (c *Compiler) compileAllocConstNode(v *parser.AllocNode) { 160 | for i, varName := range v.Name { 161 | cnst := v.Val[i].(*parser.ConstantNode) 162 | c.setVar(varName, value.Value{ 163 | Type: &types.UntypedConstantNumber{}, 164 | Value: constant.NewInt(i64.LLVM().(*irTypes.IntType), cnst.Value), 165 | }) 166 | } 167 | } 168 | 169 | func (c *Compiler) compileAssignNode(v *parser.AssignNode) { 170 | tmpStores := make([]llvmValue.Value, len(v.Target)) 171 | realTargets := make([]value.Value, len(v.Target)) 172 | 173 | // Skip temporary variables if we're assigning to one single var 174 | if len(v.Target) == 1 { 175 | // Assignment to _, do nothing. 176 | if nameNode, ok := v.Target[0].(*parser.NameNode); ok && nameNode.Name == "_" { 177 | return 178 | } 179 | 180 | dst := c.compileValue(v.Target[0]) 181 | if !dst.IsVariable { 182 | compilePanic("Can only assign to variable") 183 | } 184 | s := c.compileSingleAssign(dst.Type, dst, v.Val[0]) 185 | c.contextBlock.NewStore(s, dst.Value) 186 | return 187 | } 188 | 189 | for i := range v.Target { 190 | target := v.Target[i] 191 | 192 | // Assignment to _, do nothing. 193 | if nameNode, ok := target.(*parser.NameNode); ok && nameNode.Name == "_" { 194 | return 195 | } 196 | 197 | dst := c.compileValue(target) 198 | 199 | // Allocate a temporary storage 200 | llvmType := dst.Value.Type() 201 | 202 | if dst.IsVariable { 203 | p := llvmType.(*irTypes.PointerType) 204 | llvmType = p.ElemType 205 | } 206 | 207 | singleAssignVal := c.compileSingleAssign(dst.Type, dst, v.Val[i]) 208 | 209 | tmpStore := c.contextBlock.NewAlloca(llvmType) 210 | c.contextBlock.NewStore(singleAssignVal, tmpStore) 211 | tmpStores[i] = tmpStore 212 | realTargets[i] = dst 213 | } 214 | 215 | for i := range v.Target { 216 | x := c.contextBlock.NewLoad(pointer.ElemType(tmpStores[i]), tmpStores[i]) 217 | c.contextBlock.NewStore(x, realTargets[i].Value) 218 | } 219 | } 220 | 221 | func (c *Compiler) compileSingleAssign(temporaryDst types.Type, realDst value.Value, val parser.Node) llvmValue.Value { 222 | // Push assign type stack 223 | // Can be used later when evaluating integer constants 224 | // Is also used by append() 225 | c.contextAssignDest = append(c.contextAssignDest, realDst) 226 | 227 | // Allocate from value 228 | comVal := c.compileValue(val) 229 | 230 | // Cast to interface if needed 231 | comVal = c.valueToInterfaceValue(comVal, temporaryDst) 232 | llvmV := internal.LoadIfVariable(c.contextBlock, comVal) 233 | 234 | // Pop assigng type stack 235 | c.contextAssignDest = c.contextAssignDest[0 : len(c.contextAssignDest)-1] 236 | 237 | return llvmV 238 | } 239 | -------------------------------------------------------------------------------- /compiler/compiler/types.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/llir/llvm/ir" 7 | 8 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 9 | "github.com/zegl/tre/compiler/compiler/name" 10 | 11 | "github.com/zegl/tre/compiler/compiler/internal" 12 | "github.com/zegl/tre/compiler/compiler/value" 13 | 14 | "github.com/zegl/tre/compiler/compiler/types" 15 | "github.com/zegl/tre/compiler/parser" 16 | 17 | "github.com/llir/llvm/ir/constant" 18 | "github.com/llir/llvm/ir/enum" 19 | llvmTypes "github.com/llir/llvm/ir/types" 20 | llvmValue "github.com/llir/llvm/ir/value" 21 | ) 22 | 23 | // Is used in interfaces to keep track of the backing data type 24 | var typeIDs = map[string]int64{} 25 | var nextTypeID int64 26 | 27 | func getTypeID(typeName string) int64 { 28 | if id, ok := typeIDs[typeName]; ok { 29 | return id 30 | } 31 | 32 | nextTypeID++ 33 | typeIDs[typeName] = nextTypeID 34 | return nextTypeID 35 | } 36 | 37 | func (c *Compiler) parserTypeToType(typeNode parser.TypeNode) types.Type { 38 | switch t := typeNode.(type) { 39 | case *parser.SingleTypeNode: 40 | if len(t.PackageName) > 0 { 41 | tp, ok := c.packages[t.PackageName].GetPkgType(t.TypeName, false) 42 | if !ok { 43 | panic("unknown type: " + t.PackageName + "." + t.TypeName) 44 | } 45 | return tp 46 | } 47 | 48 | if res, ok := c.currentPackage.GetPkgType(t.TypeName, true); ok { 49 | return res 50 | } 51 | 52 | // TODO: Find a better way to organize builtin types 53 | if res, ok := c.packages["global"].GetPkgType(t.TypeName, true); ok { 54 | return res 55 | } 56 | 57 | panic("unknown type: " + t.TypeName) 58 | 59 | case *parser.ArrayTypeNode: 60 | itemType := c.parserTypeToType(t.ItemType) 61 | return &types.Array{ 62 | Type: itemType, 63 | LlvmType: llvmTypes.NewArray(uint64(t.Len), itemType.LLVM()), 64 | } 65 | 66 | case *parser.StructTypeNode: 67 | var structTypes []llvmTypes.Type 68 | members := make(map[string]types.Type) 69 | memberIndexes := t.Names 70 | 71 | inverseNamesIndex := make(map[int]string) 72 | for name, index := range memberIndexes { 73 | inverseNamesIndex[index] = name 74 | } 75 | 76 | for i, tt := range t.Types { 77 | ty := c.parserTypeToType(tt) 78 | members[inverseNamesIndex[i]] = ty 79 | structTypes = append(structTypes, ty.LLVM()) 80 | } 81 | 82 | return &types.Struct{ 83 | SourceName: t.GetName(), 84 | Members: members, 85 | MemberIndexes: memberIndexes, 86 | Type: llvmTypes.NewStruct(structTypes...), 87 | } 88 | 89 | case *parser.SliceTypeNode: 90 | itemType := c.parserTypeToType(t.ItemType) 91 | return &types.Slice{ 92 | Type: itemType, 93 | LlvmType: internal.Slice(itemType.LLVM()), 94 | } 95 | 96 | case *parser.InterfaceTypeNode: 97 | requiredMethods := make(map[string]types.InterfaceMethod) 98 | 99 | for name, def := range t.Methods { 100 | ifaceMethod := types.InterfaceMethod{ 101 | ArgumentTypes: make([]types.Type, 0), 102 | ReturnTypes: make([]types.Type, 0), 103 | } 104 | 105 | for _, arg := range def.ArgumentTypes { 106 | ifaceMethod.ArgumentTypes = append(ifaceMethod.ArgumentTypes, c.parserTypeToType(arg)) 107 | } 108 | for _, ret := range def.ReturnTypes { 109 | ifaceMethod.ReturnTypes = append(ifaceMethod.ReturnTypes, c.parserTypeToType(ret)) 110 | } 111 | 112 | requiredMethods[name] = ifaceMethod 113 | } 114 | 115 | return &types.Interface{RequiredMethods: requiredMethods} 116 | 117 | case *parser.PointerTypeNode: 118 | return &types.Pointer{ 119 | Type: c.parserTypeToType(t.ValueType), 120 | } 121 | 122 | case *parser.FuncTypeNode: 123 | retType, treReturnTypes, llvmArgTypes, treParams, _, _ := c.funcType(t.ArgTypes, t.RetTypes) 124 | 125 | fn := ir.NewFunc("UNNAMEDFUNC", retType.LLVM(), llvmArgTypes...) 126 | 127 | return &types.Function{ 128 | ArgumentTypes: treParams, 129 | ReturnTypes: treReturnTypes, 130 | FuncType: fn.Type(), 131 | LlvmReturnType: retType, 132 | } 133 | } 134 | 135 | panic(fmt.Sprintf("unknown typeNode: %T", typeNode)) 136 | } 137 | 138 | func (c *Compiler) compileTypeCastNode(v *parser.TypeCastNode) value.Value { 139 | val := c.compileValue(v.Val) 140 | 141 | var current *llvmTypes.IntType 142 | var ok bool 143 | 144 | current, ok = val.Type.LLVM().(*llvmTypes.IntType) 145 | if !ok { 146 | panic("TypeCast origin must be int type") 147 | } 148 | 149 | targetType := c.parserTypeToType(v.Type) 150 | target, ok := targetType.LLVM().(*llvmTypes.IntType) 151 | if !ok { 152 | panic("TypeCast target must be int type") 153 | } 154 | 155 | llvmVal := internal.LoadIfVariable(c.contextBlock, val) 156 | 157 | // Same size, nothing to do here 158 | if current.BitSize == target.BitSize { 159 | return val 160 | } 161 | 162 | res := c.contextBlock.NewAlloca(target) 163 | 164 | var changedSize llvmValue.Value 165 | 166 | if current.BitSize < target.BitSize { 167 | changedSize = c.contextBlock.NewSExt(llvmVal, target) 168 | } else { 169 | changedSize = c.contextBlock.NewTrunc(llvmVal, target) 170 | } 171 | 172 | c.contextBlock.NewStore(changedSize, res) 173 | 174 | return value.Value{ 175 | Value: res, 176 | Type: targetType, 177 | IsVariable: true, 178 | } 179 | } 180 | 181 | func (c *Compiler) compileTypeCastInterfaceNode(v *parser.TypeCastInterfaceNode) value.Value { 182 | tryCastToType := c.parserTypeToType(v.Type) 183 | 184 | // Allocate the OK variable 185 | okVal := c.contextBlock.NewAlloca(types.Bool.LLVM()) 186 | types.Bool.Zero(c.contextBlock, okVal) 187 | okVal.SetName(name.Var("ok")) 188 | 189 | resCastedVal := c.contextBlock.NewAlloca(tryCastToType.LLVM()) 190 | tryCastToType.Zero(c.contextBlock, resCastedVal) 191 | resCastedVal.SetName(name.Var("rescastedval")) 192 | 193 | interfaceVal := c.compileValue(v.Item) 194 | 195 | interfaceDataType := c.contextBlock.NewGetElementPtr(pointer.ElemType(interfaceVal.Value), interfaceVal.Value, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 1)) 196 | loadedInterfaceDataType := c.contextBlock.NewLoad(pointer.ElemType(interfaceDataType), interfaceDataType) 197 | 198 | trueBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-was-correct-type") 199 | falseBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-was-other-type") 200 | afterBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after-type-check") 201 | 202 | trueBlock.NewBr(afterBlock) 203 | falseBlock.NewBr(afterBlock) 204 | 205 | backingTypeID := getTypeID(tryCastToType.Name()) 206 | cmp := c.contextBlock.NewICmp(enum.IPredEQ, loadedInterfaceDataType, constant.NewInt(llvmTypes.I32, backingTypeID)) 207 | c.contextBlock.NewCondBr(cmp, trueBlock, falseBlock) 208 | 209 | trueBlock.NewStore(constant.NewInt(llvmTypes.I1, 1), okVal) 210 | 211 | backingDataPtr := trueBlock.NewGetElementPtr(pointer.ElemType(interfaceVal.Value), interfaceVal.Value, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 0)) 212 | loadedBackingDataPtr := trueBlock.NewLoad(pointer.ElemType(backingDataPtr), backingDataPtr) 213 | casted := trueBlock.NewBitCast(loadedBackingDataPtr, llvmTypes.NewPointer(tryCastToType.LLVM())) 214 | loadedCasted := trueBlock.NewLoad(pointer.ElemType(casted), casted) 215 | trueBlock.NewStore(loadedCasted, resCastedVal) 216 | 217 | c.contextBlock = afterBlock 218 | 219 | return value.Value{ 220 | Type: &types.MultiValue{ 221 | Types: []types.Type{ 222 | tryCastToType, 223 | types.Bool, 224 | }, 225 | }, 226 | MultiValues: []value.Value{ 227 | {Type: tryCastToType, Value: resCastedVal, IsVariable: true}, 228 | {Type: types.Bool, Value: okVal, IsVariable: true}, 229 | }, 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /compiler/compiler/condition.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/zegl/tre/compiler/compiler/internal" 7 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 8 | "github.com/zegl/tre/compiler/compiler/name" 9 | 10 | "github.com/zegl/tre/compiler/compiler/types" 11 | "github.com/zegl/tre/compiler/compiler/value" 12 | "github.com/zegl/tre/compiler/parser" 13 | 14 | "github.com/llir/llvm/ir/constant" 15 | "github.com/llir/llvm/ir/enum" 16 | llvmTypes "github.com/llir/llvm/ir/types" 17 | llvmValue "github.com/llir/llvm/ir/value" 18 | ) 19 | 20 | func getConditionLLVMpred(operator parser.Operator) enum.IPred { 21 | m := map[parser.Operator]enum.IPred{ 22 | parser.OP_GT: enum.IPredSGT, 23 | parser.OP_GTEQ: enum.IPredSGE, 24 | parser.OP_LT: enum.IPredSLT, 25 | parser.OP_LTEQ: enum.IPredSLE, 26 | parser.OP_EQ: enum.IPredEQ, 27 | parser.OP_NEQ: enum.IPredNE, 28 | } 29 | 30 | if op, ok := m[operator]; ok { 31 | return op 32 | } 33 | 34 | panic("unknown op: " + string(operator)) 35 | } 36 | 37 | func (c *Compiler) compileOperatorNode(v *parser.OperatorNode) value.Value { 38 | left := c.compileValue(v.Left) 39 | right := c.compileValue(v.Right) 40 | 41 | _, rightIsUntyped := right.Type.(*types.UntypedConstantNumber) 42 | _, leftIsUntyped := left.Type.(*types.UntypedConstantNumber) 43 | 44 | if rightIsUntyped && !leftIsUntyped { 45 | right = value.UntypedConstAs(right, left) 46 | } 47 | if leftIsUntyped && !rightIsUntyped { 48 | left = value.UntypedConstAs(left, right) 49 | } 50 | 51 | leftLLVM := internal.LoadIfVariable(c.contextBlock, left) 52 | rightLLVM := internal.LoadIfVariable(c.contextBlock, right) 53 | 54 | if !leftLLVM.Type().Equal(rightLLVM.Type()) && !rightIsUntyped && !leftIsUntyped { 55 | panic(fmt.Sprintf("Different types in operation: %T and %T (%+v and %+v)", left.Type, right.Type, leftLLVM.Type(), rightLLVM.Type())) 56 | } 57 | 58 | switch leftLLVM.Type().Name() { 59 | case "string": 60 | if v.Operator == parser.OP_ADD { 61 | leftLen := c.contextBlock.NewExtractValue(leftLLVM, 0) 62 | rightLen := c.contextBlock.NewExtractValue(rightLLVM, 0) 63 | sumLen := c.contextBlock.NewAdd(leftLen, rightLen) 64 | 65 | backingArray := c.contextBlock.NewAlloca(i8.LLVM()) 66 | backingArray.NElems = sumLen 67 | 68 | // Copy left to new backing array 69 | c.contextBlock.NewCall(c.externalFuncs.Strcpy.Value.(llvmValue.Named), backingArray, c.contextBlock.NewExtractValue(leftLLVM, 1)) 70 | 71 | // Append right to backing array 72 | c.contextBlock.NewCall(c.externalFuncs.Strcat.Value.(llvmValue.Named), backingArray, c.contextBlock.NewExtractValue(rightLLVM, 1)) 73 | 74 | sType, ok := c.packages["global"].GetPkgType("string", true) 75 | if !ok { 76 | panic("string type not found") 77 | } 78 | alloc := c.contextBlock.NewAlloca(sType.LLVM()) 79 | 80 | // Save length of the string 81 | lenItem := c.contextBlock.NewGetElementPtr(pointer.ElemType(alloc), alloc, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 0)) 82 | c.contextBlock.NewStore(sumLen, lenItem) 83 | 84 | // Save i8* version of string 85 | strItem := c.contextBlock.NewGetElementPtr(pointer.ElemType(alloc), alloc, constant.NewInt(llvmTypes.I32, 0), constant.NewInt(llvmTypes.I32, 1)) 86 | c.contextBlock.NewStore(backingArray, strItem) 87 | 88 | return value.Value{ 89 | Value: c.contextBlock.NewLoad(pointer.ElemType(alloc), alloc), 90 | Type: types.String, 91 | IsVariable: false, 92 | } 93 | } 94 | 95 | panic("string does not implement operation " + v.Operator) 96 | } 97 | 98 | var opRes llvmValue.Value 99 | 100 | switch v.Operator { 101 | case parser.OP_ADD: 102 | opRes = c.contextBlock.NewAdd(leftLLVM, rightLLVM) 103 | case parser.OP_SUB: 104 | opRes = c.contextBlock.NewSub(leftLLVM, rightLLVM) 105 | case parser.OP_MUL: 106 | opRes = c.contextBlock.NewMul(leftLLVM, rightLLVM) 107 | case parser.OP_DIV: 108 | if left.Type.IsSigned() { 109 | opRes = c.contextBlock.NewSDiv(leftLLVM, rightLLVM) // SDiv == Signed Division 110 | } else { 111 | opRes = c.contextBlock.NewUDiv(leftLLVM, rightLLVM) // SDiv == Signed Division 112 | } 113 | case parser.OP_BIT_AND: 114 | opRes = c.contextBlock.NewAnd(leftLLVM, rightLLVM) 115 | case parser.OP_BIT_OR: 116 | opRes = c.contextBlock.NewOr(leftLLVM, rightLLVM) 117 | case parser.OP_BIT_XOR: 118 | opRes = c.contextBlock.NewXor(leftLLVM, rightLLVM) 119 | case parser.OP_BIT_CLEAR: 120 | not := c.contextBlock.NewXor(rightLLVM, constant.NewInt(rightLLVM.Type().(*llvmTypes.IntType), -1)) 121 | opRes = c.contextBlock.NewAnd(leftLLVM, not) 122 | case parser.OP_LEFT_SHIFT: 123 | opRes = c.contextBlock.NewShl(leftLLVM, rightLLVM) 124 | case parser.OP_RIGHT_SHIFT: 125 | opRes = c.contextBlock.NewLShr(leftLLVM, rightLLVM) 126 | default: 127 | // Boolean operations 128 | return value.Value{ 129 | Type: types.Bool, 130 | Value: c.contextBlock.NewICmp(getConditionLLVMpred(v.Operator), leftLLVM, rightLLVM), 131 | IsVariable: false, 132 | } 133 | } 134 | 135 | return value.Value{ 136 | Value: opRes, 137 | Type: left.Type, 138 | IsVariable: false, 139 | } 140 | } 141 | 142 | func (c *Compiler) compileSubNode(v *parser.SubNode) value.Value { 143 | right := c.compileValue(v.Item) 144 | rVal := internal.LoadIfVariable(c.contextBlock, right) 145 | 146 | res := c.contextBlock.NewSub( 147 | constant.NewInt(rVal.Type().(*llvmTypes.IntType), 0), 148 | rVal, 149 | ) 150 | 151 | return value.Value{ 152 | Value: res, 153 | Type: right.Type, 154 | IsVariable: false, 155 | } 156 | } 157 | 158 | func (c *Compiler) compileConditionNode(v *parser.ConditionNode) { 159 | 160 | c.pushVariablesStack() 161 | 162 | cond := c.compileOperatorNode(v.Cond) 163 | 164 | afterBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-after") 165 | trueBlock := c.contextBlock.Parent.NewBlock(name.Block() + "-true") 166 | falseBlock := afterBlock 167 | 168 | // push afterBlock stack 169 | c.contextCondAfter = append(c.contextCondAfter, afterBlock) 170 | 171 | if len(v.False) > 0 { 172 | falseBlock = c.contextBlock.Parent.NewBlock(name.Block() + "-false") 173 | } 174 | 175 | c.contextBlock.NewCondBr(cond.Value, trueBlock, falseBlock) 176 | 177 | c.contextBlock = trueBlock 178 | c.compile(v.True) 179 | 180 | // Jump to after-block if no terminator has been set (such as a return statement) 181 | if trueBlock.Term == nil { 182 | trueBlock.NewBr(afterBlock) 183 | } 184 | 185 | if len(v.False) > 0 { 186 | c.contextBlock = falseBlock 187 | c.compile(v.False) 188 | 189 | // Jump to after-block if no terminator has been set (such as a return statement) 190 | if falseBlock.Term == nil { 191 | falseBlock.NewBr(afterBlock) 192 | } 193 | } 194 | 195 | c.contextBlock = afterBlock 196 | 197 | // pop after block stack 198 | c.contextCondAfter = c.contextCondAfter[0 : len(c.contextCondAfter)-1] 199 | 200 | // set after block to jump to the after block 201 | if len(c.contextCondAfter) > 0 { 202 | afterBlock.NewBr(c.contextCondAfter[len(c.contextCondAfter)-1]) 203 | } 204 | 205 | c.popVariablesStack() 206 | } 207 | 208 | func (c *Compiler) compileDecrementNode(v *parser.DecrementNode) value.Value { 209 | input := c.compileValue(v.Item) 210 | val := input.Value 211 | if input.IsVariable { 212 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 213 | added := c.contextBlock.NewAdd(val, constant.NewInt(val.Type().(*llvmTypes.IntType), -1)) 214 | c.contextBlock.NewStore(added, input.Value) 215 | return input 216 | } else { 217 | panic("not implemented") 218 | } 219 | } 220 | 221 | func (c *Compiler) compileIncrementNode(v *parser.IncrementNode) value.Value { 222 | input := c.compileValue(v.Item) 223 | val := input.Value 224 | if input.IsVariable { 225 | val = c.contextBlock.NewLoad(pointer.ElemType(val), val) 226 | added := c.contextBlock.NewAdd(val, constant.NewInt(val.Type().(*llvmTypes.IntType), 1)) 227 | c.contextBlock.NewStore(added, input.Value) 228 | return input 229 | } else { 230 | panic("not implemented") 231 | } 232 | } 233 | 234 | func (c *Compiler) compileGroupNode(v *parser.GroupNode) value.Value { 235 | return c.compileValue(v.Item) 236 | } 237 | -------------------------------------------------------------------------------- /compiler/compiler/types/type.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/llir/llvm/ir" 8 | "github.com/llir/llvm/ir/constant" 9 | "github.com/llir/llvm/ir/types" 10 | llvmValue "github.com/llir/llvm/ir/value" 11 | 12 | "github.com/zegl/tre/compiler/compiler/internal/pointer" 13 | "github.com/zegl/tre/compiler/compiler/name" 14 | 15 | "github.com/zegl/tre/compiler/compiler/strings" 16 | ) 17 | 18 | type Type interface { 19 | LLVM() types.Type 20 | Name() string 21 | 22 | // Size of type in bytes 23 | Size() int64 24 | 25 | AddMethod(string, *Method) 26 | GetMethod(string) (*Method, bool) 27 | 28 | Zero(*ir.Block, llvmValue.Value) 29 | 30 | IsSigned() bool 31 | } 32 | 33 | type backingType struct { 34 | methods map[string]*Method 35 | } 36 | 37 | func (b *backingType) AddMethod(name string, method *Method) { 38 | if b.methods == nil { 39 | b.methods = make(map[string]*Method) 40 | } 41 | b.methods[name] = method 42 | } 43 | 44 | func (b *backingType) GetMethod(name string) (*Method, bool) { 45 | m, ok := b.methods[name] 46 | return m, ok 47 | } 48 | 49 | func (backingType) Size() int64 { 50 | panic("Type does not have size set") 51 | } 52 | 53 | func (backingType) Zero(*ir.Block, llvmValue.Value) { 54 | // NOOP 55 | } 56 | 57 | func (backingType) IsSigned() bool { 58 | return false 59 | } 60 | 61 | type Struct struct { 62 | backingType 63 | 64 | Members map[string]Type 65 | MemberIndexes map[string]int 66 | 67 | IsHeapAllocated bool 68 | 69 | SourceName string 70 | Type types.Type 71 | } 72 | 73 | func (s Struct) LLVM() types.Type { 74 | return s.Type 75 | } 76 | 77 | func (s Struct) Name() string { 78 | return fmt.Sprintf("struct(%s)", s.SourceName) 79 | } 80 | 81 | func (s Struct) Zero(block *ir.Block, alloca llvmValue.Value) { 82 | for key, valType := range s.Members { 83 | ptr := block.NewGetElementPtr(pointer.ElemType(alloca), alloca, 84 | constant.NewInt(types.I32, 0), 85 | constant.NewInt(types.I32, int64(s.MemberIndexes[key])), 86 | ) 87 | valType.Zero(block, ptr) 88 | } 89 | } 90 | 91 | func (s Struct) Size() int64 { 92 | var sum int64 93 | for _, valType := range s.Members { 94 | sum += valType.Size() 95 | } 96 | return sum 97 | } 98 | 99 | type Method struct { 100 | backingType 101 | 102 | Function *Function 103 | LlvmFunction llvmValue.Named 104 | PointerReceiver bool 105 | MethodName string 106 | } 107 | 108 | func (m Method) LLVM() types.Type { 109 | return m.Function.LLVM() 110 | } 111 | 112 | func (m Method) Name() string { 113 | return m.MethodName 114 | } 115 | 116 | type Function struct { 117 | backingType 118 | 119 | // LlvmFunction llvmValue.Named 120 | FuncType types.Type 121 | 122 | // The return type of the LLVM function (is always 1) 123 | LlvmReturnType Type 124 | // Return types of the Tre function 125 | ReturnTypes []Type 126 | 127 | IsVariadic bool 128 | ArgumentTypes []Type 129 | IsExternal bool 130 | 131 | // Is used when calling an interface method 132 | JumpFunction *ir.Func 133 | } 134 | 135 | func (f Function) LLVM() types.Type { 136 | return f.FuncType 137 | } 138 | 139 | func (f Function) Name() string { 140 | return "func" 141 | } 142 | 143 | type BoolType struct { 144 | backingType 145 | } 146 | 147 | func (BoolType) LLVM() types.Type { 148 | return types.I1 149 | } 150 | 151 | func (BoolType) Name() string { 152 | return "bool" 153 | } 154 | 155 | func (BoolType) Size() int64 { 156 | return 1 157 | } 158 | 159 | func (b BoolType) Zero(block *ir.Block, alloca llvmValue.Value) { 160 | block.NewStore(constant.NewInt(types.I1, 0), alloca) 161 | } 162 | 163 | type VoidType struct { 164 | backingType 165 | } 166 | 167 | func (VoidType) LLVM() types.Type { 168 | return types.Void 169 | } 170 | 171 | func (VoidType) Name() string { 172 | return "void" 173 | } 174 | 175 | func (VoidType) Size() int64 { 176 | return 0 177 | } 178 | 179 | type Int struct { 180 | backingType 181 | 182 | Type *types.IntType 183 | TypeName string 184 | TypeSize int64 185 | Signed bool 186 | } 187 | 188 | func (i Int) LLVM() types.Type { 189 | return i.Type 190 | } 191 | 192 | func (i Int) Name() string { 193 | return i.TypeName 194 | } 195 | 196 | func (i Int) Size() int64 { 197 | return i.TypeSize 198 | } 199 | 200 | func (i Int) Zero(block *ir.Block, alloca llvmValue.Value) { 201 | b := big.NewInt(0) 202 | if !i.IsSigned() { 203 | b.SetUint64(0) 204 | } 205 | 206 | c := &constant.Int{ 207 | Typ: i.Type, 208 | X: b, 209 | } 210 | 211 | block.NewStore(c, alloca) 212 | } 213 | 214 | func (i Int) IsSigned() bool { 215 | return i.Signed 216 | } 217 | 218 | type StringType struct { 219 | backingType 220 | Type types.Type 221 | } 222 | 223 | // Populated by compiler.go 224 | var ModuleStringType types.Type 225 | var EmptyStringConstant *ir.Global 226 | 227 | func (StringType) LLVM() types.Type { 228 | return ModuleStringType 229 | } 230 | 231 | func (StringType) Name() string { 232 | return "string" 233 | } 234 | 235 | func (StringType) Size() int64 { 236 | return 16 237 | } 238 | 239 | func (s StringType) Zero(block *ir.Block, alloca llvmValue.Value) { 240 | lenPtr := block.NewGetElementPtr(pointer.ElemType(alloca), alloca, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 0)) 241 | backingDataPtr := block.NewGetElementPtr(pointer.ElemType(alloca), alloca, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 1)) 242 | block.NewStore(constant.NewInt(types.I64, 0), lenPtr) 243 | block.NewStore(strings.Toi8Ptr(block, EmptyStringConstant), backingDataPtr) 244 | } 245 | 246 | type Array struct { 247 | backingType 248 | Type Type 249 | Len uint64 250 | LlvmType types.Type 251 | } 252 | 253 | func (a Array) LLVM() types.Type { 254 | return a.LlvmType 255 | } 256 | 257 | func (a Array) Name() string { 258 | return "array" 259 | } 260 | 261 | func (a Array) Zero(block *ir.Block, alloca llvmValue.Value) { 262 | for i := uint64(0); i < a.Len; i++ { 263 | ptr := block.NewGetElementPtr(pointer.ElemType(alloca), alloca, constant.NewInt(types.I64, 0), constant.NewInt(types.I64, int64(i))) 264 | a.Type.Zero(block, ptr) 265 | } 266 | } 267 | 268 | type Slice struct { 269 | backingType 270 | Type Type // type of the items in the slice []int => int 271 | LlvmType types.Type 272 | } 273 | 274 | func (s Slice) LLVM() types.Type { 275 | return s.LlvmType 276 | } 277 | 278 | func (Slice) Name() string { 279 | return "slice" 280 | } 281 | 282 | func (Slice) Size() int64 { 283 | return 3*4 + 8 // 3 int32s and a pointer 284 | } 285 | 286 | func (s Slice) SliceZero(block *ir.Block, mallocFunc llvmValue.Named, initCap int, emptySlice llvmValue.Value) { 287 | // The cap must always be larger than 0 288 | // Use 2 as the default value 289 | if initCap < 2 { 290 | initCap = 2 291 | } 292 | 293 | len := block.NewGetElementPtr(pointer.ElemType(emptySlice), emptySlice, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 0)) 294 | len.SetName(name.Var("len")) 295 | cap := block.NewGetElementPtr(pointer.ElemType(emptySlice), emptySlice, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 1)) 296 | cap.SetName(name.Var("cap")) 297 | offset := block.NewGetElementPtr(pointer.ElemType(emptySlice), emptySlice, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 2)) 298 | offset.SetName(name.Var("offset")) 299 | backingArray := block.NewGetElementPtr(pointer.ElemType(emptySlice), emptySlice, constant.NewInt(types.I32, 0), constant.NewInt(types.I32, 3)) 300 | backingArray.SetName(name.Var("backing")) 301 | 302 | block.NewStore(constant.NewInt(types.I32, 0), len) 303 | block.NewStore(constant.NewInt(types.I32, int64(initCap)), cap) 304 | block.NewStore(constant.NewInt(types.I32, 0), offset) 305 | 306 | mallocatedSpaceRaw := block.NewCall(mallocFunc, constant.NewInt(types.I64, int64(initCap)*s.Type.Size())) 307 | mallocatedSpaceRaw.SetName(name.Var("slicezero")) 308 | bitcasted := block.NewBitCast(mallocatedSpaceRaw, types.NewPointer(s.Type.LLVM())) 309 | block.NewStore(bitcasted, backingArray) 310 | } 311 | 312 | type Pointer struct { 313 | backingType 314 | 315 | Type Type 316 | IsNonAllocDereference bool 317 | 318 | LlvmType types.Type 319 | } 320 | 321 | func (p Pointer) LLVM() types.Type { 322 | return types.NewPointer(p.Type.LLVM()) 323 | } 324 | 325 | func (p Pointer) Name() string { 326 | return fmt.Sprintf("pointer(%s)", p.Type.Name()) 327 | } 328 | 329 | func (p Pointer) Size() int64 { 330 | return 8 331 | } 332 | 333 | // MultiValue is used when returning multiple values from a function 334 | type MultiValue struct { 335 | backingType 336 | Types []Type 337 | } 338 | 339 | func (m MultiValue) Name() string { 340 | return "multivalue" 341 | } 342 | 343 | func (m MultiValue) LLVM() types.Type { 344 | panic("MutliValue has no LLVM type") 345 | } 346 | 347 | type UntypedConstantNumber struct { 348 | backingType 349 | } 350 | 351 | func (m UntypedConstantNumber) Name() string { 352 | return "UntypedConstantNumber" 353 | } 354 | 355 | func (m UntypedConstantNumber) LLVM() types.Type { 356 | panic("UntypedConstantNumber has no LLVM type") 357 | } 358 | -------------------------------------------------------------------------------- /compiler/compiler/compiler.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "runtime/debug" 7 | 8 | "github.com/zegl/tre/compiler/compiler/internal" 9 | "github.com/zegl/tre/compiler/compiler/name" 10 | "github.com/zegl/tre/compiler/compiler/strings" 11 | "github.com/zegl/tre/compiler/compiler/types" 12 | "github.com/zegl/tre/compiler/compiler/value" 13 | "github.com/zegl/tre/compiler/parser" 14 | 15 | "errors" 16 | 17 | "github.com/llir/llvm/ir" 18 | "github.com/llir/llvm/ir/constant" 19 | llvmTypes "github.com/llir/llvm/ir/types" 20 | llvmValue "github.com/llir/llvm/ir/value" 21 | ) 22 | 23 | type Compiler struct { 24 | module *ir.Module 25 | 26 | // functions provided by the OS, such as printf and malloc 27 | externalFuncs ExternalFuncs 28 | 29 | packages map[string]*pkg 30 | currentPackage *pkg 31 | 32 | // TODO: Replace with currentPackage.Name() 33 | currentPackageName string 34 | 35 | contextFunc *types.Function 36 | 37 | initGlobalsFunc *ir.Func 38 | mainFunc *ir.Func 39 | 40 | // Stack of return values pointers, is used both used if a function returns more 41 | // than one value (arg pointers), and single stack based returns 42 | contextFuncRetVals [][]value.Value 43 | 44 | contextBlock *ir.Block 45 | 46 | // Stack of variables that are in scope 47 | contextBlockVariables []map[string]value.Value 48 | 49 | // What a break or continue should resolve to 50 | contextLoopBreak []*ir.Block 51 | contextLoopContinue []*ir.Block 52 | 53 | // Where a condition should jump when done 54 | contextCondAfter []*ir.Block 55 | 56 | // What type the current assign operation is assigning to. 57 | // Is used when evaluating what type an integer constant should be. 58 | contextAssignDest []value.Value 59 | 60 | // Stack of Alloc instructions 61 | // Is used to decide if values should be stack or heap allocated 62 | contextAlloc []*parser.AllocNode 63 | 64 | stringConstants map[string]*ir.Global 65 | 66 | // runtime.GOOS and runtime.GOARCH 67 | GOOS, GOARCH string 68 | } 69 | 70 | var ( 71 | i8 = types.I8 72 | i32 = types.I32 73 | i64 = types.I64 74 | ) 75 | 76 | func NewCompiler() *Compiler { 77 | c := &Compiler{ 78 | module: ir.NewModule(), 79 | 80 | packages: make(map[string]*pkg), 81 | 82 | contextFuncRetVals: make([][]value.Value, 0), 83 | 84 | contextBlockVariables: make([]map[string]value.Value, 0), 85 | 86 | contextLoopBreak: make([]*ir.Block, 0), 87 | contextLoopContinue: make([]*ir.Block, 0), 88 | contextCondAfter: make([]*ir.Block, 0), 89 | 90 | contextAssignDest: make([]value.Value, 0), 91 | 92 | stringConstants: make(map[string]*ir.Global), 93 | } 94 | 95 | c.createExternalPackage() 96 | c.addGlobal() 97 | c.pushVariablesStack() 98 | 99 | // Triple examples: 100 | // x86_64-apple-macosx10.13.0 101 | // x86_64-pc-linux-gnu 102 | var targetTriple [2]string 103 | 104 | switch runtime.GOARCH { 105 | case "amd64": 106 | targetTriple[0] = "x86_64" 107 | default: 108 | panic("unsupported GOARCH: " + runtime.GOARCH) 109 | } 110 | 111 | switch runtime.GOOS { 112 | case "darwin": 113 | targetTriple[1] = "apple-macosx10.13.0" 114 | case "linux": 115 | targetTriple[1] = "pc-linux-gnu" 116 | case "windows": 117 | targetTriple[1] = "pc-windows" 118 | default: 119 | panic("unsupported GOOS: " + runtime.GOOS) 120 | } 121 | 122 | c.module.TargetTriple = fmt.Sprintf("%s-%s", targetTriple[0], targetTriple[1]) 123 | 124 | // TODO: Allow cross compilation 125 | c.GOOS = runtime.GOOS 126 | c.GOARCH = runtime.GOARCH 127 | 128 | return c 129 | } 130 | 131 | func (c *Compiler) Compile(root parser.PackageNode) (err error) { 132 | defer func() { 133 | if r := recover(); r != nil { 134 | // Compile time panics, that are not errors in the compiler 135 | if _, ok := r.(Panic); ok { 136 | err = errors.New(fmt.Sprint(r)) 137 | return 138 | } 139 | 140 | // Bugs in the compiler 141 | err = fmt.Errorf("%s\n\nInternal compiler stacktrace:\n%s", 142 | fmt.Sprint(r), 143 | string(debug.Stack()), 144 | ) 145 | } 146 | }() 147 | 148 | c.currentPackage = NewPkg(root.Name) 149 | c.currentPackageName = root.Name 150 | c.packages[c.currentPackageName] = c.currentPackage 151 | 152 | for _, fileNode := range root.Files { 153 | c.compile(fileNode.Instructions) 154 | } 155 | 156 | return 157 | } 158 | 159 | func (c *Compiler) GetIR() string { 160 | return c.module.String() 161 | } 162 | 163 | func (c *Compiler) addGlobal() { 164 | types.ModuleStringType = c.module.NewTypeDef("string", internal.String()) 165 | 166 | // Create empty string constant 167 | types.EmptyStringConstant = c.module.NewGlobalDef(strings.NextStringName(), strings.Constant("")) 168 | types.EmptyStringConstant.Immutable = true 169 | 170 | // TODO: Use a different name? Runtime? 171 | global := NewPkg("global") 172 | 173 | strLen := internal.StringLen(types.ModuleStringType) 174 | global.DefinePkgVar("len_string", value.Value{ 175 | Type: &types.Function{ 176 | FuncType: strLen.Type(), 177 | LlvmReturnType: types.I64, 178 | }, 179 | Value: strLen, 180 | IsVariable: false, 181 | }) 182 | 183 | global.DefinePkgType("bool", types.Bool) 184 | global.DefinePkgType("int", types.I64) // TODO: Size based on arch 185 | global.DefinePkgType("int8", types.I8) 186 | global.DefinePkgType("uint8", types.U8) 187 | global.DefinePkgType("int16", types.I16) 188 | global.DefinePkgType("uint16", types.U16) 189 | global.DefinePkgType("int32", types.I32) 190 | global.DefinePkgType("uint32", types.U32) 191 | global.DefinePkgType("int64", types.I64) 192 | global.DefinePkgType("uint64", types.U64) 193 | global.DefinePkgType("uintptr", types.Uintptr) 194 | global.DefinePkgType("string", types.String) 195 | 196 | c.packages["global"] = global 197 | 198 | c.module.Funcs = append(c.module.Funcs, strLen) 199 | 200 | // Initialization function 201 | c.initGlobalsFunc = c.module.NewFunc(name.Var("global-init"), types.Void.LLVM()) 202 | b := c.initGlobalsFunc.NewBlock(name.Block()) 203 | b.NewRet(nil) 204 | 205 | // main.main function, body will be added later 206 | c.mainFunc = c.module.NewFunc("main", types.I32.LLVM()) 207 | mainBlock := c.mainFunc.NewBlock(name.Block()) 208 | mainBlock.NewCall(c.initGlobalsFunc) 209 | } 210 | 211 | func (c *Compiler) compile(instructions []parser.Node) { 212 | for _, i := range instructions { 213 | switch v := i.(type) { 214 | case *parser.ConditionNode: 215 | c.compileConditionNode(v) 216 | case *parser.DefineFuncNode: 217 | c.compileDefineFuncNode(v) 218 | case *parser.ReturnNode: 219 | c.compileReturnNode(v) 220 | case *parser.AllocNode: 221 | c.compileAllocNode(v) 222 | case *parser.AllocGroup: 223 | for _, a := range v.Allocs { 224 | c.compileAllocNode(a) 225 | } 226 | case *parser.AssignNode: 227 | c.compileAssignNode(v) 228 | case *parser.ForNode: 229 | c.compileForNode(v) 230 | case *parser.BreakNode: 231 | c.compileBreakNode(v) 232 | case *parser.ContinueNode: 233 | c.compileContinueNode(v) 234 | 235 | case *parser.DeclarePackageNode: 236 | // TODO: Make use of it 237 | break 238 | case *parser.ImportNode: 239 | // NOOP 240 | break 241 | 242 | case *parser.DefineTypeNode: 243 | t := c.parserTypeToType(v.Type) 244 | 245 | // Add type to module and override the structtype to use the named 246 | // type in the module 247 | if structType, ok := t.(*types.Struct); ok { 248 | structType.Type = c.module.NewTypeDef(v.Name, t.LLVM()) 249 | } 250 | 251 | // Add to tre mapping 252 | c.currentPackage.DefinePkgType(v.Name, t) 253 | 254 | case *parser.SwitchNode: 255 | c.compileSwitchNode(v) 256 | 257 | default: 258 | c.compileValue(v) 259 | break 260 | } 261 | } 262 | } 263 | 264 | func (c *Compiler) compileNameNode(v *parser.NameNode) value.Value { 265 | pkg := c.currentPackage 266 | inSamePackage := true 267 | 268 | if len(v.Package) > 0 { 269 | // Imported package? 270 | if p, ok := c.packages[v.Package]; ok { 271 | pkg = p 272 | inSamePackage = false 273 | } else { 274 | panic(fmt.Sprintf("package %s does not exist", v.Package)) 275 | } 276 | } 277 | 278 | // Search scope in reverse (most specific first) 279 | for i := len(c.contextBlockVariables) - 1; i >= 0; i-- { 280 | if val, ok := c.contextBlockVariables[i][v.Name]; ok { 281 | return val 282 | } 283 | } 284 | 285 | if pkgVar, ok := pkg.GetPkgVar(v.Name, inSamePackage); ok { 286 | return pkgVar 287 | } 288 | 289 | panic(fmt.Sprintf("package %s has no memeber %s", v.Package, v.Name)) 290 | } 291 | 292 | func (c *Compiler) setVar(name string, val value.Value) { 293 | c.contextBlockVariables[len(c.contextBlockVariables)-1][name] = val 294 | } 295 | 296 | func (c *Compiler) pushVariablesStack() { 297 | c.contextBlockVariables = append(c.contextBlockVariables, make(map[string]value.Value)) 298 | } 299 | 300 | func (c *Compiler) popVariablesStack() { 301 | c.contextBlockVariables = c.contextBlockVariables[0 : len(c.contextBlockVariables)-1] 302 | } 303 | 304 | func (c *Compiler) compileValue(node parser.Node) value.Value { 305 | switch v := node.(type) { 306 | 307 | case *parser.ConstantNode: 308 | return c.compileConstantNode(v) 309 | case *parser.OperatorNode: 310 | return c.compileOperatorNode(v) 311 | case *parser.SubNode: 312 | return c.compileSubNode(v) 313 | case *parser.NameNode: 314 | return c.compileNameNode(v) 315 | case *parser.CallNode: 316 | return c.compileCallNode(v) 317 | case *parser.TypeCastNode: 318 | return c.compileTypeCastNode(v) 319 | case *parser.StructLoadElementNode: 320 | return c.compileStructLoadElementNode(v) 321 | case *parser.LoadArrayElement: 322 | return c.compileLoadArrayElement(v) 323 | case *parser.GetReferenceNode: 324 | return c.compileGetReferenceNode(v) 325 | case *parser.DereferenceNode: 326 | return c.compileDereferenceNode(v) 327 | case *parser.NegateNode: 328 | return c.compileNegateBoolNode(v) 329 | case *parser.InitializeSliceNode: 330 | return c.compileInitializeSliceNode(v) 331 | case *parser.SliceArrayNode: 332 | src := c.compileValue(v.Val) 333 | 334 | if _, ok := src.Type.(*types.StringType); ok { 335 | return c.compileSubstring(src, v) 336 | } 337 | 338 | return c.compileSliceArray(src, v) 339 | case *parser.InitializeStructNode: 340 | return c.compileInitStructWithValues(v) 341 | case *parser.TypeCastInterfaceNode: 342 | return c.compileTypeCastInterfaceNode(v) 343 | case *parser.DefineFuncNode: 344 | return c.compileDefineFuncNode(v) 345 | case *parser.InitializeArrayNode: 346 | return c.compileInitializeArrayNode(v) 347 | case *parser.DecrementNode: 348 | return c.compileDecrementNode(v) 349 | case *parser.IncrementNode: 350 | return c.compileIncrementNode(v) 351 | case *parser.GroupNode: 352 | return c.compileGroupNode(v) 353 | } 354 | 355 | panic("compileValue fail: " + fmt.Sprintf("%T: %+v", node, node)) 356 | } 357 | 358 | func (c *Compiler) panic(block *ir.Block, message string) { 359 | globMsg := c.module.NewGlobalDef(strings.NextStringName(), strings.Constant("runtime panic: "+message+"\n")) 360 | globMsg.Immutable = true 361 | block.NewCall(c.externalFuncs.Printf.Value.(llvmValue.Named), strings.Toi8Ptr(block, globMsg)) 362 | block.NewCall(c.externalFuncs.Exit.Value.(llvmValue.Named), constant.NewInt(llvmTypes.I32, 1)) 363 | } 364 | 365 | type Panic string 366 | 367 | func compilePanic(message string) { 368 | panic(Panic(fmt.Sprintf("compile panic: %s\n", message))) 369 | } 370 | --------------------------------------------------------------------------------