Merge pull request #38 from ValKmjolnir/develop
✨ optimize import module & add limited execution mode (disable unsafe system api)
This commit is contained in:
commit
2e321fc4d6
26
src/main.cpp
26
src/main.cpp
|
@ -30,6 +30,7 @@ const u32 VM_SYMINFO = 1<<7;
|
||||||
const u32 VM_PROFILE = 1<<8;
|
const u32 VM_PROFILE = 1<<8;
|
||||||
const u32 VM_PROF_ALL = 1<<9;
|
const u32 VM_PROF_ALL = 1<<9;
|
||||||
const u32 VM_REF_FILE = 1<<10;
|
const u32 VM_REF_FILE = 1<<10;
|
||||||
|
const u32 VM_LIMIT = 1<<11;
|
||||||
|
|
||||||
std::ostream& help(std::ostream& out) {
|
std::ostream& help(std::ostream& out) {
|
||||||
out
|
out
|
||||||
|
@ -59,6 +60,7 @@ std::ostream& help(std::ostream& out) {
|
||||||
<< " --prof | show profiling result, available in debug mode.\n"
|
<< " --prof | show profiling result, available in debug mode.\n"
|
||||||
<< " --prof-all | show profiling result of all files,"
|
<< " --prof-all | show profiling result of all files,"
|
||||||
<< "available under debug mode.\n"
|
<< "available under debug mode.\n"
|
||||||
|
<< " --limit | use limited execution mode."
|
||||||
<< "file:\n"
|
<< "file:\n"
|
||||||
<< " <filename> | execute file.\n"
|
<< " <filename> | execute file.\n"
|
||||||
<< "argv:\n"
|
<< "argv:\n"
|
||||||
|
@ -75,13 +77,18 @@ std::ostream& logo(std::ostream& out) {
|
||||||
<< " / \\/ / _` / __|/ _` | |\n"
|
<< " / \\/ / _` / __|/ _` | |\n"
|
||||||
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||||
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\n"
|
<< "\n"
|
||||||
|
<< "ver : " << __nasver__
|
||||||
|
<< " " << nasal::get_platform() << " " << nasal::get_arch()
|
||||||
|
<< " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||||
<< "std : c++ " << __cplusplus << "\n"
|
<< "std : c++ " << __cplusplus << "\n"
|
||||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||||
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
|
<< "presented by fgprc members - http://fgprc.org.cn\n"
|
||||||
|
<< "\n"
|
||||||
<< "input <nasal -h> to get help .\n\n";
|
<< "input <nasal -h> to get help .\n\n";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -95,7 +102,8 @@ std::ostream& version(std::ostream& out) {
|
||||||
if (num<0.01) {
|
if (num<0.01) {
|
||||||
nasal::parse::easter_egg();
|
nasal::parse::easter_egg();
|
||||||
}
|
}
|
||||||
out << "nasal interpreter version " << __nasver;
|
out << "nasal interpreter version " << __nasver__;
|
||||||
|
out << " " << nasal::get_platform() << " " << nasal::get_arch();
|
||||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -127,12 +135,11 @@ void execute(
|
||||||
// parser gets lexer's token list to compile
|
// parser gets lexer's token list to compile
|
||||||
parse.compile(lex).chkerr();
|
parse.compile(lex).chkerr();
|
||||||
if (cmd&VM_RAW_AST) {
|
if (cmd&VM_RAW_AST) {
|
||||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
nasal::ast_dumper().dump(parse.tree());
|
||||||
dumper->dump(parse.tree());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// linker gets parser's ast and load import files to this ast
|
// linker gets parser's ast and load import files to this ast
|
||||||
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
ld.link(parse, cmd&VM_DETAIL).chkerr();
|
||||||
if (cmd&VM_REF_FILE) {
|
if (cmd&VM_REF_FILE) {
|
||||||
if (ld.get_file_list().size()) {
|
if (ld.get_file_list().size()) {
|
||||||
std::cout << "referenced file(s):\n";
|
std::cout << "referenced file(s):\n";
|
||||||
|
@ -146,12 +153,11 @@ void execute(
|
||||||
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
|
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
|
||||||
opt->do_optimization(parse.tree());
|
opt->do_optimization(parse.tree());
|
||||||
if (cmd&VM_AST) {
|
if (cmd&VM_AST) {
|
||||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
nasal::ast_dumper().dump(parse.tree());
|
||||||
dumper->dump(parse.tree());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// code generator gets parser's ast and import file list to generate code
|
// code generator gets parser's ast and import file list to generate code
|
||||||
gen.compile(parse, ld, false).chkerr();
|
gen.compile(parse, ld, false, cmd&VM_LIMIT).chkerr();
|
||||||
if (cmd&VM_CODE) {
|
if (cmd&VM_CODE) {
|
||||||
gen.print(std::cout);
|
gen.print(std::cout);
|
||||||
}
|
}
|
||||||
|
@ -167,6 +173,7 @@ void execute(
|
||||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||||
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
|
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
|
||||||
runtime->set_detail_report_info(cmd&VM_DETAIL);
|
runtime->set_detail_report_info(cmd&VM_DETAIL);
|
||||||
|
runtime->set_limit_mode_flag(cmd&VM_LIMIT);
|
||||||
runtime->run(gen, ld, argv);
|
runtime->run(gen, ld, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +230,8 @@ i32 main(i32 argc, const char* argv[]) {
|
||||||
{"--prof", VM_PROFILE},
|
{"--prof", VM_PROFILE},
|
||||||
{"--prof-all", VM_PROF_ALL},
|
{"--prof-all", VM_PROF_ALL},
|
||||||
{"-f", VM_REF_FILE},
|
{"-f", VM_REF_FILE},
|
||||||
{"--ref-file", VM_REF_FILE}
|
{"--ref-file", VM_REF_FILE},
|
||||||
|
{"--limit", VM_LIMIT|VM_EXEC}
|
||||||
};
|
};
|
||||||
u32 cmd = 0;
|
u32 cmd = 0;
|
||||||
std::string filename = "";
|
std::string filename = "";
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __nasver
|
#ifndef __nasver__
|
||||||
#define __nasver "11.1"
|
#define __nasver__ "11.1"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
@ -34,7 +34,8 @@ bool is_aarch64();
|
||||||
bool is_ia64();
|
bool is_ia64();
|
||||||
bool is_powerpc();
|
bool is_powerpc();
|
||||||
bool is_superh();
|
bool is_superh();
|
||||||
|
const char* get_platform();
|
||||||
|
const char* get_arch();
|
||||||
|
|
||||||
// virtual machine stack depth, both global depth and value stack depth
|
// virtual machine stack depth, both global depth and value stack depth
|
||||||
const u32 STACK_DEPTH = 4096;
|
const u32 STACK_DEPTH = 4096;
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
|
var builtin_unsafe(context* ctx, gc* ngc) {
|
||||||
|
return nas_err(
|
||||||
|
"unsafe_redirect",
|
||||||
|
"you are using unsafe system api under limited mode!"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var builtin_print(context* ctx, gc* ngc) {
|
var builtin_print(context* ctx, gc* ngc) {
|
||||||
for(auto& i : ctx->localr[1].vec().elems) {
|
for(auto& i : ctx->localr[1].vec().elems) {
|
||||||
std::cout << i;
|
std::cout << i;
|
||||||
|
@ -447,35 +454,11 @@ var builtin_sleep(context* ctx, gc* ngc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_platform(context* ctx, gc* ngc) {
|
var builtin_platform(context* ctx, gc* ngc) {
|
||||||
if (is_windows()) {
|
return ngc->newstr(get_platform());
|
||||||
return ngc->newstr("windows");
|
|
||||||
} else if (is_linux()) {
|
|
||||||
return ngc->newstr("linux");
|
|
||||||
} else if (is_macos()) {
|
|
||||||
return ngc->newstr("macOS");
|
|
||||||
}
|
|
||||||
return ngc->newstr("unknown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_arch(context* ctx, gc* ngc) {
|
var builtin_arch(context* ctx, gc* ngc) {
|
||||||
if (is_x86()) {
|
return ngc->newstr(get_arch());
|
||||||
return ngc->newstr("x86");
|
|
||||||
} else if (is_x86_64()) {
|
|
||||||
return ngc->newstr("x86-64");
|
|
||||||
} else if (is_amd64()) {
|
|
||||||
return ngc->newstr("amd64");
|
|
||||||
} else if (is_arm()) {
|
|
||||||
return ngc->newstr("arm");
|
|
||||||
} else if (is_aarch64()) {
|
|
||||||
return ngc->newstr("aarch64");
|
|
||||||
} else if (is_ia64()) {
|
|
||||||
return ngc->newstr("ia64");
|
|
||||||
} else if (is_powerpc()) {
|
|
||||||
return ngc->newstr("powerpc");
|
|
||||||
} else if (is_superh()) {
|
|
||||||
return ngc->newstr("superh");
|
|
||||||
}
|
|
||||||
return ngc->newstr("unknown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// md5 related functions
|
// md5 related functions
|
||||||
|
@ -683,7 +666,7 @@ var builtin_logtime(context* ctx, gc* ngc) {
|
||||||
tm* tm_t = localtime(&t);
|
tm* tm_t = localtime(&t);
|
||||||
char s[64];
|
char s[64];
|
||||||
snprintf(
|
snprintf(
|
||||||
s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d",
|
s, 64, "%d-%.2d-%.2d %.2d:%.2d:%.2d",
|
||||||
tm_t->tm_year+1900,
|
tm_t->tm_year+1900,
|
||||||
tm_t->tm_mon+1,
|
tm_t->tm_mon+1,
|
||||||
tm_t->tm_mday,
|
tm_t->tm_mday,
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
|
var builtin_unsafe(context*, gc*);
|
||||||
var builtin_print(context*, gc*);
|
var builtin_print(context*, gc*);
|
||||||
var builtin_println(context*, gc*);
|
var builtin_println(context*, gc*);
|
||||||
var builtin_exit(context*, gc*);
|
var builtin_exit(context*, gc*);
|
||||||
|
|
|
@ -11,12 +11,18 @@ void codegen::init_file_map(const std::vector<std::string>& file_list) {
|
||||||
|
|
||||||
void codegen::load_native_function_table(nasal_builtin_table* table) {
|
void codegen::load_native_function_table(nasal_builtin_table* table) {
|
||||||
for(usize i = 0; table[i].func; ++i) {
|
for(usize i = 0; table[i].func; ++i) {
|
||||||
|
// check confliction
|
||||||
if (native_function_mapper.count(table[i].name)) {
|
if (native_function_mapper.count(table[i].name)) {
|
||||||
err.err("code", "\"" + std::string(table[i].name) + "\" conflicts.");
|
err.err("code", "\"" + std::string(table[i].name) + "\" conflicts.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
native_function.push_back(table[i]);
|
if (flag_limited_mode && unsafe_system_api.count(table[i].name)) {
|
||||||
|
native_function.push_back({"__unsafe_redirect", builtin_unsafe});
|
||||||
|
} else {
|
||||||
|
native_function.push_back(table[i]);
|
||||||
|
}
|
||||||
auto index = native_function_mapper.size();
|
auto index = native_function_mapper.size();
|
||||||
|
// insert into mapper
|
||||||
native_function_mapper[table[i].name] = index;
|
native_function_mapper[table[i].name] = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1302,10 +1308,14 @@ void codegen::ret_gen(return_expr* node) {
|
||||||
emit(op_ret, 0, node->get_location());
|
emit(op_ret, 0, node->get_location());
|
||||||
}
|
}
|
||||||
|
|
||||||
const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
|
const error& codegen::compile(parse& parse,
|
||||||
|
linker& import,
|
||||||
|
bool repl_flag,
|
||||||
|
bool limit_mode) {
|
||||||
|
need_repl_output = repl_flag;
|
||||||
|
flag_limited_mode = limit_mode;
|
||||||
init_native_function();
|
init_native_function();
|
||||||
init_file_map(import.get_file_list());
|
init_file_map(import.get_file_list());
|
||||||
need_repl_output = repl_flag;
|
|
||||||
|
|
||||||
in_foreach_loop_level.push_back(0);
|
in_foreach_loop_level.push_back(0);
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,24 @@ private:
|
||||||
|
|
||||||
// repl output flag, will generate op_repl to output stack top value if true
|
// repl output flag, will generate op_repl to output stack top value if true
|
||||||
bool need_repl_output;
|
bool need_repl_output;
|
||||||
|
// limit mode flag
|
||||||
|
bool flag_limited_mode;
|
||||||
|
const std::unordered_set<std::string> unsafe_system_api = {
|
||||||
|
// builtin
|
||||||
|
"__system", "__input",
|
||||||
|
// io
|
||||||
|
"__fout", "__open", "__write", "__stat"
|
||||||
|
// bits
|
||||||
|
"__fld", "__sfld", "__setfld",
|
||||||
|
"__buf",
|
||||||
|
// fg
|
||||||
|
"__logprint",
|
||||||
|
// dylib
|
||||||
|
"__dlopen", "__dlclose", "__dlcallv", "__dlcall",
|
||||||
|
// unix
|
||||||
|
"__pipe", "__fork", "__waitpid", "__chdir",
|
||||||
|
"__environ", "__getcwd", "__getenv"
|
||||||
|
};
|
||||||
|
|
||||||
// file mapper for file -> index
|
// file mapper for file -> index
|
||||||
std::unordered_map<std::string, usize> file_map;
|
std::unordered_map<std::string, usize> file_map;
|
||||||
|
@ -141,7 +159,7 @@ public:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
codegen() = default;
|
codegen() = default;
|
||||||
const error& compile(parse&, linker&, bool);
|
const error& compile(parse&, linker&, bool, bool);
|
||||||
void print(std::ostream&);
|
void print(std::ostream&);
|
||||||
void symbol_dump(std::ostream&) const;
|
void symbol_dump(std::ostream&) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
||||||
std::vector<std::string> res;
|
std::vector<std::string> res;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
flstream():file("") {}
|
flstream(): file("") {}
|
||||||
void load(const std::string&);
|
void load(const std::string&);
|
||||||
const std::string& operator[](usize n) const {return res[n];}
|
const std::string& operator[](usize n) const {return res[n];}
|
||||||
const auto& name() const {return file;}
|
const auto& name() const {return file;}
|
||||||
|
@ -39,23 +39,23 @@ public:
|
||||||
usize size() const {return res.size();}
|
usize size() const {return res.size();}
|
||||||
};
|
};
|
||||||
|
|
||||||
class error:public flstream {
|
class error: public flstream {
|
||||||
private:
|
private:
|
||||||
u32 cnt; // counter for errors
|
u32 cnt; // counter for errors
|
||||||
|
|
||||||
std::string identation(usize len) {
|
std::string identation(usize len) {
|
||||||
return std::string(len,' ');
|
return std::string(len, ' ');
|
||||||
}
|
}
|
||||||
std::string leftpad(u32 num, usize len) {
|
std::string leftpad(u32 num, usize len) {
|
||||||
auto tmp = std::to_string(num);
|
auto tmp = std::to_string(num);
|
||||||
while(tmp.length()<len) {
|
while(tmp.length()<len) {
|
||||||
tmp=" "+tmp;
|
tmp = " "+tmp;
|
||||||
}
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
error():cnt(0) {}
|
error(): cnt(0) {}
|
||||||
void err(const std::string&, const std::string&);
|
void err(const std::string&, const std::string&);
|
||||||
void warn(const std::string&, const std::string&);
|
void warn(const std::string&, const std::string&);
|
||||||
void err(const std::string&, const span&, const std::string&);
|
void err(const std::string&, const span&, const std::string&);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
|
linker::linker(): show_path_flag(false), this_file("") {
|
||||||
const auto seperator = is_windows()? ';':':';
|
const auto seperator = is_windows()? ';':':';
|
||||||
const auto PATH = std::string(getenv("PATH"));
|
const auto PATH = std::string(getenv("PATH"));
|
||||||
usize last = 0, position = PATH.find(seperator, 0);
|
usize last = 0, position = PATH.find(seperator, 0);
|
||||||
|
@ -155,7 +155,7 @@ std::string linker::generate_self_import_path(const std::string& filename) {
|
||||||
return res + "[" + filename + "]";
|
return res + "[" + filename + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
|
void linker::merge_tree(code_block* new_tree_root, code_block* old_tree_root) {
|
||||||
// add children of add_root to the back of root
|
// add children of add_root to the back of root
|
||||||
for(auto& i : old_tree_root->get_expressions()) {
|
for(auto& i : old_tree_root->get_expressions()) {
|
||||||
new_tree_root->add_expression(i);
|
new_tree_root->add_expression(i);
|
||||||
|
@ -179,32 +179,48 @@ code_block* linker::import_regular_file(
|
||||||
// check self import, avoid infinite loading loop
|
// 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" +
|
node->get_location(),
|
||||||
" reference path: " + generate_self_import_path(filename)
|
"self-referenced module <" + filename + ">, " +
|
||||||
|
"reference path: " + generate_self_import_path(filename)
|
||||||
);
|
);
|
||||||
return new code_block({0, 0, 0, 0, filename});
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
check_exist_or_record_file(filename);
|
check_exist_or_record_file(filename);
|
||||||
|
|
||||||
module_load_stack.push_back(filename);
|
module_load_stack.push_back(filename);
|
||||||
|
// avoid stack overflow
|
||||||
|
if (module_load_stack.size()>MAX_RECURSION_DEPTH) {
|
||||||
|
err.err("link",
|
||||||
|
node->get_location(),
|
||||||
|
"too deep module import stack (>" +
|
||||||
|
std::to_string(MAX_RECURSION_DEPTH) + ")."
|
||||||
|
);
|
||||||
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
|
}
|
||||||
// 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(filename).geterr()) {
|
||||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
err.err("link",
|
||||||
|
node->get_location(),
|
||||||
|
"error occurred when analysing <" + filename + ">"
|
||||||
|
);
|
||||||
return new code_block({0, 0, 0, 0, filename});
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
if (nasal_parser.compile(nasal_lexer).geterr()) {
|
if (nasal_parser.compile(nasal_lexer).geterr()) {
|
||||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
err.err("link",
|
||||||
|
node->get_location(),
|
||||||
|
"error occurred when analysing <" + filename + ">"
|
||||||
|
);
|
||||||
return new code_block({0, 0, 0, 0, filename});
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
// swap result out
|
// swap result out
|
||||||
auto parse_result = nasal_parser.swap(nullptr);
|
auto parse_result = nasal_parser.swap(nullptr);
|
||||||
|
|
||||||
// check if parse result has 'import'
|
// check if parse result has 'import'
|
||||||
auto result = load(parse_result, filename);
|
load(parse_result, filename);
|
||||||
module_load_stack.pop_back();
|
module_load_stack.pop_back();
|
||||||
return result;
|
return parse_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
code_block* linker::import_nasal_lib() {
|
code_block* linker::import_nasal_lib() {
|
||||||
|
@ -238,10 +254,12 @@ code_block* linker::import_nasal_lib() {
|
||||||
// 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, path);
|
load(parse_result, path);
|
||||||
|
return parse_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string linker::generate_module_name(const std::string& file_path) {
|
std::string linker::generate_module_name(const std::string& file_path) {
|
||||||
|
// import("...") may trigger this error module name
|
||||||
auto error_name = "module@[" + file_path + "]";
|
auto error_name = "module@[" + file_path + "]";
|
||||||
if (!file_path.length()) {
|
if (!file_path.length()) {
|
||||||
return error_name;
|
return error_name;
|
||||||
|
@ -316,13 +334,19 @@ return_expr* linker::generate_module_return(code_block* block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
definition_expr* linker::generate_module_definition(code_block* block) {
|
definition_expr* linker::generate_module_definition(code_block* block) {
|
||||||
|
// generate ast node like this:
|
||||||
|
// var {module_name} = (func() {
|
||||||
|
// ... # module itself
|
||||||
|
// })();
|
||||||
auto def = new definition_expr(block->get_location());
|
auto def = new definition_expr(block->get_location());
|
||||||
def->set_identifier(new identifier(
|
def->set_identifier(new identifier(
|
||||||
block->get_location(),
|
block->get_location(),
|
||||||
generate_module_name(block->get_location().file)
|
generate_module_name(block->get_location().file)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// (func() {...})();
|
||||||
auto call = new call_expr(block->get_location());
|
auto call = new call_expr(block->get_location());
|
||||||
|
// func() {...}
|
||||||
auto func = new function(block->get_location());
|
auto func = new function(block->get_location());
|
||||||
func->set_code_block(block);
|
func->set_code_block(block);
|
||||||
func->get_code_block()->add_expression(generate_module_return(block));
|
func->get_code_block()->add_expression(generate_module_return(block));
|
||||||
|
@ -333,18 +357,7 @@ definition_expr* linker::generate_module_definition(code_block* block) {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
code_block* linker::load(code_block* program_root, const std::string& filename) {
|
void 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 (!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;
|
|
||||||
library_loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// load imported modules
|
// load imported modules
|
||||||
std::unordered_set<std::string> used_modules = {};
|
std::unordered_set<std::string> used_modules = {};
|
||||||
for(auto& import_node : program_root->get_expressions()) {
|
for(auto& import_node : program_root->get_expressions()) {
|
||||||
|
@ -353,46 +366,46 @@ code_block* linker::load(code_block* program_root, const std::string& filename)
|
||||||
}
|
}
|
||||||
// parse file and get ast
|
// parse file and get ast
|
||||||
auto module_code_block = import_regular_file(import_node, used_modules);
|
auto module_code_block = import_regular_file(import_node, used_modules);
|
||||||
auto replace_node = new null_expr(import_node->get_location());
|
|
||||||
// after importing the regular file as module, delete this node
|
|
||||||
delete import_node;
|
|
||||||
// and replace the node with null_expr node
|
|
||||||
import_node = replace_node;
|
|
||||||
|
|
||||||
// avoid repeatedly importing the same module
|
// avoid repeatedly importing the same module in one file
|
||||||
const auto& module_path = module_code_block->get_location().file;
|
const auto& module_path = module_code_block->get_location().file;
|
||||||
if (used_modules.count(module_path)) {
|
if (used_modules.count(module_path)) {
|
||||||
delete module_code_block;
|
delete module_code_block;
|
||||||
|
auto replace_node = new null_expr(import_node->get_location());
|
||||||
|
// after importing the regular file as module, delete this node
|
||||||
|
delete import_node;
|
||||||
|
// and replace the node with null_expr node
|
||||||
|
import_node = replace_node;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
used_modules.insert(module_path);
|
||||||
|
delete import_node;
|
||||||
// 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
|
||||||
used_modules.insert(module_path);
|
import_node = generate_module_definition(module_code_block);
|
||||||
tree->add_expression(generate_module_definition(module_code_block));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert program root to the back of tree
|
|
||||||
link(tree, program_root);
|
|
||||||
return tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const error& linker::link(
|
const error& linker::link(parse& parse, bool spath = false) {
|
||||||
parse& parse, const std::string& self, bool spath = false) {
|
|
||||||
// switch for showing path when errors occur
|
// switch for showing path when errors occur
|
||||||
show_path_flag = spath;
|
show_path_flag = spath;
|
||||||
|
|
||||||
// initializing file map
|
// initializing file map
|
||||||
this_file = self;
|
this_file = parse.tree()->get_location().file;
|
||||||
imported_files = {self};
|
imported_files = {this_file};
|
||||||
module_load_stack = {self};
|
module_load_stack = {this_file};
|
||||||
|
|
||||||
// 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
|
||||||
auto new_tree_root = load(parse.tree(), self);
|
// dfs load file
|
||||||
auto old_tree_root = parse.swap(new_tree_root);
|
auto library = import_nasal_lib();
|
||||||
delete old_tree_root;
|
// load used modules of this file
|
||||||
|
load(parse.tree(), this_file);
|
||||||
|
// then insert the whole tree into library tree root
|
||||||
|
merge_tree(library, parse.tree());
|
||||||
|
// swap tree root, and delete old root
|
||||||
|
delete parse.swap(library);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@ namespace nasal {
|
||||||
|
|
||||||
class linker {
|
class linker {
|
||||||
private:
|
private:
|
||||||
|
const u32 MAX_RECURSION_DEPTH = 256;
|
||||||
bool show_path_flag;
|
bool show_path_flag;
|
||||||
bool library_loaded;
|
|
||||||
std::string this_file;
|
std::string this_file;
|
||||||
error err;
|
error err;
|
||||||
std::vector<std::string> imported_files;
|
std::vector<std::string> imported_files;
|
||||||
|
@ -36,7 +36,7 @@ private:
|
||||||
bool check_exist_or_record_file(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 merge_tree(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*, std::unordered_set<std::string>&);
|
code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
|
||||||
|
@ -44,11 +44,11 @@ private:
|
||||||
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*);
|
||||||
definition_expr* generate_module_definition(code_block*);
|
definition_expr* generate_module_definition(code_block*);
|
||||||
code_block* load(code_block*, const std::string&);
|
void load(code_block*, const std::string&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
linker();
|
linker();
|
||||||
const error& link(parse&, const std::string&, bool);
|
const error& link(parse&, bool);
|
||||||
const auto& get_file_list() const {return imported_files;}
|
const auto& get_file_list() const {return imported_files;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,38 @@ bool is_superh() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* get_platform() {
|
||||||
|
if (is_windows()) {
|
||||||
|
return "windows";
|
||||||
|
} else if (is_linux()) {
|
||||||
|
return "linux";
|
||||||
|
} else if (is_macos()) {
|
||||||
|
return "macOS";
|
||||||
|
}
|
||||||
|
return "unknown platform";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* get_arch() {
|
||||||
|
if (is_x86()) {
|
||||||
|
return "x86";
|
||||||
|
} else if (is_x86_64()) {
|
||||||
|
return "x86-64";
|
||||||
|
} else if (is_amd64()) {
|
||||||
|
return "amd64";
|
||||||
|
} else if (is_arm()) {
|
||||||
|
return "arm";
|
||||||
|
} else if (is_aarch64()) {
|
||||||
|
return "aarch64";
|
||||||
|
} else if (is_ia64()) {
|
||||||
|
return "ia64";
|
||||||
|
} else if (is_powerpc()) {
|
||||||
|
return "powerpc";
|
||||||
|
} else if (is_superh()) {
|
||||||
|
return "superh";
|
||||||
|
}
|
||||||
|
return "unknown arch";
|
||||||
|
}
|
||||||
|
|
||||||
f64 hex_to_f64(const char* str) {
|
f64 hex_to_f64(const char* str) {
|
||||||
f64 ret = 0;
|
f64 ret = 0;
|
||||||
for(; *str; ++str) {
|
for(; *str; ++str) {
|
||||||
|
|
|
@ -45,6 +45,9 @@ protected:
|
||||||
bool first_exec_flag = true;
|
bool first_exec_flag = true;
|
||||||
bool allow_repl_output = false;
|
bool allow_repl_output = false;
|
||||||
|
|
||||||
|
/* limited mode, will not load unsafe system api if switch on */
|
||||||
|
bool flag_limited_mode = false;
|
||||||
|
|
||||||
/* vm initializing function */
|
/* vm initializing function */
|
||||||
void init(
|
void init(
|
||||||
const std::vector<std::string>&,
|
const std::vector<std::string>&,
|
||||||
|
@ -192,6 +195,8 @@ public:
|
||||||
void set_repl_mode_flag(bool flag) {is_repl_mode = flag;}
|
void set_repl_mode_flag(bool flag) {is_repl_mode = flag;}
|
||||||
/* set repl output flag */
|
/* set repl output flag */
|
||||||
void set_allow_repl_output_flag(bool flag) {allow_repl_output = flag;}
|
void set_allow_repl_output_flag(bool flag) {allow_repl_output = flag;}
|
||||||
|
/* set limit mode flag */
|
||||||
|
void set_limit_mode_flag(bool flag) {flag_limited_mode = flag;}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool vm::cond(var& val) {
|
inline bool vm::cond(var& val) {
|
||||||
|
|
|
@ -92,12 +92,12 @@ bool repl::run() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
|
if (nasal_linker->link(*nasal_parser, true).geterr()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nasal_opt->do_optimization(nasal_parser->tree());
|
nasal_opt->do_optimization(nasal_parser->tree());
|
||||||
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true).geterr()) {
|
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true, false).geterr()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ void repl::execute() {
|
||||||
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
||||||
|
|
||||||
// finish initialization, output version info
|
// finish initialization, output version info
|
||||||
std::cout << "Nasal REPL interpreter version " << __nasver;
|
std::cout << "Nasal REPL interpreter version " << __nasver__;
|
||||||
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||||
help();
|
help();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
use std.unix;
|
||||||
|
|
||||||
|
srand();
|
||||||
|
|
||||||
|
var ant = {
|
||||||
|
new: func(color, pos_x, pos_y) {
|
||||||
|
return {
|
||||||
|
move: int(rand()*4),
|
||||||
|
color: color,
|
||||||
|
pos_x: pos_x,
|
||||||
|
pos_y: pos_y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
# generate agents
|
||||||
|
var ants = [
|
||||||
|
ant.new(10, 30, 15),
|
||||||
|
ant.new(11, 31, 16),
|
||||||
|
ant.new(4, 29, 13),
|
||||||
|
ant.new(9, 30, 14),
|
||||||
|
ant.new(13, 30, 12),
|
||||||
|
ant.new(99, 25, 18)
|
||||||
|
];
|
||||||
|
|
||||||
|
# initialize game map
|
||||||
|
var map = [];
|
||||||
|
var map_color = [];
|
||||||
|
setsize(map, 60*30);
|
||||||
|
setsize(map_color, 60*30);
|
||||||
|
forindex(var i; map) {
|
||||||
|
map[i] = 0;
|
||||||
|
map_color[i] = 0;
|
||||||
|
}
|
||||||
|
foreach(var a; ants) {
|
||||||
|
map_color[a.pos_x + a.pos_y*60] = a.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
var print_map = func {
|
||||||
|
var res = "\e[1;1H";
|
||||||
|
for(var y = 0; y<30; y += 1) {
|
||||||
|
for(var x = 0; x<60; x += 1) {
|
||||||
|
res ~= "\e[38;5;"~map_color[x + y*60]~";1m";
|
||||||
|
res ~= map[x + y*60] ~ " \e[0m";
|
||||||
|
}
|
||||||
|
res ~= "\n";
|
||||||
|
}
|
||||||
|
print(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var move = func {
|
||||||
|
var move_step = [
|
||||||
|
[0, -1],
|
||||||
|
[1, 0],
|
||||||
|
[0, 1],
|
||||||
|
[-1, 0]
|
||||||
|
];
|
||||||
|
var temp_map = [];
|
||||||
|
setsize(temp_map, 60*30);
|
||||||
|
forindex(var i; map) {
|
||||||
|
temp_map[i] = map[i];
|
||||||
|
}
|
||||||
|
foreach(var a; ants) {
|
||||||
|
var map_state = map[a.pos_x + a.pos_y*60];
|
||||||
|
temp_map[a.pos_x + a.pos_y*60] = map_state==0? 1:0;
|
||||||
|
if (map_state==1) {
|
||||||
|
a.move -= 1;
|
||||||
|
if (a.move < 0) {
|
||||||
|
a.move = 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.move += 1;
|
||||||
|
if (a.move > 3) {
|
||||||
|
a.move = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.pos_x += move_step[a.move][0];
|
||||||
|
a.pos_y += move_step[a.move][1];
|
||||||
|
if (a.pos_x < 0) { a.pos_x = 59; }
|
||||||
|
if (a.pos_x > 59) { a.pos_x = 0; }
|
||||||
|
if (a.pos_y < 0) { a.pos_y = 29; }
|
||||||
|
if (a.pos_y > 29) { a.pos_y = 0; }
|
||||||
|
|
||||||
|
if (map_color[a.pos_x + a.pos_y*60]!=a.color) {
|
||||||
|
map_color[a.pos_x + a.pos_y*60] = a.color;
|
||||||
|
} else {
|
||||||
|
map_color[a.pos_x + a.pos_y*60] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map = temp_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
print_map();
|
||||||
|
move();
|
||||||
|
unix.sleep(1/24);
|
||||||
|
}
|
Loading…
Reference in New Issue