v2: initial module support
							parent
							
								
									d87cb3f672
								
							
						
					
					
						commit
						2d5c70832c
					
				|  | @ -7,6 +7,7 @@ import ( | |||
| 	os | ||||
| 	strings | ||||
| 	filepath | ||||
| 	v.builder | ||||
| 	//compiler.x64
 | ||||
| 	time | ||||
| ) | ||||
|  | @ -35,7 +36,7 @@ mut: | |||
| 	table                  &Table | ||||
| 	import_table           ImportTable // Holds imports for just the file being parsed
 | ||||
| 	pass                   Pass | ||||
| 	os                     OS | ||||
| 	os                     builder.OS | ||||
| 	inside_const           bool | ||||
| 	expr_var               Var | ||||
| 	has_immutable_field    bool | ||||
|  |  | |||
|  | @ -29,20 +29,6 @@ const ( | |||
| 		'dragonfly', 'android', 'js', 'solaris', 'haiku', 'linux_or_macos'] | ||||
| ) | ||||
| 
 | ||||
| enum OS { | ||||
| 	mac | ||||
| 	linux | ||||
| 	windows | ||||
| 	freebsd | ||||
| 	openbsd | ||||
| 	netbsd | ||||
| 	dragonfly | ||||
| 	js // TODO
 | ||||
| 	android | ||||
| 	solaris | ||||
| 	haiku | ||||
| } | ||||
| 
 | ||||
| enum Pass { | ||||
| 	// A very short pass that only looks at imports in the beginning of
 | ||||
| 	// each file
 | ||||
|  | @ -59,7 +45,7 @@ enum Pass { | |||
| 
 | ||||
| struct V { | ||||
| pub mut: | ||||
| 	os                  OS // the OS to build for
 | ||||
| 	os                  builder.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?)
 | ||||
|  | @ -393,7 +379,7 @@ pub fn (v mut V) compile2() { | |||
| 		println('all .v files:') | ||||
| 		println(v.files) | ||||
| 	} | ||||
| 	mut b := builder.new_builder() | ||||
| 	mut b := builder.new_builder(v.v2_prefs()) | ||||
| 	b.build_c(v.files, v.out_name) | ||||
| 	v.cc() | ||||
| } | ||||
|  | @ -405,10 +391,25 @@ pub fn (v mut V) compile_x64() { | |||
| 	} | ||||
| 	//v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare'))
 | ||||
| 	v.files << v.dir | ||||
| 	mut b := builder.new_builder() | ||||
| 	mut b := builder.new_builder(v.v2_prefs()) | ||||
| 	// move all this logic to v2
 | ||||
| 	b.build_x64(v.files, v.out_name) | ||||
| } | ||||
| 
 | ||||
| // make v2 prefs from v1
 | ||||
| fn (v &V) v2_prefs() builder.Preferences { | ||||
| 	return builder.Preferences{ | ||||
| 		os: v.os | ||||
| 		vpath: v.pref.vpath | ||||
| 		vlib_path: v.pref.vlib_path | ||||
| 		mod_path: v_modules_path | ||||
| 		compile_dir: v.compiled_dir | ||||
| 		user_mod_path: v.pref.user_mod_path | ||||
| 		is_test: v.pref.is_test | ||||
| 		is_verbose: v.pref.is_verbose, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (v mut V) generate_init() { | ||||
| 	$if js { | ||||
| 		return | ||||
|  | @ -1013,7 +1014,7 @@ pub fn new_v(args []string) &V { | |||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	mut _os := OS.mac | ||||
| 	mut _os := builder.OS.mac | ||||
| 	// No OS specifed? Use current system
 | ||||
| 	if target_os == '' { | ||||
| 		$if linux { | ||||
|  | @ -1224,7 +1225,7 @@ pub fn cescaped_path(s string) string { | |||
| 	return s.replace('\\', '\\\\') | ||||
| } | ||||
| 
 | ||||
| pub fn os_from_string(os string) OS { | ||||
| pub fn os_from_string(os string) builder.OS { | ||||
| 	match os { | ||||
| 		'linux' { | ||||
| 			return .linux | ||||
|  |  | |||
|  | @ -175,6 +175,7 @@ mut: | |||
| 
 | ||||
| pub struct File { | ||||
| pub: | ||||
| 	path    string | ||||
| 	mod     Module | ||||
| 	imports []Import | ||||
| 	stmts   []Stmt | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ module builder | |||
| import ( | ||||
| 	os | ||||
| 	time | ||||
| 	filepath | ||||
| 	v.ast | ||||
| 	v.table | ||||
| 	v.checker | ||||
| 	v.parser | ||||
|  | @ -14,20 +16,27 @@ pub struct Builder { | |||
| pub: | ||||
| 	table   &table.Table | ||||
| 	checker checker.Checker | ||||
| mut: | ||||
| 	prefs   Preferences | ||||
| 	parsed_files []ast.File | ||||
| } | ||||
| 
 | ||||
| pub fn new_builder() Builder { | ||||
| pub fn new_builder(prefs Preferences) Builder { | ||||
| 	table := table.new_table() | ||||
| 	return Builder{ | ||||
| 	mut b:= Builder{ | ||||
| 		prefs: prefs | ||||
| 		table: table | ||||
| 		checker: checker.new_checker(table) | ||||
| 	} | ||||
| 	b.set_module_search_paths() | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| pub fn (b mut Builder) gen_c(v_files []string) string { | ||||
| 	ast_files := parser.parse_files(v_files, b.table) | ||||
| 	b.checker.check_files(v_files, ast_files) | ||||
| 	return gen.cgen(ast_files, b.table) | ||||
| 	b.parsed_files = parser.parse_files(v_files, b.table) | ||||
| 	b.parse_imports() | ||||
| 	b.checker.check_files(b.parsed_files) | ||||
| 	return gen.cgen(b.parsed_files, b.table) | ||||
| } | ||||
| 
 | ||||
| pub fn (b mut Builder) build_c(v_files []string, out_file string) { | ||||
|  | @ -36,10 +45,124 @@ pub fn (b mut Builder) build_c(v_files []string, out_file string) { | |||
| 
 | ||||
| pub fn (b mut Builder) build_x64(v_files []string, out_file string) { | ||||
| 	ticks := time.ticks() | ||||
| 	ast_files := parser.parse_files(v_files, b.table) | ||||
| 	b.parsed_files = parser.parse_files(v_files, b.table) | ||||
| 	b.parse_imports() | ||||
| 	println('PARSE: ${time.ticks() - ticks}ms') | ||||
| 	b.checker.check_files(v_files, ast_files) | ||||
| 	b.checker.check_files(b.parsed_files) | ||||
| 	println('CHECK: ${time.ticks() - ticks}ms') | ||||
| 	x64.gen(ast_files, out_file) | ||||
| 	x64.gen(b.parsed_files, out_file) | ||||
| 	println('x64 GEN: ${time.ticks() - ticks}ms') | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn (b &Builder) parse_module_files() { | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // parse all deps from already parsed files
 | ||||
| pub fn (b mut Builder) parse_imports() { | ||||
| 	mut done_imports := []string | ||||
| 	for i in 0 .. b.parsed_files.len { | ||||
| 		ast_file := b.parsed_files[i] | ||||
| 		for _, imp in ast_file.imports { | ||||
| 			mod := imp.mod | ||||
| 			if mod in done_imports { | ||||
| 				continue | ||||
| 			} | ||||
| 			import_path := b.find_module_path(mod) or { | ||||
| 				//v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_table.get_import_tok_idx(mod))
 | ||||
| 				//break
 | ||||
| 				panic('cannot import module "$mod" (not found)') | ||||
| 			} | ||||
| 			v_files := b.v_files_from_dir(import_path) | ||||
| 			if v_files.len == 0 { | ||||
| 				//v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_table.get_import_tok_idx(mod))
 | ||||
| 				panic('cannot import module "$mod" (no .v files in "$import_path")') | ||||
| 			} | ||||
| 			// Add all imports referenced by these libs
 | ||||
| 			parsed_files := parser.parse_files(v_files, b.table) | ||||
| 			for file in parsed_files { | ||||
| 				if file.mod.name != mod { | ||||
| 					//v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1
 | ||||
| 					panic('bad module definition: ${ast_file.path} imports module "$mod" but $file.path is defined as module `$ast_file.mod.name`') | ||||
| 				} | ||||
| 			} | ||||
| 			b.parsed_files << parsed_files | ||||
| 			done_imports << mod | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (b &Builder) v_files_from_dir(dir string) []string { | ||||
| 	mut res := []string | ||||
| 	if !os.exists(dir) { | ||||
| 		if dir == 'compiler' && os.is_dir('vlib') { | ||||
| 			println('looks like you are trying to build V with an old command') | ||||
| 			println('use `v -o v v.v` instead of `v -o v compiler`') | ||||
| 		} | ||||
| 		verror("$dir doesn't exist") | ||||
| 	} | ||||
| 	else if !os.is_dir(dir) { | ||||
| 		verror("$dir isn't a directory") | ||||
| 	} | ||||
| 	mut files := os.ls(dir)or{ | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	if b.prefs.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') || file.ends_with('_windows.v')) && b.prefs.os != .windows { | ||||
| 			continue | ||||
| 		} | ||||
| 		if (file.ends_with('_lin.v') || file.ends_with('_linux.v')) && b.prefs.os != .linux { | ||||
| 			continue | ||||
| 		} | ||||
| 		if (file.ends_with('_mac.v') || file.ends_with('_darwin.v')) && b.prefs.os != .mac { | ||||
| 			continue | ||||
| 		} | ||||
| 		if file.ends_with('_nix.v') && b.prefs.os == .windows { | ||||
| 			continue | ||||
| 		} | ||||
| 		if file.ends_with('_js.v') && b.prefs.os != .js { | ||||
| 			continue | ||||
| 		} | ||||
| 		if file.ends_with('_c.v') && b.prefs.os == .js { | ||||
| 			continue | ||||
| 		} | ||||
| 		/* | ||||
| 		if v.compile_defines_all.len > 0 && file.contains('_d_') { | ||||
| 			mut allowed := false | ||||
| 			for cdefine in v.compile_defines { | ||||
| 				file_postfix := '_d_${cdefine}.v' | ||||
| 				if file.ends_with(file_postfix) { | ||||
| 					allowed = true | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 			if !allowed { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		*/ | ||||
| 		res << filepath.join(dir,file) | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| fn verror(err string) { | ||||
| 	panic('v error: $err') | ||||
| } | ||||
| 
 | ||||
| pub fn (b &Builder) log(s string) { | ||||
| 	if b.prefs.is_verbose { | ||||
| 		println(s) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,59 @@ | |||
| module builder | ||||
| 
 | ||||
| import ( | ||||
| 	os | ||||
| 	filepath | ||||
| ) | ||||
| 
 | ||||
| fn (b mut Builder) set_module_search_paths() { | ||||
| 	msearch_path := if b.prefs.vpath.len > 0 { b.prefs.vpath } else { b.prefs.mod_path } | ||||
| 	// Module search order:
 | ||||
| 	// 0) V test files are very commonly located right inside the folder of the
 | ||||
| 	// module, which they test. Adding the parent folder of the module folder
 | ||||
| 	// with the _test.v files, *guarantees* that the tested module can be found
 | ||||
| 	// without needing to set custom options/flags.
 | ||||
| 	// 1) search in the *same* directory, as the compiled final v program source
 | ||||
| 	// (i.e. the . in `v .` or file.v in `v file.v`)
 | ||||
| 	// 2) search in the modules/ in the same directory.
 | ||||
| 	// 3) search in vlib/
 | ||||
| 	// 4.1) search in -vpath (if given)
 | ||||
| 	// 4.2) search in ~/.vmodules/ (i.e. modules installed with vpm) (no -vpath)
 | ||||
| 	b.prefs.module_search_paths = [] | ||||
| 	if b.prefs.is_test { | ||||
| 		b.prefs.module_search_paths << filepath.basedir(b.prefs.compile_dir) // pdir of _test.v
 | ||||
| 	} | ||||
| 	b.prefs.module_search_paths << b.prefs.compile_dir | ||||
| 	b.prefs.module_search_paths << filepath.join(b.prefs.compile_dir,'modules') | ||||
| 	b.prefs.module_search_paths << b.prefs.vlib_path | ||||
| 	b.prefs.module_search_paths << msearch_path | ||||
| 	if b.prefs.user_mod_path.len > 0 { | ||||
| 		b.prefs.module_search_paths << b.prefs.user_mod_path | ||||
| 	} | ||||
| 	if b.prefs.is_verbose { | ||||
| 		b.log('b.prefs.module_search_paths: $b.prefs.module_search_paths') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| [inline] | ||||
| fn module_path(mod string) string { | ||||
| 	// submodule support
 | ||||
| 	return mod.replace('.', os.path_separator) | ||||
| } | ||||
| 
 | ||||
| fn (b &Builder) find_module_path(mod string) ?string { | ||||
| 	mod_path := module_path(mod) | ||||
| 	for search_path in b.prefs.module_search_paths { | ||||
| 		try_path := filepath.join(search_path,mod_path) | ||||
| 		if b.prefs.is_verbose { | ||||
| 			println('  >> trying to find $mod in $try_path ..') | ||||
| 		} | ||||
| 		if os.is_dir(try_path) { | ||||
| 			if b.prefs.is_verbose { | ||||
| 				println('  << found $try_path .') | ||||
| 			} | ||||
| 			return try_path | ||||
| 		} | ||||
| 	} | ||||
| 	return error('module "$mod" not found') | ||||
| } | ||||
|  | @ -0,0 +1,30 @@ | |||
| module builder | ||||
| 
 | ||||
| pub struct Preferences { | ||||
| pub mut: | ||||
| 	// paths
 | ||||
| 	vpath string | ||||
| 	vlib_path string | ||||
| 	compile_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
 | ||||
| 	mod_path string | ||||
| 	user_mod_path string | ||||
| 	module_search_paths []string | ||||
| 	// settings
 | ||||
| 	os OS // the OS to build for
 | ||||
| 	is_test bool | ||||
| 	is_verbose bool | ||||
| } | ||||
| 
 | ||||
| pub enum OS { | ||||
| 	mac | ||||
| 	linux | ||||
| 	windows | ||||
| 	freebsd | ||||
| 	openbsd | ||||
| 	netbsd | ||||
| 	dragonfly | ||||
| 	js // TODO
 | ||||
| 	android | ||||
| 	solaris | ||||
| 	haiku | ||||
| } | ||||
|  | @ -28,9 +28,9 @@ pub fn (c &Checker) check(ast_file ast.File) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| pub fn (c mut Checker) check_files(v_files []string, ast_files []ast.File) { | ||||
| 	for i, file in ast_files { | ||||
| 		c.file_name = v_files[i] | ||||
| pub fn (c mut Checker) check_files(ast_files []ast.File) { | ||||
| 	for file in ast_files { | ||||
| 		c.file_name = file.path | ||||
| 		c.check(file) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ fn test_c_files() { | |||
| 		ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		mut b := builder.new_builder() | ||||
| 		mut b := builder.new_builder(builder.Preferences{}) | ||||
| 		res := b.gen_c([path]) | ||||
| 		if compare_texts(res, ctext) { | ||||
| 			eprintln('${term_ok} ${i}') | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import moda | ||||
| import modb as mb | ||||
| //import moda
 | ||||
| //import modb as mb
 | ||||
| 
 | ||||
| const ( | ||||
| 	pi = 3 | ||||
|  |  | |||
|  | @ -88,6 +88,7 @@ pub fn parse_file(path string, table &table.Table) ast.File { | |||
| 	// println('nr stmts = $stmts.len')
 | ||||
| 	// println(stmts[0])
 | ||||
| 	return ast.File{ | ||||
| 		path: path | ||||
| 		mod: module_decl | ||||
| 		imports: imports | ||||
| 		stmts: stmts | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue