refactor variable stuff to use fewer op types and be more consistent

This commit is contained in:
Brian Picciano 2017-02-16 11:37:31 -07:00
parent ea869e7306
commit 97f972f287
3 changed files with 52 additions and 64 deletions

View File

@ -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 { type varOp struct {
op op
name string v llvm.Value
built bool
} }
func (vo varOp) build(mod *Module) (llvm.Value, error) { func (vo *varOp) build(mod *Module) (llvm.Value, error) {
o, err := mod.ctx.get(vo.name) if !vo.built {
if err != nil { var err error
if vo.v, err = vo.op.build(mod); err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
return o.build(mod) vo.built = true
}
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 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: case Var:
if !lang.Match(aPat(lang.AUnder), v) { if !lang.Match(aPat(lang.AUnder), v) {
return nil, errors.New("var requires atom arg") return nil, errors.New("var requires atom arg")
} }
name := v.(lang.Atom).String() 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 { if err != nil {
return nil, err 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, // Add is special in some way, I think it's a function not a compiler op,
// not sure yet though // not sure yet though

View File

@ -24,14 +24,7 @@ var (
Var = lang.Atom("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, // 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

View File

@ -8,6 +8,7 @@ import (
func TestCompiler(t *T) { func TestCompiler(t *T) {
mkcmd := func(a lang.Atom, args ...lang.Term) lang.Tuple { 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 { if len(args) == 1 {
return lang.Tuple{a, args[0]} return lang.Tuple{a, args[0]}
} }
@ -24,9 +25,9 @@ func TestCompiler(t *T) {
one := mkint("1") one := mkint("1")
two := mkint("2") two := mkint("2")
foo := lang.Atom("foo") foo := mkcmd(Var, lang.Atom("foo"))
bar := lang.Atom("bar") bar := mkcmd(Var, lang.Atom("bar"))
baz := lang.Atom("baz") baz := mkcmd(Var, lang.Atom("baz"))
tests := []test{ tests := []test{
{ {
@ -42,23 +43,23 @@ func TestCompiler(t *T) {
{ {
in: []lang.Term{ in: []lang.Term{
mkcmd(Assign, foo, one), mkcmd(Assign, foo, one),
mkcmd(Add, mkcmd(Tuple, mkcmd(Var, foo), two)), mkcmd(Add, mkcmd(Tuple, foo, two)),
}, },
exp: 3, exp: 3,
}, },
{ {
in: []lang.Term{ in: []lang.Term{
mkcmd(Assign, foo, mkcmd(Tuple, one, two)), mkcmd(Assign, foo, mkcmd(Tuple, one, two)),
mkcmd(Add, mkcmd(Var, foo)), mkcmd(Add, foo),
}, },
exp: 3, exp: 3,
}, },
{ {
in: []lang.Term{ in: []lang.Term{
mkcmd(Assign, foo, mkcmd(Tuple, one, two)), mkcmd(Assign, foo, mkcmd(Tuple, one, two)),
mkcmd(Assign, bar, mkcmd(Add, mkcmd(Var, foo))), mkcmd(Assign, bar, mkcmd(Add, foo)),
mkcmd(Assign, baz, mkcmd(Add, mkcmd(Var, foo))), mkcmd(Assign, baz, mkcmd(Add, foo)),
mkcmd(Add, mkcmd(Tuple, mkcmd(Var, bar), mkcmd(Var, baz))), mkcmd(Add, mkcmd(Tuple, bar, baz)),
}, },
exp: 6, exp: 6,
}, },