Skip to content

Commit

Permalink
feat: initial and partial support of composite expressions (#9)
Browse files Browse the repository at this point in the history
A new `Composite` token is created. Literal composite expressions
are recognized and partially handled by the parser but not yet
by the code generator.

Other cosmetic changes are present.
  • Loading branch information
mvertes authored Apr 23, 2024
1 parent 1bff92c commit 0063922
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 24 deletions.
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

0 comments on commit 0063922

Please sign in to comment.