diff options
| -rw-r--r-- | docs/proposals/PIP-000.md | 35 | ||||
| -rw-r--r-- | docs/proposals/README.md | 11 | ||||
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | lang/golang/go.go | 2 | ||||
| -rw-r--r-- | main.go | 6 | ||||
| -rw-r--r-- | parser/compiler.go | 75 | ||||
| -rw-r--r-- | parser/decl.go | 12 | ||||
| -rw-r--r-- | parser/expr.go | 4 | ||||
| -rw-r--r-- | parser/interpreter.go | 6 | ||||
| -rw-r--r-- | parser/interpreter_test.go | 7 | ||||
| -rw-r--r-- | parser/parse.go | 21 | ||||
| -rw-r--r-- | parser/symbol.go | 31 | ||||
| -rw-r--r-- | parser/tokens.go | 4 | ||||
| -rw-r--r-- | parser/type.go | 31 | ||||
| -rw-r--r-- | readme.md | 18 | ||||
| -rw-r--r-- | scanner/scan.go | 2 | ||||
| -rw-r--r-- | scanner/scan_test.go | 4 | ||||
| -rw-r--r-- | vm/type.go | 74 | ||||
| -rw-r--r-- | vm/vm.go | 88 | ||||
| -rw-r--r-- | vm/vm_test.go | 12 |
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) | @@ -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{ @@ -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) @@ -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)} +} @@ -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}, |
