diff options
| author | Marc Vertes <mvertes@free.fr> | 2026-01-13 11:38:22 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2026-01-13 11:38:22 +0100 |
| commit | de3baf0e06862f0420950f025b3328068f3b6df2 (patch) | |
| tree | 10dbb5cce38f48b902329af2b721617c8d5966a3 | |
| parent | 5a04b5512a128bd1805792cca4eabacf5fd49b27 (diff) | |
fix: improve literal composite on nested types
| -rw-r--r-- | interp/interpreter_test.go | 25 | ||||
| -rw-r--r-- | parser/decl.go | 2 | ||||
| -rw-r--r-- | parser/expr.go | 47 | ||||
| -rw-r--r-- | parser/parse.go | 2 | ||||
| -rw-r--r-- | parser/type.go | 66 |
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 } |
