summaryrefslogtreecommitdiff
path: root/interp/interpreter.go
diff options
context:
space:
mode:
Diffstat (limited to 'interp/interpreter.go')
-rw-r--r--interp/interpreter.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/interp/interpreter.go b/interp/interpreter.go
new file mode 100644
index 0000000..8d372c3
--- /dev/null
+++ b/interp/interpreter.go
@@ -0,0 +1,55 @@
+// Package interp implements an interpreter.
+package interp
+
+import (
+ "reflect"
+
+ "github.com/mvertes/parscan/comp"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/vm"
+)
+
+const debug = true
+
+// Interp represents the state of an interpreter.
+type Interp struct {
+ *comp.Compiler
+ *vm.Machine
+}
+
+// NewInterpreter returns a new interpreter.
+func NewInterpreter(s *lang.Spec) *Interp {
+ return &Interp{comp.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.Generate(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].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().Value, err
+}