diff --git a/compiler/cgen.v b/compiler/cgen.v index 9bae6058bc..d7dc301b03 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -151,10 +151,9 @@ fn (g mut CGen) register_thread_fn(wrapper_name, wrapper_text, struct_text strin fn (c mut V) prof_counters() string { mut res := []string // Global fns - for f in c.table.fns { - res << 'double ${c.table.cgen_name(f)}_time;' - // println(f.name) - } + //for f in c.table.fns { + //res << 'double ${c.table.cgen_name(f)}_time;' + //} // Methods for typ in c.table.types { // println('') @@ -170,11 +169,10 @@ fn (c mut V) prof_counters() string { fn (p mut Parser) print_prof_counters() string { mut res := []string // Global fns - for f in p.table.fns { - counter := '${p.table.cgen_name(f)}_time' - res << 'if ($counter) printf("%%f : $f.name \\n", $counter);' - // println(f.name) - } + //for f in p.table.fns { + //counter := '${p.table.cgen_name(f)}_time' + //res << 'if ($counter) printf("%%f : $f.name \\n", $counter);' + //} // Methods for typ in p.table.types { // println('') diff --git a/compiler/main.v b/compiler/main.v index 978e53988e..ca0d0095bb 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -59,26 +59,6 @@ enum Pass { } */ -struct Preferences { - mut: - build_mode BuildMode - nofmt bool // disable vfmt - is_test bool // `v test string_test.v` - is_script bool // single file mode (`v program.v`), `fn main(){}` can be skipped - is_live bool // for hot code reloading - is_so bool - is_prof bool // benchmark every function - translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc - is_prod bool // use "-O2" - is_verbose bool // print extra information with `v.log()` - obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" - is_play bool // playground mode - is_repl bool - is_run bool - show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c - sanitize bool // use Clang's new "-fsanitize" option -} - struct V { mut: os Os // the OS to build for @@ -93,8 +73,27 @@ mut: vroot string } +struct Preferences { +mut: + build_mode BuildMode + nofmt bool // disable vfmt + is_test bool // `v test string_test.v` + is_script bool // single file mode (`v program.v`), `fn main(){}` can be skipped + is_live bool // for hot code reloading + is_so bool + is_prof bool // benchmark every function + translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc + is_prod bool // use "-O2" + is_verbose bool // print extra information with `v.log()` + obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" + is_play bool // playground mode + is_repl bool + is_run bool + show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c + sanitize bool // use Clang's new "-fsanitize" option +} + fn main() { - // There's no `flags` module yet, so args have to be parsed manually args := os.args // Print the version and exit. if '-v' in args || 'version' in args { @@ -142,35 +141,35 @@ fn main() { return } // Construct the V object from command line arguments - mut c := new_v(args) - if c.pref.is_verbose { + mut v := new_v(args) + if v.pref.is_verbose { println(args) } // Generate the docs and exit if args.contains('doc') { - // c.gen_doc_html_for_module(args.last()) + // v.gen_doc_html_for_module(args.last()) exit(0) } - c.compile() + v.compile() } -fn (c mut V) compile() { - mut cgen := c.cgen +fn (v mut V) compile() { + mut cgen := v.cgen cgen.genln('// Generated by V') // Add user files to compile - c.add_user_v_files() - if c.pref.is_verbose { + v.add_user_v_files() + if v.pref.is_verbose { println('all .v files:') - println(c.files) + println(v.files) } // First pass (declarations) - for file in c.files { - mut p := c.new_parser(file, RUN_DECLS) + for file in v.files { + mut p := v.new_parser(file, RUN_DECLS) p.parse() } // Main pass cgen.run = RUN_MAIN - if c.pref.is_play { + if v.pref.is_play { cgen.genln('#define VPLAY (1) ') } cgen.genln(' @@ -242,31 +241,31 @@ byteptr g_str_buf; int load_so(byteptr); void reload_so(); void init_consts();') - imports_json := c.table.imports.contains('json') + imports_json := v.table.imports.contains('json') // TODO remove global UI hack - if c.os == MAC && ((c.pref.build_mode == EMBED_VLIB && c.table.imports.contains('ui')) || - (c.pref.build_mode == BUILD && c.dir.contains('/ui'))) { + if v.os == MAC && ((v.pref.build_mode == EMBED_VLIB && v.table.imports.contains('ui')) || + (v.pref.build_mode == BUILD && v.dir.contains('/ui'))) { cgen.genln('id defaultFont = 0; // main.v') } // TODO remove ugly .c include once V has its own json parser // Embed cjson either in embedvlib or in json.o - if imports_json && c.pref.build_mode == EMBED_VLIB || - (c.pref.build_mode == BUILD && c.out_name.contains('json.o')) { + if imports_json && v.pref.build_mode == EMBED_VLIB || + (v.pref.build_mode == BUILD && v.out_name.contains('json.o')) { cgen.genln('#include "cJSON.c" ') } // We need the cjson header for all the json decoding user will do in default mode - if c.pref.build_mode == DEFAULT_MODE { + if v.pref.build_mode == DEFAULT_MODE { if imports_json { cgen.genln('#include "cJSON.h"') } } - if c.pref.build_mode == EMBED_VLIB || c.pref.build_mode == DEFAULT_MODE { + if v.pref.build_mode == EMBED_VLIB || v.pref.build_mode == DEFAULT_MODE { // If we declare these for all modes, then when running `v a.v` we'll get // `/usr/bin/ld: multiple definition of 'total_m'` // TODO //cgen.genln('i64 total_m = 0; // For counting total RAM allocated') cgen.genln('int g_test_ok = 1; ') - if c.table.imports.contains('json') { + if v.table.imports.contains('json') { cgen.genln(' #define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) ') @@ -278,16 +277,16 @@ void init_consts();') cgen.genln('/*================================== FNS =================================*/') cgen.genln('this line will be replaced with definitions') defs_pos := cgen.lines.len - 1 - for file in c.files { - mut p := c.new_parser(file, RUN_MAIN) + for file in v.files { + mut p := v.new_parser(file, RUN_MAIN) p.parse() // p.g.gen_x64() // Format all files (don't format automatically generated vlib headers) - if !c.pref.nofmt && !file.contains('/vlib/') { + if !v.pref.nofmt && !file.contains('/vlib/') { // new vfmt is not ready yet } } - c.log('Done parsing.') + v.log('Done parsing.') // Write everything mut d := new_string_builder(10000)// Just to avoid some unnecessary allocations d.writeln(cgen.includes.join_lines()) @@ -298,14 +297,14 @@ void init_consts();') d.writeln(cgen.fns.join_lines()) d.writeln(cgen.consts.join_lines()) d.writeln(cgen.thread_args.join_lines()) - if c.pref.is_prof { + if v.pref.is_prof { d.writeln('; // Prof counters:') - d.writeln(c.prof_counters()) + d.writeln(v.prof_counters()) } dd := d.str() cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile - // if c.build_mode in [.default, .embed_vlib] { - if c.pref.build_mode == DEFAULT_MODE || c.pref.build_mode == EMBED_VLIB { + // if v.build_mode in [.default, .embed_vlib] { + if v.pref.build_mode == DEFAULT_MODE || v.pref.build_mode == EMBED_VLIB { // vlib can't have `init_consts()` cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }') // _STR function can't be defined in vlib @@ -345,29 +344,31 @@ string _STR_TMP(const char *fmt, ...) { } // Make sure the main function exists // Obviously we don't need it in libraries - if c.pref.build_mode != BUILD { - if !c.table.main_exists() && !c.pref.is_test { + if v.pref.build_mode != BUILD { + if !v.table.main_exists() && !v.pref.is_test { // It can be skipped in single file programs - if c.pref.is_script { + if v.pref.is_script { //println('Generating main()...') cgen.genln('int main() { $cgen.fn_main; return 0; }') } else { println('panic: function `main` is undeclared in the main module') + exit(1) } } // Generate `main` which calls every single test function - else if c.pref.is_test { + else if v.pref.is_test { cgen.genln('int main() { init_consts();') - for v in c.table.fns { - if v.name.starts_with('test_') { - cgen.genln('$v.name();') + for entry in v.table.fns.entries { + f := v.table.fns[entry.key] + if f.name.starts_with('test_') { + cgen.genln('$f.name();') } } cgen.genln('return g_test_ok == 0; }') } } - if c.pref.is_live { + if v.pref.is_live { cgen.genln(' int load_so(byteptr path) { printf("load_so %s\\n", path); dlclose(live_lib); live_lib = dlopen(path, RTLD_LAZY); if (!live_lib) {puts("open failed"); exit(1); return 0;} @@ -378,70 +379,71 @@ string _STR_TMP(const char *fmt, ...) { cgen.genln('return 1; }') } cgen.save() - if c.pref.is_verbose { - c.log('flags=') - println(c.table.flags) + if v.pref.is_verbose { + v.log('flags=') + println(v.table.flags) } - c.cc() - if c.pref.is_test || c.pref.is_run { - if true || c.pref.is_verbose { - println('============ running $c.out_name ============') + v.cc() + if v.pref.is_test || v.pref.is_run { + if true || v.pref.is_verbose { + println('============ running $v.out_name ============') } - mut cmd := if c.out_name.starts_with('/') { - c.out_name + mut cmd := if v.out_name.starts_with('/') { + v.out_name } else { - './' + c.out_name + './' + v.out_name } $if windows { - cmd = c.out_name + cmd = v.out_name } if os.args.len > 3 { cmd += ' ' + os.args.right(3).join(' ') } ret := os.system(cmd) if ret != 0 { - s := os.exec(cmd) - println(s) - println('failed to run the compiled program, this should never happen') - println('please submit a GitHub issue') + if !v.pref.is_test { + s := os.exec(cmd) + println(s) + println('failed to run the compiled program') + } exit(1) } } } -fn (c mut V) cc() { +fn (v mut V) cc() { linux_host := os.user_os() == 'linux' - c.log('cc() isprod=$c.pref.is_prod outname=$c.out_name') + v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name') mut a := ['-w']// arguments for the C compiler - flags := c.table.flags.join(' ') + flags := v.table.flags.join(' ') /* mut shared := '' - if c.pref.is_so { + if v.pref.is_so { a << '-shared'// -Wl,-z,defs' - c.out_name = c.out_name + '.so' + v.out_name = v.out_name + '.so' } */ - if c.pref.is_prod { + if v.pref.is_prod { a << '-O2' } else { a << '-g' } mut libs := ''// builtin.o os.o http.o etc - if c.pref.build_mode == BUILD { + if v.pref.build_mode == BUILD { a << '-c' } - else if c.pref.build_mode == EMBED_VLIB { + else if v.pref.build_mode == EMBED_VLIB { // } - else if c.pref.build_mode == DEFAULT_MODE { + else if v.pref.build_mode == DEFAULT_MODE { libs = '$TmpPath/vlib/builtin.o' if !os.file_exists(libs) { println('`builtin.o` not found') exit(1) } - for imp in c.table.imports { + for imp in v.table.imports { if imp == 'webview' { continue } @@ -451,62 +453,62 @@ fn (c mut V) cc() { // -I flags /* mut args := '' - for flag in c.table.flags { + for flag in v.table.flags { if !flag.starts_with('-l') { args += flag args += ' ' } } */ - if c.pref.sanitize { + if v.pref.sanitize { a << '-fsanitize=leak' } // Cross compiling linux sysroot := '/Users/alex/tmp/lld/linuxroot/' - if c.os == LINUX && !linux_host { + if v.os == LINUX && !linux_host { // Build file.o a << '-c --sysroot=$sysroot -target x86_64-linux-gnu' // Right now `out_name` can be `file`, not `file.o` - if !c.out_name.ends_with('.o') { - c.out_name = c.out_name + '.o' + if !v.out_name.ends_with('.o') { + v.out_name = v.out_name + '.o' } } // Cross compiling windows // sysroot := '/Users/alex/tmp/lld/linuxroot/' // Output executable name // else { - a << '-o $c.out_name' + a << '-o $v.out_name' // The C file we are compiling - a << '$TmpPath/$c.out_name_c' + a << '$TmpPath/$v.out_name_c' // } // Min macos version is mandatory I think? - if c.os == MAC { + if v.os == MAC { a << '-mmacosx-version-min=10.7' } a << flags a << libs // macOS code can include objective C TODO remove once objective C is replaced with C - if c.os == MAC { + if v.os == MAC { a << '-x objective-c' } // Without these libs compilation will fail on Linux - if c.os == LINUX && c.pref.build_mode != BUILD { + if v.os == LINUX && v.pref.build_mode != BUILD { a << '-lm -ldl -lpthread' } // Find clang executable - fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang' + //fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang' args := a.join(' ') - mut cmd := if os.file_exists(fast_clang) { - '$fast_clang $args' - } - else { - 'cc $args' - } + //mut cmd := if os.file_exists(fast_clang) { + //'$fast_clang $args' + //} + //else { + mut cmd := 'cc $args' + //} $if windows { cmd = 'gcc $args' } // Print the C command - if c.pref.show_c_cmd || c.pref.is_verbose { + if v.pref.show_c_cmd || v.pref.is_verbose { println('\n==========\n$cmd\n=========\n') } // Run @@ -517,29 +519,29 @@ mut args := '' panic('clang error') } // Link it if we are cross compiling and need an executable - if c.os == LINUX && !linux_host && c.pref.build_mode != BUILD { - c.out_name = c.out_name.replace('.o', '') - obj_file := c.out_name + '.o' - println('linux obj_file=$obj_file out_name=$c.out_name') + if v.os == LINUX && !linux_host && v.pref.build_mode != BUILD { + v.out_name = v.out_name.replace('.o', '') + obj_file := v.out_name + '.o' + println('linux obj_file=$obj_file out_name=$v.out_name') ress := os.exec('/usr/local/Cellar/llvm/8.0.0/bin/ld.lld --sysroot=$sysroot ' + - '-v -o $c.out_name ' + + '-v -o $v.out_name ' + '-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 ' + '/usr/lib/x86_64-linux-gnu/crt1.o ' + '$sysroot/lib/x86_64-linux-gnu/libm-2.28.a ' + '/usr/lib/x86_64-linux-gnu/crti.o ' + obj_file + - ' /usr/lib/x86_64-linux-gnu/libc.so ' + + ' /usr/lib/x86_64-linux-gnu/libv.so ' + '/usr/lib/x86_64-linux-gnu/crtn.o') println(ress) if ress.contains('error:') { exit(1) } - println('linux cross compilation done. resulting binary: "$c.out_name"') + println('linux cross compilation done. resulting binary: "$v.out_name"') } - //os.rm('$TmpPath/$c.out_name_c') + //os.rm('$TmpPath/$v.out_name_c') } -fn (c &V) v_files_from_dir(dir string) []string { +fn (v &V) v_files_from_dir(dir string) []string { mut res := []string if !os.file_exists(dir) { panic('$dir doesn\'t exist') @@ -547,38 +549,38 @@ fn (c &V) v_files_from_dir(dir string) []string { panic('$dir isn\'t a directory') } mut files := os.ls(dir) - if c.pref.is_verbose { + if v.pref.is_verbose { println('v_files_from_dir ("$dir")') } // println(files.len) // println(files) files.sort() for file in files { - c.log('F=$file') + v.log('F=$file') if !file.ends_with('.v') && !file.ends_with('.vh') { continue } if file.ends_with('_test.v') { continue } - if file.ends_with('_win.v') && c.os != WINDOWS { + if file.ends_with('_win.v') && v.os != WINDOWS { continue } - if file.ends_with('_lin.v') && c.os != LINUX { + if file.ends_with('_lin.v') && v.os != LINUX { continue } - if file.ends_with('_mac.v') && c.os != MAC { - lin_file := file.replace('_mac.v', '_lin.v') + if file.ends_with('_mav.v') && v.os != MAC { + lin_file := file.replace('_mav.v', '_lin.v') // println('lin_file="$lin_file"') - // If there are both _mac.v and _lin.v, don't use _mac.v + // If there are both _mav.v and _lin.v, don't use _mav.v if os.file_exists('$dir/$lin_file') { continue } - else if c.os == WINDOWS { + else if v.os == WINDOWS { continue } else { - // If there's only _mac.v, then it can be used on Linux too + // If there's only _mav.v, then it can be used on Linux too } } res << '$dir/$file' @@ -587,9 +589,9 @@ fn (c &V) v_files_from_dir(dir string) []string { } // Parses imports, adds necessary libs, and then user files -fn (c mut V) add_user_v_files() { - mut dir := c.dir - c.log('add_v_files($dir)') +fn (v mut V) add_user_v_files() { + mut dir := v.dir + v.log('add_v_files($dir)') // Need to store user files separately, because they have to be added after libs, but we dont know // which libs need to be added yet mut user_files := []string @@ -609,7 +611,7 @@ fn (c mut V) add_user_v_files() { } else { // Add files from the dir user is compiling (only .v files) - files := c.v_files_from_dir(dir) + files := v.v_files_from_dir(dir) for file in files { user_files << file } @@ -618,65 +620,65 @@ fn (c mut V) add_user_v_files() { println('No input .v files') exit(1) } - if c.pref.is_verbose { - c.log('user_files:') + if v.pref.is_verbose { + v.log('user_files:') println(user_files) } // Parse user imports for file in user_files { - mut p := c.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, RUN_IMPORTS) p.parse() } // Parse lib imports - if c.pref.build_mode == DEFAULT_MODE { - for i := 0; i < c.table.imports.len; i++ { - pkg := c.table.imports[i] - vfiles := c.v_files_from_dir('$TmpPath/vlib/$pkg') + if v.pref.build_mode == DEFAULT_MODE { + for i := 0; i < v.table.imports.len; i++ { + pkg := v.table.imports[i] + vfiles := v.v_files_from_dir('$TmpPath/vlib/$pkg') // Add all imports referenced by these libs for file in vfiles { - mut p := c.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, RUN_IMPORTS) p.parse() } } } else { // TODO this used to crash compiler? - // for pkg in c.table.imports { - for i := 0; i < c.table.imports.len; i++ { - pkg := c.table.imports[i] - // mut import_path := '$c.lang_dir/$pkg' - vfiles := c.v_files_from_dir('$c.lang_dir/vlib/$pkg') + // for pkg in v.table.imports { + for i := 0; i < v.table.imports.len; i++ { + pkg := v.table.imports[i] + // mut import_path := '$v.lang_dir/$pkg' + vfiles := v.v_files_from_dir('$v.lang_dir/vlib/$pkg') // Add all imports referenced by these libs for file in vfiles { - mut p := c.new_parser(file, RUN_IMPORTS) + mut p := v.new_parser(file, RUN_IMPORTS) p.parse() } } } - if c.pref.is_verbose { - c.log('imports:') - println(c.table.imports) + if v.pref.is_verbose { + v.log('imports:') + println(v.table.imports) } // Only now add all combined lib files - for pkg in c.table.imports { - mut module_path := '$c.lang_dir/vlib/$pkg' + for pkg in v.table.imports { + mut module_path := '$v.lang_dir/vlib/$pkg' // If we are in default mode, we don't parse vlib .v files, but header .vh files in // TmpPath/vlib // These were generated by vfmt - if c.pref.build_mode == DEFAULT_MODE || c.pref.build_mode == BUILD { + if v.pref.build_mode == DEFAULT_MODE || v.pref.build_mode == BUILD { module_path = '$TmpPath/vlib/$pkg' } - vfiles := c.v_files_from_dir(module_path) + vfiles := v.v_files_from_dir(module_path) for vfile in vfiles { - c.files << vfile + v.files << vfile } - // TODO c.files.append_array(vfiles) + // TODO v.files.append_array(vfiles) } // Add user code last for file in user_files { - c.files << file + v.files << file } - // c.files.append_array(user_files) + // v.files.append_array(user_files) } fn get_arg(joined_args, arg, def string) string { @@ -695,8 +697,8 @@ fn get_arg(joined_args, arg, def string) string { return res } -fn (c &V) log(s string) { - if !c.pref.is_verbose { +fn (v &V) log(s string) { + if !v.pref.is_verbose { return } println(s) diff --git a/compiler/parser.v b/compiler/parser.v index b6444b2f51..1d71ecc177 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -763,6 +763,21 @@ fn (p mut Parser) get_type() string { p.check(RSBR) } } + // map[string]int + if !p.builtin_pkg && p.tok == NAME && p.lit == 'map' { + p.next() + p.check(LSBR) + key_type := p.check_name() + if key_type != 'string' { + p.error('maps only support string keys for now') + } + p.check(RSBR) + val_type := p.check_name() + typ= 'map_$val_type' + p.register_map(typ) + return typ + } + // for p.tok == MUL { mul = true nr_muls++ @@ -2345,6 +2360,18 @@ fn (p mut Parser) register_array(typ string) { } } + +fn (p mut Parser) register_map(typ string) { + if typ.contains('*') { + println('bad map $typ') + return + } + if !p.table.known_type(typ) { + p.register_type_with_parent(typ, 'map') + p.cgen.typedefs << 'typedef map $typ;' + } +} + fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.is_struct_init = true mut typ := p.get_type() @@ -2390,11 +2417,12 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { if !t.has_field(field) { p.error('`$t.name` has no field `$field`') } + f := t.find_field(field) inited_fields << field p.gen('.$field = ') p.check(COLON) p.fspace() - p.expression() + p.check_types(p.bool_expression(), f.typ) if p.tok == COMMA { p.next() } @@ -2419,7 +2447,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.error('pointer field `${typ}.${field.name}` must be initialized') } def_val := type_default(field_typ) - if def_val != '' { + if def_val != '' && def_val != '{}' { p.gen('.$field.name = $def_val') if i != t.fields.len - 1 { p.gen(',') @@ -2938,6 +2966,9 @@ fn (p mut Parser) switch_statement() { } fn (p mut Parser) assert_statement() { + if p.first_run() { + return + } p.check(ASSERT) p.fspace() tmp := p.get_tmp() diff --git a/compiler/table.v b/compiler/table.v index 5c9f5dbdfb..44820fe1c7 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -8,8 +8,8 @@ struct Table { mut: types []Type consts []Var - fns []Fn - obf_ids map_int // obf_ids 'myfunction'] == 23 + fns map[string]Fn + obf_ids map[string]int // obf_ids 'myfunction'] == 23 packages []string // List of all modules registered by the application imports []string // List of all imports flags []string // ['-framework Cocoa', '-lglfw3'] @@ -112,6 +112,7 @@ fn is_float_type(typ string) bool { fn new_table(obfuscate bool) *Table { mut t := &Table { obf_ids: map[string]int{} + fns: map[string]Fn{} obfuscate: obfuscate } t.register_type('int') @@ -186,15 +187,8 @@ fn (p mut Parser) register_global(name, typ string) { } } -// TODO PERF O(N) this slows down the comiler a lot! -fn (t mut Table) register_fn(f Fn) { - // Avoid duplicate fn names TODO why? the name should already be unique? - for ff in t.fns { - if ff.name == f.name { - return - } - } - t.fns << f +fn (t mut Table) register_fn(new_fn Fn) { + t.fns[new_fn.name] = new_fn } fn (table &Table) known_type(typ string) bool { @@ -210,24 +204,17 @@ fn (table &Table) known_type(typ string) bool { return false } -// TODO PERF O(N) this slows down the compiler a lot! fn (t &Table) find_fn(name string) Fn { - for f in t.fns { - if f.name == name { - return f - } - } + f := t.fns[name] + if !isnil(f.name.str) { + return f + } return Fn{} } -// TODO PERF O(N) this slows down the compiler a lot! fn (t &Table) known_fn(name string) bool { - for f in t.fns { - if f.name == name { - return true - } - } - return false + f := t.find_fn(name) + return f.name != '' } fn (t &Table) known_const(name string) bool { @@ -240,7 +227,6 @@ fn (t mut Table) register_type(typ string) { if typ.len == 0 { return } - // println('REGISTER TYPE $typ') for typ2 in t.types { if typ2.name == typ { return @@ -553,6 +539,7 @@ fn type_default(typ string) string { case 'byte*': return '0' case 'bool': return '0' } + return '{}' return '' } @@ -568,7 +555,8 @@ fn (t &Table) is_interface(name string) bool { // Do we have fn main()? fn (t &Table) main_exists() bool { - for f in t.fns { + for entry in t.fns.entries { + f := t.fns[entry.key] if f.name == 'main' { return true } diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 7a86a22724..af89fb91d1 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -25,7 +25,7 @@ pub: // next *Entry } -pub fn new_map(cap, elm_size int) map { +fn new_map(cap, elm_size int) map { res := map { // len: len, element_size: elm_size @@ -59,15 +59,6 @@ fn (m mut map) _set(key string, val voidptr) { m.is_sorted = false } -fn volt_abs(n int) int { - // println('volt_abs($n)') - if n < 0 { - // println('< 0: -($n)') - return -n - } - return n -} - fn (m map) bs(query string, start, end int, out voidptr) { // println('bs "$query" $start -> $end') mid := start + ((end - start) / 2) diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index 06cc7ecc42..7758f71e3e 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -1,5 +1,10 @@ +struct User { + name string +} + struct A { - m map_int + m map[string]int + users map[string]User } fn (a mut A) set(key string, val int) { @@ -10,10 +15,19 @@ fn test_map() { mut m := map[string]int{} m['hi'] = 80 assert m['hi'] == 80 + //// + mut users := map[string]User{} + users['1'] = User{'Peter'} + peter := users['1'] + assert peter.name == 'Peter' mut a := A{ - m: new_map(1, sizeof(int)) + m: map[string]int{} + users: map[string]User{} } + a.users['Bob'] = User{'Bob'} + q := a.users['Bob'] + assert q.name == 'Bob' a.m['one'] = 1 a.set('two', 2) assert a.m['one'] == 1 diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 9c62625bf6..904fbc5037 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -636,7 +636,6 @@ __global g_ustring_runes []int pub fn (s string) ustring_tmp() ustring { mut res := ustring { s: s - runes: 0 } res.runes = g_ustring_runes res.runes.len = s.len