diff --git a/src/ast_dumper.h b/src/ast_dumper.h index b5014d5..e20f5c4 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace nasal { diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index a1018b4..bf4b332 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1166,13 +1166,21 @@ void codegen::repl_mode_info_output_gen(expr* node) { } void codegen::block_gen(code_block* node) { + bool is_use_statement = true; for(auto tmp : node->get_expressions()) { + if (tmp->get_type()!=expr_type::ast_use) { + is_use_statement = false; + } switch(tmp->get_type()) { case expr_type::ast_use: if (!local.empty()) { die("module import is not allowed here.", tmp->get_location() ); + } else if (!is_use_statement) { + die("module import should be used at the top of the file.", + tmp->get_location() + ); } break; case expr_type::ast_null: break; diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index f00abdb..d6cdebc 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -2,22 +2,21 @@ #include "symbol_finder.h" #include +#include namespace nasal { -linker::linker(): - show_path(false), lib_loaded(false), - this_file(""), lib_path("") { - char sep = is_windows()? ';':':'; - std::string PATH = getenv("PATH"); - usize last = 0, pos = PATH.find(sep, 0); - while(pos!=std::string::npos) { - std::string dirpath = PATH.substr(last, pos-last); +linker::linker(): show_path_flag(false), library_loaded(false), this_file("") { + const auto seperator= is_windows()? ';':':'; + const auto PATH = std::string(getenv("PATH")); + usize last = 0, position = PATH.find(seperator, 0); + while(position!=std::string::npos) { + std::string dirpath = PATH.substr(last, position-last); if (dirpath.length()) { envpath.push_back(dirpath); } - last = pos+1; - pos = PATH.find(sep, last); + last = position+1; + position = PATH.find(seperator, last); } if (last!=PATH.length()) { envpath.push_back(PATH.substr(last)); @@ -26,32 +25,36 @@ linker::linker(): std::string linker::get_path(expr* node) { if (node->get_type()==expr_type::ast_use) { - auto file_relative_path = std::string("."); - for(auto i : reinterpret_cast(node)->get_path()) { - file_relative_path += (is_windows()? "\\":"/") +i->get_name(); + auto file_relative_path = std::string(""); + const auto& path = reinterpret_cast(node)->get_path(); + for(auto i : path) { + file_relative_path += i->get_name(); + if (i!=path.back()) { + file_relative_path += (is_windows()? "\\":"/"); + } } return file_relative_path + ".nas"; } auto call_node = reinterpret_cast(node); - auto tmp = reinterpret_cast(call_node->get_calls()[0]); - auto content = reinterpret_cast(tmp->get_argument()[0]); + auto arguments = reinterpret_cast(call_node->get_calls()[0]); + auto content = reinterpret_cast(arguments->get_argument()[0]); return content->get_content(); } std::string linker::find_real_file_path( const std::string& filename, const span& location) { // first add file name itself into the file path - std::vector fpath = {filename}; + std::vector path_list = {filename}; // generate search path from environ path for(const auto& p : envpath) { - fpath.push_back(p + (is_windows()? "\\":"/") + filename); + path_list.push_back(p + (is_windows()? "\\":"/") + filename); } // search file - for(const auto& i : fpath) { - if (access(i.c_str(), F_OK)!=-1) { - return i; + for(const auto& path : path_list) { + if (access(path.c_str(), F_OK)!=-1) { + return path; } } @@ -61,7 +64,7 @@ std::string linker::find_real_file_path( find_real_file_path("std\\lib.nas", location): find_real_file_path("std/lib.nas", location); } - if (!show_path) { + if (!show_path_flag) { err.err("link", "in <" + location.file + ">: " + "cannot find file <" + filename + ">, " + @@ -69,13 +72,14 @@ std::string linker::find_real_file_path( ); return ""; } - auto paths = std::string(""); - for(const auto& i : fpath) { - paths += " -> " + i + "\n"; + auto path_list_info = std::string(""); + for(const auto& path : path_list) { + path_list_info += " -> " + path + "\n"; } err.err("link", "in <" + location.file + ">: " + - "cannot find file <" + filename + "> in these paths:\n" + paths + "cannot find file <" + filename + + "> in these paths:\n" + path_list_info ); return ""; } @@ -123,20 +127,20 @@ bool linker::import_check(expr* node) { return true; } -bool linker::exist(const std::string& file) { +bool linker::check_exist_or_record_file(const std::string& file) { // avoid importing the same file - for(const auto& fname : files) { - if (file==fname) { + for(const auto& name : imported_files) { + if (file==name) { return true; } } - files.push_back(file); + imported_files.push_back(file); return false; } bool linker::check_self_import(const std::string& file) { - for(const auto& i : module_load_stack) { - if (file==i) { + for(const auto& name : module_load_stack) { + if (file==name) { return true; } } @@ -160,29 +164,21 @@ void linker::link(code_block* new_tree_root, code_block* old_tree_root) { old_tree_root->get_expressions().clear(); } -code_block* linker::import_regular_file(expr* node) { +code_block* linker::import_regular_file( + expr* node, std::unordered_set& used_modules) { // get filename auto filename = get_path(node); - // clear import("xxx/xxx.nas") node - if (node->get_type()!=expr_type::ast_use) { - auto cast_node = reinterpret_cast(node); - for(auto i : cast_node->get_calls()) { - delete i; - } - cast_node->get_calls().clear(); - const auto& location = cast_node->get_first()->get_location(); - delete cast_node->get_first(); - cast_node->set_first(new nil_expr(location)); - // this will make node to call_expr(nil), - // will not be optimized when generating bytecodes - } - // avoid infinite loading loop filename = find_real_file_path(filename, node->get_location()); if (!filename.length()) { return new code_block({0, 0, 0, 0, filename}); } + if (used_modules.count(filename)) { + return new code_block({0, 0, 0, 0, filename}); + } + + // check self import, avoid infinite loading loop if (check_self_import(filename)) { err.err("link", "self-referenced module <" + filename + ">:\n" + @@ -190,7 +186,7 @@ code_block* linker::import_regular_file(expr* node) { ); return new code_block({0, 0, 0, 0, filename}); } - exist(filename); + check_exist_or_record_file(filename); module_load_stack.push_back(filename); // start importing... @@ -214,36 +210,37 @@ code_block* linker::import_regular_file(expr* node) { } code_block* linker::import_nasal_lib() { - auto filename = find_real_file_path("lib.nas", {0, 0, 0, 0, files[0]}); - if (!filename.length()) { - return new code_block({0, 0, 0, 0, filename}); + auto path = find_real_file_path( + "lib.nas", {0, 0, 0, 0, this_file} + ); + if (!path.length()) { + return new code_block({0, 0, 0, 0, path}); } - lib_path = filename; // avoid infinite loading library - if (exist(filename)) { - return new code_block({0, 0, 0, 0, filename}); + if (check_exist_or_record_file(path)) { + return new code_block({0, 0, 0, 0, path}); } // start importing... lexer nasal_lexer; parse nasal_parser; - if (nasal_lexer.scan(filename).geterr()) { + if (nasal_lexer.scan(path).geterr()) { err.err("link", - "error occurred when analysing library <" + filename + ">" + "error occurred when analysing library <" + path + ">" ); - return new code_block({0, 0, 0, 0, filename}); + return new code_block({0, 0, 0, 0, path}); } if (nasal_parser.compile(nasal_lexer).geterr()) { err.err("link", - "error occurred when analysing library <" + filename + ">" + "error occurred when analysing library <" + path + ">" ); - return new code_block({0, 0, 0, 0, filename}); + return new code_block({0, 0, 0, 0, path}); } // swap result out auto parse_result = nasal_parser.swap(nullptr); // check if library has 'import' (in fact it should not) - return load(parse_result, filename); + return load(parse_result, path); } std::string linker::generate_module_name(const std::string& file_path) { @@ -351,26 +348,35 @@ code_block* linker::load(code_block* program_root, const std::string& filename) auto tree = new code_block({0, 0, 0, 0, filename}); // load library, this ast will be linked with root directly // so no extra namespace is generated - if (!lib_loaded) { + if (!library_loaded) { auto nasal_lib_code_block = import_nasal_lib(); // insert nasal lib code to the back of tree link(tree, nasal_lib_code_block); delete nasal_lib_code_block; - lib_loaded = true; + library_loaded = true; } // load imported modules + std::unordered_set used_modules = {}; for(auto& import_ast_node : program_root->get_expressions()) { if (!import_check(import_ast_node)) { break; } - auto module_code_block = import_regular_file(import_ast_node); + auto module_code_block = import_regular_file(import_ast_node, used_modules); // this location should not be a reference, may cause use after free! const auto location = import_ast_node->get_location(); // after importing the regular file as module, delete this node delete import_ast_node; // and replace the node with null_expr node import_ast_node = new null_expr(location); + // avoid repeatedly importing the same module + const auto& module_path = module_code_block->get_location().file; + if (used_modules.count(module_path)) { + delete module_code_block; + continue; + } else { + used_modules.insert(module_path); + } // then we generate a function warping the code block, // and export the necessary global symbols in this code block // by generate a return statement, with a hashmap return value @@ -384,14 +390,16 @@ code_block* linker::load(code_block* program_root, const std::string& filename) const error& linker::link( parse& parse, const std::string& self, bool spath = false) { - show_path = spath; + // switch for showing path when errors occur + show_path_flag = spath; + // initializing file map this_file = self; - files = {self}; + imported_files = {self}; module_load_stack = {self}; + // scan root and import files // then generate a new ast and return to import_ast - // the main file's index is 0 auto new_tree_root = load(parse.tree(), self); auto old_tree_root = parse.swap(new_tree_root); delete old_tree_root; diff --git a/src/nasal_import.h b/src/nasal_import.h index c09eabe..7fa0df3 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -18,30 +18,32 @@ #include "nasal_parse.h" #include "symbol_finder.h" +#include +#include #include +#include namespace nasal { class linker { private: - bool show_path; - bool lib_loaded; + bool show_path_flag; + bool library_loaded; std::string this_file; - std::string lib_path; error err; - std::vector files; + std::vector imported_files; std::vector module_load_stack; std::vector envpath; private: bool import_check(expr*); - bool exist(const std::string&); + bool check_exist_or_record_file(const std::string&); bool check_self_import(const std::string&); std::string generate_self_import_path(const std::string&); void link(code_block*, code_block*); std::string get_path(expr*); std::string find_real_file_path(const std::string&, const span&); - code_block* import_regular_file(expr*); + code_block* import_regular_file(expr*, std::unordered_set&); code_block* import_nasal_lib(); std::string generate_module_name(const std::string&); return_expr* generate_module_return(code_block*); @@ -51,9 +53,7 @@ private: public: linker(); const error& link(parse&, const std::string&, bool); - const auto& get_file_list() const {return files;} - const auto& get_this_file() const {return this_file;} - const auto& get_lib_path() const {return lib_path;} + const auto& get_file_list() const {return imported_files;} }; } diff --git a/std/stack.nas b/std/stack.nas index 3e6a14b..596a603 100644 --- a/std/stack.nas +++ b/std/stack.nas @@ -1,22 +1,23 @@ # stack.nas # valkmjolnir 2021/3/31 -var stack=func(){ - var vec=[]; - return{ - push:func(elem){ - append(vec,elem); +var stack = func() { + var vec = []; + return { + push: func(elem) { + append(vec, elem); }, - pop:func(){ + pop: func() { return pop(vec); }, - top:func(){ - if(size(vec)!=0) + top: func() { + if (size(vec)!=0) { return vec[-1]; + } }, - clear:func(){ - vec=[]; + clear: func() { + vec = []; }, - empty:func(){ + empty: func() { return size(vec)==0; } }; diff --git a/tools/search_file.nas b/tools/search_file.nas index 72e52d2..d35fb02 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -1,4 +1,6 @@ use std.file; +use std.padding; +use std.process_bar; var tips = func() { println("usage:"); @@ -20,7 +22,7 @@ var needle = arg[0]; var do_flat = func(vec) { var flat = []; var bfs = [vec]; - while(size(bfs)) { + while(size(bfs)!=0) { var d = pop(bfs); foreach(var f; d.files) { if (ishash(f)) { @@ -30,21 +32,21 @@ var do_flat = func(vec) { append(flat, d.dir~"/"~f); } } - sort(flat, func(a, b){return cmp(a, b)<0}); + sort(flat, func(a, b) {return cmp(a, b)<0}); return flat; } -var count = 0; -foreach(var f; do_flat(file.recursive_find_files("."))) { +var result = []; +var all_files = file.recursive_find_files("."); +foreach(var f; do_flat(all_files)) { var pos = find(needle, f); if (pos == -1) { continue; } - count += 1; var begin = substr(f, 0, pos); var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f)); var file_size = fstat(f).st_size; - var unit = "b"; + var unit = " b"; if (file_size>1024) { file_size/=1024; unit = "kb"; @@ -58,7 +60,20 @@ foreach(var f; do_flat(file.recursive_find_files("."))) { unit = "gb"; } file_size = int(file_size); - println(begin, "\e[95;1m", needle, "\e[0m", end, " | ", file_size, " ", unit); + append(result, { + info: begin~"\e[95;1m"~needle~"\e[0m"~end, + size: file_size, + unit: unit + }); } -println("\n", count, " result(s)."); \ No newline at end of file +var max_len = 0; +foreach(var elem; result) { + var temp = size(str(elem.size)~" "~elem.unit); + max_len = math.max(max_len, temp); +} +foreach(var elem; result) { + var temp = padding.leftpad(str(elem.size)~" "~elem.unit, max_len); + println(temp, " | ", elem.info); +} +println("\n", size(result), " result(s)."); \ No newline at end of file