1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00
v/vlib/compiler/struct.v

536 lines
14 KiB
V
Raw Normal View History

2019-10-24 15:56:10 +03:00
// 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
2019-12-17 17:28:25 +03:00
import (
strings
)
2019-10-24 15:56:10 +03:00
// also unions and interfaces
2019-12-22 00:54:37 +03:00
2019-12-21 03:53:58 +03:00
fn (p mut Parser) struct_decl(generic_param_types []string) {
decl_tok_idx := p.cur_tok_index()
2019-10-24 15:56:10 +03:00
is_pub := p.tok == .key_pub
if is_pub {
p.next()
2019-11-09 22:05:44 +03:00
p.fspace()
2019-12-03 13:08:57 +03:00
}
2019-10-24 15:56:10 +03:00
// 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[15..] } else { '' }
2019-10-24 15:56:10 +03:00
// 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.next()
2019-11-09 19:13:26 +03:00
p.fspace()
// Get type name
2019-10-24 15:56:10 +03:00
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() {
2019-11-11 17:18:32 +03:00
p.error('mod=$p.mod struct names must be capitalized: use `struct ${name.capitalize()}`')
2019-10-24 15:56:10 +03:00
}
2019-12-22 04:34:37 +03:00
if is_interface && !name.ends_with('er') && name[0] != `I` {
2019-10-24 15:56:10 +03:00
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
}
2019-12-21 03:53:58 +03:00
mut generic_types := map[string]string
mut is_generic := false
if p.tok == .lt {
p.check(.lt)
2019-12-21 09:54:43 +03:00
for i := 0; ; i++ {
2019-12-22 00:54:37 +03:00
if generic_param_types.len > 0 && i > generic_param_types.len - 1 {
2019-12-21 03:53:58 +03:00
p.error('mismatched generic type params')
}
type_param := p.check_name()
2019-12-22 00:54:37 +03:00
generic_types[type_param] = if generic_param_types.len > 0 { generic_param_types[i] } else { '' }
if p.tok != .comma {
break
}
2019-12-21 03:53:58 +03:00
p.check(.comma)
}
p.check(.gt)
is_generic = true
}
is_generic_instance := is_generic && generic_param_types.len > 0
2019-10-24 15:56:10 +03:00
is_c := name == 'C' && p.tok == .dot
if is_c {
/*
2019-11-06 00:32:15 +03:00
if !p.pref.building_v && !p.fileis('vlib') {
p.warn('Virtual C structs will soon be removed from the language' +
'\ndefine the C structs and functions in V')
}
*/
2019-10-24 15:56:10 +03:00
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')
2019-12-03 13:08:57 +03:00
}
2019-10-24 15:56:10 +03:00
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) {
2019-12-20 00:29:37 +03:00
// if name in reserved_type_param_names {
// p.error('name `$name` is reserved for type parameters')
// } else {
2019-10-24 15:56:10 +03:00
p.error('type `$name` redeclared')
2019-12-20 00:29:37 +03:00
// }
2019-10-24 15:56:10 +03:00
}
if is_objc {
// Forward declaration of an Objective-C interface with `@class` :)
p.gen_typedef('@class $name;')
}
else if !is_c {
2019-12-20 00:29:37 +03:00
kind := if is_union { 'union' } else { 'struct' }
2019-10-24 15:56:10 +03:00
p.gen_typedef('typedef $kind $name $name;')
}
2019-12-21 05:33:59 +03:00
// TODO: handle error
2019-12-22 00:54:37 +03:00
parser_idx := p.v.get_file_parser_index(p.file_path) or {
0
}
// if !p.scanner.is_vh {
// parser_idx = p.v.get_file_parser_index(p.file_path) or { panic('cant find parser idx for $p.file_path') }
// }
2019-10-24 15:56:10 +03:00
// 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
2019-10-31 13:08:01 +03:00
typ.is_public = is_pub || p.is_vh
2019-12-21 03:53:58 +03:00
typ.is_generic = is_generic && !is_generic_instance
typ.decl_tok_idx = decl_tok_idx
typ.parser_idx = parser_idx
2019-10-24 15:56:10 +03:00
p.table.rewrite_type(typ)
}
else {
2019-12-20 00:29:37 +03:00
typ = Type{
2019-10-24 15:56:10 +03:00
name: name
mod: p.mod
is_c: is_c
cat: cat
parent: objc_parent
2019-10-31 13:08:01 +03:00
is_public: is_pub || p.is_vh
2019-12-21 03:53:58 +03:00
is_generic: is_generic && !is_generic_instance
decl_tok_idx: decl_tok_idx
parser_idx: parser_idx
2019-10-24 15:56:10 +03:00
}
}
// Struct `C.Foo` declaration, no body
if is_c && is_struct && p.tok != .lcbr {
2019-12-05 14:09:33 +03:00
p.table.register_type(typ)
2019-10-24 15:56:10 +03:00
return
}
2019-12-21 05:33:59 +03:00
// generic struct
if is_generic {
// template
if !is_generic_instance {
p.table.register_type(typ)
p.table.generic_struct_params[typ.name] = generic_types.keys()
// NOTE: remove to store fields in generic struct template
p.skip_block(false)
return
}
// instance
else {
typ.rename_generic_struct(generic_types)
}
2019-12-21 03:53:58 +03:00
}
2019-11-09 19:13:26 +03:00
p.fspace()
2019-10-24 15:56:10 +03:00
p.check(.lcbr)
// Struct fields
mut access_mod := AccessMod.private
2019-12-20 00:29:37 +03:00
// mut is_pub_field := false
// mut is_mut := false
mut names := []string // to avoid dup names TODO alloc perf
2019-12-17 17:28:25 +03:00
mut fmt_max_len := p.table.max_field_len[name]
2019-12-20 00:29:37 +03:00
// println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
2019-12-21 03:53:58 +03:00
if (!is_ph && p.first_pass()) || is_generic {
2019-12-05 14:09:33 +03:00
p.table.register_type(typ)
2019-12-20 00:29:37 +03:00
// println('registering 1 nrfields=$typ.fields.len')
2019-10-24 15:56:10 +03:00
}
mut did_gen_something := false
mut used := []AccessMod
2019-11-24 13:53:59 +03:00
mut i := -1
2019-10-24 15:56:10 +03:00
for p.tok != .rcbr {
2019-11-24 13:53:59 +03:00
i++
mut new_access_mod := access_mod
2019-10-24 15:56:10 +03:00
if p.tok == .key_pub {
2019-12-17 17:28:25 +03:00
p.fmt_dec()
2019-10-24 15:56:10 +03:00
p.check(.key_pub)
if p.tok == .key_mut {
2019-12-17 17:28:25 +03:00
p.fspace()
new_access_mod = .public_mut
p.next() // skip `mut`
2019-12-20 00:29:37 +03:00
}
else {
new_access_mod = .public
2019-10-24 15:56:10 +03:00
}
if new_access_mod in used {
p.error('structs can only have one `pub:`/`pub mut:`, all public fields have to be grouped')
}
p.check(.colon)
2019-10-24 15:56:10 +03:00
p.fmt_inc()
2019-11-18 13:10:31 +03:00
p.fgen_nl()
2019-10-24 15:56:10 +03:00
}
else if p.tok == .key_mut {
new_access_mod = .private_mut
if new_access_mod in used {
2019-10-24 15:56:10 +03:00
p.error('structs can only have one `mut:`, all private mutable fields have to be grouped')
}
p.fmt_dec()
p.check(.key_mut)
p.check(.colon)
2019-10-24 15:56:10 +03:00
p.fmt_inc()
2019-11-18 13:10:31 +03:00
p.fgen_nl()
2019-10-24 15:56:10 +03:00
}
2019-12-13 20:28:28 +03:00
else if p.tok == .key_global {
new_access_mod = .global
if new_access_mod in used {
p.error('structs can only have one `__global:`, all global fields have to be grouped')
}
p.fmt_dec()
p.check(.key_global)
p.check(.colon)
p.fmt_inc()
p.fgen_nl()
}
if new_access_mod != access_mod {
used << new_access_mod
}
access_mod = new_access_mod
2019-10-24 15:56:10 +03:00
// (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' && !is_interface { p.table.var_cgen_name(p.check_name()) } else { p.check_name() }
2019-12-17 17:28:25 +03:00
if p.pass == .main {
2019-11-11 17:18:32 +03:00
p.fgen(strings.repeat(` `, fmt_max_len - field_name.len))
}
2019-10-24 15:56:10 +03:00
// Check dups
if field_name in names {
p.error('duplicate field `$field_name`')
}
2019-12-17 17:28:25 +03:00
if p.scanner.is_fmt && p.pass == .decl && field_name.len > fmt_max_len {
fmt_max_len = field_name.len
}
2019-10-24 15:56:10 +03:00
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
2019-12-20 00:29:37 +03:00
// access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
2019-11-09 19:13:26 +03:00
p.fspace()
2019-12-22 00:54:37 +03:00
defer {
if is_generic_instance {
p.generic_dispatch = TypeInst{
}
}
}
2019-12-21 03:53:58 +03:00
if is_generic_instance {
2019-12-22 00:54:37 +03:00
p.generic_dispatch = TypeInst{
2019-12-21 03:53:58 +03:00
inst: generic_types
}
}
2019-12-05 19:02:33 +03:00
tt := p.get_type2()
field_type := tt.name
2019-10-24 15:56:10 +03:00
if field_type == name {
2019-12-05 19:02:33 +03:00
p.error_with_token_index('cannot embed struct `$name` in itself (field `$field_name`)', field_name_token_idx)
2019-10-24 15:56:10 +03:00
}
// Register ?option type
if field_type.starts_with('Option_') {
p.gen_typedef('typedef Option $field_type;')
}
2019-10-24 15:56:10 +03:00
p.check_and_register_used_imported_type(field_type)
is_atomic := p.tok == .key_atomic
if is_atomic {
p.next()
}
2019-11-24 13:53:59 +03:00
// `a int = 4`
if p.tok == .assign {
p.next()
2019-12-20 00:29:37 +03:00
def_val_type,expr := p.tmp_expr()
2019-11-24 13:53:59 +03:00
if def_val_type != field_type {
p.error('expected `$field_type` but got `$def_val_type`')
}
2019-12-20 00:29:37 +03:00
// println('pass=$p.pass $typ.name ADDING field=$field_name "$def_val_type" "$expr"')
2019-11-24 15:56:14 +03:00
if !p.first_pass() {
p.table.add_default_val(i, typ.name, expr)
}
2019-12-03 13:08:57 +03:00
}
2019-10-24 15:56:10 +03:00
// [ATTR]
mut attr := ''
if p.tok == .lsbr {
2019-11-09 19:13:26 +03:00
p.fspace()
2019-10-24 15:56:10 +03:00
p.next()
attr = p.check_name()
if p.tok == .colon {
p.check(.colon)
mut val := ''
match p.tok {
2019-12-20 00:29:37 +03:00
.name {
val = p.check_name()
}
.str {
val = p.check_string()
}
2019-10-24 15:56:10 +03:00
else {
p.error('attribute value should be either name or string')
2019-12-20 00:29:37 +03:00
}}
2019-10-24 15:56:10 +03:00
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
2019-12-13 20:28:28 +03:00
is_mut := access_mod in [.private_mut, .public_mut, .global]
2019-12-21 03:53:58 +03:00
if p.first_pass() || is_generic {
2019-12-22 00:54:37 +03:00
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
2019-10-24 15:56:10 +03:00
}
2019-11-18 13:10:31 +03:00
p.fgen_nl() // newline between struct fields
2019-10-24 15:56:10 +03:00
}
2019-12-17 17:28:25 +03:00
if p.scanner.is_fmt && p.pass == .decl {
p.table.max_field_len[typ.name] = fmt_max_len
}
2019-12-20 00:29:37 +03:00
// p.fgen_require_nl()
2019-10-24 15:56:10 +03:00
p.check(.rcbr)
2019-11-08 06:03:06 +03:00
if !is_c && !did_gen_something && p.first_pass() {
p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private)
2019-10-24 15:56:10 +03:00
}
2019-12-17 17:28:25 +03:00
p.fgen_nl()
p.fgen_nl()
2019-12-20 00:29:37 +03:00
// p.fgenln('//kek')
2019-10-24 15:56:10 +03:00
}
// `User{ foo: bar }`
2019-12-21 03:53:58 +03:00
fn (p mut Parser) struct_init(typ_ string) string {
2019-10-24 15:56:10 +03:00
p.is_struct_init = true
2019-12-21 03:53:58 +03:00
mut typ := typ_
mut t := p.table.find_type(typ)
2019-10-24 15:56:10 +03:00
if !t.is_public && t.mod != p.mod {
p.warn('type `$t.name` is private')
2019-12-03 13:08:57 +03:00
}
2019-12-21 03:53:58 +03:00
// generic struct init
if p.peek() == .lt {
p.next()
p.check(.lt)
mut type_params := []string
for {
mut type_param := p.check_name()
if type_param in p.generic_dispatch.inst {
type_param = p.generic_dispatch.inst[type_param]
}
type_params << type_param
2019-12-22 00:54:37 +03:00
if p.tok != .comma {
break
}
2019-12-21 03:53:58 +03:00
p.check(.comma)
}
p.dispatch_generic_struct(mut t, type_params)
t = p.table.find_type(t.name)
typ = t.name
}
if p.gen_struct_init(typ, &t) {
2019-12-20 00:29:37 +03:00
return typ
}
2019-10-24 15:56:10 +03:00
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 {
2019-12-20 00:29:37 +03:00
field := if typ != 'Option' { p.table.var_cgen_name(p.check_name()) } else { p.check_name() }
2019-10-24 15:56:10 +03:00
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
}
tt := p.table.find_type(f.typ)
if tt.is_flag {
p.error(err_modify_bitfield)
}
2019-10-24 15:56:10 +03:00
inited_fields << field
p.gen_struct_field_init(field)
p.check(.colon)
p.fspace()
p.expected_type = f.typ
2019-12-20 00:29:37 +03:00
p.check_types(p.bool_expression(), f.typ)
2019-10-24 15:56:10 +03:00
if p.tok == .comma {
p.next()
2019-12-18 08:13:31 +03:00
p.fremove_last()
2019-10-24 15:56:10 +03:00
}
if p.tok != .rcbr {
p.gen(',')
}
2019-11-09 19:13:26 +03:00
p.fspace()
2019-10-24 15:56:10 +03:00
did_gen_something = true
2019-11-18 13:10:31 +03:00
p.fgen_nl() // newline between struct fields
2019-10-24 15:56:10 +03:00
}
// 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 {
2019-12-20 00:29:37 +03:00
sanitized_name := if typ != 'Option' { p.table.var_cgen_name(field.name) } else { field.name }
2019-10-24 15:56:10 +03:00
// println('### field.name')
// Skip if this field has already been assigned to
if sanitized_name in inited_fields {
continue
}
field_typ := field.typ
2019-12-20 00:29:37 +03:00
if !p.builtin_mod && field_typ.ends_with('*') && p.mod != 'os' {
// &&
2019-12-03 13:08:57 +03:00
p.warn('reference field `${typ}.${field.name}` must be initialized')
2019-10-24 15:56:10 +03:00
}
// init map fields
if field_typ.starts_with('map_') {
p.gen_struct_field_init(sanitized_name)
p.gen_empty_map(parse_pointer(field_typ[4..]))
2019-10-24 15:56:10 +03:00
inited_fields << sanitized_name
if i != t.fields.len - 1 {
p.gen(',')
}
did_gen_something = true
continue
}
2019-11-24 15:56:14 +03:00
// Did the user provide a default value for this struct field?
// Use it. Otherwise zero it.
2019-12-20 00:29:37 +03:00
def_val := if t.default_vals.len > i && t.default_vals[i] != '' { t.default_vals[i] } else { type_default(field_typ) }
2019-10-24 15:56:10 +03:00
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` ')
}
tt := p.table.find_type(ffield.typ)
if tt.is_flag {
p.error(err_modify_bitfield)
}
2019-10-24 15:56:10 +03:00
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
}
2019-12-21 03:53:58 +03:00
fn (t mut Type) rename_generic_struct(generic_types map[string]string) {
t.name = t.name + '_T'
for _, v in generic_types {
t.name = t.name + '_' + type_to_safe_str(v)
}
}
fn (p mut Parser) dispatch_generic_struct(t mut Type, type_params []string) {
mut generic_types := map[string]string
if t.name in p.table.generic_struct_params {
mut i := 0
2019-12-22 00:54:37 +03:00
for _, v in p.table.generic_struct_params[t.name] {
2019-12-21 03:53:58 +03:00
generic_types[v] = type_params[i]
i++
}
t.rename_generic_struct(generic_types)
if p.table.known_type(t.name) {
return
}
p.cgen.typedefs << 'typedef struct $t.name $t.name;\n'
}
mut gp := p.v.parsers[t.parser_idx]
gp.is_vgen = true
saved_state := p.save_state()
p.clear_state(false, true)
gp.token_idx = t.decl_tok_idx
// FIXME: TODO: why are tokens cleared?
if gp.tokens.len == 0 {
gp.scanner.pos = 0
gp.scan_tokens()
}
gp.next()
gp.struct_decl(type_params)
p.cgen.lines_extra << p.cgen.lines
p.restore_state(saved_state, false, true)
}