avoid repeatedly importing the same modules

This commit is contained in:
ValKmjolnir 2023-11-01 23:49:15 +08:00
parent ccbe341dc5
commit 49f8cefca0
6 changed files with 126 additions and 93 deletions

View File

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <cstring> #include <cstring>
#include <sstream> #include <sstream>
#include <vector>
namespace nasal { namespace nasal {

View File

@ -1166,13 +1166,21 @@ void codegen::repl_mode_info_output_gen(expr* node) {
} }
void codegen::block_gen(code_block* node) { void codegen::block_gen(code_block* node) {
bool is_use_statement = true;
for(auto tmp : node->get_expressions()) { for(auto tmp : node->get_expressions()) {
if (tmp->get_type()!=expr_type::ast_use) {
is_use_statement = false;
}
switch(tmp->get_type()) { switch(tmp->get_type()) {
case expr_type::ast_use: case expr_type::ast_use:
if (!local.empty()) { if (!local.empty()) {
die("module import is not allowed here.", die("module import is not allowed here.",
tmp->get_location() tmp->get_location()
); );
} else if (!is_use_statement) {
die("module import should be used at the top of the file.",
tmp->get_location()
);
} }
break; break;
case expr_type::ast_null: break; case expr_type::ast_null: break;

View File

@ -2,22 +2,21 @@
#include "symbol_finder.h" #include "symbol_finder.h"
#include <memory> #include <memory>
#include <unordered_set>
namespace nasal { namespace nasal {
linker::linker(): linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
show_path(false), lib_loaded(false), const auto seperator= is_windows()? ';':':';
this_file(""), lib_path("") { const auto PATH = std::string(getenv("PATH"));
char sep = is_windows()? ';':':'; usize last = 0, position = PATH.find(seperator, 0);
std::string PATH = getenv("PATH"); while(position!=std::string::npos) {
usize last = 0, pos = PATH.find(sep, 0); std::string dirpath = PATH.substr(last, position-last);
while(pos!=std::string::npos) {
std::string dirpath = PATH.substr(last, pos-last);
if (dirpath.length()) { if (dirpath.length()) {
envpath.push_back(dirpath); envpath.push_back(dirpath);
} }
last = pos+1; last = position+1;
pos = PATH.find(sep, last); position = PATH.find(seperator, last);
} }
if (last!=PATH.length()) { if (last!=PATH.length()) {
envpath.push_back(PATH.substr(last)); envpath.push_back(PATH.substr(last));
@ -26,32 +25,36 @@ linker::linker():
std::string linker::get_path(expr* node) { std::string linker::get_path(expr* node) {
if (node->get_type()==expr_type::ast_use) { if (node->get_type()==expr_type::ast_use) {
auto file_relative_path = std::string("."); auto file_relative_path = std::string("");
for(auto i : reinterpret_cast<use_stmt*>(node)->get_path()) { const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
file_relative_path += (is_windows()? "\\":"/") +i->get_name(); for(auto i : path) {
file_relative_path += i->get_name();
if (i!=path.back()) {
file_relative_path += (is_windows()? "\\":"/");
}
} }
return file_relative_path + ".nas"; return file_relative_path + ".nas";
} }
auto call_node = reinterpret_cast<call_expr*>(node); auto call_node = reinterpret_cast<call_expr*>(node);
auto tmp = reinterpret_cast<call_function*>(call_node->get_calls()[0]); auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]);
auto content = reinterpret_cast<string_literal*>(tmp->get_argument()[0]); auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]);
return content->get_content(); return content->get_content();
} }
std::string linker::find_real_file_path( std::string linker::find_real_file_path(
const std::string& filename, const span& location) { const std::string& filename, const span& location) {
// first add file name itself into the file path // first add file name itself into the file path
std::vector<std::string> fpath = {filename}; std::vector<std::string> path_list = {filename};
// generate search path from environ path // generate search path from environ path
for(const auto& p : envpath) { for(const auto& p : envpath) {
fpath.push_back(p + (is_windows()? "\\":"/") + filename); path_list.push_back(p + (is_windows()? "\\":"/") + filename);
} }
// search file // search file
for(const auto& i : fpath) { for(const auto& path : path_list) {
if (access(i.c_str(), F_OK)!=-1) { if (access(path.c_str(), F_OK)!=-1) {
return i; 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):
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", err.err("link",
"in <" + location.file + ">: " + "in <" + location.file + ">: " +
"cannot find file <" + filename + ">, " + "cannot find file <" + filename + ">, " +
@ -69,13 +72,14 @@ std::string linker::find_real_file_path(
); );
return ""; return "";
} }
auto paths = std::string(""); auto path_list_info = std::string("");
for(const auto& i : fpath) { for(const auto& path : path_list) {
paths += " -> " + i + "\n"; path_list_info += " -> " + path + "\n";
} }
err.err("link", err.err("link",
"in <" + location.file + ">: " + "in <" + location.file + ">: " +
"cannot find file <" + filename + "> in these paths:\n" + paths "cannot find file <" + filename +
"> in these paths:\n" + path_list_info
); );
return ""; return "";
} }
@ -123,20 +127,20 @@ bool linker::import_check(expr* node) {
return true; 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 // avoid importing the same file
for(const auto& fname : files) { for(const auto& name : imported_files) {
if (file==fname) { if (file==name) {
return true; return true;
} }
} }
files.push_back(file); imported_files.push_back(file);
return false; return false;
} }
bool linker::check_self_import(const std::string& file) { bool linker::check_self_import(const std::string& file) {
for(const auto& i : module_load_stack) { for(const auto& name : module_load_stack) {
if (file==i) { if (file==name) {
return true; 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(); 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<std::string>& used_modules) {
// get filename // get filename
auto filename = get_path(node); auto filename = get_path(node);
// clear import("xxx/xxx.nas") node
if (node->get_type()!=expr_type::ast_use) {
auto cast_node = reinterpret_cast<call_expr*>(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 // avoid infinite loading loop
filename = find_real_file_path(filename, node->get_location()); filename = find_real_file_path(filename, node->get_location());
if (!filename.length()) { if (!filename.length()) {
return new code_block({0, 0, 0, 0, filename}); 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)) { if (check_self_import(filename)) {
err.err("link", err.err("link",
"self-referenced module <" + filename + ">:\n" + "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}); return new code_block({0, 0, 0, 0, filename});
} }
exist(filename); check_exist_or_record_file(filename);
module_load_stack.push_back(filename); module_load_stack.push_back(filename);
// start importing... // start importing...
@ -214,36 +210,37 @@ code_block* linker::import_regular_file(expr* node) {
} }
code_block* linker::import_nasal_lib() { code_block* linker::import_nasal_lib() {
auto filename = find_real_file_path("lib.nas", {0, 0, 0, 0, files[0]}); auto path = find_real_file_path(
if (!filename.length()) { "lib.nas", {0, 0, 0, 0, this_file}
return new code_block({0, 0, 0, 0, filename}); );
if (!path.length()) {
return new code_block({0, 0, 0, 0, path});
} }
lib_path = filename;
// avoid infinite loading library // avoid infinite loading library
if (exist(filename)) { if (check_exist_or_record_file(path)) {
return new code_block({0, 0, 0, 0, filename}); return new code_block({0, 0, 0, 0, path});
} }
// start importing... // start importing...
lexer nasal_lexer; lexer nasal_lexer;
parse nasal_parser; parse nasal_parser;
if (nasal_lexer.scan(filename).geterr()) { if (nasal_lexer.scan(path).geterr()) {
err.err("link", 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()) { if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link", 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 // swap result out
auto parse_result = nasal_parser.swap(nullptr); auto parse_result = nasal_parser.swap(nullptr);
// check if library has 'import' (in fact it should not) // 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) { 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}); auto tree = new code_block({0, 0, 0, 0, filename});
// load library, this ast will be linked with root directly // load library, this ast will be linked with root directly
// so no extra namespace is generated // so no extra namespace is generated
if (!lib_loaded) { if (!library_loaded) {
auto nasal_lib_code_block = import_nasal_lib(); auto nasal_lib_code_block = import_nasal_lib();
// insert nasal lib code to the back of tree // insert nasal lib code to the back of tree
link(tree, nasal_lib_code_block); link(tree, nasal_lib_code_block);
delete nasal_lib_code_block; delete nasal_lib_code_block;
lib_loaded = true; library_loaded = true;
} }
// load imported modules // load imported modules
std::unordered_set<std::string> used_modules = {};
for(auto& import_ast_node : program_root->get_expressions()) { for(auto& import_ast_node : program_root->get_expressions()) {
if (!import_check(import_ast_node)) { if (!import_check(import_ast_node)) {
break; 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! // this location should not be a reference, may cause use after free!
const auto location = import_ast_node->get_location(); const auto location = import_ast_node->get_location();
// after importing the regular file as module, delete this node // after importing the regular file as module, delete this node
delete import_ast_node; delete import_ast_node;
// and replace the node with null_expr node // and replace the node with null_expr node
import_ast_node = new null_expr(location); 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, // then we generate a function warping the code block,
// and export the necessary global symbols in this code block // and export the necessary global symbols in this code block
// by generate a return statement, with a hashmap return value // 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( const error& linker::link(
parse& parse, const std::string& self, bool spath = false) { 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 // initializing file map
this_file = self; this_file = self;
files = {self}; imported_files = {self};
module_load_stack = {self}; module_load_stack = {self};
// scan root and import files // scan root and import files
// then generate a new ast and return to import_ast // 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 new_tree_root = load(parse.tree(), self);
auto old_tree_root = parse.swap(new_tree_root); auto old_tree_root = parse.swap(new_tree_root);
delete old_tree_root; delete old_tree_root;

View File

@ -18,30 +18,32 @@
#include "nasal_parse.h" #include "nasal_parse.h"
#include "symbol_finder.h" #include "symbol_finder.h"
#include <cstring>
#include <sstream>
#include <vector> #include <vector>
#include <unordered_set>
namespace nasal { namespace nasal {
class linker { class linker {
private: private:
bool show_path; bool show_path_flag;
bool lib_loaded; bool library_loaded;
std::string this_file; std::string this_file;
std::string lib_path;
error err; error err;
std::vector<std::string> files; std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack; std::vector<std::string> module_load_stack;
std::vector<std::string> envpath; std::vector<std::string> envpath;
private: private:
bool import_check(expr*); 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&); bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&); std::string generate_self_import_path(const std::string&);
void link(code_block*, code_block*); void link(code_block*, code_block*);
std::string get_path(expr*); std::string get_path(expr*);
std::string find_real_file_path(const std::string&, const span&); 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<std::string>&);
code_block* import_nasal_lib(); code_block* import_nasal_lib();
std::string generate_module_name(const std::string&); std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*); return_expr* generate_module_return(code_block*);
@ -51,9 +53,7 @@ private:
public: public:
linker(); linker();
const error& link(parse&, const std::string&, bool); const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return files;} const auto& get_file_list() const {return imported_files;}
const auto& get_this_file() const {return this_file;}
const auto& get_lib_path() const {return lib_path;}
}; };
} }

View File

@ -1,22 +1,23 @@
# stack.nas # stack.nas
# valkmjolnir 2021/3/31 # valkmjolnir 2021/3/31
var stack=func(){ var stack = func() {
var vec=[]; var vec = [];
return{ return {
push:func(elem){ push: func(elem) {
append(vec,elem); append(vec, elem);
}, },
pop:func(){ pop: func() {
return pop(vec); return pop(vec);
}, },
top:func(){ top: func() {
if(size(vec)!=0) if (size(vec)!=0) {
return vec[-1]; return vec[-1];
}
}, },
clear:func(){ clear: func() {
vec=[]; vec = [];
}, },
empty:func(){ empty: func() {
return size(vec)==0; return size(vec)==0;
} }
}; };

View File

@ -1,4 +1,6 @@
use std.file; use std.file;
use std.padding;
use std.process_bar;
var tips = func() { var tips = func() {
println("usage:"); println("usage:");
@ -20,7 +22,7 @@ var needle = arg[0];
var do_flat = func(vec) { var do_flat = func(vec) {
var flat = []; var flat = [];
var bfs = [vec]; var bfs = [vec];
while(size(bfs)) { while(size(bfs)!=0) {
var d = pop(bfs); var d = pop(bfs);
foreach(var f; d.files) { foreach(var f; d.files) {
if (ishash(f)) { if (ishash(f)) {
@ -30,21 +32,21 @@ var do_flat = func(vec) {
append(flat, d.dir~"/"~f); 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; return flat;
} }
var count = 0; var result = [];
foreach(var f; do_flat(file.recursive_find_files("."))) { var all_files = file.recursive_find_files(".");
foreach(var f; do_flat(all_files)) {
var pos = find(needle, f); var pos = find(needle, f);
if (pos == -1) { if (pos == -1) {
continue; continue;
} }
count += 1;
var begin = substr(f, 0, pos); var begin = substr(f, 0, pos);
var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f)); var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f));
var file_size = fstat(f).st_size; var file_size = fstat(f).st_size;
var unit = "b"; var unit = " b";
if (file_size>1024) { if (file_size>1024) {
file_size/=1024; file_size/=1024;
unit = "kb"; unit = "kb";
@ -58,7 +60,20 @@ foreach(var f; do_flat(file.recursive_find_files("."))) {
unit = "gb"; unit = "gb";
} }
file_size = int(file_size); 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)."); 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).");