1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

fix maps; use maps for storing functions; verify struct initialization

This commit is contained in:
Alexander Medvednikov 2019-06-30 22:44:15 +02:00
parent 8e6cb1e1c2
commit 5936ab16c8
7 changed files with 222 additions and 199 deletions

View File

@ -151,10 +151,9 @@ fn (g mut CGen) register_thread_fn(wrapper_name, wrapper_text, struct_text strin
fn (c mut V) prof_counters() string { fn (c mut V) prof_counters() string {
mut res := []string mut res := []string
// Global fns // Global fns
for f in c.table.fns { //for f in c.table.fns {
res << 'double ${c.table.cgen_name(f)}_time;' //res << 'double ${c.table.cgen_name(f)}_time;'
// println(f.name) //}
}
// Methods // Methods
for typ in c.table.types { for typ in c.table.types {
// println('') // println('')
@ -170,11 +169,10 @@ fn (c mut V) prof_counters() string {
fn (p mut Parser) print_prof_counters() string { fn (p mut Parser) print_prof_counters() string {
mut res := []string mut res := []string
// Global fns // Global fns
for f in p.table.fns { //for f in p.table.fns {
counter := '${p.table.cgen_name(f)}_time' //counter := '${p.table.cgen_name(f)}_time'
res << 'if ($counter) printf("%%f : $f.name \\n", $counter);' //res << 'if ($counter) printf("%%f : $f.name \\n", $counter);'
// println(f.name) //}
}
// Methods // Methods
for typ in p.table.types { for typ in p.table.types {
// println('') // println('')

View File

@ -59,8 +59,22 @@ enum Pass {
} }
*/ */
struct V {
mut:
os Os // the OS to build for
out_name_c string // name of the temporary C file
files []string // all V files that need to be parsed and compiled
dir string // directory (or file) being compiled (TODO rename to path?)
table *Table // table with types, vars, functions etc
cgen *CGen // C code generator
pref *Preferences // all the prefrences and settings extracted to a struct for reusability
lang_dir string // "~/code/v"
out_name string // "program.exe"
vroot string
}
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`
@ -79,22 +93,7 @@ struct Preferences {
sanitize bool // use Clang's new "-fsanitize" option sanitize bool // use Clang's new "-fsanitize" option
} }
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 prefrences and settings extracted to a struct for reusability
lang_dir string // "~/code/v"
out_name string // "program.exe"
vroot string
}
fn main() { fn main() {
// There's no `flags` module yet, so args have to be parsed manually
args := os.args args := os.args
// Print the version and exit. // Print the version and exit.
if '-v' in args || 'version' in args { if '-v' in args || 'version' in args {
@ -142,35 +141,35 @@ fn main() {
return return
} }
// Construct the V object from command line arguments // Construct the V object from command line arguments
mut c := new_v(args) mut v := new_v(args)
if c.pref.is_verbose { if v.pref.is_verbose {
println(args) println(args)
} }
// Generate the docs and exit // Generate the docs and exit
if args.contains('doc') { if args.contains('doc') {
// c.gen_doc_html_for_module(args.last()) // v.gen_doc_html_for_module(args.last())
exit(0) exit(0)
} }
c.compile() v.compile()
} }
fn (c mut V) compile() { fn (v mut V) compile() {
mut cgen := c.cgen mut cgen := v.cgen
cgen.genln('// Generated by V') cgen.genln('// Generated by V')
// Add user files to compile // Add user files to compile
c.add_user_v_files() v.add_user_v_files()
if c.pref.is_verbose { if v.pref.is_verbose {
println('all .v files:') println('all .v files:')
println(c.files) println(v.files)
} }
// First pass (declarations) // First pass (declarations)
for file in c.files { for file in v.files {
mut p := c.new_parser(file, RUN_DECLS) mut p := v.new_parser(file, RUN_DECLS)
p.parse() p.parse()
} }
// Main pass // Main pass
cgen.run = RUN_MAIN cgen.run = RUN_MAIN
if c.pref.is_play { if v.pref.is_play {
cgen.genln('#define VPLAY (1) ') cgen.genln('#define VPLAY (1) ')
} }
cgen.genln(' cgen.genln('
@ -242,31 +241,31 @@ byteptr g_str_buf;
int load_so(byteptr); int load_so(byteptr);
void reload_so(); void reload_so();
void init_consts();') void init_consts();')
imports_json := c.table.imports.contains('json') imports_json := v.table.imports.contains('json')
// TODO remove global UI hack // TODO remove global UI hack
if c.os == MAC && ((c.pref.build_mode == EMBED_VLIB && c.table.imports.contains('ui')) || if v.os == MAC && ((v.pref.build_mode == EMBED_VLIB && v.table.imports.contains('ui')) ||
(c.pref.build_mode == BUILD && c.dir.contains('/ui'))) { (v.pref.build_mode == BUILD && v.dir.contains('/ui'))) {
cgen.genln('id defaultFont = 0; // main.v') cgen.genln('id defaultFont = 0; // main.v')
} }
// TODO remove ugly .c include once V has its own json parser // TODO remove ugly .c include once V has its own json parser
// Embed cjson either in embedvlib or in json.o // Embed cjson either in embedvlib or in json.o
if imports_json && c.pref.build_mode == EMBED_VLIB || if imports_json && v.pref.build_mode == EMBED_VLIB ||
(c.pref.build_mode == BUILD && c.out_name.contains('json.o')) { (v.pref.build_mode == BUILD && v.out_name.contains('json.o')) {
cgen.genln('#include "cJSON.c" ') cgen.genln('#include "cJSON.c" ')
} }
// We need the cjson header for all the json decoding user will do in default mode // We need the cjson header for all the json decoding user will do in default mode
if c.pref.build_mode == DEFAULT_MODE { if v.pref.build_mode == DEFAULT_MODE {
if imports_json { if imports_json {
cgen.genln('#include "cJSON.h"') cgen.genln('#include "cJSON.h"')
} }
} }
if c.pref.build_mode == EMBED_VLIB || c.pref.build_mode == DEFAULT_MODE { if v.pref.build_mode == EMBED_VLIB || v.pref.build_mode == DEFAULT_MODE {
// If we declare these for all modes, then when running `v a.v` we'll get // If we declare these for all modes, then when running `v a.v` we'll get
// `/usr/bin/ld: multiple definition of 'total_m'` // `/usr/bin/ld: multiple definition of 'total_m'`
// TODO // TODO
//cgen.genln('i64 total_m = 0; // For counting total RAM allocated') //cgen.genln('i64 total_m = 0; // For counting total RAM allocated')
cgen.genln('int g_test_ok = 1; ') cgen.genln('int g_test_ok = 1; ')
if c.table.imports.contains('json') { if v.table.imports.contains('json') {
cgen.genln(' cgen.genln('
#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) #define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key))
') ')
@ -278,16 +277,16 @@ void init_consts();')
cgen.genln('/*================================== FNS =================================*/') cgen.genln('/*================================== FNS =================================*/')
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 c.files { for file in v.files {
mut p := c.new_parser(file, RUN_MAIN) mut p := v.new_parser(file, RUN_MAIN)
p.parse() p.parse()
// 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)
if !c.pref.nofmt && !file.contains('/vlib/') { if !v.pref.nofmt && !file.contains('/vlib/') {
// new vfmt is not ready yet // new vfmt is not ready yet
} }
} }
c.log('Done parsing.') v.log('Done parsing.')
// Write everything // Write everything
mut d := new_string_builder(10000)// Just to avoid some unnecessary allocations mut d := new_string_builder(10000)// Just to avoid some unnecessary allocations
d.writeln(cgen.includes.join_lines()) d.writeln(cgen.includes.join_lines())
@ -298,14 +297,14 @@ void init_consts();')
d.writeln(cgen.fns.join_lines()) d.writeln(cgen.fns.join_lines())
d.writeln(cgen.consts.join_lines()) d.writeln(cgen.consts.join_lines())
d.writeln(cgen.thread_args.join_lines()) d.writeln(cgen.thread_args.join_lines())
if c.pref.is_prof { if v.pref.is_prof {
d.writeln('; // Prof counters:') d.writeln('; // Prof counters:')
d.writeln(c.prof_counters()) d.writeln(v.prof_counters())
} }
dd := d.str() dd := d.str()
cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile
// if c.build_mode in [.default, .embed_vlib] { // if v.build_mode in [.default, .embed_vlib] {
if c.pref.build_mode == DEFAULT_MODE || c.pref.build_mode == EMBED_VLIB { if v.pref.build_mode == DEFAULT_MODE || v.pref.build_mode == EMBED_VLIB {
// vlib can't have `init_consts()` // vlib can't have `init_consts()`
cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }') cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }')
// _STR function can't be defined in vlib // _STR function can't be defined in vlib
@ -345,29 +344,31 @@ string _STR_TMP(const char *fmt, ...) {
} }
// Make sure the main function exists // Make sure the main function exists
// Obviously we don't need it in libraries // Obviously we don't need it in libraries
if c.pref.build_mode != BUILD { if v.pref.build_mode != BUILD {
if !c.table.main_exists() && !c.pref.is_test { if !v.table.main_exists() && !v.pref.is_test {
// It can be skipped in single file programs // It can be skipped in single file programs
if c.pref.is_script { if v.pref.is_script {
//println('Generating main()...') //println('Generating main()...')
cgen.genln('int main() { $cgen.fn_main; return 0; }') cgen.genln('int main() { $cgen.fn_main; return 0; }')
} }
else { else {
println('panic: function `main` is undeclared in the main module') println('panic: function `main` is undeclared in the main module')
exit(1)
} }
} }
// Generate `main` which calls every single test function // Generate `main` which calls every single test function
else if c.pref.is_test { else if v.pref.is_test {
cgen.genln('int main() { init_consts();') cgen.genln('int main() { init_consts();')
for v in c.table.fns { for entry in v.table.fns.entries {
if v.name.starts_with('test_') { f := v.table.fns[entry.key]
cgen.genln('$v.name();') if f.name.starts_with('test_') {
cgen.genln('$f.name();')
} }
} }
cgen.genln('return g_test_ok == 0; }') cgen.genln('return g_test_ok == 0; }')
} }
} }
if c.pref.is_live { if v.pref.is_live {
cgen.genln(' int load_so(byteptr path) { cgen.genln(' int load_so(byteptr path) {
printf("load_so %s\\n", path); dlclose(live_lib); live_lib = dlopen(path, RTLD_LAZY); printf("load_so %s\\n", path); dlclose(live_lib); live_lib = dlopen(path, RTLD_LAZY);
if (!live_lib) {puts("open failed"); exit(1); return 0;} if (!live_lib) {puts("open failed"); exit(1); return 0;}
@ -378,70 +379,71 @@ string _STR_TMP(const char *fmt, ...) {
cgen.genln('return 1; }') cgen.genln('return 1; }')
} }
cgen.save() cgen.save()
if c.pref.is_verbose { if v.pref.is_verbose {
c.log('flags=') v.log('flags=')
println(c.table.flags) println(v.table.flags)
} }
c.cc() v.cc()
if c.pref.is_test || c.pref.is_run { if v.pref.is_test || v.pref.is_run {
if true || c.pref.is_verbose { if true || v.pref.is_verbose {
println('============ running $c.out_name ============') println('============ running $v.out_name ============')
} }
mut cmd := if c.out_name.starts_with('/') { mut cmd := if v.out_name.starts_with('/') {
c.out_name v.out_name
} }
else { else {
'./' + c.out_name './' + v.out_name
} }
$if windows { $if windows {
cmd = c.out_name cmd = v.out_name
} }
if os.args.len > 3 { if os.args.len > 3 {
cmd += ' ' + os.args.right(3).join(' ') cmd += ' ' + os.args.right(3).join(' ')
} }
ret := os.system(cmd) ret := os.system(cmd)
if ret != 0 { if ret != 0 {
if !v.pref.is_test {
s := os.exec(cmd) s := os.exec(cmd)
println(s) println(s)
println('failed to run the compiled program, this should never happen') println('failed to run the compiled program')
println('please submit a GitHub issue') }
exit(1) exit(1)
} }
} }
} }
fn (c mut V) cc() { fn (v mut V) cc() {
linux_host := os.user_os() == 'linux' linux_host := os.user_os() == 'linux'
c.log('cc() isprod=$c.pref.is_prod outname=$c.out_name') v.log('cc() isprod=$v.pref.is_prod outname=$v.out_name')
mut a := ['-w']// arguments for the C compiler mut a := ['-w']// arguments for the C compiler
flags := c.table.flags.join(' ') flags := v.table.flags.join(' ')
/* /*
mut shared := '' mut shared := ''
if c.pref.is_so { if v.pref.is_so {
a << '-shared'// -Wl,-z,defs' a << '-shared'// -Wl,-z,defs'
c.out_name = c.out_name + '.so' v.out_name = v.out_name + '.so'
} }
*/ */
if c.pref.is_prod { if v.pref.is_prod {
a << '-O2' a << '-O2'
} }
else { else {
a << '-g' a << '-g'
} }
mut libs := ''// builtin.o os.o http.o etc mut libs := ''// builtin.o os.o http.o etc
if c.pref.build_mode == BUILD { if v.pref.build_mode == BUILD {
a << '-c' a << '-c'
} }
else if c.pref.build_mode == EMBED_VLIB { else if v.pref.build_mode == EMBED_VLIB {
// //
} }
else if c.pref.build_mode == DEFAULT_MODE { else if v.pref.build_mode == DEFAULT_MODE {
libs = '$TmpPath/vlib/builtin.o' libs = '$TmpPath/vlib/builtin.o'
if !os.file_exists(libs) { if !os.file_exists(libs) {
println('`builtin.o` not found') println('`builtin.o` not found')
exit(1) exit(1)
} }
for imp in c.table.imports { for imp in v.table.imports {
if imp == 'webview' { if imp == 'webview' {
continue continue
} }
@ -451,62 +453,62 @@ fn (c mut V) cc() {
// -I flags // -I flags
/* /*
mut args := '' mut args := ''
for flag in c.table.flags { for flag in v.table.flags {
if !flag.starts_with('-l') { if !flag.starts_with('-l') {
args += flag args += flag
args += ' ' args += ' '
} }
} }
*/ */
if c.pref.sanitize { if v.pref.sanitize {
a << '-fsanitize=leak' a << '-fsanitize=leak'
} }
// Cross compiling linux // Cross compiling linux
sysroot := '/Users/alex/tmp/lld/linuxroot/' sysroot := '/Users/alex/tmp/lld/linuxroot/'
if c.os == LINUX && !linux_host { if v.os == LINUX && !linux_host {
// Build file.o // Build file.o
a << '-c --sysroot=$sysroot -target x86_64-linux-gnu' a << '-c --sysroot=$sysroot -target x86_64-linux-gnu'
// Right now `out_name` can be `file`, not `file.o` // Right now `out_name` can be `file`, not `file.o`
if !c.out_name.ends_with('.o') { if !v.out_name.ends_with('.o') {
c.out_name = c.out_name + '.o' v.out_name = v.out_name + '.o'
} }
} }
// Cross compiling windows // Cross compiling windows
// sysroot := '/Users/alex/tmp/lld/linuxroot/' // sysroot := '/Users/alex/tmp/lld/linuxroot/'
// Output executable name // Output executable name
// else { // else {
a << '-o $c.out_name' a << '-o $v.out_name'
// The C file we are compiling // The C file we are compiling
a << '$TmpPath/$c.out_name_c' a << '$TmpPath/$v.out_name_c'
// } // }
// Min macos version is mandatory I think? // Min macos version is mandatory I think?
if c.os == MAC { if v.os == MAC {
a << '-mmacosx-version-min=10.7' a << '-mmacosx-version-min=10.7'
} }
a << flags a << flags
a << libs a << libs
// macOS code can include objective C TODO remove once objective C is replaced with C // macOS code can include objective C TODO remove once objective C is replaced with C
if c.os == MAC { if v.os == MAC {
a << '-x objective-c' a << '-x objective-c'
} }
// Without these libs compilation will fail on Linux // Without these libs compilation will fail on Linux
if c.os == LINUX && c.pref.build_mode != BUILD { if v.os == LINUX && v.pref.build_mode != BUILD {
a << '-lm -ldl -lpthread' a << '-lm -ldl -lpthread'
} }
// Find clang executable // Find clang executable
fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang' //fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang'
args := a.join(' ') args := a.join(' ')
mut cmd := if os.file_exists(fast_clang) { //mut cmd := if os.file_exists(fast_clang) {
'$fast_clang $args' //'$fast_clang $args'
} //}
else { //else {
'cc $args' mut cmd := 'cc $args'
} //}
$if windows { $if windows {
cmd = 'gcc $args' cmd = 'gcc $args'
} }
// Print the C command // Print the C command
if c.pref.show_c_cmd || c.pref.is_verbose { if v.pref.show_c_cmd || v.pref.is_verbose {
println('\n==========\n$cmd\n=========\n') println('\n==========\n$cmd\n=========\n')
} }
// Run // Run
@ -517,29 +519,29 @@ mut args := ''
panic('clang error') panic('clang error')
} }
// Link it if we are cross compiling and need an executable // Link it if we are cross compiling and need an executable
if c.os == LINUX && !linux_host && c.pref.build_mode != BUILD { if v.os == LINUX && !linux_host && v.pref.build_mode != BUILD {
c.out_name = c.out_name.replace('.o', '') v.out_name = v.out_name.replace('.o', '')
obj_file := c.out_name + '.o' obj_file := v.out_name + '.o'
println('linux obj_file=$obj_file out_name=$c.out_name') println('linux obj_file=$obj_file out_name=$v.out_name')
ress := os.exec('/usr/local/Cellar/llvm/8.0.0/bin/ld.lld --sysroot=$sysroot ' + ress := os.exec('/usr/local/Cellar/llvm/8.0.0/bin/ld.lld --sysroot=$sysroot ' +
'-v -o $c.out_name ' + '-v -o $v.out_name ' +
'-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 ' + '-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 ' +
'/usr/lib/x86_64-linux-gnu/crt1.o ' + '/usr/lib/x86_64-linux-gnu/crt1.o ' +
'$sysroot/lib/x86_64-linux-gnu/libm-2.28.a ' + '$sysroot/lib/x86_64-linux-gnu/libm-2.28.a ' +
'/usr/lib/x86_64-linux-gnu/crti.o ' + '/usr/lib/x86_64-linux-gnu/crti.o ' +
obj_file + obj_file +
' /usr/lib/x86_64-linux-gnu/libc.so ' + ' /usr/lib/x86_64-linux-gnu/libv.so ' +
'/usr/lib/x86_64-linux-gnu/crtn.o') '/usr/lib/x86_64-linux-gnu/crtn.o')
println(ress) println(ress)
if ress.contains('error:') { if ress.contains('error:') {
exit(1) exit(1)
} }
println('linux cross compilation done. resulting binary: "$c.out_name"') println('linux cross compilation done. resulting binary: "$v.out_name"')
} }
//os.rm('$TmpPath/$c.out_name_c') //os.rm('$TmpPath/$v.out_name_c')
} }
fn (c &V) v_files_from_dir(dir string) []string { fn (v &V) v_files_from_dir(dir string) []string {
mut res := []string mut res := []string
if !os.file_exists(dir) { if !os.file_exists(dir) {
panic('$dir doesn\'t exist') panic('$dir doesn\'t exist')
@ -547,38 +549,38 @@ fn (c &V) v_files_from_dir(dir string) []string {
panic('$dir isn\'t a directory') panic('$dir isn\'t a directory')
} }
mut files := os.ls(dir) mut files := os.ls(dir)
if c.pref.is_verbose { if v.pref.is_verbose {
println('v_files_from_dir ("$dir")') println('v_files_from_dir ("$dir")')
} }
// println(files.len) // println(files.len)
// println(files) // println(files)
files.sort() files.sort()
for file in files { for file in files {
c.log('F=$file') v.log('F=$file')
if !file.ends_with('.v') && !file.ends_with('.vh') { if !file.ends_with('.v') && !file.ends_with('.vh') {
continue continue
} }
if file.ends_with('_test.v') { if file.ends_with('_test.v') {
continue continue
} }
if file.ends_with('_win.v') && c.os != WINDOWS { if file.ends_with('_win.v') && v.os != WINDOWS {
continue continue
} }
if file.ends_with('_lin.v') && c.os != LINUX { if file.ends_with('_lin.v') && v.os != LINUX {
continue continue
} }
if file.ends_with('_mac.v') && c.os != MAC { if file.ends_with('_mav.v') && v.os != MAC {
lin_file := file.replace('_mac.v', '_lin.v') lin_file := file.replace('_mav.v', '_lin.v')
// println('lin_file="$lin_file"') // println('lin_file="$lin_file"')
// If there are both _mac.v and _lin.v, don't use _mac.v // If there are both _mav.v and _lin.v, don't use _mav.v
if os.file_exists('$dir/$lin_file') { if os.file_exists('$dir/$lin_file') {
continue continue
} }
else if c.os == WINDOWS { else if v.os == WINDOWS {
continue continue
} }
else { else {
// If there's only _mac.v, then it can be used on Linux too // If there's only _mav.v, then it can be used on Linux too
} }
} }
res << '$dir/$file' res << '$dir/$file'
@ -587,9 +589,9 @@ fn (c &V) v_files_from_dir(dir string) []string {
} }
// Parses imports, adds necessary libs, and then user files // Parses imports, adds necessary libs, and then user files
fn (c mut V) add_user_v_files() { fn (v mut V) add_user_v_files() {
mut dir := c.dir mut dir := v.dir
c.log('add_v_files($dir)') v.log('add_v_files($dir)')
// Need to store user files separately, because they have to be added after libs, but we dont know // 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
@ -609,7 +611,7 @@ fn (c mut V) add_user_v_files() {
} }
else { else {
// Add files from the dir user is compiling (only .v files) // Add files from the dir user is compiling (only .v files)
files := c.v_files_from_dir(dir) files := v.v_files_from_dir(dir)
for file in files { for file in files {
user_files << file user_files << file
} }
@ -618,65 +620,65 @@ fn (c mut V) add_user_v_files() {
println('No input .v files') println('No input .v files')
exit(1) exit(1)
} }
if c.pref.is_verbose { if v.pref.is_verbose {
c.log('user_files:') v.log('user_files:')
println(user_files) println(user_files)
} }
// Parse user imports // Parse user imports
for file in user_files { for file in user_files {
mut p := c.new_parser(file, RUN_IMPORTS) mut p := v.new_parser(file, RUN_IMPORTS)
p.parse() p.parse()
} }
// Parse lib imports // Parse lib imports
if c.pref.build_mode == DEFAULT_MODE { if v.pref.build_mode == DEFAULT_MODE {
for i := 0; i < c.table.imports.len; i++ { for i := 0; i < v.table.imports.len; i++ {
pkg := c.table.imports[i] pkg := v.table.imports[i]
vfiles := c.v_files_from_dir('$TmpPath/vlib/$pkg') vfiles := v.v_files_from_dir('$TmpPath/vlib/$pkg')
// Add all imports referenced by these libs // Add all imports referenced by these libs
for file in vfiles { for file in vfiles {
mut p := c.new_parser(file, RUN_IMPORTS) mut p := v.new_parser(file, RUN_IMPORTS)
p.parse() p.parse()
} }
} }
} }
else { else {
// TODO this used to crash compiler? // TODO this used to crash compiler?
// for pkg in c.table.imports { // for pkg in v.table.imports {
for i := 0; i < c.table.imports.len; i++ { for i := 0; i < v.table.imports.len; i++ {
pkg := c.table.imports[i] pkg := v.table.imports[i]
// mut import_path := '$c.lang_dir/$pkg' // mut import_path := '$v.lang_dir/$pkg'
vfiles := c.v_files_from_dir('$c.lang_dir/vlib/$pkg') vfiles := v.v_files_from_dir('$v.lang_dir/vlib/$pkg')
// Add all imports referenced by these libs // Add all imports referenced by these libs
for file in vfiles { for file in vfiles {
mut p := c.new_parser(file, RUN_IMPORTS) mut p := v.new_parser(file, RUN_IMPORTS)
p.parse() p.parse()
} }
} }
} }
if c.pref.is_verbose { if v.pref.is_verbose {
c.log('imports:') v.log('imports:')
println(c.table.imports) println(v.table.imports)
} }
// Only now add all combined lib files // Only now add all combined lib files
for pkg in c.table.imports { for pkg in v.table.imports {
mut module_path := '$c.lang_dir/vlib/$pkg' mut module_path := '$v.lang_dir/vlib/$pkg'
// If we are in default mode, we don't parse vlib .v files, but header .vh files in // If we are in default mode, we don't parse vlib .v files, but header .vh files in
// TmpPath/vlib // TmpPath/vlib
// These were generated by vfmt // These were generated by vfmt
if c.pref.build_mode == DEFAULT_MODE || c.pref.build_mode == BUILD { if v.pref.build_mode == DEFAULT_MODE || v.pref.build_mode == BUILD {
module_path = '$TmpPath/vlib/$pkg' module_path = '$TmpPath/vlib/$pkg'
} }
vfiles := c.v_files_from_dir(module_path) vfiles := v.v_files_from_dir(module_path)
for vfile in vfiles { for vfile in vfiles {
c.files << vfile v.files << vfile
} }
// TODO c.files.append_array(vfiles) // TODO v.files.append_array(vfiles)
} }
// Add user code last // Add user code last
for file in user_files { for file in user_files {
c.files << file v.files << file
} }
// c.files.append_array(user_files) // v.files.append_array(user_files)
} }
fn get_arg(joined_args, arg, def string) string { fn get_arg(joined_args, arg, def string) string {
@ -695,8 +697,8 @@ fn get_arg(joined_args, arg, def string) string {
return res return res
} }
fn (c &V) log(s string) { fn (v &V) log(s string) {
if !c.pref.is_verbose { if !v.pref.is_verbose {
return return
} }
println(s) println(s)

View File

@ -763,6 +763,21 @@ fn (p mut Parser) get_type() string {
p.check(RSBR) p.check(RSBR)
} }
} }
// map[string]int
if !p.builtin_pkg && p.tok == NAME && p.lit == 'map' {
p.next()
p.check(LSBR)
key_type := p.check_name()
if key_type != 'string' {
p.error('maps only support string keys for now')
}
p.check(RSBR)
val_type := p.check_name()
typ= 'map_$val_type'
p.register_map(typ)
return typ
}
//
for p.tok == MUL { for p.tok == MUL {
mul = true mul = true
nr_muls++ nr_muls++
@ -2345,6 +2360,18 @@ fn (p mut Parser) register_array(typ string) {
} }
} }
fn (p mut Parser) register_map(typ string) {
if typ.contains('*') {
println('bad map $typ')
return
}
if !p.table.known_type(typ) {
p.register_type_with_parent(typ, 'map')
p.cgen.typedefs << 'typedef map $typ;'
}
}
fn (p mut Parser) struct_init(is_c_struct_init bool) string { fn (p mut Parser) struct_init(is_c_struct_init bool) string {
p.is_struct_init = true p.is_struct_init = true
mut typ := p.get_type() mut typ := p.get_type()
@ -2390,11 +2417,12 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string {
if !t.has_field(field) { if !t.has_field(field) {
p.error('`$t.name` has no field `$field`') p.error('`$t.name` has no field `$field`')
} }
f := t.find_field(field)
inited_fields << field inited_fields << field
p.gen('.$field = ') p.gen('.$field = ')
p.check(COLON) p.check(COLON)
p.fspace() p.fspace()
p.expression() p.check_types(p.bool_expression(), f.typ)
if p.tok == COMMA { if p.tok == COMMA {
p.next() p.next()
} }
@ -2419,7 +2447,7 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string {
p.error('pointer field `${typ}.${field.name}` must be initialized') p.error('pointer field `${typ}.${field.name}` must be initialized')
} }
def_val := type_default(field_typ) def_val := type_default(field_typ)
if def_val != '' { if def_val != '' && def_val != '{}' {
p.gen('.$field.name = $def_val') p.gen('.$field.name = $def_val')
if i != t.fields.len - 1 { if i != t.fields.len - 1 {
p.gen(',') p.gen(',')
@ -2938,6 +2966,9 @@ fn (p mut Parser) switch_statement() {
} }
fn (p mut Parser) assert_statement() { fn (p mut Parser) assert_statement() {
if p.first_run() {
return
}
p.check(ASSERT) p.check(ASSERT)
p.fspace() p.fspace()
tmp := p.get_tmp() tmp := p.get_tmp()

View File

@ -8,8 +8,8 @@ struct Table {
mut: mut:
types []Type types []Type
consts []Var consts []Var
fns []Fn fns map[string]Fn
obf_ids map_int // obf_ids 'myfunction'] == 23 obf_ids map[string]int // obf_ids 'myfunction'] == 23
packages []string // List of all modules registered by the application packages []string // List of all modules registered by the application
imports []string // List of all imports imports []string // List of all imports
flags []string // ['-framework Cocoa', '-lglfw3'] flags []string // ['-framework Cocoa', '-lglfw3']
@ -112,6 +112,7 @@ fn is_float_type(typ string) bool {
fn new_table(obfuscate bool) *Table { fn new_table(obfuscate bool) *Table {
mut t := &Table { mut t := &Table {
obf_ids: map[string]int{} obf_ids: map[string]int{}
fns: map[string]Fn{}
obfuscate: obfuscate obfuscate: obfuscate
} }
t.register_type('int') t.register_type('int')
@ -186,15 +187,8 @@ fn (p mut Parser) register_global(name, typ string) {
} }
} }
// TODO PERF O(N) this slows down the comiler a lot! fn (t mut Table) register_fn(new_fn Fn) {
fn (t mut Table) register_fn(f Fn) { t.fns[new_fn.name] = new_fn
// Avoid duplicate fn names TODO why? the name should already be unique?
for ff in t.fns {
if ff.name == f.name {
return
}
}
t.fns << f
} }
fn (table &Table) known_type(typ string) bool { fn (table &Table) known_type(typ string) bool {
@ -210,24 +204,17 @@ fn (table &Table) known_type(typ string) bool {
return false return false
} }
// TODO PERF O(N) this slows down the compiler a lot!
fn (t &Table) find_fn(name string) Fn { fn (t &Table) find_fn(name string) Fn {
for f in t.fns { f := t.fns[name]
if f.name == name { if !isnil(f.name.str) {
return f return f
} }
}
return Fn{} return Fn{}
} }
// TODO PERF O(N) this slows down the compiler a lot!
fn (t &Table) known_fn(name string) bool { fn (t &Table) known_fn(name string) bool {
for f in t.fns { f := t.find_fn(name)
if f.name == name { return f.name != ''
return true
}
}
return false
} }
fn (t &Table) known_const(name string) bool { fn (t &Table) known_const(name string) bool {
@ -240,7 +227,6 @@ fn (t mut Table) register_type(typ string) {
if typ.len == 0 { if typ.len == 0 {
return return
} }
// println('REGISTER TYPE $typ')
for typ2 in t.types { for typ2 in t.types {
if typ2.name == typ { if typ2.name == typ {
return return
@ -553,6 +539,7 @@ fn type_default(typ string) string {
case 'byte*': return '0' case 'byte*': return '0'
case 'bool': return '0' case 'bool': return '0'
} }
return '{}'
return '' return ''
} }
@ -568,7 +555,8 @@ fn (t &Table) is_interface(name string) bool {
// Do we have fn main()? // Do we have fn main()?
fn (t &Table) main_exists() bool { fn (t &Table) main_exists() bool {
for f in t.fns { for entry in t.fns.entries {
f := t.fns[entry.key]
if f.name == 'main' { if f.name == 'main' {
return true return true
} }

View File

@ -25,7 +25,7 @@ pub:
// next *Entry // next *Entry
} }
pub fn new_map(cap, elm_size int) map { fn new_map(cap, elm_size int) map {
res := map { res := map {
// len: len, // len: len,
element_size: elm_size element_size: elm_size
@ -59,15 +59,6 @@ fn (m mut map) _set(key string, val voidptr) {
m.is_sorted = false m.is_sorted = false
} }
fn volt_abs(n int) int {
// println('volt_abs($n)')
if n < 0 {
// println('< 0: -($n)')
return -n
}
return n
}
fn (m map) bs(query string, start, end int, out voidptr) { fn (m map) bs(query string, start, end int, out voidptr) {
// println('bs "$query" $start -> $end') // println('bs "$query" $start -> $end')
mid := start + ((end - start) / 2) mid := start + ((end - start) / 2)

View File

@ -1,5 +1,10 @@
struct User {
name string
}
struct A { struct A {
m map_int m map[string]int
users map[string]User
} }
fn (a mut A) set(key string, val int) { fn (a mut A) set(key string, val int) {
@ -10,10 +15,19 @@ fn test_map() {
mut m := map[string]int{} mut m := map[string]int{}
m['hi'] = 80 m['hi'] = 80
assert m['hi'] == 80 assert m['hi'] == 80
////
mut users := map[string]User{}
users['1'] = User{'Peter'}
peter := users['1']
assert peter.name == 'Peter'
mut a := A{ mut a := A{
m: new_map(1, sizeof(int)) m: map[string]int{}
users: map[string]User{}
} }
a.users['Bob'] = User{'Bob'}
q := a.users['Bob']
assert q.name == 'Bob'
a.m['one'] = 1 a.m['one'] = 1
a.set('two', 2) a.set('two', 2)
assert a.m['one'] == 1 assert a.m['one'] == 1

View File

@ -636,7 +636,6 @@ __global g_ustring_runes []int
pub fn (s string) ustring_tmp() ustring { pub fn (s string) ustring_tmp() ustring {
mut res := ustring { mut res := ustring {
s: s s: s
runes: 0
} }
res.runes = g_ustring_runes res.runes = g_ustring_runes
res.runes.len = s.len res.runes.len = s.len