got variable assignment working

This commit is contained in:
Brian Picciano 2017-02-15 15:41:14 -07:00
parent 463d693b17
commit 685cde7afb
3 changed files with 119 additions and 22 deletions

24
main.go
View File

@ -9,16 +9,32 @@ import (
func main() { func main() {
mkcmd := func(a lang.Atom, args ...lang.Term) lang.Tuple { mkcmd := func(a lang.Atom, args ...lang.Term) lang.Tuple {
return lang.Tuple{a, lang.Tuple{vm.Tuple, lang.Tuple(args)}} if len(args) == 1 {
return lang.Tuple{a, args[0]}
}
return lang.Tuple{a, lang.Tuple(args)}
} }
mkint := func(i string) lang.Tuple { mkint := func(i string) lang.Tuple {
return lang.Tuple{vm.Int, lang.Const(i)} return lang.Tuple{vm.Int, lang.Const(i)}
} }
t := mkcmd(vm.Add, mkint("1"), //foo := lang.Atom("foo")
mkcmd(vm.Add, mkint("2"), mkint("3"))) //tt := []lang.Term{
// mkcmd(vm.Assign, foo, mkint("1")),
// mkcmd(vm.Add, mkcmd(vm.Tuple, mkcmd(vm.Var, foo), mkint("2"))),
//}
mod, err := vm.Build(t) foo := lang.Atom("foo")
bar := lang.Atom("bar")
baz := lang.Atom("baz")
tt := []lang.Term{
mkcmd(vm.Assign, foo, mkcmd(vm.Tuple, mkint("1"), mkint("2"))),
mkcmd(vm.Assign, bar, mkcmd(vm.Add, mkcmd(vm.Var, foo))),
mkcmd(vm.Assign, baz, mkcmd(vm.Add, mkcmd(vm.Var, foo))),
mkcmd(vm.Add, mkcmd(vm.Tuple, mkcmd(vm.Var, bar), mkcmd(vm.Var, baz))),
}
mod, err := vm.Build(tt...)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -30,6 +30,7 @@ func (vt valType) eq(vt2 valType) bool {
// primitive valTypes // primitive valTypes
var ( var (
valTypeVoid = valType{term: lang.Tuple{}, llvm: llvm.VoidType()}
valTypeInt = valType{term: Int, llvm: llvm.Int64Type()} valTypeInt = valType{term: Int, llvm: llvm.Int64Type()}
) )
@ -39,10 +40,7 @@ var (
type voidIn struct{} type voidIn struct{}
func (voidIn) inType() valType { func (voidIn) inType() valType {
return valType{ return valTypeVoid
term: lang.Tuple{},
llvm: llvm.VoidType(),
}
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -94,8 +92,9 @@ func (to tupOp) build(mod *Module) (llvm.Value, error) {
var err error var err error
for i := range to.els { for i := range to.els {
if val, err = to.els[i].build(mod); err != nil { if val, err = to.els[i].build(mod); err != nil {
str = mod.b.CreateInsertValue(str, val, i, "") return llvm.Value{}, err
} }
str = mod.b.CreateInsertValue(str, val, i, "")
} }
return str, err return str, err
} }
@ -130,6 +129,48 @@ func (teo tupElOp) build(mod *Module) (llvm.Value, error) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
type assignOp struct {
name string
op
}
func (ao assignOp) build(mod *Module) (llvm.Value, error) {
v, err := ao.op.build(mod)
if err != nil {
return llvm.Value{}, err
}
mod.ctx[ao.name] = builtOp{op: ao.op, v: v}
return v, nil
}
////////////////////////////////////////////////////////////////////////////////
type builtOp struct {
op
v llvm.Value
}
func (bo builtOp) build(*Module) (llvm.Value, error) {
return bo.v, nil
}
////////////////////////////////////////////////////////////////////////////////
type varOp struct {
op
name string
}
func (vo varOp) build(mod *Module) (llvm.Value, error) {
o, err := mod.ctx.get(vo.name)
if err != nil {
return llvm.Value{}, err
}
return o.build(mod)
}
////////////////////////////////////////////////////////////////////////////////
type addOp struct { type addOp struct {
voidIn voidIn
a, b op a, b op
@ -153,7 +194,7 @@ func (ao addOp) build(mod *Module) (llvm.Value, error) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
func termToOp(t lang.Term) (op, error) { func termToOp(ctx varCtx, t lang.Term) (op, error) {
aPat := func(a lang.Atom) lang.Tuple { aPat := func(a lang.Atom) lang.Tuple {
return lang.Tuple{lang.AAtom, a} return lang.Tuple{lang.AAtom, a}
} }
@ -172,7 +213,7 @@ func termToOp(t lang.Term) (op, error) {
// for when v is a Tuple argument, convenience function for casting // for when v is a Tuple argument, convenience function for casting
vAsTup := func(n int) ([]op, error) { vAsTup := func(n int) ([]op, error) {
vop, err := termToOp(v) vop, err := termToOp(ctx, v)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -198,11 +239,37 @@ func termToOp(t lang.Term) (op, error) {
tc := tupOp{els: make([]op, len(tup))} tc := tupOp{els: make([]op, len(tup))}
var err error var err error
for i := range tup { for i := range tup {
if tc.els[i], err = termToOp(tup[i]); err != nil { if tc.els[i], err = termToOp(ctx, tup[i]); err != nil {
return nil, err return nil, err
} }
} }
return tc, nil return tc, nil
case Assign:
if !lang.Match(tPat(aPat(lang.AUnder), lang.TDblUnder), v) {
return nil, errors.New("assign requires 2-tuple arg")
}
tup := v.(lang.Tuple)
name := tup[0].String()
o, err := termToOp(ctx, tup[1])
if err != nil {
return nil, err
}
ao := assignOp{op: o, name: name}
ctx[name] = o
return ao, nil
case Var:
if !lang.Match(aPat(lang.AUnder), v) {
return nil, errors.New("var requires atom arg")
}
name := v.(lang.Atom).String()
o, err := ctx.get(name)
if err != nil {
return nil, err
}
return varOp{op: o, name: name}, nil
// Add is special in some way, I think it's a function not a compiler op,
// not sure yet though
case Add: case Add:
els, err := vAsTup(2) els, err := vAsTup(2)
if err != nil { if err != nil {

View File

@ -17,24 +17,37 @@ var (
Int = lang.Atom("int") Int = lang.Atom("int")
) )
// Commands supported by the vm. Each of the types are also commands // Ops supported by the vm
var ( var (
Add = lang.Atom("add") Add = lang.Atom("add")
Assign = lang.Atom("assign")
Var = lang.Atom("var")
) )
type varCtx map[string]op
func (c varCtx) get(v string) (op, error) {
if o, ok := c[v]; ok {
return o, nil
}
return nil, fmt.Errorf("unknown assigned %q in this context", v)
}
// Module contains a compiled set of code which can be run, dumped in IR form, // Module contains a compiled set of code which can be run, dumped in IR form,
// or compiled. A Module should be Dispose()'d of once it's no longer being // or compiled. A Module should be Dispose()'d of once it's no longer being
// used. // used.
type Module struct { type Module struct {
b llvm.Builder b llvm.Builder
m llvm.Module m llvm.Module
ctx varCtx
mainFn llvm.Value mainFn llvm.Value
} }
var initOnce sync.Once var initOnce sync.Once
// Build creates a new Module by compiling the given Term as code // Build creates a new Module by compiling the given Terms as code
func Build(t lang.Term) (*Module, error) { // TODO only take in a single Term, implement List and use that with a do op
func Build(tt ...lang.Term) (*Module, error) {
initOnce.Do(func() { initOnce.Do(func() {
llvm.LinkInMCJIT() llvm.LinkInMCJIT()
llvm.InitializeNativeTarget() llvm.InitializeNativeTarget()
@ -43,10 +56,11 @@ func Build(t lang.Term) (*Module, error) {
mod := &Module{ mod := &Module{
b: llvm.NewBuilder(), b: llvm.NewBuilder(),
m: llvm.NewModule(""), m: llvm.NewModule(""),
ctx: varCtx{},
} }
var err error var err error
if mod.mainFn, err = mod.buildFn(t); err != nil { if mod.mainFn, err = mod.buildFn(tt...); err != nil {
mod.Dispose() mod.Dispose()
return nil, err return nil, err
} }
@ -75,7 +89,7 @@ func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) {
ops := make([]op, len(tt)) ops := make([]op, len(tt))
var err error var err error
for i := range tt { for i := range tt {
if ops[i], err = termToOp(tt[i]); err != nil { if ops[i], err = termToOp(mod.ctx, tt[i]); err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
} }
@ -95,7 +109,7 @@ func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) {
var out llvm.Value var out llvm.Value
for i := range ops { for i := range ops {
if out, err = ops[i].build(mod); err != nil { if out, err = ops[i].build(mod); err != nil {
return llvm.Value{}, nil return llvm.Value{}, err
} }
} }
mod.b.CreateRet(out) mod.b.CreateRet(out)