refactor a lot, got recursive statement eval kind of working

This commit is contained in:
Brian Picciano 2016-08-06 12:20:53 -06:00
parent 813117c0f4
commit 38d2d8893b
6 changed files with 158 additions and 106 deletions

View File

@ -2,27 +2,78 @@ package expr
import "llvm.org/llvm/bindings/go/llvm" import "llvm.org/llvm/bindings/go/llvm"
type LLVMCtx struct { type BuildCtx struct {
C *Ctx
B llvm.Builder B llvm.Builder
M llvm.Module 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 { 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) { func (bctx BuildCtx) BuildStmt(s Statement) Expr {
s := stmtE.Actual.(Statement) m := s.Op.(Macro)
m := s.Op.Actual.(Macro)
fn := ctx.GetMacro(m) fn := bctx.C.GetMacro(m)
if fn == nil { if fn == nil {
panicf("unknown macro: %q", m) panicf("unknown macro: %q", m)
} }
lv, ok := fn(ctx, lctx, s.Arg)
if ok { return fn(bctx, bctx.buildExpr(s.Arg))
ctx.LastVal = lv
} }
// 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
},
},
} }

View File

@ -2,29 +2,64 @@ package expr
import "llvm.org/llvm/bindings/go/llvm" 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 // Ctx contains all the Macros and Identifiers available. A Ctx also keeps a
// parent it was created from. If the current Ctx doesn't have a particular key // reference to the global context, which has a number of macros available for
// being looked up, the parent is called instead, and so on. A consequence of // all contexts to use.
// this is that keys in the children take precedence over the parent's
type Ctx struct { type Ctx struct {
Parent *Ctx global *Ctx
Macros map[Macro]MacroFn macros map[Macro]MacroFn
idents map[Identifier]llvm.Value
LastVal llvm.Value
} }
// GetMacro returns the first instance of the given of the given Macro found. If // NewCtx returns a blank context instance
// not found nil is returned. 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 { func (c *Ctx) GetMacro(m Macro) MacroFn {
if c.Macros != nil { if fn := c.macros[m]; fn != nil {
if fn, ok := c.Macros[m]; ok {
return fn return fn
} }
if fn := c.global.macros[m]; fn != nil {
return fn
} }
if c.Parent != nil { panicf("macro %q not found in context", m)
return c.Parent.GetMacro(m)
}
return nil 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
//}

View File

@ -4,20 +4,10 @@ import (
"fmt" "fmt"
"llvm.org/llvm/bindings/go/llvm" "llvm.org/llvm/bindings/go/llvm"
"github.com/mediocregopher/ginger/lexer"
) )
// TODO empty blocks? // Expr represents the actual expression in question.
// TODO empty parenthesis type Expr interface{}
// 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 {
}
// equaler is used to compare two expressions. The comparison should not take // equaler is used to compare two expressions. The comparison should not take
// into account Token values, only the actual value being represented // into account Token values, only the actual value being represented
@ -25,30 +15,30 @@ type equaler interface {
equal(equaler) bool equal(equaler) bool
} }
// Expr contains the actual expression as well as some contextual information // will panic if either Expr doesn't implement equaler
// wrapping it. Most interactions will be with this and not with the Actual func exprEqual(e1, e2 Expr) bool {
// directly. eq1, ok1 := e1.(equaler)
type Expr struct { eq2, ok2 := e2.(equaler)
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)
if !ok1 || !ok2 { 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) 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) // Void represents no data (size = 0)
type Void struct{} type Void struct{}
@ -56,6 +46,7 @@ func (v Void) equal(e equaler) bool {
_, ok := e.(Void) _, ok := e.(Void)
return ok return ok
} }
*/
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/* /*
@ -75,12 +66,6 @@ func (b Bool) equal(e equaler) bool {
// Int represents an integer value // Int represents an integer value
type Int int64 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 { func (i Int) equal(e equaler) bool {
ii, ok := e.(Int) ii, ok := e.(Int)
return ok && ii == i return ok && ii == i
@ -133,6 +118,11 @@ func (m Macro) equal(e equaler) bool {
// they were a single value // they were a single value
type Tuple []Expr 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 { func (tup Tuple) String() string {
return "(" + exprsJoin(tup) + ")" return "(" + exprsJoin(tup) + ")"
} }
@ -152,12 +142,12 @@ type Statement struct {
} }
func (s Statement) String() string { 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 { func (s Statement) equal(e equaler) bool {
ss, ok := e.(Statement) 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)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

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

View File

@ -18,7 +18,7 @@ func randStr() string {
func exprsJoin(ee []Expr) string { func exprsJoin(ee []Expr) string {
strs := make([]string, len(ee)) strs := make([]string, len(ee))
for i := range ee { for i := range ee {
strs[i] = fmt.Sprint(ee[i].Actual) strs[i] = fmt.Sprint(ee[i])
} }
return strings.Join(strs, ", ") return strings.Join(strs, ", ")
} }
@ -28,7 +28,7 @@ func exprsEqual(ee1, ee2 []Expr) bool {
return false return false
} }
for i := range ee1 { for i := range ee1 {
if !ee1[i].equal(ee2[i]) { if !exprEqual(ee1[i], ee2[i]) {
return false return false
} }
} }

31
main.go
View File

@ -21,53 +21,52 @@ func main() {
llvm.InitializeNativeTarget() llvm.InitializeNativeTarget()
llvm.InitializeNativeAsmPrinter() llvm.InitializeNativeAsmPrinter()
// setup our builder and module // setup our context, builder, and module
lctx := expr.LLVMCtx{ bctx := expr.BuildCtx{
C: expr.NewCtx(),
B: llvm.NewBuilder(), B: llvm.NewBuilder(),
M: llvm.NewModule("my_module"), M: llvm.NewModule("my_module"),
} }
// do the work in the function // do the work in the function
a := expr.Expr{Actual: expr.Int(1)} tup := expr.NewTuple(expr.Int(1), expr.Int(2))
b := expr.Expr{Actual: expr.Int(2)} addMacro := expr.Macro("add")
c := expr.Expr{Actual: expr.Int(3)} stmt := expr.Statement{Op: addMacro, Arg: tup}
tup := expr.Expr{Actual: expr.Tuple([]expr.Expr{a, b, c})} stmt = expr.Statement{Op: addMacro, Arg: expr.NewTuple(stmt, expr.Int(3))}
addMacro := expr.Expr{Actual: expr.Macro("add")}
stmt := expr.Expr{Actual: expr.Statement{Op: addMacro, Arg: tup}}
//block := expr.Block([]expr.Expr{stmt}) //block := expr.Block([]expr.Expr{stmt})
//fn := block.LLVMVal(expr.RootCtx, lctx) //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(bctx.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()) bctx.B.SetInsertPoint(mainBlock, mainBlock.FirstInstruction())
expr.BuildStmt(expr.RootCtx, lctx, stmt) v := bctx.Build(stmt)
lctx.B.CreateRet(expr.RootCtx.LastVal) bctx.B.CreateRet(v)
//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(bctx.M, llvm.ReturnStatusAction); err != nil {
panic(err) panic(err)
} }
fmt.Println("# verified") fmt.Println("# verified")
// Dump the IR // Dump the IR
fmt.Println("# dumping IR") fmt.Println("# dumping IR")
lctx.M.Dump() bctx.M.Dump()
fmt.Println("# done dumping IR") fmt.Println("# done dumping IR")
// create our exe engine // create our exe engine
fmt.Println("# creating new execution engine") fmt.Println("# creating new execution engine")
engine, err := llvm.NewExecutionEngine(lctx.M) engine, err := llvm.NewExecutionEngine(bctx.M)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// run the function! // run the function!
fmt.Println("# running the function main") 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)) fmt.Printf("%d\n", funcResult.Int(false))
} }