diff --git a/CMakeLists.txt b/CMakeLists.txt index f623951d..b230a315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/fg_props.cpp ${CMAKE_SOURCE_DIR}/src/bits_lib.cpp ${CMAKE_SOURCE_DIR}/src/io_lib.cpp + ${CMAKE_SOURCE_DIR}/src/json_lib.cpp ${CMAKE_SOURCE_DIR}/src/math_lib.cpp ${CMAKE_SOURCE_DIR}/src/dylib_lib.cpp ${CMAKE_SOURCE_DIR}/src/unix_lib.cpp @@ -84,4 +85,4 @@ target_link_libraries(mat module-used-object) add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp) target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src) -target_link_libraries(nasock module-used-object) \ No newline at end of file +target_link_libraries(nasock module-used-object) diff --git a/README.md b/README.md index 4bd68381..6bc2a99e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ![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.2-blue?style=flat-square&logo=github) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE) +![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github) > This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md) @@ -33,17 +33,21 @@ __Contact us if having great ideas to share!__ ## __Introduction__ +![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) + [Nasal](http://wiki.flightgear.org/Nasal_scripting_language) is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/). The designer is [Andy Ross](https://github.com/andyross). -This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`) -without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal). -But we really appreciate that Andy created this amazing programming language. +This interpreter is rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`). +We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter](https://github.com/andyross/nasal). This project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5), __GPL v2 license__ (since 2023/6). -### __Why writing this nasal interpreter?__ +### __Why writing this Nasal interpreter?__ 2019 summer, members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear, diff --git a/doc/README_zh.md b/doc/README_zh.md index c7efa099..b37df487 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -4,8 +4,8 @@ ![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.2-blue?style=flat-square&logo=github) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE) +![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github) > 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md) @@ -33,15 +33,20 @@ __如果有好的意见或建议,欢迎联系我们!__ ## __简介__ +![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) +![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) + [Nasal](http://wiki.flightgear.org/Nasal_scripting_language) -是一款语法与ECMAscript相似的编程语言,并作为运行脚本被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。 +是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。 该语言的设计者为 [Andy Ross](https://github.com/andyross)。 -该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++17`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言。 +该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 使用 `C++`(`-std=c++17`)重新实现,我们非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言: [Andy Ross 的 nasal 解释器](https://github.com/andyross/nasal)。 该项目使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始使用 __GPL v2__ 协议。 -### __我们为什么想要重新写一个nasal解释器?__ +### __我们为什么想要重新写一个 Nasal 解释器?__ 2019年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在Flightgear中提供的nasal控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。 diff --git a/doc/benchmark.md b/doc/benchmark.md index 6c3d3beb..417ba724 100644 --- a/doc/benchmark.md +++ b/doc/benchmark.md @@ -111,14 +111,12 @@ In 2022/2/17 update we added `\e` into the lexer. And the `bfcolored.nas` uses t ![mandelbrot](../doc/pic/mandelbrot.png) -## More nasal generated pictures - -![mandelbrotset](../doc/pic/mandelbrotset.png) - -![mandelbrotset_reverse](../doc/pic/mandelbrotset_reverse.png) - -![burningship](../doc/pic/burningship.png) - -![burningship_reverse](../doc/pic/burningship_reverse.png) - -![feigenbaum](../doc/pic/feigenbaum.png) +## More Nasal Generated Pictures + +|Mandelbrot Set|Mandelbrot Set|Julia Set| +|:----:|:----:|:----:| +|[mandelbrotset.nas](../test/mandelbrotset.nas)|[mandelbrotset.nas](../test/mandelbrotset.nas)|[juliaset.nas](../test/juliaset.nas)| +|![mandelbrotset](../doc/pic/mandelbrotset.png)|![mandelbrotset_reverse](../doc/pic/mandelbrotset_reverse.png)|![juliaset](../doc/pic/juliaset.png)| +|__Burning Ship__|__Burning Ship__|__Feigenbaum__| +|[burningship.nas](../test/burningship.nas)|[burningship.nas](../test/burningship.nas)|[feigenbaum.nas](../test/feigenbaum.nas)| +|![burningship](../doc/pic/burningship.png)|![burningship_reverse](../doc/pic/burningship_reverse.png)|![feigenbaum](../doc/pic/feigenbaum.png)| \ No newline at end of file diff --git a/doc/pic/juliaset.png b/doc/pic/juliaset.png new file mode 100644 index 00000000..4ff8b2a9 Binary files /dev/null and b/doc/pic/juliaset.png differ diff --git a/doc/tutorial.md b/doc/tutorial.md index 1797a758..2cfef5ad 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -593,7 +593,7 @@ module_func_info func_tbl[] = { // the reason why using this way to get function pointer // is because `var` has constructors, which is not compatiable in C // so "extern "C" var fib" may get compilation warnings -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return func_tbl; } ``` diff --git a/doc/tutorial_zh.md b/doc/tutorial_zh.md index f97c1392..b6f70708 100644 --- a/doc/tutorial_zh.md +++ b/doc/tutorial_zh.md @@ -574,7 +574,7 @@ module_func_info func_tbl[] = { // 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的 // 有构造函数的类型作为返回值, 和C是不兼容的, 这导致 // 类似 "extern "C" var fib" 的写法会得到编译错误 -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return func_tbl; } ``` diff --git a/makefile b/makefile index 404ac74a..af03a182 100644 --- a/makefile +++ b/makefile @@ -9,7 +9,7 @@ else CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC endif -NASAL_HEADER=\ +NASAL_HEADER = \ src/ast_dumper.h\ src/ast_visitor.h\ src/nasal_ast.h\ @@ -32,11 +32,12 @@ NASAL_HEADER=\ src/io_lib.h\ src/math_lib.h\ src/dylib_lib.h\ + src/json_lib.h\ src/unix_lib.h\ src/coroutine.h\ src/repl.h -NASAL_OBJECT=\ +NASAL_OBJECT = \ build/nasal_err.o\ build/nasal_ast.o\ build/ast_visitor.o\ @@ -57,6 +58,7 @@ NASAL_OBJECT=\ build/math_lib.o\ build/unix_lib.o\ build/dylib_lib.o\ + build/json_lib.o\ build/coroutine.o\ build/nasal_type.o\ build/nasal_vm.o\ @@ -161,6 +163,13 @@ build/dylib_lib.o: \ src/dylib_lib.h src/dylib_lib.cpp | build $(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o +build/json_lib.o: \ + src/nasal.h\ + src/nasal_type.h\ + src/nasal_gc.h\ + src/json_lib.h src/json_lib.cpp | build + $(CXX) $(CXXFLAGS) src/json_lib.cpp -o build/json_lib.o + build/unix_lib.o: \ src/nasal.h\ src/nasal_type.h\ @@ -237,6 +246,7 @@ clean: .PHONY: test test:nasal + @ ./nasal test/argparse_test.nas @ ./nasal -e test/ascii-art.nas @ ./nasal -t -d test/bfs.nas @ ./nasal -t test/bigloop.nas diff --git a/module/fib.cpp b/module/fib.cpp index c54831a1..68e72648 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -42,18 +42,40 @@ var quick_fib(var* args, usize size, gc* ngc) { const auto ghost_for_test = "ghost_for_test"; +struct ghost_obj { + u32 number = 0; + var test_string = nil; +}; + +// if the dynamic library is closed, the pointer of this function will be unsafe +// make sure gc deletes the object before closing the dynamic library +// or just do not close the dynamic library... void ghost_for_test_destructor(void* ptr) { std::cout << "ghost_for_test::destructor (0x"; std::cout << std::hex << reinterpret_cast(ptr) << std::dec << ") {\n"; - delete static_cast(ptr); + delete static_cast(ptr); std::cout << " delete 0x" << std::hex; std::cout << reinterpret_cast(ptr) << std::dec << ";\n"; std::cout << "}\n"; } +void ghost_for_test_gc_marker(void* ptr, std::vector* bfs_queue) { + std::cout << "ghost_for_test::mark (0x"; + std::cout << std::hex << reinterpret_cast(ptr) << std::dec << ") {\n"; + bfs_queue->push_back(static_cast(ptr)->test_string); + std::cout << " mark 0x" << std::hex; + std::cout << reinterpret_cast(ptr) << std::dec << "->test_string;\n"; + std::cout << "}\n"; +} + var create_new_ghost(var* args, usize size, gc* ngc) { - var res = ngc->alloc(vm_obj); - res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32); + var res = ngc->alloc(vm_type::vm_ghost); + res.ghost().set( + ghost_for_test, + ghost_for_test_destructor, + ghost_for_test_gc_marker, + new ghost_obj + ); return res; } @@ -64,8 +86,10 @@ var set_new_ghost(var* args, usize size, gc* ngc) { 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"; + reinterpret_cast(res.ghost().pointer)->number = static_cast(num); + std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n"; + reinterpret_cast(res.ghost().pointer)->test_string = ngc->newstr("just for test"); + std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n"; return nil; } @@ -75,8 +99,11 @@ var print_new_ghost(var* args, usize size, gc* ngc) { std::cout << "print_new_ghost: not ghost for test type.\n"; return nil; } - std::cout << "print_new_ghost: " << res.ghost() << " result = " - << *((u32*)res.ghost().pointer) << "\n"; + std::cout << "print_new_ghost: " << res.ghost() << " number = " + << reinterpret_cast(res.ghost().pointer)->number + << " test_string = " + << reinterpret_cast(res.ghost().pointer)->test_string + << "\n"; return nil; } @@ -91,7 +118,7 @@ module_func_info func_tbl[] = { } -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return fib_module::func_tbl; } diff --git a/module/keyboard.cpp b/module/keyboard.cpp index 8586c581..6fbf05b8 100644 --- a/module/keyboard.cpp +++ b/module/keyboard.cpp @@ -7,6 +7,10 @@ #include #endif +#ifdef _MSC_VER +#pragma warning (disable:4996) +#endif + #ifdef _WIN32 #include #else @@ -106,7 +110,7 @@ module_func_info func_tbl[] = { {nullptr, nullptr} }; -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return func_tbl; } diff --git a/module/libfib.nas b/module/libfib.nas index 18819a7d..9df5c2c8 100644 --- a/module/libfib.nas +++ b/module/libfib.nas @@ -1,4 +1,5 @@ use std.dylib; +use std.os; var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so")); @@ -18,37 +19,40 @@ var _call = dylib.limitcall(1); var _test_call = dylib.limitcall(2); - var fib = func(x) { return _call(_fib, x) } - var qfib = func(x) { return _call(_qfib, x) } - var create_ghost = func() { return _zero_call(_create_ghost) } - var set_ghost = func(object, x) { return _test_call(_set_ghost, object, x) } - var print_ghost = func(object) { return _call(_print_ghost, object) } - var test_ghost = func() { var ghost = create_ghost(); print_ghost(nil); # err + print("\n"); print_ghost(ghost); # random + print("\n"); set_ghost(nil, 114); # err + print("\n"); set_ghost(ghost, 114); # success + print("\n"); + for(var i = 0; i<256; i+=1) { + var temp = []; # try to trigger gc + } + print("\n"); print_ghost(ghost); # 114 + print("\n"); } diff --git a/module/libkey.nas b/module/libkey.nas index 564266b8..26f46da1 100644 --- a/module/libkey.nas +++ b/module/libkey.nas @@ -1,4 +1,5 @@ use std.dylib; +use std.os; var ( kbhit, diff --git a/module/libmat.nas b/module/libmat.nas index be7ef605..cbf02c90 100644 --- a/module/libmat.nas +++ b/module/libmat.nas @@ -1,4 +1,5 @@ use std.dylib; +use std.os; var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so")); diff --git a/module/libsock.nas b/module/libsock.nas index d8ace4e5..e686cf1b 100644 --- a/module/libsock.nas +++ b/module/libsock.nas @@ -1,4 +1,5 @@ use std.dylib; +use std.os; var socket = func() { var lib = dylib.dlopen("libnasock"~(os.platform()=="windows"? ".dll":".so")); diff --git a/module/makefile b/module/makefile index d80702ea..17fe33bb 100644 --- a/module/makefile +++ b/module/makefile @@ -1,4 +1,4 @@ -.PHONY=clean all winall +.PHONY = clean all winall dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll diff --git a/module/matrix.cpp b/module/matrix.cpp index 5481af4a..3108427a 100644 --- a/module/matrix.cpp +++ b/module/matrix.cpp @@ -6,14 +6,14 @@ namespace nasal { var nas_vec2(var* args, usize size, gc* ngc) { - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(args[0]); res.vec().elems.push_back(args[1]); return res; } var nas_vec3(var* args, usize size, gc* ngc) { - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(args[0]); res.vec().elems.push_back(args[1]); res.vec().elems.push_back(args[2]); @@ -21,71 +21,71 @@ var nas_vec3(var* args, usize size, gc* ngc) { } var nas_vec2_add(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=2 || v1.size()!=2) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num())); return res; } var nas_vec2_sub(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=2 || v1.size()!=2) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num())); return res; } var nas_vec2_mult(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=2 || v1.size()!=2) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num())); return res; } var nas_vec2_div(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=2 || v1.size()!=2) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num())); return res; } var nas_vec2_neg(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=2) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(-v0[0].num())); res.vec().elems.push_back(var::num(-v0[1].num())); return res; } var nas_vec2_norm(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=2) @@ -93,14 +93,14 @@ var nas_vec2_norm(var* args, usize size, gc* ngc) { auto x = v0[0].num(); auto y = v0[1].num(); auto t = std::sqrt(x*x+y*y); - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(x/t)); res.vec().elems.push_back(var::num(y/t)); return res; } var nas_vec2_len(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=2) @@ -111,7 +111,7 @@ var nas_vec2_len(var* args, usize size, gc* ngc) { } var nas_vec2_dot(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; @@ -121,13 +121,13 @@ var nas_vec2_dot(var* args, usize size, gc* ngc) { } var nas_vec3_add(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=3 || v1.size()!=3) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num())); res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num())); @@ -135,13 +135,13 @@ var nas_vec3_add(var* args, usize size, gc* ngc) { } var nas_vec3_sub(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=3 || v1.size()!=3) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num())); res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num())); @@ -149,13 +149,13 @@ var nas_vec3_sub(var* args, usize size, gc* ngc) { } var nas_vec3_mult(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=3 || v1.size()!=3) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num())); res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num())); @@ -163,13 +163,13 @@ var nas_vec3_mult(var* args, usize size, gc* ngc) { } var nas_vec3_div(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; if (v0.size()!=3 || v1.size()!=3) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num())); res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num())); res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num())); @@ -177,12 +177,12 @@ var nas_vec3_div(var* args, usize size, gc* ngc) { } var nas_vec3_neg(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) return nil; - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(-v0[0].num())); res.vec().elems.push_back(var::num(-v0[1].num())); res.vec().elems.push_back(var::num(-v0[2].num())); @@ -190,7 +190,7 @@ var nas_vec3_neg(var* args, usize size, gc* ngc) { } var nas_vec3_norm(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) @@ -199,7 +199,7 @@ var nas_vec3_norm(var* args, usize size, gc* ngc) { auto y = v0[1].num(); auto z = v0[2].num(); auto t = std::sqrt(x*x+y*y+z*z); - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(x/t)); res.vec().elems.push_back(var::num(y/t)); res.vec().elems.push_back(var::num(z/t)); @@ -207,7 +207,7 @@ var nas_vec3_norm(var* args, usize size, gc* ngc) { } var nas_vec3_len(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) @@ -219,13 +219,13 @@ var nas_vec3_len(var* args, usize size, gc* ngc) { } var nas_rotate_x(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) return nil; auto angle = args[1].num(); - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num())); res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle))); res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle))); @@ -233,13 +233,13 @@ var nas_rotate_x(var* args, usize size, gc* ngc) { } var nas_rotate_y(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) return nil; auto angle = args[1].num(); - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle))); res.vec().elems.push_back(var::num(v0[1].num())); res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle))); @@ -247,13 +247,13 @@ var nas_rotate_y(var* args, usize size, gc* ngc) { } var nas_rotate_z(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec) + if (!args[0].is_vec()) return nil; auto& v0 = args[0].vec().elems; if (v0.size()!=3) return nil; auto angle = args[1].num(); - var res = ngc->alloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle))); res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle))); res.vec().elems.push_back(var::num(v0[2].num())); @@ -261,7 +261,7 @@ var nas_rotate_z(var* args, usize size, gc* ngc) { } var nas_vec3_dot(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_vec || args[1].type!=vm_vec) + if (!args[0].is_vec() || !args[1].is_vec()) return nil; auto& v0 = args[0].vec().elems; auto& v1 = args[1].vec().elems; @@ -295,7 +295,7 @@ module_func_info func_tbl[] = { {nullptr, nullptr} }; -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return func_tbl; } diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 80f2d4fa..12e6fe20 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -6,6 +6,10 @@ #include #endif +#ifdef _MSC_VER +#pragma warning (disable:4996) +#endif + #ifdef _WIN32 #include #pragma comment(lib,"ws2_32") @@ -33,14 +37,14 @@ static WSAmanager win; namespace nasal { var nas_socket(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num) + if (!args[0].is_num() || !args[1].is_num() || !args[2].is_num()) return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number"); int sd = socket(args[0].num(), args[1].num(), args[2].num()); return var::num(static_cast(sd)); } var nas_closesocket(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("closesocket", "\"sd\" should be number"); #ifdef _WIN32 return var::num(static_cast(closesocket(args[0].num()))); @@ -50,19 +54,19 @@ var nas_closesocket(var* args, usize size, gc* ngc) { } var nas_shutdown(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("shutdown", "\"sd\" must be a number"); - if (args[1].type!=vm_num) + if (!args[1].is_num()) return nas_err("shutdown", "\"how\" must be a number"); return var::num(static_cast(shutdown(args[0].num(), args[1].num()))); } var nas_bind(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("bind", "\"sd\" muse be a number"); - if (args[1].type!=vm_str) + if (!args[1].is_str()) return nas_err("bind", "\"ip\" should be a string including an ip with correct format"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("bind", "\"port\" must be a number"); sockaddr_in server; memset(&server, 0, sizeof(sockaddr_in)); @@ -77,19 +81,19 @@ var nas_bind(var* args, usize size, gc* ngc) { } var nas_listen(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("listen", "\"sd\" must be a number"); - if (args[1].type!=vm_num) + if (!args[1].is_num()) return nas_err("listen", "\"backlog\" must be a number"); return var::num(static_cast(listen(args[0].num(), args[1].num()))); } var nas_connect(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("connect", "\"sd\" must be a number"); - if (args[1].type!=vm_str) + if (!args[1].is_str()) return nas_err("connect", "\"hostname\" must be a string"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("connect", "\"port\" must be a number"); sockaddr_in addr; memset(&addr, 0, sizeof(sockaddr_in)); @@ -105,7 +109,7 @@ var nas_connect(var* args, usize size, gc* ngc) { } var nas_accept(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("accept", "\"sd\" must be a number"); sockaddr_in client; int socklen = sizeof(sockaddr_in); @@ -122,7 +126,7 @@ var nas_accept(var* args, usize size, gc* ngc) { reinterpret_cast(&socklen) ); #endif - var res = ngc->temp = ngc->alloc(vm_hash); + var res = ngc->temp = ngc->alloc(vm_type::vm_hash); auto& hash = res.hash().elems; hash["sd"] = var::num(static_cast(client_sd)); hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr)); @@ -131,11 +135,11 @@ var nas_accept(var* args, usize size, gc* ngc) { } var nas_send(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("send", "\"sd\" must be a number"); - if (args[1].type!=vm_str) + if (!args[1].is_str()) return nas_err("send", "\"buff\" must be a string"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("send", "\"flags\" muse be a number"); return var::num(static_cast(send( args[0].num(), @@ -146,15 +150,15 @@ var nas_send(var* args, usize size, gc* ngc) { } var nas_sendto(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("sendto", "\"sd\" must be a number"); - if (args[1].type!=vm_str) + if (!args[1].is_str()) return nas_err("sendto", "\"hostname\" must be a string"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("sendto", "\"port\" must be a number"); - if (args[3].type!=vm_str) + if (!args[3].is_str()) return nas_err("sendto", "\"buff\" must be a string"); - if (args[4].type!=vm_num) + if (!args[4].is_num()) return nas_err("sendto", "\"flags\" must be a number"); sockaddr_in addr; memset(&addr, 0, sizeof(sockaddr_in)); @@ -173,15 +177,15 @@ var nas_sendto(var* args, usize size, gc* ngc) { } var nas_recv(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("recv", "\"sd\" must be a number"); - if (args[1].type!=vm_num) + if (!args[1].is_num()) return nas_err("recv", "\"len\" must be a number"); if (args[1].num()<=0 || args[1].num()>16*1024*1024) return nas_err("recv", "\"len\" out of range"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("recv", "\"flags\" muse be a number"); - var res = ngc->temp = ngc->alloc(vm_hash); + var res = ngc->temp = ngc->alloc(vm_type::vm_hash); auto& hash = res.hash().elems; char* buf = new char[static_cast(args[1].num())]; auto recvsize = recv(args[0].num(), buf, args[1].num(), args[2].num()); @@ -194,17 +198,17 @@ var nas_recv(var* args, usize size, gc* ngc) { } var nas_recvfrom(var* args, usize size, gc* ngc) { - if (args[0].type!=vm_num) + if (!args[0].is_num()) return nas_err("recvfrom", "\"sd\" must be a number"); - if (args[1].type!=vm_num) + if (!args[1].is_num()) return nas_err("recvfrom", "\"len\" must be a number"); if (args[1].num()<=0 || args[1].num()>16*1024*1024) return nas_err("recvfrom", "\"len\" out of range"); - if (args[2].type!=vm_num) + if (!args[2].is_num()) return nas_err("recvfrom", "\"flags\" muse be a number"); sockaddr_in addr; int socklen = sizeof(sockaddr_in); - var res = ngc->temp = ngc->alloc(vm_hash); + var res = ngc->temp = ngc->alloc(vm_type::vm_hash); auto& hash = res.hash().elems; char* buf = new char[static_cast(args[1].num()+1)]; #ifdef _WIN32 @@ -256,7 +260,7 @@ module_func_info func_tbl[] = { {nullptr, nullptr} }; -extern "C" module_func_info* get() { +NASAL_EXTERN module_func_info* get() { return func_tbl; } diff --git a/src/bits_lib.cpp b/src/bits_lib.cpp index 4ca04554..d8b2d321 100644 --- a/src/bits_lib.cpp +++ b/src/bits_lib.cpp @@ -48,11 +48,11 @@ var builtin_fld(context* ctx, gc* ngc) { auto str = local[1]; auto startbit = local[2]; auto length = local[3]; - if (str.type!=vm_str || str.val.gcobj->unmutable) { + if (!str.is_str() || str.val.gcobj->unmutable) { return nas_err("bits::fld", "\"str\" must be mutable string"); } - if (startbit.type!=vm_num || length.type!=vm_num) { - return nas_err("bits::fld", "\"startbit\",\"len\" must be number"); + if (!startbit.is_num() || !length.is_num()) { + return nas_err("bits::fld", "\"startbit\", \"len\" must be number"); } u32 bit = static_cast(startbit.num()); u32 len = static_cast(length.num()); @@ -78,10 +78,10 @@ var builtin_sfld(context* ctx, gc* ngc) { auto str = local[1]; auto startbit = local[2]; auto length = local[3]; - if (str.type!=vm_str || str.val.gcobj->unmutable) { + if (!str.is_str() || str.val.gcobj->unmutable) { return nas_err("bits::sfld", "\"str\" must be mutable string"); } - if (startbit.type!=vm_num || length.type!=vm_num) { + if (!startbit.is_num() || !length.is_num()) { return nas_err("bits::sfld", "\"startbit\",\"len\" must be number"); } u32 bit = static_cast(startbit.num()); @@ -112,10 +112,10 @@ var builtin_setfld(context* ctx, gc* ngc) { auto startbit = local[2]; auto length = local[3]; auto value = local[4]; - if (str.type!=vm_str || str.val.gcobj->unmutable) { + if (!str.is_str() || str.val.gcobj->unmutable) { return nas_err("bits::setfld", "\"str\" must be mutable string"); } - if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) { + if (!startbit.is_num() || !length.is_num() || !value.is_num()) { return nas_err("bits::setfld", "\"startbit\", \"len\", \"val\" must be number" ); @@ -139,10 +139,10 @@ var builtin_setfld(context* ctx, gc* ngc) { var builtin_buf(context* ctx, gc* ngc) { var length = ctx->localr[1]; - if (length.type!=vm_num || length.num()<=0) { + if (!length.is_num() || length.num()<=0) { return nas_err("bits::buf", "\"len\" must be number greater than 0"); } - var str = ngc->alloc(vm_str); + var str = ngc->alloc(vm_type::vm_str); auto& s = str.str(); s.resize(length.num(), '\0'); return str; diff --git a/src/coroutine.cpp b/src/coroutine.cpp index 3aeed490..23401a61 100644 --- a/src/coroutine.cpp +++ b/src/coroutine.cpp @@ -18,7 +18,7 @@ var builtin_cocreate(context* ctx, gc* ngc) { // +-------------+ // ``` auto coroutine_function = ctx->localr[1]; - if (coroutine_function.type!=vm_func) { + if (!coroutine_function.is_func()) { return nas_err( "coroutine::create", "must use a function to create coroutine" @@ -30,7 +30,7 @@ var builtin_cocreate(context* ctx, gc* ngc) { "cannot create another coroutine in a coroutine" ); } - auto coroutine_object = ngc->alloc(vm_co); + auto coroutine_object = ngc->alloc(vm_type::vm_co); auto& coroutine = coroutine_object.co(); coroutine.ctx.pc = coroutine_function.func().entry-1; @@ -69,7 +69,7 @@ var builtin_coresume(context* ctx, gc* ngc) { auto main_local_frame = ctx->localr; auto coroutine_object = main_local_frame[1]; // return nil if is not a coroutine object or coroutine exited - if (coroutine_object.type!=vm_co || + if (!coroutine_object.is_coroutine() || coroutine_object.co().status==nas_co::status::dead) { return nil; } @@ -80,7 +80,7 @@ var builtin_coresume(context* ctx, gc* ngc) { // fetch coroutine's stack top and return // then coroutine's stack top will catch this return value // so the coroutine's stack top in fact is not changed - if (ngc->running_context->top[0].type==vm_ret) { + if (ngc->running_context->top[0].is_ret()) { // when first calling this coroutine, the stack top must be vm_ret return ngc->running_context->top[0]; } @@ -114,7 +114,7 @@ var builtin_coyield(context* ctx, gc* ngc) { var builtin_costatus(context* ctx, gc* ngc) { auto coroutine_object = ctx->localr[1]; - if (coroutine_object.type!=vm_co) { + if (!coroutine_object.is_coroutine()) { return ngc->newstr("error"); } switch(coroutine_object.co().status) { diff --git a/src/dylib_lib.cpp b/src/dylib_lib.cpp index a3402cc6..df1b794d 100644 --- a/src/dylib_lib.cpp +++ b/src/dylib_lib.cpp @@ -15,7 +15,7 @@ void dynamic_library_destructor(void* pointer) { var builtin_dlopen(context* ctx, gc* ngc) { auto dlname = ctx->localr[1]; - if (dlname.type!=vm_str) { + if (!dlname.is_str()) { return nas_err("dylib::dlopen", "\"libname\" must be string"); } @@ -42,11 +42,12 @@ var builtin_dlopen(context* ctx, gc* ngc) { "cannot open dynamic lib <" + dlname.str() + ">" ); } - auto return_hash = ngc->temp = ngc->alloc(vm_hash); - auto library_object = ngc->alloc(vm_obj); + auto return_hash = ngc->temp = ngc->alloc(vm_type::vm_hash); + auto library_object = ngc->alloc(vm_type::vm_ghost); library_object.ghost().set( dynamic_library_type_name, dynamic_library_destructor, + nullptr, dynamic_library_pointer ); return_hash.hash().elems["lib"] = library_object; @@ -72,10 +73,11 @@ var builtin_dlopen(context* ctx, gc* ngc) { } for(u32 i = 0; table[i].name; ++i) { auto function_pointer = reinterpret_cast(table[i].fd); - auto function_object = ngc->alloc(vm_obj); + auto function_object = ngc->alloc(vm_type::vm_ghost); function_object.ghost().set( function_address_type_name, nullptr, + nullptr, function_pointer ); return_hash.hash().elems[table[i].name] = function_object; diff --git a/src/fg_props.cpp b/src/fg_props.cpp index 8c5f3712..70a13d90 100644 --- a/src/fg_props.cpp +++ b/src/fg_props.cpp @@ -8,7 +8,7 @@ var builtin_logprint(context* ctx, gc* ngc) { auto local = ctx->localr; auto level = local[1]; auto elems = local[2]; - if (elems.type!=vm_vec) { + if (!elems.is_vec()) { return nas_err("fg_env::logprint", "received argument is not vector."); } std::ofstream out("fgfs.log", std::ios::app); diff --git a/src/io_lib.cpp b/src/io_lib.cpp index 5af223ac..28cb5088 100644 --- a/src/io_lib.cpp +++ b/src/io_lib.cpp @@ -1,245 +1,247 @@ -#include "io_lib.h" - -namespace nasal { - -const auto file_type_name = "file"; - -void filehandle_destructor(void* ptr) { - fclose(static_cast(ptr)); -} - -var builtin_readfile(context* ctx, gc* ngc) { - auto filename = ctx->localr[1]; - if (filename.type!=vm_str) { - return nas_err("io::readfile", "\"filename\" must be string"); - } - std::ifstream in(filename.str(), std::ios::binary); - std::stringstream rd; - if (!in.fail()) { - rd << in.rdbuf(); - } - return ngc->newstr(rd.str()); -} - -var builtin_fout(context* ctx, gc* ngc) { - auto local = ctx->localr; - auto filename = local[1]; - auto source = local[2]; - if (filename.type!=vm_str) { - return nas_err("io::fout", "\"filename\" must be string"); - } - std::ofstream out(filename.str()); - if (out.fail()) { - return nas_err("io::fout", "cannot open <" + filename.str() + ">"); - } - out << source; - return nil; -} - -var builtin_exists(context* ctx, gc* ngc) { - auto filename = ctx->localr[1]; - if (filename.type!=vm_str) { - return zero; - } - return access(filename.str().c_str(), F_OK)!=-1? one:zero; -} - -var builtin_open(context* ctx, gc* ngc) { - auto local = ctx->localr; - auto name = local[1]; - auto mode = local[2]; - if (name.type!=vm_str) { - return nas_err("io::open", "\"filename\" must be string"); - } - if (mode.type!=vm_str) { - return nas_err("io::open", "\"mode\" must be string"); - } - auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str()); - if (!file_descriptor) { - return nas_err("io::open", "failed to open file <" + name.str() + ">"); - } - var return_object = ngc->alloc(vm_obj); - return_object.ghost().set( - file_type_name, filehandle_destructor, file_descriptor - ); - return return_object; -} - -var builtin_close(context* ctx, gc* ngc) { - var file_descriptor = ctx->localr[1]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::close", "not a valid filehandle"); - } - file_descriptor.ghost().clear(); - return nil; -} - -var builtin_read(context* ctx, gc* ngc) { - auto local = ctx->localr; - auto file_descriptor = local[1]; - auto buffer = local[2]; - auto length = local[3]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::read", "not a valid filehandle"); - } - if (buffer.type!=vm_str || buffer.val.gcobj->unmutable) { - return nas_err("io::read", "\"buf\" must be mutable string"); - } - if (length.type!=vm_num) { - return nas_err("io::read", "\"len\" must be number"); - } - if (length.num()<=0 || length.num()>=(1<<30)) { - return nas_err("io::read", "\"len\" less than 1 or too large"); - } - auto temp_buffer = new char[static_cast(length.num())+1]; - if (!temp_buffer) { - return nas_err("io::read", "malloc failed"); - } - auto read_size = fread( - temp_buffer, 1, length.num(), - static_cast(file_descriptor.ghost().pointer) - ); - buffer.str() = temp_buffer; - buffer.val.gcobj->unmutable = true; - delete []temp_buffer; - return var::num(read_size); -} - -var builtin_write(context* ctx, gc* ngc) { - auto local = ctx->localr; - auto file_descriptor = local[1]; - auto source = local[2]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::write", "not a valid filehandle"); - } - if (source.type!=vm_str) { - return nas_err("io::write", "\"str\" must be string"); - } - return var::num(static_cast(fwrite( - source.str().c_str(), 1, source.str().length(), - static_cast(file_descriptor.ghost().pointer) - ))); -} - -var builtin_seek(context* ctx, gc* ngc) { - auto local = ctx->localr; - auto file_descriptor = local[1]; - auto position = local[2]; - auto whence = local[3]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::seek", "not a valid filehandle"); - } - return var::num(static_cast(fseek( - static_cast(file_descriptor.ghost().pointer), - position.num(), - whence.num() - ))); -} - -var builtin_tell(context* ctx, gc* ngc) { - auto file_descriptor = ctx->localr[1]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::tell", "not a valid filehandle"); - } - return var::num(static_cast( - ftell(static_cast(file_descriptor.ghost().pointer)) - )); -} - -var builtin_readln(context* ctx, gc* ngc) { - auto file_descriptor = ctx->localr[1]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::readln", "not a valid filehandle"); - } - auto result = ngc->alloc(vm_str); - char c; - while((c = fgetc(static_cast(file_descriptor.ghost().pointer)))!=EOF) { - if (c=='\r') { - continue; - } - if (c=='\n') { - return result; - } - result.str().push_back(c); - } - if (result.str().length()) { - return result; - } - return nil; -} - -var builtin_stat(context* ctx, gc* ngc) { - auto name = ctx->localr[1]; - if (name.type!=vm_str) { - return nas_err("io::stat", "\"filename\" must be string"); - } - struct stat buffer; - if (stat(name.str().c_str(), &buffer)<0) { - return nas_err("io::stat", "failed to open file <" + name.str() + ">"); - } - auto result = ngc->alloc(vm_vec); - result.vec().elems = { - var::num(static_cast(buffer.st_dev)), - var::num(static_cast(buffer.st_ino)), - var::num(static_cast(buffer.st_mode)), - var::num(static_cast(buffer.st_nlink)), - var::num(static_cast(buffer.st_uid)), - var::num(static_cast(buffer.st_gid)), - var::num(static_cast(buffer.st_rdev)), - var::num(static_cast(buffer.st_size)), - var::num(static_cast(buffer.st_atime)), - var::num(static_cast(buffer.st_mtime)), - var::num(static_cast(buffer.st_ctime)) - }; - return result; -} - -var builtin_eof(context* ctx, gc* ngc) { - auto file_descriptor = ctx->localr[1]; - if (!file_descriptor.object_check(file_type_name)) { - return nas_err("io::readln", "not a valid filehandle"); - } - return var::num(static_cast( - feof(static_cast(file_descriptor.ghost().pointer)) - )); -} - -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}, - {"__exists", builtin_exists}, - {"__open", builtin_open}, - {"__close", builtin_close}, - {"__read", builtin_read}, - {"__write", builtin_write}, - {"__seek", builtin_seek}, - {"__tell", builtin_tell}, - {"__readln", builtin_readln}, - {"__stat", builtin_stat}, - {"__eof", builtin_eof}, - {"__stdin", builtin_stdin}, - {"__stdout", builtin_stdout}, - {"__stderr", builtin_stderr}, - {nullptr, nullptr} -}; - -} +#include "io_lib.h" + +#include + +namespace nasal { + +const auto file_type_name = "file"; + +void filehandle_destructor(void* ptr) { + fclose(static_cast(ptr)); +} + +var builtin_readfile(context* ctx, gc* ngc) { + auto filename = ctx->localr[1]; + if (!filename.is_str()) { + return nas_err("io::readfile", "\"filename\" must be string"); + } + std::ifstream in(filename.str(), std::ios::binary); + std::stringstream rd; + if (!in.fail()) { + rd << in.rdbuf(); + } + return ngc->newstr(rd.str()); +} + +var builtin_fout(context* ctx, gc* ngc) { + auto local = ctx->localr; + auto filename = local[1]; + auto source = local[2]; + if (!filename.is_str()) { + return nas_err("io::fout", "\"filename\" must be string"); + } + std::ofstream out(filename.str()); + if (out.fail()) { + return nas_err("io::fout", "cannot open <" + filename.str() + ">"); + } + out << source; + return nil; +} + +var builtin_exists(context* ctx, gc* ngc) { + auto filename = ctx->localr[1]; + if (!filename.is_str()) { + return zero; + } + return fs::exists(filename.str())? one:zero; +} + +var builtin_open(context* ctx, gc* ngc) { + auto local = ctx->localr; + auto name = local[1]; + auto mode = local[2]; + if (!name.is_str()) { + return nas_err("io::open", "\"filename\" must be string"); + } + if (!mode.is_str()) { + return nas_err("io::open", "\"mode\" must be string"); + } + auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str()); + if (!file_descriptor) { + return nas_err("io::open", "failed to open file <" + name.str() + ">"); + } + var return_object = ngc->alloc(vm_type::vm_ghost); + return_object.ghost().set( + file_type_name, filehandle_destructor, nullptr, file_descriptor + ); + return return_object; +} + +var builtin_close(context* ctx, gc* ngc) { + var file_descriptor = ctx->localr[1]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::close", "not a valid filehandle"); + } + file_descriptor.ghost().clear(); + return nil; +} + +var builtin_read(context* ctx, gc* ngc) { + auto local = ctx->localr; + auto file_descriptor = local[1]; + auto buffer = local[2]; + auto length = local[3]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::read", "not a valid filehandle"); + } + if (!buffer.is_str() || buffer.val.gcobj->unmutable) { + return nas_err("io::read", "\"buf\" must be mutable string"); + } + if (!length.is_num()) { + return nas_err("io::read", "\"len\" must be number"); + } + if (length.num()<=0 || length.num()>=(1<<30)) { + return nas_err("io::read", "\"len\" less than 1 or too large"); + } + auto temp_buffer = new char[static_cast(length.num())+1]; + if (!temp_buffer) { + return nas_err("io::read", "malloc failed"); + } + auto read_size = fread( + temp_buffer, 1, length.num(), + static_cast(file_descriptor.ghost().pointer) + ); + buffer.str() = temp_buffer; + buffer.val.gcobj->unmutable = true; + delete []temp_buffer; + return var::num(read_size); +} + +var builtin_write(context* ctx, gc* ngc) { + auto local = ctx->localr; + auto file_descriptor = local[1]; + auto source = local[2]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::write", "not a valid filehandle"); + } + if (!source.is_str()) { + return nas_err("io::write", "\"str\" must be string"); + } + return var::num(static_cast(fwrite( + source.str().c_str(), 1, source.str().length(), + static_cast(file_descriptor.ghost().pointer) + ))); +} + +var builtin_seek(context* ctx, gc* ngc) { + auto local = ctx->localr; + auto file_descriptor = local[1]; + auto position = local[2]; + auto whence = local[3]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::seek", "not a valid filehandle"); + } + return var::num(static_cast(fseek( + static_cast(file_descriptor.ghost().pointer), + position.num(), + whence.num() + ))); +} + +var builtin_tell(context* ctx, gc* ngc) { + auto file_descriptor = ctx->localr[1]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::tell", "not a valid filehandle"); + } + return var::num(static_cast( + ftell(static_cast(file_descriptor.ghost().pointer)) + )); +} + +var builtin_readln(context* ctx, gc* ngc) { + auto file_descriptor = ctx->localr[1]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::readln", "not a valid filehandle"); + } + auto result = ngc->alloc(vm_type::vm_str); + char c; + while((c = fgetc(static_cast(file_descriptor.ghost().pointer)))!=EOF) { + if (c=='\r') { + continue; + } + if (c=='\n') { + return result; + } + result.str().push_back(c); + } + if (result.str().length()) { + return result; + } + return nil; +} + +var builtin_stat(context* ctx, gc* ngc) { + auto name = ctx->localr[1]; + if (!name.is_str()) { + return nas_err("io::stat", "\"filename\" must be string"); + } + struct stat buffer; + if (stat(name.str().c_str(), &buffer)<0) { + return nas_err("io::stat", "failed to open file <" + name.str() + ">"); + } + auto result = ngc->alloc(vm_type::vm_vec); + result.vec().elems = { + var::num(static_cast(buffer.st_dev)), + var::num(static_cast(buffer.st_ino)), + var::num(static_cast(buffer.st_mode)), + var::num(static_cast(buffer.st_nlink)), + var::num(static_cast(buffer.st_uid)), + var::num(static_cast(buffer.st_gid)), + var::num(static_cast(buffer.st_rdev)), + var::num(static_cast(buffer.st_size)), + var::num(static_cast(buffer.st_atime)), + var::num(static_cast(buffer.st_mtime)), + var::num(static_cast(buffer.st_ctime)) + }; + return result; +} + +var builtin_eof(context* ctx, gc* ngc) { + auto file_descriptor = ctx->localr[1]; + if (!file_descriptor.object_check(file_type_name)) { + return nas_err("io::readln", "not a valid filehandle"); + } + return var::num(static_cast( + feof(static_cast(file_descriptor.ghost().pointer)) + )); +} + +var builtin_stdin(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_type::vm_ghost); + file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdin); + return file_descriptor; +} + +var builtin_stdout(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_type::vm_ghost); + file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdout); + return file_descriptor; +} + +var builtin_stderr(context* ctx, gc* ngc) { + auto file_descriptor = ngc->alloc(vm_type::vm_ghost); + file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stderr); + return file_descriptor; +} + + +nasal_builtin_table io_lib_native[] = { + {"__readfile", builtin_readfile}, + {"__fout", builtin_fout}, + {"__exists", builtin_exists}, + {"__open", builtin_open}, + {"__close", builtin_close}, + {"__read", builtin_read}, + {"__write", builtin_write}, + {"__seek", builtin_seek}, + {"__tell", builtin_tell}, + {"__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 e1f4e97c..0940867e 100644 --- a/src/io_lib.h +++ b/src/io_lib.h @@ -1,41 +1,35 @@ -#pragma once - -#include "nasal.h" -#include "nasal_gc.h" -#include "nasal_builtin.h" - -#include - -#ifndef _MSC_VER -#include -#else -#include -#endif - -#ifdef _MSC_VER -#define F_OK 0 // fuck msc -#endif - -namespace nasal { - -void filehandle_destructor(void*); - -var builtin_readfile(context*, gc*); -var builtin_fout(context*, gc*); -var builtin_exists(context*, gc*); -var builtin_open(context*, gc*); -var builtin_close(context*, gc*); -var builtin_read(context*, gc*); -var builtin_write(context*, gc*); -var builtin_seek(context*, gc*); -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[]; - -} +#pragma once + +#include "nasal.h" +#include "nasal_gc.h" +#include "nasal_builtin.h" + +#ifndef _MSC_VER +#include +#else +#include +#endif + +namespace nasal { + +void filehandle_destructor(void*); + +var builtin_readfile(context*, gc*); +var builtin_fout(context*, gc*); +var builtin_exists(context*, gc*); +var builtin_open(context*, gc*); +var builtin_close(context*, gc*); +var builtin_read(context*, gc*); +var builtin_write(context*, gc*); +var builtin_seek(context*, gc*); +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/json_lib.cpp b/src/json_lib.cpp new file mode 100644 index 00000000..f3d7dc8f --- /dev/null +++ b/src/json_lib.cpp @@ -0,0 +1,405 @@ +#include "json_lib.h" + +#include +#include +#include +#include + +namespace nasal { + +enum class json_token_type { + tok_eof, + tok_lbrace, + tok_rbrace, + tok_lbrkt, + tok_rbrkt, + tok_comma, + tok_colon, + tok_str, + tok_num, + tok_id, + tok_bool +}; + +std::string get_content(json_token_type type) { + switch(type) { + case json_token_type::tok_eof: return "eof"; + case json_token_type::tok_lbrace: return "`{`"; + case json_token_type::tok_rbrace: return "`}`"; + case json_token_type::tok_lbrkt: return "`[`"; + case json_token_type::tok_rbrkt: return "`]`"; + case json_token_type::tok_comma: return "`,`"; + case json_token_type::tok_colon: return "`:`"; + case json_token_type::tok_str: return "string"; + case json_token_type::tok_num: return "number"; + case json_token_type::tok_id: return "identifier"; + case json_token_type::tok_bool: return "boolean"; + } + // unreachable + return ""; +} + +struct token { + json_token_type type; + std::string content; +}; + +class json { +private: + std::string text = ""; + usize line = 1; + usize ptr = 0; + token this_token; + var temp_stack = nil; + std::string info = ""; + +private: + std::string var_generate(var&); + std::string vector_generate(nas_vec&); + std::string hash_generate(nas_hash&); + +private: + bool is_num(char c) { + return std::isdigit(c); + } + bool is_id(char c) { + return std::isalpha(c) || c=='_'; + } + bool check(char c) { + return c=='{' || c=='}' || c=='[' || c==']' || + c==':' || c==',' || c=='"' || c=='\'' || + c=='-' || c=='+' || is_num(c) || is_id(c); + } + void next(); + void match(json_token_type); + void vector_member(nas_vec&, gc*); + var vector_object_generate(gc*); + void hash_member(nas_hash&, gc*); + var hash_object_generate(gc*); + void check_eof(); + std::string& error_info() { + return info; + } + +public: + std::string stringify(var&); + var parse(const std::string&, gc*); + const std::string& get_error() { return error_info(); } +}; + +std::string json::var_generate(var& value) { + switch(value.type) { + case vm_type::vm_num: { + std::stringstream out; + out << value.num(); + if (std::isnan(value.num())) { + error_info() += "json::stringify: cannot generate number nan\n"; + } + if (std::isinf(value.num())) { + error_info() += "json::stringify: cannot generate number inf\n"; + } + return out.str(); + } + case vm_type::vm_str: return "\"" + value.str() + "\""; + case vm_type::vm_vec: return vector_generate(value.vec()); + case vm_type::vm_hash: return hash_generate(value.hash()); + case vm_type::vm_func: + error_info() += "json::stringify: cannot generate function\n"; break; + case vm_type::vm_ghost: + error_info() += "json::stringify: cannot generate ghost type\n"; break; + case vm_type::vm_map: + error_info() += "json::stringify: cannot generate namespace type\n"; break; + default: break; + } + return "\"undefined\""; +} + +std::string json::vector_generate(nas_vec& vect) { + // avoid stackoverflow + if (vect.printed) { + error_info() += "json::stringify: get vector containing itself\n"; + return "undefined"; + } + vect.printed = true; + std::string out = "["; + for(auto& i : vect.elems) { + out += var_generate(i) + ","; + } + if (out.back()==',') { + out.pop_back(); + } + out += "]"; + vect.printed = false; + return out; +} + +std::string json::hash_generate(nas_hash& hash) { + // avoid stackoverflow + if (hash.printed) { + error_info() += "json::stringify: get hash containing itself\n"; + return "undefined"; + } + hash.printed = true; + std::string out = "{"; + for(auto& i : hash.elems) { + out += "\"" + i.first + "\":"; + out += var_generate(i.second) + ","; + } + if (out.back()==',') { + out.pop_back(); + } + out += "}"; + hash.printed = false; + return out; +} + +std::string json::stringify(var& object) { + error_info() = ""; + if (object.is_vec()) { + return vector_generate(object.vec()); + } else if (object.is_hash()) { + return hash_generate(object.hash()); + } + return "[]"; +} + +void json::next() { + while(ptr=text.length()) { + this_token = {json_token_type::tok_eof, "eof"}; + return; + } + auto c = text[ptr]; + switch(c) { + case '{': this_token = {json_token_type::tok_lbrace, "{"}; ++ptr; return; + case '}': this_token = {json_token_type::tok_rbrace, "}"}; ++ptr; return; + case '[': this_token = {json_token_type::tok_lbrkt, "["}; ++ptr; return; + case ']': this_token = {json_token_type::tok_rbrkt, "]"}; ++ptr; return; + case ',': this_token = {json_token_type::tok_comma, ","}; ++ptr; return; + case ':': this_token = {json_token_type::tok_colon, ":"}; ++ptr; return; + default: break; + } + if (is_num(c) || c=='-' || c=='+') { + auto temp = std::string(1, c); + ++ptr; + while(ptrnewstr(this_token.content)); + next(); + } else if (this_token.type==json_token_type::tok_num) { + vec.elems.push_back(var::num(str_to_num(this_token.content.c_str()))); + next(); + } +} + +var json::vector_object_generate(gc* ngc) { + auto vect_object = ngc->alloc(vm_type::vm_vec); + temp_stack.vec().elems.push_back(vect_object); + match(json_token_type::tok_lbrkt); + vector_member(vect_object.vec(), ngc); + while(this_token.type==json_token_type::tok_comma) { + match(json_token_type::tok_comma); + vector_member(vect_object.vec(), ngc); + } + match(json_token_type::tok_rbrkt); + temp_stack.vec().elems.pop_back(); + return vect_object; +} + +void json::hash_member(nas_hash& hash, gc* ngc) { + const auto name = this_token.content; + if (this_token.type==json_token_type::tok_rbrace) { + return; + } + if (this_token.type==json_token_type::tok_str) { + match(json_token_type::tok_str); + } else { + match(json_token_type::tok_id); + } + match(json_token_type::tok_colon); + if (this_token.type==json_token_type::tok_lbrace) { + hash.elems.insert({name, hash_object_generate(ngc)}); + } else if (this_token.type==json_token_type::tok_lbrkt) { + hash.elems.insert({name, vector_object_generate(ngc)}); + } else if (this_token.type==json_token_type::tok_str || + this_token.type==json_token_type::tok_bool) { + hash.elems.insert({name, ngc->newstr(this_token.content)}); + next(); + } else if (this_token.type==json_token_type::tok_num) { + hash.elems.insert({name, var::num(str_to_num(this_token.content.c_str()))}); + next(); + } +} + +var json::hash_object_generate(gc* ngc) { + auto hash_object = ngc->alloc(vm_type::vm_hash); + temp_stack.vec().elems.push_back(hash_object); + match(json_token_type::tok_lbrace); + hash_member(hash_object.hash(), ngc); + while(this_token.type==json_token_type::tok_comma) { + match(json_token_type::tok_comma); + hash_member(hash_object.hash(), ngc); + } + match(json_token_type::tok_rbrace); + temp_stack.vec().elems.pop_back(); + return hash_object; +} + +void json::check_eof() { + next(); + if (this_token.type==json_token_type::tok_eof) { + return; + } + while (this_token.type!=json_token_type::tok_eof) { + error_info() += "json::parse: line " + std::to_string(line); + error_info() += ": expect " + get_content(json_token_type::tok_eof); + error_info() += " but get `" + this_token.content + "`.\n"; + next(); + } +} + +var json::parse(const std::string& input, gc* ngc) { + line = 1; + ptr = 0; + this_token = {json_token_type::tok_eof, ""}; + error_info() = ""; + + if (input.empty()) { + error_info() += "json::parse: empty string.\n"; + return nil; + } + text = input; + next(); + if (this_token.type==json_token_type::tok_lbrkt) { + ngc->temp = temp_stack = ngc->alloc(vm_type::vm_vec); + auto result = vector_object_generate(ngc); + check_eof(); + ngc->temp = temp_stack = nil; + return result; + } else { + ngc->temp = temp_stack = ngc->alloc(vm_type::vm_vec); + auto result = hash_object_generate(ngc); + check_eof(); + ngc->temp = temp_stack = nil; + return result; + } + return nil; +} + +void json_destructor(void* ptr) { + delete static_cast(ptr); +} + +var builtin_json_new(context* ctx, gc* ngc) { + var res = ngc->alloc(vm_type::vm_ghost); + res.ghost().set("nasal::json", json_destructor, nullptr, new json); + return res; +} + +var builtin_json_stringify(context* ctx, gc* ngc) { + auto json_object = ctx->localr[1]; + auto object = ctx->localr[2]; + if (!json_object.object_check("nasal::json")) { + return nas_err("json::stringify", "expect a json object."); + } + auto json_ptr = static_cast(json_object.ghost().pointer); + return ngc->newstr(json_ptr->stringify(object)); +} + +var builtin_json_parse(context* ctx, gc* ngc) { + auto json_object = ctx->localr[1]; + auto input_string = ctx->localr[2]; + if (!json_object.object_check("nasal::json")) { + return nas_err("json::parse", "expect a json object."); + } + if (!input_string.is_str()) { + return nas_err("json::parse", "require string as the input."); + } + auto json_ptr = static_cast(json_object.ghost().pointer); + return json_ptr->parse(input_string.str(), ngc); +} + +var builtin_json_get_error(context* ctx, gc* ngc) { + auto json_object = ctx->localr[1]; + if (!json_object.object_check("nasal::json")) { + return nas_err("json::get_error", "expect a json object."); + } + auto json_ptr = static_cast(json_object.ghost().pointer); + return ngc->newstr(json_ptr->get_error()); +} + +nasal_builtin_table json_lib_native[] = { + {"_json_new", builtin_json_new}, + {"_json_stringify", builtin_json_stringify}, + {"_json_parse", builtin_json_parse}, + {"_json_get_error", builtin_json_get_error}, + {nullptr, nullptr} +}; + +} \ No newline at end of file diff --git a/src/json_lib.h b/src/json_lib.h new file mode 100644 index 00000000..ddec72a7 --- /dev/null +++ b/src/json_lib.h @@ -0,0 +1,16 @@ +#pragma once + +#include "nasal.h" +#include "nasal_gc.h" +#include "nasal_builtin.h" + +namespace nasal { + +var builtin_json_new(context*, gc*); +var builtin_json_stringify(context*, gc*); +var builtin_json_parse(context*, gc*); +var builtin_json_get_error(context*, gc*); + +extern nasal_builtin_table json_lib_native[]; + +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1867f676..d9e75880 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,6 +29,7 @@ const u32 VM_DEBUG = 1<<6; const u32 VM_SYMINFO = 1<<7; const u32 VM_PROFILE = 1<<8; const u32 VM_PROF_ALL = 1<<9; +const u32 VM_REF_FILE = 1<<10; std::ostream& help(std::ostream& out) { out @@ -53,6 +54,7 @@ std::ostream& help(std::ostream& out) { << " -e, --exec | execute directly.\n" << " -t, --time | show execute time.\n" << " -d, --detail | get detail info.\n" + << " -f, --ref-file | get referenced files.\n" << " -dbg, --debug | debug mode.\n" << " --prof | show profiling result, available in debug mode.\n" << " --prof-all | show profiling result of all files," @@ -131,6 +133,14 @@ void execute( // linker gets parser's ast and load import files to this ast ld.link(parse, file, cmd&VM_DETAIL).chkerr(); + if (cmd&VM_REF_FILE) { + if (ld.get_file_list().size()) { + std::cout << "referenced file(s):\n"; + } + for(const auto& file: ld.get_file_list()) { + std::cout << " " << file << "\n"; + } + } // optimizer does simple optimization on ast auto opt = std::unique_ptr(new nasal::optimizer); @@ -211,7 +221,9 @@ i32 main(i32 argc, const char* argv[]) { {"--debug", VM_DEBUG}, {"-dbg", VM_DEBUG}, {"--prof", VM_PROFILE}, - {"--prof-all", VM_PROF_ALL} + {"--prof-all", VM_PROF_ALL}, + {"-f", VM_REF_FILE}, + {"--ref-file", VM_REF_FILE} }; u32 cmd = 0; std::string filename = ""; diff --git a/src/math_lib.cpp b/src/math_lib.cpp index 750648a8..6177f82b 100644 --- a/src/math_lib.cpp +++ b/src/math_lib.cpp @@ -5,7 +5,7 @@ namespace nasal { var builtin_pow(context* ctx, gc* ngc) { auto x = ctx->localr[1]; auto y = ctx->localr[2]; - if (x.type!=vm_num || y.type!=vm_num) { + if (!x.is_num() || !y.is_num()) { return var::num(std::nan("")); } return var::num(std::pow(x.num(), y.num())); @@ -13,43 +13,43 @@ var builtin_pow(context* ctx, gc* ngc) { var builtin_sin(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? sin(val.num()):std::nan("")); + return var::num(val.is_num()? sin(val.num()):std::nan("")); } var builtin_cos(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? cos(val.num()):std::nan("")); + return var::num(val.is_num()? cos(val.num()):std::nan("")); } var builtin_tan(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? tan(val.num()):std::nan("")); + return var::num(val.is_num()? tan(val.num()):std::nan("")); } var builtin_exp(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? exp(val.num()):std::nan("")); + return var::num(val.is_num()? exp(val.num()):std::nan("")); } var builtin_lg(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan("")); + return var::num(val.is_num()? log(val.num())/log(10.0):std::nan("")); } var builtin_ln(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? log(val.num()):std::nan("")); + return var::num(val.is_num()? log(val.num()):std::nan("")); } var builtin_sqrt(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - return var::num(val.type==vm_num? sqrt(val.num()):std::nan("")); + return var::num(val.is_num()? sqrt(val.num()):std::nan("")); } var builtin_atan2(context* ctx, gc* ngc) { auto x = ctx->localr[1]; auto y = ctx->localr[2]; - if (x.type!=vm_num || y.type!=vm_num) { + if (!x.is_num() || !y.is_num()) { return var::num(std::nan("")); } return var::num(atan2(y.num(), x.num())); @@ -57,7 +57,7 @@ var builtin_atan2(context* ctx, gc* ngc) { var builtin_isnan(context* ctx, gc* ngc) { auto x = ctx->localr[1]; - return (x.type==vm_num && std::isnan(x.num()))? one:zero; + return (x.is_num() && std::isnan(x.num()))? one:zero; } nasal_builtin_table math_lib_native[] = { diff --git a/src/nasal.h b/src/nasal.h index ad651b5b..ccfd9b78 100644 --- a/src/nasal.h +++ b/src/nasal.h @@ -39,20 +39,42 @@ bool is_superh(); // virtual machine stack depth, both global depth and value stack depth const u32 STACK_DEPTH = 4096; -f64 hex2f(const char*); -f64 oct2f(const char*); - +f64 hex_to_f64(const char*); +f64 oct_to_f64(const char*); // we have the same reason not using atof here // just as andy's interpreter does. // it is not platform independent, and may have strange output. // so we write a new function here to convert str to number manually. // but this also makes 0.1+0.2==0.3, // not another result that you may get in other languages. -f64 dec2f(const char*); +f64 dec_to_f64(const char*); -f64 str2num(const char*); +f64 str_to_num(const char*); i32 utf8_hdchk(const char); -std::string chrhex(const char); +std::string char_to_hex(const char); std::string rawstr(const std::string&, const usize maxlen = 0); +namespace fs { + +class path { +private: + std::string file_system_path; + +public: + path(const path&) = default; + path(const std::string& file_path): file_system_path(file_path) {} + path& operator/(const path&); + const char* c_str() const { + return file_system_path.c_str(); + } + std::string str() const { + return file_system_path; + } +}; + +bool exists(const path&); +bool is_regular(const path&); + +} + } \ No newline at end of file diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index 0aec1684..e3bc8343 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -33,7 +33,7 @@ var builtin_append(context* ctx, gc* ngc) { auto local = ctx->localr; var vec = local[1]; var elem = local[2]; - if (vec.type!=vm_vec) { + if (!vec.is_vec()) { return nas_err("append", "\"vec\" must be vector"); } auto& v = vec.vec().elems; @@ -47,10 +47,10 @@ var builtin_setsize(context* ctx, gc* ngc) { auto local = ctx->localr; var vec = local[1]; var size = local[2]; - if (vec.type!=vm_vec) { + if (!vec.is_vec()) { return nas_err("setsize", "\"vec\" must be vector"); } - if (size.type!=vm_num || size.num()<0) { + if (!size.is_num() || size.num()<0) { return nil; } vec.vec().elems.resize(static_cast(size.num()), nil); @@ -59,7 +59,7 @@ var builtin_setsize(context* ctx, gc* ngc) { var builtin_system(context* ctx, gc* ngc) { auto str = ctx->localr[1]; - if (str.type!=vm_str) { + if (!str.is_str()) { return var::num(-1); } return var::num(static_cast(system(str.str().c_str()))); @@ -68,8 +68,8 @@ var builtin_system(context* ctx, gc* ngc) { var builtin_input(context* ctx, gc* ngc) { auto local = ctx->localr; var end = local[1]; - var ret = ngc->alloc(vm_str); - if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) { + var ret = ngc->alloc(vm_type::vm_str); + if (!end.is_str() || end.str().length()>1 || !end.str().length()) { std::cin >> ret.str(); } else { std::getline(std::cin, ret.str(), end.str()[0]); @@ -81,17 +81,17 @@ var builtin_split(context* ctx, gc* ngc) { auto local = ctx->localr; var delimeter = local[1]; var str = local[2]; - if (delimeter.type!=vm_str) { + if (!delimeter.is_str()) { return nas_err("split", "\"separator\" must be string"); } - if (str.type!=vm_str) { + if (!str.is_str()) { return nas_err("split", "\"str\" must be string"); } const auto& deli = delimeter.str(); const auto& s = str.str(); // avoid being sweeped - auto res = ngc->temp = ngc->alloc(vm_vec); + auto res = ngc->temp = ngc->alloc(vm_type::vm_vec); auto& vec = res.vec().elems; if (!deli.length()) { @@ -119,10 +119,10 @@ var builtin_split(context* ctx, gc* ngc) { var builtin_rand(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type!=vm_num && val.type!=vm_nil) { + if (!val.is_num() && !val.is_nil()) { return nas_err("rand", "\"seed\" must be nil or number"); } - if (val.type==vm_num) { + if (val.is_num()) { srand(static_cast(val.num())); return nil; } @@ -137,7 +137,7 @@ var builtin_id(context* ctx, gc* ngc) { auto val = ctx->localr[1]; std::stringstream ss; ss << "0"; - if (val.type>vm_num) { + if (val.type>vm_type::vm_num) { ss << "x" << std::hex; ss << reinterpret_cast(val.val.gcobj) << std::dec; } @@ -146,7 +146,7 @@ var builtin_id(context* ctx, gc* ngc) { var builtin_int(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type!=vm_num && val.type!=vm_str) { + if (!val.is_num() && !val.is_str()) { return nil; } return var::num(static_cast(static_cast(val.to_num()))); @@ -164,10 +164,10 @@ var builtin_ceil(context* ctx, gc* ngc) { var builtin_num(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type==vm_num) { + if (val.is_num()) { return val; } - if (val.type!=vm_str) { + if (!val.is_str()) { return nil; } auto res = val.to_num(); @@ -179,7 +179,7 @@ var builtin_num(context* ctx, gc* ngc) { var builtin_pop(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type!=vm_vec) { + if (!val.is_vec()) { return nas_err("pop", "\"vec\" must be vector"); } auto& vec = val.vec().elems; @@ -199,18 +199,19 @@ var builtin_size(context* ctx, gc* ngc) { auto val = ctx->localr[1]; f64 num = 0; switch(val.type) { - case vm_num: num = val.num(); break; - case vm_str: num = val.str().length(); break; - case vm_vec: num = val.vec().size(); break; - case vm_hash: num = val.hash().size(); break; - case vm_map: num = val.map().mapper.size(); break; + case vm_type::vm_num: num = val.num(); break; + case vm_type::vm_str: num = val.str().length(); break; + case vm_type::vm_vec: num = val.vec().size(); break; + case vm_type::vm_hash: num = val.hash().size(); break; + case vm_type::vm_map: num = val.map().mapper.size(); break; + default: break; } return var::num(num); } var builtin_time(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type!=vm_num) { + if (!val.is_num()) { return nas_err("time", "\"begin\" must be number"); } auto begin = static_cast(val.num()); @@ -221,7 +222,7 @@ var builtin_contains(context* ctx, gc* ngc) { auto local = ctx->localr; var hash = local[1]; var key = local[2]; - if (hash.type!=vm_hash || key.type!=vm_str) { + if (!hash.is_hash() || !key.is_str()) { return zero; } return hash.hash().elems.count(key.str())? one:zero; @@ -231,10 +232,10 @@ var builtin_delete(context* ctx, gc* ngc) { auto local = ctx->localr; var hash = local[1]; var key = local[2]; - if (hash.type!=vm_hash) { + if (!hash.is_hash()) { return nas_err("delete", "\"hash\" must be hash"); } - if (key.type!=vm_str) { + if (!key.is_str()) { return nil; } if (hash.hash().elems.count(key.str())) { @@ -245,13 +246,13 @@ var builtin_delete(context* ctx, gc* ngc) { var builtin_keys(context* ctx, gc* ngc) { auto hash = ctx->localr[1]; - if (hash.type!=vm_hash && hash.type!=vm_map) { + if (!hash.is_hash() && !hash.is_map()) { return nas_err("keys", "\"hash\" must be hash"); } // avoid being sweeped - auto res = ngc->temp = ngc->alloc(vm_vec); + auto res = ngc->temp = ngc->alloc(vm_type::vm_vec); auto& vec = res.vec().elems; - if (hash.type==vm_hash) { + if (hash.is_hash()) { for(const auto& iter : hash.hash().elems) { vec.push_back(ngc->newstr(iter.first)); } @@ -281,16 +282,17 @@ var builtin_find(context* ctx, gc* ngc) { var builtin_type(context* ctx, gc* ngc) { switch(ctx->localr[1].type) { - case vm_none: return ngc->newstr("undefined"); - case vm_nil: return ngc->newstr("nil"); - case vm_num: return ngc->newstr("num"); - case vm_str: return ngc->newstr("str"); - case vm_vec: return ngc->newstr("vec"); - case vm_hash: return ngc->newstr("hash"); - case vm_func: return ngc->newstr("func"); - case vm_obj: return ngc->newstr("obj"); - case vm_co: return ngc->newstr("coroutine"); - case vm_map: return ngc->newstr("namespace"); + case vm_type::vm_none: return ngc->newstr("undefined"); + case vm_type::vm_nil: return ngc->newstr("nil"); + case vm_type::vm_num: return ngc->newstr("num"); + case vm_type::vm_str: return ngc->newstr("str"); + case vm_type::vm_vec: return ngc->newstr("vec"); + case vm_type::vm_hash: return ngc->newstr("hash"); + case vm_type::vm_func: return ngc->newstr("func"); + case vm_type::vm_ghost: return ngc->newstr("ghost"); + case vm_type::vm_co: return ngc->newstr("coroutine"); + case vm_type::vm_map: return ngc->newstr("namespace"); + default: break; } return nil; } @@ -300,13 +302,13 @@ var builtin_substr(context* ctx, gc* ngc) { var str = local[1]; var beg = local[2]; var len = local[3]; - if (str.type!=vm_str) { + if (!str.is_str()) { return nas_err("substr", "\"str\" must be string"); } - if (beg.type!=vm_num || beg.num()<0) { + if (!beg.is_num() || beg.num()<0) { return nas_err("substr", "\"begin\" should be number >= 0"); } - if (len.type!=vm_num || len.num()<0) { + if (!len.is_num() || len.num()<0) { return nas_err("substr", "\"length\" should be number >= 0"); } auto begin = static_cast(beg.num()); @@ -322,7 +324,7 @@ var builtin_streq(context* ctx, gc* ngc) { var a = local[1]; var b = local[2]; return var::num(static_cast( - (a.type!=vm_str || b.type!=vm_str)? 0:(a.str()==b.str()) + (!a.is_str() || !b.is_str())? 0:(a.str()==b.str()) )); } @@ -330,10 +332,10 @@ var builtin_left(context* ctx, gc* ngc) { auto local = ctx->localr; var str = local[1]; var len = local[2]; - if (str.type!=vm_str) { + if (!str.is_str()) { return nas_err("left", "\"string\" must be string"); } - if (len.type!=vm_num) { + if (!len.is_num()) { return nas_err("left", "\"length\" must be number"); } if (len.num()<0) { @@ -346,10 +348,10 @@ var builtin_right(context* ctx, gc* ngc) { auto local = ctx->localr; var str = local[1]; var len = local[2]; - if (str.type!=vm_str) { + if (!str.is_str()) { return nas_err("right", "\"string\" must be string"); } - if (len.type!=vm_num) { + if (!len.is_num()) { return nas_err("right", "\"length\" must be number"); } i32 length = static_cast(len.num()); @@ -367,7 +369,7 @@ var builtin_cmp(context* ctx, gc* ngc) { auto local = ctx->localr; var a = local[1]; var b = local[2]; - if (a.type!=vm_str || b.type!=vm_str) { + if (!a.is_str() || !b.is_str()) { return nas_err("cmp", "\"a\" and \"b\" must be string"); } return var::num(static_cast(strcmp( @@ -410,12 +412,12 @@ var builtin_char(context* ctx, gc* ngc) { var builtin_values(context* ctx, gc* ngc) { auto hash = ctx->localr[1]; - if (hash.type!=vm_hash && hash.type!=vm_map) { + if (!hash.is_hash() && !hash.is_map()) { return nas_err("values", "\"hash\" must be hash or namespace"); } - auto vec = ngc->alloc(vm_vec); + auto vec = ngc->alloc(vm_type::vm_vec); auto& v = vec.vec().elems; - if (hash.type==vm_hash) { + if (hash.is_hash()) { for(auto& i : hash.hash().elems) { v.push_back(i.second); } @@ -429,7 +431,7 @@ var builtin_values(context* ctx, gc* ngc) { var builtin_sleep(context* ctx, gc* ngc) { auto val = ctx->localr[1]; - if (val.type!=vm_num) { + if (!val.is_num()) { return nil; } #if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS) @@ -561,46 +563,106 @@ std::string md5(const std::string& src) { var builtin_md5(context* ctx, gc* ngc) { auto str = ctx->localr[1]; - if (str.type!=vm_str) { + if (!str.is_str()) { return nas_err("md5", "\"str\" must be string"); } return ngc->newstr(md5(str.str())); } -var builtin_millisec(context* ctx, gc* ngc) { - f64 res = std::chrono::duration_cast - (std::chrono::high_resolution_clock::now().time_since_epoch()) - .count(); - return var::num(res); +class time_stamp { +private: + std::chrono::high_resolution_clock::time_point stamp; + +public: + time_stamp() { + stamp = std::chrono::high_resolution_clock::now(); + } + + void make_stamp() { + stamp = std::chrono::high_resolution_clock::now(); + } + + f64 elapsed_milliseconds() { + auto duration = std::chrono::high_resolution_clock::now() - stamp; + return std::chrono::duration_cast(duration).count(); + } + + f64 elapsed_microseconds() { + auto duration = std::chrono::high_resolution_clock::now() - stamp; + return std::chrono::duration_cast(duration).count(); + } +}; + +void time_stamp_destructor(void* ptr) { + delete static_cast(ptr); +} + +var builtin_maketimestamp(context* ctx, gc* ngc) { + auto res = ngc->alloc(vm_type::vm_ghost); + res.ghost().set( + "nasal-time-stamp", + time_stamp_destructor, + nullptr, + new time_stamp + ); + return res; +} + +var builtin_time_stamp(context* ctx, gc* ngc) { + auto object = ctx->localr[1]; + if (!object.object_check("nasal-time-stamp")) { + return nil; + } + auto stamp = static_cast(object.ghost().pointer); + stamp->make_stamp(); + return nil; +} + +var builtin_elapsed_millisecond(context* ctx, gc* ngc) { + auto object = ctx->localr[1]; + if (!object.object_check("nasal-time-stamp")) { + return var::num(-1); + } + auto stamp = static_cast(object.ghost().pointer); + return var::num(stamp->elapsed_milliseconds()); +} + +var builtin_elapsed_microsecond(context* ctx, gc* ngc) { + auto object = ctx->localr[1]; + if (!object.object_check("nasal-time-stamp")) { + return var::num(-1); + } + auto stamp = static_cast(object.ghost().pointer); + return var::num(stamp->elapsed_microseconds()); } var builtin_gcextend(context* ctx, gc* ngc) { auto type = ctx->localr[1]; - if (type.type!=vm_str) { + if (!type.is_str()) { return nil; } const auto& s = type.str(); if (s=="str") { - ngc->extend(vm_str); + ngc->extend(vm_type::vm_str); } else if (s=="vec") { - ngc->extend(vm_vec); + ngc->extend(vm_type::vm_vec); } else if (s=="hash") { - ngc->extend(vm_hash); + ngc->extend(vm_type::vm_hash); } else if (s=="func") { - ngc->extend(vm_func); + ngc->extend(vm_type::vm_func); } else if (s=="upval") { - ngc->extend(vm_upval); - } else if (s=="obj") { - ngc->extend(vm_obj); + ngc->extend(vm_type::vm_upval); + } else if (s=="ghost") { + ngc->extend(vm_type::vm_ghost); } else if (s=="co") { - ngc->extend(vm_co); + ngc->extend(vm_type::vm_co); } return nil; } var builtin_gcinfo(context* ctx, gc* ngc) { auto den = std::chrono::high_resolution_clock::duration::period::den; - var res = ngc->alloc(vm_hash); + var res = ngc->alloc(vm_type::vm_hash); double total = 0; for(u32 i = 0; ilocalr[1]; - if (arg.type!=vm_obj) { + if (!arg.is_ghost()) { return nas_err("ghosttype", "this is not a ghost object."); } const auto& name = arg.ghost().get_ghost_name(); @@ -682,7 +744,10 @@ nasal_builtin_table builtin[] = { {"__platform", builtin_platform}, {"__arch", builtin_arch}, {"__md5", builtin_md5}, - {"__millisec", builtin_millisec}, + {"__maketimestamp", builtin_maketimestamp}, + {"__time_stamp", builtin_time_stamp}, + {"__elapsed_millisecond", builtin_elapsed_millisecond}, + {"__elapsed_microsecond", builtin_elapsed_microsecond}, {"__gcextd", builtin_gcextend}, {"__gcinfo", builtin_gcinfo}, {"__logtime", builtin_logtime}, diff --git a/src/nasal_builtin.h b/src/nasal_builtin.h index d7c3dd55..d3668799 100644 --- a/src/nasal_builtin.h +++ b/src/nasal_builtin.h @@ -69,7 +69,10 @@ var builtin_arch(context*, gc*); std::string tohex(u32); std::string md5(const std::string&); var builtin_md5(context*, gc*); -var builtin_millisec(context*, gc*); +var builtin_maketimestamp(context*, gc*); +var builtin_time_stamp(context*, gc*); +var builtin_elapsed_millisecond(context*, gc*); +var builtin_elapsed_microsecond(context*, gc*); var builtin_gcextend(context*, gc*); var builtin_gcinfo(context*, gc*); var builtin_logtime(context*, gc*); diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index 7c5a72c7..95a135e8 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -30,6 +30,7 @@ void codegen::init_native_function() { load_native_function_table(flight_gear_native); load_native_function_table(dylib_lib_native); load_native_function_table(unix_lib_native); + load_native_function_table(json_lib_native); } void codegen::check_id_exist(identifier* node) { diff --git a/src/nasal_codegen.h b/src/nasal_codegen.h index af1635cb..221a0d35 100644 --- a/src/nasal_codegen.h +++ b/src/nasal_codegen.h @@ -14,6 +14,7 @@ #include "math_lib.h" #include "fg_props.h" #include "io_lib.h" +#include "json_lib.h" #include "dylib_lib.h" #include "unix_lib.h" diff --git a/src/nasal_gc.cpp b/src/nasal_gc.cpp index d0bdda3e..e4024479 100644 --- a/src/nasal_gc.cpp +++ b/src/nasal_gc.cpp @@ -38,7 +38,7 @@ void gc::mark() { while(!bfs.empty()) { var value = bfs.back(); bfs.pop_back(); - if (value.type<=vm_num || + if (value.type<=vm_type::vm_num || value.val.gcobj->mark!=nas_val::gc_status::uncollected) { continue; } @@ -50,7 +50,7 @@ void gc::concurrent_mark(std::vector& vec, usize begin, usize end) { std::vector bfs; for(auto i = begin; imark!=nas_val::gc_status::uncollected) { continue; } @@ -59,7 +59,7 @@ void gc::concurrent_mark(std::vector& vec, usize begin, usize end) { while(!bfs.empty()) { var value = bfs.back(); bfs.pop_back(); - if (value.type<=vm_num || + if (value.type<=vm_type::vm_num || value.val.gcobj->mark!=nas_val::gc_status::uncollected) { continue; } @@ -71,13 +71,13 @@ void gc::mark_context_root(std::vector& bfs_queue) { // scan global for(usize i = 0; ivm_num) { + if (val.type>vm_type::vm_num) { bfs_queue.push_back(val); } } // scan now running context, this context maybe related to coroutine or main for(var* i = running_context->stack; i<=running_context->top; ++i) { - if (i->type>vm_num) { + if (i->type>vm_type::vm_num) { bfs_queue.push_back(*i); } } @@ -91,7 +91,7 @@ void gc::mark_context_root(std::vector& bfs_queue) { // coroutine is running, so scan main process stack from mctx for(var* i = main_context.stack; i<=main_context.top; ++i) { - if (i->type>vm_num) { + if (i->type>vm_type::vm_num) { bfs_queue.push_back(*i); } } @@ -102,19 +102,20 @@ void gc::mark_context_root(std::vector& bfs_queue) { void gc::mark_var(std::vector& bfs_queue, var& value) { value.val.gcobj->mark = nas_val::gc_status::found; switch(value.type) { - case vm_vec: mark_vec(bfs_queue, value.vec()); break; - case vm_hash: mark_hash(bfs_queue, value.hash()); break; - case vm_func: mark_func(bfs_queue, value.func()); break; - case vm_upval: mark_upval(bfs_queue, value.upval()); break; - case vm_co: mark_co(bfs_queue, value.co()); break; - case vm_map: mark_map(bfs_queue, value.map()); break; + case vm_type::vm_vec: mark_vec(bfs_queue, value.vec()); break; + case vm_type::vm_hash: mark_hash(bfs_queue, value.hash()); break; + case vm_type::vm_func: mark_func(bfs_queue, value.func()); break; + case vm_type::vm_upval: mark_upval(bfs_queue, value.upval()); break; + case vm_type::vm_ghost: mark_ghost(bfs_queue, value.ghost()); break; + case vm_type::vm_co: mark_co(bfs_queue, value.co()); break; + case vm_type::vm_map: mark_map(bfs_queue, value.map()); break; default: break; } } void gc::mark_vec(std::vector& bfs_queue, nas_vec& vec) { for(auto& i : vec.elems) { - if (i.type>vm_num) { + if (i.type>vm_type::vm_num) { bfs_queue.push_back(i); } } @@ -122,7 +123,7 @@ void gc::mark_vec(std::vector& bfs_queue, nas_vec& vec) { void gc::mark_hash(std::vector& bfs_queue, nas_hash& hash) { for(auto& i : hash.elems) { - if (i.second.type>vm_num) { + if (i.second.type>vm_type::vm_num) { bfs_queue.push_back(i.second); } } @@ -130,7 +131,7 @@ void gc::mark_hash(std::vector& bfs_queue, nas_hash& hash) { void gc::mark_func(std::vector& bfs_queue, nas_func& function) { for(auto& i : function.local) { - if (i.type>vm_num) { + if (i.type>vm_type::vm_num) { bfs_queue.push_back(i); } } @@ -141,17 +142,24 @@ void gc::mark_func(std::vector& bfs_queue, nas_func& function) { void gc::mark_upval(std::vector& bfs_queue, nas_upval& upval) { for(auto& i : upval.elems) { - if (i.type>vm_num) { + if (i.type>vm_type::vm_num) { bfs_queue.push_back(i); } } } +void gc::mark_ghost(std::vector& bfs_queue, nas_ghost& ghost) { + if (!ghost.gc_mark_function) { + return; + } + ghost.gc_mark_function(ghost.pointer, &bfs_queue); +} + void gc::mark_co(std::vector& bfs_queue, nas_co& co) { bfs_queue.push_back(co.ctx.funcr); bfs_queue.push_back(co.ctx.upvalr); for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) { - if (i->type>vm_num) { + if (i->type>vm_type::vm_num) { bfs_queue.push_back(*i); } } @@ -159,7 +167,7 @@ void gc::mark_co(std::vector& bfs_queue, nas_co& co) { void gc::mark_map(std::vector& bfs_queue, nas_map& mp) { for(const auto& i : mp.mapper) { - if (i.second->type>vm_num) { + if (i.second->type>vm_type::vm_num) { bfs_queue.push_back(*i.second); } } @@ -169,7 +177,7 @@ void gc::sweep() { for(auto i : memory) { if (i->mark==nas_val::gc_status::uncollected) { i->clear(); - unused[i->type-vm_str].push_back(i); + unused[static_cast(i->type)-static_cast(vm_type::vm_str)].push_back(i); i->mark = nas_val::gc_status::collected; } else if (i->mark==nas_val::gc_status::found) { i->mark = nas_val::gc_status::uncollected; @@ -177,8 +185,8 @@ void gc::sweep() { } } -void gc::extend(u8 type) { - const u8 index = type-vm_str; +void gc::extend(const vm_type type) { + const u8 index = static_cast(type)-static_cast(vm_type::vm_str); size[index] += incr[index]; for(u32 i = 0; iunmutable = 1; strs[i].str() = constant_strings[i]; } @@ -222,10 +230,10 @@ void gc::init( env_argv.resize(argv.size()); for(usize i = 0; iunmutable = 1; env_argv[i].str() = argv[i]; } @@ -263,7 +271,7 @@ void gc::info() const { "hashmap", "function", "upvalue", - "object", + "ghost", "coroutine", "namespace", nullptr @@ -336,8 +344,8 @@ void gc::info() const { std::clog << last_line << "\n"; } -var gc::alloc(u8 type) { - const u8 index = type-vm_str; +var gc::alloc(const vm_type type) { + const u8 index = static_cast(type)-static_cast(vm_type::vm_str); ++acnt[index]; if (unused[index].empty()) { ++gcnt[index]; diff --git a/src/nasal_gc.h b/src/nasal_gc.h index 4ce39e7b..63df51a5 100644 --- a/src/nasal_gc.h +++ b/src/nasal_gc.h @@ -45,7 +45,7 @@ struct gc { 128, // vm_str 128, // vm_vec 64, // vm_hash - 128, // vm_func + 256, // vm_func 256, // vm_upval 16, // vm_obj 16, // vm_co @@ -78,34 +78,35 @@ struct gc { void mark_hash(std::vector&, nas_hash&); void mark_func(std::vector&, nas_func&); void mark_upval(std::vector&, nas_upval&); + void mark_ghost(std::vector&, nas_ghost&); void mark_co(std::vector&, nas_co&); void mark_map(std::vector&, nas_map&); void sweep(); public: - void extend(u8); + void extend(const vm_type); void init(const std::vector&, const std::vector&); void clear(); void info() const; - var alloc(const u8); + var alloc(const vm_type); void context_change(nas_co*); void context_reserve(); public: var newstr(char c) { - var s = alloc(vm_str); + var s = alloc(vm_type::vm_str); s.str() = c; return s; } var newstr(const char* buff) { - var s = alloc(vm_str); + var s = alloc(vm_type::vm_str); s.str() = std::string(buff); return s; } var newstr(const std::string& buff) { - var s = alloc(vm_str); + var s = alloc(vm_type::vm_str); s.str() = buff; return s; } @@ -123,4 +124,13 @@ struct module_func_info { // module function "get" type typedef module_func_info* (*get_func_ptr)(); + +// avoid error loading function bug in MSVC version nasal.exe +#ifdef _MSC_VER + // and fuck MSVC again + #define NASAL_EXTERN extern "C" __declspec(dllexport) +#else + #define NASAL_EXTERN extern "C" +#endif + } diff --git a/src/nasal_import.cpp b/src/nasal_import.cpp index 2904b824..0d06ea70 100644 --- a/src/nasal_import.cpp +++ b/src/nasal_import.cpp @@ -1,399 +1,399 @@ -#include "nasal_import.h" -#include "symbol_finder.h" - -#include -#include - -namespace nasal { - -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 = position+1; - position = PATH.find(seperator, last); - } - if (last!=PATH.length()) { - envpath.push_back(PATH.substr(last)); - } -} - -std::string linker::get_path(expr* node) { - if (node->get_type()==expr_type::ast_use) { - 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 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 path_list = {filename}; - - // generate search path from environ path - for(const auto& p : envpath) { - path_list.push_back(p + (is_windows()? "\\":"/") + filename); - } - - // search file - for(const auto& path : path_list) { - if (access(path.c_str(), F_OK)!=-1) { - return path; - } - } - - // we will find lib.nas in nasal std directory - if (filename=="lib.nas") { - return is_windows()? - find_real_file_path("std\\lib.nas", location): - find_real_file_path("std/lib.nas", location); - } - if (!show_path_flag) { - err.err("link", - "in <" + location.file + ">: " + - "cannot find file <" + filename + ">, " + - "use <-d> to get detail search path" - ); - return ""; - } - 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" + path_list_info - ); - return ""; -} - -bool linker::import_check(expr* node) { - if (node->get_type()==expr_type::ast_use) { - return true; - } -/* - call - |_id:import - |_call_func - |_string:'filename' -*/ - if (node->get_type()!=expr_type::ast_call) { - return false; - } - 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 (!call_node->get_calls().size()) { - return false; - } - - // import("xxx"); - if (call_node->get_calls().size()!=1) { - return false; - } - auto maybe_func_call = call_node->get_calls()[0]; - if (maybe_func_call->get_type()!=expr_type::ast_callf) { - return false; - } - auto func_call = reinterpret_cast(maybe_func_call); - if (func_call->get_argument().size()!=1) { - return false; - } - if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) { - return false; - } - return true; -} - -bool linker::check_exist_or_record_file(const std::string& file) { - // avoid importing the same file - for(const auto& name : imported_files) { - if (file==name) { - return true; - } - } - imported_files.push_back(file); - return false; -} - -bool linker::check_self_import(const std::string& file) { - for(const auto& name : module_load_stack) { - if (file==name) { - return true; - } - } - return false; -} - -std::string linker::generate_self_import_path(const std::string& filename) { - std::string res = ""; - for(const auto& i : module_load_stack) { - res += "[" + i + "] -> "; - } - return res + "[" + filename + "]"; -} - -void linker::link(code_block* new_tree_root, code_block* old_tree_root) { - // add children of add_root to the back of root - for(auto& i : old_tree_root->get_expressions()) { - new_tree_root->add_expression(i); - } - // clean old root - old_tree_root->get_expressions().clear(); -} - -code_block* linker::import_regular_file( - expr* node, std::unordered_set& used_modules) { - // get filename - auto filename = get_path(node); - - // avoid infinite loading loop - filename = find_real_file_path(filename, node->get_location()); - // 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}); - } - - // check self import, avoid infinite loading loop - if (check_self_import(filename)) { - err.err("link", - "self-referenced module <" + filename + ">:\n" + - " reference path: " + generate_self_import_path(filename) - ); - return new code_block({0, 0, 0, 0, filename}); - } - check_exist_or_record_file(filename); - - module_load_stack.push_back(filename); - // start importing... - 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 (nasal_parser.compile(nasal_lexer).geterr()) { - err.err("link", "error occurred when analysing <" + filename + ">"); - return new code_block({0, 0, 0, 0, filename}); - } - // swap result out - auto parse_result = nasal_parser.swap(nullptr); - - // check if parse result has 'import' - auto result = load(parse_result, filename); - module_load_stack.pop_back(); - return result; -} - -code_block* linker::import_nasal_lib() { - 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}); - } - - // avoid infinite loading library - 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(path).geterr()) { - err.err("link", - "error occurred when analysing library <" + path + ">" - ); - return new code_block({0, 0, 0, 0, path}); - } - if (nasal_parser.compile(nasal_lexer).geterr()) { - err.err("link", - "error occurred when analysing library <" + path + ">" - ); - 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, path); -} - -std::string linker::generate_module_name(const std::string& file_path) { - auto error_name = "module@[" + file_path + "]"; - if (!file_path.length()) { - return error_name; - } - - // check file suffix and get file suffix position - auto suffix_position = file_path.find(".nas"); - if (suffix_position==std::string::npos) { - err.warn("link", - "get invalid module name from <" + file_path + ">, " + - "will not be easily accessed. " + - "\".nas\" suffix is required." - ); - return error_name; - } - if (suffix_position+4!=file_path.length()) { - err.warn("link", - "get invalid module name from <" + file_path + ">, " + - "will not be easily accessed. " + - "only one \".nas\" suffix is required in the path." - ); - return error_name; - } - - // only get the file name as module name, directory path is not included - auto split_position = file_path.find_last_of("/"); - // find "\\" in windows platform - if (split_position==std::string::npos) { - split_position = file_path.find_last_of("\\"); - } - - // split file path to get module name - auto module_name = split_position==std::string::npos? - file_path.substr(0, suffix_position): - file_path.substr(split_position+1, suffix_position-split_position-1); - - // check validation of module name - if (!module_name.length()) { - err.warn("link", - "get empty module name from <" + file_path + ">, " + - "will not be easily accessed." - ); - return module_name; - } - 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." - ); - } - return module_name; -} - -return_expr* linker::generate_module_return(code_block* block) { - auto finder = std::unique_ptr(new symbol_finder); - auto result = new return_expr(block->get_location()); - auto value = new hash_expr(block->get_location()); - result->set_value(value); - for(const auto& i : finder->do_find(block)) { - auto pair = new hash_pair(block->get_location()); - // do not export symbol begins with '_' - if (i.name.length() && i.name[0]=='_') { - continue; - } - pair->set_name(i.name); - pair->set_value(new identifier(block->get_location(), i.name)); - value->add_member(pair); - } - return result; -} - -definition_expr* linker::generate_module_definition(code_block* block) { - auto def = new definition_expr(block->get_location()); - def->set_identifier(new identifier( - block->get_location(), - generate_module_name(block->get_location().file) - )); - - auto call = new call_expr(block->get_location()); - auto func = new function(block->get_location()); - func->set_code_block(block); - func->get_code_block()->add_expression(generate_module_return(block)); - call->set_first(func); - call->add_call(new call_function(block->get_location())); - - def->set_value(call); - return def; -} - -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 (!library_loaded) { - auto nasal_lib_code_block = import_nasal_lib(); - // insert nasal lib code to the back of tree - link(tree, nasal_lib_code_block); - delete nasal_lib_code_block; - library_loaded = true; - } - - // load imported modules - std::unordered_set used_modules = {}; - for(auto& import_node : program_root->get_expressions()) { - if (!import_check(import_node)) { - break; - } - // 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_node; - // and replace the node with null_expr node - 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; - } - - // 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)); - } - - // insert program root to the back of tree - link(tree, program_root); - return tree; -} - -const error& linker::link( - parse& parse, const std::string& self, bool spath = false) { - // switch for showing path when errors occur - show_path_flag = spath; - - // initializing file map - this_file = self; - imported_files = {self}; - module_load_stack = {self}; - - // scan root and import files - // then generate a new ast and return to import_ast - auto new_tree_root = load(parse.tree(), self); - auto old_tree_root = parse.swap(new_tree_root); - delete old_tree_root; - return err; -} - -} +#include "nasal_import.h" +#include "symbol_finder.h" + +#include +#include + +namespace nasal { + +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 = position+1; + position = PATH.find(seperator, last); + } + if (last!=PATH.length()) { + envpath.push_back(PATH.substr(last)); + } +} + +std::string linker::get_path(expr* node) { + if (node->get_type()==expr_type::ast_use) { + 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 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 path_list = {filename}; + + // generate search path from environ path + for(const auto& p : envpath) { + path_list.push_back(fs::path(p)/filename); + } + + // search file + for(const auto& path : path_list) { + if (fs::exists(path)) { + return path.str(); + } + } + + // we will find lib.nas in nasal std directory + if (filename=="lib.nas") { + return is_windows()? + find_real_file_path("std\\lib.nas", location): + find_real_file_path("std/lib.nas", location); + } + if (!show_path_flag) { + err.err("link", + "in <" + location.file + ">: " + + "cannot find file <" + filename + ">, " + + "use <-d> to get detail search path" + ); + return ""; + } + auto path_list_info = std::string(""); + for(const auto& path : path_list) { + path_list_info += " -> " + path.str() + "\n"; + } + err.err("link", + "in <" + location.file + ">: " + + "cannot find file <" + filename + + "> in these paths:\n" + path_list_info + ); + return ""; +} + +bool linker::import_check(expr* node) { + if (node->get_type()==expr_type::ast_use) { + return true; + } +/* + call + |_id:import + |_call_func + |_string:'filename' +*/ + if (node->get_type()!=expr_type::ast_call) { + return false; + } + 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 (!call_node->get_calls().size()) { + return false; + } + + // import("xxx"); + if (call_node->get_calls().size()!=1) { + return false; + } + auto maybe_func_call = call_node->get_calls()[0]; + if (maybe_func_call->get_type()!=expr_type::ast_callf) { + return false; + } + auto func_call = reinterpret_cast(maybe_func_call); + if (func_call->get_argument().size()!=1) { + return false; + } + if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) { + return false; + } + return true; +} + +bool linker::check_exist_or_record_file(const std::string& file) { + // avoid importing the same file + for(const auto& name : imported_files) { + if (file==name) { + return true; + } + } + imported_files.push_back(file); + return false; +} + +bool linker::check_self_import(const std::string& file) { + for(const auto& name : module_load_stack) { + if (file==name) { + return true; + } + } + return false; +} + +std::string linker::generate_self_import_path(const std::string& filename) { + std::string res = ""; + for(const auto& i : module_load_stack) { + res += "[" + i + "] -> "; + } + return res + "[" + filename + "]"; +} + +void linker::link(code_block* new_tree_root, code_block* old_tree_root) { + // add children of add_root to the back of root + for(auto& i : old_tree_root->get_expressions()) { + new_tree_root->add_expression(i); + } + // clean old root + old_tree_root->get_expressions().clear(); +} + +code_block* linker::import_regular_file( + expr* node, std::unordered_set& used_modules) { + // get filename + auto filename = get_path(node); + + // avoid infinite loading loop + filename = find_real_file_path(filename, node->get_location()); + // 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}); + } + + // check self import, avoid infinite loading loop + if (check_self_import(filename)) { + err.err("link", + "self-referenced module <" + filename + ">:\n" + + " reference path: " + generate_self_import_path(filename) + ); + return new code_block({0, 0, 0, 0, filename}); + } + check_exist_or_record_file(filename); + + module_load_stack.push_back(filename); + // start importing... + 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 (nasal_parser.compile(nasal_lexer).geterr()) { + err.err("link", "error occurred when analysing <" + filename + ">"); + return new code_block({0, 0, 0, 0, filename}); + } + // swap result out + auto parse_result = nasal_parser.swap(nullptr); + + // check if parse result has 'import' + auto result = load(parse_result, filename); + module_load_stack.pop_back(); + return result; +} + +code_block* linker::import_nasal_lib() { + 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}); + } + + // avoid infinite loading library + 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(path).geterr()) { + err.err("link", + "error occurred when analysing library <" + path + ">" + ); + return new code_block({0, 0, 0, 0, path}); + } + if (nasal_parser.compile(nasal_lexer).geterr()) { + err.err("link", + "error occurred when analysing library <" + path + ">" + ); + 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, path); +} + +std::string linker::generate_module_name(const std::string& file_path) { + auto error_name = "module@[" + file_path + "]"; + if (!file_path.length()) { + return error_name; + } + + // check file suffix and get file suffix position + auto suffix_position = file_path.find(".nas"); + if (suffix_position==std::string::npos) { + err.warn("link", + "get invalid module name from <" + file_path + ">, " + + "will not be easily accessed. " + + "\".nas\" suffix is required." + ); + return error_name; + } + if (suffix_position+4!=file_path.length()) { + err.warn("link", + "get invalid module name from <" + file_path + ">, " + + "will not be easily accessed. " + + "only one \".nas\" suffix is required in the path." + ); + return error_name; + } + + // only get the file name as module name, directory path is not included + auto split_position = file_path.find_last_of("/"); + // find "\\" in windows platform + if (split_position==std::string::npos) { + split_position = file_path.find_last_of("\\"); + } + + // split file path to get module name + auto module_name = split_position==std::string::npos? + file_path.substr(0, suffix_position): + file_path.substr(split_position+1, suffix_position-split_position-1); + + // check validation of module name + if (!module_name.length()) { + err.warn("link", + "get empty module name from <" + file_path + ">, " + + "will not be easily accessed." + ); + return module_name; + } + 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." + ); + } + return module_name; +} + +return_expr* linker::generate_module_return(code_block* block) { + auto finder = std::unique_ptr(new symbol_finder); + auto result = new return_expr(block->get_location()); + auto value = new hash_expr(block->get_location()); + result->set_value(value); + for(const auto& i : finder->do_find(block)) { + auto pair = new hash_pair(block->get_location()); + // do not export symbol begins with '_' + if (i.name.length() && i.name[0]=='_') { + continue; + } + pair->set_name(i.name); + pair->set_value(new identifier(block->get_location(), i.name)); + value->add_member(pair); + } + return result; +} + +definition_expr* linker::generate_module_definition(code_block* block) { + auto def = new definition_expr(block->get_location()); + def->set_identifier(new identifier( + block->get_location(), + generate_module_name(block->get_location().file) + )); + + auto call = new call_expr(block->get_location()); + auto func = new function(block->get_location()); + func->set_code_block(block); + func->get_code_block()->add_expression(generate_module_return(block)); + call->set_first(func); + call->add_call(new call_function(block->get_location())); + + def->set_value(call); + return def; +} + +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 (!library_loaded) { + auto nasal_lib_code_block = import_nasal_lib(); + // insert nasal lib code to the back of tree + link(tree, nasal_lib_code_block); + delete nasal_lib_code_block; + library_loaded = true; + } + + // load imported modules + std::unordered_set used_modules = {}; + for(auto& import_node : program_root->get_expressions()) { + if (!import_check(import_node)) { + break; + } + // 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_node; + // and replace the node with null_expr node + 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; + } + + // 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)); + } + + // insert program root to the back of tree + link(tree, program_root); + return tree; +} + +const error& linker::link( + parse& parse, const std::string& self, bool spath = false) { + // switch for showing path when errors occur + show_path_flag = spath; + + // initializing file map + this_file = self; + imported_files = {self}; + module_load_stack = {self}; + + // scan root and import files + // then generate a new ast and return to import_ast + 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 7fa0df36..692c9222 100644 --- a/src/nasal_import.h +++ b/src/nasal_import.h @@ -1,59 +1,55 @@ -#pragma once - -#ifndef _MSC_VER -#include -#else -#define _CRT_SECURE_NO_DEPRECATE 1 -#define _CRT_NONSTDC_NO_DEPRECATE 1 -#include -#endif - -#ifdef _MSC_VER -#define F_OK 0 -#endif - -#include "nasal.h" -#include "nasal_ast.h" -#include "nasal_lexer.h" -#include "nasal_parse.h" -#include "symbol_finder.h" - -#include -#include -#include -#include - -namespace nasal { - -class linker { -private: - bool show_path_flag; - bool library_loaded; - std::string this_file; - error err; - std::vector imported_files; - std::vector module_load_stack; - std::vector envpath; - -private: - bool import_check(expr*); - 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*, std::unordered_set&); - 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*, const std::string&); - -public: - linker(); - const error& link(parse&, const std::string&, bool); - const auto& get_file_list() const {return imported_files;} -}; - -} +#pragma once + +#ifndef _MSC_VER +#include +#else +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#include +#endif + +#include "nasal.h" +#include "nasal_ast.h" +#include "nasal_lexer.h" +#include "nasal_parse.h" +#include "symbol_finder.h" + +#include +#include +#include +#include + +namespace nasal { + +class linker { +private: + bool show_path_flag; + bool library_loaded; + std::string this_file; + error err; + std::vector imported_files; + std::vector module_load_stack; + std::vector envpath; + +private: + bool import_check(expr*); + 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*, std::unordered_set&); + 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*, const std::string&); + +public: + linker(); + const error& link(parse&, const std::string&, bool); + const auto& get_file_list() const {return imported_files;} +}; + +} diff --git a/src/nasal_lexer.cpp b/src/nasal_lexer.cpp index cd8cce34..628ab53c 100644 --- a/src/nasal_lexer.cpp +++ b/src/nasal_lexer.cpp @@ -1,393 +1,392 @@ -#ifdef _MSC_VER -#pragma warning (disable:4244) -#pragma warning (disable:4267) -#pragma warning (disable:4102) -#endif - -#include "nasal_lexer.h" -#include "repl.h" - -namespace nasal { - -bool lexer::skip(char c) { - return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0; -} - -bool lexer::is_id(char c) { - return (c=='_') || std::isalpha(c) || (c<0); -} - -bool lexer::is_hex(char c) { - return std::isxdigit(c); -} - -bool lexer::is_oct(char c) { - return '0'<=c && c<='7'; -} - -bool lexer::is_dec(char c) { - return std::isdigit(c); -} - -bool lexer::is_str(char c) { - return c=='\'' || c=='\"' || c=='`'; -} - -bool lexer::is_single_opr(char c) { - return ( - c=='(' || c==')' || c=='[' || c==']' || - c=='{' || c=='}' || c==',' || c==';' || - c==':' || c=='?' || c=='`' || c=='@' || - c=='%' || c=='$' || c=='\\' - ); -} - -bool lexer::is_calc_opr(char c) { - return ( - c=='=' || c=='+' || c=='-' || c=='*' || - c=='!' || c=='/' || c=='<' || c=='>' || - c=='~' || c=='|' || c=='&' || c=='^' - ); -} - -void lexer::skip_note() { - // avoid note, after this process ptr will point to '\n' - // so next loop line counter+1 - while(++ptrin_repl_mode && - repl::info::instance()->repl_file_name==file) { - err.load(file); - filename = file; - res = repl::info::instance()->repl_file_source; - return; - } - - // check file exsits and it is a regular file - struct stat buffer; - if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) { - err.err("lexer", "<"+file+"> is not a regular file"); - err.chkerr(); - } - - // load - filename = file; - std::ifstream in(file, std::ios::binary); - if (in.fail()) { - err.err("lexer", "failed to open <" + file + ">"); - res = ""; - return; - } - err.load(file); - std::stringstream ss; - ss << in.rdbuf(); - res = ss.str(); -} - -tok lexer::get_type(const std::string& str) { - return typetbl.count(str)? typetbl.at(str):tok::null; -} - -std::string lexer::utf8_gen() { - std::string str = ""; - while(ptr" - ); - ++invalid_char; - } - str += tmp; - // may have some problems because not all the unicode takes 2 space - column += 2; - } - return str; -} - -token lexer::id_gen() { - u32 begin_line = line; - u32 begin_column = column; - std::string str = ""; - while(ptr [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) - std::string str = ""; - while(ptr=res.size()) { - err.err("lexer", - {begin_line, begin_column, line, column, filename}, - "get EOF when generating string" - ); - return {{begin_line, begin_column, line, column, filename}, tok::str, str}; - } - ++column; - - // if is not utf8, 1+utf8_hdchk should be 1 - if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) { - err.err("lexer", - {begin_line, begin_column, line, column, filename}, - "\'`\' is used for string including one character" - ); - } - return {{begin_line, begin_column, line, column, filename}, tok::str, str}; -} - -token lexer::single_opr() { - u32 begin_line = line; - u32 begin_column = column; - std::string str(1, res[ptr]); - ++column; - tok type = get_type(str); - if (type==tok::null) { - err.err("lexer", - {begin_line, begin_column, line, column, filename}, - "invalid operator `"+str+"`" - ); - } - ++ptr; - return {{begin_line, begin_column, line, column, filename}, type, str}; -} - -token lexer::dots() { - u32 begin_line = line; - u32 begin_column = column; - std::string str = "."; - if (ptr+2=res.size()) { - break; - } - if (is_id(res[ptr])) { - toks.push_back(id_gen()); - } else if (is_dec(res[ptr])) { - toks.push_back(num_gen()); - } else if (is_str(res[ptr])) { - toks.push_back(str_gen()); - } else if (is_single_opr(res[ptr])) { - toks.push_back(single_opr()); - } else if (res[ptr]=='.') { - toks.push_back(dots()); - } else if (is_calc_opr(res[ptr])) { - toks.push_back(calc_opr()); - } else if (res[ptr]=='#') { - skip_note(); - } else { - err_char(); - } - if (invalid_char>10) { - err.err("lexer", "too many invalid characters, stop"); - break; - } - } - if (toks.size()) { - // eof token's location is the last token's location - toks.push_back({toks.back().loc, tok::eof, ""}); - } else { - // if token sequence is empty, generate a default location - toks.push_back({{line, column, line, column, filename}, tok::eof, ""}); - } - res = ""; - return err; -} - -} +#ifdef _MSC_VER +#pragma warning (disable:4244) +#pragma warning (disable:4267) +#pragma warning (disable:4102) +#endif + +#include "nasal_lexer.h" +#include "repl.h" + +namespace nasal { + +bool lexer::skip(char c) { + return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0; +} + +bool lexer::is_id(char c) { + return (c=='_') || std::isalpha(c) || (c<0); +} + +bool lexer::is_hex(char c) { + return std::isxdigit(c); +} + +bool lexer::is_oct(char c) { + return '0'<=c && c<='7'; +} + +bool lexer::is_dec(char c) { + return std::isdigit(c); +} + +bool lexer::is_str(char c) { + return c=='\'' || c=='\"' || c=='`'; +} + +bool lexer::is_single_opr(char c) { + return ( + c=='(' || c==')' || c=='[' || c==']' || + c=='{' || c=='}' || c==',' || c==';' || + c==':' || c=='?' || c=='`' || c=='@' || + c=='%' || c=='$' || c=='\\' + ); +} + +bool lexer::is_calc_opr(char c) { + return ( + c=='=' || c=='+' || c=='-' || c=='*' || + c=='!' || c=='/' || c=='<' || c=='>' || + c=='~' || c=='|' || c=='&' || c=='^' + ); +} + +void lexer::skip_note() { + // avoid note, after this process ptr will point to '\n' + // so next loop line counter+1 + while(++ptrin_repl_mode && + repl::info::instance()->repl_file_name==file) { + err.load(file); + filename = file; + res = repl::info::instance()->repl_file_source; + return; + } + + // check file exsits and it is a regular file + if (!fs::is_regular(file)) { + err.err("lexer", "<"+file+"> is not a regular file"); + err.chkerr(); + } + + // load + filename = file; + std::ifstream in(file, std::ios::binary); + if (in.fail()) { + err.err("lexer", "failed to open <" + file + ">"); + res = ""; + return; + } + err.load(file); + std::stringstream ss; + ss << in.rdbuf(); + res = ss.str(); +} + +tok lexer::get_type(const std::string& str) { + return typetbl.count(str)? typetbl.at(str):tok::null; +} + +std::string lexer::utf8_gen() { + std::string str = ""; + while(ptr" + ); + ++invalid_char; + } + str += tmp; + // may have some problems because not all the unicode takes 2 space + column += 2; + } + return str; +} + +token lexer::id_gen() { + u32 begin_line = line; + u32 begin_column = column; + std::string str = ""; + while(ptr [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) + std::string str = ""; + while(ptr=res.size()) { + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "get EOF when generating string" + ); + return {{begin_line, begin_column, line, column, filename}, tok::str, str}; + } + ++column; + + // if is not utf8, 1+utf8_hdchk should be 1 + if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) { + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "\'`\' is used for string including one character" + ); + } + return {{begin_line, begin_column, line, column, filename}, tok::str, str}; +} + +token lexer::single_opr() { + u32 begin_line = line; + u32 begin_column = column; + std::string str(1, res[ptr]); + ++column; + tok type = get_type(str); + if (type==tok::null) { + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "invalid operator `"+str+"`" + ); + } + ++ptr; + return {{begin_line, begin_column, line, column, filename}, type, str}; +} + +token lexer::dots() { + u32 begin_line = line; + u32 begin_column = column; + std::string str = "."; + if (ptr+2=res.size()) { + break; + } + if (is_id(res[ptr])) { + toks.push_back(id_gen()); + } else if (is_dec(res[ptr])) { + toks.push_back(num_gen()); + } else if (is_str(res[ptr])) { + toks.push_back(str_gen()); + } else if (is_single_opr(res[ptr])) { + toks.push_back(single_opr()); + } else if (res[ptr]=='.') { + toks.push_back(dots()); + } else if (is_calc_opr(res[ptr])) { + toks.push_back(calc_opr()); + } else if (res[ptr]=='#') { + skip_note(); + } else { + err_char(); + } + if (invalid_char>10) { + err.err("lexer", "too many invalid characters, stop"); + break; + } + } + if (toks.size()) { + // eof token's location is the last token's location + toks.push_back({toks.back().loc, tok::eof, ""}); + } else { + // if token sequence is empty, generate a default location + toks.push_back({{line, column, line, column, filename}, tok::eof, ""}); + } + res = ""; + return err; +} + +} diff --git a/src/nasal_lexer.h b/src/nasal_lexer.h index 99c200d5..4c57f8ee 100644 --- a/src/nasal_lexer.h +++ b/src/nasal_lexer.h @@ -10,15 +10,10 @@ #include #include #include -#include #include "nasal.h" #include "nasal_err.h" -#ifdef _MSC_VER -#define S_ISREG(m) (((m)&0xF000)==0x8000) -#endif - namespace nasal { enum class tok:u32 { diff --git a/src/nasal_misc.cpp b/src/nasal_misc.cpp index 183ec9ed..11f8297f 100644 --- a/src/nasal_misc.cpp +++ b/src/nasal_misc.cpp @@ -1,5 +1,16 @@ #include "nasal.h" +#ifndef _MSC_VER +#include +#else +#include +#endif +#include + +#ifdef _MSC_VER +#pragma warning (disable:4996) +#endif + namespace nasal { bool is_windows() { @@ -86,7 +97,7 @@ bool is_superh() { #endif } -f64 hex2f(const char* str) { +f64 hex_to_f64(const char* str) { f64 ret = 0; for(; *str; ++str) { if ('0'<=*str && *str<='9') { @@ -102,7 +113,7 @@ f64 hex2f(const char* str) { return ret; } -f64 oct2f(const char* str) { +f64 oct_to_f64(const char* str) { f64 ret = 0; while('0'<=*str && *str<'8') { ret = ret*8+(*str++-'0'); @@ -119,7 +130,7 @@ f64 oct2f(const char* str) { // so we write a new function here to convert str to number manually. // but this also makes 0.1+0.2==0.3, // not another result that you may get in other languages. -f64 dec2f(const char* str) { +f64 dec_to_f64(const char* str) { f64 ret = 0, num_pow = 0; bool negative = false; while('0'<=*str && *str<='9') { @@ -165,7 +176,7 @@ f64 dec2f(const char* str) { ret*std::pow(10, num_pow-1)*10; } -f64 str2num(const char* str) { +f64 str_to_num(const char* str) { bool negative = false; f64 res = 0; if (*str=='-' || *str=='+') { @@ -175,11 +186,11 @@ f64 str2num(const char* str) { return nan(""); } if (str[0]=='0' && str[1]=='x') { - res = hex2f(str+2); + res = hex_to_f64(str+2); } else if (str[0]=='0' && str[1]=='o') { - res = oct2f(str+2); + res = oct_to_f64(str+2); } else { - res = dec2f(str); + res = dec_to_f64(str); } return negative? -res:res; } @@ -199,7 +210,7 @@ i32 utf8_hdchk(const char head) { return 0; } -std::string chrhex(const char c) { +std::string char_to_hex(const char c) { const char hextbl[] = "0123456789abcdef"; return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]}; } @@ -209,7 +220,7 @@ std::string rawstr(const std::string& str, const usize maxlen) { for(auto i : str) { // windows doesn't output unicode normally, so we output the hex if (is_windows() && i<=0) { - ret += "\\x"+chrhex(i); + ret += "\\x" + char_to_hex(i); continue; } switch(i) { @@ -234,4 +245,32 @@ std::string rawstr(const std::string& str, const usize maxlen) { return ret; } +namespace fs { + +path& path::operator/(const path& another) { + this->file_system_path += is_windows()? "\\":"/"; + this->file_system_path += another.file_system_path; + return *this; +} + +bool exists(const path& file_path) { +#ifdef _MSC_VER + #define F_OK 0 // fuck msc +#endif + return access(file_path.c_str(), F_OK)==0; +} + +bool is_regular(const path& file_path) { +#ifdef _MSC_VER + #define S_ISREG(m) (((m)&0xF000)==0x8000) +#endif + struct stat buffer; + if (stat(file_path.c_str(), &buffer)!=0) { + return false; + } + return S_ISREG(buffer.st_mode); +} + +} + } diff --git a/src/nasal_opcode.cpp b/src/nasal_opcode.cpp index 8d2478f9..6b8a36c8 100644 --- a/src/nasal_opcode.cpp +++ b/src/nasal_opcode.cpp @@ -111,7 +111,7 @@ void codestream::dump(std::ostream& out) const { break; } if (files) { - out << "(" << files[code.fidx] << ":" << code.line << ")"; + out << " (" << files[code.fidx] << ":" << code.line << ")"; } } diff --git a/src/nasal_parse.cpp b/src/nasal_parse.cpp index e62ff280..20994cb3 100644 --- a/src/nasal_parse.cpp +++ b/src/nasal_parse.cpp @@ -223,7 +223,7 @@ nil_expr* parse::nil() { number_literal* parse::num() { auto node = new number_literal(toks[ptr].loc, - str2num(toks[ptr].str.c_str())); + str_to_num(toks[ptr].str.c_str())); match(tok::num); return node; } diff --git a/src/nasal_type.cpp b/src/nasal_type.cpp index 410300d6..498b6991 100644 --- a/src/nasal_type.cpp +++ b/src/nasal_type.cpp @@ -42,16 +42,16 @@ var nas_hash::get_value(const std::string& key) { } else if (!elems.count("parents")) { return var::none(); } - var ret = var::none(); - var val = elems.at("parents"); - if (val.type!=vm_vec) { + auto ret = var::none(); + auto& val = elems.at("parents"); + if (!val.is_vec()) { return ret; } for(auto& i : val.vec().elems) { - if (i.type==vm_hash) { + if (i.is_hash()) { ret = i.hash().get_value(key); } - if (ret.type!=vm_none) { + if (!ret.is_none()) { return ret; } } @@ -65,12 +65,12 @@ var* nas_hash::get_memory(const std::string& key) { return nullptr; } var* addr = nullptr; - var val = elems.at("parents"); - if (val.type!=vm_vec) { + var& val = elems.at("parents"); + if (!val.is_vec()) { return addr; } for(auto& i : val.vec().elems) { - if (i.type==vm_hash) { + if (i.is_hash()) { addr = i.hash().get_memory(key); } if (addr) { @@ -105,9 +105,11 @@ void nas_func::clear() { void nas_ghost::set( const std::string& ghost_type_name, destructor destructor_pointer, + marker gc_marker_pointer, void* ghost_pointer) { type_name = ghost_type_name; destructor_function = destructor_pointer; + gc_mark_function = gc_marker_pointer; pointer = ghost_pointer; } @@ -129,6 +131,7 @@ void nas_ghost::clear() { type_name = ""; pointer = nullptr; destructor_function = nullptr; + gc_mark_function = nullptr; } std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) { @@ -192,57 +195,60 @@ std::ostream& operator<<(std::ostream& out, nas_map& mp) { return out; } -nas_val::nas_val(u8 val_type) { +nas_val::nas_val(vm_type val_type) { mark = gc_status::collected; type = val_type; unmutable = 0; switch(val_type) { - case vm_str: ptr.str = new std::string; break; - case vm_vec: ptr.vec = new nas_vec; break; - case vm_hash: ptr.hash = new nas_hash; break; - case vm_func: ptr.func = new nas_func; break; - case vm_upval: ptr.upval = new nas_upval; break; - case vm_obj: ptr.obj = new nas_ghost; break; - case vm_co: ptr.co = new nas_co; break; - case vm_map: ptr.map = new nas_map; break; + case vm_type::vm_str: ptr.str = new std::string; break; + case vm_type::vm_vec: ptr.vec = new nas_vec; break; + case vm_type::vm_hash: ptr.hash = new nas_hash; break; + case vm_type::vm_func: ptr.func = new nas_func; break; + case vm_type::vm_upval: ptr.upval = new nas_upval; break; + case vm_type::vm_ghost: ptr.obj = new nas_ghost; break; + case vm_type::vm_co: ptr.co = new nas_co; break; + case vm_type::vm_map: ptr.map = new nas_map; break; + default: break; } } nas_val::~nas_val() { switch(type) { - case vm_str: delete ptr.str; break; - case vm_vec: delete ptr.vec; break; - case vm_hash: delete ptr.hash; break; - case vm_func: delete ptr.func; break; - case vm_upval:delete ptr.upval; break; - case vm_obj: delete ptr.obj; break; - case vm_co: delete ptr.co; break; - case vm_map: delete ptr.map; break; + case vm_type::vm_str: delete ptr.str; break; + case vm_type::vm_vec: delete ptr.vec; break; + case vm_type::vm_hash: delete ptr.hash; break; + case vm_type::vm_func: delete ptr.func; break; + case vm_type::vm_upval:delete ptr.upval; break; + case vm_type::vm_ghost:delete ptr.obj; break; + case vm_type::vm_co: delete ptr.co; break; + case vm_type::vm_map: delete ptr.map; break; + default: break; } - type=vm_nil; + type = vm_type::vm_nil; } void nas_val::clear() { switch(type) { - case vm_str: ptr.str->clear(); break; - case vm_vec: ptr.vec->elems.clear(); break; - case vm_hash: ptr.hash->elems.clear(); break; - case vm_func: ptr.func->clear(); break; - case vm_upval:ptr.upval->clear(); break; - case vm_obj: ptr.obj->clear(); break; - case vm_co: ptr.co->clear(); break; - case vm_map: ptr.map->clear(); break; + case vm_type::vm_str: ptr.str->clear(); break; + case vm_type::vm_vec: ptr.vec->elems.clear(); break; + case vm_type::vm_hash: ptr.hash->elems.clear(); break; + case vm_type::vm_func: ptr.func->clear(); break; + case vm_type::vm_upval:ptr.upval->clear(); break; + case vm_type::vm_ghost:ptr.obj->clear(); break; + case vm_type::vm_co: ptr.co->clear(); break; + case vm_type::vm_map: ptr.map->clear(); break; + default: break; } } f64 var::to_num() { - return type!=vm_str? val.num:str2num(str().c_str()); + return type!=vm_type::vm_str? val.num:str_to_num(str().c_str()); } std::string var::to_str() { - if (type==vm_str) { + if (type==vm_type::vm_str) { return str(); - } else if (type==vm_num) { + } else if (type==vm_type::vm_num) { std::string tmp = std::to_string(num()); tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos); tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos); @@ -253,42 +259,43 @@ std::string var::to_str() { std::ostream& operator<<(std::ostream& out, var& ref) { switch(ref.type) { - case vm_none: out << "undefined"; break; - case vm_nil: out << "nil"; break; - case vm_num: out << ref.val.num; break; - case vm_str: out << ref.str(); break; - case vm_vec: out << ref.vec(); break; - case vm_hash: out << ref.hash(); break; - case vm_func: out << "func(..) {..}"; break; - case vm_obj: out << ref.ghost(); break; - case vm_co: out << ref.co(); break; - case vm_map: out << ref.map(); break; + case vm_type::vm_none: out << "undefined"; break; + case vm_type::vm_nil: out << "nil"; break; + case vm_type::vm_num: out << ref.val.num; break; + case vm_type::vm_str: out << ref.str(); break; + case vm_type::vm_vec: out << ref.vec(); break; + case vm_type::vm_hash: out << ref.hash(); break; + case vm_type::vm_func: out << "func(..) {..}"; break; + case vm_type::vm_ghost:out << ref.ghost(); break; + case vm_type::vm_co: out << ref.co(); break; + case vm_type::vm_map: out << ref.map(); break; + default: break; } return out; } bool var::object_check(const std::string& name) { - return type==vm_obj && ghost().type_name==name && ghost().pointer; + return is_ghost() && ghost().type_name==name && ghost().pointer; } var var::none() { - return {vm_none, static_cast(0)}; + return {vm_type::vm_none, static_cast(0)}; } var var::nil() { - return {vm_nil, static_cast(0)}; + return {vm_type::vm_nil, static_cast(0)}; } var var::ret(u32 pc) { - return {vm_ret, pc}; + return {vm_type::vm_ret, pc}; } var var::cnt(i64 n) { - return {vm_cnt, n}; + return {vm_type::vm_cnt, n}; } var var::num(f64 n) { - return {vm_num, n}; + return {vm_type::vm_num, n}; } var var::gcobj(nas_val* p) { @@ -296,7 +303,7 @@ var var::gcobj(nas_val* p) { } var var::addr(var* p) { - return {vm_addr, p}; + return {vm_type::vm_addr, p}; } var* var::addr() { diff --git a/src/nasal_type.h b/src/nasal_type.h index 8bb589b3..a2179965 100644 --- a/src/nasal_type.h +++ b/src/nasal_type.h @@ -7,7 +7,7 @@ namespace nasal { -enum vm_type:u8 { +enum class vm_type: u8 { /* none-gc object */ vm_none = 0, // error type vm_cnt, // counter for forindex/foreach loop @@ -21,7 +21,7 @@ enum vm_type:u8 { vm_hash, // hashmap(dict) vm_func, // function(lambda) vm_upval, // upvalue - vm_obj, // ghost type + vm_ghost, // ghost type vm_co, // coroutine vm_map, // for globals and namespaces /* mark type range */ @@ -29,7 +29,9 @@ enum vm_type:u8 { }; // size of gc object type -const u32 gc_type_size = vm_type_size_max-vm_str; +const u32 gc_type_size = + static_cast(vm_type::vm_type_size_max) - + static_cast(vm_type::vm_str); // basic types struct nas_vec; // vector @@ -45,7 +47,7 @@ struct nas_val; // nas_val includes gc-managed types struct var { public: - u8 type = vm_none; + vm_type type = vm_type::vm_none; union { u32 ret; i64 cnt; @@ -55,11 +57,11 @@ struct var { } val; private: - var(u8 t, u32 pc) {type = t; val.ret = pc;} - var(u8 t, i64 ct) {type = t; val.cnt = ct;} - var(u8 t, f64 n) {type = t; val.num = n;} - var(u8 t, var* p) {type = t; val.addr = p;} - var(u8 t, nas_val* p) {type = t; val.gcobj = p;} + var(vm_type t, u32 pc) {type = t; val.ret = pc;} + var(vm_type t, i64 ct) {type = t; val.cnt = ct;} + var(vm_type t, f64 n) {type = t; val.num = n;} + var(vm_type t, var* p) {type = t; val.addr = p;} + var(vm_type t, nas_val* p) {type = t; val.gcobj = p;} public: var() = default; @@ -76,6 +78,7 @@ struct var { std::string to_str(); bool object_check(const std::string&); +public: // create new var object static var none(); static var nil(); @@ -85,6 +88,7 @@ struct var { static var gcobj(nas_val*); static var addr(var*); +public: // get value var* addr(); u32 ret() const; @@ -98,6 +102,22 @@ struct var { nas_ghost& ghost(); nas_co& co(); nas_map& map(); + +public: + bool is_none() const { return type==vm_type::vm_none; } + bool is_cnt() const { return type==vm_type::vm_cnt; } + bool is_addr() const { return type==vm_type::vm_addr; } + bool is_ret() const { return type==vm_type::vm_ret; } + bool is_nil() const { return type==vm_type::vm_nil; } + bool is_num() const { return type==vm_type::vm_num; } + bool is_str() const { return type==vm_type::vm_str; } + bool is_vec() const { return type==vm_type::vm_vec; } + bool is_hash() const { return type==vm_type::vm_hash; } + bool is_func() const { return type==vm_type::vm_func; } + bool is_upval() const { return type==vm_type::vm_upval; } + bool is_ghost() const { return type==vm_type::vm_ghost; } + bool is_coroutine() const { return type==vm_type::vm_co; } + bool is_map() const { return type==vm_type::vm_map; } }; struct nas_vec { @@ -166,21 +186,24 @@ struct nas_upval { struct nas_ghost { private: using destructor = void (*)(void*); + using marker = void (*)(void*, std::vector*); public: std::string type_name; destructor destructor_function; + marker gc_mark_function; void* pointer; public: nas_ghost(): - type_name(""), destructor_function(nullptr), pointer(nullptr) {} - ~nas_ghost() {clear();} - void set(const std::string&, destructor, void*); + type_name(""), destructor_function(nullptr), + gc_mark_function(nullptr), pointer(nullptr) {} + ~nas_ghost() { clear(); } + void set(const std::string&, destructor, marker, void*); void clear(); public: - const auto& get_ghost_name() const {return type_name;} + const auto& get_ghost_name() const { return type_name; } }; struct context { @@ -234,7 +257,7 @@ struct nas_val { }; gc_status mark; - u8 type; // value type + vm_type type; // value type u8 unmutable; // used to mark if a string is unmutable union { std::string* str; @@ -247,7 +270,7 @@ struct nas_val { nas_map* map; } ptr; - nas_val(u8); + nas_val(vm_type); ~nas_val(); void clear(); }; diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 5b2bf10f..0dfde286 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -31,14 +31,14 @@ void vm::init( ngc.init(strs, argv); /* init vm globals */ - auto map_instance = ngc.alloc(vm_map); + auto map_instance = ngc.alloc(vm_type::vm_map); global[global_symbol.at("globals")] = map_instance; for(const auto& i : global_symbol) { map_instance.map().mapper[i.first] = global+i.second; } /* init vm arg */ - auto arg_instance = ngc.alloc(vm_vec); + auto arg_instance = ngc.alloc(vm_type::vm_vec); global[global_symbol.at("arg")] = arg_instance; arg_instance.vec().elems = ngc.env_argv; } @@ -67,41 +67,42 @@ void vm::context_and_global_init() { void vm::value_info(var& val) { const auto p = reinterpret_cast(val.val.gcobj); switch(val.type) { - case vm_none: std::clog << "| null |"; break; - case vm_ret: std::clog << "| pc | 0x" << std::hex - << val.ret() << std::dec; break; - case vm_addr: std::clog << "| addr | 0x" << std::hex - << reinterpret_cast(val.addr()) - << std::dec; break; - case vm_cnt: std::clog << "| cnt | " << val.cnt(); break; - case vm_nil: std::clog << "| nil |"; break; - case vm_num: std::clog << "| num | " << val.num(); break; - case vm_str: std::clog << "| str | <0x" << std::hex << p - << "> " << rawstr(val.str(), 16) - << std::dec; break; - case vm_func: std::clog << "| func | <0x" << std::hex << p - << "> entry:0x" << val.func().entry - << std::dec; break; - case vm_upval:std::clog << "| upval| <0x" << std::hex << p - << std::dec << "> [" << val.upval().size - << " val]"; break; - case vm_vec: std::clog << "| vec | <0x" << std::hex << p - << std::dec << "> [" << val.vec().size() - << " val]"; break; - case vm_hash: std::clog << "| hash | <0x" << std::hex << p - << std::dec << "> {" << val.hash().size() - << " val}"; break; - case vm_obj: std::clog << "| obj | <0x" << std::hex << p - << "> obj:0x" - << reinterpret_cast(val.ghost().pointer) - << std::dec; break; - case vm_co: std::clog << "| co | <0x" << std::hex << p - << std::dec << "> coroutine"; break; - case vm_map: std::clog << "| nmspc| <0x" << std::hex << p - << std::dec << "> namespace [" - << val.map().mapper.size() << " val]"; break; - default: std::clog << "| err | <0x" << std::hex << p - << std::dec << "> unknown object"; break; + case vm_type::vm_none: std::clog << "| null |"; break; + case vm_type::vm_ret: std::clog << "| pc | 0x" << std::hex + << val.ret() << std::dec; break; + case vm_type::vm_addr: std::clog << "| addr | 0x" << std::hex + << reinterpret_cast(val.addr()) + << std::dec; break; + case vm_type::vm_cnt: std::clog << "| cnt | " << val.cnt(); break; + case vm_type::vm_nil: std::clog << "| nil |"; break; + case vm_type::vm_num: std::clog << "| num | " << val.num(); break; + case vm_type::vm_str: std::clog << "| str | <0x" << std::hex << p + << "> " << rawstr(val.str(), 16) + << std::dec; break; + case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p + << "> entry:0x" << val.func().entry + << std::dec; break; + case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p + << std::dec << "> [" << val.upval().size + << " val]"; break; + case vm_type::vm_vec: std::clog << "| vec | <0x" << std::hex << p + << std::dec << "> [" << val.vec().size() + << " val]"; break; + case vm_type::vm_hash: std::clog << "| hash | <0x" << std::hex << p + << std::dec << "> {" << val.hash().size() + << " val}"; break; + case vm_type::vm_ghost:std::clog << "| obj | <0x" << std::hex << p + << "> obj:0x" + << reinterpret_cast(val.ghost().pointer) + << std::dec; break; + case vm_type::vm_co: std::clog << "| co | <0x" << std::hex << p + << std::dec << "> coroutine"; break; + case vm_type::vm_map: std::clog << "| nmspc| <0x" << std::hex << p + << std::dec << "> namespace [" + << val.map().mapper.size() + << " val]"; break; + default: std::clog << "| err | <0x" << std::hex << p + << std::dec << "> unknown object"; break; } std::clog << "\n"; } @@ -128,7 +129,8 @@ void vm::function_detail_info(const nas_func& func) { std::clog << const_string[func.dynamic_parameter_index] << "..."; } std::clog << ") "; - std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; + const auto& code = bytecode[func.entry]; + std::clog << "{ entry: " << files[code.fidx] << ":" << code.line << " }"; } void vm::function_call_trace() { @@ -138,7 +140,7 @@ void vm::function_call_trace() { // generate trace back std::stack functions; for(var* i = bottom; i<=top; ++i) { - if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) { + if (i->is_func() && i-1>=bottom && (i-1)->is_ret()) { functions.push(&i->func()); } } @@ -176,7 +178,7 @@ void vm::trace_back() { // generate trace back std::stack ret; for(var* i = ctx.stack; i<=ctx.top; ++i) { - if (i->type==vm_ret && i->ret()!=0) { + if (i->is_ret() && i->ret()!=0) { ret.push(i->ret()); } } @@ -236,7 +238,7 @@ void vm::register_info() { } void vm::global_state() { - if (!global_size || global[0].type==vm_none) { + if (!global_size || global[0].is_none()) { return; } std::clog << "\nglobal (0x" << std::hex @@ -266,7 +268,7 @@ void vm::local_state() { } void vm::upvalue_state() { - if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) { + if (ctx.funcr.is_nil() || ctx.funcr.func().upval.empty()) { return; } std::clog << "\nupvalue\n"; @@ -312,7 +314,8 @@ std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const { } result += ") "; std::stringstream out; - out << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; + const auto& code = bytecode[func.entry]; + out << "{ entry: " << files[code.fidx] << ":" << code.line << " }"; out << " @ 0x" << std::hex << reinterpret_cast(&func) << std::dec; return result + out.str(); } @@ -326,7 +329,7 @@ std::string vm::report_special_call_lack_arguments( argument_list[i.second-1] = i.first; } for(const auto& key : argument_list) { - if (local[func.keys.at(key)].type==vm_none) { + if (local[func.keys.at(key)].is_none()) { result += key + ", "; } else { result += key + "[get], "; @@ -335,7 +338,8 @@ std::string vm::report_special_call_lack_arguments( result = result.substr(0, result.length()-2); result += ") "; std::stringstream out; - out << "{entry: 0x" << std::hex << func.entry << std::dec << "}"; + const auto& code = bytecode[func.entry]; + out << "{ entry: " << files[code.fidx] << ":" << code.line << " }"; out << " @ 0x" << std::hex << reinterpret_cast(&func) << std::dec; return result + out.str(); } @@ -366,20 +370,21 @@ std::string vm::report_out_of_range(f64 index, usize real_size) const { std::string vm::type_name_string(const var& value) const { switch(value.type) { - case vm_none: return "none"; - case vm_cnt: return "counter"; - case vm_addr: return "address"; - case vm_ret: return "program counter"; - case vm_nil: return "nil"; - case vm_num: return "number"; - case vm_str: return "string"; - case vm_vec: return "vector"; - case vm_hash: return "hash"; - case vm_func: return "function"; - case vm_upval: return "upvalue"; - case vm_obj: return "ghost type"; - case vm_co: return "coroutine"; - case vm_map: return "namespace"; + case vm_type::vm_none: return "none"; + case vm_type::vm_cnt: return "counter"; + case vm_type::vm_addr: return "address"; + case vm_type::vm_ret: return "program counter"; + case vm_type::vm_nil: return "nil"; + case vm_type::vm_num: return "number"; + case vm_type::vm_str: return "string"; + case vm_type::vm_vec: return "vector"; + case vm_type::vm_hash: return "hash"; + case vm_type::vm_func: return "function"; + case vm_type::vm_upval: return "upvalue"; + case vm_type::vm_ghost: return "ghost type"; + case vm_type::vm_co: return "coroutine"; + case vm_type::vm_map: return "namespace"; + default: break; } return "unknown"; } diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 45ca5a35..86538185 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -195,10 +195,10 @@ class vm { }; inline bool vm::cond(var& val) { - if (val.type==vm_num) { + if (val.is_num()) { return val.num(); - } else if (val.type==vm_str) { - const f64 num = str2num(val.str().c_str()); + } else if (val.is_str()) { + const f64 num = str_to_num(val.str().c_str()); return std::isnan(num)? !val.str().empty():num; } return false; @@ -242,7 +242,7 @@ inline void vm::o_pstr() { } inline void vm::o_newv() { - var newv = ngc.alloc(vm_vec); + var newv = ngc.alloc(vm_type::vm_vec); auto& vec = newv.vec().elems; vec.resize(imm[ctx.pc]); // use top-=imm[pc]-1 here will cause error if imm[pc] is 0 @@ -254,11 +254,11 @@ inline void vm::o_newv() { } inline void vm::o_newh() { - (++ctx.top)[0] = ngc.alloc(vm_hash); + (++ctx.top)[0] = ngc.alloc(vm_type::vm_hash); } inline void vm::o_newf() { - (++ctx.top)[0] = ngc.alloc(vm_func); + (++ctx.top)[0] = ngc.alloc(vm_type::vm_func); auto& func = ctx.top[0].func(); func.entry = imm[ctx.pc]; func.parameter_size = 1; @@ -268,7 +268,9 @@ inline void vm::o_newf() { func.upval = ctx.funcr.func().upval; // function created in the same local scope shares one closure // so this size & stk setting has no problem - var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr; + var upval = (ctx.upvalr.is_nil())? + ngc.alloc(vm_type::vm_upval): + ctx.upvalr; upval.upval().size = ctx.funcr.func().local_size; upval.upval().stack_frame_offset = ctx.localr; func.upval.push_back(upval); @@ -303,10 +305,10 @@ inline void vm::o_dyn() { inline void vm::o_lnot() { var val = ctx.top[0]; switch(val.type) { - case vm_nil: ctx.top[0] = one; break; - case vm_num: ctx.top[0] = val.num()? zero:one; break; - case vm_str: { - const f64 num = str2num(val.str().c_str()); + case vm_type::vm_nil: ctx.top[0] = one; break; + case vm_type::vm_num: ctx.top[0] = val.num()? zero:one; break; + case vm_type::vm_str: { + const f64 num = str_to_num(val.str().c_str()); if (std::isnan(num)) { ctx.top[0] = var::num(static_cast(val.str().empty())); } else { @@ -361,8 +363,8 @@ inline void vm::o_mul() {op_calc(*);} inline void vm::o_div() {op_calc(/);} inline void vm::o_lnk() { // concat two vectors into one - if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) { - ngc.temp = ngc.alloc(vm_vec); + if (ctx.top[-1].is_vec() && ctx.top[0].is_vec()) { + ngc.temp = ngc.alloc(vm_type::vm_vec); for(auto i : ctx.top[-1].vec().elems) { ngc.temp.vec().elems.push_back(i); } @@ -496,12 +498,12 @@ inline void vm::o_meq() { inline void vm::o_eq() { var val2 = ctx.top[0]; var val1 = (--ctx.top)[0]; - if (val1.type==vm_nil && val2.type==vm_nil) { + if (val1.is_nil() && val2.is_nil()) { ctx.top[0] = one; - } else if (val1.type==vm_str && val2.type==vm_str) { + } else if (val1.is_str() && val2.is_str()) { ctx.top[0] = (val1.str()==val2.str())? one:zero; - } else if ((val1.type==vm_num || val2.type==vm_num) - && val1.type!=vm_nil && val2.type!=vm_nil) { + } else if ((val1.is_num() || val2.is_num()) + && !val1.is_nil() && !val2.is_nil()) { ctx.top[0] = (val1.to_num()==val2.to_num())? one:zero; } else { ctx.top[0] = (val1==val2)? one:zero; @@ -511,12 +513,12 @@ inline void vm::o_eq() { inline void vm::o_neq() { var val2 = ctx.top[0]; var val1 = (--ctx.top)[0]; - if (val1.type==vm_nil && val2.type==vm_nil) { + if (val1.is_nil() && val2.is_nil()) { ctx.top[0] = zero; - } else if (val1.type==vm_str && val2.type==vm_str) { + } else if (val1.is_str() && val2.is_str()) { ctx.top[0] = (val1.str()!=val2.str())? one:zero; - } else if ((val1.type==vm_num || val2.type==vm_num) - && val1.type!=vm_nil && val2.type!=vm_nil) { + } else if ((val1.is_num() || val2.is_num()) + && !val1.is_nil() && !val2.is_nil()) { ctx.top[0] = (val1.to_num()!=val2.to_num())? one:zero; } else { ctx.top[0] = (val1!=val2)? one:zero; @@ -565,7 +567,7 @@ inline void vm::o_jf() { } inline void vm::o_cnt() { - if (ctx.top[0].type!=vm_vec) { + if (!ctx.top[0].is_vec()) { die("must use vector in forindex/foreach but get "+ type_name_string(ctx.top[0]) ); @@ -611,25 +613,25 @@ inline void vm::o_upval() { inline void vm::o_callv() { var val = ctx.top[0]; var vec = (--ctx.top)[0]; - if (vec.type==vm_vec) { + if (vec.is_vec()) { ctx.top[0] = vec.vec().get_value(val.to_num()); - if (ctx.top[0].type==vm_none) { + if (ctx.top[0].is_none()) { die(report_out_of_range(val.to_num(), vec.vec().size())); return; } - } else if (vec.type==vm_hash) { - if (val.type!=vm_str) { + } else if (vec.is_hash()) { + if (!val.is_str()) { die("must use string as the key but get "+type_name_string(val)); return; } ctx.top[0] = vec.hash().get_value(val.str()); - if (ctx.top[0].type==vm_none) { + if (ctx.top[0].is_none()) { die(report_key_not_found(val.str(), vec.hash())); return; - } else if (ctx.top[0].type==vm_func) { + } else if (ctx.top[0].is_func()) { ctx.top[0].func().local[0] = val; // 'me' } - } else if (vec.type==vm_str) { + } else if (vec.is_str()) { const auto& str = vec.str(); i32 num = val.to_num(); i32 len = str.length(); @@ -640,13 +642,13 @@ inline void vm::o_callv() { ctx.top[0] = var::num( static_cast(static_cast(str[num>=0? num:num+len])) ); - } else if (vec.type==vm_map) { - if (val.type!=vm_str) { + } else if (vec.is_map()) { + if (!val.is_str()) { die("must use string as the key but get "+type_name_string(val)); return; } ctx.top[0] = vec.map().get_value(val.str()); - if (ctx.top[0].type==vm_none) { + if (ctx.top[0].is_none()) { die("cannot find symbol \""+val.str()+"\""); return; } @@ -658,13 +660,13 @@ inline void vm::o_callv() { inline void vm::o_callvi() { var val = ctx.top[0]; - if (val.type!=vm_vec) { + if (!val.is_vec()) { die("must use a vector but get "+type_name_string(val)); return; } // cannot use operator[],because this may cause overflow (++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]); - if (ctx.top[0].type==vm_none) { + if (ctx.top[0].is_none()) { die(report_out_of_range(imm[ctx.pc], val.vec().size())); return; } @@ -672,22 +674,22 @@ inline void vm::o_callvi() { inline void vm::o_callh() { var val = ctx.top[0]; - if (val.type!=vm_hash && val.type!=vm_map) { + if (!val.is_hash() && !val.is_map()) { die("must call a hash but get "+type_name_string(val)); return; } const auto& str = const_string[imm[ctx.pc]]; - if (val.type==vm_hash) { + if (val.is_hash()) { ctx.top[0] = val.hash().get_value(str); } else { ctx.top[0] = val.map().get_value(str); } - if (ctx.top[0].type==vm_none) { - val.type==vm_hash? + if (ctx.top[0].is_none()) { + val.is_hash()? die(report_key_not_found(str, val.hash())): die("cannot find symbol \"" + str + "\""); return; - } else if (ctx.top[0].type==vm_func) { + } else if (ctx.top[0].is_func()) { ctx.top[0].func().local[0] = val; // 'me' } } @@ -695,7 +697,7 @@ inline void vm::o_callh() { inline void vm::o_callfv() { const u32 argc = imm[ctx.pc]; // arguments counter var* local = ctx.top-argc+1; // arguments begin address - if (local[-1].type!=vm_func) { + if (!local[-1].is_func()) { die("must call a function but get "+type_name_string(local[-1])); return; } @@ -713,7 +715,7 @@ inline void vm::o_callfv() { } // parameter size is func->psize-1, 1 is reserved for "me" const u32 parameter_size = func.parameter_size-1; - if (argc=0) { // load dynamic argument - dynamic = ngc.alloc(vm_vec); + dynamic = ngc.alloc(vm_type::vm_vec); for(u32 i = parameter_size; ialloc(vm_vec); + var res = ngc->alloc(vm_type::vm_vec); if (pipe(fd)==-1) { return nas_err("unix::pipe", "failed to create pipe"); } @@ -28,7 +28,7 @@ var builtin_pipe(context* ctx, gc* ngc) { var builtin_fork(context* ctx, gc* ngc) { #ifndef _WIN32 - f64 res=fork(); + f64 res = fork(); if (res<0) { return nas_err("unix::fork", "failed to fork a process"); } @@ -40,13 +40,13 @@ var builtin_fork(context* ctx, gc* ngc) { var builtin_waitpid(context* ctx, gc* ngc) { auto pid = ctx->localr[1]; auto nohang = ctx->localr[2]; - if (pid.type!=vm_num || nohang.type!=vm_num) { + if (!pid.is_num() || !nohang.is_num()) { return nas_err("unix::waitpid", "pid and nohang must be number"); } #ifndef _WIN32 i32 ret_pid, status; ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG); - var vec = ngc->alloc(vm_vec); + var vec = ngc->alloc(vm_type::vm_vec); vec.vec().elems.push_back(var::num(static_cast(ret_pid))); vec.vec().elems.push_back(var::num(static_cast(status))); return vec; @@ -56,7 +56,7 @@ var builtin_waitpid(context* ctx, gc* ngc) { var builtin_opendir(context* ctx, gc* ngc) { auto path = ctx->localr[1]; - if (path.type!=vm_str) { + if (!path.is_str()) { return nas_err("unix::opendir", "\"path\" must be string"); } #ifdef _MSC_VER @@ -72,8 +72,8 @@ var builtin_opendir(context* ctx, gc* ngc) { return nas_err("unix::opendir", "cannot open dir <"+path.str()+">"); } #endif - var ret = ngc->alloc(vm_obj); - ret.ghost().set(dir_type_name, dir_entry_destructor, p); + var ret = ngc->alloc(vm_type::vm_ghost); + ret.ghost().set(dir_type_name, dir_entry_destructor, nullptr, p); return ret; } @@ -105,14 +105,14 @@ var builtin_closedir(context* ctx, gc* ngc) { var builtin_chdir(context* ctx, gc* ngc) { auto path = ctx->localr[1]; - if (path.type!=vm_str) { + if (!path.is_str()) { return var::num(-1.0); } return var::num(static_cast(chdir(path.str().c_str()))); } var builtin_environ(context* ctx, gc* ngc) { - var res = ngc->temp = ngc->alloc(vm_vec); + var res = ngc->temp = ngc->alloc(vm_type::vm_vec); auto& vec = res.vec().elems; for(char** env = environ; *env; ++env) { vec.push_back(ngc->newstr(*env)); @@ -131,7 +131,7 @@ var builtin_getcwd(context* ctx, gc* ngc) { var builtin_getenv(context* ctx, gc* ngc) { auto envvar = ctx->localr[1]; - if (envvar.type!=vm_str) { + if (!envvar.is_str()) { return nas_err("unix::getenv", "\"envvar\" must be string"); } char* res = getenv(envvar.str().c_str()); diff --git a/std/argparse.nas b/std/argparse.nas new file mode 100644 index 00000000..a183329a --- /dev/null +++ b/std/argparse.nas @@ -0,0 +1,172 @@ +# argparse.nas +# 2023/12/7 by ValKmjolnir + +use std.padding; + +var new = func(description) { + var _arg = globals.arg; + var parser = { + description: description, + subparser: [], + command_list: [], + add_command: func(long, short, help, need_arg = false, need_nargs = false) { + return _add_command(parser, long, short, help, need_arg, need_nargs); + }, + add_subparser: func(name, help) { + return _add_subparser(parser, name, help); + }, + parse_args: func() { + var result_hash = {}; + _parse(parser, _arg, result_hash); + return result_hash; + } + }; + parser.add_command("--help", "-h", "Get help info and exit"); + return parser; +} + +var _new_sub_parser = func(description) { + var parser = { + description: description, + subparser: [], + command_list: [], + add_command: func(long, short, help, need_arg = false, need_nargs = false) { + return _add_command(parser, long, short, help, need_arg, need_nargs); + }, + add_subparser: func(name, help) { + return _add_subparser(parser, name, help); + } + }; + parser.add_command("--help", "-h", "Get help info and exit"); + return parser; +} + +var _help = func(parser) { + println(parser.description, "\n"); + if (size(parser.subparser)>0) { + println("Subcommand:"); + var max_pad_length = 0; + var info_pairs = []; + foreach(var cmd; parser.subparser) { + var info = " "~cmd.name; + append(info_pairs, {info: info, help: cmd.parser.description}); + info_length = size(info); + max_pad_length = max_pad_length>info_length? max_pad_length:info_length; + } + foreach(var pair; info_pairs) { + println(padding.rightpad(pair.info, max_pad_length), " ", pair.help); + } + println(); + } + if (size(parser.command_list)>0) { + println("Options:"); + var max_pad_length = 0; + var info_pairs = []; + foreach(var cmd; parser.command_list) { + if (cmd.need_nargs) { + var info = " "~cmd.full_name~" [args...] "~cmd.short_name~" [args...]"; + append(info_pairs, {info: info, help: cmd.help}); + } elsif (cmd.need_arg) { + var info = " "~cmd.full_name~" arg "~cmd.short_name~" arg"; + append(info_pairs, {info: info, help: cmd.help}); + } else { + var info = " "~cmd.full_name~" "~cmd.short_name; + append(info_pairs, {info: info, help: cmd.help}); + } + var info_length = size(info); + max_pad_length = max_pad_length>info_length? max_pad_length:info_length; + } + foreach(var pair; info_pairs) { + println(padding.rightpad(pair.info, max_pad_length), " ", pair.help); + } + } +} + +var _in_list = func(arginfo, command_list) { + foreach(var cmd; command_list) { + if (arginfo==cmd.full_name or arginfo==cmd.short_name) { + return true; + } + } + return false; +} + +var _parse = func(parser, args, result_hash) { + if (size(args)==0) { + println("Require more command, use \"--help\" to get help.\n"); + _help(parser); + exit(0); + } + foreach(var subparser; parser.subparser) { + if (subparser.name==args[0]) { + result_hash[subparser.name] = true; + _parse(subparser.parser, size(args)>1? args[1:]:[], result_hash); + return; + } + } + for(var i = 0; i1 and in_vec(tmp[-1])) { append(res, f); @@ -74,14 +75,55 @@ var recursive_find_files = func(path) { }; while(var n = unix.readdir(dd)) { if (unix.isfile(path~"/"~n)) { - append(res.files,n); + append(res.files, n); } elsif (unix.isdir(path~"/"~n) and n!="." and n!="..") { var tmp = recursive_find_files(path~"/"~n); if (tmp!=nil) { - append(res.files,tmp); + append(res.files, tmp); } } } unix.closedir(dd); return res; +} + +var recursive_find_files_flat = func(path) { + var tree_files = recursive_find_files(path); + if (tree_files==nil) { + return []; + } + var flat = []; + var bfs = [tree_files]; + while(size(bfs)!=0) { + var first = pop(bfs); + foreach(var file_record; first.files) { + if (ishash(file_record)) { + append(bfs, file_record); + continue; + } + append(flat, first.dir~"/"~file_record); + } + } + return flat; +} + +var recursive_find_files_with_extension = func(path, extensions...) { + var in_vec = func(ext) { + foreach(var i; extensions) { + if (ext==i) { + return 1; + } + } + return 0; + } + + var files = recursive_find_files_flat(path); + var res = []; + foreach(var filename; files) { + var tmp = split('.', filename); + if (size(tmp)>1 and in_vec(tmp[-1])) { + append(res, filename); + } + } + return res; } \ No newline at end of file diff --git a/std/io.nas b/std/io.nas index d2a950a8..bbc9e745 100644 --- a/std/io.nas +++ b/std/io.nas @@ -72,3 +72,21 @@ var stdin = func() { return __stdin; }(); var stdout = func() { return __stdout;}(); var stderr = func() { return __stderr; }(); + +# get file status. using data from stat +var fstat = func(filename) { + var s = stat(filename); + return { + st_dev: s[0], + st_ino: s[1], + st_mode: s[2], + st_nlink: s[3], + st_uid: s[4], + st_gid: s[5], + st_rdev: s[6], + st_size: s[7], + st_atime: s[8], + st_mtime: s[9], + st_ctime: s[10] + }; +} diff --git a/std/json.nas b/std/json.nas index 8aab1859..27a443f0 100644 --- a/std/json.nas +++ b/std/json.nas @@ -1,329 +1,30 @@ # lib json.nas # 2021 ValKmjolnir -var ( - _j_eof, - _j_lbrace, - _j_rbrace, - _j_lbrkt, - _j_rbrkt, - _j_comma, - _j_colon, - _j_str, - _j_num, - _j_id, - _j_bool -) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - -var _j_content = [ - "eof", - "`{`", - "`}`", - "`[`", - "`]`", - "`,`", - "`:`", - "string", - "number", - "identifier", - "boolean" -]; - -var _parse_error = 0; - -var parse = func() { - var text = ""; - var line = 1; - var text_size = 0; - var ptr = 0; - var token = { - content: "", - type: "" - }; - - var init = func() { - text = ""; - line = 1; - text_size = 0; - ptr = 0; - token = { - content: "", - type: "" - }; - } - - var isnum = func(c) { - return '0'<=c and c<='9'; - } - - var isid = func(c) { - var tmp = c[0]; - return ('a'[0]<=tmp and tmp<='z'[0]) or - ('A'[0]<=tmp and tmp<='Z'[0]) or - c=='_'; - } - - var check = func() { - var c = char(text[ptr]); - return ( - c=='{' or c=='}' or - c=='[' or c==']' or - c==',' or c==':' or - c=='\"' or c=='\'' or - isnum(c) or isid(c) - ); - } - - var get = func(str) { - init(); - if (!size(str)) { - println("json::parse: empty string"); - _parse_error += 1; - str = "[]"; - } - text = str; - text_size = size(text); - return; - } - - var next = func() { - while(ptr=text_size) { - token.content = "eof"; - token.type = _j_eof; - return; - } - - var c = char(text[ptr]); - if (c=='{') { - token.content = '{'; - token.type = _j_lbrace; - } elsif (c=='}') { - token.content = '}'; - token.type = _j_rbrace; - } elsif (c=='[') { - token.content = '['; - token.type = _j_lbrkt; - } elsif (c==']') { - token.content = ']'; - token.type = _j_rbrkt; - } elsif (c==',') { - token.content = ','; - token.type = _j_comma; - } elsif (c==':') { - token.content = ':'; - token.type = _j_colon; - } elsif (c=='\"' or c=='\'') { - var strbegin = c; - var s = ""; - ptr += 1; - while(ptr0) { + println("encounter error when parsing \"", path, "\":\n", json.get_error()); logprint(LOG_DEBUG, _raw_str(data)); return {path: path}; } diff --git a/std/process_bar.nas b/std/process_bar.nas index cac9f079..6bf76aa5 100644 --- a/std/process_bar.nas +++ b/std/process_bar.nas @@ -1,6 +1,8 @@ # process_bar.nas # ValKmjolnir 2022/6/14 # this file is inspired by a Python lib: alive_progress +use std.os; +use std.unix; var bar = func() { var bar = { @@ -240,96 +242,96 @@ var default_bar = func(name = "classic", length = 20) { if (typeof(name)!="str") name="classic"; if (name=="classic") - return process_bar.bar("sharp","point","bracket",length); + return bar("sharp","point","bracket",length); elsif (name=="classic2") - return process_bar.bar("equal","point","bracket",length); + return bar("equal","point","bracket",length); elsif (name=="classic3") - return process_bar.bar("sharp","point","line",length); + return bar("sharp","point","line",length); elsif (name=="classic4") - return process_bar.bar("equal","point","line",length); + return bar("equal","point","line",length); elsif (name=="triangle") - return process_bar.bar("solid_triangle_right","hollow_triangle_right","angle_bracket",length); + return bar("solid_triangle_right","hollow_triangle_right","angle_bracket",length); elsif (name=="dots") - return process_bar.bar("solid_circle","hollow_circle","curve",length); + return bar("solid_circle","hollow_circle","curve",length); elsif (name=="ticks") - return process_bar.bar("tick","space","line",length); + return bar("tick","space","line",length); elsif (name=="deep_shadow") - return process_bar.bar("deep_shadow","light_shadow","line",length); + return bar("deep_shadow","light_shadow","line",length); elsif (name=="block") - return process_bar.bar("block","light_shadow","line",length); + return bar("block","light_shadow","line",length); elsif (name=="oneline") - return process_bar.bar("line","space","space",length); + return bar("line","space","space",length); else - return process_bar.bar("sharp","point","bracket",length); + return bar("sharp","point","bracket",length); } var default_spinner = func(name = "classic", repeat = 1) { if (typeof(name)!="str") name="classic"; if (name=="rise") - return process_bar.spinner("rise",repeat); + return spinner("rise",repeat); elsif (name=="vertical") - return process_bar.spinner("vertical",repeat); + return spinner("vertical",repeat); elsif (name=="dot") - return process_bar.spinner("dot",repeat); + return spinner("dot",repeat); elsif (name=="dots") - return process_bar.spinner("dots",repeat); + return spinner("dots",repeat); elsif (name=="arrow") - return process_bar.spinner("arrow",repeat); + return spinner("arrow",repeat); elsif (name=="classic") - return process_bar.spinner("classic",repeat); + return spinner("classic",repeat); elsif (name=="balls") - return process_bar.spinner("balls",repeat); + return spinner("balls",repeat); elsif (name=="dots_wave") - return process_bar.spinner("dots_wave",repeat); + return spinner("dots_wave",repeat); elsif (name=="pulse") - return process_bar.spinner("pulse",repeat); + return spinner("pulse",repeat); elsif (name=="wave") - return process_bar.spinner("wave",repeat); + return spinner("wave",repeat); elsif (name=="short_wave") - return process_bar.spinner("short_wave",repeat); + return spinner("short_wave",repeat); elsif (name=="fish") - return process_bar.spinner("fish",repeat); + return spinner("fish",repeat); elsif (name=="happy") - return process_bar.spinner("happy",repeat); + return spinner("happy",repeat); elsif (name=="wait") - return process_bar.spinner("wait",repeat); + return spinner("wait",repeat); elsif (name=="stars") - return process_bar.spinner("stars",repeat); + return spinner("stars",repeat); else - return process_bar.spinner("classic",repeat); + return spinner("classic",repeat); } var show = func() { print("\ec"); var bars={ - "classic ":process_bar.default_bar("classic",40), - "classic2 ":process_bar.default_bar("classic2",40), - "classic3 ":process_bar.default_bar("classic3",40), - "classic4 ":process_bar.default_bar("classic4",40), - "triangle ":process_bar.default_bar("triangle",40), - "dots ":process_bar.default_bar("dots",40), - "ticks ":process_bar.default_bar("ticks",40), - "deep_shadow":process_bar.default_bar("deep_shadow",40), - "block ":process_bar.default_bar("block",40), - "oneline ":process_bar.default_bar("oneline",40) + "classic ":default_bar("classic",40), + "classic2 ":default_bar("classic2",40), + "classic3 ":default_bar("classic3",40), + "classic4 ":default_bar("classic4",40), + "triangle ":default_bar("triangle",40), + "dots ":default_bar("dots",40), + "ticks ":default_bar("ticks",40), + "deep_shadow":default_bar("deep_shadow",40), + "block ":default_bar("block",40), + "oneline ":default_bar("oneline",40) }; var spinners={ - "rise ":process_bar.default_spinner("rise",16), - "vertical ":process_bar.default_spinner("vertical",16), - "dot ":process_bar.default_spinner("dot",16), - "dots ":process_bar.default_spinner("dots",16), - "arrow ":process_bar.default_spinner("arrow",16), - "classic ":process_bar.default_spinner("classic",16), - "balls ":process_bar.default_spinner("balls",4), - "dots_wave ":process_bar.default_spinner("dots_wave",2), - "pulse ":process_bar.default_spinner("pulse",1), - "wave ":process_bar.default_spinner("wave",2), - "short_wave ":process_bar.default_spinner("short_wave",4), - "fish ":process_bar.default_spinner("fish",1), - "happy ":process_bar.default_spinner("happy",1), - "wait ":process_bar.default_spinner("wait",1), - "stars ":process_bar.default_spinner("stars",1) + "rise ":default_spinner("rise",16), + "vertical ":default_spinner("vertical",16), + "dot ":default_spinner("dot",16), + "dots ":default_spinner("dots",16), + "arrow ":default_spinner("arrow",16), + "classic ":default_spinner("classic",16), + "balls ":default_spinner("balls",4), + "dots_wave ":default_spinner("dots_wave",2), + "pulse ":default_spinner("pulse",1), + "wave ":default_spinner("wave",2), + "short_wave ":default_spinner("short_wave",4), + "fish ":default_spinner("fish",1), + "happy ":default_spinner("happy",1), + "wait ":default_spinner("wait",1), + "stars ":default_spinner("stars",1) }; var bar_key=keys(bars); var spin_key=keys(spinners); diff --git a/std/props.nas b/std/props.nas index ffb693f1..202aa154 100644 --- a/std/props.nas +++ b/std/props.nas @@ -9,6 +9,8 @@ # local node, there is no equivalent of the "relative path" variants # available in C++; just use node.getNode(path).whatever() instead. # +use std.math; +use std.string; var _new = func { return { diff --git a/std/string.nas b/std/string.nas index bed819ff..10aac2c7 100644 --- a/std/string.nas +++ b/std/string.nas @@ -9,4 +9,33 @@ var join = func(sep, vec) { res ~= (i==len-1? "":sep); } return res; -} \ No newline at end of file +} + +var __temp_char = func(number) { + return __char(number); +} + +var __temp_append = func(vec, elem...) { + return __append(vec, elem); +} + +var __temp_contains = func(hash, key) { + return __contains(hash, key); +} + +var __num_to_char = []; +var __char_to_num = {}; +func() { + for(var i = 0; i<256; i += 1) { + __temp_append(__num_to_char, __temp_char(i)); + __char_to_num[__temp_char(i)] = i; + } +}(); + +var to_char = func(number) { + return 0<=number and number<256? __num_to_char[number]:""; +} + +var to_num = func(character) { + return __temp_contains(__char_to_num, character)? __char_to_num[character]:-1; +} diff --git a/std/udp.nas b/std/udp.nas index 6f6210b3..0f4efbf5 100644 --- a/std/udp.nas +++ b/std/udp.nas @@ -1,4 +1,6 @@ use module.libsock; +use std.os; +use std.unix; var udp_server = func(hostname, port, retry_delay = 5) { var socket = libsock.socket; diff --git a/std/unix.nas b/std/unix.nas index 88f02825..a966c09e 100644 --- a/std/unix.nas +++ b/std/unix.nas @@ -1,6 +1,8 @@ # unix.nas # 2023 by ValKmjolnir use std.bits; +use std.os; +use std.io; var _S_IFDIR = 0x4000; var _S_IFREG = 0x8000; @@ -70,5 +72,5 @@ var getenv = func(envvar) { } var getpath = func() { - return split(os.platform()=="windows"? ";":":", unix.getenv("PATH")); + return split(os.platform()=="windows"? ";":":", getenv("PATH")); } diff --git a/std/utils.nas b/std/utils.nas index 0d2bf41e..e309483e 100644 --- a/std/utils.nas +++ b/std/utils.nas @@ -1,6 +1,8 @@ # utils.nas # 2023 by ValKmjolnir +use std.math; +# when count can be divided exactly by times, return true var times_trigger = func(times, count) { return math.mod(times, count)==0; } \ No newline at end of file diff --git a/test/argparse_test.nas b/test/argparse_test.nas new file mode 100644 index 00000000..afa6bec4 --- /dev/null +++ b/test/argparse_test.nas @@ -0,0 +1,39 @@ +use std.argparse; + +var test_cli = func(args) { + globals.arg = args; + var args = argparse.new("Nasal ArgParse Test CLI"); + args.add_command(long: "--what", short: "-wa", help: "What-command"); + args.add_command(long: "--who", short: "-wo", help: "Who-command"); + args.add_command(long: "--where", short: "-we", help: "Where-command"); + args.add_command(long: "--test_arg", short: "-ta", help: "Test-single-arg", need_arg: true); + args.add_command(long: "--test_args", short: "-tas", help: "Test-multiple-args", need_nargs: true); + var subparser0 = args.add_subparser(name: "subcommand0", help: "Nasal ArgParse Test CLI Sub-Command 0"); + subparser0.add_command(long: "--sub0-what", short: "-s0w", help: "Sub0-what-command"); + subparser0.add_command(long: "--test_arg", short: "-ta", help: "Test-single-arg", need_arg: true); + subparser0.add_command(long: "--test_args", short: "-tas", help: "Test-multiple-args", need_nargs: true); + var subparser1 = args.add_subparser(name: "subcommand1", help: "Nasal ArgParse Test CLI Sub-Command 1"); + subparser1.add_command(long: "--sub1-what", short: "-s1w", help: "Sub1-what-command"); + subparser1.add_command(long: "--test_arg", short: "-ta", help: "Test-single-arg", need_arg: true); + subparser1.add_command(long: "--test_args", short: "-tas", help: "Test-multiple-args", need_nargs: true); + var subparser_test = args.add_subparser(name: "subtest", help: "Test Subcommand"); + var result = args.parse_args(); + println(result); +} + +# should cause error or exit +# test_cli(["--help"]); +# test_cli(["subtest"]); +# test_cli(["--test_args", "--what", "--who", "--where"]); +# test_cli(["--test_arg", "a", "b", "--what", "--who", "--where"]); + +# should be correct +test_cli(["--test_arg", "a", "--what", "--who", "--where"]); +test_cli(["--test_args", "a", "--what", "--who", "--where"]); +test_cli(["--test_args", "a", "b", "c", "--what", "--who", "--where"]); +test_cli(["subcommand0", "--test_arg", "a", "-s0w"]); +test_cli(["subcommand0", "--test_args", "a", "-s0w"]); +test_cli(["subcommand0", "--test_args", "a", "b", "c", "-s0w"]); +test_cli(["subcommand1", "--test_arg", "a", "-s1w"]); +test_cli(["subcommand1", "--test_args", "a", "-s1w"]); +test_cli(["subcommand1", "--test_args", "a", "b", "c", "-s1w"]); diff --git a/test/ascii-art.nas b/test/ascii-art.nas index 95c53eaa..1ea8bdff 100644 --- a/test/ascii-art.nas +++ b/test/ascii-art.nas @@ -1,5 +1,7 @@ use std.padding; use std.process_bar; +use std.os; +use std.unix; var char_ttf=[ [" "," "," "," "," "," "], diff --git a/test/auto_crash.nas b/test/auto_crash.nas index a18c4af2..db925d99 100644 --- a/test/auto_crash.nas +++ b/test/auto_crash.nas @@ -1,5 +1,6 @@ # Road check and auto pilot by ValKmjolnir use std.fg_env; +use std.math; var props = fg_env.props; var geodinfo = fg_env.geodinfo; diff --git a/test/bf.nas b/test/bf.nas index 546446c2..7c4c4bf5 100644 --- a/test/bf.nas +++ b/test/bf.nas @@ -1,3 +1,5 @@ +use std.os; + var mandelbrot= "[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman] +++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[ diff --git a/test/bfconvertor.nas b/test/bfconvertor.nas index 074c8441..388a4f3c 100644 --- a/test/bfconvertor.nas +++ b/test/bfconvertor.nas @@ -1,3 +1,5 @@ +use std.io; + var mandelbrot= "[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman] +++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[ diff --git a/test/bfs.nas b/test/bfs.nas index 3775981f..294f292e 100644 --- a/test/bfs.nas +++ b/test/bfs.nas @@ -1,4 +1,6 @@ use std.queue; +use std.os; +use std.unix; rand(time(0)); var pixel=[' ','#','.','*']; diff --git a/test/bp.nas b/test/bp.nas index 198ac38e..5611faac 100644 --- a/test/bp.nas +++ b/test/bp.nas @@ -1,4 +1,5 @@ use std.mat; +use std.math; rand(time(0)); diff --git a/test/burningship.nas b/test/burningship.nas index 70750598..357b1315 100644 --- a/test/burningship.nas +++ b/test/burningship.nas @@ -1,4 +1,7 @@ use std.process_bar; +use std.os; +use std.io; +use std.math; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number @@ -7,7 +10,7 @@ var ppm = func(filename, width, height, RGB) { io.write(fd, "P6\n"~width~" "~height~"\n255\n"); for(var i = 0; ilen?size(f):len; + var len = 0; + foreach(var v; vec) + foreach(var f; v) + len = size(f)>len? size(f):len; return len; } -var padding_length=longest(source,lib,testfile,module); +var padding_length = longest(source, lib, testfile, module); var blank = func(s) { if (!size(s)) { return 1; } - var space=[" "[0],"\n"[0],"\t"[0],"\r"[0]]; - for(var i=0;i=1000?substr(str(number/1000),0,4)~'k':str(number); - return padding.leftpad(number,6); + number = number>=1000? substr(str(number/1000), 0, 4)~'k':str(number); + return padding.leftpad(number, 6); } -var calc = func(codetype,files,path="") { +var calc = func(codetype, files, path = "") { println(codetype); - var (bytes,ctx,line,semi,line_cnt,semi_cnt)=(0,"",0,0,0,0); - forindex(var i;files) { - var s=io.exists(path~files[i])?io.readfile(path~files[i]):""; - (line_cnt,semi_cnt)=(count(s,'\n'),count(s,';')); - println(padding.rightpad(files[i],padding_length),'|', - column(line_cnt),' line |', - column(semi_cnt),' semi |', - padding.leftpad(str(int(size(s)/1024)),4),' kb | ', - md5(s),' |'); - bytes+=size(s); - ctx~=s; - line+=line_cnt; - semi+=semi_cnt; + var (bytes, ctx, line, semi, line_cnt, semi_cnt) = (0, "", 0, 0, 0, 0); + forindex(var i; files) { + var s = io.exists(path~files[i])? io.readfile(path~files[i]):""; + (line_cnt, semi_cnt) = (count(s, '\n'), count(s, ';')); + println(padding.rightpad(files[i], padding_length), '|', + column(line_cnt), ' line |', + column(semi_cnt), ' semi |', + padding.leftpad(str(int(size(s)/1024)), 4), ' kb | ', + md5(s), ' |'); + bytes += size(s); + ctx ~= s; + line += line_cnt; + semi += semi_cnt; } - println(padding.rightpad("total:",padding_length),'|', - column(line),' line |', - column(semi),' semi |', - padding.leftpad(str(int(bytes/1024)),4),' kb | ', - md5(ctx),' |\n'); + println(padding.rightpad("total:", padding_length), '|', + column(line), ' line |', + column(semi), ' semi |', + padding.leftpad(str(int(bytes/1024)), 4), ' kb | ', + md5(ctx), ' |\n'); return int(bytes/1024); } -var all=calc("source code:",source,"src/") - +calc("lib:",lib,"std/") - +calc("test file:",testfile,"test/") - +calc("module:",module,"module/"); -println(padding.rightpad("total:",padding_length),'|',padding.leftpad(str(all),6),' kb |'); \ No newline at end of file +var all = calc("source code:", source, "src/") + + calc("lib:", lib, "std/") + + calc("test file:", testfile, "test/") + + calc("module:", module, "module/"); +println(padding.rightpad("total:", padding_length), '|', padding.leftpad(str(all), 6), ' kb |'); \ No newline at end of file diff --git a/test/console3D.nas b/test/console3D.nas index 0703a464..7c137b34 100644 --- a/test/console3D.nas +++ b/test/console3D.nas @@ -23,6 +23,7 @@ # SOFTWARE. use module.libmat; use std.runtime; +use std.math; func() { # allocate more spaces diff --git a/test/coroutine.nas b/test/coroutine.nas index b1010494..deb1c1d4 100644 --- a/test/coroutine.nas +++ b/test/coroutine.nas @@ -3,6 +3,7 @@ use std.coroutine; use std.process_bar; use std.padding; +use std.os; if (os.platform()=="windows") { system("chcp 65001"); diff --git a/test/diff.nas b/test/diff.nas index 9a7a492a..40b2a864 100644 --- a/test/diff.nas +++ b/test/diff.nas @@ -1,3 +1,5 @@ +use std.io; + var myers = func(src,dst,show_table=0) { (src,dst)=(split("\n",src),split("\n",dst)); append(src,""); diff --git a/test/donuts.nas b/test/donuts.nas index 8d8bcc33..340c730d 100644 --- a/test/donuts.nas +++ b/test/donuts.nas @@ -1,4 +1,5 @@ use std.runtime; +use std.math; var mod = math.mod; diff --git a/test/feigenbaum.nas b/test/feigenbaum.nas index 009b8ad2..d56d315f 100644 --- a/test/feigenbaum.nas +++ b/test/feigenbaum.nas @@ -1,4 +1,7 @@ use std.process_bar; +use std.os; +use std.io; +use std.math; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number @@ -13,8 +16,8 @@ var ppm = func(filename, width, height, RGB) { io.close(fd); } -var width = 1600; -var height = 900; +var width = 1920; +var height = 1080; var bar = (os.platform()=="windows")? process_bar.bar(front:"sharp", back:"point", sep:"line", length:50): process_bar.high_resolution_bar(50); diff --git a/test/filesystem.nas b/test/filesystem.nas index 92d65218..27d67e95 100644 --- a/test/filesystem.nas +++ b/test/filesystem.nas @@ -1,3 +1,7 @@ +use std.os; +use std.io; +use std.unix; + var files = func(path) { if (!io.exists(path)) return []; diff --git a/test/flush_screen.nas b/test/flush_screen.nas index 499797a9..e9937f49 100644 --- a/test/flush_screen.nas +++ b/test/flush_screen.nas @@ -1,4 +1,5 @@ use module.libkey; +use std.unix; srand(); diff --git a/test/gc_test.nas b/test/gc_test.nas index fd3f28ec..8263c77e 100644 --- a/test/gc_test.nas +++ b/test/gc_test.nas @@ -1,4 +1,5 @@ use std.runtime; +use std.os; var test_func = func(test_processes...) { var test_process_total = maketimestamp(); diff --git a/test/hexdump.nas b/test/hexdump.nas index b90606d0..cf5c66b5 100644 --- a/test/hexdump.nas +++ b/test/hexdump.nas @@ -2,6 +2,8 @@ # 2021/8/13 use std.file; use std.runtime; +use std.os; +use std.io; # init var hex = func() { diff --git a/test/httptest.nas b/test/httptest.nas index 3ec6719a..ee919fba 100644 --- a/test/httptest.nas +++ b/test/httptest.nas @@ -1,4 +1,7 @@ use module.libsock; +use std.os; +use std.io; +use std.unix; var socket = libsock.socket; diff --git a/test/json.nas b/test/json.nas index c8f3167b..4d0aa2e4 100644 --- a/test/json.nas +++ b/test/json.nas @@ -36,7 +36,51 @@ var ss = json.stringify([{ println(ss, "\n"); println(json.parse(ss), "\n"); +var test_func = func(input_string) { + var result = json.parse(input_string); + var errno = json.get_error(); + if (!size(errno)) { + println("\e[92;1msuccess\e[0m:"); + println(" ", result); + } else { + println("\e[91;1merror\e[0m:"); + foreach(var err; split("\n", errno)) { + println(" |-> ", err); + } + println(" +-> generated with error:"); + println(" +-> ", result); + } +} + +test_func("[[]]"); +test_func("{\"1\":1}"); +test_func("[[[}]]]"); +test_func("=-==_+_+_+"); +test_func("123"); +test_func(json.stringify({ + a: 0, + b: nil, + c: "this is a string", + d: [{}, [[1, 2, 4, 8, 16]]] +})); +test_func(""); +println(); + func { + var a = {}; + a.a = a; + + var b = []; + append(b, b); + + println(json.stringify(a)); + println(json.get_error()); + println(json.stringify(b)); + println(json.get_error()); + println(); +}(); + +var test_json = func(json) { var bar = process_bar.high_resolution_bar(30); var tmp = [ {t0:nil}, @@ -50,18 +94,25 @@ func { ]; srand(); - foreach(var h; tmp) { - var name = keys(h)[0]; - h[name] = []; + foreach(var hash; tmp) { + var name = keys(hash)[0]; + hash[name] = []; print("\e[1000D", bar.bar(0)); for(var i = 0; i<500; i+=1) { - append(h[name], {id:i, content:int(rand()*1e7)}); + append(hash[name], {id:i, content:int(rand()*1e7)}); print("\e[1000D", bar.bar((i+1)/500)); } print("\e[1000D", bar.bar(1), " executing...\n"); } print("\e[1000D", "\e["~str(size(tmp))~"A"); - foreach(var h; json.parse(json.stringify(tmp))) { - println("\e[1000D", bar.bar(1), " parse done ", keys(h)[0], " ", size(h[keys(h)[0]])); + foreach(var hash; json.parse(json.stringify(tmp))) { + println("\e[1000D", bar.bar(1), " parse done ", keys(hash)[0], " ", size(hash[keys(hash)[0]])); } -}(); \ No newline at end of file +}; + +var stamp = maketimestamp(); +for(var i = 0; i<10; i += 1) { + stamp.stamp(); + test_json(json); + println("time usage: ", stamp.elapsedUSec()/1000, " ms"); +} diff --git a/test/jsonrpc.nas b/test/jsonrpc.nas index c5dfaa70..3b7f95bd 100644 --- a/test/jsonrpc.nas +++ b/test/jsonrpc.nas @@ -1,6 +1,8 @@ use module.libsock; use std.json; use std.runtime; +use std.os; +use std.unix; var socket = libsock.socket; diff --git a/test/juliaset.nas b/test/juliaset.nas new file mode 100644 index 00000000..0bb5e631 --- /dev/null +++ b/test/juliaset.nas @@ -0,0 +1,47 @@ +use std.process_bar; +use std.os; +use std.io; + +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; i4) { + 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("juliaset.ppm", width, height, f); +println(); diff --git a/test/lexer.nas b/test/lexer.nas index 6055a90f..211ed044 100644 --- a/test/lexer.nas +++ b/test/lexer.nas @@ -1,3 +1,5 @@ +use std.io; + var lexer = func(file) { var (ptr,token)=(0,[]); var s=io.readfile(file); diff --git a/test/life.nas b/test/life.nas index 8bdcbcb0..18e0b250 100644 --- a/test/life.nas +++ b/test/life.nas @@ -1,5 +1,8 @@ use std.process_bar; use std.runtime; +use std.os; +use std.io; +use std.unix; var new_map = func(width,height) { var tmp=[]; diff --git a/test/mandelbrotset.nas b/test/mandelbrotset.nas index 857b5e49..c4573bc1 100644 --- a/test/mandelbrotset.nas +++ b/test/mandelbrotset.nas @@ -1,4 +1,6 @@ use std.process_bar; +use std.os; +use std.io; var ppm = func(filename, width, height, RGB) { # P3 use ASCII number @@ -7,7 +9,7 @@ var ppm = func(filename, width, height, RGB) { io.write(fd, "P6\n"~width~" "~height~"\n255\n"); for(var i = 0; i does not exist\e[0m"); break; } - var latest_modified_time=fstat(filename).st_mtime; + var latest_modified_time = io.fstat(filename).st_mtime; if (latest_modified_time!=modified_time) { modified_time=latest_modified_time; println(os_time(),modified_hd(),"\e[1m",filename,"\e[0m"); diff --git a/test/wavecollapse.nas b/test/wavecollapse.nas index 1cdbf900..5c34caa8 100644 --- a/test/wavecollapse.nas +++ b/test/wavecollapse.nas @@ -1,5 +1,7 @@ # wave collapse function 2022/4/10 # by ValKmjolnir +use std.os; + srand(); var table=[ # c ,w,a,s,d diff --git a/test/word_collector.nas b/test/word_collector.nas index 38e33db2..4b2eecaf 100644 --- a/test/word_collector.nas +++ b/test/word_collector.nas @@ -1,4 +1,5 @@ use std.runtime; +use std.io; var to_lower = func(s) { var tmp=""; diff --git a/tools/andy_gc_test.nas b/tools/andy_gc_test.nas index 3b623164..2db82cc8 100644 --- a/tools/andy_gc_test.nas +++ b/tools/andy_gc_test.nas @@ -1,3 +1,4 @@ +use std.math; var REPS = 10; var COUNT = 16384; diff --git a/tools/compiling_test.nas b/tools/compiling_test.nas index 8c3fab67..ad419da2 100644 --- a/tools/compiling_test.nas +++ b/tools/compiling_test.nas @@ -2,15 +2,15 @@ use std.file; var check = func(dir_name) { var ts = maketimestamp(); - var f = file.find_all_files_with_extension(dir_name, "nas"); + var files = file.recursive_find_files_with_extension(dir_name, "nas"); var res = []; - foreach(var k; f) { + foreach(var f; files) { ts.stamp(); - if (system("nasal -c "~dir_name~"/"~k~" 1>/dev/null 2>/dev/null")!=0) { - println("\e[31merror\e[0m ", dir_name, "/", k); - append(res, dir_name~"/"~k); + if (system("nasal -c "~f~" 1>/dev/null 2>/dev/null")!=0) { + println("\e[31merror\e[0m ", f); + append(res, f); } - println("compiling ", dir_name, "/", k, " in \e[32m", ts.elapsedMSec(), "\e[0m ms"); + println("compiling ", f, " in \e[32m", ts.elapsedUSec()/1000, "\e[0m ms"); } return res; } diff --git a/tools/fgfs_props_getter.nas b/tools/fgfs_props_getter.nas index 7987a8c0..8f3b6060 100644 --- a/tools/fgfs_props_getter.nas +++ b/tools/fgfs_props_getter.nas @@ -1,4 +1,8 @@ use std.phi; +use std.math; +use std.utils; + +# if on WSL2, you may need to use `ip route` to see the ip of the host var tips = func() { println("usage:"); @@ -20,7 +24,7 @@ var connect = phi.new(arg[0], num(arg[1])); var count = 0; var recursive_get_prop = func(path = "/") { count += 1; - if (math.mod(count, 50)==0) { + if (utils.times_trigger(count, 20)) { println("get ", count," nodes, now: \"", path, "\""); } var props = connect.getprop(path); diff --git a/tools/file2ppm.nas b/tools/file2ppm.nas index b2498dcd..f84e288e 100644 --- a/tools/file2ppm.nas +++ b/tools/file2ppm.nas @@ -1,3 +1,5 @@ +use std.io; +use std.math; var ppm = func(filename, buffer) { # P3 use ASCII number diff --git a/tools/push.nas b/tools/push.nas index c4c1a606..61aefea6 100644 --- a/tools/push.nas +++ b/tools/push.nas @@ -1,3 +1,5 @@ +use std.os; +use std.unix; println("[", os.time(), "] (=.=) auto push, please wait..."); diff --git a/tools/search_file.nas b/tools/search_file.nas index d35fb025..19741703 100644 --- a/tools/search_file.nas +++ b/tools/search_file.nas @@ -1,6 +1,8 @@ use std.file; use std.padding; use std.process_bar; +use std.io; +use std.math; var tips = func() { println("usage:"); @@ -19,33 +21,17 @@ if (size(arg)<1) { var needle = arg[0]; -var do_flat = func(vec) { - var flat = []; - var bfs = [vec]; - while(size(bfs)!=0) { - var d = pop(bfs); - foreach(var f; d.files) { - if (ishash(f)) { - append(bfs,f); - continue; - } - append(flat, d.dir~"/"~f); - } - } - sort(flat, func(a, b) {return cmp(a, b)<0}); - return flat; -} - var result = []; -var all_files = file.recursive_find_files("."); -foreach(var f; do_flat(all_files)) { +var all_files = file.recursive_find_files_flat("."); +sort(all_files, func(a, b) {return cmp(a, b)<=0;}); +foreach(var f; all_files) { var pos = find(needle, f); if (pos == -1) { continue; } 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 file_size = io.fstat(f).st_size; var unit = " b"; if (file_size>1024) { file_size/=1024;