2020-01-18 23:26:14 +01:00
module builder
2020-04-16 15:35:19 +02:00
import os
import v . ast
import v . table
import v . pref
import v . util
import v . vmod
import v . checker
import v . parser
import v . depgraph
2020-01-18 23:26:14 +01:00
2020-05-09 15:16:48 +02:00
const (
max_nr_errors = 100
)
2020-01-18 23:26:14 +01:00
pub struct Builder {
pub :
2020-02-07 18:46:42 +01:00
table & table . Table
checker checker . Checker
2020-03-20 16:41:18 +01:00
compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .`
2020-02-07 18:46:42 +01:00
module_path string
2020-02-03 07:31:54 +01:00
mut :
2020-04-27 22:53:26 +02:00
pref & pref . Preferences
2020-02-03 12:09:17 +01:00
parsed_files [ ] ast . File
2020-04-04 05:14:40 +02:00
global_scope & ast . Scope
2020-04-07 00:44:19 +02:00
out_name_c string
2020-04-16 15:35:19 +02:00
out_name_js string
2020-05-09 18:34:04 +02:00
pub mut :
module_search_paths [ ] string
2020-01-18 23:26:14 +01:00
}
2020-02-03 12:09:17 +01:00
pub fn new_builder ( pref & pref . Preferences ) Builder {
2020-04-07 00:44:19 +02:00
rdir := os . real_path ( pref . path )
compiled_dir := if os . is_dir ( rdir ) { rdir } else { os . dir ( rdir ) }
2020-01-18 23:26:14 +01:00
table := table . new_table ( )
2020-05-20 19:33:29 +02:00
if pref . use_color == . always {
util . emanager . set_support_color ( true )
}
if pref . use_color == . never {
util . emanager . set_support_color ( false )
}
2020-04-16 15:35:19 +02:00
return Builder {
2020-02-03 12:09:17 +01:00
pref : pref
2020-01-18 23:26:14 +01:00
table : table
2020-04-03 11:01:09 +02:00
checker : checker . new_checker ( table , pref )
2020-04-04 05:14:40 +02:00
global_scope : & ast . Scope {
parent : 0
}
2020-04-07 00:44:19 +02:00
compiled_dir : compiled_dir
2020-01-18 23:26:14 +01:00
}
}
2020-02-03 07:31:54 +01:00
// parse all deps from already parsed files
2020-04-25 17:49:16 +02:00
pub fn ( mut b Builder ) parse_imports ( ) {
2020-04-26 09:17:13 +02:00
mut done_imports := [ ] string { }
2020-04-16 11:29:36 +02:00
// NB: b.parsed_files is appended in the loop,
// so we can not use the shorter `for in` form.
2020-04-16 15:35:19 +02:00
for i := 0 ; i < b . parsed_files . len ; i ++ {
2020-02-03 07:31:54 +01:00
ast_file := b . parsed_files [ i ]
for _ , imp in ast_file . imports {
mod := imp . mod
2020-05-21 16:31:41 +02:00
if mod == ' b u i l t i n ' {
verror ( ' c a n n o t i m p o r t m o d u l e " $ mod " ' )
break
}
2020-02-03 07:31:54 +01:00
if mod in done_imports {
continue
}
2020-04-11 08:57:31 +02:00
import_path := b . find_module_path ( mod , ast_file . path ) or {
2020-02-07 18:46:42 +01:00
// v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_table.get_import_tok_idx(mod))
// break
2020-03-01 21:56:07 +01:00
// println('module_search_paths:')
// println(b.module_search_paths)
2020-05-14 09:59:29 +02:00
verror ( ' c a n n o t i m p o r t m o d u l e " $ mod " ( n o t f o u n d ) ' )
break
2020-02-03 07:31:54 +01:00
}
v_files := b . v_files_from_dir ( import_path )
if v_files . len == 0 {
2020-02-07 18:46:42 +01:00
// 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))
2020-05-14 09:59:29 +02:00
verror ( ' c a n n o t i m p o r t m o d u l e " $ mod " ( n o . v f i l e s i n " $ import_path " ) ' )
2020-02-03 07:31:54 +01:00
}
// Add all imports referenced by these libs
2020-04-04 05:14:40 +02:00
parsed_files := parser . parse_files ( v_files , b . table , b . pref , b . global_scope )
2020-02-03 07:31:54 +01:00
for file in parsed_files {
if file . mod . name != mod {
2020-02-07 18:46:42 +01:00
// 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
2020-04-13 10:06:41 +02:00
verror ( ' b a d m o d u l e d e f i n i t i o n : $ { ast_file . path } i m p o r t s m o d u l e " $ mod " b u t $ file . path i s d e f i n e d a s m o d u l e ` $ file . mod . name ` ' )
2020-02-03 07:31:54 +01:00
}
}
b . parsed_files << parsed_files
done_imports << mod
}
}
2020-04-16 11:29:36 +02:00
b . resolve_deps ( )
2020-04-30 18:15:30 +02:00
//
if b . pref . print_v_files {
for p in b . parsed_files {
println ( p . path )
}
2020-05-05 07:00:52 +02:00
exit ( 0 )
2020-04-30 18:15:30 +02:00
}
2020-04-16 11:29:36 +02:00
}
2020-04-25 17:49:16 +02:00
pub fn ( mut b Builder ) resolve_deps ( ) {
2020-04-16 11:29:36 +02:00
graph := b . import_graph ( )
deps_resolved := graph . resolve ( )
2020-05-19 13:17:03 +02:00
cycles := deps_resolved . display_cycles ( )
if cycles . len > 1 {
eprintln ( ' w a r n i n g : i m p o r t c y c l e d e t e c t e d b e t w e e n t h e f o l l o w i n g m o d u l e s : \n ' + cycles )
2020-04-16 11:29:36 +02:00
// TODO: error, when v itself does not have v.table -> v.ast -> v.table cycles anymore
return
}
if b . pref . is_verbose {
eprintln ( ' - - - - - - r e s o l v e d d e p e n d e n c i e s g r a p h : - - - - - - ' )
eprintln ( deps_resolved . display ( ) )
eprintln ( ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' )
}
2020-04-26 09:17:13 +02:00
mut mods := [ ] string { }
2020-04-16 11:29:36 +02:00
for node in deps_resolved . nodes {
mods << node . name
}
if b . pref . is_verbose {
eprintln ( ' - - - - - - i m p o r t e d m o d u l e s : - - - - - - ' )
eprintln ( mods . str ( ) )
eprintln ( ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' )
}
2020-04-26 09:17:13 +02:00
mut reordered_parsed_files := [ ] ast . File { }
2020-04-16 11:29:36 +02:00
for m in mods {
for pf in b . parsed_files {
if m == pf . mod . name {
reordered_parsed_files << pf
2020-04-16 15:35:19 +02:00
// eprintln('pf.mod.name: $pf.mod.name | pf.path: $pf.path')
2020-04-16 11:29:36 +02:00
}
}
}
b . parsed_files = reordered_parsed_files
}
// graph of all imported modules
pub fn ( b & Builder ) import_graph ( ) & depgraph . DepGraph {
2020-04-23 01:16:58 +02:00
mut builtins := util . builtin_module_parts
2020-04-16 11:29:36 +02:00
builtins << ' b u i l t i n '
2020-04-23 01:16:58 +02:00
mut graph := depgraph . new_dep_graph ( )
2020-04-16 11:29:36 +02:00
for p in b . parsed_files {
2020-04-26 09:17:13 +02:00
mut deps := [ ] string { }
2020-04-16 11:29:36 +02:00
if p . mod . name ! in builtins {
deps << ' b u i l t i n '
}
for _ , m in p . imports {
deps << m . mod
}
graph . add ( p . mod . name , deps )
}
return graph
2020-02-03 07:31:54 +01:00
}
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) v_files_from_dir ( dir string ) [ ] string {
2020-02-03 07:31:54 +01:00
if ! os . exists ( dir ) {
if dir == ' c o m p i l e r ' && os . is_dir ( ' v l i b ' ) {
println ( ' l o o k s l i k e y o u a r e t r y i n g t o b u i l d V w i t h a n o l d c o m m a n d ' )
2020-02-09 10:08:04 +01:00
println ( ' u s e ` v - o v c m d / v ` i n s t e a d o f ` v - o v c o m p i l e r ` ' )
2020-02-03 07:31:54 +01:00
}
verror ( " $ dir d o e s n ' t e x i s t " )
2020-04-07 00:44:19 +02:00
} else if ! os . is_dir ( dir ) {
2020-03-24 11:14:11 +01:00
verror ( " $ dir i s n ' t a d i r e c t o r y ! " )
2020-02-03 07:31:54 +01:00
}
2020-04-23 01:16:58 +02:00
mut files := os . ls ( dir ) or {
2020-02-03 07:31:54 +01:00
panic ( err )
}
2020-04-07 00:44:19 +02:00
if b . pref . is_verbose {
2020-02-03 07:31:54 +01:00
println ( ' v _ f i l e s _ f r o m _ d i r ( " $ dir " ) ' )
}
2020-05-05 07:00:52 +02:00
return b . pref . should_compile_filtered_files ( dir , files )
2020-04-10 18:25:23 +02:00
}
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) log ( s string ) {
if b . pref . is_verbose {
2020-02-03 07:31:54 +01:00
println ( s )
}
}
2020-03-01 21:56:07 +01:00
2020-04-07 00:44:19 +02:00
pub fn ( b Builder ) info ( s string ) {
if b . pref . is_verbose {
2020-03-31 19:58:44 +02:00
println ( s )
}
}
2020-03-01 21:56:07 +01:00
[ inline ]
fn module_path ( mod string ) string {
// submodule support
2020-03-07 22:26:26 +01:00
return mod . replace ( ' . ' , os . path_separator )
2020-03-01 21:56:07 +01:00
}
2020-04-16 15:35:19 +02:00
pub fn ( b Builder ) find_module_path ( mod , fpath string ) ? string {
2020-04-11 08:57:31 +02:00
// support @VROOT/v.mod relative paths:
2020-04-16 15:35:19 +02:00
vmod_file_location := vmod . mod_file_cacher . get ( fpath )
2020-03-01 21:56:07 +01:00
mod_path := module_path ( mod )
2020-04-26 09:17:13 +02:00
mut module_lookup_paths := [ ] string { }
2020-04-26 06:39:23 +02:00
if vmod_file_location . vmod_file . len != 0 && vmod_file_location . vmod_folder ! in b . module_search_paths {
2020-04-11 08:57:31 +02:00
module_lookup_paths << vmod_file_location . vmod_folder
}
module_lookup_paths << b . module_search_paths
for search_path in module_lookup_paths {
2020-04-07 00:44:19 +02:00
try_path := os . join_path ( search_path , mod_path )
if b . pref . is_verbose {
2020-03-01 21:56:07 +01:00
println ( ' > > t r y i n g t o f i n d $ mod i n $ try_path . . ' )
}
if os . is_dir ( try_path ) {
2020-04-07 00:44:19 +02:00
if b . pref . is_verbose {
2020-03-01 21:56:07 +01:00
println ( ' < < f o u n d $ try_path . ' )
}
return try_path
}
}
2020-04-11 08:57:31 +02:00
smodule_lookup_paths := module_lookup_paths . join ( ' , ' )
return error ( ' m o d u l e " $ mod " n o t f o u n d i n : \n $ smodule_lookup_paths ' )
2020-03-01 21:56:07 +01:00
}
2020-04-03 17:38:41 +02:00
2020-05-05 07:00:52 +02:00
fn ( b & Builder ) print_warnings_and_errors ( ) {
2020-05-10 11:26:57 +02:00
if b . pref . output_mode == . silent {
if b . checker . nr_errors > 0 {
exit ( 1 )
}
return
}
2020-05-09 15:16:48 +02:00
if b . pref . is_verbose && b . checker . nr_warnings > 1 {
println ( ' $ b . checker . nr_warnings w a r n i n g s ' )
}
2020-05-20 17:27:31 +02:00
if b . checker . nr_warnings > 0 && ! b . pref . skip_warnings {
2020-05-09 15:16:48 +02:00
for i , err in b . checker . warnings {
2020-04-29 12:04:09 +02:00
kind := if b . pref . is_verbose { ' $ err . reporter w a r n i n g # $ b . checker . nr_warnings : ' } else { ' w a r n i n g : ' }
ferror := util . formatted_error ( kind , err . message , err . file_path , err . pos )
eprintln ( ferror )
2020-05-09 15:16:48 +02:00
// eprintln('')
if i > max_nr_errors {
return
}
2020-04-29 12:04:09 +02:00
}
2020-04-13 01:56:01 +02:00
}
2020-05-09 15:16:48 +02:00
//
if b . pref . is_verbose && b . checker . nr_errors > 1 {
println ( ' $ b . checker . nr_errors e r r o r s ' )
}
2020-04-29 12:04:09 +02:00
if b . checker . nr_errors > 0 {
2020-05-09 15:16:48 +02:00
for i , err in b . checker . errors {
2020-04-29 12:04:09 +02:00
kind := if b . pref . is_verbose { ' $ err . reporter e r r o r # $ b . checker . nr_errors : ' } else { ' e r r o r : ' }
ferror := util . formatted_error ( kind , err . message , err . file_path , err . pos )
eprintln ( ferror )
2020-05-09 15:16:48 +02:00
// eprintln('')
if i > max_nr_errors {
return
}
2020-04-29 12:04:09 +02:00
}
exit ( 1 )
2020-04-29 11:38:36 +02:00
}
2020-05-20 17:27:31 +02:00
if b . table . redefined_fns . len > 0 {
for fn_name in b . table . redefined_fns {
eprintln ( ' r e d e f i n i t i o n o f f u n c t i o n ` $ fn_name ` ' )
// eprintln('previous declaration at')
// Find where this function was already declared
for file in b . parsed_files {
for stmt in file . stmts {
if stmt is ast . FnDecl {
f := stmt as ast . FnDecl
if f . name == fn_name {
println ( file . path + ' : ' + f . pos . line_nr . str ( ) )
}
}
}
}
exit ( 1 )
}
}
2020-04-29 11:38:36 +02:00
}
2020-04-03 17:38:41 +02:00
fn verror ( s string ) {
util . verror ( ' b u i l d e r e r r o r ' , s )
}