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:
pos token.Position
is_short bool
pos token.Position
is_short bool
pub mut:
pre_comments []Comment
typ table.Type
fields []StructInitField
embeds []StructInitEmbed
pre_comments []Comment
typ table.Type
update_expr Expr
update_expr_type table.Type
has_update_expr bool
fields []StructInitField
embeds []StructInitEmbed
}
// import statement

View File

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

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

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 {
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 {
g.writeln(',')
} else {

View File

@ -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,12 +375,14 @@ fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit {
}
comments << p.eat_line_end_comments()
nline_comments << p.eat_comments()
fields << ast.StructInitField{
name: field_name
expr: expr
pos: field_pos
comments: comments
next_comments: nline_comments
if !is_update_expr {
fields << ast.StructInitField{
name: field_name
expr: expr
pos: field_pos
comments: comments
next_comments: nline_comments
}
}
}
last_pos := p.tok.position()
@ -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

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