From 53c64abdeb644a0339ef5d5c1c64708a79a3b88d Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 13 Oct 2019 16:37:43 +0300 Subject: [PATCH] compiler: make compiler an ordinary vlib/compiler module * Move compiler/ under vlib/compiler/ . * Add a minimal compiler/main.v driver program. * Cleanup compiler/main.v . * Make most compiler tests pass again. * Apply the fix by @joe-conigliaro , so that the rest of the compiler tests are fixed too. * Thanks to @avitkauskas, now the vlib/vcompiler/tests/str_gen_test.v test does not need to be special cased anymore. * Reapply @joe-conigliaro fix for vgen. --- compiler/main.v | 1060 +---------------- {compiler => vlib/compiler}/cc.v | 2 +- {compiler => vlib/compiler}/cflags.v | 2 +- {compiler => vlib/compiler}/cgen.v | 2 +- {compiler => vlib/compiler}/cheaders.v | 2 +- {compiler => vlib/compiler}/compile_errors.v | 2 +- {compiler => vlib/compiler}/comptime.v | 2 +- {compiler => vlib/compiler}/depgraph.v | 2 +- {compiler => vlib/compiler}/fn.v | 2 +- {compiler => vlib/compiler}/gen_c.v | 2 +- {compiler => vlib/compiler}/gen_js.v | 2 +- {compiler => vlib/compiler}/jsgen.v | 2 +- {compiler => vlib/compiler}/live.v | 2 +- vlib/compiler/main.v | 1043 ++++++++++++++++ {compiler => vlib/compiler}/module_header.v | 2 +- {compiler => vlib/compiler}/modules.v | 2 +- {compiler => vlib/compiler}/msvc.v | 2 +- {compiler => vlib/compiler}/optimization.v | 2 +- {compiler => vlib/compiler}/parser.v | 4 +- {compiler => vlib/compiler}/parser2.v | 2 +- {compiler => vlib/compiler}/query.v | 2 +- {compiler => vlib/compiler}/repl.v | 6 +- {compiler => vlib/compiler}/scanner.v | 2 +- {compiler => vlib/compiler}/table.v | 2 +- .../compiler}/tests/bench/val_vs_ptr.c | 0 .../compiler}/tests/defer_test.v | 0 {compiler => vlib/compiler}/tests/enum_test.v | 0 .../compiler}/tests/fixed_array_test.v | 0 .../tests/fn_multiple_returns_test.v | 0 {compiler => vlib/compiler}/tests/fn_test.v | 0 .../compiler}/tests/fn_variadic_test.v | 0 .../compiler}/tests/interface_test.v | 0 .../compiler}/tests/local/local.v | 0 .../compiler}/tests/local_test.v | 0 .../compiler}/tests/match_test.v | 0 .../compiler}/tests/module_test.v | 0 {compiler => vlib/compiler}/tests/msvc_test.v | 0 {compiler => vlib/compiler}/tests/mut_test.v | 0 .../compiler}/tests/option_test.v | 0 .../compiler}/tests/repl/.gitattributes | 0 .../compiler}/tests/repl/.gitignore | 0 .../compiler}/tests/repl/README.md | 0 .../compiler}/tests/repl/arr_decl.repl | 0 .../tests/repl/chained_fields.correct.repl | 0 .../compiler}/tests/repl/chained_fields.repl | 0 .../tests/repl/conditional_blocks.repl | 0 .../tests/repl/default_printing.repl | 0 .../compiler}/tests/repl/empty_struct.repl | 0 .../compiler}/tests/repl/error.repl | 0 .../compiler}/tests/repl/error_nosave.repl | 0 .../compiler}/tests/repl/function.repl | 0 .../tests/repl/immutable_len_fields.repl | 0 .../compiler}/tests/repl/interpolation.repl | 0 .../compiler}/tests/repl/multiple_decl.repl | 0 .../tests/repl/multiple_println.repl | 0 .../compiler}/tests/repl/naked_strings.repl | 0 .../compiler}/tests/repl/newlines.repl | 0 .../compiler}/tests/repl/nothing.repl | 0 .../compiler}/tests/repl/println.repl | 0 .../compiler}/tests/repl/repl_test.v | 0 {compiler => vlib/compiler}/tests/repl/run.v | 2 +- .../compiler}/tests/repl/runner/runner.v | 2 +- .../compiler}/tests/repl/var_decl.repl | 0 .../compiler}/tests/return_voidptr_test.v | 0 .../compiler}/tests/str_gen_test.v | 0 .../tests/string_interpolation_test.v | 0 .../compiler}/tests/struct_test.v | 0 {compiler => vlib/compiler}/token.v | 2 +- {compiler => vlib/compiler}/vfmt.v | 2 +- {compiler => vlib/compiler}/vhelp.v | 2 +- {compiler => vlib/compiler}/vtest.v | 12 +- 71 files changed, 1095 insertions(+), 1080 deletions(-) mode change 100644 => 100755 compiler/main.v rename {compiler => vlib/compiler}/cc.v (99%) rename {compiler => vlib/compiler}/cflags.v (99%) rename {compiler => vlib/compiler}/cgen.v (99%) rename {compiler => vlib/compiler}/cheaders.v (99%) rename {compiler => vlib/compiler}/compile_errors.v (99%) rename {compiler => vlib/compiler}/comptime.v (99%) rename {compiler => vlib/compiler}/depgraph.v (99%) rename {compiler => vlib/compiler}/fn.v (99%) rename {compiler => vlib/compiler}/gen_c.v (99%) rename {compiler => vlib/compiler}/gen_js.v (99%) rename {compiler => vlib/compiler}/jsgen.v (99%) rename {compiler => vlib/compiler}/live.v (99%) create mode 100644 vlib/compiler/main.v rename {compiler => vlib/compiler}/module_header.v (99%) rename {compiler => vlib/compiler}/modules.v (99%) rename {compiler => vlib/compiler}/msvc.v (99%) rename {compiler => vlib/compiler}/optimization.v (98%) rename {compiler => vlib/compiler}/parser.v (99%) rename {compiler => vlib/compiler}/parser2.v (99%) rename {compiler => vlib/compiler}/query.v (99%) rename {compiler => vlib/compiler}/repl.v (98%) rename {compiler => vlib/compiler}/scanner.v (99%) rename {compiler => vlib/compiler}/table.v (99%) rename {compiler => vlib/compiler}/tests/bench/val_vs_ptr.c (100%) rename {compiler => vlib/compiler}/tests/defer_test.v (100%) rename {compiler => vlib/compiler}/tests/enum_test.v (100%) rename {compiler => vlib/compiler}/tests/fixed_array_test.v (100%) rename {compiler => vlib/compiler}/tests/fn_multiple_returns_test.v (100%) rename {compiler => vlib/compiler}/tests/fn_test.v (100%) rename {compiler => vlib/compiler}/tests/fn_variadic_test.v (100%) rename {compiler => vlib/compiler}/tests/interface_test.v (100%) rename {compiler => vlib/compiler}/tests/local/local.v (100%) rename {compiler => vlib/compiler}/tests/local_test.v (100%) rename {compiler => vlib/compiler}/tests/match_test.v (100%) rename {compiler => vlib/compiler}/tests/module_test.v (100%) rename {compiler => vlib/compiler}/tests/msvc_test.v (100%) rename {compiler => vlib/compiler}/tests/mut_test.v (100%) rename {compiler => vlib/compiler}/tests/option_test.v (100%) rename {compiler => vlib/compiler}/tests/repl/.gitattributes (100%) rename {compiler => vlib/compiler}/tests/repl/.gitignore (100%) rename {compiler => vlib/compiler}/tests/repl/README.md (100%) rename {compiler => vlib/compiler}/tests/repl/arr_decl.repl (100%) rename {compiler => vlib/compiler}/tests/repl/chained_fields.correct.repl (100%) rename {compiler => vlib/compiler}/tests/repl/chained_fields.repl (100%) rename {compiler => vlib/compiler}/tests/repl/conditional_blocks.repl (100%) rename {compiler => vlib/compiler}/tests/repl/default_printing.repl (100%) rename {compiler => vlib/compiler}/tests/repl/empty_struct.repl (100%) rename {compiler => vlib/compiler}/tests/repl/error.repl (100%) rename {compiler => vlib/compiler}/tests/repl/error_nosave.repl (100%) rename {compiler => vlib/compiler}/tests/repl/function.repl (100%) rename {compiler => vlib/compiler}/tests/repl/immutable_len_fields.repl (100%) rename {compiler => vlib/compiler}/tests/repl/interpolation.repl (100%) rename {compiler => vlib/compiler}/tests/repl/multiple_decl.repl (100%) rename {compiler => vlib/compiler}/tests/repl/multiple_println.repl (100%) rename {compiler => vlib/compiler}/tests/repl/naked_strings.repl (100%) rename {compiler => vlib/compiler}/tests/repl/newlines.repl (100%) rename {compiler => vlib/compiler}/tests/repl/nothing.repl (100%) rename {compiler => vlib/compiler}/tests/repl/println.repl (100%) rename {compiler => vlib/compiler}/tests/repl/repl_test.v (100%) rename {compiler => vlib/compiler}/tests/repl/run.v (93%) rename {compiler => vlib/compiler}/tests/repl/runner/runner.v (96%) rename {compiler => vlib/compiler}/tests/repl/var_decl.repl (100%) rename {compiler => vlib/compiler}/tests/return_voidptr_test.v (100%) rename {compiler => vlib/compiler}/tests/str_gen_test.v (100%) rename {compiler => vlib/compiler}/tests/string_interpolation_test.v (100%) rename {compiler => vlib/compiler}/tests/struct_test.v (100%) rename {compiler => vlib/compiler}/token.v (99%) rename {compiler => vlib/compiler}/vfmt.v (98%) rename {compiler => vlib/compiler}/vhelp.v (99%) rename {compiler => vlib/compiler}/vtest.v (96%) diff --git a/compiler/main.v b/compiler/main.v old mode 100644 new mode 100755 index 57778c6d32..a09acccce0 --- a/compiler/main.v +++ b/compiler/main.v @@ -5,127 +5,21 @@ module main import ( - os - strings + compiler benchmark ) -const ( - Version = '0.1.21' -) - -enum BuildMode { - // `v program.v' - // Build user code only, and add pre-compiled vlib (`cc program.o builtin.o os.o...`) - default_mode - // `v -lib ~/v/os` - // build any module (generate os.o + os.vh) - build_module -} - -const ( - supported_platforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', - 'netbsd', 'dragonfly', 'msvc', 'android', 'js', 'solaris'] -) - -enum OS { - mac - linux - windows - freebsd - openbsd - netbsd - dragonfly - msvc // TODO not an OS - js // TODO - android - solaris -} - -enum Pass { - // A very short pass that only looks at imports in the beginning of - // each file - imports - // First pass, only parses and saves declarations (fn signatures, - // consts, types). - // Skips function bodies. - // We need this because in V things can be used before they are - // declared. - decl - // Second pass, parses function bodies and generates C or machine code. - main -} - -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 - pref &Preferences // all the preferences and settings extracted to a struct for reusability - lang_dir string // "~/code/v" - out_name string // "program.exe" - vroot string - mod string // module being built with -lib - parsers []Parser - vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) - cached_mods []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`), 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_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. - is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly). - is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes) - // NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files, - // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). - is_cache bool // turns on v usage of the module cache to speed up compilation. - - is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run - 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 - //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() { // There's no `flags` module yet, so args have to be parsed manually - args := env_vflags_and_os_args() + args := compiler.env_vflags_and_os_args() // Print the version and exit. if '-v' in args || '--version' in args || 'version' in args { - version_hash := vhash() - println('V $Version $version_hash') + version_hash := compiler.vhash() + println('V $compiler.Version $version_hash') return } if '-h' in args || '--help' in args || 'help' in args { - println(HelpText) + println(compiler.HelpText) return } if 'translate' in args { @@ -133,7 +27,7 @@ fn main() { return } if 'up' in args { - update_v() + compiler.update_v() return } if 'get' in args { @@ -141,28 +35,29 @@ fn main() { return } if 'symlink' in args { - create_symlink() + compiler.create_symlink() return } if 'install' in args { - install_v(args) + compiler.install_v(args) return } - // TODO quit if the compiler is too old + // TODO quit if the v compiler is too old // u := os.file_last_mod_unix('v') // If there's no tmp path with current version yet, the user must be using a pre-built package // // Just fmt and exit if 'fmt' in args { - vfmt(args) + compiler.vfmt(args) return } if 'test' in args { - test_v() + compiler.test_v() return } // Construct the V object from command line arguments - mut v := new_v(args) + mut v := compiler.new_v(args) + v.pref.building_v = true if v.pref.is_verbose { println(args) } @@ -181,7 +76,7 @@ fn main() { // No args? REPL if args.len < 2 || (args.len == 2 && args[1] == '-') || 'runrepl' in args { - run_repl() + compiler.run_repl() return } @@ -195,929 +90,6 @@ fn main() { if v.pref.is_test { v.run_compiled_executable_and_exit() } - - // TODO remove - if v.pref.autofree { - println('started freeing v struct') - v.table.typesmap.free() - v.table.obf_ids.free() - v.cgen.lines.free() - free(v.cgen) - for _, f in v.table.fns { - //f.local_vars.free() - f.args.free() - //f.defer_text.free() - } - v.table.fns.free() - free(v.table) - //for p in parsers {} - println('done!') - } -} - -fn (v mut V) add_parser(parser Parser) { - v.parsers << parser -} - -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 - } - } - return error('parser for "$file" not found') -} - -// find existing parser or create new one. returns v.parsers index -fn (v mut V) parse(file string, pass Pass) int { - //println('parse($file, $pass)') - pidx := v.get_file_parser_index(file) or { - 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 - } - v.parsers[pidx].parse(pass) - //if v.parsers[i].pref.autofree { v.parsers[i].scanner.text.free() free(v.parsers[i].scanner) } - return pidx -} - - -fn (v mut V) compile() { - // Emily: Stop people on linux from being able to build with msvc - 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 { - println('all .v files before:') - println(v.files) - } - v.add_v_files_to_compile() - if v.pref.is_verbose || v.pref.is_debug { - println('all .v files:') - println(v.files) - } - /* - if v.pref.is_debug { - println('\nparsers:') - for q in v.parsers { - println(q.file_name) - } - println('\nfiles:') - for q in v.files { - println(q) - } - } - */ - // First pass (declarations) - for file in v.files { - v.parse(file, .decl) - } - - // Main pass - cgen.pass = Pass.main - if v.pref.is_debug { - $if js { - cgen.genln('const VDEBUG = 1;\n') - } $else { - cgen.genln('#define VDEBUG (1)') - } - } - if v.os == .js { - cgen.genln('#define _VJS (1) ') - } - - if v.pref.building_v { - cgen.genln('#ifndef V_COMMIT_HASH') - 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() - // 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 == .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'` - $if !js { - cgen.genln('int g_test_oks = 0;') - cgen.genln('int g_test_fails = 0;') - } - if imports_json { - cgen.genln(' -#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) -') - } - } - if '-debug_alloc' in os.args { - cgen.genln('#define DEBUG_ALLOC 1') - } - //cgen.genln('/*================================== FNS =================================*/') - cgen.genln('this line will be replaced with definitions') - 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) } - // Format all files (don't format automatically generated vlib headers) - if !v.pref.nofmt && !file.contains('/vlib/') { - // 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_from_string(v.vgen_buf.str(), 'vgen') - // free the string builder which held the generated methods - v.vgen_buf.free() - vgen_parser.parse(.main) - // v.parsers.add(vgen_parser) - - // All definitions - mut def := strings.new_builder(10000)// Avoid unnecessary allocations - $if !js { - 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 { - def.writeln(v.type_definitions()) - } - def.writeln(cgen.consts.join_lines()) - def.writeln(cgen.thread_args.join_lines()) - if v.pref.is_prof { - def.writeln('; // Prof counters:') - def.writeln(v.prof_counters()) - } - cgen.lines[defs_pos] = def.str() - v.generate_init() - v.generate_main() - v.generate_hot_reload_code() - if v.pref.is_verbose { - v.log('flags=') - for flag in v.get_os_cflags() { - println(' * ' + flag.format()) - } - } - $if js { - cgen.genln('main__main();') - } - cgen.save() - v.cc() -} - -fn (v mut V) generate_init() { - $if js { return } - if v.pref.build_mode == .build_module { - nogen := v.cgen.nogen - v.cgen.nogen = false - consts_init_body := v.cgen.consts_init.join_lines() - init_fn_name := mod_gen_name(v.mod) + '__init_consts' - v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}') - v.cgen.nogen = nogen - } - if v.pref.build_mode == .default_mode { - mut call_mod_init := '' - mut call_mod_init_consts := '' - if 'builtin' in v.cached_mods { - v.cgen.genln('void builtin__init_consts();') - call_mod_init_consts += 'builtin__init_consts();\n' - } - for mod in v.table.imports { - init_fn_name := mod_gen_name(mod) + '__init' - if v.table.known_fn(init_fn_name) { - call_mod_init += '${init_fn_name}();\n' - } - if mod in v.cached_mods { - v.cgen.genln('void ${init_fn_name}_consts();') - call_mod_init_consts += '${init_fn_name}_consts();\n' - } - } - consts_init_body := v.cgen.consts_init.join_lines() - // vlib can't have `init_consts()` - v.cgen.genln('void init() { -g_str_buf=malloc(1000); -$call_mod_init_consts -$consts_init_body -builtin__init(); -$call_mod_init -}') - // _STR function can't be defined in vlib - v.cgen.genln(' -string _STR(const char *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - size_t len = vsnprintf(0, 0, fmt, argptr) + 1; - va_end(argptr); - byte* buf = malloc(len); - va_start(argptr, fmt); - vsprintf((char *)buf, fmt, argptr); - va_end(argptr); -#ifdef DEBUG_ALLOC - puts("_STR:"); - puts(buf); -#endif - return tos2(buf); -} - -string _STR_TMP(const char *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - //size_t len = vsnprintf(0, 0, fmt, argptr) + 1; - va_end(argptr); - va_start(argptr, fmt); - vsprintf((char *)g_str_buf, fmt, argptr); - va_end(argptr); -#ifdef DEBUG_ALLOC - //puts("_STR_TMP:"); - //puts(g_str_buf); -#endif - return tos2(g_str_buf); -} - -') - } -} - -fn (v mut V) generate_main() { - mut cgen := v.cgen - $if js { return } - - if v.pref.is_vlines { - ///// After this point, the v files are compiled. - ///// The rest is auto generated code, which will not have - ///// different .v source file/line numbers. - lines_so_far := cgen.lines.join('\n').count('\n') + 5 - cgen.genln('') - cgen.genln('////////////////// Reset the file/line numbers //////////') - cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"' - cgen.genln('') - } - - // Make sure the main function exists - // Obviously we don't need it in libraries - if v.pref.build_mode != .build_module { - if !v.table.main_exists() && !v.pref.is_test { - // It can be skipped in single file programs - if v.pref.is_script { - //println('Generating main()...') - v.gen_main_start(true) - cgen.genln('$cgen.fn_main;') - v.gen_main_end('return 0') - } - else { - verror('function `main` is not declared in the main module') - } - } - else if v.pref.is_test { - if v.table.main_exists() { - verror('test files cannot have function `main`') - } - if !v.table.has_at_least_one_test_fn() { - verror('test files need to have at least one test function') - } - // Generate a C `main`, which calls every single test function - v.gen_main_start(false) - - if v.pref.is_stats { cgen.genln('BenchedTests bt = main__start_testing();') } - - for _, f in v.table.fns { - if f.name.starts_with('main__test_') { - if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') } - cgen.genln('$f.name();') - if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') } - } - } - if v.pref.is_stats { cgen.genln('BenchedTests_end_testing(&bt);') } - v.gen_main_end('return g_test_fails > 0') - } - else if v.table.main_exists() { - v.gen_main_start(true) - cgen.genln(' main__main();') - v.gen_main_end('return 0') - } - } -} - -fn (v mut V) gen_main_start(add_os_args bool){ - v.cgen.genln('int main(int argc, char** argv) { ') - v.cgen.genln(' init();') - if add_os_args && 'os' in v.table.imports { - v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') - } - v.generate_hotcode_reloading_main_caller() - v.cgen.genln('') -} -fn (v mut V) gen_main_end(return_statement string){ - v.cgen.genln('') - v.cgen.genln(' $return_statement;') - v.cgen.genln('}') -} - -fn final_target_out_name(out_name string) string { - $if windows { - return out_name.replace('/', '\\') + '.exe' - } - return if out_name.starts_with('/') { - out_name - } - else { - './' + out_name - } -} - -fn (v V) run_compiled_executable_and_exit() { - if v.pref.is_verbose { - println('============ running $v.out_name ============') - } - mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"' - if os.args.len > 3 { - cmd += ' ' + os.args.right(3).join(' ') - } - if v.pref.is_test { - ret := os.system(cmd) - if ret != 0 { - exit(1) - } - } - if v.pref.is_run { - ret := os.system(cmd) - // TODO: make the runner wrapping as transparent as possible - // (i.e. use execve when implemented). For now though, the runner - // just returns the same exit code as the child process. - exit( ret ) - } - exit(0) -} - -fn (v &V) v_files_from_dir(dir string) []string { - mut res := []string - if !os.file_exists(dir) { - verror('$dir doesn\'t exist') - } else if !os.dir_exists(dir) { - verror('$dir isn\'t a directory') - } - mut files := os.ls(dir) - if v.pref.is_verbose { - println('v_files_from_dir ("$dir")') - } - files.sort() - for file in files { - if !file.ends_with('.v') && !file.ends_with('.vh') { - continue - } - if file.ends_with('_test.v') { - continue - } - if file.ends_with('_win.v') && (v.os != .windows && v.os != .msvc) { - continue - } - if file.ends_with('_lin.v') && v.os != .linux { - continue - } - if file.ends_with('_mac.v') && v.os != .mac { - continue - } - if file.ends_with('_nix.v') && (v.os == .windows || v.os == .msvc) { - continue - } - if file.ends_with('_js.v') && v.os != .js { - continue - } - if file.ends_with('_c.v') && v.os == .js { - continue - } - res << '$dir${os.path_separator}$file' - } - return res -} - -// 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${os.path_separator}vlib${os.path_separator}builtin.vh' - if v.pref.is_cache && os.file_exists(builtin_vh) { - v.cached_mods << 'builtin' - builtin_files = [builtin_vh] - } - // Parse builtin imports - for file in 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) } - v.add_parser(p) - } - // 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) } - v.add_parser(p) - } - // Parse lib imports - v.parse_lib_imports() - if v.pref.is_verbose { - v.log('imports:') - println(v.table.imports) - } - // 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 - if v.pref.build_mode != .build_module && !mod.contains('vweb') { - mod_path := mod.replace('.', os.path_separator) - vh_path := '$v_modules_path${os.path_separator}vlib${os.path_separator}${mod_path}.vh' - if v.pref.is_cache && os.file_exists(vh_path) { - println('using cached module `$mod`: $vh_path') - v.cached_mods << mod - 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) - 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_id - } -} - -fn (v &V) get_builtin_files() []string { - // .vh cache exists? Use it - - $if js { - return v.v_files_from_dir('$v.vroot${os.path_separator}vlib${os.path_separator}builtin${os.path_separator}js') - } - return v.v_files_from_dir('$v.vroot${os.path_separator}vlib${os.path_separator}builtin') -} - -// get user files -fn (v &V) get_user_files() []string { - mut dir := v.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 - - if v.pref.is_test && v.pref.is_stats { - user_files << [v.vroot, 'vlib', 'benchmark', 'tests', 'always_imported.v'].join( os.path_separator ) - } - - // v volt/slack_test.v: compile all .v files to get the environment - // I need to implement user packages! TODO - is_test_with_imports := dir.ends_with('_test.v') && - (dir.contains('${os.path_separator}volt') || dir.contains('${os.path_separator}c2volt'))// TODO - if is_test_with_imports { - user_files << dir - pos := dir.last_index(os.path_separator) - dir = dir.left(pos) + os.path_separator// TODO WHY IS THIS .neEDED? - } - if dir.ends_with('.v') { - // Just compile one file and get parent dir - user_files << dir - dir = dir.all_before('${os.path_separator}') - } - else { - // Add .v files from the directory being compiled - files := v.v_files_from_dir(dir) - for file in files { - user_files << file - } - } - if user_files.len == 0 { - println('No input .v files') - exit(1) - } - if v.pref.is_verbose { - v.log('user_files:') - println(user_files) - } - return user_files -} - -// parse deps from already parsed builtin/user files -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 { - 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)) - 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)) - } - // 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 - 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) - } - } - 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) -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') - } - return deps_resolved -} - -fn get_arg(joined_args, arg, def string) string { - return get_param_after(joined_args, '-$arg', def) -} - -fn get_param_after(joined_args, arg, def string) string { - key := '$arg ' - mut pos := joined_args.index(key) - if pos == -1 { - return def - } - pos += key.len - mut space := joined_args.index_after(' ', pos) - if space == -1 { - space = joined_args.len - } - res := joined_args.substr(pos, space) - return res -} - -fn (v &V) log(s string) { - if !v.pref.is_verbose { - return - } - println(s) -} - -fn new_v(args[]string) &V { - // Create modules dirs if they are missing - if !os.dir_exists(v_modules_path) { - os.mkdir(v_modules_path) - os.mkdir('$v_modules_path${os.path_separator}cache') - } - - mut vgen_buf := strings.new_builder(1000) - vgen_buf.writeln('module main\nimport strings') - - joined_args := args.join(' ') - target_os := get_arg(joined_args, 'os', '') - mut out_name := get_arg(joined_args, 'o', 'a.out') - - mut dir := args.last() - if 'run' in args { - dir = get_param_after(joined_args, 'run', '') - } - if dir.ends_with(os.path_separator) { - dir = dir.all_before_last(os.path_separator) - } - if dir.starts_with('.$os.path_separator') { - dir = dir.right(2) - } - if args.len < 2 { - dir = '' - } - // build mode - mut build_mode := BuildMode.default_mode - mut mod := '' - if joined_args.contains('build module ') { - build_mode = .build_module - // v build module ~/v/os => os.o - mod_path := if dir.contains('vlib') { - dir.all_after('vlib'+os.path_separator) - } - else if dir.starts_with('.\\') || dir.starts_with('./') { - dir.right(2) - } - else if dir.starts_with(os.path_separator) { - dir.all_after(os.path_separator) - } else { - dir - } - mod = mod_path.replace(os.path_separator, '.') - println('Building module "${mod}" (dir="$dir")...') - //out_name = '$TmpPath/vlib/${base}.o' - out_name = mod - // 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') - } - */ - } - is_test := dir.ends_with('_test.v') - is_script := dir.ends_with('.v') - if is_script && !os.file_exists(dir) { - println('`$dir` does not exist') - exit(1) - } - // No -o provided? foo.v => foo - if out_name == 'a.out' && dir.ends_with('.v') && dir != '.v' { - out_name = dir.left(dir.len - 2) - } - // if we are in `/foo` and run `v .`, the executable should be `foo` - if dir == '.' && out_name == 'a.out' { - base := os.getwd().all_after(os.path_separator) - out_name = base.trim_space() - } - mut _os := OS.mac - // No OS specifed? Use current system - if target_os == '' { - $if linux { - _os = .linux - } - $if mac { - _os = .mac - } - $if windows { - _os = .windows - } - $if freebsd { - _os = .freebsd - } - $if openbsd { - _os = .openbsd - } - $if netbsd { - _os = .netbsd - } - $if dragonfly { - _os = .dragonfly - } - $if solaris { - _os = .solaris - } - } - else { - _os = os_from_string(target_os) - } - // Location of all vlib files - vroot := os.dir(os.executable()) - //println('VROOT=$vroot') - // v.exe's parent directory should contain vlib - if !os.dir_exists(vroot) || !os.dir_exists(vroot + '/vlib/builtin') { - println('vlib not found, downloading it...') - /* - ret := os.system('git clone --depth=1 https://github.com/vlang/v .') - if ret != 0 { - println('failed to `git clone` vlib') - println('make sure you are online and have git installed') - exit(1) - } - */ - println('vlib not found. It should be next to the V executable. ') - println('Go to https://vlang.io to install V.') - exit(1) - } - // println('out_name:$out_name') - mut out_name_c := os.realpath('${out_name}.tmp.c') - - cflags := get_cmdline_cflags(args) - - rdir := os.realpath( dir ) - rdir_name := os.filename( rdir ) - - obfuscate := '-obf' in args - is_repl := '-repl' in args - pref := &Preferences { - is_test: is_test - is_script: is_script - is_so: '-shared' in args - is_prod: '-prod' in args - is_verbose: '-verbose' in args || '--verbose' in args - - is_debug: '-g' in args || '-cg' in args - is_vlines: '-g' in args && !('-cg' in args) - is_keep_c: '-keep_c' in args - is_cache: '-cache' in args - - is_stats: '-stats' in args - obfuscate: obfuscate - is_prof: '-prof' in args - is_live: '-live' in args - sanitize: '-sanitize' in args - nofmt: '-nofmt' in args - show_c_cmd: '-show_c_cmd' in args - translated: 'translated' in args - is_run: 'run' in args - autofree: '-autofree' in args - compress: '-compress' in args - is_repl: is_repl - build_mode: build_mode - cflags: cflags - ccompiler: find_c_compiler() - building_v: !is_repl && (rdir_name == 'compiler' || dir.contains('vlib')) - } - if pref.is_verbose || pref.is_debug { - println('C compiler=$pref.ccompiler') - } - if pref.is_so { - out_name_c = out_name.all_after(os.path_separator) + '_shared_lib.c' - } - return &V{ - os: _os - out_name: out_name - dir: dir - lang_dir: vroot - table: new_table(obfuscate) - out_name_c: out_name_c - cgen: new_cgen(out_name_c) - vroot: vroot - pref: pref - mod: mod - vgen_buf: vgen_buf - } -} - -fn env_vflags_and_os_args() []string { - mut args := []string - vflags := os.getenv('VFLAGS') - if '' != vflags { - args << os.args[0] - args << vflags.split(' ') - if os.args.len > 1 { - args << os.args.right(1) - } - } else{ - args << os.args - } - return args -} - -fn update_v() { - println('Updating V...') - vroot := os.dir(os.executable()) - s := os.exec('git -C "$vroot" pull --rebase origin master') or { - verror(err) - return - } - println(s.output) - $if windows { - v_backup_file := '$vroot/v_old.exe' - if os.file_exists( v_backup_file ) { - os.rm( v_backup_file ) - } - os.mv('$vroot/v.exe', v_backup_file) - s2 := os.exec('"$vroot/make.bat"') or { - verror(err) - return - } - println(s2.output) - } $else { - s2 := os.exec('make -C "$vroot"') or { - verror(err) - return - } - println(s2.output) - } -} - -fn vfmt(args[]string) { - file := args.last() - if !os.file_exists(file) { - println('"$file" does not exist') - exit(1) - } - if !file.ends_with('.v') { - println('v fmt can only be used on .v files') - exit(1) - } - println('vfmt is temporarily disabled') -} - -fn install_v(args[]string) { - if args.len < 3 { - println('usage: v install [module] [module] [...]') - return - } - names := args.slice(2, args.len) - vexec := os.executable() - vroot := os.dir(vexec) - vget := '$vroot/tools/vget' - if true { - //println('Building vget...') - os.chdir(vroot + '/tools') - vget_compilation := os.exec('$vexec -o $vget vget.v') or { - verror(err) - return - } - if vget_compilation.exit_code != 0 { - verror( vget_compilation.output ) - return - } - } - vgetresult := os.exec('$vget ' + names.join(' ')) or { - verror(err) - return - } - if vgetresult.exit_code != 0 { - verror( vgetresult.output ) - return - } -} - -fn create_symlink() { - vexe := os.executable() - link_path := '/usr/local/bin/v' - ret := os.system('ln -sf $vexe $link_path') - if ret == 0 { - println('symlink "$link_path" has been created') - } else { - println('failed to create symlink "$link_path", '+ - 'make sure you run with sudo') - } -} - -pub fn verror(s string) { - println('V error: $s') - os.flush_stdout() - exit(1) -} - -fn vhash() string { - mut buf := [50]byte - buf[0] = 0 - C.snprintf(*char(buf), 50, '%s', C.V_COMMIT_HASH ) - return tos_clone(buf) -} - -fn cescaped_path(s string) string { - return s.replace('\\','\\\\') -} - -fn os_from_string(os string) OS { - switch os { - case 'linux': return .linux - case 'windows': return .windows - case 'mac': return .mac - case 'freebsd': return .freebsd - case 'openbsd': return .openbsd - case 'netbsd': return .netbsd - case 'dragonfly': return .dragonfly - case 'msvc': return .msvc - case 'js': return .js - case 'solaris': return .solaris - case 'android': return .android - } - println('bad os $os') // todo panic? - return .linux + + v.finalize_compilation() } diff --git a/compiler/cc.v b/vlib/compiler/cc.v similarity index 99% rename from compiler/cc.v rename to vlib/compiler/cc.v index 61eb079f3b..94054ab0a2 100644 --- a/compiler/cc.v +++ b/vlib/compiler/cc.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import ( os diff --git a/compiler/cflags.v b/vlib/compiler/cflags.v similarity index 99% rename from compiler/cflags.v rename to vlib/compiler/cflags.v index 2393870337..da6d4a2cb7 100644 --- a/compiler/cflags.v +++ b/vlib/compiler/cflags.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import os diff --git a/compiler/cgen.v b/vlib/compiler/cgen.v similarity index 99% rename from compiler/cgen.v rename to vlib/compiler/cgen.v index 16da086c86..c0fdcdfaab 100644 --- a/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import os diff --git a/compiler/cheaders.v b/vlib/compiler/cheaders.v similarity index 99% rename from compiler/cheaders.v rename to vlib/compiler/cheaders.v index 05a6a187f2..ee75567399 100644 --- a/compiler/cheaders.v +++ b/vlib/compiler/cheaders.v @@ -1,4 +1,4 @@ -module main +module compiler const ( diff --git a/compiler/compile_errors.v b/vlib/compiler/compile_errors.v similarity index 99% rename from compiler/compile_errors.v rename to vlib/compiler/compile_errors.v index 82745dc945..308d86fa76 100644 --- a/compiler/compile_errors.v +++ b/vlib/compiler/compile_errors.v @@ -1,4 +1,4 @@ -module main +module compiler import ( os diff --git a/compiler/comptime.v b/vlib/compiler/comptime.v similarity index 99% rename from compiler/comptime.v rename to vlib/compiler/comptime.v index 987018d347..96c672f38e 100644 --- a/compiler/comptime.v +++ b/vlib/compiler/comptime.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import ( vweb.tmpl // for `$vweb_html()` diff --git a/compiler/depgraph.v b/vlib/compiler/depgraph.v similarity index 99% rename from compiler/depgraph.v rename to vlib/compiler/depgraph.v index 05900ff7d7..f0e94b8455 100644 --- a/compiler/depgraph.v +++ b/vlib/compiler/depgraph.v @@ -5,7 +5,7 @@ // Directed acyclic graph // this implementation is specifically suited to ordering dependencies -module main +module compiler struct DepGraphNode { mut: diff --git a/compiler/fn.v b/vlib/compiler/fn.v similarity index 99% rename from compiler/fn.v rename to vlib/compiler/fn.v index 1b3089fd52..dd88225b07 100644 --- a/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import( strings diff --git a/compiler/gen_c.v b/vlib/compiler/gen_c.v similarity index 99% rename from compiler/gen_c.v rename to vlib/compiler/gen_c.v index 6e842bb9ee..194b6756a0 100644 --- a/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -1,4 +1,4 @@ -module main +module compiler import strings diff --git a/compiler/gen_js.v b/vlib/compiler/gen_js.v similarity index 99% rename from compiler/gen_js.v rename to vlib/compiler/gen_js.v index c37d255d48..b09c3b6f68 100644 --- a/compiler/gen_js.v +++ b/vlib/compiler/gen_js.v @@ -1,4 +1,4 @@ -module main +module compiler import strings diff --git a/compiler/jsgen.v b/vlib/compiler/jsgen.v similarity index 99% rename from compiler/jsgen.v rename to vlib/compiler/jsgen.v index e3676ecb6b..8552aa433d 100644 --- a/compiler/jsgen.v +++ b/vlib/compiler/jsgen.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler // TODO replace with comptime code generation. // TODO remove cJSON dependency. diff --git a/compiler/live.v b/vlib/compiler/live.v similarity index 99% rename from compiler/live.v rename to vlib/compiler/live.v index fcb6339829..f17c6e2ac8 100644 --- a/compiler/live.v +++ b/vlib/compiler/live.v @@ -1,4 +1,4 @@ -module main +module compiler import os import time diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v new file mode 100644 index 0000000000..5b730b047a --- /dev/null +++ b/vlib/compiler/main.v @@ -0,0 +1,1043 @@ +// 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 compiler + +import ( + os + strings +) + +const ( + Version = '0.1.21' +) + +enum BuildMode { + // `v program.v' + // Build user code only, and add pre-compiled vlib (`cc program.o builtin.o os.o...`) + default_mode + // `v -lib ~/v/os` + // build any module (generate os.o + os.vh) + build_module +} + +const ( + supported_platforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', + 'netbsd', 'dragonfly', 'msvc', 'android', 'js', 'solaris'] +) + +enum OS { + mac + linux + windows + freebsd + openbsd + netbsd + dragonfly + msvc // TODO not an OS + js // TODO + android + solaris +} + +enum Pass { + // A very short pass that only looks at imports in the beginning of + // each file + imports + // First pass, only parses and saves declarations (fn signatures, + // consts, types). + // Skips function bodies. + // We need this because in V things can be used before they are + // declared. + decl + // Second pass, parses function bodies and generates C or machine code. + main +} + +struct V { +pub 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 + pref &Preferences // all the preferences and settings extracted to a struct for reusability + lang_dir string // "~/code/v" + out_name string // "program.exe" + vroot string + mod string // module being built with -lib + parsers []Parser + vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) + cached_mods []string +} + +struct Preferences { +pub 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_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. + is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly). + is_keep_c bool // -keep_c , tell v to leave the generated .tmp.c alone (since by default v will delete them after c backend finishes) + // NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files, + // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). + is_cache bool // turns on v usage of the module cache to speed up compilation. + + is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run + 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 + //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. +} + +// Should be called by main at the end of the compilation process, to cleanup +pub fn (v mut V) finalize_compilation(){ + // TODO remove + if v.pref.autofree { + println('started freeing v struct') + v.table.typesmap.free() + v.table.obf_ids.free() + v.cgen.lines.free() + free(v.cgen) + for _, f in v.table.fns { + //f.local_vars.free() + f.args.free() + //f.defer_text.free() + } + v.table.fns.free() + free(v.table) + //for p in parsers {} + println('done!') + } +} + +pub fn (v mut V) add_parser(parser Parser) { + v.parsers << parser +} + +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 + } + } + return error('parser for "$file" not found') +} + +// find existing parser or create new one. returns v.parsers index +pub fn (v mut V) parse(file string, pass Pass) int { + //println('parse($file, $pass)') + pidx := v.get_file_parser_index(file) or { + 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 + } + v.parsers[pidx].parse(pass) + //if v.parsers[i].pref.autofree { v.parsers[i].scanner.text.free() free(v.parsers[i].scanner) } + return pidx +} + + +pub fn (v mut V) compile() { + // Emily: Stop people on linux from being able to build with msvc + 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 { + println('all .v files before:') + println(v.files) + } + v.add_v_files_to_compile() + if v.pref.is_verbose || v.pref.is_debug { + println('all .v files:') + println(v.files) + } + /* + if v.pref.is_debug { + println('\nparsers:') + for q in v.parsers { + println(q.file_name) + } + println('\nfiles:') + for q in v.files { + println(q) + } + } + */ + // First pass (declarations) + for file in v.files { + v.parse(file, .decl) + } + + // Main pass + cgen.pass = Pass.main + if v.pref.is_debug { + $if js { + cgen.genln('const VDEBUG = 1;\n') + } $else { + cgen.genln('#define VDEBUG (1)') + } + } + if v.os == .js { + cgen.genln('#define _VJS (1) ') + } + + if v.pref.building_v { + cgen.genln('#ifndef V_COMMIT_HASH') + 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() + // 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 == .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'` + $if !js { + cgen.genln('int g_test_oks = 0;') + cgen.genln('int g_test_fails = 0;') + } + if imports_json { + cgen.genln(' +#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) +') + } + } + if '-debug_alloc' in os.args { + cgen.genln('#define DEBUG_ALLOC 1') + } + //cgen.genln('/*================================== FNS =================================*/') + cgen.genln('this line will be replaced with definitions') + 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) } + // Format all files (don't format automatically generated vlib headers) + if !v.pref.nofmt && !file.contains('/vlib/') { + // 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_from_string(v.vgen_buf.str(), 'vgen') + // free the string builder which held the generated methods + v.vgen_buf.free() + vgen_parser.parse(.main) + // v.parsers.add(vgen_parser) + + // All definitions + mut def := strings.new_builder(10000)// Avoid unnecessary allocations + $if !js { + 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 { + def.writeln(v.type_definitions()) + } + def.writeln(cgen.consts.join_lines()) + def.writeln(cgen.thread_args.join_lines()) + if v.pref.is_prof { + def.writeln('; // Prof counters:') + def.writeln(v.prof_counters()) + } + cgen.lines[defs_pos] = def.str() + v.generate_init() + v.generate_main() + v.generate_hot_reload_code() + if v.pref.is_verbose { + v.log('flags=') + for flag in v.get_os_cflags() { + println(' * ' + flag.format()) + } + } + $if js { + cgen.genln('main__main();') + } + cgen.save() + v.cc() +} + +fn (v mut V) generate_init() { + $if js { return } + if v.pref.build_mode == .build_module { + nogen := v.cgen.nogen + v.cgen.nogen = false + consts_init_body := v.cgen.consts_init.join_lines() + init_fn_name := mod_gen_name(v.mod) + '__init_consts' + v.cgen.genln('void ${init_fn_name}();\nvoid ${init_fn_name}() {\n$consts_init_body\n}') + v.cgen.nogen = nogen + } + if v.pref.build_mode == .default_mode { + mut call_mod_init := '' + mut call_mod_init_consts := '' + if 'builtin' in v.cached_mods { + v.cgen.genln('void builtin__init_consts();') + call_mod_init_consts += 'builtin__init_consts();\n' + } + for mod in v.table.imports { + init_fn_name := mod_gen_name(mod) + '__init' + if v.table.known_fn(init_fn_name) { + call_mod_init += '${init_fn_name}();\n' + } + if mod in v.cached_mods { + v.cgen.genln('void ${init_fn_name}_consts();') + call_mod_init_consts += '${init_fn_name}_consts();\n' + } + } + consts_init_body := v.cgen.consts_init.join_lines() + // vlib can't have `init_consts()` + v.cgen.genln('void init() { +g_str_buf=malloc(1000); +$call_mod_init_consts +$consts_init_body +builtin__init(); +$call_mod_init +}') + // _STR function can't be defined in vlib + v.cgen.genln(' +string _STR(const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + size_t len = vsnprintf(0, 0, fmt, argptr) + 1; + va_end(argptr); + byte* buf = malloc(len); + va_start(argptr, fmt); + vsprintf((char *)buf, fmt, argptr); + va_end(argptr); +#ifdef DEBUG_ALLOC + puts("_STR:"); + puts(buf); +#endif + return tos2(buf); +} + +string _STR_TMP(const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + //size_t len = vsnprintf(0, 0, fmt, argptr) + 1; + va_end(argptr); + va_start(argptr, fmt); + vsprintf((char *)g_str_buf, fmt, argptr); + va_end(argptr); +#ifdef DEBUG_ALLOC + //puts("_STR_TMP:"); + //puts(g_str_buf); +#endif + return tos2(g_str_buf); +} + +') + } +} + +pub fn (v mut V) generate_main() { + mut cgen := v.cgen + $if js { return } + + if v.pref.is_vlines { + ///// After this point, the v files are compiled. + ///// The rest is auto generated code, which will not have + ///// different .v source file/line numbers. + lines_so_far := cgen.lines.join('\n').count('\n') + 5 + cgen.genln('') + cgen.genln('////////////////// Reset the file/line numbers //////////') + cgen.lines << '#line $lines_so_far "${cescaped_path(os.realpath(cgen.out_path))}"' + cgen.genln('') + } + + // Make sure the main function exists + // Obviously we don't need it in libraries + if v.pref.build_mode != .build_module { + if !v.table.main_exists() && !v.pref.is_test { + // It can be skipped in single file programs + if v.pref.is_script { + //println('Generating main()...') + v.gen_main_start(true) + cgen.genln('$cgen.fn_main;') + v.gen_main_end('return 0') + } + else { + verror('function `main` is not declared in the main module') + } + } + else if v.pref.is_test { + if v.table.main_exists() { + verror('test files cannot have function `main`') + } + if !v.table.has_at_least_one_test_fn() { + verror('test files need to have at least one test function') + } + // Generate a C `main`, which calls every single test function + v.gen_main_start(false) + + if v.pref.is_stats { cgen.genln('BenchedTests bt = main__start_testing();') } + + for _, f in v.table.fns { + if f.name.starts_with('main__test_') { + if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_start(&bt, tos3("$f.name"));') } + cgen.genln('$f.name();') + if v.pref.is_stats { cgen.genln('BenchedTests_testing_step_end(&bt);') } + } + } + if v.pref.is_stats { cgen.genln('BenchedTests_end_testing(&bt);') } + v.gen_main_end('return g_test_fails > 0') + } + else if v.table.main_exists() { + v.gen_main_start(true) + cgen.genln(' main__main();') + v.gen_main_end('return 0') + } + } +} + +pub fn (v mut V) gen_main_start(add_os_args bool){ + v.cgen.genln('int main(int argc, char** argv) { ') + v.cgen.genln(' init();') + if add_os_args && 'os' in v.table.imports { + v.cgen.genln(' os__args = os__init_os_args(argc, (byteptr*)argv);') + } + v.generate_hotcode_reloading_main_caller() + v.cgen.genln('') +} +pub fn (v mut V) gen_main_end(return_statement string){ + v.cgen.genln('') + v.cgen.genln(' $return_statement;') + v.cgen.genln('}') +} + +pub fn final_target_out_name(out_name string) string { + $if windows { + return out_name.replace('/', '\\') + '.exe' + } + return if out_name.starts_with('/') { + out_name + } + else { + './' + out_name + } +} + +pub fn (v V) run_compiled_executable_and_exit() { + if v.pref.is_verbose { + println('============ running $v.out_name ============') + } + mut cmd := '"' + final_target_out_name(v.out_name).replace('.exe','') + '"' + if os.args.len > 3 { + cmd += ' ' + os.args.right(3).join(' ') + } + if v.pref.is_test { + ret := os.system(cmd) + if ret != 0 { + exit(1) + } + } + if v.pref.is_run { + ret := os.system(cmd) + // TODO: make the runner wrapping as transparent as possible + // (i.e. use execve when implemented). For now though, the runner + // just returns the same exit code as the child process. + exit( ret ) + } + exit(0) +} + +pub fn (v &V) v_files_from_dir(dir string) []string { + mut res := []string + if !os.file_exists(dir) { + verror('$dir doesn\'t exist') + } else if !os.dir_exists(dir) { + verror('$dir isn\'t a directory') + } + mut files := os.ls(dir) + if v.pref.is_verbose { + println('v_files_from_dir ("$dir")') + } + files.sort() + for file in files { + if !file.ends_with('.v') && !file.ends_with('.vh') { + continue + } + if file.ends_with('_test.v') { + continue + } + if file.ends_with('_win.v') && (v.os != .windows && v.os != .msvc) { + continue + } + if file.ends_with('_lin.v') && v.os != .linux { + continue + } + if file.ends_with('_mac.v') && v.os != .mac { + continue + } + if file.ends_with('_nix.v') && (v.os == .windows || v.os == .msvc) { + continue + } + if file.ends_with('_js.v') && v.os != .js { + continue + } + if file.ends_with('_c.v') && v.os == .js { + continue + } + res << '$dir${os.path_separator}$file' + } + return res +} + +// Parses imports, adds necessary libs, and then user files +pub 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${os.path_separator}vlib${os.path_separator}builtin.vh' + if v.pref.is_cache && os.file_exists(builtin_vh) { + v.cached_mods << 'builtin' + builtin_files = [builtin_vh] + } + // Parse builtin imports + for file in 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) } + v.add_parser(p) + } + // 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) } + v.add_parser(p) + } + // Parse lib imports + v.parse_lib_imports() + if v.pref.is_verbose { + v.log('imports:') + println(v.table.imports) + } + // 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 + if v.pref.build_mode != .build_module && !mod.contains('vweb') { + mod_path := mod.replace('.', os.path_separator) + vh_path := '$v_modules_path${os.path_separator}vlib${os.path_separator}${mod_path}.vh' + if v.pref.is_cache && os.file_exists(vh_path) { + println('using cached module `$mod`: $vh_path') + v.cached_mods << mod + 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) + 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_id + } +} + +pub fn (v &V) get_builtin_files() []string { + // .vh cache exists? Use it + + $if js { + return v.v_files_from_dir('$v.vroot${os.path_separator}vlib${os.path_separator}builtin${os.path_separator}js') + } + return v.v_files_from_dir('$v.vroot${os.path_separator}vlib${os.path_separator}builtin') +} + +// get user files +pub fn (v &V) get_user_files() []string { + mut dir := v.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 + + if v.pref.is_test && v.pref.is_stats { + user_files << [v.vroot, 'vlib', 'benchmark', 'tests', 'always_imported.v'].join( os.path_separator ) + } + + // v volt/slack_test.v: compile all .v files to get the environment + // I need to implement user packages! TODO + is_test_with_imports := dir.ends_with('_test.v') && + (dir.contains('${os.path_separator}volt') || dir.contains('${os.path_separator}c2volt'))// TODO + if is_test_with_imports { + user_files << dir + pos := dir.last_index(os.path_separator) + dir = dir.left(pos) + os.path_separator// TODO WHY IS THIS .neEDED? + } + if dir.ends_with('.v') { + // Just compile one file and get parent dir + user_files << dir + dir = dir.all_before('${os.path_separator}') + } + else { + // Add .v files from the directory being compiled + files := v.v_files_from_dir(dir) + for file in files { + user_files << file + } + } + if user_files.len == 0 { + println('No input .v files') + exit(1) + } + if v.pref.is_verbose { + v.log('user_files:') + println(user_files) + } + return user_files +} + +// 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 { + 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)) + 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)) + } + // 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 + 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) + } + } + 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 { + deps_resolved.display() + verror('import cycle detected') + } + return deps_resolved +} + +pub fn get_arg(joined_args, arg, def string) string { + return get_param_after(joined_args, '-$arg', def) +} + +pub fn get_param_after(joined_args, arg, def string) string { + key := '$arg ' + mut pos := joined_args.index(key) + if pos == -1 { + return def + } + pos += key.len + mut space := joined_args.index_after(' ', pos) + if space == -1 { + space = joined_args.len + } + res := joined_args.substr(pos, space) + return res +} + +pub fn (v &V) log(s string) { + if !v.pref.is_verbose { + return + } + println(s) +} + +pub fn new_v(args[]string) &V { + // Create modules dirs if they are missing + if !os.dir_exists(v_modules_path) { + os.mkdir(v_modules_path) + os.mkdir('$v_modules_path${os.path_separator}cache') + } + + mut vgen_buf := strings.new_builder(1000) + vgen_buf.writeln('module main\nimport strings') + + joined_args := args.join(' ') + target_os := get_arg(joined_args, 'os', '') + mut out_name := get_arg(joined_args, 'o', 'a.out') + + mut dir := args.last() + if 'run' in args { + dir = get_param_after(joined_args, 'run', '') + } + if dir.ends_with(os.path_separator) { + dir = dir.all_before_last(os.path_separator) + } + if dir.starts_with('.$os.path_separator') { + dir = dir.right(2) + } + if args.len < 2 { + dir = '' + } + // build mode + mut build_mode := BuildMode.default_mode + mut mod := '' + if joined_args.contains('build module ') { + build_mode = .build_module + // v build module ~/v/os => os.o + mod_path := if dir.contains('vlib') { + dir.all_after('vlib'+os.path_separator) + } + else if dir.starts_with('.\\') || dir.starts_with('./') { + dir.right(2) + } + else if dir.starts_with(os.path_separator) { + dir.all_after(os.path_separator) + } else { + dir + } + mod = mod_path.replace(os.path_separator, '.') + println('Building module "${mod}" (dir="$dir")...') + //out_name = '$TmpPath/vlib/${base}.o' + out_name = mod + // 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') + } + */ + } + is_test := dir.ends_with('_test.v') + is_script := dir.ends_with('.v') + if is_script && !os.file_exists(dir) { + println('`$dir` does not exist') + exit(1) + } + // No -o provided? foo.v => foo + if out_name == 'a.out' && dir.ends_with('.v') && dir != '.v' { + out_name = dir.left(dir.len - 2) + } + // if we are in `/foo` and run `v .`, the executable should be `foo` + if dir == '.' && out_name == 'a.out' { + base := os.getwd().all_after(os.path_separator) + out_name = base.trim_space() + } + mut _os := OS.mac + // No OS specifed? Use current system + if target_os == '' { + $if linux { + _os = .linux + } + $if mac { + _os = .mac + } + $if windows { + _os = .windows + } + $if freebsd { + _os = .freebsd + } + $if openbsd { + _os = .openbsd + } + $if netbsd { + _os = .netbsd + } + $if dragonfly { + _os = .dragonfly + } + $if solaris { + _os = .solaris + } + } + else { + _os = os_from_string(target_os) + } + // Location of all vlib files + vroot := os.dir(os.executable()) + //println('VROOT=$vroot') + // v.exe's parent directory should contain vlib + if !os.dir_exists(vroot) || !os.dir_exists(vroot + '/vlib/builtin') { + println('vlib not found, downloading it...') + /* + ret := os.system('git clone --depth=1 https://github.com/vlang/v .') + if ret != 0 { + println('failed to `git clone` vlib') + println('make sure you are online and have git installed') + exit(1) + } + */ + println('vlib not found. It should be next to the V executable. ') + println('Go to https://vlang.io to install V.') + exit(1) + } + // println('out_name:$out_name') + mut out_name_c := os.realpath('${out_name}.tmp.c') + + cflags := get_cmdline_cflags(args) + + rdir := os.realpath( dir ) + rdir_name := os.filename( rdir ) + + obfuscate := '-obf' in args + is_repl := '-repl' in args + pref := &Preferences { + is_test: is_test + is_script: is_script + is_so: '-shared' in args + is_prod: '-prod' in args + is_verbose: '-verbose' in args || '--verbose' in args + + is_debug: '-g' in args || '-cg' in args + is_vlines: '-g' in args && !('-cg' in args) + is_keep_c: '-keep_c' in args + is_cache: '-cache' in args + + is_stats: '-stats' in args + obfuscate: obfuscate + is_prof: '-prof' in args + is_live: '-live' in args + sanitize: '-sanitize' in args + nofmt: '-nofmt' in args + show_c_cmd: '-show_c_cmd' in args + translated: 'translated' in args + is_run: 'run' in args + autofree: '-autofree' in args + compress: '-compress' in args + is_repl: is_repl + build_mode: build_mode + cflags: cflags + ccompiler: find_c_compiler() + building_v: !is_repl && (rdir_name == 'compiler' || dir.contains('vlib')) + } + if pref.is_verbose || pref.is_debug { + println('C compiler=$pref.ccompiler') + } + if pref.is_so { + out_name_c = out_name.all_after(os.path_separator) + '_shared_lib.c' + } + return &V{ + os: _os + out_name: out_name + dir: dir + lang_dir: vroot + table: new_table(obfuscate) + out_name_c: out_name_c + cgen: new_cgen(out_name_c) + vroot: vroot + pref: pref + mod: mod + vgen_buf: vgen_buf + } +} + +pub fn env_vflags_and_os_args() []string { + mut args := []string + vflags := os.getenv('VFLAGS') + if '' != vflags { + args << os.args[0] + args << vflags.split(' ') + if os.args.len > 1 { + args << os.args.right(1) + } + } else{ + args << os.args + } + return args +} + +pub fn update_v() { + println('Updating V...') + vroot := os.dir(os.executable()) + s := os.exec('git -C "$vroot" pull --rebase origin master') or { + verror(err) + return + } + println(s.output) + $if windows { + v_backup_file := '$vroot/v_old.exe' + if os.file_exists( v_backup_file ) { + os.rm( v_backup_file ) + } + os.mv('$vroot/v.exe', v_backup_file) + s2 := os.exec('"$vroot/make.bat"') or { + verror(err) + return + } + println(s2.output) + } $else { + s2 := os.exec('make -C "$vroot"') or { + verror(err) + return + } + println(s2.output) + } +} + +pub fn vfmt(args[]string) { + file := args.last() + if !os.file_exists(file) { + println('"$file" does not exist') + exit(1) + } + if !file.ends_with('.v') { + println('v fmt can only be used on .v files') + exit(1) + } + println('vfmt is temporarily disabled') +} + +pub fn install_v(args[]string) { + if args.len < 3 { + println('usage: v install [module] [module] [...]') + return + } + names := args.slice(2, args.len) + vexec := os.executable() + vroot := os.dir(vexec) + vget := '$vroot/tools/vget' + if true { + //println('Building vget...') + os.chdir(vroot + '/tools') + vget_compilation := os.exec('$vexec -o $vget vget.v') or { + verror(err) + return + } + if vget_compilation.exit_code != 0 { + verror( vget_compilation.output ) + return + } + } + vgetresult := os.exec('$vget ' + names.join(' ')) or { + verror(err) + return + } + if vgetresult.exit_code != 0 { + verror( vgetresult.output ) + return + } +} + +pub fn create_symlink() { + vexe := os.executable() + link_path := '/usr/local/bin/v' + ret := os.system('ln -sf $vexe $link_path') + if ret == 0 { + println('symlink "$link_path" has been created') + } else { + println('failed to create symlink "$link_path", '+ + 'make sure you run with sudo') + } +} + +pub fn verror(s string) { + println('V error: $s') + os.flush_stdout() + exit(1) +} + +pub fn vhash() string { + mut buf := [50]byte + buf[0] = 0 + C.snprintf(*char(buf), 50, '%s', C.V_COMMIT_HASH ) + return tos_clone(buf) +} + +pub fn cescaped_path(s string) string { + return s.replace('\\','\\\\') +} + +pub fn os_from_string(os string) OS { + switch os { + case 'linux': return .linux + case 'windows': return .windows + case 'mac': return .mac + case 'freebsd': return .freebsd + case 'openbsd': return .openbsd + case 'netbsd': return .netbsd + case 'dragonfly': return .dragonfly + case 'msvc': return .msvc + case 'js': return .js + case 'solaris': return .solaris + case 'android': return .android + } + println('bad os $os') // todo panic? + return .linux +} diff --git a/compiler/module_header.v b/vlib/compiler/module_header.v similarity index 99% rename from compiler/module_header.v rename to vlib/compiler/module_header.v index 0972d0426f..a7dfaa2212 100644 --- a/compiler/module_header.v +++ b/vlib/compiler/module_header.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import ( strings diff --git a/compiler/modules.v b/vlib/compiler/modules.v similarity index 99% rename from compiler/modules.v rename to vlib/compiler/modules.v index afc883ecb7..6933534597 100644 --- a/compiler/modules.v +++ b/vlib/compiler/modules.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import os diff --git a/compiler/msvc.v b/vlib/compiler/msvc.v similarity index 99% rename from compiler/msvc.v rename to vlib/compiler/msvc.v index 9cb41abeba..2696b5f6ad 100644 --- a/compiler/msvc.v +++ b/vlib/compiler/msvc.v @@ -1,4 +1,4 @@ -module main +module compiler import os diff --git a/compiler/optimization.v b/vlib/compiler/optimization.v similarity index 98% rename from compiler/optimization.v rename to vlib/compiler/optimization.v index 13cc5ab925..e2d8c4b698 100644 --- a/compiler/optimization.v +++ b/vlib/compiler/optimization.v @@ -1,4 +1,4 @@ -module main +module compiler // `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` // avoid allocation diff --git a/compiler/parser.v b/vlib/compiler/parser.v similarity index 99% rename from compiler/parser.v rename to vlib/compiler/parser.v index 3feebcbf41..a07a78e071 100644 --- a/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import ( os @@ -2183,7 +2183,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 { + if field.access_mod == .private && !p.builtin_mod && !p.pref.translated && p.mod != typ.mod && p.file_path_id != '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`)', fname_tidx) diff --git a/compiler/parser2.v b/vlib/compiler/parser2.v similarity index 99% rename from compiler/parser2.v rename to vlib/compiler/parser2.v index 286f4e1487..e9a84b1708 100644 --- a/compiler/parser2.v +++ b/vlib/compiler/parser2.v @@ -1,4 +1,4 @@ -module main +module compiler import strings diff --git a/compiler/query.v b/vlib/compiler/query.v similarity index 99% rename from compiler/query.v rename to vlib/compiler/query.v index d42ac88fbd..29ae37986d 100644 --- a/compiler/query.v +++ b/vlib/compiler/query.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import strings diff --git a/compiler/repl.v b/vlib/compiler/repl.v similarity index 98% rename from compiler/repl.v rename to vlib/compiler/repl.v index 31ef9925fb..3fb49cf444 100644 --- a/compiler/repl.v +++ b/vlib/compiler/repl.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import os import term @@ -58,7 +58,7 @@ fn (r &Repl) function_call(line string) bool { return false } -fn repl_help() { +pub fn repl_help() { version_hash := vhash() println(' V $Version $version_hash @@ -68,7 +68,7 @@ V $Version $version_hash ') } -fn run_repl() []string { +pub fn run_repl() []string { version_hash := vhash() println('V $Version $version_hash') println('Use Ctrl-C or `exit` to exit') diff --git a/compiler/scanner.v b/vlib/compiler/scanner.v similarity index 99% rename from compiler/scanner.v rename to vlib/compiler/scanner.v index b0cddb223f..773345b995 100644 --- a/compiler/scanner.v +++ b/vlib/compiler/scanner.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import ( os diff --git a/compiler/table.v b/vlib/compiler/table.v similarity index 99% rename from compiler/table.v rename to vlib/compiler/table.v index aeb5cb812b..a706a52b79 100644 --- a/compiler/table.v +++ b/vlib/compiler/table.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import os import math diff --git a/compiler/tests/bench/val_vs_ptr.c b/vlib/compiler/tests/bench/val_vs_ptr.c similarity index 100% rename from compiler/tests/bench/val_vs_ptr.c rename to vlib/compiler/tests/bench/val_vs_ptr.c diff --git a/compiler/tests/defer_test.v b/vlib/compiler/tests/defer_test.v similarity index 100% rename from compiler/tests/defer_test.v rename to vlib/compiler/tests/defer_test.v diff --git a/compiler/tests/enum_test.v b/vlib/compiler/tests/enum_test.v similarity index 100% rename from compiler/tests/enum_test.v rename to vlib/compiler/tests/enum_test.v diff --git a/compiler/tests/fixed_array_test.v b/vlib/compiler/tests/fixed_array_test.v similarity index 100% rename from compiler/tests/fixed_array_test.v rename to vlib/compiler/tests/fixed_array_test.v diff --git a/compiler/tests/fn_multiple_returns_test.v b/vlib/compiler/tests/fn_multiple_returns_test.v similarity index 100% rename from compiler/tests/fn_multiple_returns_test.v rename to vlib/compiler/tests/fn_multiple_returns_test.v diff --git a/compiler/tests/fn_test.v b/vlib/compiler/tests/fn_test.v similarity index 100% rename from compiler/tests/fn_test.v rename to vlib/compiler/tests/fn_test.v diff --git a/compiler/tests/fn_variadic_test.v b/vlib/compiler/tests/fn_variadic_test.v similarity index 100% rename from compiler/tests/fn_variadic_test.v rename to vlib/compiler/tests/fn_variadic_test.v diff --git a/compiler/tests/interface_test.v b/vlib/compiler/tests/interface_test.v similarity index 100% rename from compiler/tests/interface_test.v rename to vlib/compiler/tests/interface_test.v diff --git a/compiler/tests/local/local.v b/vlib/compiler/tests/local/local.v similarity index 100% rename from compiler/tests/local/local.v rename to vlib/compiler/tests/local/local.v diff --git a/compiler/tests/local_test.v b/vlib/compiler/tests/local_test.v similarity index 100% rename from compiler/tests/local_test.v rename to vlib/compiler/tests/local_test.v diff --git a/compiler/tests/match_test.v b/vlib/compiler/tests/match_test.v similarity index 100% rename from compiler/tests/match_test.v rename to vlib/compiler/tests/match_test.v diff --git a/compiler/tests/module_test.v b/vlib/compiler/tests/module_test.v similarity index 100% rename from compiler/tests/module_test.v rename to vlib/compiler/tests/module_test.v diff --git a/compiler/tests/msvc_test.v b/vlib/compiler/tests/msvc_test.v similarity index 100% rename from compiler/tests/msvc_test.v rename to vlib/compiler/tests/msvc_test.v diff --git a/compiler/tests/mut_test.v b/vlib/compiler/tests/mut_test.v similarity index 100% rename from compiler/tests/mut_test.v rename to vlib/compiler/tests/mut_test.v diff --git a/compiler/tests/option_test.v b/vlib/compiler/tests/option_test.v similarity index 100% rename from compiler/tests/option_test.v rename to vlib/compiler/tests/option_test.v diff --git a/compiler/tests/repl/.gitattributes b/vlib/compiler/tests/repl/.gitattributes similarity index 100% rename from compiler/tests/repl/.gitattributes rename to vlib/compiler/tests/repl/.gitattributes diff --git a/compiler/tests/repl/.gitignore b/vlib/compiler/tests/repl/.gitignore similarity index 100% rename from compiler/tests/repl/.gitignore rename to vlib/compiler/tests/repl/.gitignore diff --git a/compiler/tests/repl/README.md b/vlib/compiler/tests/repl/README.md similarity index 100% rename from compiler/tests/repl/README.md rename to vlib/compiler/tests/repl/README.md diff --git a/compiler/tests/repl/arr_decl.repl b/vlib/compiler/tests/repl/arr_decl.repl similarity index 100% rename from compiler/tests/repl/arr_decl.repl rename to vlib/compiler/tests/repl/arr_decl.repl diff --git a/compiler/tests/repl/chained_fields.correct.repl b/vlib/compiler/tests/repl/chained_fields.correct.repl similarity index 100% rename from compiler/tests/repl/chained_fields.correct.repl rename to vlib/compiler/tests/repl/chained_fields.correct.repl diff --git a/compiler/tests/repl/chained_fields.repl b/vlib/compiler/tests/repl/chained_fields.repl similarity index 100% rename from compiler/tests/repl/chained_fields.repl rename to vlib/compiler/tests/repl/chained_fields.repl diff --git a/compiler/tests/repl/conditional_blocks.repl b/vlib/compiler/tests/repl/conditional_blocks.repl similarity index 100% rename from compiler/tests/repl/conditional_blocks.repl rename to vlib/compiler/tests/repl/conditional_blocks.repl diff --git a/compiler/tests/repl/default_printing.repl b/vlib/compiler/tests/repl/default_printing.repl similarity index 100% rename from compiler/tests/repl/default_printing.repl rename to vlib/compiler/tests/repl/default_printing.repl diff --git a/compiler/tests/repl/empty_struct.repl b/vlib/compiler/tests/repl/empty_struct.repl similarity index 100% rename from compiler/tests/repl/empty_struct.repl rename to vlib/compiler/tests/repl/empty_struct.repl diff --git a/compiler/tests/repl/error.repl b/vlib/compiler/tests/repl/error.repl similarity index 100% rename from compiler/tests/repl/error.repl rename to vlib/compiler/tests/repl/error.repl diff --git a/compiler/tests/repl/error_nosave.repl b/vlib/compiler/tests/repl/error_nosave.repl similarity index 100% rename from compiler/tests/repl/error_nosave.repl rename to vlib/compiler/tests/repl/error_nosave.repl diff --git a/compiler/tests/repl/function.repl b/vlib/compiler/tests/repl/function.repl similarity index 100% rename from compiler/tests/repl/function.repl rename to vlib/compiler/tests/repl/function.repl diff --git a/compiler/tests/repl/immutable_len_fields.repl b/vlib/compiler/tests/repl/immutable_len_fields.repl similarity index 100% rename from compiler/tests/repl/immutable_len_fields.repl rename to vlib/compiler/tests/repl/immutable_len_fields.repl diff --git a/compiler/tests/repl/interpolation.repl b/vlib/compiler/tests/repl/interpolation.repl similarity index 100% rename from compiler/tests/repl/interpolation.repl rename to vlib/compiler/tests/repl/interpolation.repl diff --git a/compiler/tests/repl/multiple_decl.repl b/vlib/compiler/tests/repl/multiple_decl.repl similarity index 100% rename from compiler/tests/repl/multiple_decl.repl rename to vlib/compiler/tests/repl/multiple_decl.repl diff --git a/compiler/tests/repl/multiple_println.repl b/vlib/compiler/tests/repl/multiple_println.repl similarity index 100% rename from compiler/tests/repl/multiple_println.repl rename to vlib/compiler/tests/repl/multiple_println.repl diff --git a/compiler/tests/repl/naked_strings.repl b/vlib/compiler/tests/repl/naked_strings.repl similarity index 100% rename from compiler/tests/repl/naked_strings.repl rename to vlib/compiler/tests/repl/naked_strings.repl diff --git a/compiler/tests/repl/newlines.repl b/vlib/compiler/tests/repl/newlines.repl similarity index 100% rename from compiler/tests/repl/newlines.repl rename to vlib/compiler/tests/repl/newlines.repl diff --git a/compiler/tests/repl/nothing.repl b/vlib/compiler/tests/repl/nothing.repl similarity index 100% rename from compiler/tests/repl/nothing.repl rename to vlib/compiler/tests/repl/nothing.repl diff --git a/compiler/tests/repl/println.repl b/vlib/compiler/tests/repl/println.repl similarity index 100% rename from compiler/tests/repl/println.repl rename to vlib/compiler/tests/repl/println.repl diff --git a/compiler/tests/repl/repl_test.v b/vlib/compiler/tests/repl/repl_test.v similarity index 100% rename from compiler/tests/repl/repl_test.v rename to vlib/compiler/tests/repl/repl_test.v diff --git a/compiler/tests/repl/run.v b/vlib/compiler/tests/repl/run.v similarity index 93% rename from compiler/tests/repl/run.v rename to vlib/compiler/tests/repl/run.v index 6578e359c5..6eb06ef60b 100644 --- a/compiler/tests/repl/run.v +++ b/vlib/compiler/tests/repl/run.v @@ -1,6 +1,6 @@ module main -import compiler.tests.repl.runner +import vcompiler.tests.repl.runner import log import benchmark diff --git a/compiler/tests/repl/runner/runner.v b/vlib/compiler/tests/repl/runner/runner.v similarity index 96% rename from compiler/tests/repl/runner/runner.v rename to vlib/compiler/tests/repl/runner/runner.v index 8f595b6736..3fab55055d 100644 --- a/compiler/tests/repl/runner/runner.v +++ b/vlib/compiler/tests/repl/runner/runner.v @@ -11,7 +11,7 @@ pub: pub fn full_path_to_v() string { vname := if os.user_os() == 'windows' { 'v.exe' } else { 'v' } - vexec := os.dir(os.dir(os.dir(os.dir( os.executable() )))) + os.path_separator + vname + vexec := os.dir(os.dir(os.dir(os.dir(os.dir( os.executable() ))))) + os.path_separator + vname /* args := os.args vreal := os.realpath('v') diff --git a/compiler/tests/repl/var_decl.repl b/vlib/compiler/tests/repl/var_decl.repl similarity index 100% rename from compiler/tests/repl/var_decl.repl rename to vlib/compiler/tests/repl/var_decl.repl diff --git a/compiler/tests/return_voidptr_test.v b/vlib/compiler/tests/return_voidptr_test.v similarity index 100% rename from compiler/tests/return_voidptr_test.v rename to vlib/compiler/tests/return_voidptr_test.v diff --git a/compiler/tests/str_gen_test.v b/vlib/compiler/tests/str_gen_test.v similarity index 100% rename from compiler/tests/str_gen_test.v rename to vlib/compiler/tests/str_gen_test.v diff --git a/compiler/tests/string_interpolation_test.v b/vlib/compiler/tests/string_interpolation_test.v similarity index 100% rename from compiler/tests/string_interpolation_test.v rename to vlib/compiler/tests/string_interpolation_test.v diff --git a/compiler/tests/struct_test.v b/vlib/compiler/tests/struct_test.v similarity index 100% rename from compiler/tests/struct_test.v rename to vlib/compiler/tests/struct_test.v diff --git a/compiler/token.v b/vlib/compiler/token.v similarity index 99% rename from compiler/token.v rename to vlib/compiler/token.v index c04cd8d05c..6c6a7d6b89 100644 --- a/compiler/token.v +++ b/vlib/compiler/token.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler enum TokenKind { eof diff --git a/compiler/vfmt.v b/vlib/compiler/vfmt.v similarity index 98% rename from compiler/vfmt.v rename to vlib/compiler/vfmt.v index e90ac353f7..7375dd9768 100644 --- a/compiler/vfmt.v +++ b/vlib/compiler/vfmt.v @@ -2,7 +2,7 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. -module main +module compiler import strings diff --git a/compiler/vhelp.v b/vlib/compiler/vhelp.v similarity index 99% rename from compiler/vhelp.v rename to vlib/compiler/vhelp.v index 9d0940effe..69f7d13d1e 100644 --- a/compiler/vhelp.v +++ b/vlib/compiler/vhelp.v @@ -1,4 +1,4 @@ -module main +module compiler const ( HelpText = 'Usage: v [options/commands] [file.v | directory] diff --git a/compiler/vtest.v b/vlib/compiler/vtest.v similarity index 96% rename from compiler/vtest.v rename to vlib/compiler/vtest.v index 82ec00b93e..2556756272 100644 --- a/compiler/vtest.v +++ b/vlib/compiler/vtest.v @@ -1,4 +1,4 @@ -module main +module compiler import ( os @@ -15,14 +15,14 @@ mut: benchmark benchmark.Benchmark } -fn new_test_sesion(vargs string) TestSession { +pub fn new_test_sesion(vargs string) TestSession { return TestSession{ vexe: os.executable() vargs: vargs } } -fn test_v() { +pub fn test_v() { args := os.args if args.last() == 'test' { println('Usage:') @@ -71,7 +71,7 @@ fn test_v() { } } -fn (ts mut TestSession) test() { +pub fn (ts mut TestSession) test() { ok := term.ok_message('OK') fail := term.fail_message('FAIL') cmd_needs_quoting := (os.user_os() == 'windows') @@ -121,7 +121,7 @@ fn stable_example(example string, index int, arr []string) bool { return !example.contains('vweb') } -fn v_test_v(args_before_test string){ +pub fn v_test_v(args_before_test string){ vexe := os.executable() parent_dir := os.dir(vexe) // Changing the current directory is needed for some of the compiler tests, @@ -165,7 +165,7 @@ fn v_test_v(args_before_test string){ } } -fn test_vget() { +pub fn test_vget() { /* vexe := os.executable() ret := os.system('$vexe install nedpals.args')