Skip to content

Commit eb6adad

Browse files
committed
evaluator: Add move and line to builtins
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.
1 parent 7068cfe commit eb6adad

File tree

3 files changed

+90
-22
lines changed

3 files changed

+90
-22
lines changed

pkg/evaluator/builtin.go

Lines changed: 79 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,63 @@ package evaluator
33
import (
44
"strconv"
55
"strings"
6+
7+
"foxygo.at/evy/pkg/parser"
68
)
79

8-
type Builtin func(args []Value) Value
10+
type Builtin struct {
11+
Func BuiltinFunc
12+
Decl *parser.FuncDecl
13+
}
14+
15+
type Builtins map[string]Builtin
16+
17+
func (b Builtins) Decls() map[string]*parser.FuncDecl {
18+
decls := make(map[string]*parser.FuncDecl, len(b))
19+
for name, builtin := range b {
20+
decls[name] = builtin.Decl
21+
}
22+
return decls
23+
}
24+
25+
type BuiltinFunc func(args []Value) Value
926

10-
func (b Builtin) Type() ValueType { return BUILTIN }
11-
func (b Builtin) String() string { return "builtin function" }
27+
func (b BuiltinFunc) Type() ValueType { return BUILTIN }
28+
func (b BuiltinFunc) String() string { return "builtin function" }
1229

13-
func newBuiltins(e *Evaluator) map[string]Builtin {
14-
return map[string]Builtin{
15-
"print": Builtin(e.Print),
16-
"len": Builtin(Len),
30+
func DefaultBuiltins(print func(string)) Builtins {
31+
return Builtins{
32+
"print": {Func: printFunc(print), Decl: printDecl},
33+
"len": {Func: BuiltinFunc(lenFunc), Decl: lenDecl},
34+
"move": {Func: moveFunc(print), Decl: moveDecl},
35+
"line": {Func: lineFunc(print), Decl: lineDecl},
1736
}
1837
}
1938

20-
func (e *Evaluator) Print(args []Value) Value {
21-
argList := make([]string, len(args))
22-
for i, arg := range args {
23-
argList[i] = arg.String()
39+
var printDecl = &parser.FuncDecl{
40+
Name: "print",
41+
VariadicParam: &parser.Var{Name: "a", T: parser.ANY_TYPE},
42+
ReturnType: parser.NONE_TYPE,
43+
}
44+
45+
func printFunc(printFn func(string)) BuiltinFunc {
46+
return func(args []Value) Value {
47+
argList := make([]string, len(args))
48+
for i, arg := range args {
49+
argList[i] = arg.String()
50+
}
51+
printFn(strings.Join(argList, " ") + "\n")
52+
return nil
2453
}
25-
e.print(strings.Join(argList, " "))
26-
return nil
2754
}
2855

29-
func Len(args []Value) Value {
56+
var lenDecl = &parser.FuncDecl{
57+
Name: "len",
58+
Params: []*parser.Var{{Name: "a", T: parser.ANY_TYPE}},
59+
ReturnType: parser.NUM_TYPE,
60+
}
61+
62+
func lenFunc(args []Value) Value {
3063
if len(args) != 1 {
3164
return newError("'len' takes 1 argument not " + strconv.Itoa(len(args)))
3265
}
@@ -37,8 +70,38 @@ func Len(args []Value) Value {
3770
return &Num{Val: float64(len(arg.Elements))}
3871
case *String:
3972
return &Num{Val: float64(len(arg.Val))}
40-
default:
41-
return newError("'len' takes 1 argument of type 'string', array '[]' or map '{}' not " + args[0].Type().String())
4273
}
74+
return newError("'len' takes 1 argument of type 'string', array '[]' or map '{}' not " + args[0].Type().String())
75+
}
76+
77+
var moveDecl = &parser.FuncDecl{
78+
Name: "move",
79+
Params: []*parser.Var{
80+
&parser.Var{Name: "x", T: parser.NUM_TYPE},
81+
&parser.Var{Name: "y", T: parser.NUM_TYPE},
82+
},
83+
ReturnType: parser.NUM_TYPE,
84+
}
85+
86+
func moveFunc(printFn func(string)) BuiltinFunc {
87+
return func(args []Value) Value {
88+
printFn("'move' not yet implemented\n")
89+
return nil
90+
}
91+
}
4392

93+
var lineDecl = &parser.FuncDecl{
94+
Name: "line",
95+
Params: []*parser.Var{
96+
&parser.Var{Name: "x", T: parser.NUM_TYPE},
97+
&parser.Var{Name: "y", T: parser.NUM_TYPE},
98+
},
99+
ReturnType: parser.NUM_TYPE,
100+
}
101+
102+
func lineFunc(printFn func(string)) BuiltinFunc {
103+
return func(args []Value) Value {
104+
printFn("'line' not yet implemented\n")
105+
return nil
106+
}
44107
}

pkg/evaluator/evaluator.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import (
55
)
66

77
func Run(input string, print func(string)) {
8-
p := parser.New(input)
8+
builtins := DefaultBuiltins(print)
9+
p := parser.NewWithBuiltins(input, builtins.Decls())
910
prog := p.Parse()
1011
e := &Evaluator{print: print}
11-
e.builtins = newBuiltins(e)
12+
e.builtins = builtins
1213
val := e.Eval(prog, newScope())
1314
if isError(val) {
1415
print(val.String())
@@ -72,7 +73,7 @@ func (e *Evaluator) evalFunctionCall(funcCall *parser.FunctionCall, scope *scope
7273
if !ok {
7374
return newError("cannot find builtin function " + funcCall.Name)
7475
}
75-
return builtin(args)
76+
return builtin.Func(args)
7677
}
7778

7879
func (e *Evaluator) evalVar(v *parser.Var, scope *scope) Value {

pkg/evaluator/evaluator_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99

1010
func TestBasicEval(t *testing.T) {
1111
in := "a:=1\n print a 2"
12-
want := "1 2"
12+
want := "1 2\n"
1313
b := bytes.Buffer{}
1414
fn := func(s string) { b.WriteString(s) }
1515
Run(in, fn)
@@ -30,7 +30,7 @@ func TestParseDeclaration(t *testing.T) {
3030
b := bytes.Buffer{}
3131
fn := func(s string) { b.WriteString(s) }
3232
Run(in, fn)
33-
assert.Equal(t, want, b.String())
33+
assert.Equal(t, want+"\n", b.String())
3434
})
3535
}
3636
}
@@ -48,6 +48,10 @@ end`
4848
b := bytes.Buffer{}
4949
fn := func(s string) { b.WriteString(s) }
5050
Run(prog, fn)
51-
want := "x: 12"
51+
want := `
52+
'move' not yet implemented
53+
'line' not yet implemented
54+
x: 12
55+
`[1:]
5256
assert.Equal(t, want, b.String())
5357
}

0 commit comments

Comments
 (0)