struct.v
parent
bac690bbc8
commit
695245d307
|
@ -668,227 +668,6 @@ fn key_to_type_cat(tok TokenKind) TypeCategory {
|
|||
return TypeCategory.builtin
|
||||
}
|
||||
|
||||
// also unions and interfaces
|
||||
fn (p mut Parser) struct_decl() {
|
||||
is_pub := p.tok == .key_pub
|
||||
if is_pub {
|
||||
p.next()
|
||||
}
|
||||
// V can generate Objective C for integration with Cocoa
|
||||
// `[objc_interface:ParentInterface]`
|
||||
is_objc := p.attr.starts_with('objc_interface')
|
||||
objc_parent := if is_objc { p.attr.right(15) } else { '' }
|
||||
// interface, union, struct
|
||||
is_interface := p.tok == .key_interface
|
||||
is_union := p.tok == .key_union
|
||||
is_struct := p.tok == .key_struct
|
||||
mut cat := key_to_type_cat(p.tok)
|
||||
if is_objc {
|
||||
cat = .objc_interface
|
||||
}
|
||||
p.fgen(p.tok.str() + ' ')
|
||||
// Get type name
|
||||
p.next()
|
||||
mut name := p.check_name()
|
||||
if name.contains('_') && !p.pref.translated {
|
||||
p.error('type names cannot contain `_`')
|
||||
}
|
||||
if !p.builtin_mod && !name[0].is_capital() {
|
||||
p.error('struct names must be capitalized: use `struct ${name.capitalize()}`')
|
||||
}
|
||||
if is_interface && !name.ends_with('er') {
|
||||
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
|
||||
}
|
||||
is_c := name == 'C' && p.tok == .dot
|
||||
if is_c {
|
||||
p.check(.dot)
|
||||
name = p.check_name()
|
||||
cat = .c_struct
|
||||
if p.attr == 'typedef' {
|
||||
cat = .c_typedef
|
||||
}
|
||||
}
|
||||
if name.len == 1 && !p.pref.building_v && !p.pref.is_repl {
|
||||
p.warn('struct names must have more than one character')
|
||||
}
|
||||
if !is_c && !good_type_name(name) {
|
||||
p.error('bad struct name, e.g. use `HttpRequest` instead of `HTTPRequest`')
|
||||
}
|
||||
// Specify full type name
|
||||
if !is_c && !p.builtin_mod && p.mod != 'main' {
|
||||
name = p.prepend_mod(name)
|
||||
}
|
||||
mut typ := p.table.find_type(name)
|
||||
if p.pass == .decl && p.table.known_type_fast(typ) {
|
||||
//if name in reserved_type_param_names {
|
||||
//p.error('name `$name` is reserved for type parameters')
|
||||
//} else {
|
||||
p.error('type `$name` redeclared')
|
||||
//}
|
||||
}
|
||||
if is_objc {
|
||||
// Forward declaration of an Objective-C interface with `@class` :)
|
||||
p.gen_typedef('@class $name;')
|
||||
}
|
||||
else if !is_c {
|
||||
kind := if is_union {'union'} else {'struct'}
|
||||
p.gen_typedef('typedef $kind $name $name;')
|
||||
}
|
||||
// Register the type
|
||||
mut is_ph := false
|
||||
if typ.is_placeholder {
|
||||
// Update the placeholder
|
||||
is_ph = true
|
||||
typ.name = name
|
||||
typ.mod = p.mod
|
||||
typ.is_c = is_c
|
||||
typ.is_placeholder = false
|
||||
typ.cat = cat
|
||||
typ.parent = objc_parent
|
||||
p.table.rewrite_type(typ)
|
||||
}
|
||||
else {
|
||||
typ = Type {
|
||||
name: name
|
||||
mod: p.mod
|
||||
is_c: is_c
|
||||
cat: cat
|
||||
parent: objc_parent
|
||||
is_public: is_pub
|
||||
}
|
||||
}
|
||||
// Struct `C.Foo` declaration, no body
|
||||
if is_c && is_struct && p.tok != .lcbr {
|
||||
p.table.register_type2(typ)
|
||||
return
|
||||
}
|
||||
p.fgen(' ')
|
||||
p.check(.lcbr)
|
||||
// Struct fields
|
||||
mut is_pub_field := false
|
||||
mut is_mut := false
|
||||
mut names := []string// to avoid dup names TODO alloc perf
|
||||
/*
|
||||
mut fmt_max_len := 0
|
||||
for field in typ.fields {
|
||||
if field.name.len > max_len {
|
||||
fmt_max_len = field.name.len
|
||||
}
|
||||
}
|
||||
println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
||||
*/
|
||||
|
||||
if !is_ph && p.first_pass() {
|
||||
p.table.register_type2(typ)
|
||||
//println('registering 1 nrfields=$typ.fields.len')
|
||||
}
|
||||
|
||||
mut did_gen_something := false
|
||||
for p.tok != .rcbr {
|
||||
if p.tok == .key_pub {
|
||||
if is_pub_field {
|
||||
p.error('structs can only have one `pub:`, all public fields have to be grouped')
|
||||
}
|
||||
is_pub_field = true
|
||||
p.fmt_dec()
|
||||
p.check(.key_pub)
|
||||
if p.tok != .key_mut {
|
||||
p.check(.colon)
|
||||
}
|
||||
p.fmt_inc()
|
||||
p.fgenln('')
|
||||
}
|
||||
if p.tok == .key_mut {
|
||||
if is_mut {
|
||||
p.error('structs can only have one `mut:`, all private mutable fields have to be grouped')
|
||||
}
|
||||
is_mut = true
|
||||
p.fmt_dec()
|
||||
p.check(.key_mut)
|
||||
if p.tok != .key_mut {
|
||||
p.check(.colon)
|
||||
}
|
||||
p.fmt_inc()
|
||||
p.fgenln('')
|
||||
}
|
||||
// if is_pub {
|
||||
// }
|
||||
// (mut) user *User
|
||||
// if p.tok == .plus {
|
||||
// p.next()
|
||||
// }
|
||||
// Check if reserved name
|
||||
field_name_token_idx := p.cur_tok_index()
|
||||
field_name := if name != 'Option' { p.table.var_cgen_name(p.check_name()) } else { p.check_name() }
|
||||
// Check dups
|
||||
if field_name in names {
|
||||
p.error('duplicate field `$field_name`')
|
||||
}
|
||||
if !is_c && p.mod != 'os' && contains_capital(field_name) {
|
||||
p.error('struct fields cannot contain uppercase letters, use snake_case instead')
|
||||
}
|
||||
names << field_name
|
||||
// We are in an interface?
|
||||
// `run() string` => run is a method, not a struct field
|
||||
if is_interface {
|
||||
f := p.interface_method(field_name, name)
|
||||
if p.first_pass() {
|
||||
p.add_method(typ.name, f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// `pub` access mod
|
||||
access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
||||
p.fgen(' ')
|
||||
field_type := p.get_type()
|
||||
if field_type == name {
|
||||
p.error_with_token_index( 'cannot embed struct `$name` in itself (field `$field_name`)', field_name_token_idx)
|
||||
}
|
||||
p.check_and_register_used_imported_type(field_type)
|
||||
is_atomic := p.tok == .key_atomic
|
||||
if is_atomic {
|
||||
p.next()
|
||||
}
|
||||
// [ATTR]
|
||||
mut attr := ''
|
||||
if p.tok == .lsbr {
|
||||
p.next()
|
||||
attr = p.check_name()
|
||||
if p.tok == .colon {
|
||||
p.check(.colon)
|
||||
mut val := ''
|
||||
match p.tok {
|
||||
.name { val = p.check_name() }
|
||||
.str { val = p.check_string() }
|
||||
else {
|
||||
p.error('attribute value should be either name or string')
|
||||
}
|
||||
}
|
||||
attr += ':' + val
|
||||
}
|
||||
p.check(.rsbr)
|
||||
}
|
||||
if attr == 'raw' && field_type != 'string' {
|
||||
p.error('struct field with attribute "raw" should be of type "string" but got "$field_type"')
|
||||
}
|
||||
|
||||
did_gen_something = true
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
|
||||
}
|
||||
p.fgenln('')
|
||||
}
|
||||
p.check(.rcbr)
|
||||
if !is_c {
|
||||
if !did_gen_something {
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.fgenln('\n')
|
||||
}
|
||||
|
||||
// check_name checks for a name token and returns its literal
|
||||
fn (p mut Parser) check_name() string {
|
||||
name := p.lit
|
||||
|
@ -3198,129 +2977,6 @@ fn (p mut Parser) array_init() string {
|
|||
return typ
|
||||
}
|
||||
|
||||
fn (p mut Parser) struct_init(typ string) string {
|
||||
p.is_struct_init = true
|
||||
t := p.table.find_type(typ)
|
||||
if !t.is_public && t.mod != p.mod {
|
||||
p.warn('type `$t.name` is private')
|
||||
}
|
||||
if p.gen_struct_init(typ, t) { return typ }
|
||||
p.scanner.fmt_out.cut(typ.len)
|
||||
ptr := typ.contains('*')
|
||||
mut did_gen_something := false
|
||||
// Loop thru all struct init keys and assign values
|
||||
// u := User{age:20, name:'bob'}
|
||||
// Remember which fields were set, so that we dont have to zero them later
|
||||
mut inited_fields := []string
|
||||
peek := p.peek()
|
||||
if peek == .colon || p.tok == .rcbr {
|
||||
for p.tok != .rcbr {
|
||||
field := if typ != 'Option' { p.table.var_cgen_name( p.check_name() ) } else { p.check_name() }
|
||||
if !p.first_pass() && !t.has_field(field) {
|
||||
p.error('`$t.name` has no field `$field`')
|
||||
}
|
||||
if field in inited_fields {
|
||||
p.error('already initialized field `$field` in `$t.name`')
|
||||
}
|
||||
f := t.find_field(field) or {
|
||||
p.error('no such field: "$field" in type $typ')
|
||||
break
|
||||
}
|
||||
inited_fields << field
|
||||
p.gen_struct_field_init(field)
|
||||
p.check(.colon)
|
||||
p.fspace()
|
||||
p.check_types(p.bool_expression(), f.typ)
|
||||
if p.tok == .comma {
|
||||
p.next()
|
||||
}
|
||||
if p.tok != .rcbr {
|
||||
p.gen(',')
|
||||
}
|
||||
p.fgenln('')
|
||||
did_gen_something = true
|
||||
}
|
||||
// If we already set some fields, need to prepend a comma
|
||||
if t.fields.len != inited_fields.len && inited_fields.len > 0 {
|
||||
p.gen(',')
|
||||
}
|
||||
// Zero values: init all fields (ints to 0, strings to '' etc)
|
||||
for i, field in t.fields {
|
||||
sanitized_name := if typ != 'Option' { p.table.var_cgen_name( field.name ) } else { field.name }
|
||||
// println('### field.name')
|
||||
// Skip if this field has already been assigned to
|
||||
if sanitized_name in inited_fields {
|
||||
continue
|
||||
}
|
||||
field_typ := field.typ
|
||||
if !p.builtin_mod && field_typ.ends_with('*') && field_typ.contains('Cfg') {
|
||||
p.error('pointer field `${typ}.${field.name}` must be initialized')
|
||||
}
|
||||
// init map fields
|
||||
if field_typ.starts_with('map_') {
|
||||
p.gen_struct_field_init(sanitized_name)
|
||||
p.gen_empty_map(field_typ.right(4))
|
||||
inited_fields << sanitized_name
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
}
|
||||
did_gen_something = true
|
||||
continue
|
||||
}
|
||||
def_val := type_default(field_typ)
|
||||
if def_val != '' && def_val != '{0}' {
|
||||
p.gen_struct_field_init(sanitized_name)
|
||||
p.gen(def_val)
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
}
|
||||
did_gen_something = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Point{3,4} syntax
|
||||
else {
|
||||
mut T := p.table.find_type(typ)
|
||||
// Aliases (TODO Hack, implement proper aliases)
|
||||
if T.fields.len == 0 && T.parent != '' {
|
||||
T = p.table.find_type(T.parent)
|
||||
}
|
||||
for i, ffield in T.fields {
|
||||
expr_typ := p.bool_expression()
|
||||
if !p.check_types_no_throw(expr_typ, ffield.typ) {
|
||||
p.error('field value #${i+1} `$ffield.name` has type `$ffield.typ`, got `$expr_typ` ')
|
||||
}
|
||||
if i < T.fields.len - 1 {
|
||||
if p.tok != .comma {
|
||||
p.error('too few values in `$typ` literal (${i+1} instead of $T.fields.len)')
|
||||
}
|
||||
p.gen(',')
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
// Allow `user := User{1,2,3,}`
|
||||
// The final comma will be removed by vfmt, since we are not calling `p.fgen()`
|
||||
if p.tok == .comma {
|
||||
p.next()
|
||||
}
|
||||
if p.tok != .rcbr {
|
||||
p.error('too many fields initialized: `$typ` has $T.fields.len field(s)')
|
||||
}
|
||||
did_gen_something = true
|
||||
}
|
||||
if !did_gen_something {
|
||||
p.gen('EMPTY_STRUCT_INITIALIZATION')
|
||||
}
|
||||
p.gen('}')
|
||||
if ptr && !p.is_js {
|
||||
p.gen(', sizeof($t.name))')
|
||||
}
|
||||
p.check(.rcbr)
|
||||
p.is_struct_init = false
|
||||
p.is_c_struct_init = false
|
||||
return typ
|
||||
}
|
||||
|
||||
// `f32(3)`
|
||||
// tok is `f32` or `)` if `(*int)(ptr)`
|
||||
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
module compiler
|
||||
|
||||
// also unions and interfaces
|
||||
fn (p mut Parser) struct_decl() {
|
||||
is_pub := p.tok == .key_pub
|
||||
if is_pub {
|
||||
p.next()
|
||||
}
|
||||
// V can generate Objective C for integration with Cocoa
|
||||
// `[objc_interface:ParentInterface]`
|
||||
is_objc := p.attr.starts_with('objc_interface')
|
||||
objc_parent := if is_objc { p.attr.right(15) } else { '' }
|
||||
// interface, union, struct
|
||||
is_interface := p.tok == .key_interface
|
||||
is_union := p.tok == .key_union
|
||||
is_struct := p.tok == .key_struct
|
||||
mut cat := key_to_type_cat(p.tok)
|
||||
if is_objc {
|
||||
cat = .objc_interface
|
||||
}
|
||||
p.fgen(p.tok.str() + ' ')
|
||||
// Get type name
|
||||
p.next()
|
||||
mut name := p.check_name()
|
||||
if name.contains('_') && !p.pref.translated {
|
||||
p.error('type names cannot contain `_`')
|
||||
}
|
||||
if !p.builtin_mod && !name[0].is_capital() {
|
||||
p.error('struct names must be capitalized: use `struct ${name.capitalize()}`')
|
||||
}
|
||||
if is_interface && !name.ends_with('er') {
|
||||
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
|
||||
}
|
||||
is_c := name == 'C' && p.tok == .dot
|
||||
if is_c {
|
||||
p.check(.dot)
|
||||
name = p.check_name()
|
||||
cat = .c_struct
|
||||
if p.attr == 'typedef' {
|
||||
cat = .c_typedef
|
||||
}
|
||||
}
|
||||
if name.len == 1 && !p.pref.building_v && !p.pref.is_repl {
|
||||
p.warn('struct names must have more than one character')
|
||||
}
|
||||
if !is_c && !good_type_name(name) {
|
||||
p.error('bad struct name, e.g. use `HttpRequest` instead of `HTTPRequest`')
|
||||
}
|
||||
// Specify full type name
|
||||
if !is_c && !p.builtin_mod && p.mod != 'main' {
|
||||
name = p.prepend_mod(name)
|
||||
}
|
||||
mut typ := p.table.find_type(name)
|
||||
if p.pass == .decl && p.table.known_type_fast(typ) {
|
||||
//if name in reserved_type_param_names {
|
||||
//p.error('name `$name` is reserved for type parameters')
|
||||
//} else {
|
||||
p.error('type `$name` redeclared')
|
||||
//}
|
||||
}
|
||||
if is_objc {
|
||||
// Forward declaration of an Objective-C interface with `@class` :)
|
||||
p.gen_typedef('@class $name;')
|
||||
}
|
||||
else if !is_c {
|
||||
kind := if is_union {'union'} else {'struct'}
|
||||
p.gen_typedef('typedef $kind $name $name;')
|
||||
}
|
||||
// Register the type
|
||||
mut is_ph := false
|
||||
if typ.is_placeholder {
|
||||
// Update the placeholder
|
||||
is_ph = true
|
||||
typ.name = name
|
||||
typ.mod = p.mod
|
||||
typ.is_c = is_c
|
||||
typ.is_placeholder = false
|
||||
typ.cat = cat
|
||||
typ.parent = objc_parent
|
||||
p.table.rewrite_type(typ)
|
||||
}
|
||||
else {
|
||||
typ = Type {
|
||||
name: name
|
||||
mod: p.mod
|
||||
is_c: is_c
|
||||
cat: cat
|
||||
parent: objc_parent
|
||||
is_public: is_pub
|
||||
}
|
||||
}
|
||||
// Struct `C.Foo` declaration, no body
|
||||
if is_c && is_struct && p.tok != .lcbr {
|
||||
p.table.register_type2(typ)
|
||||
return
|
||||
}
|
||||
p.fgen(' ')
|
||||
p.check(.lcbr)
|
||||
// Struct fields
|
||||
mut is_pub_field := false
|
||||
mut is_mut := false
|
||||
mut names := []string// to avoid dup names TODO alloc perf
|
||||
/*
|
||||
mut fmt_max_len := 0
|
||||
for field in typ.fields {
|
||||
if field.name.len > max_len {
|
||||
fmt_max_len = field.name.len
|
||||
}
|
||||
}
|
||||
println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
||||
*/
|
||||
|
||||
if !is_ph && p.first_pass() {
|
||||
p.table.register_type2(typ)
|
||||
//println('registering 1 nrfields=$typ.fields.len')
|
||||
}
|
||||
|
||||
mut did_gen_something := false
|
||||
for p.tok != .rcbr {
|
||||
if p.tok == .key_pub {
|
||||
if is_pub_field {
|
||||
p.error('structs can only have one `pub:`, all public fields have to be grouped')
|
||||
}
|
||||
is_pub_field = true
|
||||
p.fmt_dec()
|
||||
p.check(.key_pub)
|
||||
if p.tok != .key_mut {
|
||||
p.check(.colon)
|
||||
}
|
||||
p.fmt_inc()
|
||||
p.fgenln('')
|
||||
}
|
||||
if p.tok == .key_mut {
|
||||
if is_mut {
|
||||
p.error('structs can only have one `mut:`, all private mutable fields have to be grouped')
|
||||
}
|
||||
is_mut = true
|
||||
p.fmt_dec()
|
||||
p.check(.key_mut)
|
||||
if p.tok != .key_mut {
|
||||
p.check(.colon)
|
||||
}
|
||||
p.fmt_inc()
|
||||
p.fgenln('')
|
||||
}
|
||||
// if is_pub {
|
||||
// }
|
||||
// (mut) user *User
|
||||
// if p.tok == .plus {
|
||||
// p.next()
|
||||
// }
|
||||
// Check if reserved name
|
||||
field_name_token_idx := p.cur_tok_index()
|
||||
field_name := if name != 'Option' { p.table.var_cgen_name(p.check_name()) } else { p.check_name() }
|
||||
// Check dups
|
||||
if field_name in names {
|
||||
p.error('duplicate field `$field_name`')
|
||||
}
|
||||
if !is_c && p.mod != 'os' && contains_capital(field_name) {
|
||||
p.error('struct fields cannot contain uppercase letters, use snake_case instead')
|
||||
}
|
||||
names << field_name
|
||||
// We are in an interface?
|
||||
// `run() string` => run is a method, not a struct field
|
||||
if is_interface {
|
||||
f := p.interface_method(field_name, name)
|
||||
if p.first_pass() {
|
||||
p.add_method(typ.name, f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// `pub` access mod
|
||||
access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
||||
p.fgen(' ')
|
||||
field_type := p.get_type()
|
||||
if field_type == name {
|
||||
p.error_with_token_index( 'cannot embed struct `$name` in itself (field `$field_name`)', field_name_token_idx)
|
||||
}
|
||||
p.check_and_register_used_imported_type(field_type)
|
||||
is_atomic := p.tok == .key_atomic
|
||||
if is_atomic {
|
||||
p.next()
|
||||
}
|
||||
// [ATTR]
|
||||
mut attr := ''
|
||||
if p.tok == .lsbr {
|
||||
p.next()
|
||||
attr = p.check_name()
|
||||
if p.tok == .colon {
|
||||
p.check(.colon)
|
||||
mut val := ''
|
||||
match p.tok {
|
||||
.name { val = p.check_name() }
|
||||
.str { val = p.check_string() }
|
||||
else {
|
||||
p.error('attribute value should be either name or string')
|
||||
}
|
||||
}
|
||||
attr += ':' + val
|
||||
}
|
||||
p.check(.rsbr)
|
||||
}
|
||||
if attr == 'raw' && field_type != 'string' {
|
||||
p.error('struct field with attribute "raw" should be of type "string" but got "$field_type"')
|
||||
}
|
||||
|
||||
did_gen_something = true
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
|
||||
}
|
||||
p.fgenln('')
|
||||
}
|
||||
p.check(.rcbr)
|
||||
if !is_c {
|
||||
if !did_gen_something {
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.fgenln('\n')
|
||||
}
|
||||
|
||||
// `User{ foo: bar }`
|
||||
fn (p mut Parser) struct_init(typ string) string {
|
||||
p.is_struct_init = true
|
||||
t := p.table.find_type(typ)
|
||||
if !t.is_public && t.mod != p.mod {
|
||||
p.warn('type `$t.name` is private')
|
||||
}
|
||||
if p.gen_struct_init(typ, t) { return typ }
|
||||
p.scanner.fmt_out.cut(typ.len)
|
||||
ptr := typ.contains('*')
|
||||
mut did_gen_something := false
|
||||
// Loop thru all struct init keys and assign values
|
||||
// u := User{age:20, name:'bob'}
|
||||
// Remember which fields were set, so that we dont have to zero them later
|
||||
mut inited_fields := []string
|
||||
peek := p.peek()
|
||||
if peek == .colon || p.tok == .rcbr {
|
||||
for p.tok != .rcbr {
|
||||
field := if typ != 'Option' { p.table.var_cgen_name( p.check_name() ) } else { p.check_name() }
|
||||
if !p.first_pass() && !t.has_field(field) {
|
||||
p.error('`$t.name` has no field `$field`')
|
||||
}
|
||||
if field in inited_fields {
|
||||
p.error('already initialized field `$field` in `$t.name`')
|
||||
}
|
||||
f := t.find_field(field) or {
|
||||
p.error('no such field: "$field" in type $typ')
|
||||
break
|
||||
}
|
||||
inited_fields << field
|
||||
p.gen_struct_field_init(field)
|
||||
p.check(.colon)
|
||||
p.fspace()
|
||||
p.check_types(p.bool_expression(), f.typ)
|
||||
if p.tok == .comma {
|
||||
p.next()
|
||||
}
|
||||
if p.tok != .rcbr {
|
||||
p.gen(',')
|
||||
}
|
||||
p.fgenln('')
|
||||
did_gen_something = true
|
||||
}
|
||||
// If we already set some fields, need to prepend a comma
|
||||
if t.fields.len != inited_fields.len && inited_fields.len > 0 {
|
||||
p.gen(',')
|
||||
}
|
||||
// Zero values: init all fields (ints to 0, strings to '' etc)
|
||||
for i, field in t.fields {
|
||||
sanitized_name := if typ != 'Option' { p.table.var_cgen_name( field.name ) } else { field.name }
|
||||
// println('### field.name')
|
||||
// Skip if this field has already been assigned to
|
||||
if sanitized_name in inited_fields {
|
||||
continue
|
||||
}
|
||||
field_typ := field.typ
|
||||
if !p.builtin_mod && field_typ.ends_with('*') && field_typ.contains('Cfg') {
|
||||
p.error('pointer field `${typ}.${field.name}` must be initialized')
|
||||
}
|
||||
// init map fields
|
||||
if field_typ.starts_with('map_') {
|
||||
p.gen_struct_field_init(sanitized_name)
|
||||
p.gen_empty_map(field_typ.right(4))
|
||||
inited_fields << sanitized_name
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
}
|
||||
did_gen_something = true
|
||||
continue
|
||||
}
|
||||
def_val := type_default(field_typ)
|
||||
if def_val != '' && def_val != '{0}' {
|
||||
p.gen_struct_field_init(sanitized_name)
|
||||
p.gen(def_val)
|
||||
if i != t.fields.len - 1 {
|
||||
p.gen(',')
|
||||
}
|
||||
did_gen_something = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// Point{3,4} syntax
|
||||
else {
|
||||
mut T := p.table.find_type(typ)
|
||||
// Aliases (TODO Hack, implement proper aliases)
|
||||
if T.fields.len == 0 && T.parent != '' {
|
||||
T = p.table.find_type(T.parent)
|
||||
}
|
||||
for i, ffield in T.fields {
|
||||
expr_typ := p.bool_expression()
|
||||
if !p.check_types_no_throw(expr_typ, ffield.typ) {
|
||||
p.error('field value #${i+1} `$ffield.name` has type `$ffield.typ`, got `$expr_typ` ')
|
||||
}
|
||||
if i < T.fields.len - 1 {
|
||||
if p.tok != .comma {
|
||||
p.error('too few values in `$typ` literal (${i+1} instead of $T.fields.len)')
|
||||
}
|
||||
p.gen(',')
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
// Allow `user := User{1,2,3,}`
|
||||
// The final comma will be removed by vfmt, since we are not calling `p.fgen()`
|
||||
if p.tok == .comma {
|
||||
p.next()
|
||||
}
|
||||
if p.tok != .rcbr {
|
||||
p.error('too many fields initialized: `$typ` has $T.fields.len field(s)')
|
||||
}
|
||||
did_gen_something = true
|
||||
}
|
||||
if !did_gen_something {
|
||||
p.gen('EMPTY_STRUCT_INITIALIZATION')
|
||||
}
|
||||
p.gen('}')
|
||||
if ptr && !p.is_js {
|
||||
p.gen(', sizeof($t.name))')
|
||||
}
|
||||
p.check(.rcbr)
|
||||
p.is_struct_init = false
|
||||
p.is_c_struct_init = false
|
||||
return typ
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue