From 52f4f4026b23185f3c80470a5f6b81ef61884a8c Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 4 Oct 2019 15:48:09 +0300 Subject: [PATCH] compiler: cache modules --- compiler/cc.v | 9 +- compiler/comptime.v | 4 +- compiler/fn.v | 17 +- compiler/main.v | 337 +++++++++++++++++---------------------- compiler/module_header.v | 201 +++++++++++++++++++++++ compiler/modules.v | 16 +- compiler/parser.v | 10 +- vlib/builtin/builtin.v | 9 +- vlib/builtin/string.v | 59 ++++--- vlib/strings/builder.v | 2 +- 10 files changed, 422 insertions(+), 242 deletions(-) create mode 100644 compiler/module_header.v diff --git a/compiler/cc.v b/compiler/cc.v index 237d41c2e5..9166c9840b 100644 --- a/compiler/cc.v +++ b/compiler/cc.v @@ -63,11 +63,12 @@ fn (v mut V) cc() { v.out_name = v.out_name + '.so' } if v.pref.build_mode == .build_module { - // Create the modules directory if it's not there. - if !os.file_exists(v_modules_path) { - os.mkdir(v_modules_path) + // Create the modules & out directory if it's not there. + out_dir := v_modules_path + v.dir + if !os.dir_exists(out_dir) { + os.mkdir(out_dir) } - v.out_name = v_modules_path + v.dir + '.o' //v.out_name + v.out_name = '${out_dir}.o' //v.out_name println('Building ${v.out_name}...') } diff --git a/compiler/comptime.v b/compiler/comptime.v index 14be5a1d1b..7da150302d 100644 --- a/compiler/comptime.v +++ b/compiler/comptime.v @@ -117,7 +117,7 @@ fn (p mut Parser) comp_time() { // Parse the function and embed resulting C code in current function so that // all variables are available. pos := p.cgen.lines.len - 1 - mut pp := p.v.new_parser_file('.vwebtmpl.v') + mut pp := p.v.new_parser_from_file('.vwebtmpl.v') if !p.pref.is_debug { os.rm('.vwebtmpl.v') } @@ -279,7 +279,7 @@ fn (p mut Parser) gen_struct_str(typ Type) { for field in typ.fields { sb.writeln('\t$field.name: $' + 'a.${field.name}') } - sb.writeln("\n}'") + sb.writeln("}'") sb.writeln('}') p.v.vgen_buf.writeln(sb.str()) // Need to manually add the definition to `fns` so that it stays diff --git a/compiler/fn.v b/compiler/fn.v index 929b323ff0..8214451ef5 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -144,6 +144,7 @@ fn (p mut Parser) is_sig() bool { fn (p mut Parser) fn_decl() { p.clear_vars() // clear local vars every time a new fn is started p.fgen('fn ') + //defer { p.fgenln('\n') } // If we are in the first pass, create a new function. // In the second pass fetch the one we created. @@ -426,10 +427,17 @@ fn (p mut Parser) fn_decl() { 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 { - return + + } 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 { @@ -1092,11 +1100,6 @@ fn (f &Fn) typ_str() string { return sb.str() } -// "fn foo(a int) stirng", for .vh module headers -fn (f &Fn) v_definition() string { - return 'fn '//$f.name(${f.str_args()})' -} - // f.args => "int a, string b" fn (f &Fn) str_args(table &Table) string { mut s := '' diff --git a/compiler/main.v b/compiler/main.v index c194653fcd..2487e2f6a2 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -29,7 +29,6 @@ enum BuildMode { const ( supported_platforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'msvc', 'android', 'js', 'solaris'] - v_modules_path = os.home_dir() + '/.vmodules/' ) enum OS { @@ -41,7 +40,7 @@ enum OS { netbsd dragonfly msvc // TODO not an OS - js // TODO + js // TODO android solaris } @@ -62,54 +61,54 @@ enum Pass { struct V { mut: - os OS // the OS to build for - out_name_c string // name of the temporary C file - files []string // all V files that need to be parsed and compiled - dir string // directory (or file) being compiled (TODO rename to path?) - table &Table // table with types, vars, functions etc - cgen &CGen // C code generator + os OS // the OS to build for + out_name_c string // name of the temporary C file + files []string // all V files that need to be parsed and compiled + dir string // directory (or file) being compiled (TODO rename to path?) + table &Table // table with types, vars, functions etc + cgen &CGen // C code generator pref &Preferences // all the preferences and settings extracted to a struct for reusability - lang_dir string // "~/code/v" - out_name string // "program.exe" + lang_dir string // "~/code/v" + out_name string // "program.exe" vroot string - mod string // module being built with -lib + mod string // module being built with -lib parsers []Parser vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) } 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`), main function 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_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 - is_debuggable bool - is_debug bool // keep compiled C files - no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers) + build_mode BuildMode + nofmt bool // disable vfmt + is_test bool // `v test string_test.v` + is_script bool // single file mode (`v program.v`), main function 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_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 + is_debuggable bool + is_debug bool // keep compiled C files + no_auto_free bool // `v -nofree` disable automatic `free()` insertion for better performance in some applications (e.g. compilers) cflags string // Additional options which will be passed to the C compiler. // For example, passing -cflags -Os will cause the C compiler to optimize the generated binaries for size. // You could pass several -cflags XXX arguments. They will be merged with each other. // You can also quote several options at the same time: -cflags '-Os -fno-inline-small-functions'. - ccompiler string // the name of the used C compiler - building_v bool - autofree bool - compress bool - // Skips re-compilation of the builtin module - // to increase compilation time. - // This is on by default, since a vast majority of users do not - // work on the builtin module itself. - skip_builtin bool + ccompiler string // the name of the used C compiler + building_v bool + autofree bool + compress bool + skip_builtin bool // Skips re-compilation of the builtin module + // to increase compilation time. + // This is on by default, since a vast majority of users do not + // work on the builtin module itself. + } fn main() { @@ -202,20 +201,28 @@ fn main() { } v.table.fns.free() free(v.table) - //for p in parsers { - - //} + //for p in parsers {} println('done!') } } fn (v mut V) add_parser(parser Parser) { - for p in v.parsers { - if p.id == parser.id { - return - } - } - v.parsers << parser + for p in v.parsers { + if p.id == parser.id { + return + } + } + v.parsers << parser +} + +fn (v mut V) parse(file string, pass Pass) { + //println('parse($file, $pass)') + for i, p in v.parsers { + if p.file_path == file { + v.parsers[i].parse(pass) + return + } + } } @@ -224,7 +231,6 @@ fn (v mut V) compile() { if os.user_os() != 'windows' && v.os == .msvc { verror('Cannot build with msvc on ${os.user_os()}') } - mut cgen := v.cgen cgen.genln('// Generated by V') if v.pref.is_verbose { @@ -250,12 +256,7 @@ fn (v mut V) compile() { */ // First pass (declarations) for file in v.files { - for i, p in v.parsers { - if p.file_path == file { - v.parsers[i].parse(.decl) - break - } - } + v.parse(file, .decl) } // Main pass cgen.pass = Pass.main @@ -321,12 +322,7 @@ fn (v mut V) compile() { cgen.genln('this line will be replaced with definitions') defs_pos := cgen.lines.len - 1 for file in v.files { - for i, p in v.parsers { - if p.file_path == file { - v.parsers[i].parse(.main) - break - } - } + 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) @@ -334,8 +330,12 @@ fn (v mut V) compile() { // new vfmt is not ready yet } } + // Generate .vh if we are building a module + if v.pref.build_mode == .build_module { + v.generate_vh() + } // parse generated V code (str() methods etc) - mut vgen_parser := v.new_parser_string(v.vgen_buf.str(), 'vgen') + mut vgen_parser := v.new_parser_from_string(v.vgen_buf.str(), 'vgen') // free the string builder which held the generated methods v.vgen_buf.free() vgen_parser.parse(.main) @@ -573,8 +573,63 @@ 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() { + // Parse builtin imports + for file in v.get_builtin_files() { + // add builtins first + v.files << file + mut p := v.new_parser_from_file(file) + p.parse(.imports) + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + } + // Parse user imports + for file in v.get_user_files() { + mut p := v.new_parser_from_file(file) + p.parse(.imports) + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + } + // Parse lib imports + v.parse_lib_imports() + if v.pref.is_verbose { + 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 == 'main' { continue } // main files will get added last + + // 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 + } + // standard module + vfiles := v.v_files_from_dir(v.find_module_path(mod)) + for file in vfiles { + v.files << file + } + } + // add remaining main files last + for _, fit in v.table.file_imports { + if fit.module_name != 'main' { continue } + v.files << fit.file_path + } +} + +// get builtin files +fn (v &V) get_builtin_files() []string { + $if js { + return v.v_files_from_dir('$v.vroot/vlib/builtin/js/') + } + return v.v_files_from_dir('$v.vroot/vlib/builtin/') +} + +// get user files +fn (v &V) get_user_files() []string { mut dir := v.dir - v.log('add_v_files($dir)') + v.log('get_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 @@ -607,46 +662,25 @@ fn (v mut V) add_v_files_to_compile() { v.log('user_files:') println(user_files) } - // Parse builtin imports - for file in v.files { - mut p := v.new_parser_file(file) - p.parse(.imports) - //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } - } - // Parse user imports - for file in user_files { - mut p := v.new_parser_file(file) - p.parse(.imports) - //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } - } - // Parse lib imports -/* - if v.pref.build_mode == .default_mode { - // strange ( for mod in v.table.imports ) dosent loop all items - // for mod in v.table.imports { - for i := 0; i < v.table.imports.len; i++ { - mod := v.table.imports[i] - mod_path := v.module_path(mod) - import_path := '$v_modules_path/vlib/$mod_path' - vfiles := v.v_files_from_dir(import_path) - if vfiles.len == 0 { - verror('cannot import module $mod (no .v files in "$import_path")') - } - // Add all imports referenced by these libs - for file in vfiles { - mut p := v.new_parser_file(file, Pass.imports) - p.parse() - - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } - } + return user_files +} + +// parse deps from already parsed builtin/user files +fn (v mut V) parse_lib_imports() { + mut done_fits := []string + for { + for _, fit in v.table.file_imports { + if fit.file_path in done_fits { continue } + v.parse_file_imports(fit) + done_fits << fit.file_path } + if v.table.file_imports.size == done_fits.len { break} } - else { -*/ - // strange ( for mod in v.table.imports ) dosent loop all items - // for mod in v.table.imports { - for i := 0; i < v.table.imports.len; i++ { - mod := v.table.imports[i] +} + +// parse imports from file import table +fn (v mut V) parse_file_imports(fit &FileImportTable) { + for _, mod in fit.imports { import_path := v.find_module_path(mod) vfiles := v.v_files_from_dir(import_path) if vfiles.len == 0 { @@ -654,7 +688,7 @@ fn (v mut V) add_v_files_to_compile() { } // Add all imports referenced by these libs for file in vfiles { - mut p := v.new_parser_file(file) + mut p := v.new_parser_from_file(file) p.parse(.imports) if p.import_table.module_name != mod { verror('bad module name: $file was imported as `$mod` but it is defined as module `$p.import_table.module_name`') @@ -662,65 +696,18 @@ fn (v mut V) add_v_files_to_compile() { //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } } } - if v.pref.is_verbose { - v.log('imports:') - println(v.table.imports) - } - // graph deps +} + +// return resolved dep graph (order deps) +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 { deps_resolved.display() - verror('Import cycle detected') - } - // add imports in correct order - for mod in deps_resolved.imports() { - // Building this module? Skip. TODO it's a hack. - if mod == v.mod { - continue - } - mod_path := v.find_module_path(mod) - // 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 v.pref.build_mode == .default_mode || v.pref.build_mode == .build_module { - module_path = '$v_modules_path/vlib/$mod_p' - } -*/ - if mod == 'builtin' { continue } // builtin files were already added - vfiles := v.v_files_from_dir(mod_path) - for file in vfiles { - if !(file in v.files) { - v.files << file - } - } - } - // Add remaining user files - mut i := 0 - mut j := 0 - mut len := -1 - for _, fit in v.table.file_imports { - // Don't add a duplicate; builtin files are always there - if fit.file_path in v.files || fit.module_name == 'builtin' { - i++ - continue - } - if len == -1 { - len = i - } - j++ - // TODO remove this once imports work with .build - if v.pref.build_mode == .build_module && j >= len / 2{ - break - } - //println(fit) - //println('fit $fit.file_path') - v.files << fit.file_path - i++ + verror('import cycle detected') } + return deps_resolved } fn get_arg(joined_args, arg, def string) string { @@ -743,15 +730,6 @@ fn get_all_after(joined_args, arg, def string) string { return res } -fn (v &V) module_path(mod string) string { - // submodule support - if mod.contains('.') { - //return mod.replace('.', os.PathSeparator) - return mod.replace('.', '/') - } - return mod -} - fn (v &V) log(s string) { if !v.pref.is_verbose { return @@ -795,14 +773,14 @@ fn new_v(args[]string) &V { //out_name = '$TmpPath/vlib/${base}.o' out_name = mod + '.o' // Cross compiling? Use separate dirs for each os -/* + /* if target_os != os.user_os() { os.mkdir('$TmpPath/vlib/$target_os') out_name = '$TmpPath/vlib/$target_os/${base}.o' println('target_os=$target_os user_os=${os.user_os()}') println('!Cross compiling $out_name') } -*/ + */ } // TODO embed_vlib is temporarily the default mode. It's much slower. else if !('-embed_vlib' in args) { @@ -867,18 +845,7 @@ fn new_v(args[]string) &V { } } //println('OS=$_os') - builtin := 'builtin.v' - builtins := [ - 'array.v', - 'string.v', - 'builtin.v', - 'int.v', - 'utf8.v', - 'map.v', - 'hashmap.v', - 'option.v', - ] - //println(builtins) + // Location of all vlib files vroot := os.dir(os.executable()) //println('VROOT=$vroot') @@ -898,21 +865,6 @@ fn new_v(args[]string) &V { } //println('out_name:$out_name') mut out_name_c := os.realpath( out_name ) + '.tmp.c' - mut files := []string - // Add builtin files - //if !out_name.contains('builtin.o') { - for builtin in builtins { - mut f := '$vroot/vlib/builtin/$builtin' - __ := 1 - $if js { - f = '$vroot/vlib/builtin/js/$builtin' - } - // In default mode we use precompiled vlib.o, point to .vh files with signatures - if build_mode == .default_mode || build_mode == .build_module { - //f = '$TmpPath/vlib/builtin/${builtin}h' - } - files << f - } cflags := get_cmdline_cflags(args) @@ -954,7 +906,6 @@ fn new_v(args[]string) &V { return &V{ os: _os out_name: out_name - files: files dir: dir lang_dir: vroot table: new_table(obfuscate) diff --git a/compiler/module_header.v b/compiler/module_header.v new file mode 100644 index 0000000000..cefb3bc905 --- /dev/null +++ b/compiler/module_header.v @@ -0,0 +1,201 @@ +// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module main + +import ( + strings + os +) + +/* .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" +fn (f &Fn) v_definition() string { + //t :=time.ticks() + mut sb := strings.new_builder(100) + if f.is_public { + sb.write('pub ') + } + sb.write('fn ') + if f.is_c { + sb.write('C.') + } + if f.is_method { + recv := f.args[0] + typ := v_type_str(recv.typ) + mut mu := if recv.is_mut { 'mut' } else { '' } + if recv.ref { + mu = '&' + } + sb.write('($recv.name $mu $typ) ') + } + if f.name.contains('__') { + sb.write(f.name.all_after('__') + '(') + } else { + sb.write('$f.name(') + } + for i, arg in f.args { + typ := v_type_str(arg.typ) + if arg.name == '' { + sb.write(typ) + } else { + sb.write('$arg.name $typ') + } + if i != f.args.len - 1 { + sb.write(', ') + } + } + sb.write(')') + if f.typ != 'void' { + typ := v_type_str(f.typ) + sb.write(' ') + sb.write(typ) + sb.writeln(' ') + } + //println('ms: ${time.ticks() - t}') + return sb.str() +} + +fn v_type_str(typ_ string) string { + typ := if typ_.ends_with('*') { + '*' + typ_.left(typ_.len - 1) + } else { + typ_ + } + //println('"$typ"') + if typ == '*void' { + return 'voidptr' + } + if typ == '*byte' { + return 'byteptr' + } + if typ.starts_with('array_') { + return '[]' + typ.right(6) + } + if typ.contains('__') { + return typ.all_after('__') + } + return typ.replace('Option_', '?') +} + +fn (v &V) generate_vh() { + println('Generating a V header file for module `$v.mod`') + dir := v_modules_path + v.mod + path := dir + '.vh' + if !os.dir_exists(dir) { + os.mkdir(dir) + } + println(path) + + file := os.create(path) or { panic(err) } + // Consts + file.writeln('// $v.mod module header \n') + file.writeln('// Consts') + if v.table.consts.len > 0 { + file.writeln('const (') + for i, c in v.table.consts { + if c.mod != v.mod { + continue + } + println('$i $c.name') + //if !c.name.contains('__') { + //continue + //} + name := c.name.all_after('__') + typ := v_type_str(c.typ) + file.writeln('\t$name $typ') + } + file.writeln(')\n') + // Globals + for var in v.table.consts { + if var.mod != v.mod { + continue + } + if !var.is_global { + continue + } + name := var.name.all_after('__') + typ := v_type_str(var.typ) + file.writeln('__global $name $typ') + } + file.writeln('\n') + } + // Types + file.writeln('// Types') + for _, typ in v.table.typesmap { + if typ.mod != v.mod { + continue + } + if typ.name.contains('__') { + continue + } + if typ.cat == .struct_ { + file.writeln('struct $typ.name {') + // Private fields + for field in typ.fields { + if field.access_mod == .public { + continue + } + field_type := v_type_str(field.typ) + file.writeln('\t$field.name $field_type') + } + file.writeln('pub:') + 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') + } + file.writeln('}\n') + } + } + // Functions & methods + file.writeln('// Functions') + // Public first + mut fns := []Fn + // TODO fns := v.table.fns.filter(.mod == v.mod) + for _, f in v.table.fns { + if f.mod == v.mod { + fns << f + } + } + for _, f in fns { + if !f.is_public { + continue + } + file.writeln(f.v_definition()) + } + // Private + for _, f in fns { + if f.is_public { + continue + } + file.writeln(f.v_definition()) + } + // Methods + file.writeln('\n// Methods //////////////////') + for _, typ in v.table.typesmap { + if typ.mod != v.mod { + continue + } + for method in typ.methods { + file.writeln(method.v_definition()) + } + } + file.close() + + /* + for i, p in v.parsers { + if v.parsers[i].vh_lines.len > 0 { + os.write_file(p.file_name +'.vh', v.parsers[i].vh_lines.join('\n')) + } + } + */ +} + diff --git a/compiler/modules.v b/compiler/modules.v index 2e607d7203..0e4c594097 100644 --- a/compiler/modules.v +++ b/compiler/modules.v @@ -6,6 +6,10 @@ module main import os +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 { @@ -21,14 +25,20 @@ pub fn(graph mut DepGraph) from_import_tables(import_tables map[string]FileImpor pub fn(graph &DepGraph) imports() []string { mut mods := []string for node in graph.nodes { - if node.name == 'main' { - continue - } mods << node.name } return mods } +fn (v &V) module_path(mod string) string { + // submodule support + if mod.contains('.') { + //return mod.replace('.', os.PathSeparator) + return mod.replace('.', '/') + } + return mod +} + // 'strings' => 'VROOT/vlib/strings' // 'installed_mod' => '~/.vmodules/installed_mod' // 'local_mod' => '/path/to/current/dir/local_mod' diff --git a/compiler/parser.v b/compiler/parser.v index 9e6f678161..666e7cca1c 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -96,7 +96,7 @@ 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_string(text string, id string) Parser { +fn (v mut V) new_parser_from_string(text string, id string) Parser { mut p := v.new_parser(new_scanner(text), id) p.scan_tokens() v.add_parser(p) @@ -104,7 +104,7 @@ fn (v mut V) new_parser_string(text string, id string) Parser { } // new parser from file. -fn (v mut V) new_parser_file(path string) Parser { +fn (v mut V) new_parser_from_file(path string) Parser { //println('new_parser("$path")') mut path_pcguard := '' mut path_platform := '.v' @@ -505,7 +505,9 @@ fn (p mut Parser) const_decl() { if p.first_pass() && p.table.known_const(name) { p.error('redefinition of `$name`') } - p.table.register_const(name, typ, p.mod) + if p.first_pass() { + p.table.register_const(name, typ, p.mod) + } if p.pass == .main { // TODO hack // cur_line has const's value right now. if it's just a number, then optimize generation: @@ -898,6 +900,7 @@ if p.scanner.line_comment != '' { } +[inline] fn (p &Parser) first_pass() bool { return p.pass == .decl } @@ -1221,6 +1224,7 @@ 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 } diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 9ee6548e82..10b06e6ffd 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -9,7 +9,6 @@ fn C.memmove(byteptr, byteptr, int) //fn C.malloc(int) byteptr fn C.realloc(byteptr, int) byteptr - pub fn exit(code int) { C.exit(code) } @@ -91,17 +90,17 @@ pub fn println(s string) { pub fn eprintln(s string) { if isnil(s.str) { panic('eprintln(NIL)') - } + } $if mac { C.fprintf(stderr, '%.*s\n', s.len, s.str) C.fflush(stderr) - return - } + return + } $if linux { C.fprintf(stderr, '%.*s\n', s.len, s.str) C.fflush(stderr) return - } + } // TODO issues with stderr and cross compiling for Linux println(s) } diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 76582058bf..1586e7279a 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -5,39 +5,39 @@ module builtin /* -NB: A V string should be/is immutable from the point of view of - V user programs after it is first created. A V string is - also slightly larger than the equivalent C string because +NB: A V string should be/is immutable from the point of view of + V user programs after it is first created. A V string is + also slightly larger than the equivalent C string because the V string also has an integer length attached. - + This tradeoff is made, since V strings are created just *once*, but potentially used *many times* over their lifetime. - + The V string implementation uses a struct, that has a .str field, which points to a C style 0 terminated memory block. Although not - strictly necessary from the V point of view, that additional 0 + strictly necessary from the V point of view, that additional 0 is *very useful for C interoperability*. - - The V string implementation also has an integer .len field, - containing the length of the .str field, excluding the + + The V string implementation also has an integer .len field, + containing the length of the .str field, excluding the terminating 0 (just like the C's strlen(s) would do). - + The 0 ending of .str, and the .len field, mean that in practice: a) a V string s can be used very easily, wherever a C string is needed, just by passing s.str, without a need for further conversion/copying. - - b) where strlen(s) is needed, you can just pass s.len, - without having to constantly recompute the length of s + + b) where strlen(s) is needed, you can just pass s.len, + without having to constantly recompute the length of s *over and over again* like some C programs do. This is because V strings are immutable and so their length does not change. - Ordinary V code *does not need* to be concerned with the - additional 0 in the .str field. The 0 *must* be put there by the + Ordinary V code *does not need* to be concerned with the + additional 0 in the .str field. The 0 *must* be put there by the low level string creating functions inside this module. - - Failing to do this will lead to programs that work most of the - time, when used with pure V functions, but fail in strange ways, + + Failing to do this will lead to programs that work most of the + time, when used with pure V functions, but fail in strange ways, when used with modules using C functions (for example os and so on). */ @@ -69,7 +69,7 @@ fn todo() { } // String data is reused, not copied. pub fn tos(s byteptr, len int) string { // This should never happen. - if isnil(s) { + if s == 0 { panic('tos(): nil string') } return string { @@ -79,7 +79,7 @@ pub fn tos(s byteptr, len int) string { } pub fn tos_clone(s byteptr) string { - if isnil(s) { + if s == 0 { panic('tos: nil string') } return tos2(s).clone() @@ -88,12 +88,23 @@ pub fn tos_clone(s byteptr) string { // Same as `tos`, but calculates the length. Called by `string(bytes)` casts. // Used only internally. fn tos2(s byteptr) string { - if isnil(s) { + if s == 0 { panic('tos2: nil string') } - len := vstrlen(s) - res := tos(s, len) - return res + return string { + str: s + len: vstrlen(s) + } +} + +fn tos3(s *C.char) string { + if s == 0 { + panic('tos3: nil string') + } + return string { + str: byteptr(s) + len: C.strlen(s) + } } pub fn (a string) clone() string { diff --git a/vlib/strings/builder.v b/vlib/strings/builder.v index a36037c231..59a3e52c5c 100644 --- a/vlib/strings/builder.v +++ b/vlib/strings/builder.v @@ -13,7 +13,7 @@ pub: pub fn new_builder(initial_size int) Builder { return Builder { - //buf: _make(0, initial_size, sizeof(byte)) + // buf: _make(0, initial_size, sizeof(byte)) } }