diff --git a/expr/build.go b/expr/build.go new file mode 100644 index 0000000..92a08a7 --- /dev/null +++ b/expr/build.go @@ -0,0 +1,28 @@ +package expr + +import "llvm.org/llvm/bindings/go/llvm" + +type LLVMCtx struct { + B llvm.Builder + M llvm.Module +} + +func Build(ctx *Ctx, lctx LLVMCtx, stmts []Expr) { + for _, stmt := range stmts { + BuildStmt(ctx, lctx, stmt) + } +} + +func BuildStmt(ctx *Ctx, lctx LLVMCtx, stmtE Expr) { + s := stmtE.Actual.(Statement) + m := s.Op.Actual.(Macro) + + fn := ctx.GetMacro(m) + if fn == nil { + panicf("unknown macro: %q", m) + } + lv, ok := fn(ctx, lctx, s.Arg) + if ok { + ctx.LastVal = lv + } +} diff --git a/expr/ctx.go b/expr/ctx.go index 67592a8..ac5ae14 100644 --- a/expr/ctx.go +++ b/expr/ctx.go @@ -1,17 +1,23 @@ package expr +import "llvm.org/llvm/bindings/go/llvm" + +type MacroFn func(*Ctx, LLVMCtx, Expr) (llvm.Value, bool) + // Ctx contains all the Macros and Identifiers available. A Ctx is based on the // parent it was created from. If the current Ctx doesn't have a particular key // being looked up, the parent is called instead, and so on. A consequence of // this is that keys in the children take precedence over the parent's type Ctx struct { Parent *Ctx - Macros map[Macro]func(Expr) (Expr, error) + Macros map[Macro]MacroFn + + LastVal llvm.Value } // GetMacro returns the first instance of the given of the given Macro found. If // not found nil is returned. -func (c *Ctx) GetMacro(m Macro) func(Expr) (Expr, error) { +func (c *Ctx) GetMacro(m Macro) MacroFn { if c.Macros != nil { if fn, ok := c.Macros[m]; ok { return fn diff --git a/expr/expr.go b/expr/expr.go index 42ee4dc..84f4e53 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -2,7 +2,6 @@ package expr import ( "fmt" - "strings" "llvm.org/llvm/bindings/go/llvm" @@ -13,25 +12,11 @@ import ( // TODO empty parenthesis // TODO need to figure out how to test LLVMVal stuff // TODO once we're a bit more confident, make ActualFunc -// TODO LLVMVal -> LLVMBuild? -type LLVMCtx struct { - B llvm.Builder - M llvm.Module -} - -// Actual represents the actual expression in question, and has certain -// properties. It is wrapped by Expr which also holds onto contextual -// information, like the token to which Actual was originally parsed from +// Actual represents the actual expression in question. It is wrapped by Expr +// which also holds onto contextual information, like the token to which Actual +// was originally parsed from type Actual interface { - // Returns the llvm.Type which the expression accepts as an input, if any - LLVMInType(ctx *Ctx) llvm.Type - - // Returns the llvm.Type which the expressions outputs - LLVMOutType(ctx *Ctx) llvm.Type - - // Initializes an llvm.Value and returns it. - LLVMVal(*Ctx, LLVMCtx) llvm.Value } // equaler is used to compare two expressions. The comparison should not take @@ -52,27 +37,6 @@ type Expr struct { val *llvm.Value } -// LLVMInType passes through to the method on the underlying Actual -func (e Expr) LLVMInType(ctx *Ctx) llvm.Type { - return e.Actual.LLVMInType(ctx) -} - -// LLVMOutType passes through to the method on the underlying Actual -func (e Expr) LLVMOutType(ctx *Ctx) llvm.Type { - return e.Actual.LLVMOutType(ctx) -} - -// LLVMVal passes its arguments to the underlying Actual instance. It caches the -// result, so if this is called multiple times the underlying one is only called -// the first time. -func (e Expr) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - if e.val == nil { - v := e.Actual.LLVMVal(ctx, lctx) - e.val = &v - } - return *e.val -} - // will panic if either Expr's Actual doesn't implement equaler func (e Expr) equal(e2 Expr) bool { eq1, ok1 := e.Actual.(equaler) @@ -88,21 +52,9 @@ func (e Expr) equal(e2 Expr) bool { // Void represents no data (size = 0) type Void struct{} -// LLVMInType implements the Actual interface method -func (v Void) LLVMInType(ctx *Ctx) llvm.Type { - panic("Void has no InType") -} - -// LLVMOutType implements the Actual interface method -func (v Void) LLVMOutType(ctx *Ctx) llvm.Type { - return llvm.VoidType() -} - -// LLVMVal implements the Actual interface method -func (v Void) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - // Kind of weird that this only works for return type, but I guess makes - // sense - return lctx.B.CreateRetVoid() +func (v Void) equal(e equaler) bool { + _, ok := e.(Void) + return ok } //////////////////////////////////////////////////////////////////////////////// @@ -110,21 +62,6 @@ func (v Void) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { // Bool represents a true or false value type Bool bool -// LLVMInType implements the Actual interface method -func (b Bool) LLVMInType(ctx *Ctx) llvm.Type { - panic("Bool has no InType") -} - -// LLVMOutType implements the Actual interface method -func (b Bool) LLVMOutType(ctx *Ctx) llvm.Type { - return llvm.IntType(1) -} - -// LLVMVal implements the Actual interface method -func (b Bool) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - return llvm.Value{} -} - func (b Bool) equal(e equaler) bool { bb, ok := e.(Bool) if !ok { @@ -138,18 +75,7 @@ func (b Bool) equal(e equaler) bool { // Int represents an integer value type Int int64 -// LLVMInType implements the Actual interface method -func (i Int) LLVMInType(ctx *Ctx) llvm.Type { - panic("Int has no InType") -} - -// LLVMOutType implements the Actual interface method -func (i Int) LLVMOutType(ctx *Ctx) llvm.Type { - return llvm.Int64Type() -} - -// LLVMVal implements the Actual interface method -func (i Int) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { +func (i Int) build(lctx LLVMCtx) llvm.Value { v := lctx.B.CreateAlloca(llvm.Int64Type(), "") lctx.B.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v) return v @@ -157,10 +83,7 @@ func (i Int) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { func (i Int) equal(e equaler) bool { ii, ok := e.(Int) - if !ok { - return false - } - return ii == i + return ok && ii == i } //////////////////////////////////////////////////////////////////////////////// @@ -168,21 +91,6 @@ func (i Int) equal(e equaler) bool { // String represents a string value type String string -// LLVMInType implements the Actual interface method -func (s String) LLVMInType(ctx *Ctx) llvm.Type { - panic("String has no InType") -} - -// LLVMOutType implements the Actual interface method -func (s String) LLVMOutType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMVal implements the Actual interface method -func (s String) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - panic("TODO") -} - func (s String) equal(e equaler) bool { ss, ok := e.(String) if !ok { @@ -197,27 +105,9 @@ func (s String) equal(e equaler) bool { // name type Identifier string -// LLVMInType implements the Actual interface method -func (id Identifier) LLVMInType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMOutType implements the Actual interface method -func (id Identifier) LLVMOutType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMVal implements the Actual interface method -func (id Identifier) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - panic("TODO") -} - func (id Identifier) equal(e equaler) bool { idid, ok := e.(Identifier) - if !ok { - return false - } - return idid == id + return ok && idid == id } //////////////////////////////////////////////////////////////////////////////// @@ -232,27 +122,9 @@ func (m Macro) String() string { return "%" + string(m) } -// LLVMInType implements the Actual interface method -func (m Macro) LLVMInType(ctx *Ctx) llvm.Type { - panic("Macro has no InType") -} - -// LLVMOutType implements the Actual interface method -func (m Macro) LLVMOutType(ctx *Ctx) llvm.Type { - panic("Macro has no OutType") -} - -// LLVMVal implements the Actual interface method -func (m Macro) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - panic("Macro has no Val") -} - func (m Macro) equal(e equaler) bool { mm, ok := e.(Macro) - if !ok { - return false - } - return m == mm + return ok && m == mm } //////////////////////////////////////////////////////////////////////////////// @@ -262,39 +134,12 @@ func (m Macro) equal(e equaler) bool { type Tuple []Expr func (tup Tuple) String() string { - strs := make([]string, len(tup)) - for i := range tup { - strs[i] = fmt.Sprint(tup[i].Actual) - } - return "(" + strings.Join(strs, ", ") + ")" -} - -// LLVMInType implements the Actual interface method -func (tup Tuple) LLVMInType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMOutType implements the Actual interface method -func (tup Tuple) LLVMOutType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMVal implements the Actual interface method -func (tup Tuple) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - panic("TODO") + return "(" + exprsJoin(tup) + ")" } func (tup Tuple) equal(e equaler) bool { tuptup, ok := e.(Tuple) - if !ok || len(tuptup) != len(tup) { - return false - } - for i := range tup { - if !tup[i].equal(tuptup[i]) { - return false - } - } - return true + return ok && exprsEqual(tup, tuptup) } //////////////////////////////////////////////////////////////////////////////// @@ -303,89 +148,39 @@ func (tup Tuple) equal(e equaler) bool { // used as the input to the pipe, and the output of the pipe is the output of // the statement type Statement struct { - // TODO change to Op and Arg - In Expr - To Expr + Op, Arg Expr } func (s Statement) String() string { - return fmt.Sprintf("(%v > %s)", s.In.Actual, s.To.Actual) -} - -func (s Statement) maybeMacro(ctx *Ctx) (Expr, bool) { - m, ok := s.To.Actual.(Macro) - if !ok { - return Expr{}, false - } - - fn := ctx.GetMacro(m) - if fn == nil { - return Expr{}, false - } - newe, err := fn(s.In) - if err != nil { - // TODO proper error - panic(err) - } - return newe, true -} - -// LLVMInType implements the Actual interface method -func (s Statement) LLVMInType(ctx *Ctx) llvm.Type { - if newe, ok := s.maybeMacro(ctx); ok { - return newe.LLVMInType(ctx) - } - // TODO futures - panic("unknown Statement.To") -} - -// LLVMOutType implements the Actual interface method -func (s Statement) LLVMOutType(ctx *Ctx) llvm.Type { - if newe, ok := s.maybeMacro(ctx); ok { - return newe.LLVMOutType(ctx) - } - panic("unknown Statement.To") -} - -// LLVMVal implements the Actual interface method -func (s Statement) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - if newe, ok := s.maybeMacro(ctx); ok { - return newe.LLVMVal(ctx, lctx) - } - panic("unknown Statement.To") + return fmt.Sprintf("(%v %s)", s.Op.Actual, s.Arg.Actual) } func (s Statement) equal(e equaler) bool { ss, ok := e.(Statement) - return ok && s.In.equal(ss.In) && s.To.equal(ss.To) + return ok && s.Op.equal(ss.Op) && s.Arg.equal(ss.Arg) } //////////////////////////////////////////////////////////////////////////////// // Block represents a set of statements which share a scope, i.e. If one // statement binds a variable the rest of the statements in the block can use -// that variable, including sub-blocks within this one. -type Block []Expr +// that variable +type Block struct { + In []Expr + Stmts []Expr + Out []Expr +} func (b Block) String() string { - strs := make([]string, len(b)) - for i := range b { - strs[i] = b[i].Actual.(Statement).String() - } - return fmt.Sprintf("{ %s }", strings.Join(strs, " ")) + return fmt.Sprintf( + "{[%s][%s][%s]}", + exprsJoin(b.In), + exprsJoin(b.Stmts), + exprsJoin(b.Out), + ) } -// LLVMInType implements the Actual interface method -func (b Block) LLVMInType(ctx *Ctx) llvm.Type { - panic("TODO") -} - -// LLVMOutType implements the Actual interface method -func (b Block) LLVMOutType(ctx *Ctx) llvm.Type { - return b[len(b)-1].LLVMOutType(ctx) -} - -// LLVMVal implements the Actual interface method +/* func (b Block) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { name := randStr() // TODO make this based on token // TODO make these based on actual statements @@ -404,16 +199,12 @@ func (b Block) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { lctx.B.CreateRet(v) return fn } +*/ func (b Block) equal(e equaler) bool { bb, ok := e.(Block) - if !ok { - return false - } - for i := range b { - if !b[i].equal(bb[i]) { - return false - } - } - return true + return ok && + exprsEqual(b.In, bb.In) && + exprsEqual(b.Stmts, bb.Stmts) && + exprsEqual(b.Out, bb.Out) } diff --git a/expr/macros.go b/expr/macros.go index 0e21dd1..3abeb14 100644 --- a/expr/macros.go +++ b/expr/macros.go @@ -1,45 +1,23 @@ package expr -import ( - "errors" - - "llvm.org/llvm/bindings/go/llvm" -) - -type addActual []Expr - -func (aa addActual) LLVMInType(ctx *Ctx) llvm.Type { - panic("addActual has no InType") -} - -func (aa addActual) LLVMOutType(ctx *Ctx) llvm.Type { - return aa[0].LLVMOutType(ctx) -} - -func (aa addActual) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value { - a := lctx.B.CreateLoad(aa[0].LLVMVal(ctx, lctx), "") - for i := range aa[1:] { - b := lctx.B.CreateLoad(aa[i+1].LLVMVal(ctx, lctx), "") - a = lctx.B.CreateAdd(a, b, "") - } - return a -} +import "llvm.org/llvm/bindings/go/llvm" // RootCtx describes what's available to *all* contexts, and is what all // contexts should have as the root parent in the tree var RootCtx = &Ctx{ - Macros: map[Macro]func(Expr) (Expr, error){ - "add": func(e Expr) (Expr, error) { - tup, ok := e.Actual.(Tuple) - if !ok { - // TODO proper error - return Expr{}, errors.New("add only accepts a tuple") + Macros: map[Macro]MacroFn{ + "add": func(ctx *Ctx, lctx LLVMCtx, e Expr) (llvm.Value, bool) { + tup := e.Actual.(Tuple) + buildInt := func(e Expr) llvm.Value { + return lctx.B.CreateLoad(e.Actual.(Int).build(lctx), "") } - // TODO check that it's a tuple of integers too - return Expr{ - Actual: addActual(tup), - Token: e.Token, - }, nil + + a := buildInt(tup[0]) + for i := range tup[1:] { + b := buildInt(tup[i+1]) + a = lctx.B.CreateAdd(a, b, "") + } + return a, true }, }, } diff --git a/expr/util.go b/expr/util.go index 7648094..1cdc336 100644 --- a/expr/util.go +++ b/expr/util.go @@ -2,7 +2,9 @@ package expr import ( "encoding/hex" + "fmt" "math/rand" + "strings" ) func randStr() string { @@ -12,3 +14,27 @@ func randStr() string { } return hex.EncodeToString(b) } + +func exprsJoin(ee []Expr) string { + strs := make([]string, len(ee)) + for i := range ee { + strs[i] = fmt.Sprint(ee[i].Actual) + } + return strings.Join(strs, ", ") +} + +func exprsEqual(ee1, ee2 []Expr) bool { + if len(ee1) != len(ee2) { + return false + } + for i := range ee1 { + if !ee1[i].equal(ee2[i]) { + return false + } + } + return true +} + +func panicf(msg string, args ...interface{}) { + panic(fmt.Sprintf(msg, args...)) +} diff --git a/main.go b/main.go index ef3c819..2a6a852 100644 --- a/main.go +++ b/main.go @@ -33,17 +33,20 @@ func main() { c := expr.Expr{Actual: expr.Int(3)} tup := expr.Expr{Actual: expr.Tuple([]expr.Expr{a, b, c})} addMacro := expr.Expr{Actual: expr.Macro("add")} - stmt := expr.Expr{Actual: expr.Statement{In: tup, To: addMacro}} - block := expr.Block([]expr.Expr{stmt}) - fn := block.LLVMVal(expr.RootCtx, lctx) + stmt := expr.Expr{Actual: expr.Statement{Op: addMacro, Arg: tup}} + + //block := expr.Block([]expr.Expr{stmt}) + //fn := block.LLVMVal(expr.RootCtx, lctx) // create main and call our function mainFn := llvm.AddFunction(lctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)) mainBlock := llvm.AddBasicBlock(mainFn, "entry") lctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction()) + expr.BuildStmt(expr.RootCtx, lctx, stmt) + lctx.B.CreateRet(expr.RootCtx.LastVal) - ret := lctx.B.CreateCall(fn, []llvm.Value{}, "") - lctx.B.CreateRet(ret) + //ret := lctx.B.CreateCall(fn, []llvm.Value{}, "") + //lctx.B.CreateRet(ret) // verify it's all good if err := llvm.VerifyModule(lctx.M, llvm.ReturnStatusAction); err != nil {