Skip to content

Commit

Permalink
go: Refactor builtins
Browse files Browse the repository at this point in the history
Refactor builtins, such that parser package doesn't have any default
builtins other than for testing. Builtins need to be provided to parser
by caller. This ensures builtins are always only defined in one place.
  • Loading branch information
juliaogris committed Sep 14, 2022
1 parent 06fcee6 commit a3a9b9b
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 41 deletions.
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func (c *cmdRun) Run() error {
if err != nil {
return err
}
print := func(s string) { fmt.Print(s) }
evaluator.Run(string(b), print)
printFunc := func(s string) { fmt.Print(s) }
evaluator.Run(string(b), printFunc)
return nil
}

Expand All @@ -61,7 +61,9 @@ func (c *cmdParse) Run() error {
if err != nil {
return err
}
result := parser.Run(string(b))
printFunc := func(s string) { fmt.Print(s) }
builtins := evaluator.DefaultBuiltins(printFunc).Decls()
result := parser.Run(string(b), builtins)
fmt.Println(result)
return nil
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
)

func Run(input string, print func(string)) {
builtins := DefaultBuiltins(print)
p := parser.NewWithBuiltins(input, builtins.Decls())
RunWithBuiltins(input, print, DefaultBuiltins(print))
}

func RunWithBuiltins(input string, print func(string), builtins Builtins) {
p := parser.New(input, builtins.Decls())
prog := p.Parse()
if p.HasErrors() {
print(p.MaxErrorsString(8))
Expand Down
25 changes: 3 additions & 22 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"foxygo.at/evy/pkg/lexer"
)

func Run(input string) string {
parser := New(input)
func Run(input string, builtins map[string]*FuncDecl) string {
parser := New(input, builtins)
prog := parser.Parse()
if len(parser.errors) > 0 {
errs := make([]string, len(parser.errors))
Expand Down Expand Up @@ -41,11 +41,7 @@ func (e Error) String() string {
return e.token.Location() + ": " + e.message
}

func New(input string) *Parser {
return NewWithBuiltins(input, builtins())
}

func NewWithBuiltins(input string, builtins map[string]*FuncDecl) *Parser {
func New(input string, builtins map[string]*FuncDecl) *Parser {
l := lexer.New(input)
p := &Parser{funcs: builtins}

Expand Down Expand Up @@ -74,21 +70,6 @@ func NewWithBuiltins(input string, builtins map[string]*FuncDecl) *Parser {
return p
}

func builtins() map[string]*FuncDecl {
return map[string]*FuncDecl{
"print": &FuncDecl{
Name: "print",
VariadicParam: &Var{Name: "a", T: ANY_TYPE},
ReturnType: NONE_TYPE,
},
"len": &FuncDecl{
Name: "len",
Params: []*Var{{Name: "a", T: ANY_TYPE}},
ReturnType: NUM_TYPE,
},
}
}

func (p *Parser) Errors() []Error {
return p.errors
}
Expand Down
41 changes: 28 additions & 13 deletions pkg/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestParseDeclaration(t *testing.T) {
}
for input, wantSlice := range tests {
want := strings.Join(wantSlice, "\n") + "\n"
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertNoParseError(t, parser, input)
assert.Equal(t, want, got.String())
Expand All @@ -53,7 +53,7 @@ func TestEmptyProgram(t *testing.T) {
" \n //blabla",
}
for _, input := range tests {
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertNoParseError(t, parser, input)
assert.Equal(t, "\n", got.String())
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestParseDeclarationError(t *testing.T) {
"a :num{}num": "line 1 column 9: expected end of line, found 'num'",
}
for input, err1 := range tests {
parser := New(input)
parser := New(input, testBuiltins())
_ = parser.Parse()
assertParseError(t, parser, input)
assert.Equal(t, err1, parser.errors[0].String(), "input: %s\nerrors:\n%s", input, parser.ErrorsString())
Expand All @@ -105,15 +105,15 @@ func TestFunctionCall(t *testing.T) {
}
for input, wantSlice := range tests {
want := strings.Join(wantSlice, "\n") + "\n"
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertNoParseError(t, parser, input)
assert.Equal(t, want, got.String())
}
}

func TestFunctionCallError(t *testing.T) {
builtins := builtins()
builtins := testBuiltins()
builtins["f0"] = &FuncDecl{Name: "f0", ReturnType: NONE_TYPE}
builtins["f1"] = &FuncDecl{Name: "f1", VariadicParam: &Var{Name: "a", T: NUM_TYPE}, ReturnType: NONE_TYPE}
builtins["f2"] = &FuncDecl{Name: "f2", Params: []*Var{&Var{Name: "a", T: NUM_TYPE}}, ReturnType: NONE_TYPE}
Expand All @@ -135,7 +135,7 @@ func TestFunctionCallError(t *testing.T) {
`foo 0`: "line 1 column 1: unknown function 'foo'",
}
for input, err1 := range tests {
parser := NewWithBuiltins(input, builtins)
parser := New(input, builtins)
_ = parser.Parse()
assertParseError(t, parser, input)
assert.Equal(t, err1, parser.errors[0].String(), "input: %s\nerrors:\n%s", input, parser.ErrorsString())
Expand All @@ -155,7 +155,7 @@ func TestBlock(t *testing.T) {
}
for input, wantSlice := range tests {
want := strings.Join(wantSlice, "\n") + "\n"
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertNoParseError(t, parser, input)
assert.Equal(t, want, got.String())
Expand All @@ -166,7 +166,7 @@ func TestToplevelExprFuncCall(t *testing.T) {
input := `
x := len "123"
`
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertNoParseError(t, parser, input)
want := `
Expand All @@ -190,10 +190,10 @@ on mousedown
end
end
`
parser := New(input)
parser := New(input, testBuiltins())
_ = parser.Parse()
assertNoParseError(t, parser, input)
builtinCnt := len(builtins())
builtinCnt := len(testBuiltins())
assert.Equal(t, builtinCnt+1, len(parser.funcs))
got := parser.funcs["add"]
assert.Equal(t, "add", got.Name)
Expand Down Expand Up @@ -233,7 +233,7 @@ end
`,
}
for _, input := range inputs {
parser := New(input)
parser := New(input, testBuiltins())
_ = parser.Parse()
assertNoParseError(t, parser, input)
}
Expand Down Expand Up @@ -304,7 +304,7 @@ end
`: "line 2 column 8: invalid declaration of parameter 'x', already used as function name",
}
for input, wantErr := range inputs {
parser := New(input)
parser := New(input, testBuiltins())
_ = parser.Parse()
assertParseError(t, parser, input)
assert.Equal(t, wantErr, parser.errors[0].String())
Expand All @@ -321,7 +321,7 @@ print "x:" x
if x > 10
print "🍦 big x"
end`
parser := New(input)
parser := New(input, testBuiltins())
got := parser.Parse()
assertParseError(t, parser, input)
assert.Equal(t, "line 2 column 1: unknown function 'move'", parser.errors[0].String())
Expand All @@ -342,3 +342,18 @@ func assertNoParseError(t *testing.T, parser *Parser, input string) {
t.Helper()
assert.Equal(t, 0, len(parser.errors), "Unexpected parser error\n input: %s\nerrors:\n%s", input, parser.ErrorsString())
}

func testBuiltins() map[string]*FuncDecl {
return map[string]*FuncDecl{
"print": &FuncDecl{
Name: "print",
VariadicParam: &Var{Name: "a", T: ANY_TYPE},
ReturnType: NONE_TYPE,
},
"len": &FuncDecl{
Name: "len",
Params: []*Var{{Name: "a", T: ANY_TYPE}},
ReturnType: NUM_TYPE,
},
}
}
3 changes: 2 additions & 1 deletion pkg/wasm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func jsTokenize(ptr *uint32, length int) {
//export parse
func jsParse(ptr *uint32, length int) {
s := getString(ptr, length)
jsPrint(parser.Run(s))
builtins := evaluator.DefaultBuiltins(jsPrint).Decls()
jsPrint(parser.Run(s, builtins))
}

// alloc pre-allocates memory used in string parameter passing.
Expand Down

0 comments on commit a3a9b9b

Please sign in to comment.