mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
implement generic structs
This commit is contained in:

committed by
Alexander Medvednikov

parent
b3a402eb82
commit
fbd9fedbfb
@ -7,9 +7,8 @@ import (
|
||||
strings
|
||||
)
|
||||
// also unions and interfaces
|
||||
|
||||
|
||||
fn (p mut Parser) struct_decl() {
|
||||
fn (p mut Parser) struct_decl(generic_param_types []string) {
|
||||
decl_tok_idx := p.cur_tok_index()
|
||||
is_pub := p.tok == .key_pub
|
||||
if is_pub {
|
||||
p.next()
|
||||
@ -40,6 +39,31 @@ fn (p mut Parser) struct_decl() {
|
||||
if is_interface && !name.ends_with('er') {
|
||||
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
|
||||
}
|
||||
|
||||
mut generic_types := map[string]string
|
||||
mut is_generic := false
|
||||
if p.tok == .lt {
|
||||
p.check(.lt)
|
||||
mut i := 0
|
||||
for {
|
||||
if generic_param_types.len > 0 && i != generic_param_types.len-1 {
|
||||
p.error('mismatched generic type params')
|
||||
}
|
||||
type_param := p.check_name()
|
||||
if generic_param_types.len > 0 {
|
||||
generic_types[type_param] = generic_param_types[i]
|
||||
} else {
|
||||
generic_types[type_param] = ''
|
||||
}
|
||||
if p.tok != .comma { break }
|
||||
p.check(.comma)
|
||||
i++
|
||||
}
|
||||
p.check(.gt)
|
||||
is_generic = true
|
||||
}
|
||||
is_generic_instance := is_generic && generic_param_types.len > 0
|
||||
|
||||
is_c := name == 'C' && p.tok == .dot
|
||||
if is_c {
|
||||
/*
|
||||
@ -81,6 +105,11 @@ fn (p mut Parser) struct_decl() {
|
||||
kind := if is_union { 'union' } else { 'struct' }
|
||||
p.gen_typedef('typedef $kind $name $name;')
|
||||
}
|
||||
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') }
|
||||
// println('HERE: $parser_idx')
|
||||
//}
|
||||
// Register the type
|
||||
mut is_ph := false
|
||||
if typ.is_placeholder {
|
||||
@ -93,6 +122,9 @@ fn (p mut Parser) struct_decl() {
|
||||
typ.cat = cat
|
||||
typ.parent = objc_parent
|
||||
typ.is_public = is_pub || p.is_vh
|
||||
typ.is_generic = is_generic && !is_generic_instance
|
||||
typ.decl_tok_idx = decl_tok_idx
|
||||
typ.parser_idx = parser_idx
|
||||
p.table.rewrite_type(typ)
|
||||
}
|
||||
else {
|
||||
@ -103,6 +135,9 @@ fn (p mut Parser) struct_decl() {
|
||||
cat: cat
|
||||
parent: objc_parent
|
||||
is_public: is_pub || p.is_vh
|
||||
is_generic: is_generic && !is_generic_instance
|
||||
decl_tok_idx: decl_tok_idx
|
||||
parser_idx: parser_idx
|
||||
}
|
||||
}
|
||||
// Struct `C.Foo` declaration, no body
|
||||
@ -110,6 +145,18 @@ fn (p mut Parser) struct_decl() {
|
||||
p.table.register_type(typ)
|
||||
return
|
||||
}
|
||||
// generic template
|
||||
if is_generic && !is_generic_instance {
|
||||
p.table.register_type(typ)
|
||||
p.table.generic_struct_params[typ.name] = generic_types.keys()
|
||||
//}
|
||||
// TODO: re are skipping genrcic struct in gen (cgen.v) we can go as normal and remove this
|
||||
p.skip_block(false)
|
||||
return
|
||||
}
|
||||
if is_generic_instance {
|
||||
typ.rename_generic_struct(generic_types)
|
||||
}
|
||||
p.fspace()
|
||||
p.check(.lcbr)
|
||||
// Struct fields
|
||||
@ -119,7 +166,7 @@ fn (p mut Parser) struct_decl() {
|
||||
mut names := []string // to avoid dup names TODO alloc perf
|
||||
mut fmt_max_len := p.table.max_field_len[name]
|
||||
// println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
||||
if !is_ph && p.first_pass() {
|
||||
if (!is_ph && p.first_pass()) || is_generic {
|
||||
p.table.register_type(typ)
|
||||
// println('registering 1 nrfields=$typ.fields.len')
|
||||
}
|
||||
@ -206,6 +253,12 @@ fn (p mut Parser) struct_decl() {
|
||||
// `pub` access mod
|
||||
// access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
||||
p.fspace()
|
||||
defer { if is_generic_instance { p.generic_dispatch=TypeInst{} } }
|
||||
if is_generic_instance {
|
||||
p.generic_dispatch=TypeInst{
|
||||
inst: generic_types
|
||||
}
|
||||
}
|
||||
tt := p.get_type2()
|
||||
field_type := tt.name
|
||||
if field_type == name {
|
||||
@ -260,8 +313,9 @@ fn (p mut Parser) struct_decl() {
|
||||
}
|
||||
did_gen_something = true
|
||||
is_mut := access_mod in [.private_mut, .public_mut, .global]
|
||||
if p.first_pass() {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
|
||||
if p.first_pass() || is_generic {
|
||||
p.table.add_field(typ.name, field_name, field_type, is_mut,
|
||||
attr, access_mod)
|
||||
}
|
||||
p.fgen_nl() // newline between struct fields
|
||||
}
|
||||
@ -278,13 +332,34 @@ fn (p mut Parser) struct_decl() {
|
||||
// p.fgenln('//kek')
|
||||
}
|
||||
// `User{ foo: bar }`
|
||||
fn (p mut Parser) struct_init(typ string) string {
|
||||
fn (p mut Parser) struct_init(typ_ string) string {
|
||||
p.is_struct_init = true
|
||||
t := p.table.find_type(typ)
|
||||
mut typ := typ_
|
||||
mut 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) {
|
||||
|
||||
// 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
|
||||
if p.tok != .comma { break }
|
||||
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) {
|
||||
return typ
|
||||
}
|
||||
ptr := typ.contains('*')
|
||||
@ -416,3 +491,41 @@ fn (p mut Parser) struct_init(typ string) string {
|
||||
return typ
|
||||
}
|
||||
|
||||
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
|
||||
for _,v in p.table.generic_struct_params[t.name] {
|
||||
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)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user