diff options
| author | Marc Vertes <mvertes@free.fr> | 2023-08-24 17:16:39 +0200 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2023-08-24 17:16:39 +0200 |
| commit | 9fdef50606a2942389189cd61397e17c0a0ccfd7 (patch) | |
| tree | e89a4aa3a61a6ab41abb4b6533ec9a2d9d7e2ce9 | |
| parent | ec8eb9defc7cd68fcd3afa0058773bcbc33ecd63 (diff) | |
codegen: add Interpreter struct
This makes the code easier to use.
| -rw-r--r-- | cmd/gint/main.go | 26 | ||||
| -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.go | 38 | ||||
| -rw-r--r-- | vm0/README.md | 27 | ||||
| -rw-r--r-- | vm1/README.md | 2 | ||||
| -rw-r--r-- | vm1/vm.go | 19 |
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. @@ -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) { |
