From 7d418e910592f87fe861fa4be06fdb31576dfd84 Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Sat, 26 Oct 2019 00:34:12 +1100 Subject: [PATCH] parser: cleanup & reuse --- vlib/compiler/compile_errors.v | 2 +- vlib/compiler/fn.v | 10 +-- vlib/compiler/main.v | 75 ++++++++--------- vlib/compiler/modules.v | 112 +++++++++++++++++++++++-- vlib/compiler/parser.v | 42 +++++----- vlib/compiler/table.v | 146 +++++---------------------------- 6 files changed, 187 insertions(+), 200 deletions(-) diff --git a/vlib/compiler/compile_errors.v b/vlib/compiler/compile_errors.v index 7c306d8c3f..23ee082367 100644 --- a/vlib/compiler/compile_errors.v +++ b/vlib/compiler/compile_errors.v @@ -158,7 +158,7 @@ fn (p mut Parser) print_error_context(){ p.cgen.save() // V up hint cur_path := os.getwd() - if !p.pref.is_repl && !p.pref.is_test && ( p.file_path_id.contains('v/compiler') || cur_path.contains('v/compiler') ){ + if !p.pref.is_repl && !p.pref.is_test && ( p.file_path.contains('v/compiler') || cur_path.contains('v/compiler') ){ println('\n=========================') println('It looks like you are building V. It is being frequently updated every day.') println('If you didn\'t modify V\'s code, most likely there was a change that ') diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 58dcc55748..4127dc3953 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -229,7 +229,7 @@ fn (p mut Parser) fn_decl() { } // Don't allow modifying types from a different module if !p.first_pass() && !p.builtin_mod && t.mod != p.mod && - p.file_path_id != 'vgen' // allow .str() on builtin arrays + !p.is_vgen // allow .str() { //println('T.mod=$T.mod') //println('p.mod=$p.mod') @@ -866,7 +866,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) { if p.v.pref.is_debug && f.name == 'panic' && !p.is_js { mod_name := p.mod.replace('_dot_', '.') fn_name := p.cur_fn.name.replace('${p.mod}__', '') - file_path := cescaped_path(p.file_path_id) + 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"), ' @@ -1435,9 +1435,9 @@ fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string { } n := name.all_after('.') if var.name == '' || (n.len - var.name.len > 2 || var.name.len - n.len > 2) { continue } - coeff := strings.dice_coefficient(var.name, n) - if coeff > closest { - closest = coeff + c := strings.dice_coefficient(var.name, n) + if c > closest { + closest = c closest_var = var.name } } diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 58a214a14b..ea67b82818 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -67,8 +67,9 @@ pub mut: out_name string // "program.exe" vroot string mod string // module being built with -lib - parsers []Parser + parsers []Parser // file parsers vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) + file_parser_idx map[string]int // map absolute file path to v.parsers index cached_mods []string } @@ -137,15 +138,17 @@ pub fn (v mut V) finalize_compilation(){ } } -pub fn (v mut V) add_parser(parser Parser) { +pub fn (v mut V) add_parser(parser Parser) int { v.parsers << parser + pidx := v.parsers.len-1 + v.file_parser_idx[os.realpath(parser.file_path)] = pidx + return pidx } pub fn (v &V) get_file_parser_index(file string) ?int { - for i, p in v.parsers { - if os.realpath(p.file_path_id) == os.realpath(file) { - return i - } + file_path := os.realpath(file) + if file_path in v.file_parser_idx { + return v.file_parser_idx[file_path] } return error('parser for "$file" not found') } @@ -157,9 +160,9 @@ pub fn (v mut V) parse(file string, pass Pass) int { mut p := v.new_parser_from_file(file) p.parse(pass) //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } - v.add_parser(p) - return v.parsers.len-1 + return v.add_parser(p) } + // println('matched ' + v.parsers[pidx].file_path + ' with $file') v.parsers[pidx].parse(pass) //if v.parsers[i].pref.autofree { v.parsers[i].scanner.text.free() free(v.parsers[i].scanner) } return pidx @@ -274,8 +277,9 @@ pub fn (v mut V) compile() { } // parse generated V code (str() methods etc) - mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str(), 'vgen') + mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str()) // free the string builder which held the generated methods + vgen_parser.is_vgen = true v.vgen_buf.free() vgen_parser.parse(.main) // v.parsers.add(vgen_parser) @@ -582,8 +586,7 @@ pub fn (v mut V) add_v_files_to_compile() { v.log('imports0:') println(v.table.imports) println(v.files) - p.import_table.register_import('os', 0) - v.table.file_imports[p.file_path_id] = p.import_table + p.register_import('os', 0) p.table.imports << 'os' p.table.register_module('os') } @@ -622,9 +625,9 @@ pub fn (v mut V) add_v_files_to_compile() { } } // add remaining main files last - for _, fit in v.table.file_imports { - if fit.module_name != 'main' { continue } - v.files << fit.file_path_id + for p in v.parsers { + if p.mod != 'main' { continue } + v.files << p.file_path } } @@ -684,9 +687,9 @@ pub fn (v &V) get_user_files() []string { // get module files from already parsed imports fn (v &V) get_imported_module_files(mod string) []string { mut files := []string - for _, fit in v.table.file_imports { - if fit.module_name == mod { - files << fit.file_path_id + for p in v.parsers { + if p.mod == mod { + files << p.file_path } } return files @@ -694,48 +697,34 @@ fn (v &V) get_imported_module_files(mod string) []string { // parse deps from already parsed builtin/user files pub fn (v mut V) parse_lib_imports() { - mut done_fits := []string mut done_imports := []string - for { - for _, fit in v.table.file_imports { - if fit.file_path_id in done_fits { continue } - for _, mod in fit.imports { + for i in 0..v.parsers.len { + for _, mod in v.parsers[i].import_table.imports { if mod in done_imports { continue } import_path := v.find_module_path(mod) or { - pidx := v.get_file_parser_index(fit.file_path_id) or { verror(err) break } - v.parsers[pidx].error_with_token_index('cannot import module "$mod" (not found)', fit.get_import_tok_idx(mod)) + v.parsers[i].error_with_token_index( + 'cannot import module "$mod" (not found)', + v.parsers[i].import_table.get_import_tok_idx(mod)) break } vfiles := v.v_files_from_dir(import_path) if vfiles.len == 0 { - pidx := v.get_file_parser_index(fit.file_path_id) or { verror(err) break } - v.parsers[pidx].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', fit.get_import_tok_idx(mod)) + v.parsers[i].error_with_token_index( + 'cannot import module "$mod" (no .v files in "$import_path")', + v.parsers[i].import_table.get_import_tok_idx(mod)) } // Add all imports referenced by these libs for file in vfiles { - pid := v.parse(file, .imports) - p_mod := v.parsers[pid].import_table.module_name + pidx := v.parse(file, .imports) + p_mod := v.parsers[pidx].mod if p_mod != mod { - v.parsers[pid].error_with_token_index('bad module definition: $fit.file_path_id imports module "$mod" but $file is defined as module `$p_mod`', 1) + v.parsers[pidx].error_with_token_index( + 'bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1) } } done_imports << mod } - done_fits << fit.file_path_id } - if v.table.file_imports.size == done_fits.len { break} - } -} - -// return resolved dep graph (order deps) -pub fn (v &V) resolve_deps() &DepGraph { - mut dep_graph := new_dep_graph() - dep_graph.from_import_tables(v.table.file_imports) - deps_resolved := dep_graph.resolve() - if !deps_resolved.acyclic { - verror('import cycle detected between the following modules: \n' + deps_resolved.display_cycles()) - } - return deps_resolved } pub fn get_arg(joined_args, arg, def string) string { diff --git a/vlib/compiler/modules.v b/vlib/compiler/modules.v index 6933534597..b32b9c5c29 100644 --- a/vlib/compiler/modules.v +++ b/vlib/compiler/modules.v @@ -10,15 +10,117 @@ const ( v_modules_path = os.home_dir() + '.vmodules' ) -// add a module and its deps (module speficic dag method) -pub fn(graph mut DepGraph) from_import_tables(import_tables map[string]FileImportTable) { - for _, fit in import_tables { +// Holds import information scoped to the parsed file +struct ImportTable { +mut: + imports map[string]string // alias => module + used_imports []string // alias + import_tok_idx map[string]int // module => idx +} + +// Once we have a module format we can read from module file instead +// this is not optimal +fn (table &Table) qualify_module(mod string, file_path string) string { + for m in table.imports { + if m.contains('.') && m.contains(mod) { + m_parts := m.split('.') + m_path := m_parts.join(os.path_separator) + if mod == m_parts[m_parts.len-1] && file_path.contains(m_path) { + return m + } + } + } + return mod +} + +fn new_import_table() ImportTable { + return ImportTable{ + imports: map[string]string + } +} + +fn (p mut Parser) register_import(mod string, tok_idx int) { + p.register_import_alias(mod, mod, tok_idx) +} + +fn (p mut Parser) register_import_alias(alias string, mod string, tok_idx int) { + // NOTE: come back here + // if alias in it.imports && it.imports[alias] == mod {} + if alias in p.import_table.imports && p.import_table.imports[alias] != mod { + p.error('cannot import $mod as $alias: import name $alias already in use"') + } + if mod.contains('.internal.') { + mod_parts := mod.split('.') + mut internal_mod_parts := []string + for part in mod_parts { + if part == 'internal' { break } + internal_mod_parts << part + } + internal_parent := internal_mod_parts.join('.') + if !p.mod.starts_with(internal_parent) { + p.error('module $mod can only be imported internally by libs') + } + } + p.import_table.imports[alias] = mod + p.import_table.import_tok_idx[mod] = tok_idx +} + +fn (it &ImportTable) get_import_tok_idx(mod string) int { + return it.import_tok_idx[mod] +} + +fn (it &ImportTable) known_import(mod string) bool { + return mod in it.imports || it.is_aliased(mod) +} + +fn (it &ImportTable) known_alias(alias string) bool { + return alias in it.imports +} + +fn (it &ImportTable) is_aliased(mod string) bool { + for _, val in it.imports { + if val == mod { + return true + } + } + return false +} + +fn (it &ImportTable) resolve_alias(alias string) string { + return it.imports[alias] +} + +fn (it mut ImportTable) register_used_import(alias string) { + if !(alias in it.used_imports) { + it.used_imports << alias + } +} + +fn (it &ImportTable) is_used_import(alias string) bool { + return alias in it.used_imports +} + +// return resolved dep graph (order deps) +pub fn (v &V) resolve_deps() &DepGraph { + graph := v.import_graph() + deps_resolved := graph.resolve() + if !deps_resolved.acyclic { + verror('import cycle detected between the following modules: \n' + deps_resolved.display_cycles()) + } + return deps_resolved +} + +// graph of all imported modules +pub fn(v &V) import_graph() &DepGraph { + mut graph := new_dep_graph() + for p in v.parsers { mut deps := []string - for _, m in fit.imports { + for _, m in p.import_table.imports { deps << m } - graph.add(fit.module_name, deps) + graph.add(p.mod, deps) } + return graph } // get ordered imports (module speficic dag method) diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index f00de855b0..14337c13b0 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -10,7 +10,7 @@ import ( ) struct Parser { - file_path_id string // unique id. if parsing file will be path eg, "/home/user/hello.v" + file_path string // if parsing file will be path eg, "/home/user/hello.v" file_name string // "hello.v" file_platform string // ".v", "_windows.v", "_nix.v", "_darwin.v", "_linux.v" ... // When p.file_pcguard != '', it contains a @@ -29,7 +29,7 @@ mut: lit string cgen &CGen table &Table - import_table FileImportTable // Holds imports for just the file being parsed + import_table ImportTable // Holds imports for just the file being parsed pass Pass os OS inside_const bool @@ -64,6 +64,7 @@ mut: is_alloc bool // Whether current expression resulted in an allocation is_const_literal bool // `1`, `2.0` etc, so that `u64_var == 0` works in_dispatch bool // dispatching generic instance? + is_vgen bool is_vweb bool is_sql bool is_js bool @@ -81,8 +82,8 @@ const ( // new parser from string. unique id specified in `id`. // tip: use a hashing function to auto generate `id` from `text` eg. sha1.hexhash(text) -fn (v mut V) new_parser_from_string(text string, id string) Parser { - mut p := v.new_parser(new_scanner(text), id) +fn (v mut V) new_parser_from_string(text string) Parser { + mut p := v.new_parser(new_scanner(text)) p.scan_tokens() return p } @@ -119,12 +120,17 @@ fn (v mut V) new_parser_from_file(path string) Parser { } } - mut p := v.new_parser(new_scanner_file(path), path) + mut p := v.new_parser(new_scanner_file(path)) p = { p| + file_path: path, file_name: path.all_after(os.path_separator), file_platform: path_platform, file_pcguard: path_pcguard, - is_vh: path.ends_with('.vh') + is_vh: path.ends_with('.vh'), + v_script: path.ends_with('.vsh') + } + if p.v_script { + println('new_parser: V script') } if p.pref.building_v { p.scanner.should_print_relative_paths_on_error = true @@ -140,10 +146,9 @@ fn (v mut V) new_parser_from_file(path string) Parser { // creates a new parser. most likely you will want to use // `new_parser_file` or `new_parser_string` instead. -fn (v mut V) new_parser(scanner &Scanner, id string) Parser { +fn (v mut V) new_parser(scanner &Scanner) Parser { v.reset_cgen_file_line_parameters() mut p := Parser { - file_path_id: id scanner: scanner v: v table: v.table @@ -153,12 +158,8 @@ fn (v mut V) new_parser(scanner &Scanner, id string) Parser { os: v.os vroot: v.vroot local_vars: [Var{}].repeat(MaxLocalVars) - import_table: v.table.get_file_import_table(id) - v_script: id.ends_with('.vsh') + import_table: new_import_table() } - if p.v_script { - println('new_parser: V script') - } $if js { p.is_js = true } @@ -241,7 +242,7 @@ fn (p &Parser) log(s string) { fn (p mut Parser) parse(pass Pass) { p.cgen.line = 0 - p.cgen.file = cescaped_path(os.realpath(p.file_path_id)) + p.cgen.file = cescaped_path(os.realpath(p.file_path)) ///////////////////////////////////// p.pass = pass p.token_idx = 0 @@ -281,9 +282,8 @@ fn (p mut Parser) parse(pass Pass) { } // fully qualify the module name, eg base64 to encoding.base64 else { - p.table.qualify_module(p.mod, p.file_path_id) + p.table.qualify_module(p.mod, p.file_path) } - p.import_table.module_name = fq_mod p.table.register_module(fq_mod) p.mod = fq_mod @@ -294,8 +294,6 @@ fn (p mut Parser) parse(pass Pass) { if 'builtin' in p.table.imports { p.error('module `builtin` cannot be imported') } - // save file import table - p.table.file_imports[p.file_path_id] = p.import_table return } // Go through every top level token or throw a compilation error if a non-top level token is met @@ -496,7 +494,7 @@ fn (p mut Parser) import_statement() { mod_alias = p.check_name() } // add import to file scope import table - p.import_table.register_alias(mod_alias, mod, import_tok_idx) + p.register_import_alias(mod_alias, mod, import_tok_idx) // Make sure there are no duplicate imports if mod in p.table.imports { return @@ -2013,7 +2011,7 @@ struct $f.parent_fn { ', fname_tidx) } // Don't allow `arr.data` - if field.access_mod == .private && !p.builtin_mod && !p.pref.translated && p.mod != typ.mod && p.file_path_id != 'vgen' { + if field.access_mod == .private && !p.builtin_mod && !p.pref.translated && p.mod != typ.mod && !p.is_vgen { // println('$typ.name :: $field.name ') // println(field.access_mod) p.error_with_token_index('cannot refer to unexported field `$struct_field` (type `$typ.name`)\n' + @@ -3617,7 +3615,7 @@ fn (p mut Parser) assert_statement() { p.gen('bool $tmp = ') p.check_types(p.bool_expression(), 'bool') // TODO print "expected: got" for failed tests - filename := cescaped_path(p.file_path_id) + filename := cescaped_path(p.file_path) p.genln('; \n @@ -3906,7 +3904,7 @@ fn (p mut Parser) check_and_register_used_imported_type(typ_name string) { fn (p mut Parser) check_unused_imports() { // Don't run in the generated V file with `.str()` - if p.file_path_id == 'vgen' { + if p.is_vgen { return } mut output := '' diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 79af50d227..db56594cab 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -4,7 +4,6 @@ module compiler -import os import strings struct Table { @@ -15,7 +14,6 @@ pub mut: obf_ids map[string]int // obf_ids['myfunction'] == 23 modules []string // List of all modules registered by the application imports []string // List of all imports - file_imports map[string]FileImportTable // List of imports for file cflags []CFlag // ['-framework Cocoa', '-lglfw3'] fn_cnt int //atomic obfuscate bool @@ -39,16 +37,6 @@ enum NameCategory { struct Name { cat NameCategory idx int // e.g. typ := types[name.idx] -} - -// Holds import information scoped to the parsed file -struct FileImportTable { -mut: - module_name string - file_path_id string // file path or id - imports map[string]string // alias => module - used_imports []string // alias - import_tok_idx map[string]int // module => idx } enum AccessMod { @@ -872,96 +860,6 @@ fn is_compile_time_const(s_ string) bool { return true } -// Once we have a module format we can read from module file instead -// this is not optimal -fn (table &Table) qualify_module(mod string, file_path string) string { - for m in table.imports { - if m.contains('.') && m.contains(mod) { - m_parts := m.split('.') - m_path := m_parts.join(os.path_separator) - if mod == m_parts[m_parts.len-1] && file_path.contains(m_path) { - return m - } - } - } - return mod -} - -fn (table &Table) get_file_import_table(file_path_id string) FileImportTable { - if file_path_id in table.file_imports { - return table.file_imports[file_path_id] - } - return new_file_import_table(file_path_id) -} - -fn new_file_import_table(file_path_id string) FileImportTable { - return FileImportTable{ - file_path_id: file_path_id - imports: map[string]string - } -} - -fn (fit &FileImportTable) known_import(mod string) bool { - return mod in fit.imports || fit.is_aliased(mod) -} - -fn (fit mut FileImportTable) register_import(mod string, tok_idx int) { - fit.register_alias(mod, mod, tok_idx) -} - -fn (fit mut FileImportTable) register_alias(alias string, mod string, tok_idx int) { - // NOTE: come back here - // if alias in fit.imports && fit.imports[alias] == mod {} - if alias in fit.imports && fit.imports[alias] != mod { - verror('cannot import $mod as $alias: import name $alias already in use in "${fit.file_path_id}"') - } - if mod.contains('.internal.') { - mod_parts := mod.split('.') - mut internal_mod_parts := []string - for part in mod_parts { - if part == 'internal' { break } - internal_mod_parts << part - } - internal_parent := internal_mod_parts.join('.') - if !fit.module_name.starts_with(internal_parent) { - verror('module $mod can only be imported internally by libs') - } - } - fit.imports[alias] = mod - fit.import_tok_idx[mod] = tok_idx -} - -fn (fit &FileImportTable) get_import_tok_idx(mod string) int { - return fit.import_tok_idx[mod] -} - -fn (fit &FileImportTable) known_alias(alias string) bool { - return alias in fit.imports -} - -fn (fit &FileImportTable) is_aliased(mod string) bool { - for _, val in fit.imports { - if val == mod { - return true - } - } - return false -} - -fn (fit &FileImportTable) resolve_alias(alias string) string { - return fit.imports[alias] -} - -fn (fit mut FileImportTable) register_used_import(alias string) { - if !(alias in fit.used_imports) { - fit.used_imports << alias - } -} - -fn (fit &FileImportTable) is_used_import(alias string) bool { - return alias in fit.used_imports -} - fn (t &Type) contains_field_type(typ string) bool { if !t.name[0].is_capital() { return false @@ -982,17 +880,17 @@ fn (p &Parser) identify_typo(name string) string { min_match := 0.50 // for dice coefficient between 0.0 - 1.0 mut output := '' // check imported modules - mut n := p.table.find_misspelled_imported_mod(name_dotted, p.import_table, min_match) + mut n := p.table.find_misspelled_imported_mod(name_dotted, p, min_match) if n != '' { output += '\n * module: `$n`' } // check consts - n = p.table.find_misspelled_const(name, p.import_table, min_match) + n = p.table.find_misspelled_const(name, p, min_match) if n != '' { output += '\n * const: `$n`' } // check functions - n = p.table.find_misspelled_fn(name, p.import_table, min_match) + n = p.table.find_misspelled_fn(name, p, min_match) if n != '' { output += '\n * function: `$n`' } @@ -1005,7 +903,7 @@ fn (p &Parser) identify_typo(name string) string { } // find function with closest name to `name` -fn (table &Table) find_misspelled_fn(name string, fit &FileImportTable, min_match f32) string { +fn (table &Table) find_misspelled_fn(name string, p &Parser, min_match f32) string { mut closest := f32(0) mut closest_fn := '' n1 := if name.starts_with('main__') { name.right(6) } else { name } @@ -1013,7 +911,7 @@ fn (table &Table) find_misspelled_fn(name string, fit &FileImportTable, min_matc if n1.len - f.name.len > 2 || f.name.len - n1.len > 2 { continue } if !(f.mod in ['', 'main', 'builtin']) { mut mod_imported := false - for _, m in fit.imports { + for _, m in p.import_table.imports { if f.mod == m { mod_imported = true break @@ -1021,10 +919,10 @@ fn (table &Table) find_misspelled_fn(name string, fit &FileImportTable, min_matc } if !mod_imported { continue } } - p := strings.dice_coefficient(n1, f.name) + c := strings.dice_coefficient(n1, f.name) f_name_orig := mod_gen_name_rev(f.name.replace('__', '.')) - if p > closest { - closest = p + if c > closest { + closest = c closest_fn = f_name_orig } } @@ -1032,16 +930,16 @@ fn (table &Table) find_misspelled_fn(name string, fit &FileImportTable, min_matc } // find imported module with closest name to `name` -fn (table &Table) find_misspelled_imported_mod(name string, fit &FileImportTable, min_match f32) string { +fn (table &Table) find_misspelled_imported_mod(name string, p &Parser, min_match f32) string { mut closest := f32(0) mut closest_mod := '' n1 := if name.starts_with('main.') { name.right(5) } else { name } - for alias, mod in fit.imports { + for alias, mod in p.import_table.imports { if n1.len - alias.len > 2 || alias.len - n1.len > 2 { continue } mod_alias := if alias == mod { alias } else { '$alias ($mod)' } - p := strings.dice_coefficient(n1, alias) - if p > closest { - closest = p + c := strings.dice_coefficient(n1, alias) + if c > closest { + closest = c closest_mod = '$mod_alias' } } @@ -1049,20 +947,20 @@ fn (table &Table) find_misspelled_imported_mod(name string, fit &FileImportTable } // find const with closest name to `name` -fn (table &Table) find_misspelled_const(name string, fit &FileImportTable, min_match f32) string { +fn (table &Table) find_misspelled_const(name string, p &Parser, min_match f32) string { mut closest := f32(0) mut closest_const := '' mut mods_in_scope := ['builtin', 'main'] - for _, mod in fit.imports { + for _, mod in p.import_table.imports { mods_in_scope << mod } - for c in table.consts { - if c.mod != fit.module_name && !(c.mod in mods_in_scope) && c.mod.contains('__') { continue } - if name.len - c.name.len > 2 || c.name.len - name.len > 2 { continue } - const_name_orig := mod_gen_name_rev(c.name.replace('__', '.')) - p := strings.dice_coefficient(name, c.name.replace('builtin__', 'main__')) - if p > closest { - closest = p + for cnst in table.consts { + if cnst.mod != p.mod && !(cnst.mod in mods_in_scope) && cnst.mod.contains('__') { continue } + if name.len - cnst.name.len > 2 || cnst.name.len - name.len > 2 { continue } + const_name_orig := mod_gen_name_rev(cnst.name.replace('__', '.')) + c := strings.dice_coefficient(name, cnst.name.replace('builtin__', 'main__')) + if c > closest { + closest = c closest_const = const_name_orig } }