summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/gint/main.go26
-rw-r--r--codegen/compiler.go (renamed from codegen/codegen.go)2
-rw-r--r--codegen/compiler_test.go (renamed from codegen/codegen_test.go)2
-rw-r--r--codegen/interpreter.go38
-rw-r--r--vm0/README.md27
-rw-r--r--vm1/README.md2
-rw-r--r--vm1/vm.go19
7 files changed, 83 insertions, 33 deletions
diff --git a/cmd/gint/main.go b/cmd/gint/main.go
index 85fdba0..15a79b0 100644
--- a/cmd/gint/main.go
+++ b/cmd/gint/main.go
@@ -7,9 +7,7 @@ import (
"github.com/gnolang/parscan/codegen"
"github.com/gnolang/parscan/lang/golang"
- "github.com/gnolang/parscan/parser"
"github.com/gnolang/parscan/vm0"
- "github.com/gnolang/parscan/vm1"
)
func main() {
@@ -50,25 +48,7 @@ func run0(src string) error {
}
func run1(src string) (err error) {
- m := &vm1.Machine{}
- c := codegen.New()
- c.AddSym(fmt.Println, "println", false)
- n := &parser.Node{}
- if n.Child, err = golang.GoParser.Parse(src); err != nil {
- return err
- }
- n.Dot(os.Getenv("DOT"), "")
- if err = c.CodeGen(n); err != nil {
- return err
- }
- c.Emit(n, vm1.Exit)
- log.Println("data:", c.Data)
- log.Println("code:", vm1.Disassemble(c.Code))
- for _, v := range c.Data {
- m.Push(v)
- }
- m.PushCode(c.Code)
- m.SetIP(c.Entry)
- m.Run()
- return
+ i := codegen.NewInterpreter(golang.GoParser)
+ i.AddSym(fmt.Println, "println", false)
+ return i.Eval(src)
}
diff --git a/codegen/codegen.go b/codegen/compiler.go
index d7702cd..e0e97ab 100644
--- a/codegen/codegen.go
+++ b/codegen/compiler.go
@@ -21,7 +21,7 @@ type Compiler struct {
symbols map[string]symbol
}
-func New() *Compiler { return &Compiler{symbols: map[string]symbol{}, Entry: -1} }
+func NewCompiler() *Compiler { return &Compiler{symbols: map[string]symbol{}, Entry: -1} }
type nodedata struct {
ipstart, ipend, symind, fsp int // CFG and symbol node annotations
diff --git a/codegen/codegen_test.go b/codegen/compiler_test.go
index 7262ddc..5989210 100644
--- a/codegen/codegen_test.go
+++ b/codegen/compiler_test.go
@@ -15,7 +15,7 @@ func TestCodeGen(t *testing.T) {
for _, test := range tests {
test := test
t.Run("", func(t *testing.T) {
- c := New()
+ c := NewCompiler()
c.AddSym(fmt.Println, "println", false)
n := &parser.Node{}
var err error
diff --git a/codegen/interpreter.go b/codegen/interpreter.go
new file mode 100644
index 0000000..8527631
--- /dev/null
+++ b/codegen/interpreter.go
@@ -0,0 +1,38 @@
+package codegen
+
+import (
+ "os"
+
+ "github.com/gnolang/parscan/parser"
+ "github.com/gnolang/parscan/vm1"
+)
+
+const debug = true
+
+type Interpreter struct {
+ *parser.Parser
+ *Compiler
+ *vm1.Machine
+}
+
+func NewInterpreter(p *parser.Parser) *Interpreter {
+ return &Interpreter{p, NewCompiler(), &vm1.Machine{}}
+}
+
+func (i *Interpreter) Eval(src string) (err error) {
+ n := &parser.Node{}
+ if n.Child, err = i.Parse(src); err != nil {
+ return err
+ }
+ if debug {
+ n.Dot(os.Getenv("DOT"), "")
+ }
+ if err = i.CodeGen(n); err != nil {
+ return err
+ }
+ i.Emit(n, vm1.Exit)
+ i.Push(i.Data...)
+ i.PushCode(i.Code)
+ i.SetIP(i.Entry)
+ return i.Run()
+}
diff --git a/vm0/README.md b/vm0/README.md
new file mode 100644
index 0000000..fc89429
--- /dev/null
+++ b/vm0/README.md
@@ -0,0 +1,27 @@
+# vm0
+
+vm0 is a virtual machine executing directly the syntax tree.
+
+```mermaid
+graph LR
+s[ ] --> |source| a(scanner)
+--> |tokens| b(parser)
+--> |AST| c(vm)
+subgraph vm0
+ c
+end
+style s height:0px;
+```
+
+The execution is performed by walking the AST and evaluating each
+visited node.
+
+
+## Motivation
+
+- have a reference execution model for each defined language
+- usable for compilation time evaluation
+- to modelize similar VMs (i.e. gnovm)
+- to validate and compare with other VMs (once it is itself validated)
+- could serve as a basis for AST based symbolic execution (to be
+ investigated)
diff --git a/vm1/README.md b/vm1/README.md
index 7c3cd01..a06dd34 100644
--- a/vm1/README.md
+++ b/vm1/README.md
@@ -1,6 +1,6 @@
# vm1
-`vm1` is bytecode based stack machine.
+`vm1` is a bytecode based stack machine.
The purpose of `vm1` is to provide a simple, fast, embeddable and
portable Go execution environment.
diff --git a/vm1/vm.go b/vm1/vm.go
index 6b74e7a..0e9c7f7 100644
--- a/vm1/vm.go
+++ b/vm1/vm.go
@@ -6,6 +6,8 @@ import (
"strconv" // for tracing only
)
+const debug = false
+
// Byte-code instruction set.
const (
// instruction effect on stack: values consumed -- values produced
@@ -55,16 +57,19 @@ type Machine struct {
code [][]int64 // code to execute
mem []any // memory, as a stack
ip, fp int // instruction and frame pointer
- // flags uint // to set debug mode, restrict CallX, etc...
+ // flags uint // to set options such as restrict CallX, etc...
}
// Run runs a program.
-func (m *Machine) Run() {
+func (m *Machine) Run() (err error) {
code, mem, ip, fp, sp := m.code, m.mem, m.ip, m.fp, 0
defer func() { m.mem, m.ip, m.fp = mem, ip, fp }()
trace := func() {
+ if !debug {
+ return
+ }
var op2, op3 string
c := code[ip]
if l := len(c); l > 2 {
@@ -75,7 +80,6 @@ func (m *Machine) Run() {
}
fmt.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-9s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, mem)
}
- _ = trace
for {
sp = len(mem) // stack pointer
@@ -149,17 +153,18 @@ func (m *Machine) Run() {
}
ip++
}
+ return
}
func (m *Machine) PushCode(code [][]int64) (p int) {
p = len(m.code)
m.code = append(m.code, code...)
- return
+ return p
}
-func (m *Machine) SetIP(ip int) { m.ip = ip }
-func (m *Machine) Push(v any) (l int) { l = len(m.mem); m.mem = append(m.mem, v); return }
-func (m *Machine) Pop() (v any) { l := len(m.mem) - 1; v = m.mem[l]; m.mem = m.mem[:l]; return }
+func (m *Machine) SetIP(ip int) { m.ip = ip }
+func (m *Machine) Push(v ...any) (l int) { l = len(m.mem); m.mem = append(m.mem, v...); return }
+func (m *Machine) Pop() (v any) { l := len(m.mem) - 1; v = m.mem[l]; m.mem = m.mem[:l]; return }
// Disassemble returns the code as a readable string.
func Disassemble(code [][]int64) (asm string) {