From a3a9b9b8b0ceaa38a14d3ab66cc7d66abbdeb9d1 Mon Sep 17 00:00:00 2001 From: Julia Ogris Date: Wed, 14 Sep 2022 13:22:28 +1000 Subject: [PATCH] go: Refactor builtins 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. --- main.go | 8 +++++--- pkg/evaluator/evaluator.go | 7 +++++-- pkg/parser/parser.go | 25 +++-------------------- pkg/parser/parser_test.go | 41 ++++++++++++++++++++++++++------------ pkg/wasm/main.go | 3 ++- 5 files changed, 43 insertions(+), 41 deletions(-) diff --git a/main.go b/main.go index 36d4acab..ccfb3ef0 100644 --- a/main.go +++ b/main.go @@ -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 } @@ -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 } diff --git a/pkg/evaluator/evaluator.go b/pkg/evaluator/evaluator.go index 0573971e..0dcdb881 100644 --- a/pkg/evaluator/evaluator.go +++ b/pkg/evaluator/evaluator.go @@ -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)) diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go index 5ad80c30..5216ca8f 100644 --- a/pkg/parser/parser.go +++ b/pkg/parser/parser.go @@ -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)) @@ -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} @@ -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 } diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go index 6208e9c6..333cb30c 100644 --- a/pkg/parser/parser_test.go +++ b/pkg/parser/parser_test.go @@ -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()) @@ -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()) @@ -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()) @@ -105,7 +105,7 @@ 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()) @@ -113,7 +113,7 @@ func TestFunctionCall(t *testing.T) { } 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} @@ -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()) @@ -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()) @@ -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 := ` @@ -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) @@ -233,7 +233,7 @@ end `, } for _, input := range inputs { - parser := New(input) + parser := New(input, testBuiltins()) _ = parser.Parse() assertNoParseError(t, parser, input) } @@ -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()) @@ -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()) @@ -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, + }, + } +} diff --git a/pkg/wasm/main.go b/pkg/wasm/main.go index 3acd0eca..bf3dbb25 100644 --- a/pkg/wasm/main.go +++ b/pkg/wasm/main.go @@ -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.