Skip to content

Commit

Permalink
evaluator: Add move and line to builtins
Browse files Browse the repository at this point in the history
Add `move` and `line` to builtins. Add `\n` to print function.
Restructure Builtins such that we can extract parser builtins as map
[string]*parser.FuncDecl, this is necessary for custom builtins to flow
through from evaluator to parser. Builtin definition should happen in
one place only.
  • Loading branch information
juliaogris committed Sep 14, 2022
1 parent 7068cfe commit eb6adad
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 22 deletions.
95 changes: 79 additions & 16 deletions pkg/evaluator/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,63 @@ package evaluator
import (
"strconv"
"strings"

"foxygo.at/evy/pkg/parser"
)

type Builtin func(args []Value) Value
type Builtin struct {
Func BuiltinFunc
Decl *parser.FuncDecl
}

type Builtins map[string]Builtin

func (b Builtins) Decls() map[string]*parser.FuncDecl {
decls := make(map[string]*parser.FuncDecl, len(b))
for name, builtin := range b {
decls[name] = builtin.Decl
}
return decls
}

type BuiltinFunc func(args []Value) Value

func (b Builtin) Type() ValueType { return BUILTIN }
func (b Builtin) String() string { return "builtin function" }
func (b BuiltinFunc) Type() ValueType { return BUILTIN }
func (b BuiltinFunc) String() string { return "builtin function" }

func newBuiltins(e *Evaluator) map[string]Builtin {
return map[string]Builtin{
"print": Builtin(e.Print),
"len": Builtin(Len),
func DefaultBuiltins(print func(string)) Builtins {
return Builtins{
"print": {Func: printFunc(print), Decl: printDecl},
"len": {Func: BuiltinFunc(lenFunc), Decl: lenDecl},
"move": {Func: moveFunc(print), Decl: moveDecl},
"line": {Func: lineFunc(print), Decl: lineDecl},
}
}

func (e *Evaluator) Print(args []Value) Value {
argList := make([]string, len(args))
for i, arg := range args {
argList[i] = arg.String()
var printDecl = &parser.FuncDecl{
Name: "print",
VariadicParam: &parser.Var{Name: "a", T: parser.ANY_TYPE},
ReturnType: parser.NONE_TYPE,
}

func printFunc(printFn func(string)) BuiltinFunc {
return func(args []Value) Value {
argList := make([]string, len(args))
for i, arg := range args {
argList[i] = arg.String()
}
printFn(strings.Join(argList, " ") + "\n")
return nil
}
e.print(strings.Join(argList, " "))
return nil
}

func Len(args []Value) Value {
var lenDecl = &parser.FuncDecl{
Name: "len",
Params: []*parser.Var{{Name: "a", T: parser.ANY_TYPE}},
ReturnType: parser.NUM_TYPE,
}

func lenFunc(args []Value) Value {
if len(args) != 1 {
return newError("'len' takes 1 argument not " + strconv.Itoa(len(args)))
}
Expand All @@ -37,8 +70,38 @@ func Len(args []Value) Value {
return &Num{Val: float64(len(arg.Elements))}
case *String:
return &Num{Val: float64(len(arg.Val))}
default:
return newError("'len' takes 1 argument of type 'string', array '[]' or map '{}' not " + args[0].Type().String())
}
return newError("'len' takes 1 argument of type 'string', array '[]' or map '{}' not " + args[0].Type().String())
}

var moveDecl = &parser.FuncDecl{
Name: "move",
Params: []*parser.Var{
&parser.Var{Name: "x", T: parser.NUM_TYPE},
&parser.Var{Name: "y", T: parser.NUM_TYPE},
},
ReturnType: parser.NUM_TYPE,
}

func moveFunc(printFn func(string)) BuiltinFunc {
return func(args []Value) Value {
printFn("'move' not yet implemented\n")
return nil
}
}

var lineDecl = &parser.FuncDecl{
Name: "line",
Params: []*parser.Var{
&parser.Var{Name: "x", T: parser.NUM_TYPE},
&parser.Var{Name: "y", T: parser.NUM_TYPE},
},
ReturnType: parser.NUM_TYPE,
}

func lineFunc(printFn func(string)) BuiltinFunc {
return func(args []Value) Value {
printFn("'line' not yet implemented\n")
return nil
}
}
7 changes: 4 additions & 3 deletions pkg/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import (
)

func Run(input string, print func(string)) {
p := parser.New(input)
builtins := DefaultBuiltins(print)
p := parser.NewWithBuiltins(input, builtins.Decls())
prog := p.Parse()
e := &Evaluator{print: print}
e.builtins = newBuiltins(e)
e.builtins = builtins
val := e.Eval(prog, newScope())
if isError(val) {
print(val.String())
Expand Down Expand Up @@ -72,7 +73,7 @@ func (e *Evaluator) evalFunctionCall(funcCall *parser.FunctionCall, scope *scope
if !ok {
return newError("cannot find builtin function " + funcCall.Name)
}
return builtin(args)
return builtin.Func(args)
}

func (e *Evaluator) evalVar(v *parser.Var, scope *scope) Value {
Expand Down
10 changes: 7 additions & 3 deletions pkg/evaluator/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

func TestBasicEval(t *testing.T) {
in := "a:=1\n print a 2"
want := "1 2"
want := "1 2\n"
b := bytes.Buffer{}
fn := func(s string) { b.WriteString(s) }
Run(in, fn)
Expand All @@ -30,7 +30,7 @@ func TestParseDeclaration(t *testing.T) {
b := bytes.Buffer{}
fn := func(s string) { b.WriteString(s) }
Run(in, fn)
assert.Equal(t, want, b.String())
assert.Equal(t, want+"\n", b.String())
})
}
}
Expand All @@ -48,6 +48,10 @@ end`
b := bytes.Buffer{}
fn := func(s string) { b.WriteString(s) }
Run(prog, fn)
want := "x: 12"
want := `
'move' not yet implemented
'line' not yet implemented
x: 12
`[1:]
assert.Equal(t, want, b.String())
}

0 comments on commit eb6adad

Please sign in to comment.