diff --git a/.gitignore b/.gitignore index 5e477ca..7b57570 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ nasal.exe .vscode dump fgfs.log +.temp.* # build dir build diff --git a/CMakeLists.txt b/CMakeLists.txt index a2a6f13..ef2d704 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp ${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp ${CMAKE_SOURCE_DIR}/src/optimizer.cpp - ${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp) + ${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp + ${CMAKE_SOURCE_DIR}/src/repl.cpp) add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE}) target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src) diff --git a/makefile b/makefile index 456f98d..5ec7f3e 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,8 @@ NASAL_HEADER=\ src/math_lib.h\ src/dylib_lib.h\ src/unix_lib.h\ - src/coroutine.h + src/coroutine.h\ + src/repl.h NASAL_OBJECT=\ build/nasal_err.o\ @@ -49,6 +50,7 @@ NASAL_OBJECT=\ build/coroutine.o\ build/nasal_vm.o\ build/nasal_dbg.o\ + build/repl.o\ build/main.o # for test @@ -67,7 +69,10 @@ build/main.o: $(NASAL_HEADER) src/main.cpp | build build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build $(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o build/nasal_misc.o -I . -build/nasal_err.o: src/nasal.h src/nasal_err.h src/nasal_err.cpp | build +build/repl.o: src/nasal.h src/repl.h src/repl.cpp | build + $(CXX) -std=$(STD) -c -O3 src/repl.cpp -fno-exceptions -fPIC -o build/repl.o -I . + +build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build $(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o build/nasal_err.o -I . build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build @@ -83,6 +88,7 @@ build/nasal_import.o: \ build/nasal_lexer.o: \ src/nasal.h\ + src/repl.h\ src/nasal_err.h\ src/nasal_lexer.h src/nasal_lexer.cpp | build $(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o build/nasal_lexer.o -I . diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 71b95c8..7dba4c1 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -8,7 +8,7 @@ #include #pragma comment(lib,"ws2_32") -class WSAmanager{ +class WSAmanager { private: WSAData data; public: diff --git a/src/ast_dumper.cpp b/src/ast_dumper.cpp index 84f62f2..c46bfd0 100644 --- a/src/ast_dumper.cpp +++ b/src/ast_dumper.cpp @@ -2,6 +2,8 @@ #include +namespace nasal { + bool ast_dumper::visit_null_expr(null_expr* node) { dump_indent(); std::cout << "null" << format_location(node->get_location()); @@ -478,4 +480,6 @@ bool ast_dumper::visit_return_expr(return_expr* node) { pop_indent(); } return true; -} \ No newline at end of file +} + +} diff --git a/src/ast_dumper.h b/src/ast_dumper.h index 1f520d2..1385698 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -6,6 +6,8 @@ #include #include +namespace nasal { + class ast_dumper:public ast_visitor { private: std::vector indent; @@ -78,4 +80,6 @@ public: void dump(code_block* root) { root->accept(this); } -}; \ No newline at end of file +}; + +} diff --git a/src/ast_visitor.cpp b/src/ast_visitor.cpp index d9aa45a..677c043 100644 --- a/src/ast_visitor.cpp +++ b/src/ast_visitor.cpp @@ -1,5 +1,7 @@ #include "ast_visitor.h" +namespace nasal { + bool ast_visitor::visit_expr(expr* node) { node->accept(this); return true; @@ -231,4 +233,6 @@ bool ast_visitor::visit_return_expr(return_expr* node) { node->get_value()->accept(this); } return true; -} \ No newline at end of file +} + +} diff --git a/src/ast_visitor.h b/src/ast_visitor.h index 9586633..62f4dff 100644 --- a/src/ast_visitor.h +++ b/src/ast_visitor.h @@ -2,6 +2,8 @@ #include "nasal_ast.h" +namespace nasal { + class ast_visitor { public: virtual bool visit_expr(expr*); @@ -40,4 +42,6 @@ public: virtual bool visit_continue_expr(continue_expr*); virtual bool visit_break_expr(break_expr*); virtual bool visit_return_expr(return_expr*); -}; \ No newline at end of file +}; + +} diff --git a/src/main.cpp b/src/main.cpp index 29c8864..6f6316e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,21 +11,22 @@ #include "nasal_codegen.h" #include "nasal_vm.h" #include "nasal_dbg.h" +#include "repl.h" #include #include #include -const u32 VM_RAW_AST = 1; -const u32 VM_AST = 1<<1; -const u32 VM_CODE = 1<<2; -const u32 VM_TIME = 1<<3; -const u32 VM_EXEC = 1<<4; -const u32 VM_DETAIL = 1<<5; -const u32 VM_DEBUG = 1<<6; -const u32 VM_SYMINFO = 1<<7; -const u32 VM_PROFILE = 1<<8; -const u32 VM_PROF_ALL = 1<<9; +const u32 VM_RAW_AST = 1; +const u32 VM_AST = 1<<1; +const u32 VM_CODE = 1<<2; +const u32 VM_TIME = 1<<3; +const u32 VM_EXEC = 1<<4; +const u32 VM_DETAIL = 1<<5; +const u32 VM_DEBUG = 1<<6; +const u32 VM_SYMINFO = 1<<7; +const u32 VM_PROFILE = 1<<8; +const u32 VM_PROF_ALL = 1<<9; std::ostream& help(std::ostream& out) { out @@ -40,6 +41,7 @@ std::ostream& help(std::ostream& out) { << "option:\n" << " -h, --help | get help.\n" << " -v, --version | get version.\n" + << " -r, --repl | use repl interpreter(experimental).\n" << "\nnasal [option] [argv]\n" << "option:\n" << " -a, --ast | view ast after link/optimize process.\n" @@ -87,7 +89,7 @@ std::ostream& version(std::ostream& out) { num = (num+rand())*(1.0/(RAND_MAX+1.0)); } if (num<0.01) { - parse::easter_egg(); + nasal::parse::easter_egg(); } out << "nasal interpreter version " << __nasver; out << " (" << __DATE__ << " " << __TIME__ << ")\n"; @@ -110,11 +112,10 @@ void execute( using clk = std::chrono::high_resolution_clock; const auto den = clk::duration::period::den; - lexer lex; - parse parse; - linker ld; - codegen gen; - vm ctx; + nasal::lexer lex; + nasal::parse parse; + nasal::linker ld; + nasal::codegen gen; // lexer scans file to get tokens lex.scan(file).chkerr(); @@ -122,7 +123,7 @@ void execute( // parser gets lexer's token list to compile parse.compile(lex).chkerr(); if (cmd&VM_RAW_AST) { - auto dumper = std::unique_ptr(new ast_dumper); + auto dumper = std::unique_ptr(new nasal::ast_dumper); dumper->dump(parse.tree()); } @@ -130,11 +131,10 @@ void execute( ld.link(parse, file, cmd&VM_DETAIL).chkerr(); // optimizer does simple optimization on ast - auto opt = new optimizer; + auto opt = std::unique_ptr(new nasal::optimizer); opt->do_optimization(parse.tree()); - delete opt; if (cmd&VM_AST) { - auto dumper = std::unique_ptr(new ast_dumper); + auto dumper = std::unique_ptr(new nasal::ast_dumper); dumper->dump(parse.tree()); } @@ -150,15 +150,18 @@ void execute( // run auto start = clk::now(); if (cmd&VM_DEBUG) { - dbg().run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL); + auto debugger = std::unique_ptr(new nasal::dbg); + debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL); } else if (cmd&VM_TIME || cmd&VM_EXEC) { - ctx.run(gen, ld, argv, cmd&VM_DETAIL); + auto runtime = std::unique_ptr(new nasal::vm); + runtime->run(gen, ld, argv, cmd&VM_DETAIL); } // get running time + auto end = clk::now(); if (cmd&VM_TIME) { - f64 tm = (clk::now()-start).count()*1.0/den; - std::clog << "process exited after " << tm << "s.\n\n"; + std::clog << "process exited after "; + std::clog << (end-start).count()*1.0/den << "s.\n\n"; } } @@ -176,6 +179,9 @@ i32 main(i32 argc, const char* argv[]) { std::clog << help; } else if (s=="-v" || s=="--version") { std::clog << version; + } else if (s=="-r" || s=="--repl") { + auto repl = std::unique_ptr(new nasal::repl::repl); + repl->execute(); } else if (s[0]!='-') { execute(s, {}, VM_EXEC); } else { diff --git a/src/nasal_ast.cpp b/src/nasal_ast.cpp index 5b2610c..a1c8859 100644 --- a/src/nasal_ast.cpp +++ b/src/nasal_ast.cpp @@ -1,6 +1,8 @@ #include "nasal_ast.h" #include "ast_visitor.h" +namespace nasal { + void expr::accept(ast_visitor* visitor) { visitor->visit_expr(this); } @@ -365,4 +367,6 @@ return_expr::~return_expr() { void return_expr::accept(ast_visitor* visitor) { visitor->visit_return_expr(this); -} \ No newline at end of file +} + +} diff --git a/src/nasal_ast.h b/src/nasal_ast.h index ffffd95..6e04e84 100644 --- a/src/nasal_ast.h +++ b/src/nasal_ast.h @@ -6,6 +6,8 @@ #include #include +namespace nasal { + enum class expr_type:u32 { ast_null = 0, // null node ast_block, // code block @@ -669,3 +671,5 @@ public: expr* get_value() {return value;} void accept(ast_visitor*) override; }; + +} diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 8e41af7..1b3c6d6 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1,5 +1,7 @@ #include "nasal_codegen.h" +namespace nasal { + void codegen::init_file_map(const std::vector& file_list) { file_map = {}; for(usize i = 0; i(new symbol_finder); for(const auto& i : finder->do_find(node)) { - if (!experimental_namespace.count(i.file)) { - experimental_namespace[i.file] = {}; + if (native_function_mapper.count(i.name)) { + die("definition conflicts with native function", i.location); } - if (local.empty() && !experimental_namespace.at(i.file).count(i.name)) { - experimental_namespace.at(i.file).insert(i.name); + if (!experimental_namespace.count(i.location.file)) { + experimental_namespace[i.location.file] = {}; + } + if (local.empty() && !experimental_namespace.at(i.location.file).count(i.name)) { + experimental_namespace.at(i.location.file).insert(i.name); } add_symbol(i.name); } @@ -1141,7 +1146,7 @@ void codegen::ret_gen(return_expr* node) { gen(op_ret, 0, node->get_location()); } -const error& codegen::compile(parse& parse, linker& import, bool repl) { +const error& codegen::compile(parse& parse, linker& import) { init_native_function(); init_file_map(import.get_file_list()); @@ -1154,7 +1159,7 @@ const error& codegen::compile(parse& parse, linker& import, bool repl) { // search global symbols first find_symbol(parse.tree()); - gen(op_intg, repl? STACK_DEPTH/2:global.size(), parse.tree()->get_location()); + gen(op_intg, global.size(), parse.tree()->get_location()); // generate main block block_gen(parse.tree()); @@ -1252,3 +1257,5 @@ void codegen::symbol_dump(std::ostream& out) const { } } } + +} diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 141f75b..7756d22 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -28,6 +28,8 @@ #pragma warning (disable:4267) #endif +namespace nasal { + class codegen { private: error err; @@ -132,7 +134,9 @@ public: public: codegen() = default; - const error& compile(parse&, linker&, bool repl = false); + const error& compile(parse&, linker&); void print(std::ostream&); void symbol_dump(std::ostream&) const; }; + +} diff --git a/src/nasal_dbg.cpp b/src/nasal_dbg.cpp index 266633e..72c7325 100644 --- a/src/nasal_dbg.cpp +++ b/src/nasal_dbg.cpp @@ -1,5 +1,7 @@ #include "nasal_dbg.h" +namespace nasal { + void debug_prof_data::init_counter() { for(usize i = 0; i #include +namespace nasal { + class debug_prof_data { private: static const usize operand_size = op_code_type::op_ret + 1; @@ -167,3 +169,5 @@ public: bool ); }; + +} diff --git a/src/nasal_err.cpp b/src/nasal_err.cpp index d13471f..c3ce95e 100644 --- a/src/nasal_err.cpp +++ b/src/nasal_err.cpp @@ -1,4 +1,8 @@ #include "nasal_err.h" +#include "repl.h" + +namespace nasal { + #ifdef _WIN32 #include // use SetConsoleTextAttribute struct for_reset { @@ -6,7 +10,11 @@ struct for_reset { for_reset() { GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr); } -} reset_ter_color; + static for_reset* singleton() { + static for_reset windows_set; + return &windows_set; + } +}; #endif std::ostream& back_white(std::ostream& s) { @@ -57,7 +65,7 @@ std::ostream& white(std::ostream& s) { std::ostream& reset(std::ostream& s) { #ifdef _WIN32 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), - reset_ter_color.scr.wAttributes); + for_reset::singleton()->scr.wAttributes); #else s << "\033[0m"; #endif @@ -68,7 +76,24 @@ void flstream::load(const std::string& f) { if (file==f) { // don't need to load a loaded file return; } else { - file=f; + file = f; + } + + if (repl::info::instance()->in_repl_mode && + repl::info::instance()->repl_file_name==file) { + const auto& source = repl::info::instance()->repl_file_source; + res = {}; + size_t pos = 0, last = 0; + while ((pos = source.find("\n", last))!=std::string::npos) { + res.push_back(source.substr(last, pos - last)); + last = pos + 1; + } + if (last& vec, usize begin, usize end) { } void gc::mark_context_root(std::vector& bfs_queue) { - + // scan global + for(usize i = 0; ivm_num) { + bfs_queue.push_back(val); + } + } // scan now running context, this context maybe related to coroutine or main for(var* i = rctx->stack; i<=rctx->top; ++i) { if (i->type>vm_num) { @@ -469,7 +488,7 @@ void gc::mark_upval(std::vector& bfs_queue, nas_upval& upval) { void gc::mark_co(std::vector& bfs_queue, nas_co& co) { bfs_queue.push_back(co.ctx.funcr); bfs_queue.push_back(co.ctx.upvalr); - for(var* i = co.stack; i<=co.ctx.top; ++i) { + for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) { if (i->type>vm_num) { bfs_queue.push_back(*i); } diff --git a/src/nasal_gc.h b/src/nasal_gc.h index 901db8e..51909ca 100644 --- a/src/nasal_gc.h +++ b/src/nasal_gc.h @@ -202,14 +202,14 @@ public: }; struct context { - u32 pc; - var* localr; - var* memr; - var funcr; - var upvalr; - var* canary; - var* stack; - var* top; + u32 pc = 0; + var* localr = nullptr; + var* memr = nullptr; + var funcr = var::nil(); + var upvalr = var::nil(); + var* canary = nullptr; + var* stack = nullptr; + var* top = nullptr; }; struct nas_co { @@ -219,11 +219,16 @@ struct nas_co { dead }; - var stack[STACK_DEPTH]; context ctx; status status; - nas_co() {clear();} + nas_co() { + ctx.stack = new var[STACK_DEPTH]; + clear(); + } + ~nas_co() { + delete[] ctx.stack; + } void clear(); }; @@ -281,6 +286,10 @@ struct gc { /* main context temporary storage */ context mctx; + /* global storage */ + var* main_context_global = nullptr; + usize main_context_global_size = 0; + /* runtime context */ context* rctx = nullptr; nas_co* cort = nullptr; // running coroutine @@ -315,7 +324,11 @@ struct gc { i64 max_mark_time = 0; i64 max_sweep_time = 0; - gc(context* _ctx): rctx(_ctx) {} + void set(context* _ctx, var* _global, usize _size) { + rctx = _ctx; + main_context_global = _global; + main_context_global_size = _size; + } private: /* gc functions */ diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index 31b862b..f7390ae 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -1,6 +1,8 @@ #include "nasal_import.h" #include "symbol_finder.h" +namespace nasal { + linker::linker(): show_path(false), lib_loaded(false), this_file(""), lib_path("") { @@ -346,3 +348,5 @@ const error& linker::link( delete old_tree_root; return err; } + +} diff --git a/src/nasal_import.h b/src/nasal_import.h index e50161f..fc53fe9 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -20,7 +20,9 @@ #include -class linker{ +namespace nasal { + +class linker { private: bool show_path; bool lib_loaded; @@ -31,6 +33,7 @@ private: std::vector module_load_stack; std::vector envpath; +private: bool import_check(expr*); bool exist(const std::string&); u16 find(const std::string&); @@ -53,3 +56,5 @@ public: const auto& get_this_file() const {return this_file;} const auto& get_lib_path() const {return lib_path;} }; + +} diff --git a/src/nasal_lexer.cpp b/src/nasal_lexer.cpp index facfe13..9b63ab8 100644 --- a/src/nasal_lexer.cpp +++ b/src/nasal_lexer.cpp @@ -5,17 +5,20 @@ #endif #include "nasal_lexer.h" +#include "repl.h" + +namespace nasal { bool lexer::skip(char c) { return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0; } bool lexer::is_id(char c) { - return (c=='_') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') || (c<0); + return (c=='_') || std::isalpha(c) || (c<0); } bool lexer::is_hex(char c) { - return ('0'<=c && c<='9') || ('a'<=c && c<='f') || ('A'<=c && c<='F'); + return std::isxdigit(c); } bool lexer::is_oct(char c) { @@ -23,7 +26,7 @@ bool lexer::is_oct(char c) { } bool lexer::is_dec(char c) { - return '0'<=c && c<='9'; + return std::isdigit(c); } bool lexer::is_str(char c) { @@ -58,10 +61,18 @@ void lexer::err_char() { err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c)); - err.fatal("lexer", "fatal error occurred, stop"); + ++invalid_char; } void lexer::open(const std::string& file) { + if (repl::info::instance()->in_repl_mode && + repl::info::instance()->repl_file_name==file) { + err.load(file); + filename = file; + res = repl::info::instance()->repl_file_source; + return; + } + // check file exsits and it is a regular file struct stat buffer; if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) { @@ -115,7 +126,7 @@ std::string lexer::utf8_gen() { err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">"); - err.fatal("lexer", "fatal error occurred, stop"); + ++invalid_char; } str += tmp; column += 2; // may have some problems because not all the unicode takes 2 space @@ -127,7 +138,7 @@ token lexer::id_gen() { u32 begin_line = line; u32 begin_column = column; std::string str = ""; - while(ptr10) { + err.err("lexer", "too many invalid characters, stop"); + break; + } + } + if (toks.size()) { + toks.push_back({toks.back().loc, tok::eof, ""}); + } else { + toks.push_back({{line, column, line, column, filename}, tok::eof, ""}); } - toks.push_back({{line, column, line, column, filename}, tok::eof, ""}); res = ""; return err; } + +} diff --git a/src/nasal_lexer.h b/src/nasal_lexer.h index b824ce9..2862a57 100644 --- a/src/nasal_lexer.h +++ b/src/nasal_lexer.h @@ -19,6 +19,8 @@ #define S_ISREG(m) (((m)&0xF000)==0x8000) #endif +namespace nasal { + enum class tok:u32 { null=0, // null token (default token type) num, // number literal @@ -95,8 +97,11 @@ private: usize ptr; std::string filename; std::string res; + error err; + u64 invalid_char; std::vector toks; + const std::unordered_map typetbl { {"true" ,tok::tktrue }, {"false" ,tok::tkfalse }, @@ -175,7 +180,9 @@ private: token dots(); token calc_opr(); public: - lexer(): line(1), column(0), ptr(0), filename(""), res("") {} + lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {} const error& scan(const std::string&); const std::vector& result() const {return toks;} }; + +} diff --git a/src/nasal_opcode.h b/src/nasal_opcode.h index d7f93fc..2a4e4d5 100644 --- a/src/nasal_opcode.h +++ b/src/nasal_opcode.h @@ -7,7 +7,7 @@ enum op_code_type:u8 { op_exit, // stop the virtual machine - op_intg, // global scope size, set stack top + op_intg, // init global scope op_intl, // local scope size op_loadg, // load global value op_loadl, // load local value diff --git a/src/nasal_parse.cpp b/src/nasal_parse.cpp index ebfd397..82257e6 100644 --- a/src/nasal_parse.cpp +++ b/src/nasal_parse.cpp @@ -1,6 +1,8 @@ #include "nasal_ast.h" #include "nasal_parse.h" +namespace nasal { + const error& parse::compile(const lexer& lexer) { toks=lexer.result().data(); ptr=in_func=in_loop=0; @@ -13,7 +15,7 @@ const error& parse::compile(const lexer& lexer) { match(tok::semi); } else if (need_semi_check(root->get_expressions().back()) && !lookahead(tok::eof)) { // the last expression can be recognized without semi - die(prevspan, "expected \";\""); + die(prevspan, "expected \";\" after this token"); } } update_location(root); @@ -60,6 +62,13 @@ void parse::die(const span& loc, std::string info) { err.err("parse", loc, info); } +void parse::next() { + if (lookahead(tok::eof)) { + return; + } + ++ptr; +} + void parse::match(tok type, const char* info) { if (!lookahead(type)) { if (info) { @@ -70,13 +79,10 @@ void parse::match(tok type, const char* info) { case tok::num:die(thisspan, "expected number"); break; case tok::str:die(thisspan, "expected string"); break; case tok::id: die(thisspan, "expected identifier");break; - default: die(thisspan, "expected '"+tokname.at(type)+"'"); break; + default: die(thisspan, "expected \""+tokname.at(type)+"\""); break; } return; } - if (lookahead(tok::eof)) { - return; - } next(); } @@ -91,7 +97,7 @@ bool parse::is_call(tok type) { bool parse::check_comma(const tok* panic_set) { for(u32 i=0;panic_set[i]!=tok::null;++i) { if (lookahead(panic_set[i])) { - die(prevspan, "expected ',' between scalars"); + die(prevspan, "expected \",\" between scalars"); return true; } } @@ -118,7 +124,11 @@ bool parse::check_tuple() { } bool parse::check_func_end(expr* node) { - auto type=node->get_type(); + // avoid error parse caused nullptr return value + if (!node) { + return true; + } + auto type = node->get_type(); if (type==expr_type::ast_func) { return true; } else if (type==expr_type::ast_def) { @@ -129,9 +139,21 @@ bool parse::check_func_end(expr* node) { return false; } +bool parse::check_in_curve_multi_definition() { + // we do not allow syntax like: + // func {}(var a, b, c) + // but we still allow syntax like: + // func {}(var a = 1) + // in fact, this syntax is not recommended + if (!lookahead(tok::lcurve) || toks[ptr+1].type!=tok::var) { + return false; + } + return toks[ptr+2].type==tok::id && toks[ptr+3].type==tok::comma; +} + bool parse::check_special_call() { // special call means like this: function_name(a:1,b:2,c:3); - u32 check_ptr=ptr, curve=1, bracket=0, brace=0; + u32 check_ptr = ptr, curve = 1, bracket = 0, brace = 0; while(toks[++check_ptr].type!=tok::eof && curve) { switch(toks[check_ptr].type) { case tok::lcurve: ++curve; break; @@ -154,7 +176,11 @@ bool parse::check_special_call() { } bool parse::need_semi_check(expr* node) { - auto type=node->get_type(); + // avoid error parse caused nullptr return value + if (!node) { + return true; + } + auto type = node->get_type(); if (type==expr_type::ast_for || type==expr_type::ast_forei || type==expr_type::ast_while || @@ -165,7 +191,10 @@ bool parse::need_semi_check(expr* node) { } void parse::update_location(expr* node) { - node->update_location(toks[ptr].loc); + if (!ptr) { + return; + } + node->update_location(toks[ptr-1].loc); } null_expr* parse::null() { @@ -228,7 +257,7 @@ vector_expr* parse::vec() { } } update_location(node); - match(tok::rbracket, "expected ']' when generating vector"); + match(tok::rbracket, "expected \"]\" when generating vector"); return node; } @@ -240,13 +269,13 @@ hash_expr* parse::hash() { if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id) || lookahead(tok::str)) { // first set of hashmember - die(prevspan, "expected ',' between hash members"); + die(prevspan, "expected \",\" between hash members"); } else { break; } } update_location(node); - match(tok::rbrace, "expected '}' when generating hash"); + match(tok::rbrace, "expected \"}\" when generating hash"); return node; } @@ -301,13 +330,13 @@ void parse::params(function* func_node) { if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id)) { // first set of identifier - die(prevspan, "expected ',' between identifiers"); + die(prevspan, "expected \",\" between identifiers"); } else { break; } } update_location(func_node); - match(tok::rcurve, "expected ')' after parameter list"); + match(tok::rcurve, "expected \")\" after parameter list"); return; } @@ -348,7 +377,7 @@ expr* parse::expression() { case tok::cont: return continue_expression(); case tok::brk: return break_expression(); case tok::ret: return return_expression(); - case tok::semi: break; + case tok::semi: break; default: die(thisspan, "incorrect token <"+toks[ptr].str+">"); next(); @@ -373,10 +402,10 @@ code_block* parse::expression_block() { match(tok::semi); } else if (need_semi_check(node->get_expressions().back()) && !lookahead(tok::rbrace)) { // the last expression can be recognized without semi - die(prevspan, "expected ';'"); + die(prevspan, "expected \";\" after this token"); } } - match(tok::rbrace, "expected '}' when generating expressions"); + match(tok::rbrace, "expected \"}\" when generating expressions"); } else { node->add_expression(expression()); if (lookahead(tok::semi)) { @@ -626,8 +655,12 @@ expr* parse::scalar() { die(thisspan, "expected scalar"); return null(); } - // check call and avoid ambiguous syntax - if (is_call(toks[ptr].type) && !(lookahead(tok::lcurve) && toks[ptr+1].type==tok::var)) { + // check call and avoid ambiguous syntax: + // var f = func(){} + // (var a, b, c) = (1, 2, 3); + // will be incorrectly recognized like: + // var f = func(){}(var a, b, c) + if (is_call(toks[ptr].type) && !check_in_curve_multi_definition()) { auto call_node = new call_expr(toks[ptr].loc); call_node->set_first(node); while(is_call(toks[ptr].type)) { @@ -685,7 +718,7 @@ call_vector* parse::callv() { die(thisspan, "expected index value"); } update_location(node); - match(tok::rbracket, "expected ']' when calling vector"); + match(tok::rbracket, "expected \"]\" when calling vector"); return node; } @@ -712,7 +745,7 @@ call_function* parse::callf() { break; } update_location(node); - match(tok::rcurve, "expected ')' when calling function"); + match(tok::rcurve, "expected \")\" when calling function"); return node; } @@ -780,7 +813,7 @@ multi_identifier* parse::multi_id() { if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id)) { // first set of identifier - die(prevspan, "expected ',' between identifiers"); + die(prevspan, "expected \",\" between identifiers"); } else { break; } @@ -810,7 +843,7 @@ tuple_expr* parse::multi_scalar() { } } update_location(node); - match(tok::rcurve, "expected ')' after multi-scalar"); + match(tok::rcurve, "expected \")\" after multi-scalar"); return node; } @@ -877,7 +910,7 @@ for_expr* parse::for_loop() { } else { node->set_initial(calc()); } - match(tok::semi, "expected ';' in for(;;)"); + match(tok::semi, "expected \";\" in for(;;)"); // conditional expression if (lookahead(tok::eof)) { @@ -888,7 +921,7 @@ for_expr* parse::for_loop() { } else { node->set_condition(calc()); } - match(tok::semi, "expected ';' in for(;;)"); + match(tok::semi, "expected \";\" in for(;;)"); //after loop expression if (lookahead(tok::eof)) { @@ -925,7 +958,7 @@ forei_expr* parse::forei_loop() { die(thisspan, "expected iterator"); } node->set_iterator(iter_gen()); - match(tok::semi, "expected ';' in foreach/forindex(iter;vector)"); + match(tok::semi, "expected \";\" in foreach/forindex(iter;vector)"); if (lookahead(tok::eof)) { die(thisspan, "expected vector"); } @@ -1026,3 +1059,5 @@ return_expr* parse::return_expression() { update_location(node); return node; } + +} diff --git a/src/nasal_parse.h b/src/nasal_parse.h index c29e7bf..2d9f6dd 100644 --- a/src/nasal_parse.h +++ b/src/nasal_parse.h @@ -7,6 +7,8 @@ #include "nasal_lexer.h" #include "nasal_err.h" +namespace nasal { + class parse { #define thisspan (toks[ptr].loc) @@ -77,13 +79,14 @@ private: private: void die(const span&,std::string); - void next() {++ptr;} - void match(tok type, const char* info=nullptr); + void next(); + void match(tok, const char* info=nullptr); bool lookahead(tok); bool is_call(tok); bool check_comma(const tok*); bool check_tuple(); bool check_func_end(expr*); + bool check_in_curve_multi_definition(); bool check_special_call(); bool need_semi_check(expr*); void update_location(expr*); @@ -154,4 +157,6 @@ public: } const error& compile(const lexer&); static void easter_egg(); -}; \ No newline at end of file +}; + +} diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 6249aec..4ac5050 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -1,11 +1,13 @@ #include "nasal_vm.h" +namespace nasal { + void vm::init( const std::vector& strs, const std::vector& nums, const std::vector& natives, const std::vector& code, - const std::unordered_map& global, + const std::unordered_map& global_symbol, const std::vector& filenames, const std::vector& argv ) { @@ -13,6 +15,7 @@ void vm::init( cstr = strs.data(); bytecode = code.data(); files = filenames.data(); + global_size = global_symbol.size(); /* set native functions */ native = natives; @@ -23,28 +26,29 @@ void vm::init( ctx.memr = nullptr; ctx.funcr = nil; ctx.upvalr = nil; - ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] - ctx.top = stack; - ctx.stack = stack; + ctx.canary = ctx.stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] + ctx.top = ctx.stack; // nothing is on stack - /* clear main stack */ + /* clear main stack and global */ for(u32 i = 0; istack==stack? stack+bytecode[0].num:ngc.rctx->stack; - var* ctx_top = ngc.rctx->stack==stack? ctx.top:ngc.rctx->top; + var* bottom = ctx.stack; + var* top = ctx.top; + std::stack ret; - for(var* i = bottom; i<=ctx_top; ++i) { + for(var* i = bottom; i<=top; ++i) { if (i->type==vm_ret && i->ret()!=0) { ret.push(i->ret()); } } ret.push(ctx.pc); // store the position program crashed + std::clog << "trace back (" - << (ngc.rctx->stack==stack? "main":"coroutine") + << (ngc.cort? "coroutine":"main") << ")\n"; codestream::set(cnum, cstr, native.data(), files); for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) { @@ -121,19 +126,17 @@ void vm::traceback() { } void vm::stackinfo(const u32 limit = 10) { - /* bytecode[0].num is the global size */ - const u32 gsize = ngc.rctx->stack==stack? bytecode[0].num:0; - var* t = ctx.top; - var* bottom = ngc.rctx->stack+gsize; + var* top = ctx.top; + var* bottom = ctx.stack; std::clog << "stack (0x" << std::hex << (u64)bottom << std::dec; - std::clog << " <+" << gsize << ">, limit " << limit << ", total "; - std::clog << (t=bottom; ++i, --t) { + std::clog << ", limit " << limit << ", total "; + std::clog << (top=bottom; ++i, --top) { std::clog << " 0x" << std::hex << std::setw(6) << std::setfill('0') - << (u64)(t-ngc.rctx->stack) << std::dec + << (u64)(top-bottom) << std::dec << " "; - valinfo(t[0]); + valinfo(top[0]); } } @@ -141,7 +144,7 @@ void vm::reginfo() { std::clog << "registers (" << (ngc.cort? "coroutine":"main") << ")\n" << std::hex << " [pc ] | pc | 0x" << ctx.pc << "\n" - << " [global] | addr | 0x" << (u64)stack << "\n" + << " [global] | addr | 0x" << (u64)global << "\n" << " [local ] | addr | 0x" << (u64)ctx.localr << "\n" << " [memr ] | addr | 0x" << (u64)ctx.memr << "\n" << " [canary] | addr | 0x" << (u64)ctx.canary << "\n" @@ -152,17 +155,16 @@ void vm::reginfo() { } void vm::gstate() { - // bytecode[0].op is op_intg - if (!bytecode[0].num || stack[0].type==vm_none) { + if (!global_size || global[0].type==vm_none) { return; } std::clog << "global (0x" << std::hex - << (u64)stack << " <+0>)\n" << std::dec; - for(u32 i = 0; istack) + << " <+" << (u64)(ctx.localr-ctx.stack) << ">)\n" << std::dec; for(u32 i = 0; istack==stack) { + if (!ngc.cort) { // in main context, exit directly std::exit(1); } else { @@ -442,3 +444,5 @@ mcallh: exec_nodie(o_mcallh); // -0 ret: exec_nodie(o_ret ); // -2 #endif } + +} diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 3b54cf7..af86bf6 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -13,10 +13,12 @@ #pragma warning (disable:4102) #endif +namespace nasal { + class vm { protected: - /* registers and constants of vm */ + /* registers of vm */ context ctx; /* constants */ @@ -29,7 +31,8 @@ protected: gc ngc; /* main stack */ - var stack[STACK_DEPTH]; + var* global = nullptr; + usize global_size = 0; /* values used for debugger */ const std::string* files = nullptr; // file name list @@ -46,7 +49,7 @@ protected: const std::vector&); /* debug functions */ - bool verbose; + bool verbose = false; void valinfo(var&); void traceback(); void stackinfo(const u32); @@ -151,7 +154,14 @@ protected: public: /* constructor of vm instance */ - vm(): ngc(&ctx), verbose(false) {} + vm() { + ctx.stack = new var[STACK_DEPTH]; + global = new var[STACK_DEPTH]; + } + ~vm() { + delete[] ctx.stack; + delete[] global; + } /* execution entry */ void run( @@ -173,7 +183,7 @@ inline bool vm::cond(var& val) { inline void vm::o_intg() { // global values store on stack - ctx.top += imm[ctx.pc]; + // ctx.top += imm[ctx.pc]; // point to the top --ctx.top; } @@ -184,7 +194,7 @@ inline void vm::o_intl() { } inline void vm::o_loadg() { - stack[imm[ctx.pc]] = (ctx.top--)[0]; + global[imm[ctx.pc]] = (ctx.top--)[0]; } inline void vm::o_loadl() { @@ -534,7 +544,7 @@ inline void vm::o_feach() { inline void vm::o_callg() { // get main stack directly - (++ctx.top)[0] = stack[imm[ctx.pc]]; + (++ctx.top)[0] = global[imm[ctx.pc]]; } inline void vm::o_calll() { @@ -819,7 +829,7 @@ inline void vm::o_slc2() { } inline void vm::o_mcallg() { - ctx.memr = stack+imm[ctx.pc]; + ctx.memr = global+imm[ctx.pc]; (++ctx.top)[0] = ctx.memr[0]; // push value in this memory space on stack // to avoid being garbage collected @@ -946,4 +956,6 @@ inline void vm::o_ret() { if (!ctx.pc) { ngc.ctxreserve(); } -} \ No newline at end of file +} + +} diff --git a/src/optimizer.cpp b/src/optimizer.cpp index ed5a0fe..3b785f4 100644 --- a/src/optimizer.cpp +++ b/src/optimizer.cpp @@ -1,5 +1,7 @@ #include "optimizer.h" +namespace nasal { + void optimizer::const_string( binary_operator* node, string_literal* left_node, @@ -129,4 +131,6 @@ bool optimizer::visit_unary_operator(unary_operator* node) { void optimizer::do_optimization(code_block* root) { root->accept(this); -} \ No newline at end of file +} + +} diff --git a/src/optimizer.h b/src/optimizer.h index dc6ba7a..3c33453 100644 --- a/src/optimizer.h +++ b/src/optimizer.h @@ -5,6 +5,8 @@ #include "nasal_ast.h" #include "ast_visitor.h" +namespace nasal { + class optimizer:public ast_visitor { private: void const_string(binary_operator*, string_literal*, string_literal*); @@ -18,3 +20,5 @@ public: public: void do_optimization(code_block*); }; + +} diff --git a/src/repl.cpp b/src/repl.cpp new file mode 100644 index 0000000..a9f3ae1 --- /dev/null +++ b/src/repl.cpp @@ -0,0 +1,147 @@ +#include "repl.h" +#include "nasal_lexer.h" +#include "nasal_parse.h" +#include "nasal_import.h" +#include "optimizer.h" +#include "nasal_codegen.h" +#include "nasal_vm.h" + +namespace nasal { +namespace repl { + +std::string repl::readline(std::string prompt = ">>> ") { + auto line = std::string(""); + std::cout << prompt; + std::getline(std::cin, line,'\n'); + return line; +} + +void repl::update_temp_file() { + auto content = std::string(""); + for(const auto& i : source) { + content += i + "\n"; + } + info::instance()->repl_file_source = content; +} + +bool repl::check_need_more_input() { + while(true) { + update_temp_file(); + auto nasal_lexer = std::unique_ptr(new lexer); + if (nasal_lexer->scan("").geterr()) { + return false; + } + + i64 in_curve = 0; + i64 in_bracket = 0; + i64 in_brace = 0; + for(const auto& t : nasal_lexer->result()) { + switch(t.type) { + case tok::lcurve: ++in_curve; break; + case tok::rcurve: --in_curve; break; + case tok::lbracket: ++in_bracket; break; + case tok::rbracket: --in_bracket; break; + case tok::lbrace: ++in_brace; break; + case tok::rbrace: --in_brace; break; + default: break; + } + } + if (in_curve<=0 && in_bracket<=0 && in_brace<=0) { + break; + } + auto line = readline("... "); + source.back() += "\n" + line; + } + return true; +} + +void repl::help() { + std::cout << ".h, .help | show help\n"; + std::cout << ".e, .exit | quit the REPL\n"; + std::cout << ".q, .quit | quit the REPL\n"; + std::cout << ".c, .clear | clear the screen\n"; + std::cout << "\n"; +} + +bool repl::run() { + update_temp_file(); + + using clk = std::chrono::high_resolution_clock; + const auto den = clk::duration::period::den; + auto start = clk::now(); + + auto nasal_lexer = std::unique_ptr(new lexer); + auto nasal_parser = std::unique_ptr(new parse); + auto nasal_linker = std::unique_ptr(new linker); + auto nasal_opt = std::unique_ptr(new optimizer); + auto nasal_codegen = std::unique_ptr(new codegen); + auto nasal_runtime = std::unique_ptr(new vm); + + if (nasal_lexer->scan("").geterr()) { + return false; + } + + if (nasal_parser->compile(*nasal_lexer).geterr()) { + return false; + } + + if (nasal_linker->link(*nasal_parser, "", true).geterr()) { + return false; + } + + nasal_opt->do_optimization(nasal_parser->tree()); + if (nasal_codegen->compile(*nasal_parser, *nasal_linker).geterr()) { + return false; + } + + auto end = clk::now(); + std::clog << "[compile time: " << (end-start).count()*1000.0/den << " ms]\n"; + nasal_runtime->run(*nasal_codegen, *nasal_linker, {}, false); + + return true; +} + +void repl::execute() { + source = {}; + info::instance()->in_repl_mode = true; + + std::cout << "Nasal REPL interpreter(experimental).\n"; + help(); + + while(true) { + auto line = readline(); + if (!line.length()) { + continue; + } + + if (line == ".e" || line == ".exit" || line == ".q" || line == ".quit") { + break; + } else if (line == ".h" || line == ".help") { + help(); + continue; + } else if (line == ".c" || line == ".clear") { + std::cout << "\033c"; + continue; + } else if (line[0] == "."[0]) { + std::cout << "no such command \"" << line; + std::cout << "\", input \".help\" for help\n"; + continue; + } + + source.push_back(line); + if (!check_need_more_input()) { + source.pop_back(); + continue; + } + + // run program + if (!run()) { + source.pop_back(); + } + + std::cout << "\n"; + } +} + +} +} diff --git a/src/repl.h b/src/repl.h new file mode 100644 index 0000000..ffa0d6e --- /dev/null +++ b/src/repl.h @@ -0,0 +1,42 @@ +#pragma once + +#include "nasal.h" + +#include +#include +#include +#include +#include + +namespace nasal { +namespace repl { + +struct info { + bool in_repl_mode = false; + std::string repl_file_name = ""; + std::string repl_file_source = ""; + + // singleton + static info* instance() { + static info info; + return &info; + } +}; + +class repl { +private: + std::vector source; + +private: + std::string readline(std::string); + bool check_need_more_input(); + void update_temp_file(); + void help(); + bool run(); + +public: + void execute(); +}; + +} +} diff --git a/src/symbol_finder.cpp b/src/symbol_finder.cpp index 58a84fe..c5105ae 100644 --- a/src/symbol_finder.cpp +++ b/src/symbol_finder.cpp @@ -1,16 +1,18 @@ #include "symbol_finder.h" +namespace nasal { + bool symbol_finder::visit_definition_expr(definition_expr* node) { if (node->get_variable_name()) { symbols.push_back({ node->get_variable_name()->get_name(), - node->get_variable_name()->get_location().file + node->get_variable_name()->get_location() }); } else { for(auto i : node->get_variables()->get_variables()) { symbols.push_back({ i->get_name(), - i->get_location().file + i->get_location() }); } } @@ -30,7 +32,8 @@ bool symbol_finder::visit_iter_expr(iter_expr* node) { if (node->get_name()) { symbols.push_back({ node->get_name()->get_name(), - node->get_name()->get_location().file}); + node->get_name()->get_location() + }); } return true; } @@ -39,4 +42,6 @@ const std::vector& symbol_finder::do_find(code_block symbols.clear(); root->accept(this); return symbols; -} \ No newline at end of file +} + +} diff --git a/src/symbol_finder.h b/src/symbol_finder.h index a0eb852..3114179 100644 --- a/src/symbol_finder.h +++ b/src/symbol_finder.h @@ -7,11 +7,13 @@ #include #include +namespace nasal { + class symbol_finder:public ast_visitor { public: struct symbol_info { std::string name; - std::string file; + span location; }; private: @@ -22,4 +24,6 @@ public: bool visit_function(function*) override; bool visit_iter_expr(iter_expr*) override; const std::vector& do_find(code_block*); -}; \ No newline at end of file +}; + +} diff --git a/std/csv.nas b/std/csv.nas index bf2e54a..94800a9 100644 --- a/std/csv.nas +++ b/std/csv.nas @@ -1,16 +1,16 @@ # lib csv.nas # ValKmjolnir 2022/10/15 -var read_csv=func(path,delimeter=",",endline="\n"){ - var context=io.readfile(path); - context=split(endline,context); +var read = func(path, delimeter=",", endline="\n"){ + var context = io.readfile(path); + context = split(endline, context); forindex(var i;context){ - context[i]=split(delimeter,context[i]); + context[i] = split(delimeter,context[i]); } if(size(context)<=1){ die("incorrect csv file <"~path~">: "~size(context)~" line(s)."); } return { - property:context[0], - data:context[1:] + property: context[0], + data: context[1:] }; } \ No newline at end of file diff --git a/std/file.nas b/std/file.nas index 7824c3e..4159561 100644 --- a/std/file.nas +++ b/std/file.nas @@ -1,78 +1,85 @@ # lib file.nas # ValKmjolnir 2022/3/6 -var file={ - SEEK_SET:io.SEEK_SET, - SEEK_CUR:io.SEEK_CUR, - SEEK_END:io.SEEK_END, - new: func(filename,mode="r"){ - if(!io.exists(filename)) - return nil; - var fd=io.open(filename,mode); - return { - close: func(){io.close(fd);}, - read: func(len){ - var buf=mut(""); - io.read(fd,buf,len); - return buf; - }, - write: func(str){return io.write(fd,str);}, - seek: func(pos,whence){return io.seek(fd,pos,whence);}, - tell: func(){return io.tell(fd);}, - readln: func(){return io.readln(fd);}, - stat: func(){return io.stat(filename);}, - eof: func(){return io.eof(fd);} - }; - } -}; -var find_all_files_with_extension=func(path,extensions...){ - var in_vec=func(ext){ - foreach(var i;extensions){ - if(ext==i){ +var SEEK_SET = io.SEEK_SET; + +var SEEK_CUR = io.SEEK_CUR; + +var SEEK_END = io.SEEK_END; + + +var new = func(filename, mode="r"){ + if (!io.exists(filename)) { + return nil; + } + var fd = io.open(filename, mode); + return { + close: func() {io.close(fd);}, + read: func(len) { + var buf = mut(""); + io.read(fd, buf, len); + return buf; + }, + write: func(str) {return io.write(fd, str);}, + seek: func(pos, whence) {return io.seek(fd, pos, whence);}, + tell: func() {return io.tell(fd);}, + readln: func() {return io.readln(fd);}, + stat: func() {return io.stat(filename);}, + eof: func() {return io.eof(fd);} + }; +} + +var find_all_files_with_extension = func(path, extensions...){ + var in_vec = func(ext) { + foreach(var i;extensions) { + if (ext==i){ return 1; } } return 0; } - var res=[]; + var res = []; foreach(var f;find_all_files(path)){ - var tmp=split('.',f); - if(size(tmp)>1 and in_vec(tmp[-1])){ - append(res,f); + var tmp = split('.', f); + if (size(tmp)>1 and in_vec(tmp[-1])) { + append(res, f); } } return res; } -var find_all_files=func(path){ - if(!io.exists(path)) +var find_all_files = func(path){ + if (!io.exists(path)) { return []; - var dd=unix.opendir(path); - var res=[]; - while(var n=unix.readdir(dd)) - if(unix.isfile(path~"/"~n)) - append(res,n); + } + var dd = unix.opendir(path); + var res = []; + while(var n = unix.readdir(dd)) + if(unix.isfile(path~"/"~n)) { + append(res, n); + } unix.closedir(dd); return res; } -var recursive_find_files=func(path){ - if(!io.exists(path)) +var recursive_find_files = func(path) { + if (!io.exists(path)) { return nil; - var dd=unix.opendir(path); - var res={ - dir:path, - files:[] + } + var dd = unix.opendir(path); + var res = { + dir: path, + files: [] }; - while(var n=unix.readdir(dd)){ - if(unix.isfile(path~"/"~n)){ + while(var n = unix.readdir(dd)) { + if (unix.isfile(path~"/"~n)) { append(res.files,n); - }elsif(unix.isdir(path~"/"~n) and n!="." and n!=".."){ - var tmp=recursive_find_files(path~"/"~n); - if(tmp!=nil) + } elsif (unix.isdir(path~"/"~n) and n!="." and n!="..") { + var tmp = recursive_find_files(path~"/"~n); + if (tmp!=nil) { append(res.files,tmp); + } } - } unix.closedir(dd); return res; diff --git a/std/json.nas b/std/json.nas index 145ca01..e2ff68a 100644 --- a/std/json.nas +++ b/std/json.nas @@ -1,59 +1,59 @@ # lib json.nas # 2021 ValKmjolnir -var JSON=func() { - var ( - j_eof, - j_lbrace, - j_rbrace, - j_lbrkt, - j_rbrkt, - j_comma, - j_colon, - j_str, - j_num, - j_id - )=(0,1,2,3,4,5,6,7,8,9); - var j_content=[ - "eof", - "`{`", - "`}`", - "`[`", - "`]`", - "`,`", - "`:`", - "string", - "number", - "identifier" - ]; +var ( + _j_eof, _j_lbrace, _j_rbrace, _j_lbrkt, _j_rbrkt, + _j_comma, _j_colon, _j_str, _j_num, _j_id +) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - var text=''; - var line=1; - var text_size=0; - var ptr=0; - var token={content:'',type:''}; +var _j_content = [ + "eof", + "`{`", + "`}`", + "`[`", + "`]`", + "`,`", + "`:`", + "string", + "number", + "identifier" +]; - var init=func() { - text=''; - line=1; - text_size=0; - ptr=0; - token={content:'',type:''}; +var JSON = func() { + + var text = ""; + var line = 1; + var text_size = 0; + var ptr = 0; + var token = { + content: "", + type: "" + }; + + var init = func() { + text = ""; + line = 1; + text_size = 0; + ptr = 0; + token = { + content: "", + type: "" + }; } - var isnum=func(c) { + var isnum = func(c) { return '0'<=c and c<='9'; } - var isid=func(c) { - var tmp=c[0]; + var isid = func(c) { + var tmp = c[0]; return ('a'[0]<=tmp and tmp<='z'[0]) or - ('A'[0]<=tmp and tmp<='Z'[0]) or - c=='_'; + ('A'[0]<=tmp and tmp<='Z'[0]) or + c=='_'; } - var check=func() { - var c=char(text[ptr]); + var check = func() { + var c = char(text[ptr]); return ( c=='{' or c=='}' or c=='[' or c==']' or @@ -63,151 +63,152 @@ var JSON=func() { ); } - var get=func(str) { + var get = func(str) { init(); - if(!size(str)) { + if (!size(str)) { println("JSON.parse: empty string"); - str="[]"; + str = "[]"; } - text=str; - text_size=size(text); + text = str; + text_size = size(text); return; } - var next=func() { + var next = func() { while(ptr=text_size) { - token.content="eof"; - token.type=j_eof; + token.content = "eof"; + token.type = _j_eof; return; } - var c=char(text[ptr]); - if(c=='{') { - token.content='{'; - token.type=j_lbrace; - } elsif(c=='}') { - token.content='}'; - token.type=j_rbrace; - } elsif(c=='[') { - token.content='['; - token.type=j_lbrkt; - } elsif(c==']') { - token.content=']'; - token.type=j_rbrkt; - } elsif(c==',') { - token.content=','; - token.type=j_comma; - } elsif(c==':') { - token.content=':'; - token.type=j_colon; - } elsif(c=='\"' or c=='\'') { - var strbegin=c; - var s=""; - ptr+=1; + var c = char(text[ptr]); + if (c=='{') { + token.content = '{'; + token.type = _j_lbrace; + } elsif (c=='}') { + token.content = '}'; + token.type = _j_rbrace; + } elsif (c=='[') { + token.content = '['; + token.type = _j_lbrkt; + } elsif (c==']') { + token.content = ']'; + token.type = _j_rbrkt; + } elsif (c==',') { + token.content = ','; + token.type = _j_comma; + } elsif (c==':') { + token.content = ':'; + token.type = _j_colon; + } elsif (c=='\"' or c=='\'') { + var strbegin = c; + var s = ""; + ptr += 1; while(ptr=1000?substr(str(number/1000),0,3)~'k':str(number); + number=number>=1000?substr(str(number/1000),0,4)~'k':str(number); return padding.leftpad(number,6); } diff --git a/test/datalog.nas b/test/datalog.nas index 4530a24..6757856 100644 --- a/test/datalog.nas +++ b/test/datalog.nas @@ -159,4 +159,46 @@ for(var i=10;i<1e6;i*=10) { println("cartesian"); for(var i=100;i<600;i+=100) { cartesian(i); +} + +var person_data = []; +var person = func(name, age) { + append(person_data, [name, age]); + return person_data; +} + +var know_data = []; +var know = func(name_a, name_b) { + append(know_data, [name_a, name_b]); +} + +person("a", 1); +person("b", 2); +person("c", 3); +person("d", 4); + +know("a", "b"); +know("b", "c"); +know("c", "d"); +know("d", "a"); + +# maybe_know(a, b) :- know(a, tmp), know(tmp, b). +var maybe_know_data = []; + +var temp = []; + +foreach(var i; know_data) { + foreach(var j; know_data) { + append(temp, [i[0], i[1], j[0], j[1]]); + } +} + +foreach(var i; temp) { + if (!cmp(i[1], i[2])) { + append(maybe_know_data, [i, i[0], i[3]]); + } +} + +foreach(var res; maybe_know_data) { + println(res[0], " -> ", res[1], " ", res[2]); } \ No newline at end of file diff --git a/test/httptest.nas b/test/httptest.nas index 2fac7f8..be0f6bb 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -48,28 +48,31 @@ http.establish("127.0.0.1",8080); var highlight_style=" "; var html_read_file=func(filename){ diff --git a/tools/repl.nas b/tools/repl.nas new file mode 100644 index 0000000..74a3cc2 --- /dev/null +++ b/tools/repl.nas @@ -0,0 +1,76 @@ +# experimental repl +# 2023/8/19 by ValKmjolnir + +var help = func() { + println(" .help | show help"); + println(" .exit | quit the REPL"); + println(" .quit | quit the REPL"); + println(" .clear | clear the screen"); + println(); +} + +var content = []; +var log_cache = ""; + +println("Nasal: This is experimental REPL"); +println("Tips : \";\" is automatically added at the end of the input line."); +help(); + +var count_bracket = func(line) { + var len = size(line); + var count = 0; + for(var i = 0; i < len; i += 1) { + if (line[i] == "{"[0]) { + count += 1; + } elsif (line[i] == "}"[0]) { + count -= 1; + } + } + return count; +} + +while(1) { + var line = readline(">>> "); + if (!size(line)) { + continue; + } + if (line == ".exit" or line == ".quit") { + break; + } elsif (line == ".help") { + println(); + help(); + continue; + } elsif (line == ".clear") { + print("\ec"); + continue; + } elsif (line[0] == "."[0]) { + println("no such command \"", line, "\", input \".help\" for help\n"); + continue; + } + var in_bracket_level = count_bracket(line); + while(in_bracket_level > 0) { + var temp_line = readline("... "); + in_bracket_level += count_bracket(temp_line); + line ~= temp_line ~ "\n"; + } + + append(content, line); + + var source = ""; + foreach(var i; content) { + source ~= i ~ ";\n"; + } + + io.fout(".temp.nas", source); + var result = system("nasal .temp.nas > .temp.log"); + if (result != 0) { + pop(content); + continue; + } + + var log = io.readfile(".temp.log"); + if (size(log) and size(log) != size(log_cache)) { + println(substr(log, size(log_cache), size(log)), "\n"); + log_cache = log; + } +} \ No newline at end of file