diff --git a/vm/cmds.go b/vm/cmds.go index fedb071..2358af9 100644 --- a/vm/cmds.go +++ b/vm/cmds.go @@ -129,44 +129,38 @@ 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 + v llvm.Value + built bool } -func (vo varOp) build(mod *Module) (llvm.Value, error) { - o, err := mod.ctx.get(vo.name) - if err != nil { - return llvm.Value{}, err +func (vo *varOp) build(mod *Module) (llvm.Value, error) { + if !vo.built { + var err error + if vo.v, err = vo.op.build(mod); err != nil { + return llvm.Value{}, err + } + vo.built = true } - return o.build(mod) + return vo.v, nil +} + +type varCtx map[string]*varOp + +func (c varCtx) assign(name string, vo *varOp) error { + if _, ok := c[name]; ok { + return fmt.Errorf("var %q already assigned", name) + } + c[name] = vo + return nil +} + +func (c varCtx) get(name string) (*varOp, error) { + if o, ok := c[name]; ok { + return o, nil + } + return nil, fmt.Errorf("var %q referenced before assignment", name) } //////////////////////////////////////////////////////////////////////////////// @@ -244,29 +238,29 @@ func termToOp(ctx varCtx, t lang.Term) (op, error) { } } 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) + return ctx.get(name) + + case Assign: + if !lang.Match(tPat(tPat(aPat(Var), aPat(lang.AUnder)), lang.TDblUnder), v) { + return nil, errors.New("assign requires 2-tuple arg, the first being a var") + } + tup := v.(lang.Tuple) + name := tup[0].(lang.Tuple)[1].String() + o, err := termToOp(ctx, tup[1]) if err != nil { return nil, err } - return varOp{op: o, name: name}, nil + + vo := &varOp{op: o} + if err := ctx.assign(name, vo); err != nil { + return nil, err + } + return vo, nil // Add is special in some way, I think it's a function not a compiler op, // not sure yet though diff --git a/vm/vm.go b/vm/vm.go index 3d78bee..1ce39fb 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -24,14 +24,7 @@ var ( 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 diff --git a/vm/vm_test.go b/vm/vm_test.go index aee387a..b08a78c 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -8,6 +8,7 @@ import ( func TestCompiler(t *T) { mkcmd := func(a lang.Atom, args ...lang.Term) lang.Tuple { + // TODO a 1-tuple should be the same as its element? if len(args) == 1 { return lang.Tuple{a, args[0]} } @@ -24,9 +25,9 @@ func TestCompiler(t *T) { one := mkint("1") two := mkint("2") - foo := lang.Atom("foo") - bar := lang.Atom("bar") - baz := lang.Atom("baz") + foo := mkcmd(Var, lang.Atom("foo")) + bar := mkcmd(Var, lang.Atom("bar")) + baz := mkcmd(Var, lang.Atom("baz")) tests := []test{ { @@ -42,23 +43,23 @@ func TestCompiler(t *T) { { in: []lang.Term{ mkcmd(Assign, foo, one), - mkcmd(Add, mkcmd(Tuple, mkcmd(Var, foo), two)), + mkcmd(Add, mkcmd(Tuple, foo, two)), }, exp: 3, }, { in: []lang.Term{ mkcmd(Assign, foo, mkcmd(Tuple, one, two)), - mkcmd(Add, mkcmd(Var, foo)), + mkcmd(Add, foo), }, exp: 3, }, { in: []lang.Term{ mkcmd(Assign, foo, mkcmd(Tuple, one, two)), - mkcmd(Assign, bar, mkcmd(Add, mkcmd(Var, foo))), - mkcmd(Assign, baz, mkcmd(Add, mkcmd(Var, foo))), - mkcmd(Add, mkcmd(Tuple, mkcmd(Var, bar), mkcmd(Var, baz))), + mkcmd(Assign, bar, mkcmd(Add, foo)), + mkcmd(Assign, baz, mkcmd(Add, foo)), + mkcmd(Add, mkcmd(Tuple, bar, baz)), }, exp: 6, },