summaryrefslogtreecommitdiff
path: root/interpreter
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2025-11-29 19:46:34 +0100
committerMarc Vertes <mvertes@free.fr>2025-11-29 19:46:34 +0100
commitf40a1c23467eef36f53635e525f8b25f591e8a45 (patch)
treebeae2442ecee1d734b9a676f7b176376a1d80af5 /interpreter
parentfbc73922e9853d7e344e388f3fdfedb8fa323682 (diff)
chore: shorter name for packages, simpilfy vm values
Diffstat (limited to 'interpreter')
-rw-r--r--interpreter/dump_test.go49
-rw-r--r--interpreter/interpreter.go55
-rw-r--r--interpreter/interpreter_test.go263
-rw-r--r--interpreter/repl.go35
4 files changed, 0 insertions, 402 deletions
diff --git a/interpreter/dump_test.go b/interpreter/dump_test.go
deleted file mode 100644
index d8fcc55..0000000
--- a/interpreter/dump_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package interpreter_test
-
-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(golang.GoSpec)
- r, e := interp.Eval(initProgram)
- t.Log(r, e)
- if e != nil {
- t.Fatal(e)
- }
-
- r, e = interp.Eval("a = 100")
- t.Log(r, e)
- if e != nil {
- t.Fatal(e)
- }
-
- d := interp.Dump()
- t.Log(d)
-
- interp = interpreter.NewInterpreter(golang.GoSpec)
- r, e = interp.Eval(initProgram)
- t.Log(r, e)
- if e != nil {
- t.Fatal(e)
- }
-
- e = interp.ApplyDump(d)
- if e != nil {
- t.Fatal(e)
- }
-
- r, e = interp.Eval("a = a + 1;a")
- t.Log(r, e)
- if e != nil {
- t.Fatal(e)
- }
-
- if r.Interface() != int(101) {
- t.Fatalf("unexpected result: %v", r)
- }
-}
diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go
deleted file mode 100644
index 032a66f..0000000
--- a/interpreter/interpreter.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Package interpreter implements an interpreter.
-package interpreter
-
-import (
- "reflect"
-
- "github.com/mvertes/parscan/compiler"
- "github.com/mvertes/parscan/lang"
- "github.com/mvertes/parscan/vm"
-)
-
-const debug = true
-
-// Interp represents the state of an interpreter.
-type Interp struct {
- *compiler.Compiler
- *vm.Machine
-}
-
-// NewInterpreter returns a new interpreter.
-func NewInterpreter(s *lang.Spec) *Interp {
- return &Interp{compiler.NewCompiler(s), &vm.Machine{}}
-}
-
-// 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 {
- // All data must be copied to the VM the first time only (re-entrance).
- dataOffset = len(i.Data)
- }
- i.PopExit() // Remove last exit from previous run (re-entrance).
-
- t, err := i.Parse(src)
- if err != nil {
- return res, err
- }
- if err = i.Codegen(t); err != nil {
- return res, err
- }
- i.Push(i.Data[dataOffset:]...)
- i.PushCode(i.Code[codeOffset:]...)
- if s, ok := i.Symbols["main"]; ok {
- i.PushCode(vm.Instruction{Op: vm.Calli, Arg: []int{int(i.Data[s.Index].Data.Int())}})
- }
- i.PushCode(vm.Instruction{Op: vm.Exit})
- i.SetIP(max(codeOffset, i.Entry))
- if debug {
- i.PrintData()
- i.PrintCode()
- }
- err = i.Run()
- return i.Top().Data, err
-}
diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go
deleted file mode 100644
index e48efdb..0000000
--- a/interpreter/interpreter_test.go
+++ /dev/null
@@ -1,263 +0,0 @@
-package interpreter_test
-
-import (
- "fmt"
- "log"
- "testing"
-
- "github.com/mvertes/parscan/interpreter"
- "github.com/mvertes/parscan/lang/golang"
-)
-
-type etest struct {
- src, res, err string
- skip bool
-}
-
-func init() {
- log.SetFlags(log.Lshortfile)
-}
-
-func gen(test etest) func(*testing.T) {
- return func(t *testing.T) {
- t.Parallel()
- if test.skip {
- t.Skip()
- }
- interp := interpreter.NewInterpreter(golang.GoSpec)
- errStr := ""
- r, e := interp.Eval(test.src)
- t.Log(r, e)
- if e != nil {
- errStr = e.Error()
- }
- if errStr != test.err {
- t.Errorf("got error %#v, want error %#v", errStr, test.err)
- }
- if res := fmt.Sprintf("%v", r); test.err == "" && res != test.res {
- t.Errorf("got %#v, want %#v", res, test.res)
- }
- }
-}
-
-func run(t *testing.T, tests []etest) {
- for _, test := range tests {
- t.Run("", gen(test))
- }
-}
-
-func TestExpr(t *testing.T) {
- run(t, []etest{
- {src: "", res: "<invalid reflect.Value>"},
- {src: "1+2", res: "3"},
- {src: "1+", err: "block not terminated"},
- {src: "a := 1 + 2; b := 0; a + 1", res: "4"},
- {src: "1+(2+3)", res: "6"},
- {src: "(1+2)+3", res: "6"},
- {src: "(6+(1+2)+3)+5", res: "17"},
- {src: "(6+(1+2+3)+5", err: "1:1: block not terminated"},
- {src: "a := 2; a = 3; a", res: "3"},
- {src: "2 * 3 + 1 == 7", res: "true"},
- {src: "7 == 2 * 3 + 1", res: "true"},
- {src: "1 + 3 * 2 == 2 * 3 + 1", res: "true"},
- {src: "a := 1 + 3 * 2 == 2 * 3 + 1; a", res: "true"},
- {src: "-2", res: "-2"},
- {src: "-2 + 5", res: "3"},
- {src: "5 + -2", res: "3"},
- {src: "!false", res: "true"},
- {src: `a := "hello"`, res: "hello"},
- })
-}
-
-func TestLogical(t *testing.T) {
- run(t, []etest{
- {src: "true && false", res: "false"},
- {src: "true && true", res: "true"},
- {src: "true && true && false", res: "false"},
- {src: "false || true && true", res: "true"},
- {src: "2 < 3 && 1 > 2 || 3 == 3", res: "true"},
- {src: "2 > 3 && 1 > 2 || 3 == 3", res: "true"},
- {src: "2 > 3 || 2 == 1+1 && 3>0", res: "true"},
- {src: "2 > 3 || 2 == 1+1 && 3>4 || 1<2", res: "true"},
- {src: "a := 1+1 < 3 && 4 == 2+2; a", res: "true"},
- {src: "a := 1+1 < 3 || 3 == 2+2; a", res: "true"},
- })
-}
-
-func TestFunc(t *testing.T) {
- run(t, []etest{
- {src: "func f() int {return 2}; a := f(); a", res: "2"},
- {src: "func f() int {return 2}; f()", res: "2"},
- {src: "func f(a int) int {return a+2}; f(3)", res: "5"},
- {src: "func f(a int) int {if a < 4 {a = 5}; return a}; f(3)", res: "5"},
- {src: "func f(a int) int {return a+2}; 7 - f(3)", res: "2"},
- {src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"},
- {src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"},
- {src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"},
- {src: "var a int; func f() {a = a+2}; f(); a", res: "2"},
- {src: "var f = func(a int) int {return a+3}; f(2)", res: "5"},
- })
-}
-
-func TestIf(t *testing.T) {
- run(t, []etest{
- {src: "a := 0; if a == 0 { a = 2 } else { a = 1 }; a", res: "2"},
- {src: "a := 0; if a == 1 { a = 2 } else { a = 1 }; a", res: "1"},
- {src: "a := 0; if a == 1 { a = 2 } else if a == 0 { a = 3 } else { a = 1 }; a", res: "3"},
- {src: "a := 0; if a == 1 { a = 2 } else if a == 2 { a = 3 } else { a = 1 }; a", res: "1"},
- {src: "a := 1; if a > 0 && a < 2 { a = 3 }; a", res: "3"},
- {src: "a := 1; if a < 0 || a < 2 { a = 3 }; a", res: "3"},
- })
-}
-
-func TestFor(t *testing.T) {
- run(t, []etest{
- {src: "a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; a", res: "3"},
- {src: "func f() int {a := 0; for i := 0; i < 3; i = i+1 {a = a+i}; return a}; f()", res: "3"},
- {src: "a := 0; for {a = a+1; if a == 3 {break}}; a", res: "3"},
- {src: "func f() int {a := 0; for {a = a+1; if a == 3 {break}}; return a}; f()", res: "3"},
- {src: "func f() int {a := 0; for {a = a+1; if a < 3 {continue}; break}; return a}; f()", res: "3"},
- })
-}
-
-func TestGoto(t *testing.T) {
- run(t, []etest{
- {src: `
-func f(a int) int {
- a = a+1
- goto end
- a = a+1
-end:
- return a
-}
-f(3)`, res: "4"},
- })
-}
-
-func TestSwitch(t *testing.T) {
- src0 := `func f(a int) int {
- switch a {
- default: a = 0
- case 1,2: a = a+1
- case 3: a = a+2; break; a = 3
- case 4: a = 10
- }
- return a
-}
-`
- src1 := `func f(a int) int {
- switch {
- case a < 3: return 2
- case a < 5: return 5
- default: a = 0
- }
- return a
-}
-`
- run(t, []etest{
- {src: src0 + "f(1)", res: "2"},
- {src: src0 + "f(2)", res: "3"},
- {src: src0 + "f(3)", res: "5"},
- {src: src0 + "f(4)", res: "10"},
- {src: src0 + "f(5)", res: "0"},
-
- {src: src1 + "f(1)", res: "2"},
- {src: src1 + "f(4)", res: "5"},
- {src: src1 + "f(6)", res: "0"},
- })
-}
-
-func TestConst(t *testing.T) {
- src0 := `const (
- a = iota
- b
- c
-)
-`
- run(t, []etest{
- {src: "const a = 1+2; a", res: "3"},
- {src: "const a, b = 1, 2; a+b", res: "3"},
- {src: "const huge = 1 << 100; const four = huge >> 98; four", res: "4"},
-
- {src: src0 + "c", res: "2"},
- })
-}
-
-func TestArray(t *testing.T) {
- run(t, []etest{
- {src: "type T []int; var t T; t", res: "[]"},
- {src: "type T [3]int; var t T; t", res: "[0 0 0]"},
- {src: "type T [3]int; var t T; t[1] = 2; t", res: "[0 2 0]"},
- })
-}
-
-func TestPointer(t *testing.T) {
- run(t, []etest{
- {src: "var a *int; a", res: "<nil>"},
- {src: "var a int; var b *int = &a; *b", res: "0"},
- {src: "var a int = 2; var b *int = &a; *b", res: "2"},
- })
-}
-
-func TestStruct(t *testing.T) {
- run(t, []etest{
- {src: "type T struct {a string; b, c int}; var t T; t", res: "{ 0 0}"},
- {src: "type T struct {a int}; var t T; t.a", res: "0"},
- {src: "type T struct {a int}; var t T; t.a = 1; t.a", res: "1"},
- })
-}
-
-func TestType(t *testing.T) {
- src0 := `type (
- I int
- S string
-)
-`
- run(t, []etest{
- {src: "type t int; var a t = 1; a", res: "1"},
- {src: "type t = int; var a t = 1; a", res: "1"},
- {src: src0 + `var s S = "xx"; s`, res: "xx"},
- })
-}
-
-func TestVar(t *testing.T) {
- run(t, []etest{
- {src: "var a int; a", res: "0"},
- {src: "var a, b, c int; a", res: "0"},
- {src: "var a, b, c int; a + b", res: "0"},
- {src: "var a, b, c int; a + b + c", res: "0"},
- {src: "var a int = 2+1; a", res: "3"},
- {src: "var a, b int = 2, 5; a+b", res: "7"},
- {src: "var x = 5; x", res: "5"},
- {src: "var a = 1; func f() int { var a, b int = 3, 4; return a+b}; a+f()", res: "8"},
- {src: `var a = "hello"; a`, res: "hello"},
- {src: `var (
- a, b int = 4+1, 3
- c = 8
-); a+b+c`, res: "16"},
- })
-}
-
-func TestImport(t *testing.T) {
- src0 := `import (
- "fmt"
-)
-`
- run(t, []etest{
- {src: "fmt.Println(4)", err: "symbol not found: fmt"},
- {src: `import "xxx"`, err: "package not found: xxx"},
- {src: `import "fmt"; fmt.Println(4)`, res: "<nil>"},
- {src: src0 + "fmt.Println(4)", res: "<nil>"},
- {src: `func main() {import "fmt"; fmt.Println("hello")}`, err: "unexpected import"},
- {src: `import m "fmt"; m.Println(4)`, res: "<nil>"},
- {src: `import . "fmt"; Println(4)`, res: "<nil>"},
- })
-}
-
-func TestComposite(t *testing.T) {
- run(t, []etest{
- {src: "type T struct{}; t := T{}; t", res: "{}"},
- {src: "t := struct{}{}; t", res: "{}"},
- // {src: `type T struct{N int; S string}; t := T{2, "foo"}`, res: `{2 foo}`},
- })
-}
diff --git a/interpreter/repl.go b/interpreter/repl.go
deleted file mode 100644
index 73af8a7..0000000
--- a/interpreter/repl.go
+++ /dev/null
@@ -1,35 +0,0 @@
-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
-}