generics: fix errors & simplify

pull/2927/head
joe-conigliaro 2019-11-30 00:46:43 +11:00 committed by Alexander Medvednikov
parent 82d4a731f3
commit 11aaee685a
5 changed files with 84 additions and 110 deletions

View File

@ -16,6 +16,7 @@ struct CGen {
is_user bool
mut:
lines []string
lines_extra []string
typedefs []string
type_aliases []string
includes []string
@ -102,6 +103,7 @@ fn (g mut CGen) resetln(s string) {
fn (g mut CGen) save() {
s := g.lines.join('\n')
g.out.writeln(s)
g.out.writeln(g.lines_extra.join('\n'))
g.out.close()
}

View File

@ -160,6 +160,12 @@ fn (p mut Parser) name_expr() string {
}
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 ')
if (name == 'r' || name == 'c') && p.peek() == .str && p.prev_tok != .dollar {
p.string_expr()

View File

@ -40,7 +40,8 @@ mut:
type_pars []string
type_inst []TypeInst
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
comptime_define string
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() {
p.clear_vars() // clear local vars every time a new fn is started
defer { p.fgenln('\n') }
fn_start_idx := p.cur_tok_index()
// If we are in the first pass, create a new function.
// In the second pass fetch the one we created.
/*
@ -343,7 +345,13 @@ fn (p mut Parser) fn_decl() {
}
// Generic?
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()
for {
type_par := p.check_name()
@ -357,8 +365,8 @@ fn (p mut Parser) fn_decl() {
if p.tok == .gt { break }
p.check(.comma)
}
p.set_current_fn(f)
p.check(.gt)
p.set_current_fn(f)
}
// Args (...)
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
if f.is_generic {
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 {
rcv := p.table.find_type(receiver_typ)
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
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' &&
!p.builtin_mod //&& arg.is_moved
clone := p.pref.autofree && p.mod != 'string' && arg.typ == 'string' &&
!p.builtin_mod //&& arg.is_moved
if 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
}
// 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) {
if f.is_method {
if f.is_method && f.dispatch_of.inst.size == 0 {
f.name = f.receiver_typ + '_' + f.name
}
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
p.table.register_fn(f)
rename_generic_fn_instance(mut f, ti)
replace_generic_type_params(mut f, ti)
// 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 {
p.table.register_fn(f)
}
mut fn_code := '${p.fn_signature_v(f)} {\n${f.generic_tmpl_to_inst(ti)}\n}'
// TODO: parse incrementally as needed & set typeinst
if f.mod in p.v.gen_parser_idx {
pidx := p.v.gen_parser_idx[f.mod]
p.v.parsers[pidx].add_text(fn_code)
for mod in p.table.imports {
if p.v.parsers[pidx].import_table.known_import(mod) { continue }
p.v.parsers[pidx].register_import(mod, 0)
}
} else {
// TODO: add here after I work out bug
}
mut gp := p.v.parsers[f.parser_idx]
gp.is_vgen = true
gp.generic_dispatch = *ti
saved_state := p.save_state()
p.clear_state(false, true)
gp.token_idx = f.generic_fn_idx
gp.next()
gp.fn_decl()
p.cgen.lines_extra << p.cgen.lines
p.restore_state(saved_state, false, true)
p.cgen.fns << '${p.fn_signature(f)};'
}
@ -1556,20 +1538,6 @@ fn (f &Fn) str_args(table &Table) string {
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`
fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string {
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)})'
}
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 {
return f.mod
}

View File

@ -302,12 +302,8 @@ pub fn (v mut V) compile() {
// free the string builder which held the generated methods
v.vgen_buf.free()
vgen_parser.is_vgen = true
v.add_parser(vgen_parser)
// run vgen / generic parsers
for i, _ in v.parsers {
if !v.parsers[i].is_vgen { continue }
v.parsers[i].parse(.main)
}
// v.add_parser(vgen_parser)
vgen_parser.parse(.main)
// Generate .vh if we are building a module
if v.pref.build_mode == .build_module {
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
imported_mods := v.resolve_deps().imports()
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' {
// builtin already added
// main files will get added last

View File

@ -74,6 +74,7 @@ mut:
sql_params []string // ("select * from users where id = $1", ***"100"***)
sql_types []string // int, string and so on; see sql_params
is_vh bool // parsing .vh file (for example `const (a int)` is allowed)
generic_dispatch TypeInst
pub:
mod string
}
@ -94,6 +95,9 @@ struct ParserState {
scanner_pos int
scanner_line_ends []int
scanner_nlines int
cgen_lines []string
cgen_cur_line string
cgen_tmp_line string
tokens []Token
token_idx int
tok TokenKind
@ -285,6 +289,9 @@ pub fn (p mut Parser) save_state() ParserState {
scanner_pos : p.scanner.pos
scanner_line_ends : p.scanner.line_ends
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
token_idx : p.token_idx
tok : p.tok
@ -294,12 +301,19 @@ pub fn (p mut Parser) save_state() ParserState {
}
}
pub fn (p mut Parser) restore_state(state ParserState) {
p.scanner.line_nr = state.scanner_line_nr
p.scanner.text = state.scanner_text
p.scanner.pos = state.scanner_pos
p.scanner.line_ends = state.scanner_line_ends
p.scanner.nlines = state.scanner_nlines
pub fn (p mut Parser) restore_state(state ParserState, scanner bool, cgen bool) {
if scanner {
p.scanner.line_nr = state.scanner_line_nr
p.scanner.text = state.scanner_text
p.scanner.pos = state.scanner_pos
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.token_idx = state.token_idx
p.tok = state.tok
@ -308,12 +322,19 @@ pub fn (p mut Parser) restore_state(state ParserState) {
p.lit = state.lit
}
fn (p mut Parser) clear_state() {
p.scanner.line_nr = 0
p.scanner.text = ''
p.scanner.pos = 0
p.scanner.line_ends = []
p.scanner.nlines = 0
fn (p mut Parser) clear_state(scanner bool, cgen bool) {
if scanner {
p.scanner.line_nr = 0
p.scanner.text = ''
p.scanner.pos = 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.token_idx = 0
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) {
saved_state := p.save_state()
p.clear_state()
p.clear_state(true, false)
p.add_text(text)
p.next()
if rcbr {
@ -337,7 +358,7 @@ fn (p mut Parser) statements_from_text(text string, rcbr bool) {
} else {
p.statements_no_rcbr()
}
p.restore_state(saved_state)
p.restore_state(saved_state, true, false)
}
fn (p mut Parser) parse(pass Pass) {
@ -961,7 +982,13 @@ fn (p mut Parser) get_type() string {
nr_muls++
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
if p.lit == 'C' && p.peek() == .dot {
p.next()