From 11aaee685a058d0396655db5c28d78a06913a9d8 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 30 Nov 2019 00:46:43 +1100 Subject: [PATCH] generics: fix errors & simplify --- vlib/compiler/cgen.v | 2 + vlib/compiler/expression.v | 6 ++ vlib/compiler/fn.v | 114 +++++++++++-------------------------- vlib/compiler/main.v | 15 +---- vlib/compiler/parser.v | 57 ++++++++++++++----- 5 files changed, 84 insertions(+), 110 deletions(-) diff --git a/vlib/compiler/cgen.v b/vlib/compiler/cgen.v index d37a54d5f9..288ff2d09d 100644 --- a/vlib/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -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() } diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index 538e1b1f73..3a93b4f68e 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -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() diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 3617dad5b1..8c138f5931 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -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 } diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index c176fde61f..9d0c1a5efd 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -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 diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 85b6029a26..4fd40115d6 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -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()