From a9a73d93153d868648e079d369f1601e40ab67cd Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 7 Oct 2019 01:31:01 +0300 Subject: [PATCH] caching modules: almost there --- compiler/cc.v | 12 ++--- compiler/cgen.v | 3 ++ compiler/compile_errors.v | 2 +- compiler/comptime.v | 5 +- compiler/fn.v | 45 +++++------------ compiler/gen_c.v | 21 ++++---- compiler/main.v | 104 ++++++++++++++++++-------------------- compiler/module_header.v | 50 ++++++++++++------ compiler/msvc.v | 23 ++++----- compiler/parser.v | 55 ++++++++++++-------- compiler/vhelp.v | 4 -- vlib/os2/os2_mac.v | 32 ++++++++++++ vlib/os2/os2_test.v | 9 ++++ vlib/strings/similarity.v | 3 +- vlib/time/time.v | 2 + 15 files changed, 209 insertions(+), 161 deletions(-) create mode 100644 vlib/os2/os2_mac.v create mode 100644 vlib/os2/os2_test.v diff --git a/compiler/cc.v b/compiler/cc.v index 9ba1082d18..ba5fa990c8 100644 --- a/compiler/cc.v +++ b/compiler/cc.v @@ -106,14 +106,13 @@ fn (v mut V) cc() { a << f } - mut libs := ''// builtin.o os.o http.o etc + libs := ''// builtin.o os.o http.o etc if v.pref.build_mode == .build_module { a << '-c' } - else if v.pref.build_mode == .embed_vlib { - // - } else if v.pref.build_mode == .default_mode { + /* + // TODO libs = '$v_modules_path/vlib/builtin.o' if !os.file_exists(libs) { println('object file `$libs` not found') @@ -125,6 +124,7 @@ fn (v mut V) cc() { } libs += ' "$v_modules_path/vlib/${imp}.o"' } + */ } if v.pref.sanitize { a << '-fsanitize=leak' @@ -248,8 +248,8 @@ fn (v mut V) cc() { println('linux cross compilation done. resulting binary: "$v.out_name"') } */ - if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { - os.rm(v.out_name_c) + if !v.pref.is_debug && v.out_name_c != 'v.c' { + //os.rm(v.out_name_c) } if v.pref.compress { $if windows { diff --git a/compiler/cgen.v b/compiler/cgen.v index c675c9a6f5..5e8c1c0abc 100644 --- a/compiler/cgen.v +++ b/compiler/cgen.v @@ -161,6 +161,9 @@ fn (g mut CGen) set_placeholder(pos int, val string) { } fn (g mut CGen) insert_before(val string) { + if g.nogen { + return + } prev := g.lines[g.lines.len - 1] g.lines[g.lines.len - 1] = '$prev \n $val \n' } diff --git a/compiler/compile_errors.v b/compiler/compile_errors.v index cdbd258127..e0cd92f4e2 100644 --- a/compiler/compile_errors.v +++ b/compiler/compile_errors.v @@ -254,7 +254,7 @@ fn (s mut Scanner) get_scanner_pos_of_token(t &Token) ScannerPos { sptoken.pos += tcol } s.ignore_line() s.eat_single_newline() - sline := s.text.substr( prevlinepos, s.pos ).trim_right('\r\n') + sline := s.text.substr( prevlinepos, s.pos )//.trim_right('\r\n') s.file_lines << sline } ////////////////////////////////////////////////// diff --git a/compiler/comptime.v b/compiler/comptime.v index becb64bca9..bb594786d3 100644 --- a/compiler/comptime.v +++ b/compiler/comptime.v @@ -144,9 +144,8 @@ fn (p mut Parser) comp_time() { // #include, #flag, #v fn (p mut Parser) chash() { hash := p.lit.trim_space() - // println('chsh() file=$p.file is_sig=${p.is_sig()} hash="$hash"') + // println('chsh() file=$p.file hash="$hash"') p.next() - is_sig := p.is_sig() if hash.starts_with('flag ') { mut flag := hash.right(5) // expand `@VROOT` `@VMOD` to absolute path @@ -157,7 +156,7 @@ fn (p mut Parser) chash() { return } if hash.starts_with('include') { - if p.first_pass() && !is_sig { + if p.first_pass() && !p.is_vh { if p.file_pcguard.len != 0 { //println('p: $p.file_platform $p.file_pcguard') p.cgen.includes << '$p.file_pcguard\n#$hash\n#endif' diff --git a/compiler/fn.v b/compiler/fn.v index 52062223a1..b91a80c6d4 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -134,12 +134,6 @@ fn (p mut Parser) clear_vars() { } } -// vlib header file? -fn (p mut Parser) is_sig() bool { - return (p.pref.build_mode == .default_mode || p.pref.build_mode == .build_module) && - (p.file_path.contains(v_modules_path)) -} - // Function signatures are added to the top of the .c file in the first run. fn (p mut Parser) fn_decl() { p.clear_vars() // clear local vars every time a new fn is started @@ -231,10 +225,9 @@ fn (p mut Parser) fn_decl() { // C function header def? (fn C.NSMakeRect(int,int,int,int)) is_c := f.name == 'C' && p.tok == .dot // Just fn signature? only builtin.v + default build mode - // is_sig := p.builtin_mod && p.pref.build_mode == default_mode - // is_sig := p.pref.build_mode == default_mode && (p.builtin_mod || p.file.contains(LANG_TMP)) - is_sig := p.is_sig() - // println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ') + if p.pref.build_mode == .build_module { + println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen') + } if is_c { p.check(.dot) f.name = p.check_name() @@ -312,13 +305,15 @@ fn (p mut Parser) fn_decl() { } p.cgen.typedefs << 'typedef struct $typ $typ;' } - // Translated C code can have empty functions (just definitions) - is_fn_header := !is_c && !is_sig && (p.pref.translated || p.pref.is_test) && p.tok != .lcbr + // Translated C code and .vh can have empty functions (just definitions) + is_fn_header := !is_c && !p.is_vh && + (p.pref.translated || p.pref.is_test || p.is_vh) && + p.tok != .lcbr if is_fn_header { f.is_decl = true } // { required only in normal function declarations - if !is_c && !is_sig && !is_fn_header { + if !is_c && !p.is_vh && !is_fn_header { p.fgen(' ') p.check(.lcbr) } @@ -350,7 +345,7 @@ fn (p mut Parser) fn_decl() { mut fn_name_cgen := p.table.fn_gen_name(f) // Start generation of the function body skip_main_in_test := false - if !is_c && !is_live && !is_sig && !is_fn_header && !skip_main_in_test { + if !is_c && !is_live && !p.is_vh && !is_fn_header && !skip_main_in_test { if p.pref.obfuscate { p.genln('; // $f.name') } @@ -403,10 +398,10 @@ fn (p mut Parser) fn_decl() { // println('register_fn typ=$typ isg=$is_generic') p.table.register_fn(f) } - if is_sig || p.first_pass() || is_live || is_fn_header || skip_main_in_test { + if p.is_vh || p.first_pass() || is_live || is_fn_header || skip_main_in_test { // First pass? Skip the body for now // Look for generic calls. - if !is_sig && !is_fn_header { + if !p.is_vh && !is_fn_header { p.skip_fn_body() } // Live code reloading? Load all fns from .so @@ -422,19 +417,8 @@ fn (p mut Parser) fn_decl() { } // Add function definition to the top if !is_c && p.first_pass() { - // TODO hack to make Volt compile without -embed_vlib - if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode { - - } else { - p.cgen.fns << fn_decl + ';' - } + p.cgen.fns << fn_decl + ';' } - // Generate .vh header files when building a module - /* - if p.pref.build_mode == .build_module { - p.vh_genln(f.v_definition()) - } - */ return } if p.attr == 'live' && p.pref.is_so { @@ -448,8 +432,7 @@ fn (p mut Parser) fn_decl() { } } // println('is_c=$is_c name=$f.name') - if is_c || is_sig || is_fn_header { - // println('IS SIG .key_returnING tok=${p.strtok()}') + if is_c || p.is_vh || is_fn_header { return } // Profiling mode? Start counting at the beginning of the function (save current time). @@ -465,7 +448,7 @@ fn (p mut Parser) fn_decl() { p.cgen.nogen = true } p.statements_no_rcbr() - p.cgen.nogen = false + //p.cgen.nogen = false // Print counting result after all statements in main if p.pref.is_prof && f.name == 'main' { p.genln(p.print_prof_counters()) diff --git a/compiler/gen_c.v b/compiler/gen_c.v index 3a8ce285cd..c957738e1c 100644 --- a/compiler/gen_c.v +++ b/compiler/gen_c.v @@ -169,7 +169,8 @@ fn (p mut Parser) index_get(typ string, fn_ph int, cfg IndexCfg) { if cfg.is_map { p.gen('$tmp') def := type_default(typ) - p.cgen.insert_before('$typ $tmp = $def; bool $tmp_ok = map_get($index_expr, & $tmp);') + p.cgen.insert_before('$typ $tmp = $def; ' + + 'bool $tmp_ok = map_get(/*$p.file_name : $p.scanner.line_nr*/$index_expr, & $tmp);') } else if cfg.is_arr { if p.pref.translated && !p.builtin_mod { @@ -214,18 +215,18 @@ fn (table mut Table) fn_gen_name(f &Fn) string { // Obfuscate but skip certain names // TODO ugly, fix // NB: the order here is from faster to potentially slower checks - if table.obfuscate && + if table.obfuscate && !f.is_c && - f.name != 'main' && f.name != 'WinMain' && f.name != 'main__main' && + f.name != 'main' && f.name != 'WinMain' && f.name != 'main__main' && f.name != 'gg__vec2' && - f.name != 'build_token_str' && - f.name != 'build_keys' && - f.mod != 'builtin' && - f.mod != 'darwin' && - f.mod != 'os' && + f.name != 'build_token_str' && + f.name != 'build_keys' && + f.mod != 'builtin' && + f.mod != 'darwin' && + f.mod != 'os' && f.mod != 'json' && - !f.name.contains('window_proc') && - !name.ends_with('_str') && + !f.name.contains('window_proc') && + !name.ends_with('_str') && !name.contains('contains') { mut idx := table.obf_ids[name] // No such function yet, register it diff --git a/compiler/main.v b/compiler/main.v index 57a9cb74cd..52367c534b 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -18,9 +18,6 @@ enum BuildMode { // `v program.v' // Build user code only, and add pre-compiled vlib (`cc program.o builtin.o os.o...`) default_mode - // `v -embed_vlib program.v` - // vlib + user code in one file (slower compilation, but easier when working on vlib and cross-compiling) - embed_vlib // `v -lib ~/v/os` // build any module (generate os.o + os.vh) build_module @@ -290,36 +287,25 @@ fn (v mut V) compile() { cgen.genln('#define V_COMMIT_HASH "' + vhash() + '"') cgen.genln('#endif') } - + q := cgen.nogen // TODO hack + cgen.nogen = false $if js { cgen.genln(js_headers) } $else { cgen.genln(CommonCHeaders) } - v.generate_hotcode_reloading_declarations() - - imports_json := 'json' in v.table.imports - // TODO remove global UI hack - if v.os == .mac && ((v.pref.build_mode == .embed_vlib && 'ui' in - v.table.imports) || (v.pref.build_mode == .build_module && - v.dir.contains('/ui'))) { - cgen.genln('id defaultFont = 0; // main.v') - } // We need the cjson header for all the json decoding that will be done in // default mode + imports_json := 'json' in v.table.imports if v.pref.build_mode == .default_mode { if imports_json { cgen.genln('#include "cJSON.h"') } } - if v.pref.build_mode == .embed_vlib || v.pref.build_mode == .default_mode { - //if v.pref.build_mode in [.embed_vlib, .default_mode] { + if 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') - //if v.pref.is_test { $if !js { cgen.genln('int g_test_oks = 0;') cgen.genln('int g_test_fails = 0;') @@ -335,11 +321,14 @@ fn (v mut V) compile() { } //cgen.genln('/*================================== FNS =================================*/') cgen.genln('this line will be replaced with definitions') - defs_pos := cgen.lines.len - 1 + mut defs_pos := cgen.lines.len - 1 + if defs_pos == -1 { + defs_pos = 0 + } + cgen.nogen = q for file in v.files { v.parse(file, .main) //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } - // p.g.gen_x64() // Format all files (don't format automatically generated vlib headers) if !v.pref.nofmt && !file.contains('/vlib/') { // new vfmt is not ready yet @@ -356,25 +345,25 @@ fn (v mut V) compile() { vgen_parser.parse(.main) // v.parsers.add(vgen_parser) v.log('Done parsing.') - // Write everything - mut d := strings.new_builder(10000)// Avoid unnecessary allocations + // All definitions + mut def := strings.new_builder(10000)// Avoid unnecessary allocations $if !js { - d.writeln(cgen.includes.join_lines()) - d.writeln(cgen.typedefs.join_lines()) - d.writeln(v.type_definitions()) - d.writeln('\nstring _STR(const char*, ...);\n') - d.writeln('\nstring _STR_TMP(const char*, ...);\n') - d.writeln(cgen.fns.join_lines()) // fn definitions + def.writeln(cgen.includes.join_lines()) + def.writeln(cgen.typedefs.join_lines()) + def.writeln(v.type_definitions()) + def.writeln('\nstring _STR(const char*, ...);\n') + def.writeln('\nstring _STR_TMP(const char*, ...);\n') + def.writeln(cgen.fns.join_lines()) // fn definitions } $else { - d.writeln(v.type_definitions()) + def.writeln(v.type_definitions()) } - d.writeln(cgen.consts.join_lines()) - d.writeln(cgen.thread_args.join_lines()) + def.writeln(cgen.consts.join_lines()) + def.writeln(cgen.thread_args.join_lines()) if v.pref.is_prof { - d.writeln('; // Prof counters:') - d.writeln(v.prof_counters()) + def.writeln('; // Prof counters:') + def.writeln(v.prof_counters()) } - cgen.lines[defs_pos] = d.str() + cgen.lines[defs_pos] = def.str() v.generate_main() v.generate_hot_reload_code() if v.pref.is_verbose { @@ -394,8 +383,7 @@ fn (v mut V) generate_main() { mut cgen := v.cgen $if js { return } - // if v.build_mode in [.default, .embed_vlib] { - if v.pref.build_mode == .default_mode || v.pref.build_mode == .embed_vlib { + if v.pref.build_mode == .default_mode { mut consts_init_body := cgen.consts_init.join_lines() // vlib can't have `init_consts()` cgen.genln('void init_consts() { @@ -591,8 +579,14 @@ fn (v &V) v_files_from_dir(dir string) []string { // Parses imports, adds necessary libs, and then user files fn (v mut V) add_v_files_to_compile() { + mut builtin_files := v.get_builtin_files() + // Builtin cache exists? Use it. + builtin_vh := '$v_modules_path/builtin.vh' + if v.pref.is_debug && os.file_exists(builtin_vh) { + builtin_files = [builtin_vh] + } // Parse builtin imports - for file in v.get_builtin_files() { + for file in builtin_files { // add builtins first v.files << file mut p := v.new_parser_from_file(file) @@ -613,19 +607,25 @@ fn (v mut V) add_v_files_to_compile() { v.log('imports:') println(v.table.imports) } - // resolve deps & add imports in correct order - for mod in v.resolve_deps().imports() { - // if mod == v.mod { continue } // Building this module? Skip. TODO it's a hack. - if mod == 'builtin' { continue } // builtin already added - if mod == 'main' { continue } // main files will get added last + // resolve deps and add imports in correct order + imported_mods := v.resolve_deps().imports() + for mod in imported_mods { + if mod == 'builtin' || mod == 'main' { + // builtin already added + // main files will get added last + continue + } // use cached built module if exists - // vh_path := '$v_modules_path/${mod}.vh' - // if os.file_exists(vh_path) { - // println('using cached module `$mod`: $vh_path') - // v.files << vh_path - // continue - // } + if v.pref.build_mode != .build_module { + vh_path := '$v_modules_path/${mod}.vh' + //println(vh_path) + if v.pref.is_debug && os.file_exists(vh_path) { + println('using cached module `$mod`: $vh_path') + v.files << vh_path + continue + } + } // standard module mod_path := v.find_module_path(mod) or { verror(err) break } vfiles := v.v_files_from_dir(mod_path) @@ -640,8 +640,9 @@ fn (v mut V) add_v_files_to_compile() { } } -// get builtin files fn (v &V) get_builtin_files() []string { + // .vh cache exists? Use it + $if js { return v.v_files_from_dir('$v.vroot${os.PathSeparator}vlib${os.PathSeparator}builtin${os.PathSeparator}js') } @@ -812,11 +813,6 @@ fn new_v(args[]string) &V { } */ } - // TODO embed_vlib is temporarily the default mode. It's much slower. - else if !('-embed_vlib' in args) { - build_mode = .embed_vlib - } - // is_test := dir.ends_with('_test.v') is_script := dir.ends_with('.v') if is_script && !os.file_exists(dir) { diff --git a/compiler/module_header.v b/compiler/module_header.v index f661118f3f..04e2ddfc62 100644 --- a/compiler/module_header.v +++ b/compiler/module_header.v @@ -9,9 +9,10 @@ import ( os ) -/* .vh generation logic. - .vh files contains only function signatures, consts, and types. - They are used together with pre-compiled modules. +/* + .vh generation logic. + .vh files contains only function signatures, consts, and types. + They are used together with pre-compiled modules. */ // "fn foo(a int) string" @@ -27,7 +28,7 @@ fn (f &Fn) v_definition() string { } if f.is_method { recv := f.args[0] - typ := v_type_str(recv.typ) + typ := v_type_str(recv.typ).replace('*', '') mut mu := if recv.is_mut { 'mut' } else { '' } if recv.ref { mu = '&' @@ -40,6 +41,9 @@ fn (f &Fn) v_definition() string { sb.write('$f.name(') } for i, arg in f.args { + if i == 0 && f.is_method { // skip the receiver + continue + } typ := v_type_str(arg.typ) if arg.name == '' { sb.write(typ) @@ -62,11 +66,12 @@ fn (f &Fn) v_definition() string { } fn v_type_str(typ_ string) string { - typ := if typ_.ends_with('*') { + mut typ := if typ_.ends_with('*') { '*' + typ_.left(typ_.len - 1) } else { typ_ } + typ = typ.replace('Option_', '?') //println('"$typ"') if typ == '*void' { return 'voidptr' @@ -80,11 +85,11 @@ fn v_type_str(typ_ string) string { if typ.contains('__') { return typ.all_after('__') } - return typ.replace('Option_', '?') + return typ } fn (v &V) generate_vh() { - println('Generating a V header file for module `$v.mod`') + println('\n\n\n\nGenerating a V header file for module `$v.mod`') dir := '$v_modules_path${os.PathSeparator}$v.mod' path := dir + '.vh' if !os.dir_exists(dir) { @@ -95,6 +100,7 @@ fn (v &V) generate_vh() { file := os.create(path) or { panic(err) } // Consts file.writeln('// $v.mod module header \n') + file.writeln('module $v.mod') file.writeln('// Consts') if v.table.consts.len > 0 { file.writeln('const (') @@ -126,16 +132,20 @@ fn (v &V) generate_vh() { file.writeln('\n') } // Types - file.writeln('// Types') + file.writeln('// Types2') for _, typ in v.table.typesmap { - if typ.mod != v.mod { + //println(typ.name) + if typ.mod != v.mod && typ.mod != ''{ // int, string etc mod == '' + //println('skipping type "$typ.name"') continue } + mut name := typ.name if typ.name.contains('__') { - continue + name = typ.name.all_after('__') } - if typ.cat == .struct_ { - file.writeln('struct $typ.name {') + if typ.cat in [TypeCategory.struct_, .c_struct] { + c := if typ.is_c { 'C.' } else { '' } + file.writeln('struct ${c}$name {') // Private fields for field in typ.fields { if field.access_mod == .public { @@ -144,13 +154,18 @@ fn (v &V) generate_vh() { field_type := v_type_str(field.typ) file.writeln('\t$field.name $field_type') } - file.writeln('pub:') + //file.writeln('pub:') + mut public_str := '' for field in typ.fields { if field.access_mod == .private { continue } field_type := v_type_str(field.typ) - file.writeln('\t$field.name $field_type') + public_str += '\t$field.name $field_type\n' + //file.writeln('\t$field.name $field_type') + } + if public_str != '' { + file.writeln('pub:' + public_str) } file.writeln('}\n') } @@ -161,8 +176,10 @@ fn (v &V) generate_vh() { mut fns := []Fn // TODO fns := v.table.fns.filter(.mod == v.mod) for _, f in v.table.fns { - if f.mod == v.mod { + if f.mod == v.mod || f.mod == ''{ fns << f + } else { + println('skipping fn $f.name mod=$f.mod') } } for _, f in fns { @@ -181,7 +198,8 @@ fn (v &V) generate_vh() { // Methods file.writeln('\n// Methods //////////////////') for _, typ in v.table.typesmap { - if typ.mod != v.mod { + if typ.mod != v.mod { //&& typ.mod != '' { + println('skipping method typ $typ.name mod=$typ.mod') continue } for method in typ.methods { diff --git a/compiler/msvc.v b/compiler/msvc.v index 11b062e209..3d4cd5eb75 100644 --- a/compiler/msvc.v +++ b/compiler/msvc.v @@ -29,7 +29,7 @@ const ( KEY_ENUMERATE_SUB_KEYS = (0x0008) ) -// Given a root key look for one of the subkeys in 'versions' and get the path +// Given a root key look for one of the subkeys in 'versions' and get the path fn find_windows_kit_internal(key RegKey, versions []string) ?string { $if windows { for version in versions { @@ -139,7 +139,7 @@ fn find_vs() ?VsInstallation { } // Emily: // VSWhere is guaranteed to be installed at this location now - // If its not there then end user needs to update their visual studio + // If its not there then end user needs to update their visual studio // installation! res := os.exec('""%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath"') or { return error(err) @@ -207,7 +207,7 @@ fn find_msvc() ?MsvcResult { } } -pub fn (v mut V) cc_msvc() { +pub fn (v mut V) cc_msvc() { r := find_msvc() or { // TODO: code reuse if !v.pref.is_debug && v.out_name_c != 'v.c' && v.out_name_c != 'v_macos.c' { @@ -253,9 +253,6 @@ pub fn (v mut V) cc_msvc() { mut alibs := []string // builtin.o os.o http.o etc if v.pref.build_mode == .build_module { } - else if v.pref.build_mode == .embed_vlib { - // - } else if v.pref.build_mode == .default_mode { b := os.realpath( '$v_modules_path/vlib/builtin.obj' ) alibs << '"$b"' @@ -334,8 +331,8 @@ pub fn (v mut V) cc_msvc() { args := a.join(' ') - cmd := '""$r.full_cl_exe_path" $args"' - // It is hard to see it at first, but the quotes above ARE balanced :-| ... + cmd := '""$r.full_cl_exe_path" $args"' + // It is hard to see it at first, but the quotes above ARE balanced :-| ... // Also the double quotes at the start ARE needed. if v.pref.show_c_cmd || v.pref.is_verbose { println('\n========== cl cmd line:') @@ -376,16 +373,16 @@ fn build_thirdparty_obj_file_with_msvc(path string, moduleflags []CFlag) { if os.file_exists(obj_path) { println('$obj_path already build.') - return - } + return + } - println('$obj_path not found, building it (with msvc)...') + println('$obj_path not found, building it (with msvc)...') parent := os.dir(obj_path) files := os.ls(parent) - mut cfiles := '' + mut cfiles := '' for file in files { - if file.ends_with('.c') { + if file.ends_with('.c') { cfiles += '"' + os.realpath( parent + os.PathSeparator + file ) + '" ' } } diff --git a/compiler/parser.v b/compiler/parser.v index 1a4839fc77..868c78478e 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -51,7 +51,6 @@ mut: tmp_cnt int is_script bool builtin_mod bool - vh_lines []string inside_if_expr bool inside_unwrapping_match_statement bool inside_return_expr bool @@ -73,7 +72,7 @@ mut: v_script bool // "V bash", import all os functions into global space var_decl_name string // To allow declaring the variable so that it can be used in the struct initialization is_alloc bool // Whether current expression resulted in an allocation - is_const_literal bool // `1`, `2.0` etc, so that `u64 == 0` works + is_const_literal bool // `1`, `2.0` etc, so that `u64_var == 0` works cur_gen_type string // "App" to replace "T" in current generic function is_vweb bool is_sql bool @@ -81,6 +80,7 @@ mut: sql_i int // $1 $2 $3 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) } const ( @@ -100,7 +100,6 @@ fn (v mut V) new_parser_from_string(text string, id string) Parser { return p } -// new parser from file. fn (v mut V) new_parser_from_file(path string) Parser { //println('new_parser("$path")') mut path_pcguard := '' @@ -119,7 +118,8 @@ fn (v mut V) new_parser_from_file(path string) Parser { file_name: path.all_after(os.PathSeparator), file_platform: path_platform, file_pcguard: path_pcguard, - is_script: (v.pref.is_script && os.realpath(path) == os.realpath(path)) + is_script: (v.pref.is_script && os.realpath(path) == os.realpath(path)), + is_vh: path.ends_with('.vh') } if p.pref.building_v { p.scanner.should_print_relative_paths_on_error = false @@ -249,6 +249,14 @@ fn (p mut Parser) parse(pass Pass) { p.fspace() p.mod = p.check_name() } + // + + p.cgen.nogen = false + if p.pref.build_mode == .build_module && p.mod != p.v.mod { + //println('skipping $p.mod (v.mod = $p.v.mod)') + p.cgen.nogen = true + //defer { p.cgen.nogen = false } + } p.fgenln('\n') p.builtin_mod = p.mod == 'builtin' p.can_chash = p.mod=='ui' || p.mod == 'darwin'// TODO tmp remove @@ -343,16 +351,15 @@ fn (p mut Parser) parse(pass Pass) { mut g := p.table.cgen_name_type_pair(name, typ) if p.tok == .assign { p.next() - // p.gen(' = ') g += ' = ' - p.cgen.start_tmp() - p.bool_expression() - // g += '<<< ' + p.cgen.end_tmp() + '>>>' - g += p.cgen.end_tmp() + _, expr := p.tmp_expr() + g += expr } // p.genln('; // global') g += '; // global' - p.cgen.consts << g + if !p.cgen.nogen { + p.cgen.consts << g + } case TokenKind.eof: //p.log('end of parse()') // TODO: check why this was added? everything seems to work @@ -484,27 +491,36 @@ fn (p mut Parser) const_decl() { p.fgenln('') p.fmt_inc() for p.tok == .name { - if p.lit == '_' && p.peek() == .assign { + if p.lit == '_' && p.peek() == .assign && !p.cgen.nogen { p.gen_blank_identifier_assign() + //if !p.cgen.nogen { p.cgen.consts_init << p.cgen.cur_line.trim_space() p.cgen.resetln('') + //} continue } - // `Age = 20` - mut name := p.check_name() + mut name := p.check_name() // `Age = 20` //if ! (name[0] >= `A` && name[0] <= `Z`) { //p.error('const name must be capitalized') //} name = p.prepend_mod(name) - p.check_space(.assign) - typ := p.expression() + mut typ := '' + if p.is_vh { + // .vh files don't have const values, just types: `const (a int)` + typ = p.get_type() + p.table.register_const(name, typ, p.mod) + continue // Don't generate C code when building a .vh file + } else { + p.check_space(.assign) + typ = p.expression() + } if p.first_pass() && p.table.known_const(name) { p.error('redefinition of `$name`') } if p.first_pass() { p.table.register_const(name, typ, p.mod) } - if p.pass == .main { + if p.pass == .main && !p.cgen.nogen { // TODO hack // cur_line has const's value right now. if it's just a number, then optimize generation: // output a #define so that we don't pollute the binary with unnecessary global vars @@ -1219,11 +1235,6 @@ fn (p mut Parser) gen(s string) { } // Generate V header from V source -fn (p mut Parser) vh_genln(s string) { - //println('vh $s') - p.vh_lines << s -} - fn (p mut Parser) statement(add_semi bool) string { if p.returns && !p.is_vweb { p.error('unreachable code') @@ -2806,7 +2817,7 @@ fn (p mut Parser) string_expr() { p.gen('"$f"') } else { - p.gen('tos2((byte*)"$f")') + p.gen('tos3("$f")') } p.next() return diff --git a/compiler/vhelp.v b/compiler/vhelp.v index 83197e46a5..de4a7f1fab 100644 --- a/compiler/vhelp.v +++ b/compiler/vhelp.v @@ -70,8 +70,4 @@ Options/commands: /* - To disable automatic formatting: v -nofmt file.v - -- To build a program with an embedded vlib (use this if you do not have prebuilt vlib libraries or if you -are working on vlib) -v -embed_vlib file.v */ diff --git a/vlib/os2/os2_mac.v b/vlib/os2/os2_mac.v new file mode 100644 index 0000000000..0d5a47ce3a --- /dev/null +++ b/vlib/os2/os2_mac.v @@ -0,0 +1,32 @@ +module os2 + +#include + +struct File { + fd int +} + +fn C.open(byteptr, int, int) int +fn C.write(voidptr, byteptr, int) int + +pub fn create(path string) ?File { + fd := C.creat(path.str, 0644)//511) + if fd == -1 { + return error('failed to create "$path":') + //os.print_c_errno() + } + return File{fd} +} + +pub fn (f File) writeln(s string) { + ss := s + '\n' + ret := C.write(f.fd, ss.str, s.len + 1) + if ret == -1 { + C.perror('failed to write') + } +} + +pub fn (f File) close() { + C.close(f.fd) +} + diff --git a/vlib/os2/os2_test.v b/vlib/os2/os2_test.v new file mode 100644 index 0000000000..0b2a1a689a --- /dev/null +++ b/vlib/os2/os2_test.v @@ -0,0 +1,9 @@ +import os2 + +fn test_open() { + $if mac { + f := os2.create('os2.test') + f.writeln('hello world!') + f.close() + } +} diff --git a/vlib/strings/similarity.v b/vlib/strings/similarity.v index 1f733ee4e1..12648d307c 100644 --- a/vlib/strings/similarity.v +++ b/vlib/strings/similarity.v @@ -45,7 +45,8 @@ pub fn dice_coefficient(s1, s2 string) f32 { mut first_bigrams := map[string]int for i := 0; i < a.len-1; i++ { bigram := a.substr(i, i+2) - first_bigrams[bigram] = if bigram in first_bigrams { first_bigrams[bigram]+1 } else { 1 } + q := if bigram in first_bigrams { first_bigrams[bigram]+1 } else { 1 } + first_bigrams[bigram] = q } mut intersection_size := 0 for i := 0; i < b.len-1; i++ { diff --git a/vlib/time/time.v b/vlib/time/time.v index 5e42dc8bd8..3c4d888fe1 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -29,6 +29,8 @@ fn C.localtime(int) &C.tm fn remove_me_when_c_bug_is_fixed() { // TODO } +struct C.time_t {} + struct C.tm { tm_year int tm_mon int