refactor a lot, got recursive statement eval kind of working
This commit is contained in:
parent
813117c0f4
commit
38d2d8893b
@ -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
|
||||
},
|
||||
},
|
||||
}
|
||||
|
67
expr/ctx.go
67
expr/ctx.go
@ -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
|
||||
|
||||
LastVal llvm.Value
|
||||
global *Ctx
|
||||
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.
|
||||
// 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 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 {
|
||||
if fn := c.macros[m]; fn != nil {
|
||||
return fn
|
||||
}
|
||||
if fn := c.global.macros[m]; fn != nil {
|
||||
return fn
|
||||
}
|
||||
if c.Parent != nil {
|
||||
return c.Parent.GetMacro(m)
|
||||
}
|
||||
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
|
||||
//}
|
||||
|
64
expr/expr.go
64
expr/expr.go
@ -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
|
||||
}
|
||||
}
|
||||
|
31
main.go
31
main.go
@ -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…
Reference in New Issue
Block a user