diff --git a/expr/build.go b/expr/build.go index 92a08a7..0df1159 100644 --- a/expr/build.go +++ b/expr/build.go @@ -2,27 +2,78 @@ package expr import "llvm.org/llvm/bindings/go/llvm" -type LLVMCtx struct { +type BuildCtx struct { + C *Ctx B llvm.Builder M llvm.Module } -func Build(ctx *Ctx, lctx LLVMCtx, stmts []Expr) { +func (bctx BuildCtx) Build(stmts ...Statement) llvm.Value { + var lastVal llvm.Value for _, stmt := range stmts { - BuildStmt(ctx, lctx, stmt) + if e := bctx.BuildStmt(stmt); e != nil { + lastVal = bctx.buildVal(e) + } } + if (lastVal == llvm.Value{}) { + lastVal = bctx.B.CreateRetVoid() + } + return lastVal } -func BuildStmt(ctx *Ctx, lctx LLVMCtx, stmtE Expr) { - s := stmtE.Actual.(Statement) - m := s.Op.Actual.(Macro) +func (bctx BuildCtx) BuildStmt(s Statement) Expr { + m := s.Op.(Macro) - fn := ctx.GetMacro(m) + fn := bctx.C.GetMacro(m) if fn == nil { panicf("unknown macro: %q", m) } - lv, ok := fn(ctx, lctx, s.Arg) - if ok { - ctx.LastVal = lv - } + + return fn(bctx, bctx.buildExpr(s.Arg)) +} + +// may return nil if e is a Statement which has no return +func (bctx BuildCtx) buildExpr(e Expr) Expr { + switch ea := e.(type) { + case llvmVal: + return e + case Int: + return llvmVal(llvm.ConstInt(llvm.Int64Type(), uint64(ea), false)) + case Identifier: + return bctx.buildExpr(bctx.C.GetIdentifier(ea)) + case Statement: + return bctx.BuildStmt(ea) + case Tuple: + for i := range ea { + ea[i] = bctx.buildExpr(ea[i]) + } + return ea + default: + panicf("type %T can't express a value", ea) + } + panic("go is dumb") +} + +func (bctx BuildCtx) buildVal(e Expr) llvm.Value { + return llvm.Value(bctx.buildExpr(e).(llvmVal)) +} + +// globalCtx describes what's available to *all* contexts, and is what all +// contexts should have as the root parent in the tree +var globalCtx = &Ctx{ + macros: map[Macro]MacroFn{ + "add": func(bctx BuildCtx, e Expr) Expr { + tup := e.(Tuple) + a := bctx.buildVal(tup[0]) + b := bctx.buildVal(tup[1]) + return llvmVal(bctx.B.CreateAdd(a, b, "")) + }, + + "bind": func(bctx BuildCtx, e Expr) Expr { + tup := e.(Tuple) + id := bctx.buildExpr(tup[0]).(Identifier) + bctx.C.idents[id] = bctx.buildVal(tup[1]) + return nil + }, + }, } diff --git a/expr/ctx.go b/expr/ctx.go index ac5ae14..1538586 100644 --- a/expr/ctx.go +++ b/expr/ctx.go @@ -2,29 +2,64 @@ package expr import "llvm.org/llvm/bindings/go/llvm" -type MacroFn func(*Ctx, LLVMCtx, Expr) (llvm.Value, bool) +// MacroFn is a compiler function which takes in an existing Expr and returns +// the llvm Value for it +type MacroFn func(BuildCtx, Expr) Expr -// 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 +// Ctx contains all the Macros and Identifiers available. A Ctx also keeps a +// reference to the global context, which has a number of macros available for +// all contexts to use. type Ctx struct { - Parent *Ctx - Macros map[Macro]MacroFn - - LastVal llvm.Value + global *Ctx + macros map[Macro]MacroFn + idents map[Identifier]llvm.Value } -// GetMacro returns the first instance of the given of the given Macro found. If -// not found nil is returned. +// NewCtx returns a blank context instance +func NewCtx() *Ctx { + return &Ctx{ + global: globalCtx, + macros: map[Macro]MacroFn{}, + idents: map[Identifier]llvm.Value{}, + } +} + +// GetMacro returns the MacroFn associated with the given identifier, or panics +// if the macro isn't found func (c *Ctx) GetMacro(m Macro) MacroFn { - if c.Macros != nil { - if fn, ok := c.Macros[m]; ok { - return fn - } + if fn := c.macros[m]; fn != nil { + return fn } - if c.Parent != nil { - return c.Parent.GetMacro(m) + if fn := c.global.macros[m]; fn != nil { + return fn } + panicf("macro %q not found in context", m) return nil } + +// GetIdentifier returns the llvm.Value for the Identifier, or panics +func (c *Ctx) GetIdentifier(i Identifier) llvm.Value { + if v, ok := c.idents[i]; ok { + return v + } + // The global context doesn't have any identifiers, so don't bother checking + panicf("identifier %q not found in context", i) + return llvm.Value{} +} + +// NewWith returns a new Ctx instance which imports the given macros from the +// parent +//func (c *Ctx) NewWith(mm ...Macro) *Ctx { +// nc := &Ctx{ +// global: c.global, +// macros: map[Macro]MacroFn{}, +// } +// for _, m := range mm { +// fn := c.macros[m] +// if fn == nil { +// panicf("no macro %q found in context", m) +// } +// nc.macros[m] = fn +// } +// return nc +//} diff --git a/expr/expr.go b/expr/expr.go index 84f4e53..32866d0 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -4,20 +4,10 @@ import ( "fmt" "llvm.org/llvm/bindings/go/llvm" - - "github.com/mediocregopher/ginger/lexer" ) -// TODO empty blocks? -// TODO empty parenthesis -// TODO need to figure out how to test LLVMVal stuff -// TODO once we're a bit more confident, make ActualFunc - -// 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 { -} +// Expr represents the actual expression in question. +type Expr interface{} // equaler is used to compare two expressions. The comparison should not take // into account Token values, only the actual value being represented @@ -25,30 +15,30 @@ type equaler interface { equal(equaler) bool } -// Expr contains the actual expression as well as some contextual information -// wrapping it. Most interactions will be with this and not with the Actual -// directly. -type Expr struct { - Actual Actual - - // Token is a nice-to-have, nothing will break if it's not there - Token lexer.Token - - val *llvm.Value -} - -// will panic if either Expr's Actual doesn't implement equaler -func (e Expr) equal(e2 Expr) bool { - eq1, ok1 := e.Actual.(equaler) - eq2, ok2 := e2.Actual.(equaler) +// will panic if either Expr doesn't implement equaler +func exprEqual(e1, e2 Expr) bool { + eq1, ok1 := e1.(equaler) + eq2, ok2 := e2.(equaler) if !ok1 || !ok2 { - panic(fmt.Sprintf("can't compare %T and %T", e.Actual, e2.Actual)) + panic(fmt.Sprintf("can't compare %T and %T", e1, e2)) } return eq1.equal(eq2) } //////////////////////////////////////////////////////////////////////////////// +// an Expr which simply wraps an existing llvm.Value +type llvmVal llvm.Value + +/* +func voidVal(lctx LLVMCtx) llvmVal { + return llvmVal{lctx.B.CreateRetVoid()} +} +*/ + +//////////////////////////////////////////////////////////////////////////////// + +/* // Void represents no data (size = 0) type Void struct{} @@ -56,6 +46,7 @@ func (v Void) equal(e equaler) bool { _, ok := e.(Void) return ok } +*/ //////////////////////////////////////////////////////////////////////////////// /* @@ -75,12 +66,6 @@ func (b Bool) equal(e equaler) bool { // Int represents an integer value type Int int64 -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 -} - func (i Int) equal(e equaler) bool { ii, ok := e.(Int) return ok && ii == i @@ -133,6 +118,11 @@ func (m Macro) equal(e equaler) bool { // they were a single value type Tuple []Expr +// NewTuple returns a Tuple around the given list of Exprs +func NewTuple(ee ...Expr) Tuple { + return Tuple(ee) +} + func (tup Tuple) String() string { return "(" + exprsJoin(tup) + ")" } @@ -152,12 +142,12 @@ type Statement struct { } func (s Statement) String() string { - return fmt.Sprintf("(%v %s)", s.Op.Actual, s.Arg.Actual) + return fmt.Sprintf("(%v %s)", s.Op, s.Arg) } func (s Statement) equal(e equaler) bool { ss, ok := e.(Statement) - return ok && s.Op.equal(ss.Op) && s.Arg.equal(ss.Arg) + return ok && exprEqual(s.Op, ss.Op) && exprEqual(s.Arg, ss.Arg) } //////////////////////////////////////////////////////////////////////////////// diff --git a/expr/macros.go b/expr/macros.go deleted file mode 100644 index 3abeb14..0000000 --- a/expr/macros.go +++ /dev/null @@ -1,23 +0,0 @@ -package expr - -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]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), "") - } - - 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 1cdc336..f783f48 100644 --- a/expr/util.go +++ b/expr/util.go @@ -18,7 +18,7 @@ func randStr() string { func exprsJoin(ee []Expr) string { strs := make([]string, len(ee)) for i := range ee { - strs[i] = fmt.Sprint(ee[i].Actual) + strs[i] = fmt.Sprint(ee[i]) } return strings.Join(strs, ", ") } @@ -28,7 +28,7 @@ func exprsEqual(ee1, ee2 []Expr) bool { return false } for i := range ee1 { - if !ee1[i].equal(ee2[i]) { + if !exprEqual(ee1[i], ee2[i]) { return false } } diff --git a/main.go b/main.go index 2a6a852..8054686 100644 --- a/main.go +++ b/main.go @@ -21,53 +21,52 @@ func main() { llvm.InitializeNativeTarget() llvm.InitializeNativeAsmPrinter() - // setup our builder and module - lctx := expr.LLVMCtx{ + // setup our context, builder, and module + bctx := expr.BuildCtx{ + C: expr.NewCtx(), B: llvm.NewBuilder(), M: llvm.NewModule("my_module"), } // do the work in the function - a := expr.Expr{Actual: expr.Int(1)} - b := expr.Expr{Actual: expr.Int(2)} - 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{Op: addMacro, Arg: tup}} + tup := expr.NewTuple(expr.Int(1), expr.Int(2)) + addMacro := expr.Macro("add") + stmt := expr.Statement{Op: addMacro, Arg: tup} + stmt = expr.Statement{Op: addMacro, Arg: expr.NewTuple(stmt, expr.Int(3))} //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)) + mainFn := llvm.AddFunction(bctx.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) + bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction()) + v := bctx.Build(stmt) + bctx.B.CreateRet(v) //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 { + if err := llvm.VerifyModule(bctx.M, llvm.ReturnStatusAction); err != nil { panic(err) } fmt.Println("# verified") // Dump the IR fmt.Println("# dumping IR") - lctx.M.Dump() + bctx.M.Dump() fmt.Println("# done dumping IR") // create our exe engine fmt.Println("# creating new execution engine") - engine, err := llvm.NewExecutionEngine(lctx.M) + engine, err := llvm.NewExecutionEngine(bctx.M) if err != nil { panic(err) } // run the function! fmt.Println("# running the function main") - funcResult := engine.RunFunction(lctx.M.NamedFunction("main"), []llvm.GenericValue{}) + funcResult := engine.RunFunction(bctx.M.NamedFunction("main"), []llvm.GenericValue{}) fmt.Printf("%d\n", funcResult.Int(false)) }