From 4c8e1dfe91ef1ba78350eb9e6f9c3d56e16fdcff Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 29 Oct 2023 00:01:14 +0800 Subject: [PATCH 01/12] :sparkles: add new test file burningship.nas --- doc/pic/burningship.png | Bin 0 -> 165244 bytes std/props.nas | 10 ++++---- test/burningship.nas | 45 ++++++++++++++++++++++++++++++++++ test/feigenbaum.nas | 6 ++--- test/ppmgen.nas | 52 +++++++++++++++++++++------------------- 5 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 doc/pic/burningship.png create mode 100644 test/burningship.nas diff --git a/doc/pic/burningship.png b/doc/pic/burningship.png new file mode 100644 index 0000000000000000000000000000000000000000..63df13c8fe39fa72a8ed58ffef2f98943c1f6f8c GIT binary patch literal 165244 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 literal 0 HcmV?d00001 diff --git a/std/props.nas b/std/props.nas index c9ba7488..5719e37c 100644 --- a/std/props.nas +++ b/std/props.nas @@ -379,7 +379,7 @@ var dump = func { # Don't recurse into aliases, lest we get stuck in a loop if(type != "ALIAS") { var children = node.getChildren(); - foreach(c; children) { dump(name ~ "/", c); } + foreach(var c; children) { dump(name ~ "/", c); } } } @@ -498,7 +498,7 @@ var createNodeObjectsFromHash = func (property_list, namespace = nil) { logprint(LOG_WARN, "createNodeObjectsFromHash: Error, property_list argument is not a hash."); return nil; } - foreach (key; keys(property_list)) { + foreach (var key; keys(property_list)) { namespace[key] = props.getNode(property_list[key],1); } } @@ -743,7 +743,7 @@ var UpdateManager = obj.needs_update = 0; obj.property = {}; obj.is_numeric = {}; - foreach (hashkey; obj.hashkeylist) { + foreach (var hashkey; obj.hashkeylist) { obj.property[hashkey] = props.globals.getNode(hashkey); obj.lastval[hashkey] = nil; # var ty = obj.property[hashkey].getType(); @@ -833,7 +833,7 @@ var UpdateManager = me.needs_update = 0; if (obj != nil or me.lastval == nil) { - foreach (hashkey; me.hashkeylist) { + foreach (var hashkey; me.hashkeylist) { if (me.isnum) { if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) { me.needs_update = 1; @@ -847,7 +847,7 @@ var UpdateManager = } if (me.needs_update) { me.changed(obj); - foreach (hashkey; me.hashkeylist) { + foreach (var hashkey; me.hashkeylist) { me.lastval[hashkey] = obj[hashkey]; } } diff --git a/test/burningship.nas b/test/burningship.nas new file mode 100644 index 00000000..a247522c --- /dev/null +++ b/test/burningship.nas @@ -0,0 +1,45 @@ +import.std.process_bar; + +var ppm = func(filename, width, height, RGB) { + # P3 use ASCII number + # P6 use binary character + var fd = io.open(filename, "wb"); + io.write(fd, "P6\n"~width~" "~height~"\n255\n"); + for(var i = 0; i8) { + break; + } + } + var progress = (i*width+j+1)/(width*height); + if (progress*100-int(progress*100)==0) { + print(bar.bar(progress), " ", progress*100, "% \r"); + } + iter = iter>=25? 255:int(iter/25*255); + var c = char(iter); + return c~c~c; +} + +ppm("burningship.ppm", width, height, f); +println(); diff --git a/test/feigenbaum.nas b/test/feigenbaum.nas index 6f1535d0..b235ae83 100644 --- a/test/feigenbaum.nas +++ b/test/feigenbaum.nas @@ -1,6 +1,6 @@ import.std.process_bar; -var ppm=func(filename, width, height, RGB){ +var ppm = func(filename, width, height, RGB) { # P3 use ASCII number # P6 use binary character var fd = io.open(filename, "wb"); @@ -15,8 +15,8 @@ var ppm=func(filename, width, height, RGB){ var width = 1600; var height = 900; -var bar=(os.platform()=="windows")? - process_bar.bar(front:"sharp",back:"point",sep:"line",length:50): +var bar = (os.platform()=="windows")? + process_bar.bar(front:"sharp", back:"point", sep:"line", length:50): process_bar.high_resolution_bar(50); var RGB = func(h, w) { diff --git a/test/ppmgen.nas b/test/ppmgen.nas index 75e725df..f2000563 100644 --- a/test/ppmgen.nas +++ b/test/ppmgen.nas @@ -1,41 +1,43 @@ import.std.process_bar; -var ppm=func(filename,width,height,RGB){ +var ppm = func(filename, width, height, RGB) { # P3 use ASCII number # P6 use binary character - var fd=io.open(filename,"wb"); - io.write(fd,"P3\n"~width~" "~height~"\n255\n"); - for(var i=0;i4){ +var f = func(i, j) { + var (yMin, yMax, xMin, xMax) = (-1.35, 1.35, -3.3, 1.5); + var (yDel, xDel) = (yMax-yMin, xMax-xMin); + var (y, x) = ((i/height)*yDel+yMin, (j/width)*xDel+xMin); + var (x0, y0) = (x, y); + for(var iter = 0; iter<64; iter += 1) { + var (x1, y1) = ((x0*x0)-(y0*y0)+x, 2*x0*y0+y); + (x0, y0) = (x1, y1); + if ((x0*x0)+(y0*y0)>4) { break; } } - var progress=(i*width+j+1)/(width*height); - if(progress*100-int(progress*100)==0){ - print(bar.bar(progress)," ",progress*100,"% \r"); + var progress = (i*width+j+1)/(width*height); + if (progress*100-int(progress*100)==0) { + print(bar.bar(progress), " ", progress*100, "% \r"); } - iter=iter==25?255:int(iter/25*255); - return iter~" "~iter~" "~iter~" "; + iter = iter>=25? 255:int(iter/25*255); + var c = char(iter); + return c~c~c; } -ppm("a.ppm",width,height,f); + +ppm("mandelbrotset.ppm", width, height, f); println(); From c35ad2e6fa575acb7f9e7dfb36628ea4bd99e8f6 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Mon, 30 Oct 2023 23:20:49 +0800 Subject: [PATCH 02/12] :sparkles: add stdin, stdout, stderr in io module --- src/io_lib.cpp | 25 ++++++++++++++++++++++--- src/io_lib.h | 3 +++ src/nasal_codegen.cpp | 4 ++-- std/io.nas | 6 ++++++ 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/io_lib.cpp b/src/io_lib.cpp index 848ba136..5af223ac 100644 --- a/src/io_lib.cpp +++ b/src/io_lib.cpp @@ -5,9 +5,6 @@ namespace nasal { const auto file_type_name = "file"; void filehandle_destructor(void* ptr) { - if (static_cast(ptr)==stdin) { - return; - } fclose(static_cast(ptr)); } @@ -207,6 +204,25 @@ var builtin_eof(context* ctx, gc* ngc) { )); } +var builtin_stdin(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_obj); + file_descriptor.ghost().set(file_type_name, nullptr, stdin); + return file_descriptor; +} + +var builtin_stdout(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_obj); + file_descriptor.ghost().set(file_type_name, nullptr, stdout); + return file_descriptor; +} + +var builtin_stderr(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_obj); + file_descriptor.ghost().set(file_type_name, nullptr, stderr); + return file_descriptor; +} + + nasal_builtin_table io_lib_native[] = { {"__readfile", builtin_readfile}, {"__fout", builtin_fout}, @@ -220,6 +236,9 @@ nasal_builtin_table io_lib_native[] = { {"__readln", builtin_readln}, {"__stat", builtin_stat}, {"__eof", builtin_eof}, + {"__stdin", builtin_stdin}, + {"__stdout", builtin_stdout}, + {"__stderr", builtin_stderr}, {nullptr, nullptr} }; diff --git a/src/io_lib.h b/src/io_lib.h index a4dc1364..e1f4e97c 100644 --- a/src/io_lib.h +++ b/src/io_lib.h @@ -32,6 +32,9 @@ var builtin_tell(context*, gc*); var builtin_readln(context*, gc*); var builtin_stat(context*, gc*); var builtin_eof(context*, gc*); +var builtin_stdin(context*, gc*); +var builtin_stdout(context*, gc*); +var builtin_stderr(context*, gc*); extern nasal_builtin_table io_lib_native[]; diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index bbb7971e..04443e66 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1168,7 +1168,7 @@ void codegen::block_gen(code_block* node) { switch(tmp->get_type()) { case expr_type::ast_null: break; case expr_type::ast_id: - if (need_repl_output) { + if (need_repl_output && local.empty()) { repl_mode_info_output_gen(tmp); } else { check_id_exist((identifier*)tmp); @@ -1178,7 +1178,7 @@ void codegen::block_gen(code_block* node) { case expr_type::ast_num: case expr_type::ast_str: case expr_type::ast_bool: - if (need_repl_output) { + if (need_repl_output && local.empty()) { repl_mode_info_output_gen(tmp); } break; diff --git a/std/io.nas b/std/io.nas index cfca4ad6..d2a950a8 100644 --- a/std/io.nas +++ b/std/io.nas @@ -66,3 +66,9 @@ var stat = func(filename) { var eof = func(filehandle) { return __eof(filehandle); } + +var stdin = func() { return __stdin; }(); + +var stdout = func() { return __stdout;}(); + +var stderr = func() { return __stderr; }(); From 88e4b86ccb3c3da535651e4f124d1f4a83a10be3 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Tue, 31 Oct 2023 00:36:49 +0800 Subject: [PATCH 03/12] :sparkles: add new module import syntax rule --- src/ast_dumper.cpp | 14 ++++++++++++++ src/ast_dumper.h | 1 + src/ast_visitor.cpp | 7 +++++++ src/ast_visitor.h | 1 + src/nasal_ast.cpp | 10 ++++++++++ src/nasal_ast.h | 15 +++++++++++++++ src/nasal_codegen.cpp | 1 + src/nasal_lexer.h | 2 ++ src/nasal_parse.cpp | 13 +++++++++++++ src/nasal_parse.h | 2 ++ 10 files changed, 66 insertions(+) diff --git a/src/ast_dumper.cpp b/src/ast_dumper.cpp index 525d42a9..900fdd8e 100644 --- a/src/ast_dumper.cpp +++ b/src/ast_dumper.cpp @@ -4,6 +4,20 @@ namespace nasal { +bool ast_dumper::visit_use_stmt(use_stmt* node) { + dump_indent(); + std::cout << "use" << format_location(node->get_location()); + push_indent(); + for(auto i : node->get_path()) { + if (i==node->get_path().back()) { + set_last(); + } + i->accept(this); + } + pop_indent(); + return true; +} + bool ast_dumper::visit_null_expr(null_expr* node) { dump_indent(); std::cout << "null" << format_location(node->get_location()); diff --git a/src/ast_dumper.h b/src/ast_dumper.h index 13856986..b5014d5d 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -41,6 +41,7 @@ class ast_dumper:public ast_visitor { } public: + bool visit_use_stmt(use_stmt*) override; bool visit_null_expr(null_expr*) override; bool visit_nil_expr(nil_expr*) override; bool visit_number_literal(number_literal*) override; diff --git a/src/ast_visitor.cpp b/src/ast_visitor.cpp index 677c0433..f438b4b1 100644 --- a/src/ast_visitor.cpp +++ b/src/ast_visitor.cpp @@ -7,6 +7,13 @@ bool ast_visitor::visit_expr(expr* node) { return true; } +bool ast_visitor::visit_use_stmt(use_stmt* node) { + for(auto i : node->get_path()) { + i->accept(this); + } + return true; +} + bool ast_visitor::visit_call(call* node) { node->accept(this); return true; diff --git a/src/ast_visitor.h b/src/ast_visitor.h index 62f4dff3..4235541d 100644 --- a/src/ast_visitor.h +++ b/src/ast_visitor.h @@ -7,6 +7,7 @@ namespace nasal { class ast_visitor { public: virtual bool visit_expr(expr*); + virtual bool visit_use_stmt(use_stmt*); virtual bool visit_call(call*); virtual bool visit_null_expr(null_expr*); virtual bool visit_nil_expr(nil_expr*); diff --git a/src/nasal_ast.cpp b/src/nasal_ast.cpp index a1c88597..b1c27e14 100644 --- a/src/nasal_ast.cpp +++ b/src/nasal_ast.cpp @@ -7,6 +7,16 @@ void expr::accept(ast_visitor* visitor) { visitor->visit_expr(this); } +use_stmt::~use_stmt() { + for(auto i : path) { + delete i; + } +} + +void use_stmt::accept(ast_visitor* visitor) { + visitor->visit_use_stmt(this); +} + void call::accept(ast_visitor* visitor) { visitor->visit_call(this); } diff --git a/src/nasal_ast.h b/src/nasal_ast.h index 4d8c6c17..eb75e168 100644 --- a/src/nasal_ast.h +++ b/src/nasal_ast.h @@ -10,6 +10,7 @@ namespace nasal { enum class expr_type:u32 { ast_null = 0, // null node + ast_use, // use statement ast_block, // code block ast_nil, // nil keyword ast_num, // number, basic value type @@ -46,6 +47,7 @@ enum class expr_type:u32 { }; class ast_visitor; +class identifier; class hash_pair; class parameter; class slice_vector; @@ -77,6 +79,19 @@ class expr { virtual void accept(ast_visitor*); }; +class use_stmt: public expr { +private: + std::vector path; + +public: + use_stmt(const span& location): + expr(location, expr_type::ast_use) {} + ~use_stmt() override; + void accept(ast_visitor*) override; + void add_path(identifier* node) {path.push_back(node);} + const auto& get_path() const {return path;} +}; + class call: public expr { public: call(const span& location, expr_type node_type): diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 04443e66..37d4906a 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1166,6 +1166,7 @@ void codegen::repl_mode_info_output_gen(expr* node) { void codegen::block_gen(code_block* node) { for(auto tmp : node->get_expressions()) { switch(tmp->get_type()) { + case expr_type::ast_use: break; case expr_type::ast_null: break; case expr_type::ast_id: if (need_repl_output && local.empty()) { diff --git a/src/nasal_lexer.h b/src/nasal_lexer.h index 2862a571..99c200d5 100644 --- a/src/nasal_lexer.h +++ b/src/nasal_lexer.h @@ -28,6 +28,7 @@ enum class tok:u32 { id, // identifier tktrue, // keyword true tkfalse, // keyword false + use, // keyword use rfor, // loop keyword for forindex, // loop keyword forindex foreach, // loop keyword foreach @@ -103,6 +104,7 @@ class lexer { std::vector toks; const std::unordered_map typetbl { + {"use" ,tok::use }, {"true" ,tok::tktrue }, {"false" ,tok::tkfalse }, {"for" ,tok::rfor }, diff --git a/src/nasal_parse.cpp b/src/nasal_parse.cpp index 71111afb..60d4dbc7 100644 --- a/src/nasal_parse.cpp +++ b/src/nasal_parse.cpp @@ -197,6 +197,18 @@ void parse::update_location(expr* node) { node->update_location(toks[ptr-1].loc); } +use_stmt* parse::use_stmt_gen() { + auto node = new use_stmt(toks[ptr].loc); + match(tok::use); + node->add_path(id()); + while(lookahead(tok::dot)) { + match(tok::dot); + node->add_path(id()); + } + update_location(node); + return node; +} + null_expr* parse::null() { return new null_expr(toks[ptr].loc); } @@ -355,6 +367,7 @@ expr* parse::expression() { die(thisspan, "must use return in functions"); } switch(type) { + case tok::use: return use_stmt_gen(); case tok::tknil: case tok::num: case tok::str: diff --git a/src/nasal_parse.h b/src/nasal_parse.h index 1bcf5080..472b3427 100644 --- a/src/nasal_parse.h +++ b/src/nasal_parse.h @@ -24,6 +24,7 @@ class parse { private: const std::unordered_map tokname { + {tok::use ,"use" }, {tok::rfor ,"for" }, {tok::forindex,"forindex"}, {tok::foreach ,"foreach" }, @@ -92,6 +93,7 @@ class parse { void update_location(expr*); private: + use_stmt* use_stmt_gen(); null_expr* null(); nil_expr* nil(); number_literal* num(); From 4757dd220ae1ec134541e7014422090bf0bb2c45 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Tue, 31 Oct 2023 19:53:01 +0800 Subject: [PATCH 04/12] :memo: add check for use statement --- src/nasal_codegen.cpp | 8 +++++++- tools/push.nas | 6 ++++-- tools/search_file.nas | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 37d4906a..a3c5afcc 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1166,7 +1166,13 @@ void codegen::repl_mode_info_output_gen(expr* node) { void codegen::block_gen(code_block* node) { for(auto tmp : node->get_expressions()) { switch(tmp->get_type()) { - case expr_type::ast_use: break; + case expr_type::ast_use: + if (!local.empty()) { + die("module import is not allowed here.", + tmp->get_location() + ); + } + break; case expr_type::ast_null: break; case expr_type::ast_id: if (need_repl_output && local.empty()) { diff --git a/tools/push.nas b/tools/push.nas index 13918d83..51c45d3c 100644 --- a/tools/push.nas +++ b/tools/push.nas @@ -1,7 +1,9 @@ -println("[",os.time(),"] auto push, please wait..."); +println("[",os.time(),"] (=.=) auto push, please wait..."); while(system("git push")!=0) { - println("[",os.time(),"] failed to push, retrying..."); + println("[",os.time(),"] (ToT) failed to push, retrying..."); unix.sleep(0.5); } + +println("[",os.time(),"] (^o^) auto push complete."); diff --git a/tools/search_file.nas b/tools/search_file.nas index 53290259..bce4c1e1 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -1,7 +1,17 @@ import.std.file; -if (size(arg)!=1) { +var tips = func() { + println("usage:"); + println(" nasal search_file.nas [key]"); +} + +if (size(arg)<1) { println("need a key string to search files."); + tips(); + exit(-1); +} else if (size(arg)>1) { + println("too many arguments."); + tips(); exit(-1); } @@ -12,7 +22,7 @@ var do_flat = func(vec) { var bfs = [vec]; while(size(bfs)) { var d = pop(bfs); - foreach(var f;d.files) { + foreach(var f; d.files) { if (ishash(f)) { append(bfs,f); continue; @@ -20,12 +30,12 @@ var do_flat = func(vec) { append(flat, d.dir~"/"~f); } } - sort(flat, func(a,b){return cmp(a,b)<0}); + sort(flat, func(a, b){return cmp(a, b)<0}); return flat; } var count = 0; -foreach(var f;do_flat(file.recursive_find_files("."))) { +foreach(var f; do_flat(file.recursive_find_files("."))) { var pos = find(needle, f); if (pos == -1) { continue; @@ -33,7 +43,22 @@ foreach(var f;do_flat(file.recursive_find_files("."))) { count += 1; var begin = substr(f, 0, pos); var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f)); - println(begin, "\e[95;1m", needle, "\e[0m", end); + var file_size = fstat(f).st_size; + var unit = "b"; + if (file_size>1024) { + file_size/=1024; + unit = "kb"; + } + if (file_size>1024) { + file_size/=1024; + unit = "mb"; + } + if (file_size>1024) { + file_size/=1024; + unit = "gb"; + } + file_size = int(file_size); + println(begin, "\e[95;1m", needle, "\e[0m", end, " | ", file_size, " ", unit); } println("\n", count, " result(s)."); \ No newline at end of file From ccbe341dc5c85d9fe9efcc5f150f3c696cdb0fef Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Wed, 1 Nov 2023 00:37:02 +0800 Subject: [PATCH 05/12] :sparkles: add import logic for use statement --- module/libfib.nas | 2 +- module/libkey.nas | 2 +- module/libmat.nas | 2 +- module/libsock.nas | 2 +- src/nasal_ast.h | 2 +- src/nasal_codegen.cpp | 4 +- src/nasal_import.cpp | 151 ++++++++++++++++------------------ src/nasal_import.h | 9 +- std/fg_env.nas | 2 +- std/lib.nas | 14 ++-- test/ascii-art.nas | 4 +- test/auto_crash.nas | 2 +- test/bfs.nas | 2 +- test/bp.nas | 2 +- test/burningship.nas | 2 +- test/calc.nas | 4 +- test/console3D.nas | 4 +- test/coroutine.nas | 4 +- test/datalog.nas | 4 +- test/donuts.nas | 2 +- test/feigenbaum.nas | 2 +- test/flush_screen.nas | 2 +- test/gc_test.nas | 2 +- test/hexdump.nas | 4 +- test/httptest.nas | 2 +- test/json.nas | 4 +- test/jsonrpc.nas | 6 +- test/life.nas | 4 +- test/md5compare.nas | 6 +- test/module_test.nas | 4 +- test/occupation.nas | 6 +- test/ppmgen.nas | 2 +- test/scalar.nas | 2 +- test/self_ref_module/a.nas | 2 +- test/self_ref_module/b.nas | 2 +- test/self_ref_module/c.nas | 2 +- test/self_ref_module/main.nas | 6 +- test/snake.nas | 6 +- test/tetris.nas | 4 +- test/watchdog.nas | 2 +- test/word_collector.nas | 2 +- tools/search_file.nas | 2 +- 42 files changed, 145 insertions(+), 149 deletions(-) diff --git a/module/libfib.nas b/module/libfib.nas index 355a3570..a42ed9b8 100644 --- a/module/libfib.nas +++ b/module/libfib.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); diff --git a/module/libkey.nas b/module/libkey.nas index a494c22b..829e99ce 100644 --- a/module/libkey.nas +++ b/module/libkey.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var ( kbhit, diff --git a/module/libmat.nas b/module/libmat.nas index cd069289..be7ef605 100644 --- a/module/libmat.nas +++ b/module/libmat.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); diff --git a/module/libsock.nas b/module/libsock.nas index 15a57c19..fa6b3568 100644 --- a/module/libsock.nas +++ b/module/libsock.nas @@ -1,4 +1,4 @@ -import.std.dylib; +use std.dylib; var socket=func(){ var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so")); diff --git a/src/nasal_ast.h b/src/nasal_ast.h index eb75e168..480e304d 100644 --- a/src/nasal_ast.h +++ b/src/nasal_ast.h @@ -136,7 +136,7 @@ class string_literal: public expr { string_literal(const span& location, const std::string& str): expr(location, expr_type::ast_str), content(str) {} ~string_literal() override = default; - const std::string get_content() const {return content;} + const std::string& get_content() const {return content;} void accept(ast_visitor*) override; }; diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index a3c5afcc..a1018b44 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -307,7 +307,7 @@ void codegen::func_gen(function* node) { void codegen::call_gen(call_expr* node) { calc_gen(node->get_first()); - if (code.back().op==op_callb) { + if (code.size() && code.back().op==op_callb) { return; } for(auto i : node->get_calls()) { @@ -349,6 +349,8 @@ void codegen::call_id(identifier* node) { return; } die("undefined symbol \"" + name + "\"", node->get_location()); + // generation failed, put a push nil operand here to fill the space + emit(op_pnil, index, node->get_location()); } void codegen::call_hash_gen(call_hash* node) { diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index f5b31f03..f00abdbf 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -24,19 +24,21 @@ linker::linker(): } } -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(); +std::string linker::get_path(expr* node) { + if (node->get_type()==expr_type::ast_use) { + auto file_relative_path = std::string("."); + for(auto i : reinterpret_cast(node)->get_path()) { + file_relative_path += (is_windows()? "\\":"/") +i->get_name(); + } + return file_relative_path + ".nas"; } - return fpath + ".nas"; + auto call_node = reinterpret_cast(node); + auto tmp = reinterpret_cast(call_node->get_calls()[0]); + auto content = reinterpret_cast(tmp->get_argument()[0]); + return content->get_content(); } -std::string linker::find_file( +std::string linker::find_real_file_path( const std::string& filename, const span& location) { // first add file name itself into the file path std::vector fpath = {filename}; @@ -56,65 +58,58 @@ std::string linker::find_file( // we will find lib.nas in nasal std directory if (filename=="lib.nas") { return is_windows()? - find_file("std\\lib.nas", location): - find_file("std/lib.nas", location); + find_real_file_path("std\\lib.nas", location): + find_real_file_path("std/lib.nas", location); } if (!show_path) { err.err("link", "in <" + location.file + ">: " + "cannot find file <" + filename + ">, " + - "use <-d> to get detail search path"); + "use <-d> to get detail search path" + ); return ""; } - std::string paths = ""; + auto paths = std::string(""); for(const auto& i : fpath) { paths += " -> " + i + "\n"; } err.err("link", "in <" + location.file + ">: " + - "cannot find file <" + filename + "> in these paths:\n" + paths); + "cannot find file <" + filename + "> in these paths:\n" + paths + ); return ""; } bool linker::import_check(expr* node) { + if (node->get_type()==expr_type::ast_use) { + return true; + } /* call |_id:import - |_callh:std - |_callh:file + |_call_func + |_string:'filename' */ 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) { + auto tmp = reinterpret_cast(node); + auto first_expr = tmp->get_first(); + if (first_expr->get_type()!=expr_type::ast_id) { return false; } - if (((identifier*)tmp->get_first())->get_name()!="import") { + if (reinterpret_cast(first_expr)->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; } @@ -139,17 +134,6 @@ bool linker::exist(const std::string& file) { return false; } -u16 linker::find(const std::string& file) { - for(usize i = 0; i(i); - } - } - std::cerr << "unreachable: using this method incorrectly\n"; - std::exit(-1); - return UINT16_MAX; -} - bool linker::check_self_import(const std::string& file) { for(const auto& i : module_load_stack) { if (file==i) { @@ -176,24 +160,26 @@ void linker::link(code_block* new_tree_root, code_block* old_tree_root) { old_tree_root->get_expressions().clear(); } -code_block* linker::import_regular_file(call_expr* node) { - lexer lex; - parse par; +code_block* linker::import_regular_file(expr* node) { // get filename auto filename = get_path(node); - // clear this node - for(auto i : node->get_calls()) { - delete i; + + // clear import("xxx/xxx.nas") node + if (node->get_type()!=expr_type::ast_use) { + auto cast_node = reinterpret_cast(node); + for(auto i : cast_node->get_calls()) { + delete i; + } + cast_node->get_calls().clear(); + const auto& location = cast_node->get_first()->get_location(); + delete cast_node->get_first(); + cast_node->set_first(new nil_expr(location)); + // this will make node to call_expr(nil), + // will not be optimized when generating bytecodes } - node->get_calls().clear(); - auto location = node->get_first()->get_location(); - delete node->get_first(); - node->set_first(new nil_expr(location)); - // this will make node to call_expr(nil), - // will not be optimized when generating bytecodes // avoid infinite loading loop - filename = find_file(filename, node->get_location()); + filename = find_real_file_path(filename, node->get_location()); if (!filename.length()) { return new code_block({0, 0, 0, 0, filename}); } @@ -208,27 +194,27 @@ code_block* linker::import_regular_file(call_expr* node) { module_load_stack.push_back(filename); // start importing... - if (lex.scan(filename).geterr()) { + lexer nasal_lexer; + parse nasal_parser; + if (nasal_lexer.scan(filename).geterr()) { err.err("link", "error occurred when analysing <" + filename + ">"); return new code_block({0, 0, 0, 0, filename}); } - if (par.compile(lex).geterr()) { + if (nasal_parser.compile(nasal_lexer).geterr()) { err.err("link", "error occurred when analysing <" + filename + ">"); return new code_block({0, 0, 0, 0, filename}); } - - auto parse_result = par.swap(nullptr); + // swap result out + auto parse_result = nasal_parser.swap(nullptr); // check if parse result has 'import' - auto result = load(parse_result, find(filename)); + auto result = load(parse_result, filename); module_load_stack.pop_back(); return result; } code_block* linker::import_nasal_lib() { - lexer lex; - parse par; - auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]}); + auto filename = find_real_file_path("lib.nas", {0, 0, 0, 0, files[0]}); if (!filename.length()) { return new code_block({0, 0, 0, 0, filename}); } @@ -240,22 +226,24 @@ code_block* linker::import_nasal_lib() { } // start importing... - if (lex.scan(filename).geterr()) { + lexer nasal_lexer; + parse nasal_parser; + if (nasal_lexer.scan(filename).geterr()) { err.err("link", "error occurred when analysing library <" + filename + ">" ); return new code_block({0, 0, 0, 0, filename}); } - if (par.compile(lex).geterr()) { + if (nasal_parser.compile(nasal_lexer).geterr()) { err.err("link", "error occurred when analysing library <" + filename + ">" ); return new code_block({0, 0, 0, 0, filename}); } - - auto parse_result = par.swap(nullptr); + // swap result out + auto parse_result = nasal_parser.swap(nullptr); // check if library has 'import' (in fact it should not) - return load(parse_result, find(filename)); + return load(parse_result, filename); } std::string linker::generate_module_name(const std::string& file_path) { @@ -314,6 +302,12 @@ std::string linker::generate_module_name(const std::string& file_path) { "will not be easily accessed." ); } + if (module_name.length() && module_name.find("-")!=std::string::npos) { + err.warn("link", + "get module <" + module_name + "> from <" + file_path + ">, " + + "will not be easily accessed." + ); + } return module_name; } @@ -353,8 +347,8 @@ definition_expr* linker::generate_module_definition(code_block* block) { return def; } -code_block* linker::load(code_block* program_root, u16 fileindex) { - auto tree = new code_block({0, 0, 0, 0, files[fileindex]}); +code_block* linker::load(code_block* program_root, const std::string& filename) { + auto tree = new code_block({0, 0, 0, 0, filename}); // load library, this ast will be linked with root directly // so no extra namespace is generated if (!lib_loaded) { @@ -370,12 +364,13 @@ code_block* linker::load(code_block* program_root, u16 fileindex) { if (!import_check(import_ast_node)) { break; } - auto module_code_block = import_regular_file((call_expr*)import_ast_node); + auto module_code_block = import_regular_file(import_ast_node); + // this location should not be a reference, may cause use after free! + const auto location = import_ast_node->get_location(); // after importing the regular file as module, delete this node - const auto loc = import_ast_node->get_location(); delete import_ast_node; // and replace the node with null_expr node - import_ast_node = new null_expr(loc); + import_ast_node = new null_expr(location); // then we generate a function warping the code block, // and export the necessary global symbols in this code block // by generate a return statement, with a hashmap return value @@ -397,7 +392,7 @@ const error& linker::link( // 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 new_tree_root = load(parse.tree(), self); auto old_tree_root = parse.swap(new_tree_root); delete old_tree_root; return err; diff --git a/src/nasal_import.h b/src/nasal_import.h index fc53fe9f..c09eabeb 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -36,18 +36,17 @@ class linker { private: bool import_check(expr*); bool exist(const std::string&); - u16 find(const std::string&); bool check_self_import(const std::string&); std::string generate_self_import_path(const std::string&); void link(code_block*, code_block*); - std::string get_path(call_expr*); - std::string find_file(const std::string&, const span&); - code_block* import_regular_file(call_expr*); + std::string get_path(expr*); + std::string find_real_file_path(const std::string&, const span&); + code_block* import_regular_file(expr*); code_block* import_nasal_lib(); std::string generate_module_name(const std::string&); return_expr* generate_module_return(code_block*); definition_expr* generate_module_definition(code_block*); - code_block* load(code_block*, u16); + code_block* load(code_block*, const std::string&); public: linker(); diff --git a/std/fg_env.nas b/std/fg_env.nas index 47d435d0..81bfb929 100644 --- a/std/fg_env.nas +++ b/std/fg_env.nas @@ -1,6 +1,6 @@ # flightgear developer environments simulator (beta) # ValKmjolnir 2022 -import.std.runtime; +use std.runtime; println("-------------------------------------------------------------"); println(" FlightGear simulated-env for developers project, since 2019"); diff --git a/std/lib.nas b/std/lib.nas index 95cb4a60..ce2afeff 100644 --- a/std/lib.nas +++ b/std/lib.nas @@ -1,13 +1,13 @@ # lib.nas # 2019 ValKmjolnir -import.std.coroutine; -import.std.math; -import.std.string; -import.std.io; -import.std.os; -import.std.bits; -import.std.unix; +use std.coroutine; +use std.math; +use std.string; +use std.io; +use std.os; +use std.bits; +use std.unix; # print is used to print all things in nasal, try and see how it works. # this function uses std::cout to output logs. diff --git a/test/ascii-art.nas b/test/ascii-art.nas index 9e8335f9..2f04b89c 100644 --- a/test/ascii-art.nas +++ b/test/ascii-art.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.process_bar; +use std.padding; +use std.process_bar; var char_ttf=[ [" "," "," "," "," "," "], diff --git a/test/auto_crash.nas b/test/auto_crash.nas index b277c4b3..37003957 100644 --- a/test/auto_crash.nas +++ b/test/auto_crash.nas @@ -1,5 +1,5 @@ # Road check and auto pilot by ValKmjolnir -import.std.fg_env; +use std.fg_env; var props = fg_env.props; var geodinfo = fg_env.geodinfo; diff --git a/test/bfs.nas b/test/bfs.nas index 4568d6bf..1520e7a0 100644 --- a/test/bfs.nas +++ b/test/bfs.nas @@ -1,4 +1,4 @@ -import.std.queue; +use std.queue; rand(time(0)); var pixel=[' ','#','.','*']; diff --git a/test/bp.nas b/test/bp.nas index f685a68c..88dadf97 100644 --- a/test/bp.nas +++ b/test/bp.nas @@ -1,4 +1,4 @@ -import.std.mat; +use std.mat; rand(time(0)); diff --git a/test/burningship.nas b/test/burningship.nas index a247522c..70750598 100644 --- a/test/burningship.nas +++ b/test/burningship.nas @@ -1,4 +1,4 @@ -import.std.process_bar; +use std.process_bar; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number diff --git a/test/calc.nas b/test/calc.nas index 440701ef..516a2db8 100644 --- a/test/calc.nas +++ b/test/calc.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.file; +use std.padding; +use std.file; var source=file.find_all_files_with_extension("./src","cpp","h"); sort(source,func(a,b){return cmp(a,b)<0}); diff --git a/test/console3D.nas b/test/console3D.nas index ccc33328..9226d689 100644 --- a/test/console3D.nas +++ b/test/console3D.nas @@ -21,8 +21,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import.module.libmat; -import.std.runtime; +use module.libmat; +use std.runtime; func(){ # allocate more spaces diff --git a/test/coroutine.nas b/test/coroutine.nas index 4d6c09aa..3eeacbbf 100644 --- a/test/coroutine.nas +++ b/test/coroutine.nas @@ -1,7 +1,7 @@ # coroutine.nas by ValKmjolnir # 2022/5/19 -import.std.process_bar; -import.std.padding; +use std.process_bar; +use std.padding; if(os.platform()=="windows"){ system("chcp 65001"); diff --git a/test/datalog.nas b/test/datalog.nas index 6757856c..eab13efe 100644 --- a/test/datalog.nas +++ b/test/datalog.nas @@ -1,5 +1,5 @@ -import.std.padding; -import.std.process_bar; +use std.padding; +use std.process_bar; var mess=func(vec) { srand(); diff --git a/test/donuts.nas b/test/donuts.nas index 5fe5c27e..58dac114 100644 --- a/test/donuts.nas +++ b/test/donuts.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var mod = math.mod; diff --git a/test/feigenbaum.nas b/test/feigenbaum.nas index b235ae83..e7aab39f 100644 --- a/test/feigenbaum.nas +++ b/test/feigenbaum.nas @@ -1,4 +1,4 @@ -import.std.process_bar; +use std.process_bar; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number diff --git a/test/flush_screen.nas b/test/flush_screen.nas index 2c241138..499797a9 100644 --- a/test/flush_screen.nas +++ b/test/flush_screen.nas @@ -1,4 +1,4 @@ -import.module.libkey; +use module.libkey; srand(); diff --git a/test/gc_test.nas b/test/gc_test.nas index b7e223c5..fd3f28ec 100644 --- a/test/gc_test.nas +++ b/test/gc_test.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var test_func = func(test_processes...) { var test_process_total = maketimestamp(); diff --git a/test/hexdump.nas b/test/hexdump.nas index dcfaafca..d77c15e0 100644 --- a/test/hexdump.nas +++ b/test/hexdump.nas @@ -1,7 +1,7 @@ # hexdump.nas by ValKmjolnir # 2021/8/13 -import.std.file; -import.std.runtime; +use std.file; +use std.runtime; # init var hex=func(){ diff --git a/test/httptest.nas b/test/httptest.nas index be0f6bb8..3b8fcaf8 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -1,4 +1,4 @@ -import.module.libsock; +use module.libsock; var socket = libsock.socket; diff --git a/test/json.nas b/test/json.nas index d5be9342..fbc053dc 100644 --- a/test/json.nas +++ b/test/json.nas @@ -1,5 +1,5 @@ -import.std.json; -import.std.process_bar; +use std.json; +use std.process_bar; var ss = json.stringify({ vec:[0,1,2], diff --git a/test/jsonrpc.nas b/test/jsonrpc.nas index f976fc5f..56b44847 100644 --- a/test/jsonrpc.nas +++ b/test/jsonrpc.nas @@ -1,6 +1,6 @@ -import.module.libsock; -import.std.json; -import.std.runtime; +use module.libsock; +use std.json; +use std.runtime; var socket = libsock.socket; diff --git a/test/life.nas b/test/life.nas index 9e292988..3b6fb0ea 100644 --- a/test/life.nas +++ b/test/life.nas @@ -1,5 +1,5 @@ -import.std.process_bar; -import.std.runtime; +use std.process_bar; +use std.runtime; var new_map=func(width,height){ var tmp=[]; diff --git a/test/md5compare.nas b/test/md5compare.nas index 7ffca1be..29cd027a 100644 --- a/test/md5compare.nas +++ b/test/md5compare.nas @@ -1,6 +1,6 @@ -import.test.md5_self; -import.std.process_bar; -import.std.file; +use test.md5_self; +use std.process_bar; +use std.file; srand(); diff --git a/test/module_test.nas b/test/module_test.nas index da9678c5..109ceda8 100644 --- a/test/module_test.nas +++ b/test/module_test.nas @@ -1,5 +1,5 @@ -import.std.dylib; -import.module.libfib; +use std.dylib; +use module.libfib; println(keys(libfib)); libfib.test_ghost(); diff --git a/test/occupation.nas b/test/occupation.nas index 9ea43029..99e5aa03 100644 --- a/test/occupation.nas +++ b/test/occupation.nas @@ -1,6 +1,6 @@ -import.std.process_bar; -import.module.libkey; -import.std.runtime; +use std.process_bar; +use module.libkey; +use std.runtime; var is_windows_platform=os.platform()=="windows"; var is_macos_platform=os.platform()=="macOS"; diff --git a/test/ppmgen.nas b/test/ppmgen.nas index f2000563..857b5e49 100644 --- a/test/ppmgen.nas +++ b/test/ppmgen.nas @@ -1,4 +1,4 @@ -import.std.process_bar; +use std.process_bar; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number diff --git a/test/scalar.nas b/test/scalar.nas index 3e6039f5..e2c9feba 100644 --- a/test/scalar.nas +++ b/test/scalar.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; # basic type nil; diff --git a/test/self_ref_module/a.nas b/test/self_ref_module/a.nas index d84ea337..a25d913f 100644 --- a/test/self_ref_module/a.nas +++ b/test/self_ref_module/a.nas @@ -1,5 +1,5 @@ # this will cause error -# import.b; +# use b; println("init a"); var a = "hello"; \ No newline at end of file diff --git a/test/self_ref_module/b.nas b/test/self_ref_module/b.nas index dcace502..a8a66342 100644 --- a/test/self_ref_module/b.nas +++ b/test/self_ref_module/b.nas @@ -1,3 +1,3 @@ -import.a; +use a; println("init b"); \ No newline at end of file diff --git a/test/self_ref_module/c.nas b/test/self_ref_module/c.nas index cb81e5d3..039b2f73 100644 --- a/test/self_ref_module/c.nas +++ b/test/self_ref_module/c.nas @@ -1,3 +1,3 @@ -import.b; +use b; println("init c"); \ No newline at end of file diff --git a/test/self_ref_module/main.nas b/test/self_ref_module/main.nas index 36b0a1a7..da69dfff 100644 --- a/test/self_ref_module/main.nas +++ b/test/self_ref_module/main.nas @@ -1,6 +1,6 @@ -import.c; -import.a; -import.b; +use c; +use a; +use b; println(a); println(b); diff --git a/test/snake.nas b/test/snake.nas index 08b82a90..27babbb6 100644 --- a/test/snake.nas +++ b/test/snake.nas @@ -1,6 +1,6 @@ -import.module.libkey; -import.std.list; -import.std.runtime; +use module.libkey; +use std.list; +use std.runtime; var game=func(x,y){ rand(time(0)); diff --git a/test/tetris.nas b/test/tetris.nas index e2e1cf46..0d8ccc95 100644 --- a/test/tetris.nas +++ b/test/tetris.nas @@ -1,5 +1,5 @@ -import.module.libkey; -import.std.runtime; +use module.libkey; +use std.runtime; var color=[ "\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m", diff --git a/test/watchdog.nas b/test/watchdog.nas index a27813c8..f89c59ae 100644 --- a/test/watchdog.nas +++ b/test/watchdog.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var os_time=func(){ return "[\e[33;1m"~os.time()~"\e[0m] "; diff --git a/test/word_collector.nas b/test/word_collector.nas index 87e7151c..32d69162 100644 --- a/test/word_collector.nas +++ b/test/word_collector.nas @@ -1,4 +1,4 @@ -import.std.runtime; +use std.runtime; var to_lower=func(s){ var tmp=""; diff --git a/tools/search_file.nas b/tools/search_file.nas index bce4c1e1..72e52d24 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -1,4 +1,4 @@ -import.std.file; +use std.file; var tips = func() { println("usage:"); From 49f8cefca008ec2312c3fec284e247d1ae4e9fd7 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Wed, 1 Nov 2023 23:49:15 +0800 Subject: [PATCH 06/12] :zap: avoid repeatedly importing the same modules --- src/ast_dumper.h | 1 + src/nasal_codegen.cpp | 8 +++ src/nasal_import.cpp | 138 ++++++++++++++++++++++-------------------- src/nasal_import.h | 18 +++--- std/stack.nas | 23 +++---- tools/search_file.nas | 31 +++++++--- 6 files changed, 126 insertions(+), 93 deletions(-) diff --git a/src/ast_dumper.h b/src/ast_dumper.h index b5014d5d..e20f5c4d 100644 --- a/src/ast_dumper.h +++ b/src/ast_dumper.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace nasal { diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index a1018b44..bf4b3321 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -1166,13 +1166,21 @@ void codegen::repl_mode_info_output_gen(expr* node) { } void codegen::block_gen(code_block* node) { + bool is_use_statement = true; for(auto tmp : node->get_expressions()) { + if (tmp->get_type()!=expr_type::ast_use) { + is_use_statement = false; + } switch(tmp->get_type()) { case expr_type::ast_use: if (!local.empty()) { die("module import is not allowed here.", tmp->get_location() ); + } else if (!is_use_statement) { + die("module import should be used at the top of the file.", + tmp->get_location() + ); } break; case expr_type::ast_null: break; diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index f00abdbf..d6cdebcf 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -2,22 +2,21 @@ #include "symbol_finder.h" #include +#include namespace nasal { -linker::linker(): - show_path(false), lib_loaded(false), - this_file(""), lib_path("") { - char sep = is_windows()? ';':':'; - std::string PATH = getenv("PATH"); - usize last = 0, pos = PATH.find(sep, 0); - while(pos!=std::string::npos) { - std::string dirpath = PATH.substr(last, pos-last); +linker::linker(): show_path_flag(false), library_loaded(false), this_file("") { + const auto seperator= is_windows()? ';':':'; + const auto PATH = std::string(getenv("PATH")); + usize last = 0, position = PATH.find(seperator, 0); + while(position!=std::string::npos) { + std::string dirpath = PATH.substr(last, position-last); if (dirpath.length()) { envpath.push_back(dirpath); } - last = pos+1; - pos = PATH.find(sep, last); + last = position+1; + position = PATH.find(seperator, last); } if (last!=PATH.length()) { envpath.push_back(PATH.substr(last)); @@ -26,32 +25,36 @@ linker::linker(): std::string linker::get_path(expr* node) { if (node->get_type()==expr_type::ast_use) { - auto file_relative_path = std::string("."); - for(auto i : reinterpret_cast(node)->get_path()) { - file_relative_path += (is_windows()? "\\":"/") +i->get_name(); + auto file_relative_path = std::string(""); + const auto& path = reinterpret_cast(node)->get_path(); + for(auto i : path) { + file_relative_path += i->get_name(); + if (i!=path.back()) { + file_relative_path += (is_windows()? "\\":"/"); + } } return file_relative_path + ".nas"; } auto call_node = reinterpret_cast(node); - auto tmp = reinterpret_cast(call_node->get_calls()[0]); - auto content = reinterpret_cast(tmp->get_argument()[0]); + auto arguments = reinterpret_cast(call_node->get_calls()[0]); + auto content = reinterpret_cast(arguments->get_argument()[0]); return content->get_content(); } std::string linker::find_real_file_path( const std::string& filename, const span& location) { // first add file name itself into the file path - std::vector fpath = {filename}; + std::vector path_list = {filename}; // generate search path from environ path for(const auto& p : envpath) { - fpath.push_back(p + (is_windows()? "\\":"/") + filename); + path_list.push_back(p + (is_windows()? "\\":"/") + filename); } // search file - for(const auto& i : fpath) { - if (access(i.c_str(), F_OK)!=-1) { - return i; + for(const auto& path : path_list) { + if (access(path.c_str(), F_OK)!=-1) { + return path; } } @@ -61,7 +64,7 @@ std::string linker::find_real_file_path( find_real_file_path("std\\lib.nas", location): find_real_file_path("std/lib.nas", location); } - if (!show_path) { + if (!show_path_flag) { err.err("link", "in <" + location.file + ">: " + "cannot find file <" + filename + ">, " + @@ -69,13 +72,14 @@ std::string linker::find_real_file_path( ); return ""; } - auto paths = std::string(""); - for(const auto& i : fpath) { - paths += " -> " + i + "\n"; + auto path_list_info = std::string(""); + for(const auto& path : path_list) { + path_list_info += " -> " + path + "\n"; } err.err("link", "in <" + location.file + ">: " + - "cannot find file <" + filename + "> in these paths:\n" + paths + "cannot find file <" + filename + + "> in these paths:\n" + path_list_info ); return ""; } @@ -123,20 +127,20 @@ bool linker::import_check(expr* node) { return true; } -bool linker::exist(const std::string& file) { +bool linker::check_exist_or_record_file(const std::string& file) { // avoid importing the same file - for(const auto& fname : files) { - if (file==fname) { + for(const auto& name : imported_files) { + if (file==name) { return true; } } - files.push_back(file); + imported_files.push_back(file); return false; } bool linker::check_self_import(const std::string& file) { - for(const auto& i : module_load_stack) { - if (file==i) { + for(const auto& name : module_load_stack) { + if (file==name) { return true; } } @@ -160,29 +164,21 @@ void linker::link(code_block* new_tree_root, code_block* old_tree_root) { old_tree_root->get_expressions().clear(); } -code_block* linker::import_regular_file(expr* node) { +code_block* linker::import_regular_file( + expr* node, std::unordered_set& used_modules) { // get filename auto filename = get_path(node); - // clear import("xxx/xxx.nas") node - if (node->get_type()!=expr_type::ast_use) { - auto cast_node = reinterpret_cast(node); - for(auto i : cast_node->get_calls()) { - delete i; - } - cast_node->get_calls().clear(); - const auto& location = cast_node->get_first()->get_location(); - delete cast_node->get_first(); - cast_node->set_first(new nil_expr(location)); - // this will make node to call_expr(nil), - // will not be optimized when generating bytecodes - } - // avoid infinite loading loop filename = find_real_file_path(filename, node->get_location()); if (!filename.length()) { return new code_block({0, 0, 0, 0, filename}); } + if (used_modules.count(filename)) { + return new code_block({0, 0, 0, 0, filename}); + } + + // check self import, avoid infinite loading loop if (check_self_import(filename)) { err.err("link", "self-referenced module <" + filename + ">:\n" + @@ -190,7 +186,7 @@ code_block* linker::import_regular_file(expr* node) { ); return new code_block({0, 0, 0, 0, filename}); } - exist(filename); + check_exist_or_record_file(filename); module_load_stack.push_back(filename); // start importing... @@ -214,36 +210,37 @@ code_block* linker::import_regular_file(expr* node) { } code_block* linker::import_nasal_lib() { - auto filename = find_real_file_path("lib.nas", {0, 0, 0, 0, files[0]}); - if (!filename.length()) { - return new code_block({0, 0, 0, 0, filename}); + auto path = find_real_file_path( + "lib.nas", {0, 0, 0, 0, this_file} + ); + if (!path.length()) { + return new code_block({0, 0, 0, 0, path}); } - lib_path = filename; // avoid infinite loading library - if (exist(filename)) { - return new code_block({0, 0, 0, 0, filename}); + if (check_exist_or_record_file(path)) { + return new code_block({0, 0, 0, 0, path}); } // start importing... lexer nasal_lexer; parse nasal_parser; - if (nasal_lexer.scan(filename).geterr()) { + if (nasal_lexer.scan(path).geterr()) { err.err("link", - "error occurred when analysing library <" + filename + ">" + "error occurred when analysing library <" + path + ">" ); - return new code_block({0, 0, 0, 0, filename}); + return new code_block({0, 0, 0, 0, path}); } if (nasal_parser.compile(nasal_lexer).geterr()) { err.err("link", - "error occurred when analysing library <" + filename + ">" + "error occurred when analysing library <" + path + ">" ); - return new code_block({0, 0, 0, 0, filename}); + return new code_block({0, 0, 0, 0, path}); } // swap result out auto parse_result = nasal_parser.swap(nullptr); // check if library has 'import' (in fact it should not) - return load(parse_result, filename); + return load(parse_result, path); } std::string linker::generate_module_name(const std::string& file_path) { @@ -351,26 +348,35 @@ code_block* linker::load(code_block* program_root, const std::string& filename) auto tree = new code_block({0, 0, 0, 0, filename}); // load library, this ast will be linked with root directly // so no extra namespace is generated - if (!lib_loaded) { + if (!library_loaded) { auto nasal_lib_code_block = import_nasal_lib(); // insert nasal lib code to the back of tree link(tree, nasal_lib_code_block); delete nasal_lib_code_block; - lib_loaded = true; + library_loaded = true; } // load imported modules + std::unordered_set used_modules = {}; for(auto& import_ast_node : program_root->get_expressions()) { if (!import_check(import_ast_node)) { break; } - auto module_code_block = import_regular_file(import_ast_node); + auto module_code_block = import_regular_file(import_ast_node, used_modules); // this location should not be a reference, may cause use after free! const auto location = import_ast_node->get_location(); // after importing the regular file as module, delete this node delete import_ast_node; // and replace the node with null_expr node import_ast_node = new null_expr(location); + // avoid repeatedly importing the same module + const auto& module_path = module_code_block->get_location().file; + if (used_modules.count(module_path)) { + delete module_code_block; + continue; + } else { + used_modules.insert(module_path); + } // then we generate a function warping the code block, // and export the necessary global symbols in this code block // by generate a return statement, with a hashmap return value @@ -384,14 +390,16 @@ code_block* linker::load(code_block* program_root, const std::string& filename) const error& linker::link( parse& parse, const std::string& self, bool spath = false) { - show_path = spath; + // switch for showing path when errors occur + show_path_flag = spath; + // initializing file map this_file = self; - files = {self}; + imported_files = {self}; module_load_stack = {self}; + // scan root and import files // then generate a new ast and return to import_ast - // the main file's index is 0 auto new_tree_root = load(parse.tree(), self); auto old_tree_root = parse.swap(new_tree_root); delete old_tree_root; diff --git a/src/nasal_import.h b/src/nasal_import.h index c09eabeb..7fa0df36 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -18,30 +18,32 @@ #include "nasal_parse.h" #include "symbol_finder.h" +#include +#include #include +#include namespace nasal { class linker { private: - bool show_path; - bool lib_loaded; + bool show_path_flag; + bool library_loaded; std::string this_file; - std::string lib_path; error err; - std::vector files; + std::vector imported_files; std::vector module_load_stack; std::vector envpath; private: bool import_check(expr*); - bool exist(const std::string&); + bool check_exist_or_record_file(const std::string&); bool check_self_import(const std::string&); std::string generate_self_import_path(const std::string&); void link(code_block*, code_block*); std::string get_path(expr*); std::string find_real_file_path(const std::string&, const span&); - code_block* import_regular_file(expr*); + code_block* import_regular_file(expr*, std::unordered_set&); code_block* import_nasal_lib(); std::string generate_module_name(const std::string&); return_expr* generate_module_return(code_block*); @@ -51,9 +53,7 @@ class linker { public: linker(); const error& link(parse&, const std::string&, bool); - const auto& get_file_list() const {return files;} - const auto& get_this_file() const {return this_file;} - const auto& get_lib_path() const {return lib_path;} + const auto& get_file_list() const {return imported_files;} }; } diff --git a/std/stack.nas b/std/stack.nas index 3e6a14b5..596a6036 100644 --- a/std/stack.nas +++ b/std/stack.nas @@ -1,22 +1,23 @@ # stack.nas # valkmjolnir 2021/3/31 -var stack=func(){ - var vec=[]; - return{ - push:func(elem){ - append(vec,elem); +var stack = func() { + var vec = []; + return { + push: func(elem) { + append(vec, elem); }, - pop:func(){ + pop: func() { return pop(vec); }, - top:func(){ - if(size(vec)!=0) + top: func() { + if (size(vec)!=0) { return vec[-1]; + } }, - clear:func(){ - vec=[]; + clear: func() { + vec = []; }, - empty:func(){ + empty: func() { return size(vec)==0; } }; diff --git a/tools/search_file.nas b/tools/search_file.nas index 72e52d24..d35fb025 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -1,4 +1,6 @@ use std.file; +use std.padding; +use std.process_bar; var tips = func() { println("usage:"); @@ -20,7 +22,7 @@ var needle = arg[0]; var do_flat = func(vec) { var flat = []; var bfs = [vec]; - while(size(bfs)) { + while(size(bfs)!=0) { var d = pop(bfs); foreach(var f; d.files) { if (ishash(f)) { @@ -30,21 +32,21 @@ var do_flat = func(vec) { append(flat, d.dir~"/"~f); } } - sort(flat, func(a, b){return cmp(a, b)<0}); + sort(flat, func(a, b) {return cmp(a, b)<0}); return flat; } -var count = 0; -foreach(var f; do_flat(file.recursive_find_files("."))) { +var result = []; +var all_files = file.recursive_find_files("."); +foreach(var f; do_flat(all_files)) { var pos = find(needle, f); if (pos == -1) { continue; } - count += 1; var begin = substr(f, 0, pos); var end = pos+size(needle)>=size(f)? "":substr(f, pos+size(needle), size(f)); var file_size = fstat(f).st_size; - var unit = "b"; + var unit = " b"; if (file_size>1024) { file_size/=1024; unit = "kb"; @@ -58,7 +60,20 @@ foreach(var f; do_flat(file.recursive_find_files("."))) { unit = "gb"; } file_size = int(file_size); - println(begin, "\e[95;1m", needle, "\e[0m", end, " | ", file_size, " ", unit); + append(result, { + info: begin~"\e[95;1m"~needle~"\e[0m"~end, + size: file_size, + unit: unit + }); } -println("\n", count, " result(s)."); \ No newline at end of file +var max_len = 0; +foreach(var elem; result) { + var temp = size(str(elem.size)~" "~elem.unit); + max_len = math.max(max_len, temp); +} +foreach(var elem; result) { + var temp = padding.leftpad(str(elem.size)~" "~elem.unit, max_len); + println(temp, " | ", elem.info); +} +println("\n", size(result), " result(s)."); \ No newline at end of file From 5174551aa1a989738ff1da7cd00453774f9a572a Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 2 Nov 2023 00:12:45 +0800 Subject: [PATCH 07/12] :zap: improve import --- README.md | 6 ++--- doc/README_zh.md | 6 ++--- src/nasal_import.cpp | 54 ++++++++++++++++++-------------------------- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9dd33091..3434afce 100644 --- a/README.md +++ b/README.md @@ -625,7 +625,7 @@ Use `import("filename.nas")` to get the nasal file including your built-in funct Also there's another way of importing nasal files, the two way of importing have the same function: ```javascript -import.dirname.dirname.filename; +use dirname.dirname.filename; import("./dirname/dirname/filename.nas"); ``` @@ -728,7 +728,7 @@ Windows(`.dll`): Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; for(var i = 1; i<30; i+=1) @@ -745,7 +745,7 @@ dylib.dlclose(dlhandle.lib); `dylib.limitcall` is used to get `dlcall` function that has limited parameter size, this function will prove the performance of your code because it does not use `vm_vec` to store the arguments, instead it uses local scope to store them, so this could avoid frequently garbage collecting. And the code above could also be written like this: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter diff --git a/doc/README_zh.md b/doc/README_zh.md index c1fb67f1..b8eeb4ea 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -605,7 +605,7 @@ var print=func(elems...){ 当然也有另外一种办法来导入这些nasal文件,下面两种导入方式的效果是一样的: ```javascript -import.dirname.dirname.filename; +use dirname.dirname.filename; import("./dirname/dirname/filename.nas"); ``` @@ -702,7 +702,7 @@ Windows(`.dll`): 下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; for(var i = 1; i<30; i+=1) @@ -719,7 +719,7 @@ dylib.dlclose(dlhandle.lib); `dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写: ```javascript -import.std.dylib; +use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index d6cdebcf..874cc028 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -97,27 +97,27 @@ bool linker::import_check(expr* node) { if (node->get_type()!=expr_type::ast_call) { return false; } - auto tmp = reinterpret_cast(node); - auto first_expr = tmp->get_first(); + auto call_node = reinterpret_cast(node); + auto first_expr = call_node->get_first(); if (first_expr->get_type()!=expr_type::ast_id) { return false; } if (reinterpret_cast(first_expr)->get_name()!="import") { return false; } - if (!tmp->get_calls().size()) { + if (!call_node->get_calls().size()) { return false; } // import("xxx"); - if (tmp->get_calls().size()!=1) { + if (call_node->get_calls().size()!=1) { return false; } - if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) { + if (call_node->get_calls()[0]->get_type()!=expr_type::ast_callf) { return false; } - auto func_call = (call_function*)tmp->get_calls()[0]; + auto func_call = (call_function*)call_node->get_calls()[0]; if (func_call->get_argument().size()!=1) { return false; } @@ -171,10 +171,8 @@ code_block* linker::import_regular_file( // avoid infinite loading loop filename = find_real_file_path(filename, node->get_location()); - if (!filename.length()) { - return new code_block({0, 0, 0, 0, filename}); - } - if (used_modules.count(filename)) { + // if get empty string(error) or this file is used before, do not parse + if (!filename.length() || used_modules.count(filename)) { return new code_block({0, 0, 0, 0, filename}); } @@ -286,20 +284,11 @@ std::string linker::generate_module_name(const std::string& file_path) { "get empty module name from <" + file_path + ">, " + "will not be easily accessed." ); + return module_name; } - if (module_name.length() && '0' <= module_name[0] && module_name[0] <= '9') { - err.warn("link", - "get module <" + module_name + "> from <" + file_path + ">, " + - "will not be easily accessed." - ); - } - if (module_name.length() && module_name.find(".")!=std::string::npos) { - err.warn("link", - "get module <" + module_name + "> from <" + file_path + ">, " + - "will not be easily accessed." - ); - } - if (module_name.length() && module_name.find("-")!=std::string::npos) { + if (std::isdigit(module_name[0]) || + module_name.find(".")!=std::string::npos || + module_name.find("-")!=std::string::npos) { err.warn("link", "get module <" + module_name + "> from <" + file_path + ">, " + "will not be easily accessed." @@ -358,28 +347,29 @@ code_block* linker::load(code_block* program_root, const std::string& filename) // load imported modules std::unordered_set used_modules = {}; - for(auto& import_ast_node : program_root->get_expressions()) { - if (!import_check(import_ast_node)) { + for(auto& import_node : program_root->get_expressions()) { + if (!import_check(import_node)) { break; } - auto module_code_block = import_regular_file(import_ast_node, used_modules); - // this location should not be a reference, may cause use after free! - const auto location = import_ast_node->get_location(); + // parse file and get ast + 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_ast_node; + delete import_node; // and replace the node with null_expr node - import_ast_node = new null_expr(location); + import_node = replace_node; + // avoid repeatedly importing the same module const auto& module_path = module_code_block->get_location().file; if (used_modules.count(module_path)) { delete module_code_block; continue; - } else { - used_modules.insert(module_path); } + // then we generate a function warping the code block, // and export the necessary global symbols in this code block // by generate a return statement, with a hashmap return value + used_modules.insert(module_path); tree->add_expression(generate_module_definition(module_code_block)); } From 2f58a7c2235552d5346c22cad4e9913e1d3fbc28 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Thu, 2 Nov 2023 23:01:47 +0800 Subject: [PATCH 08/12] :memo: update documents --- README.md | 2 +- doc/README_zh.md | 2 +- doc/dev.md | 11 +++++++++++ doc/dev_zh.md | 11 +++++++++++ doc/nasal-http-test-web.html | 7 ++++--- test/httptest.nas | 3 ++- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3434afce..d7c1262b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) -![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github) +![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE) > This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md) diff --git a/doc/README_zh.md b/doc/README_zh.md index b8eeb4ea..26c4503b 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -4,7 +4,7 @@ ![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) -![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github) +![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE) > 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md) diff --git a/doc/dev.md b/doc/dev.md index 9d61ff64..6f1aca1e 100644 --- a/doc/dev.md +++ b/doc/dev.md @@ -1,5 +1,7 @@ # __Development History__ +![buringship](./pic/burningship.png) + ## __Contents__ * [__Parser__](#parser) @@ -22,6 +24,7 @@ * [__Release Notes__](#release-notes) * [v8.0](#version-80-release) * [v11.0](#version-110-release) + * [v11.1](#version-111-release) ## __Parser__ @@ -697,3 +700,11 @@ This bug is fixed in `v9.0`. So we suggest that do not use `v8.0`. 9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`). 10. New ghost type register process. + +### __version 11.1 release__ + +1. Bug fix: debugger in v11.0 is malfunctional. + +2. Bug fix: symbol_finder does not check definition in foreach/forindex loop. + +3. Change extension syntax `import.xx.xx` to `use xx.xx`. diff --git a/doc/dev_zh.md b/doc/dev_zh.md index 4a8fc240..baeb5635 100644 --- a/doc/dev_zh.md +++ b/doc/dev_zh.md @@ -1,5 +1,7 @@ # __开发历史记录__ +![buringship](./pic/burningship.png) + ## __目录__ * [__语法分析__](#语法分析) @@ -22,6 +24,7 @@ * [__发行日志__](#发行日志) * [v8.0](#version-80-release) * [v11.0](#version-110-release) + * [v11.1](#version-111-release) ## __语法分析__ @@ -630,3 +633,11 @@ in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;` 9. 添加`CMakeLists.txt` (可在`Visual Studio`中使用)。 10. 全新的自定义类型注册流程。 + +### __version 11.1 release__ + +1. Bug 修复: 修复 v11.0 的 debugger 无法启动的问题。 + +2. Bug 修复: symbol_finder 不检查 foreach/forindex 中的迭代变量声明的问题。 + +3. 扩展语法 `import.xx.xx` 改为 `use xx.xx`。 diff --git a/doc/nasal-http-test-web.html b/doc/nasal-http-test-web.html index 01191754..234f247f 100644 --- a/doc/nasal-http-test-web.html +++ b/doc/nasal-http-test-web.html @@ -36,7 +36,7 @@

 Nasal | Not another scripting language!

 Introduction | 介绍

@@ -79,13 +79,14 @@

 Benchmark | 执行效率

在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。

- The figure below is the feigenbaum-figure generated by ppm script written in nasal. + The figure below is the feigenbaum-figure and burningship-figure generated by ppm script written in nasal.

- 下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 图形。 + 下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 和 burningship 图形。


+

 Example | 样例代码

diff --git a/test/httptest.nas b/test/httptest.nas index 3b8fcaf8..63f5a1d4 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -322,7 +322,8 @@ while(1){ elsif(path=="/doc/pic/nasal.png" or path=="/doc/pic/benchmark.png" or path=="/doc/pic/mandelbrot.png" or - path=="/doc/pic/feigenbaum.png") + path=="/doc/pic/feigenbaum.png" or + path=="/doc/pic/burningship.png") http.send(client,respond.ok(io.readfile("."~path))); else{ var filename=substr(path,1,size(path)-1); From c946e9debd9df3b7e8ac604c7bb95bc887eb393b Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sat, 4 Nov 2023 00:09:59 +0800 Subject: [PATCH 09/12] :memo: update documents --- README.md | 517 +++++++++++++++++++--------------- doc/README_zh.md | 526 ++++++++++++++++++++--------------- doc/namespace.md | 24 ++ doc/nasal-http-test-web.html | 1 + module/fib.cpp | 2 +- module/nasocket.cpp | 4 +- src/nasal_builtin.cpp | 2 +- test/httptest.nas | 1 + 8 files changed, 618 insertions(+), 459 deletions(-) diff --git a/README.md b/README.md index d7c1262b..5918eec6 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ __Contact us if having great ideas to share!__ -* __E-mail__: __lhk101lhk101@qq.com__ +* __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762) ## __Introduction__ @@ -97,8 +97,9 @@ You could choose which compiler you want to use: If your system is `Windows` and you want to output unicode, you could write this in nasal code: ```javascript -if(os.platform()=="windows") +if (os.platform()=="windows") { system("chcp 65001"); +} ``` ## __Tutorial__ @@ -116,30 +117,30 @@ This type is not created by user program. __`nil`__ is a null type. Just like `null`. ```javascript -var spc=nil; +var spc = nil; ``` __`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store. ```javascript # this language use '#' to write notes -var n=2.71828; # dec -var n=2.147e16; # dec -var n=1e-10; # dec -var n=0xAA55; # hex -var n=0o170001; # oct +var n = 2.71828; # dec +var n = 2.147e16; # dec +var n = 1e-10; # dec +var n = 0xAA55; # hex +var n = 0o170001; # oct # caution: true and false also useful in nasal now -var n=true; # in fact n is now 1.0 -var n=false; # in face n is now 0.0 +var n = true; # in fact n is now 1.0 +var n = false; # in face n is now 0.0 ``` __`str`__ has 3 formats. The third one is used to declare a character. ```javascript -var s='str'; -var s="another string"; -var s=`c`; +var s = 'str'; +var s = "another string"; +var s = `c`; # some special characters is allowed in this language: '\a'; '\b'; '\e'; '\f'; '\n'; '\r'; '\t'; '\v'; @@ -150,19 +151,19 @@ var s=`c`; __`vec`__ has unlimited length and can store all types of values. ```javascript -var vec=[]; -var vec=[0,nil,{},[],func(){return 0}]; -append(vec,0,1,2); +var vec = []; +var vec = [0, nil, {}, [], func(){return 0}]; +append(vec, 0, 1, 2); ``` __`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key. ```javascript -var hash={ - member1:nil, - member2:"str", - "member3":"member\'s name can also be a string constant", - funct:func(){ +var hash = { + member1: nil, + member2: "str", + "member3": "member\'s name can also be a string constant", + funct: func() { return me.member2~me.member3; } }; @@ -171,27 +172,28 @@ var hash={ __`func`__ is a function type (in fact it is `lambda`). ```javascript -var f=func(x,y,z){ +var f = func(x, y, z) { return nil; } # function could be declared without parameters and `(`, `)` -var f=func{ +var f = func { return 114514; } -var f=func(x,y,z,deft=1){ +var f = func(x, y, z, deft = 1) { return x+y+z+deft; } -var f=func(args...){ - var sum=0; - foreach(var i;args) - sum+=i; +var f = func(args...) { + var sum = 0; + foreach(var i; args) { + sum += i; + } return sum; } ``` __`upval`__ is used to store upvalues, used in __`vm`__ to make sure closure runs correctly. -__`obj`__ is used to store other complex `C/C++` data types. +__`ghost`__ is used to store other complex `C/C++` data types. This type is created by native-function of nasal. If want to define a new data type, see how to add native-functions by editing code. @@ -237,16 +239,16 @@ Bitwise operators `~` `|` `&` `^` have the same function as C/C++. Operators `=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=` are used in assignment expressions. ```javascript -a=b=c=d=1; -a+=1; -a-=1; -a*=1; -a/=1; -a~="string"; - -a^=0xff; -a&=0xca; -a|=0xba; +a = b = c = d = 1; +a += 1; +a -= 1; +a *= 1; +a /= 1; +a ~= "string"; + +a ^= 0xff; +a &= 0xca; +a |= 0xba; ``` @@ -256,9 +258,9 @@ a|=0xba; As follows. ```javascript -var a=1; # define single variable -var (a,b,c)=[0,1,2]; # define multiple variables from a vector -var (a,b,c)=(0,1,2); # define multiple variables from a tuple +var a = 1; # define single variable +var (a, b, c) = [0, 1, 2]; # define multiple variables from a vector +var (a, b, c) = (0, 1, 2); # define multiple variables from a tuple ``` Nasal has many special global symbols: @@ -292,9 +294,9 @@ func() { The last one is often used to swap two variables. ```javascript -(a,b[0],c.d)=[0,1,2]; -(a,b[1],c.e)=(0,1,2); -(a,b)=(b,a); +(a, b[0], c.d) = [0, 1, 2]; +(a, b[1], c.e) = (0, 1, 2); +(a, b) = (b, a); ``` @@ -305,13 +307,13 @@ In nasal there's a new key word `elsif`. It has the same functions as `else if`. ```javascript -if(1){ +if (1) { ; -}elsif(2){ +} elsif (2) { ; -}else if(3){ +} else if (3) { ; -}else{ +} else { ; } ``` @@ -323,10 +325,12 @@ if(1){ While loop and for loop is simalar to C/C++. ```javascript -while(condition) +while(condition) { continue; -for(var i=0;i<10;i+=1) +} +for(var i = 0; i<10; i += 1) { break; +} ``` Nasal has another two kinds of loops that iterates through a vector: @@ -334,15 +338,17 @@ Nasal has another two kinds of loops that iterates through a vector: `forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`. ```javascript -forindex(var i;elem) +forindex(var i; elem) { print(elem[i]); +} ``` `foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`. ```javascript -foreach(var i;elem) +foreach(var i; elem) { print(i); +} ``` @@ -356,7 +362,7 @@ If you want to get the character, use built-in function `chr()`. ```javascript a[0]; -a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; +a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil]; "hello world"[0]; ``` @@ -370,7 +376,7 @@ because hashmap use string as the key to compare. But if it really useful, the efficientcy may not be so important... ```javascript -f(x:0,y:nil,z:[]); +f(x:0, y:nil, z:[]); ``` @@ -380,10 +386,10 @@ f(x:0,y:nil,z:[]); Also functions have this kind of use: ```javascript -func(x,y){ +func(x, y) { return x+y -}(0,1); -func(x){ +}(0, 1); +func(x) { return 1/(1+math.exp(-x)); }(0.5); ``` @@ -392,11 +398,11 @@ There's an interesting test file `y-combinator.nas`, try it for fun: ```javascript -var fib=func(f){ +var fib = func(f) { return f(f); }( - func(f){ - return func(x){ + func(f) { + return func(x) { if(x<2) return x; return f(f)(x-1)+f(f)(x-2); } @@ -412,9 +418,9 @@ Closure means you could get the variable that is not in the local scope of a fun Here is an example, result is `1`: ```javascript -var f=func(){ - var a=1; - return func(){return a;}; +var f = func() { + var a = 1; + return func() {return a;}; } print(f()()); ``` @@ -422,14 +428,14 @@ print(f()()); Using closure makes it easier to OOP. ```javascript -var student=func(n,a){ - var (name,age)=(n,a); +var student = func(n, a) { + var (name, age) = (n, a); return { - print_info:func() {println(name,' ',age);}, - set_age: func(a){age=a;}, - get_age: func() {return age;}, - set_name: func(n){name=n;}, - get_name: func() {return name;} + print_info: func() {println(name, ' ', age);}, + set_age: func(a) {age = a;}, + get_age: func() {return age;}, + set_name: func(n) {name = n;}, + get_name: func() {return name;} }; } ``` @@ -448,20 +454,20 @@ If there is a hash that has the member, you will get the member's value. Using this mechanism, we could OOP like this, the result is `114514`: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); +var a = class.new(); a.set(114514); println(a.get()); ``` @@ -473,28 +479,28 @@ And `get` has the same process. And we must remind you that if you do this: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); -var b=class.new(); +var a = class.new(); +var b = class.new(); a.set(114); b.set(514); println(a.get()); println(b.get()); -var c=a.get; -var d=b.get; +var c = a.get; +var d = b.get; println(c()); println(c()); @@ -532,33 +538,19 @@ Definition: ```C++ // you could also use a macro to define one. -nas_native(builtin_print); +var builtin_print(context*, gc*); ``` Then complete this function using C++: ```C++ -var builtin_print(var* local,gc& ngc) -{ +var builtin_print(context* ctx, gc* ngc) { // find value with index begin from 1 // because local[0] is reserved for value 'me' - var vec=local[1]; - // main process - // also check number of arguments and type here - // if get an error,use nas_err - for(auto& i:vec.vec().elems) - switch(i.type) - { - case vm_none: std::cout<<"undefined"; break; - case vm_nil: std::cout<<"nil"; break; - case vm_num: std::cout<"; break; - } - std::cout<localr[1].vec().elems) { + std::cout << i; + } + std::cout << std::flush; // generate return value, // use ngc::alloc(type) to make a new value // or use reserved reference nil/one/zero @@ -572,17 +564,24 @@ The value got before will be collected, but stil in use in this builtin function So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this: ```C++ -var builtin_keys(var* local,gc& ngc) -{ - var hash=local[1]; - if(hash.type!=vm_hash) - return nas_err("keys","\"hash\" must be hash"); +var builtin_keys(context* ctx, gc* ngc) { + auto hash = ctx->localr[1]; + if (hash.type!=vm_hash && hash.type!=vm_map) { + return nas_err("keys", "\"hash\" must be hash"); + } // use gc.temp to store the gc-managed-value, to avoid being sweeped - var res=ngc.temp=ngc.alloc(vm_vec); - auto& vec=res.vec().elems; - for(auto& iter:hash.hash().elems) - vec.push_back(ngc.newstr(iter.first)); - ngc.temp=nil; + auto res = ngc->temp = ngc->alloc(vm_vec); + auto& vec = res.vec().elems; + if (hash.type==vm_hash) { + for(const auto& iter : hash.hash().elems) { + vec.push_back(ngc->newstr(iter.first)); + } + } else { + for(const auto& iter : hash.map().mapper) { + vec.push_back(ngc->newstr(iter.first)); + } + } + ngc->temp = nil; return res; } ``` @@ -590,21 +589,16 @@ var builtin_keys(var* local,gc& ngc) After that, register the built-in function's name(in nasal) and the function's pointer in this table: ```C++ -struct func -{ - const char* name; - var (*func)(var*,gc&); -} builtin[]= -{ - {"__print",builtin_print}, - {nullptr, nullptr } +nasal_builtin_table builtin[] = { + {"__print", builtin_print}, + {nullptr, nullptr} }; ``` At last,warp the `__print` in a nasal file: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print(elems); }; ``` @@ -613,7 +607,7 @@ In fact the arguments that `__print` uses are not necessary. So writting it like this is also right: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print; }; ``` @@ -631,7 +625,7 @@ import("./dirname/dirname/filename.nas"); -
Modules(for lib developers) +
Modules (for lib developers) If there is only one way to add your own functions into nasal, that is really inconvenient. @@ -731,7 +725,7 @@ Then we write a test nasal file to run this fib function, using `os.platform()` use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; -for(var i = 1; i<30; i+=1) +for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -749,7 +743,7 @@ use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter -for(var i = 1; i<30; i+=1) +for(var i = 1; i<30; i += 1) println(invoke(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -791,7 +785,7 @@ If get this, Congratulations!
-
Ghost Type(for lib developers) +
Ghost Type (for lib developers) It's quite easy to create a new ghost by yourself now. Look at the example below: @@ -812,19 +806,31 @@ void ghost_for_test_destructor(void* ptr) { var create_new_ghost(var* args, usize size, gc* ngc) { var res = ngc->alloc(vm_obj); // create ghost type - res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32); + res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32); return res; } +var set_new_ghost(var* args, usize size, gc* ngc) { + var res = args[0]; + if (!res.object_check(ghost_for_test)) { + std::cout << "set_new_ghost: not ghost for test type.\n"; + return nil; + } + f64 num = args[1].num(); + *(reinterpret_cast(res.ghost().pointer)) = static_cast(num); + std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; + return nil; +} + var print_new_ghost(var* args, usize size, gc* ngc) { var res = args[0]; // check ghost type by the type name - if (!res.objchk(ghost_for_test)) { + if (!res.object_check(ghost_for_test)) { std::cout << "print_new_ghost: not ghost for test type.\n"; return nil; } - std::cout << "print_new_ghost: " << res.obj() << " result = " - << *((u32*)res.obj().ptr) << "\n"; + std::cout << "print_new_ghost: " << res.ghost() << " result = " + << *((u32*)res.ghost().pointer) << "\n"; return nil; } ``` @@ -841,7 +847,7 @@ We use this function to create a new ghost type: And we use this function to check if value is the correct ghost type: -`bool var::objchk(const std::string&);` +`bool var::object_check(const std::string&);` The parameter is the name of the ghost type. @@ -860,7 +866,7 @@ So do not use variable without using `var` to declare it. In Andy's interpreter: ```javascript -foreach(i;[0,1,2,3]) +foreach(i; [0, 1, 2, 3]) print(i) ``` @@ -877,7 +883,7 @@ If you forget to add the keyword `var`, you will get this: code: undefined symbol "i" --> test.nas:1:9 | -1 | foreach(i;[0,1,2,3]) +1 | foreach(i; [0, 1, 2, 3]) | ^ undefined symbol "i" code: undefined symbol "i" @@ -901,8 +907,7 @@ it will print trace back information: Function `die` is used to throw error and crash immediately. ```javascript -func() -{ +func() { println("hello"); die("error occurred this line"); return; @@ -912,17 +917,27 @@ func() ```javascript hello [vm] error: error occurred this line -[vm] native function error. -trace back: - 0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) - 0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) - 0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6) -vm stack (0x7fffcd21bc68 , limit 10, total 12): - 0x0000005b | null | - ... - 0x00000057 | str | <0x138ff60> error occurred t... - ... - 0x00000052 | nil | +[vm] error: error occurred in native function + +call trace (main) + call func@0x557513935710() {entry: 0x850} + +trace back (main) + 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x5575138e8c40, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x5575138e8c50 + 0x00000a | nil | + 0x000009 | nil | + 0x000008 | str | <0x5575138d9190> error occurred t... + 0x000007 | nil | + 0x000006 | func | <0x5575139356f0> entry:0x850 + 0x000005 | pc | 0x85a + 0x000004 | addr | 0x0 ```
@@ -932,28 +947,41 @@ vm stack (0x7fffcd21bc68 , limit 10, total 12): Here is an example of stack overflow: ```javascript -func(f){ +func(f) { return f(f); }( - func(f){ + func(f) { f(f); } )(); ``` ```javascript -[vm] stack overflow -trace back: - 0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) - 0x000004fb 1349 same call(s) - 0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) - 0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) -vm stack (0x7fffd3781d58 , limit 10, total 8108): - 0x00001ffb | func | <0x15f8d90> entry:0x4f9 - 0x00001ffa | func | <0x15f8d90> entry:0x4f9 - 0x00001ff9 | pc | 0x4fb - ... - 0x00001ff2 | addr | 0x7fffd37a16e8 +[vm] error: stack overflow + +call trace (main) + call func@0x564106058620(f) {entry: 0x859} + --> 583 same call(s) + call func@0x5641060586c0(f) {entry: 0x851} + +trace back (main) + 0x000859 45 00 00 01 calll 0x1(a.nas:5) + 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5) + 0x00085b 582 same call(s) + 0x000853 4a 00 00 01 callfv 0x1(a.nas:2) + 0x00085f 4a 00 00 01 callfv 0x1(a.nas:3) + +stack (0x56410600be00, limit 10, total 4096) + 0x000fff | func | <0x564106058600> entry:0x859 + 0x000ffe | pc | 0x85b + 0x000ffd | addr | 0x56410601bd20 + 0x000ffc | nil | + 0x000ffb | nil | + 0x000ffa | func | <0x564106058600> entry:0x859 + 0x000ff9 | nil | + 0x000ff8 | func | <0x564106058600> entry:0x859 + 0x000ff7 | pc | 0x85b + 0x000ff6 | addr | 0x56410601bcb0 ```
@@ -963,17 +991,19 @@ vm stack (0x7fffd3781d58 , limit 10, total 8108): Error will be thrown if there's a fatal error when executing: ```javascript -func(){ +func() { return 0; }()[1]; ``` ```javascript -[vm] callv: must call a vector/hash/string -trace back: - 0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) -vm stack (0x7fffff539c28 , limit 10, total 1): - 0x00000050 | num | 0 +[vm] error: must call a vector/hash/string but get number + +trace back (main) + 0x000854 47 00 00 00 callv 0x0(a.nas:3) + +stack (0x564993f462b0, limit 10, total 1) + 0x000000 | num | 0 ```
@@ -985,35 +1015,48 @@ Use command __`-d`__ or __`--detail`__ the trace back info will show more detail ```javascript hello [vm] error: error occurred this line -[vm] error: native function error +[vm] error: error occurred in native function + +call trace (main) + call func@0x55dcb5b8fbf0() {entry: 0x850} + trace back (main) - 0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) - 0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) - 0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) -vm stack (0x7fffe0ffed90 , limit 10, total 12) - 0x0000004a | null | - 0x00000049 | pc | 0x553 - 0x00000048 | addr | 0x7fffe0ffeda0 - ... - 0x00000041 | nil | + 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x55dcb5b43120, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x55dcb5b43130 + 0x00000a | nil | + 0x000009 | nil | + 0x000008 | str | <0x55dcb5b33670> error occurred t... + 0x000007 | nil | + 0x000006 | func | <0x55dcb5b8fbd0> entry:0x850 + 0x000005 | pc | 0x85a + 0x000004 | addr | 0x0 + registers (main) - [ pc ] | pc | 0xb0 - [ global ] | addr | 0x7fffe0ffe9a0 - [ localr ] | addr | 0x7fffe0ffedf0 - [ memr ] | addr | 0x0 - [ canary ] | addr | 0x7fffe1002990 - [ top ] | addr | 0x7fffe0ffee40 - [ funcr ] | func | <0x677cd0> entry:0xb0 - [ upvalr ] | nil | -global (0x7fffe0ffe9a0 ) - 0x00000000 | func | <0x65fb00> entry:0x5 - 0x00000001 | func | <0x65fb20> entry:0xd + [pc ] | pc | 0x547 + [global] | addr | 0x55dcb5b53130 + [local ] | addr | 0x55dcb5b43190 + [memr ] | addr | 0x0 + [canary] | addr | 0x55dcb5b53110 + [top ] | addr | 0x55dcb5b431f0 + [funcr ] | func | <0x55dcb5b65620> entry:0x547 + [upval ] | nil | + +global (0x55dcb5b53130) + 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val] + 0x000001 | vec | <0x55dcb5b64c20> [0 val] ... - 0x0000003d | func | <0x66bf00> entry:0x51f - 0x0000003e | hash | <0x65ffa0> {5 val} -local (0x7fffe0ffedf0 ) - 0x00000000 | nil | - 0x00000001 | str | <0x6cb630> error occurred t... + 0x00005e | func | <0x55dcb5b8fc70> entry:0x846 + +local (0x55dcb5b43190 <+7>) + 0x000000 | nil | + 0x000001 | str | <0x55dcb5b33670> error occurred t... + 0x000002 | nil | ``` @@ -1038,16 +1081,18 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: ---> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) - 0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) - 0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) - 0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) - 0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) - 0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) - 0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) - 0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) -vm stack (0x7fffd0259138 , limit 10, total 0) + 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427) + 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427) + 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423) + 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423) + 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423) +--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1) + 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1) + 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1) + +stack (0x55ccd0a1b9d0, limit 10, total 0) >> ``` @@ -1070,23 +1115,26 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: - 0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) - 0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) - 0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) - 0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) - 0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) - 0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) ---> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) - 0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) -vm stack (0x7fffd0259138 , limit 10, total 7) - 0x00000047 | pc | 0x566 - 0x00000046 | addr | 0x0 - 0x00000045 | nil | - 0x00000044 | num | 0 - 0x00000043 | nil | - 0x00000042 | nil | - 0x00000041 | func | <0x88d2f0> entry:0x5 + 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1) +--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3) + 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3) + 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3) + 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4) + 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4) + +stack (0x55ccd0a1b9d0, limit 10, total 8) + 0x000007 | pc | 0x869 + 0x000006 | addr | 0x0 + 0x000005 | nil | + 0x000004 | nil | + 0x000003 | num | 0 + 0x000002 | nil | + 0x000001 | nil | + 0x000000 | func | <0x55ccd0a58fa0> entry:0x487 >> ``` @@ -1114,3 +1162,22 @@ Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31) >>> ``` + +Try import `std/json.nas`~ + +```bash +[nasal-repl] Initializating enviroment... +[nasal-repl] Initialization complete. + +Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30) +.h, .help | show help +.e, .exit | quit the REPL +.q, .quit | quit the REPL +.c, .clear | clear the screen +.s, .source | show source code + +>>> use std.json; +{stringify:func(..) {..},parse:func(..) {..}} + +>>> +``` diff --git a/doc/README_zh.md b/doc/README_zh.md index 26c4503b..9ad0b061 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -25,7 +25,7 @@ __如果有好的意见或建议,欢迎联系我们!__ -* __E-mail__: __lhk101lhk101@qq.com__ +* __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762) ## __简介__ @@ -88,8 +88,9 @@ __注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构 如果你是 `Windows` 用户且想正常输出unicode,在nasal代码里写这个来开启unicode代码页: ```javascript -if(os.platform()=="windows") +if (os.platform()=="windows") { system("chcp 65001"); +} ``` ## __教程__ @@ -104,30 +105,30 @@ __`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行 __`nil`__ 是空类型。类似于null。 ```javascript -var spc=nil; +var spc = nil; ``` __`num`__ 有三种形式:十进制,十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。 ```javascript # 该语言用 '#' 来作为注释的开头 -var n=2.71828; # dec 十进制 -var n=2.147e16; # dec 十进制 -var n=1e-10; # dec 十进制 -var n=0xAA55; # hex 十六进制 -var n=0o170001; # oct 八进制 +var n = 2.71828; # dec 十进制 +var n = 2.147e16; # dec 十进制 +var n = 1e-10; # dec 十进制 +var n = 0xAA55; # hex 十六进制 +var n = 0o170001; # oct 八进制 # 注意: true 和 false 关键字在现在的 nasal 里也是可用的 -var n=true; # n 实际上是数字 1.0 -var n=false; # n 实际上是数字 0.0 +var n = true; # n 实际上是数字 1.0 +var n = false; # n 实际上是数字 0.0 ``` __`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。 ```javascript -var s='str'; -var s="another string"; -var s=`c`; +var s = 'str'; +var s = "another string"; +var s = `c`; # 该语言也支持一些特别的转义字符: '\a'; '\b'; '\e'; '\f'; '\n'; '\r'; '\t'; '\v'; @@ -138,19 +139,19 @@ var s=`c`; __`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度) ```javascript -var vec=[]; -var vec=[0,nil,{},[],func(){return 0}]; -append(vec,0,1,2); +var vec = []; +var vec = [0, nil, {}, [], func(){return 0}]; +append(vec, 0, 1, 2); ``` __`hash`__ 使用哈希表 (类似于`python`中的`dict`),通过键值对来存储数据。key可以是一个字符串,也可以是一个标识符。 ```javascript -var hash={ - member1:nil, - member2:"str", - "member3":"member\'s name can also be a string constant", - funct:func(){ +var hash = { + member1: nil, + member2: "str", + "member3": "member\'s name can also be a string constant", + funct: func() { return me.member2~me.member3; } }; @@ -159,27 +160,28 @@ var hash={ __`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式) ```javascript -var f=func(x,y,z){ +var f = func(x, y, z) { return nil; } # 函数声明可以没有参数列表以及 `(`, `)` -var f=func{ +var f = func { return 114514; } -var f=func(x,y,z,deft=1){ +var f = func(x, y, z, deft = 1) { return x+y+z+deft; } -var f=func(args...){ - var sum=0; - foreach(var i;args) - sum+=i; +var f = func(args...) { + var sum = 0; + foreach(var i; args) { + sum += i; + } return sum; } ``` __`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。 -__`obj`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。 +__`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。 @@ -223,16 +225,16 @@ Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的 赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。 ```javascript -a=b=c=d=1; -a+=1; -a-=1; -a*=1; -a/=1; -a~="string"; - -a^=0xff; -a&=0xca; -a|=0xba; +a = b = c = d = 1; +a += 1; +a -= 1; +a *= 1; +a /= 1; +a ~= "string"; + +a ^= 0xff; +a &= 0xca; +a |= 0xba; ``` @@ -242,9 +244,9 @@ a|=0xba; 如下所示。 ```javascript -var a=1; # 定义单个变量 -var (a,b,c)=[0,1,2]; # 从数组中初始化多个变量 -var (a,b,c)=(0,1,2); # 从元组中初始化多个变量 +var a = 1; # 定义单个变量 +var (a, b, c) = [0, 1, 2]; # 从数组中初始化多个变量 +var (a, b, c) = (0, 1, 2); # 从元组中初始化多个变量 ``` Nasal 有很多特别的全局变量: @@ -278,9 +280,9 @@ func() { 最后这个语句通常用于交换两个变量的数据,类似于Python中的操作。 ```javascript -(a,b[0],c.d)=[0,1,2]; -(a,b[1],c.e)=(0,1,2); -(a,b)=(b,a); +(a, b[0], c.d) = [0, 1, 2]; +(a, b[1], c.e) = (0, 1, 2); +(a, b) = (b, a); ``` @@ -290,13 +292,13 @@ func() { nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。 ```javascript -if(1){ +if (1) { ; -}elsif(2){ +} elsif (2) { ; -}else if(3){ +} else if (3) { ; -}else{ +} else { ; } ``` @@ -308,10 +310,12 @@ if(1){ while循环和for循环大体上与C/C++是一致的。 ```javascript -while(condition) +while(condition) { continue; -for(var i=0;i<10;i+=1) +} +for(var i = 0; i<10; i += 1) { break; +} ``` 同时,nasal还有另外两种直接遍历列表的循环方式: @@ -319,15 +323,17 @@ for(var i=0;i<10;i+=1) `forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。 ```javascript -forindex(var i;elem) +forindex(var i; elem) { print(elem[i]); +} ``` `foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`. ```javascript -foreach(var i;elem) +foreach(var i; elem) { print(i); +} ``` @@ -338,7 +344,7 @@ nasal提供了下面第一句的类似语法来从列表中随机或者按照一 ```javascript a[0]; -a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; +a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil]; "hello world"[0]; ``` @@ -351,7 +357,7 @@ a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil]; 然而如果它用起来非常舒适,那效率也显得不是非常重要了…… ```javascript -f(x:0,y:nil,z:[]); +f(x:0, y:nil, z:[]); ``` @@ -361,10 +367,10 @@ f(x:0,y:nil,z:[]); 函数有这样一种直接编写函数体并且立即调用的方式: ```javascript -func(x,y){ +func(x, y) { return x+y; -}(0,1); -func(x){ +}(0, 1); +func(x) { return 1/(1+math.exp(-x)); }(0.5); ``` @@ -372,11 +378,11 @@ func(x){ 测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试: ```javascript -var fib=func(f){ +var fib = func(f) { return f(f); }( - func(f){ - return func(x){ + func(f) { + return func(x) { if(x<2) return x; return f(f)(x-1)+f(f)(x-2); } @@ -393,9 +399,9 @@ var fib=func(f){ 下面这个例子里,结果是`1`: ```javascript -var f=func(){ - var a=1; - return func(){return a;}; +var f = func() { + var a = 1; + return func() {return a;}; } print(f()()); ``` @@ -403,14 +409,14 @@ print(f()()); 如果善用闭包,你可以使用它来进行面向对象编程。 ```javascript -var student=func(n,a){ - var (name,age)=(n,a); +var student = func(n, a) { + var (name, age) = (n, a); return { - print_info:func() {println(name,' ',age);}, - set_age: func(a){age=a;}, - get_age: func() {return age;}, - set_name: func(n){name=n;}, - get_name: func() {return name;} + print_info: func() {println(name, ' ', age);}, + set_age: func(a) {age = a;}, + get_age: func() {return age;}, + set_name: func(n) {name = n;}, + get_name: func() {return name;} }; } ``` @@ -429,20 +435,20 @@ var student=func(n,a){ 使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); +var a = class.new(); a.set(114514); println(a.get()); ``` @@ -453,28 +459,28 @@ println(a.get()); 不过我们必须提醒你一点,如果你在这个地方使用该优化来减少hash的搜索开销: ```javascript -var trait={ - get:func{return me.val;}, - set:func(x){me.val=x;} +var trait = { + get: func {return me.val;}, + set: func(x) {me.val = x;} }; -var class={ - new:func(){ +var class = { + new: func() { return { - val:nil, - parents:[trait] + val: nil, + parents: [trait] }; } }; -var a=class.new(); -var b=class.new(); +var a = class.new(); +var b = class.new(); a.set(114); b.set(514); println(a.get()); println(b.get()); -var c=a.get; -var d=b.get; +var c = a.get; +var d = b.get; println(c()); println(c()); @@ -506,42 +512,27 @@ println(d()); __警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。 -如果你确实是想修改源码来搞一个自己私人订制的解释器,那么你可以说:“我他妈就是想自己私人订制,你们他妈的管得着吗”, -然后看看源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,还有下面的样例: +如果你确实是想修改源码来搞一个自己私人订制的解释器 ———— “我他妈就是想自己私人订制,你们他妈的管得着吗?”, +参考源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,下面是其中一个样例: 定义新的内置函数: ```C++ // 你可以使用这个宏来直接定义一个新的内置函数 -nas_native(builtin_print); +var builtin_print(context*, gc*); ``` 然后用C++完成这个函数的函数体: ```C++ -var builtin_print(var* local,gc& ngc) -{ - // 局部变量的下标其实是从1开始的 - // 因为local[0]是保留给'me'的空间 - var vec=local[1]; - // 主要部分 - // 一些必要的类型检查和输入合法性检测也要在这里写出 - // 如果检测到问题,用builtin_err函数来返回vm_null - // 并且狠狠地骂那些不好好写代码的混蛋(玩笑) - for(auto& i:vec.vec().elems) - switch(i.type) - { - case vm_none: std::cout<<"undefined"; break; - case vm_nil: std::cout<<"nil"; break; - case vm_num: std::cout<"; break; - } - std::cout<localr[1].vec().elems) { + std::cout << i; + } + std::cout << std::flush; + // 最后生成返回值,返回值必须是一个内置的类型, // 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构 // 或者用我们已经定义好的nil/one/zero,这些可以直接使用 return nil; @@ -554,17 +545,24 @@ var builtin_print(var* local,gc& ngc) 可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量,这样可以防止内部所有的申请错误触发垃圾回收。如下所示: ```C++ -var builtin_keys(var* local,gc& ngc) -{ - var hash=local[1]; - if(hash.type!=vm_hash) - return nas_err("keys","\"hash\" must be hash"); +var builtin_keys(context* ctx, gc* ngc) { + auto hash = ctx->localr[1]; + if (hash.type!=vm_hash && hash.type!=vm_map) { + return nas_err("keys", "\"hash\" must be hash"); + } // 使用gc.temp来存储gc管理的变量,防止错误的回收 - var res=ngc.temp=ngc.alloc(vm_vec); - auto& vec=res.vec().elems; - for(auto& iter:hash.hash().elems) - vec.push_back(ngc.newstr(iter.first)); - ngc.temp=nil; + auto res = ngc->temp = ngc->alloc(vm_vec); + auto& vec = res.vec().elems; + if (hash.type==vm_hash) { + for(const auto& iter : hash.hash().elems) { + vec.push_back(ngc->newstr(iter.first)); + } + } else { + for(const auto& iter : hash.map().mapper) { + vec.push_back(ngc->newstr(iter.first)); + } + } + ngc->temp = nil; return res; } ``` @@ -572,21 +570,16 @@ var builtin_keys(var* local,gc& ngc) 这些工作都完成之后,在内置函数注册表中填写它在nasal中的别名,并且在表中填对这个函数的函数指针: ```C++ -struct func -{ - const char* name; - var (*func)(var*,gc&); -} builtin[]= -{ - {"__print",builtin_print}, - {nullptr, nullptr } +nasal_builtin_table builtin[] = { + {"__print", builtin_print}, + {nullptr, nullptr} }; ``` 最后,将其包装到nasal文件中: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print(elems); }; ``` @@ -594,7 +587,7 @@ var print=func(elems...){ 事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对: ```javascript -var print=func(elems...){ +var print = func(elems...) { return __print; }; ``` @@ -705,7 +698,7 @@ Windows(`.dll`): use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; -for(var i = 1; i<30; i+=1) +for(var i = 1; i<30; i += 1) println(dylib.dlcall(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -723,7 +716,7 @@ use std.dylib; var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); var fib = dlhandle.fib; var invoke = dylib.limitcall(1); # this means the called function has only one parameter -for(var i = 1; i<30; i+=1) +for(var i = 1; i<30; i += 1) println(invoke(fib, i)); dylib.dlclose(dlhandle.lib); ``` @@ -767,7 +760,7 @@ dylib.dlclose(dlhandle.lib);
自定义类型(开发者教程) -创建一个自定义类型现在不是很困难。下面是使用示例: +创建一个自定义类型很容易。下面是使用示例: ```c++ const auto ghost_for_test = "ghost_for_test"; @@ -785,19 +778,31 @@ void ghost_for_test_destructor(void* ptr) { var create_new_ghost(var* args, usize size, gc* ngc) { var res = ngc->alloc(vm_obj); // 创建自定义类型 - res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32); + res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32); return res; } +var set_new_ghost(var* args, usize size, gc* ngc) { + var res = args[0]; + if (!res.object_check(ghost_for_test)) { + std::cout << "set_new_ghost: not ghost for test type.\n"; + return nil; + } + f64 num = args[1].num(); + *(reinterpret_cast(res.ghost().pointer)) = static_cast(num); + std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; + return nil; +} + var print_new_ghost(var* args, usize size, gc* ngc) { var res = args[0]; // 用自定义类型的名字来检查是否是正确的自定义类型 - if (!res.objchk(ghost_for_test)) { + if (!res.object_check(ghost_for_test)) { std::cout << "print_new_ghost: not ghost for test type.\n"; return nil; } - std::cout << "print_new_ghost: " << res.obj() << " result = " - << *((u32*)res.obj().ptr) << "\n"; + std::cout << "print_new_ghost: " << res.ghost() << " result = " + << *((u32*)res.ghost().pointer) << "\n"; return nil; } ``` @@ -814,7 +819,7 @@ var print_new_ghost(var* args, usize size, gc* ngc) { 我们使用下面的这个函数检测是否是正确的自定义类型: -`bool var::objchk(const std::string&);` +`bool var::object_check(const std::string&);` 参数是自定义类型的类型名。 @@ -832,7 +837,7 @@ var print_new_ghost(var* args, usize size, gc* ngc) { 在Andy的解释器中: ```javascript -foreach(i;[0,1,2,3]) +foreach(i; [0, 1, 2, 3]) print(i) ``` @@ -844,7 +849,7 @@ foreach(i;[0,1,2,3]) code: undefined symbol "i" --> test.nas:1:9 | -1 | foreach(i;[0,1,2,3]) +1 | foreach(i; [0, 1, 2, 3]) | ^ undefined symbol "i" code: undefined symbol "i" @@ -866,8 +871,7 @@ code: undefined symbol "i" `die`函数用于直接抛出错误并终止执行。 ```javascript -func() -{ +func() { println("hello"); die("error occurred this line"); return; @@ -877,17 +881,27 @@ func() ```javascript hello [vm] error: error occurred this line -[vm] native function error. -trace back: - 0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131) - 0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4) - 0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6) -vm stack (0x7fffcd21bc68 , limit 10, total 12): - 0x0000005b | null | - ... - 0x00000057 | str | <0x138ff60> error occurred t... - ... - 0x00000052 | nil | +[vm] error: error occurred in native function + +call trace (main) + call func@0x557513935710() {entry: 0x850} + +trace back (main) + 0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x5575138e8c40, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x5575138e8c50 + 0x00000a | nil | + 0x000009 | nil | + 0x000008 | str | <0x5575138d9190> error occurred t... + 0x000007 | nil | + 0x000006 | func | <0x5575139356f0> entry:0x850 + 0x000005 | pc | 0x85a + 0x000004 | addr | 0x0 ```
@@ -897,28 +911,41 @@ vm stack (0x7fffcd21bc68 , limit 10, total 12): 这是一个会导致栈溢出的例子: ```javascript -func(f){ +func(f) { return f(f); }( - func(f){ + func(f) { f(f); } )(); ``` ```javascript -[vm] stack overflow -trace back: - 0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5) - 0x000004fb 1349 same call(s) - 0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2) - 0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3) -vm stack (0x7fffd3781d58 , limit 10, total 8108): - 0x00001ffb | func | <0x15f8d90> entry:0x4f9 - 0x00001ffa | func | <0x15f8d90> entry:0x4f9 - 0x00001ff9 | pc | 0x4fb - ... - 0x00001ff2 | addr | 0x7fffd37a16e8 +[vm] error: stack overflow + +call trace (main) + call func@0x564106058620(f) {entry: 0x859} + --> 583 same call(s) + call func@0x5641060586c0(f) {entry: 0x851} + +trace back (main) + 0x000859 45 00 00 01 calll 0x1(a.nas:5) + 0x00085b 4a 00 00 01 callfv 0x1(a.nas:5) + 0x00085b 582 same call(s) + 0x000853 4a 00 00 01 callfv 0x1(a.nas:2) + 0x00085f 4a 00 00 01 callfv 0x1(a.nas:3) + +stack (0x56410600be00, limit 10, total 4096) + 0x000fff | func | <0x564106058600> entry:0x859 + 0x000ffe | pc | 0x85b + 0x000ffd | addr | 0x56410601bd20 + 0x000ffc | nil | + 0x000ffb | nil | + 0x000ffa | func | <0x564106058600> entry:0x859 + 0x000ff9 | nil | + 0x000ff8 | func | <0x564106058600> entry:0x859 + 0x000ff7 | pc | 0x85b + 0x000ff6 | addr | 0x56410601bcb0 ``` @@ -928,17 +955,19 @@ vm stack (0x7fffd3781d58 , limit 10, total 8108): 如果在执行的时候出现错误,程序会直接终止执行: ```javascript -func(){ +func() { return 0; }()[1]; ``` ```javascript -[vm] callv: must call a vector/hash/string -trace back: - 0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3) -vm stack (0x7fffff539c28 , limit 10, total 1): - 0x00000050 | num | 0 +[vm] error: must call a vector/hash/string but get number + +trace back (main) + 0x000854 47 00 00 00 callv 0x0(a.nas:3) + +stack (0x564993f462b0, limit 10, total 1) + 0x000000 | num | 0 ``` @@ -950,35 +979,48 @@ vm stack (0x7fffff539c28 , limit 10, total 1): ```javascript hello [vm] error: error occurred this line -[vm] error: native function error +[vm] error: error occurred in native function + +call trace (main) + call func@0x55dcb5b8fbf0() {entry: 0x850} + trace back (main) - 0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131) - 0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4) - 0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6) -vm stack (0x7fffe0ffed90 , limit 10, total 12) - 0x0000004a | null | - 0x00000049 | pc | 0x553 - 0x00000048 | addr | 0x7fffe0ffeda0 - ... - 0x00000041 | nil | + 0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150) + 0x000856 4a 00 00 01 callfv 0x1(a.nas:3) + 0x00085a 4a 00 00 00 callfv 0x0(a.nas:5) + +stack (0x55dcb5b43120, limit 10, total 14) + 0x00000d | null | + 0x00000c | pc | 0x856 + 0x00000b | addr | 0x55dcb5b43130 + 0x00000a | nil | + 0x000009 | nil | + 0x000008 | str | <0x55dcb5b33670> error occurred t... + 0x000007 | nil | + 0x000006 | func | <0x55dcb5b8fbd0> entry:0x850 + 0x000005 | pc | 0x85a + 0x000004 | addr | 0x0 + registers (main) - [ pc ] | pc | 0xb0 - [ global ] | addr | 0x7fffe0ffe9a0 - [ localr ] | addr | 0x7fffe0ffedf0 - [ memr ] | addr | 0x0 - [ canary ] | addr | 0x7fffe1002990 - [ top ] | addr | 0x7fffe0ffee40 - [ funcr ] | func | <0x677cd0> entry:0xb0 - [ upvalr ] | nil | -global (0x7fffe0ffe9a0 ) - 0x00000000 | func | <0x65fb00> entry:0x5 - 0x00000001 | func | <0x65fb20> entry:0xd + [pc ] | pc | 0x547 + [global] | addr | 0x55dcb5b53130 + [local ] | addr | 0x55dcb5b43190 + [memr ] | addr | 0x0 + [canary] | addr | 0x55dcb5b53110 + [top ] | addr | 0x55dcb5b431f0 + [funcr ] | func | <0x55dcb5b65620> entry:0x547 + [upval ] | nil | + +global (0x55dcb5b53130) + 0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val] + 0x000001 | vec | <0x55dcb5b64c20> [0 val] ... - 0x0000003d | func | <0x66bf00> entry:0x51f - 0x0000003e | hash | <0x65ffa0> {5 val} -local (0x7fffe0ffedf0 ) - 0x00000000 | nil | - 0x00000001 | str | <0x6cb630> error occurred t... + 0x00005e | func | <0x55dcb5b8fc70> entry:0x846 + +local (0x55dcb5b43190 <+7>) + 0x000000 | nil | + 0x000001 | str | <0x55dcb5b33670> error occurred t... + 0x000002 | nil | ``` @@ -1002,16 +1044,18 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: ---> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0) - 0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6) - 0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6) - 0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6) - 0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6) - 0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7) - 0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7) - 0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6) -vm stack (0x7fffd0259138 , limit 10, total 0) + 0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427) + 0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427) + 0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423) + 0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423) + 0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423) +--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1) + 0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1) + 0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1) + +stack (0x55ccd0a1b9d0, limit 10, total 0) >> ``` @@ -1034,23 +1078,26 @@ source code: for(var i=0;i<31;i+=1) print(fib(i),'\n'); + next bytecode: - 0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503) - 0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498) - 0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1) - 0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1) - 0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1) - 0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1) ---> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3) - 0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3) -vm stack (0x7fffd0259138 , limit 10, total 7) - 0x00000047 | pc | 0x566 - 0x00000046 | addr | 0x0 - 0x00000045 | nil | - 0x00000044 | num | 0 - 0x00000043 | nil | - 0x00000042 | nil | - 0x00000041 | func | <0x88d2f0> entry:0x5 + 0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1) +--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3) + 0x000853 40 00 08 56 jf 0x856(test/fib.nas:3) + 0x000854 45 00 00 01 calll 0x1(test/fib.nas:3) + 0x000855 56 00 00 00 ret 0x0(test/fib.nas:3) + 0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4) + 0x000857 45 00 00 01 calll 0x1(test/fib.nas:4) + +stack (0x55ccd0a1b9d0, limit 10, total 8) + 0x000007 | pc | 0x869 + 0x000006 | addr | 0x0 + 0x000005 | nil | + 0x000004 | nil | + 0x000003 | num | 0 + 0x000002 | nil | + 0x000001 | nil | + 0x000000 | func | <0x55ccd0a58fa0> entry:0x487 >> ``` @@ -1077,3 +1124,22 @@ Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31) >>> ``` + +试试引入 `std/json.nas` 模块 ~ + +```bash +[nasal-repl] Initializating enviroment... +[nasal-repl] Initialization complete. + +Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30) +.h, .help | show help +.e, .exit | quit the REPL +.q, .quit | quit the REPL +.c, .clear | clear the screen +.s, .source | show source code + +>>> use std.json; +{stringify:func(..) {..},parse:func(..) {..}} + +>>> +``` diff --git a/doc/namespace.md b/doc/namespace.md index de183f3c..8c3be10b 100644 --- a/doc/namespace.md +++ b/doc/namespace.md @@ -62,3 +62,27 @@ var example_module = func { }; }(); ``` + +## Import a module + +Here is a module named `std/example_module.nas`: + +```nasal +var a = 1; +``` + +Then there's a script file named `test.nas`, import module in this file using this way: + +```nasal +use std.example_module; + +println(example_module.a); # 1 +``` + +Or this way: + +```nasal +import("std/example_module.nas"); + +println(example_module.a); # 1 +``` diff --git a/doc/nasal-http-test-web.html b/doc/nasal-http-test-web.html index 234f247f..d2c2e83c 100644 --- a/doc/nasal-http-test-web.html +++ b/doc/nasal-http-test-web.html @@ -33,6 +33,7 @@

 Nasal | Not another scripting language!

+
diff --git a/module/fib.cpp b/module/fib.cpp index 529d77b9..c54831a1 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -64,7 +64,7 @@ var set_new_ghost(var* args, usize size, gc* ngc) { return nil; } f64 num = args[1].num(); - *((u32*)res.ghost().pointer) = static_cast(num); + *(reinterpret_cast(res.ghost().pointer)) = static_cast(num); std::cout << "set_new_ghost: successfully set ghost = " << num << "\n"; return nil; } diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 2ac1069c..d44fc9b5 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -99,7 +99,7 @@ var nas_connect(var* args, usize size, gc* ngc) { memcpy(&addr.sin_addr, entry->h_addr, entry->h_length); return var::num(static_cast(connect( args[0].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), sizeof(sockaddr_in) ))); } @@ -114,7 +114,7 @@ var nas_accept(var* args, usize size, gc* ngc) { #else int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen); #endif - var res=ngc->temp = ngc->alloc(vm_hash); + var res = ngc->temp = ngc->alloc(vm_hash); auto& hash = res.hash().elems; hash["sd"] = var::num(static_cast(client_sd)); hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr)); diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index 47c13d62..72743f53 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -260,7 +260,7 @@ var builtin_keys(context* ctx, gc* ngc) { vec.push_back(ngc->newstr(iter.first)); } } - ngc->temp=nil; + ngc->temp = nil; return res; } diff --git a/test/httptest.nas b/test/httptest.nas index 63f5a1d4..ff6edf56 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -320,6 +320,7 @@ while(1){ elsif(path=="/license") http.send(client,respond.ok(io.readfile("./LICENSE"))); elsif(path=="/doc/pic/nasal.png" or + path=="/doc/pic/social.png" or path=="/doc/pic/benchmark.png" or path=="/doc/pic/mandelbrot.png" or path=="/doc/pic/feigenbaum.png" or From 8a160dc7f2790bd007a940876ea72f63ca7c412c Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 5 Nov 2023 00:10:26 +0800 Subject: [PATCH 10/12] :sparkles: use reinterpret_cast as pointer cast --- module/nasocket.cpp | 22 +++++++--- src/coroutine.cpp | 2 +- src/dylib_lib.cpp | 4 +- src/nasal_import.cpp | 6 +-- src/nasal_parse.cpp | 8 +++- src/optimizer.cpp | 102 +++++++++++++++++++++++++------------------ 6 files changed, 86 insertions(+), 58 deletions(-) diff --git a/module/nasocket.cpp b/module/nasocket.cpp index d44fc9b5..7b692c6f 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -71,7 +71,7 @@ var nas_bind(var* args, usize size, gc* ngc) { server.sin_port = htons(args[2].num()); return var::num(static_cast(bind( args[0].num(), - (sockaddr*)&server, + reinterpret_cast(&server), sizeof(server) ))); } @@ -110,9 +110,17 @@ var nas_accept(var* args, usize size, gc* ngc) { sockaddr_in client; int socklen = sizeof(sockaddr_in); #ifdef _WIN32 - int client_sd = accept(args[0].num(), (sockaddr*)&client, &socklen); + int client_sd = accept( + args[0].num(), + reinterpret_cast(&client), + &socklen + ); #else - int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen); + int client_sd = accept( + args[0].num(), + reinterpret_cast(&client), + reinterpret_cast(&socklen) + ); #endif var res = ngc->temp = ngc->alloc(vm_hash); auto& hash = res.hash().elems; @@ -159,7 +167,7 @@ var nas_sendto(var* args, usize size, gc* ngc) { args[3].str().c_str(), args[3].str().length(), args[4].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), sizeof(sockaddr_in) ))); } @@ -205,7 +213,7 @@ var nas_recvfrom(var* args, usize size, gc* ngc) { buf, args[1].num(), args[2].num(), - (sockaddr*)&addr, + reinterpret_cast(&addr), &socklen ); #else @@ -214,8 +222,8 @@ var nas_recvfrom(var* args, usize size, gc* ngc) { buf, args[1].num(), args[2].num(), - (sockaddr*)&addr, - (socklen_t*)&socklen + reinterpret_cast(&addr), + reinterpret_cast(&socklen) ); #endif hash["size"] = var::num(static_cast(recvsize)); diff --git a/src/coroutine.cpp b/src/coroutine.cpp index c0f5003b..3aeed490 100644 --- a/src/coroutine.cpp +++ b/src/coroutine.cpp @@ -45,7 +45,7 @@ var builtin_cocreate(context* ctx, gc* ngc) { coroutine.ctx.top++; // store old localr on stack - coroutine.ctx.top[0] = var::addr((var*)nullptr); + coroutine.ctx.top[0] = var::addr(nullptr); coroutine.ctx.top++; // store old pc on stack diff --git a/src/dylib_lib.cpp b/src/dylib_lib.cpp index 494466d9..a3402cc6 100644 --- a/src/dylib_lib.cpp +++ b/src/dylib_lib.cpp @@ -53,9 +53,9 @@ var builtin_dlopen(context* ctx, gc* ngc) { // get "get" function, to get the register table #ifdef _WIN32 - void* register_table_get_function = (void*)GetProcAddress( + void* register_table_get_function = reinterpret_cast(GetProcAddress( static_cast(library_object.ghost().pointer), "get" - ); + )); #else void* register_table_get_function = dlsym( library_object.ghost().pointer, "get" diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index 874cc028..2904b824 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -113,11 +113,11 @@ bool linker::import_check(expr* node) { if (call_node->get_calls().size()!=1) { return false; } - - if (call_node->get_calls()[0]->get_type()!=expr_type::ast_callf) { + auto maybe_func_call = call_node->get_calls()[0]; + if (maybe_func_call->get_type()!=expr_type::ast_callf) { return false; } - auto func_call = (call_function*)call_node->get_calls()[0]; + auto func_call = reinterpret_cast(maybe_func_call); if (func_call->get_argument().size()!=1) { return false; } diff --git a/src/nasal_parse.cpp b/src/nasal_parse.cpp index 60d4dbc7..e62ff280 100644 --- a/src/nasal_parse.cpp +++ b/src/nasal_parse.cpp @@ -132,9 +132,13 @@ bool parse::check_func_end(expr* node) { if (type==expr_type::ast_func) { return true; } else if (type==expr_type::ast_def) { - return check_func_end(((definition_expr*)node)->get_value()); + return check_func_end( + reinterpret_cast(node)->get_value() + ); } else if (type==expr_type::ast_assign) { - return check_func_end(((assignment_expr*)node)->get_right()); + return check_func_end( + reinterpret_cast(node)->get_right() + ); } return false; } diff --git a/src/optimizer.cpp b/src/optimizer.cpp index 25bd769f..357f0f56 100644 --- a/src/optimizer.cpp +++ b/src/optimizer.cpp @@ -12,7 +12,8 @@ void optimizer::const_string( const auto& left = left_node->get_content(); const auto& right = right_node->get_content(); node->set_optimized_string( - new string_literal(node->get_location(), left+right)); + new string_literal(node->get_location(), left+right) + ); } void optimizer::const_number( @@ -43,7 +44,8 @@ void optimizer::const_number( return; } node->set_optimized_number( - new number_literal(node->get_location(), res)); + new number_literal(node->get_location(), res) + ); } void optimizer::const_number( @@ -62,46 +64,56 @@ void optimizer::const_number( return; } node->set_optimized_number( - new number_literal(node->get_location(), res)); + new number_literal(node->get_location(), res) + ); } bool optimizer::visit_binary_operator(binary_operator* node) { - node->get_left()->accept(this); - node->get_right()->accept(this); + auto left_node = node->get_left(); + auto right_node = node->get_right(); + left_node->accept(this); + right_node->accept(this); + number_literal* left_num_node = nullptr; number_literal* right_num_node = nullptr; string_literal* left_str_node = nullptr; string_literal* right_str_node = nullptr; - if (node->get_left()->get_type()==expr_type::ast_num) { - left_num_node = (number_literal*)node->get_left(); - } else if (node->get_left()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_left())->get_optimized_number()) { - left_num_node = ((binary_operator*)node->get_left())->get_optimized_number(); - } else if (node->get_left()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_left())->get_optimized_number()) { - left_num_node = ((unary_operator*)node->get_left())->get_optimized_number(); + if (left_node->get_type()==expr_type::ast_num) { + left_num_node = reinterpret_cast(left_node); + } else if (left_node->get_type()==expr_type::ast_binary && + reinterpret_cast(left_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(left_node); + left_num_node = optimized->get_optimized_number(); + } else if (left_node->get_type()==expr_type::ast_unary && + reinterpret_cast(left_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(left_node); + left_num_node = optimized->get_optimized_number(); } - if (node->get_right()->get_type()==expr_type::ast_num) { - right_num_node = (number_literal*)node->get_right(); - } else if (node->get_right()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_right())->get_optimized_number()) { - right_num_node = ((binary_operator*)node->get_right())->get_optimized_number(); - } else if (node->get_right()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_right())->get_optimized_number()) { - right_num_node = ((unary_operator*)node->get_right())->get_optimized_number(); + if (right_node->get_type()==expr_type::ast_num) { + right_num_node = reinterpret_cast(right_node); + } else if (right_node->get_type()==expr_type::ast_binary && + reinterpret_cast(right_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(right_node); + right_num_node = optimized->get_optimized_number(); + } else if (right_node->get_type()==expr_type::ast_unary && + reinterpret_cast(right_node)->get_optimized_number()) { + auto optimized = reinterpret_cast(right_node); + right_num_node = optimized->get_optimized_number(); } - if (node->get_left()->get_type()==expr_type::ast_str) { - left_str_node = (string_literal*)node->get_left(); - } else if (node->get_left()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_left())->get_optimized_string()) { - left_str_node = ((binary_operator*)node->get_left())->get_optimized_string(); + if (left_node->get_type()==expr_type::ast_str) { + left_str_node = reinterpret_cast(left_node); + } else if (left_node->get_type()==expr_type::ast_binary && + reinterpret_cast(left_node)->get_optimized_string()) { + auto optimized = reinterpret_cast(left_node); + left_str_node = optimized->get_optimized_string(); } - if (node->get_right()->get_type()==expr_type::ast_str) { - right_str_node = (string_literal*)node->get_right(); - } else if (node->get_right()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_right())->get_optimized_string()) { - right_str_node = ((binary_operator*)node->get_right())->get_optimized_string(); + if (right_node->get_type()==expr_type::ast_str) { + right_str_node = reinterpret_cast(right_node); + } else if (right_node->get_type()==expr_type::ast_binary && + reinterpret_cast(right_node)->get_optimized_string()) { + auto optimized = reinterpret_cast(right_node); + right_str_node = optimized->get_optimized_string(); } if (left_num_node && right_num_node) { const_number(node, left_num_node, right_num_node); @@ -115,19 +127,23 @@ bool optimizer::visit_binary_operator(binary_operator* node) { } bool optimizer::visit_unary_operator(unary_operator* node) { - node->get_value()->accept(this); - number_literal* value_node = nullptr; - if (node->get_value()->get_type()==expr_type::ast_num) { - value_node = (number_literal*)node->get_value(); - } else if (node->get_value()->get_type()==expr_type::ast_binary && - ((binary_operator*)node->get_value())->get_optimized_number()) { - value_node = ((binary_operator*)node->get_value())->get_optimized_number(); - } else if (node->get_value()->get_type()==expr_type::ast_unary && - ((unary_operator*)node->get_value())->get_optimized_number()) { - value_node = ((unary_operator*)node->get_value())->get_optimized_number(); + auto value = node->get_value(); + value->accept(this); + + number_literal* num_node = nullptr; + if (value->get_type()==expr_type::ast_num) { + num_node = reinterpret_cast(value); + } else if (value->get_type()==expr_type::ast_binary && + reinterpret_cast(value)->get_optimized_number()) { + auto optimized = reinterpret_cast(value); + num_node = optimized->get_optimized_number(); + } else if (value->get_type()==expr_type::ast_unary && + reinterpret_cast(value)->get_optimized_number()) { + auto optimized = reinterpret_cast(value); + num_node = optimized->get_optimized_number(); } - if (value_node) { - const_number(node, value_node); + if (num_node) { + const_number(node, num_node); } return true; } From 336139dcae46cd53da577112132443fc943c0501 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 5 Nov 2023 20:30:54 +0800 Subject: [PATCH 11/12] :bug: fix segfault in multi-assign/multi-def codegen --- src/nasal_codegen.cpp | 75 ++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index bf4b3321..922c532e 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -312,9 +312,12 @@ void codegen::call_gen(call_expr* node) { } for(auto i : node->get_calls()) { switch(i->get_type()) { - case expr_type::ast_callh: call_hash_gen((call_hash*)i); break; - case expr_type::ast_callv: call_vector_gen((call_vector*)i); break; - case expr_type::ast_callf: call_func_gen((call_function*)i); break; + case expr_type::ast_callh: + call_hash_gen(reinterpret_cast(i)); break; + case expr_type::ast_callv: + call_vector_gen(reinterpret_cast(i)); break; + case expr_type::ast_callf: + call_func_gen(reinterpret_cast(i)); break; default: break; } } @@ -385,8 +388,9 @@ void codegen::call_func_gen(call_function* node) { node->get_argument()[0]->get_type()==expr_type::ast_pair) { emit(op_newh, 0, node->get_location()); for(auto child : node->get_argument()) { - calc_gen(((hash_pair*)child)->get_value()); - const auto& field_name = ((hash_pair*)child)->get_name(); + auto pair_node = reinterpret_cast(child); + calc_gen(pair_node->get_value()); + const auto& field_name = pair_node->get_name(); regist_str(field_name); emit(op_happ, const_string_map.at(field_name), child->get_location()); } @@ -415,7 +419,7 @@ void codegen::mcall(expr* node) { } // generate symbol call if node is just ast_id and return if (node->get_type()==expr_type::ast_id) { - mcall_id((identifier*)node); + mcall_id(reinterpret_cast(node)); return; } // generate call expression until the last sub-node @@ -504,14 +508,16 @@ void codegen::multi_def(definition_expr* node) { // (var a,b,c) = (c,b,a); if (node->get_tuple()) { auto& vals = node->get_tuple()->get_elements(); - if (identifiers.size()vals.size()) { die("lack values in multi-definition", node->get_tuple()->get_location() ); - } else if (identifiers.size()>vals.size()) { + return; + } else if (identifiers.size()get_tuple()->get_location() ); + return; } for(usize i = 0; iget_right()); // get memory space of left identifier - mcall_id((identifier*)node->get_left()); + mcall_id(reinterpret_cast(node->get_left())); // check memory get operand type and replace it with load operand switch(code.back().op) { case op_mcallg: code.back().op = op_loadg; break; @@ -699,21 +705,40 @@ void codegen::assignment_statement(assignment_expr* node) { } void codegen::multi_assign_gen(multi_assign* node) { - if (node->get_value()->get_type()==expr_type::ast_tuple && - node->get_tuple()->get_elements().size()<((tuple_expr*)node->get_value())->get_elements().size()) { - die("lack values in multi-assignment", node->get_value()->get_location()); - } else if (node->get_value()->get_type()==expr_type::ast_tuple && - node->get_tuple()->get_elements().size()>((tuple_expr*)node->get_value())->get_elements().size()) { - die("too many values in multi-assignment", node->get_value()->get_location()); + auto tuple_node = node->get_tuple(); + auto value_node = node->get_value(); + if (value_node->get_type()==expr_type::ast_tuple) { + auto tuple_size = tuple_node->get_elements().size(); + auto value_size = reinterpret_cast(value_node) + ->get_elements().size(); + if (tuple_size>value_size) { + die( + "lack value(s) in multi-assignment, expect " + + std::to_string(tuple_size) + " but get " + + std::to_string(value_size), + value_node->get_location() + ); + return; + } else if (tuple_sizeget_location() + ); + return; + } } - i32 size = node->get_tuple()->get_elements().size(); + i32 size = tuple_node->get_elements().size(); // generate multiple assignment: (a, b, c) = (1, 2, 3); - if (node->get_value()->get_type()==expr_type::ast_tuple) { + if (value_node->get_type()==expr_type::ast_tuple) { + const auto& value_tuple = reinterpret_cast(value_node) + ->get_elements(); for(i32 i = size-1; i>=0; --i) { - calc_gen(((tuple_expr*)node->get_value())->get_elements()[i]); + calc_gen(value_tuple[i]); } - auto& tuple = node->get_tuple()->get_elements(); + auto& tuple = tuple_node->get_elements(); for(i32 i = 0; iget_value()); - auto& tuple = node->get_tuple()->get_elements(); + calc_gen(value_node); + auto& tuple = tuple_node->get_elements(); for(i32 i = 0; iget_value()->get_location()); + emit(op_callvi, i, value_node->get_location()); mcall(tuple[i]); // use load operands to avoid meq's pop operand // and this operation changes local and global value directly @@ -1188,7 +1213,7 @@ void codegen::block_gen(code_block* node) { if (need_repl_output && local.empty()) { repl_mode_info_output_gen(tmp); } else { - check_id_exist((identifier*)tmp); + check_id_exist(reinterpret_cast(tmp)); } break; case expr_type::ast_nil: @@ -1200,7 +1225,7 @@ void codegen::block_gen(code_block* node) { } break; case expr_type::ast_cond: - cond_gen((condition_expr*)tmp); break; + cond_gen(reinterpret_cast(tmp)); break; case expr_type::ast_continue: continue_ptr.front().push_back(code.size()); emit(op_jmp, 0, tmp->get_location()); @@ -1225,7 +1250,7 @@ void codegen::block_gen(code_block* node) { case expr_type::ast_multi_assign: statement_generation(tmp); break; case expr_type::ast_ret: - ret_gen((return_expr*)tmp); break; + ret_gen(reinterpret_cast(tmp)); break; default: break; } } From e8c8a6446b2973e45879f76c4ded73b5433f5a10 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 5 Nov 2023 21:25:20 +0800 Subject: [PATCH 12/12] :sparkles: use reinterpret_cast and static_cast --- src/nasal_builtin.cpp | 6 +- src/nasal_codegen.cpp | 212 ++++++++++++++++++++++++------------------ src/nasal_codegen.h | 18 ++-- test/md5compare.nas | 107 +++++++++++---------- 4 files changed, 193 insertions(+), 150 deletions(-) diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index 72743f53..0aec1684 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -309,8 +309,8 @@ var builtin_substr(context* ctx, gc* ngc) { if (len.type!=vm_num || len.num()<0) { return nas_err("substr", "\"length\" should be number >= 0"); } - usize begin = (usize)beg.num(); - usize length = (usize)len.num(); + auto begin = static_cast(beg.num()); + auto length = static_cast(len.num()); if (begin>=str.str().length()) { return nas_err("susbtr", "begin index out of range: "+std::to_string(begin)); } @@ -397,7 +397,7 @@ var builtin_chr(context* ctx, gc* ngc) { }; auto num = static_cast(ctx->localr[1].num()); if (0<=num && num<128) { - return ngc->newstr((char)num); + return ngc->newstr(static_cast(num)); } else if (128<=num && num<256) { return ngc->newstr(extend[num-128]); } diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 922c532e..7c5a72c7 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -58,7 +58,7 @@ void codegen::check_id_exist(identifier* node) { ); } -void codegen::regist_num(const f64 num) { +void codegen::regist_number(const f64 num) { if (const_number_map.count(num)) { return; } @@ -67,7 +67,7 @@ void codegen::regist_num(const f64 num) { const_number_table.push_back(num); } -void codegen::regist_str(const std::string& str) { +void codegen::regist_string(const std::string& str) { if (const_string_map.count(str)) { return; } @@ -94,11 +94,11 @@ void codegen::find_symbol(code_block* node) { scope.insert(i.name); } // add symbol for codegen symbol check - add_symbol(i.name); + regist_symbol(i.name); } } -void codegen::add_symbol(const std::string& name) { +void codegen::regist_symbol(const std::string& name) { if (local.empty()) { if (global.count(name)) { return; @@ -150,25 +150,25 @@ void codegen::emit(u8 operation_code, u32 immediate_num, const span& location) { }); } -void codegen::num_gen(number_literal* node) { +void codegen::number_gen(number_literal* node) { f64 num = node->get_number(); - regist_num(num); + regist_number(num); emit(op_pnum, const_number_map.at(num), node->get_location()); } -void codegen::str_gen(string_literal* node) { +void codegen::string_gen(string_literal* node) { const auto& str = node->get_content(); - regist_str(str); + regist_string(str); emit(op_pstr, const_string_map.at(str), node->get_location()); } void codegen::bool_gen(bool_literal* node) { f64 num = node->get_flag()? 1:0; - regist_num(num); + regist_number(num); emit(op_pnum, const_number_map.at(num), node->get_location()); } -void codegen::vec_gen(vector_expr* node) { +void codegen::vector_gen(vector_expr* node) { for(auto child : node->get_elements()) { calc_gen(child); } @@ -180,7 +180,7 @@ void codegen::hash_gen(hash_expr* node) { for(auto child : node->get_members()) { calc_gen(child->get_value()); const auto& field_name = child->get_name(); - regist_str(field_name); + regist_string(field_name); emit(op_happ, const_string_map.at(field_name), child->get_location()); } } @@ -243,7 +243,7 @@ void codegen::func_gen(function* node) { tmp->get_location() ); } - regist_str(name); + regist_string(name); switch(tmp->get_parameter_type()) { case parameter::param_type::normal_parameter: emit(op_para, const_string_map.at(name), tmp->get_location()); @@ -256,7 +256,7 @@ void codegen::func_gen(function* node) { emit(op_dyn, const_string_map.at(name), tmp->get_location()); break; } - add_symbol(name); + regist_symbol(name); } code[newf].num = code.size()+1; // entry @@ -282,7 +282,7 @@ void codegen::func_gen(function* node) { while(local_symbol_find(arg)>=0) { arg = "0" + arg; } - add_symbol(arg); + regist_symbol(arg); // generate code block in_foreach_loop_level.push_back(0); @@ -323,7 +323,7 @@ void codegen::call_gen(call_expr* node) { } } -void codegen::call_id(identifier* node) { +void codegen::call_identifier(identifier* node) { const auto& name = node->get_name(); if (native_function_mapper.count(name)) { emit(op_callb, @@ -357,7 +357,7 @@ void codegen::call_id(identifier* node) { } void codegen::call_hash_gen(call_hash* node) { - regist_str(node->get_field()); + regist_string(node->get_field()); emit(op_callh, const_string_map.at(node->get_field()), node->get_location()); } @@ -391,7 +391,7 @@ void codegen::call_func_gen(call_function* node) { auto pair_node = reinterpret_cast(child); calc_gen(pair_node->get_value()); const auto& field_name = pair_node->get_name(); - regist_str(field_name); + regist_string(field_name); emit(op_happ, const_string_map.at(field_name), child->get_location()); } emit(op_callfh, 0, node->get_location()); @@ -419,7 +419,7 @@ void codegen::mcall(expr* node) { } // generate symbol call if node is just ast_id and return if (node->get_type()==expr_type::ast_id) { - mcall_id(reinterpret_cast(node)); + mcall_identifier(reinterpret_cast(node)); return; } // generate call expression until the last sub-node @@ -428,17 +428,22 @@ void codegen::mcall(expr* node) { for(usize i = 0; iget_calls().size()-1; ++i) { auto tmp = call_node->get_calls()[i]; switch(tmp->get_type()) { - case expr_type::ast_callh: call_hash_gen((call_hash*)tmp); break; - case expr_type::ast_callv: call_vector_gen((call_vector*)tmp); break; - case expr_type::ast_callf: call_func_gen((call_function*)tmp); break; + case expr_type::ast_callh: + call_hash_gen(reinterpret_cast(tmp)); break; + case expr_type::ast_callv: + call_vector_gen(reinterpret_cast(tmp)); break; + case expr_type::ast_callf: + call_func_gen(reinterpret_cast(tmp)); break; default: break; } } // the last sub-node will be used to generate memory call expression auto tmp = call_node->get_calls().back(); switch(tmp->get_type()) { - case expr_type::ast_callh: mcall_hash((call_hash*)tmp); break; - case expr_type::ast_callv: mcall_vec((call_vector*)tmp); break; + case expr_type::ast_callh: + mcall_hash(reinterpret_cast(tmp)); break; + case expr_type::ast_callv: + mcall_vec(reinterpret_cast(tmp)); break; case expr_type::ast_callf: die("bad left-value: function call", tmp->get_location()); break; default: @@ -446,7 +451,7 @@ void codegen::mcall(expr* node) { } } -void codegen::mcall_id(identifier* node) { +void codegen::mcall_identifier(identifier* node) { const auto& name = node->get_name(); if (native_function_mapper.count(name)) { die("cannot modify native function", node->get_location()); @@ -484,7 +489,7 @@ void codegen::mcall_vec(call_vector* node) { } void codegen::mcall_hash(call_hash* node) { - regist_str(node->get_field()); + regist_string(node->get_field()); emit(op_mcallh, const_string_map.at(node->get_field()), node->get_location()); } @@ -509,12 +514,16 @@ void codegen::multi_def(definition_expr* node) { if (node->get_tuple()) { auto& vals = node->get_tuple()->get_elements(); if (identifiers.size()>vals.size()) { - die("lack values in multi-definition", + die("lack values in multi-definition, expect " + + std::to_string(identifiers.size()) + " but get " + + std::to_string(vals.size()), node->get_tuple()->get_location() ); return; } else if (identifiers.size()get_tuple()->get_location() ); return; @@ -540,7 +549,7 @@ void codegen::multi_def(definition_expr* node) { emit(op_pop, 0, node->get_location()); } -void codegen::def_gen(definition_expr* node) { +void codegen::definition_gen(definition_expr* node) { if (node->get_variable_name() && node->get_tuple()) { die("cannot accept too many values", node->get_value()->get_location()); } @@ -562,8 +571,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_addeq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_addeqc, const_number_map[num], node->get_location()); } break; @@ -575,8 +585,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_subeq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_subeqc, const_number_map[num], node->get_location()); } break; @@ -588,8 +599,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_muleq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_muleqc, const_number_map[num], node->get_location()); } break; @@ -601,8 +613,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_num) { emit(op_diveq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_diveqc, const_number_map[num], node->get_location()); } break; @@ -614,8 +627,9 @@ void codegen::assignment_expression(assignment_expr* node) { if (node->get_right()->get_type()!=expr_type::ast_str) { emit(op_lnkeq, 0, node->get_location()); } else { - const auto& str = ((string_literal*)node->get_right())->get_content(); - regist_str(str); + const auto& str = reinterpret_cast( + node->get_right())->get_content(); + regist_string(str); emit(op_lnkeqc, const_string_map[str], node->get_location()); } break; @@ -655,7 +669,7 @@ void codegen::gen_assignment_equal_statement(assignment_expr* node) { // generate symbol load calc_gen(node->get_right()); // get memory space of left identifier - mcall_id(reinterpret_cast(node->get_left())); + mcall_identifier(reinterpret_cast(node->get_left())); // check memory get operand type and replace it with load operand switch(code.back().op) { case op_mcallg: code.back().op = op_loadg; break; @@ -802,9 +816,12 @@ void codegen::loop_gen(expr* node) { break_ptr.push_front({}); switch(node->get_type()) { - case expr_type::ast_while: while_gen((while_expr*)node); break; - case expr_type::ast_for: for_gen((for_expr*)node); break; - case expr_type::ast_forei: forei_gen((forei_expr*)node); break; + case expr_type::ast_while: + while_gen(reinterpret_cast(node)); break; + case expr_type::ast_for: + for_gen(reinterpret_cast(node)); break; + case expr_type::ast_forei: + forei_gen(reinterpret_cast(node)); break; default: break; } } @@ -836,7 +853,7 @@ void codegen::for_gen(for_expr* node) { statement_generation(node->get_initial()); usize jmp_place = code.size(); if (node->get_condition()->get_type()==expr_type::ast_null) { - regist_num(1); + regist_number(1); emit(op_pnum, const_number_map.at(1), node->get_condition()->get_location()); } else { calc_gen(node->get_condition()); @@ -900,11 +917,11 @@ void codegen::statement_generation(expr* node) { switch(node->get_type()) { case expr_type::ast_null: break; case expr_type::ast_def: - def_gen((definition_expr*)node); break; + definition_gen(reinterpret_cast(node)); break; case expr_type::ast_multi_assign: - multi_assign_gen((multi_assign*)node); break; + multi_assign_gen(reinterpret_cast(node)); break; case expr_type::ast_assign: - assignment_statement((assignment_expr*)node); break; + assignment_statement(reinterpret_cast(node)); break; case expr_type::ast_nil: case expr_type::ast_num: case expr_type::ast_str: @@ -963,7 +980,7 @@ void codegen::and_gen(binary_operator* node) { void codegen::unary_gen(unary_operator* node) { // generate optimized result if (node->get_optimized_number()) { - num_gen(node->get_optimized_number()); + number_gen(node->get_optimized_number()); return; } @@ -981,11 +998,11 @@ void codegen::unary_gen(unary_operator* node) { void codegen::binary_gen(binary_operator* node) { // generate optimized result if (node->get_optimized_number()) { - num_gen(node->get_optimized_number()); + number_gen(node->get_optimized_number()); return; } if (node->get_optimized_string()) { - str_gen(node->get_optimized_string()); + string_gen(node->get_optimized_string()); return; } @@ -1029,8 +1046,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_add, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_addc, const_number_map.at(num), node->get_location()); } return; @@ -1040,8 +1058,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_sub, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_subc, const_number_map.at(num), node->get_location()); } return; @@ -1051,8 +1070,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_mul, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_mulc, const_number_map.at(num), node->get_location()); } return; @@ -1062,8 +1082,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_div, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_divc, const_number_map.at(num), node->get_location()); } return; @@ -1073,8 +1094,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_lnk, 0, node->get_location()); } else { - const auto& str = ((string_literal*)node->get_right())->get_content(); - regist_str(str); + const auto& str = reinterpret_cast( + node->get_right())->get_content(); + regist_string(str); emit(op_lnkc, const_string_map.at(str), node->get_location()); } break; @@ -1084,8 +1106,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_less, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_lessc, const_number_map.at(num), node->get_location()); } return; @@ -1095,8 +1118,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_leq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_leqc, const_number_map.at(num), node->get_location()); } return; @@ -1106,8 +1130,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_grt, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_grtc, const_number_map.at(num), node->get_location()); } return; @@ -1117,8 +1142,9 @@ void codegen::binary_gen(binary_operator* node) { calc_gen(node->get_right()); emit(op_geq, 0, node->get_location()); } else { - auto num = ((number_literal*)node->get_right())->get_number(); - regist_num(num); + auto num = reinterpret_cast(node->get_right()) + ->get_number(); + regist_number(num); emit(op_geqc, const_number_map.at(num), node->get_location()); } return; @@ -1143,33 +1169,38 @@ void codegen::calc_gen(expr* node) { case expr_type::ast_nil: emit(op_pnil, 0, node->get_location()); break; case expr_type::ast_num: - num_gen((number_literal*)node); break; + number_gen(reinterpret_cast(node)); break; case expr_type::ast_str: - str_gen((string_literal*)node); break; + string_gen(reinterpret_cast(node)); break; case expr_type::ast_id: - call_id((identifier*)node); break; + call_identifier(reinterpret_cast(node)); break; case expr_type::ast_bool: - bool_gen((bool_literal*)node); break; + bool_gen(reinterpret_cast(node)); break; case expr_type::ast_vec: - vec_gen((vector_expr*)node); break; + vector_gen(reinterpret_cast(node)); break; case expr_type::ast_hash: - hash_gen((hash_expr*)node); break; + hash_gen(reinterpret_cast(node)); break; case expr_type::ast_func: - func_gen((function*)node); break; + func_gen(reinterpret_cast(node)); break; case expr_type::ast_call: - call_gen((call_expr*)node); break; + call_gen(reinterpret_cast(node)); break; case expr_type::ast_assign: - assignment_expression((assignment_expr*)node); break; + assignment_expression( + reinterpret_cast(node) + ); + break; case expr_type::ast_ternary: - trino_gen((ternary_operator*)node); break; + trino_gen(reinterpret_cast(node)); break; case expr_type::ast_unary: - unary_gen((unary_operator*)node); break; + unary_gen(reinterpret_cast(node)); break; case expr_type::ast_binary: - binary_gen((binary_operator*)node); break; + binary_gen(reinterpret_cast(node)); break; case expr_type::ast_def: // definition in calculation only should be single def - single_def((definition_expr*)node); - call_id(((definition_expr*)node)->get_variable_name()); + single_def(reinterpret_cast(node)); + call_identifier( + (reinterpret_cast(node))->get_variable_name() + ); break; default: break; } @@ -1177,11 +1208,16 @@ void codegen::calc_gen(expr* node) { void codegen::repl_mode_info_output_gen(expr* node) { switch(node->get_type()) { - case expr_type::ast_id: call_id((identifier*)node); break; - case expr_type::ast_nil: emit(op_pnil, 0, node->get_location()); break; - case expr_type::ast_num: num_gen((number_literal*)node); break; - case expr_type::ast_str: str_gen((string_literal*)node); break; - case expr_type::ast_bool: bool_gen((bool_literal*)node); break; + case expr_type::ast_id: + call_identifier(reinterpret_cast(node)); break; + case expr_type::ast_nil: + emit(op_pnil, 0, node->get_location()); break; + case expr_type::ast_num: + number_gen(reinterpret_cast(node)); break; + case expr_type::ast_str: + string_gen(reinterpret_cast(node)); break; + case expr_type::ast_bool: + bool_gen(reinterpret_cast(node)); break; default: return; } // generate repl output operand @@ -1273,9 +1309,9 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) { in_foreach_loop_level.push_back(0); // add special symbol globals, which is a hash stores all global variables - add_symbol("globals"); + regist_symbol("globals"); // add special symbol arg here, which is used to store command line args - add_symbol("arg"); + regist_symbol("arg"); // search global symbols first find_symbol(parse.tree()); diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index 4aaf212c..af1635cb 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -78,34 +78,34 @@ class codegen { err.err("code", loc, info); } - void regist_num(const f64); - void regist_str(const std::string&); + void regist_number(const f64); + void regist_string(const std::string&); void find_symbol(code_block*); - void add_symbol(const std::string&); + void regist_symbol(const std::string&); i32 local_symbol_find(const std::string&); i32 global_symbol_find(const std::string&); i32 upvalue_symbol_find(const std::string&); void emit(u8, u32, const span&); - void num_gen(number_literal*); - void str_gen(string_literal*); + void number_gen(number_literal*); + void string_gen(string_literal*); void bool_gen(bool_literal*); - void vec_gen(vector_expr*); + void vector_gen(vector_expr*); void hash_gen(hash_expr*); void func_gen(function*); void call_gen(call_expr*); - void call_id(identifier*); + void call_identifier(identifier*); void call_hash_gen(call_hash*); void call_vector_gen(call_vector*); void call_func_gen(call_function*); void mcall(expr*); - void mcall_id(identifier*); + void mcall_identifier(identifier*); void mcall_vec(call_vector*); void mcall_hash(call_hash*); void multi_def(definition_expr*); void single_def(definition_expr*); - void def_gen(definition_expr*); + void definition_gen(definition_expr*); void assignment_expression(assignment_expr*); void gen_assignment_equal_statement(assignment_expr*); void replace_left_assignment_with_load(const span&); diff --git a/test/md5compare.nas b/test/md5compare.nas index 29cd027a..09a8ef16 100644 --- a/test/md5compare.nas +++ b/test/md5compare.nas @@ -4,95 +4,102 @@ use std.file; srand(); -var compare=func() { - var ch=[ +var compare = func() { + var ch = [ "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+", "_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%", "^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?" ]; - return func(begin,end) { - var byte=0; - var total=end-begin; - var timestamp=maketimestamp(); + return func(begin, end) { + var byte = 0; + var total = end-begin; + var timestamp = maketimestamp(); timestamp.stamp(); - var bar=process_bar.high_resolution_bar(40); - for(var i=begin;i
- +