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"
|
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
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
67
expr/ctx.go
67
expr/ctx.go
@ -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
|
||||||
|
//}
|
||||||
|
64
expr/expr.go
64
expr/expr.go
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -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 {
|
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
31
main.go
@ -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))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user