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