130 lines
3.2 KiB
Go
130 lines
3.2 KiB
Go
|
package vm
|
||
|
|
||
|
import (
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/mediocregopher/ginger/lang"
|
||
|
"llvm.org/llvm/bindings/go/llvm"
|
||
|
)
|
||
|
|
||
|
type buildCmd struct {
|
||
|
pattern lang.Tuple
|
||
|
inTypeFn func(lang.Term) (llvm.Type, error)
|
||
|
outTypeFn func(lang.Term) (llvm.Type, error)
|
||
|
buildFn func(lang.Term) (llvm.Value, error)
|
||
|
}
|
||
|
|
||
|
func (cmd buildCmd) matches(t lang.Term) bool {
|
||
|
return lang.Match(cmd.pattern, t)
|
||
|
}
|
||
|
|
||
|
func (cmd buildCmd) inType(t lang.Term) (llvm.Type, error) {
|
||
|
if cmd.inTypeFn == nil {
|
||
|
return llvm.VoidType(), nil
|
||
|
}
|
||
|
return cmd.inTypeFn(t)
|
||
|
}
|
||
|
|
||
|
func (cmd buildCmd) outType(t lang.Term) (llvm.Type, error) {
|
||
|
if cmd.outTypeFn == nil {
|
||
|
return llvm.VoidType(), nil
|
||
|
}
|
||
|
return cmd.outTypeFn(t)
|
||
|
}
|
||
|
|
||
|
func (cmd buildCmd) build(t lang.Term) (llvm.Value, error) {
|
||
|
return cmd.buildFn(t)
|
||
|
}
|
||
|
|
||
|
func buildCmds(mod *Module) []buildCmd {
|
||
|
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 {
|
||
|
return lang.Tuple{lang.ATuple, lang.Tuple(el)}
|
||
|
}
|
||
|
buildPat := func(a lang.Atom, b lang.Tuple) lang.Tuple {
|
||
|
return tPat(aPat(a), b)
|
||
|
}
|
||
|
return []buildCmd{
|
||
|
{ // (int 42)
|
||
|
pattern: buildPat(lang.AInt, cPat(lang.AUnder)),
|
||
|
outTypeFn: func(t lang.Term) (llvm.Type, error) {
|
||
|
return llvm.Int64Type(), nil
|
||
|
},
|
||
|
buildFn: func(t lang.Term) (llvm.Value, error) {
|
||
|
con := t.(lang.Const)
|
||
|
coni, err := strconv.ParseInt(string(con), 10, 64)
|
||
|
if err != nil {
|
||
|
return llvm.Value{}, err
|
||
|
}
|
||
|
// TODO why does this have to be cast?
|
||
|
return llvm.ConstInt(llvm.Int64Type(), uint64(coni), false), nil
|
||
|
},
|
||
|
},
|
||
|
|
||
|
{ // (tup ((atom foo) (const 10)))
|
||
|
pattern: buildPat(lang.ATuple, lang.Tuple{lang.ATuple, lang.AUnder}),
|
||
|
outTypeFn: func(t lang.Term) (llvm.Type, error) {
|
||
|
tup := t.(lang.Tuple)
|
||
|
if len(tup) == 0 {
|
||
|
return llvm.VoidType(), nil
|
||
|
}
|
||
|
var err error
|
||
|
typs := make([]llvm.Type, len(tup))
|
||
|
for i := range tup {
|
||
|
if typs[i], err = mod.outType(tup[i]); err != nil {
|
||
|
return llvm.Type{}, err
|
||
|
}
|
||
|
}
|
||
|
return llvm.StructType(typs, false), nil
|
||
|
},
|
||
|
buildFn: func(t lang.Term) (llvm.Value, error) {
|
||
|
tup := t.(lang.Tuple)
|
||
|
// if the tuple is empty then it is a void
|
||
|
if len(tup) == 0 {
|
||
|
return llvm.Undef(llvm.VoidType()), nil
|
||
|
}
|
||
|
|
||
|
var err error
|
||
|
vals := make([]llvm.Value, len(tup))
|
||
|
typs := make([]llvm.Type, len(tup))
|
||
|
for i := range tup {
|
||
|
if vals[i], err = mod.build(tup[i]); err != nil {
|
||
|
return llvm.Value{}, err
|
||
|
}
|
||
|
typs[i] = vals[i].Type()
|
||
|
}
|
||
|
|
||
|
str := llvm.Undef(llvm.StructType(typs, false))
|
||
|
for i := range vals {
|
||
|
str = mod.b.CreateInsertValue(str, vals[i], i, "")
|
||
|
}
|
||
|
return str, nil
|
||
|
},
|
||
|
},
|
||
|
|
||
|
{ // (add ((const 5) (var foo)))
|
||
|
pattern: buildPat(lang.AAdd, tPat(lang.TDblUnder, lang.TDblUnder)),
|
||
|
outTypeFn: func(t lang.Term) (llvm.Type, error) {
|
||
|
return llvm.Int64Type(), nil
|
||
|
},
|
||
|
buildFn: func(t lang.Term) (llvm.Value, error) {
|
||
|
tup := t.(lang.Tuple)
|
||
|
v1, err := mod.build(tup[0])
|
||
|
if err != nil {
|
||
|
return llvm.Value{}, err
|
||
|
}
|
||
|
v2, err := mod.build(tup[1])
|
||
|
if err != nil {
|
||
|
return llvm.Value{}, err
|
||
|
}
|
||
|
return mod.b.CreateAdd(v1, v2, ""), nil
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
}
|