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

View File

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

View File

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

View File

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

13
main.go
View File

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