parser: struct updating syntax with `Abc{...oldabc newfield: val}` (#7865)
parent
4f5db46bc2
commit
164d7bf5fb
|
@ -252,6 +252,9 @@ pub:
|
|||
pub mut:
|
||||
pre_comments []Comment
|
||||
typ table.Type
|
||||
update_expr Expr
|
||||
update_expr_type table.Type
|
||||
has_update_expr bool
|
||||
fields []StructInitField
|
||||
embeds []StructInitEmbed
|
||||
}
|
||||
|
|
|
@ -641,6 +641,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
|||
}
|
||||
else {}
|
||||
}
|
||||
if struct_init.has_update_expr {
|
||||
update_type := c.expr(struct_init.update_expr)
|
||||
struct_init.update_expr_type = update_type
|
||||
update_sym := c.table.get_type_symbol(update_type)
|
||||
sym := c.table.get_type_symbol(struct_init.typ)
|
||||
if update_sym.kind != .struct_ {
|
||||
c.error('expected struct `$sym.name`, found `$update_sym.name`', struct_init.update_expr.position())
|
||||
} else if update_type != struct_init.typ {
|
||||
c.error('expected struct `$sym.name`, found struct `$update_sym.name`', struct_init.update_expr.position())
|
||||
}
|
||||
}
|
||||
return struct_init.typ
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/struct_init_update_struct_err.vv:11:6: error: expected struct `Foo`, found struct `Foo2`
|
||||
9 | f3 := Foo2{}
|
||||
10 | _ := Foo{
|
||||
11 | ...f3
|
||||
| ~~
|
||||
12 | name: 'f2'
|
||||
13 | }
|
|
@ -0,0 +1,15 @@
|
|||
struct Foo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
struct Foo2 {}
|
||||
|
||||
fn main() {
|
||||
f3 := Foo2{}
|
||||
_ := Foo{
|
||||
...f3
|
||||
name: 'f2'
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/struct_init_update_type_err.vv:11:6: error: expected struct `Foo`, found `int`
|
||||
9 | f3 := 2
|
||||
10 | _ := Foo{
|
||||
11 | ...f3
|
||||
| ~~
|
||||
12 | name: 'f2'
|
||||
13 | }
|
|
@ -0,0 +1,15 @@
|
|||
struct Foo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
struct Foo2 {}
|
||||
|
||||
fn main() {
|
||||
f3 := 2
|
||||
_ := Foo{
|
||||
...f3
|
||||
name: 'f2'
|
||||
}
|
||||
}
|
||||
|
|
@ -2015,7 +2015,7 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
|
|||
if name == 'void' {
|
||||
name = ''
|
||||
}
|
||||
if it.fields.len == 0 {
|
||||
if it.fields.len == 0 && !it.has_update_expr {
|
||||
// `Foo{}` on one line if there are no fields or comments
|
||||
if it.pre_comments.len == 0 {
|
||||
f.write('$name{}')
|
||||
|
@ -2029,6 +2029,11 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
|
|||
// if name != '' {
|
||||
f.write('$name{')
|
||||
// }
|
||||
if it.has_update_expr {
|
||||
f.write('...')
|
||||
f.expr(it.update_expr)
|
||||
f.write(', ')
|
||||
}
|
||||
for i, field in it.fields {
|
||||
f.prefix_expr_cast_expr(field.expr)
|
||||
if i < it.fields.len - 1 {
|
||||
|
@ -2049,6 +2054,11 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
|
|||
}
|
||||
init_start := f.out.len
|
||||
f.indent++
|
||||
if it.has_update_expr {
|
||||
f.write('...')
|
||||
f.expr(it.update_expr)
|
||||
f.writeln('')
|
||||
}
|
||||
short_args_loop: for {
|
||||
f.comments(it.pre_comments, inline: true, has_nl: true, level: .keep)
|
||||
for i, field in it.fields {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
struct Foo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
struct Foo2 {}
|
||||
|
||||
fn main() {
|
||||
f := Foo{
|
||||
name: 'test'
|
||||
age: 18
|
||||
}
|
||||
f2 := Foo{
|
||||
...f
|
||||
name: 'f2'
|
||||
}
|
||||
}
|
|
@ -4529,7 +4529,17 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) {
|
|||
if field.typ in info.embeds {
|
||||
continue
|
||||
}
|
||||
if struct_init.has_update_expr {
|
||||
g.expr(struct_init.update_expr)
|
||||
if struct_init.update_expr_type.is_ptr() {
|
||||
g.write('->')
|
||||
} else {
|
||||
g.write('.')
|
||||
}
|
||||
g.write(field.name)
|
||||
} else {
|
||||
g.zero_struct_field(field)
|
||||
}
|
||||
if is_multiline {
|
||||
g.writeln(',')
|
||||
} else {
|
||||
|
|
|
@ -331,21 +331,29 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
pre_comments := p.eat_comments()
|
||||
mut fields := []ast.StructInitField{}
|
||||
mut i := 0
|
||||
no_keys := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c}
|
||||
no_keys := p.peek_tok.kind != .colon && p.tok.kind != .rcbr && p.tok.kind != .ellipsis // `Vec{a,b,c}
|
||||
// p.warn(is_short_syntax.str())
|
||||
saved_is_amp := p.is_amp
|
||||
p.is_amp = false
|
||||
mut update_expr := ast.Expr{}
|
||||
mut has_update_expr := false
|
||||
for p.tok.kind !in [.rcbr, .rpar, .eof] {
|
||||
mut field_name := ''
|
||||
mut expr := ast.Expr{}
|
||||
mut field_pos := token.Position{}
|
||||
mut comments := []ast.Comment{}
|
||||
mut nline_comments := []ast.Comment{}
|
||||
is_update_expr := fields.len == 0 && p.tok.kind == .ellipsis
|
||||
if no_keys {
|
||||
// name will be set later in checker
|
||||
expr = p.expr(0)
|
||||
field_pos = expr.position()
|
||||
comments = p.eat_line_end_comments()
|
||||
} else if is_update_expr {
|
||||
// struct updating syntax; f2 := Foo{ ...f, name: 'f2' }
|
||||
p.check(.ellipsis)
|
||||
update_expr = p.expr(0)
|
||||
has_update_expr = true
|
||||
} else {
|
||||
first_field_pos := p.tok.position()
|
||||
field_name = p.check_name()
|
||||
|
@ -367,6 +375,7 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
}
|
||||
comments << p.eat_line_end_comments()
|
||||
nline_comments << p.eat_comments()
|
||||
if !is_update_expr {
|
||||
fields << ast.StructInitField{
|
||||
name: field_name
|
||||
expr: expr
|
||||
|
@ -375,6 +384,7 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
next_comments: nline_comments
|
||||
}
|
||||
}
|
||||
}
|
||||
last_pos := p.tok.position()
|
||||
if !short_syntax {
|
||||
p.check(.rcbr)
|
||||
|
@ -383,6 +393,8 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
|||
node := ast.StructInit{
|
||||
typ: typ
|
||||
fields: fields
|
||||
update_expr: update_expr
|
||||
has_update_expr: has_update_expr
|
||||
pos: token.Position{
|
||||
line_nr: first_pos.line_nr
|
||||
pos: first_pos.pos
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/parser/tests/struct_update_err.vv:15:3: error: unexpected `...`, expecting `name`
|
||||
13 | f2 := Foo{
|
||||
14 | name: 'f2'
|
||||
15 | ...f
|
||||
| ~~~
|
||||
16 | }
|
||||
17 | }
|
|
@ -0,0 +1,17 @@
|
|||
struct Foo {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
struct Foo2 {}
|
||||
|
||||
fn main() {
|
||||
f := Foo{
|
||||
name: 'test'
|
||||
age: 18
|
||||
}
|
||||
f2 := Foo{
|
||||
name: 'f2'
|
||||
...f
|
||||
}
|
||||
}
|
|
@ -366,3 +366,15 @@ fn test_fields_array_of_fn() {
|
|||
println(commands.show)
|
||||
assert '$commands.show' == '[fn () string, fn () string]'
|
||||
}
|
||||
|
||||
fn test_struct_update() {
|
||||
c := Country{name: 'test'}
|
||||
c2 := Country{
|
||||
...c
|
||||
capital: City{
|
||||
name: 'city'
|
||||
}
|
||||
}
|
||||
assert c2.capital.name == 'city'
|
||||
assert c2.name == 'test'
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue