parser: struct updating syntax with `Abc{...oldabc newfield: val}` (#7865)
parent
4f5db46bc2
commit
164d7bf5fb
|
@ -247,13 +247,16 @@ pub mut:
|
||||||
|
|
||||||
pub struct StructInit {
|
pub struct StructInit {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
is_short bool
|
is_short bool
|
||||||
pub mut:
|
pub mut:
|
||||||
pre_comments []Comment
|
pre_comments []Comment
|
||||||
typ table.Type
|
typ table.Type
|
||||||
fields []StructInitField
|
update_expr Expr
|
||||||
embeds []StructInitEmbed
|
update_expr_type table.Type
|
||||||
|
has_update_expr bool
|
||||||
|
fields []StructInitField
|
||||||
|
embeds []StructInitEmbed
|
||||||
}
|
}
|
||||||
|
|
||||||
// import statement
|
// import statement
|
||||||
|
|
|
@ -641,6 +641,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
}
|
}
|
||||||
else {}
|
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
|
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' {
|
if name == 'void' {
|
||||||
name = ''
|
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
|
// `Foo{}` on one line if there are no fields or comments
|
||||||
if it.pre_comments.len == 0 {
|
if it.pre_comments.len == 0 {
|
||||||
f.write('$name{}')
|
f.write('$name{}')
|
||||||
|
@ -2029,6 +2029,11 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
|
||||||
// if name != '' {
|
// if name != '' {
|
||||||
f.write('$name{')
|
f.write('$name{')
|
||||||
// }
|
// }
|
||||||
|
if it.has_update_expr {
|
||||||
|
f.write('...')
|
||||||
|
f.expr(it.update_expr)
|
||||||
|
f.write(', ')
|
||||||
|
}
|
||||||
for i, field in it.fields {
|
for i, field in it.fields {
|
||||||
f.prefix_expr_cast_expr(field.expr)
|
f.prefix_expr_cast_expr(field.expr)
|
||||||
if i < it.fields.len - 1 {
|
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
|
init_start := f.out.len
|
||||||
f.indent++
|
f.indent++
|
||||||
|
if it.has_update_expr {
|
||||||
|
f.write('...')
|
||||||
|
f.expr(it.update_expr)
|
||||||
|
f.writeln('')
|
||||||
|
}
|
||||||
short_args_loop: for {
|
short_args_loop: for {
|
||||||
f.comments(it.pre_comments, inline: true, has_nl: true, level: .keep)
|
f.comments(it.pre_comments, inline: true, has_nl: true, level: .keep)
|
||||||
for i, field in it.fields {
|
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 {
|
if field.typ in info.embeds {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
g.zero_struct_field(field)
|
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 {
|
if is_multiline {
|
||||||
g.writeln(',')
|
g.writeln(',')
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -331,21 +331,29 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
||||||
pre_comments := p.eat_comments()
|
pre_comments := p.eat_comments()
|
||||||
mut fields := []ast.StructInitField{}
|
mut fields := []ast.StructInitField{}
|
||||||
mut i := 0
|
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())
|
// p.warn(is_short_syntax.str())
|
||||||
saved_is_amp := p.is_amp
|
saved_is_amp := p.is_amp
|
||||||
p.is_amp = false
|
p.is_amp = false
|
||||||
|
mut update_expr := ast.Expr{}
|
||||||
|
mut has_update_expr := false
|
||||||
for p.tok.kind !in [.rcbr, .rpar, .eof] {
|
for p.tok.kind !in [.rcbr, .rpar, .eof] {
|
||||||
mut field_name := ''
|
mut field_name := ''
|
||||||
mut expr := ast.Expr{}
|
mut expr := ast.Expr{}
|
||||||
mut field_pos := token.Position{}
|
mut field_pos := token.Position{}
|
||||||
mut comments := []ast.Comment{}
|
mut comments := []ast.Comment{}
|
||||||
mut nline_comments := []ast.Comment{}
|
mut nline_comments := []ast.Comment{}
|
||||||
|
is_update_expr := fields.len == 0 && p.tok.kind == .ellipsis
|
||||||
if no_keys {
|
if no_keys {
|
||||||
// name will be set later in checker
|
// name will be set later in checker
|
||||||
expr = p.expr(0)
|
expr = p.expr(0)
|
||||||
field_pos = expr.position()
|
field_pos = expr.position()
|
||||||
comments = p.eat_line_end_comments()
|
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 {
|
} else {
|
||||||
first_field_pos := p.tok.position()
|
first_field_pos := p.tok.position()
|
||||||
field_name = p.check_name()
|
field_name = p.check_name()
|
||||||
|
@ -367,12 +375,14 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
||||||
}
|
}
|
||||||
comments << p.eat_line_end_comments()
|
comments << p.eat_line_end_comments()
|
||||||
nline_comments << p.eat_comments()
|
nline_comments << p.eat_comments()
|
||||||
fields << ast.StructInitField{
|
if !is_update_expr {
|
||||||
name: field_name
|
fields << ast.StructInitField{
|
||||||
expr: expr
|
name: field_name
|
||||||
pos: field_pos
|
expr: expr
|
||||||
comments: comments
|
pos: field_pos
|
||||||
next_comments: nline_comments
|
comments: comments
|
||||||
|
next_comments: nline_comments
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_pos := p.tok.position()
|
last_pos := p.tok.position()
|
||||||
|
@ -383,6 +393,8 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
|
||||||
node := ast.StructInit{
|
node := ast.StructInit{
|
||||||
typ: typ
|
typ: typ
|
||||||
fields: fields
|
fields: fields
|
||||||
|
update_expr: update_expr
|
||||||
|
has_update_expr: has_update_expr
|
||||||
pos: token.Position{
|
pos: token.Position{
|
||||||
line_nr: first_pos.line_nr
|
line_nr: first_pos.line_nr
|
||||||
pos: first_pos.pos
|
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)
|
println(commands.show)
|
||||||
assert '$commands.show' == '[fn () string, fn () string]'
|
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