ginger/vm/cmds.go

281 lines
6.2 KiB
Go
Raw Normal View History

2017-02-11 20:35:02 +00:00
package vm
import (
"errors"
"fmt"
2017-02-11 20:35:02 +00:00
"strconv"
"github.com/mediocregopher/ginger/lang"
"llvm.org/llvm/bindings/go/llvm"
)
2017-02-15 15:18:19 +00:00
type op interface {
inType() valType
outType() valType
build(*Module) (llvm.Value, error)
2017-02-11 20:35:02 +00:00
}
type valType struct {
term lang.Term
llvm llvm.Type
2017-02-11 20:35:02 +00:00
}
2017-02-12 18:44:10 +00:00
func (vt valType) isInt() bool {
return lang.Equal(Int, vt.term)
}
2017-02-15 15:18:19 +00:00
func (vt valType) eq(vt2 valType) bool {
return lang.Equal(vt.term, vt2.term) && vt.llvm == vt2.llvm
}
// primitive valTypes
var (
2017-02-15 22:41:14 +00:00
valTypeVoid = valType{term: lang.Tuple{}, llvm: llvm.VoidType()}
valTypeInt = valType{term: Int, llvm: llvm.Int64Type()}
2017-02-15 15:18:19 +00:00
)
////////////////////////////////////////////////////////////////////////////////
// most types don't have an input, so we use this as a shortcut
type voidIn struct{}
func (voidIn) inType() valType {
2017-02-15 22:41:14 +00:00
return valTypeVoid
}
////////////////////////////////////////////////////////////////////////////////
2017-02-15 15:18:19 +00:00
type intOp struct {
voidIn
c lang.Const
}
2017-02-15 15:18:19 +00:00
func (io intOp) outType() valType {
return valTypeInt
2017-02-11 20:35:02 +00:00
}
2017-02-15 15:18:19 +00:00
func (io intOp) build(mod *Module) (llvm.Value, error) {
ci, err := strconv.ParseInt(string(io.c), 10, 64)
if err != nil {
return llvm.Value{}, err
2017-02-11 20:35:02 +00:00
}
return llvm.ConstInt(llvm.Int64Type(), uint64(ci), false), nil
2017-02-11 20:35:02 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2017-02-15 15:18:19 +00:00
type tupOp struct {
voidIn
2017-02-15 15:18:19 +00:00
els []op
}
2017-02-15 15:18:19 +00:00
func (to tupOp) outType() valType {
termTypes := make(lang.Tuple, len(to.els))
llvmTypes := make([]llvm.Type, len(to.els))
for i := range to.els {
elValType := to.els[i].outType()
termTypes[i] = elValType.term
llvmTypes[i] = elValType.llvm
}
vt := valType{term: lang.Tuple{Tuple, termTypes}}
if len(llvmTypes) == 0 {
vt.llvm = llvm.VoidType()
} else {
vt.llvm = llvm.StructType(llvmTypes, false)
}
return vt
}
2017-02-15 15:18:19 +00:00
func (to tupOp) build(mod *Module) (llvm.Value, error) {
str := llvm.Undef(to.outType().llvm)
var val llvm.Value
var err error
2017-02-15 15:18:19 +00:00
for i := range to.els {
if val, err = to.els[i].build(mod); err != nil {
2017-02-15 22:41:14 +00:00
return llvm.Value{}, err
}
2017-02-15 22:41:14 +00:00
str = mod.b.CreateInsertValue(str, val, i, "")
}
return str, err
2017-02-11 20:35:02 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2017-02-15 15:36:23 +00:00
type tupElOp struct {
voidIn
tup op
i int
}
func (teo tupElOp) outType() valType {
tupType := teo.tup.outType()
return valType{
llvm: tupType.llvm.StructElementTypes()[teo.i],
term: tupType.term.(lang.Tuple)[1].(lang.Tuple)[1],
}
}
func (teo tupElOp) build(mod *Module) (llvm.Value, error) {
if to, ok := teo.tup.(tupOp); ok {
return to.els[teo.i].build(mod)
}
tv, err := teo.tup.build(mod)
if err != nil {
return llvm.Value{}, err
}
return mod.b.CreateExtractValue(tv, teo.i, ""), nil
}
////////////////////////////////////////////////////////////////////////////////
type varOp struct {
2017-02-15 22:41:14 +00:00
op
v llvm.Value
built bool
2017-02-15 22:41:14 +00:00
}
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
2017-02-15 22:41:14 +00:00
}
return vo.v, nil
2017-02-15 22:41:14 +00:00
}
type varCtx map[string]*varOp
2017-02-15 22:41:14 +00:00
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
2017-02-15 22:41:14 +00:00
}
func (c varCtx) get(name string) (*varOp, error) {
if o, ok := c[name]; ok {
return o, nil
2017-02-15 22:41:14 +00:00
}
return nil, fmt.Errorf("var %q referenced before assignment", name)
2017-02-15 22:41:14 +00:00
}
////////////////////////////////////////////////////////////////////////////////
2017-02-15 15:18:19 +00:00
type addOp struct {
voidIn
2017-02-15 15:18:19 +00:00
a, b op
}
2017-02-15 15:18:19 +00:00
func (ao addOp) outType() valType {
return ao.a.outType()
}
2017-02-15 15:18:19 +00:00
func (ao addOp) build(mod *Module) (llvm.Value, error) {
av, err := ao.a.build(mod)
if err != nil {
return llvm.Value{}, err
}
2017-02-15 15:18:19 +00:00
bv, err := ao.b.build(mod)
if err != nil {
return llvm.Value{}, err
}
return mod.b.CreateAdd(av, bv, ""), nil
}
////////////////////////////////////////////////////////////////////////////////
2017-02-15 22:41:14 +00:00
func termToOp(ctx varCtx, t lang.Term) (op, error) {
2017-02-11 20:35:02 +00:00
aPat := func(a lang.Atom) lang.Tuple {
return lang.Tuple{lang.AAtom, a}
}
cPat := func(t lang.Term) lang.Tuple {
return lang.Tuple{lang.AConst, t}
}
tPat := func(el ...lang.Term) lang.Tuple {
2017-02-12 16:53:59 +00:00
return lang.Tuple{Tuple, lang.Tuple(el)}
2017-02-11 20:35:02 +00:00
}
if !lang.Match(tPat(aPat(lang.AUnder), lang.TDblUnder), t) {
return nil, fmt.Errorf("term %v does not look like a vm command", t)
2017-02-11 20:35:02 +00:00
}
k := t.(lang.Tuple)[0].(lang.Atom)
v := t.(lang.Tuple)[1]
// for when v is a Tuple argument, convenience function for casting
2017-02-15 15:18:19 +00:00
vAsTup := func(n int) ([]op, error) {
2017-02-15 22:41:14 +00:00
vop, err := termToOp(ctx, v)
if err != nil {
return nil, err
}
2017-02-15 15:36:23 +00:00
ops := make([]op, n)
for i := range ops {
ops[i] = tupElOp{tup: vop, i: i}
}
2017-02-15 15:36:23 +00:00
return ops, nil
}
switch k {
case Int:
if !lang.Match(cPat(lang.AUnder), v) {
return nil, errors.New("int requires constant arg")
}
2017-02-15 15:18:19 +00:00
return intOp{c: v.(lang.Const)}, nil
case Tuple:
if !lang.Match(lang.Tuple{Tuple, lang.AUnder}, v) {
return nil, errors.New("tup requires tuple arg")
}
tup := v.(lang.Tuple)
2017-02-15 15:18:19 +00:00
tc := tupOp{els: make([]op, len(tup))}
var err error
for i := range tup {
2017-02-15 22:41:14 +00:00
if tc.els[i], err = termToOp(ctx, tup[i]); err != nil {
return nil, err
}
}
return tc, nil
case Var:
if !lang.Match(aPat(lang.AUnder), v) {
return nil, errors.New("var requires atom arg")
}
name := v.(lang.Atom).String()
return ctx.get(name)
2017-02-15 22:41:14 +00:00
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")
2017-02-15 22:41:14 +00:00
}
tup := v.(lang.Tuple)
name := tup[0].(lang.Tuple)[1].String()
2017-02-15 22:41:14 +00:00
o, err := termToOp(ctx, tup[1])
if err != nil {
return nil, err
}
vo := &varOp{op: o}
if err := ctx.assign(name, vo); err != nil {
2017-02-15 22:41:14 +00:00
return nil, err
}
return vo, nil
2017-02-15 22:41:14 +00:00
// 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 {
return nil, err
2017-02-15 15:18:19 +00:00
} else if !els[0].outType().eq(valTypeInt) {
return nil, errors.New("add args must be numbers of the same type")
} else if !els[1].outType().eq(valTypeInt) {
2017-02-12 18:44:10 +00:00
return nil, errors.New("add args must be numbers of the same type")
}
2017-02-15 15:18:19 +00:00
return addOp{a: els[0], b: els[1]}, nil
default:
2017-02-15 15:18:19 +00:00
return nil, fmt.Errorf("op %v unknown, or its args are malformed", t)
2017-02-11 20:35:02 +00:00
}
}