diff --git a/gnovm/pkg/repl/repl.go b/gnovm/pkg/repl/repl.go index bf1cf0c20b8..0f60b948f39 100644 --- a/gnovm/pkg/repl/repl.go +++ b/gnovm/pkg/repl/repl.go @@ -150,16 +150,17 @@ func (r *Repl) Process(input string) (out string, err error) { } }() r.state.id++ - exp, expErr := r.parseExpression(input) - if expErr == nil { - return r.handleExpression(exp) - } decl, declErr := r.parseDeclaration(input) if declErr == nil { return r.handleDeclarations(decl) } + exp, expErr := r.parseExpression(input) + if expErr == nil { + return r.handleExpression(exp) + } + return "", fmt.Errorf("error parsing code:\n\t- as expression (error: %q)\n\t- as declarations (error: %q)", expErr.Error(), declErr.Error()) } @@ -173,7 +174,6 @@ func (r *Repl) handleExpression(e *ast.File) (string, error) { r.state.machine.RunStatement(gno.S(gno.Call(gno.X(fmt.Sprintf("%s%d", executedFunc, r.state.id))))) // Read the result from the output buffer after calling main function. - b, err := io.ReadAll(r.rw) if err != nil { return "", fmt.Errorf("error reading output buffer: %w", err) @@ -187,27 +187,36 @@ func (r *Repl) handleDeclarations(fn *ast.File) (string, error) { b bytes.Buffer nonImportsCount int ) - ast.Inspect(fn, func(n ast.Node) bool { - var writeNode bool - var ns string + var ( + writeNode bool + ns string + ) + switch t := n.(type) { - // TODO: - // token.CONST *ValueSpec - // token.TYPE *TypeSpec - // token.VAR *ValueSpec case *ast.GenDecl: - if t.Tok != token.IMPORT { + tok := t.Tok + + if tok != token.IMPORT && + tok != token.TYPE && + tok != token.CONST && + tok != token.VAR { break } + writeNode = true + ns = r.nodeToString(n) + + if tok != token.IMPORT { + nonImportsCount++ + break + } i, ok := t.Specs[0].(*ast.ImportSpec) if !ok { break } - ns = r.nodeToString(n) r.state.imports[i.Path.Value] = ns case *ast.FuncDecl: writeNode = true diff --git a/gnovm/pkg/repl/repl_test.go b/gnovm/pkg/repl/repl_test.go index a3abf92394a..3c4d1f7c6c6 100644 --- a/gnovm/pkg/repl/repl_test.go +++ b/gnovm/pkg/repl/repl_test.go @@ -25,6 +25,45 @@ var fixtures = []struct { }, }, }, + { + Name: "Add new constant", + CodeSteps: []step{ + { + Line: "const test2, test3 = \"test_string2\", \"test_string3\"", + Result: "const test2, test3 = \"test_string2\", \"test_string3\"\n", + }, + { + Line: "const test = \"test_string\"", + Result: "const test = \"test_string\"\n", + }, + }, + }, + { + Name: "Add struct and functions", + CodeSteps: []step{ + { + Line: "type MyStruct struct { count int}", + Result: "type MyStruct struct{ count int }\n", + }, + { + Line: "func (s *MyStruct) Add(){s.count++}", + Result: "func (s *MyStruct) Add()\t{ s.count++ }\n", + }, + }, + }, + { + Name: "Add new var", + CodeSteps: []step{ + { + Line: "var test2, test3 string = \"test_string2\", \"test_string3\"", + Result: "var test2, test3 string = \"test_string2\", \"test_string3\"\n", + }, + { + Line: "var test int = 42", + Result: "var test int = 42\n", + }, + }, + }, { Name: "Add wrong code", CodeSteps: []step{ @@ -89,6 +128,54 @@ var fixtures = []struct { }, }, }, + { + Name: "Meaning of life", + CodeSteps: []step{ + { + Line: ` + const ( + oneConst = 1 + tenConst = 10 + magicConst = 19 + ) + `, + Result: "const (\n\toneConst\t= 1\n\ttenConst\t= 10\n\tmagicConst\t= 19\n)\n", + }, + { + Line: "var outVar int", + Result: "var outVar int\n", + }, + { + Line: ` + type MyStruct struct { + counter int + } + + func (s *MyStruct) Add() { + s.counter++ + } + + func (s *MyStruct) Get() int { + return s.counter + } + `, + Result: "type MyStruct struct {\n\tcounter int\n}\nfunc (s *MyStruct) Add() {\n\ts.counter++\n}\nfunc (s *MyStruct) Get() int {\n\treturn s.counter\n}\n", + }, + { + Line: ` + ms := &MyStruct{counter: 10} + + ms.Add() + ms.Add() + + outVar = ms.Get() + oneConst + tenConst + magicConst + + println(outVar) + `, + Result: "42\n", + }, + }, + }, } func TestRepl(t *testing.T) { @@ -101,6 +188,7 @@ func TestRepl(t *testing.T) { if cs.Error == "" { require.NoError(t, err) } else { + require.Error(t, err) require.Contains(t, err.Error(), cs.Error) }