From 685cde7afba436c37abdf00d5c9afd6ef026139a Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Wed, 15 Feb 2017 15:41:14 -0700 Subject: [PATCH] got variable assignment working --- main.go | 24 ++++++++++++--- vm/cmds.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------ vm/vm.go | 32 ++++++++++++++------ 3 files changed, 119 insertions(+), 22 deletions(-) diff --git a/main.go b/main.go index 8eee009..3194c67 100644 --- a/main.go +++ b/main.go @@ -9,16 +9,32 @@ import ( func main() { 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 { return lang.Tuple{vm.Int, lang.Const(i)} } - t := mkcmd(vm.Add, mkint("1"), - mkcmd(vm.Add, mkint("2"), mkint("3"))) + //foo := lang.Atom("foo") + //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 { panic(err) } diff --git a/vm/cmds.go b/vm/cmds.go index 9571b54..fedb071 100644 --- a/vm/cmds.go +++ b/vm/cmds.go @@ -30,7 +30,8 @@ func (vt valType) eq(vt2 valType) bool { // primitive valTypes var ( - valTypeInt = valType{term: Int, llvm: llvm.Int64Type()} + valTypeVoid = valType{term: lang.Tuple{}, llvm: llvm.VoidType()} + valTypeInt = valType{term: Int, llvm: llvm.Int64Type()} ) //////////////////////////////////////////////////////////////////////////////// @@ -39,10 +40,7 @@ var ( type voidIn struct{} func (voidIn) inType() valType { - return valType{ - term: lang.Tuple{}, - llvm: llvm.VoidType(), - } + return valTypeVoid } //////////////////////////////////////////////////////////////////////////////// @@ -94,8 +92,9 @@ func (to tupOp) build(mod *Module) (llvm.Value, error) { var err error for i := range to.els { 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 } @@ -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 { voidIn 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 { 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 vAsTup := func(n int) ([]op, error) { - vop, err := termToOp(v) + vop, err := termToOp(ctx, v) if err != nil { return nil, err } @@ -198,11 +239,37 @@ func termToOp(t lang.Term) (op, error) { tc := tupOp{els: make([]op, len(tup))} var err error 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 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: els, err := vAsTup(2) if err != nil { diff --git a/vm/vm.go b/vm/vm.go index 1757a6c..3d78bee 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -17,36 +17,50 @@ var ( Int = lang.Atom("int") ) -// Commands supported by the vm. Each of the types are also commands +// Ops supported by the vm 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, // or compiled. A Module should be Dispose()'d of once it's no longer being // used. type Module struct { b llvm.Builder m llvm.Module + ctx varCtx mainFn llvm.Value } var initOnce sync.Once -// Build creates a new Module by compiling the given Term as code -func Build(t lang.Term) (*Module, error) { +// Build creates a new Module by compiling the given Terms as code +// 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() { llvm.LinkInMCJIT() llvm.InitializeNativeTarget() llvm.InitializeNativeAsmPrinter() }) mod := &Module{ - b: llvm.NewBuilder(), - m: llvm.NewModule(""), + b: llvm.NewBuilder(), + m: llvm.NewModule(""), + ctx: varCtx{}, } var err error - if mod.mainFn, err = mod.buildFn(t); err != nil { + if mod.mainFn, err = mod.buildFn(tt...); err != nil { mod.Dispose() return nil, err } @@ -75,7 +89,7 @@ func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) { ops := make([]op, len(tt)) var err error 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 } } @@ -95,7 +109,7 @@ func (mod *Module) buildFn(tt ...lang.Term) (llvm.Value, error) { var out llvm.Value for i := range ops { if out, err = ops[i].build(mod); err != nil { - return llvm.Value{}, nil + return llvm.Value{}, err } } mod.b.CreateRet(out)