diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 103b5daa9e..da7760b5e3 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -35,7 +35,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr mut concrete_types := []ast.Type{} mut concrete_list_pos := p.tok.pos() - if p.tok.kind == .lt { + if p.tok.kind in [.lt, .lsbr] { // `foo(10)` p.expr_mod = '' concrete_types = p.parse_concrete_types() diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 40ff213e4a..ffd1b3e649 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -629,7 +629,8 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d if name.len == 1 && name[0].is_capital() { return p.parse_generic_type(name) } - if p.tok.kind == .lt { + if p.tok.kind in [.lt, .lsbr] + && p.tok.pos - p.prev_tok.pos == p.prev_tok.len { return p.parse_generic_inst_type(name) } return p.find_type_or_add_placeholder(name, language) @@ -707,7 +708,7 @@ pub fn (mut p Parser) parse_generic_inst_type(name string) ast.Type { p.struct_init_generic_types = concrete_types } concrete_types_pos := start_pos.extend(p.tok.pos()) - p.check(.gt) + p.next() p.inside_generic_params = false bs_name += '>' // fmt operates on a per-file basis, so is_instance might be not set correctly. Thus it's ignored. diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 27a2b72586..bff82e7754 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2159,6 +2159,32 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { } } +fn (p &Parser) is_generic_struct_init() bool { + lit0_is_capital := p.tok.kind != .eof && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() + if !lit0_is_capital || p.peek_tok.kind !in [.lt, .lsbr] { + return false + } + if p.peek_tok.kind == .lt { + return true + } else { + mut i := 2 + for { + cur_tok := p.peek_token(i) + if cur_tok.kind == .eof || cur_tok.kind !in [.amp, .dot, .comma, .name, .lsbr, .rsbr] { + break + } + if cur_tok.kind == .rsbr { + if p.peek_token(i + 1).kind == .lcbr { + return true + } + break + } + i++ + } + } + return false +} + fn (p &Parser) is_typename(t token.Token) bool { return t.kind == .name && (t.lit[0].is_capital() || p.table.known_type(t.lit)) } @@ -2177,7 +2203,7 @@ fn (p &Parser) is_typename(t token.Token) bool { // see also test_generic_detection in vlib/v/tests/generics_test.v fn (p &Parser) is_generic_call() bool { lit0_is_capital := p.tok.kind != .eof && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() - if lit0_is_capital || p.peek_tok.kind != .lt { + if lit0_is_capital || p.peek_tok.kind !in [.lt, .lsbr] { return false } mut tok2 := p.peek_token(2) @@ -2206,13 +2232,31 @@ fn (p &Parser) is_generic_call() bool { // case 2 return true } - return match kind3 { - .gt { true } // case 3 - .lt { !(tok4.lit.len == 1 && tok4.lit[0].is_capital()) } // case 4 - .comma { p.is_typename(tok2) } // case 5 - // case 6 and 7 - .dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) } - else { false } + if p.peek_tok.kind == .lt { + return match kind3 { + .gt { true } // case 3 + .lt { !(tok4.lit.len == 1 && tok4.lit[0].is_capital()) } // case 4 + .comma { p.is_typename(tok2) } // case 5 + // case 6 and 7 + .dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) } + else { false } + } + } else if p.peek_tok.kind == .lsbr { + mut i := 3 + for { + cur_tok := p.peek_token(i) + if cur_tok.kind == .eof + || cur_tok.kind !in [.amp, .dot, .comma, .name, .lsbr, .rsbr] { + break + } + if cur_tok.kind == .rsbr { + if p.peek_token(i + 1).kind == .lpar { + return true + } + break + } + i++ + } } } return false @@ -2231,10 +2275,10 @@ fn (mut p Parser) is_generic_cast() bool { i++ tok := p.peek_token(i) - if tok.kind == .lt { + if tok.kind in [.lt, .lsbr] { lt_count++ level++ - } else if tok.kind == .gt { + } else if tok.kind in [.gt, .rsbr] { level-- } if lt_count > 0 && level == 0 { @@ -2414,6 +2458,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { is_optional := p.tok.kind == .question is_generic_call := p.is_generic_call() is_generic_cast := p.is_generic_cast() + is_generic_struct_init := p.is_generic_struct_init() // p.warn('name expr $p.tok.lit $p.peek_tok.str()') same_line := p.tok.line_nr == p.peek_tok.line_nr // `(` must be on same line as name token otherwise it's a ParExpr @@ -2494,7 +2539,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { } } } - } else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital)) + } else if (p.peek_tok.kind == .lcbr || is_generic_struct_init) && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && !p.inside_match_case && (!p.inside_if || p.inside_select) && (!p.inside_for || p.inside_select) && !known_var { @@ -2975,13 +3020,14 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { fn (mut p Parser) parse_generic_types() ([]ast.Type, []string) { mut types := []ast.Type{} mut param_names := []string{} - if p.tok.kind != .lt { + if p.tok.kind !in [.lt, .lsbr] { return types, param_names } - p.check(.lt) + end_kind := if p.tok.kind == .lt { token.Kind.gt } else { token.Kind.rsbr } + p.next() mut first_done := false mut count := 0 - for p.tok.kind !in [.gt, .eof] { + for p.tok.kind !in [end_kind, .eof] { if first_done { p.check(.comma) } @@ -3018,25 +3064,26 @@ fn (mut p Parser) parse_generic_types() ([]ast.Type, []string) { first_done = true count++ } - p.check(.gt) + p.check(end_kind) return types, param_names } fn (mut p Parser) parse_concrete_types() []ast.Type { mut types := []ast.Type{} - if p.tok.kind != .lt { + if p.tok.kind !in [.lt, .lsbr] { return types } + end_kind := if p.tok.kind == .lt { token.Kind.gt } else { token.Kind.rsbr } p.next() // `<` mut first_done := false - for p.tok.kind !in [.eof, .gt] { + for p.tok.kind !in [.eof, end_kind] { if first_done { p.check(.comma) } types << p.parse_type() first_done = true } - p.check(.gt) // `>` + p.check(end_kind) // `>` return types }