summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interp/interpreter_test.go25
-rw-r--r--parser/decl.go2
-rw-r--r--parser/expr.go47
-rw-r--r--parser/parse.go2
-rw-r--r--parser/type.go66
5 files changed, 78 insertions, 64 deletions
diff --git a/interp/interpreter_test.go b/interp/interpreter_test.go
index 0be2409..0cb4160 100644
--- a/interp/interpreter_test.go
+++ b/interp/interpreter_test.go
@@ -274,15 +274,20 @@ func TestImport(t *testing.T) {
func TestComposite(t *testing.T) {
run(t, []etest{
- {src: "type T struct{}; t := T{}; t", res: "{}"}, // #00
- {src: "t := struct{}{}; t", res: "{}"}, // #01
- {src: `type T struct {}; var t T; t = T{}; t`, res: "{}"}, // #02
- {src: `type T struct{N int; S string}; var t T; t = T{2, "foo"}; t`, res: `{2 foo}`}, // #03
- {src: `type T struct{N int; S string}; t := T{2, "foo"}; t`, res: `{2 foo}`}, // #04
- {src: `type T struct{N int; S string}; t := T{S: "foo"}; t`, res: `{0 foo}`}, // #05
- {src: `a := []int{}`, res: `[]`}, // #06
- {src: `a := []int{1, 2, 3}; a`, res: `[1 2 3]`}, // #07
- {src: `m := map[string]bool{}`, res: `map[]`}, // #08
- {src: `m := map[string]bool{"hello": true}; m`, res: `map[hello:true]`}, // #09
+ {src: "type T struct{}; t := T{}; t", res: "{}"}, // #00
+ {src: "t := struct{}{}; t", res: "{}"}, // #01
+ {src: `type T struct {}; var t T; t = T{}; t`, res: "{}"}, // #02
+ {src: `type T struct{N int; S string}; var t T; t = T{2, "foo"}; t`, res: `{2 foo}`}, // #03
+ {src: `type T struct{N int; S string}; t := T{2, "foo"}; t`, res: `{2 foo}`}, // #04
+ {src: `type T struct{N int; S string}; t := T{S: "foo"}; t`, res: `{0 foo}`}, // #05
+ {src: `a := []int{}`, res: `[]`}, // #06
+ {src: `a := []int{1, 2, 3}; a`, res: `[1 2 3]`}, // #07
+ {src: `m := map[string]bool{}`, res: `map[]`}, // #08
+ {src: `m := map[string]bool{"hello": true}; m`, res: `map[hello:true]`}, // #09
+ {src: `m := map[int]struct{b bool}{1:struct {b bool}{true}}; m`, res: `map[1:{true}]`}, // #10
+ {src: `type T struct {b bool}; m := []T{T{true}}; m`, res: `[{true}]`}, // #11
+ {src: `type T struct {b bool}; m := []T{{true}}; m`, res: `[{true}]`}, // #12
+ {src: `m := []struct{b bool}{{true}}; m`, res: `[{true}]`}, // #13
+ {src: `m := map[int]struct{b bool}{1:{true}}; m`, res: `map[1:{true}]`}, // #14
})
}
diff --git a/parser/decl.go b/parser/decl.go
index e418b5e..8833580 100644
--- a/parser/decl.go
+++ b/parser/decl.go
@@ -297,7 +297,7 @@ 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
}
diff --git a/parser/expr.go b/parser/expr.go
index 35e54ff..5a1c670 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -14,6 +14,7 @@ import (
func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
log.Println("parseExpr in:", in)
var ops Tokens
+ var ctype string
popop := func() (t scanner.Token) {
l := len(ops) - 1
@@ -62,6 +63,7 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
if i == 0 || in[i-1].Tok.IsOperator() {
// An operator preceded by an operator or no token is unary.
t.Tok = lang.UnaryOp[t.Tok]
+ // FIXME: parsetype for composite if & or *
}
for len(ops) > 0 && p.precedence(t) < p.precedence(ops[len(ops)-1]) {
out = append(out, popop())
@@ -94,7 +96,7 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
t.Str = sc + "/" + t.Str
}
if s != nil && s.Kind == symbol.Type {
- typeStr = s.Type.String()
+ ctype = s.Type.String()
}
out = append(out, t)
if i+1 < len(in) && in[i+1].Tok == lang.Define {
@@ -120,24 +122,31 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
}
case lang.BraceBlock:
- toks, err := p.parseComposite(t.Block(), typeStr)
+ if ctype == "" {
+ // Infer composite inner type from passed typeStr
+ typ := p.Symbols[typeStr].Type.Elem()
+ ctype = typ.String()
+ p.Symbols.Add(symbol.UnsetAddr, ctype, vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
+ out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: ctype})
+ }
+ toks, err := p.parseComposite(t.Block(), ctype)
out = append(out, toks...)
if err != nil {
return out, err
}
- ops = append(ops, scanner.Token{Tok: lang.Composite, Pos: t.Pos, Str: typeStr})
+ ops = append(ops, scanner.Token{Tok: lang.Composite, Pos: t.Pos, Str: ctype})
case lang.BracketBlock:
if i == 0 || in[i-1].Tok.IsOperator() {
// array or slice type expression
- typ, err := p.parseTypeExpr(in[i:])
+ typ, n, err := p.parseTypeExpr(in[i:])
if err != nil {
return out, err
}
- typeStr = typ.String()
- p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
- out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typeStr})
- i++
+ ctype = typ.String()
+ p.Symbols.Add(symbol.UnsetAddr, ctype, vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
+ out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: ctype})
+ i += n - 1
break
}
toks, err := p.parseExprStr(t.Block(), typeStr)
@@ -146,7 +155,7 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
}
out = append(out, toks...)
if i < len(in)-2 && in[i+1].Tok == lang.Assign {
- // A brace block followed by assign implies a MapAssing token,
+ // A bracket block followed by assign implies a MapAssing token,
// as assignement to a map element cannot be implemented through a normal Assign.
ops = append(ops, scanner.Token{Tok: lang.MapAssign, Pos: t.Pos})
i++
@@ -155,24 +164,24 @@ func (p *Parser) parseExpr(in Tokens, typeStr string) (out Tokens, err error) {
}
case lang.Struct:
- typ, err := p.parseTypeExpr(in[i : i+2])
+ typ, _, err := p.parseTypeExpr(in[i : i+2])
if err != nil {
return out, err
}
- typeStr = typ.String()
- p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
- out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typeStr})
- i++ // FIXME: number of tokens to skip should be computed from type parsing.
+ ctype = typ.String()
+ p.Symbols.Add(symbol.UnsetAddr, ctype, vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
+ out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: ctype})
+ i++
case lang.Map:
- typ, err := p.parseTypeExpr(in[i:])
+ typ, n, err := p.parseTypeExpr(in[i:])
if err != nil {
return out, err
}
- typeStr = typ.String()
- p.Symbols.Add(symbol.UnsetAddr, typ.String(), vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
- out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: typeStr})
- i += 2 // FIXME: number of tokens to skip should be computed from type parsing.
+ ctype = typ.String()
+ p.Symbols.Add(symbol.UnsetAddr, ctype, vm.NewValue(typ), symbol.Type, typ, p.funcScope != "")
+ out = append(out, scanner.Token{Tok: lang.Ident, Pos: t.Pos, Str: ctype})
+ i += n - 1
case lang.Comment:
diff --git a/parser/parse.go b/parser/parse.go
index 32ee0e3..46b1724 100644
--- a/parser/parse.go
+++ b/parser/parse.go
@@ -281,7 +281,7 @@ func (p *Parser) parseFunc(in Tokens) (out Tokens, err error) {
if bi < 0 {
return out, ErrBody
}
- typ, err := p.parseTypeExpr(in[:bi])
+ typ, _, err := p.parseTypeExpr(in[:bi])
if err != nil {
return out, err
}
diff --git a/parser/type.go b/parser/type.go
index 5e8df02..b56a332 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -29,36 +29,36 @@ var (
ErrNotImplemented = errors.New("not implemented")
)
-func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
+func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, n int, err error) {
switch in[0].Tok {
case lang.BracketBlock:
- typ, err := p.parseTypeExpr(in[1:])
+ typ, i, err := p.parseTypeExpr(in[1:])
if err != nil {
- return nil, err
+ return nil, 0, err
}
if b := in[0].Block(); len(b) > 0 {
x, err := p.Scan(b, false)
if err != nil {
- return nil, err
+ return nil, 0, err
}
cval, _, err := p.evalConstExpr(x)
if err != nil {
- return nil, err
+ return nil, 0, err
}
size, ok := constValue(cval).(int)
if !ok {
- return nil, ErrSize
+ return nil, 0, ErrSize
}
- return vm.ArrayOf(size, typ), nil
+ return vm.ArrayOf(size, typ), 1 + i, nil
}
- return vm.SliceOf(typ), nil
+ return vm.SliceOf(typ), 1 + i, nil
case lang.Mul:
- typ, err := p.parseTypeExpr(in[1:])
+ typ, i, err := p.parseTypeExpr(in[1:])
if err != nil {
- return nil, err
+ return nil, 0, err
}
- return vm.PointerTo(typ), nil
+ return vm.PointerTo(typ), 1 + i, nil
case lang.Func:
// Get argument and return token positions depending on function pattern:
@@ -74,79 +74,79 @@ func (p *Parser) parseTypeExpr(in Tokens) (typ *vm.Type, err error) {
case l >= 2 && in1.Tok == lang.ParenBlock:
indexArgs, out = 1, in[2:]
default:
- return nil, ErrFuncType
+ return nil, 0, ErrFuncType
}
// We can now parse function input and output parameter types.
// Input parameters are always enclosed by parenthesis.
iargs, err := p.Scan(in[indexArgs].Block(), false)
if err != nil {
- return nil, err
+ return nil, 0, err
}
arg, _, err := p.parseParamTypes(iargs, parseTypeIn)
if err != nil {
- return nil, err
+ return nil, 0, err
}
// Output parameters may be empty, or enclosed or not by parenthesis.
if len(out) == 1 && out[0].Tok == lang.ParenBlock {
if out, err = p.Scan(out[0].Block(), false); err != nil {
- return nil, err
+ return nil, 0, err
}
}
ret, _, err := p.parseParamTypes(out, parseTypeOut)
if err != nil {
- return nil, err
+ return nil, 0, err
}
- return vm.FuncOf(arg, ret, false), nil
+ return vm.FuncOf(arg, ret, false), 1 + indexArgs, nil
case lang.Ident:
// TODO: selector expression (pkg.type)
s, _, ok := p.Symbols.Get(in[0].Str, p.scope)
if !ok || s.Kind != symbol.Type {
- return nil, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str)
+ return nil, 0, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str)
}
- return s.Type, nil
+ return s.Type, 1, nil
case lang.Struct:
- if len(in) != 2 || in[1].Tok != lang.BraceBlock {
- return nil, fmt.Errorf("%w: %v", ErrSyntax, in)
+ if len(in) < 2 || in[1].Tok != lang.BraceBlock {
+ return nil, 0, fmt.Errorf("%w: %v", ErrSyntax, in)
}
if in, err = p.Scan(in[1].Block(), false); err != nil {
- return nil, err
+ return nil, 0, err
}
var fields []*vm.Type
for _, lt := range in.Split(lang.Semicolon) {
types, names, err := p.parseParamTypes(lt, parseTypeType)
if err != nil {
- return nil, err
+ return nil, 0, err
}
for i, name := range names {
fields = append(fields, &vm.Type{Name: name, PkgPath: p.pkgName, Rtype: types[i].Rtype})
// TODO: handle embedded fields
}
}
- return vm.StructOf(fields), nil
+ return vm.StructOf(fields), 2, nil
case lang.Map:
if len(in) < 3 || in[1].Tok != lang.BracketBlock {
- return nil, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str)
+ return nil, 0, fmt.Errorf("%w: %s", ErrInvalidType, in[0].Str)
}
kin, err := p.Scan(in[1].Block(), false)
if err != nil {
- return nil, err
+ return nil, 0, err
}
- ktyp, err := p.parseTypeExpr(kin) // Key type
+ ktyp, _, err := p.parseTypeExpr(kin) // Key type
if err != nil {
- return nil, err
+ return nil, 0, err
}
- etyp, err := p.parseTypeExpr(in[2:]) // Element type
+ etyp, i, err := p.parseTypeExpr(in[2:]) // Element type
if err != nil {
- return nil, err
+ return nil, 0, err
}
- return vm.MapOf(ktyp, etyp), nil
+ return vm.MapOf(ktyp, etyp), 2 + i, nil
default:
- return nil, fmt.Errorf("%w: %v", ErrNotImplemented, in[0].Name())
+ return nil, 0, fmt.Errorf("%w: %v", ErrNotImplemented, in[0].Name())
}
}
@@ -176,7 +176,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
}