parser: struct updating syntax with `Abc{...oldabc newfield: val}` (#7865)

pull/7869/head
Daniel Däschle 2021-01-04 19:19:03 +01:00 committed by GitHub
parent 4f5db46bc2
commit 164d7bf5fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 158 additions and 15 deletions

View File

@ -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

View File

@ -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
} }

View File

@ -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 | }

View File

@ -0,0 +1,15 @@
struct Foo {
name string
age int
}
struct Foo2 {}
fn main() {
f3 := Foo2{}
_ := Foo{
...f3
name: 'f2'
}
}

View File

@ -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 | }

View File

@ -0,0 +1,15 @@
struct Foo {
name string
age int
}
struct Foo2 {}
fn main() {
f3 := 2
_ := Foo{
...f3
name: 'f2'
}
}

View File

@ -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 {

View File

@ -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'
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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 | }

View File

@ -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
}
}

View File

@ -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'
}