compiler: cache modules

pull/2229/head
Alexander Medvednikov 2019-10-04 15:48:09 +03:00 committed by GitHub
parent f45d3f07ed
commit 52f4f4026b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 422 additions and 242 deletions

View File

@ -63,11 +63,12 @@ fn (v mut V) cc() {
v.out_name = v.out_name + '.so' v.out_name = v.out_name + '.so'
} }
if v.pref.build_mode == .build_module { if v.pref.build_mode == .build_module {
// Create the modules directory if it's not there. // Create the modules & out directory if it's not there.
if !os.file_exists(v_modules_path) { out_dir := v_modules_path + v.dir
os.mkdir(v_modules_path) 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}...') println('Building ${v.out_name}...')
} }

View File

@ -117,7 +117,7 @@ fn (p mut Parser) comp_time() {
// Parse the function and embed resulting C code in current function so that // Parse the function and embed resulting C code in current function so that
// all variables are available. // all variables are available.
pos := p.cgen.lines.len - 1 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 { if !p.pref.is_debug {
os.rm('.vwebtmpl.v') os.rm('.vwebtmpl.v')
} }
@ -279,7 +279,7 @@ fn (p mut Parser) gen_struct_str(typ Type) {
for field in typ.fields { for field in typ.fields {
sb.writeln('\t$field.name: $' + 'a.${field.name}') sb.writeln('\t$field.name: $' + 'a.${field.name}')
} }
sb.writeln("\n}'") sb.writeln("}'")
sb.writeln('}') sb.writeln('}')
p.v.vgen_buf.writeln(sb.str()) p.v.vgen_buf.writeln(sb.str())
// Need to manually add the definition to `fns` so that it stays // Need to manually add the definition to `fns` so that it stays

View File

@ -144,6 +144,7 @@ fn (p mut Parser) is_sig() bool {
fn (p mut Parser) fn_decl() { fn (p mut Parser) fn_decl() {
p.clear_vars() // clear local vars every time a new fn is started p.clear_vars() // clear local vars every time a new fn is started
p.fgen('fn ') p.fgen('fn ')
//defer { p.fgenln('\n') } //defer { p.fgenln('\n') }
// If we are in the first pass, create a new function. // If we are in the first pass, create a new function.
// In the second pass fetch the one we created. // 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() { if !is_c && p.first_pass() {
// TODO hack to make Volt compile without -embed_vlib // TODO hack to make Volt compile without -embed_vlib
if f.name == 'darwin__nsstring' && p.pref.build_mode == .default_mode { 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 return
} }
if p.attr == 'live' && p.pref.is_so { if p.attr == 'live' && p.pref.is_so {
@ -1092,11 +1100,6 @@ fn (f &Fn) typ_str() string {
return sb.str() 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" // f.args => "int a, string b"
fn (f &Fn) str_args(table &Table) string { fn (f &Fn) str_args(table &Table) string {
mut s := '' mut s := ''

View File

@ -29,7 +29,6 @@ enum BuildMode {
const ( const (
supported_platforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd', supported_platforms = ['windows', 'mac', 'linux', 'freebsd', 'openbsd',
'netbsd', 'dragonfly', 'msvc', 'android', 'js', 'solaris'] 'netbsd', 'dragonfly', 'msvc', 'android', 'js', 'solaris']
v_modules_path = os.home_dir() + '/.vmodules/'
) )
enum OS { enum OS {
@ -41,7 +40,7 @@ enum OS {
netbsd netbsd
dragonfly dragonfly
msvc // TODO not an OS msvc // TODO not an OS
js // TODO js // TODO
android android
solaris solaris
} }
@ -62,54 +61,54 @@ enum Pass {
struct V { struct V {
mut: mut:
os OS // the OS to build for os OS // the OS to build for
out_name_c string // name of the temporary C file out_name_c string // name of the temporary C file
files []string // all V files that need to be parsed and compiled files []string // all V files that need to be parsed and compiled
dir string // directory (or file) being compiled (TODO rename to path?) dir string // directory (or file) being compiled (TODO rename to path?)
table &Table // table with types, vars, functions etc table &Table // table with types, vars, functions etc
cgen &CGen // C code generator cgen &CGen // C code generator
pref &Preferences // all the preferences and settings extracted to a struct for reusability pref &Preferences // all the preferences and settings extracted to a struct for reusability
lang_dir string // "~/code/v" lang_dir string // "~/code/v"
out_name string // "program.exe" out_name string // "program.exe"
vroot string vroot string
mod string // module being built with -lib mod string // module being built with -lib
parsers []Parser parsers []Parser
vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc) vgen_buf strings.Builder // temporary buffer for generated V code (.str() etc)
} }
struct Preferences { struct Preferences {
mut: mut:
build_mode BuildMode build_mode BuildMode
nofmt bool // disable vfmt nofmt bool // disable vfmt
is_test bool // `v test string_test.v` is_test bool // `v test string_test.v`
is_script bool // single file mode (`v program.v`), main function can be skipped is_script bool // single file mode (`v program.v`), main function can be skipped
is_live bool // for hot code reloading is_live bool // for hot code reloading
is_so bool is_so bool
is_prof bool // benchmark every function is_prof bool // benchmark every function
translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc translated bool // `v translate doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
is_prod bool // use "-O2" is_prod bool // use "-O2"
is_verbose bool // print extra information with `v.log()` is_verbose bool // print extra information with `v.log()`
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX" obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
is_repl bool is_repl bool
is_run bool is_run bool
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c 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 sanitize bool // use Clang's new "-fsanitize" option
is_debuggable bool is_debuggable bool
is_debug bool // keep compiled C files 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) 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. 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. // 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 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'. // 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 ccompiler string // the name of the used C compiler
building_v bool building_v bool
autofree bool autofree bool
compress bool compress bool
// Skips re-compilation of the builtin module skip_builtin bool // Skips re-compilation of the builtin module
// to increase compilation time. // to increase compilation time.
// This is on by default, since a vast majority of users do not // This is on by default, since a vast majority of users do not
// work on the builtin module itself. // work on the builtin module itself.
skip_builtin bool
} }
fn main() { fn main() {
@ -202,20 +201,28 @@ fn main() {
} }
v.table.fns.free() v.table.fns.free()
free(v.table) free(v.table)
//for p in parsers { //for p in parsers {}
//}
println('done!') println('done!')
} }
} }
fn (v mut V) add_parser(parser Parser) { fn (v mut V) add_parser(parser Parser) {
for p in v.parsers { for p in v.parsers {
if p.id == parser.id { if p.id == parser.id {
return return
} }
} }
v.parsers << parser 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 { if os.user_os() != 'windows' && v.os == .msvc {
verror('Cannot build with msvc on ${os.user_os()}') verror('Cannot build with msvc on ${os.user_os()}')
} }
mut cgen := v.cgen mut cgen := v.cgen
cgen.genln('// Generated by V') cgen.genln('// Generated by V')
if v.pref.is_verbose { if v.pref.is_verbose {
@ -250,12 +256,7 @@ fn (v mut V) compile() {
*/ */
// First pass (declarations) // First pass (declarations)
for file in v.files { for file in v.files {
for i, p in v.parsers { v.parse(file, .decl)
if p.file_path == file {
v.parsers[i].parse(.decl)
break
}
}
} }
// Main pass // Main pass
cgen.pass = Pass.main cgen.pass = Pass.main
@ -321,12 +322,7 @@ fn (v mut V) compile() {
cgen.genln('this line will be replaced with definitions') cgen.genln('this line will be replaced with definitions')
defs_pos := cgen.lines.len - 1 defs_pos := cgen.lines.len - 1
for file in v.files { for file in v.files {
for i, p in v.parsers { v.parse(file, .main)
if p.file_path == file {
v.parsers[i].parse(.main)
break
}
}
//if p.pref.autofree { p.scanner.text.free() free(p.scanner) } //if p.pref.autofree { p.scanner.text.free() free(p.scanner) }
// p.g.gen_x64() // p.g.gen_x64()
// Format all files (don't format automatically generated vlib headers) // 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 // 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) // 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 // free the string builder which held the generated methods
v.vgen_buf.free() v.vgen_buf.free()
vgen_parser.parse(.main) 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 // Parses imports, adds necessary libs, and then user files
fn (v mut V) add_v_files_to_compile() { 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 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 // 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 // which libs need to be added yet
mut user_files := []string mut user_files := []string
@ -607,46 +662,25 @@ fn (v mut V) add_v_files_to_compile() {
v.log('user_files:') v.log('user_files:')
println(user_files) println(user_files)
} }
// Parse builtin imports return user_files
for file in v.files { }
mut p := v.new_parser_file(file)
p.parse(.imports) // parse deps from already parsed builtin/user files
//if p.pref.autofree { p.scanner.text.free() free(p.scanner) } fn (v mut V) parse_lib_imports() {
} mut done_fits := []string
// Parse user imports for {
for file in user_files { for _, fit in v.table.file_imports {
mut p := v.new_parser_file(file) if fit.file_path in done_fits { continue }
p.parse(.imports) v.parse_file_imports(fit)
//if p.pref.autofree { p.scanner.text.free() free(p.scanner) } done_fits << fit.file_path
}
// 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) }
}
} }
if v.table.file_imports.size == done_fits.len { break}
} }
else { }
*/
// strange ( for mod in v.table.imports ) dosent loop all items // parse imports from file import table
// for mod in v.table.imports { fn (v mut V) parse_file_imports(fit &FileImportTable) {
for i := 0; i < v.table.imports.len; i++ { for _, mod in fit.imports {
mod := v.table.imports[i]
import_path := v.find_module_path(mod) import_path := v.find_module_path(mod)
vfiles := v.v_files_from_dir(import_path) vfiles := v.v_files_from_dir(import_path)
if vfiles.len == 0 { if vfiles.len == 0 {
@ -654,7 +688,7 @@ fn (v mut V) add_v_files_to_compile() {
} }
// Add all imports referenced by these libs // Add all imports referenced by these libs
for file in vfiles { for file in vfiles {
mut p := v.new_parser_file(file) mut p := v.new_parser_from_file(file)
p.parse(.imports) p.parse(.imports)
if p.import_table.module_name != mod { 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`') 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 p.pref.autofree { p.scanner.text.free() free(p.scanner) }
} }
} }
if v.pref.is_verbose { }
v.log('imports:')
println(v.table.imports) // return resolved dep graph (order deps)
} fn (v &V) resolve_deps() &DepGraph {
// graph deps
mut dep_graph := new_dep_graph() mut dep_graph := new_dep_graph()
dep_graph.from_import_tables(v.table.file_imports) dep_graph.from_import_tables(v.table.file_imports)
deps_resolved := dep_graph.resolve() deps_resolved := dep_graph.resolve()
if !deps_resolved.acyclic { if !deps_resolved.acyclic {
deps_resolved.display() deps_resolved.display()
verror('Import cycle detected') 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++
} }
return deps_resolved
} }
fn get_arg(joined_args, arg, def string) string { 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 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) { fn (v &V) log(s string) {
if !v.pref.is_verbose { if !v.pref.is_verbose {
return return
@ -795,14 +773,14 @@ fn new_v(args[]string) &V {
//out_name = '$TmpPath/vlib/${base}.o' //out_name = '$TmpPath/vlib/${base}.o'
out_name = mod + '.o' out_name = mod + '.o'
// Cross compiling? Use separate dirs for each os // Cross compiling? Use separate dirs for each os
/* /*
if target_os != os.user_os() { if target_os != os.user_os() {
os.mkdir('$TmpPath/vlib/$target_os') os.mkdir('$TmpPath/vlib/$target_os')
out_name = '$TmpPath/vlib/$target_os/${base}.o' out_name = '$TmpPath/vlib/$target_os/${base}.o'
println('target_os=$target_os user_os=${os.user_os()}') println('target_os=$target_os user_os=${os.user_os()}')
println('!Cross compiling $out_name') println('!Cross compiling $out_name')
} }
*/ */
} }
// TODO embed_vlib is temporarily the default mode. It's much slower. // TODO embed_vlib is temporarily the default mode. It's much slower.
else if !('-embed_vlib' in args) { else if !('-embed_vlib' in args) {
@ -867,18 +845,7 @@ fn new_v(args[]string) &V {
} }
} }
//println('OS=$_os') //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 // Location of all vlib files
vroot := os.dir(os.executable()) vroot := os.dir(os.executable())
//println('VROOT=$vroot') //println('VROOT=$vroot')
@ -898,21 +865,6 @@ fn new_v(args[]string) &V {
} }
//println('out_name:$out_name') //println('out_name:$out_name')
mut out_name_c := os.realpath( out_name ) + '.tmp.c' 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) cflags := get_cmdline_cflags(args)
@ -954,7 +906,6 @@ fn new_v(args[]string) &V {
return &V{ return &V{
os: _os os: _os
out_name: out_name out_name: out_name
files: files
dir: dir dir: dir
lang_dir: vroot lang_dir: vroot
table: new_table(obfuscate) table: new_table(obfuscate)

View File

@ -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'))
}
}
*/
}

View File

@ -6,6 +6,10 @@ module main
import os import os
const (
v_modules_path = os.home_dir() + '/.vmodules/'
)
// add a module and its deps (module speficic dag method) // add a module and its deps (module speficic dag method)
pub fn(graph mut DepGraph) from_import_tables(import_tables map[string]FileImportTable) { pub fn(graph mut DepGraph) from_import_tables(import_tables map[string]FileImportTable) {
for _, fit in import_tables { 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 { pub fn(graph &DepGraph) imports() []string {
mut mods := []string mut mods := []string
for node in graph.nodes { for node in graph.nodes {
if node.name == 'main' {
continue
}
mods << node.name mods << node.name
} }
return mods 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' // 'strings' => 'VROOT/vlib/strings'
// 'installed_mod' => '~/.vmodules/installed_mod' // 'installed_mod' => '~/.vmodules/installed_mod'
// 'local_mod' => '/path/to/current/dir/local_mod' // 'local_mod' => '/path/to/current/dir/local_mod'

View File

@ -96,7 +96,7 @@ const (
// new parser from string. unique id specified in `id`. // new parser from string. unique id specified in `id`.
// tip: use a hashing function to auto generate `id` from `text` eg. sha1.hexhash(text) // 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) mut p := v.new_parser(new_scanner(text), id)
p.scan_tokens() p.scan_tokens()
v.add_parser(p) v.add_parser(p)
@ -104,7 +104,7 @@ fn (v mut V) new_parser_string(text string, id string) Parser {
} }
// new parser from file. // 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")') //println('new_parser("$path")')
mut path_pcguard := '' mut path_pcguard := ''
mut path_platform := '.v' mut path_platform := '.v'
@ -505,7 +505,9 @@ fn (p mut Parser) const_decl() {
if p.first_pass() && p.table.known_const(name) { if p.first_pass() && p.table.known_const(name) {
p.error('redefinition of `$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 { if p.pass == .main {
// TODO hack // TODO hack
// cur_line has const's value right now. if it's just a number, then optimize generation: // 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 { fn (p &Parser) first_pass() bool {
return p.pass == .decl return p.pass == .decl
} }
@ -1221,6 +1224,7 @@ fn (p mut Parser) gen(s string) {
// Generate V header from V source // Generate V header from V source
fn (p mut Parser) vh_genln(s string) { fn (p mut Parser) vh_genln(s string) {
//println('vh $s')
p.vh_lines << s p.vh_lines << s
} }

View File

@ -9,7 +9,6 @@ fn C.memmove(byteptr, byteptr, int)
//fn C.malloc(int) byteptr //fn C.malloc(int) byteptr
fn C.realloc(byteptr, int) byteptr fn C.realloc(byteptr, int) byteptr
pub fn exit(code int) { pub fn exit(code int) {
C.exit(code) C.exit(code)
} }
@ -91,17 +90,17 @@ pub fn println(s string) {
pub fn eprintln(s string) { pub fn eprintln(s string) {
if isnil(s.str) { if isnil(s.str) {
panic('eprintln(NIL)') panic('eprintln(NIL)')
} }
$if mac { $if mac {
C.fprintf(stderr, '%.*s\n', s.len, s.str) C.fprintf(stderr, '%.*s\n', s.len, s.str)
C.fflush(stderr) C.fflush(stderr)
return return
} }
$if linux { $if linux {
C.fprintf(stderr, '%.*s\n', s.len, s.str) C.fprintf(stderr, '%.*s\n', s.len, s.str)
C.fflush(stderr) C.fflush(stderr)
return return
} }
// TODO issues with stderr and cross compiling for Linux // TODO issues with stderr and cross compiling for Linux
println(s) println(s)
} }

View File

@ -5,39 +5,39 @@
module builtin module builtin
/* /*
NB: A V string should be/is immutable from the point of view of 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 V user programs after it is first created. A V string is
also slightly larger than the equivalent C string because also slightly larger than the equivalent C string because
the V string also has an integer length attached. the V string also has an integer length attached.
This tradeoff is made, since V strings are created just *once*, This tradeoff is made, since V strings are created just *once*,
but potentially used *many times* over their lifetime. but potentially used *many times* over their lifetime.
The V string implementation uses a struct, that has a .str field, The V string implementation uses a struct, that has a .str field,
which points to a C style 0 terminated memory block. Although not 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*. is *very useful for C interoperability*.
The V string implementation also has an integer .len field, The V string implementation also has an integer .len field,
containing the length of the .str field, excluding the containing the length of the .str field, excluding the
terminating 0 (just like the C's strlen(s) would do). terminating 0 (just like the C's strlen(s) would do).
The 0 ending of .str, and the .len field, mean that in practice: 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 a) a V string s can be used very easily, wherever a
C string is needed, just by passing s.str, C string is needed, just by passing s.str,
without a need for further conversion/copying. without a need for further conversion/copying.
b) where strlen(s) is needed, you can just pass s.len, b) where strlen(s) is needed, you can just pass s.len,
without having to constantly recompute the length of s without having to constantly recompute the length of s
*over and over again* like some C programs do. This is because *over and over again* like some C programs do. This is because
V strings are immutable and so their length does not change. V strings are immutable and so their length does not change.
Ordinary V code *does not need* to be concerned with 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 additional 0 in the .str field. The 0 *must* be put there by the
low level string creating functions inside this module. low level string creating functions inside this module.
Failing to do this will lead to programs that work most of the 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, 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). 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. // String data is reused, not copied.
pub fn tos(s byteptr, len int) string { pub fn tos(s byteptr, len int) string {
// This should never happen. // This should never happen.
if isnil(s) { if s == 0 {
panic('tos(): nil string') panic('tos(): nil string')
} }
return string { return string {
@ -79,7 +79,7 @@ pub fn tos(s byteptr, len int) string {
} }
pub fn tos_clone(s byteptr) string { pub fn tos_clone(s byteptr) string {
if isnil(s) { if s == 0 {
panic('tos: nil string') panic('tos: nil string')
} }
return tos2(s).clone() 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. // Same as `tos`, but calculates the length. Called by `string(bytes)` casts.
// Used only internally. // Used only internally.
fn tos2(s byteptr) string { fn tos2(s byteptr) string {
if isnil(s) { if s == 0 {
panic('tos2: nil string') panic('tos2: nil string')
} }
len := vstrlen(s) return string {
res := tos(s, len) str: s
return res 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 { pub fn (a string) clone() string {

View File

@ -13,7 +13,7 @@ pub:
pub fn new_builder(initial_size int) Builder { pub fn new_builder(initial_size int) Builder {
return Builder { return Builder {
//buf: _make(0, initial_size, sizeof(byte)) // buf: _make(0, initial_size, sizeof(byte))
} }
} }