generics: fix errors & simplify
parent
82d4a731f3
commit
11aaee685a
|
@ -16,6 +16,7 @@ struct CGen {
|
||||||
is_user bool
|
is_user bool
|
||||||
mut:
|
mut:
|
||||||
lines []string
|
lines []string
|
||||||
|
lines_extra []string
|
||||||
typedefs []string
|
typedefs []string
|
||||||
type_aliases []string
|
type_aliases []string
|
||||||
includes []string
|
includes []string
|
||||||
|
@ -102,6 +103,7 @@ fn (g mut CGen) resetln(s string) {
|
||||||
fn (g mut CGen) save() {
|
fn (g mut CGen) save() {
|
||||||
s := g.lines.join('\n')
|
s := g.lines.join('\n')
|
||||||
g.out.writeln(s)
|
g.out.writeln(s)
|
||||||
|
g.out.writeln(g.lines_extra.join('\n'))
|
||||||
g.out.close()
|
g.out.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,12 @@ fn (p mut Parser) name_expr() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
mut name := p.lit
|
mut name := p.lit
|
||||||
|
|
||||||
|
// generic type check
|
||||||
|
if name in p.cur_fn.dispatch_of.inst.keys() {
|
||||||
|
name = p.cur_fn.dispatch_of.inst[name]
|
||||||
|
}
|
||||||
|
|
||||||
// Raw string (`s := r'hello \n ')
|
// Raw string (`s := r'hello \n ')
|
||||||
if (name == 'r' || name == 'c') && p.peek() == .str && p.prev_tok != .dollar {
|
if (name == 'r' || name == 'c') && p.peek() == .str && p.prev_tok != .dollar {
|
||||||
p.string_expr()
|
p.string_expr()
|
||||||
|
|
|
@ -40,7 +40,8 @@ mut:
|
||||||
type_pars []string
|
type_pars []string
|
||||||
type_inst []TypeInst
|
type_inst []TypeInst
|
||||||
dispatch_of TypeInst // current type inst of this generic instance
|
dispatch_of TypeInst // current type inst of this generic instance
|
||||||
generic_tmpl []Token
|
generic_fn_idx int
|
||||||
|
parser_idx int
|
||||||
fn_name_token_idx int // used by error reporting
|
fn_name_token_idx int // used by error reporting
|
||||||
comptime_define string
|
comptime_define string
|
||||||
is_used bool // so that we can skip unused fns in resulting C code
|
is_used bool // so that we can skip unused fns in resulting C code
|
||||||
|
@ -195,6 +196,7 @@ fn (p mut Parser) clear_vars() {
|
||||||
fn (p mut Parser) fn_decl() {
|
fn (p mut Parser) fn_decl() {
|
||||||
p.clear_vars() // clear local vars every time a new fn is started
|
p.clear_vars() // clear local vars every time a new fn is started
|
||||||
defer { p.fgenln('\n') }
|
defer { p.fgenln('\n') }
|
||||||
|
fn_start_idx := p.cur_tok_index()
|
||||||
// If we are in the first pass, create a new function.
|
// If we are in the first pass, create a new function.
|
||||||
// In the second pass fetch the one we created.
|
// In the second pass fetch the one we created.
|
||||||
/*
|
/*
|
||||||
|
@ -343,7 +345,13 @@ fn (p mut Parser) fn_decl() {
|
||||||
}
|
}
|
||||||
// Generic?
|
// Generic?
|
||||||
if p.tok == .lt {
|
if p.tok == .lt {
|
||||||
f.is_generic = true
|
// instance (dispatch)
|
||||||
|
if p.generic_dispatch.inst.size > 0 {
|
||||||
|
f.dispatch_of = p.generic_dispatch
|
||||||
|
rename_generic_fn_instance(mut f, f.dispatch_of)
|
||||||
|
} else {
|
||||||
|
f.is_generic = true
|
||||||
|
}
|
||||||
p.next()
|
p.next()
|
||||||
for {
|
for {
|
||||||
type_par := p.check_name()
|
type_par := p.check_name()
|
||||||
|
@ -357,8 +365,8 @@ fn (p mut Parser) fn_decl() {
|
||||||
if p.tok == .gt { break }
|
if p.tok == .gt { break }
|
||||||
p.check(.comma)
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
p.set_current_fn(f)
|
|
||||||
p.check(.gt)
|
p.check(.gt)
|
||||||
|
p.set_current_fn(f)
|
||||||
}
|
}
|
||||||
// Args (...)
|
// Args (...)
|
||||||
p.fn_args(mut f)
|
p.fn_args(mut f)
|
||||||
|
@ -419,7 +427,13 @@ fn (p mut Parser) fn_decl() {
|
||||||
// Generic functions are inserted as needed from the call site
|
// Generic functions are inserted as needed from the call site
|
||||||
if f.is_generic {
|
if f.is_generic {
|
||||||
if p.first_pass() {
|
if p.first_pass() {
|
||||||
p.save_generic_tmpl(mut f, p.cur_tok_index())
|
if !p.scanner.is_vh {
|
||||||
|
gpidx := p.v.get_file_parser_index(p.file_path) or {
|
||||||
|
panic('error finding parser for: $p.file_path')
|
||||||
|
}
|
||||||
|
f.parser_idx = gpidx
|
||||||
|
}
|
||||||
|
f.generic_fn_idx = fn_start_idx
|
||||||
if f.is_method {
|
if f.is_method {
|
||||||
rcv := p.table.find_type(receiver_typ)
|
rcv := p.table.find_type(receiver_typ)
|
||||||
if p.first_pass() && rcv.name == '' {
|
if p.first_pass() && rcv.name == '' {
|
||||||
|
@ -1015,8 +1029,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.expected_type = arg.typ
|
p.expected_type = arg.typ
|
||||||
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' &&
|
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' &&
|
||||||
!p.builtin_mod //&& arg.is_moved
|
!p.builtin_mod //&& arg.is_moved
|
||||||
if clone {
|
if clone {
|
||||||
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
|
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
|
||||||
}
|
}
|
||||||
|
@ -1410,38 +1424,8 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the tokens for the generic funciton body (between `{}`)
|
|
||||||
// the function signature isn't saved, it is reconstructed from Fn
|
|
||||||
fn (p mut Parser) save_generic_tmpl(f mut Fn, pos int) {
|
|
||||||
mut cbr_depth := 1
|
|
||||||
mut tokens := []Token
|
|
||||||
for i in pos..p.tokens.len-1 {
|
|
||||||
tok := p.tokens[i]
|
|
||||||
if tok.tok == .lcbr { cbr_depth++ }
|
|
||||||
if tok.tok == .rcbr {
|
|
||||||
cbr_depth--
|
|
||||||
if cbr_depth == 0 { break }
|
|
||||||
}
|
|
||||||
tokens << tok
|
|
||||||
}
|
|
||||||
f.generic_tmpl = tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace generic types in function body template with types from TypeInst
|
|
||||||
fn (f &Fn) generic_tmpl_to_inst(ti &TypeInst) string {
|
|
||||||
mut fn_body := ''
|
|
||||||
for tok in f.generic_tmpl {
|
|
||||||
mut tok_str := tok.str()
|
|
||||||
if tok.tok == .name && tok_str in ti.inst {
|
|
||||||
tok_str = ti.inst[tok_str]
|
|
||||||
}
|
|
||||||
fn_body += ' $tok_str'
|
|
||||||
}
|
|
||||||
return fn_body
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rename_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
fn rename_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||||
if f.is_method {
|
if f.is_method && f.dispatch_of.inst.size == 0 {
|
||||||
f.name = f.receiver_typ + '_' + f.name
|
f.name = f.receiver_typ + '_' + f.name
|
||||||
}
|
}
|
||||||
f.name = f.name + '_T'
|
f.name = f.name + '_T'
|
||||||
|
@ -1469,7 +1453,6 @@ 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)
|
||||||
|
|
||||||
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)
|
||||||
// TODO: save dispatch info when update to incremental parsing
|
// TODO: save dispatch info when update to incremental parsing
|
||||||
|
@ -1485,18 +1468,17 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti &TypeInst) {
|
||||||
} else {
|
} else {
|
||||||
p.table.register_fn(f)
|
p.table.register_fn(f)
|
||||||
}
|
}
|
||||||
mut fn_code := '${p.fn_signature_v(f)} {\n${f.generic_tmpl_to_inst(ti)}\n}'
|
mut gp := p.v.parsers[f.parser_idx]
|
||||||
// TODO: parse incrementally as needed & set typeinst
|
gp.is_vgen = true
|
||||||
if f.mod in p.v.gen_parser_idx {
|
gp.generic_dispatch = *ti
|
||||||
pidx := p.v.gen_parser_idx[f.mod]
|
saved_state := p.save_state()
|
||||||
p.v.parsers[pidx].add_text(fn_code)
|
p.clear_state(false, true)
|
||||||
for mod in p.table.imports {
|
gp.token_idx = f.generic_fn_idx
|
||||||
if p.v.parsers[pidx].import_table.known_import(mod) { continue }
|
gp.next()
|
||||||
p.v.parsers[pidx].register_import(mod, 0)
|
gp.fn_decl()
|
||||||
}
|
p.cgen.lines_extra << p.cgen.lines
|
||||||
} else {
|
p.restore_state(saved_state, false, true)
|
||||||
// TODO: add here after I work out bug
|
|
||||||
}
|
|
||||||
p.cgen.fns << '${p.fn_signature(f)};'
|
p.cgen.fns << '${p.fn_signature(f)};'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,20 +1538,6 @@ fn (f &Fn) str_args(table &Table) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (f &Fn) str_args_v(table &Table) string {
|
|
||||||
mut str_args := ''
|
|
||||||
for i, arg in f.args {
|
|
||||||
if f.is_method && i == 0 { continue }
|
|
||||||
mut arg_typ := arg.typ.replace('array_', '[]').replace('map_', 'map[string]')
|
|
||||||
if arg_typ == 'void*' { arg_typ = 'voidptr' } else if arg_typ == 'byte*' { arg_typ = 'byteptr' }
|
|
||||||
if arg.is_mut { arg_typ = 'mut '+arg_typ.trim('*') }
|
|
||||||
else if arg_typ.ends_with('*') || arg.ptr { arg_typ = '&'+arg_typ.trim_right('*') }
|
|
||||||
str_args += '$arg.name $arg_typ'
|
|
||||||
if i < f.args.len-1 { str_args += ','}
|
|
||||||
}
|
|
||||||
return str_args
|
|
||||||
}
|
|
||||||
|
|
||||||
// find local function variable with closest name to `name`
|
// find local function variable with closest name to `name`
|
||||||
fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string {
|
fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string {
|
||||||
mut closest := f32(0)
|
mut closest := f32(0)
|
||||||
|
@ -1601,24 +1569,6 @@ fn (p &Parser) fn_signature(f &Fn) string {
|
||||||
return '$f.typ $f.name(${f.str_args(p.table)})'
|
return '$f.typ $f.name(${f.str_args(p.table)})'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p &Parser) fn_signature_v(f &Fn) string {
|
|
||||||
mut method := ''
|
|
||||||
mut f_name := f.name.all_after('__')
|
|
||||||
if f.is_method {
|
|
||||||
receiver_arg := f.args[0]
|
|
||||||
receiver_type := receiver_arg.typ.trim('*')
|
|
||||||
f_name = f_name.all_after('${receiver_type}_')
|
|
||||||
mut rcv_typ := receiver_arg.typ.replace('array_', '[]').replace('map_', 'map[string]')
|
|
||||||
if receiver_arg.is_mut { rcv_typ = 'mut '+rcv_typ.trim('*') }
|
|
||||||
else if rcv_typ.ends_with('*') || receiver_arg.ptr { rcv_typ = '&'+rcv_typ.trim_right('&*') }
|
|
||||||
method = '($receiver_arg.name $rcv_typ) '
|
|
||||||
}
|
|
||||||
vis := if f.is_public { 'pub ' } else { '' }
|
|
||||||
f_type := if f.typ == 'void' { '' } else if f.typ == 'void*' { 'voidptr' }
|
|
||||||
else if f.typ == 'byte*' { 'byteptr' } else { f.typ }
|
|
||||||
return '${vis}fn $method$f_name(${f.str_args_v(p.table)}) $f_type'
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (f &Fn) v_fn_module() string {
|
pub fn (f &Fn) v_fn_module() string {
|
||||||
return f.mod
|
return f.mod
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,12 +302,8 @@ pub fn (v mut V) compile() {
|
||||||
// free the string builder which held the generated methods
|
// free the string builder which held the generated methods
|
||||||
v.vgen_buf.free()
|
v.vgen_buf.free()
|
||||||
vgen_parser.is_vgen = true
|
vgen_parser.is_vgen = true
|
||||||
v.add_parser(vgen_parser)
|
// v.add_parser(vgen_parser)
|
||||||
// run vgen / generic parsers
|
vgen_parser.parse(.main)
|
||||||
for i, _ in v.parsers {
|
|
||||||
if !v.parsers[i].is_vgen { continue }
|
|
||||||
v.parsers[i].parse(.main)
|
|
||||||
}
|
|
||||||
// Generate .vh if we are building a module
|
// Generate .vh if we are building a module
|
||||||
if v.pref.build_mode == .build_module {
|
if v.pref.build_mode == .build_module {
|
||||||
generate_vh(v.dir)
|
generate_vh(v.dir)
|
||||||
|
@ -664,13 +660,6 @@ pub fn (v mut V) add_v_files_to_compile() {
|
||||||
// resolve deps and add imports in correct order
|
// resolve deps and add imports in correct order
|
||||||
imported_mods := v.resolve_deps().imports()
|
imported_mods := v.resolve_deps().imports()
|
||||||
for mod in imported_mods {
|
for mod in imported_mods {
|
||||||
// TODO: work out bug and only add when needed in fn.v
|
|
||||||
if !mod in v.gen_parser_idx {
|
|
||||||
mut gp := v.new_parser_from_string('module '+mod.all_after('.')+'\n')
|
|
||||||
gp.is_vgen = true
|
|
||||||
gp.mod = mod
|
|
||||||
v.gen_parser_idx[mod] = v.add_parser(gp)
|
|
||||||
}
|
|
||||||
if mod == 'builtin' || mod == 'main' {
|
if mod == 'builtin' || mod == 'main' {
|
||||||
// builtin already added
|
// builtin already added
|
||||||
// main files will get added last
|
// main files will get added last
|
||||||
|
|
|
@ -74,6 +74,7 @@ mut:
|
||||||
sql_params []string // ("select * from users where id = $1", ***"100"***)
|
sql_params []string // ("select * from users where id = $1", ***"100"***)
|
||||||
sql_types []string // int, string and so on; see sql_params
|
sql_types []string // int, string and so on; see sql_params
|
||||||
is_vh bool // parsing .vh file (for example `const (a int)` is allowed)
|
is_vh bool // parsing .vh file (for example `const (a int)` is allowed)
|
||||||
|
generic_dispatch TypeInst
|
||||||
pub:
|
pub:
|
||||||
mod string
|
mod string
|
||||||
}
|
}
|
||||||
|
@ -94,6 +95,9 @@ struct ParserState {
|
||||||
scanner_pos int
|
scanner_pos int
|
||||||
scanner_line_ends []int
|
scanner_line_ends []int
|
||||||
scanner_nlines int
|
scanner_nlines int
|
||||||
|
cgen_lines []string
|
||||||
|
cgen_cur_line string
|
||||||
|
cgen_tmp_line string
|
||||||
tokens []Token
|
tokens []Token
|
||||||
token_idx int
|
token_idx int
|
||||||
tok TokenKind
|
tok TokenKind
|
||||||
|
@ -285,6 +289,9 @@ pub fn (p mut Parser) save_state() ParserState {
|
||||||
scanner_pos : p.scanner.pos
|
scanner_pos : p.scanner.pos
|
||||||
scanner_line_ends : p.scanner.line_ends
|
scanner_line_ends : p.scanner.line_ends
|
||||||
scanner_nlines : p.scanner.nlines
|
scanner_nlines : p.scanner.nlines
|
||||||
|
cgen_lines : p.cgen.lines
|
||||||
|
cgen_cur_line : p.cgen.cur_line
|
||||||
|
cgen_tmp_line : p.cgen.tmp_line
|
||||||
tokens : p.tokens
|
tokens : p.tokens
|
||||||
token_idx : p.token_idx
|
token_idx : p.token_idx
|
||||||
tok : p.tok
|
tok : p.tok
|
||||||
|
@ -294,12 +301,19 @@ pub fn (p mut Parser) save_state() ParserState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (p mut Parser) restore_state(state ParserState) {
|
pub fn (p mut Parser) restore_state(state ParserState, scanner bool, cgen bool) {
|
||||||
p.scanner.line_nr = state.scanner_line_nr
|
if scanner {
|
||||||
p.scanner.text = state.scanner_text
|
p.scanner.line_nr = state.scanner_line_nr
|
||||||
p.scanner.pos = state.scanner_pos
|
p.scanner.text = state.scanner_text
|
||||||
p.scanner.line_ends = state.scanner_line_ends
|
p.scanner.pos = state.scanner_pos
|
||||||
p.scanner.nlines = state.scanner_nlines
|
p.scanner.line_ends = state.scanner_line_ends
|
||||||
|
p.scanner.nlines = state.scanner_nlines
|
||||||
|
}
|
||||||
|
if cgen {
|
||||||
|
p.cgen.lines = state.cgen_lines
|
||||||
|
p.cgen.cur_line = state.cgen_cur_line
|
||||||
|
p.cgen.tmp_line = state.cgen_tmp_line
|
||||||
|
}
|
||||||
p.tokens = state.tokens
|
p.tokens = state.tokens
|
||||||
p.token_idx = state.token_idx
|
p.token_idx = state.token_idx
|
||||||
p.tok = state.tok
|
p.tok = state.tok
|
||||||
|
@ -308,12 +322,19 @@ pub fn (p mut Parser) restore_state(state ParserState) {
|
||||||
p.lit = state.lit
|
p.lit = state.lit
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) clear_state() {
|
fn (p mut Parser) clear_state(scanner bool, cgen bool) {
|
||||||
p.scanner.line_nr = 0
|
if scanner {
|
||||||
p.scanner.text = ''
|
p.scanner.line_nr = 0
|
||||||
p.scanner.pos = 0
|
p.scanner.text = ''
|
||||||
p.scanner.line_ends = []
|
p.scanner.pos = 0
|
||||||
p.scanner.nlines = 0
|
p.scanner.line_ends = []
|
||||||
|
p.scanner.nlines = 0
|
||||||
|
}
|
||||||
|
if cgen {
|
||||||
|
p.cgen.lines = []
|
||||||
|
p.cgen.cur_line = ''
|
||||||
|
p.cgen.tmp_line = ''
|
||||||
|
}
|
||||||
p.tokens = []
|
p.tokens = []
|
||||||
p.token_idx = 0
|
p.token_idx = 0
|
||||||
p.lit = ''
|
p.lit = ''
|
||||||
|
@ -329,7 +350,7 @@ pub fn (p mut Parser) add_text(text string) {
|
||||||
|
|
||||||
fn (p mut Parser) statements_from_text(text string, rcbr bool) {
|
fn (p mut Parser) statements_from_text(text string, rcbr bool) {
|
||||||
saved_state := p.save_state()
|
saved_state := p.save_state()
|
||||||
p.clear_state()
|
p.clear_state(true, false)
|
||||||
p.add_text(text)
|
p.add_text(text)
|
||||||
p.next()
|
p.next()
|
||||||
if rcbr {
|
if rcbr {
|
||||||
|
@ -337,7 +358,7 @@ fn (p mut Parser) statements_from_text(text string, rcbr bool) {
|
||||||
} else {
|
} else {
|
||||||
p.statements_no_rcbr()
|
p.statements_no_rcbr()
|
||||||
}
|
}
|
||||||
p.restore_state(saved_state)
|
p.restore_state(saved_state, true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) parse(pass Pass) {
|
fn (p mut Parser) parse(pass Pass) {
|
||||||
|
@ -961,7 +982,13 @@ fn (p mut Parser) get_type() string {
|
||||||
nr_muls++
|
nr_muls++
|
||||||
p.check(.amp)
|
p.check(.amp)
|
||||||
}
|
}
|
||||||
typ += p.lit
|
// generic type check
|
||||||
|
ti := p.cur_fn.dispatch_of.inst
|
||||||
|
if p.lit in ti.keys() {
|
||||||
|
typ += ti[p.lit]
|
||||||
|
} else {
|
||||||
|
typ += p.lit
|
||||||
|
}
|
||||||
// C.Struct import
|
// C.Struct import
|
||||||
if p.lit == 'C' && p.peek() == .dot {
|
if p.lit == 'C' && p.peek() == .dot {
|
||||||
p.next()
|
p.next()
|
||||||
|
|
Loading…
Reference in New Issue