summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.golangci.yaml2
-rw-r--r--Makefile2
-rw-r--r--compiler/compiler.go4
-rw-r--r--interpreter/dump_test.go5
-rw-r--r--interpreter/interpreter.go16
-rw-r--r--interpreter/interpreter_test.go7
-rw-r--r--interpreter/repl.go35
-rw-r--r--lang/spec.go2
-rw-r--r--main.go59
-rw-r--r--parser/parse.go4
10 files changed, 68 insertions, 68 deletions
diff --git a/.golangci.yaml b/.golangci.yaml
index a185bb8..bc1fbb6 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -14,10 +14,10 @@ linters:
exclusions:
generated: lax
presets:
- - comments
- common-false-positives
- legacy
- std-error-handling
+ - comments
paths:
- third_party$
- builtin$
diff --git a/Makefile b/Makefile
index 3d7c94e..70b2f3a 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ lint:
# Run tests with race detector, measure coverage.
test:
- go test -race -covermode=atomic -coverpkg=./... -coverprofile=cover.out ./...
+ go test -race -covermode=atomic -coverpkg=./... -coverprofile=cover.out ./interpreter
# Open coverage info in browser
cover: test
diff --git a/compiler/compiler.go b/compiler/compiler.go
index d63dd14..f7c8a12 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -26,9 +26,9 @@ type Compiler struct {
}
// NewCompiler returns a new compiler state for a given scanner.
-func NewCompiler(scanner *scanner.Scanner) *Compiler {
+func NewCompiler(spec *lang.Spec) *Compiler {
return &Compiler{
- Parser: parser.NewParser(scanner, true),
+ Parser: parser.NewParser(spec, true),
Entry: -1,
strings: map[string]int{},
}
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
+}
diff --git a/lang/spec.go b/lang/spec.go
index 92d90f7..b1b2580 100644
--- a/lang/spec.go
+++ b/lang/spec.go
@@ -28,7 +28,7 @@ type TokenProp struct {
Precedence int // operator precedence
}
-// Spec represents the token specification for scanning.
+// Spec represents the language specification for scanning.
type Spec struct {
CharProp [ASCIILen]uint // special Character properties
End map[string]string // end delimiters, indexed by start
diff --git a/main.go b/main.go
index 99652ad..b0ca700 100644
--- a/main.go
+++ b/main.go
@@ -1,25 +1,17 @@
package main
import (
- "bufio"
- "errors"
"flag"
"fmt"
"io"
"log"
"os"
- "reflect"
"strings"
"github.com/mvertes/parscan/interpreter"
"github.com/mvertes/parscan/lang/golang"
- "github.com/mvertes/parscan/scanner"
)
-type Interpreter interface {
- Eval(string) (reflect.Value, error)
-}
-
func main() {
log.SetFlags(log.Lshortfile)
if err := run(os.Args[1:]); err != nil {
@@ -27,41 +19,6 @@ func main() {
}
}
-// isatty returns true if the input stream is a tty (i.e. a character device).
-func isatty(in io.Reader) bool {
- s, ok := in.(interface{ Stat() (os.FileInfo, error) })
- if !ok {
- return false
- }
- stat, err := s.Stat()
- return err == nil && stat.Mode()&os.ModeCharDevice != 0
-}
-
-// repl executes an interactive line oriented Read Eval Print Loop (REPL).
-func repl(interp Interpreter, in io.Reader) (err error) {
- liner := bufio.NewScanner(in)
- text, prompt := "", "> "
- fmt.Print(prompt)
- for liner.Scan() {
- text += liner.Text()
- res, err := interp.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
-}
-
func run(arg []string) (err error) {
var str string
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
@@ -76,7 +33,7 @@ func run(arg []string) (err error) {
}
args := rflag.Args()
- intpr := interpreter.NewInterpreter(scanner.NewScanner(golang.GoSpec))
+ interp := interpreter.NewInterpreter(golang.GoSpec)
var in io.Reader
if str != "" {
@@ -94,13 +51,23 @@ func run(arg []string) (err error) {
}
if isatty(in) {
- return repl(intpr, in)
+ return interp.Repl(in)
}
buf, err := io.ReadAll(in)
if err != nil {
return err
}
- _, err = intpr.Eval(string(buf))
+ _, err = interp.Eval(string(buf))
return err
}
+
+// isatty returns true if the input stream is a tty (i.e. a character device).
+func isatty(in io.Reader) bool {
+ s, ok := in.(interface{ Stat() (os.FileInfo, error) })
+ if !ok {
+ return false
+ }
+ stat, err := s.Stat()
+ return err == nil && stat.Mode()&os.ModeCharDevice != 0
+}
diff --git a/parser/parse.go b/parser/parse.go
index 3a7ce5e..6cfdc08 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -41,9 +41,9 @@ var (
)
// NewParser returns a new parser.
-func NewParser(scanner *scanner.Scanner, noPkg bool) *Parser {
+func NewParser(spec *lang.Spec, noPkg bool) *Parser {
return &Parser{
- Scanner: scanner,
+ Scanner: scanner.NewScanner(spec),
noPkg: noPkg,
Symbols: initUniverse(),
framelen: map[string]int{},