218 lines
6.0 KiB
C++
218 lines
6.0 KiB
C++
#include "nasal_new_import.h"
|
|
|
|
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) {
|
|
char sep=is_windows()? ';':':';
|
|
std::string PATH=getenv("PATH");
|
|
usize last=0;
|
|
usize pos=PATH.find(sep, 0);
|
|
while(pos!=std::string::npos) {
|
|
std::string dirpath=PATH.substr(last, pos-last);
|
|
if (dirpath.length()) {
|
|
envpath.push_back(dirpath);
|
|
}
|
|
last=pos+1;
|
|
pos=PATH.find(sep, last);
|
|
}
|
|
if (last!=PATH.length()) {
|
|
envpath.push_back(PATH.substr(last));
|
|
}
|
|
}
|
|
|
|
std::string linker::get_path(call_expr* node) {
|
|
if (node->get_calls()[0]->get_type()==expr_type::ast_callf) {
|
|
auto tmp = (call_function*)node->get_calls()[0];
|
|
return ((string_literal*)tmp->get_argument()[0])->get_content();
|
|
}
|
|
auto fpath = std::string(".");
|
|
for(auto i : node->get_calls()) {
|
|
fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field();
|
|
}
|
|
return fpath + ".nas";
|
|
}
|
|
|
|
std::string linker::find_file(const std::string& filename) {
|
|
// first add file name itself into the file path
|
|
std::vector<std::string> fpath = {filename};
|
|
|
|
// generate search path from environ path
|
|
for(const auto& p : envpath) {
|
|
fpath.push_back(p + (is_windows()? "\\":"/") + filename);
|
|
}
|
|
|
|
// search file
|
|
for(const auto& i : fpath) {
|
|
if (access(i.c_str(), F_OK)!=-1) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// we will find lib.nas in nasal std directory
|
|
if (filename=="lib.nas") {
|
|
return is_windows()? find_file("stl\\lib.nas"):find_file("stl/lib.nas");
|
|
}
|
|
if (!show_path) {
|
|
err.err("link", "cannot find file <" + filename + ">");
|
|
return "";
|
|
}
|
|
std::string paths = "";
|
|
for(const auto& i : fpath) {
|
|
paths += " " + i + "\n";
|
|
}
|
|
err.err("link", "cannot find file <" + filename + "> in these paths:\n" + paths);
|
|
return "";
|
|
}
|
|
|
|
bool linker::import_check(expr* node) {
|
|
/*
|
|
call
|
|
|_id:import
|
|
|_callh:stl
|
|
|_callh:file
|
|
*/
|
|
if (node->get_type()!=expr_type::ast_call) {
|
|
return false;
|
|
}
|
|
auto tmp = (call_expr*)node;
|
|
if (tmp->get_first()->get_type()!=expr_type::ast_id) {
|
|
return false;
|
|
}
|
|
if (((identifier*)tmp->get_first())->get_name()!="import") {
|
|
return false;
|
|
}
|
|
if (!tmp->get_calls().size()) {
|
|
return false;
|
|
}
|
|
// import.xxx.xxx;
|
|
if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) {
|
|
for(auto i : tmp->get_calls()) {
|
|
if (i->get_type()!=expr_type::ast_callh) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// import("xxx");
|
|
if (tmp->get_calls().size()!=1) {
|
|
return false;
|
|
}
|
|
/*
|
|
call
|
|
|_id:import
|
|
|_call_func
|
|
|_string:'filename'
|
|
*/
|
|
if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) {
|
|
return false;
|
|
}
|
|
auto func_call = (call_function*)tmp->get_calls()[0];
|
|
if (func_call->get_argument().size()!=1) {
|
|
return false;
|
|
}
|
|
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool linker::exist(const std::string& file) {
|
|
// avoid importing the same file
|
|
for(const auto& fname : files) {
|
|
if (file==fname) {
|
|
return true;
|
|
}
|
|
}
|
|
files.push_back(file);
|
|
return false;
|
|
}
|
|
|
|
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
|
|
// add children of add_root to the back of root
|
|
for(auto& i:old_tree_root->get_expressions()) {
|
|
new_tree_root->add_expression(i);
|
|
}
|
|
// clean old root
|
|
old_tree_root->get_expressions().clear();
|
|
}
|
|
|
|
code_block* linker::import_regular_file(call_expr* node) {
|
|
lexer lex(err);
|
|
parse par(err);
|
|
// get filename and set node to ast_null
|
|
auto filename = get_path(node);
|
|
// node.clear();
|
|
|
|
// avoid infinite loading loop
|
|
filename = find_file(filename);
|
|
if (!filename.length() || exist(filename)) {
|
|
return new code_block({0, 0, 0, 0, filename});
|
|
}
|
|
|
|
// start importing...
|
|
lex.scan(filename);
|
|
par.compile(lex);
|
|
auto tmp = par.swap(nullptr);
|
|
|
|
// check if tmp has 'import'
|
|
return load(tmp, files.size()-1);
|
|
}
|
|
|
|
code_block* linker::import_nasal_lib() {
|
|
lexer lex(err);
|
|
parse par(err);
|
|
auto filename = find_file("lib.nas");
|
|
if (!filename.length()) {
|
|
return new code_block({0, 0, 0, 0, filename});
|
|
}
|
|
|
|
// avoid infinite loading loop
|
|
if (exist(filename)) {
|
|
return new code_block({0, 0, 0, 0, filename});
|
|
}
|
|
|
|
// start importing...
|
|
lex.scan(filename);
|
|
par.compile(lex);
|
|
auto tmp = par.swap(nullptr);
|
|
|
|
// check if tmp has 'import'
|
|
return load(tmp, files.size()-1);
|
|
}
|
|
|
|
code_block* linker::load(code_block* root, u16 fileindex) {
|
|
auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
|
|
if (!lib_loaded) {
|
|
auto tmp = import_nasal_lib();
|
|
link(tree, tmp);
|
|
delete tmp;
|
|
lib_loaded = true;
|
|
}
|
|
for(auto i : root->get_expressions()) {
|
|
if (!import_check(i)) {
|
|
break;
|
|
}
|
|
auto tmp = import_regular_file((call_expr*)i);
|
|
link(tree, tmp);
|
|
delete tmp;
|
|
}
|
|
// add root to the back of tree
|
|
auto file_head = new file_info(
|
|
{0, 0, 0, 0, files[fileindex]}, fileindex, files[fileindex]);
|
|
tree->add_expression(file_head);
|
|
link(tree, root);
|
|
return tree;
|
|
}
|
|
|
|
const error& linker::link(
|
|
parse& parse, const std::string& self, bool spath = false) {
|
|
show_path = spath;
|
|
// initializing
|
|
files = {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(), 0);
|
|
auto old_tree_root = parse.swap(new_tree_root);
|
|
delete old_tree_root;
|
|
return err;
|
|
}
|