Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial and partial support of composite expressions #9

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lang/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const (
// Internal virtual machine tokens (no corresponding keyword).
Call
CallX
Composite
EqualSet
Grow
Index
Expand Down
21 changes: 11 additions & 10 deletions lang/token_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions parser/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
}
emit(int64(t.Pos), vm.CallX, int64(t.Beg))

case lang.Composite:
log.Println("COMPOSITE")

case lang.Grow:
emit(int64(t.Pos), vm.Grow, int64(t.Beg))

Expand Down
3 changes: 2 additions & 1 deletion parser/decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,11 @@ func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) {
if isAlias {
toks = toks[1:]
}
typ, err := p.ParseTypeExpr(toks)
typ, err := p.parseTypeExpr(toks)
if err != nil {
return out, err
}
typ.Name = in[0].Str
p.addSym(unsetAddr, in[0].Str, vm.NewValue(typ), symType, typ, p.funcScope != "")
return out, err
}
Expand Down
25 changes: 22 additions & 3 deletions parser/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (

"github.com/mvertes/parscan/lang"
"github.com/mvertes/parscan/scanner"
"github.com/mvertes/parscan/vm"
)

func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) {
log.Println("ParseExpr in:", in)
log.Println("parseExpr in:", in)
var ops, selectors Tokens
var vl int
var selectorIndex string
Expand Down Expand Up @@ -84,6 +85,24 @@ func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) {
out = append(out, t)
vl++
ops = append(ops, scanner.Token{Tok: lang.Call, Pos: t.Pos, Beg: p.numItems(t.Block(), lang.Comma)})
case lang.BraceBlock:
// the block can be a func body or a composite type content.
// In both cases it is preceded by a type definition. We must determine the starting token of type def,
// parse the type def, and substitute the type def by a single ident.
// TODO: handle implicit type in composite expression.
ti := p.typeStartIndex(in[:len(in)-1])
if ti == -1 {
return out, ErrInvalidType
}
typ, err := p.parseTypeExpr(in[ti : len(in)-1])
if err != nil {
return out, ErrInvalidType
}
p.addSym(unsetAddr, typ.String(), vm.NewValue(typ), symType, typ, p.funcScope != "")
out = append(out, t, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typ.String()})
i = ti
vl += 2
ops = append(ops, scanner.Token{Tok: lang.Composite, Pos: t.Pos})
case lang.BracketBlock:
out = append(out, t)
vl++
Expand Down Expand Up @@ -113,7 +132,7 @@ func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) {
}
out = append(out, ops...)

log.Println("ParseExpr out:", out, "vl:", vl, "ops:", ops)
log.Println("parseExpr out:", out, "vl:", vl, "ops:", ops)
// A logical operator (&&, ||) involves additional control flow operations.
if out, err = p.parseLogical(out); err != nil {
return out, err
Expand All @@ -133,7 +152,7 @@ func (p *Parser) parseExpr(in Tokens) (out Tokens, err error) {
t := out[i]
var toks Tokens
switch t.Tok {
case lang.ParenBlock, lang.BracketBlock:
case lang.ParenBlock, lang.BracketBlock, lang.BraceBlock:
if toks, err = p.parseExprStr(t.Block()); err != nil {
return out, err
}
Expand Down
8 changes: 8 additions & 0 deletions parser/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,11 @@ func TestImport(t *testing.T) {
{src: `import . "fmt"; Println(4)`, res: "<nil>"},
})
}

func TestComposite(t *testing.T) {
run(t, []etest{
{src: "type T struct{}; t := T{}; t", res: "{}"},
{src: "t := struct{}{}; t", res: "{}"},
// {src: "type T struct{N int}; t := T{2}; t", res: "{2}"},
})
}
4 changes: 2 additions & 2 deletions parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (p *Parser) parseStmt(in Tokens) (out Tokens, err error) {
if len(in) == 0 {
return nil, nil
}
log.Println("ParseStmt in:", in)
log.Println("parseStmt in:", in)
switch t := in[0]; t.Tok {
case lang.Break:
return p.parseBreak(in)
Expand Down Expand Up @@ -249,7 +249,7 @@ func (p *Parser) parseFunc(in Tokens) (out Tokens, err error) {
if bi < 0 {
return out, fmt.Errorf("no function body")
}
typ, err := p.ParseTypeExpr(in[:bi])
typ, err := p.parseTypeExpr(in[:bi])
if err != nil {
return out, err
}
Expand Down
24 changes: 18 additions & 6 deletions parser/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ var (
ErrTypeNotImplemented = errors.New("not implemented")
)

// 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 *vm.Type, err error) {
func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
switch in[0].Tok {
case lang.BracketBlock:
typ, err := p.ParseTypeExpr(in[1:])
typ, err := p.parseTypeExpr(in[1:])
if err != nil {
return nil, err
}
Expand All @@ -53,7 +51,7 @@ func (p *Parser) ParseTypeExpr(in Tokens) (typ *vm.Type, err error) {
return vm.SliceOf(typ), nil

case lang.Mul:
typ, err := p.ParseTypeExpr(in[1:])
typ, err := p.parseTypeExpr(in[1:])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -157,7 +155,7 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []*vm.Type, va
continue
}
}
typ, err := p.ParseTypeExpr(t)
typ, err := p.parseTypeExpr(t)
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -196,3 +194,17 @@ func (p *Parser) hasFirstParam(in Tokens) bool {
s, _, ok := p.getSym(in[0].Str, p.scope)
return !ok || s.kind != symType
}

// typeStartIndex returns the index of the start of type expression in tokens, or -1.
func (p *Parser) typeStartIndex(in Tokens) int {
index := len(in) - 1
for i := index; i >= 0; i-- {
switch in[i].Tok {
case lang.Ident, lang.Struct, lang.Map, lang.Func, lang.Interface, lang.Mul, lang.BraceBlock, lang.BracketBlock, lang.ParenBlock:
index = i
default:
return index
}
}
return -1
}
18 changes: 16 additions & 2 deletions vm/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ type Type struct {
Rtype reflect.Type
}

func (t *Type) String() string {
if t.Name != "" {
return t.Name
}
return t.Rtype.String()
}

// Elem returns a type's element type.
func (t *Type) Elem() *Type {
return &Type{Rtype: t.Rtype.Elem()}
}

// Out returns the type's i'th output parameter.
func (t *Type) Out(i int) *Type {
return &Type{Rtype: t.Rtype.Out(i)}
}
Expand Down Expand Up @@ -43,18 +52,22 @@ func ValueOf(v any) Value {
return Value{Data: reflect.ValueOf(v)}
}

// PointerTo returns the pointer type with element t.
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)}
// ArrayOf returns the array type with the given length and element type.
func ArrayOf(length int, t *Type) *Type {
return &Type{Rtype: reflect.ArrayOf(length, t.Rtype)}
}

// SliceOf returns the slice type with the given element type.
func SliceOf(t *Type) *Type {
return &Type{Rtype: reflect.SliceOf(t.Rtype)}
}

// FuncOf returns the function type with the given argument and result types.
func FuncOf(arg, ret []*Type, variadic bool) *Type {
a := make([]reflect.Type, len(arg))
for i, e := range arg {
Expand All @@ -67,6 +80,7 @@ func FuncOf(arg, ret []*Type, variadic bool) *Type {
return &Type{Rtype: reflect.FuncOf(a, r, variadic)}
}

// StructOf returns the struct type with the given field types.
func StructOf(fields []*Type) *Type {
rf := make([]reflect.StructField, len(fields))
for i, f := range fields {
Expand Down
11 changes: 11 additions & 0 deletions vm/vm.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package vm implement a stack based virtual machine.
package vm

import (
Expand Down Expand Up @@ -85,6 +86,7 @@ var strop = [...]string{ // for VM tracing.
Vassign: "Vassign",
}

// Code represents the virtual machine byte code.
type Code [][]int64

// Machine represents a virtual machine.
Expand Down Expand Up @@ -256,39 +258,47 @@ func (m *Machine) Run() (err error) {
}
}

// PushCode adds instructions to the machine code.
func (m *Machine) PushCode(code ...[]int64) (p int) {
p = len(m.code)
m.code = append(m.code, code...)
return p
}

// SetIP sets the value of machine instruction pointer to given index.
func (m *Machine) SetIP(ip int) { m.ip = ip }

// Push pushes data values on top of machine memory stack.
func (m *Machine) Push(v ...Value) (l int) {
l = len(m.mem)
m.mem = append(m.mem, v...)
return l
}

// Pop removes and returns the value on the top of machine stack.
func (m *Machine) Pop() (v Value) {
l := len(m.mem) - 1
v = m.mem[l]
m.mem = m.mem[:l]
return v
}

// Top returns (but not remove) the value on the top of machine stack.
func (m *Machine) Top() (v Value) {
if l := len(m.mem); l > 0 {
v = m.mem[l-1]
}
return v
}

// PopExit removes the last machine code instruction if is Exit.
func (m *Machine) PopExit() {
if l := len(m.code); l > 0 && m.code[l-1][1] == Exit {
m.code = m.code[:l-1]
}
}

// CodeString returns the string representation of a machine code instruction.
func CodeString(op []int64) string {
switch len(op) {
case 2:
Expand Down Expand Up @@ -317,6 +327,7 @@ func slint(a []int64) []int {
return r
}

// Vstring returns the string repreentation of a list of values.
func Vstring(lv []Value) string {
s := "["
for _, v := range lv {
Expand Down