refactor to use Build and BuildStmt, remove a buttload of code

This commit is contained in:
Brian Picciano 2016-08-05 12:34:17 -06:00
parent bdd5711773
commit 813117c0f4
6 changed files with 116 additions and 284 deletions

28
expr/build.go Normal file
View File

@ -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
}
}

View File

@ -1,17 +1,23 @@
package expr 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 // 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 // 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 // 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 // this is that keys in the children take precedence over the parent's
type Ctx struct { type Ctx struct {
Parent *Ctx 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 // GetMacro returns the first instance of the given of the given Macro found. If
// not found nil is returned. // 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 c.Macros != nil {
if fn, ok := c.Macros[m]; ok { if fn, ok := c.Macros[m]; ok {
return fn return fn

View File

@ -2,7 +2,6 @@ package expr
import ( import (
"fmt" "fmt"
"strings"
"llvm.org/llvm/bindings/go/llvm" "llvm.org/llvm/bindings/go/llvm"
@ -13,25 +12,11 @@ import (
// TODO empty parenthesis // TODO empty parenthesis
// TODO need to figure out how to test LLVMVal stuff // TODO need to figure out how to test LLVMVal stuff
// TODO once we're a bit more confident, make ActualFunc // TODO once we're a bit more confident, make ActualFunc
// TODO LLVMVal -> LLVMBuild?
type LLVMCtx struct { // Actual represents the actual expression in question. It is wrapped by Expr
B llvm.Builder // which also holds onto contextual information, like the token to which Actual
M llvm.Module // was originally parsed from
}
// 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
type Actual interface { 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 // equaler is used to compare two expressions. The comparison should not take
@ -52,27 +37,6 @@ type Expr struct {
val *llvm.Value 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 // will panic if either Expr's Actual doesn't implement equaler
func (e Expr) equal(e2 Expr) bool { func (e Expr) equal(e2 Expr) bool {
eq1, ok1 := e.Actual.(equaler) eq1, ok1 := e.Actual.(equaler)
@ -88,21 +52,9 @@ func (e Expr) equal(e2 Expr) bool {
// Void represents no data (size = 0) // Void represents no data (size = 0)
type Void struct{} type Void struct{}
// LLVMInType implements the Actual interface method func (v Void) equal(e equaler) bool {
func (v Void) LLVMInType(ctx *Ctx) llvm.Type { _, ok := e.(Void)
panic("Void has no InType") return ok
}
// 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()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -110,21 +62,6 @@ func (v Void) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
// Bool represents a true or false value // Bool represents a true or false value
type Bool bool 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 { func (b Bool) equal(e equaler) bool {
bb, ok := e.(Bool) bb, ok := e.(Bool)
if !ok { if !ok {
@ -138,18 +75,7 @@ func (b Bool) equal(e equaler) bool {
// Int represents an integer value // Int represents an integer value
type Int int64 type Int int64
// LLVMInType implements the Actual interface method func (i Int) build(lctx LLVMCtx) llvm.Value {
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 {
v := lctx.B.CreateAlloca(llvm.Int64Type(), "") v := lctx.B.CreateAlloca(llvm.Int64Type(), "")
lctx.B.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v) lctx.B.CreateStore(llvm.ConstInt(llvm.Int64Type(), uint64(i), false), v)
return v return v
@ -157,10 +83,7 @@ func (i Int) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
func (i Int) equal(e equaler) bool { func (i Int) equal(e equaler) bool {
ii, ok := e.(Int) ii, ok := e.(Int)
if !ok { return ok && ii == i
return false
}
return ii == i
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -168,21 +91,6 @@ func (i Int) equal(e equaler) bool {
// String represents a string value // String represents a string value
type String string 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 { func (s String) equal(e equaler) bool {
ss, ok := e.(String) ss, ok := e.(String)
if !ok { if !ok {
@ -197,27 +105,9 @@ func (s String) equal(e equaler) bool {
// name // name
type Identifier string 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 { func (id Identifier) equal(e equaler) bool {
idid, ok := e.(Identifier) idid, ok := e.(Identifier)
if !ok { return ok && idid == id
return false
}
return idid == id
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -232,27 +122,9 @@ func (m Macro) String() string {
return "%" + string(m) 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 { func (m Macro) equal(e equaler) bool {
mm, ok := e.(Macro) mm, ok := e.(Macro)
if !ok { return ok && m == mm
return false
}
return m == mm
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -262,39 +134,12 @@ func (m Macro) equal(e equaler) bool {
type Tuple []Expr type Tuple []Expr
func (tup Tuple) String() string { func (tup Tuple) String() string {
strs := make([]string, len(tup)) return "(" + exprsJoin(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")
} }
func (tup Tuple) equal(e equaler) bool { func (tup Tuple) equal(e equaler) bool {
tuptup, ok := e.(Tuple) tuptup, ok := e.(Tuple)
if !ok || len(tuptup) != len(tup) { return ok && exprsEqual(tup, tuptup)
return false
}
for i := range tup {
if !tup[i].equal(tuptup[i]) {
return false
}
}
return true
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -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 // used as the input to the pipe, and the output of the pipe is the output of
// the statement // the statement
type Statement struct { type Statement struct {
// TODO change to Op and Arg Op, Arg Expr
In Expr
To Expr
} }
func (s Statement) String() string { func (s Statement) String() string {
return fmt.Sprintf("(%v > %s)", s.In.Actual, s.To.Actual) return fmt.Sprintf("(%v %s)", s.Op.Actual, s.Arg.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")
} }
func (s Statement) equal(e equaler) bool { func (s Statement) equal(e equaler) bool {
ss, ok := e.(Statement) 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 // 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 // statement binds a variable the rest of the statements in the block can use
// that variable, including sub-blocks within this one. // that variable
type Block []Expr type Block struct {
In []Expr
Stmts []Expr
Out []Expr
}
func (b Block) String() string { func (b Block) String() string {
strs := make([]string, len(b)) return fmt.Sprintf(
for i := range b { "{[%s][%s][%s]}",
strs[i] = b[i].Actual.(Statement).String() exprsJoin(b.In),
} exprsJoin(b.Stmts),
return fmt.Sprintf("{ %s }", strings.Join(strs, " ")) 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 { func (b Block) LLVMVal(ctx *Ctx, lctx LLVMCtx) llvm.Value {
name := randStr() // TODO make this based on token name := randStr() // TODO make this based on token
// TODO make these based on actual statements // 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) lctx.B.CreateRet(v)
return fn return fn
} }
*/
func (b Block) equal(e equaler) bool { func (b Block) equal(e equaler) bool {
bb, ok := e.(Block) bb, ok := e.(Block)
if !ok { return ok &&
return false exprsEqual(b.In, bb.In) &&
} exprsEqual(b.Stmts, bb.Stmts) &&
for i := range b { exprsEqual(b.Out, bb.Out)
if !b[i].equal(bb[i]) {
return false
}
}
return true
} }

View File

@ -1,45 +1,23 @@
package expr package expr
import ( import "llvm.org/llvm/bindings/go/llvm"
"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
}
// RootCtx describes what's available to *all* contexts, and is what all // RootCtx 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 RootCtx = &Ctx{ var RootCtx = &Ctx{
Macros: map[Macro]func(Expr) (Expr, error){ Macros: map[Macro]MacroFn{
"add": func(e Expr) (Expr, error) { "add": func(ctx *Ctx, lctx LLVMCtx, e Expr) (llvm.Value, bool) {
tup, ok := e.Actual.(Tuple) tup := e.Actual.(Tuple)
if !ok { buildInt := func(e Expr) llvm.Value {
// TODO proper error return lctx.B.CreateLoad(e.Actual.(Int).build(lctx), "")
return Expr{}, errors.New("add only accepts a tuple")
} }
// TODO check that it's a tuple of integers too
return Expr{ a := buildInt(tup[0])
Actual: addActual(tup), for i := range tup[1:] {
Token: e.Token, b := buildInt(tup[i+1])
}, nil a = lctx.B.CreateAdd(a, b, "")
}
return a, true
}, },
}, },
} }

View File

@ -2,7 +2,9 @@ package expr
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"math/rand" "math/rand"
"strings"
) )
func randStr() string { func randStr() string {
@ -12,3 +14,27 @@ func randStr() string {
} }
return hex.EncodeToString(b) 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...))
}

13
main.go
View File

@ -33,17 +33,20 @@ func main() {
c := expr.Expr{Actual: expr.Int(3)} c := expr.Expr{Actual: expr.Int(3)}
tup := expr.Expr{Actual: expr.Tuple([]expr.Expr{a, b, c})} tup := expr.Expr{Actual: expr.Tuple([]expr.Expr{a, b, c})}
addMacro := expr.Expr{Actual: expr.Macro("add")} addMacro := expr.Expr{Actual: expr.Macro("add")}
stmt := expr.Expr{Actual: expr.Statement{In: tup, To: addMacro}} stmt := expr.Expr{Actual: expr.Statement{Op: addMacro, Arg: tup}}
block := expr.Block([]expr.Expr{stmt})
fn := block.LLVMVal(expr.RootCtx, lctx) //block := expr.Block([]expr.Expr{stmt})
//fn := block.LLVMVal(expr.RootCtx, lctx)
// create main and call our function // create main and call our function
mainFn := llvm.AddFunction(lctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false)) mainFn := llvm.AddFunction(lctx.M, "main", llvm.FunctionType(llvm.Int64Type(), []llvm.Type{}, false))
mainBlock := llvm.AddBasicBlock(mainFn, "entry") mainBlock := llvm.AddBasicBlock(mainFn, "entry")
lctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction()) 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{}, "") //ret := lctx.B.CreateCall(fn, []llvm.Value{}, "")
lctx.B.CreateRet(ret) //lctx.B.CreateRet(ret)
// verify it's all good // verify it's all good
if err := llvm.VerifyModule(lctx.M, llvm.ReturnStatusAction); err != nil { if err := llvm.VerifyModule(lctx.M, llvm.ReturnStatusAction); err != nil {