diff options
| author | Marc Vertes <mvertes@free.fr> | 2025-11-27 14:50:07 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2025-11-27 14:50:07 +0100 |
| commit | 22b020225ae77ca1cf9f9984817df9b7fd1aaa12 (patch) | |
| tree | fdbb8d0ccae08b248c1e1d787ba66d624ab6a34c /interpreter | |
| parent | 8e32cc1a5617f84d0bd7bf1fd898251675d5a653 (diff) | |
fix: improve parser, compiler and interpreter APIs
Pass a language spec as an argument when create a new parser,
compiler or interpreter.
Also move the REPL code in interpreter package.
Diffstat (limited to 'interpreter')
| -rw-r--r-- | interpreter/dump_test.go | 5 | ||||
| -rw-r--r-- | interpreter/interpreter.go | 16 | ||||
| -rw-r--r-- | interpreter/interpreter_test.go | 7 | ||||
| -rw-r--r-- | interpreter/repl.go | 35 |
4 files changed, 48 insertions, 15 deletions
diff --git a/interpreter/dump_test.go b/interpreter/dump_test.go index dd172b9..d8fcc55 100644 --- a/interpreter/dump_test.go +++ b/interpreter/dump_test.go @@ -4,11 +4,12 @@ import ( "testing" "github.com/mvertes/parscan/interpreter" + "github.com/mvertes/parscan/lang/golang" ) func TestDump(t *testing.T) { initProgram := "var a int = 2+1; a" - interp := interpreter.NewInterpreter(GoScanner) + interp := interpreter.NewInterpreter(golang.GoSpec) r, e := interp.Eval(initProgram) t.Log(r, e) if e != nil { @@ -24,7 +25,7 @@ func TestDump(t *testing.T) { d := interp.Dump() t.Log(d) - interp = interpreter.NewInterpreter(GoScanner) + interp = interpreter.NewInterpreter(golang.GoSpec) r, e = interp.Eval(initProgram) t.Log(r, e) if e != nil { diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 06a4bc1..62b593b 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -4,25 +4,25 @@ import ( "reflect" "github.com/mvertes/parscan/compiler" - "github.com/mvertes/parscan/scanner" + "github.com/mvertes/parscan/lang" "github.com/mvertes/parscan/vm" ) const debug = true -// Interpreter represents the state of an interpreter. -type Interpreter struct { +// Interp represents the state of an interpreter. +type Interp struct { *compiler.Compiler *vm.Machine } -// NewInterpreter returns a new interpreter state. -func NewInterpreter(s *scanner.Scanner) *Interpreter { - return &Interpreter{compiler.NewCompiler(s), &vm.Machine{}} +// NewInterpreter returns a new interpreter. +func NewInterpreter(s *lang.Spec) *Interp { + return &Interp{compiler.NewCompiler(s), &vm.Machine{}} } -// Eval interprets a src program and return the last produced value if any, or an error. -func (i *Interpreter) Eval(src string) (res reflect.Value, err error) { +// Eval evaluates code string and return the last produced value if any, or an error. +func (i *Interp) Eval(src string) (res reflect.Value, err error) { codeOffset := len(i.Code) dataOffset := 0 if codeOffset > 0 { diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index 800ace5..71c7dd1 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -7,7 +7,6 @@ import ( "github.com/mvertes/parscan/interpreter" "github.com/mvertes/parscan/lang/golang" - "github.com/mvertes/parscan/scanner" ) type etest struct { @@ -15,19 +14,17 @@ type etest struct { skip bool } -var GoScanner *scanner.Scanner - func init() { log.SetFlags(log.Lshortfile) - GoScanner = scanner.NewScanner(golang.GoSpec) } func gen(test etest) func(*testing.T) { return func(t *testing.T) { + t.Parallel() if test.skip { t.Skip() } - interp := interpreter.NewInterpreter(GoScanner) + interp := interpreter.NewInterpreter(golang.GoSpec) errStr := "" r, e := interp.Eval(test.src) t.Log(r, e) diff --git a/interpreter/repl.go b/interpreter/repl.go new file mode 100644 index 0000000..73af8a7 --- /dev/null +++ b/interpreter/repl.go @@ -0,0 +1,35 @@ +package interpreter + +import ( + "bufio" + "errors" + "fmt" + "io" + + "github.com/mvertes/parscan/scanner" +) + +// Repl executes an interactive line oriented Read Eval Print Loop (REPL). +func (i *Interp) Repl(in io.Reader) (err error) { + liner := bufio.NewScanner(in) + text, prompt := "", "> " + fmt.Print(prompt) + for liner.Scan() { + text += liner.Text() + res, err := i.Eval(text + "\n") + switch { + case err == nil: + if res.IsValid() { + fmt.Println(": ", res) + } + text, prompt = "", "> " + case errors.Is(err, scanner.ErrBlock): + prompt = ">> " + default: + fmt.Println("Error:", err) + text, prompt = "", "> " + } + fmt.Print(prompt) + } + return err +} |
