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:
parent
b3a402eb82
commit
fbd9fedbfb
@ -401,7 +401,7 @@ fn (v &V) type_definitions() string {
|
|||||||
}
|
}
|
||||||
// everything except builtin will get sorted
|
// everything except builtin will get sorted
|
||||||
for t_name, t in v.table.typesmap {
|
for t_name, t in v.table.typesmap {
|
||||||
if t_name in builtins {
|
if t_name in builtins || t.is_generic {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
types << t
|
types << t
|
||||||
|
@ -214,8 +214,8 @@ fn (p mut Parser) name_expr() string {
|
|||||||
p.error('cannot use `_` as value')
|
p.error('cannot use `_` as value')
|
||||||
}
|
}
|
||||||
// generic type check
|
// generic type check
|
||||||
if name in p.cur_fn.dispatch_of.inst.keys() {
|
if name in p.generic_dispatch.inst.keys() {
|
||||||
name = p.cur_fn.dispatch_of.inst[name]
|
name = p.generic_dispatch.inst[name]
|
||||||
}
|
}
|
||||||
// Raw string (`s := r'hello \n ')
|
// Raw string (`s := r'hello \n ')
|
||||||
if name == 'r' && p.peek() == .str && p.prev_tok != .str_dollar {
|
if name == 'r' && p.peek() == .str && p.prev_tok != .str_dollar {
|
||||||
@ -349,7 +349,7 @@ fn (p mut Parser) name_expr() string {
|
|||||||
return enum_type.name
|
return enum_type.name
|
||||||
}
|
}
|
||||||
// normal struct init (non-C)
|
// normal struct init (non-C)
|
||||||
else if p.peek() == .lcbr {
|
else if p.peek() == .lcbr || p.peek() == .lt {
|
||||||
return p.get_struct_type(name, false, ptr)
|
return p.get_struct_type(name, false, ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ mut:
|
|||||||
defer_text []string
|
defer_text []string
|
||||||
type_pars []string
|
type_pars []string
|
||||||
type_inst []TypeInst
|
type_inst []TypeInst
|
||||||
dispatch_of TypeInst // current type inst of this generic instance
|
|
||||||
generic_fn_idx int
|
generic_fn_idx int
|
||||||
parser_idx int
|
parser_idx int
|
||||||
fn_name_token_idx int // used by error reporting
|
fn_name_token_idx int // used by error reporting
|
||||||
@ -349,8 +348,7 @@ fn (p mut Parser) fn_decl() {
|
|||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
// instance (dispatch)
|
// instance (dispatch)
|
||||||
if p.generic_dispatch.inst.size > 0 {
|
if p.generic_dispatch.inst.size > 0 {
|
||||||
f.dispatch_of = p.generic_dispatch
|
rename_generic_fn_instance(mut f, &p.generic_dispatch)
|
||||||
rename_generic_fn_instance(mut f, f.dispatch_of)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
f.is_generic = true
|
f.is_generic = true
|
||||||
@ -475,8 +473,9 @@ fn (p mut Parser) fn_decl() {
|
|||||||
p.table.register_fn(f)
|
p.table.register_fn(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.set_current_fn(EmptyFn)
|
p.set_current_fn( EmptyFn )
|
||||||
p.skip_fn_body()
|
//p.skip_fn_body()
|
||||||
|
p.skip_block(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -604,6 +603,22 @@ fn (p mut Parser) fn_decl() {
|
|||||||
p.returns = false
|
p.returns = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
fn (p mut Parser) skip_block(inside_lcbr bool) {
|
||||||
|
mut cbr_depth := if inside_lcbr { 1 } else { 0 }
|
||||||
|
for {
|
||||||
|
if p.tok == .lcbr {
|
||||||
|
cbr_depth++
|
||||||
|
}
|
||||||
|
if p.tok == .rcbr {
|
||||||
|
cbr_depth--
|
||||||
|
if cbr_depth == 0 { break }
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.check(.rcbr)
|
||||||
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
// Skips the entire function's body in the first pass.
|
// Skips the entire function's body in the first pass.
|
||||||
fn (p mut Parser) skip_fn_body() {
|
fn (p mut Parser) skip_fn_body() {
|
||||||
@ -769,10 +784,16 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|||||||
}
|
}
|
||||||
f.is_used = true
|
f.is_used = true
|
||||||
cgen_name := p.table.fn_gen_name(f)
|
cgen_name := p.table.fn_gen_name(f)
|
||||||
p.next() // fn name
|
p.next() // fn name
|
||||||
|
mut generic_param_types := []string
|
||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
mut i := p.token_idx
|
p.check(.lt)
|
||||||
|
mut i := 0
|
||||||
for {
|
for {
|
||||||
|
param_type := p.check_name()
|
||||||
|
generic_param_types << param_type
|
||||||
|
if p.tok != .comma { break }
|
||||||
|
p.check(.comma)
|
||||||
if p.tokens[i].tok == .gt {
|
if p.tokens[i].tok == .gt {
|
||||||
p.error('explicit type arguments are not allowed; remove `<...>`')
|
p.error('explicit type arguments are not allowed; remove `<...>`')
|
||||||
}
|
}
|
||||||
@ -782,6 +803,17 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
p.check(.gt)
|
||||||
|
// mut i := p.token_idx
|
||||||
|
// for {
|
||||||
|
// if p.tokens[i].tok == .gt {
|
||||||
|
// //p.error('explicit type arguments are not allowed; remove `<...>`')
|
||||||
|
// } else if p.tokens[i].tok == .lpar {
|
||||||
|
// // probably a typo, do not concern the user with the above error message
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// i++
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
// if p.pref.is_prof {
|
// if p.pref.is_prof {
|
||||||
// p.cur_fn.called_fns << cgen_name
|
// p.cur_fn.called_fns << cgen_name
|
||||||
@ -838,7 +870,7 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s
|
|||||||
// if f is generic, the name is changed to a suitable instance in dispatch_generic_fn_instance()
|
// if f is generic, the name is changed to a suitable instance in dispatch_generic_fn_instance()
|
||||||
// we then replace `cgen_name` with the instance's name
|
// we then replace `cgen_name` with the instance's name
|
||||||
generic := f.is_generic
|
generic := f.is_generic
|
||||||
p.fn_call_args(mut f)
|
p.fn_call_args(mut f, generic_param_types)
|
||||||
if generic {
|
if generic {
|
||||||
line := if p.cgen.is_tmp { p.cgen.tmp_line } else { p.cgen.cur_line }
|
line := if p.cgen.is_tmp { p.cgen.tmp_line } else { p.cgen.cur_line }
|
||||||
p.cgen.resetln(line.replace('$cgen_name (', '$f.name ('))
|
p.cgen.resetln(line.replace('$cgen_name (', '$f.name ('))
|
||||||
@ -975,7 +1007,7 @@ fn (p mut Parser) fn_args(f mut Fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// foo *(1, 2, 3, mut bar)*
|
// foo *(1, 2, 3, mut bar)*
|
||||||
fn (p mut Parser) fn_call_args(f mut Fn) {
|
fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) {
|
||||||
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||||
// C func. # of args is not known
|
// C func. # of args is not known
|
||||||
p.check(.lpar)
|
p.check(.lpar)
|
||||||
@ -1007,7 +1039,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
|||||||
file_path := cescaped_path(p.file_path)
|
file_path := cescaped_path(p.file_path)
|
||||||
p.cgen.resetln(p.cgen.cur_line.replace('v_panic (', 'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '))
|
p.cgen.resetln(p.cgen.cur_line.replace('v_panic (', 'panic_debug ($p.scanner.line_nr, tos3("$file_path"), tos3("$mod_name"), tos2((byte *)"$fn_name"), '))
|
||||||
}
|
}
|
||||||
mut saved_args := []string
|
//mut saved_args := []string
|
||||||
|
mut saved_args := generic_param_types
|
||||||
for i, arg in f.args {
|
for i, arg in f.args {
|
||||||
// Receiver is the first arg
|
// Receiver is the first arg
|
||||||
// Skip the receiver, because it was already generated in the expression
|
// Skip the receiver, because it was already generated in the expression
|
||||||
@ -1382,6 +1415,10 @@ fn replace_generic_type_params(f mut Fn, ti &TypeInst) {
|
|||||||
}
|
}
|
||||||
f.args = args
|
f.args = args
|
||||||
f.typ = replace_generic_type(f.typ, ti)
|
f.typ = replace_generic_type(f.typ, ti)
|
||||||
|
if f.typ.ends_with('_T') {
|
||||||
|
par := ti.inst.keys()[0]
|
||||||
|
f.typ = f.typ + '_' + ti.inst[par]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) register_vargs_stuct(typ string, len int) string {
|
fn (p mut Parser) register_vargs_stuct(typ string, len int) string {
|
||||||
@ -1483,9 +1520,6 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rename_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
fn rename_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||||
if f.is_method && f.dispatch_of.inst.size == 0 {
|
|
||||||
f.name = f.receiver_typ + '_' + f.name
|
|
||||||
}
|
|
||||||
f.name = f.name + '_T'
|
f.name = f.name + '_T'
|
||||||
for k in ti.inst.keys() {
|
for k in ti.inst.keys() {
|
||||||
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
|
f.name = f.name + '_' + type_to_safe_str(ti.inst[k])
|
||||||
@ -1512,9 +1546,9 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
|||||||
}
|
}
|
||||||
f.type_inst << *ti
|
f.type_inst << *ti
|
||||||
p.table.register_fn(f)
|
p.table.register_fn(f)
|
||||||
|
if f.is_method { f.name = f.receiver_typ + '_' + f.name }
|
||||||
rename_generic_fn_instance(mut f, ti)
|
rename_generic_fn_instance(mut f, ti)
|
||||||
replace_generic_type_params(mut f, ti)
|
replace_generic_type_params(mut f, ti)
|
||||||
f.dispatch_of = *ti
|
|
||||||
// TODO: Handle case where type not defined yet, see above
|
// TODO: Handle case where type not defined yet, see above
|
||||||
// if f.typ in f.type_pars { f.typ = '_ANYTYPE_' }
|
// if f.typ in f.type_pars { f.typ = '_ANYTYPE_' }
|
||||||
// if f.typ in ti.inst {
|
// if f.typ in ti.inst {
|
||||||
@ -1529,12 +1563,13 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
|||||||
}
|
}
|
||||||
mut gp := p.v.parsers[f.parser_idx]
|
mut gp := p.v.parsers[f.parser_idx]
|
||||||
gp.is_vgen = true
|
gp.is_vgen = true
|
||||||
gp.generic_dispatch = *ti
|
|
||||||
saved_state := p.save_state()
|
saved_state := p.save_state()
|
||||||
p.clear_state(false, true)
|
p.clear_state(false, true)
|
||||||
gp.token_idx = f.generic_fn_idx
|
gp.token_idx = f.generic_fn_idx
|
||||||
|
gp.generic_dispatch = *ti
|
||||||
gp.next()
|
gp.next()
|
||||||
gp.fn_decl()
|
gp.fn_decl()
|
||||||
|
gp.generic_dispatch = TypeInst{}
|
||||||
p.cgen.lines_extra << p.cgen.lines
|
p.cgen.lines_extra << p.cgen.lines
|
||||||
p.restore_state(saved_state, false, true)
|
p.restore_state(saved_state, false, true)
|
||||||
p.cgen.fns << '${p.fn_signature(f)};'
|
p.cgen.fns << '${p.fn_signature(f)};'
|
||||||
|
@ -472,7 +472,7 @@ fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool, fn_ph, assign_p
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns true in case of an early return
|
// returns true in case of an early return
|
||||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
fn (p mut Parser) gen_struct_init(typ string, t &Type) bool {
|
||||||
// TODO hack. If it's a C type, we may need to add "struct" before declaration:
|
// TODO hack. If it's a C type, we may need to add "struct" before declaration:
|
||||||
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
|
// a := &C.A{} ==> struct A* a = malloc(sizeof(struct A));
|
||||||
if p.is_c_struct_init {
|
if p.is_c_struct_init {
|
||||||
|
@ -163,7 +163,7 @@ fn (p mut Parser) gen_array_set(typ string, is_ptr, is_map bool,fn_ph, assign_po
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns true in case of an early return
|
// returns true in case of an early return
|
||||||
fn (p mut Parser) gen_struct_init(typ string, t Type) bool {
|
fn (p mut Parser) gen_struct_init(typ string, t &Type) bool {
|
||||||
p.next()
|
p.next()
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
ptr := typ.contains('*')
|
ptr := typ.contains('*')
|
||||||
|
@ -125,7 +125,7 @@ fn (p mut Parser) get_type2() Type {
|
|||||||
p.check(.amp)
|
p.check(.amp)
|
||||||
}
|
}
|
||||||
// generic type check
|
// generic type check
|
||||||
ti := p.cur_fn.dispatch_of.inst
|
ti := p.generic_dispatch.inst
|
||||||
if p.lit in ti.keys() {
|
if p.lit in ti.keys() {
|
||||||
typ += ti[p.lit]
|
typ += ti[p.lit]
|
||||||
}
|
}
|
||||||
|
@ -154,14 +154,17 @@ pub fn (v &V) finalize_compilation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (v mut V) add_parser(parser Parser) int {
|
pub fn (v mut V) add_parser(parser Parser) int {
|
||||||
|
pidx := v.parsers.len
|
||||||
v.parsers << parser
|
v.parsers << parser
|
||||||
pidx := v.parsers.len - 1
|
file_path := if filepath.is_abs(parser.file_path) {
|
||||||
v.file_parser_idx[os.realpath(parser.file_path)] = pidx
|
parser.file_path } else { os.realpath(parser.file_path) }
|
||||||
|
v.file_parser_idx[file_path] = pidx
|
||||||
return pidx
|
return pidx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (v &V) get_file_parser_index(file string) ?int {
|
pub fn (v &V) get_file_parser_index(file string) ?int {
|
||||||
file_path := os.realpath(file)
|
file_path := if filepath.is_abs(file) {
|
||||||
|
file } else { os.realpath(file) }
|
||||||
if file_path in v.file_parser_idx {
|
if file_path in v.file_parser_idx {
|
||||||
return v.file_parser_idx[file_path]
|
return v.file_parser_idx[file_path]
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ fn (p mut Parser) parse(pass Pass) {
|
|||||||
p.const_decl()
|
p.const_decl()
|
||||||
}
|
}
|
||||||
.key_struct, .key_union, .key_interface {
|
.key_struct, .key_union, .key_interface {
|
||||||
p.struct_decl()
|
p.struct_decl([])
|
||||||
}
|
}
|
||||||
.key_enum {
|
.key_enum {
|
||||||
p.enum_decl(false)
|
p.enum_decl(false)
|
||||||
@ -497,7 +497,7 @@ fn (p mut Parser) parse(pass Pass) {
|
|||||||
p.attribute()
|
p.attribute()
|
||||||
}
|
}
|
||||||
.key_struct, .key_interface, .key_union, .lsbr {
|
.key_struct, .key_interface, .key_union, .lsbr {
|
||||||
p.struct_decl()
|
p.struct_decl([])
|
||||||
}
|
}
|
||||||
.key_const {
|
.key_const {
|
||||||
p.const_decl()
|
p.const_decl()
|
||||||
@ -1035,7 +1035,7 @@ fn (p mut Parser) get_type() string {
|
|||||||
p.check(.amp)
|
p.check(.amp)
|
||||||
}
|
}
|
||||||
// generic type check
|
// generic type check
|
||||||
ti := p.cur_fn.dispatch_of.inst
|
ti := p.generic_dispatch.inst
|
||||||
if p.lit in ti.keys() {
|
if p.lit in ti.keys() {
|
||||||
typ += ti[p.lit]
|
typ += ti[p.lit]
|
||||||
}
|
}
|
||||||
@ -1090,6 +1090,25 @@ fn (p mut Parser) get_type() string {
|
|||||||
p.error('type `$t.name` is private')
|
p.error('type `$t.name` is private')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// generic struct
|
||||||
|
if p.peek() == .lt {
|
||||||
|
p.next()
|
||||||
|
p.check(.lt)
|
||||||
|
typ = '${typ}_T'
|
||||||
|
mut type_params := []string
|
||||||
|
for p.tok != .gt {
|
||||||
|
type_param := p.check_name()
|
||||||
|
type_params << type_param
|
||||||
|
if p.generic_dispatch.inst.size > 0 {
|
||||||
|
if type_param in p.generic_dispatch.inst {
|
||||||
|
typ = '${typ}_' + p.generic_dispatch.inst[type_param]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.tok == .comma { p.check(.comma) }
|
||||||
|
}
|
||||||
|
p.check(.gt)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
if typ == 'void' {
|
if typ == 'void' {
|
||||||
p.error('unknown type `$typ`')
|
p.error('unknown type `$typ`')
|
||||||
}
|
}
|
||||||
@ -1829,7 +1848,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
||||||
T := p.table.find_type(p.base_type(typ))
|
T := p.table.find_type(p.base_type(typ))
|
||||||
p.gen('(')
|
p.gen('(')
|
||||||
p.fn_call_args(mut T.func)
|
p.fn_call_args(mut T.func, [])
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
typ = T.func.typ
|
typ = T.func.typ
|
||||||
}
|
}
|
||||||
@ -1839,7 +1858,7 @@ fn (p mut Parser) var_expr(v Var) string {
|
|||||||
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
if p.base_type(typ).starts_with('fn ') && p.tok == .lpar {
|
||||||
T := p.table.find_type(p.base_type(typ))
|
T := p.table.find_type(p.base_type(typ))
|
||||||
p.gen('(')
|
p.gen('(')
|
||||||
p.fn_call_args(mut T.func)
|
p.fn_call_args(mut T.func, [])
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
typ = T.func.typ
|
typ = T.func.typ
|
||||||
}
|
}
|
||||||
@ -2014,7 +2033,7 @@ pub:
|
|||||||
p.gen('$dot$field.name')
|
p.gen('$dot$field.name')
|
||||||
p.gen('(')
|
p.gen('(')
|
||||||
p.check(.name)
|
p.check(.name)
|
||||||
p.fn_call_args(mut f)
|
p.fn_call_args(mut f, [])
|
||||||
p.gen(')')
|
p.gen(')')
|
||||||
return f.typ
|
return f.typ
|
||||||
}
|
}
|
||||||
@ -2915,7 +2934,7 @@ fn (p mut Parser) attribute() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
else if p.tok == .key_struct {
|
else if p.tok == .key_struct {
|
||||||
p.struct_decl()
|
p.struct_decl([])
|
||||||
p.attr = ''
|
p.attr = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,8 @@ import (
|
|||||||
strings
|
strings
|
||||||
)
|
)
|
||||||
// also unions and interfaces
|
// also unions and interfaces
|
||||||
|
fn (p mut Parser) struct_decl(generic_param_types []string) {
|
||||||
|
decl_tok_idx := p.cur_tok_index()
|
||||||
fn (p mut Parser) struct_decl() {
|
|
||||||
is_pub := p.tok == .key_pub
|
is_pub := p.tok == .key_pub
|
||||||
if is_pub {
|
if is_pub {
|
||||||
p.next()
|
p.next()
|
||||||
@ -40,6 +39,31 @@ fn (p mut Parser) struct_decl() {
|
|||||||
if is_interface && !name.ends_with('er') {
|
if is_interface && !name.ends_with('er') {
|
||||||
p.error('interface names temporarily have to end with `er` (e.g. `Speaker`, `Reader`)')
|
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
|
is_c := name == 'C' && p.tok == .dot
|
||||||
if is_c {
|
if is_c {
|
||||||
/*
|
/*
|
||||||
@ -81,6 +105,11 @@ fn (p mut Parser) struct_decl() {
|
|||||||
kind := if is_union { 'union' } else { 'struct' }
|
kind := if is_union { 'union' } else { 'struct' }
|
||||||
p.gen_typedef('typedef $kind $name $name;')
|
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
|
// Register the type
|
||||||
mut is_ph := false
|
mut is_ph := false
|
||||||
if typ.is_placeholder {
|
if typ.is_placeholder {
|
||||||
@ -93,6 +122,9 @@ fn (p mut Parser) struct_decl() {
|
|||||||
typ.cat = cat
|
typ.cat = cat
|
||||||
typ.parent = objc_parent
|
typ.parent = objc_parent
|
||||||
typ.is_public = is_pub || p.is_vh
|
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)
|
p.table.rewrite_type(typ)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -103,6 +135,9 @@ fn (p mut Parser) struct_decl() {
|
|||||||
cat: cat
|
cat: cat
|
||||||
parent: objc_parent
|
parent: objc_parent
|
||||||
is_public: is_pub || p.is_vh
|
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
|
// Struct `C.Foo` declaration, no body
|
||||||
@ -110,6 +145,18 @@ fn (p mut Parser) struct_decl() {
|
|||||||
p.table.register_type(typ)
|
p.table.register_type(typ)
|
||||||
return
|
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.fspace()
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
// Struct fields
|
// Struct fields
|
||||||
@ -119,7 +166,7 @@ fn (p mut Parser) struct_decl() {
|
|||||||
mut names := []string // to avoid dup names TODO alloc perf
|
mut names := []string // to avoid dup names TODO alloc perf
|
||||||
mut fmt_max_len := p.table.max_field_len[name]
|
mut fmt_max_len := p.table.max_field_len[name]
|
||||||
// println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass')
|
// 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)
|
p.table.register_type(typ)
|
||||||
// println('registering 1 nrfields=$typ.fields.len')
|
// println('registering 1 nrfields=$typ.fields.len')
|
||||||
}
|
}
|
||||||
@ -206,6 +253,12 @@ fn (p mut Parser) struct_decl() {
|
|||||||
// `pub` access mod
|
// `pub` access mod
|
||||||
// access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
// access_mod := if is_pub_field { AccessMod.public } else { AccessMod.private}
|
||||||
p.fspace()
|
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()
|
tt := p.get_type2()
|
||||||
field_type := tt.name
|
field_type := tt.name
|
||||||
if field_type == name {
|
if field_type == name {
|
||||||
@ -260,8 +313,9 @@ fn (p mut Parser) struct_decl() {
|
|||||||
}
|
}
|
||||||
did_gen_something = true
|
did_gen_something = true
|
||||||
is_mut := access_mod in [.private_mut, .public_mut, .global]
|
is_mut := access_mod in [.private_mut, .public_mut, .global]
|
||||||
if p.first_pass() {
|
if p.first_pass() || is_generic {
|
||||||
p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod)
|
p.table.add_field(typ.name, field_name, field_type, is_mut,
|
||||||
|
attr, access_mod)
|
||||||
}
|
}
|
||||||
p.fgen_nl() // newline between struct fields
|
p.fgen_nl() // newline between struct fields
|
||||||
}
|
}
|
||||||
@ -278,13 +332,34 @@ fn (p mut Parser) struct_decl() {
|
|||||||
// p.fgenln('//kek')
|
// p.fgenln('//kek')
|
||||||
}
|
}
|
||||||
// `User{ foo: bar }`
|
// `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
|
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 {
|
if !t.is_public && t.mod != p.mod {
|
||||||
p.warn('type `$t.name` is private')
|
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
|
return typ
|
||||||
}
|
}
|
||||||
ptr := typ.contains('*')
|
ptr := typ.contains('*')
|
||||||
@ -416,3 +491,41 @@ fn (p mut Parser) struct_init(typ string) string {
|
|||||||
return typ
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ pub mut:
|
|||||||
// enum_vals map[string][]string
|
// enum_vals map[string][]string
|
||||||
// names []Name
|
// names []Name
|
||||||
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
|
max_field_len map[string]int // for vfmt: max_field_len['Parser'] == 12
|
||||||
|
generic_struct_params map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VargAccess {
|
struct VargAccess {
|
||||||
@ -114,6 +115,8 @@ pub mut:
|
|||||||
enum_vals []string
|
enum_vals []string
|
||||||
gen_types []string
|
gen_types []string
|
||||||
default_vals []string // `struct Foo { bar int = 2 }`
|
default_vals []string // `struct Foo { bar int = 2 }`
|
||||||
|
parser_idx int
|
||||||
|
decl_tok_idx int
|
||||||
// `is_placeholder` is used for types that are not defined yet but are known to exist.
|
// `is_placeholder` is used for types that are not defined yet but are known to exist.
|
||||||
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
||||||
// This information is needed in the first pass.
|
// This information is needed in the first pass.
|
||||||
@ -121,6 +124,7 @@ pub mut:
|
|||||||
gen_str bool // needs `.str()` method generation
|
gen_str bool // needs `.str()` method generation
|
||||||
is_flag bool // enum bitfield flag
|
is_flag bool // enum bitfield flag
|
||||||
// max_field_len int
|
// max_field_len int
|
||||||
|
is_generic bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TypeNode {
|
struct TypeNode {
|
||||||
|
@ -98,3 +98,34 @@ fn test_generic_fn_in_for_in_expression() {
|
|||||||
assert value == 'a'
|
assert value == 'a'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test generic struct
|
||||||
|
struct DB {
|
||||||
|
driver string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct User {
|
||||||
|
db DB
|
||||||
|
mut:
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Repo<T> {
|
||||||
|
db DB
|
||||||
|
mut:
|
||||||
|
model T
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_repo<U>(db DB) Repo<U> {
|
||||||
|
return Repo<U>{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generic_struct() {
|
||||||
|
mut a := new_repo<User>(DB{})
|
||||||
|
a.model.name = 'joe'
|
||||||
|
mut b := Repo<User>{db: DB{}}
|
||||||
|
b.model.name = 'joe'
|
||||||
|
assert a.model.name == 'joe'
|
||||||
|
assert b.model.name == 'joe'
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user