summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/proposals/PIP-000.md35
-rw-r--r--docs/proposals/README.md11
-rw-r--r--go.mod2
-rw-r--r--lang/golang/go.go2
-rw-r--r--main.go6
-rw-r--r--parser/compiler.go75
-rw-r--r--parser/decl.go12
-rw-r--r--parser/expr.go4
-rw-r--r--parser/interpreter.go6
-rw-r--r--parser/interpreter_test.go7
-rw-r--r--parser/parse.go21
-rw-r--r--parser/symbol.go31
-rw-r--r--parser/tokens.go4
-rw-r--r--parser/type.go31
-rw-r--r--readme.md18
-rw-r--r--scanner/scan.go2
-rw-r--r--scanner/scan_test.go4
-rw-r--r--vm/type.go74
-rw-r--r--vm/vm.go88
-rw-r--r--vm/vm_test.go12
20 files changed, 278 insertions, 167 deletions
diff --git a/docs/proposals/PIP-000.md b/docs/proposals/PIP-000.md
new file mode 100644
index 0000000..67b0c3d
--- /dev/null
+++ b/docs/proposals/PIP-000.md
@@ -0,0 +1,35 @@
+# PIP Template
+
+| Field | Value |
+| --- | --- |
+| ENIP | 0 |
+| Title | PIP Template |
+| Author | Antonio Navarro |
+| Status | Accepted |
+| Created | 2024-01-15 |
+| Updated | 2024-01-18 |
+| Target version | optional |
+
+## Abstract
+
+A short description of the technical issue being addressed.
+
+## Rationale
+
+Proposal motivation.
+
+## Specification
+
+Technical specification of the changes proposed.
+
+## Alternatives
+
+How the issue is currently solved or can be solved if this change is not accepted?
+
+## Impact
+
+How this change would impact parscan functionality: backward compatibility will break, performance improvements or issues, corner cases and so on.
+
+## References
+
+Links to additional documentation describing related features or other kind of related information.
diff --git a/docs/proposals/README.md b/docs/proposals/README.md
new file mode 100644
index 0000000..2addbeb
--- /dev/null
+++ b/docs/proposals/README.md
@@ -0,0 +1,11 @@
+# Parscan Improvement Proposals
+
+## Introduction
+
+This is the index of Parscan Improvement Proposals, known as PIPs.
+
+## All Proposals by Number
+
+| Number | Status | Title |
+| ------ | -------- |----------------------------------------------------------------------|
+| 0 | Accepted | [PIP Template](PIP-000.md) |
diff --git a/go.mod b/go.mod
index a7c2c9e..bd9551c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module github.com/gnolang/parscan
+module github.com/mvertes/parscan
go 1.20
diff --git a/lang/golang/go.go b/lang/golang/go.go
index f5a6578..5b0ffa6 100644
--- a/lang/golang/go.go
+++ b/lang/golang/go.go
@@ -1,6 +1,6 @@
package golang
-import "github.com/gnolang/parscan/lang"
+import "github.com/mvertes/parscan/lang"
var GoSpec = &lang.Spec{
CharProp: [lang.ASCIILen]uint{
diff --git a/main.go b/main.go
index 511b5ab..5e0989b 100644
--- a/main.go
+++ b/main.go
@@ -9,9 +9,9 @@ import (
"log"
"os"
- "github.com/gnolang/parscan/lang/golang"
- "github.com/gnolang/parscan/parser"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang/golang"
+ "github.com/mvertes/parscan/parser"
+ "github.com/mvertes/parscan/scanner"
)
type Interpreter interface {
diff --git a/parser/compiler.go b/parser/compiler.go
index bb88026..82228ed 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -3,19 +3,18 @@ package parser
import (
"fmt"
"log"
- "reflect"
"strconv"
- "github.com/gnolang/parscan/lang"
- "github.com/gnolang/parscan/scanner"
- "github.com/gnolang/parscan/vm"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/scanner"
+ "github.com/mvertes/parscan/vm"
)
type Compiler struct {
*Parser
- vm.Code // produced code, to fill VM with
- Data []reflect.Value // produced data, will be at the bottom of VM stack
- Entry int // offset in Code to start execution from (skip function defintions)
+ vm.Code // produced code, to fill VM with
+ Data []vm.Value // produced data, will be at the bottom of VM stack
+ Entry int // offset in Code to start execution from (skip function defintions)
strings map[string]int // locations of strings in Data
}
@@ -28,7 +27,7 @@ func NewCompiler(scanner *scanner.Scanner) *Compiler {
}
}
-func (c *Compiler) AddSym(name string, value reflect.Value) int {
+func (c *Compiler) AddSym(name string, value vm.Value) int {
p := len(c.Data)
c.Data = append(c.Data, value)
c.Parser.AddSym(p, name, value)
@@ -51,7 +50,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if err != nil {
return err
}
- push(&symbol{kind: symConst, value: reflect.ValueOf(n), Type: reflect.TypeOf(0)})
+ push(&symbol{kind: symConst, value: vm.ValueOf(n), Type: vm.TypeOf(0)})
emit(int64(t.Pos), vm.Push, int64(n))
case lang.String:
@@ -59,10 +58,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
i, ok := c.strings[s]
if !ok {
i = len(c.Data)
- c.Data = append(c.Data, reflect.ValueOf(s))
+ c.Data = append(c.Data, vm.ValueOf(s))
c.strings[s] = i
}
- push(&symbol{kind: symConst, value: reflect.ValueOf(s)})
+ push(&symbol{kind: symConst, value: vm.ValueOf(s)})
emit(int64(t.Pos), vm.Dup, int64(i))
case lang.Add:
@@ -88,7 +87,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
// Nothing to do.
case lang.Addr:
- push(&symbol{Type: reflect.PointerTo(pop().Type)})
+ push(&symbol{Type: vm.PointerTo(pop().Type)})
emit(int64(t.Pos), vm.Addr)
case lang.Deref:
@@ -110,16 +109,16 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
case lang.Call:
typ := pop().Type
// TODO: pop input types (careful with variadic function)
- for i := 0; i < typ.NumOut(); i++ {
+ for i := 0; i < typ.Rtype.NumOut(); i++ {
push(&symbol{Type: typ.Out(i)})
}
emit(int64(t.Pos), vm.Call)
case lang.CallX:
- typ := pop().Type
+ rtyp := pop().value.Data.Type()
// TODO: pop input types (careful with variadic function)
- for i := 0; i < typ.NumOut(); i++ {
- push(&symbol{Type: typ.Out(i)})
+ for i := 0; i < rtyp.NumOut(); i++ {
+ push(&symbol{Type: &vm.Type{Rtype: rtyp.Out(i)}})
}
emit(int64(t.Pos), vm.CallX, int64(t.Beg))
@@ -131,7 +130,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
st := tokens[i-1]
l := len(c.Data)
typ := pop().Type
- v := reflect.New(typ).Elem()
+ v := vm.NewValue(typ)
c.addSym(l, st.Str, v, symVar, typ, false)
c.Data = append(c.Data, v)
emit(int64(st.Pos), vm.Assign, int64(l))
@@ -149,7 +148,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
typ := pop().Type
if s.Type == nil {
s.Type = typ
- s.value = reflect.New(typ).Elem()
+ s.value = vm.NewValue(typ)
}
if s.local {
if !s.used {
@@ -199,7 +198,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
lc := len(c.Code)
s, ok := c.symbols[t.Str]
if ok {
- s.value = reflect.ValueOf(lc)
+ s.value = vm.ValueOf(lc)
if s.kind == symFunc {
// label is a function entry point, register its code address in data.
s.index = len(c.Data)
@@ -208,7 +207,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
c.Data[s.index] = s.value
}
} else {
- c.symbols[t.Str] = &symbol{kind: symLabel, value: reflect.ValueOf(lc)}
+ c.symbols[t.Str] = &symbol{kind: symLabel, value: vm.ValueOf(lc)}
}
case lang.JumpFalse:
@@ -219,7 +218,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
t.Beg = len(c.Code)
fixList = append(fixList, t)
} else {
- i = s.value.Int() - int64(len(c.Code))
+ i = s.value.Data.Int() - int64(len(c.Code))
}
emit(int64(t.Pos), vm.JumpFalse, i)
@@ -231,7 +230,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
t.Beg = len(c.Code)
fixList = append(fixList, t)
} else {
- i = s.value.Int() - int64(len(c.Code))
+ i = s.value.Data.Int() - int64(len(c.Code))
}
emit(int64(t.Pos), vm.JumpSetFalse, i)
@@ -243,7 +242,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
t.Beg = len(c.Code)
fixList = append(fixList, t)
} else {
- i = s.value.Int() - int64(len(c.Code))
+ i = s.value.Data.Int() - int64(len(c.Code))
}
emit(int64(t.Pos), vm.JumpSetTrue, int64(i))
@@ -254,12 +253,12 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
t.Beg = len(c.Code)
fixList = append(fixList, t)
} else {
- i = s.value.Int() - int64(len(c.Code))
+ i = s.value.Data.Int() - int64(len(c.Code))
}
emit(int64(t.Pos), vm.Jump, i)
case lang.Period:
- if f, ok := pop().Type.FieldByName("X" + t.Str[1:]); ok {
+ if f, ok := pop().Type.Rtype.FieldByName("X" + t.Str[1:]); ok {
emit(append([]int64{int64(t.Pos), vm.Field}, slint64(f.Index)...)...)
break
}
@@ -291,14 +290,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if !ok {
return fmt.Errorf("label not found: %q", label)
}
- c.Code[t.Beg][2] = s.value.Int() - int64(t.Beg)
+ c.Code[t.Beg][2] = s.value.Data.Int() - int64(t.Beg)
}
return err
}
-func arithmeticOpType(s1, s2 *symbol) reflect.Type { return symtype(s1) }
-func booleanOpType(s1, s2 *symbol) reflect.Type { return reflect.TypeOf(true) }
+func arithmeticOpType(s1, s2 *symbol) *vm.Type { return symtype(s1) }
+func booleanOpType(s1, s2 *symbol) *vm.Type { return vm.TypeOf(true) }
func (c *Compiler) PrintCode() {
labels := map[int][]string{} // labels indexed by code location
@@ -306,7 +305,7 @@ func (c *Compiler) PrintCode() {
for name, sym := range c.symbols {
if sym.kind == symLabel || sym.kind == symFunc {
- i := int(sym.value.Int())
+ i := int(sym.value.Data.Int())
labels[i] = append(labels[i], name)
}
if sym.used {
@@ -354,27 +353,19 @@ func (c *Compiler) PrintData() {
}
fmt.Println("# Data:")
for i, d := range c.Data {
- fmt.Printf("%4d %T %v %v\n", i, d.Interface(), d, dict[i])
- }
-}
-
-func (c *Compiler) NumIn(i int) (int, bool) {
- t := reflect.TypeOf(c.Data[i])
- if t.Kind() == reflect.Func {
- return t.NumIn(), t.IsVariadic()
+ fmt.Printf("%4d %T %v %v\n", i, d.Data.Interface(), d.Data, dict[i])
}
- return -1, false
}
-func (c *Compiler) typeSym(t reflect.Type) *symbol {
- tsym, ok := c.symbols[t.String()]
+func (c *Compiler) typeSym(t *vm.Type) *symbol {
+ tsym, ok := c.symbols[t.Rtype.String()]
if !ok {
tsym = &symbol{index: unsetAddr, kind: symType, Type: t}
- c.symbols[t.String()] = tsym
+ c.symbols[t.Rtype.String()] = tsym
}
if tsym.index == unsetAddr {
tsym.index = len(c.Data)
- c.Data = append(c.Data, reflect.New(t).Elem())
+ c.Data = append(c.Data, vm.NewValue(t))
}
return tsym
}
diff --git a/parser/decl.go b/parser/decl.go
index 54a2989..d794131 100644
--- a/parser/decl.go
+++ b/parser/decl.go
@@ -4,14 +4,14 @@ import (
"errors"
"go/constant"
"go/token"
- "reflect"
"strings"
- "github.com/gnolang/parscan/lang"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/scanner"
+ "github.com/mvertes/parscan/vm"
)
-var nilValue = reflect.ValueOf(nil)
+var nilValue = vm.ValueOf(nil)
func (p *Parser) ParseConst(in Tokens) (out Tokens, err error) {
if len(in) < 2 {
@@ -78,7 +78,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) {
kind: symConst,
index: unsetAddr,
cval: cval,
- value: reflect.ValueOf(constValue(cval)),
+ value: vm.ValueOf(constValue(cval)),
local: p.funcScope != "",
used: true,
}
@@ -225,7 +225,7 @@ func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) {
if err != nil {
return out, err
}
- p.addSym(unsetAddr, in[0].Str, reflect.New(typ).Elem(), symType, typ, p.funcScope != "")
+ p.addSym(unsetAddr, in[0].Str, vm.NewValue(typ), symType, typ, p.funcScope != "")
return out, err
}
diff --git a/parser/expr.go b/parser/expr.go
index 45eb880..e4cef1a 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -5,8 +5,8 @@ import (
"log"
"strconv"
- "github.com/gnolang/parscan/lang"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/scanner"
)
func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) {
diff --git a/parser/interpreter.go b/parser/interpreter.go
index 72fc667..c5e463a 100644
--- a/parser/interpreter.go
+++ b/parser/interpreter.go
@@ -1,8 +1,8 @@
package parser
import (
- "github.com/gnolang/parscan/scanner"
- "github.com/gnolang/parscan/vm"
+ "github.com/mvertes/parscan/scanner"
+ "github.com/mvertes/parscan/vm"
)
const debug = true
@@ -41,7 +41,7 @@ func (i *Interpreter) Eval(src string) (res any, err error) {
i.PrintCode()
}
err = i.Run()
- return i.Top(), err
+ return i.Top().Data, err
}
func max(a, b int) int {
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index 9110e3c..8f166df 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -5,9 +5,9 @@ import (
"log"
"testing"
- "github.com/gnolang/parscan/lang/golang"
- "github.com/gnolang/parscan/parser"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang/golang"
+ "github.com/mvertes/parscan/parser"
+ "github.com/mvertes/parscan/scanner"
)
type etest struct {
@@ -97,6 +97,7 @@ func TestFunc(t *testing.T) {
{src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"},
{src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"},
{src: "func f(a, b, c int) int {return a+b-c} ; f(7, 1, 3)", res: "5"},
+ {src: "var a int; func f() {a = a+2}; f(); a", res: "2"},
})
}
diff --git a/parser/parse.go b/parser/parse.go
index 131e9c0..547957b 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -7,8 +7,8 @@ import (
"strconv"
"strings"
- "github.com/gnolang/parscan/lang"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/scanner"
)
type Parser struct {
@@ -246,6 +246,15 @@ func (p *Parser) ParseFunc(in Tokens) (out Tokens, err error) {
out = append(out, scanner.Token{Id: lang.Grow, Beg: l})
}
out = append(out, toks...)
+ if out[len(out)-1].Id != lang.Return {
+ // Ensure that a return statment is always added at end of function.
+ // TODO: detect missing or wrong returns.
+ x, err := p.ParseReturn(nil)
+ if err != nil {
+ return out, err
+ }
+ out = append(out, x...)
+ }
out = append(out, scanner.Token{Id: lang.Label, Str: fname + "_end"})
return out, err
}
@@ -411,17 +420,19 @@ func (p *Parser) ParseLabel(in Tokens) (out Tokens, err error) {
}
func (p *Parser) ParseReturn(in Tokens) (out Tokens, err error) {
- if len(in) > 1 {
+ if l := len(in); l > 1 {
if out, err = p.ParseExpr(in[1:]); err != nil {
return out, err
}
+ } else if l == 0 {
+ in = Tokens{{Id: lang.Return}} // Implicit return in functions with no return parameters.
}
// TODO: the function symbol should be already present in the parser context.
// otherwise no way to handle anonymous func.
s := p.function
- in[0].Beg = s.Type.NumOut()
- in[0].End = s.Type.NumIn()
+ in[0].Beg = s.Type.Rtype.NumOut()
+ in[0].End = s.Type.Rtype.NumIn()
out = append(out, in[0])
return out, err
}
diff --git a/parser/symbol.go b/parser/symbol.go
index 6c5ab9b..c75f241 100644
--- a/parser/symbol.go
+++ b/parser/symbol.go
@@ -3,8 +3,9 @@ package parser
import (
"fmt"
"go/constant"
- "reflect"
"strings"
+
+ "github.com/mvertes/parscan/vm"
)
type symKind int
@@ -23,25 +24,25 @@ const unsetAddr = -65535
type symbol struct {
kind symKind
index int // address of symbol in frame
- value reflect.Value //
+ Type *vm.Type //
+ value vm.Value //
cval constant.Value //
- Type reflect.Type //
local bool // if true address is relative to local frame, otherwise global
used bool //
}
-func symtype(s *symbol) reflect.Type {
+func symtype(s *symbol) *vm.Type {
if s.Type != nil {
return s.Type
}
- return reflect.TypeOf(s.value)
+ return vm.TypeOf(s.value)
}
-func (p *Parser) AddSym(i int, name string, v reflect.Value) {
+func (p *Parser) AddSym(i int, name string, v vm.Value) {
p.addSym(i, name, v, symValue, nil, false)
}
-func (p *Parser) addSym(i int, name string, v reflect.Value, k symKind, t reflect.Type, local bool) {
+func (p *Parser) addSym(i int, name string, v vm.Value, k symKind, t *vm.Type, local bool) {
name = strings.TrimPrefix(name, "/")
p.symbols[name] = &symbol{kind: k, index: i, local: local, value: v, Type: t}
}
@@ -66,17 +67,17 @@ func (p *Parser) getSym(name, scope string) (sym *symbol, sc string, ok bool) {
func initUniverse() map[string]*symbol {
return map[string]*symbol{
- "any": {kind: symType, index: unsetAddr, Type: reflect.TypeOf((*any)(nil)).Elem()},
- "bool": {kind: symType, index: unsetAddr, Type: reflect.TypeOf((*bool)(nil)).Elem()},
- "error": {kind: symType, index: unsetAddr, Type: reflect.TypeOf((*error)(nil)).Elem()},
- "int": {kind: symType, index: unsetAddr, Type: reflect.TypeOf((*int)(nil)).Elem()},
- "string": {kind: symType, index: unsetAddr, Type: reflect.TypeOf((*string)(nil)).Elem()},
+ "any": {kind: symType, index: unsetAddr, Type: vm.TypeOf((*any)(nil)).Elem()},
+ "bool": {kind: symType, index: unsetAddr, Type: vm.TypeOf((*bool)(nil)).Elem()},
+ "error": {kind: symType, index: unsetAddr, Type: vm.TypeOf((*error)(nil)).Elem()},
+ "int": {kind: symType, index: unsetAddr, Type: vm.TypeOf((*int)(nil)).Elem()},
+ "string": {kind: symType, index: unsetAddr, Type: vm.TypeOf((*string)(nil)).Elem()},
"nil": {index: unsetAddr},
"iota": {kind: symConst, index: unsetAddr},
- "true": {index: unsetAddr, value: reflect.ValueOf(true), Type: reflect.TypeOf(true)},
- "false": {index: unsetAddr, value: reflect.ValueOf(false), Type: reflect.TypeOf(false)},
+ "true": {index: unsetAddr, value: vm.ValueOf(true), Type: vm.TypeOf(true)},
+ "false": {index: unsetAddr, value: vm.ValueOf(false), Type: vm.TypeOf(false)},
- "println": {index: unsetAddr, value: reflect.ValueOf(func(v ...any) { fmt.Println(v...) })},
+ "println": {index: unsetAddr, value: vm.ValueOf(func(v ...any) { fmt.Println(v...) })},
}
}
diff --git a/parser/tokens.go b/parser/tokens.go
index acffe58..e581eb5 100644
--- a/parser/tokens.go
+++ b/parser/tokens.go
@@ -3,8 +3,8 @@ package parser
import (
"fmt"
- "github.com/gnolang/parscan/lang"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/scanner"
)
type Tokens []scanner.Token
diff --git a/parser/type.go b/parser/type.go
index ff16e4f..cd63a46 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -3,10 +3,10 @@ package parser
import (
"errors"
"fmt"
- "reflect"
"strings"
- "github.com/gnolang/parscan/lang"
+ "github.com/mvertes/parscan/lang"
+ "github.com/mvertes/parscan/vm"
)
type typeFlag int
@@ -27,7 +27,7 @@ var (
// ParseTypeExpr parses a list of tokens defining a type expresssion and returns
// the corresponding runtime type or an error.
-func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.Type, err error) {
+func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) {
switch in[0].Id {
case lang.BracketBlock:
typ, err := p.ParseTypeExpr(in[1:])
@@ -47,16 +47,16 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.Type, err error) {
if !ok {
return nil, fmt.Errorf("invalid size")
}
- return reflect.ArrayOf(size, typ), nil
+ return vm.ArrayOf(size, typ), nil
}
- return reflect.SliceOf(typ), nil
+ return vm.SliceOf(typ), nil
case lang.Mul:
typ, err := p.ParseTypeExpr(in[1:])
if err != nil {
return nil, err
}
- return reflect.PointerTo(typ), nil
+ return vm.PointerTo(typ), nil
case lang.Func:
// Get argument and return token positions depending on function pattern:
@@ -95,7 +95,7 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.Type, err error) {
if err != nil {
return nil, err
}
- return reflect.FuncOf(arg, ret, false), nil
+ return vm.FuncOf(arg, ret, false), nil
case lang.Ident:
// TODO: selector expression (pkg.type)
@@ -112,18 +112,19 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.Type, err error) {
if in, err = p.Scan(in[1].Block(), false); err != nil {
return nil, err
}
- var fields []reflect.StructField
+ var fields []*vm.Type
for _, lt := range in.Split(lang.Semicolon) {
types, names, err := p.parseParamTypes(lt, parseTypeType)
+ fmt.Println("### Names:", names, types)
if err != nil {
return nil, err
}
for i, name := range names {
- fields = append(fields, reflect.StructField{Name: "X" + name, Type: types[i]})
+ fields = append(fields, &vm.Type{Name: name, Rtype: types[i].Rtype})
// TODO: handle embedded fields
}
}
- return reflect.StructOf(fields), nil
+ return vm.StructOf(fields), nil
default:
return nil, fmt.Errorf("%w: %v", TypeNotImplementedErr, in[0].Name())
@@ -132,7 +133,7 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ reflect.Type, err error) {
// parseParamTypes parses a list of comma separated typed parameters and returns a list of
// runtime types. Implicit parameter names and types are supported.
-func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type, vars []string, err error) {
+func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []*vm.Type, vars []string, err error) {
// Parse from right to left, to allow multiple comma separated parameters of the same type.
list := in.Split(lang.Comma)
for i := len(list) - 1; i >= 0; i-- {
@@ -150,7 +151,7 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type
return nil, nil, MissingTypeErr
}
// Type was ommitted, apply the previous one from the right.
- types = append([]reflect.Type{types[0]}, types...)
+ types = append([]*vm.Type{types[0]}, types...)
p.addSymVar(i, param, types[0], flag, local)
vars = append(vars, param)
continue
@@ -163,14 +164,14 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type
if param != "" {
p.addSymVar(i, param, typ, flag, local)
}
- types = append([]reflect.Type{typ}, types...)
+ types = append([]*vm.Type{typ}, types...)
vars = append(vars, param)
}
return types, vars, err
}
-func (p *Parser) addSymVar(index int, name string, typ reflect.Type, flag typeFlag, local bool) {
- zv := reflect.New(typ).Elem()
+func (p *Parser) addSymVar(index int, name string, typ *vm.Type, flag typeFlag, local bool) {
+ zv := vm.NewValue(typ)
switch flag {
case parseTypeIn:
p.addSym(-index-2, name, zv, symVar, typ, true)
diff --git a/readme.md b/readme.md
index 7c28d83..a149a20 100644
--- a/readme.md
+++ b/readme.md
@@ -6,23 +6,11 @@ languages and virtual machines.
The first language definition is a subset of Go, enough to implement
simple benchmarks, as fibonacci numbers.
-The first VM is a stack machine, operated by walking directly the AST.
-
-The next step is to add a byte-code based VM and the corresponding byte code
-generator.
-
-Further steps is to get closer to full Go spec and / or introduce new
-languages definitions and new VM implementations.
+A byte-code based VM and the corresponding byte code generator are
+provided.
Note: this is highly experimental and unstable.
-
## Usage
-`go run ./cmd/gint < ./samples/fib`
-
-To debug visually the AST, you can set the `DOT` env to a command able
-to a display a graphviz dot input, such as `dot -Txlib` or `dotty -`
-(for old graphviz versions like mine):
-
-`DOT="dot -Txlib" go run ./cmd/gint < ./samples/fib`
+`go run . ./samples/fib`
diff --git a/scanner/scan.go b/scanner/scan.go
index 99d6768..f5d56d1 100644
--- a/scanner/scan.go
+++ b/scanner/scan.go
@@ -6,7 +6,7 @@ import (
"regexp"
"strings"
- "github.com/gnolang/parscan/lang"
+ "github.com/mvertes/parscan/lang"
)
var (
diff --git a/scanner/scan_test.go b/scanner/scan_test.go
index 496e4e4..ff86535 100644
--- a/scanner/scan_test.go
+++ b/scanner/scan_test.go
@@ -5,8 +5,8 @@ import (
"log"
"testing"
- "github.com/gnolang/parscan/lang/golang"
- "github.com/gnolang/parscan/scanner"
+ "github.com/mvertes/parscan/lang/golang"
+ "github.com/mvertes/parscan/scanner"
)
var GoScanner *scanner.Scanner
diff --git a/vm/type.go b/vm/type.go
new file mode 100644
index 0000000..d29de48
--- /dev/null
+++ b/vm/type.go
@@ -0,0 +1,74 @@
+package vm
+
+import "reflect"
+
+// Runtime type and value representations (based on reflect).
+
+// Type is the representation of a runtime type.
+type Type struct {
+ Name string
+ Rtype reflect.Type
+}
+
+func (t *Type) Elem() *Type {
+ return &Type{Rtype: t.Rtype.Elem()}
+}
+
+func (t *Type) Out(i int) *Type {
+ return &Type{Rtype: t.Rtype.Out(i)}
+}
+
+// Value is the representation of a runtime value.
+type Value struct {
+ Type *Type
+ Data reflect.Value
+}
+
+// NewValue returns an addressable zero value for the specified type.
+func NewValue(typ *Type) Value {
+ return Value{Type: typ, Data: reflect.New(typ.Rtype).Elem()}
+}
+
+// TypeOf returns the runtime type of v.
+func TypeOf(v any) *Type {
+ t := reflect.TypeOf(v)
+ return &Type{Name: t.Name(), Rtype: t}
+}
+
+// ValueOf returns the runtime value of v.
+func ValueOf(v any) Value {
+ return Value{Data: reflect.ValueOf(v)}
+}
+
+func PointerTo(t *Type) *Type {
+ return &Type{Rtype: reflect.PointerTo(t.Rtype)}
+}
+
+func ArrayOf(size int, t *Type) *Type {
+ return &Type{Rtype: reflect.ArrayOf(size, t.Rtype)}
+}
+
+func SliceOf(t *Type) *Type {
+ return &Type{Rtype: reflect.SliceOf(t.Rtype)}
+}
+
+func FuncOf(arg, ret []*Type, variadic bool) *Type {
+ a := make([]reflect.Type, len(arg))
+ for i, e := range arg {
+ a[i] = e.Rtype
+ }
+ r := make([]reflect.Type, len(ret))
+ for i, e := range ret {
+ r[i] = e.Rtype
+ }
+ return &Type{Rtype: reflect.FuncOf(a, r, variadic)}
+}
+
+func StructOf(fields []*Type) *Type {
+ rf := make([]reflect.StructField, len(fields))
+ for i, f := range fields {
+ rf[i].Name = "X" + f.Name
+ rf[i].Type = f.Rtype
+ }
+ return &Type{Rtype: reflect.StructOf(rf)}
+}
diff --git a/vm/vm.go b/vm/vm.go
index f282367..a8c1f28 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -89,10 +89,10 @@ type Code [][]int64
// Machine represents a virtual machine.
type Machine struct {
- code Code // code to execute
- mem []reflect.Value // memory, as a stack
- ip, fp int // instruction and frame pointer
- ic uint64 // instruction counter, incremented at each instruction executed
+ code Code // code to execute
+ mem []Value // memory, as a stack
+ ip, fp int // instruction and frame pointer
+ ic uint64 // instruction counter, incremented at each instruction executed
// flags uint // to set options such as restrict CallX, etc...
}
@@ -123,27 +123,27 @@ func (m *Machine) Run() (err error) {
ic++
switch op := code[ip]; op[1] {
case Add:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() + mem[sp-1].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-2].Data.Int() + mem[sp-1].Data.Int()))
mem = mem[:sp-1]
case Mul:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() * mem[sp-1].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-2].Data.Int() * mem[sp-1].Data.Int()))
mem = mem[:sp-1]
case Addr:
- mem[sp-1] = mem[sp-1].Addr()
+ mem[sp-1].Data = mem[sp-1].Data.Addr()
case Assign:
- mem[op[2]].Set(mem[sp-1])
+ mem[op[2]].Data.Set(mem[sp-1].Data)
mem = mem[:sp-1]
case Fassign:
- mem[fp+int(op[2])-1].Set(mem[sp-1])
+ mem[fp+int(op[2])-1].Data.Set(mem[sp-1].Data)
mem = mem[:sp-1]
case Call:
- nip := int(mem[sp-1].Int())
- mem = append(mem[:sp-1], reflect.ValueOf(ip+1), reflect.ValueOf(fp))
+ nip := int(mem[sp-1].Data.Int())
+ mem = append(mem[:sp-1], ValueOf(ip+1), ValueOf(fp))
ip = nip
fp = sp + 1
continue
case Calli:
- mem = append(mem, reflect.ValueOf(ip+1), reflect.ValueOf(fp))
+ mem = append(mem, ValueOf(ip+1), ValueOf(fp))
fp = sp + 2
ip += int(op[2])
continue
@@ -151,57 +151,57 @@ func (m *Machine) Run() (err error) {
l := int(op[2])
in := make([]reflect.Value, l)
for i := range in {
- in[i] = mem[sp-2-i]
+ in[i] = mem[sp-2-i].Data
}
- f := mem[sp-1]
+ f := mem[sp-1].Data
mem = mem[:sp-l-1]
for _, v := range f.Call(in) {
- mem = append(mem, v)
+ mem = append(mem, Value{Data: v})
}
case Deref:
- mem[sp-1] = mem[sp-1].Elem()
+ mem[sp-1].Data = mem[sp-1].Data.Elem()
case Dup:
mem = append(mem, mem[int(op[2])])
case New:
- mem[int(op[2])+fp-1] = reflect.New(mem[int(op[3])].Type()).Elem()
+ mem[int(op[2])+fp-1] = NewValue(mem[int(op[3])].Type)
case Equal:
- mem[sp-2] = reflect.ValueOf(mem[sp-2].Equal(mem[sp-1]))
+ mem[sp-2] = ValueOf(mem[sp-2].Data.Equal(mem[sp-1].Data))
mem = mem[:sp-1]
case EqualSet:
- if mem[sp-2].Equal(mem[sp-1]) {
+ if mem[sp-2].Data.Equal(mem[sp-1].Data) {
// If equal then lhs and rhs are popped, replaced by test result, as in Equal.
- mem[sp-2] = reflect.ValueOf(true)
+ mem[sp-2] = ValueOf(true)
mem = mem[:sp-1]
} else {
// If not equal then the lhs is let on stack for further processing.
// This is used to simplify bytecode in case clauses of switch statments.
- mem[sp-1] = reflect.ValueOf(false)
+ mem[sp-1] = ValueOf(false)
}
case Exit:
return err
case Fdup:
mem = append(mem, mem[int(op[2])+fp-1])
case Field:
- mem[sp-1] = mem[sp-1].FieldByIndex(slint(op[2:]))
+ mem[sp-1].Data = mem[sp-1].Data.FieldByIndex(slint(op[2:]))
case Jump:
ip += int(op[2])
continue
case JumpTrue:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
mem = mem[:sp-1]
if cond {
ip += int(op[2])
continue
}
case JumpFalse:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
mem = mem[:sp-1]
if !cond {
ip += int(op[2])
continue
}
case JumpSetTrue:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
if cond {
ip += int(op[2])
// Note that stack is not modified if cond is true
@@ -209,7 +209,7 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case JumpSetFalse:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
if !cond {
ip += int(op[2])
// Note that stack is not modified if cond is false
@@ -217,39 +217,39 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case Greater:
- mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() > mem[sp-2].Int())
+ mem[sp-2] = ValueOf(mem[sp-1].Data.Int() > mem[sp-2].Data.Int())
mem = mem[:sp-1]
case Lower:
- mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() < mem[sp-2].Int())
+ mem[sp-2] = ValueOf(mem[sp-1].Data.Int() < mem[sp-2].Data.Int())
mem = mem[:sp-1]
case Loweri:
- mem[sp-1] = reflect.ValueOf(mem[sp-1].Int() < op[2])
+ mem[sp-1] = ValueOf(mem[sp-1].Data.Int() < op[2])
case Not:
- mem[sp-1] = reflect.ValueOf(!mem[sp-1].Bool())
+ mem[sp-1] = ValueOf(!mem[sp-1].Data.Bool())
case Pop:
mem = mem[:sp-int(op[2])]
case Push:
//mem = append(mem, reflect.ValueOf(int(op[2])))
- mem = append(mem, reflect.New(reflect.TypeOf(0)).Elem())
- mem[sp].SetInt(op[2])
+ mem = append(mem, NewValue(TypeOf(0)))
+ mem[sp].Data.SetInt(op[2])
case Grow:
- mem = append(mem, make([]reflect.Value, op[2])...)
+ mem = append(mem, make([]Value, op[2])...)
case Return:
- ip = int(mem[fp-2].Int())
+ ip = int(mem[fp-2].Data.Int())
ofp := fp
- fp = int(mem[fp-1].Int())
+ fp = int(mem[fp-1].Data.Int())
mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...)
continue
case Sub:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-1].Int() - mem[sp-2].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-1].Data.Int() - mem[sp-2].Data.Int()))
mem = mem[:sp-1]
case Subi:
- mem[sp-1] = reflect.ValueOf(int(mem[sp-1].Int() - op[2]))
+ mem[sp-1] = ValueOf(int(mem[sp-1].Data.Int() - op[2]))
case Index:
- mem[sp-2] = mem[sp-1].Index(int(mem[sp-2].Int()))
+ mem[sp-2].Data = mem[sp-1].Data.Index(int(mem[sp-2].Data.Int()))
mem = mem[:sp-1]
case Vassign:
- mem[sp-1].Set(mem[sp-2])
+ mem[sp-1].Data.Set(mem[sp-2].Data)
mem = mem[:sp-2]
}
ip++
@@ -263,18 +263,18 @@ func (m *Machine) PushCode(code ...[]int64) (p int) {
}
func (m *Machine) SetIP(ip int) { m.ip = ip }
-func (m *Machine) Push(v ...reflect.Value) (l int) {
+func (m *Machine) Push(v ...Value) (l int) {
l = len(m.mem)
m.mem = append(m.mem, v...)
return l
}
-func (m *Machine) Pop() (v reflect.Value) {
+func (m *Machine) Pop() (v Value) {
l := len(m.mem) - 1
v = m.mem[l]
m.mem = m.mem[:l]
return v
}
-func (m *Machine) Top() (v reflect.Value) {
+func (m *Machine) Top() (v Value) {
if l := len(m.mem); l > 0 {
v = m.mem[l-1]
}
@@ -315,13 +315,13 @@ func slint(a []int64) []int {
return r
}
-func Vstring(lv []reflect.Value) string {
+func Vstring(lv []Value) string {
s := "["
for _, v := range lv {
if s != "[" {
s += " "
}
- s += fmt.Sprintf("%v", v)
+ s += fmt.Sprintf("%v", v.Data)
}
return s + "]"
}
diff --git a/vm/vm_test.go b/vm/vm_test.go
index 07c063e..fb71176 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -3,7 +3,6 @@ package vm
import (
"fmt"
"log"
- "reflect"
"testing"
)
@@ -50,11 +49,10 @@ func BenchmarkVM(b *testing.B) {
}
var tests = []struct {
- //sym []any // initial memory values
- sym []reflect.Value // initial memory values
- code [][]int64 // bytecode to execute
- start, end int //
- mem string // expected memory content
+ sym []Value // initial memory values
+ code [][]int64 // bytecode to execute
+ start, end int //
+ mem string // expected memory content
}{{ // #00 -- A simple addition.
code: [][]int64{
{0, Push, 1},
@@ -64,7 +62,7 @@ var tests = []struct {
},
start: 0, end: 1, mem: "[3]",
}, { // #01 -- Calling a function defined outside the VM.
- sym: []reflect.Value{reflect.ValueOf(fmt.Println), reflect.ValueOf("Hello")},
+ sym: []Value{ValueOf(fmt.Println), ValueOf("Hello")},
code: [][]int64{
{0, Dup, 0},
{0, CallX, 1},