wrote a shitty eval
This commit is contained in:
parent
f7bc7be1f7
commit
0520386674
77
eval/eval.go
Normal file
77
eval/eval.go
Normal file
@ -0,0 +1,77 @@
|
||||
// The eval package encompasses all that is necesary to do runtime evaluation of
|
||||
// ginger structures. These are different than macros in that they aren't turned
|
||||
// into go code, instead the compiled go code evaluates them at runtime
|
||||
package eval
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mediocregopher/ginger/macros/pkgctx"
|
||||
"github.com/mediocregopher/ginger/seq"
|
||||
"github.com/mediocregopher/ginger/types"
|
||||
)
|
||||
|
||||
// Evaler is a function which can be used inside of eval. It must take in its
|
||||
// arguments as a sequence of Elems, and return a resulting Elem
|
||||
type Evaler func(seq.Seq) types.Elem
|
||||
|
||||
// Bail stops compilation. The given element should be the reason compilation
|
||||
// has stopped
|
||||
func Bail(el types.Elem, reason string) {
|
||||
fmt.Fprintln(os.Stderr, reason)
|
||||
time.Sleep(100 * time.Second)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Bailf is like Bail, but takes in formatting
|
||||
func Bailf(el types.Elem, format string, args ...interface{}) {
|
||||
reason := fmt.Sprintf(format, args...)
|
||||
Bail(el, reason)
|
||||
}
|
||||
|
||||
var colon = types.GoType{":"}
|
||||
|
||||
// Eval takes in the pkgctx it is being executed in, as well as a single Elem to
|
||||
// be evaluated, and returns the Elem it evaluates to
|
||||
func Eval(p *pkgctx.PkgCtx, el types.Elem) types.Elem {
|
||||
l, ok := el.(*seq.List)
|
||||
if !ok {
|
||||
return el
|
||||
}
|
||||
|
||||
first, rest, ok := l.FirstRest()
|
||||
if !ok || !first.Equal(colon) {
|
||||
return el
|
||||
}
|
||||
|
||||
fnEl, args, ok := rest.FirstRest()
|
||||
if !ok {
|
||||
Bail(el, "Empty list after colon, no function given")
|
||||
}
|
||||
|
||||
|
||||
var fnName string
|
||||
if gt, ok := fnEl.(types.GoType); ok {
|
||||
fnName, _ = gt.V.(string)
|
||||
}
|
||||
|
||||
if fnName == "" || fnName[0] != ':' {
|
||||
Bail(el, "Must give a function reference to execute")
|
||||
}
|
||||
fnName = fnName[1:]
|
||||
|
||||
fn, ok := p.CallMap[fnName]
|
||||
if !ok {
|
||||
Bailf(el, "Unknown function name %q", fnName)
|
||||
}
|
||||
|
||||
evalArgFn := func(el types.Elem) types.Elem {
|
||||
return Eval(p, el)
|
||||
}
|
||||
|
||||
evaldArgs := seq.Map(evalArgFn, args)
|
||||
|
||||
return fn.(Evaler)(evaldArgs)
|
||||
}
|
50
eval/eval_test.go
Normal file
50
eval/eval_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package eval
|
||||
|
||||
import (
|
||||
. "testing"
|
||||
|
||||
"github.com/mediocregopher/ginger/macros/pkgctx"
|
||||
"github.com/mediocregopher/ginger/parse"
|
||||
"github.com/mediocregopher/ginger/seq"
|
||||
"github.com/mediocregopher/ginger/types"
|
||||
)
|
||||
|
||||
// This is NOT how I want eval to really work in the end, but I wanted to get
|
||||
// something down before I kept thinking about it, so I would know what would
|
||||
// work
|
||||
|
||||
func shittyPlus(s seq.Seq) types.Elem {
|
||||
fn := func(acc, el types.Elem) (types.Elem, bool) {
|
||||
i := acc.(types.GoType).V.(int) + el.(types.GoType).V.(int)
|
||||
return types.GoType{i}, false
|
||||
}
|
||||
|
||||
return seq.Reduce(fn, types.GoType{0}, s)
|
||||
}
|
||||
|
||||
func TestShittyPlus(t *T) {
|
||||
p := &pkgctx.PkgCtx{
|
||||
CallMap: map[string]interface{}{
|
||||
"shittyPlus": Evaler(shittyPlus),
|
||||
},
|
||||
}
|
||||
|
||||
m := map[string]types.Elem{
|
||||
"(: shittyPlus)": types.GoType{0},
|
||||
"(: shittyPlus 1 2 3)": types.GoType{6},
|
||||
`(: shittyPlus 1 2 3
|
||||
(: shittyPlus 1 2 3))`: types.GoType{12},
|
||||
}
|
||||
|
||||
for input, output := range m {
|
||||
parsed, err := parse.ParseString(input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
evald := Eval(p, parsed)
|
||||
if !evald.Equal(output) {
|
||||
t.Fatalf("input: %q %#v != %#v", input, output, evald)
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ type PkgCtx struct {
|
||||
// string for no alias)
|
||||
Packages map[string]string
|
||||
|
||||
// CallDict is a map used by Eval for making actual calls dynamically. The
|
||||
// CallMap is a map used by Eval for making actual calls dynamically. The
|
||||
// key is the string representation of the call to be used (for example,
|
||||
// "fmt.Println") and must agree with the aliases being used in Packages.
|
||||
// The value need not be set during actual compilation, but it is useful to
|
||||
// use it during testing
|
||||
CallDict map[string]interface{}
|
||||
CallMap map[string]interface{}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user