summaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2024-03-08 19:29:34 +0100
committerMarc Vertes <mvertes@free.fr>2024-03-08 19:29:34 +0100
commit8c4fa9d85cd274439dbd7d0a5c699fe1cea557dc (patch)
treea8c910cf13aa06e61ee2db40b5dd2f1f63a25b9d /parser
parent828cfd1c8da5e243fd0f34530fb1a7faab49784e (diff)
feat: add type representation in vm package
Type and Value types in vm package are now used in place of reflect.Type and reflect.Value. It allows to remove the dependency on reflect for parser and compiler packages. The main purpose of Type is to provide a solution to implement recursive structs, named types, interfaces and methods, despite the limitations of Go reflect. The goal is to provide the thinnest layer around reflect.
Diffstat (limited to 'parser')
-rw-r--r--parser/compiler.go65
-rw-r--r--parser/decl.go8
-rw-r--r--parser/interpreter.go2
-rw-r--r--parser/parse.go4
-rw-r--r--parser/symbol.go31
-rw-r--r--parser/type.go29
6 files changed, 66 insertions, 73 deletions
diff --git a/parser/compiler.go b/parser/compiler.go
index c77538d..7a60565 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -3,7 +3,6 @@ package parser
import (
"fmt"
"log"
- "reflect"
"strconv"
"github.com/mvertes/parscan/lang"
@@ -13,9 +12,9 @@ import (
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,7 +109,7 @@ 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)
@@ -118,7 +117,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
case lang.CallX:
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.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])
+ fmt.Printf("%4d %T %v %v\n", i, d.Data.Interface(), d.Data, 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()
- }
- 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 2c8941d..d794131 100644
--- a/parser/decl.go
+++ b/parser/decl.go
@@ -4,14 +4,14 @@ import (
"errors"
"go/constant"
"go/token"
- "reflect"
"strings"
"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/interpreter.go b/parser/interpreter.go
index 037ae5f..c5e463a 100644
--- a/parser/interpreter.go
+++ b/parser/interpreter.go
@@ -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/parse.go b/parser/parse.go
index 59083b6..ffa8102 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -420,8 +420,8 @@ func (p *Parser) ParseReturn(in Tokens) (out Tokens, err error) {
// 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/type.go b/parser/type.go
index 012a574..cd63a46 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -3,10 +3,10 @@ package parser
import (
"errors"
"fmt"
- "reflect"
"strings"
"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)