refactor a lot, got recursive statement eval kind of working

viewRender
Brian Picciano 8 years ago
parent 813117c0f4
commit 38d2d8893b
  1. 71
      expr/build.go
  2. 67
      expr/ctx.go
  3. 64
      expr/expr.go
  4. 23
      expr/macros.go
  5. 4
      expr/util.go
  6. 31
      main.go

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

@ -2,29 +2,64 @@ package expr
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
// 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
// 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
// all contexts to use.
type Ctx struct {
Parent *Ctx
Macros map[Macro]MacroFn
global *Ctx
macros map[Macro]MacroFn
idents map[Identifier]llvm.Value
}
LastVal llvm.Value
// NewCtx returns a blank context instance
func NewCtx() *Ctx {
return &Ctx{
global: globalCtx,
macros: map[Macro]MacroFn{},
idents: map[Identifier]llvm.Value{},
}
}
// GetMacro returns the first instance of the given of the given Macro found. If
// not found nil is returned.
// GetMacro returns the MacroFn associated with the given identifier, or panics
// if the macro isn't found
func (c *Ctx) GetMacro(m Macro) MacroFn {
if c.Macros != nil {
if fn, ok := c.Macros[m]; ok {
return fn
}
if fn := c.macros[m]; fn != nil {
return fn
}
if c.Parent != nil {
return c.Parent.GetMacro(m)
if fn := c.global.macros[m]; fn != nil {
return fn
}
panicf("macro %q not found in context", m)
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
//}

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

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

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

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

Loading…
Cancel
Save