From 51367a253a5118bc7a3d11b739d2b9ad6760b33e Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Mon, 8 Aug 2016 19:43:05 -0600 Subject: [PATCH] implement ctx management macros, and do.... oooooh yeaaaaaah --- expr/build.go | 113 +++++++++++++++++++++++++++++++++++--------------- expr/ctx.go | 9 ++-- expr/expr.go | 56 ++++--------------------- main.go | 57 ++++++++++++++++++------- 4 files changed, 134 insertions(+), 101 deletions(-) diff --git a/expr/build.go b/expr/build.go index 3cc5637..517f64b 100644 --- a/expr/build.go +++ b/expr/build.go @@ -1,18 +1,24 @@ package expr -import "llvm.org/llvm/bindings/go/llvm" +import ( + "fmt" + + "llvm.org/llvm/bindings/go/llvm" +) type BuildCtx struct { - C *Ctx B llvm.Builder M llvm.Module } -func (bctx BuildCtx) Build(stmts ...Statement) llvm.Value { +func (bctx BuildCtx) Build(ctx Ctx, stmts ...Statement) llvm.Value { var lastVal llvm.Value for _, stmt := range stmts { - if e := bctx.BuildStmt(stmt); e != nil { - lastVal = bctx.buildVal(e) + fmt.Println(stmt) + if e := bctx.BuildStmt(ctx, stmt); e != nil { + if lv, ok := e.(llvmVal); ok { + lastVal = llvm.Value(lv) + } } } if (lastVal == llvm.Value{}) { @@ -21,19 +27,19 @@ func (bctx BuildCtx) Build(stmts ...Statement) llvm.Value { return lastVal } -func (bctx BuildCtx) BuildStmt(s Statement) Expr { +func (bctx BuildCtx) BuildStmt(ctx Ctx, s Statement) Expr { m := s.Op.(Macro) - return bctx.C.Macro(m)(bctx, s.Arg) + return ctx.Macro(m)(bctx, ctx, s.Arg) } // may return nil if e is a Statement which has no return -func (bctx BuildCtx) buildExpr(e Expr) Expr { - return bctx.buildExprTill(e, func(Expr) bool { return false }) +func (bctx BuildCtx) buildExpr(ctx Ctx, e Expr) Expr { + return bctx.buildExprTill(ctx, e, func(Expr) bool { return false }) } // like buildExpr, but will stop short and stop recursing when the function // returns true -func (bctx BuildCtx) buildExprTill(e Expr, fn func(e Expr) bool) Expr { +func (bctx BuildCtx) buildExprTill(ctx Ctx, e Expr, fn func(e Expr) bool) Expr { if fn(e) { return e } @@ -44,45 +50,86 @@ func (bctx BuildCtx) buildExprTill(e Expr, fn func(e Expr) bool) Expr { case Int: return llvmVal(llvm.ConstInt(llvm.Int64Type(), uint64(ea), false)) case Identifier: - return bctx.C.Identifier(ea) + return ctx.Identifier(ea) case Statement: - return bctx.BuildStmt(ea) + return bctx.BuildStmt(ctx, ea) case Tuple: for i := range ea { - ea[i] = bctx.buildExprTill(ea[i], fn) + ea[i] = bctx.buildExprTill(ctx, ea[i], fn) } return ea case List: for i := range ea { - ea[i] = bctx.buildExprTill(ea[i], fn) + ea[i] = bctx.buildExprTill(ctx, ea[i], fn) } return ea + case Ctx: + return ea default: - panicf("type %T can't express a value", ea) + panicf("%v (type %T) can't express a value", ea, ea) } panic("go is dumb") } -func (bctx BuildCtx) buildVal(e Expr) llvm.Value { - return llvm.Value(bctx.buildExpr(e).(llvmVal)) +func (bctx BuildCtx) buildVal(ctx Ctx, e Expr) llvm.Value { + return llvm.Value(bctx.buildExpr(ctx, 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 := bctx.buildExpr(e).(Tuple) - a := bctx.buildVal(tup[0]) - b := bctx.buildVal(tup[1]) - return llvmVal(bctx.B.CreateAdd(a, b, "")) - }, +// contexts should have as the root parent in the tree. +// +// We define in this weird way cause NewCtx actually references globalCtx +var globalCtx *Ctx +var _ = func() bool { + globalCtx = &Ctx{ + macros: map[Macro]MacroFn{ + "add": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + tup := bctx.buildExpr(ctx, e).(Tuple) + a := bctx.buildVal(ctx, tup[0]) + b := bctx.buildVal(ctx, tup[1]) + return llvmVal(bctx.B.CreateAdd(a, b, "")) + }, - "bind": func(bctx BuildCtx, e Expr) Expr { - tup := bctx.buildExprTill(e, isIdentifier).(Tuple) - id := bctx.buildExprTill(tup[0], isIdentifier).(Identifier) - *bctx.C = bctx.C.Bind(id, bctx.buildExpr(tup[1])) - return nil + // TODO this chould be a user macro!!!! WUT this language is baller + "bind": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + tup := bctx.buildExprTill(ctx, e, isIdentifier).(Tuple) + id := bctx.buildExprTill(ctx, tup[0], isIdentifier).(Identifier) + ctx.Bind(id, bctx.buildExpr(ctx, tup[1])) + return NewTuple() + }, + + "ctxnew": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + return NewCtx() + }, + + "ctxthis": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + return ctx + }, + + "ctxbind": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + tup := bctx.buildExprTill(ctx, e, isIdentifier).(Tuple) + thisCtx := bctx.buildExpr(ctx, tup[0]).(Ctx) + id := bctx.buildExprTill(ctx, tup[1], isIdentifier).(Identifier) + thisCtx.Bind(id, bctx.buildExpr(ctx, tup[2])) + return NewTuple() + }, + + "ctxget": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + tup := bctx.buildExprTill(ctx, e, isIdentifier).(Tuple) + thisCtx := bctx.buildExpr(ctx, tup[0]).(Ctx) + id := bctx.buildExprTill(ctx, tup[1], isIdentifier).(Identifier) + return thisCtx.Identifier(id) + }, + + "do": func(bctx BuildCtx, ctx Ctx, e Expr) Expr { + tup := bctx.buildExprTill(ctx, e, isStmt).(Tuple) + thisCtx := tup[0].(Ctx) + for _, stmtE := range tup[1].(List) { + bctx.BuildStmt(thisCtx, stmtE.(Statement)) + } + return NewTuple() + }, }, - }, -} + } + return false +}() diff --git a/expr/ctx.go b/expr/ctx.go index 4df708f..5c05a3d 100644 --- a/expr/ctx.go +++ b/expr/ctx.go @@ -2,7 +2,7 @@ package expr // MacroFn is a compiler function which takes in an existing Expr and returns // the llvm Value for it -type MacroFn func(BuildCtx, Expr) Expr +type MacroFn func(BuildCtx, Ctx, Expr) Expr // 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 @@ -45,7 +45,8 @@ func (c Ctx) Identifier(i Identifier) Expr { panic("go is dumb") } -func (c Ctx) cp() Ctx { +// Copy returns a deep copy of the Ctx +func (c Ctx) Copy() Ctx { cc := Ctx{ global: c.global, macros: make(map[Macro]MacroFn, len(c.macros)), @@ -63,11 +64,9 @@ func (c Ctx) cp() Ctx { // Bind returns a new Ctx which is a copy of this one, but with the given // Identifier bound to the given Expr. Will panic if the Identifier is already // bound -func (c Ctx) Bind(i Identifier, e Expr) Ctx { +func (c Ctx) Bind(i Identifier, e Expr) { if _, ok := c.idents[i]; ok { panicf("identifier %q is already bound", i) } - c = c.cp() c.idents[i] = e - return c } diff --git a/expr/expr.go b/expr/expr.go index 8ef915a..baea022 100644 --- a/expr/expr.go +++ b/expr/expr.go @@ -167,14 +167,16 @@ type Statement struct { } // NewStatement returns a Statement whose Op is the first Expr. If the given -// list is empty Arg will be nil, if its length is one Arg will be that single -// Expr, otherwise Arg will be a Tuple of the list +// list is empty Arg will be 0-tuple, if its length is one Arg will be that +// single Expr, otherwise Arg will be a Tuple of the list func NewStatement(e Expr, ee ...Expr) Statement { s := Statement{Op: e} if len(ee) > 1 { s.Arg = NewTuple(ee...) } else if len(ee) == 1 { s.Arg = ee[0] + } else if len(ee) == 0 { + s.Arg = NewTuple() } return s } @@ -188,51 +190,7 @@ func (s Statement) equal(e equaler) bool { return ok && exprEqual(s.Op, ss.Op) && exprEqual(s.Arg, 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 -type Block struct { - In []Expr - Stmts []Expr - Out []Expr -} - -func (b Block) String() string { - return fmt.Sprintf( - "{[%s][%s][%s]}", - exprsJoin(b.In), - exprsJoin(b.Stmts), - exprsJoin(b.Out), - ) -} - -/* -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 - out := llvm.Int64Type() - in := []llvm.Type{} - fn := llvm.AddFunction(lctx.M, name, llvm.FunctionType(out, in, false)) - block := llvm.AddBasicBlock(fn, "entry") - lctx.B.SetInsertPoint(block, block.FirstInstruction()) - - var v llvm.Value - for _, se := range b { - v = se.Actual.LLVMVal(ctx, lctx) - } - // last v is used as return - // TODO empty return - lctx.B.CreateRet(v) - return fn -} -*/ - -func (b Block) equal(e equaler) bool { - bb, ok := e.(Block) - return ok && - exprsEqual(b.In, bb.In) && - exprsEqual(b.Stmts, bb.Stmts) && - exprsEqual(b.Out, bb.Out) +func isStmt(e Expr) bool { + _, ok := e.(Statement) + return ok } diff --git a/main.go b/main.go index 8bdfb1b..998093b 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,6 @@ func main() { // setup our context, builder, and module ctx := expr.NewCtx() bctx := expr.BuildCtx{ - C: &ctx, B: llvm.NewBuilder(), M: llvm.NewModule("my_module"), } @@ -32,29 +31,59 @@ func main() { // do the work in the function add := expr.Macro("add") bind := expr.Macro("bind") + do := expr.Macro("do") + ctxnew := expr.Macro("ctxnew") + ctxbind := expr.Macro("ctxbind") + ctxget := expr.Macro("ctxget") + + ctx1 := expr.Identifier("ctx1") + ctx2 := expr.Identifier("ctx2") idA := expr.Identifier("A") idB := expr.Identifier("B") - idC := expr.Identifier("C") - stmts := []expr.Statement{ - expr.NewStatement(bind, idA, expr.NewStatement(add, expr.Int(1), expr.Int(2))), - expr.NewStatement(bind, idB, expr.Int(3)), - expr.NewStatement(bind, idC, expr.NewTuple(idA, idB)), - expr.NewStatement(add, expr.NewStatement(add, idC), expr.NewStatement(add, idC)), - } - //block := expr.Block([]expr.Expr{stmt}) - //fn := block.LLVMVal(expr.RootCtx, lctx) + //myAdd := expr.Identifier("myAdd") + out := expr.Identifier("out") + // TODO we couldn't actually use this either, because the builder was + // changing out the internal values of the List the first time it was hit, + // and then just using those the second time around + //myAddStmts := expr.NewList( + // expr.NewStatement(bind, out, expr.NewStatement(add, idA, idB)), + //) + + stmts := []expr.Statement{ + // TODO revisit how bind and related macros (maybe all macros?) deal + // with arguments and their evaluation (keeping an identifier vs + // eval-ing it) + //expr.NewStatement(bind, myAdd, myAddStmts), + + expr.NewStatement(bind, ctx1, expr.NewStatement(ctxnew)), + expr.NewStatement(ctxbind, ctx1, idA, expr.Int(1)), + expr.NewStatement(ctxbind, ctx1, idB, expr.Int(2)), + expr.NewStatement(do, ctx1, expr.NewList( + expr.NewStatement(bind, out, expr.NewStatement(add, idA, idB)), + )), + + expr.NewStatement(bind, ctx2, expr.NewStatement(ctxnew)), + expr.NewStatement(ctxbind, ctx2, idA, expr.Int(3)), + expr.NewStatement(ctxbind, ctx2, idB, expr.Int(4)), + expr.NewStatement(do, ctx2, expr.NewList( + expr.NewStatement(bind, out, expr.NewStatement(add, idA, idB)), + )), + + expr.NewStatement( + add, + expr.NewStatement(ctxget, ctx1, out), + expr.NewStatement(ctxget, ctx2, out), + ), + } // create main and call our function mainFn := llvm.AddFunction(bctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)) mainBlock := llvm.AddBasicBlock(mainFn, "entry") bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction()) - v := bctx.Build(stmts...) + v := bctx.Build(ctx, stmts...) bctx.B.CreateRet(v) - //ret := lctx.B.CreateCall(fn, []llvm.Value{}, "") - //lctx.B.CreateRet(ret) - // verify it's all good if err := llvm.VerifyModule(bctx.M, llvm.ReturnStatusAction); err != nil { panic(err)