diff options
| -rw-r--r-- | .golangci.yaml | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | compiler/compiler.go | 4 | ||||
| -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 | ||||
| -rw-r--r-- | lang/spec.go | 2 | ||||
| -rw-r--r-- | main.go | 59 | ||||
| -rw-r--r-- | parser/parse.go | 4 |
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$ @@ -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 @@ -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{}, |
