implement ctx management macros, and do.... oooooh yeaaaaaah

This commit is contained in:
Brian Picciano 2016-08-08 19:43:05 -06:00
parent f751924b26
commit 51367a253a
4 changed files with 134 additions and 101 deletions

View File

@ -1,18 +1,24 @@
package expr package expr
import "llvm.org/llvm/bindings/go/llvm" import (
"fmt"
"llvm.org/llvm/bindings/go/llvm"
)
type BuildCtx struct { type BuildCtx struct {
C *Ctx
B llvm.Builder B llvm.Builder
M llvm.Module 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 var lastVal llvm.Value
for _, stmt := range stmts { for _, stmt := range stmts {
if e := bctx.BuildStmt(stmt); e != nil { fmt.Println(stmt)
lastVal = bctx.buildVal(e) if e := bctx.BuildStmt(ctx, stmt); e != nil {
if lv, ok := e.(llvmVal); ok {
lastVal = llvm.Value(lv)
}
} }
} }
if (lastVal == llvm.Value{}) { if (lastVal == llvm.Value{}) {
@ -21,19 +27,19 @@ func (bctx BuildCtx) Build(stmts ...Statement) llvm.Value {
return lastVal return lastVal
} }
func (bctx BuildCtx) BuildStmt(s Statement) Expr { func (bctx BuildCtx) BuildStmt(ctx Ctx, s Statement) Expr {
m := s.Op.(Macro) 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 // may return nil if e is a Statement which has no return
func (bctx BuildCtx) buildExpr(e Expr) Expr { func (bctx BuildCtx) buildExpr(ctx Ctx, e Expr) Expr {
return bctx.buildExprTill(e, func(Expr) bool { return false }) return bctx.buildExprTill(ctx, e, func(Expr) bool { return false })
} }
// like buildExpr, but will stop short and stop recursing when the function // like buildExpr, but will stop short and stop recursing when the function
// returns true // 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) { if fn(e) {
return e return e
} }
@ -44,45 +50,86 @@ func (bctx BuildCtx) buildExprTill(e Expr, fn func(e Expr) bool) Expr {
case Int: case Int:
return llvmVal(llvm.ConstInt(llvm.Int64Type(), uint64(ea), false)) return llvmVal(llvm.ConstInt(llvm.Int64Type(), uint64(ea), false))
case Identifier: case Identifier:
return bctx.C.Identifier(ea) return ctx.Identifier(ea)
case Statement: case Statement:
return bctx.BuildStmt(ea) return bctx.BuildStmt(ctx, ea)
case Tuple: case Tuple:
for i := range ea { for i := range ea {
ea[i] = bctx.buildExprTill(ea[i], fn) ea[i] = bctx.buildExprTill(ctx, ea[i], fn)
} }
return ea return ea
case List: case List:
for i := range ea { for i := range ea {
ea[i] = bctx.buildExprTill(ea[i], fn) ea[i] = bctx.buildExprTill(ctx, ea[i], fn)
} }
return ea return ea
case Ctx:
return ea
default: 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") panic("go is dumb")
} }
func (bctx BuildCtx) buildVal(e Expr) llvm.Value { func (bctx BuildCtx) buildVal(ctx Ctx, e Expr) llvm.Value {
return llvm.Value(bctx.buildExpr(e).(llvmVal)) return llvm.Value(bctx.buildExpr(ctx, e).(llvmVal))
} }
// globalCtx describes what's available to *all* contexts, and is what all // globalCtx describes what's available to *all* contexts, and is what all
// contexts should have as the root parent in the tree // contexts should have as the root parent in the tree.
var globalCtx = &Ctx{ //
macros: map[Macro]MacroFn{ // We define in this weird way cause NewCtx actually references globalCtx
"add": func(bctx BuildCtx, e Expr) Expr { var globalCtx *Ctx
tup := bctx.buildExpr(e).(Tuple) var _ = func() bool {
a := bctx.buildVal(tup[0]) globalCtx = &Ctx{
b := bctx.buildVal(tup[1]) macros: map[Macro]MacroFn{
return llvmVal(bctx.B.CreateAdd(a, b, "")) "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 { // TODO this chould be a user macro!!!! WUT this language is baller
tup := bctx.buildExprTill(e, isIdentifier).(Tuple) "bind": func(bctx BuildCtx, ctx Ctx, e Expr) Expr {
id := bctx.buildExprTill(tup[0], isIdentifier).(Identifier) tup := bctx.buildExprTill(ctx, e, isIdentifier).(Tuple)
*bctx.C = bctx.C.Bind(id, bctx.buildExpr(tup[1])) id := bctx.buildExprTill(ctx, tup[0], isIdentifier).(Identifier)
return nil 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
}()

View File

@ -2,7 +2,7 @@ package expr
// MacroFn is a compiler function which takes in an existing Expr and returns // MacroFn is a compiler function which takes in an existing Expr and returns
// the llvm Value for it // 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 // 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 // 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") panic("go is dumb")
} }
func (c Ctx) cp() Ctx { // Copy returns a deep copy of the Ctx
func (c Ctx) Copy() Ctx {
cc := Ctx{ cc := Ctx{
global: c.global, global: c.global,
macros: make(map[Macro]MacroFn, len(c.macros)), 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 // 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 // Identifier bound to the given Expr. Will panic if the Identifier is already
// bound // bound
func (c Ctx) Bind(i Identifier, e Expr) Ctx { func (c Ctx) Bind(i Identifier, e Expr) {
if _, ok := c.idents[i]; ok { if _, ok := c.idents[i]; ok {
panicf("identifier %q is already bound", i) panicf("identifier %q is already bound", i)
} }
c = c.cp()
c.idents[i] = e c.idents[i] = e
return c
} }

View File

@ -167,14 +167,16 @@ type Statement struct {
} }
// NewStatement returns a Statement whose Op is the first Expr. If the given // 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 // list is empty Arg will be 0-tuple, if its length is one Arg will be that
// Expr, otherwise Arg will be a Tuple of the list // single Expr, otherwise Arg will be a Tuple of the list
func NewStatement(e Expr, ee ...Expr) Statement { func NewStatement(e Expr, ee ...Expr) Statement {
s := Statement{Op: e} s := Statement{Op: e}
if len(ee) > 1 { if len(ee) > 1 {
s.Arg = NewTuple(ee...) s.Arg = NewTuple(ee...)
} else if len(ee) == 1 { } else if len(ee) == 1 {
s.Arg = ee[0] s.Arg = ee[0]
} else if len(ee) == 0 {
s.Arg = NewTuple()
} }
return s 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) return ok && exprEqual(s.Op, ss.Op) && exprEqual(s.Arg, ss.Arg)
} }
//////////////////////////////////////////////////////////////////////////////// func isStmt(e Expr) bool {
_, ok := e.(Statement)
// Block represents a set of statements which share a scope, i.e. If one return ok
// 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)
} }

57
main.go
View File

@ -24,7 +24,6 @@ func main() {
// setup our context, builder, and module // setup our context, builder, and module
ctx := expr.NewCtx() ctx := expr.NewCtx()
bctx := expr.BuildCtx{ bctx := expr.BuildCtx{
C: &ctx,
B: llvm.NewBuilder(), B: llvm.NewBuilder(),
M: llvm.NewModule("my_module"), M: llvm.NewModule("my_module"),
} }
@ -32,29 +31,59 @@ func main() {
// do the work in the function // do the work in the function
add := expr.Macro("add") add := expr.Macro("add")
bind := expr.Macro("bind") 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") idA := expr.Identifier("A")
idB := expr.Identifier("B") 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}) //myAdd := expr.Identifier("myAdd")
//fn := block.LLVMVal(expr.RootCtx, lctx) 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 // create main and call our function
mainFn := llvm.AddFunction(bctx.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") mainBlock := llvm.AddBasicBlock(mainFn, "entry")
bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction()) bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction())
v := bctx.Build(stmts...) v := bctx.Build(ctx, stmts...)
bctx.B.CreateRet(v) bctx.B.CreateRet(v)
//ret := lctx.B.CreateCall(fn, []llvm.Value{}, "")
//lctx.B.CreateRet(ret)
// verify it's all good // verify it's all good
if err := llvm.VerifyModule(bctx.M, llvm.ReturnStatusAction); err != nil { if err := llvm.VerifyModule(bctx.M, llvm.ReturnStatusAction); err != nil {
panic(err) panic(err)