191 Commits
v8.0 ... v10.0

Author SHA1 Message Date
ValKmjolnir
73278ea2dd 📝 update test/ascii-art.nas 2022-08-16 20:41:05 +08:00
ValKmjolnir
732a00a1cd 🚀 add [[noreturn]] 2022-08-16 01:40:22 +08:00
ValKmjolnir
bb0a2e3669 reduce unnecessary errors & make io.read safer 2022-08-11 01:48:02 +08:00
ValKmjolnir
2f43c47e77 🐛 fix compilation error of modules on macOS platform 2022-08-10 19:48:01 +08:00
ValKmjolnir
d65868f1e9 reduce unnecessary code & optimize code 2022-08-10 01:44:47 +08:00
ValKmjolnir
170f34a5e2 reduce unnecessary native-function errors. 2022-08-09 01:34:47 +08:00
ValKmjolnir
0493e18d0e 🚀 optimize code 2022-08-08 22:10:51 +08:00
ValKmjolnir
7ddb8593ad 🚀 optimize stl/stack.nas, test/calc.nas & io.fin now returns empty string when cannot open file 2022-08-07 00:35:55 +08:00
ValKmjolnir
2937c10c77 📝 little change 2022-08-05 23:22:32 +08:00
ValKmjolnir
feea8f8d66 🐛 fix builtin_err in module to nas_err 2022-08-04 21:20:27 +08:00
ValKmjolnir
e51878266a reduce code size 2022-08-03 23:03:49 +08:00
ValKmjolnir
37841fc91e optimize code & shrink code size 2022-08-02 22:03:28 +08:00
ValKmjolnir
44bfd74ca9 📝 optimize codes 2022-08-01 22:51:19 +08:00
ValKmjolnir
04a45064c8 📝 delete unnecessary code & change test file. 2022-07-31 19:26:13 +08:00
ValKmjolnir
068743aa4c 📝 change int in code to i32, optimize code. 2022-07-31 02:19:29 +08:00
ValKmjolnir
7ad1d69c64 optimize code & chdir will not trigger crash if failed to call 2022-07-30 19:17:33 +08:00
ValKmjolnir
52fcc9118c 🚀 input now can choose std::getline to read one line. 2022-07-30 01:31:56 +08:00
ValKmjolnir
c0862704f0 🐛 fix bug that dylib.dlopen cannot load dynamic lib in local dir. 2022-07-29 23:30:15 +08:00
ValKmjolnir
854850d9b1 🐛 fix bug of incorrectly searching paths of lib.nas and dynamic libs.
`dylib.dlopen` now only needs file name of dynamic lib, not the real path.
2022-07-29 22:49:50 +08:00
ValKmjolnir
006ed644e6 🚀 move fg constants to stl/fg_env.nas, add io.exists. now dlopen and import can search file in PATH. 2022-07-28 21:44:55 +08:00
ValKmjolnir
33e584ab5c 📝 add doc to help you create visual studio project. 2022-07-27 19:31:14 +08:00
ValKmjolnir
db896e932f correct types in nasal_codegen.h 2022-07-27 00:59:35 +08:00
ValKmjolnir
3e8b9e4846 optimize nasal_parser 2022-07-26 20:04:57 +08:00
ValKmjolnir
ea33ce020c 🚀 add new default bar in stl.process_bar 2022-07-25 22:15:40 +08:00
ValKmjolnir
ac8652f864 📝 update README 2022-07-25 20:09:41 +08:00
ValKmjolnir
4c95d622f6 🚀 shrink stack size & optimize nasal_vm::traceback 2022-07-25 00:23:07 +08:00
ValKmjolnir
61dcfb9395 📝 fix error in README_zh.md 2022-07-24 17:16:44 +08:00
ValKmjolnir
899b327608 📝 fix error in README.md 2022-07-24 17:15:44 +08:00
ValKmjolnir
c702f02a8a 📝 change README.md & README_zh.md 2022-07-24 17:05:36 +08:00
ValKmjolnir
afbee18e26 📝 update README.md & README_zh.md 2022-07-24 13:01:42 +08:00
ValKmjolnir
c6129267aa 📝 update docs 2022-07-24 00:10:26 +08:00
ValKmjolnir
6187395ce8 📝 split README.md to doc/benchmark.md doc/dev.md doc/dev_zh.md 2022-07-23 23:04:31 +08:00
ValKmjolnir
5dcf2ede66 📝 doc fix 2022-07-23 17:21:44 +08:00
ValKmjolnir
99a131c552 🚀 change std::string to string, change identifiers' name, -o is now a function available in debug mode. 2022-07-23 17:00:25 +08:00
ValKmjolnir
46516485b5 📝 change name of used types 2022-07-23 12:35:21 +08:00
ValKmjolnir
e9fc70bba8 optimize op_para and op_deft: change hashmap<string,u64> to hashmap<u32,u32> 2022-07-23 12:02:41 +08:00
ValKmjolnir
8ca0bc52c3 change some branches 2022-07-22 22:24:48 +08:00
ValKmjolnir
af761641d6 🚀 change std::cout to std::clog to put some log info 2022-07-21 23:16:16 +08:00
ValKmjolnir
f68a512845 📝 shrink size of README.md 2022-07-20 22:22:50 +08:00
ValKmjolnir
f7cd3a027b 📝 change README.md 2022-07-20 00:06:00 +08:00
ValKmjolnir
cc4ff38f28 optimize codes 2022-07-19 23:55:12 +08:00
ValKmjolnir
cfbec9a3f1 📝 change identifiers' name 2022-07-18 23:54:44 +08:00
ValKmjolnir
d904123695 🐛 fix bug in test/module_test.nas 2022-07-17 23:46:37 +08:00
ValKmjolnir
d8d457ce74 🐛 fix information print format on MSVC. 2022-07-16 18:47:27 +08:00
ValKmjolnir
b3d65f0183 📝 fix bug in doc README_zh.md 2022-07-16 18:01:53 +08:00
ValKmjolnir
fc7e0a569e 📝 update docs. 2022-07-16 17:58:01 +08:00
ValKmjolnir
51f780e637 🐛 fix error use of FindFirstFile. 2022-07-16 17:47:43 +08:00
ValKmjolnir
577ecd14df 🚀 update makefile to choose different compilers by users & fix some codes for MSVC to compile. 2022-07-16 16:53:11 +08:00
ValKmjolnir
f1f48b4881 first step trying to make this project compiled by MSVC: using indirect-threading, change \e to \033. 2022-07-16 01:02:33 +08:00
ValKmjolnir
29084143d6 📝 change codes in test files. 2022-07-14 19:52:27 +08:00
ValKmjolnir
d1a7d32e7b 📝 fix some wanings. 2022-07-11 23:53:23 +08:00
ValKmjolnir
8cc69c709f 📝 fix some warnings. 2022-07-10 23:55:51 +08:00
ValKmjolnir
bcdc55a652 optimize code & replace all printf with std::cout. 2022-07-09 23:36:14 +08:00
ValKmjolnir
2ed20f6362 📝 change some identifiers' name. 2022-07-09 16:24:58 +08:00
ValKmjolnir
9890b46f02 optimize code. 2022-07-08 23:00:36 +08:00
ValKmjolnir
11e9567b55 📝 change some identifiers' name. 2022-07-08 18:16:00 +08:00
ValKmjolnir
5d4cff0aa8 🚀 shrink code size & now import module can search lib file from local dir and dirs in PATH 2022-07-08 00:42:56 +08:00
ValKmjolnir
c1d13ecd85 🐛 fix a bug that split doesn't work if delimeter's length is greater than 1. 2022-07-07 19:32:06 +08:00
ValKmjolnir
824a31ef55 optimize builtin_split. 2022-07-07 18:07:24 +08:00
ValKmjolnir
fb25a4973c optimize codes. details:
1. delete gc::builtin_alloc
2. add convenient way of getting new string object: gc::newstr, and shrink the size of codes
3. update doc
4. add gc::temp to be used in native/module functions to avoid being recognized as garbage incorrectly when triggered mark-sweep
2022-07-07 17:51:30 +08:00
ValKmjolnir
75c46fa727 🚀 change usleep in builtin_sleep to cross platform c++11::std::this_thread::sleep_for.
Although this is not so accurate in Windows platform, the accuracy is still improved and this line of code does not need MinGW to build on Windows platform.
2022-07-07 14:35:10 +08:00
ValKmjolnir
bd5044add2 🚀 add a new way to import other files:
`import("./stl/queue.nas");` now has same function as `import.stl.queue;`
2022-07-06 22:21:01 +08:00
ValKmjolnir
b638708722 📝 change identifiers' name and test/coroutine.nas, test/filesystem.nas 2022-07-06 16:04:21 +08:00
ValKmjolnir
a04ed2a4aa 📝 change makefile and update output format of --opcnt and --detail 2022-07-06 11:57:40 +08:00
ValKmjolnir
82b33ffe4a 📝 change name of some identifiers. shrink size of main stack. 2022-07-05 18:13:54 +08:00
ValKmjolnir
2bc15697df optimize codes and delete unused variables 2022-07-05 00:49:38 +08:00
ValKmjolnir
92d68b357c optimize vm 2022-07-04 00:16:04 +08:00
ValKmjolnir
452bb4a5d8 📝 change some identifiers' name. 2022-07-03 22:46:28 +08:00
ValKmjolnir
e8bd4664b3 optimize codes 2022-07-02 18:08:41 +08:00
ValKmjolnir
5bc1925082 📝 update test files 2022-07-02 15:48:02 +08:00
ValKmjolnir
8b8fb79013 📝 unify code style of ./stl ./test 2022-07-02 13:53:50 +08:00
ValKmjolnir
c993c77b78 change format of error info
parser now can report exact place where is lacking ';' or ','
2022-06-30 23:15:10 +08:00
ValKmjolnir
a4a7f5075a 🚀 update test/httptest.nas and doc/.html 2022-06-24 15:47:53 +08:00
ValKmjolnir
4c0ef0fb99 📝 update test/httptest.nas 2022-06-23 22:16:24 +08:00
ValKmjolnir
9114ecd820 📝 finish translation of README.md 2022-06-18 18:48:00 +08:00
ValKmjolnir
abbdb22478 📝 update README.md and README_zh.md 2022-06-18 16:35:21 +08:00
ValKmjolnir
4f62d2c7ea 📝 update doc/README_zh.md 2022-06-18 01:31:51 +08:00
ValKmjolnir
bb84c6d143 📝 add README_zh.md(haven't finished translation). 2022-06-18 00:30:43 +08:00
ValKmjolnir
e27f773749 📝 update docs 2022-06-17 22:18:34 +08:00
ValKmjolnir
478d1aadf9 📝 update test/hexdump.nas test/md5compare.nas 2022-06-16 18:21:11 +08:00
ValKmjolnir
393b37d109 📝 update stl/process_bar.nas 2022-06-16 01:03:51 +08:00
ValKmjolnir
c2a23ecba6 📝 update stl/process_bar.nas and test/md5compare.nas 2022-06-15 20:21:03 +08:00
ValKmjolnir
ef3afa14b9 🚀 add stl/log.nas and stl/process_bar.nas 2022-06-15 00:36:49 +08:00
ValKmjolnir
6b20e0f167 🚀 add os.time() to get the local time string 2022-06-12 19:08:31 +08:00
ValKmjolnir
dea9c4ad3b 📝 optimize stl/fg_env.nas 2022-06-12 16:55:59 +08:00
ValKmjolnir
f59c3c7787 📝 update stl/fg_env.nas 2022-06-11 20:53:13 +08:00
ValKmjolnir
0515e388da 🐛 fix bug of forindex/foreach, which will cause SIGSEGV.
example:

var a=func{};
forindex(a();[0,1,2]);
2022-06-11 13:55:10 +08:00
ValKmjolnir
21b345af27 📝 update test/httptest.nas 2022-06-11 12:49:26 +08:00
ValKmjolnir
04806fc2c5 📝 update test/httptest.nas 2022-06-08 20:01:22 +08:00
ValKmjolnir
12d7dde42d 🚀 finish socket library:
socket.socket()

socket.closesocket()

socket.shutdown()

socket.bind()

socket.listen()

socket.connect()

socket.accept()

socket.send() socket.sendto()

socket.recv() socket.recvfrom()

socket.errno()
2022-06-08 19:06:16 +08:00
ValKmjolnir
53b85cd459 🚀 add module/libsock.nas, preparing to add socket lib into this language. 2022-06-06 16:34:21 +08:00
ValKmjolnir
a2e2d5d8f6 add native function runtime.argv() to get command line arguments 2022-06-04 20:12:00 +08:00
ValKmjolnir
cb0fee04a9 🚀 add native function println & add test/occupation.nas 2022-06-04 19:22:28 +08:00
ValKmjolnir
4e6cd82ccb 🐛 fix unix.isdir & unix.isfile 2022-06-03 21:53:14 +08:00
ValKmjolnir
c3a8e118f5 add u32 and() xor() or() nand() not() 2022-06-03 21:34:02 +08:00
ValKmjolnir
7ec8418740 🚀 use ftime to calculate run time 2022-06-02 22:51:43 +08:00
ValKmjolnir
04ab09586b 🚀 add stl/fg_env.nas & delete test/props props_sim maketimer_sim.nas 2022-05-31 20:36:39 +08:00
ValKmjolnir
ab37495960 🐛 visual changement & bug fix in test/json.nas & optimize test/bp.nas 2022-05-30 21:27:24 +08:00
ValKmjolnir
882ecac100 📝 update README.md 2022-05-25 14:05:58 +08:00
ValKmjolnir
36a7868b0b 📝 add maketimer_multi_coroutine_test(vm_num) in test/maketimer_sim.nas 2022-05-23 22:12:58 +08:00
ValKmjolnir
190014a3e5 🐛 fix bug that gc cannot mark values in coroutine & add maketimer_sim.nas
update props_sim.nas

update auto_crash.nas
2022-05-23 20:23:20 +08:00
ValKmjolnir
ebfacd9197 optimize codes 2022-05-22 18:19:13 +08:00
ValKmjolnir
99dca532f6 📝 add notes in nasal_codegen.h and nasal_vm.h. add register_info() in debugger. 2022-05-21 14:22:54 +08:00
ValKmjolnir
dcad554eba 🎨 fix a bug in module/keyboard.cpp that if program exited with an error, the terminal may not echo the text you input 2022-05-20 21:42:28 +08:00
ValKmjolnir
07eeaadf96 optimize codes: store upvalue on stack & debugger now can output information of registers 2022-05-20 21:19:43 +08:00
ValKmjolnir
120ceb429a 🚀 add coroutine library(beta) and lib function settimer&maketimestamp 2022-05-19 20:09:23 +08:00
ValKmjolnir
d567f5abf8 📝 update test/module_test.nas 2022-05-17 21:48:08 +08:00
ValKmjolnir
f8692f1e4e 📝 update test files 2022-05-17 18:22:24 +08:00
ValKmjolnir
712a047a43 🐛 now builtin_md5 uses unsigned char instead of char 2022-05-15 20:35:20 +08:00
ValKmjolnir
396d55a207 fix some warnings 2022-05-09 18:42:40 +08:00
ValKmjolnir
c5171c735a 💬 change code related to macros 'PRTHEX' 2022-05-08 19:43:27 +08:00
ValKmjolnir
07857d980c 📝 update test/utf8chk.nas 2022-05-07 17:18:29 +08:00
ValKmjolnir
51a1279110 add utf-8 identifier check in nasal_lexer & fix printf format at windows platform & add test file utf8chk.nas 2022-05-07 16:50:13 +08:00
ValKmjolnir
de262980cc 🎨 improve rawstr() and builtin_getcwd(..), change format output in nasal_vm::valinfo() 2022-05-07 02:22:49 +08:00
ValKmjolnir
23a5c1b1ad 🐛 bug fix 2022-05-06 20:58:02 +08:00
ValKmjolnir
fd8a148d0c bytecode info print and debugger will show the front 16 characters of strings whose length is greater than 16 2022-05-02 16:56:03 +08:00
ValKmjolnir
9c7f5f1a6e update test file diff.nas& add math.max math.min 2022-05-01 21:10:23 +08:00
ValKmjolnir
f049e1f9fb add test file diff.nas 2022-04-30 20:00:15 +08:00
ValKmjolnir
c4b7712e53 update README.md 2022-04-27 12:49:29 +08:00
ValKmjolnir
7417d5e635 change makefile 2022-04-26 22:53:35 +08:00
ValKmjolnir
785572634b change visual settings in -c/-dbg 2022-04-23 01:51:49 +08:00
ValKmjolnir
2dc8459cbf use effective way to check if file exists 2022-04-21 20:51:47 +08:00
ValKmjolnir
6e035d1951 add lib load code to automatically load lib.nas 2022-04-21 19:45:16 +08:00
ValKmjolnir
4f5fd3de33 op_addeq~op_lnkeq and op_addeqc~op_lnkeqc and op_meq operands now can do pop, this will decrease the frequency of calling op_pop 2022-04-17 17:20:18 +08:00
ValKmjolnir
fc25dd69e1 fix bug in codegen: foreach/forindex(id;vec/hash) may cause segmentation fault because of incorrect generated operand 2022-04-16 19:45:55 +08:00
ValKmjolnir
ca073499ae optimize codes 2022-04-13 19:08:06 +08:00
ValKmjolnir
022460755f optimize codes 2022-04-12 18:26:54 +08:00
ValKmjolnir
b6f174e869 update test/wavecollapse.nas 2022-04-10 16:47:27 +08:00
ValKmjolnir
7b160c4589 add test file wavecollapse.nas 2022-04-10 15:34:20 +08:00
ValKmjolnir
4503239731 change STACK_MAX_DEPTH to nasal_gc::stack_depth 2022-04-09 23:14:28 +08:00
ValKmjolnir
c12e812651 change test/md5compare.nas 2022-04-06 22:56:21 +08:00
ValKmjolnir
87cff700e8 change module/libmd5 to native function builtin_md5() in nasal_builtin.h 2022-04-06 22:30:49 +08:00
ValKmjolnir
bf5737ecfd add native function abort(), assert() 2022-04-06 21:25:20 +08:00
ValKmjolnir
651ae4ef77 add native function isa(object,class) 2022-04-05 22:33:55 +08:00
ValKmjolnir
e846e51175 add native function: srand, values, find. 2022-04-05 22:15:05 +08:00
ValKmjolnir
399b2f0ce9 bug fixed & add systime, finished vecindex 2022-04-04 19:08:48 +08:00
ValKmjolnir
aed5e27409 bug fix & optimize test code & add lib function: subvec, floor, abs, isfunc, isghost, isint, isnum, isscalar, isstr, isvec, vecindex(unfinished) 2022-04-04 18:38:25 +08:00
ValKmjolnir
a2b51fe212 optimize libmd5.nas & test/md5.nas 2022-04-03 18:10:00 +08:00
ValKmjolnir
92b684624d change module/makefile and test/md5compare.nas 2022-04-01 22:52:04 +08:00
ValKmjolnir
83a8632e8e update test/md5compare.nas & optimize test/md5.nas 2022-03-31 19:31:00 +08:00
ValKmjolnir
41b5304712 update test/md5compare.nas
and CAUTION: now the cpp&nas md5 program may not calculate strings including unicode
2022-03-30 21:42:30 +08:00
ValKmjolnir
c5a12ade5c update md5compare.nas 2022-03-29 22:54:17 +08:00
ValKmjolnir
7a939b417d fix bug in test/md5.nas & add test file md5compare.nas 2022-03-29 22:36:28 +08:00
ValKmjolnir
dd7740f1fd update test/md5.nas(still has bug) & add libmd5 in module(written in C++) 2022-03-28 17:14:11 +08:00
ValKmjolnir
617ad03d33 add math.pow 2022-03-27 16:14:55 +08:00
ValKmjolnir
b66ebbef4b fix bytecode output format bug 2022-03-27 13:10:35 +08:00
ValKmjolnir
7f22b25909 update README.md 2022-03-27 01:36:38 +08:00
ValKmjolnir
86f6296268 change opcode print format 2022-03-27 01:20:17 +08:00
ValKmjolnir
cf722fd98c fix bug: windows ps/cmd output unicode in bytecodes abnormally.
so we use the hex format to print them.
2022-03-23 22:54:07 +08:00
ValKmjolnir
1dd3fd445c add test/snake.nas 2022-03-20 22:08:11 +08:00
ValKmjolnir
27e25f84ec update main.cpp and bug fix 2022-03-20 18:28:45 +08:00
ValKmjolnir
e6457651d3 add unix.waitpid 2022-03-16 18:44:38 +08:00
ValKmjolnir
c4d52a88cd update docs & fix bug in nasal_builtin.h 2022-03-16 15:30:34 +08:00
ValKmjolnir
9bcad59e45 change enum obj_type to nasal_obj::obj_type 2022-03-15 22:51:14 +08:00
ValKmjolnir
9a099f66cb update docs 2022-03-14 20:29:49 +08:00
ValKmjolnir
b79d60fab5 add destructors for obj_file, obj_dylib, obj_dir & bug fix 2022-03-13 00:11:50 +08:00
ValKmjolnir
82e9e97a26 add destructor for obj_file & change arguments in test/tetris.nas 2022-03-12 23:37:16 +08:00
ValKmjolnir
6a1338bb23 update test/tetris.nas 2022-03-11 23:42:09 +08:00
ValKmjolnir
3b8a092f36 fix bug in test/json.nas 2022-03-11 15:26:38 +08:00
ValKmjolnir
3d86a32b12 update fully functional test/json.nas 2022-03-11 15:11:24 +08:00
ValKmjolnir
f26719e1d3 visual update 2022-03-10 16:05:49 +08:00
ValKmjolnir
e54ef9620f change nasal_ref value option functions' output types from pointers to references 2022-03-09 22:54:54 +08:00
ValKmjolnir
d8156e839b add unix.fork&unix.pipe(do not work on windows platform) 2022-03-09 19:03:12 +08:00
ValKmjolnir
61666d275d delete lstk&fstk, store local address and function on stack and in vm registers 2022-03-08 17:30:40 +08:00
ValKmjolnir
99f595e16f update num->string algorithm, now the to_string will output strings with no tailing zeros 2022-03-08 13:40:27 +08:00
ValKmjolnir
debe32b187 safer stl/module.nas & add stl/file.nas encapsulated from lib.nas/io 2022-03-06 15:17:39 +08:00
ValKmjolnir
ca9b8581b4 add module.nas to safely use dylib 2022-03-05 21:52:29 +08:00
ValKmjolnir
a0b341deb5 try fix bug 'use of undeclared identifier 'environ'' on MacOS 2022-03-05 19:24:33 +08:00
ValKmjolnir
d3df356299 add unix.environ() & use LPCWSTR in dylib.dlopen on Windows platform 2022-03-05 19:15:52 +08:00
ValKmjolnir
cd808a5e6d update detailed-info 2022-03-03 19:00:23 +08:00
ValKmjolnir
40f61a9dd4 fix bug of upval_state 2022-03-02 19:28:17 +08:00
ValKmjolnir
f312250d27 update 2022-03-01 14:36:05 +08:00
ValKmjolnir
3fac8aa665 fix bug in test/tetris.nas 2022-02-25 23:34:02 +08:00
ValKmjolnir
243aafd417 fix bug of -o not working. 2022-02-23 21:56:45 +08:00
ValKmjolnir
6bc03601d9 update test/tetris.nas 2022-02-23 01:09:31 +08:00
ValKmjolnir
f05acaecc7 fix bug in libkey and test/tetris.nas 2022-02-22 18:01:49 +08:00
ValKmjolnir
9456a903d7 finish tetris.nas 2022-02-21 17:10:13 +08:00
ValKmjolnir
984deed883 finish basic functions of tetris 2022-02-21 00:47:32 +08:00
ValKmjolnir
557cb2ebcf finish map drawing in test/tetris.nas 2022-02-20 17:58:13 +08:00
ValKmjolnir
9c055a9a23 add new third-lib libkey that includes function: kbhit,getch,nonblock 2022-02-19 16:55:54 +08:00
ValKmjolnir
13a09343e6 update README.md 2022-02-18 15:59:27 +08:00
ValKmjolnir
8c67e04cc4 update test file 2022-02-18 15:36:34 +08:00
ValKmjolnir
e77bb73a82 add special character \e. 2022-02-18 01:58:49 +08:00
ValKmjolnir
05fc5db337 change parameter name in native function split. 2022-02-17 23:34:58 +08:00
ValKmjolnir
a4738e8c7d delete operand op_nop 2022-02-16 23:27:22 +08:00
ValKmjolnir
5f6051e333 change tutorial's place in README.md 2022-02-15 23:42:17 +08:00
ValKmjolnir
51afe3dacd fixed bug when -O0 the program crashes with code 01.
add `nasal_err():error(0){}` in `nasal_err.h:47` as the constructor.
2022-02-14 17:40:19 +08:00
ValKmjolnir
0291908675 update README.md 2022-02-14 17:29:26 +08:00
ValKmjolnir
5fba784d05 add new gc type vm_upval 2022-02-13 22:40:54 +08:00
ValKmjolnir
9139e34c0b update README & bug fixed 2022-02-13 16:10:02 +08:00
ValKmjolnir
e7f503fae1 now local values are stored on stack. upvalue is generated when creating a new function in local scope.
may include some bugs inside. but all test files has passed the test so i decide to push it.
2022-02-12 23:12:30 +08:00
88 changed files with 14138 additions and 7955 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.nas linguist-language=Nasal

12
.gitignore vendored
View File

@@ -31,6 +31,18 @@
*.out
*.app
# VS C++ sln
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
.vs
x64
# nasal executable
nasal
nasal.exe
# misc
.vscode
dump

1293
README.md

File diff suppressed because it is too large Load Diff

899
doc/README_zh.md Normal file
View File

@@ -0,0 +1,899 @@
# __Nasal 脚本语言__
```C++
__ _
/\ \ \__ _ ___ __ _| |
/ \/ / _` / __|/ _` | |
/ /\ / (_| \__ \ (_| | |
\_\ \/ \__,_|___/\__,_|_|
```
![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-v10.0-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github)](../LICENSE)
> 这篇文档包含多种语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
## __目录__
* [__简介__](#简介)
* [__编译__](#编译)
* [__使用方法__](#使用方法)
* [__教程__](#教程)
* [__发行日志__](../doc/dev_zh.md#发行日志)
* [__开发历史__](../doc/dev_zh.md)
* [__测试数据__](../doc/benchmark.md)
* [__特殊之处__](#与andy解释器的不同之处)
* [__堆栈追踪信息__](#trace-back-info)
* [__调试器__](#调试器)
__如果有好的意见或建议欢迎联系我们!__
* __E-mail__: __lhk101lhk101@qq.com__
## __简介__
__[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)__
是一个与ECMAscript标准语法设计相似的编程语言并且作为运行脚本被著名的开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所依赖。
该语言的设计者和初版解释器实现者为 [Andy Ross](https://github.com/andyross)。
这个解释器项目则由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++11`)重新实现,没有复用 [Andy Ross的nasal解释器](<https://github.com/andyross/nasal>) 中的任何一行代码。尽管没有任何的参考代码我们依然非常感谢Andy为我们带来了这样一个神奇且容易上手的编程语言。
现在这个项目已经使用 __MIT 协议__ 开源 (2021/5/4)。根据该协议的内容,你们可以根据自己的需求进行修改,使用它来学习或者创造更多有趣的东西(不过可别忘了,如果要开源必须要附带本项目拥有者的相关信息)。
__我们为什么想要重新写一个nasal解释器?__
这是个很偶然的想法。2019年暑假[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我在Flightgear中提供的nasal控制台窗口中进行调试实在是太费劲了有时候只是想检查语法错误也得花费时间打开这个软件等待加载进去之后进行调试。所以我就想也许可以写一个全新的解释器来帮助他们检查语法错误甚至是检查运行时的错误。
我编写了nasal的词法分析器和语法分析器以及一个全新的字节码虚拟机(曾经我们使用ast解释器来直接在抽象语法树中执行然而在v4.0之后这个解释器已经淘汰)并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误非常快捷远比每次都需要复制nasal代码到Flightgear的nasal控制台中去查看要方便且错误信息清晰直观。
当然你也可以使用这个语言来写一些与Flightgear运行环境无关的其他有趣的程序并用这个解释器来执行让这个语言脱离Flightgear的环境去别的地方大展身手。你也可以编写你自己的模块让nasal来调用使得这个语言成为你的项目中一个非常有用的工具。
## __编译__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
我们推荐你下载最新更新的代码包来直接编译,这个项目非常小巧因此你可以非常快速地将它编译出来。
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug(有的严重bug都是在发行之后才发现非常搞心态)。在发行版日志中我们会告知如何在代码中手动修复这个严重的bug。
__`Windows`__ 用户通过g++(`MinGW-w64`)使用以下命令或者使用MSVC(`Visual Studio`)来进行编译. 没有编译环境的请在[__这里__](https://www.mingw-w64.org/downloads/)下载MinGW-w64。(VS同样也有MinGW-w64)
__`linux/macOS/Unix`__ 用户可以使用g++或者clang++替代下面命令中中括号的部分来进行编译(我们建议您使用`clang`)。
使用makefile我们就可以编译这个解释器。
`mingw32-make`是 __`Windows(MinGW-w64)`__ 平台的`make`:
> mingw32-make nasal.exe
>
> mingw32-make.exe nasal.exe
__`linux/macOS/Unix`__ 平台直接使用make即可:
> make nasal
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
> make nasal CXX=clang++
>
> make nasal CXX=g++
>
> make nasal CXX=...
如果你觉得`-O3`编译的版本不是那么安全和稳定,你也可以选择生成稳定的版本:
> make stable-release
>
> mingw32-make stable-release-mingw
你可以在`Visual Studio`中用这种方式来创建项目:[__点击跳转__](../doc/vs.md)。
## __使用方法__
首先我们要通过[__教程__](#教程)知道这个语言的语法以及如何使用这个解释器来运行nasal程序。
使用这个命令查看解释器的版本:
> ./nasal
输入下面的命令来 __直接__ 执行:
> ./nasal filename
下面两个命令可以用于查看帮助(调试器的使用方法可以进入调试模式之后根据提示来查询):
> ./nasal -h | --help
如果你的操作系统是 __`Windows`__ 并且你想输出unicode请保证你的控制台程序的代码页支持utf-8若不支持使用下面这个命令启用代码页:
> chcp 65001
或者你可以直接在nasal代码里写这个来开启:
```javascript
if(os.platform()=="windows")
system("chcp 65001");
```
## __教程__
Nasal是非常容易上手的你甚至可以在15分钟之内看完这里的基本教程并且直接开始编写你想要的程序。
__如果你先前已经是C/C++,javascript选手那么这个教程几乎可以不用看了……__ 在看完该教程之后,基本上你就完全掌握了这个语言:
<details><summary>基本类型</summary>
__`vm_none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行,用户是无法申请到这个类型的,该类型只能由字节码虚拟机自己在抛出错误时产生。
__`vm_nil`__ 是空类型。类似于null。
```javascript
var spc=nil;
```
__`vm_num`__ 有三种形式:十进制十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。
```javascript
# this language use '#' to write notes
var n=2.71828; # dec
var n=2.147e16; # dec
var n=1e-10; # dec
var n=0xAA55; # hex
var n=0o170001; # oct
```
__`vm_str`__ 也有三种不同的格式。第三种只允许包含一个的字符。
```javascript
var s='str';
var s="another string";
var s=`c`;
# 该语言也支持一些特别的转义字符:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vm_vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度)
```javascript
var vec=[];
var vec=[0,nil,{},[],func(){return 0}];
append(vec,0,1,2);
```
__`vm_hash`__ 使用哈希表(类似于`python`中的字典)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。
```javascript
var hash={
member1:nil,
member2:"str",
"member3":"member\'s name can also be a string constant",
funct:func(){
return me.member2~me.member3;
}
};
```
__`vm_func`__ 函数类型。(实际上在这个语言里函数也是一种lambda表达式)
```javascript
var f=func(x,y,z){return nil;}
var f=func{return 114514;}
var f=func(x,y,z,deft=1){
return x+y+z+deft;
}
var f=func(args...){
var sum=0;
foreach(var i;args)
sum+=i;
return sum;
}
```
__`vm_upval`__ 是存储闭包数据的特殊类型, 在 __`nasal_vm`__ 中使用,用于确保闭包功能正常。
__`vm_obj`__ 是用来存储C/C++的一些复杂数据结构。这种类型的数据一般由内置函数或者库开发者提供的模块函数生成。如果你想为nasal添加一种新的数据结构, 可以看下文如何通过修改本项目来添加自己的内置函数。
</details>
<details><summary>运算符</summary>
Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的运算符 `~`,这个运算符用于拼接两个字符串。
```javascript
1+2-(1+3)*(2+4)/(16-9);
'str1'~'str2';
```
对于条件语句,可以使用`==` `!=` `<` `>` `<=` `>=`来比较两个数据。`and` `or` 有着与C/C++中 `&&` `||`运算符相同的功能,用于连接两个不同的条件语句。
```javascript
1+1 and 0;
1<0 or 1>0;
1<=0 and 1>=0;
1==0 or 1!=0;
```
单目运算符`-` `!`与C/C++中的运算符功能类似.
```javascript
-1;
!0;
```
赋值运算符`=` `+=` `-=` `*=` `/=` `~=`正如其名,用于进行赋值。
```javascript
a=b=c=d=1;
a+=1; a-=1; a*=1; a/=1;
a~='string';
```
</details>
<details><summary>定义变量</summary>
```javascript
var a=1;
var (a,b,c)=[0,1,2];
var (a,b,c)=(0,1,2);
(var a,b,c)=[0,1,2];
(var a,b,c)=(0,1,2);
```
</details>
<details><summary>多变量赋值</summary>
最后这个语句通常用于交换两个变量的数据类似于Python中的操作。
```javascript
(a,b[0],c.d)=[0,1,2];
(a,b[1],c.e)=(0,1,2);
(a,b)=(b,a);
```
</details>
<details><summary>条件语句</summary>
nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。
```javascript
if(1){
;
}elsif(2){
;
}else if(3){
;
}else{
;
}
```
</details>
<details><summary>循环语句</summary>
while循环和for循环大体上与C/C++是一致的。
```javascript
while(condition)
continue;
for(var i=0;i<10;i+=1)
break;
```
同时nasal还有另外两种直接遍历列表的循环方式:
`forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。
```javascript
forindex(var i;elem)
print(elem[i]);
```
`foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`.
```javascript
foreach(var i;elem)
print(i);
```
</details>
<details><summary>生成子列表(subvec)</summary>
nasal提供了下面第一句的类似语法来从列表中随机或者按照一个区间获取数据并且拼接生成一个新的列表。当然如果中括号内只有一个下标的话你会直接获得这个下标对应的数据而不是一个子列表。如果直接对string使用下标来获取内容的话会得到对应字符的 __ascii值__。如果你想进一步获得这个字符串可以尝试使用内置函数`chr()`。
```javascript
a[0];
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil];
"hello world"[0];
```
</details>
<details><summary>特殊函数调用语法</summary>
这种特别的调用方式有时非常有用,但是切记这种调用方式不是很高效,因为哈希表会使用字符串比对来找到数据存放的位置。
```javascript
f(x:0,y:nil,z:[]);
```
</details>
<details><summary>lambda表达式</summary>
正如上文所述,函数有这样一种直接编写函数体并且直接调用的方式:
```javascript
func(x,y){return x+y}(0,1);
func(x){return 1/(1+math.exp(-x));}(0.5);
```
测试文件中有一个非常有趣的文件`y-combinator.nas`也就是y组合子可以试一试非常有趣:
```javascript
var fib=func(f){
return f(f);
}(
func(f){
return func(x){
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
}
}
);
```
</details>
<details><summary>闭包</summary>
闭包是一种特别的作用域,你可以从这个作用域中获取其保存的所有变量,而这些变量原本不是你当前运行的函数的局部作用域中的。下面这个例子里,结果是`1`:
```javascript
var f=func(){
var a=1;
return func(){return a;};
}
print(f()());
```
如果善用闭包,你可以使用它来进行面向对象编程。
```javascript
var student=func(n,a){
var (name,age)=(n,a);
return {
print_info:func() {println(name,' ',age);},
set_age: func(a){age=a;},
get_age: func() {return age;},
set_name: func(n){name=n;},
get_name: func() {return name;}
};
}
```
</details>
<details><summary>特性与继承</summary>
当然,也有另外一种办法来面向对象编程,那就是利用`trait`。
当一个hash类型中有一个成员的key是`parents`并且该成员是一个数组的话那么当你试图从这个hash中寻找一个它自己没有的成员名时虚拟机会进一步搜索`parents`数组。
如果该数组中有一个hash类型有一个成员的key与当前你搜索的成员名一致那么你会得到这个成员对应的值。
使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`:
```javascript
var trait={
get:func{return me.val;},
set:func(x){me.val=x;}
};
var class={
new:func(){
return {
val:nil,
parents:[trait]
};
}
};
var a=class.new();
a.set(114514);
println(a.get());
```
首先虚拟机会发现在`a`中找不到成员`set`,但是在`a.parents`中有个hash类型`trait`存在该成员,所以返回了这个成员的值。
成员`me`指向的是`a`自身,类似于一些语言中的`this`,所以我们通过这个函数,实际上修改了`a.val`。`get`函数的调用实际上也经过了相同的过程。
不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销:
```javascript
var trait={
get:func{return me.val;},
set:func(x){me.val=x;}
};
var class={
new:func(){
return {
val:nil,
parents:[trait]
};
}
};
var a=class.new();
var b=class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c=a.get;
var d=b.get;
println(c());
println(c());
println(d());
println(d());
```
那么你会发现现在虚拟机会输出这个结果:
```bash
114
514
514
514
514
514
```
因为执行`a.get`时在`trait.get`函数的属性中进行了`me=a`的操作。而`b.get`则执行了`me=b`的操作。所以在运行`var d=b.get`后实际上c也变成`b.get`了。
如果你想要用这种小技巧来让程序运行更高效的话,最好是要知道这里存在这样一个机制。
</details>
<details><summary>原生内置函数以及模块导入(import)语法</summary>
这个部分对于纯粹的使用者来说是不需要了解的,它将告诉你我们是如何为这个解释器添加新的内置函数的。如果你对于添加自己私人订制的内置函数很感兴趣,那么这个部分可能会帮到你,并且……
__警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个部分 __`模块`__ 的内容,而不是这个部分的内容。
如果你确实是想修改源码来搞一个自己私人订制的解释器,那么你可以说:“我他妈就是想自己私人订制,你们他妈的管得着吗”,然后看看源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,还有下面的样例:
定义新的内置函数:
```C++
nas_ref builtin_print(nas_ref*,nasal_gc&);
// 你可以使用这个宏来直接定义一个新的内置函数
nas_native(builtin_print);
```
然后用C++完成这个函数的函数体:
```C++
nas_ref builtin_print(nas_ref* local,nasal_gc& gc)
{
// 局部变量的下标其实是从1开始的
// 因为local[0]是保留给'me'的空间
nas_ref vec=local[1];
// 主要部分
// 一些必要的类型检查和输入合法性检测也要在这里写出
// 如果检测到问题用builtin_err函数来返回vm_null
// 并且狠狠地骂那些不好好写代码的混蛋(玩笑)
for(auto& i:vec.vec().elems)
switch(i.type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<i.str(); break;
case vm_vec: i.vec().print(); break;
case vm_hash: i.hash().print(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
}
std::cout<<std::flush;
// 最后一定要记得生成返回值,返回值必须是一个内置的类型,
// 可以使用gc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil;
}
```
这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针:
```C++
struct func
{
const char* name;
nas_ref (*func)(nas_ref*,nasal_gc&);
} builtin[]=
{
{"__print",builtin_print},
{nullptr, nullptr }
};
```
最后将其包装起来扔到nasal文件中:
```javascript
var print=func(elems...){
return __print(elems);
};
```
事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对:
```javascript
var print=func(elems...){
return __print;
};
```
一定要注意如果你不把内置函数包装到一个普通的nasal函数中那么直接调用这个内置函数会在参数传入阶段出现严重的错误这个错误会导致 __segmentation error__。也就是大家的老朋友段错误。
在nasal文件中使用`import("文件名.nas")`可以导入该文件中你包装的所有内置函数,接下来你就可以使用他们了。
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
```javascript
import.dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
当运行内置函数的时候内存分配器如果运行超过一次那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
所以请使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++
nas_ref builtin_keys(nas_ref* local,nasal_gc& gc)
{
nas_ref hash=local[1];
if(hash.type!=vm_hash)
return nas_err("keys","\"hash\" must be hash");
// 使用gc.temp来存储gc管理的变量防止错误的回收
nas_ref res=gc.temp=gc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
vec.push_back(gc.newstr(iter.first));
gc.temp=nil;
return res;
}
```
</details>
<details><summary>模块(开发者教程)</summary>
如果只有上文中那种方式来添加你自定义的函数到nasal中这肯定是非常麻烦的。因此我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。
在2021/12/3更新后我们给`lib.nas`添加了下面的这一批函数:
```javascript
var dylib=
{
dlopen: func(libname){return __dlopen;},
dlsym: func(lib,sym){return __dlsym; },
dlclose: func(lib){return __dlclose; },
dlcall: func(funcptr,args...){return __dlcall}
};
```
看名字就大概能猜出来这些函数就是用来加载动态库的这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。
首先用C++写个项目,并且编译成动态库。我们就拿`fib.cpp`作为例子来说明(样例代码可以在`./module`中找到):
```C++
// 这个头文件得加上因为我们需要拿到nasal的api
#include "nasal.h"
double fibonaci(double x){
if(x<=2)
return x;
return fibonaci(x-1)+fibonaci(x-2);
}
// 记得用extern "C"
// 这样找符号会更加快速便捷不要在意编译时的warning
extern "C" nas_ref fib(std::vector<nas_ref>& args,nasal_gc& gc){
// 传参会被送到一个vm_vec类型中送过来而不是上文中那种指针直接指向局部作用域
nas_ref num=args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
// builtin_err会输出错误信息并返回错误类型让虚拟机终止执行
if(num.type!=vm_num)
return nas_err("extern_fib","\"num\" must be number");
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用gc.alloc(type)
return {vm_num,fibonaci(num.tonum())};
}
```
接着我们把`fib.cpp`编译成动态库。
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac(`.so` & `.dylib`): 和Linux下操作相同。
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
好了那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样我们可以对不同系统进行适配:
```javascript
import("lib.nas");
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib=dylib.dlsym(dlhandle,"fib");
for(var i=1;i<30;i+=1)
println(dylib.dlcall(fib,i));
dylib.dlclose(dlhandle);
```
`dylib.dlopen`用于加载动态库。
`dylib.dlsym`通过符号从动态库中获得函数地址。
`dylib.dlcall`用于调用函数第一个参数是动态库函数的地址这是个特殊类型一定要保证这个参数是vm_obj类型并且type=obj_extern。
`dylib.dlclose`用于卸载动态库,当然,在这个函数调用之后,所有从该库中获取的函数都作废。
如果接下来你看到了这个运行结果,恭喜你!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
</details>
## __与andy解释器的不同之处__
### 1. 必须用`var`定义变量
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。
在Andy的解释器中:
```javascript
import("lib.nas");
foreach(i;[0,1,2,3])
print(i)
```
这个程序可以正常运行。然而这个`i`标识符实际上在这里是被第一次定义,而且没有使用`var`。我认为这样的设计很容易让使用者迷惑。他们可能都没有发现这里实际上是第一次定义`i`的地方。没有使用`var`的定义会让程序员认为这个`i`也许是在别的地方定义的。
所以在这个解释器中,我直接使用严格的语法检查方法来强行要求用户必须要使用`var`来定义新的变量或者迭代器。如果你忘了加这个关键字,那么你就会得到这个:
```javascript
[code] test.nas:2 undefined symbol "i".
foreach(i;[0,1,2,3])
[code] test.nas:3 undefined symbol "i".
print(i)
```
### 2. 默认不定长参数
这个解释器在运行时,函数不会将超出参数表的那部分不定长参数放到默认的`arg`中。所以你如果不定义`arg`就使用它,那你只会得到`undefined symbol`。
## __堆栈追踪信息__
当解释器崩溃时,它会反馈错误产生过程的堆栈追踪信息:
<details><summary>1. 内置函数die</summary>
`die`函数用于直接抛出错误并终止执行。
```javascript
func()
{
println("hello");
die("error occurred this line");
return;
}();
```
```javascript
hello
[vm] error: error occurred this line
[vm] native function error.
trace back:
0x000000ac: 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131)
0x000004f6: 3e 00 00 00 01 callfv 0x1 (a.nas:4)
0x000004fa: 3e 00 00 00 00 callfv 0x0 (a.nas:6)
vm stack(0x7fffcd21bc68<sp+80>, limit 10, total 12):
0x0000005b | null |
...
0x00000057 | str | <0x138ff60> error occurred t...
...
0x00000052 | nil |
```
</details>
<details><summary>2. 栈溢出</summary>
这是一个会导致栈溢出的例子:
```javascript
func(f){
return f(f);
}(
func(f){
f(f);
}
)();
```
```javascript
[vm] stack overflow
trace back:
0x000004fb: 3e 00 00 00 01 callfv 0x1 (a.nas:5)
0x000004fb: 1349 same call(s)
0x000004f3: 3e 00 00 00 01 callfv 0x1 (a.nas:2)
0x000004ff: 3e 00 00 00 01 callfv 0x1 (a.nas:3)
vm stack(0x7fffd3781d58<sp+80>, limit 10, total 8108):
0x00001ffb | func | <0x15f8d90> entry:0x4f9
0x00001ffa | func | <0x15f8d90> entry:0x4f9
0x00001ff9 | pc | 0x4fb
...
0x00001ff2 | addr | 0x7fffd37a16e8
```
</details>
<details><summary>3. 运行时错误</summary>
如果在执行的时候出现错误,程序会直接终止执行:
```javascript
func(){
return 0;
}()[1];
```
```javascript
[vm] callv: must call a vector/hash/string
trace back:
0x000004f4: 3b 00 00 00 00 callv 0x0 (a.nas:3)
vm stack(0x7fffff539c28<sp+80>, limit 10, total 1):
0x00000050 | num | 0
```
</details>
<details><summary>4. 详细的崩溃信息</summary>
使用命令 __`-d`__ 或 __`--detail`__ 后trace back信息会包含更多的细节内容:
```javascript
hello
[vm] error: error occurred this line
[vm] native function error.
trace back:
0x000000ac: 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131)
0x000004f6: 3e 00 00 00 01 callfv 0x1 (a.nas:4)
0x000004fa: 3e 00 00 00 00 callfv 0x0 (a.nas:6)
vm stack(0x7ffff42f3d08<sp+80>, limit 10, total 12):
0x0000005b | null |
0x0000005a | pc | 0x4f6
0x00000059 | addr | 0x7ffff42f3d18
...
0x00000052 | nil |
registers(main):
[ pc ] | pc | 0xac
[ global ] | addr | 0x7ffff42f3808
[ localr ] | addr | 0x7ffff42f3d68
[ memr ] | addr | 0x0
[ funcr ] | func | <0x18fbe50> entry:0xac
[ upvalr ] | nil |
[ canary ] | addr | 0x7ffff43137f8
[ top ] | addr | 0x7ffff42f3db8
global(0x7ffff42f3808<sp+0>):
0x00000000 | func | <0x18d62d0> entry:0x5
0x00000001 | func | <0x18d7e40> entry:0xc
...
0x0000004e | func | <0x18e6710> entry:0x4c2
0x0000004f | hash | <0x191f8b0> {5 val}
local(0x7ffff42f3d68<sp+86>):
0x00000000 | nil |
0x00000001 | str | <0x1932480> error occurred t...
```
</details>
## __调试器__
在`v8.0`版本中我们添加了调试器。
使用这个命令`./nasal -dbg xxx.nas`来启用调试器,接下来调试器会打开文件并输出以下内容:
```javascript
[debug] nasal debug mode
input 'h' to get help
source code:
--> var fib=func(x)
{
if(x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
next bytecode:
--> 0x00000000: 01 00 00 00 50 intg 0x50 (test/fib.nas:0)
0x00000001: 0b 00 00 00 05 newf 0x5 (./lib.nas:5)
0x00000002: 02 00 00 00 02 intl 0x2 (./lib.nas:5)
0x00000003: 0f 00 00 00 00 dyn 0x0 ("elems") (./lib.nas:5)
0x00000004: 32 00 00 00 07 jmp 0x7 (./lib.nas:5)
0x00000005: 40 00 00 00 00 callb 0x0 <__print@0x419400> (./lib.nas:6)
0x00000006: 4a 00 00 00 00 ret 0x0 (./lib.nas:6)
0x00000007: 03 00 00 00 00 loadg 0x0 (./lib.nas:5)
vm stack(0x7fffce09e6e8<sp+80>, limit 10, total 0)
>>
```
如果需要查看命令的使用方法,可以输入`h`获取帮助信息。
当运行调试器的时候,你可以看到现在的操作数栈上到底有些什么数据。
这些信息可以帮助你调试,同时也可以帮助你理解这个虚拟机是如何工作的:
```javascript
source code:
...
next bytecode:
...
vm stack(0x7fffce09e6e8<sp+80>, limit 10, total 7)
0x00000056 | pc | 0x533
0x00000055 | addr | 0x0
0x00000054 | nil |
0x00000053 | num | 0
0x00000052 | nil |
0x00000051 | nil |
0x00000050 | func | <0x166e000> entry:0x5
```

112
doc/benchmark.md Normal file
View File

@@ -0,0 +1,112 @@
# __Benchmark__
![benchmark](../pic/benchmark.png)
## version 6.5 (i5-8250U windows10 2021/6/19)
running time and gc time:
|file|call gc|total time|gc time|
|:----|:----|:----|:----|
|pi.nas|12000049|0.593s|0.222s|
|fib.nas|10573747|2.838s|0.187s|
|bp.nas|4419829|1.99s|0.18s|
|bigloop.nas|4000000|0.419s|0.039s|
|mandelbrot.nas|1044630|0.433s|0.041s|
|life.nas|817112|8.557s|0.199s|
|ascii-art.nas|45612|0.48s|0.027s|
|calc.nas|8089|0.068s|0.006s|
|quick_sort.nas|2768|0.107s|0s|
|bfs.nas|2471|1.763s|0.003s|
operands calling frequency:
|file|1st|2nd|3rd|4th|5th|
|:----|:----|:----|:----|:----|:----|
|pi.nas|callg|pop|mcallg|pnum|pone|
|fib.nas|calll|pnum|callg|less|jf|
|bp.nas|calll|callg|pop|callv|addeq|
|bigloop.nas|pnum|less|jf|callg|pone|
|mandelbrot.nas|callg|mult|loadg|pnum|pop|
|life.nas|calll|callv|pnum|jf|callg|
|ascii-art.nas|calll|pop|mcalll|callg|callb|
|calc.nas|calll|pop|pstr|mcalll|jmp|
|quick_sort.nas|calll|pop|jt|jf|less|
|bfs.nas|calll|pop|callv|mcalll|jf|
operands calling total times:
|file|1st|2nd|3rd|4th|5th|
|:----|:----|:----|:----|:----|:----|
|pi.nas|6000004|6000003|6000000|4000005|4000002|
|fib.nas|17622792|10573704|7049218|7049155|7049155|
|bp.nas|7081480|4227268|2764676|2617112|2065441|
|bigloop.nas|4000001|4000001|4000001|4000001|4000000|
|mandelbrot.nas|1519632|563856|290641|286795|284844|
|life.nas|2114371|974244|536413|534794|489743|
|ascii-art.nas|37906|22736|22402|18315|18292|
|calc.nas|191|124|109|99|87|
|quick_sort.nas|16226|5561|4144|3524|2833|
|bfs.nas|24707|16297|14606|14269|8672|
## version 7.0 (i5-8250U ubuntu-WSL on windows10 2021/6/29)
running time:
|file|total time|info|
|:----|:----|:----|
|pi.nas|0.15625s|great improvement|
|fib.nas|0.75s|great improvement|
|bp.nas|0.4218s(7162 epoch)|good improvement|
|bigloop.nas|0.09375s|great improvement|
|mandelbrot.nas|0.0312s|great improvement|
|life.nas|8.80s(windows) 1.25(ubuntu WSL)|little improvement|
|ascii-art.nas|0.015s|little improvement|
|calc.nas|0.0468s|little improvement|
|quick_sort.nas|0s|great improvement|
|bfs.nas|0.0156s|great improvement|
## version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)
running time:
|file|total time|info|
|:----|:----|:----|
|bf.nas|1100.19s||
|mandel.nas|28.98s||
|life.nas|0.56s|0.857s(windows)|
|ycombinator.nas|0.64s||
|fib.nas|0.28s||
|bfs.nas|0.156s|random result|
|pi.nas|0.0625s||
|bigloop.nas|0.047s||
|calc.nas|0.03125s|changed test file|
|mandelbrot.nas|0.0156s||
|ascii-art.nas|0s||
|quick_sort.nas|0s||
## version 9.0 (R9-5900HX ubuntu-WSL 2022/2/13)
running time:
|file|total time|info|
|:----|:----|:----|
|bf.nas|276.55s|great improvement|
|mandel.nas|28.16s||
|ycombinator.nas|0.59s||
|life.nas|0.2s|0.649s(windows)|
|fib.nas|0.234s|little improvement|
|bfs.nas|0.14s|random result|
|pi.nas|0.0625s||
|bigloop.nas|0.047s||
|calc.nas|0.0469s|changed test file|
|quick_sort.nas|0.016s|changed test file:100->1e4|
|mandelbrot.nas|0.0156s||
|ascii-art.nas|0s||
`bf.nas` is a very interesting test file that there is a brainfuck interpreter written in nasal.
And we use this bf interpreter to draw a mandelbrot set.
In 2022/2/17 update we added `\e` into the lexer. And the `bfcolored.nas` uses this special ASCII code. Here is the result:
![mandelbrot](../pic/mandelbrot.png)

668
doc/dev.md Normal file
View File

@@ -0,0 +1,668 @@
# __Development History__
## __Contents__
* [__Parser__](#parser)
* [v1.0](#version-10-parser-last-update-20191014)
* [__Abstract Syntax Tree__](#abstract-syntax-tree)
* [v1.2](#version-12-ast-last-update-20191031)
* [v2.0](#version-20-ast-last-update-2020831)
* [v3.0](#version-30-ast-last-update-20201023)
* [v5.0](#version-50-ast-last-update-202137)
* [__Bytecode VM__](#bytecode-virtual-machine)
* [v4.0](#version-40-vm-last-update-20201217)
* [v5.0](#version-50-vm-last-update-202137)
* [v6.0](#version-60-vm-last-update-202161)
* [v6.5](#version-65-vm-last-update-2021624)
* [v7.0](#version-70-vm-last-update-2021108)
* [v8.0](#version-80-vm-last-update-2022212)
* [v9.0](#version-90-vm-last-update-2022518)
* [v10.0](#version-100-vm-latest)
* [__Release Notes__](#release-notes)
## __Parser__
`LL(1)` parser with special check.
```javascript
(var a,b,c)=[{b:nil},[1,2],func return 0;];
(a.b,b[0],c)=(1,2,3);
```
These two expressions have the same first set,so `LL(1)` is useless for this language. We add some special checks in it.
Problems mentioned above have been solved for a long time, but recently i found a new problem here:
```javascript
var f=func(x,y,z){return x+y+z}
(a,b,c)=(0,1,2);
```
This will be recognized as this:
```javascript
var f=func(x,y,z){return x+y+z}(a,b,c)
=(0,1,2);
```
and causes fatal syntax error.
And i tried this program in flightgear nasal console.
It also found this is a syntax error.
I think this is a serious design fault.
To avoid this syntax error, change program like this, just add a semicolon:
```javascript
var f=func(x,y,z){return x+y+z};
^ here
(a,b,c)=(0,1,2);
```
### version 1.0 parser (last update 2019/10/14)
First fully functional version of nasal_parser.
Before version 1.0,i tried many times to create a correct parser.
Finally i learned `LL(1)` and `LL(k)` and wrote a parser for math formulas in version 0.16(last update 2019/9/14).
In version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)i was playing the parser happily and after that i wrote version 1.0.
__This project began at 2019/7/25__.
## __Abstract Syntax Tree__
### version 1.2 ast (last update 2019/10/31)
The ast has been completed in this version.
### version 2.0 ast (last update 2020/8/31)
A completed ast-interpreter with unfinished lib functions.
### version 3.0 ast (last update 2020/10/23)
The ast is refactored and is now easier to read and maintain.
Ast-interpreter uses new techniques so it can run codes more efficiently.
Now you can add your own functions as builtin-functions in this interpreter!
I decide to save the ast interpreter after releasing v4.0. Because it took me a long time to think and write...
### version 5.0 ast (last update 2021/3/7)
I change my mind.
AST interpreter leaves me too much things to do.
If i continue saving this interpreter,
it will be harder for me to make the bytecode vm become more efficient.
## __Bytecode Virtual Machine__
### version 4.0 vm (last update 2020/12/17)
I have just finished the first version of bytecode-interpreter.
This interpreter is still in test.
After this test, i will release version 4.0!
Now i am trying to search hidden bugs in this interpreter.
Hope you could help me! :)
There's an example of byte code below:
```javascript
for(var i=0;i<4000000;i+=1);
```
```x86asm
.number 0
.number 4e+006
.number 1
.symbol i
0x00000000: pzero 0x00000000
0x00000001: loadg 0x00000000 (i)
0x00000002: callg 0x00000000 (i)
0x00000003: pnum 0x00000001 (4e+006)
0x00000004: less 0x00000000
0x00000005: jf 0x0000000b
0x00000006: pone 0x00000000
0x00000007: mcallg 0x00000000 (i)
0x00000008: addeq 0x00000000
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000002
0x0000000b: nop 0x00000000
```
### version 5.0 vm (last update 2021/3/7)
I decide to optimize bytecode vm in this version.
Because it takes more than 1.5s to count i from `0` to `4000000-1`.This is not efficient at all!
2021/1/23 update: Now it can count from `0` to `4000000-1` in 1.5s.
### version 6.0 vm (last update 2021/6/1)
Use `loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll` to avoid branches.
Delete type `vm_scop`.
Use const `vm_num` to avoid frequently new & delete.
Change garbage collector from reference-counting to mark-sweep.
`vapp` and `newf` operand use .num to reduce the size of `exec_code`.
2021/4/3 update: Now it can count from `0` to `4e6-1` in 0.8s.
2021/4/19 update: Now it can count from `0` to `4e6-1` in 0.4s.
In this update i changed global and local scope from `unordered_map` to `vector`.
So the bytecode generator changed a lot.
```javascript
for(var i=0;i<4000000;i+=1);
```
```x86asm
.number 4e+006
0x00000000: intg 0x00000001
0x00000001: pzero 0x00000000
0x00000002: loadg 0x00000000
0x00000003: callg 0x00000000
0x00000004: pnum 0x00000000 (4e+006)
0x00000005: less 0x00000000
0x00000006: jf 0x0000000c
0x00000007: pone 0x00000000
0x00000008: mcallg 0x00000000
0x00000009: addeq 0x00000000
0x0000000a: pop 0x00000000
0x0000000b: jmp 0x00000003
0x0000000c: nop 0x00000000
```
### version 6.5 vm (last update 2021/6/24)
2021/5/31 update:
Now gc can collect garbage correctly without re-collecting,
which will cause fatal error.
Add `builtin_alloc` to avoid mark-sweep when running a built-in function,
which will mark useful items as useless garbage to collect.
Better use setsize and assignment to get a big array,
`append` is very slow in this situation.
2021/6/3 update:
Fixed a bug that gc still re-collects garbage,
this time i use three mark states to make sure garbage is ready to be collected.
Change `callf` to `callfv` and `callfh`.
And `callfv` fetches arguments from `val_stack` directly instead of using `vm_vec`,
a not very efficient way.
Better use `callfv` instead of `callfh`,
`callfh` will fetch a `vm_hash` from stack and parse it,
making this process slow.
```javascript
var f=func(x,y){return x+y;}
f(1024,2048);
```
```x86asm
.number 1024
.number 2048
.symbol x
.symbol y
0x00000000: intg 0x00000001
0x00000001: newf 0x00000007
0x00000002: intl 0x00000003
0x00000003: offset 0x00000001
0x00000004: para 0x00000000 (x)
0x00000005: para 0x00000001 (y)
0x00000006: jmp 0x0000000b
0x00000007: calll 0x00000001
0x00000008: calll 0x00000002
0x00000009: add 0x00000000
0x0000000a: ret 0x00000000
0x0000000b: loadg 0x00000000
0x0000000c: callg 0x00000000
0x0000000d: pnum 0x00000000 (1024)
0x0000000e: pnum 0x00000001 (2048)
0x0000000f: callfv 0x00000002
0x00000010: pop 0x00000000
0x00000011: nop 0x00000000
```
2021/6/21 update: Now gc will not collect nullptr.
And the function of assignment is complete,
now these kinds of assignment is allowed:
```javascript
var f=func()
{
var _=[{_:0},{_:1}];
return func(x)
{
return _[x];
}
}
var m=f();
m(0)._=m(1)._=10;
[0,1,2][1:2][0]=0;
```
In the old version,
parser will check this left-value and tells that these kinds of left-value are not allowed(bad lvalue).
But now it can work.
And you could see its use by reading the code above.
To make sure this assignment works correctly,
codegen will generate byte code by `nasal_codegen::call_gen()` instead of `nasal_codegen::mcall_gen()`,
and the last child of the ast will be generated by `nasal_codegen::mcall_gen()`.
So the bytecode is totally different now:
```x86asm
.number 10
.number 2
.symbol _
.symbol x
0x00000000: intg 0x00000002
0x00000001: newf 0x00000005
0x00000002: intl 0x00000002
0x00000003: offset 0x00000001
0x00000004: jmp 0x00000017
0x00000005: newh 0x00000000
0x00000006: pzero 0x00000000
0x00000007: happ 0x00000000 (_)
0x00000008: newh 0x00000000
0x00000009: pone 0x00000000
0x0000000a: happ 0x00000000 (_)
0x0000000b: newv 0x00000002
0x0000000c: loadl 0x00000001
0x0000000d: newf 0x00000012
0x0000000e: intl 0x00000003
0x0000000f: offset 0x00000002
0x00000010: para 0x00000001 (x)
0x00000011: jmp 0x00000016
0x00000012: calll 0x00000001
0x00000013: calll 0x00000002
0x00000014: callv 0x00000000
0x00000015: ret 0x00000000
0x00000016: ret 0x00000000
0x00000017: loadg 0x00000000
0x00000018: callg 0x00000000
0x00000019: callfv 0x00000000
0x0000001a: loadg 0x00000001
0x0000001b: pnum 0x00000000 (10.000000)
0x0000001c: callg 0x00000001
0x0000001d: pone 0x00000000
0x0000001e: callfv 0x00000001
0x0000001f: mcallh 0x00000000 (_)
0x00000020: meq 0x00000000
0x00000021: callg 0x00000001
0x00000022: pzero 0x00000000
0x00000023: callfv 0x00000001
0x00000024: mcallh 0x00000000 (_)
0x00000025: meq 0x00000000
0x00000026: pop 0x00000000
0x00000027: pzero 0x00000000
0x00000028: pzero 0x00000000
0x00000029: pone 0x00000000
0x0000002a: pnum 0x00000001 (2.000000)
0x0000002b: newv 0x00000003
0x0000002c: slcbeg 0x00000000
0x0000002d: pone 0x00000000
0x0000002e: pnum 0x00000001 (2.000000)
0x0000002f: slc2 0x00000000
0x00000030: slcend 0x00000000
0x00000031: pzero 0x00000000
0x00000032: mcallv 0x00000000
0x00000033: meq 0x00000000
0x00000034: pop 0x00000000
0x00000035: nop 0x00000000
```
As you could see from the bytecode above,
`mcall`/`mcallv`/`mcallh` operands' using frequency will reduce,
`call`/`callv`/`callh`/`callfv`/`callfh` at the opposite.
And because of the new structure of `mcall`,
`addr_stack`, a stack used to store the memory address,
is deleted from `nasal_vm`,
and now `nasal_vm` use `nasal_val** mem_addr` to store the memory address.
This will not cause fatal errors because the memory address is used __immediately__ after getting it.
### version 7.0 vm (last update 2021/10/8)
2021/6/26 update:
Instruction dispatch is changed from call-threading to computed-goto(with inline function).
After changing the way of instruction dispatch,
there is a great improvement in nasal_vm.
Now vm can run test/bigloop and test/pi in 0.2s!
And vm runs test/fib in 0.8s on linux.
You could see the time use data below,
in Test data section.
This version uses g++ extension "labels as values",
which is also supported by clang++.
(But i don't know if MSVC supports this)
There is also a change in nasal_gc:
`std::vector` global is deleted,
now the global values are all stored on stack(from `val_stack+0` to `val_stack+intg-1`).
2021/6/29 update:
Add some instructions that execute const values:
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`.
Now the bytecode of test/bigloop.nas seems like this:
```x86asm
.number 4e+006
.number 1
0x00000000: intg 0x00000001
0x00000001: pzero 0x00000000
0x00000002: loadg 0x00000000
0x00000003: callg 0x00000000
0x00000004: pnum 0x00000000 (4000000)
0x00000005: less 0x00000000
0x00000006: jf 0x0000000b
0x00000007: mcallg 0x00000000
0x00000008: addeqc 0x00000001 (1)
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000003
0x0000000b: nop 0x00000000
```
And this test file runs in 0.1s after this update.
Most of the calculations are accelerated.
Also, assignment bytecode has changed a lot.
Now the first identifier that called in assignment will use `op_load` to assign,
instead of `op_meq`,`op_pop`.
```javascript
var (a,b)=(1,2);
a=b=0;
```
```x86asm
.number 2
0x00000000: intg 0x00000002
0x00000001: pone 0x00000000
0x00000002: loadg 0x00000000
0x00000003: pnum 0x00000000 (2)
0x00000004: loadg 0x00000001
0x00000005: pzero 0x00000000
0x00000006: mcallg 0x00000001
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
0x00000008: loadg 0x00000000 (a=b use loadg)
0x00000009: nop 0x00000000
```
### version 8.0 vm (last update 2022/2/12)
2021/10/8 update:
In this version vm_nil and vm_num now is not managed by `nasal_gc`,
this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
New value type is added: `vm_obj`.
This type is reserved for user to define their own value types.
Related API will be added in the future.
Fully functional closure:
Add new operands that get and set upvalues.
Delete an old operand `op_offset`.
2021/10/13 update:
The format of output information of bytecodes changes to this:
```x86asm
0x000002f2: newf 0x2f6
0x000002f3: intl 0x2
0x000002f4: para 0x3e ("x")
0x000002f5: jmp 0x309
0x000002f6: calll 0x1
0x000002f7: lessc 0x0 (2)
0x000002f8: jf 0x2fb
0x000002f9: calll 0x1
0x000002fa: ret
0x000002fb: upval 0x0[0x1]
0x000002fc: upval 0x0[0x1]
0x000002fd: callfv 0x1
0x000002fe: calll 0x1
0x000002ff: subc 0x1d (1)
0x00000300: callfv 0x1
0x00000301: upval 0x0[0x1]
0x00000302: upval 0x0[0x1]
0x00000303: callfv 0x1
0x00000304: calll 0x1
0x00000305: subc 0x0 (2)
0x00000306: callfv 0x1
0x00000307: add
0x00000308: ret
0x00000309: ret
0x0000030a: callfv 0x1
0x0000030b: loadg 0x32
```
2022/1/22 update:
Delete `op_pone` and `op_pzero`.
Both of them are meaningless and will be replaced by `op_pnum`.
### version 9.0 vm (last update 2022/5/18)
2022/2/12 update:
Local values now are __stored on stack__.
So function calling will be faster than before.
Because in v8.0 when calling a function,
new `vm_vec` will be allocated by `nasal_gc`, this makes gc doing mark-sweep too many times and spends a quite lot of time.
In test file `test/bf.nas`, it takes too much time to test the file because this file has too many function calls(see test data below in table `version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`).
Upvalue now is generated when creating first new function in the local scope, using `vm_vec`.
And after that when creating new functions, they share the same upvalue, and the upvalue will synchronize with the local scope each time creating a new function.
2022/3/27 update:
In this month's updates we change upvalue from `vm_vec` to `vm_upval`,
a special gc-managed object,
which has almost the same structure of that upvalue object in another programming language __`Lua`__.
Today we change the output format of bytecode.
New output format looks like `objdump`:
```x86asm
0x0000029b: 0a 00 00 00 00 newh
func <0x29c>:
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
0x0000029d: 02 00 00 00 02 intl 0x2
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
0x000002a1: 4a 00 00 00 00 ret
<0x29c>;
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
func <0x2a3>:
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
0x000002a4: 02 00 00 00 03 intl 0x3
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
0x000002a7: 32 00 00 02 aa jmp 0x2aa
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
0x000002a9: 4a 00 00 00 00 ret
<0x2a3>;
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
```
### version 10.0 vm (latest)
2022/5/19 update:
Now we add coroutine in this runtime:
```javascript
var coroutine={
create: func(function){return __cocreate;},
resume: func(co) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
};
```
`coroutine.create` is used to create a new coroutine object using a function.
But this coroutine will not run immediately.
`coroutine.resume` is used to continue running a coroutine.
`coroutine.yield` is used to interrupt the running of a coroutine and throw some values.
These values will be accepted and returned by `coroutine.resume`.
And `coroutine.yield` it self returns `vm_nil` in the coroutine function.
`coroutine.status` is used to see the status of a coroutine.
There are 3 types of status:`suspended` means waiting for running,`running` means is running,`dead` means finished running.
`coroutine.running` is used to judge if there is a coroutine running now.
__CAUTION:__ coroutine should not be created or running inside another coroutine.
__We will explain how resume and yield work here:__
When `op_callb` is called, the stack frame is like this:
```C++
+--------------------------+(main stack)
| old pc(vm_ret) | <- top[0]
+--------------------------+
| old localr(vm_addr) | <- top[-1]
+--------------------------+
| old upvalr(vm_upval) | <- top[-2]
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
In `op_callb`'s progress, next step the stack frame is:
```C++
+--------------------------+(main stack)
| nil(vm_nil) | <- push nil
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
Then we call `resume`, this function will change stack.
As we can see, coroutine stack already has some values on it,
but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`.
So for safe running, `resume` will return `gc.top[0]`.
`op_callb` will do `top[0]=resume()`, so the value does not change.
```C++
+--------------------------+(coroutine stack)
| pc:0(vm_ret) | <- now gc.top[0]
+--------------------------+
```
When we call `yield`, the function will do like this.
And we find that `op_callb` has put the `nil` at the top.
but where is the returned `local[1]` sent?
```C++
+--------------------------+(coroutine stack)
| nil(vm_nil) | <- push nil
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
When `builtin_coyield` is finished, the stack is set to main stack,
and the returned `local[1]` in fact is set to the top of the main stack by `op_callb`:
```C++
+--------------------------+(main stack)
| return_value(nas_ref) |
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
so the main progress feels the value on the top is the returned value of `resume`.
but in fact the `resume`'s returned value is set on coroutine stack.
so we conclude this:
```C++
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
yield (coroutine->main) return a vector. main.top[0] = vector;
```
## __Release Notes__
### __version 8.0 release__
I made a __big mistake__ in `v8.0` release:
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
this will cause incorrect `stackoverflow` error.
please change it to:
`canary=gc.stack+STACK_MAX_DEPTH-1;`
If do not change this line, only the debugger runs abnormally. this bug is fixed in `v9.0`.
Another bug is that in `nasal_err.h:class nasal_err`, we should add a constructor for this class:
```C++
nasal_err():error(0){}
```
This bug is fixed in `v9.0`. So we suggest that do not use `v8.0`.

601
doc/dev_zh.md Normal file
View File

@@ -0,0 +1,601 @@
# __开发历史记录__
## __目录__
* [__语法分析__](#语法分析)
* [v1.0](#version-10-parser-last-update-20191014)
* [__抽象语法树__](#抽象语法树)
* [v1.2](#version-12-ast-last-update-20191031)
* [v2.0](#version-20-ast-last-update-2020831)
* [v3.0](#version-30-ast-last-update-20201023)
* [v5.0](#version-50-ast-last-update-202137)
* [__字节码虚拟机__](#字节码虚拟机)
* [v4.0](#version-40-vm-last-update-20201217)
* [v5.0](#version-50-vm-last-update-202137)
* [v6.0](#version-60-vm-last-update-202161)
* [v6.5](#version-65-vm-last-update-2021624)
* [v7.0](#version-70-vm-last-update-2021108)
* [v8.0](#version-80-vm-last-update-2022212)
* [v9.0](#version-90-vm-last-update-2022518)
* [v10.0](#version-100-vm-latest)
* [__发行日志__](#发行日志)
## __语法分析__
有特殊语法检查的`LL(1)`语法分析器。
```javascript
(var a,b,c)=[{b:nil},[1,2],func return 0;];
(a.b,b[0],c)=(1,2,3);
```
这两个表达式有同一个first集所以纯粹的`LL(1)`很难实现这个语言的语法分析。所以我们为其添加了特殊语法检查机制。本质上还是`LL(1)`的内核。
上面这个问题已经解决很久了,不过我最近发现了一个新的语法问题:
```javascript
var f=func(x,y,z){return x+y+z}
(a,b,c)=(0,1,2);
```
这种写法会被错误识别合并成下面这种:
```javascript
var f=func(x,y,z){return x+y+z}(a,b,c)
=(0,1,2);
```
语法分析器会认为这是个严重的语法错误。我在Flightgear中也测试了这个代码它内置的语法分析器也认为这是错误语法。当然我认为这是语法设计中的一个比较严重的缺漏。为了避免这个语法问题只需要添加一个分号就可以了:
```javascript
var f=func(x,y,z){return x+y+z};
^ 就是这里
(a,b,c)=(0,1,2);
```
### version 1.0 parser (last update 2019/10/14)
第一版功能完备的nasal语法分析器完成了。
在version 1.0之前,我多次尝试构建一个正确的语法分析器但总是存在一些问题。
最终我学习了`LL(1)``LL(k)`文法并且在version 0.16(last update 2019/9/14)中完成了一个能识别数学算式的语法分析器。
在version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)中我只是抱着玩的心态在测试语法分析器不过在那之后我还是完成了version 1.0的语法分析器。
__该项目于2019/7/25正式开始__
## __抽象语法树__
### version 1.2 ast (last update 2019/10/31)
抽象语法树在这个版本初步完成。
### version 2.0 ast (last update 2020/8/31)
在这个版本我们基于抽象语法树实现了一个树解释器,并且完成了部分内置函数。
### version 3.0 ast (last update 2020/10/23)
我们重构了抽象语法树的代码,现在可以更容易地读懂代码并进行维护。
这个版本的树解释器用了新的优化方式,所以可以更高效地执行代码。
在这个版本用户已经可以自行添加内置函数。
我想在v4.0发布之后仍然保留这个树解释器,毕竟花了很长时间才写完这坨屎。
### version 5.0 ast (last update 2021/3/7)
我改变想法了,树解释器给维护带来了太大的麻烦。如果想继续保留这个解释器,那么为了兼容性,字节码虚拟机的优化工作会更难推进。
## __字节码虚拟机__
### version 4.0 vm (last update 2020/12/17)
我在这个版本实现了第一版字节码虚拟机。不过这个虚拟机仍然在测试中在这次测试结束之后我会发布v4.0发行版。
现在我在找一些隐藏很深的bug。如果有人想帮忙的话非常欢迎:)
下面是生成的字节码的样例:
```javascript
for(var i=0;i<4000000;i+=1);
```
```x86asm
.number 0
.number 4e+006
.number 1
.symbol i
0x00000000: pzero 0x00000000
0x00000001: loadg 0x00000000 (i)
0x00000002: callg 0x00000000 (i)
0x00000003: pnum 0x00000001 (4e+006)
0x00000004: less 0x00000000
0x00000005: jf 0x0000000b
0x00000006: pone 0x00000000
0x00000007: mcallg 0x00000000 (i)
0x00000008: addeq 0x00000000
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000002
0x0000000b: nop 0x00000000
```
### version 5.0 vm (last update 2021/3/7)
从这个版本起,我决定持续优化字节码虚拟机。
毕竟现在这玩意从`0`数到`4000000-1`要花费1.5秒。这效率完全不能忍。
2021/1/23 update: 现在它确实可以在1.5秒内从`0`数到`4000000-1`了。
### version 6.0 vm (last update 2021/6/1)
使用`loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll`指令来减少分支语句的调用。
删除了`vm_scop`类型。
添加作为常量的`vm_num`来减少内存分配的开销。
将垃圾收集器从引用计数改为了标记清理。
`vapp``newf`开始使用先前未被使用的.num段来压缩字节码生成数量减少生成的`exec_code`的大小。
2021/4/3 update: 从`0`数到`4e6-1`只需要不到0.8秒了。
2021/4/19 update: 从`0`数到`4e6-1`只需要不到0.4秒了。
在这次的更新中,我把全局变量和局部变量的存储结构从`unordered_map`变为了`vector`,从而提升执行效率。所以现在生成的字节码大变样了。
```javascript
for(var i=0;i<4000000;i+=1);
```
```x86asm
.number 4e+006
0x00000000: intg 0x00000001
0x00000001: pzero 0x00000000
0x00000002: loadg 0x00000000
0x00000003: callg 0x00000000
0x00000004: pnum 0x00000000 (4e+006)
0x00000005: less 0x00000000
0x00000006: jf 0x0000000c
0x00000007: pone 0x00000000
0x00000008: mcallg 0x00000000
0x00000009: addeq 0x00000000
0x0000000a: pop 0x00000000
0x0000000b: jmp 0x00000003
0x0000000c: nop 0x00000000
```
### version 6.5 vm (last update 2021/6/24)
2021/5/31 update:
现在垃圾收集器不会错误地重复收集未使用变量了。
添加了`builtin_alloc`以防止在运行内置函数的时候错误触发标记清除。
建议在获取大空间数组的时候尽量使用setsize因为`append`在被频繁调用时可能会频繁触发垃圾收集器。
2021/6/3 update:
修复了垃圾收集器还是他妈的会重复收集的bug这次我设计了三个标记状态来保证垃圾是被正确收集了。
`callf`指令拆分为`callfv``callfh`。并且`callfv`将直接从`val_stack`获取传参,而不是先通过一个`vm_vec`把参数收集起来再传入,后者是非常低效的做法。
建议更多使用`callfv`而不是`callfh`,因为`callfh`只能从栈上获取参数并整合为`vm_hash`之后才能传给该指令进行处理,拖慢执行速度。
```javascript
var f=func(x,y){return x+y;}
f(1024,2048);
```
```x86asm
.number 1024
.number 2048
.symbol x
.symbol y
0x00000000: intg 0x00000001
0x00000001: newf 0x00000007
0x00000002: intl 0x00000003
0x00000003: offset 0x00000001
0x00000004: para 0x00000000 (x)
0x00000005: para 0x00000001 (y)
0x00000006: jmp 0x0000000b
0x00000007: calll 0x00000001
0x00000008: calll 0x00000002
0x00000009: add 0x00000000
0x0000000a: ret 0x00000000
0x0000000b: loadg 0x00000000
0x0000000c: callg 0x00000000
0x0000000d: pnum 0x00000000 (1024)
0x0000000e: pnum 0x00000001 (2048)
0x0000000f: callfv 0x00000002
0x00000010: pop 0x00000000
0x00000011: nop 0x00000000
```
2021/6/21 update:
现在垃圾收集器不会收集空指针了。并且调用链中含有函数调用的赋值语句现在也可以执行了,下面这些赋值方式是合法的:
```javascript
var f=func()
{
var _=[{_:0},{_:1}];
return func(x)
{
return _[x];
}
}
var m=f();
m(0)._=m(1)._=10;
[0,1,2][1:2][0]=0;
```
在老版本中,语法分析器会检查左值,并且在检测到有特别调用的情况下直接告知用户这种左值是不被接受的(bad lvalue)。但是现在它可以正常运作了。为了保证这种赋值语句能正常执行codegen模块会优先使用`nasal_codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `nasal_codegen::mcall_gen()`,在最后一个调用处才会使用`nasal_codegen::mcall_gen()`
所以现在生成的相关字节码也完全不同了:
```x86asm
.number 10
.number 2
.symbol _
.symbol x
0x00000000: intg 0x00000002
0x00000001: newf 0x00000005
0x00000002: intl 0x00000002
0x00000003: offset 0x00000001
0x00000004: jmp 0x00000017
0x00000005: newh 0x00000000
0x00000006: pzero 0x00000000
0x00000007: happ 0x00000000 (_)
0x00000008: newh 0x00000000
0x00000009: pone 0x00000000
0x0000000a: happ 0x00000000 (_)
0x0000000b: newv 0x00000002
0x0000000c: loadl 0x00000001
0x0000000d: newf 0x00000012
0x0000000e: intl 0x00000003
0x0000000f: offset 0x00000002
0x00000010: para 0x00000001 (x)
0x00000011: jmp 0x00000016
0x00000012: calll 0x00000001
0x00000013: calll 0x00000002
0x00000014: callv 0x00000000
0x00000015: ret 0x00000000
0x00000016: ret 0x00000000
0x00000017: loadg 0x00000000
0x00000018: callg 0x00000000
0x00000019: callfv 0x00000000
0x0000001a: loadg 0x00000001
0x0000001b: pnum 0x00000000 (10.000000)
0x0000001c: callg 0x00000001
0x0000001d: pone 0x00000000
0x0000001e: callfv 0x00000001
0x0000001f: mcallh 0x00000000 (_)
0x00000020: meq 0x00000000
0x00000021: callg 0x00000001
0x00000022: pzero 0x00000000
0x00000023: callfv 0x00000001
0x00000024: mcallh 0x00000000 (_)
0x00000025: meq 0x00000000
0x00000026: pop 0x00000000
0x00000027: pzero 0x00000000
0x00000028: pzero 0x00000000
0x00000029: pone 0x00000000
0x0000002a: pnum 0x00000001 (2.000000)
0x0000002b: newv 0x00000003
0x0000002c: slcbeg 0x00000000
0x0000002d: pone 0x00000000
0x0000002e: pnum 0x00000001 (2.000000)
0x0000002f: slc2 0x00000000
0x00000030: slcend 0x00000000
0x00000031: pzero 0x00000000
0x00000032: mcallv 0x00000000
0x00000033: meq 0x00000000
0x00000034: pop 0x00000000
0x00000035: nop 0x00000000
```
从上面这些字节码可以看出,`mcall`/`mcallv`/`mcallh`指令的使用频率比以前减小了一些,而`call`/`callv`/`callh`/`callfv`/`callfh`则相反。
并且因为新的数据结构,`mcall`指令以及`addr_stack`,一个曾用来存储指针的栈,从`nasal_vm`中被移除。现在`nasal_vm`使用`nasal_val** mem_addr`来暂存获取的内存地址。这不会导致严重的问题,因为内存空间是 __获取即使用__ 的。
### version 7.0 vm (last update 2021/10/8)
2021/6/26 update:
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后nasal_vm的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完test/bigloop和test/pi并且在linux平台虚拟机可以在0.8秒内执行完test/fib。你可以在下面的测试数据部分看到测试的结果。
这个分派方式使用了g++扩展"labels as values"clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了哈哈)
nasal_gc中也有部分改动:
全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0``val_stack+intg-1`)。
2021/6/29 update:
添加了一些直接用常量进行运算的指令:
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`
现在test/bigloop.nas的字节码是这样的:
```x86asm
.number 4e+006
.number 1
0x00000000: intg 0x00000001
0x00000001: pzero 0x00000000
0x00000002: loadg 0x00000000
0x00000003: callg 0x00000000
0x00000004: pnum 0x00000000 (4000000)
0x00000005: less 0x00000000
0x00000006: jf 0x0000000b
0x00000007: mcallg 0x00000000
0x00000008: addeqc 0x00000001 (1)
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000003
0x0000000b: nop 0x00000000
```
在这次更新之后这个测试文件可以在0.1秒内运行结束。大多数的运算操作速度都有提升。
并且赋值相关的字节码也有一些改动。现在赋值语句只包含一个标识符时,会优先调用`op_load`来赋值,而不是使用`op_meq``op_pop`
```javascript
var (a,b)=(1,2);
a=b=0;
```
```x86asm
.number 2
0x00000000: intg 0x00000002
0x00000001: pone 0x00000000
0x00000002: loadg 0x00000000
0x00000003: pnum 0x00000000 (2)
0x00000004: loadg 0x00000001
0x00000005: pzero 0x00000000
0x00000006: mcallg 0x00000001
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
0x00000008: loadg 0x00000000 (a=b use loadg)
0x00000009: nop 0x00000000
```
### version 8.0 vm (last update 2022/2/12)
2021/10/8 update:
从这个版本开始`vm_nil``vm_num`不再由`nasal_gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。
功能完备的闭包:添加了读写闭包数据的指令。删除了老的指令`op_offset`
2021/10/13 update:
字节码信息输出格式修改为如下形式:
```x86asm
0x000002f2: newf 0x2f6
0x000002f3: intl 0x2
0x000002f4: para 0x3e ("x")
0x000002f5: jmp 0x309
0x000002f6: calll 0x1
0x000002f7: lessc 0x0 (2)
0x000002f8: jf 0x2fb
0x000002f9: calll 0x1
0x000002fa: ret
0x000002fb: upval 0x0[0x1]
0x000002fc: upval 0x0[0x1]
0x000002fd: callfv 0x1
0x000002fe: calll 0x1
0x000002ff: subc 0x1d (1)
0x00000300: callfv 0x1
0x00000301: upval 0x0[0x1]
0x00000302: upval 0x0[0x1]
0x00000303: callfv 0x1
0x00000304: calll 0x1
0x00000305: subc 0x0 (2)
0x00000306: callfv 0x1
0x00000307: add
0x00000308: ret
0x00000309: ret
0x0000030a: callfv 0x1
0x0000030b: loadg 0x32
```
2022/1/22 update:
删除`op_pone``op_pzero`。这两个指令在目前已经没有实际意义,并且已经被`op_pnum`替代。
### version 9.0 vm (last update 2022/5/18)
2022/2/12 update:
局部变量现在也被 __存储在栈上__
所以函数调用比以前也会快速很多。
在v8.0如果你想调用一个函数,
新的`vm_vec`将被分配出来用于模拟局部作用域,这个操作会导致标记清除过程会被频繁触发并且浪费太多的执行时间。
在测试文件`test/bf.nas`中,这种调用方式使得大部分时间都被浪费了,因为这个测试文件包含大量且频繁的函数调用(详细数据请看测试数据一节中`version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`)。
现在闭包会在第一次在局部作用域创建新函数的时候产生,使用`vm_vec`
在那之后如果再创建新的函数,则他们会共享同一个闭包,这些闭包会在每次于局部作用域创建新函数时同步。
2022/3/27 update:
在这个月的更新中我们把闭包的数据结构从`vm_vec`换成了一个新的对象`vm_upval`,这种类型有着和另外一款编程语言 __`Lua`__ 中闭包相类似的结构。
同时我们也修改了字节码的输出格式。新的格式看起来像是 `objdump`:
```x86asm
0x0000029b: 0a 00 00 00 00 newh
func <0x29c>:
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
0x0000029d: 02 00 00 00 02 intl 0x2
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
0x000002a1: 4a 00 00 00 00 ret
<0x29c>;
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
func <0x2a3>:
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
0x000002a4: 02 00 00 00 03 intl 0x3
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
0x000002a7: 32 00 00 02 aa jmp 0x2aa
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
0x000002a9: 4a 00 00 00 00 ret
<0x2a3>;
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
```
### version 10.0 vm (latest)
2022/5/19 update:
在这个版本中我们给nasal加入了协程:
```javascript
var coroutine={
create: func(function){return __cocreate;},
resume: func(co) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
};
```
`coroutine.create`用于创建新的协程对象。不过创建之后协程并不会直接运行。
`coroutine.resume`用于继续运行一个协程。
`coroutine.yield`用于中断一个协程的运行过程并且抛出一些数据。这些数据会被`coroutine.resume`接收并返回。而在协程函数中`coroutine.yield`本身只返回`vm_nil`
`coroutine.status`用于查看协程的状态。协程有三种不同的状态:`suspended`挂起,`running`运行中,`dead`结束运行。
`coroutine.running`用于判断当前是否有协程正在运行。
__注意:__ 协程不能在其他正在运行的协程中创建。
__接下来我们解释这个协程的运行原理:__
`op_callb`被执行时,栈帧如下所示:
```C++
+--------------------------+(主操作数栈)
| old pc(vm_ret) | <- top[0]
+--------------------------+
| old localr(vm_addr) | <- top[-1]
+--------------------------+
| old upvalr(vm_upval) | <- top[-2]
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
在`op_callb`执行过程中,下一步的栈帧如下:
```C++
+--------------------------+(主操作数栈)
| nil(vm_nil) | <- push nil
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
接着我们调用`resume`,这个函数会替换操作数栈。我们会看到,协程的操作数栈上已经保存了一些数据,但是我们首次进入协程执行时,这个操作数栈的栈顶将会是`vm_ret`,并且返回的`pc`值是`0`。
为了保证栈顶的数据不会被破坏,`resume`会返回`gc.top[0]`。`op_callb`将会执行`top[0]=resume()`,所以栈顶的数据虽然被覆盖了一次,但是实际上还是原来的数据。
```C++
+--------------------------+(协程操作数栈)
| pc:0(vm_ret) | <- now gc.top[0]
+--------------------------+
```
当我们调用`yield`的时候,该函数会执行出这个情况,我们发现`op_callb` 已经把`nil`放在的栈顶。但是应该返回的`local[1]`到底发送到哪里去了?
```C++
+--------------------------+(协程操作数栈)
| nil(vm_nil) | <- push nil
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
当`builtin_coyield`执行完毕之后,栈又切换到了主操作数栈上,这时可以看到返回的`local[1]`实际上被`op_callb`放在了这里的栈顶:
```C++
+--------------------------+(主操作数栈)
| return_value(nas_ref) |
+--------------------------+
| old pc(vm_ret) |
+--------------------------+
| old localr(vm_addr) |
+--------------------------+
| old upvalr(vm_upval) |
+--------------------------+
| local scope(nas_ref) |
| ... |
+--------------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+--------------------------+
```
所以主程序会认为顶部这个返回值好像是`resume`返回的。而实际上`resume`的返回值在协程的操作数栈顶。综上所述:
```C++
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
yield (coroutine->main) return a vector. main.top[0] = vector;
```
## __发行日志__
### __version 8.0 release__
这个版本的发行版有个 __严重的问题__:
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
这个会导致不正确的`stackoverflow`报错。因为它覆盖了原有的变量。
请修改为:
`canary=gc.stack+STACK_MAX_DEPTH-1;`
如果不修改这一行,调试器运行肯定是不正常的。在`v9.0`第一个commit中我们修复了这个问题。
另外一个bug在 `nasal_err.h:class nasal_err`这边,要给这个类添加一个构造函数来进行初始化,否则会出问题:
```C++
nasal_err():error(0){}
```
同样这个也在`v9.0`中修复了。所以我们建议不要使用`v8.0`。

View File

@@ -0,0 +1,162 @@
<!DOCTYPE html>
<head>
<title> nasal-http-test-web </title>
<meta charset="utf-8">
<meta author="ValKmjolnir">
<style>
body{
background: white;
width: 60%;
}
pre{
background: #303030;
font-family: 'Courier New', Courier, monospace;
font-size: small;
color: #d4d4d4;
}
h1,h2,h3{
padding: 5px;
background-color: #555588;
font-family: Arial, Helvetica, sans-serif;
color: white;
}
p{
margin-left: 15px;
}
div.badges{
text-align: center;
}
tr{
vertical-align: top;
}
</style>
</head>
<body>
<h1>&nbsp;Nasal | Not another scripting language!</h1>
<div class="badges">
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a>
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github"><br/></img></a>
</div>
<h2>&nbsp;Introduction | 介绍</h2>
<text>
<p>
Hello, this is a simple HTML document just for test. This simple http server is written in nasal.
Nasal is an ECMAscript-like programming language that used in Flightgear designed by Andy Ross.<br/>
</p>
<p>
这是个用于测试的简易HTML文档。该http服务器是用nasal编写的。Nasal是一款用于飞行模拟器Flightgear的、类似ECMAscript的编程语言。该语言由Andy Ross设计完成。
</p>
<p>
The interpreter is totally rewritten by <a href="https://github.com/ValKmjolnir">ValKmjolnir</a> using C++(-std=c++11) without reusing the code in Andy Ross's nasal interpreter.
But we really appreciate that Andy created this amazing programming language and his interpreter project.
Now this project uses <a href="/license">MIT license</a> (2021/5/4). Edit it if you want, use this project to learn or create more interesting things(But don't forget me XD).<br/>
</p>
<p>
这个解释器是由<a href="https://github.com/ValKmjolnir">ValKmjolnir</a>用C++11编写的完全没有复用Andy Ross版解释器的代码。但是我们仍然非常感谢Andy为我们带来了这么一款有趣的编程语言。
现在这个项目使用<a href="/license">MIT协议</a>开源 (2021/5/4)。
如果你有需求,可以自行修改,用这个解释器项目来学习或者创造更有意思的东西吧!(不过别忘了在项目协议中留下原作者名字哦)
</p>
</text>
<h2>&nbsp;Benchmark | 执行效率</h2>
<img src="/pic/benchmark.png" width="450" height="350"></img>
<img src="/pic/mandelbrot.png" width="450" height="350"><br /></img>
<text>
<p>
Benchmark of different versions of nasal interpreter(left).
Beautiful picture generated by brainfuck interpreter written in nasal(right).
</p>
<p>
不同版本的nasal解释器执行效率图(左)。
nasal运行brainfuck绘制的曼德勃罗集合(右)。
</p>
<p>
Nasal can run this test file(test/bfcolored.nas) to draw this picture in about 220 seconds.
In fact this test file cost over 2200 seconds before ver 8.0 .
</p>
<p>
Nasal现在可以在220秒内运行该文件(test/bfcolored.nas)并绘制出这张图。
在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。
</p>
</text>
<h2>&nbsp;Example | 样例代码</h2>
<form method="get">
<text style="margin-left: 15px;"> </text>
<input type="text" name="filename" value="ascii-art.nas">
<input type="submit" value="search">
</form>
<table>
<tr>
<td>
<ul>
<li><a href="/ascii-art.nas">ascii-art.nas</a></li>
<li><a href="/auto_crash.nas">auto_crash.nas</a></li>
<li><a href="/bf.nas">bf.nas</a></li>
<li><a href="/bfcolored.nas">bfcolored.nas</a></li>
<li><a href="/bfconvertor.nas">bfconvertor.nas</a></li>
<li><a href="/bfs.nas">bfs.nas</a></li>
<li><a href="/bigloop.nas">bigloop.nas</a></li>
<li><a href="/bp.nas">bp.nas</a></li>
<li><a href="/calc.nas">calc.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/choice.nas">choice.nas</a></li>
<li><a href="/class.nas">class.nas</a></li>
<li><a href="/coroutine.nas">coroutine.nas</a></li>
<li><a href="/diff.nas">diff.nas</a></li>
<li><a href="/exception.nas">exception.nas</a></li>
<li><a href="/fib.nas">fib.nas</a></li>
<li><a href="/filesystem.nas">filesystem.nas</a></li>
<li><a href="/hexdump.nas">hexdump.nas</a></li>
<li><a href="/httptest.nas">httptest.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/json.nas">json.nas</a></li>
<li><a href="/leetcode1319.nas">leetcode1319.nas</a></li>
<li><a href="/lexer.nas">lexer.nas</a></li>
<li><a href="/life.nas">life.nas</a></li>
<li><a href="/loop.nas">loop.nas</a></li>
<li><a href="/mandel.nas">mandel.nas</a></li>
<li><a href="/mandelbrot.nas">mandelbrot.nas</a></li>
<li><a href="/md5.nas">md5.nas</a></li>
<li><a href="/md5compare.nas">md5compare.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/module_test.nas">module_test.nas</a></li>
<li><a href="/nasal_test.nas">nasal_test.nas</a></li>
<li><a href="/occupation.nas">occupation.nas</a></li>
<li><a href="/pi.nas">pi.nas</a></li>
<li><a href="/prime.nas">prime.nas</a></li>
<li><a href="/qrcode.nas">qrcode.nas</a></li>
<li><a href="/quick_sort.nas">quick_sort.nas</a></li>
<li><a href="/scalar.nas">scalar.nas</a></li>
<li><a href="/snake.nas">snake.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/tetris.nas">tetris.nas</a></li>
<li><a href="/trait.nas">trait.nas</a></li>
<li><a href="/turingmachine.nas">turingmachine.nas</a></li>
<li><a href="/utf8chk.nas">utf8chk.nas</a></li>
<li><a href="/wavecollapse.nas">wavecollapse.nas</a></li>
<li><a href="/ycombinator.nas">ycombinator.nas</a></li>
</ul>
</td>
</tr>
</table>
</body>
<h2>&nbsp;Shutdown | 关闭服务器</h2>
<text>
<p>Click <a href="/shutdown">here</a> to shutdown http server.</p>
<p>Don't click <a href="/teapot">me</a> besauce i am just a teapot.</p>
</text>
</html>

9
doc/vs.md Normal file
View File

@@ -0,0 +1,9 @@
# How to Create VS project | 如何创建VS工程
1. Get code from this repo using `git`. | 用`git`从这个仓库获取代码。
2. In Visual Studio, click `File`->`New`->`Project From Existing Code...`. | 在VS的界面点击文件(F)->新建(N)->从现有代码创建项目(E)。
3. Select `Visual C++`->`Next`->choose `project file location`->write the `project name` at ease->`Finish`. | 选择创建`Visual C++`项目->下一步->项目文件位置选择你下载的代码存放的文件夹->填项目名称,随便写->完成。
4. Click `Source Files` in `Search Solution Explorer` at left, right click `main.cpp`, compile. | 点开左侧解决方案资源管理器中的`Source Files`,右键点击`main.cpp`,编译。

388
lib.nas
View File

@@ -1,165 +1,281 @@
# lib.nas
# import is used to link another file, this lib function will do nothing.
# because nasal_import will recognize this and link files before generating bytecode.
var import=func(filename){
return __builtin_import(filename);
}
# 2019 ValKmjolnir
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout/printf to output logs.
# this function uses std::cout to output logs.
var print=func(elems...){
return __builtin_print(elems);
return __print(elems);
}
# append is used to add values into a vector.
var append=func(vec,elems...){
return __builtin_append(vec,elems);
return __append(vec,elems);
}
# setsize is used to change the size of vector.
# if the size is larger than before,
# this function will fill vm_nil into uninitialized space.
var setsize=func(vec,size){
return __builtin_setsize(vec,size);
return __setsize(vec,size);
}
# system has the same use in C.
var system=func(str){
return __builtin_system(str);
return __system(str);
}
# input uses std::cin and returns what we input.
var input=func(){
return __builtin_input();
var input=func(end=nil){
return __input(end);
}
# split a string by delimiter for example:
# split a string by separator for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(deli,str){
return __builtin_split(deli,str);
var split=func(separator,str){
return __split(separator,str);
}
# rand has the same function as the rand in C
# if seed is nil, it will return the random number.
# if seed is not nil, it will be initialized by this seed.
var rand=func(seed=nil){
return __builtin_rand(seed);
return __rand(seed);
}
# id will return the pointer of an gc-object.
# if this object is not managed by gc, it will return 0.
var id=func(object){
return __builtin_id(object);
return __id(object);
}
# int will get the integer of input number.
# but carefully use it, because int has range between -2147483648~2147483647
var int=func(val){
return __builtin_int(val);
return __int(val);
}
# floor will get the integral number of input argument
# which is less than or equal to this argument
var floor=func(val){
return __floor(val);
}
# abort using std::abort
var abort=func(){
__abort();
}
# abs gets absolute number.
var abs=func(n){
return n>0?n:-n;
}
# num will change all the other types into number.
# mostly used to change a numerable string.
var num=func(val){
return __builtin_num(val);
return __num(val);
}
# pop used to pop the last element in a vector.
# this function will return the value that poped if vector has element(s).
# if the vector is empty, it will return nil.
var pop=func(vec){
return __builtin_pop(vec);
return __pop(vec);
}
# str is used to change number into string.
var str=func(num){
return __builtin_str(num);
return __str(num);
}
# size can get the size of a string/vector/hashmap.
# in fact it can also get the size of number, and the result is the number itself.
# so don't do useless things, though it really works.
var size=func(object){
return __builtin_size(object);
return __size(object);
}
# contains is used to check if a key exists in a hashmap/dict.
var contains=func(hash,key){
return __builtin_contains(hash,key);
return __contains(hash,key);
}
# delete is used to delete a pair in a hashmap/dict by key.
var delete=func(hash,key){
return __builtin_delete(hash,key);
return __delete(hash,key);
}
# keys is used to get all keys in a hashmap/dict.
# this function will return a vector.
var keys=func(hash){
return __builtin_keys(hash);
return __keys(hash);
}
# time has the same function in C.
var time=func(begin){
return __builtin_time(begin);
return __time(begin);
}
var systime=func(){
return time(0);
}
# die is a special native function.
# use it at where you want the program to crash immediately.
var die=func(str){
return __builtin_die(str);
return __die(str);
}
# find will give the first position of the needle in haystack
var find=func(needle,haystack){
return __find(needle,haystack);
}
# typeof is used to get the type of an object.
# this function returns a string.
var typeof=func(object){
return __builtin_type(object);
return __type(object);
}
# subvec is used to get part of a vector
var subvec=func(vec,begin,length=nil){
return vec[begin:(length==nil?nil:begin+length-1)];
}
# substr will get the sub-string.
# it gets the string, the begin index and sub-string's length as arguments.
var substr=func(str,begin,len){
return __builtin_substr(str,begin,len);
return __substr(str,begin,len);
}
# streq is used to compare if two strings are the same.
var streq=func(a,b){
return __builtin_streq(a,b);
return __streq(a,b);
}
# left is used to get the sub-string like substr.
# but the begin index is 0.
var left=func(str,len){
return __builtin_left(str,len);
return __left(str,len);
}
# right i used to get the sub-string like substr.
# but the begin index is strlen-len.
var right=func(str,len){
return __builtin_right(str,len);
return __right(str,len);
}
# cmp is used to compare two strings.
# normal string will not be correctly compared by operators < > <= >=
# because these operators will turn strings into numbers then compare.
var cmp=func(a,b){
return __builtin_cmp(a,b);
return __cmp(a,b);
}
# chr is used to get the character by ascii-number.
# for example chr(65) -> 'A'
var chr=func(code){
return __builtin_chr(code);
return __chr(code);
}
# mut is used to change unmutable strings to mutable.
var mut=func(str){
return str~"";
}
# srand wraps up rand, using time(0) as the seed.
var srand=func(){
rand(time(0));
return 0;
}
# values() gets all values in a hash.
var values=func(hash){
return __values(hash);
}
# println has the same function as print.
# but it will output a '\n' after using print.
var println=func(elems...){
__builtin_print(elems);
elems=['\n'];
return __builtin_print(elems);
return __println(elems);
}
var isfunc=func(f){
return typeof(f)=="func";
}
var isghost=func(g){
die("this runtime has no ghost object");
return 0;
}
var ishash=func(h){
return typeof(h)=="hash";
}
var isint=func(x){
return x==floor(x);
}
var isnum=func(x){
return typeof(x)=="num" or !math.isnan(num(x));
}
var isscalar=func(s){
var t=typeof(s);
return (t=="num" or t=="str")?1:0;
}
var isstr=func(s){
return typeof(s)=="str";
}
var isvec=func(v){
return typeof(v)=="vec";
}
# get the index of val in the vec
var vecindex=func(vec,val){
forindex(var i;vec)
if(val==vec[i])
return i;
return nil;
}
# check if the object is an instance of the class
var isa=func(object,class){
if(!contains(object,"parents") or typeof(object.parents)!="vec")
return 0;
foreach(var elem;object.parents)
if(elem==class)
return 1;
return 0;
}
# assert aborts when condition is not true
var assert=func(condition,message="assertion failed!"){
if(condition)
return 1;
die(message);
}
# get time stamp, this will return a timestamp object
var maketimestamp=func(){
var t=0;
var millisec=func(){
return __millisec;
}
return {
stamp:func(){t=millisec();},
elapsedMSec:func(){return millisec()-t;},
elapsedUSec:func(){return (millisec()-t)*1000;}
};
}
# md5
var md5=func(str){
return __md5(str);
}
var io=
@@ -168,28 +284,31 @@ var io=
SEEK_CUR:1,
SEEK_END:2,
# get content of a file by filename. returns a string.
fin: func(filename){return __builtin_fin(filename);},
fin: func(filename){return __fin(filename);},
# input a string as the content of a file.
fout: func(filename,str){return __builtin_fout(filename,str);},
fout: func(filename,str){return __fout(filename,str);},
# use C access
exists:func(filename){return __exists(filename);},
# same as C fopen. open file and get the FILE*.
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
open: func(filename,mode="r"){return __open(filename,mode);},
# same as C fclose. close file by FILE*.
close: func(filehandle){return __builtin_close(filehandle);},
close: func(filehandle){return __close(filehandle);},
# same as C fread. read file by FILE*.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
read: func(filehandle,buf,len){return __read(filehandle,buf,len);},
# same as C fwrite. write file by FILE*.
write: func(filehandle,str){return __builtin_write(filehandle,str);},
write: func(filehandle,str){return __write(filehandle,str);},
# same as C fseek. seek place by FILE*.
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
seek: func(filehandle,pos,whence){return __seek(filehandle,pos,whence);},
# same as C ftell.
tell: func(filehandle){return __builtin_tell(filehandle);},
tell: func(filehandle){return __tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln:func(filehandle){return __builtin_readln(filehandle);},
readln:func(filehandle){return __readln(filehandle);},
# same as C stat.
stat: func(filename){return __builtin_stat(filename);},
stat: func(filename){return __stat(filename);},
# same as C feof. check if FILE* gets the end of file(EOF).
eof: func(filehandle){return __builtin_eof(filehandle);}
eof: func(filehandle){return __eof(filehandle);}
};
# get file status. using data from io.stat
@@ -214,35 +333,46 @@ var fstat=func(filename){
# carefully use it, all the calculations are based on integer.
var bits=
{
# xor
bitxor: func(a,b){return __builtin_xor(a,b); },
# and
bitand: func(a,b){return __builtin_and(a,b); },
# or
bitor: func(a,b){return __builtin_or(a,b); },
# nand
bitnand: func(a,b){return __builtin_nand(a,b);},
# not
bitnot: func(a) {return __builtin_not(a); },
# i32 xor
i32_xor: func(a,b){return __i32xor(a,b); },
# i32 and
i32_and: func(a,b){return __i32and(a,b); },
# i32 or
i32_or: func(a,b){return __i32or(a,b); },
# i32 nand
i32_nand:func(a,b){return __i32nand(a,b);},
# i32 not
i32_not: func(a) {return __i32not(a); },
# u32 xor
u32_xor: func(a,b){return __u32xor(a,b); },
# u32 and
u32_and: func(a,b){return __u32and(a,b); },
# u32 or
u32_or: func(a,b){return __u32or(a,b); },
# u32 nand
u32_nand:func(a,b){return __u32nand(a,b);},
# u32 not
u32_not: func(a) {return __u32not(a); },
# get bit data from a special string. for example:
# bits.fld(s,0,3);
# if s stores 10100010(162)
# will get 101(5).
fld: func(str,startbit,len){return __builtin_fld;},
fld: func(str,startbit,len){return __fld;},
# get sign-extended data from a special string. for example:
# bits.sfld(s,0,3);
# if s stores 10100010(162)
# will get 101(5) then this will be signed extended to
# 11111101(-3).
sfld: func(str,startbit,len){return __builtin_sfld;},
sfld: func(str,startbit,len){return __sfld;},
# set value into a special string to store it. little-endian, for example:
# bits.setfld(s,0,8,69);
# set 01000101(69) to string will get this:
# 10100010(162)
# so s[0]=162.
setfld: func(str,startbit,len,val){return __builtin_setfld;},
setfld: func(str,startbit,len,val){return __setfld;},
# get a special string filled by '\0' to use in setfld.
buf: func(len){return __builtin_buf;}
buf: func(len){return __buf;}
};
# mostly used math functions and special constants, you know.
@@ -250,50 +380,77 @@ var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); },
exp: func(x) {return __builtin_exp(x); },
lg: func(x) {return __builtin_lg(x); },
ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);},
isnan: func(x) {return __builtin_isnan(x); }
abs: func(x) {return x>0?x:-x; },
floor: func(x) {return __floor(x); },
pow: func(x,y){return __pow(x,y); },
sin: func(x) {return __sin(x); },
cos: func(x) {return __cos(x); },
tan: func(x) {return __tan(x); },
exp: func(x) {return __exp(x); },
lg: func(x) {return __lg(x); },
ln: func(x) {return __ln(x); },
sqrt: func(x) {return __sqrt(x); },
atan2: func(x,y){return __atan2(x,y);},
isnan: func(x) {return __isnan(x); },
max: func(x,y){return x>y?x:y; },
min: func(x,y){return x<y?x:y; }
};
var unix=
{
pipe: func(){die("not supported yet");},
fork: func(){die("not supported yet");},
pipe: func(){return __pipe;},
fork: func(){return __fork;},
dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");},
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __builtin_opendir;},
readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){return __builtin_closedir;},
waitpid: func(pid,nohang=0){return __waitpid;},
isdir: func(path){return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __opendir;},
readdir: func(handle){return __readdir;},
closedir: func(handle){return __closedir;},
time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);},
environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);}
sleep: func(secs){return __sleep(secs);},
chdir: func(path){return __chdir(path);},
environ: func(){return __environ();},
getcwd: func(){return __getcwd();},
getenv: func(envvar){return __getenv(envvar);},
getpath: func(){return split(os.platform()=="windows"?";":":",unix.getenv("PATH"));}
};
# dylib is the core hashmap for developers to load their own library.
# for safe using dynamic library, you could use 'module' in stl/module.nas
var dylib=
{
# open dynamic lib.
dlopen: func(libname){return __builtin_dlopen;},
dlopen: func(libname){
# find dynamic lib from local dir first
libname=(os.platform()=="windows"?".\\":"./")~libname;
if(io.exists(libname))
return __dlopen(libname);
# find dynamic lib through PATH
var envpath=split(os.platform()=="windows"?";":":",unix.getenv("PATH"));
# first find ./module
append(envpath,".");
var path=os.platform()=="windows"?"\\module\\":"/module/";
foreach(var p;envpath){
p~=path~libname;
if(io.exists(p)){
libname=p;
break;
}
}
return __dlopen(libname);
},
# load symbol from an open dynamic lib.
dlsym: func(lib,sym){return __builtin_dlsym; },
dlsym: func(lib,sym){return __dlsym; },
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
dlclose: func(lib){return __builtin_dlclose; },
dlclose: func(lib){return __dlclose; },
# call the loaded symbol.
dlcall: func(funcptr,args...){return __builtin_dlcall}
dlcall: func(funcptr,args...){return __dlcall}
};
# os is used to use or get some os-related info/functions.
@@ -301,31 +458,42 @@ var dylib=
var os=
{
# get a string that tell which os it runs on.
platform: func(){return __builtin_platform;}
platform: func(){return __platform;},
time: func(){return __logtime; }
};
# runtime gives us some functions that we could manage it manually.
var runtime=
{
# do garbage collection manually.
# carefully use it because using it frequently may make program running slower.
gc: func(){return __builtin_gc;}
# command line arguments
argv: func(){return __sysargv;}
};
# important global constants
var D2R=math.pi/180;
var FPS2KT=0.5925;
var FT2M=0.3048;
var GAL2L=3.7854;
var IN2M=0.0254;
var KG2LB=2.2046;
var KT2FPS=1.6878;
var KT2MPS=0.5144;
var L2GAL=0.2642;
var LB2KG=0.4536;
var M2FT=3.2808;
var M2IN=39.3701;
var M2NM=0.00054;
var MPS2KT=1.9438;
var NM2M=1852;
var R2D=180/math.pi;
# functions that not supported in this runtime:
var bind=func(function,locals,outer_scope=nil){
die("this runtime does not support bind");
}
var call=func(function,args=nil,_me=nil,locals=nil,error=nil){
die("this runtime does not support call");
}
var caller=func(level=1){
die("this runtime does not support caller");
}
var closure=func(function,level=1){
die("this runtime uses \"vm_upval\" instead of \"vm_hash\" as the closure");
}
var compile=func(code,filename="<compile>"){
die("this runtime uses static code generator");
}
var coroutine={
create: func(function){return __cocreate;},
resume: func(co) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
};

145
main.cpp
View File

@@ -1,54 +1,60 @@
#include "nasal.h"
const uint32_t VM_LEXINFO =1;
const uint32_t VM_ASTINFO =2;
const uint32_t VM_CODEINFO =4;
const uint32_t VM_EXECTIME =8;
const uint32_t VM_OPCALLNUM=16;
const uint32_t VM_EXEC =32;
const uint32_t VM_DBGINFO =64;
const uint32_t VM_DEBUG =128;
const uint32_t VM_OPTIMIZE =256;
const u32 VM_LEXINFO =0x01;
const u32 VM_ASTINFO =0x02;
const u32 VM_CODEINFO =0x04;
const u32 VM_EXECTIME =0x08;
const u32 VM_OPCALLNUM=0x10;
const u32 VM_EXEC =0x20;
const u32 VM_DBGINFO =0x40;
const u32 VM_DEBUG =0x80;
const u32 VM_OPTIMIZE =0x100;
const u32 VM_SHOWPATH =0x200;
void help()
{
std::cout
std::clog
<<" ,--#-,\n"
<<"<3 / \\____\\ <3\n"
<<" |_|__A_|\n"
#ifdef _WIN32
<<"use command \'chcp 65001\' if want to use unicode.\n"
<<"use command <chcp 65001> if want to use unicode.\n"
#endif
<<"nasal <option>\n"
<<"option:\n"
<<" -h, --help | get help.\n"
<<" -v, --version | get version of nasal interpreter.\n\n"
<<"nasal <file>\n"
<<"file:\n"
<<" input file name to execute script file.\n\n"
<<"nasal [options...] <file>\n"
<<" -h, --help | get this help and exit.\n"
<<" -v, --version | get version of nasal interpreter.\n\n"
<<"nasal [option...] <file> [argv...]\n"
<<"option:\n"
<<" -l, --lex | view token info.\n"
<<" -a, --ast | view abstract syntax tree.\n"
<<" -c, --code | view bytecode.\n"
<<" -e, --exec | execute.\n"
<<" -t, --time | execute and get the running time.\n"
<<" -o, --opcnt | execute and count used operands.\n"
<<" -d, --detail | execute and get detail crash info.\n"
<<" -t, --time | get the running time.\n"
<<" -o, --opcnt | count used operands.\n"
<<" | available in debug mode.\n"
<<" -d, --detail | get detail crash info.\n"
<<" | get garbage collector info if didn't crash.\n"
<<" -op, --optimize| use optimizer(beta).\n"
<<" | if want to use -op and run, please use -op -e/-t/-o/-d.\n"
<<" -dbg, --debug | debug mode (this will ignore -t -o -d).\n"
<<" | if want to use -op and run, please use -op -e/-t/-d.\n"
<<" -dbg, --debug | debug mode (this will ignore -t -d).\n"
<<" -cp, --chkpath | show path if linker cannot find files.\n"
<<"file:\n"
<<" input file name to execute script file.\n";
<<" input file name to execute.\n"
<<"argv:\n"
<<" command line arguments used in program.\n";
}
void logo()
{
std::cout
<<" __ _ \n"
<<" /\\ \\ \\__ _ ___ __ _| | \n"
<<" / \\/ / _` / __|/ _` | | \n"
<<" / /\\ / (_| \\__ \\ (_| | | \n"
std::clog
<<" __ _\n"
<<" /\\ \\ \\__ _ ___ __ _| |\n"
<<" / \\/ / _` / __|/ _` | |\n"
<<" / /\\ / (_| \\__ \\ (_| | |\n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"nasal interpreter ver 8.0\n"
<<"nasal ver : "<<__nasver<<" ("<<__DATE__<<")\n"
<<"c++ std : "<<__cplusplus<<"\n"
<<"thanks to : https://github.com/andyross/nasal\n"
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
@@ -56,15 +62,16 @@ void logo()
<<"input <nasal -h> to get help .\n";
}
[[noreturn]]
void err()
{
std::cout
std::cerr
<<"invalid argument(s).\n"
<<"use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(const std::string& file,const uint32_t cmd)
void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
{
// front end use the same error module
nasal_err nerr;
@@ -83,36 +90,33 @@ void execute(const std::string& file,const uint32_t cmd)
// parser gets lexer's token list to compile
parse.compile(lexer);
// linker gets parser's ast and load import files to this ast
linker.link(parse,file);
if(cmd&VM_ASTINFO)
parse.print();
linker.link(parse,file,cmd&VM_SHOWPATH);
// optimizer does simple optimization on ast
if(cmd&VM_OPTIMIZE)
optimize(parse.ast());
if(cmd&VM_ASTINFO)
parse.print();
// code generator gets parser's ast and linker's import file list to generate code
gen.compile(parse,linker);
if(cmd&VM_CODEINFO)
gen.print();
// run bytecode
// run
if(cmd&VM_DEBUG)
{
nasal_dbg debugger;
debugger.run(gen,linker);
}
nasal_dbg(nerr).run(gen,linker,argv,cmd&VM_OPCALLNUM);
else if(cmd&VM_EXECTIME)
{
clock_t t=clock();
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
std::cout<<"process exited after "<<((double)(clock()-t))/CLOCKS_PER_SEC<<"s.\n";
auto start=std::chrono::high_resolution_clock::now();
vm.run(gen,linker,argv,cmd&VM_DBGINFO);
auto end=std::chrono::high_resolution_clock::now();
std::clog<<"process exited after "<<(end-start).count()*1.0/std::chrono::high_resolution_clock::duration::period::den<<"s.\n";
}
else if(cmd&VM_EXEC)
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
vm.run(gen,linker,argv,cmd&VM_DBGINFO);
}
int main(int argc,const char* argv[])
i32 main(i32 argc,const char* argv[])
{
if(argc<=1)
{
@@ -121,42 +125,43 @@ int main(int argc,const char* argv[])
}
if(argc==2)
{
std::string s(argv[1]);
string s(argv[1]);
if(s=="-v" || s=="--version")
logo();
else if(s=="-h" || s=="--help")
help();
else if(s[0]!='-')
execute(s,VM_EXEC);
execute(s,{},VM_EXEC);
else
err();
return 0;
}
uint32_t cmd=0;
for(int i=1;i<argc-1;++i)
std::unordered_map<string,u32> cmdlst={
{"--lex",VM_LEXINFO},{"-l",VM_LEXINFO},
{"--ast",VM_ASTINFO},{"-a",VM_ASTINFO},
{"--code",VM_CODEINFO},{"-c",VM_CODEINFO},
{"--exec",VM_EXEC},{"-e",VM_EXEC},
{"--opcnt",VM_OPCALLNUM|VM_EXEC},{"-o",VM_OPCALLNUM|VM_EXEC},
{"--time",VM_EXECTIME|VM_EXEC},{"-t",VM_EXECTIME|VM_EXEC},
{"--detail",VM_DBGINFO|VM_EXEC},{"-d",VM_DBGINFO|VM_EXEC},
{"--optimize",VM_OPTIMIZE},{"-op",VM_OPTIMIZE},
{"--debug",VM_DEBUG},{"-dbg",VM_DEBUG},
{"--chkpath",VM_SHOWPATH|VM_EXEC},{"-cp",VM_SHOWPATH|VM_EXEC}
};
u32 cmd=0;
string filename;
std::vector<string> vm_argv;
for(i32 i=1;i<argc;++i)
{
std::string s(argv[i]);
if(s=="--lex" || s=="-l")
cmd|=VM_LEXINFO;
else if(s=="--ast" || s=="-a")
cmd|=VM_ASTINFO;
else if(s=="--code" || s=="-c")
cmd|=VM_CODEINFO;
else if(s=="--exec" || s=="-e")
cmd|=VM_EXEC;
else if(s=="--opcnt" || s=="-o")
cmd|=VM_OPCALLNUM|VM_EXEC;
else if(s=="--time" || s=="-t")
cmd|=VM_EXECTIME;
else if(s=="--detail" || s=="-d")
cmd|=VM_DBGINFO|VM_EXEC;
else if(s=="--optimize" || s=="-op")
cmd|=VM_OPTIMIZE;
else if(s=="--debug" || s=="-dbg")
cmd|=VM_DEBUG;
if(cmdlst.count(argv[i]))
cmd|=cmdlst[argv[i]];
else if(!filename.length())
filename=argv[i];
else
err();
vm_argv.push_back(argv[i]);
}
execute(argv[argc-1],cmd);
if(!filename.length())
err();
execute(filename,vm_argv,cmd?cmd:cmd|VM_EXEC);
return 0;
}

103
makefile
View File

@@ -1,34 +1,71 @@
.PHONY=test
nasal:main.cpp nasal_ast.h nasal_err.h nasal_builtin.h nasal_opt.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal_dbg.h nasal.h
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
.PHONY:test
SRC=\
main.cpp\
nasal_ast.h\
nasal_err.h\
nasal_builtin.h\
nasal_opt.h\
nasal_codegen.h\
nasal_gc.h\
nasal_import.h\
nasal_lexer.h\
nasal_parse.h\
nasal_vm.h\
nasal_dbg.h\
nasal.h
CPPSTANDARD=-std=c++11
nasal:$(SRC)
$(CXX) $(CPPSTANDARD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
nasal.exe:$(SRC)
$(CXX) $(CPPSTANDARD) -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
stable-release:$(SRC)
$(CXX) $(CPPSTANDARD) -O2 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
stable-release-mingw:$(SRC)
$(CXX) $(CPPSTANDARD) -O2 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
test:nasal
./nasal -op -e test/ascii-art.nas
./nasal -op -c test/bf.nas
./nasal -op -c test/bfconvertor.nas
./nasal -op -e -d test/bfs.nas
./nasal -op -t test/bigloop.nas
./nasal -op -e test/bp.nas
./nasal -op -e -d test/calc.nas
./nasal -op -e test/choice.nas
./nasal -op -e test/class.nas
./nasal -op -c test/exception.nas
./nasal -op -t -d test/fib.nas
./nasal -op -e test/filesystem.nas
./nasal -op -e -d test/hexdump.nas
./nasal -op -e test/json.nas
./nasal -op -e test/leetcode1319.nas
./nasal -op -e -d test/lexer.nas
./nasal -op -e -d test/life.nas
./nasal -op -t test/loop.nas
./nasal -op -t -d test/mandel.nas
./nasal -op -t -d test/mandelbrot.nas
./nasal -op -c test/module_test.nas
./nasal -op -e test/nasal_test.nas
./nasal -op -t -d test/pi.nas
./nasal -op -t -d test/prime.nas
./nasal -op -t -d test/quick_sort.nas
./nasal -op -e test/scalar.nas
./nasal -op -e test/trait.nas
./nasal -op -t -d test/turingmachine.nas
./nasal -op -t -d test/ycombinator.nas
@ ./nasal -op -e test/ascii-art.nas
@ ./nasal -op -c test/auto_crash.nas
@ ./nasal -op -a -c test/bf.nas
@ ./nasal -op -a -c test/bfcolored.nas
@ ./nasal -op -a -c test/bfconvertor.nas
@ ./nasal -op -e -d test/bfs.nas
@ ./nasal -op -t test/bigloop.nas
@ ./nasal -op -e test/bp.nas
@ ./nasal -op -e -d test/calc.nas
@ ./nasal -op -e test/choice.nas
@ ./nasal -op -e test/class.nas
@ ./nasal -op -d test/coroutine.nas
@ ./nasal -op -e test/diff.nas
-@ ./nasal -op -d test/exception.nas
@ ./nasal -op -t -d test/fib.nas
@ ./nasal -op -e test/filesystem.nas
@ ./nasal -op -e -d test/hexdump.nas
@ ./nasal -op -c test/httptest.nas
@ ./nasal -op -e test/json.nas
@ ./nasal -op -e test/leetcode1319.nas
@ ./nasal -op -e -d test/lexer.nas
@ ./nasal -op -e -d test/life.nas
@ ./nasal -op -t test/loop.nas
@ ./nasal -op -t -d test/mandel.nas
@ ./nasal -op -t -d test/mandelbrot.nas
@ ./nasal -op -t -d test/md5.nas
@ ./nasal -op -t -d test/md5compare.nas
-@ ./nasal -op -d test/module_test.nas
@ ./nasal -op -e test/nasal_test.nas
@ ./nasal -op -c test/occupation.nas
@ ./nasal -op -t -d test/pi.nas
@ ./nasal -op -t -d test/prime.nas
@ ./nasal -op -e test/qrcode.nas
@ ./nasal -op -t -d test/quick_sort.nas
@ ./nasal -op -e test/scalar.nas hello world
-@ ./nasal -op -c -t test/snake.nas
@ ./nasal -op -c -e test/trait.nas
-@ ./nasal -op -c -t test/tetris.nas
@ ./nasal -op -c -t -d test/turingmachine.nas
@ ./nasal -op -c -t -d test/ycombinator.nas
@ ./nasal -op -d test/wavecollapse.nas

View File

@@ -6,18 +6,22 @@ double fibonaci(double x){
return x;
return fibonaci(x-1)+fibonaci(x-2);
}
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
extern "C" nas_ref fib(std::vector<nas_ref>& args,nasal_gc& gc){
std::cout<<"[mod] this is the first test module of nasal\n";
nasal_ref num=args[0];
if(!args.size())
return nas_err("fib","lack arguments");
nas_ref num=args[0];
if(num.type!=vm_num)
return builtin_err("extern_fib","\"num\" must be number");
return {vm_num,fibonaci(num.to_number())};
return nas_err("extern_fib","\"num\" must be number");
return {vm_num,fibonaci(num.tonum())};
}
extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
extern "C" nas_ref quick_fib(std::vector<nas_ref>& args,nasal_gc& gc){
std::cout<<"[mod] this is the first test module of nasal\n";
nasal_ref num=args[0];
if(!args.size())
return nas_err("fib","lack arguments");
nas_ref num=args[0];
if(num.type!=vm_num)
return builtin_err("extern_quick_fib","\"num\" must be number");
return nas_err("extern_quick_fib","\"num\" must be number");
if(num.num()<2)
return num;
double a=1,b=1,res=0;

83
module/keyboard.cpp Normal file
View File

@@ -0,0 +1,83 @@
#include "../nasal.h"
#include <iostream>
#ifdef _WIN32
#include <conio.h>
#else
#include <fcntl.h>
#include <termios.h>
#endif
class noecho_input{
private:
#ifndef _WIN32
struct termios init_termios;
struct termios new_termios;
int peek_char=-1;
#endif
public:
noecho_input(){
#ifndef _WIN32
tcflush(0,TCIOFLUSH);
tcgetattr(0,&init_termios);
new_termios=init_termios;
new_termios.c_lflag&=~(ICANON|ECHO|ECHONL|ECHOE);
// vmin=0 is nonblock input, but in wsl there is a bug that will block input
// so we use fcntl to write the nonblock input
new_termios.c_cc[VMIN]=1;
new_termios.c_cc[VTIME]=0;
tcsetattr(0,TCSANOW,&new_termios);
#endif
}
~noecho_input(){
#ifndef _WIN32
tcflush(0,TCIOFLUSH);
tcsetattr(0,TCSANOW,&init_termios);
#endif
}
int noecho_kbhit(){
#ifndef _WIN32
unsigned char ch=0;
int nread=0;
if(peek_char!=-1)
return 1;
int flag=fcntl(0,F_GETFL);
fcntl(0,F_SETFL,flag|O_NONBLOCK);
nread=read(0,&ch,1);
fcntl(0,F_SETFL,flag);
if(nread==1){
peek_char=ch;
return 1;
}
return 0;
#else
return kbhit();
#endif
}
int noecho_getch(){
#ifndef _WIN32
int ch=0;
if(peek_char!=-1){
ch=peek_char;
peek_char=-1;
return ch;
}
read(0,&ch,1);
return ch;
#else
return getch();
#endif
}
};
noecho_input this_window;
extern "C" nas_ref nas_getch(std::vector<nas_ref>& args,nasal_gc& gc){
return {vm_num,(double)this_window.noecho_getch()};
}
extern "C" nas_ref nas_kbhit(std::vector<nas_ref>& args,nasal_gc& gc){
return {vm_num,(double)this_window.noecho_kbhit()};
}
extern "C" nas_ref nas_noblock(std::vector<nas_ref>& args,nasal_gc& gc){
if(this_window.noecho_kbhit())
return {vm_num,(double)this_window.noecho_getch()};
return nil;
}

10
module/libfib.nas Normal file
View File

@@ -0,0 +1,10 @@
var libfib=func(){
var dl=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib=dylib.dlsym(dl,"fib");
var qfib=dylib.dlsym(dl,"quick_fib");
var call=dylib.dlcall;
return {
fib: func(x){return call(fib,x)},
qfib:func(x){return call(qfib,x)}
};
}();

12
module/libkey.nas Normal file
View File

@@ -0,0 +1,12 @@
var libkey=func(){
var lib=dylib.dlopen("libkey"~(os.platform()=="windows"?".dll":".so"));
var kb=dylib.dlsym(lib,"nas_kbhit");
var gt=dylib.dlsym(lib,"nas_getch");
var nb=dylib.dlsym(lib,"nas_noblock");
var call=dylib.dlcall;
return {
kbhit:func(){return call(kb);},
getch:func(){return call(gt);},
nonblock:func(){return call(nb);}
}
}();

85
module/libsock.nas Normal file
View File

@@ -0,0 +1,85 @@
var socket=func(){
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
var sock=dylib.dlsym(lib,"nas_socket");
var closesocket=dylib.dlsym(lib,"nas_closesocket");
var shutdown=dylib.dlsym(lib,"nas_shutdown");
var bind=dylib.dlsym(lib,"nas_bind");
var listen=dylib.dlsym(lib,"nas_listen");
var connect=dylib.dlsym(lib,"nas_connect");
var accept=dylib.dlsym(lib,"nas_accept");
var send=dylib.dlsym(lib,"nas_send");
var sendto=dylib.dlsym(lib,"nas_sendto");
var recv=dylib.dlsym(lib,"nas_recv");
var recvfrom=dylib.dlsym(lib,"nas_recvfrom");
var errno=dylib.dlsym(lib,"nas_errno");
var call=dylib.dlcall;
return {
AF_UNSPEC:0,AF_UNIX:1,AF_INET:2,AF_IMPLINK:3,
AF_PUP:4,AF_CHAOS:5,AF_IPX:6,AF_NS:6,
AF_ISO:7,AF_OSI:7,AF_ECMA:8,AF_DATAKIT:9,
AF_CCITT:10,AF_SNA:11,AF_DECnet:12,AF_DLI:13,
AF_LAT:14,AF_HYLINK:15,AF_APPLETALK:16,AF_NETBIOS:17,
AF_VOICEVIEW:18,AF_FIREFOX:19,AF_UNKNOWN1:20,AF_BAN:21,
AF_MAX:22,
SOCK_STREAM:1,SOCK_DGRAM:2,SOCK_RAW:3,SOCK_RDM:4,
SOCK_SEQPACKET:5,
IPPROTO_IP:0,IPPROTO_ICMP:1,IPPROTO_IGMP:2,IPPROTO_GGP:3,
IPPROTO_TCP:6,IPPROTO_PUP:12,IPPROTO_UDP:17,IPPROTO_IDP:22,
IPPROTO_ND:77,IPPROTO_RAW:255,IPPROTO_MAX:256,
IPPORT_ECHO:7,IPPORT_DISCARD:9,IPPORT_SYSTAT:11,IPPORT_DAYTIME:13,
IPPORT_NETSTAT:15,IPPORT_FTP:21,IPPORT_TELNET:23,IPPORT_SMTP:25,
IPPORT_TIMESERVER:37,IPPORT_NAMESERVER:42,IPPORT_WHOIS:43,IPPORT_MTP:57,
IPPORT_TFTP:69,IPPORT_RJE:77,IPPORT_FINGER:79,IPPORT_TTYLINK:87,
IPPORT_SUPDUP:95,IPPORT_EXECSERVER:512,IPPORT_LOGINSERVER:513,IPPORT_CMDSERVER:514,
IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520,
IPPORT_RESERVED:1024,
SHUT_RD :0x00,
SHUT_WR :0x01,
SHUT_RDWR:0x02,
MSG_OOB:0x1,
MSG_PEEK:0x2,
MSG_DONTROUTE:0x4,
socket:func(af,type,proto){
return call(sock,af,type,proto);
},
closesocket:func(sd){
return call(closesocket,sd);
},
shutdown: func(sd,how){
return call(shutdown,sd,how);
},
bind: func(sd,ip,port){
return call(bind,sd,ip,port);
},
listen: func(sd,backlog){
return call(listen,sd,backlog);
},
connect: func(sd,hostname,port){
return call(connect,sd,hostname,port);
},
accept: func(sd){
return call(accept,sd);
},
send: func(sd,buff,flags=0){
return call(send,sd,buff,flags);
},
sendto: func(sd,hostname,port,buff,flags=0){
return call(sendto,sd,hostname,port,buff,flags);
},
recv: func(sd,len,flags=0){
return call(recv,sd,len,flags);
},
recvfrom: func(sd,len,flags=0){
return call(recvfrom,sd,len,flags);
},
errno: func(){
return call(errno);
}
};
}();

View File

@@ -1,9 +1,37 @@
.PHONY=clean
.PHONY=clean all mingw-all
CPPSTANDARD=-std=c++11
libfib.so: fib.cpp
clang++ -c -O3 fib.cpp -fPIC -o fib.o
clang++ -shared -o libfib.so fib.o
$(CXX) $(CPPSTANDARD) -c -O3 fib.cpp -fPIC -o fib.o
$(CXX) -shared -o libfib.so fib.o
rm fib.o
libfib.dll: fib.cpp
g++ -c -O3 fib.cpp -fPIC -o fib.o
g++ -shared -o libfib.dll fib.o
$(CXX) $(CPPSTANDARD) -c -O3 fib.cpp -fPIC -o fib.o
$(CXX) -shared -o libfib.dll fib.o
del fib.o
libkey.so: keyboard.cpp
$(CXX) $(CPPSTANDARD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
$(CXX) -shared -o libkey.so keyboard.o
rm keyboard.o
libkey.dll: keyboard.cpp
$(CXX) $(CPPSTANDARD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
$(CXX) -shared -o libkey.dll keyboard.o -static
del keyboard.o
libnasock.so: nasocket.cpp
$(CXX) $(CPPSTANDARD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
$(CXX) -shared -o libnasock.so nasocket.o
rm nasocket.o
libnasock.dll: nasocket.cpp
$(CXX) $(CPPSTANDARD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
$(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static
del nasocket.o
clean:
rm *.o *.so *.dll *.dylib
rm *.so *.dll *.dylib
all: libfib.so libkey.so libnasock.so
@ echo "build done"
mingw-all: libfib.dll libkey.dll libnasock.dll
@ echo build done

187
module/nasocket.cpp Normal file
View File

@@ -0,0 +1,187 @@
#include "../nasal.h"
#ifdef _WIN32
#include <winsock.h>
#pragma comment(lib,"ws2_32")
class WSAmanager{
private:
WSAData data;
public:
WSAmanager(){
WSAStartup(0x1010,&data);
}
~WSAmanager(){
WSACleanup();
}
};
static WSAmanager win;
#else
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
extern "C" nas_ref nas_socket(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
return nas_err("socket","\"af\", \"type\", \"protocol\" should be number");
int sd=socket(args[0].num(),args[1].num(),args[2].num());
return {vm_num,(double)sd};
}
extern "C" nas_ref nas_closesocket(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("closesocket","\"\" should be number");
#ifdef _WIN32
return {vm_num,(double)closesocket(args[0].num())};
#else
return {vm_num,(double)close(args[0].num())};
#endif
}
extern "C" nas_ref nas_shutdown(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("shutdown","\"sd\" must be a number");
if(args[1].type!=vm_num)
return nas_err("shutdown","\"how\" must be a number");
return {vm_num,(double)shutdown(args[0].num(),args[1].num())};
}
extern "C" nas_ref nas_bind(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("bind","\"sd\" muse be a number");
if(args[1].type!=vm_str)
return nas_err("bind","\"ip\" should be a string including an ip with correct format");
if(args[2].type!=vm_num)
return nas_err("bind","\"port\" must be a number");
sockaddr_in server;
memset(&server,0,sizeof(sockaddr_in));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr(args[1].str().c_str());
server.sin_port=htons(args[2].num());
return {vm_num,(double)bind(args[0].num(),(sockaddr*)&server,sizeof(server))};
}
extern "C" nas_ref nas_listen(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("listen","\"sd\" must be a number");
if(args[1].type!=vm_num)
return nas_err("listen","\"backlog\" must be a number");
return{vm_num,(double)listen(args[0].num(),args[1].num())};
}
extern "C" nas_ref nas_connect(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("connect","\"sd\" must be a number");
if(args[1].type!=vm_str)
return nas_err("connect","\"hostname\" must be a string");
if(args[2].type!=vm_num)
return nas_err("connect","\"port\" must be a number");
sockaddr_in addr;
memset(&addr,0,sizeof(sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(args[2].num());
hostent* entry=gethostbyname(args[1].str().c_str());
memcpy(&addr.sin_addr,entry->h_addr,entry->h_length);
return {vm_num,(double)connect(args[0].num(),(sockaddr*)&addr,sizeof(sockaddr_in))};
}
extern "C" nas_ref nas_accept(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("accept","\"sd\" must be a number");
sockaddr_in client;
int socklen=sizeof(sockaddr_in);
#ifdef _WIN32
int client_sd=accept(args[0].num(),(sockaddr*)&client,&socklen);
#else
int client_sd=accept(args[0].num(),(sockaddr*)&client,(socklen_t*)&socklen);
#endif
nas_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
hash["sd"]={vm_num,(double)client_sd};
hash["ip"]=gc.newstr(inet_ntoa(client.sin_addr));
gc.temp=nil;
return res;
}
extern "C" nas_ref nas_send(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("send","\"sd\" must be a number");
if(args[1].type!=vm_str)
return nas_err("send","\"buff\" must be a string");
if(args[2].type!=vm_num)
return nas_err("send","\"flags\" muse be a number");
return {vm_num,(double)send(args[0].num(),args[1].str().c_str(),args[1].str().length(),args[2].num())};
}
extern "C" nas_ref nas_sendto(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("sendto","\"sd\" must be a number");
if(args[1].type!=vm_str)
return nas_err("sendto","\"hostname\" must be a string");
if(args[2].type!=vm_num)
return nas_err("sendto","\"port\" must be a number");
if(args[3].type!=vm_str)
return nas_err("sendto","\"buff\" must be a string");
if(args[4].type!=vm_num)
return nas_err("sendto","\"flags\" must be a number");
sockaddr_in addr;
memset(&addr,0,sizeof(sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(args[2].num());
hostent* entry=gethostbyname(args[1].str().c_str());
memcpy(&addr.sin_addr,entry->h_addr,entry->h_length);
return {vm_num,(double)sendto(args[0].num(),args[3].str().c_str(),args[3].str().length(),args[4].num(),(sockaddr*)&addr,sizeof(sockaddr_in))};
}
extern "C" nas_ref nas_recv(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("recv","\"sd\" must be a number");
if(args[1].type!=vm_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)
return nas_err("recv","\"flags\" muse be a number");
nas_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
char* buf=new char[(int)args[1].num()];
hash["size"]={vm_num,(double)recv(args[0].num(),buf,args[1].num(),args[2].num())};
hash["str"]=gc.newstr(buf);
delete[] buf;
gc.temp=nil;
return res;
}
extern "C" nas_ref nas_recvfrom(std::vector<nas_ref>& args,nasal_gc& gc){
if(args[0].type!=vm_num)
return nas_err("recvfrom","\"sd\" must be a number");
if(args[1].type!=vm_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)
return nas_err("recvfrom","\"flags\" muse be a number");
sockaddr_in addr;
int socklen=sizeof(sockaddr_in);
nas_ref res=gc.temp=gc.alloc(vm_hash);
auto& hash=res.hash().elems;
char* buf=new char[(int)args[1].num()+1];
#ifdef _WIN32
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,&socklen)};
#else
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,(socklen_t*)&socklen)};
#endif
buf[(int)hash["size"].num()]=0;
hash["str"]=gc.newstr(buf);
delete[] buf;
hash["fromip"]=gc.newstr(inet_ntoa(addr.sin_addr));
gc.temp=nil;
return res;
}
extern "C" nas_ref nas_errno(std::vector<nas_ref>& args,nasal_gc& gc){
return gc.newstr(strerror(errno));
}

121
nasal.h
View File

@@ -1,11 +1,20 @@
#ifndef __NASAL_H__
#define __NASAL_H__
#define __nasver "10.0"
#include <stdint.h>
#ifndef _MSC_VER
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#else
#include <io.h>
#include <direct.h>
#endif
#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <algorithm>
@@ -18,20 +27,34 @@
#include <queue>
#include <vector>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/wait.h>
#endif
inline double hex_to_double(const char* str)
using i32=std::int32_t;
using i64=std::int64_t;
using u8=std::uint8_t;
using u16=std::uint16_t;
using u32=std::uint32_t;
using u64=std::uint64_t;
using usize=std::size_t;
using f64=double;
using std::string;
const u32 STACK_DEPTH=1024;
inline f64 hex_to_double(const char* str)
{
double ret=0;
f64 ret=0;
for(;*str;++str)
{
ret*=16;
@@ -46,9 +69,9 @@ inline double hex_to_double(const char* str)
}
return ret;
}
inline double oct_to_double(const char* str)
inline f64 oct_to_double(const char* str)
{
double ret=0;
f64 ret=0;
for(;*str;++str)
{
ret*=8;
@@ -59,9 +82,9 @@ inline double oct_to_double(const char* str)
}
return ret;
}
inline double dec_to_double(const char* str)
inline f64 dec_to_double(const char* str)
{
double ret=0,negative=1,num_pow=0;
f64 ret=0,negative=1,num_pow=0;
while('0'<=*str && *str<='9')
ret=ret*10+(*str++-'0');
if(!*str) return ret;
@@ -92,39 +115,79 @@ inline double dec_to_double(const char* str)
}
return ret*std::pow(10,negative*num_pow);
}
double str2num(const char* str)
f64 str2num(const char* str)
{
bool is_negative=false;
double ret_num=0;
bool negative=false;
f64 res=0;
if(*str=='-' || *str=='+')
is_negative=(*str++=='-');
negative=(*str++=='-');
if(!*str)
return nan("");
if(str[0]=='0' && str[1]=='x')
ret_num=hex_to_double(str+2);
res=hex_to_double(str+2);
else if(str[0]=='0' && str[1]=='o')
ret_num=oct_to_double(str+2);
res=oct_to_double(str+2);
else
ret_num=dec_to_double(str);
return is_negative?-ret_num:ret_num;
res=dec_to_double(str);
return negative?-res:res;
}
std::string rawstr(const std::string& str)
i32 utf8_hdchk(const char head)
{
std::string ret("");
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const u8 c=(u8)head;
if((c>>5)==0x06) // 110x xxxx (10xx xxxx)^1
return 1;
if((c>>4)==0x0e) // 1110 xxxx (10xx xxxx)^2
return 2;
if((c>>3)==0x1e) // 1111 0xxx (10xx xxxx)^3
return 3;
return 0;
}
string chrhex(const char c)
{
const char hextbl[]="0123456789abcdef";
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
}
string rawstr(const string& str,const usize maxlen=0)
{
string ret("");
for(auto i:str)
{
#ifdef _WIN32
// windows ps or cmd doesn't output unicode normally
// if 'chcp65001' is not enabled, we output the hex
if(i<=0)
{
ret+="\\x"+chrhex(i);
continue;
}
#endif
switch(i)
{
case '\a': ret+="\\a";break;
case '\b': ret+="\\b";break;
case '\f': ret+="\\f";break;
case '\n': ret+="\\n";break;
case '\r': ret+="\\r";break;
case '\t': ret+="\\t";break;
case '\v': ret+="\\v";break;
case '\0': ret+="\\0";break;
default: ret+=i; break;
case '\0': ret+="\\0"; break;
case '\a': ret+="\\a"; break;
case '\b': ret+="\\b"; break;
case '\t': ret+="\\t"; break;
case '\n': ret+="\\n"; break;
case '\v': ret+="\\v"; break;
case '\f': ret+="\\f"; break;
case '\r': ret+="\\r"; break;
#ifdef _MSC_VER
case '\033':ret+="\\e";break;
#else
case '\e': ret+="\\e"; break;
#endif
case '\"': ret+="\\\"";break;
case '\'': ret+="\\\'";break;
case '\\': ret+="\\\\";break;
default: ret+=i; break;
}
}
if(maxlen && ret.length()>maxlen)
ret=ret.substr(0,maxlen)+"...";
return ret;
}
#include "nasal_err.h"

View File

@@ -1,7 +1,7 @@
#ifndef __NASAL_AST_H__
#ifndef __NASAL_AST_H__
#define __NASAL_AST_H__
enum ast_node
enum ast_node:u32
{
ast_null=0, // null node
ast_root, // mark the root node of ast
@@ -76,7 +76,7 @@ const char* ast_name[]=
"func",
"hash",
"vec",
"hashmember",
"pair",
"call",
"callh",
"callv",
@@ -116,59 +116,57 @@ const char* ast_name[]=
"if",
"elsif",
"else",
"multi_id",
"multi_scalar",
"multi-id",
"multi-scalar",
"def",
"multi_assign",
"multi-assign",
"continue",
"break",
"return",
nullptr
"return"
};
class nasal_ast
{
private:
uint32_t _line;
uint32_t _type;
double _num;
std::string _str;
u32 _line;
u32 _type;
f64 _num;
string _str;
std::vector<nasal_ast> _child;
public:
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):_line(l),_type(t){}
nasal_ast(const u32 l=0,const u32 t=ast_null):_line(l),_type(t),_num(0){}
nasal_ast(const nasal_ast&);
nasal_ast(nasal_ast&&);
void print(int,bool);
void print(u32,bool);
void clear();
nasal_ast& operator=(const nasal_ast&);
nasal_ast& operator=(nasal_ast&&);
nasal_ast& operator[](const int index){return _child[index];}
const nasal_ast& operator[](const int index) const {return _child[index];}
size_t size() const {return _child.size();}
nasal_ast& operator[](usize n){return _child[n];}
const nasal_ast& operator[](usize n) const {return _child[n];}
usize size() const {return _child.size();}
void add(nasal_ast&& ast){_child.push_back(std::move(ast));}
void add(const nasal_ast& ast){_child.push_back(ast);}
void set_line(const uint32_t l){_line=l;}
void set_type(const uint32_t t){_type=t;}
void set_str(const std::string& s){_str=s;}
void set_num(const double n){_num=n;}
void set_line(const u32 l){_line=l;}
void set_type(const u32 t){_type=t;}
void set_str(const string& s){_str=s;}
void set_num(const f64 n){_num=n;}
inline uint32_t line() const {return _line;}
inline uint32_t type() const {return _type;}
inline double num() const {return _num;}
inline const std::string& str() const {return _str;}
inline u32 line() const {return _line;}
inline u32 type() const {return _type;}
inline f64 num() const {return _num;}
inline const string& str() const {return _str;}
inline const std::vector<nasal_ast>& child() const {return _child;}
inline std::vector<nasal_ast>& child(){return _child;}
};
nasal_ast::nasal_ast(const nasal_ast& tmp)
nasal_ast::nasal_ast(const nasal_ast& tmp):
_str(tmp._str),_child(tmp._child)
{
_line=tmp._line;
_type=tmp._type;
_num =tmp._num;
_str =tmp._str;
_child=tmp._child;
}
nasal_ast::nasal_ast(nasal_ast&& tmp)
@@ -209,18 +207,15 @@ void nasal_ast::clear()
_child.clear();
}
void nasal_ast::print(int depth,bool last=false)
void nasal_ast::print(u32 depth,bool last=false)
{
static std::vector<std::string> intentation={};
static std::vector<string> intentation={};
for(auto& i:intentation)
std::cout<<i;
std::cout<<ast_name[_type];
if(
_type==ast_str ||
_type==ast_id ||
_type==ast_default ||
_type==ast_dynamic ||
_type==ast_callh)
if(_type==ast_str || _type==ast_id ||
_type==ast_default || _type==ast_dynamic ||
_type==ast_callh)
std::cout<<":"<<rawstr(_str);
else if(_type==ast_num || _type==ast_file)
std::cout<<":"<<_num;
@@ -233,7 +228,7 @@ void nasal_ast::print(int depth,bool last=false)
#else
intentation.back()="";
#endif
for(uint32_t i=0;i<_child.size();++i)
for(u32 i=0;i<_child.size();++i)
{
#ifdef _WIN32
intentation.push_back(i==_child.size()-1?"`-":"|-");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,49 +6,51 @@
class nasal_dbg:public nasal_vm
{
private:
bool next_step;
uint16_t bk_fidx;
uint32_t bk_line;
file_line src;
bool next;
usize fsize;
u16 bk_fidx;
u32 bk_line;
nasal_err& src;
std::vector<std::string> parse(const std::string&);
uint16_t get_fileindex(const std::string&);
std::vector<string> parse(const string&);
u16 fileindex(const string&);
void err();
void help();
void opcallsort(const u64*);
void stepinfo();
void interact();
public:
nasal_dbg():
next_step(false),
bk_fidx(0),bk_line(0){}
nasal_dbg(nasal_err& nerr):
next(false),fsize(0),
bk_fidx(0),bk_line(0),
src(nerr){}
void run(
const nasal_codegen&,
const nasal_import&
const nasal_import&,
const std::vector<string>&,
bool
);
};
std::vector<std::string> nasal_dbg::parse(const std::string& cmd)
std::vector<string> nasal_dbg::parse(const string& cmd)
{
std::vector<std::string> res;
std::string tmp="";
for(uint32_t i=0;i<cmd.length();++i)
std::vector<string> res;
usize last=0,pos=cmd.find(" ",0);
while(pos!=string::npos)
{
if(cmd[i]==' ' && tmp.length())
{
res.push_back(tmp);
tmp="";
continue;
}
tmp+=cmd[i];
if(pos>last)
res.push_back(cmd.substr(last,pos-last));
last=pos+1;
pos=cmd.find(" ",last);
}
if(tmp.length())
res.push_back(tmp);
if(last<cmd.length())
res.push_back(cmd.substr(last));
return res;
}
uint16_t nasal_dbg::get_fileindex(const std::string& filename)
u16 nasal_dbg::fileindex(const string& filename)
{
for(uint16_t i=0;i<files_size;++i)
for(u16 i=0;i<fsize;++i)
if(filename==files[i])
return i;
return 65535;
@@ -65,35 +67,65 @@ void nasal_dbg::help()
{
std::cout
<<"<option>\n"
<<"\th, help | get help\n"
<<"\tbt, backtrace | get function call trace\n"
<<"\tc, continue | run program until break point or exit\n"
<<"\tg, global | see global values\n"
<<"\tl, local | see local values\n"
<<"\tu, upval | see upvalue\n"
<<"\ta, all | show global,local and upvalue\n"
<<"\tn, next | execute next bytecode\n"
<<"\tq, exit | exit debugger\n"
<<"\th, help | get help\n"
<<"\tbt, backtrace | get function call trace\n"
<<"\tc, continue | run program until break point or exit\n"
<<"\tf, file | see all the compiled files\n"
<<"\tg, global | see global values\n"
<<"\tl, local | see local values\n"
<<"\tu, upval | see upvalue\n"
<<"\tr, register | show vm register detail\n"
<<"\ta, all | show global,local and upvalue\n"
<<"\tn, next | execute next bytecode\n"
<<"\tq, exit | exit debugger\n"
<<"<option> <filename> <line>\n"
<<"\tbk, break | set break point\n";
<<"\tbk, break | set break point\n";
}
void nasal_dbg::opcallsort(const u64* arr)
{
typedef std::pair<u32,u64> op;
std::vector<op> opcall;
u64 total=0;
for(u32 i=0;i<op_ret+1;++i)
{
total+=arr[i];
opcall.push_back({i,arr[i]});
}
std::sort(opcall.begin(),opcall.end(),
[](const op& a,const op& b){return a.second>b.second;}
);
std::clog<<"\noperands call info";
for(auto& i:opcall)
{
u64 rate=i.second*100/total;
if(!rate)
{
std::clog<<"\n ...";
break;
}
std::clog<<"\n "<<code_table[i.first]
<<" : "<<i.second<<" ("<<rate<<"%)";
}
std::clog<<"\n total : "<<total<<'\n';
}
void nasal_dbg::stepinfo()
{
uint32_t begin,end;
uint32_t line=bytecode[pc].line==0?0:bytecode[pc].line-1;
u32 begin,end;
u32 line=bytecode[pc].line==0?0:bytecode[pc].line-1;
src.load(files[bytecode[pc].fidx]);
printf("\nsource code:\n");
std::cout<<"\nsource code:\n";
begin=(line>>3)==0?0:((line>>3)<<3);
end=(1+(line>>3))<<3;
for(uint32_t i=begin;i<end && i<src.size();++i)
printf("%s\t%s\n",i==line?"-->":" ",src[i].c_str());
printf("next bytecode:\n");
for(u32 i=begin;i<end && i<src.size();++i)
std::cout<<(i==line?"--> ":" ")<<src[i]<<"\n";
std::cout<<"next bytecode:\n";
begin=(pc>>3)==0?0:((pc>>3)<<3);
end=(1+(pc>>3))<<3;
for(uint32_t i=begin;i<end && bytecode[i].op!=op_exit;++i)
bytecodeinfo(i==pc?"-->\t":" \t",i);
stackinfo(5);
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i)
bytecodeinfo(i==pc?"--> ":" ",i);
stackinfo(10);
}
void nasal_dbg::interact()
@@ -105,20 +137,20 @@ void nasal_dbg::interact()
<<"[debug] nasal debug mode\n"
<<"input \'h\' to get help\n";
}
else if(bytecode[pc].op==op_nop || bytecode[pc].op==op_exit)
else if(bytecode[pc].op==op_exit)
return;
if(
(bytecode[pc].fidx!=bk_fidx || bytecode[pc].line!=bk_line) && // break point
!next_step // next step
!next // next step
)return;
next_step=false;
std::string cmd;
next=false;
string cmd;
stepinfo();
while(1)
{
printf(">> ");
std::cout<<">> ";
std::getline(std::cin,cmd);
auto res=parse(cmd);
if(res.size()==1)
@@ -129,21 +161,22 @@ void nasal_dbg::interact()
traceback();
else if(res[0]=="c" || res[0]=="continue")
return;
else if(res[0]=="f" || res[0]=="file")
for(usize i=0;i<fsize;++i)
std::cout<<"["<<i<<"] "<<files[i]<<"\n";
else if(res[0]=="g" || res[0]=="global")
global_state();
gstate();
else if(res[0]=="l" || res[0]=="local")
local_state();
lstate();
else if(res[0]=="u" || res[0]=="upval")
upval_state();
ustate();
else if(res[0]=="r" || res[0]=="register")
reginfo();
else if(res[0]=="a" || res[0]=="all")
{
global_state();
local_state();
upval_state();
}
detail();
else if(res[0]=="n" || res[0]=="next")
{
next_step=true;
next=true;
return;
}
else if(res[0]=="q" || res[0]=="exit")
@@ -151,24 +184,19 @@ void nasal_dbg::interact()
else
err();
}
else if(res.size()==3)
else if(res.size()==3 && (res[0]=="bk" || res[0]=="break"))
{
if(res[0]=="bk" || res[0]=="break")
bk_fidx=fileindex(res[1]);
if(bk_fidx==65535)
{
bk_fidx=get_fileindex(res[1]);
if(bk_fidx==65535)
{
printf("cannot find file named \"%s\"\n",res[1].c_str());
bk_fidx=0;
}
int tmp=atoi(res[2].c_str());
if(tmp<=0)
printf("incorrect line number \"%s\"\n",res[2].c_str());
else
bk_line=tmp;
std::cout<<"cannot find file named `"<<res[1]<<"`\n";
bk_fidx=0;
}
i32 tmp=atoi(res[2].c_str());
if(tmp<=0)
std::cout<<"incorrect line number `"<<res[2]<<"`\n";
else
err();
bk_line=tmp;
}
else
err();
@@ -177,130 +205,197 @@ void nasal_dbg::interact()
void nasal_dbg::run(
const nasal_codegen& gen,
const nasal_import& linker)
const nasal_import& linker,
const std::vector<string>& argv,
bool opcnt)
{
detail_info=true;
init(gen.get_strs(),gen.get_nums(),linker.get_file());
const void* opr_table[]=
fsize=linker.filelist().size();
init(gen.strs(),gen.nums(),gen.codes(),linker.filelist(),argv);
u64 count[op_ret+1]={0};
#ifndef _MSC_VER
const void* oprs[]=
{
&&nop, &&intg, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil,
&&pstr, &&newv, &&newh, &&newf,
&&happ, &&para, &&defpara,&&dynpara,
&&unot, &&usub, &&add, &&sub,
&&mul, &&div, &&lnk, &&addc,
&&subc, &&mulc, &&divc, &&lnkc,
&&addeq, &&subeq, &&muleq, &&diveq,
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
&&diveqc, &&lnkeqc, &&meq, &&eq,
&&neq, &&less, &&leq, &&grt,
&&geq, &&lessc, &&leqc, &&grtc,
&&geqc, &&pop, &&jmp, &&jt,
&&jf, &&counter, &&findex, &&feach,
&&callg, &&calll, &&upval, &&callv,
&&callvi, &&callh, &&callfv, &&callfh,
&&callb, &&slcbegin, &&slcend, &&slc,
&&slc2, &&mcallg, &&mcalll, &&mupval,
&&mcallv, &&mcallh, &&ret, &&vmexit
&&vmexit, &&intg, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil,
&&pstr, &&newv, &&newh, &&newf,
&&happ, &&para, &&deft, &&dyn,
&&unot, &&usub, &&add, &&sub,
&&mul, &&div, &&lnk, &&addc,
&&subc, &&mulc, &&divc, &&lnkc,
&&addeq, &&subeq, &&muleq, &&diveq,
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
&&diveqc, &&lnkeqc, &&meq, &&eq,
&&neq, &&less, &&leq, &&grt,
&&geq, &&lessc, &&leqc, &&grtc,
&&geqc, &&pop, &&jmp, &&jt,
&&jf, &&cnt, &&findex, &&feach,
&&callg, &&calll, &&upval, &&callv,
&&callvi, &&callh, &&callfv, &&callfh,
&&callb, &&slcbeg, &&slcend, &&slc,
&&slc2, &&mcallg, &&mcalll, &&mupval,
&&mcallv, &&mcallh, &&ret
};
bytecode=gen.get_code().data();
std::vector<const void*> code;
for(auto& i:gen.get_code())
for(auto& i:gen.codes())
{
code.push_back(opr_table[i.op]);
code.push_back(oprs[i.op]);
imm.push_back(i.num);
}
// set canary and program counter
auto canary=gc.stack+STACK_MAX_DEPTH-1;
pc=0;
// goto the first operand
goto *code[pc];
#else
typedef void (nasal_dbg::*nafunc)();
const nafunc oprs[]=
{
nullptr, &nasal_dbg::o_intg,
&nasal_dbg::o_intl, &nasal_dbg::o_loadg,
&nasal_dbg::o_loadl, &nasal_dbg::o_loadu,
&nasal_dbg::o_pnum, &nasal_dbg::o_pnil,
&nasal_dbg::o_pstr, &nasal_dbg::o_newv,
&nasal_dbg::o_newh, &nasal_dbg::o_newf,
&nasal_dbg::o_happ, &nasal_dbg::o_para,
&nasal_dbg::o_deft, &nasal_dbg::o_dyn,
&nasal_dbg::o_unot, &nasal_dbg::o_usub,
&nasal_dbg::o_add, &nasal_dbg::o_sub,
&nasal_dbg::o_mul, &nasal_dbg::o_div,
&nasal_dbg::o_lnk, &nasal_dbg::o_addc,
&nasal_dbg::o_subc, &nasal_dbg::o_mulc,
&nasal_dbg::o_divc, &nasal_dbg::o_lnkc,
&nasal_dbg::o_addeq, &nasal_dbg::o_subeq,
&nasal_dbg::o_muleq, &nasal_dbg::o_diveq,
&nasal_dbg::o_lnkeq, &nasal_dbg::o_addeqc,
&nasal_dbg::o_subeqc, &nasal_dbg::o_muleqc,
&nasal_dbg::o_diveqc, &nasal_dbg::o_lnkeqc,
&nasal_dbg::o_meq, &nasal_dbg::o_eq,
&nasal_dbg::o_neq, &nasal_dbg::o_less,
&nasal_dbg::o_leq, &nasal_dbg::o_grt,
&nasal_dbg::o_geq, &nasal_dbg::o_lessc,
&nasal_dbg::o_leqc, &nasal_dbg::o_grtc,
&nasal_dbg::o_geqc, &nasal_dbg::o_pop,
&nasal_dbg::o_jmp, &nasal_dbg::o_jt,
&nasal_dbg::o_jf, &nasal_dbg::o_cnt,
&nasal_dbg::o_findex, &nasal_dbg::o_feach,
&nasal_dbg::o_callg, &nasal_dbg::o_calll,
&nasal_dbg::o_upval, &nasal_dbg::o_callv,
&nasal_dbg::o_callvi, &nasal_dbg::o_callh,
&nasal_dbg::o_callfv, &nasal_dbg::o_callfh,
&nasal_dbg::o_callb, &nasal_dbg::o_slcbeg,
&nasal_dbg::o_slcend, &nasal_dbg::o_slc,
&nasal_dbg::o_slc2, &nasal_dbg::o_mcallg,
&nasal_dbg::o_mcalll, &nasal_dbg::o_mupval,
&nasal_dbg::o_mcallv, &nasal_dbg::o_mcallh,
&nasal_dbg::o_ret
};
std::vector<u32> code;
for(auto& i:gen.codes())
{
code.push_back(i.op);
imm.push_back(i.num);
}
while(oprs[code[pc]]){
interact();
++count[code[pc]];
(this->*oprs[code[pc]])();
if(top>=canary)
break;
++pc;
}
#endif
vmexit:
if(gc.top>=canary)
if(top>=canary)
die("stack overflow");
if(opcnt)
opcallsort(count);
gc.clear();
imm.clear();
printf("[debug] debugger exited\n");
std::cout<<"[debug] debugger exited\n";
return;
#define dbg(op) {interact();op();if(gc.top<canary)goto *code[++pc];goto vmexit;}
#ifndef _MSC_VER
#define dbg(op,num) {\
interact();\
op();\
++count[num];\
if(top<canary)\
goto *code[++pc];\
goto vmexit;}
nop: dbg(opr_nop );
intg: dbg(opr_intg );
intl: dbg(opr_intl );
loadg: dbg(opr_loadg );
loadl: dbg(opr_loadl );
loadu: dbg(opr_loadu );
pnum: dbg(opr_pnum );
pnil: dbg(opr_pnil );
pstr: dbg(opr_pstr );
newv: dbg(opr_newv );
newh: dbg(opr_newh );
newf: dbg(opr_newf );
happ: dbg(opr_happ );
para: dbg(opr_para );
defpara: dbg(opr_defpara );
dynpara: dbg(opr_dynpara );
unot: dbg(opr_unot );
usub: dbg(opr_usub );
add: dbg(opr_add );
sub: dbg(opr_sub );
mul: dbg(opr_mul );
div: dbg(opr_div );
lnk: dbg(opr_lnk );
addc: dbg(opr_addc );
subc: dbg(opr_subc );
mulc: dbg(opr_mulc );
divc: dbg(opr_divc );
lnkc: dbg(opr_lnkc );
addeq: dbg(opr_addeq );
subeq: dbg(opr_subeq );
muleq: dbg(opr_muleq );
diveq: dbg(opr_diveq );
lnkeq: dbg(opr_lnkeq );
addeqc: dbg(opr_addeqc );
subeqc: dbg(opr_subeqc );
muleqc: dbg(opr_muleqc );
diveqc: dbg(opr_diveqc );
lnkeqc: dbg(opr_lnkeqc );
meq: dbg(opr_meq );
eq: dbg(opr_eq );
neq: dbg(opr_neq );
less: dbg(opr_less );
leq: dbg(opr_leq );
grt: dbg(opr_grt );
geq: dbg(opr_geq );
lessc: dbg(opr_lessc );
leqc: dbg(opr_leqc );
grtc: dbg(opr_grtc );
geqc: dbg(opr_geqc );
pop: dbg(opr_pop );
jmp: dbg(opr_jmp );
jt: dbg(opr_jt );
jf: dbg(opr_jf );
counter: dbg(opr_counter );
findex: dbg(opr_findex );
feach: dbg(opr_feach );
callg: dbg(opr_callg );
calll: dbg(opr_calll );
upval: dbg(opr_upval );
callv: dbg(opr_callv );
callvi: dbg(opr_callvi );
callh: dbg(opr_callh );
callfv: dbg(opr_callfv );
callfh: dbg(opr_callfh );
callb: dbg(opr_callb );
slcbegin:dbg(opr_slcbegin);
slcend: dbg(opr_slcend );
slc: dbg(opr_slc );
slc2: dbg(opr_slc2 );
mcallg: dbg(opr_mcallg );
mcalll: dbg(opr_mcalll );
mupval: dbg(opr_mupval );
mcallv: dbg(opr_mcallv );
mcallh: dbg(opr_mcallh );
ret: dbg(opr_ret );
intg: dbg(o_intg ,op_intg );
intl: dbg(o_intl ,op_intl );
loadg: dbg(o_loadg ,op_loadg );
loadl: dbg(o_loadl ,op_loadl );
loadu: dbg(o_loadu ,op_loadu );
pnum: dbg(o_pnum ,op_pnum );
pnil: dbg(o_pnil ,op_pnil );
pstr: dbg(o_pstr ,op_pstr );
newv: dbg(o_newv ,op_newv );
newh: dbg(o_newh ,op_newh );
newf: dbg(o_newf ,op_newf );
happ: dbg(o_happ ,op_happ );
para: dbg(o_para ,op_para );
deft: dbg(o_deft ,op_deft );
dyn: dbg(o_dyn ,op_dyn );
unot: dbg(o_unot ,op_unot );
usub: dbg(o_usub ,op_usub );
add: dbg(o_add ,op_add );
sub: dbg(o_sub ,op_sub );
mul: dbg(o_mul ,op_mul );
div: dbg(o_div ,op_div );
lnk: dbg(o_lnk ,op_lnk );
addc: dbg(o_addc ,op_addc );
subc: dbg(o_subc ,op_subc );
mulc: dbg(o_mulc ,op_mulc );
divc: dbg(o_divc ,op_divc );
lnkc: dbg(o_lnkc ,op_lnkc );
addeq: dbg(o_addeq ,op_addeq );
subeq: dbg(o_subeq ,op_subeq );
muleq: dbg(o_muleq ,op_muleq );
diveq: dbg(o_diveq ,op_diveq );
lnkeq: dbg(o_lnkeq ,op_lnkeq );
addeqc: dbg(o_addeqc,op_addeqc);
subeqc: dbg(o_subeqc,op_subeqc);
muleqc: dbg(o_muleqc,op_muleqc);
diveqc: dbg(o_diveqc,op_diveqc);
lnkeqc: dbg(o_lnkeqc,op_lnkeqc);
meq: dbg(o_meq ,op_meq );
eq: dbg(o_eq ,op_eq );
neq: dbg(o_neq ,op_neq );
less: dbg(o_less ,op_less );
leq: dbg(o_leq ,op_leq );
grt: dbg(o_grt ,op_grt );
geq: dbg(o_geq ,op_geq );
lessc: dbg(o_lessc ,op_lessc );
leqc: dbg(o_leqc ,op_leqc );
grtc: dbg(o_grtc ,op_grtc );
geqc: dbg(o_geqc ,op_geqc );
pop: dbg(o_pop ,op_pop );
jmp: dbg(o_jmp ,op_jmp );
jt: dbg(o_jt ,op_jt );
jf: dbg(o_jf ,op_jf );
cnt: dbg(o_cnt ,op_cnt );
findex: dbg(o_findex,op_findex);
feach: dbg(o_feach ,op_feach );
callg: dbg(o_callg ,op_callg );
calll: dbg(o_calll ,op_calll );
upval: dbg(o_upval ,op_upval );
callv: dbg(o_callv ,op_callv );
callvi: dbg(o_callvi,op_callvi);
callh: dbg(o_callh ,op_callh );
callfv: dbg(o_callfv,op_callfv);
callfh: dbg(o_callfh,op_callfh);
callb: dbg(o_callb ,op_callb );
slcbeg: dbg(o_slcbeg,op_slcbeg);
slcend: dbg(o_slcend,op_slcend);
slc: dbg(o_slc ,op_slc );
slc2: dbg(o_slc2 ,op_slc2 );
mcallg: dbg(o_mcallg,op_mcallg);
mcalll: dbg(o_mcalll,op_mcalll);
mupval: dbg(o_mupval,op_mupval);
mcallv: dbg(o_mcallv,op_mcallv);
mcallh: dbg(o_mcallh,op_mcallh);
ret: dbg(o_ret ,op_ret );
#endif
}
#endif

View File

@@ -5,13 +5,13 @@
#include <fstream>
#include <cstring>
class file_line
class fstreamline
{
protected:
std::string file;
std::vector<std::string> res;
string file;
std::vector<string> res;
public:
void load(const std::string& f)
void load(const string& f)
{
if(file==f) // don't need to load a loaded file
return;
@@ -23,7 +23,7 @@ public:
std::cerr<<"[src] cannot open file <"<<f<<">\n";
std::exit(1);
}
std::string line;
string line;
while(!fin.eof())
{
std::getline(fin,line);
@@ -32,44 +32,38 @@ public:
}
void clear()
{
std::vector<std::string> tmp;
std::vector<string> tmp;
res.swap(tmp);
}
const std::string& operator[](const uint32_t line){return res[line];}
const std::string& name(){return file;}
size_t size(){return res.size();}
const string& operator[](usize n){return res[n];}
const string& name(){return file;}
usize size(){return res.size();}
};
class nasal_err:public file_line
class nasal_err:public fstreamline
{
private:
uint32_t error;
u32 error;
public:
void err(const char* stage,const std::string& info)
nasal_err():error(0){}
void err(const char* stage,const string& info)
{
++error;
std::cerr<<"["<<stage<<"] "<<info<<'\n';
std::cerr<<"["<<stage<<"] "<<info<<"\n";
}
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
void err(const char* stage,u32 line,u32 column,const string& info)
{
++error;
if(!line)
{
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
return;
}
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<res[line-1]<<'\n';
for(int i=0;i<(int)column-1;++i)
std::cerr<<char(" \t"[res[line-1][i]=='\t']);
const string& code=res[line-1];
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<code<<"\n";
for(i32 i=0;i<(i32)column-1;++i)
std::cerr<<char(" \t"[code[i]=='\t']);
std::cerr<<"^\n";
}
void err(const char* stage,uint32_t line,const std::string& info)
void err(const char* stage,u32 line,const string& info)
{
++error;
if(!line)
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
else
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
}
void chkerr(){if(error)std::exit(1);}
};

View File

@@ -1,261 +1,370 @@
#ifndef __NASAL_GC_H__
#define __NASAL_GC_H__
enum nasal_type
{
enum vm_type:u8{
/* none-gc object */
vm_none=0,
vm_cnt,
vm_addr,
vm_ret,
vm_nil,
vm_num,
/* gc object */
vm_str,
vm_func,
vm_vec,
vm_hash,
vm_func,
vm_upval,
vm_obj,
vm_type_size
vm_co,
vm_tsize
};
const u32 gc_tsize=vm_tsize-vm_str;
// change parameters here to make your own efficient gc
// better set bigger number on vm_vec
const uint32_t increment[vm_type_size]=
const u32 ini[gc_tsize]=
{
/* none-gc object */
0, // vm_none, error type
0, // vm_count, used in foreach/forindex
0, // vm_ret, used to store call-return address
0, // vm_nil
0, // vm_num
/* gc object */
256, // vm_str
128, // vm_str
128, // vm_vec
32, // vm_hash
512, // vm_func
512, // vm_upval
0, // vm_obj
0 // vm_co
};
const u32 incr[gc_tsize]=
{
256, // vm_str
512, // vm_vec
128, // vm_hash
16 // vm_obj
512, // vm_hash
512, // vm_func
128, // vm_upval
128, // vm_obj
16 // vm_co
};
struct nasal_vec; // vector
struct nasal_hash;// hashmap(dict)
struct nasal_func;// function(lambda)
struct nasal_obj; // special objects
struct nasal_val; // nasal_val includes gc-managed types
struct nas_vec; // vector
struct nas_hash; // hashmap(dict)
struct nas_func; // function(lambda)
struct nas_upval;// upvalue
struct nas_obj; // special objects
struct nas_co; // coroutine
struct nas_val; // nasal_val includes gc-managed types
struct nasal_ref
struct nas_ref
{
uint8_t type;
u8 type;
union
{
uint32_t ret;
int64_t cnt;
double num;
nasal_val* gcobj;
}value;
u32 ret;
i64 cnt;
f64 num;
nas_ref* addr;
nas_val* gcobj;
} val;
nasal_ref(const uint8_t t=vm_none):type(t){}
nasal_ref(const uint8_t t,const uint32_t n):type(t){value.ret=n;}
nasal_ref(const uint8_t t,const int64_t n):type(t){value.cnt=n;}
nasal_ref(const uint8_t t,const double n):type(t){value.num=n;}
nasal_ref(const uint8_t t,nasal_val* n):type(t){value.gcobj=n;}
nasal_ref(const nasal_ref& nr):type(nr.type),value(nr.value){}
nasal_ref& operator=(const nasal_ref& nr)
{
type=nr.type;
value=nr.value;
return *this;
}
bool operator==(const nasal_ref& nr){return type==nr.type && value.gcobj==nr.value.gcobj;}
bool operator!=(const nasal_ref& nr){return type!=nr.type || value.gcobj!=nr.value.gcobj;}
// vm_none/vm_nil
nas_ref(const u8 t=vm_none):type(t){}
// vm_ret
nas_ref(const u8 t,const u32 n):type(t){val.ret=n;}
// vm_cnt
nas_ref(const u8 t,const i64 n):type(t){val.cnt=n;}
// vm_num
nas_ref(const u8 t,const f64 n):type(t){val.num=n;}
// vm_str/vm_func/vm_vec/vm_hash/vm_upval/vm_obj
nas_ref(const u8 t,nas_val* n):type(t){val.gcobj=n;}
// vm_addr
nas_ref(const u8 t,nas_ref* n):type(t){val.addr=n;}
nas_ref(const nas_ref& nr):type(nr.type),val(nr.val){}
bool operator==(const nas_ref& nr){return type==nr.type && val.gcobj==nr.val.gcobj;}
bool operator!=(const nas_ref& nr){return type!=nr.type || val.gcobj!=nr.val.gcobj;}
// number and string can be translated to each other
double to_number();
std::string to_string();
void print();
inline uint32_t ret ();
inline int64_t& cnt ();
inline double& num ();
inline std::string* str ();
inline nasal_vec* vec ();
inline nasal_hash* hash();
inline nasal_func* func();
inline nasal_obj* obj ();
f64 tonum();
string tostr();
void print();
bool objchk(u32);
inline nas_ref* addr();
inline u32 ret ();
inline i64& cnt ();
inline f64 num ();
inline string& str ();
inline nas_vec& vec ();
inline nas_hash& hash();
inline nas_func& func();
inline nas_upval& upval();
inline nas_obj& obj ();
inline nas_co& co ();
};
struct nasal_vec
struct nas_vec
{
std::vector<nasal_ref> elems;
bool printed;
std::vector<nas_ref> elems;
void print();
nasal_ref get_val(const int);
nasal_ref* get_mem(const int);
nas_vec():printed(false){}
void print();
usize size(){return elems.size();}
nas_ref get_val(const i32);
nas_ref* get_mem(const i32);
};
struct nasal_hash
struct nas_hash
{
std::unordered_map<std::string,nasal_ref> elems;
bool printed;
std::unordered_map<string,nas_ref> elems;
void print();
nasal_ref get_val(const std::string&);
nasal_ref* get_mem(const std::string&);
nas_hash():printed(false){}
void print();
usize size(){return elems.size();}
nas_ref get_val(const string&);
nas_ref* get_mem(const string&);
};
struct nasal_func
struct nas_func
{
int32_t dynpara; // dynamic parameter name index in hash.
uint32_t entry; // pc will set to entry-1 to call this function
uint32_t psize; // used to load default parameters to a new function
uint32_t lsize; // used to expand memory space for local values on stack
std::vector<nasal_ref> local; // local scope with default value(nasal_ref)
std::vector<nasal_ref> upvalue; // closure
std::unordered_map<std::string,size_t> keys; // parameter name table, size_t begins from 1
i32 dpara; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
u32 psize; // used to load default parameters to a new function
u32 lsize; // used to expand memory space for local values on stack
std::vector<nas_ref> local; // local scope with default value(nas_ref)
std::vector<nas_ref> upval; // closure
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
nasal_func():dynpara(-1){}
nas_func():dpara(-1),entry(0),psize(0),lsize(0){}
void clear();
};
struct nasal_obj
struct nas_upval
{
uint32_t type;
void* ptr;
nasal_obj():ptr(nullptr){}
void clear(){ptr=nullptr;}
bool onstk;
u32 size;
nas_ref* stk;
std::vector<nas_ref> elems;
nas_upval(){onstk=true;stk=nullptr;size=0;}
nas_ref& operator[](usize n){return onstk?stk[n]:elems[n];}
void clear(){onstk=true;elems.clear();size=0;}
};
const uint8_t GC_UNCOLLECTED=0;
const uint8_t GC_COLLECTED =1;
const uint8_t GC_FOUND =2;
struct nasal_val
struct nas_obj
{
uint8_t mark;
uint8_t type;
uint8_t unmut; // used to mark if a string is unmutable
enum obj_t:u32
{
null,
file,
dir,
dylib,
faddr
};
/* RAII constructor */
/* new object is initialized when creating */
u32 type;
void* ptr;
/* RAII destroyer */
/* default destroyer does nothing */
typedef void (*dest)(void*);
dest dtor;
nas_obj():type(obj_t::null),ptr(nullptr),dtor(nullptr){}
~nas_obj(){clear();}
void set(u32 t=obj_t::null,void* p=nullptr,dest d=nullptr)
{
type=t;
ptr=p;
dtor=d;
}
void clear()
{
if(dtor && ptr)
dtor(ptr);
ptr=nullptr;
dtor=nullptr;
}
};
struct nas_co
{
enum costat:u32
{
suspended,
running,
dead
};
nas_ref stack[STACK_DEPTH];
u32 pc;
nas_ref* top;
nas_ref* canary;
nas_ref* localr;
nas_ref* memr;
nas_ref funcr;
nas_ref upvalr;
u32 status;
nas_co():
pc(0),top(stack),
canary(stack+STACK_DEPTH-1),
localr(nullptr),
memr(nullptr),
funcr({vm_nil,(f64)0}),
upvalr({vm_nil,(f64)0}),
status(nas_co::suspended)
{
for(u32 i=0;i<STACK_DEPTH;++i)
stack[i]={vm_nil,(f64)0};
}
void clear()
{
for(u32 i=0;i<STACK_DEPTH;++i)
stack[i]={vm_nil,(f64)0};
pc=0;
localr=nullptr;
memr=nullptr;
top=stack;
status=nas_co::suspended;
funcr={vm_nil,(f64)0};
}
};
const u8 GC_UNCOLLECTED=0;
const u8 GC_COLLECTED =1;
const u8 GC_FOUND =2;
struct nas_val
{
u8 mark;
u8 type;
u8 unmut; // used to mark if a string is unmutable
union
{
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
nasal_func* func;
nasal_obj* obj;
string* str;
nas_vec* vec;
nas_hash* hash;
nas_func* func;
nas_upval* upval;
nas_obj* obj;
nas_co* co;
}ptr;
nasal_val(uint8_t);
~nasal_val();
nas_val(u8);
~nas_val();
};
nasal_ref nasal_vec::get_val(const int index)
nas_ref nas_vec::get_val(const i32 n)
{
int size=elems.size();
if(index<-size || index>=size)
i32 size=elems.size();
if(n<-size || n>=size)
return {vm_none};
return elems[index>=0?index:index+size];
return elems[n>=0?n:n+size];
}
nasal_ref* nasal_vec::get_mem(const int index)
nas_ref* nas_vec::get_mem(const i32 n)
{
int size=elems.size();
if(index<-size || index>=size)
i32 size=elems.size();
if(n<-size || n>=size)
return nullptr;
return &elems[index>=0?index:index+size];
return &elems[n>=0?n:n+size];
}
void nasal_vec::print()
void nas_vec::print()
{
static int depth=0;
if(!elems.size() || depth>8)
if(!elems.size() || printed)
{
std::cout<<(elems.size()?"[..]":"[]");
return;
}
++depth;
size_t iter=0;
printed=true;
usize iter=0;
usize size=elems.size();
std::cout<<'[';
for(auto& i:elems)
{
i.print();
std::cout<<",]"[(++iter)==elems.size()];
std::cout<<",]"[(++iter)==size];
}
--depth;
printed=false;
}
nasal_ref nasal_hash::get_val(const std::string& key)
nas_ref nas_hash::get_val(const string& key)
{
if(elems.count(key))
return elems[key];
else if(elems.count("parents"))
{
nasal_ref ret(vm_none);
nasal_ref val=elems["parents"];
nas_ref ret(vm_none);
nas_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
for(auto& i:val.vec().elems)
{
if(i.type==vm_hash)
ret=i.hash()->get_val(key);
ret=i.hash().get_val(key);
if(ret.type!=vm_none)
return ret;
}
}
return {vm_none};
}
nasal_ref* nasal_hash::get_mem(const std::string& key)
nas_ref* nas_hash::get_mem(const string& key)
{
if(elems.count(key))
return &elems[key];
else if(elems.count("parents"))
{
nasal_ref* addr=nullptr;
nasal_ref val=elems["parents"];
nas_ref* addr=nullptr;
nas_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
for(auto& i:val.vec().elems)
{
if(i.type==vm_hash)
addr=i.hash()->get_mem(key);
addr=i.hash().get_mem(key);
if(addr)
return addr;
}
}
return nullptr;
}
void nasal_hash::print()
void nas_hash::print()
{
static int depth=0;
if(!elems.size() || depth>8)
if(!elems.size() || printed)
{
std::cout<<(elems.size()?"{..}":"{}");
return;
}
++depth;
size_t iter=0;
printed=true;
usize iter=0;
usize size=elems.size();
std::cout<<'{';
for(auto& i:elems)
{
std::cout<<i.first<<':';
i.second.print();
std::cout<<",}"[(++iter)==elems.size()];
std::cout<<",}"[(++iter)==size];
}
--depth;
printed=false;
}
void nasal_func::clear()
void nas_func::clear()
{
dynpara=-1;
dpara=-1;
local.clear();
upvalue.clear();
upval.clear();
keys.clear();
}
nasal_val::nasal_val(uint8_t val_type)
nas_val::nas_val(u8 val_type)
{
mark=GC_COLLECTED;
type=val_type;
unmut=0;
switch(val_type)
{
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec; break;
case vm_hash: ptr.hash=new nasal_hash; break;
case vm_func: ptr.func=new nasal_func; break;
case vm_obj: ptr.obj=new nasal_obj; break;
case vm_str: ptr.str=new 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_obj; break;
case vm_co: ptr.co=new nas_co; break;
}
}
nasal_val::~nasal_val()
nas_val::~nas_val()
{
switch(type)
{
@@ -263,98 +372,178 @@ nasal_val::~nasal_val()
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;
}
type=vm_nil;
}
double nasal_ref::to_number()
f64 nas_ref::tonum()
{
return type!=vm_str?value.num:str2num(str()->c_str());
return type!=vm_str?val.num:str2num(str().c_str());
}
std::string nasal_ref::to_string()
string nas_ref::tostr()
{
if(type==vm_str)
return *str();
return str();
else if(type==vm_num)
return std::to_string(num());
{
string tmp=std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1,string::npos);
tmp.erase(tmp.find_last_not_of('.')+1,string::npos);
return tmp;
}
return "";
}
void nasal_ref::print()
void nas_ref::print()
{
switch(type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<value.num; break;
case vm_str: std::cout<<*(this->str());break;
case vm_vec: this->vec()->print(); break;
case vm_hash: this->hash()->print(); break;
case vm_num: std::cout<<val.num; break;
case vm_str: std::cout<<str(); break;
case vm_vec: vec().print(); break;
case vm_hash: hash().print(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
case vm_co: std::cout<<"<coroutine>"; break;
}
}
inline uint32_t nasal_ref::ret (){return value.ret; }
inline int64_t& nasal_ref::cnt (){return value.cnt; }
inline double& nasal_ref::num (){return value.num; }
inline std::string* nasal_ref::str (){return value.gcobj->ptr.str; }
inline nasal_vec* nasal_ref::vec (){return value.gcobj->ptr.vec; }
inline nasal_hash* nasal_ref::hash(){return value.gcobj->ptr.hash;}
inline nasal_func* nasal_ref::func(){return value.gcobj->ptr.func;}
inline nasal_obj* nasal_ref::obj (){return value.gcobj->ptr.obj; }
bool nas_ref::objchk(u32 objtype)
{
return type==vm_obj && obj().type==objtype && obj().ptr;
}
inline nas_ref* nas_ref::addr (){return val.addr; }
inline u32 nas_ref::ret (){return val.ret; }
inline i64& nas_ref::cnt (){return val.cnt; }
inline f64 nas_ref::num (){return val.num; }
inline string& nas_ref::str (){return *val.gcobj->ptr.str; }
inline nas_vec& nas_ref::vec (){return *val.gcobj->ptr.vec; }
inline nas_hash& nas_ref::hash (){return *val.gcobj->ptr.hash; }
inline nas_func& nas_ref::func (){return *val.gcobj->ptr.func; }
inline nas_upval& nas_ref::upval(){return *val.gcobj->ptr.upval;}
inline nas_obj& nas_ref::obj (){return *val.gcobj->ptr.obj; }
inline nas_co& nas_ref::co (){return *val.gcobj->ptr.co; }
const nas_ref zero={vm_num,(f64)0};
const nas_ref one ={vm_num,(f64)1};
const nas_ref nil ={vm_nil,(f64)0};
const uint32_t STACK_MAX_DEPTH=4095;
const nasal_ref zero={vm_num,(double)0};
const nasal_ref one ={vm_num,(double)1};
const nasal_ref nil ={vm_nil,nullptr};
struct nasal_gc
{
nasal_ref stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow
nasal_ref* top; // stack top
std::vector<nasal_ref> strs; // reserved address for const vm_str
std::vector<nasal_val*> memory; // gc memory
std::queue<nasal_val*> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
uint64_t size[vm_type_size];
uint64_t count[vm_type_size];
void mark();
void sweep();
void init(const std::vector<std::string>&);
void clear();
void info();
nasal_ref alloc(const uint8_t);
nasal_ref builtin_alloc(const uint8_t);
/* main context */
struct
{
u32 pc;
nas_ref* top;
nas_ref* localr;
nas_ref* memr;
nas_ref funcr;
nas_ref upvalr;
nas_ref* canary;
nas_ref* stack;
} mctx;
/* runtime context */
u32& pc; // program counter
nas_ref*& localr;// local scope register
nas_ref*& memr; // used for mem_call
nas_ref& funcr; // function register
nas_ref& upvalr;// upvalue register
nas_ref*& canary;// avoid stackoverflow
nas_ref*& top; // stack top
nas_ref* stack; // stack pointer
nas_co* cort; // running coroutine
nas_ref temp; // temporary place used in builtin/module functions
/* constants and memory pool */
std::vector<nas_ref> strs; // reserved address for const vm_str
std::vector<nas_ref> env_argv; // command line arguments
std::vector<nas_val*> memory; // gc memory
std::queue<nas_val*> unused[gc_tsize]; // gc free list
/* values for analysis */
u64 size[gc_tsize];
u64 count[gc_tsize];
u64 allocc[gc_tsize];
nasal_gc(
u32& _pc,
nas_ref*& _localr,
nas_ref*& _memr,
nas_ref& _funcr,
nas_ref& _upvalr,
nas_ref*& _canary,
nas_ref*& _top,
nas_ref* _stk):
pc(_pc),localr(_localr),memr(_memr),funcr(_funcr),upvalr(_upvalr),
canary(_canary),top(_top),stack(_stk),cort(nullptr),temp(nil){}
void mark();
void sweep();
void init(const std::vector<string>&,const std::vector<string>&);
void clear();
void info();
nas_ref alloc(const u8);
nas_ref newstr(char);
nas_ref newstr(const char*);
nas_ref newstr(const string&);
void ctxchg(nas_co&);
void ctxreserve();
};
/* gc functions */
void nasal_gc::mark()
{
std::queue<nasal_ref> bfs;
for(auto& i:local)
bfs.push(i);
for(nasal_ref* i=stack;i<=top;++i)
std::queue<nas_ref> bfs;
// scan coroutine process stack when coroutine ptr is not null
// scan main process stack when coroutine ptr is null
// this scan process must execute because when running coroutine,
// the nasal_co related to it will not update it's context(like `top`) until the coroutine suspends or exits.
for(nas_ref* i=stack;i<=top;++i)
bfs.push(*i);
bfs.push(funcr);
bfs.push(upvalr);
bfs.push(temp);
if(cort) // scan main process stack
{
for(nas_ref* i=mctx.stack;i<=mctx.top;++i)
bfs.push(*i);
bfs.push(mctx.funcr);
bfs.push(mctx.upvalr);
}
while(!bfs.empty())
{
nasal_ref tmp=bfs.front();
nas_ref tmp=bfs.front();
bfs.pop();
if(tmp.type<=vm_num || tmp.value.gcobj->mark) continue;
tmp.value.gcobj->mark=GC_FOUND;
if(tmp.type<=vm_num || tmp.val.gcobj->mark) continue;
tmp.val.gcobj->mark=GC_FOUND;
switch(tmp.type)
{
case vm_vec:
for(auto& i:tmp.vec()->elems)
for(auto& i:tmp.vec().elems)
bfs.push(i);
break;
case vm_hash:
for(auto& i:tmp.hash()->elems)
for(auto& i:tmp.hash().elems)
bfs.push(i.second);
break;
case vm_func:
for(auto& i:tmp.func()->local)
for(auto& i:tmp.func().local)
bfs.push(i);
for(auto& i:tmp.func()->upvalue)
for(auto& i:tmp.func().upval)
bfs.push(i);
break;
case vm_upval:
for(auto& i:tmp.upval().elems)
bfs.push(i);
break;
case vm_co:
bfs.push(tmp.co().funcr);
bfs.push(tmp.co().upvalr);
for(nas_ref* i=tmp.co().stack;i<=tmp.co().top;++i)
bfs.push(*i);
break;
}
}
}
@@ -366,38 +555,51 @@ void nasal_gc::sweep()
{
switch(i->type)
{
case vm_str: i->ptr.str->clear(); break;
case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash:i->ptr.hash->elems.clear();break;
case vm_func:i->ptr.func->clear(); break;
case vm_obj: i->ptr.obj->clear(); break;
case vm_str: i->ptr.str->clear(); break;
case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash: i->ptr.hash->elems.clear();break;
case vm_func: i->ptr.func->clear(); break;
case vm_upval:i->ptr.upval->clear(); break;
case vm_obj: i->ptr.obj->clear(); break;
case vm_co: i->ptr.co->clear(); break;
}
free_list[i->type].push(i);
unused[i->type-vm_str].push(i);
i->mark=GC_COLLECTED;
}
else if(i->mark==GC_FOUND)
i->mark=GC_UNCOLLECTED;
}
}
void nasal_gc::init(const std::vector<std::string>& s)
void nasal_gc::init(const std::vector<string>& s,const std::vector<string>& argv)
{
for(uint8_t i=0;i<vm_type_size;++i)
size[i]=count[i]=0;
for(uint8_t i=vm_str;i<vm_type_size;++i)
for(uint32_t j=0;j<increment[i];++j)
// initiaize function register
funcr=nil;
for(u8 i=0;i<gc_tsize;++i)
size[i]=count[i]=allocc[i]=0;
for(u8 i=0;i<gc_tsize;++i)
for(u32 j=0;j<ini[i];++j)
{
nasal_val* tmp=new nasal_val(i);
nas_val* tmp=new nas_val(i+vm_str);
memory.push_back(tmp);
free_list[i].push(tmp);
unused[i].push(tmp);
}
top=stack;
cort=nullptr;
// init constant strings
strs.resize(s.size());
for(uint32_t i=0;i<strs.size();++i)
for(u32 i=0;i<strs.size();++i)
{
strs[i]={vm_str,new nasal_val(vm_str)};
strs[i].value.gcobj->unmut=1;
*strs[i].str()=s[i];
strs[i]={vm_str,new nas_val(vm_str)};
strs[i].val.gcobj->unmut=1;
strs[i].str()=s[i];
}
// record arguments
env_argv.resize(argv.size());
for(usize i=0;i<argv.size();++i)
{
env_argv[i]={vm_str,new nas_val(vm_str)};
env_argv[i].val.gcobj->unmut=1;
env_argv[i].str()=argv[i];
}
}
void nasal_gc::clear()
@@ -405,70 +607,111 @@ void nasal_gc::clear()
for(auto i:memory)
delete i;
memory.clear();
for(uint8_t i=0;i<vm_type_size;++i)
while(!free_list[i].empty())
free_list[i].pop();
local.clear();
for(u8 i=0;i<gc_tsize;++i)
while(!unused[i].empty())
unused[i].pop();
for(auto& i:strs)
delete i.value.gcobj;
delete i.val.gcobj;
strs.clear();
env_argv.clear();
}
void nasal_gc::info()
{
const char* name[]={
"null","cnt ","ret ",
"nil ","num ","str ",
"func","vec ","hash","obj "
};
std::cout<<"\ngarbage collector info:\n";
for(uint8_t i=vm_str;i<vm_type_size;++i)
std::cout<<name[i]<<" | "<<count[i]<<"\n";
std::cout<<"\nmemory allocator info(max size):\n";
for(uint8_t i=vm_str;i<vm_type_size;++i)
std::cout<<name[i]<<" | "<<(size[i]+1)*increment[i]<<"(+"<<size[i]<<")\n";
const char* name[]={"str ","vec ","hash ","func ","upval","obj ","co "};
std::cout<<"\ngarbage collector info(gc/alloc)\n";
for(u8 i=0;i<gc_tsize;++i)
if(count[i] || allocc[i])
std::cout<<" "<<name[i]<<" | "<<count[i]<<","<<allocc[i]<<"\n";
std::cout<<"\nmemory allocator info(max size)\n";
for(u8 i=0;i<gc_tsize;++i)
std::cout<<" "<<name[i]<<" | "<<ini[i]+size[i]*incr[i]<<" (+"<<size[i]<<")\n";
}
nasal_ref nasal_gc::alloc(uint8_t type)
nas_ref nasal_gc::alloc(u8 type)
{
if(free_list[type].empty())
const u8 index=type-vm_str;
++allocc[index];
if(unused[index].empty())
{
++count[type];
++count[index];
mark();
sweep();
}
if(free_list[type].empty())
if(unused[index].empty())
{
++size[type];
for(uint32_t i=0;i<increment[type];++i)
++size[index];
for(u32 i=0;i<incr[index];++i)
{
nasal_val* tmp=new nasal_val(type);
nas_val* tmp=new nas_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
unused[index].push(tmp);
}
}
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
nas_ref ret={type,unused[index].front()};
ret.val.gcobj->mark=GC_UNCOLLECTED;
unused[index].pop();
return ret;
}
nasal_ref nasal_gc::builtin_alloc(uint8_t type)
nas_ref nasal_gc::newstr(char c)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc::alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
if(free_list[type].empty())
{
++size[type];
for(uint32_t i=0;i<increment[type];++i)
{
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
}
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
nas_ref s=alloc(vm_str);
s.str()=c;
return s;
}
nas_ref nasal_gc::newstr(const char* buff)
{
nas_ref s=alloc(vm_str);
s.str()=buff;
return s;
}
nas_ref nasal_gc::newstr(const string& buff)
{
nas_ref s=alloc(vm_str);
s.str()=buff;
return s;
}
void nasal_gc::ctxchg(nas_co& ctx)
{
mctx.pc=pc;
mctx.top=top;
mctx.localr=localr;
mctx.memr=memr;
mctx.funcr=funcr;
mctx.upvalr=upvalr;
mctx.canary=canary;
mctx.stack=stack;
pc=ctx.pc;
top=ctx.top;
localr=ctx.localr;
memr=ctx.memr;
funcr=ctx.funcr;
upvalr=ctx.upvalr;
canary=ctx.canary;
stack=ctx.stack;
cort=&ctx;
cort->status=nas_co::running;
}
void nasal_gc::ctxreserve()
{
// pc=0 means this coroutine is finished
cort->status=pc?nas_co::suspended:nas_co::dead;
cort->pc=pc;
cort->localr=localr;
cort->memr=memr;
cort->funcr=funcr;
cort->upvalr=upvalr;
cort->canary=canary;
cort->top=top;
pc=mctx.pc;
localr=mctx.localr;
memr=mctx.memr;
funcr=mctx.funcr;
upvalr=mctx.upvalr;
canary=mctx.canary;
top=mctx.top;
stack=mctx.stack;
cort=nullptr;
}
#endif

View File

@@ -1,26 +1,115 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
#ifdef _MSC_VER
#define F_OK 0
#endif
class nasal_import
{
private:
bool show_path;
bool lib_loaded;
nasal_err& nerr;
std::vector<std::string> files;
bool check_import(const nasal_ast&);
bool check_exist(const std::string&);
std::vector<string> files;
std::vector<string> envpath;
bool imptchk(const nasal_ast&);
bool exist(const string&);
void linker(nasal_ast&,nasal_ast&&);
nasal_ast file_import(nasal_ast&);
nasal_ast load(nasal_ast&,uint16_t);
string path(const nasal_ast&);
string findf(const string&);
nasal_ast fimpt(nasal_ast&);
nasal_ast libimpt();
nasal_ast load(nasal_ast&,u16);
public:
nasal_import(nasal_err& e):nerr(e){}
void link(nasal_parse&,const std::string&);
const std::vector<std::string>& get_file() const {return files;}
nasal_import(nasal_err&);
void link(nasal_parse&,const string&,bool);
const std::vector<string>& filelist() const {return files;}
};
bool nasal_import::check_import(const nasal_ast& node)
nasal_import::nasal_import(nasal_err& e):lib_loaded(false),nerr(e){
#ifdef _WIN32
char sep=';';
#else
char sep=':';
#endif
string PATH=getenv("PATH");
usize last=0,pos=PATH.find(sep,0);
while(pos!=string::npos)
{
string dirpath=PATH.substr(last,pos-last);
if(dirpath.length())
envpath.push_back(dirpath);
last=pos+1;
pos=PATH.find(sep,last);
}
if(last!=PATH.length())
envpath.push_back(PATH.substr(last));
}
string nasal_import::path(const nasal_ast& node)
{
if(node[1].type()==ast_callf)
return node[1][0].str();
string fpath=".";
for(usize i=1;i<node.size();++i)
#ifndef _WIN32
fpath+="/"+node[i].str();
#else
fpath+="\\"+node[i].str();
#endif
return fpath+".nas";
}
string nasal_import::findf(const string& fname)
{
std::vector<string> filepath={fname};
for(auto&p:envpath)
{
#ifdef _WIN32
filepath.push_back(p+"\\"+fname);
#else
filepath.push_back(p+"/"+fname);
#endif
}
for(auto& i:filepath)
if(access(i.c_str(),F_OK)!=-1)
return i;
if(fname=="lib.nas")
#ifdef _WIN32
return findf("stl\\lib.nas");
#else
return findf("stl/lib.nas");
#endif
if(!show_path)
{
nerr.err("link","cannot find file <"+fname+">");
return "";
}
string paths="";
for(auto& i:filepath)
paths+=" "+i+"\n";
nerr.err("link","cannot find file <"+fname+"> in these paths:\n"+paths);
return "";
}
bool nasal_import::imptchk(const nasal_ast& node)
{
// only these two kinds of node can be recognized as 'import':
/*
call
|_id:import
|_callh:stl
|_callh:file
*/
if(node.type()==ast_call && node[0].str()=="import" && node.size()>=2 && node[1].type()==ast_callh)
{
for(usize i=1;i<node.size();++i)
if(node[i].type()!=ast_callh)
return false;
return true;
}
/*
only this kind of node can be recognized as 'import':
call
|_id:import
|_call_func
@@ -28,15 +117,15 @@ only this kind of node can be recognized as 'import':
*/
return (
node.type()==ast_call &&
node.size()==2 &&
node[0].str()=="import" &&
node.size()==2 &&
node[1].type()==ast_callf &&
node[1].size()==1 &&
node[1][0].type()==ast_str
);
}
bool nasal_import::check_exist(const std::string& file)
bool nasal_import::exist(const string& file)
{
// avoid importing the same file
for(auto& fname:files)
@@ -53,16 +142,17 @@ void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
root.add(std::move(i));
}
nasal_ast nasal_import::file_import(nasal_ast& node)
nasal_ast nasal_import::fimpt(nasal_ast& node)
{
nasal_lexer lex(nerr);
nasal_parse par(nerr);
// get filename and set node to ast_null
std::string filename=node[1][0].str();
string filename=path(node);
node.clear();
// avoid infinite loading loop
if(check_exist(filename))
filename=findf(filename);
if(!filename.length() || exist(filename))
return {0,ast_root};
// start importing...
@@ -73,12 +163,41 @@ nasal_ast nasal_import::file_import(nasal_ast& node)
return load(tmp,files.size()-1);
}
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
nasal_ast nasal_import::libimpt()
{
nasal_lexer lex(nerr);
nasal_parse par(nerr);
string filename=findf("lib.nas");
if(!filename.length())
return {0,ast_root};
// avoid infinite loading loop
if(exist(filename))
return {0,ast_root};
// start importing...
lex.scan(filename);
par.compile(lex);
nasal_ast tmp=std::move(par.ast());
// check if tmp has 'import'
return load(tmp,files.size()-1);
}
nasal_ast nasal_import::load(nasal_ast& root,u16 fileindex)
{
nasal_ast new_root(0,ast_root);
if(!lib_loaded)
{
linker(new_root,libimpt());
lib_loaded=true;
}
for(auto& i:root.child())
if(check_import(i))
linker(new_root,file_import(i));
{
if(imptchk(i))
linker(new_root,fimpt(i));
else
break;
}
// add root to the back of new_root
nasal_ast file_head(0,ast_file);
file_head.set_num(fileindex);
@@ -87,13 +206,15 @@ nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
return new_root;
}
void nasal_import::link(nasal_parse& parse,const std::string& self)
void nasal_import::link(nasal_parse& parse,const string& self,bool spath=false)
{
show_path=spath;
// initializing
files={self};
// scan root and import files,then generate a new ast and return to import_ast
// the main file's index is 0
parse.ast()=load(parse.ast(),0);
nerr.chkerr();
}
#endif

View File

@@ -1,7 +1,11 @@
#ifndef __NASAL_LEXER_H__
#define __NASAL_LEXER_H__
#define ID(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
#define ID(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0))
#define HEX(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
#define OCT(c) ('0'<=c&&c<='7')
#define DIGIT(c) ('0'<=c&&c<='9')
@@ -13,13 +17,15 @@
#define CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
#define NOTE(c) (c=='#')
enum token_type
{
tok_null=0,// null token default token type
tok_num, // number basic token type
tok_str, // string basic token type
tok_id, // identifier basic token type
tok_for,tok_forindex,tok_foreach,tok_while,
enum tok:u32{
tok_null=0, // null token (default token type)
tok_num, // number basic token type
tok_str, // string basic token type
tok_id, // identifier basic token type
tok_for, // loop keyword for
tok_forindex,// loop keyword forindex
tok_foreach, // loop keyword foreach
tok_while, // loop keyword while
tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve,
@@ -33,12 +39,10 @@ enum token_type
tok_eof // end of token list
};
struct
{
struct{
const char* str;
const uint32_t tok_type;
}token_table[]=
{
const u32 type;
}tok_table[]={
{"for" ,tok_for },
{"forindex",tok_forindex },
{"foreach" ,tok_foreach },
@@ -89,47 +93,58 @@ struct
struct token
{
uint32_t line;
uint32_t column;
uint32_t type;
std::string str;
token(uint32_t l=0,uint32_t c=0,uint32_t t=tok_null,std::string s="")
u32 line;
u32 col;
u32 type;
string str;
token(u32 l=0,u32 c=0,u32 t=tok_null,const string& s=""):str(s)
{
line=l;
column=c;
col=c;
type=t;
str=s;
}
};
class nasal_lexer
{
private:
uint32_t line;
uint32_t column;
uint32_t ptr;
nasal_err& nerr;
std::string res;
u32 line;
u32 column;
usize ptr;
string res;
nasal_err& nerr;
std::vector<token> tokens;
uint32_t get_type(const std::string&);
void die(std::string info){nerr.err("lexer",line,column,info);};
void open(const std::string&);
std::string id_gen();
std::string num_gen();
std::string str_gen();
u32 get_type(const string&);
void die(const string& info){nerr.err("lexer",line,column,info);}
void open(const string&);
string utf8_gen();
string id_gen();
string num_gen();
string str_gen();
public:
nasal_lexer(nasal_err& e):nerr(e){}
void scan(const std::string&);
nasal_lexer(nasal_err& e):
line(1),
column(0),
ptr(0),
res(""),
nerr(e){}
void scan(const string&);
void print();
const std::vector<token>& get_tokens() const {return tokens;}
const std::vector<token>& result() const {return tokens;}
};
void nasal_lexer::open(const std::string& file)
void nasal_lexer::open(const string& file)
{
struct stat buffer;
if(stat(file.c_str(),&buffer)==0 && !S_ISREG(buffer.st_mode))
{
nerr.err("lexer","<"+file+"> is not a regular file.");
nerr.chkerr();
}
std::ifstream fin(file,std::ios::binary);
if(fin.fail())
nerr.err("lexer","cannot open file <"+file+">.");
nerr.err("lexer","failed to open <"+file+">.");
else
nerr.load(file);
std::stringstream ss;
@@ -137,52 +152,93 @@ void nasal_lexer::open(const std::string& file)
res=ss.str();
}
uint32_t nasal_lexer::get_type(const std::string& tk_str)
u32 nasal_lexer::get_type(const string& str)
{
for(int i=0;token_table[i].str;++i)
if(tk_str==token_table[i].str)
return token_table[i].tok_type;
for(u32 i=0;tok_table[i].str;++i)
if(str==tok_table[i].str)
return tok_table[i].type;
return tok_null;
}
std::string nasal_lexer::id_gen()
string nasal_lexer::utf8_gen()
{
std::string str="";
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
str+=res[ptr++];
column+=str.length();
string str="";
while(ptr<res.size() && res[ptr]<0)
{
string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
if(nbytes)
{
tmp+=res[ptr++];
for(u32 i=0;i<nbytes;++i,++ptr)
if(ptr<res.size() && (res[ptr]&0xc0)==0x80)
tmp+=res[ptr];
if(tmp.length()!=1+nbytes)
{
++column;
string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i)
utf_info+=" 0x"+chrhex(tmp[i]);
die("invalid utf-8 character `"+utf_info+"`, make sure it is utf8-text file.");
std::exit(1);
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
}
else
{
++ptr;
++column;
}
}
return str;
}
std::string nasal_lexer::num_gen()
string nasal_lexer::id_gen()
{
string str="";
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
{
if(res[ptr]<0) // utf-8
str+=utf8_gen();
else // ascii
{
str+=res[ptr++];
++column;
}
}
return str;
}
string nasal_lexer::num_gen()
{
// generate hex number
if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x')
{
std::string str="0x";
string str="0x";
ptr+=2;
while(ptr<res.size() && HEX(res[ptr]))
str+=res[ptr++];
column+=str.length();
if(str.length()<3)// "0x"
die("invalid number:"+str);
die("invalid number `"+str+"`.");
return str;
}
// generate oct number
else if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o')
{
std::string str="0o";
string str="0o";
ptr+=2;
while(ptr<res.size() && OCT(res[ptr]))
str+=res[ptr++];
column+=str.length();
if(str.length()<3)// "0o"
die("invalid number:"+str);
die("invalid number `"+str+"`.");
return str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string str="";
string str="";
while(ptr<res.size() && DIGIT(res[ptr]))
str+=res[ptr++];
if(ptr<res.size() && res[ptr]=='.')
@@ -194,7 +250,7 @@ std::string nasal_lexer::num_gen()
if(str.back()=='.')
{
column+=str.length();
die("invalid number:"+str);
die("invalid number `"+str+"`.");
return "0";
}
}
@@ -209,7 +265,7 @@ std::string nasal_lexer::num_gen()
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
{
column+=str.length();
die("invalid number:"+str);
die("invalid number `"+str+"`.");
return "0";
}
}
@@ -217,10 +273,10 @@ std::string nasal_lexer::num_gen()
return str;
}
std::string nasal_lexer::str_gen()
string nasal_lexer::str_gen()
{
std::string str="";
char begin=res[ptr];
string str="";
const char begin=res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin)
{
@@ -239,6 +295,11 @@ std::string nasal_lexer::str_gen()
case '0': str+='\0'; break;
case 'a': str+='\a'; break;
case 'b': str+='\b'; break;
#ifdef _MSC_VER
case 'e': str+='\033'; break;
#else
case 'e': str+='\e'; break;
#endif
case 't': str+='\t'; break;
case 'n': str+='\n'; break;
case 'v': str+='\v'; break;
@@ -266,17 +327,17 @@ std::string nasal_lexer::str_gen()
return str;
}
void nasal_lexer::scan(const std::string& file)
void nasal_lexer::scan(const string& file)
{
line=1;
column=0;
ptr=0;
open(file);
std::string str;
string str;
while(ptr<res.size())
{
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]==0))
{
// these characters will be ignored, and '\n' will cause ++line
++column;
@@ -290,7 +351,7 @@ void nasal_lexer::scan(const std::string& file)
if(ID(res[ptr]))
{
str=id_gen();
uint32_t type=get_type(str);
u32 type=get_type(str);
tokens.push_back({line,column,type?type:tok_id,str});
}
else if(DIGIT(res[ptr]))
@@ -307,9 +368,9 @@ void nasal_lexer::scan(const std::string& file)
{
str=res[ptr];
++column;
uint32_t type=get_type(str);
u32 type=get_type(str);
if(!type)
die("invalid operator:"+str);
die("invalid operator `"+str+"`.");
tokens.push_back({line,column,type,str});
++ptr;
}
@@ -336,8 +397,8 @@ void nasal_lexer::scan(const std::string& file)
else
{
++column;
++ptr;
die("unknown character.");
char c=res[ptr++];
die("invalid character 0x"+chrhex(c)+".");
}
}
tokens.push_back({line,column,tok_eof,"eof"});
@@ -348,7 +409,7 @@ void nasal_lexer::scan(const std::string& file)
void nasal_lexer::print()
{
for(auto& tok:tokens)
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str)<<")\n";
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str,128)<<")\n";
}
#endif

View File

@@ -12,7 +12,7 @@ void const_str(nasal_ast& root)
void const_num(nasal_ast& root)
{
auto& vec=root.child();
double res;
f64 res;
switch(root.type())
{
case ast_add: res=vec[0].num()+vec[1].num(); break;
@@ -37,21 +37,27 @@ void calc_const(nasal_ast& root)
auto& vec=root.child();
for(auto& i:vec)
calc_const(i);
if(vec.size()==1 && root.type()==ast_neg && vec[0].type()==ast_num)
{
f64 res=-vec[0].num();
root.set_num(res);
root.child().clear();
root.set_type(ast_num);
return;
}
if(vec.size()!=2)
return;
if(root.type()!=ast_add &&
root.type()!=ast_sub &&
root.type()!=ast_mult &&
root.type()!=ast_div &&
root.type()!=ast_link &&
root.type()!=ast_less &&
root.type()!=ast_leq &&
root.type()!=ast_grt &&
root.type()!=ast_geq)
if(root.type()!=ast_add && root.type()!=ast_sub &&
root.type()!=ast_mult && root.type()!=ast_div &&
root.type()!=ast_link && root.type()!=ast_less &&
root.type()!=ast_leq && root.type()!=ast_grt &&
root.type()!=ast_geq)
return;
if(root.type()==ast_link && vec[0].type()==ast_str && vec[1].type()==ast_str)
if(root.type()==ast_link &&
vec[0].type()==ast_str && vec[1].type()==ast_str)
const_str(root);
else if(root.type()!=ast_link && vec[0].type()==ast_num && vec[1].type()==ast_num)
else if(root.type()!=ast_link &&
vec[0].type()==ast_num && vec[1].type()==ast_num)
const_num(root);
}
void optimize(nasal_ast& root)

View File

@@ -41,16 +41,16 @@ class nasal_parse
#define error_line (tokens[ptr].line)
#define is_call(type) ((type)==tok_lcurve || (type)==tok_lbracket || (type)==tok_dot)
private:
uint32_t ptr;
uint32_t in_func; // count function block
uint32_t in_loop; // count loop block
u32 ptr;
u32 in_func; // count function block
u32 in_loop; // count loop block
const token* tokens;// ref from nasal_lexer
nasal_ast root;
nasal_err& nerr;
void die(uint32_t,std::string);
void match(uint32_t type,const char* info=nullptr);
bool check_comma(const uint32_t*);
void die(u32,string,bool);
void match(u32 type,const char* info=nullptr);
bool check_comma(const u32*);
bool check_multi_scalar();
bool check_func_end(const nasal_ast&);
bool check_special_call();
@@ -98,7 +98,7 @@ private:
nasal_ast break_expr();
nasal_ast ret_expr();
public:
nasal_parse(nasal_err& e):nerr(e){}
nasal_parse(nasal_err& e):ptr(0),in_func(0),in_loop(0),tokens(nullptr),nerr(e){}
void print(){root.print(0);}
void compile(const nasal_lexer&);
nasal_ast& ast(){return root;}
@@ -106,7 +106,7 @@ public:
};
void nasal_parse::compile(const nasal_lexer& lexer)
{
tokens=lexer.get_tokens().data();
tokens=lexer.result().data();
ptr=in_func=in_loop=0;
root={1,ast_root};
@@ -117,18 +117,23 @@ void nasal_parse::compile(const nasal_lexer& lexer)
match(tok_semi);
// the last expression can be recognized without semi
else if(need_semi_check(root.child().back()) && tokens[ptr].type!=tok_eof)
die(error_line,"expected \";\"");
die(error_line,"expected \";\"",true);
}
nerr.chkerr();
}
void nasal_parse::die(uint32_t line,std::string info)
void nasal_parse::die(u32 line,string info,bool report_prev=false)
{
int col=(int)tokens[ptr].column-(int)tokens[ptr].str.length();
i32 col=(i32)tokens[ptr].col-(tokens[ptr].type==tok_eof?0:(i32)tokens[ptr].str.length());
if(tokens[ptr].type==tok_str)
col-=2; // tok_str's str has no \"
if(report_prev && ptr) // used to report lack of ',' ';'
{
line=tokens[ptr-1].line;
col=tokens[ptr-1].col+1;
}
nerr.err("parse",line,col<0?0:col,info);
}
void nasal_parse::match(uint32_t type,const char* info)
void nasal_parse::match(u32 type,const char* info)
{
if(tokens[ptr].type!=type)
{
@@ -142,7 +147,7 @@ void nasal_parse::match(uint32_t type,const char* info)
case tok_num:die(error_line,"expected number"); break;
case tok_str:die(error_line,"expected string"); break;
case tok_id: die(error_line,"expected identifier");break;
default: die(error_line,"expected \'"+std::string(token_table[type-tok_for].str)+"\'"); break;
default: die(error_line,"expected '"+string(tok_table[type-tok_for].str)+"'"); break;
}
return;
}
@@ -150,19 +155,19 @@ void nasal_parse::match(uint32_t type,const char* info)
return;
++ptr;
}
bool nasal_parse::check_comma(const uint32_t* panic_set)
bool nasal_parse::check_comma(const u32* panic_set)
{
for(uint32_t i=0;panic_set[i];++i)
for(u32 i=0;panic_set[i];++i)
if(tokens[ptr].type==panic_set[i])
{
die(error_line,"expected \',\' between scalars");
die(error_line,"expected ',' between scalars",true);
return true;
}
return false;
}
bool nasal_parse::check_multi_scalar()
{
uint32_t check_ptr=ptr,curve=1,bracket=0,brace=0;
u32 check_ptr=ptr,curve=1,bracket=0,brace=0;
while(tokens[++check_ptr].type!=tok_eof && curve)
{
switch(tokens[check_ptr].type)
@@ -181,7 +186,7 @@ bool nasal_parse::check_multi_scalar()
}
bool nasal_parse::check_func_end(const nasal_ast& node)
{
uint32_t type=node.type();
u32 type=node.type();
if(type==ast_func)
return true;
else if(type==ast_num || type==ast_id || type==ast_str || type==ast_nil || type==ast_vec || type==ast_hash)
@@ -205,9 +210,8 @@ bool nasal_parse::check_func_end(const nasal_ast& node)
}
bool nasal_parse::check_special_call()
{
// special call means like this:
// function_name(a:1,b:2,c:3);
uint32_t check_ptr=ptr,curve=1,bracket=0,brace=0;
// special call means like this: function_name(a:1,b:2,c:3);
u32 check_ptr=ptr,curve=1,bracket=0,brace=0;
while(tokens[++check_ptr].type!=tok_eof && curve)
{
switch(tokens[check_ptr].type)
@@ -229,7 +233,7 @@ bool nasal_parse::check_special_call()
}
bool nasal_parse::need_semi_check(const nasal_ast& node)
{
uint32_t type=node.type();
u32 type=node.type();
if(type==ast_for || type==ast_foreach || type==ast_forindex || type==ast_while || type==ast_conditional)
return false;
return !check_func_end(node);
@@ -259,18 +263,21 @@ nasal_ast nasal_parse::num()
{
nasal_ast node(tokens[ptr].line,ast_num);
node.set_num(str2num(tokens[ptr].str.c_str()));
match(tok_num);
return node;
}
nasal_ast nasal_parse::str()
{
nasal_ast node(tokens[ptr].line,ast_str);
node.set_str(tokens[ptr].str);
match(tok_str);
return node;
}
nasal_ast nasal_parse::id()
{
nasal_ast node(tokens[ptr].line,ast_id);
node.set_str(tokens[ptr].str);
match(tok_id);
return node;
}
nasal_ast nasal_parse::vec()
@@ -278,7 +285,7 @@ nasal_ast nasal_parse::vec()
// panic set for this token is not ','
// this is the FIRST set of calculation
// array end with tok_null=0
const uint32_t panic_set[]={
const u32 panic_set[]={
tok_id,tok_str,tok_num,
tok_not,tok_sub,tok_nil,
tok_func,tok_var,tok_lcurve,
@@ -296,7 +303,7 @@ nasal_ast nasal_parse::vec()
else if(tokens[ptr].type!=tok_rbracket && !check_comma(panic_set))
break;
}
match(tok_rbracket,"expected \']\' when generating vector");
match(tok_rbracket,"expected ']' when generating vector");
return node;
}
nasal_ast nasal_parse::hash()
@@ -309,34 +316,29 @@ nasal_ast nasal_parse::hash()
if(tokens[ptr].type==tok_comma)
match(tok_comma);
else if(tokens[ptr].type==tok_id || tokens[ptr].type==tok_str)// first set of hashmember
die(error_line,"expected \',\' between hash members");
die(error_line,"expected ',' between hash members",true);
else
break;
}
match(tok_rbrace,"expected \'}\' when generating hash");
match(tok_rbrace,"expected '}' when generating hash");
return node;
}
nasal_ast nasal_parse::pair()
{
nasal_ast node(tokens[ptr].line,ast_pair);
if(tokens[ptr].type==tok_id)
{
node.add(id());
match(tok_id);
}
else if(tokens[ptr].type==tok_str)
{
node.add(str());
match(tok_str);
}
else
match(tok_id);
match(tok_id,"expected hashmap key");
match(tok_colon);
node.add(calc());
return node;
}
nasal_ast nasal_parse::func()
{
++in_func;
nasal_ast node(tokens[ptr].line,ast_func);
match(tok_func);
if(tokens[ptr].type==tok_lcurve)
@@ -344,6 +346,7 @@ nasal_ast nasal_parse::func()
else
node.add(null());
node.add(exprs());
--in_func;
return node;
}
nasal_ast nasal_parse::args()
@@ -353,7 +356,6 @@ nasal_ast nasal_parse::args()
while(tokens[ptr].type!=tok_rcurve)
{
nasal_ast tmp=id();
match(tok_id);
if(tokens[ptr].type==tok_eq || tokens[ptr].type==tok_ellipsis)
{
nasal_ast special_arg(tokens[ptr].line,ast_null);
@@ -377,13 +379,13 @@ nasal_ast nasal_parse::args()
if(tokens[ptr].type==tok_comma)
match(tok_comma);
else if(tokens[ptr].type==tok_id)// first set of identifier
die(error_line,"expected \',\' between identifiers");
die(error_line,"expected ',' between identifiers",true);
else
break;
}
match(tok_rcurve,"expected \')\' after parameter list");
match(tok_rcurve,"expected ')' after parameter list");
std::string format="func(";
string format="func(";
for(auto& tmp:node.child())
{
format+=tmp.str();
@@ -407,10 +409,10 @@ nasal_ast nasal_parse::args()
if(checked_dynamic && &tmp!=&node.child().back())
die(tmp.line(),"dynamic para must be the end: "+format);
}
std::unordered_map<std::string,bool> argname;
std::unordered_map<string,bool> argname;
for(auto& tmp:node.child())
{
std::string name;
string name;
switch(tmp.type())
{
case ast_dynamic:
@@ -432,12 +434,12 @@ nasal_ast nasal_parse::lcurve_expr()
}
nasal_ast nasal_parse::expr()
{
uint32_t tok_type=tokens[ptr].type;
if((tok_type==tok_break || tok_type==tok_continue) && !in_loop)
u32 type=tokens[ptr].type;
if((type==tok_break || type==tok_continue) && !in_loop)
die(error_line,"should use break/continue in loops");
if(tok_type==tok_ret && !in_func)
if(type==tok_ret && !in_func)
die(error_line,"should use return in functions");
switch(tok_type)
switch(type)
{
case tok_nil:
case tok_num:
@@ -484,9 +486,9 @@ nasal_ast nasal_parse::exprs()
match(tok_semi);
// the last expression can be recognized without semi
else if(need_semi_check(node.child().back()) && tokens[ptr].type!=tok_rbrace)
die(error_line,"expected \";\"");
die(error_line,"expected ';'",true);
}
match(tok_rbrace,"expected \'}\' when generating expressions");
match(tok_rbrace,"expected '}' when generating expressions");
}
else
{
@@ -608,20 +610,13 @@ nasal_ast nasal_parse::unary()
nasal_ast nasal_parse::scalar()
{
nasal_ast node(tokens[ptr].line,ast_null);
if(tokens[ptr].type==tok_nil) {node=nil(); match(tok_nil);}
else if(tokens[ptr].type==tok_num){node=num(); match(tok_num);}
else if(tokens[ptr].type==tok_str){node=str(); match(tok_str);}
else if(tokens[ptr].type==tok_id) {node=id(); match(tok_id); }
else if(tokens[ptr].type==tok_func)
{
++in_func;
node=func();
--in_func;
}
else if(tokens[ptr].type==tok_lbracket)
node=vec();
else if(tokens[ptr].type==tok_lbrace)
node=hash();
if(tokens[ptr].type==tok_nil) {node=nil();match(tok_nil);}
else if(tokens[ptr].type==tok_num) node=num();
else if(tokens[ptr].type==tok_str) node=str();
else if(tokens[ptr].type==tok_id) node=id();
else if(tokens[ptr].type==tok_func) node=func();
else if(tokens[ptr].type==tok_lbracket) node=vec();
else if(tokens[ptr].type==tok_lbrace) node=hash();
else if(tokens[ptr].type==tok_lcurve)
{
match(tok_lcurve);
@@ -633,7 +628,6 @@ nasal_ast nasal_parse::scalar()
match(tok_var);
node.set_type(ast_def);
node.add(id());
match(tok_id);
match(tok_eq);
node.add(calc());
}
@@ -669,7 +663,7 @@ nasal_ast nasal_parse::callh()
nasal_ast node(tokens[ptr].line,ast_callh);
match(tok_dot);
node.set_str(tokens[ptr].str);
match(tok_id);
match(tok_id,"expected hashmap key"); // get key
return node;
}
nasal_ast nasal_parse::callv()
@@ -677,7 +671,7 @@ nasal_ast nasal_parse::callv()
// panic set for this token is not ','
// this is the FIRST set of subvec
// array end with tok_null=0
const uint32_t panic_set[]={
const u32 panic_set[]={
tok_id,tok_str,tok_num,
tok_not,tok_sub,tok_nil,
tok_func,tok_var,tok_lcurve,
@@ -696,7 +690,7 @@ nasal_ast nasal_parse::callv()
else if(tokens[ptr].type!=tok_rbracket && !check_comma(panic_set))
break;
}
match(tok_rbracket,"expected \']\' when calling vector");
match(tok_rbracket,"expected ']' when calling vector");
return node;
}
nasal_ast nasal_parse::callf()
@@ -704,7 +698,7 @@ nasal_ast nasal_parse::callf()
// panic set for this token is not ','
// this is the FIRST set of calculation/hashmember
// array end with tok_null=0
const uint32_t panic_set[]={
const u32 panic_set[]={
tok_id,tok_str,tok_num,
tok_not,tok_sub,tok_nil,
tok_func,tok_var,tok_lcurve,
@@ -723,7 +717,7 @@ nasal_ast nasal_parse::callf()
else if(tokens[ptr].type!=tok_rcurve && !check_comma(panic_set))
break;
}
match(tok_rcurve,"expected \')\' when calling function");
match(tok_rcurve,"expected ')' when calling function");
return node;
}
nasal_ast nasal_parse::subvec()
@@ -747,7 +741,7 @@ nasal_ast nasal_parse::definition()
match(tok_var);
switch(tokens[ptr].type)
{
case tok_id: node.add(id());match(tok_id);break;
case tok_id: node.add(id());break;
case tok_lcurve: node.add(outcurve_def());break;
default: die(error_line,"expected identifier");break;
}
@@ -760,10 +754,9 @@ nasal_ast nasal_parse::definition()
else
node.add(calc());
if(node[0].type()==ast_id && node[1].type()==ast_multi_scalar)
die(node[1].line(),"one identifier cannot accept too many values");
else if(node[0].type()==ast_multi_id && node[1].type()==ast_multi_scalar)
if(node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-definition");
die(node[1].line(),"one variable cannot accept too many values");
else if(node[0].type()==ast_multi_id && node[1].type()==ast_multi_scalar && node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-definition");
return node;
}
nasal_ast nasal_parse::incurve_def()
@@ -787,7 +780,6 @@ nasal_ast nasal_parse::multi_id()
while(tokens[ptr].type!=tok_eof)
{
node.add(id());
match(tok_id);
if(is_call(tokens[ptr].type))
{
call_scalar();// recognize calls but this is still a syntax error
@@ -796,7 +788,7 @@ nasal_ast nasal_parse::multi_id()
if(tokens[ptr].type==tok_comma)
match(tok_comma);
else if(tokens[ptr].type==tok_id)// first set of identifier
die(error_line,"expected \',\' between identifiers");
die(error_line,"expected ',' between identifiers",true);
else
break;
}
@@ -805,7 +797,7 @@ nasal_ast nasal_parse::multi_id()
nasal_ast nasal_parse::multi_scalar(bool check_call_memory)
{
// if check_call_memory is true,we will check if value called here can reach a memory space
const uint32_t panic_set[]={
const u32 panic_set[]={
tok_id,tok_str,tok_num,
tok_not,tok_sub,tok_nil,
tok_func,tok_var,tok_lcurve,
@@ -825,7 +817,7 @@ nasal_ast nasal_parse::multi_scalar(bool check_call_memory)
else if(tokens[ptr].type!=tok_rcurve && !check_comma(panic_set))
break;
}
match(tok_rcurve,"expected \')\' after multi-scalar");
match(tok_rcurve,"expected ')' after multi-scalar");
return node;
}
nasal_ast nasal_parse::multi_assgin()
@@ -887,7 +879,7 @@ nasal_ast nasal_parse::for_loop()
node.add(lcurve_expr());
else
node.add(calc());
match(tok_semi,"expected \';\' in for(;;)");
match(tok_semi,"expected ';' in for(;;)");
// conditional expression
if(tokens[ptr].type==tok_eof)
die(error_line,"expected conditional expr");
@@ -895,7 +887,7 @@ nasal_ast nasal_parse::for_loop()
node.add(null());
else
node.add(calc());
match(tok_semi,"expected \';\' in for(;;)");
match(tok_semi,"expected ';' in for(;;)");
//after loop expression
if(tokens[ptr].type==tok_eof)
die(error_line,"expected calculation");
@@ -921,7 +913,7 @@ nasal_ast nasal_parse::forei_loop()
if(tokens[ptr].type!=tok_var && tokens[ptr].type!=tok_id)
die(error_line,"expected iterator");
node.add(iter_gen());
match(tok_semi,"expected \';\' in foreach/forindex(iter;vector)");
match(tok_semi,"expected ';' in foreach/forindex(iter;vector)");
if(tokens[ptr].type==tok_eof)
die(error_line,"expected vector");
node.add(calc());
@@ -937,15 +929,14 @@ nasal_ast nasal_parse::iter_gen()
match(tok_var);
node.set_type(ast_iter);
node.add(id());
match(tok_id);
}
else
{
node.set_type(ast_call);
node.add(id());
match(tok_id);
while(is_call(tokens[ptr].type))
node.add(call_scalar());
check_memory_reachable(node);
}
return node;
}
@@ -994,18 +985,12 @@ nasal_ast nasal_parse::ret_expr()
{
nasal_ast node(tokens[ptr].line,ast_ret);
match(tok_ret);
uint32_t type=tokens[ptr].type;
if(
type==tok_nil ||
type==tok_num ||
type==tok_str ||
type==tok_id ||
type==tok_func ||
type==tok_sub ||
type==tok_not ||
type==tok_lcurve ||
type==tok_lbracket ||
type==tok_lbrace)
u32 type=tokens[ptr].type;
if(type==tok_nil || type==tok_num ||
type==tok_str || type==tok_id ||
type==tok_func || type==tok_sub ||
type==tok_not || type==tok_lcurve ||
type==tok_lbracket || type==tok_lbrace)
node.add(calc());
return node;
}

1457
nasal_vm.h

File diff suppressed because it is too large Load Diff

BIN
pic/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
pic/mandelbrot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

157
props.nas
View File

@@ -1,157 +0,0 @@
import("lib.nas");
var props=
{
globals:nil,
Node:nil,
getNode:func(path,index)
{
path=split('/',path);
var tmp=me.globals;
var path_size=size(path);
for(var i=0;i<path_size-1;i+=1)
tmp=tmp.val[path[i]];
if(path_size>0)
{
if(contains(tmp.val,path[i]~'['~index~']'))
return tmp.val[path[i]~'['~index~']'];
else
return tmp.val[path[i]];
}
return tmp;
}
};
props.Node=
{
new:func(values=nil)
{
var result={
parents:[props.Node],
val:{},
type:'GHOST',
parent:nil
};
if(typeof(values)=="hash")
result.val=values;
return result;
},
addChild:func(name)
{
if(!contains(me.val,name))
{
me.val[name]=props.Node.new();
me.val[name].parent=me;
return 1;
}
return 0;
},
addChildren:func(name,cnt=0)
{
for(var i=0;i<cnt;i+=1)
{
var label=name~'['~i~']';
me.val[label]=props.Node.new();
me.val[label].parent=me;
}
return;
},
setValue:func(path,val)
{
path=split('/',path);
var tmp=me;
foreach(var label;path)
tmp=tmp.val[label];
tmp.val=val;
if(typeof(val)=='str')
{
if(val=='true' or val=='false')
tmp.type='BOOL';
else
tmp.type='STRING';
}
elsif(typeof(val)=='num')
tmp.type='DOUBLE';
return;
},
setIntValue:func(num)
{
me.val=num;
me.type='INT';
return;
},
setBoolValue:func(state)
{
me.val=state;
me.type='BOOL';
return;
},
setDoubleValue:func(num)
{
me.val=num;
me.type='DOUBLE';
return;
},
getValue:func(){return me.val;},
getName:func()
{
var val=me.parent.val;
var key=keys(val);
foreach(var k;key)
if(val[k]==me)
return k;
return '';
},
getParent:func()
{
return me.parent;
},
getPath:func()
{
if(me.parent==nil) return '';
return me.parent.getPath()~'/'~me.getName();
},
equals:func(node){return me==node;},
debug:func(s='')
{
if(typeof(me.val)=='hash')
{
var key=keys(me.val);
if(!size(key))
{
println("{}");
return;
}
println('{');
foreach(var k;key)
{
print(s~' ',k,':');
me.val[k].debug(s~' ');
}
println(s,'}');
}
else
println(me.val,' (',me.type,')');
return;
}
};
props.globals=props.Node.new();
var c=['aircraft','ai','models','position','orientation','controls','sim'];
foreach(var i;c)
props.getNode('/',1).addChild(i);
props.getNode('/ai',1).addChildren('ai',4);
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
for(var i=0;i<4;i+=1)
props.getNode('/ai/ai['~i~']',1).setBoolValue('true');
props.getNode('/models',1).addChildren('building',4);
for(var i=0;i<4;i+=1)
props.getNode('/models/building['~i~']',1).setIntValue(i);
props.getNode('/',1).addChild('test');
props.getNode('/test',1).addChildren('in',4);
props.getNode('/test/in',0).setValue('/','true');
props.getNode('/test/in',1).setValue('/','false');
props.getNode('/test/in',2).setValue('/','welcome aboard,need help? use help->tutorial');
props.getNode('/test/in',3).setValue('/',2147483648);
props.globals.debug();
println(props.getNode('/test/in',3).getPath());

844
stl/fg_env.nas Normal file
View File

@@ -0,0 +1,844 @@
# flightgear developer environments simulator (beta)
# ValKmjolnir 2022
println("-------------------------------------------------------------");
println(" FlightGear simulated-env for developers project, since 2019");
println(" Developed by:");
println(" Sidi Liang (FGPRC-0762)");
println(" Haokun Lee (FGPRC-0818 aka ValKmjolnir)");
println("-------------------------------------------------------------");
println(" See help using command line argument: --fg-env-help");
println("-------------------------------------------------------------");
# important global constants
var D2R=math.pi/180;
var FPS2KT=0.5925;
var FT2M=0.3048;
var GAL2L=3.7854;
var IN2M=0.0254;
var KG2LB=2.2046;
var KT2FPS=1.6878;
var KT2MPS=0.5144;
var L2GAL=0.2642;
var LB2KG=0.4536;
var M2FT=3.2808;
var M2IN=39.3701;
var M2NM=0.00054;
var MPS2KT=1.9438;
var NM2M=1852;
var R2D=180/math.pi;
var fg_env_cli={
"--fg-env-help":{
info:"get help",
trigger:0,
f:func{
if(me.trigger)
return;
println("-------------------------------------------------------------");
println(" Help:");
foreach(var i;keys(fg_env_cli))
println(" ",i,": ",fg_env_cli[i].info);
println("-------------------------------------------------------------");
me.trigger=1;
}
},
"--fg-env-debug":{
info:"get property tree structure",
trigger:0,
f:func{
if(me.trigger)
return;
props.globals.debug();
me.trigger=1;
}
},
"--fg-env-mktmtest":{
info:"test maketimer",
trigger:0,
f:func{
if(me.trigger)
return;
maketimer_multi_coroutine_test(32);
me.trigger=1;
}
}
};
println("[\e[32m fg_env \e[0m] [",os.time(),"] init begin");
println("[\e[32m maketimer \e[0m] [",os.time(),"] init tasks");
println("[\e[32m maketimer \e[0m] [",os.time(),"] init events");
var fg_globals={
task:{},
event:{}
};
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func add_event(name,interval,function)");
var add_event=func(name,interval,function){
fg_globals.event[name]=coroutine.create(func{
var timestamp=maketimestamp();
timestamp.stamp();
while(timestamp.elapsedMSec()<interval*1000)
coroutine.yield();
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[33mevent\e[0m interval:\e[34m",interval,"\e[0m");
function();
});
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func add_task(name,interval,function)");
var add_task=func(name,interval,function){
fg_globals.task[name]=coroutine.create(func{
var counter=0;
var timestamp=maketimestamp();
while(1){
counter+=1;
timestamp.stamp();
while(timestamp.elapsedMSec()<interval*1000)
coroutine.yield();
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[34mtask\e[0m interval:\e[34m",interval,"\e[0m invoke-time:\e[96m",counter,"\e[0m");
function();
coroutine.yield();
}
});
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func remove_task(name)");
var remove_task=func(name){
if(contains(fg_globals.task,name))
delete(fg_globals.task,name);
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func remove_event(name)");
var remove_event=func(name){
if(contains(fg_globals.event,name))
delete(fg_globals.event,name);
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func maketimer(interval,function)");
var maketimer=func(interval,function){
var name="nasal-timer-";
var res={
start:func{
if(me.isRunning)
return;
me.isRunning=1;
if(me.singleShot){
add_event(name,interval,function);
}else{
add_task(name,interval,function);
}
},
stop:func{
if(me.isRunning){
remove_task(name);
me.isRunning=0;
}
},
restart:func(itv){
interval=itv;
me.stop();
me.start();
},
singleShot:0,
isRunning:0,
simulatedTime:0
};
name~=id(res);
return res;
}
println("[\e[32m settimer \e[0m] [",os.time(),"] new func settimer(function,interval,rt)");
var settimer=func(){
var index=0;
return func(function,interval,realtime=1){
var name="nasal-settimer-"~index;
index+=1;
add_task(name,interval,function);
}
}();
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func simulation()");
var simulation=func(){
var running=1;
while(running){
running=0;
foreach(var i;keys(fg_globals.task)){
if(!contains(fg_globals.task,i))
continue;
if(coroutine.resume(fg_globals.task[i])!=nil){
running=1;
}else{
remove_task(i);
}
}
foreach(var i;keys(fg_globals.event)){
if(!contains(fg_globals.event,i))
continue;
if(coroutine.resume(fg_globals.event[i])!=nil){
running=1;
}else{
remove_event(i);
}
}
}
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func maketimer_multi_coroutine_test(size)");
var maketimer_multi_coroutine_test=func(coroutine_size){
if(coroutine_size<1)
return;
var task_vec=[];
setsize(task_vec,coroutine_size);
forindex(var i;task_vec)
task_vec[i]=func{};
task_vec[coroutine_size-1]=func{
println("\e[101m",coroutine_size," tasks invoked.\e[0m");
forindex(var i;task_vec)
task_vec[i].stop();
}
var event_vec=[];
setsize(event_vec,coroutine_size);
forindex(var i;event_vec)
event_vec[i]=func{};
event_vec[coroutine_size-1]=func{
println("\e[101m",coroutine_size," events invoked.\e[0m");
}
var set_vec=[];
setsize(set_vec,coroutine_size);
forindex(var i;set_vec)
set_vec[i]=func{};
set_vec[coroutine_size-1]=func{
println("\e[101m",coroutine_size," settimer invoked.\e[0m");
}
forindex(var i;task_vec){
task_vec[i]=maketimer((i+1)/10,task_vec[i]);
task_vec[i].start();
}
forindex(var i;event_vec){
event_vec[i]=maketimer((i+1)/10,event_vec[i]);
event_vec[i].singleShot=1;
event_vec[i].start();
}
#forindex(var i;set_vec)
# settimer(set_vec[i],(i+1)/10);
simulation();
}
println("[\e[32m geodinfo \e[0m] [",os.time(),"] init geodinfo(lat,lon)");
var geodinfo=func(lat,lon){
return [nil,{
names:["Road","Freeway"]
}];
}
println("[\e[32m props \e[0m] [",os.time(),"] init props");
var props={
globals:nil,
Node:nil,
getNode:func(path,index){
path=split('/',path);
var tmp=me.globals;
var path_size=size(path);
for(var i=0;i<path_size-1;i+=1)
tmp=tmp.val[path[i]];
if(path_size>0){
if(contains(tmp.val,path[i]~'['~index~']'))
return tmp.val[path[i]~'['~index~']'];
else
return tmp.val[path[i]];
}
return tmp;
}
};
println("[\e[32m props \e[0m] [",os.time(),"] init props.Node");
props.Node={
new:func(values=nil){
var res={
parents:fg_env_props_node_traits,
val:{},
type:'GHOST',
parent:nil
};
if(typeof(values)=="hash")
res.val=values;
return res;
},
addChild:func(name){
if(!contains(me.val,name)){
me.val[name]=props.Node.new();
me.val[name].parent=me;
return 1;
}
return 0;
},
addChildren:func(name,cnt=0){
for(var i=0;i<cnt;i+=1){
var label=name~'['~i~']';
me.val[label]=props.Node.new();
me.val[label].parent=me;
}
return;
},
setValue:func(path,val){
path=split('/',path);
var tmp=me;
foreach(var label;path)
tmp=tmp.val[label];
tmp.val=val;
if(typeof(val)=='str'){
if(val=='true' or val=='false')
tmp.type='BOOL';
else
tmp.type='STRING';
}
elsif(typeof(val)=='num')
tmp.type='DOUBLE';
return;
},
setIntValue:func(num){
me.val=num;
me.type='INT';
return;
},
setBoolValue:func(state){
me.val=state;
me.type='BOOL';
return;
},
setDoubleValue:func(num){
me.val=num;
me.type='DOUBLE';
return;
},
getValue:func(){return me.val;},
getName:func(){
var val=me.parent.val;
foreach(var k;keys(val))
if(val[k]==me)
return k;
return '';
},
getParent:func(){
return me.parent;
},
getPath:func(){
if(me.parent==nil) return '';
return me.parent.getPath()~'/'~me.getName();
},
equals:func(node){return me==node;},
debug:func(s=''){
if(typeof(me.val)=='hash'){
var key=keys(me.val);
if(!size(key)){
println("\e[91m{}\e[0m");
return;
}
println('\e[91m{\e[0m');
foreach(var k;key){
print(s~" ","\e[34m",k,"\e[0m\e[95m:\e[0m");
me.val[k].debug(s~" ");
}
println(s,'\e[91m}\e[0m');
}
else
println("\e[35m ",me.val,"\e[0m\e[33m(\e[0m\e[96m",me.type,'\e[0m\e[33m)\e[0m');
return;
}
};
var fg_env_props_node_traits=[props.Node];
println("[\e[32m props \e[0m] [",os.time(),"] init props.globals");
props.globals=props.Node.new();
var c=['aircraft','ai','models','position','orientation','controls','sim','consumables','engines','velocities','accelerations','gear','instrumentation','rotors'];
foreach(var i;c)
props.getNode('/',1).addChild(i);
props.getNode('/ai',1).addChildren('ai',4);
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
props.getNode('/models',1).addChildren('building',4);
for(var i=0;i<4;i+=1)
props.getNode('/models/building['~i~']',1).setIntValue(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /consumables");
props.getNode("/consumables",1).addChild("fuel");
props.getNode("/consumables/fuel",1).addChild("total-fuel-lbs");
props.getNode("/consumables/fuel",1).addChild("total-gal_us");
props.getNode("/consumables/fuel/total-fuel-lbs",1).setValue('/',0);
props.getNode("/consumables/fuel/total-gal_us",1).setValue('/',0);
props.getNode("/consumables/fuel",1).addChildren("tank",4);
for(var i=0;i<4;i+=1){
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-lb");
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-lbs");
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-gal_us");
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("capacity-gal_us");
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("density-ppg");
props.getNode("/consumables/fuel/tank["~i~"]/level-lb",1).setValue('/',0);
props.getNode("/consumables/fuel/tank["~i~"]/level-lbs",1).setValue('/',0);
props.getNode("/consumables/fuel/tank["~i~"]/level-gal_us",1).setValue('/',0);
props.getNode("/consumables/fuel/tank["~i~"]/capacity-gal_us",1).setValue('/',0);
props.getNode("/consumables/fuel/tank["~i~"]/density-ppg",1).setValue('/',0);
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls");
foreach(var i;['anti-ice','APU','armament','autoflight','electric','engines','flight','fuel','gear','hydraulic','lighting','pneumatic','pressurization','seat'])
props.getNode("/controls",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/anti-ice");
foreach(var i;['wing-heat','pitot-heat','wiper','window-heat'])
props.getNode("/controls/anti-ice",1).addChild(i);
props.getNode("/controls/anti-ice",1).addChildren("engine",2);
for(var i=0;i<2;i+=1){
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("carb-heat");
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("inlet-heat");
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/APU");
props.getNode("/controls/APU",1).addChild("off-start-run");
props.getNode("/controls/APU",1).addChild("fire-switch");
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/armament");
props.getNode("/controls/armament",1).addChild("master-arm");
props.getNode("/controls/armament",1).addChild("station-select");
props.getNode("/controls/armament",1).addChild("release-all");
props.getNode("/controls/armament",1).addChildren("station",4);
for(var i=0;i<4;i+=1){
props.getNode("/controls/armament/station["~i~"]",1).addChild("stick-size");
props.getNode("/controls/armament/station["~i~"]",1).addChild("release-stick");
props.getNode("/controls/armament/station["~i~"]",1).addChild("release-all");
props.getNode("/controls/armament/station["~i~"]",1).addChild("jettison-all");
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/autoflight");
foreach(var i;['autothrottle-arm','autothrottle-engage','heading-select','altitude-select','bank-angle-select','vertical-speed-select','speed-select','mach-select','vertical-mode','lateral-mode'])
props.getNode("/controls/autoflight",1).addChild(i);
props.getNode("/controls/autoflight",1).addChildren("autopilot",2);
for(var i=0;i<2;i+=1)
props.getNode("/controls/autoflight/autopilot["~i~"]",1).addChild("engage");
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/electric");
foreach(var i;['battery-switch','external-power','APU-generator'])
props.getNode("/controls/electric",1).addChild(i);
props.getNode("/controls/electric",1).addChildren("engine",2);
for(var i=0;i<2;i+=1){
props.getNode("/controls/electric/engine["~i~"]",1).addChild("generator");
props.getNode("/controls/electric/engine["~i~"]",1).addChild("bus-tie");
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/engines");
props.getNode("/controls/engines",1).addChild("throttle-idle");
props.getNode("/controls/engines",1).addChildren("engine",2);
for(var i=0;i<2;i+=1)
foreach(var j;['throttle','starter','fuel-pump','fire-switch','fire-bottle-discharge','cutoff','mixture','propeller-pitch','magnetos','boost','WEP','cowl-flaps-norm','feather','ignition','augmentation','afterburner','reverser','water-injection','condition'])
props.getNode("/controls/engines/engine["~i~"]",1).addChild(j);
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/flight");
foreach(var i;['aileron','aileron-trim','elevator','elevator-trim','rudder','rudder-trim','flaps','slats','BLC','spoilers','speedbrake','wing-sweep','wing-fold','drag-chute'])
props.getNode("/controls/flight",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/fuel");
props.getNode("/controls/fuel",1).addChild("dump-value");
props.getNode("/controls/fuel",1).addChildren("tank",4);
for(var i=0;i<4;i+=1){
foreach(var j;['fuel-selector','to_engine','to_tank'])
props.getNode("/controls/fuel/tank["~i~"]",1).addChild(j);
props.getNode("/controls/fuel/tank["~i~"]",1).addChildren("boost-pump",4);
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/gear");
foreach(var i;['brake-left','brake-right','brake-parking','steering','gear-down','antiskid','tailhook','tailwheel-lock'])
props.getNode("/controls/gear",1).addChild(i);
props.getNode("/controls/gear",1).addChildren("wheel",4);
for(var i=0;i<4;i+=1)
props.getNode("/controls/gear/wheel["~i~"]",1).addChild("alternate-extension");
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/hydraulic");
props.getNode("/controls/hydraulic",1).addChildren("system",2);
for(var i=0;i<2;i+=1){
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("engine-pump");
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("electric-pump");
}
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/lighting");
foreach(var i;['landing-lights','turn-off-lights','formation-lights','taxi-light','logo-lights','nav-lights','beacon','strobe','panel-norm','instruments-norm','dome-norm'])
props.getNode("/controls/lighting",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pneumatic");
props.getNode("/controls/pneumatic",1).addChild("APU-bleed");
props.getNode("/controls/pneumatic",1).addChildren("engine",2);
for(var i=0;i<2;i+=1)
props.getNode("/controls/pneumatic/engine["~i~"]",1).addChild("bleed");
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pressurization");
foreach(var i;['mode','dump','outflow-valve'])
props.getNode("/controls/pressurization",1).addChild(i);
props.getNode("/controls/pressurization",1).addChildren("pack",4);
for(var i=0;i<4;i+=1)
props.getNode("/controls/pressurization/pack["~i~"]",1).addChild("pack-on");
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/seat");
foreach(var i;['vertical-adjust','fore-aft-adjust','cmd_selector_valve'])
props.getNode("/controls/seat",1).addChild(i);
props.getNode("/controls/seat",1).addChildren("eject",3);
for(var i=0;i<3;i+=1){
props.getNode("/controls/seat/eject["~i~"]",1).addChild("initiate");
props.getNode("/controls/seat/eject["~i~"]",1).addChild("status");
}
println("[\e[32m props \e[0m] [",os.time(),"] init /engines");
props.getNode("/engines",1).addChildren("engine",2);
for(var i=0;i<2;i+=1)
foreach(var j;
['fuel-flow-gph','fuel-flow-pph','thrust_lb','running','starter','cranking',
'n1','n2','epr','augmentation','water-injection','ignition','nozzle-pos-norm',
'inlet-pos-norm','reversed','cutoff','mp-osi','egt-degf','oil-temperature-degf',
'oil-pressure-psi','cht-degf','rpm','pitch','torque'])
props.getNode("/engines/engine["~i~"]",1).addChild(j);
println("[\e[32m props \e[0m] [",os.time(),"] init /position");
foreach(var i;['','altitude-agl-ft','altitude-ft','ground-elev-ft','ground-elev-m','latitude-deg','latitude-string','longitude-deg','longitude-string','sea-level-radius-ft'])
props.getNode("/position",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /orientation");
foreach(var i;['roll-deg','pitch-deg','heading-deg','roll-rate-degps','pitch-rate-degps','yaw-rate-degps','side-slip-rad','side-slip-deg','alpha-deg'])
props.getNode("/orientation",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /velocities");
foreach(var i;['airspeed-kt','mach','speed-north-fps','speed-east-fps','speed-down-fps','uBody-fps','vBody-fps','wBody-fps','vertical-speed-fps','glideslope'])
props.getNode("/velocities",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /accelerations");
foreach(var i;['nlf','ned','pilot'])
props.getNode("/accelerations",1).addChild(i);
foreach(var i;['north-accel-fps_sec','east-accel-fps_sec','down-accel-fps_sec'])
props.getNode("/accelerations/ned",1).addChild(i);
foreach(var i;['x-accel-fps_sec','y-accel-fps_sec','z-accel-fps_sec'])
props.getNode("/accelerations/pilot",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /gear");
props.getNode("/gear",1).addChild("serviceable");
props.getNode("/gear",1).addChildren("gear",4);
for(var i=0;i<4;i+=1)
foreach(var j;['cast-angle-deg','compression-m','compression-norm','ground-friction-factor','ground-is-solid','has-brake','rollspeed-ms','wow','xoffset-in','yoffset-in','zoffset-in'])
props.getNode("/gear/gear["~i~"]",1).addChild(j);
println("[\e[32m props \e[0m] [",os.time(),"] init /instrumentation");
foreach(var i;['adf','airspeed-indicator','altimeter','annunciator','clock','comm','comm[1]','dme','efis','encoder','flightdirector','gps','gps-annunciator','heading-indicator','heading-indicator-fg','magnetic-compass','marker-beacon','nav','nav[1]','radar','slip-skid-ball','tacan','transponder','turn-indicator','vertical-speed-indicator','wxradar'])
props.getNode("/instrumentation",1).addChild(i);
println("[\e[32m props \e[0m] [",os.time(),"] init /rotors");
foreach(var i;['gear','{name}'])
props.getNode("/rotors",1).addChild(i);
foreach(var i;['torque-sound-filtered','total-torque'])
props.getNode("/rotors/gear",1).addChild(i);
foreach(var i;['balance','bladesvisible','cone-deg','cone2-deg','roll-deg','rpm','stall','stall-filtered','tilt','torque','yaw-deg'])
props.getNode("/rotors/{name}",1).addChild(i);
props.getNode("/rotors/{name}",1).addChildren("blade",8);
for(var i=0;i<8;i+=1)
foreach(var j;['flap-deg','incidence-deg','position-deg'])
props.getNode("/rotors/{name}/blade["~i~"]",1).addChild(j);
props.getNode("/sim",1).addChild("messages");
props.getNode("/sim",1).addChild("fg-home");
props.getNode("/sim/messages",1).addChild("copilot");
props.getNode("/sim/messages/copilot",1).setValue('/',"nothing");
props.getNode("/position/latitude-deg",1).setValue('/',90);
props.getNode("/position/longitude-deg",1).setValue('/',90);
props.getNode("/position/altitude-ft",1).setValue('/',28.244);
props.getNode("/position/altitude-agl-ft",1).setValue('/',22.4704);
props.getNode("/orientation/heading-deg",1).setValue('/',90);
props.getNode("/controls/flight/rudder",1).setValue('/',0.114);
func(){
srand();
var tmp=nil;
var vec=[props.globals];
while(size(vec)){
tmp=[];
foreach(var i;vec){
if(typeof(i.val)=="hash"){
if(size(i.val)==0){
i.setDoubleValue(rand()*10);
}else{
foreach(var j;keys(i.val))
append(tmp,i.val[j]);
}
}
}
vec=tmp;
}
}();
println("[\e[32m props \e[0m] [",os.time(),"] init done");
println("[\e[32m fg_env \e[0m] [",os.time(),"] init done");
println("-------------------------------------------------------------");
foreach(var a;runtime.argv())
if(contains(fg_env_cli,a)){
fg_env_cli[a].f();
}
# related doc: https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Docs/README.properties
# ================================================================================
# CONTROLS
# ================================================================================
# Flight Controls
# ---------------
# /controls/flight/aileron
# /controls/flight/aileron-trim
# /controls/flight/elevator
# /controls/flight/elevator-trim
# /controls/flight/rudder
# /controls/flight/rudder-trim
# /controls/flight/flaps
# /controls/flight/slats
# /controls/flight/BLC // Boundary Layer Control
# /controls/flight/spoilers
# /controls/flight/speedbrake
# /controls/flight/wing-sweep
# /controls/flight/wing-fold
# /controls/flight/drag-chute
# Engines
# -------
# /controls/engines/throttle_idle
# /controls/engines/engine[%d]/throttle
# /controls/engines/engine[%d]/starter
# /controls/engines/engine[%d]/fuel-pump
# /controls/engines/engine[%d]/fire-switch
# /controls/engines/engine[%d]/fire-bottle-discharge
# /controls/engines/engine[%d]/cutoff
# /controls/engines/engine[%d]/mixture
# /controls/engines/engine[%d]/propeller-pitch
# /controls/engines/engine[%d]/magnetos
# /controls/engines/engine[%d]/boost
# /controls/engines/engine[%d]/WEP
# /controls/engines/engine[%d]/cowl-flaps-norm
# /controls/engines/engine[%d]/feather
# /controls/engines/engine[%d]/ignition
# /controls/engines/engine[%d]/augmentation
# /controls/engines/engine[%d]/afterburner
# /controls/engines/engine[%d]/reverser
# /controls/engines/engine[%d]/water-injection
# /controls/engines/engine[%d]/condition
# Fuel
# ----
# /controls/fuel/dump-valve
# /controls/fuel/tank[%d]/fuel_selector
# /controls/fuel/tank[%d]/to_engine
# /controls/fuel/tank[%d]/to_tank
# /controls/fuel/tank[%d]/boost-pump[%d]
# /consumables/fuel/tank[%d]/level-lbs
# /consumables/fuel/tank[%d]/level-gal_us
# /consumables/fuel/tank[%d]/capacity-gal_us
# /consumables/fuel/tank[%d]/density-ppg
# /consumables/fuel/total-fuel-lbs
# /consumables/fuel/total-gal_us
# Gear
# ----
# /controls/gear/brake-left
# /controls/gear/brake-right
# /controls/gear/brake-parking
# /controls/gear/steering
# /controls/gear/gear-down
# /controls/gear/antiskid
# /controls/gear/tailhook
# /controls/gear/tailwheel-lock
# /controls/gear/wheel[%d]/alternate-extension
# Anti-Ice
# --------
# /controls/anti-ice/wing-heat
# /controls/anti-ice/pitot-heat
# /controls/anti-ice/wiper
# /controls/anti-ice/window-heat
# /controls/anti-ice/engine[%d]/carb-heat
# /controls/anti-ice/engine[%d]/inlet-heat
# Hydraulics
# ----------
# /controls/hydraulic/system[%d]/engine-pump
# /controls/hydraulic/system[%d]/electric-pump
# Electric
# --------
# /controls/electric/battery-switch
# /controls/electric/external-power
# /controls/electric/APU-generator
# /controls/electric/engine[%d]/generator
# /controls/electric/engine[%d]/bus-tie
# Pneumatic
# ---------
# /controls/pneumatic/APU-bleed
# /controls/pneumatic/engine[%d]/bleed
# Pressurization
# --------------
# /controls/pressurization/mode
# /controls/pressurization/dump
# /controls/pressurization/outflow-valve
# /controls/pressurization/pack[%d]/pack-on
# Lights
# ------
# /controls/lighting/landing-lights
# /controls/lighting/turn-off-lights
# /controls/lighting/formation-lights
# /controls/lighting/taxi-light
# /controls/lighting/logo-lights
# /controls/lighting/nav-lights
# /controls/lighting/beacon
# /controls/lighting/strobe
# /controls/lighting/panel-norm
# /controls/lighting/instruments-norm
# /controls/lighting/dome-norm
# Armament
# --------
# /controls/armament/master-arm
# /controls/armament/station-select
# /controls/armament/release-all
# /controls/armament/station[%d]/stick-size
# /controls/armament/station[%d]/release-stick
# /controls/armament/station[%d]/release-all
# /controls/armament/station[%d]/jettison-all
# Seat
# ----
# /controls/seat/vertical-adjust
# /controls/seat/fore-aft-adjust
# /controls/seat/cmd_selector_valve
# /controls/seat/eject[%d]/initiate
# /controls/seat/eject[%d]/status
# APU
# ---
# /controls/APU/off-start-run
# /controls/APU/fire-switch
# Autoflight
# ----------
# /controls/autoflight/autopilot[%d]/engage
# /controls/autoflight/autothrottle-arm
# /controls/autoflight/autothrottle-engage
# /controls/autoflight/heading-select
# /controls/autoflight/altitude-select
# /controls/autoflight/bank-angle-select
# /controls/autoflight/vertical-speed-select
# /controls/autoflight/speed-select
# /controls/autoflight/mach-select
# /controls/autoflight/vertical-mode
# /controls/autoflight/lateral-mode
# ================================================================================
# FDM (Aircraft settings)
# ================================================================================
# Position
# ---------------
# /position/latitude-deg
# /position/longitude-deg
# /position/altitude-ft
# Orientation
# -----------
# /orientation/roll-deg
# /orientation/pitch-deg
# /orientation/heading-deg
# /orientation/roll-rate-degps
# /orientation/pitch-rate-degps
# /orientation/yaw-rate-degps
# /orientation/side-slip-rad
# /orientation/side-slip-deg
# /orientation/alpha-deg
# Velocities
# ----------
# /velocities/airspeed-kt
# /velocities/mach
# /velocities/speed-north-fps
# /velocities/speed-east-fps
# /velocities/speed-down-fps
# /velocities/uBody-fps
# /velocities/vBody-fps
# /velocities/wBody-fps
# /velocities/vertical-speed-fps
# /velocities/glideslope
# Acceleration
# ------------
# /accelerations/nlf
# /accelerations/ned/north-accel-fps_sec
# /accelerations/ned/east-accel-fps_sec
# /accelerations/ned/down-accel-fps_sec
# /accelerations/pilot/x-accel-fps_sec
# /accelerations/pilot/y-accel-fps_sec
# /accelerations/pilot/z-accel-fps_sec
# Engines
# -------
# common:
# /engines/engine[%d]/fuel-flow-gph
# /engines/engine[%d]/fuel-flow_pph
# /engines/engine[%d]/thrust_lb
# /engines/engine[%d]/running
# /engines/engine[%d]/starter
# /engines/engine[%d]/cranking
# piston:
# /engines/engine[%d]/mp-osi
# /engines/engine[%d]/egt-degf
# /engines/engine[%d]/oil-temperature-degf
# /engines/engine[%d]/oil-pressure-psi
# /engines/engine[%d]/cht-degf
# /engines/engine[%d]/rpm
# turbine:
# /engines/engine[%d]/n1
# /engines/engine[%d]/n2
# /engines/engine[%d]/epr
# /engines/engine[%d]/augmentation
# /engines/engine[%d]/water-injection
# /engines/engine[%d]/ignition
# /engines/engine[%d]/nozzle-pos-norm
# /engines/engine[%d]/inlet-pos-norm
# /engines/engine[%d]/reversed
# /engines/engine[%d]/cutoff
# propeller:
# /engines/engine[%d]/rpm
# /engines/engine[%d]/pitch
# /engines/engine[%d]/torque
# ================================================================================
# LIGHT
# ================================================================================
# /sim/time/sun-angle-rad
# /rendering/scene/ambient/red
# /rendering/scene/ambient/ggreen
# /rendering/scene/ambient/blue
# /rendering/scene/diffuse/red
# /rendering/scene/diffuse/green
# /rendering/scene/diffuse/blue
# /rendering/scene/specular/red
# /rendering/scene/specular/green
# /rendering/scene/specular/blue

38
stl/file.nas Normal file
View File

@@ -0,0 +1,38 @@
# lib file.nas
# ValKmjolnir 2022/3/6
var file={
SEEK_SET:io.SEEK_SET,
SEEK_CUR:io.SEEK_CUR,
SEEK_END:io.SEEK_END,
new: func(filename,mode="r"){
if(!io.exists(filename))
return nil;
var fd=io.open(filename,mode);
return {
close: func(){io.close(fd);},
read: func(len){
var buf=mut("");
io.read(fd,buf,len);
return buf;
},
write: func(str){return io.write(fd,str);},
seek: func(pos,whence){return io.seek(fd,pos,whence);},
tell: func(){return io.tell(fd);},
readln: func(){return io.readln(fd);},
stat: func(){return io.stat(filename);},
eof: func(){return io.eof(fd);}
};
}
};
var find_all_files=func(path){
if(!io.exists(path))
return [];
var dd=unix.opendir(path);
var res=[];
while(var n=unix.readdir(dd))
if(unix.isfile(path~"/"~n))
append(res,n);
unix.closedir(dd);
return res;
}

View File

@@ -1,165 +1,281 @@
# lib.nas
# import is used to link another file, this lib function will do nothing.
# because nasal_import will recognize this and link files before generating bytecode.
var import=func(filename){
return __builtin_import(filename);
}
# 2019 ValKmjolnir
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout/printf to output logs.
# this function uses std::cout to output logs.
var print=func(elems...){
return __builtin_print(elems);
return __print(elems);
}
# append is used to add values into a vector.
var append=func(vec,elems...){
return __builtin_append(vec,elems);
return __append(vec,elems);
}
# setsize is used to change the size of vector.
# if the size is larger than before,
# this function will fill vm_nil into uninitialized space.
var setsize=func(vec,size){
return __builtin_setsize(vec,size);
return __setsize(vec,size);
}
# system has the same use in C.
var system=func(str){
return __builtin_system(str);
return __system(str);
}
# input uses std::cin and returns what we input.
var input=func(){
return __builtin_input();
var input=func(end=nil){
return __input(end);
}
# split a string by delimiter for example:
# split a string by separator for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(deli,str){
return __builtin_split(deli,str);
var split=func(separator,str){
return __split(separator,str);
}
# rand has the same function as the rand in C
# if seed is nil, it will return the random number.
# if seed is not nil, it will be initialized by this seed.
var rand=func(seed=nil){
return __builtin_rand(seed);
return __rand(seed);
}
# id will return the pointer of an gc-object.
# if this object is not managed by gc, it will return 0.
var id=func(object){
return __builtin_id(object);
return __id(object);
}
# int will get the integer of input number.
# but carefully use it, because int has range between -2147483648~2147483647
var int=func(val){
return __builtin_int(val);
return __int(val);
}
# floor will get the integral number of input argument
# which is less than or equal to this argument
var floor=func(val){
return __floor(val);
}
# abort using std::abort
var abort=func(){
__abort();
}
# abs gets absolute number.
var abs=func(n){
return n>0?n:-n;
}
# num will change all the other types into number.
# mostly used to change a numerable string.
var num=func(val){
return __builtin_num(val);
return __num(val);
}
# pop used to pop the last element in a vector.
# this function will return the value that poped if vector has element(s).
# if the vector is empty, it will return nil.
var pop=func(vec){
return __builtin_pop(vec);
return __pop(vec);
}
# str is used to change number into string.
var str=func(num){
return __builtin_str(num);
return __str(num);
}
# size can get the size of a string/vector/hashmap.
# in fact it can also get the size of number, and the result is the number itself.
# so don't do useless things, though it really works.
var size=func(object){
return __builtin_size(object);
return __size(object);
}
# contains is used to check if a key exists in a hashmap/dict.
var contains=func(hash,key){
return __builtin_contains(hash,key);
return __contains(hash,key);
}
# delete is used to delete a pair in a hashmap/dict by key.
var delete=func(hash,key){
return __builtin_delete(hash,key);
return __delete(hash,key);
}
# keys is used to get all keys in a hashmap/dict.
# this function will return a vector.
var keys=func(hash){
return __builtin_keys(hash);
return __keys(hash);
}
# time has the same function in C.
var time=func(begin){
return __builtin_time(begin);
return __time(begin);
}
var systime=func(){
return time(0);
}
# die is a special native function.
# use it at where you want the program to crash immediately.
var die=func(str){
return __builtin_die(str);
return __die(str);
}
# find will give the first position of the needle in haystack
var find=func(needle,haystack){
return __find(needle,haystack);
}
# typeof is used to get the type of an object.
# this function returns a string.
var typeof=func(object){
return __builtin_type(object);
return __type(object);
}
# subvec is used to get part of a vector
var subvec=func(vec,begin,length=nil){
return vec[begin:(length==nil?nil:begin+length-1)];
}
# substr will get the sub-string.
# it gets the string, the begin index and sub-string's length as arguments.
var substr=func(str,begin,len){
return __builtin_substr(str,begin,len);
return __substr(str,begin,len);
}
# streq is used to compare if two strings are the same.
var streq=func(a,b){
return __builtin_streq(a,b);
return __streq(a,b);
}
# left is used to get the sub-string like substr.
# but the begin index is 0.
var left=func(str,len){
return __builtin_left(str,len);
return __left(str,len);
}
# right i used to get the sub-string like substr.
# but the begin index is strlen-len.
var right=func(str,len){
return __builtin_right(str,len);
return __right(str,len);
}
# cmp is used to compare two strings.
# normal string will not be correctly compared by operators < > <= >=
# because these operators will turn strings into numbers then compare.
var cmp=func(a,b){
return __builtin_cmp(a,b);
return __cmp(a,b);
}
# chr is used to get the character by ascii-number.
# for example chr(65) -> 'A'
var chr=func(code){
return __builtin_chr(code);
return __chr(code);
}
# mut is used to change unmutable strings to mutable.
var mut=func(str){
return str~"";
}
# srand wraps up rand, using time(0) as the seed.
var srand=func(){
rand(time(0));
return 0;
}
# values() gets all values in a hash.
var values=func(hash){
return __values(hash);
}
# println has the same function as print.
# but it will output a '\n' after using print.
var println=func(elems...){
__builtin_print(elems);
elems=['\n'];
return __builtin_print(elems);
return __println(elems);
}
var isfunc=func(f){
return typeof(f)=="func";
}
var isghost=func(g){
die("this runtime has no ghost object");
return 0;
}
var ishash=func(h){
return typeof(h)=="hash";
}
var isint=func(x){
return x==floor(x);
}
var isnum=func(x){
return typeof(x)=="num" or !math.isnan(num(x));
}
var isscalar=func(s){
var t=typeof(s);
return (t=="num" or t=="str")?1:0;
}
var isstr=func(s){
return typeof(s)=="str";
}
var isvec=func(v){
return typeof(v)=="vec";
}
# get the index of val in the vec
var vecindex=func(vec,val){
forindex(var i;vec)
if(val==vec[i])
return i;
return nil;
}
# check if the object is an instance of the class
var isa=func(object,class){
if(!contains(object,"parents") or typeof(object.parents)!="vec")
return 0;
foreach(var elem;object.parents)
if(elem==class)
return 1;
return 0;
}
# assert aborts when condition is not true
var assert=func(condition,message="assertion failed!"){
if(condition)
return 1;
die(message);
}
# get time stamp, this will return a timestamp object
var maketimestamp=func(){
var t=0;
var millisec=func(){
return __millisec;
}
return {
stamp:func(){t=millisec();},
elapsedMSec:func(){return millisec()-t;},
elapsedUSec:func(){return (millisec()-t)*1000;}
};
}
# md5
var md5=func(str){
return __md5(str);
}
var io=
@@ -168,28 +284,31 @@ var io=
SEEK_CUR:1,
SEEK_END:2,
# get content of a file by filename. returns a string.
fin: func(filename){return __builtin_fin(filename);},
fin: func(filename){return __fin(filename);},
# input a string as the content of a file.
fout: func(filename,str){return __builtin_fout(filename,str);},
fout: func(filename,str){return __fout(filename,str);},
# use C access
exists:func(filename){return __exists(filename);},
# same as C fopen. open file and get the FILE*.
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
open: func(filename,mode="r"){return __open(filename,mode);},
# same as C fclose. close file by FILE*.
close: func(filehandle){return __builtin_close(filehandle);},
close: func(filehandle){return __close(filehandle);},
# same as C fread. read file by FILE*.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
read: func(filehandle,buf,len){return __read(filehandle,buf,len);},
# same as C fwrite. write file by FILE*.
write: func(filehandle,str){return __builtin_write(filehandle,str);},
write: func(filehandle,str){return __write(filehandle,str);},
# same as C fseek. seek place by FILE*.
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
seek: func(filehandle,pos,whence){return __seek(filehandle,pos,whence);},
# same as C ftell.
tell: func(filehandle){return __builtin_tell(filehandle);},
tell: func(filehandle){return __tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln:func(filehandle){return __builtin_readln(filehandle);},
readln:func(filehandle){return __readln(filehandle);},
# same as C stat.
stat: func(filename){return __builtin_stat(filename);},
stat: func(filename){return __stat(filename);},
# same as C feof. check if FILE* gets the end of file(EOF).
eof: func(filehandle){return __builtin_eof(filehandle);}
eof: func(filehandle){return __eof(filehandle);}
};
# get file status. using data from io.stat
@@ -214,35 +333,46 @@ var fstat=func(filename){
# carefully use it, all the calculations are based on integer.
var bits=
{
# xor
bitxor: func(a,b){return __builtin_xor(a,b); },
# and
bitand: func(a,b){return __builtin_and(a,b); },
# or
bitor: func(a,b){return __builtin_or(a,b); },
# nand
bitnand: func(a,b){return __builtin_nand(a,b);},
# not
bitnot: func(a) {return __builtin_not(a); },
# i32 xor
i32_xor: func(a,b){return __i32xor(a,b); },
# i32 and
i32_and: func(a,b){return __i32and(a,b); },
# i32 or
i32_or: func(a,b){return __i32or(a,b); },
# i32 nand
i32_nand:func(a,b){return __i32nand(a,b);},
# i32 not
i32_not: func(a) {return __i32not(a); },
# u32 xor
u32_xor: func(a,b){return __u32xor(a,b); },
# u32 and
u32_and: func(a,b){return __u32and(a,b); },
# u32 or
u32_or: func(a,b){return __u32or(a,b); },
# u32 nand
u32_nand:func(a,b){return __u32nand(a,b);},
# u32 not
u32_not: func(a) {return __u32not(a); },
# get bit data from a special string. for example:
# bits.fld(s,0,3);
# if s stores 10100010(162)
# will get 101(5).
fld: func(str,startbit,len){return __builtin_fld;},
fld: func(str,startbit,len){return __fld;},
# get sign-extended data from a special string. for example:
# bits.sfld(s,0,3);
# if s stores 10100010(162)
# will get 101(5) then this will be signed extended to
# 11111101(-3).
sfld: func(str,startbit,len){return __builtin_sfld;},
sfld: func(str,startbit,len){return __sfld;},
# set value into a special string to store it. little-endian, for example:
# bits.setfld(s,0,8,69);
# set 01000101(69) to string will get this:
# 10100010(162)
# so s[0]=162.
setfld: func(str,startbit,len,val){return __builtin_setfld;},
setfld: func(str,startbit,len,val){return __setfld;},
# get a special string filled by '\0' to use in setfld.
buf: func(len){return __builtin_buf;}
buf: func(len){return __buf;}
};
# mostly used math functions and special constants, you know.
@@ -250,50 +380,77 @@ var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); },
exp: func(x) {return __builtin_exp(x); },
lg: func(x) {return __builtin_lg(x); },
ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);},
isnan: func(x) {return __builtin_isnan(x); }
abs: func(x) {return x>0?x:-x; },
floor: func(x) {return __floor(x); },
pow: func(x,y){return __pow(x,y); },
sin: func(x) {return __sin(x); },
cos: func(x) {return __cos(x); },
tan: func(x) {return __tan(x); },
exp: func(x) {return __exp(x); },
lg: func(x) {return __lg(x); },
ln: func(x) {return __ln(x); },
sqrt: func(x) {return __sqrt(x); },
atan2: func(x,y){return __atan2(x,y);},
isnan: func(x) {return __isnan(x); },
max: func(x,y){return x>y?x:y; },
min: func(x,y){return x<y?x:y; }
};
var unix=
{
pipe: func(){die("not supported yet");},
fork: func(){die("not supported yet");},
pipe: func(){return __pipe;},
fork: func(){return __fork;},
dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");},
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __builtin_opendir;},
readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){return __builtin_closedir;},
waitpid: func(pid,nohang=0){return __waitpid;},
isdir: func(path){return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __opendir;},
readdir: func(handle){return __readdir;},
closedir: func(handle){return __closedir;},
time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);},
environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);}
sleep: func(secs){return __sleep(secs);},
chdir: func(path){return __chdir(path);},
environ: func(){return __environ();},
getcwd: func(){return __getcwd();},
getenv: func(envvar){return __getenv(envvar);},
getpath: func(){return split(os.platform()=="windows"?";":":",unix.getenv("PATH"));}
};
# dylib is the core hashmap for developers to load their own library.
# for safe using dynamic library, you could use 'module' in stl/module.nas
var dylib=
{
# open dynamic lib.
dlopen: func(libname){return __builtin_dlopen;},
dlopen: func(libname){
# find dynamic lib from local dir first
libname=(os.platform()=="windows"?".\\":"./")~libname;
if(io.exists(libname))
return __dlopen(libname);
# find dynamic lib through PATH
var envpath=split(os.platform()=="windows"?";":":",unix.getenv("PATH"));
# first find ./module
append(envpath,".");
var path=os.platform()=="windows"?"\\module\\":"/module/";
foreach(var p;envpath){
p~=path~libname;
if(io.exists(p)){
libname=p;
break;
}
}
return __dlopen(libname);
},
# load symbol from an open dynamic lib.
dlsym: func(lib,sym){return __builtin_dlsym; },
dlsym: func(lib,sym){return __dlsym; },
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
dlclose: func(lib){return __builtin_dlclose; },
dlclose: func(lib){return __dlclose; },
# call the loaded symbol.
dlcall: func(funcptr,args...){return __builtin_dlcall}
dlcall: func(funcptr,args...){return __dlcall}
};
# os is used to use or get some os-related info/functions.
@@ -301,31 +458,42 @@ var dylib=
var os=
{
# get a string that tell which os it runs on.
platform: func(){return __builtin_platform;}
platform: func(){return __platform;},
time: func(){return __logtime; }
};
# runtime gives us some functions that we could manage it manually.
var runtime=
{
# do garbage collection manually.
# carefully use it because using it frequently may make program running slower.
gc: func(){return __builtin_gc;}
# command line arguments
argv: func(){return __sysargv;}
};
# important global constants
var D2R=math.pi/180;
var FPS2KT=0.5925;
var FT2M=0.3048;
var GAL2L=3.7854;
var IN2M=0.0254;
var KG2LB=2.2046;
var KT2FPS=1.6878;
var KT2MPS=0.5144;
var L2GAL=0.2642;
var LB2KG=0.4536;
var M2FT=3.2808;
var M2IN=39.3701;
var M2NM=0.00054;
var MPS2KT=1.9438;
var NM2M=1852;
var R2D=180/math.pi;
# functions that not supported in this runtime:
var bind=func(function,locals,outer_scope=nil){
die("this runtime does not support bind");
}
var call=func(function,args=nil,_me=nil,locals=nil,error=nil){
die("this runtime does not support call");
}
var caller=func(level=1){
die("this runtime does not support caller");
}
var closure=func(function,level=1){
die("this runtime uses \"vm_upval\" instead of \"vm_hash\" as the closure");
}
var compile=func(code,filename="<compile>"){
die("this runtime uses static code generator");
}
var coroutine={
create: func(function){return __cocreate;},
resume: func(co) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
};

View File

@@ -1,7 +1,6 @@
# lib list.nas
# list.nas
# valkmjolnir 2021/3/31
var list=func()
{
var list=func(){
var (begin,end)=(nil,nil);
return{
push_back:func(elem){

40
stl/log.nas Normal file
View File

@@ -0,0 +1,40 @@
# log.nas
# ValKmjolnir 2022/6/14
var log=func(){
var (log_date,log_time,prefix)=(1,1,"");
var os_time="";
var prt_core=func(elem){
os_time=os.time();
print(prefix," ");
if(log_date and log_time)
print(os_time," ");
elsif(log_date or log_time){
var s=split(" ",os_time);
if(log_date)
print(s[0]," ");
if(log_time)
print(s[1]," ");
}
foreach(var i;elem)
print(i);
print("\n");
}
return {
setflags:func(date,time){
log_date=!!date;
log_time=!!time;
},
setprefix:func(s){
if(typeof(s)!="str")
println("[log.nas] must use string as the prefix.");
prefix=s;
},
println:func(elem...){
prt_core(elem);
},
fatalln:func(elem...){
prt_core(elem);
die("log:fatal error");
}
};
}();

43
stl/module.nas Normal file
View File

@@ -0,0 +1,43 @@
# module.nas
# ValKmjolnir 2022/3/5
# this provides safe usage of dylib
# when dylib is closed,
# all the invalid functions cannot be called
var module_call_func=func(fptr,args){
return __dlcall;
}
var extern={
new: func(fptr){
var isopen=1;
return {
close:func(){isopen=0;},
call:func(args...){
return (!isopen)?
nil:
module_call_func(fptr,args);
}
};
}
};
var module={
new: func(name){
var lib=dylib.dlopen(name);
var f={};
return {
get:func(symbol){
if(contains(f,symbol))
return f[symbol];
var fp=extern.new(dylib.dlsym(lib,symbol));
f[symbol]=fp;
return fp;
},
close: func(){
foreach(var i;keys(f))
f[i].close();
dylib.dlclose(lib);
}
};
}
};

311
stl/process_bar.nas Normal file
View File

@@ -0,0 +1,311 @@
# process_bar.nas
# ValKmjolnir 2022/6/14
# this file is inspired by a Python lib: alive_progress
var process_bar={
bar:nil,
spinner:nil
};
process_bar.bar=func(){
var bar={
solid_triangle_right:"▶",
hollow_triangle_right:"▷",
solid_triangle_left:"◀",
hollow_triangle_left:"◁",
solid_circle:"●",
hollow_circle:"○",
tick:"✔",
cross:"✘",
light_shadow:"░",
medium_shadow:"▒",
deep_shadow:"▓",
block:"█",
sharp:"#",
square:"√",
equal:"=",
space:" ",
point:".",
line:"━"
};
var separator={
angle_bracket:["<",">"],
line:["|","|"],
bracket:["[","]"],
space:[" "," "],
curve:["(",")"]
};
return func(front="sharp",back="space",sep="line",length=20){
if(typeof(front)!="str" or !contains(bar,front))
front="sharp";
if(typeof(back)!="str" or !contains(bar,back))
back="space";
if(typeof(sep)!="str" or !contains(separator,sep))
sep="line";
front=bar[front];
back=bar[back];
sep=separator[sep];
return {
bar:func(number){
if(number>1)
number=1;
if(number<0)
number=0;
var finish_length=int(number*length);
var other=length-finish_length;
var s="";
for(var i=0;i<finish_length;i+=1)
s~=front;
for(var i=0;i<other;i+=1)
s~=back;
return sep[0]~s~sep[1];
}
};
};
}();
process_bar.spinner=func(){
var generate_scrolling_spinner=func(s){
if(typeof(s)!="str")
s="****";
if(size(s)>16)
s="****";
var vec=split("",s);
var res=[];
var len=size(vec);
var tmp="";
for(var i=0;i<len;i+=1){
tmp=pop(vec)~tmp;
append(res,tmp);
while(size(res[-1])!=16)
res[-1]~=" ";
}
tmp=res[-1];
while(tmp!=" "){
tmp=" "~substr(tmp,0,15);
append(res,tmp);
}
return res;
}
var spinner={
rise:["▁","▂","▃","▄","▅","▆","▇","█","█","▇","▆","▅","▄","▃","▂","▁"],
vertical:["▏","▎","▍","▌","▋","▊","▉","▇","▇","▉","▊","▋","▌","▍","▎","▏"],
dot:["⠁","⠈","⠐","⠠","⢀","⡀","⠄","⠂"],
dots:["⣾","⣷","⣯","⣟","⡿","⢿","⣻","⣽"],
arrow:["↑","↗","→","↘","↓","↙","←","↖"],
classic:["/","-","\\","-"],
balls:["●...",".●..","..●.","...●",],
dots_wave:[
"⠈⠁⠂⠄⡀⢀⠠⠐",
"⠐⠈⠁⠂⠄⡀⢀⠠",
"⠠⠐⠈⠁⠂⠄⡀⢀",
"⢀⠠⠐⠈⠁⠂⠄⡀",
"⡀⢀⠠⠐⠈⠁⠂⠄",
"⠄⡀⢀⠠⠐⠈⠁⠂",
"⠂⠄⡀⢀⠠⠐⠈⠁",
"⠁⠂⠄⡀⢀⠠⠐⠈"
],
pulse:[
"●---------------",
"-●--------------",
"--●-------------",
"---●------------",
"----●-----------",
"-----●----------",
"------●---------",
"-------√--------",
"-------√\\-------",
"-------√\\/------",
"-------√\\/●-----",
"--------\\/-●----",
"---------/--●---",
"-------------●--",
"--------------●-",
"---------------●"
],
wave:[
"▁▂▃▄▅▆▇█",
"▂▃▄▅▆▇█▇",
"▃▄▅▆▇█▇▆",
"▄▅▆▇█▇▆▅",
"▅▆▇█▇▆▅▄",
"▆▇█▇▆▅▄▃",
"▇█▇▆▅▄▃▂",
"█▇▆▅▄▃▂▁",
"▇▆▅▄▃▂▁▂",
"▆▅▄▃▂▁▂▃",
"▅▄▃▂▁▂▃▄",
"▄▃▂▁▂▃▄▅",
"▃▂▁▂▃▄▅▆",
"▂▁▂▃▄▅▆▇"
],
short_wave:[
"▅▄█▇",
"▄▃▇▆",
"▃▂▆▅",
"▂▁▅▄",
"▁▂▄▃",
"▂▃▃▂",
"▃▄▂▁",
"▄▅▁▂",
"▅▆▂▃",
"▆▇▃▄",
"▇█▄▅",
"█▇▅▆",
"▇▆▆▇",
"▆▅▇█"
],
happy:[
" ",
"ᕗ ",
" ᕗ ",
") ᕗ ",
"ᐛ ) ᕗ ",
" ᐛ ) ᕗ ",
"( ᐛ ) ᕗ ",
" ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ ",
" ᕕ ( ᐛ ) ᕗ",
" ᕕ ( ᐛ ) ",
" ᕕ ( ᐛ )",
" ᕕ ( ᐛ ",
" ᕕ ( ᐛ",
" ᕕ ( ",
" ᕕ (",
" ᕕ ",
" ᕕ",
],
fish:generate_scrolling_spinner("><))))`>"),
wait:generate_scrolling_spinner("please wait"),
stars:generate_scrolling_spinner("********")
};
return func(type="classic",repeat=1){
if(typeof(type)!="str" or !contains(spinner,type))
type="classic";
type=spinner[type];
var counter=0;
return {
next:func(){
var s="";
for(var i=0;i<repeat;i+=1)
s~=type[counter];
counter+=1;
if(counter>=size(type))
counter=0;
return s;
}
}
};
}();
process_bar.default_bar=func(name="classic",length=20){
if(typeof(name)!="str")
name="classic";
if(name=="classic")
return process_bar.bar("sharp","point","bracket",length);
elsif(name=="classic2")
return process_bar.bar("equal","point","bracket",length);
elsif(name=="classic3")
return process_bar.bar("sharp","point","line",length);
elsif(name=="classic4")
return process_bar.bar("equal","point","line",length);
elsif(name=="triangle")
return process_bar.bar("solid_triangle_right","hollow_triangle_right","angle_bracket",length);
elsif(name=="dots")
return process_bar.bar("solid_circle","hollow_circle","curve",length);
elsif(name=="ticks")
return process_bar.bar("tick","space","line",length);
elsif(name=="deep_shadow")
return process_bar.bar("deep_shadow","light_shadow","line",length);
elsif(name=="block")
return process_bar.bar("block","light_shadow","line",length);
elsif(name=="oneline")
return process_bar.bar("line","space","space",length);
else
return process_bar.bar("sharp","point","bracket",length);
}
process_bar.default_spinner=func(name="classic",repeat=1){
if(typeof(name)!="str")
name="classic";
if(name=="rise")
return process_bar.spinner("rise",repeat);
elsif(name=="vertical")
return process_bar.spinner("vertical",repeat);
elsif(name=="dot")
return process_bar.spinner("dot",repeat);
elsif(name=="dots")
return process_bar.spinner("dots",repeat);
elsif(name=="arrow")
return process_bar.spinner("arrow",repeat);
elsif(name=="classic")
return process_bar.spinner("classic",repeat);
elsif(name=="balls")
return process_bar.spinner("balls",repeat);
elsif(name=="dots_wave")
return process_bar.spinner("dots_wave",repeat);
elsif(name=="pulse")
return process_bar.spinner("pulse",repeat);
elsif(name=="wave")
return process_bar.spinner("wave",repeat);
elsif(name=="short_wave")
return process_bar.spinner("short_wave",repeat);
elsif(name=="fish")
return process_bar.spinner("fish",repeat);
elsif(name=="happy")
return process_bar.spinner("happy",repeat);
elsif(name=="wait")
return process_bar.spinner("wait",repeat);
elsif(name=="stars")
return process_bar.spinner("stars",repeat);
else
return process_bar.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)
};
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)
};
var bar_key=keys(bars);
var spin_key=keys(spinners);
for(var i=0;i<40;i+=1){
forindex(var j;bar_key){
var k=bar_key[j];
print("\e["~(j+1)~";1H["~k~"] "~bars[k].bar((i+1)/40));
}
forindex(var j;spin_key){
var k=spin_key[j];
print("\e["~(j+1+size(bars))~";1H["~k~"] |"~spinners[k].next()~"|");
}
unix.sleep(1/20);
}
print("\n");
}

View File

@@ -1,7 +1,6 @@
# lib queue.nas
# queue.nas
# valkmjolnir 2021/3/31
var queue=func()
{
var queue=func(){
var (begin,end)=(nil,nil);
return{
push:func(elem){

View File

@@ -1,5 +1,6 @@
# result.nas
# ValKmjolnir 2021
import("lib.nas");
var ResultTrait={
Ok:func(val){
me.ok=val;

View File

@@ -1,11 +1,9 @@
# lib sort.nas
# sort.nas
# valkmjolnir 2021/4/2
var sort=func(vec,left,right,cmp=func(a,b){return a<=b;})
{
var sort=func(vec,left,right,cmp=func(a,b){return a<=b;}){
if(left>=right) return nil;
var (L,R,tmp)=(left,right,vec[left]);
while(left<right)
{
while(left<right){
while(left<right and cmp(tmp,vec[right]))
right-=1;
while(left<right and cmp(vec[left],tmp))

View File

@@ -1,25 +1,23 @@
# lib stack.nas
# stack.nas
# valkmjolnir 2021/3/31
var stack=func()
{
var next=nil;
var stack=func(){
var vec=[];
return{
push:func(elem){
next={elem:elem,next:next};
append(vec,elem);
},
pop:func(){
if(next!=nil)
next=next.next;
return pop(vec);
},
top:func(){
if(next!=nil)
return next.elem;
if(size(vec)!=0)
return vec[-1];
},
clear:func(){
next=nil;
vec=[];
},
empty:func(){
return next==nil;
return size(vec)==0;
}
};
}

View File

@@ -1,4 +1,3 @@
import("lib.nas");
var char_ttf=[
[" "," "," "," "," "," "],
[" ████╗"," ██╔██║"," ██╔╝██║"," ███████║","██╔═══██║","╚═╝ ╚═╝"],
@@ -28,11 +27,9 @@ var char_ttf=[
["██╗ ██╗","╚██╗ ██╔╝"," ╚████╔╝ "," ╚██╔╝ "," ██║ "," ╚═╝ "],
["████████╗","╚════██╔╝"," ██╔═╝ "," ██╔═╝ ","████████╗","╚═══════╝"],
];
var trans_ttf=func(string)
{
var trans_ttf=func(string){
var str=["","","","","",""];
for(var i=0;i<size(string);i+=1)
{
for(var i=0;i<size(string);i+=1){
var number=string[i];
if(97<=number and number<=122)
for(var j=0;j<6;j+=1)
@@ -48,45 +45,37 @@ var trans_ttf=func(string)
println(i);
return;
}
var curve1=func()
{
var curve1=func(){
var shadow=["░","▒","▓","█","▀","▄","▐","▌"];
rand(100);
var s="";
for(var i=0;i<10;i+=1)
{
for(var i=0;i<10;i+=1){
for(var j=0;j<40;j+=1)
s~=shadow[int(8*rand())];
s~='\n';
}
print(s);
}
var curve2=func()
{
var curve2=func(){
var table=["╚","═","╝","╔","║","╗"];
rand(100);
var s="";
for(var i=0;i<10;i+=1)
{
for(var i=0;i<10;i+=1){
for(var j=0;j<40;j+=1)
s~=table[int(6*rand())];
s~='\n';
}
print(s);
}
var curve3=func()
{
var curve3=func(){
var s=["","","","","",""];
var cnt=0;
foreach(var char;char_ttf)
{
foreach(var char;char_ttf){
cnt+=1;
forindex(var i;char)
s[i]~=char[i];
if(cnt==9)
{
forindex(var i;s)
{
if(cnt==9){
forindex(var i;s){
println(s[i]);
s[i]='';
}
@@ -95,13 +84,10 @@ var curve3=func()
}
return;
}
var curve4=func()
{
var curve4=func(){
var arr=[0,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8];
for(var loop=0;loop<10;loop+=1)
{
for(var i=26;i>=0;i-=1)
{
for(var loop=0;loop<10;loop+=1){
for(var i=26;i>=0;i-=1){
var rand_index=int(i*rand());
(arr[i],arr[rand_index])=(arr[rand_index],arr[i]);
}
@@ -114,9 +100,54 @@ var curve4=func()
}
return;
}
var curve5=func(){
for(var i=0;i<=9;i+=1)
println(i,"\e["~i~"mh \e[0m");
for(var i=30;i<=37;i+=1)
println(i,"\e["~i~"mh \e[0m");
for(var i=40;i<=47;i+=1)
println(i,"\e["~i~"mh \e[0m");
for(var i=90;i<=97;i+=1)
println(i,"\e["~i~"mh \e[0m");
for(var i=100;i<=107;i+=1)
println(i,"\e["~i~"mh \e[0m");
}
var curve6=func(){
var shadow=["m░\e[0m","m▒\e[0m","m▓\e[0m","m█\e[0m","m▀\e[0m","m▄\e[0m","m▐\e[0m","m▌\e[0m"];
var front=[
"30","31","32","33","34","35","36","37",
"90","91","92","93","94","95","96","97"
];
var back=[
"40","41","42","43","44","45","46","47",
"100","101","102","103","104","105","106","107"
];
rand(time(0));
for(var i=0;i<15;i+=1){
for(var j=0;j<45;j+=1)
print("\e["~front[16*rand()]~";"~back[16*rand()]~shadow[8*rand()]);
print('\n');
}
}
var curve7=func(){
var vec=["▀▄─","▄▀─","▀─▄","▄─▀"];
for(var (y,p)=(0,0);y!=6;y+=1){
for(var x=0;x!=16;x+=1)
print(vec[p]);
print("\n");
p+=1;
p=p>=4?0:p;
}
}
if(os.platform()=="windows")
system("chcp 65001");
trans_ttf("just for test");
trans_ttf(" ValKmjolnir ");
trans_ttf("just for fun");
curve1();
curve2();
curve3();
curve4();
curve4();
curve5();
curve6();
curve7();

View File

@@ -1,6 +1,6 @@
# Road check and auto pilot by ValKmjolnir
import("lib.nas");
import("props.nas");
import.stl.fg_env;
var dt=0.01;
var intergral=0;
var derivative=0;
@@ -21,16 +21,14 @@ var road_check_func = func(){
var position_info = geodinfo(lat,lon);
var position_names = position_info[1].names;
if((position_names[0]=="Freeway") or (position_names[0]=="Road"))
{
if((position_names[0]=="Freeway") or (position_names[0]=="Road")){
var car_heading = 0;
var lat_change = 0;
var lon_change = 0;
var left_range = 0;
var right_range = 0;
for(var i=0;i>-0.00005;i-=0.000001)
{
for(var i=0;i>-0.00005;i-=0.000001){
car_heading = props.getNode("/orientation/heading-deg",1).getValue();
lat_change = math.sin(D2R*car_heading);
lon_change = -math.cos(D2R*car_heading);
@@ -43,8 +41,7 @@ var road_check_func = func(){
else
break;
}
for(var i=0;i<0.00005;i+=0.000001)
{
for(var i=0;i<0.00005;i+=0.000001){
car_heading = props.getNode("/orientation/heading-deg",1).getValue();
lat_change = math.sin(D2R*car_heading);
lon_change = -math.cos(D2R*car_heading);
@@ -68,21 +65,30 @@ var road_check_func = func(){
props.getNode("/", 1).setValue("/controls/flight/rudder",Kp*error*error+Ki*intergral+Kd*derivative);
else
props.getNode("/", 1).setValue("/controls/flight/rudder",0);
# for simulation test, in fg these three lines are deleted
println(" rudder :",props.getNode("/controls/flight/rudder",1).getValue());
println(" dt :",dt,'\tintergral :',intergral,'\tderivative :',derivative);
println(" prev-err :",previous_error,'\terror :',error);
previous_error=error;
}
};
var road_check_timer = maketimer(0.01,road_check_func);
var toggle_auto_pilot = func(){
if(!road_check_timer.isRunning)
{
if(!road_check_timer.isRunning){
intergral=0;
road_check_timer.start();
props.getNode("/sim/messages/copilot",1).setValue("ze dong sheng teaan see tong yee tse yung. Auto Sheng Teaan System Activated!");
}
else
{
props.getNode("/sim/messages/copilot",1).setValue('/',"ze dong sheng teaan see tong yee tse yung. Auto Sheng Teaan System Activated!");
}else{
road_check_timer.stop();
props.getNode("/sim/messages/copilot",1).setValue("ze dong sheng teaan see tong yee guan bee. Auto Sheng Teaan System is off.");
props.getNode("/sim/messages/copilot",1).setValue('/',"ze dong sheng teaan see tong yee guan bee. Auto Sheng Teaan System is off.");
}
}
# this is used to simulate the running process in fg
# when using in fg, delete these lines below
toggle_auto_pilot();
road_check_timer.restart(0.1);
simulation();

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var mandelbrot=
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
@@ -149,8 +147,20 @@ var mandelbrot=
var paper=[];
var (ptr,pc)=(0,0);
var (code,inum,stack)=([],[],[]);
var (code,inum,stack,char)=([],[],[],[]);
var (add,mov,jt,jf,in,out)=(0,1,2,3,4,5);
setsize(char,256);
forindex(var i;char)
char[i]=chr(i);
var funcs=[
func{paper[ptr]+=inum[pc];},
func{ptr+=inum[pc];},
func{if(paper[ptr])pc=inum[pc];},
func{if(!paper[ptr])pc=inum[pc];},
func{paper[ptr]=input()[0];},
func{print(char[paper[ptr]]);}
];
var bf=func(program){
setsize(paper,131072);
@@ -212,15 +222,8 @@ var bf=func(program){
return;
}
len=size(code);
for(pc=0;pc<len;pc+=1){
var c=code[pc];
if(c==add) paper[ptr]+=inum[pc];
elsif(c==mov)ptr+=inum[pc];
elsif(c==jt){if(paper[ptr])pc=inum[pc];}
elsif(c==jf){if(!paper[ptr])pc=inum[pc];}
elsif(c==in) paper[ptr]=input()[0];
else print(chr(paper[ptr]));
}
for(pc=0;pc<len;pc+=1)
funcs[code[pc]]();
return;
}

242
test/bfcolored.nas Normal file
View File

@@ -0,0 +1,242 @@
var mandelbrot=
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
>>>>>>>>>]+[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-]>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+
<<<<<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>>
>+<<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+[>>>>>>[>>>>>>>[-]>>]<<<<<<<<<[<<<<<<<<<]>>
>>>>>[-]+<<<<<<++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<+++++++[-[->>>
>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[[-]>>>>>>[>>>>>
>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>
[>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<
<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>+++++++++++++++[[
>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[
>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[
-<<+>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<
<<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<
[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>
>>>>[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+
<<<<<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>
>>>>>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<
+>>>>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<
<]<+<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>
>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<
<<<]>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<
<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->
>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<
<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]<<<<<<<[->+>>>-<<<<]>>>>>>>>>+++++++++++++++++++
+++++++>>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>[<<<<<<<+<[-<+>>>>+<<[-]]>[-<<[->+>>>-
<<<<]>>>]>>>>>>>>>>>>>[>>[-]>[-]>[-]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>>>>>>[>>>>>
[-<<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>[-<<<<<<<<
<+>>>>>>>>>]>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>>]+>[-
]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>>>>>>>]<<<
<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+>>]<
<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[->>>>
>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-]<->>>
[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[>>>>>>[-<
<<<<+>>>>>]<<<<<[->>>>>+<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>+>>>>>>>>
]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+
>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>
[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-
]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>>
[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>++++++++
+++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-<<<<<<<+
>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[
-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>>]>[-<<<<<<[->>>>>+<++<<<<]>>>>>[-<
<<<<+>>>>>]<->+>]<[->+<]<<<<<[->>>>>+<<<<<]>>>>>>[-]<<<<<<+>>>>[-<<<<->>>>]+<<<<
[->>>>->>>>>[>>[-<<->>]+<<[->>->[-<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]
+>>>>>>[>>>>>>>>>]>+<]]+>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<<<<<<<<<<[<<<<<
<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<
[<<<<<<<<<]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<
<<<+<[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<
<<<<<+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<
<<<<<<<<<<]>>>>[-]<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+<]>>>>>>>>]<<<
<<<<<+<[>[->>>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>[->>>>+<<<<]>]<[->>>>-<<<<<<<
<<<<<<<+>>>>>>>>>>]<]>>[->>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>>+<<<<
]<<<<<<<<<<<]>>>>>>+<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>>>>>>>>>]<<<<<<<<<
[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<<<<<<
+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<<<<<<
<<<<<]]>[-]>>[-]>[-]>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<
<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[
[>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+
[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>
[-<<+>>]<<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<
<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[
>[-]<->>>[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[
>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>
>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>[-]>>>>+++++++++++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<
<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<
<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-
<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>
>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>>>
[-<<<->>>]<<<[->>>+<<<]>>>>>>>>]<<<<<<<<+<[>[->+>[-<-<<<<<<<<<<+>>>>>>>>>>>>[-<<
+>>]<]>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<<<]>>[-<+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<]>
[-<<+>>]<<<<<<<<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>
>>>>>>]<<<<<<<<+<[>[->+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>[-<+>]>]<[-<-<<<<<<<<<<+>>>>
>>>>>>>]<<]>>>[-<<+>[-<-<<<<<<<<<<+>>>>>>>>>>>]>]<[-<+>]<<<<<<<<<<<<]>>>>>+<<<<<
]>>>>>>>>>[>>>[-]>[-]>[-]>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>>>[-<<<<<
<+>>>>>>]<<<<<<[->>>>>>+<<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>
>]>>[-<<<<<<<[->>>>>+<++<<<<]>>>>>[-<<<<<+>>>>>]<->+>>]<<[->>+<<]<<<<<[->>>>>+<<
<<<]+>>>>[-<<<<->>>>]+<<<<[->>>>->>>>>[>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<
<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>[-<<->>]+<<[->>->[-<<<+>>>]<
<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<
<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+
<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>[->>>+<<<]>]<[->>>-
<<<<<<<<<<<<<+>>>>>>>>>>]<]>>[->>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>+<<<
]<<<<<<<<<<<]>>>>>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]]>>>>[-<<<<+>
>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<<[->>>-
<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[
->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<<<<<]]>>>>[-]<<<<]>>>>[-<<<<+>>
>>]<<<<[->>>>+>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>[>>>>>>
>>>]<<<<<<<<<[>[->>>>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<
<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<
<<<<]]>>>>>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>[-<<<<+
>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<+>>>>>
]<<<<<[->>>>>+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>
>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>
>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[-<<+
>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>
[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-
]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>>
[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<
<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>
>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<+>>>
>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+
<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>
>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<<<<]
>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<<<<<
]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->>>+<
<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>
>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>->>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>]<<+>>>>[-<<<<
->>>>]+<<<<[->>>>-<<<<<<.>>]>>>>[-<<<<<<<.>>>>>>>]<<<[-]>[-]>[-]>[-]>[-]>[-]>>>[
>[-]>[-]>[-]>[-]>[-]>[-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-]>>>>]<<<<<<<<<
[<<<<<<<<<]>+++++++++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+>>>>>>>>>+<<<<<<<<
<<<<<<[<<<<<<<<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+[-]>>[>>>>>>>>>]<<<<<
<<<<[>>>>>>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<[<<<<<<<<<]>>>>>>>[-]+>>>]<<<<
<<<<<<]]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+>>[>+>>>>[-<<<<->>>>]<<<<[->>>
>+<<<<]>>>>>>>>]<<+<<<<<<<[>>>>>[->>+<<]<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<
<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<<<<<[->>>>>>+<<<<<<]<
+<<<<<<<<<]>>>>>>>-<<<<[-]+<<<]+>>>>>>>[-<<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>->>[>>
>>>[->>+<<]>>>>]<<<<<<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<
<<<<[->>>>>>+<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+<<<
<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<<<<<->>>>>]+<<<<<[->>>>>->>[-<<<<<<<+>>>>>>>]<<<<
<<<[->>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>[-<
<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>-<<[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<<<<<<<<<[<<<
<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<
<<[<<<<<<<<<]>>>>[-]<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>-<<<<<[<<<<<<<
<<]]>>>]<<<<.>>>>>>>>>>[>>>>>>[-]>>>]<<<<<<<<<[<<<<<<<<<]>++++++++++[-[->>>>>>>>
>+<<<<<<<<<]>>>>>>>>>]>>>>>+>>>>>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-<<<<<<
<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+[-]>[>>>>>>>>>]<<<<<<<<<[>>>>>>>>[-<<<<<<<+>>>>>>
>]<<<<<<<[->>>>>>>+<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+>>]<<<<<<<<<<]]>>>>>>>>[-<<<<<
<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+>[>+>>>>>[-<<<<<->>>>>]<<<<<[->>>>>+<<<<<]>>>>>>
>>]<+<<<<<<<<[>>>>>>[->>+<<]<<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[-]<-
>>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]<+<<<<<<
<<<]>>>>>>>>-<<<<<[-]+<<<]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>->[>>>
>>>[->>+<<]>>>]<<<<<<<<<[>[-]<->>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<
<<<<<[->>>>>>>+<<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<<->>>>>>]+<
<<<<<[->>>>>>->>[-<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<<<<<<<<<<[<<<<<<<
<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>
-<<[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>
>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>[-]<<<++++
+[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>-<<<<<<[<<<<
<<<<<]]>>>]";
var paper=[];
var (ptr,pc)=(0,0);
var (code,inum,stack,char)=([],[],[],[]);
var (add,mov,jt,jf,in,out)=(0,1,2,3,4,5);
setsize(char,256);
var color=[
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",
"\e[90m","\e[91m","\e[92m","\e[93m","\e[94m","\e[95m","\e[96m"
];
func(){
var cnt=0;
forindex(var i;char){
char[i]=color[cnt]~chr(i)~"\e[0m";
cnt+=1;
if(cnt>12)
cnt=0;
}
}();
var funcs=[
func{paper[ptr]+=inum[pc];},
func{ptr+=inum[pc];},
func{if(paper[ptr])pc=inum[pc];},
func{if(!paper[ptr])pc=inum[pc];},
func{paper[ptr]=input()[0];},
func{print(char[paper[ptr]]);}
];
var bf=func(program){
setsize(paper,131072);
var len=size(program);
for(var i=0;i<len;i+=1){
var c=chr(program[i]);
if(c=='+' or c=='-'){
append(code,add);
append(inum,0);
for(;i<len;i+=1){
if(chr(program[i])=='+')
inum[-1]+=1;
elsif(chr(program[i])=='-')
inum[-1]-=1;
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
}
}
elsif(c=='<' or c=='>'){
append(code,mov);
append(inum,0);
for(;i<len;i+=1){
if(chr(program[i])=='>')
inum[-1]+=1;
elsif(chr(program[i])=='<')
inum[-1]-=1;
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
}
}
elsif(c==','){
append(code,in);
append(inum,0);
}
elsif(c=='.'){
append(code,out);
append(inum,0);
}
elsif(c=='['){
append(code,jf);
append(inum,0);
append(stack,size(code)-1);
}
elsif(c==']'){
if(!size(stack))
die("lack [");
var label=pop(stack);
append(code,jt);
append(inum,label-1);
inum[label]=size(code)-2;
}
}
if(size(stack)){
die("lack ]");
return;
}
len=size(code);
for(pc=0;pc<len;pc+=1)
funcs[code[pc]]();
return;
}
bf(mandelbrot);

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var mandelbrot=
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
@@ -151,21 +149,17 @@ var bf=func(program)
{
var stack=[];
var len=size(program);
var f="import('lib.nas');\nvar ptr=0;\nvar paper=[];\nsetsize(paper,131072);\n";
for(var i=0;i<len;i+=1)
{
var f="#automatically generated by bfconvertor.nas\nimport('lib.nas');\nvar ptr=0;\nvar paper=[];\nsetsize(paper,131072);\n";
for(var i=0;i<len;i+=1){
var c=chr(program[i]);
if(c=='+' or c=='-')
{
if(c=='+' or c=='-'){
var cnt=0;
for(;i<len;i+=1)
{
for(;i<len;i+=1){
if(chr(program[i])=='+')
cnt+=1;
elsif(chr(program[i])=='-')
cnt-=1;
elsif(chr(program[i])!='\n')
{
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
@@ -177,18 +171,14 @@ var bf=func(program)
f~="paper[ptr]+="~cnt~";\n";
elsif(cnt<0)
f~="paper[ptr]-="~(-cnt)~";\n";
}
elsif(c=='<' or c=='>')
{
}elsif(c=='<' or c=='>'){
var cnt=0;
for(;i<len;i+=1)
{
for(;i<len;i+=1){
if(chr(program[i])=='>')
cnt+=1;
elsif(chr(program[i])=='<')
cnt-=1;
elsif(chr(program[i])!='\n')
{
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
@@ -200,21 +190,15 @@ var bf=func(program)
f~="ptr+="~cnt~";\n";
elsif(cnt<0)
f~="ptr-="~(-cnt)~";\n";
}
elsif(c==',')
{
}elsif(c==','){
for(var j=0;j<size(stack);j+=1)
f~='\t';
f~="paper[ptr]=input();\n";
}
elsif(c=='.')
{
f~="paper[ptr]=input()[0];\n";
}elsif(c=='.'){
for(var j=0;j<size(stack);j+=1)
f~='\t';
f~="print(chr(paper[ptr]));\n";
}
elsif(c=='[')
{
}elsif(c=='['){
for(var j=0;j<size(stack);j+=1)
f~='\t';
f~="while(paper[ptr])\n";
@@ -222,11 +206,8 @@ var bf=func(program)
f~='\t';
f~="{\n";
append(stack,0);
}
elsif(c==']')
{
if(!size(stack))
{
}elsif(c==']'){
if(!size(stack)){
println("lack [");
return;
}
@@ -236,8 +217,7 @@ var bf=func(program)
f~="}\n";
}
}
if(size(stack))
{
if(size(stack)){
println("lack ]");
return;
}

View File

@@ -1,26 +1,17 @@
import("stl/lib.nas");
import("stl/queue.nas");
import.stl.queue;
rand(time(0));
var pixel=[' ','#','.','*'];
var map=[];
for(var i=0;i<10;i+=1)
{
for(var i=0;i<10;i+=1){
append(map,[]);
for(var j=0;j<20;j+=1)
append(map[i],(rand()>0.7));
}
var prt=func()
{
if(os.platform()=="windows")
system("cls");
else
system("clear");
var s="+--------------------+\n";
for(var i=0;i<10;i+=1)
{
var prt=func(){
var s="\e[0;0H+--------------------+\n";
for(var i=0;i<10;i+=1){
s~="|";
for(var j=0;j<20;j+=1)
s~=pixel[map[i][j]];
@@ -28,31 +19,30 @@ var prt=func()
}
s~='+--------------------+\n';
print(s);
unix.sleep(1/144);
}
var bfs=func(begin,end)
{
var bfs=func(begin,end){
var move=[[1,0],[0,1],[-1,0],[0,-1]];
var que=queue();
que.push(begin);
map[begin[0]][begin[1]]=2;
map[end[0]][end[1]]=0;
while(!que.empty())
{
if(map[1][0]==1 and map[0][1]==1)
map[1][0]=0;
while(!que.empty()){
var vertex=que.front();
que.pop();
foreach(var i;move)
{
foreach(var i;move){
var x=vertex[0]+i[0];
var y=vertex[1]+i[1];
if(x==end[0] and y==end[1])
{
if(x==end[0] and y==end[1]){
map[x][y]=3;
prt();
print("reached.\n");
return;
}
if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0)
{
if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0){
que.push([x,y]);
map[x][y]=2;
}
@@ -63,4 +53,7 @@ var bfs=func(begin,end)
return;
}
if(os.platform()=="windows")
system("chcp 65001");
print("\ec");
bfs([0,0],[9,19]);

View File

@@ -1 +1 @@
for(var i=0;i<4e6;i+=1);
for(var i=0;i<4e6;i+=1);

View File

@@ -1,9 +1,6 @@
import("lib.nas");
rand(time(0));
var new_neuron=func()
{
var new_neuron=func(){
return {
in:0,
out:0,
@@ -13,50 +10,51 @@ var new_neuron=func()
};
}
var sigmoid=func(x)
{
var tanh=func(x){
var (a,b)=(math.exp(x),math.exp(-x));
return (a-b)/(a+b);
}
var difftanh=func(x){
x=tanh(x);
return 1-x*x;
}
var sigmoid=func(x){
return 1/(1+math.exp(-x));
}
var diffsigmoid=func(x)
{
var diffsigmoid=func(x){
x=sigmoid(x);
return x*(1-x);
}
var (inum,hnum,onum,lr)=(2,4,1,0.1);
var (inum,hnum,onum)=(2,4,1);
var training_set=[[0,0],[0,1],[1,0],[1,1]];
var expect=[0,1,1,0];
var hidden=[];
for(var i=0;i<hnum;i+=1)
{
for(var i=0;i<hnum;i+=1){
append(hidden,new_neuron());
for(var j=0;j<inum;j+=1)
append(hidden[i].w,2*rand());
hidden[i].bia=5*rand();
append(hidden[i].w,rand()>0.5?-2*rand():2*rand());
hidden[i].bia=rand()>0.5?-5*rand():5*rand();
}
var output=[];
for(var i=0;i<onum;i+=1)
{
for(var i=0;i<onum;i+=1){
append(output,new_neuron());
for(var j=0;j<hnum;j+=1)
append(output[i].w,2*rand());
output[i].bia=5*rand();
append(output[i].w,rand()>0.5?-2*rand():2*rand());
output[i].bia=rand()>0.5?-5*rand():5*rand();
}
var forward=func(x)
{
var forward=func(x){
var input=training_set[x];
for(var i=0;i<hnum;i+=1)
{
for(var i=0;i<hnum;i+=1){
hidden[i].in=hidden[i].bia;
for(var j=0;j<inum;j+=1)
hidden[i].in+=hidden[i].w[j]*input[j];
hidden[i].out=sigmoid(hidden[i].in);
hidden[i].out=tanh(hidden[i].in);
}
for(var i=0;i<onum;i+=1)
{
for(var i=0;i<onum;i+=1){
output[i].in=output[i].bia;
for(var j=0;j<hnum;j+=1)
output[i].in+=output[i].w[j]*hidden[j].out;
@@ -64,18 +62,15 @@ var forward=func(x)
}
return;
}
var run=func(vec)
{
var run=func(vec){
var input=vec;
for(var i=0;i<hnum;i+=1)
{
for(var i=0;i<hnum;i+=1){
hidden[i].in=hidden[i].bia;
for(var j=0;j<inum;j+=1)
hidden[i].in+=hidden[i].w[j]*input[j];
hidden[i].out=sigmoid(hidden[i].in);
hidden[i].out=tanh(hidden[i].in);
}
for(var i=0;i<onum;i+=1)
{
for(var i=0;i<onum;i+=1){
output[i].in=output[i].bia;
for(var j=0;j<hnum;j+=1)
output[i].in+=output[i].w[j]*hidden[j].out;
@@ -83,29 +78,24 @@ var run=func(vec)
}
return;
}
var get_error=func(x)
{
var get_error=func(x){
return 0.5*(expect[x]-output[0].out)*(expect[x]-output[0].out);
}
var backward=func(x)
{
var backward=func(x){
var input=training_set[x];
output[0].diff=(expect[x]-output[0].out)*diffsigmoid(output[0].in);
for(var i=0;i<hnum;i+=1)
{
for(var i=0;i<hnum;i+=1){
hidden[i].diff=0;
for(var j=0;j<onum;j+=1)
hidden[i].diff+=output[j].w[i]*output[j].diff;
hidden[i].diff*=diffsigmoid(hidden[i].in);
hidden[i].diff*=difftanh(hidden[i].in);
}
for(var i=0;i<hnum;i+=1)
{
for(var i=0;i<hnum;i+=1){
hidden[i].bia+=hidden[i].diff;
for(var j=0;j<inum;j+=1)
hidden[i].w[j]+=hidden[i].diff*input[j];
}
for(var i=0;i<onum;i+=1)
{
for(var i=0;i<onum;i+=1){
output[i].bia+=output[i].diff;
for(var j=0;j<hnum;j+=1)
output[i].w[j]+=output[i].diff*hidden[j].out;
@@ -114,11 +104,9 @@ var backward=func(x)
}
var (cnt,error)=(0,100);
while(error>0.0005)
{
while(error>0.0005){
error=0;
for(var i=0;i<4;i+=1)
{
for(var i=0;i<4;i+=1){
forward(i);
error+=get_error(i);
backward(i);
@@ -126,8 +114,7 @@ while(error>0.0005)
cnt+=1;
}
print('finished after ',cnt,' epoch.\n');
foreach(var v;training_set)
{
foreach(var v;training_set){
run(v);
print(v,': ',output[0].out,'\n');
}

View File

@@ -1,65 +1,85 @@
import("lib.nas");
var source=[
"main.cpp ",
"nasal_err.h ",
"nasal_ast.h ",
"nasal_builtin.h ",
"nasal_codegen.h ",
"nasal_opt.h ",
"nasal_gc.h ",
"nasal_import.h ",
"nasal_lexer.h ",
"nasal_parse.h ",
"nasal_vm.h ",
"nasal_dbg.h ",
"nasal.h "
"main.cpp ",
"nasal_ast.h ",
"nasal_builtin.h ",
"nasal_codegen.h ",
"nasal_dbg.h ",
"nasal_err.h ",
"nasal_gc.h ",
"nasal_import.h ",
"nasal_lexer.h ",
"nasal_opt.h ",
"nasal_parse.h ",
"nasal_vm.h ",
"nasal.h "
];
var lib=[
"stl/lib.nas ",
"stl/list.nas ",
"stl/queue.nas ",
"stl/result.nas ",
"stl/sort.nas ",
"stl/stack.nas "
"fg_env.nas ",
"file.nas ",
"lib.nas ",
"list.nas ",
"log.nas ",
"module.nas ",
"process_bar.nas ",
"queue.nas ",
"result.nas ",
"sort.nas ",
"stack.nas "
];
var testfile=[
"test/ascii-art.nas ",
"test/auto_crash.nas ",
"test/bf.nas ",
"test/bfconvertor.nas ",
"test/bfs.nas ",
"test/bigloop.nas ",
"test/bp.nas ",
"test/calc.nas ",
"test/choice.nas ",
"test/class.nas ",
"test/exception.nas ",
"test/fib.nas ",
"test/filesystem.nas ",
"test/hexdump.nas ",
"test/json.nas ",
"test/leetcode1319.nas ",
"test/lexer.nas ",
"test/life.nas ",
"test/loop.nas ",
"test/mandel.nas ",
"test/mandelbrot.nas ",
"test/module_test.nas ",
"test/nasal_test.nas ",
"test/pi.nas ",
"test/prime.nas ",
"test/props.nas ",
"test/quick_sort.nas ",
"test/scalar.nas ",
"test/trait.nas ",
"test/turingmachine.nas",
"test/ycombinator.nas "
"ascii-art.nas ",
"auto_crash.nas ",
"bf.nas ",
"bfcolored.nas ",
"bfconvertor.nas ",
"bfs.nas ",
"bigloop.nas ",
"bp.nas ",
"calc.nas ",
"choice.nas ",
"class.nas ",
"coroutine.nas ",
"diff.nas ",
"exception.nas ",
"fib.nas ",
"filesystem.nas ",
"hexdump.nas ",
"httptest.nas ",
"json.nas ",
"leetcode1319.nas ",
"lexer.nas ",
"life.nas ",
"loop.nas ",
"mandel.nas ",
"mandelbrot.nas ",
"md5.nas ",
"md5compare.nas ",
"module_test.nas ",
"nasal_test.nas ",
"occupation.nas ",
"pi.nas ",
"prime.nas ",
"qrcode.nas ",
"quick_sort.nas ",
"scalar.nas ",
"snake.nas ",
"tetris.nas ",
"trait.nas ",
"turingmachine.nas",
"utf8chk.nas ",
"wavecollapse.nas ",
"ycombinator.nas "
];
var module=[
"module/fib.cpp "
"fib.cpp ",
"keyboard.cpp ",
"nasocket.cpp ",
"libfib.nas ",
"libkey.nas ",
"libsock.nas "
];
var getname=func(s){
@@ -69,28 +89,27 @@ var getname=func(s){
}
var count=func(s,c){
var (cnt,len,ch)=(0,size(s),c[0]);
for(var i=0;i<len;i+=1)
cnt+=(s[i]==ch);
var cnt=0;
foreach(var i;split(c,s))
cnt+=(size(i)!=0 and i!="\r")
return cnt;
}
var calc=func(codetype,files){
var calc=func(codetype,files,path=""){
println(codetype);
var (bytes,line,semi,line_cnt,semi_cnt)=(0,0,0,0,0);
forindex(var i;files){
var s=io.fin(getname(files[i]));
var s=io.fin(getname(path~files[i]));
(line_cnt,semi_cnt)=(count(s,'\n'),count(s,';'));
println(files[i],'| ',line_cnt,' \tline | ',semi_cnt,' \tsemi');
println(files[i],'| ',line_cnt,'\tline | ',semi_cnt,' \tsemi');
bytes+=size(s);
line+=line_cnt;
semi+=semi_cnt;
}
println('total: | ',line,' \tline | ',semi,' \tsemi');
println('bytes: | ',bytes,'\tbytes| ',int(bytes/1024),' \tkb');
println('total: | ',line,'\tline | ',semi,' \tsemi');
println(' | ',int(bytes/1024),'\tkb');
return int(bytes/1024);
}
calc("source code:",source);
calc("lib:",lib);
calc("test file:",testfile);
calc("module:",module);
var all=calc("source code:",source)+calc("lib:",lib,"stl/")+calc("test file:",testfile,"test/")+calc("module:",module,"module/");
println('\ntotal: | ',all,'\tkb');

View File

@@ -1,4 +1,3 @@
import("lib.nas");
var condition_true=1;
var condition_false=0;
if(condition_true)

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var student=func(n,a){
return {
print_info:func println(n,' ',a),

57
test/coroutine.nas Normal file
View File

@@ -0,0 +1,57 @@
# coroutine.nas by ValKmjolnir
# 2022/5/19
import.stl.process_bar;
var fib=func(){
var (a,b)=(1,1);
coroutine.yield(a);
coroutine.yield(b);
while(1){
(a,b)=(b,a+b);
coroutine.yield(b);
}
return;
}
# different coroutines don't share the same local scope
var co=[coroutine.create(fib),coroutine.create(fib)];
for(var i=0;i<45;i+=1){
var res=[coroutine.resume(co[0]),coroutine.resume(co[1])];
println('co[0]: ',res[0]==nil?nil:res[0][0],'\nco[1]: ',res[1]==nil?nil:res[1][0]);
}
# test if coroutine can get upvalues
func(){
var x=1;
var co=coroutine.create(func(){
for(var j=0;j<128;j+=1){
coroutine.yield(x,i,j);
x+=1;
}
});
for(var i=0;i<16;i+=1)
println(coroutine.resume(co));
}();
# pressure test
var productor=func(){
for(var i=0;;i+=1)
coroutine.yield(i);
}
var total=4000; # ms
var co=coroutine.create(productor);
var tm=maketimestamp();
var counter=0;
var bar=process_bar.bar("block","point","line",40);
var consumer=func(){
counter+=1;
for(var i=0;i<5;i+=1)
coroutine.resume(co);
var rate=(tm.elapsedMSec()+1)/total;
print(bar.bar(rate)," ",rate*100,"% \r");
}
tm.stamp();
while(tm.elapsedMSec()<total)
consumer();
println("\nexecute ",counter," tasks during ",total," ms, avg ",counter/total," tasks/ms.")

124
test/diff.nas Normal file
View File

@@ -0,0 +1,124 @@
var myers=func(src,dst,show_table=0){
(src,dst)=(split("\n",src),split("\n",dst));
append(src,"");
append(dst,"");
var (src_len,dst_len)=(size(src),size(dst));
var mat=[];
setsize(mat,dst_len*src_len);
forindex(var i;mat){
mat[i]=0;
}
var visited=[];
setsize(visited,dst_len*src_len);
forindex(var i;visited){
visited[i]=0;
}
forindex(var y;dst)
forindex(var x;src)
mat[y*src_len+x]=(src[x]==dst[y]);
if(show_table){
var curve=[
["+---","| "],
["+---","| \\ "]
];
var s="";
forindex(var y;dst){
forindex(var t;curve[0]){
forindex(var x;src){
s~=curve[mat[y*src_len+x]][t];
}
s~=["+","|"][t]~"\n";
}
}
forindex(var i;src)
s~="+---";
print(s~"+\n");
}
var (total,path,vec)=([],[],[[0,0,-1]]);
visited[0]=1;
while(size(vec)){
append(total,vec);
var tmp=[];
forindex(var i;vec){
var elem=vec[i];
var (x,y)=(elem[1],elem[0]);
# find solution
if(x==src_len-1 and y==dst_len-1){
append(path,vec[i]);
for(var (prev,iter)=(elem[2],size(total)-1);iter>0;iter-=1){
var t=total[iter-1][prev];
append(path,t);
prev=t[2];
}
if(show_table){
for(var t=size(path)-1;t>=0;t-=1)
print("("~path[t][1]~","~path[t][0]~")",t==0?"":"->");
println();
}
# reverse path
for(var t=0;t<size(path)/2;t+=1)
(path[t],path[-1-t])=(path[-1-t],path[t]);
# print diff
for(var t=1;t<size(path);t+=1){
var (prev_x,prev_y)=(path[t-1][1],path[t-1][0]);
var (x,y)=(path[t][1],path[t][0]);
var (sub_x,sub_y)=(x-prev_x,y-prev_y);
if(sub_x==1 and sub_y==1){
if(show_table)
println(" ",src[prev_x]);
}elsif(sub_x==1 and sub_y==0){
println("\e[31m - ",src[prev_x],"\e[0m");
}elsif(sub_x==0 and sub_y==1){
println("\e[32m + ",dst[prev_y],"\e[0m");
}
}
return;
}
# do bfs
if(mat[y*src_len+x]==1){
if(x+1<src_len and y+1<dst_len and visited[(y+1)*src_len+x+1]==0){
append(tmp,[y+1,x+1,i]);
visited[(y+1)*src_len+x+1]=1;
}
}
else{
if(x+1<src_len and visited[y*src_len+x+1]==0){
append(tmp,[y,x+1,i]);
visited[y*src_len+x+1]=1;
}
if(y+1<dst_len and visited[(y+1)*src_len+x]==0){
append(tmp,[y+1,x,i]);
visited[(y+1)*src_len+x]=1;
}
}
}
vec=tmp;
}
}
func(diff){
diff(
"var a=0;\nvar b=1;\nprint(\"hello \",a);\nvar c=2;\nc=[];\nvar d=3;\nvar l=list();\nvar q=queue();\n",
"var a=0;\nvar b=1;\nb=[];\nprintln(\"hello \",a);\nvar c=2;\nvar d=3;\nprintln(\"hello world!\");\nvar l=list();\nvar q=queue();\n",
1
);
print("\n");
diff(
"A\nB\nC\nA\nB\nB\nA\n",
"C\nB\nA\nB\nA\nC\n",
1
);
print("\n");
diff(
io.fin("test/bf.nas"),
io.fin("test/bfcolored.nas")
);
}(myers);

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var ResultTrait={
Ok:func(val){
me.ok=val;

View File

@@ -1,4 +1,3 @@
import("lib.nas");
var fib=func(x)
{
if(x<2) return x;

View File

@@ -1,16 +1,9 @@
import("lib.nas");
var fd=io.open("test/filesystem.nas");
while((var line=io.readln(fd))!=nil)
println(line);
io.close(fd);
println(io.stat("test/filesystem.nas"));
var dd=unix.opendir("test");
while(var name=unix.readdir(dd))
println(name);
unix.closedir(dd);
var files=func(dir){
var dd=unix.opendir(dir);
var res=[];
@@ -20,17 +13,26 @@ var files=func(dir){
return res;
}
var prt=func(s,path){
foreach(var i;files(path)){
print(s,i);
if(unix.isdir(path~'/'~i)){
print(' <dir>\n');
if(i!='.' and i!='..')
prt(s~' |',path~'/'~i);
var vec=files(path);
var last=size(vec)-1;
forindex(var i;vec){
var f=vec[i];
if(f=="." or f=="..")
continue;
foreach(var j;s)
print("\e[34m",j,"\e[0m");
if(unix.isdir(path~"/"~f)){
println("\e[34m",i==last?"└─":"├─","\e[0m\e[36m",f," >\e[0m");
append(s,i==last?" ":"│ ");
prt(s,path~"/"~f);
pop(s);
}elsif(unix.isfile(path~"/"~f)){
println("\e[34m",i==last?"└─":"├─","\e[0m\e[32m",f,"\e[0m");
}else{
println("\e[34m",i==last?"└─":"├─","\e[0m\e[91m",f,"\e[0m");
}
elsif(unix.isfile(path~'/'~i))
print(" <file>\n");
else
print(' <unknown>\n');
}
}
prt('',".");
println("\e[36mNasal-Interpreter >\e[0m");
prt([""],".");

View File

@@ -1,8 +1,6 @@
# hexdump.nas by ValKmjolnir
# 2021/8/13
import("lib.nas");
# init
var hex_num=[
'0','1','2','3',
@@ -18,16 +16,23 @@ foreach(var i;hex_num)
# read file
var s=func(){
var filename=[
"nasal.h",
"LICENSE",
"main.cpp",
"makefile",
"nasal_ast.h",
"nasal_builtin.h",
"nasal_codegen.h",
"nasal_dbg.h",
"nasal_err.h",
"nasal_gc.h",
"nasal_import.h",
"nasal_lexer.h",
"nasal_opt.h",
"nasal_parse.h",
"nasal_vm.h",
"nasal.ebnf",
"nasal.h",
"README.md"
];
var ret="";
foreach(var elem;filename)
@@ -44,8 +49,12 @@ var hex_index=[0,0,0,0];
# print binary in text format
var textprint=func(index){
var info='';
for(var i=index-cnt;i<index;i+=1)
info~=(0<=s[i] and s[i]<32)?'.':chr(s[i]);
if(os.platform()=="windows")
for(var i=index-cnt;i<index;i+=1)
info~=(s[i]<32 or s[i]>=128)?'.':chr(s[i]);
else
for(var i=index-cnt;i<index;i+=1)
info~=(0<=s[i] and s[i]<32)?'.':chr(s[i]);
for(var i=cnt;i<16;i+=1)
info~='.';
return ' |'~info~'|\n';

333
test/httptest.nas Normal file
View File

@@ -0,0 +1,333 @@
import.module.libsock;
var http=func(){
var sd=nil;
return {
establish:func(ip,port){
sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP);
socket.bind(sd,ip,port);
socket.listen(sd,1);
println("[",os.time(),"] start server at [",ip,":",port,"]");
},
shutdown:func(){
println("[",os.time(),"] shutdown server");
socket.closesocket(sd);
},
accept:func(){
return socket.accept(sd);
},
disconnect:func(client,log=0){
if(log)
println("[",os.time(),"] [",client.ip,"] disconnected");
return socket.closesocket(client.sd);
},
recv:func(client){
var data=socket.recv(client.sd,2048);
if(!data.size){
println("[",os.time(),"] [",client.ip,"] closed connection");
return nil;
}
var first=split("\n",data.str)[0];
var (type,path)=split(" ",first)[0,1];
println("[",os.time(),"] [",client.ip,"] request ",type," [",path,"]");
return {type:type,path:path};
},
send:func(client,content){
println("[",os.time(),"] [",client.ip,"] get size ",size(content)," byte(s)");
return socket.send(client.sd,content);
}
};
}();
http.establish("127.0.0.1",8080);
var highlight_style="
<style>
body{
background: #303030;
}
pre{
background: #303030;
font-family: 'Courier New', Courier, monospace;
font-size: small;
color: #d4d4d4;
}
code{
color: white;
font-family: 'Courier New', Courier, monospace;
font-size: small;
text-align: left;
}
code.key{color: #f895e7;}
code.id{color: #8abef0;}
code.opr{color: #f895e7;}
code.brace{color: #eafd70;}
code.str{color: #a5ffd0;}
code.num{color: #ff9a41;}
code.note{color: #808080;}
</style>";
var html_read_file=func(filename){
var timer=maketimestamp();
timer.stamp();
var keyword=["var","func","for","while","foreach","forindex","break","continue","return","if","else","elsif","nil"];
var file_text=split("",io.fin(filename));
var (s,index,len)=("",-1,size(file_text));
var content="";
var next=func(){
if(index+1>=len)
return index+1;
index+=1;
s=file_text[index];
return index;
}
var prev=func(){
index-=1;
s=file_text[index];
}
while(1){
if(next()>=len)
break;
if(s==">")
content~="<code class=\"opr\">&gt;</code>";
elsif(s=="<")
content~="<code class=\"opr\">&lt;</code>";
elsif(s=="[" or s=="]" or s=="(" or s==")" or s=="{" or s=="}")
content~="<code class=\"brace\">"~s~"</code>";
elsif(s=="=" or s=="," or s==";" or s==":" or s=="|" or s=="&" or s=="!" or s=="?" or s=="+" or s=="-" or s=="*" or s=="/" or s=="~" or s==".")
content~="<code class=\"opr\">"~s~"</code>";
elsif(s=="_" or ("a"[0]<=s[0] and s[0]<="z"[0]) or ("A"[0]<=s[0] and s[0]<="Z"[0]) or s[0]<0 or s[0]>=128){
var tmp=""~s; # generate a new string
while(1){
if(next()>=len)
break;
if(s=="_" or ("a"[0]<=s[0] and s[0]<="z"[0]) or ("A"[0]<=s[0] and s[0]<="Z"[0]) or ("0"[0]<=s[0] and s[0]<="9"[0]) or s[0]<0 or s[0]>=128)
tmp~=s;
else{
prev();
break;
}
}
var is_key=0;
foreach(var i;keyword)
if(tmp==i){
is_key=1;
content~="<code class=\"key\">"~tmp~"</code>";
break;
}
if(!is_key)
content~="<code class=\"id\">"~tmp~"</code>";
}elsif("0"[0]<=s[0] and s[0]<="9"[0]){
content~="<code class=\"num\">"~s;
if(next()>=len){
content~="</code>";
break;
}
if(s=="o"){
content~="o";
while(1){
if(next()>=len)
break;
if("0"[0]<=s[0] and s[0]<="7"[0])
content~=s;
else
break;
}
content~="</code>";
prev();
}elsif(s=="x"){
content~="x";
while(1){
if(next()>=len)
break;
if(("0"[0]<=s[0] and s[0]<="9"[0]) or ("a"[0]<=s[0] and s[0]<='f') or ("A"[0]<=s[0] or s[0]<="F"))
content~=s;
else
break;
}
content~="</code>";
prev();
}elsif(("0"[0]<=s[0] and s[0]<="9"[0]) or s=="." or s=="e"){
while("0"[0]<=s[0] and s[0]<="9"[0]){
content~=s;
if(next()>=len)
break;
}
if(s=="."){
content~=s;
if(next()>=len)
break;
}
while("0"[0]<=s[0] and s[0]<="9"[0]){
content~=s;
if(next()>=len)
break;
}
if(s=="e"){
content~=s;
if(next()>=len)
break;
if(s=="-" or s=="+"){
content~=s;
if(next()>=len)
break;
}
}
while("0"[0]<=s[0] and s[0]<="9"[0]){
content~=s;
if(next()>=len)
break;
}
prev();
content~="</code>";
}else{
prev();
content~="</code>";
}
}elsif(s=="\"" or s=="'" or s=="`"){
var quot=s~""; # generate a new string
content~="<code class=\"str\">"~s;
while(1){
if(next()>=len)
break;
if(s==quot){
content~=s~"</code>";
break;
}elsif(s=="\\"){
content~=s;
if(next()>=len)
break;
content~=s;
}elsif(s==">"){
content~="&gt;";
}elsif(s=="<"){
content~="&lt;";
}else{
content~=s;
}
}
}elsif(s=="#"){
content~="<code class=\"note\">"~s;
while(1){
if(next()>=len)
break;
if(s=="\n" or s=="\r"){
content~=s;
break;
}elsif(s==">"){
content~="&gt;";
}elsif(s=="<"){
content~="&lt;";
}else{
content~=s;
}
}
content~="</code>";
}
else
content~=s;
}
println("[",os.time(),"] analyzed [",filename,"] for ",timer.elapsedMSec(),"ms");
return content;
}
var respond={
ok:func(html){
println("[",os.time(),"] respond 200 OK");
return "Http/1.1 200 OK\n\n"~html~"\n";
},
not_found:func(){
println("[",os.time(),"] respond 404 NOT FOUND");
return "Http/1.1 404 NOT FOUND\n\n<!DOCTYPE html>
<head>
<title> 404 not found </title>
<meta charset=\"utf-8\">
</head>
<body>
<text>
404 NOT FOUND!
</text>
</body>
</html>\n";
},
teapot:func(){
println("[",os.time(),"] respond 418 I'm a teapot");
return "Http/1.1 418 I'm a teapot\n\n<!DOCTYPE html>
<head>
<title> I'm a teapot </title>
<meta charset=\"utf-8\">
</head>
<body>
<text>
This server cannot brew coffee because it is a teapot.
</text>
</body>
</html>\n";
}
};
var files=func(){
var res={};
var dd=unix.opendir("./test");
while((var name=unix.readdir(dd))!=nil)
res[name]=1;
return res;
}();
while(1){
var client=http.accept();
var data=http.recv(client);
if(data==nil){
http.disconnect(client);
continue;
}
if(data.type=="GET"){
var path=data.path;
var args=split("?",path);
var tmp={};
if(size(args)==2){
path=args[0];
args=split("=",args[1]);
for(var i=0;i<size(args);i+=2)
tmp[args[i]]=args[i+1];
}
if(path=="/" or path=="/index"){
if(contains(tmp,"filename") and contains(files,tmp.filename)){
var filename=tmp.filename;
var page="<!DOCTYPE html><head><title> "~filename~" </title><meta charset=\"utf-8\">"~highlight_style~"</head>\n<body><pre>\n";
var page_back="</pre>\n</body>\n</html>\n";
http.send(client,respond.ok(page~html_read_file("./test/"~filename)~page_back));
}else{
http.send(client,respond.ok(io.fin("./doc/nasal-http-test-web.html")));
}
}
elsif(path=="/shutdown"){
http.send(client,respond.ok("http server shutdown."));
break;
}
elsif(path=="/favicon.ico")
http.send(client,respond.ok(io.fin("./pic/favicon.ico")));
elsif(path=="/license")
http.send(client,respond.ok(io.fin("./LICENSE")));
elsif(path=="/pic/nasal.png" or path=="/pic/benchmark.png" or path=="/pic/mandelbrot.png")
http.send(client,respond.ok(io.fin("."~path)));
else{
var filename=substr(path,1,size(path)-1);
if(contains(files,filename)){
var page="<!DOCTYPE html><head><title> "~filename~" </title><meta charset=\"utf-8\">"~highlight_style~"</head>\n<body><pre>\n";
var page_back="</pre>\n</body>\n</html>\n";
http.send(client,respond.ok(page~html_read_file("./test/"~filename)~page_back));
}
elsif(filename=="teapot")
http.send(client,respond.teapot());
else
http.send(client,respond.not_found());
}
}elsif(data.type=="POST"){
http.send(client,respond.not_found);
}
http.disconnect(client);
}
http.shutdown();

View File

@@ -1,196 +1,292 @@
#lib json.nas
import("lib.nas");
var json={
text:'',
line:1,
size:0,
ptr:0,
get:nil,
check:nil,
next:nil,
match:nil,
hash_gen:nil,
vec_gen:nil,
member:nil,
parse:nil,
token:{content:'',type:''},
content:[],
};
var JSON=func(){
json.get=func(filename)
{
me.line=1;
me.ptr=0;
me.content=[];
me.token={content:'',type:''};
me.text=io.fin(filename);
if(!size(me.text))
die("cannot open "~filename);
me.text=split('',me.text);
me.size=size(me.text);
return;
}
var (
j_eof,
j_lbrace,
j_rbrace,
j_lbracket,
j_rbracket,
j_comma,
j_colon,
j_str,
j_num,
j_id
)=(
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
);
var j_content=[
"eof",
"`{`",
"`}`",
"`[`",
"`]`",
"`,`",
"`:`",
"string",
"number",
"identifier"
];
json.check=func(ptr)
{
var str=me.text[ptr];
return (str=='{' or str=='}' or str=='[' or str==']' or str==',' or str==':' or str=='\"' or ('0'<=str and str<='9'));
}
json.next=func()
{
while(me.ptr<me.size and !json.check(me.ptr))
{
if(me.text[me.ptr]=='\n')
me.line+=1;
me.ptr+=1;
var text='';
var line=1;
var text_size=0;
var ptr=0;
var token={content:'',type:''};
var content={};
var init=func(){
line=1;
text_size=0;
ptr=0;
content={};
token={content:'',type:''};
text='';
}
if(me.ptr>=me.size)
{
me.token.content="";
me.token.type="eof";
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=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))
die("empty string");
text=split('',str);
text_size=size(text);
return;
}
if(me.text[me.ptr]=='{')
{
me.token.content='{';
me.token.type="left brace";
}
elsif(me.text[me.ptr]=='}')
{
me.token.content='}';
me.token.type="right brace";
}
elsif(me.text[me.ptr]=='[')
{
me.token.content='[';
me.token.type="left bracket";
}
elsif(me.text[me.ptr]==']')
{
me.token.content=']';
me.token.type="right bracket";
}
elsif(me.text[me.ptr]==',')
{
me.token.content=',';
me.token.type="comma";
}
elsif(me.text[me.ptr]==':')
{
me.token.content=':';
me.token.type="colon";
}
elsif(me.text[me.ptr]=='\"')
{
var s="";
me.ptr+=1;
while(me.ptr<me.size and me.text[me.ptr]!='\"')
{
s~=me.text[me.ptr];
me.ptr+=1;
var next=func(){
while(ptr<text_size and !check()){
if(text[ptr]=='\n')
line+=1;
ptr+=1;
}
me.token.content=s;
me.token.type="string";
}
elsif('0'<=me.text[me.ptr] and me.text[me.ptr]<='9')
{
var s=me.text[me.ptr];
me.ptr+=1;
while(me.ptr<me.size and (('0'<=me.text[me.ptr] and me.text[me.ptr]<='9') or me.text[me.ptr]=='.'))
{
s~=me.text[me.ptr];
me.ptr+=1;
if(ptr>=text_size){
token.content="eof";
token.type=j_eof;
return;
}
me.ptr-=1;
me.token.content=num(s);
me.token.type="number";
}
me.ptr+=1;
return;
}
json.match=func(type)
{
if(me.token.type!=type)
print("line ",me.line,": expect ",type," but get ",me.token.content,".");
me.next();
return;
}
json.hash_gen=func()
{
var hash={};
me.match("left brace");
me.member(hash);
while(me.token.type=="comma")
{
me.match("comma");
me.member(hash);
}
me.match("right brace");
return hash;
}
json.vec_gen=func()
{
var vec=[];
me.match("left bracket");
if(me.token.type=="left brace")
append(vec,me.hash_gen());
elsif(me.token.type=="left bracket")
append(vec,me.vec_gen());
elsif(me.token.type=="string" or me.token.type=="number")
{
append(vec,me.token.content);
me.next();
}
while(me.token.type=="comma")
{
me.match("comma");
if(me.token.type=="left brace")
append(vec,me.hash_gen());
elsif(me.token.type=="left bracket")
append(vec,me.vec_gen());
elsif(me.token.type=="string" or me.token.type=="number")
{
append(vec,me.token.content);
me.next();
var c=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_lbracket;
}elsif(c==']'){
token.content=']';
token.type=j_rbracket;
}elsif(c==','){
token.content=',';
token.type=j_comma;
}elsif(c==':'){
token.content=':';
token.type=j_colon;
}elsif(c=='\"' or c=='\''){
var strbegin=c;
var s="";
ptr+=1;
while(ptr<text_size and text[ptr]!=strbegin){
s~=text[ptr];
ptr+=1;
}
token.content=s;
token.type=j_str;
}elsif(isnum(c)){
var s=c;
ptr+=1;
while(ptr<text_size and ((isnum(text[ptr]) or text[ptr]=='.'))){
s~=text[ptr];
ptr+=1;
}
ptr-=1;
token.content=num(s);
token.type=j_num;
}elsif(isid(c)){
var s=c;
ptr+=1;
while(ptr<text_size and (isid(text[ptr]) or isnum(text[ptr]))){
s~=text[ptr];
ptr+=1;
}
ptr-=1;
token.content=s;
token.type=j_id;
}
ptr+=1;
return;
}
me.match("right bracket");
return vec;
}
json.member=func(hash)
{
var name=me.token.content;
me.match("string");
me.match("colon");
if(me.token.type=="left brace")
hash[name]=me.hash_gen();
elsif(me.token.type=="left bracket")
hash[name]=me.vec_gen();
elsif(me.token.type=="string" or me.token.type=="number")
{
hash[name]=me.token.content;
me.next();
var match=func(type){
if(token.type!=type)
print("line ",line,": expect ",j_content[type]," but get `",token.content,"`.\n");
next();
return;
}
return;
}
json.parse=func()
{
me.content={};
me.next();
me.match("left brace");
me.member(me.content);
while(me.token.type=="comma")
{
me.match("comma");
me.member(me.content);
var hash_gen=func(){
var hash={};
match(j_lbrace);
member(hash);
while(token.type==j_comma){
match(j_comma);
member(hash);
}
match(j_rbrace);
return hash;
}
me.match("right brace");
return;
}
var vec_gen=func(){
var vec=[];
match(j_lbracket);
if(token.type==j_lbrace)
append(vec,hash_gen());
elsif(token.type==j_lbracket)
append(vec,vec_gen());
elsif(token.type==j_str or token.type==j_num){
append(vec,token.content);
next();
}
while(token.type==j_comma){
match(j_comma);
if(token.type==j_lbrace)
append(vec,me.hash_gen());
elsif(token.type==j_lbracket)
append(vec,vec_gen());
elsif(token.type==j_str or token.type==j_num){
append(vec,token.content);
next();
}
}
match(j_rbracket);
return vec;
}
var member=func(hash){
var name=token.content;
if(token.type==j_rbrace)
return;
if(token.type==j_str)
match(j_str);
else
match(j_id);
match(j_colon);
if(token.type==j_lbrace)
hash[name]=hash_gen();
elsif(token.type==j_lbracket)
hash[name]=vec_gen();
elsif(token.type==j_str or token.type==j_num){
hash[name]=token.content;
next();
}
return;
}
return {
parse:func(str){
if(typeof(str)!="str")
die("JSON.parse: must use string");
get(str);
next();
match(j_lbrace);
member(content);
while(token.type==j_comma){
match(j_comma);
member(content);
}
match(j_rbrace);
var res=content;
init();
return res;
},
stringify:func(hash){
if(typeof(hash)!="hash")
die("JSON.stringify: must use hashmap");
var s="";
var gen=func(elem){
var t=typeof(elem);
if(t=="num")
s~=elem;
elsif(t=="str")
s~='"'~elem~'"';
elsif(t=="vec")
vgen(elem);
elsif(t=="hash")
hgen(elem);
else
s~='"undefined"';
}
var vgen=func(v){
s~="[";
var vsize=size(v);
for(var i=0;i<vsize;i+=1){
gen(v[i]);
if(i!=vsize-1)
s~=",";
}
s~="]";
}
var hgen=func(h){
s~="{";
var k=keys(h);
var vsize=size(k);
for(var i=0;i<vsize;i+=1){
s~=k[i]~":";
gen(h[k[i]]);
if(i!=vsize-1)
s~=",";
}
s~="}";
}
hgen(hash);
return s;
}
};
}();
var ss=JSON.stringify({
vec:[0,1,2],
hash:{
m1:0,
m2:"str",
m3:[114514],
m4:{year:1919,month:8,date:10}
},
emptyhash:{},
emptyvec:[],
empty:[{}],
empty_an:[[[[[[{}]]]]]],
function:func(){}
});
println(ss);
println(JSON.parse(ss));

View File

@@ -1,4 +1,3 @@
import("lib.nas");
# union set
var n=4;
var input=[[0,1],[0,2],[1,2]];

View File

@@ -1,23 +1,26 @@
import("lib.nas");
var lexer=func(file)
{
var (ptr,token)=(0,[]);
var s=io.fin(file);
var len=size(s);
return
{
jmp_note:func()
{
while(ptr<len and chr(s[ptr])!='\n')
var line=0;
var gen=func(tok){
append(token,{
line:line,
token:tok
});
}
return {
jmp_note:func(){
while(ptr<len and s[ptr]!='\n'[0])
ptr+=1;
if(ptr<len and s[ptr]=='\n'[0])
line+=1;
ptr+=1;
},
id_gen:func()
{
id_gen:func(){
var tmp="";
while(ptr<len)
{
while(ptr<len){
var c=s[ptr];
if(('a'[0]<=c and c<='z'[0])
or ('A'[0]<=c and c<='Z'[0])
@@ -28,21 +31,19 @@ var lexer=func(file)
break;
ptr+=1;
}
append(token,tmp);
gen(tmp);
},
str_gen:func()
{
str_gen:func(){
var str="";
var mark=chr(s[ptr]);
ptr+=1;
while(ptr<len and chr(s[ptr])!=mark)
{
if(chr(s[ptr])=='\\')
{
while(ptr<len and chr(s[ptr])!=mark){
if(chr(s[ptr])=='\\'){
ptr+=1;
var c=chr(s[ptr]);
if (c=='a' ) str~='\a';
elsif(c=='b' ) str~='\b';
elsif(c=='e' ) str~='\e';
elsif(c=='f' ) str~='\f';
elsif(c=='n' ) str~='\n';
elsif(c=='r' ) str~='\r';
@@ -54,70 +55,60 @@ var lexer=func(file)
elsif(c=='\'') str~='\'';
elsif(c=='\"') str~='\"';
else str~=c;
}
else
}else{
if(s[ptr]=='\n'[0])
line+=1;
str~=chr(s[ptr]);
}
ptr+=1;
}
if(ptr>=len)
print("read eof when generating string.\n");
ptr+=1;
append(token,str);
gen(str);
},
num_gen:func()
{
num_gen:func(){
var number=chr(s[ptr]);
ptr+=1;
if(ptr<len and chr(s[ptr])=='x')
{
if(ptr<len and chr(s[ptr])=='x'){
ptr+=1;
while(ptr<len and
('a'[0]<=s[ptr] and s[ptr]<='f'[0]
or '0'[0]<=s[ptr] and s[ptr]<='9'[0]))
{
or '0'[0]<=s[ptr] and s[ptr]<='9'[0])){
number~=chr(s[ptr]);
ptr+=1;
}
append(token,num(number));
gen(num(number));
return;
}elsif(ptr<len and chr(s[ptr])=='o'){
ptr+=1;
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='7'[0])){
number~=chr(s[ptr]);
ptr+=1;
}
gen(num(number));
return;
}
elsif(ptr<len and chr(s[ptr])=='o')
{
ptr+=1;
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='7'[0]))
{
number~=chr(s[ptr]);
ptr+=1;
}
append(token,num(number));
return;
}
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
{
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
number~=chr(s[ptr]);
ptr+=1;
}
if(ptr<len and chr(s[ptr])=='.')
{
if(ptr<len and chr(s[ptr])=='.'){
number~=chr(s[ptr]);
ptr+=1;
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
{
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
number~=chr(s[ptr]);
ptr+=1;
}
}
if(chr(s[ptr])=='e' or chr(s[ptr])=='E')
{
if(chr(s[ptr])=='e' or chr(s[ptr])=='E'){
number~=chr(s[ptr]);
ptr+=1;
if(chr(s[ptr])=='-' or chr(s[ptr])=='+')
{
if(chr(s[ptr])=='-' or chr(s[ptr])=='+'){
number~=chr(s[ptr]);
ptr+=1;
}
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
{
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
number~=chr(s[ptr]);
ptr+=1;
}
@@ -125,49 +116,45 @@ var lexer=func(file)
var last_c=chr(number[-1]);
if(last_c=='.' or last_c=='e' or last_c=='E' or last_c=='-' or last_c=='+')
println("error number: ",number);
append(token,num(number));
gen(num(number));
},
opr_gen:func()
{
opr_gen:func(){
var c=chr(s[ptr]);
if(c=='+' or c=='-' or c=='~' or c=='/' or c=='*' or c=='>' or c=='<' or c=='!' or c=='=')
{
if(c=='+' or c=='-' or c=='~' or c=='/' or c=='*' or c=='>' or c=='<' or c=='!' or c=='='){
var tmp=c;
ptr+=1;
if(ptr<len and chr(s[ptr])=='=')
{
if(ptr<len and chr(s[ptr])=='='){
tmp~=chr(s[ptr]);
ptr+=1;
}
append(token,tmp);
gen(tmp);
return;
}
elsif(c=='.')
{
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.')
{
append(token,"...");
}elsif(c=='.'){
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.'){
gen("...");
ptr+=3;
}
else
{
append(token,".");
else{
gen(".");
ptr+=1;
}
return;
}
elsif(c!=' ' and c!='\t' and c!='\n' and c!='\r' and s[ptr]>0)
append(token,c);
gen(c);
ptr+=1;
return;
},
compile:func()
{
while(ptr<len)
{
compile:func(){
line=1;
while(ptr<len){
var c=s[ptr];
if(c=='#'[0])
me.jmp_note();
elsif(c=='\n'[0]){
line+=1;
ptr+=1;
}
elsif('a'[0]<=c and c<='z'[0]
or 'A'[0]<=c and c<='Z'[0]
or c=='_'[0])
@@ -185,8 +172,7 @@ var lexer=func(file)
};
}
var lex=lexer("test/props.nas");
var lex=lexer("stl/fg_env.nas");
lex.compile();
foreach(var tok;lex.get_token())
print(tok,' ');
print('\n');
print('(',tok.line,' | ',tok.token,')\n');

View File

@@ -1,20 +1,15 @@
import("lib.nas");
var map=nil;
var check=func(x,y)
{
var check=func(x,y){
if(x>14) x=0;
if(y>19) y=0;
return map[x][y];
}
var new_map=func()
{
var new_map=func(){
var tmp=[];
setsize(tmp,15);
forindex(var i;tmp)
{
forindex(var i;tmp){
tmp[i]=[];
setsize(tmp[i],20);
}
@@ -23,35 +18,31 @@ var new_map=func()
var prt=func()
{
var s='';
foreach(var line;map)
{
var s='\e[0;0H';
foreach(var line;map){
foreach(var elem;line)
s~=elem~' ';
s~='\n';
}
if(os.platform()=="windows")
system("cls");
else
system("clear");
print(s);
unix.sleep(1/144);
}
func()
{
func(){
if(os.platform()=="windows")
system("chcp 65001");
print("\ec");
rand(time(0));
map=new_map();
forindex(var i;map)
forindex(var j;map[i])
map[i][j]=rand()>0.7?'O':' ';
var calc=[[0,1],[1,0],[0,-1],[-1,0],[1,1],[1,-1],[-1,-1],[-1,1]];
for(var r=0;r<100;r+=1)
{
for(var r=0;r<100;r+=1){
prt(map);
var tmp=new_map();
for(var i=0;i<15;i+=1)
for(var j=0;j<20;j+=1)
{
for(var j=0;j<20;j+=1){
var cnt=0;
foreach(var k;calc)
cnt+=(check(i+k[0],j+k[1])=='O');

View File

@@ -1,7 +1,5 @@
import("lib.nas");
for(;;)break;
for(;;)
{
for(;;){
var a=1;
break;
}
@@ -11,21 +9,18 @@ for(var i=1;i<10;i+=1)print(i,'\n');
while(1)break;
var j=0;
while(j<10)
{
while(j<10){
print(j,'\n');
j+=1;
}
forindex(var j;[0,1,2,3])print(j,'\n');
forindex(var j;[0,1,2,3])
{
forindex(var j;[0,1,2,3]){
var a=j;
print(a*a,'\n');
}
foreach(var j;[0,1,2,3])print([0,1,2,3][j],'\n');
foreach(var j;[0,1,2,3])
{
foreach(var j;[0,1,2,3]){
var a=[0,1,2,3][j];
print(a*a-1,'\n');
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,16 @@
import("lib.nas");
var (yMin,yMax,xMin,xMax,line)=(-0.2,0.2,-1.5,-1.0,"");
var (yDel,xDel)=(yMax-yMin,xMax-xMin);
for(var yPixel=0;yPixel<24;yPixel+=1)
{
for(var yPixel=0;yPixel<24;yPixel+=1){
var y=(yPixel/24)*yDel+yMin;
for(var xPixel=0;xPixel<80;xPixel+=1)
{
for(var xPixel=0;xPixel<80;xPixel+=1){
var x=(xPixel/80)*xDel+xMin;
var pixel=" ";
var (x0,y0)=(x,y);
for(var iter=0;iter<80;iter+=1)
{
for(var iter=0;iter<80;iter+=1){
var x1=(x0*x0)-(y0*y0)+x;
var y1=2*x0*y0+y;
(x0,y0)=(x1,y1);
if((x0*x0)+(y0*y0)>4)
{
if((x0*x0)+(y0*y0)>4){
pixel=chr(" .:;+=xX$&"[iter/8]);
break;
}

216
test/md5.nas Normal file
View File

@@ -0,0 +1,216 @@
var check=func(x){
if(x<0x100000000)
return x;
return x-floor(x/0x100000000)*0x100000000;
}
var u32_bits_and=func(x,y){
return bits.u32_and(check(x),check(y));
}
var u32_bits_or=func(x,y){
return bits.u32_or(check(x),check(y));
}
var u32_bits_xor=func(x,y){
return bits.u32_xor(check(x),check(y));
}
var u32_bits_not=func(x){
return bits.u32_not(check(x));
}
var hex32str=func(){
var ch=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];
var tbl=[];
setsize(tbl,256);
for(var i=0;i<16;i+=1){
for(var j=0;j<16;j+=1)
tbl[i*16+j]=ch[i]~ch[j];
}
return func(num){
var res="";
for(var i=0;i<4;i+=1){
res~=tbl[u32_bits_and(num,0xff)];
num=floor(num/256);
}
return res;
};
}();
var _md5=func(){
var K=[
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
];
var S=[
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
];
var idx=[
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
];
var l=func(num,cx){
for(var i=0;i<cx;i+=1){
num=check(num*2);
}
return num;
}
var r=func(num,cx){
num=check(num);
for(var i=0;i<cx;i+=1){
num=num/2;
}
return floor(num);
}
var rol=func(num,cx){
return u32_bits_or(l(num,cx),r(num,32-cx));
}
# round 1
var F=func(x,y,z){
return u32_bits_or(
u32_bits_and(x,y),
u32_bits_and(u32_bits_not(x),z)
);
}
# round 2
var G=func(x,y,z){
return u32_bits_or(
u32_bits_and(x,z),
u32_bits_and(y,u32_bits_not(z))
);
}
# round 3
var H=func(x,y,z){
return u32_bits_xor(u32_bits_xor(x,y),z);
}
# round 4
var I=func(x,y,z){
return u32_bits_xor(
y,
u32_bits_or(x,u32_bits_not(z))
);
}
var functions=[
F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,
H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I
];
return func(s){
var (s_size,len,res)=(size(s),size(s)*8,[]);
setsize(res,s_size);
for(var i=0;i<s_size;i+=1){
res[i]=s[i];
}
# +------len------+--1~512--+--64--+
# | text | fill | size |
# +---------------+---------+------+ N*512 bit
var (mod,res_size)=(s_size-floor(s_size/64)*64,0);
if(mod==56){
res_size=s_size+64;
}elsif(mod<56){
res_size=floor(s_size/64)*64+56;
}elsif(mod>56){
res_size=floor(s_size/64)*64+120; # 512+448=960 960/8=120
}
setsize(res,res_size);
res[s_size]=0x80;
for(var i=s_size+1;i<res_size;i+=1)
res[i]=0;
# little endian
setsize(res,size(res)+8);
var (s_size,lower32,higher32)=(size(res),check(len),check(len/math.pow(2,32)));
for(var i=4;i>0;i-=1){
res[s_size-4-i]=floor(lower32-floor(lower32/256)*256);
lower32=floor(lower32/256);
}
for(var i=4;i>0;i-=1){
res[s_size-i]=floor(higher32-floor(higher32/256)*256);
higher32=floor(higher32/256);
}
# 1 block=>16 uint32=>64 byte=>512 bit
# because using double to discribe number
# this may only work when string's length is under 1<<51
var tmp=[];
setsize(tmp,size(res)/4);
for(var i=0;i<size(res);i+=4){
tmp[i/4]=res[i+3]*math.pow(2,24)+
res[i+2]*math.pow(2,16)+
res[i+1]*math.pow(2,8)+
res[i];
}
res=tmp;
var A=0x67452301;
var B=0xefcdab89;
var C=0x98badcfe;
var D=0x10325476;
res_size=size(res);
for(var i=0;i<res_size;i+=16){
var (f,a,b,c,d)=(0,A,B,C,D);
for(var j=0;j<64;j+=1){
f=functions[j](b,c,d);
(a,b,c,d)=(d,check(b+rol(a+f+K[j]+res[i+idx[j]],S[j])),b,c);
}
(A,B,C,D)=(check(a+A),check(b+B),check(c+C),check(d+D));
}
return hex32str(A)~hex32str(B)~hex32str(C)~hex32str(D);
};
}();
# check if md5 runs correctly
var md5check=func(){
var test_set=[
"md5",
"github.com",
"helloworld",
"abc",
"https://www.github.com/ValKmjolnir/Nasal-Interpreter",
"https://github.com/andyross/nasal",
"var (lower32,higher32)=(check(len),check(len/math.pow(2,32)));",
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
"let the bass kick",
"f499377c9ae8454c6c8a21ddba7f00de5817fccdc611333ed004d826abb17f4efdacad297f72956e0619002cecffc8e3d18d9b03b082f3cb114bc29173954043",
"you are our last hope"
];
var result=[
"1bc29b36f623ba82aaf6724fd3b16718",
"99cd2175108d157588c04758296d1cfc",
"fc5e038d38a57032085441e7fe7010b0",
"900150983cd24fb0d6963f7d28e17f72",
"6b3a7bbc2240046c4fb1b0b3a4ed8181",
"14a6afca5f3a7b239c56b5a9678c428e",
"f499377c9ae8454c6c8a21ddba7f00de",
"fdacad297f72956e0619002cecffc8e3",
"16eadccb9799dfb4c1ca512f40638bbb",
"a7916c5ce54e73b7ddf6a286b36d976d",
"ec6d5b197ba019db23c719112f3f70b7"
];
forindex(var i;test_set){
var res=_md5(test_set[i]);
if(cmp(res,result[i]))
println(
"md5 cannot work:\n",
" test \""~test_set[i]~"\"\n",
" result \""~result[i]~"\"\n",
" but get \""~res~"\"\n"
);
}
}
# check when loading md5.nas
md5check();

94
test/md5compare.nas Normal file
View File

@@ -0,0 +1,94 @@
import.test.md5;
import.stl.process_bar;
srand();
var compare=func(){
var ch=[
"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+",
"_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%",
"^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?"
];
return func(begin,end){
var byte=0;
var total=end-begin;
var timestamp=maketimestamp();
timestamp.stamp();
var bar=process_bar.bar(front:os.platform()=="windows"?"sharp":"block",back:"point",sep:"line",length:50);
for(var i=begin;i<end;i+=1){
var s="";
for(var j=0;j<i;j+=1){
s~=ch[rand()*size(ch)];
}
byte+=size(s);
var res=md5(s);
if(cmp(res,_md5(s))){
die("error: "~str(i));
}
print(" ",bar.bar((i-begin+1)/total)," (",i-begin+1,"/",total,")\t",res," byte: ",byte," elapsed time: ",timestamp.elapsedMSec()," \r");
}
print("\n");
};
}();
var filechecksum=func(){
var files=[
"./stl/fg_env.nas", "./stl/file.nas",
"./stl/lib.nas", "./stl/list.nas",
"./stl/log.nas", "./stl/module.nas",
"./stl/process_bar.nas", "./stl/queue.nas",
"./stl/result.nas", "./stl/sort.nas",
"./stl/stack.nas", "./test/ascii-art.nas",
"./test/auto_crash.nas", "./test/bf.nas",
"./test/bfcolored.nas", "./test/bfconvertor.nas",
"./test/bfs.nas", "./test/bigloop.nas",
"./test/bp.nas", "./test/calc.nas",
"./test/choice.nas", "./test/class.nas",
"./test/coroutine.nas", "./test/diff.nas",
"./test/exception.nas", "./test/fib.nas",
"./test/filesystem.nas", "./test/hexdump.nas",
"./test/httptest.nas", "./test/json.nas",
"./test/leetcode1319.nas", "./test/lexer.nas",
"./test/life.nas", "./test/loop.nas",
"./test/mandel.nas", "./test/mandelbrot.nas",
"./test/md5.nas", "./test/md5compare.nas",
"./test/module_test.nas", "./test/nasal_test.nas",
"./test/occupation.nas", "./test/pi.nas",
"./test/prime.nas", "./test/qrcode.nas",
"./test/quick_sort.nas", "./test/scalar.nas",
"./test/snake.nas", "./test/tetris.nas",
"./test/trait.nas", "./test/turingmachine.nas",
"./test/utf8chk.nas", "./test/wavecollapse.nas",
"./test/ycombinator.nas", "LICENSE",
"main.cpp", "makefile",
"nasal_ast.h", "nasal_builtin.h",
"nasal_codegen.h", "nasal_dbg.h",
"nasal_err.h", "nasal_gc.h",
"nasal_import.h", "nasal_lexer.h",
"nasal_opt.h", "nasal_parse.h",
"nasal_vm.h", "nasal.ebnf",
"nasal.h", "README.md"
];
var byte=0;
var total=size(files);
var timestamp=maketimestamp();
timestamp.stamp();
var bar=process_bar.bar(front:os.platform()=="windows"?"sharp":"block",back:"point",sep:"line",length:50);
forindex(var i;files){
var f=io.fin(files[i]);
var res=md5(f);
byte+=size(f);
if(cmp(res,_md5(f))){
die("error: "~files[i]);
}
print(" ",bar.bar((i+1)/total)," (",i+1,"/",total,")\t",res," byte: ",byte," elapsed time: ",timestamp.elapsedMSec()," \r");
}
print("\n");
}
var randomchecksum=func(){
for(var i=0;i<4096;i+=512)
compare(i,i+512);
}
filechecksum();
randomchecksum();

View File

@@ -1,17 +1,44 @@
import("lib.nas");
var libfib=func(){
var dl=dylib.dlopen("./module/libfib."~(os.platform()=="windows"?"dll":"so"));
var fib=dylib.dlsym(dl,"fib");
var qfib=dylib.dlsym(dl,"quick_fib");
var call=dylib.dlcall;
return
{
fib: func(x){return call(fib,x)},
qfib:func(x){return call(qfib,x)}
};
var (dd,fib,qfib)=(nil,nil,nil);
return {
open:func(){
if(dd==nil){
dd=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
fib=dylib.dlsym(dd,"fib");
qfib=dylib.dlsym(dd,"quick_fib");
}else{
println("[info ] already loaded.");
}
},
close:func(){
if(dd==nil){
println("[error ] already closed.");
return;
}
dylib.dlclose(dd);
(dd,fib,qfib)=(nil,nil,nil);
},
fib:func(x){
if(fib!=nil)
return dylib.dlcall(fib,x);
println("[error ] cannot call fib.");
return nil;
},
qfib:func(x){
if(qfib!=nil)
return dylib.dlcall(qfib,x);
println("[error ] cannot call qfib.");
return nil;
}
}
}();
println(libfib);
println(libfib.fib(29));
println(libfib.qfib(29));
println("[keys ] ",keys(libfib));
libfib.open();
libfib.open();
println("[result] ",libfib.fib(40));
println("[result] ",libfib.qfib(40));
libfib.close();
println("[result] ",libfib.fib(40));
println("[result] ",libfib.qfib(40));
libfib.close();

View File

@@ -1,6 +1,5 @@
# This is written for Nasal Intepreter
# Sidi Liang
import("lib.nas");
var w = 1;
var x = "hello";
var f = func(){
@@ -45,10 +44,10 @@ println(z.numb);#//1
println(z.listt[2][1]);#//hello
println(z1.hashh.listt[2][1]);#//hello
println(y2[3].hashh.listt[2][1]);#//hello
println(f);#//func(...){...}
println(f);#//func(..){..}
f();#//f is called
println(z.funcc);#//func(...){...}
println(z.funcc);#//func(..){..}
z.funcc();#//f is called
println(z.funcccall);#//func(...){...}
println(z.funcccall);#//func(..){..}
z2.listt2[3].hashh.funcc();#//f is called
println(y1[f2()][w]);#//hello

59
test/occupation.nas Normal file
View File

@@ -0,0 +1,59 @@
import.stl.process_bar;
import.module.libkey;
var cpu_stat=func(){
var cpu=split("\n",io.fin("/proc/stat"))[0];
cpu=split(" ",cpu);
cpu={
name:cpu[0],
user:cpu[1],
nice:cpu[2],
system:cpu[3],
idle:cpu[4],
iowait:cpu[5],
irq:cpu[6],
softirq:cpu[7],
};
return cpu;
}
var cpu_occupation=func(){
var cpu0=cpu_stat();
unix.sleep(0.5);
var cpu1=cpu_stat();
var t0=cpu0.user+cpu0.nice+cpu0.system+cpu0.idle+cpu0.iowait+cpu0.irq+cpu0.softirq;
var t1=cpu1.user+cpu1.nice+cpu1.system+cpu1.idle+cpu1.iowait+cpu1.irq+cpu1.softirq;
var interval=cpu1.idle-cpu0.idle;
return t0==t1?0:(1-interval/(t1-t0))*100;
}
var mem_occupation=func(){
var meminfo=split("\n",io.fin("/proc/meminfo"));
var mem_res={};
forindex(var i;meminfo){
var tmp=split(" ",meminfo[i])[0:1];
tmp[0]=substr(tmp[0],0,size(tmp[0])-1);
mem_res[tmp[0]]=num(tmp[1]);
}
return mem_res;
}
func(){
if(os.platform()=="windows"){
println("haven't supported yet.");
return;
}
print("\ec");
while(1){
var mem=mem_occupation();
var mem_occ=(mem.MemTotal-mem.MemFree)/mem.MemTotal*100;
var cpu_occ=cpu_occupation();
var key=libkey.nonblock();
var bar=process_bar.bar("block","point","line",25);
if(key!=nil and chr(key)=="q")
break;
println("\e[1;1H Memory total(GB) : \e[36m",mem.MemTotal/1024/1024,"\e[0m");
println("\e[2;1H Memory free(GB) : \e[36m",mem.MemFree/1024/1024,"\e[0m");
println("\e[3;1H Memory occupation(%): ",mem_occ>60?"\e[91m":"\e[32m",bar.bar(mem_occ/100)~" ",mem_occ,"\e[0m ");
println("\e[4;1H CPU occupation(%) : ",cpu_occ>90?"\e[91m":"\e[32m",bar.bar(cpu_occ/100)~" ",cpu_occ,"\e[0m ");
println("\e[5;1H Press 'q' to quit.");
}
}();

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var (t,res)=(1,0);
for(var m=1;m<4e6;m+=2)
{

View File

@@ -1,5 +1,3 @@
import("lib.nas");
var is_prime=func(x){
for(var i=2;i<x;i+=1)
if(x/i==int(x/i))

View File

@@ -1,577 +0,0 @@
##
# Node class definition. The class methods simply wrap the
# low level extension functions which work on a "ghost" handle to a
# SGPropertyNode object stored in the _g field.
#
# Not all of the features of SGPropertyNode are supported. There is
# no support for ties, obviously, as that wouldn't make much sense
# from a Nasal context. The various get/set methods work only on the
# local node, there is no equivalent of the "relative path" variants
# available in C++; just use node.getNode(path).whatever() instead.
#
##
# Utility. Turns any ghosts it finds (either solo, or in an
# array) into Node objects.
#
var wrap = func(node) {
var argtype = typeof(node);
if(argtype == "ghost") {
return wrapNode(node);
} elsif(argtype == "vector") {
var v = node;
var n = size(v);
for(var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
return v;
}
return node;
}
var Node = {
getNode : func wrap(_getNode(me._g, arg)),
getParent : func wrap(_getParent(me._g, arg)),
getChild : func wrap(_getChild(me._g, arg)),
getChildren : func wrap(_getChildren(me._g, arg)),
setChildren : func wrap(_setChildren(me._g, arg)),
addChild : func wrap(_addChild(me._g, arg)),
addChildren : func wrap(_addChildren(me._g, arg)),
removeChild : func wrap(_removeChild(me._g, arg)),
removeChildren : func wrap(_removeChildren(me._g, arg)),
removeAllChildren: func wrap(_removeAllChildren(me._g, arg)),
getAliasTarget : func wrap(_getAliasTarget(me._g, arg)),
getName : func _getName(me._g, arg),
getIndex : func _getIndex(me._g, arg),
getType : func _getType(me._g, arg),
getAttribute : func _getAttribute(me._g, arg),
setAttribute : func _setAttribute(me._g, arg),
getValue : func _getValue(me._g, arg),
setValue : func _setValue(me._g, arg),
setValues : func _setValues(me._g, arg),
setIntValue : func _setIntValue(me._g, arg),
setBoolValue : func _setBoolValue(me._g, arg),
setDoubleValue : func _setDoubleValue(me._g, arg),
unalias : func _unalias(me._g, arg),
alias : func(n) _alias(me._g, [isa(n, Node) ? n._g : n]),
equals : func(n) _equals(me._g, [isa(n, Node) ? n._g : n]),
clearValue : func _alias(me._g, [_globals()]) and me.unalias(),
getPath : func {
var (name, index, parent) = (me.getName(), me.getIndex(), me.getParent());
if(index != 0) { name ~= "[" ~ index ~ "]"; }
if(parent != nil) { name = parent.getPath() ~ "/" ~ name; }
return name;
},
getBoolValue : func {
var val = me.getValue();
var mytype = me.getType();
if((mytype == "STRING" or mytype == "UNSPECIFIED") and val == "false") return 0;
return !!val;
},
remove : func {
if((var p = me.getParent()) == nil) return nil;
p.removeChild(me.getName(), me.getIndex());
},
};
##
# Static constructor for a Node object. Accepts a Nasal hash
# expression to initialize the object a-la setValues().
#
Node.new = func(values = nil) {
var result = wrapNode(_new());
if(typeof(values) == "hash")
result.setValues(values);
return result;
}
##
# Counter piece of setValues(). Returns a hash with all values
# in the subtree. Nodes with same name are returned as vector,
# where the original node indices are lost. The function should
# only be used if all or almost all values are needed, and never
# in performance-critical code paths. If it's called on a node
# without children, then the result is equivalent to getValue().
#
Node.getValues = func {
var children = me.getChildren();
if(!size(children)) return me.getValue();
var val = {};
var numchld = {};
foreach(var c; children) {
var name = c.getName();
if(contains(numchld, name)) { var nc = numchld[name]; }
else {
var nc = size(me.getChildren(name));
numchld[name] = nc;
if(nc > 1 and !contains(val, name)) val[name] = [];
}
if(nc > 1) append(val[name], c.getValues());
else val[name] = c.getValues();
}
return val;
}
##
# Initializes property if it's still undefined. First argument
# is a property name/path. It can also be nil or an empty string,
# in which case the node itself gets initialized, rather than one
# of its children. Second argument is the default value. The third,
# optional argument is a property type (one of "STRING", "DOUBLE",
# "INT", or "BOOL"). If it is omitted, then "DOUBLE" is used for
# numbers, and STRING for everything else. Returns the property
# as props.Node. The fourth optional argument enforces a type if
# non-zero.
#
Node.initNode = func(path = nil, value = 0, type = nil, force = 0) {
var prop = me.getNode(path or "", 1);
if(prop.getType() != "NONE") value = prop.getValue();
if(force) prop.clearValue();
if(type == nil) prop.setValue(value);
elsif(type == "DOUBLE") prop.setDoubleValue(value);
elsif(type == "INT") prop.setIntValue(value);
elsif(type == "BOOL") prop.setBoolValue(value);
elsif(type == "STRING") prop.setValue("" ~ value);
else die("initNode(): unsupported type '" ~ type ~ "'");
return prop;
}
##
# Useful debugging utility. Recursively dumps the full state of a
# Node object to the console. Try binding "props.dump(props.globals)"
# to a key for a fun hack.
#
var dump = func {
if(size(arg) == 1) { prefix = ""; node = arg[0]; }
else { prefix = arg[0]; node = arg[1]; }
index = node.getIndex();
type = node.getType();
name = node.getName();
val = node.getValue();
if(val == nil) { val = "nil"; }
name = prefix ~ name;
if(index > 0) { name = name ~ "[" ~ index ~ "]"; }
print(name, " {", type, "} = ", val);
# Don't recurse into aliases, lest we get stuck in a loop
if(type != "ALIAS") {
children = node.getChildren();
foreach(c; children) { dump(name ~ "/", c); }
}
}
##
# Recursively copy property branch from source Node to
# destination Node. Doesn't copy aliases. Copies attributes
# if optional third argument is set and non-zero.
#
var copy = func(src, dest, attr = 0) {
foreach(var c; src.getChildren()) {
var name = c.getName() ~ "[" ~ c.getIndex() ~ "]";
copy(src.getNode(name), dest.getNode(name, 1), attr);
}
var type = src.getType();
var val = src.getValue();
if(type == "ALIAS" or type == "NONE") return;
elsif(type == "BOOL") dest.setBoolValue(val);
elsif(type == "INT" or type == "LONG") dest.setIntValue(val);
elsif(type == "FLOAT" or type == "DOUBLE") dest.setDoubleValue(val);
else dest.setValue(val);
if(attr) dest.setAttribute(src.getAttribute());
}
##
# Utility. Returns a new object with its superclass/parent set to the
# Node object and its _g (ghost) field set to the specified object.
# Nasal's literal syntax can be pleasingly terse. I like that. :)
#
var wrapNode = func(node) { { parents : [Node], _g : node } }
##
# Global property tree. Set once at initialization. Is that OK?
# Does anything ever call globals.set_props() from C++? May need to
# turn this into a function if so.
#
var globals = wrapNode(_globals());
##
# Shortcut for props.globals.getNode().
#
var getNode = func return call(props.globals.getNode, arg, props.globals);
##
# Sets all indexed property children to a single value. arg[0]
# specifies a property name (e.g. /controls/engines/engine), arg[1] a
# path under each node of that name to set (e.g. "throttle"), arg[2]
# is the value.
#
var setAll = func(base, child, value) {
var node = props.globals.getNode(base);
if(node == nil) return;
var name = node.getName();
node = node.getParent();
if(node == nil) return;
var children = node.getChildren();
foreach(var c; children)
if(c.getName() == name)
c.getNode(child, 1).setValue(value);
}
##
# Turns about anything into a list of props.Nodes, including ghosts,
# path strings, vectors or hashes containing, as well as functions
# returning any of the former and in arbitrary nesting. This is meant
# to be used in functions whose main purpose is to handle collections
# of properties.
#
var nodeList = func {
var list = [];
foreach(var a; arg) {
var t = typeof(a);
if(isa(a, Node))
append(list, a);
elsif(t == "scalar")
append(list, props.globals.getNode(a, 1));
elsif(t == "vector")
foreach(var i; a)
list ~= nodeList(i);
elsif(t == "hash")
foreach(var i; keys(a))
list ~= nodeList(a[i]);
elsif(t == "func")
list ~= nodeList(a());
elsif(t == "ghost" and ghosttype(a) == "prop")
append(list, wrapNode(a));
else
die("nodeList: invalid nil property");
}
return list;
}
##
# Compiles a <condition> property branch according to the rules
# set out in $FG_ROOT/Docs/README.conditions into a Condition object.
# The 'test' method of the returend object can be used to evaluate
# the condition.
# The function returns nil on error.
#
var compileCondition = func(p) {
if(p == nil) return nil;
if(!isa(p, Node)) p = props.globals.getNode(p);
return _createCondition(p._g);
}
##
# Evaluates a <condition> property branch according to the rules
# set out in $FG_ROOT/Docs/README.conditions. Undefined conditions
# and a nil argument are "true". The function dumps the condition
# branch and returns nil on error.
#
var condition = func(p) {
if(p == nil) return 1;
if(!isa(p, Node)) p = props.globals.getNode(p);
return _cond_and(p)
}
var _cond_and = func(p) {
foreach(var c; p.getChildren())
if(!_cond(c)) return 0;
return 1;
}
var _cond_or = func(p) {
foreach(var c; p.getChildren())
if(_cond(c)) return 1;
return 0;
}
var _cond = func(p) {
var n = p.getName();
if(n == "or") return _cond_or(p);
if(n == "and") return _cond_and(p);
if(n == "not") return !_cond_and(p);
if(n == "equals") return _cond_cmp(p, 0);
if(n == "not-equals") return !_cond_cmp(p, 0);
if(n == "less-than") return _cond_cmp(p, -1);
if(n == "greater-than") return _cond_cmp(p, 1);
if(n == "less-than-equals") return !_cond_cmp(p, 1);
if(n == "greater-than-equals") return !_cond_cmp(p, -1);
if(n == "property") return !!getprop(p.getValue());
printlog("alert", "condition: invalid operator ", n);
dump(p);
return nil;
}
var _cond_cmp = func(p, op) {
var left = p.getChild("property", 0, 0);
if(left != nil) { left = getprop(left.getValue()); }
else {
printlog("alert", "condition: no left value");
dump(p);
return nil;
}
var right = p.getChild("property", 1, 0);
if(right != nil) { right = getprop(right.getValue()); }
else {
right = p.getChild("value", 0, 0);
if(right != nil) { right = right.getValue(); }
else {
printlog("alert", "condition: no right value");
dump(p);
return nil;
}
}
if(left == nil or right == nil) {
printlog("alert", "condition: comparing with nil");
dump(p);
return nil;
}
if(op < 0) return left < right;
if(op > 0) return left > right;
return left == right;
}
##
# Runs <binding> as described in $FG_ROOT/Docs/README.commands using
# a given module by default, and returns 1 if fgcommand() succeeded,
# or 0 otherwise. The module name won't override a <module> defined
# in the binding.
#
var runBinding = func(node, module = nil) {
if(module != nil and node.getNode("module") == nil)
node.getNode("module", 1).setValue(module);
var cmd = node.getNode("command", 1).getValue() or "null";
condition(node.getNode("condition")) ? fgcommand(cmd, node) : 0;
}
#---------------------------------------------------------------------------
# Property / object update manager
#
# - Manage updates when a value has changed more than a predetermined amount.
# This class is designed to make updating displays (e.g. canvas), or
# performing actions based on a property (or value in a hash) changing
# by more than the preset amount.
# This can make a significant improvement to performance compared to simply
# redrawing a canvas in an update loop.
# - Author : Richard Harrison (rjh@zaretto.com)
#---------------------------------------------------------------------------*/
#example usage:
# this is using the hashlist (which works well with an Emesary notification)
# basically when the method is called it will call each section (in the lambda)
# when the value changes by more than the amount specified as the second parameter.
# It is possible to reference multiple elements from the hashlist in each FromHashList; if either
# one changes then it will result in the lambda being called.
#
# obj.update_items = [
# UpdateManager.FromHashList(["VV_x","VV_y"], 0.01, func(val)
# {
# obj.VV.setTranslation (val.VV_x, val.VV_y + pitch_offset);
# }),
# UpdateManager.FromHashList(["pitch","roll"], 0.025, func(hdp)
# {
# obj.ladder.setTranslation (0.0, hdp.pitch * pitch_factor+pitch_offset);
# obj.ladder.setCenter (118,830 - hdp.pitch * pitch_factor-pitch_offset);
# obj.ladder.setRotation (-hdp.roll_rad);
# obj.roll_pointer.setRotation (hdp.roll_rad);
# }),
# props.UpdateManager.FromProperty("velocities/airspeed-kt", 0.01, func(val)
# {
# obj.ias_range.setTranslation(0, val * ias_range_factor);
# }),
# props.UpdateManager.FromPropertyHashList(["orientation/alpha-indicated-deg", "orientation/side-slip-deg"], 0.1, func(val)
# {
# obj.VV_x = val.property["orientation/side-slip-deg"].getValue()*10; # adjust for view
# obj.VV_y = val.property["orientation/alpha-indicated-deg"].getValue()*10; # adjust for view
# obj.VV.setTranslation (obj.VV_x, obj.VV_y);
# }),
# ]
#
#==== the update loop then becomes ======
#
# foreach(var update_item; me.update_items)
# {
# # hdp is a data provider that can be used as the hashlist for the property
# # update from hash methods.
# update_item.update(hdp);
# }
#
var UpdateManager =
{
_updateProperty : func(_property)
{
},
FromProperty : func(_propname, _delta, _changed_method)
{
var obj = {parents : [UpdateManager] };
obj.propname = _propname;
obj.property = props.globals.getNode(_propname);
obj.delta = _delta;
obj.curval = obj.property.getValue();
obj.lastval = obj.curval;
obj.changed = _changed_method;
obj.update = func(obj)
{
me.curval = me.property.getValue();
if (me.curval != nil)
{
me.localType = me.property.getType();
if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE")
{
if(me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta)
{
me.lastval = me.curval;
me.changed(me.curval);
}
}
else if(me.lastval == nil or me.lastval != me.curval)
{
me.lastval = me.curval;
me.changed(me.curval);
}
}
};
obj.update(obj);
return obj;
},
IsNumeric : func(hashkey)
{
me.localType = me.property[hashkey].getType();
if (me.localType == "UNSPECIFIED") {
print("UpdateManager: warning ",hashkey," is ",ty, " excluding from update");
me.property[hashkey] = nil;
}
if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE")
return 1;
else
return 0;
},
FromPropertyHashList : func(_keylist, _delta, _changed_method)
{
var obj = {parents : [UpdateManager] };
obj.hashkeylist = _keylist;
obj.delta = _delta;
obj.lastval = {};
obj.hashkey = nil;
obj.changed = _changed_method;
obj.needs_update = 0;
obj.property = {};
obj.is_numeric = {};
foreach (hashkey; obj.hashkeylist) {
obj.property[hashkey] = props.globals.getNode(hashkey);
obj.lastval[hashkey] = nil;
# var ty = obj.property[hashkey].getType();
# if (ty == "INT" or ty == "LONG" or ty == "FLOAT" or ty == "DOUBLE") {
# obj.is_numeric[hashkey] = 1;
# } else
# obj.is_numeric[hashkey] = 0;
#print("create: ", hashkey," ", ty, " isnum=",obj.is_numeric[hashkey]);
# if (ty == "UNSPECIFIED")
# print("UpdateManager: warning ",hashkey," is ",ty);
}
obj.update = func(obj)
{
if (me.lastval == nil)
me.needs_update = 1;
else {
me.needs_update = 0;
foreach (hashkey; me.hashkeylist) {
if (me.property[hashkey] != nil) {
me.valIsNumeric = me.IsNumeric(hashkey);
if (me.lastval[hashkey] == nil
or (me.valIsNumeric and (math.abs(me.lastval[hashkey] - me.property[hashkey].getValue()) >= me.delta))
or (!me.valIsNumeric and (me.lastval[hashkey] != me.property[hashkey].getValue()))) {
me.needs_update = 1;
break;
}
}
}
}
if (me.needs_update) {
me.changed(me);
foreach (hashkey; me.hashkeylist) {
me.lastval[hashkey] = me.property[hashkey].getValue();
}
}
}
;
return obj;
},
FromHashValue : func(_key, _delta, _changed_method)
{
var obj = {parents : [UpdateManager] };
obj.hashkey = _key;
obj.delta = _delta;
obj.isnum = _delta != nil;
obj.curval = nil;
obj.lastval = nil;
obj.changed = _changed_method;
obj.update = func(obj)
{
me.curval = obj[me.hashkey];
if (me.curval != nil) {
if (me.isnum) {
me.curval = num(me.curval);
if (me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta) {
me.lastval = me.curval;
me.changed(me.curval);
}
} else {
if (me.lastval == nil or me.lastval != me.curval) {
me.lastval = me.curval;
me.changed(me.curval);
}
}
}
}
;
return obj;
},
FromHashList : func(_keylist, _delta, _changed_method)
{
var obj = {parents : [UpdateManager] };
obj.hashkeylist = _keylist;
obj.delta = _delta;
obj.lastval = {};
obj.hashkey = nil;
obj.changed = _changed_method;
obj.needs_update = 0;
obj.isnum = _delta != nil;
obj.update = func(obj)
{
if (me.lastval == nil)
me.needs_update = 1;
else
me.needs_update = 0;
if (obj != nil or me.lastval == nil) {
foreach (hashkey; me.hashkeylist) {
if (me.isnum) {
if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) {
me.needs_update = 1;
break;
}
} elsif (me.lastval[hashkey] == nil or me.lastval[hashkey] != obj[hashkey]) {
me.needs_update = 1;
break;
}
}
}
if (me.needs_update) {
me.changed(obj);
foreach (hashkey; me.hashkeylist) {
me.lastval[hashkey] = obj[hashkey];
}
}
};
return obj;
},
};

63
test/qrcode.nas Normal file
View File

@@ -0,0 +1,63 @@
var code=[
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1],
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
[1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
];
if(os.platform()=="windows")
system("chcp 65001");
var texture=[" ","██"];
for(var i=0;i<size(code);i+=1){
for(var j=0;j<size(code[i]);j+=1)
print(texture[code[i][j]]);
print('\n');
}
var transfer=func(s){
var mode=[0,1,0,0];
println(mode);
var len=size(s);
var vec=[0,0,0,0,0,0,0,0,0];
for(var i=8;i>=0;i-=1){
vec[i]=bits.u32_and(1,len);
len=int(len/2);
}
println(vec);
var bitstr=[];
for(var i=0;i<size(s);i+=1){
var tmp=[0,0,0,0,0,0,0,0];
var c=s[i];
for(var j=7;j>=0;j-=1){
tmp[j]=bits.u32_and(1,c);
c=int(c/2);
}
foreach(var j;tmp)
append(bitstr,j);
}
println(bitstr);
var end=[0,0,0,0];
println(end);
}
transfer("github.com/ValKmjolnir");

View File

@@ -1,10 +1,7 @@
import("lib.nas");
var sort=func(vec,left,right)
{
var sort=func(vec,left,right){
if(left>=right) return;
var (L,R,tmp)=(left,right,vec[left]);
while(left<right)
{
while(left<right){
while(left<right and tmp<=vec[right])
right-=1;
while(left<right and tmp>=vec[left])

View File

@@ -1,5 +1,4 @@
# basic type
import("lib.nas");
nil;
2147483647;
0x7fffffff;
@@ -149,4 +148,60 @@ f1() or f2();
# print '1'
# this means that when using 'or' or 'and',
# if the result is clear when calculating,
# objects behind will not be calculated
# objects behind will not be calculated
print(
subvec([0,1,2,3],2),'\n',
subvec([0,1,2,3],2,1),'\n',
abs(1),'\n',
abs(-1),'\n',
systime(),'\n',
isfunc(func{}),' ',isfunc([]),'\n',
ishash({}),' ',ishash([]),'\n',
isint(114.514),' ',isint(114514),'\n',
isnum("0xaa55"),' ',isnum("?"),'\n',
isscalar(0.618),' ',isscalar("hello"),' ',isscalar([]),'\n',
isstr("hello"),' ',isstr(func{}),'\n',
isvec([]),' ',isvec("[]"),'\n',
vecindex([0,1,2,3,4],1),'\n',
vecindex(["apple","banana"],"apple")!=nil,'\n'
);
println(values({
a:1,
b:2,
c:3
}));
println(find("cd", "abcdef")); # prints 2
println(find("x", "abcdef")); # prints -1
println(find("cd", "abcdef")); # prints 2
var a={
new: func(x=0){
return {
x:x,
parents:[a]
};
},
new2: func(x=0){
return {
x:x,
parents:a
};
}
};
println(isa(a.new(),a)); # 1
println(isa(a.new2(),a));# 0
var a=[10,-10,0,1,2,3,nil,"string","hello",[],[0,1,2,3],{},{a:0,b:1,c:2},func{}];
println("type\tsize\tnum\tsrc");
foreach(var i;a){
println(typeof(i),'\t',size(i),'\t',num(i),'\t',i);
}
foreach(i;a){
;
}
println(runtime.argv());
func(a,b,c,d="只有红茶可以吗"){
println(a,' ',b,' ',c,' ',d);
}(c:1919810,b:514,a:114);

224
test/snake.nas Normal file
View File

@@ -0,0 +1,224 @@
import.module.libkey;
var list=func(){
var (begin,end,len)=(nil,nil,0);
return{
push_back:func(elem){
var tmp={
elem:elem,
prev:nil,
next:nil
};
if(end!=nil){
end.next=tmp;
tmp.prev=end;
end=tmp;
}else{
begin=end=tmp;
}
len+=1;
},
push_front:func(elem){
var tmp={
elem:elem,
prev:nil,
next:nil
};
if(begin!=nil){
begin.prev=tmp;
tmp.next=begin;
begin=tmp;
}else{
begin=end=tmp;
}
len+=1;
},
pop_back:func(){
if(end!=nil)
end=end.prev;
if(end==nil)
begin=nil;
else
end.next=nil;
if(len)
len-=1;
},
pop_front:func(){
if(begin!=nil)
begin=begin.next;
if(begin==nil)
end=nil;
else
begin.prev=nil;
if(len)
len-=1;
},
front:func(){
if(begin!=nil)
return begin.elem;
},
back:func(){
if(end!=nil)
return end.elem;
},
length:func(){
return len;
}
};
}
var game=func(x,y){
rand(time(0));
var texture=[" ","██","\e[91m██\e[0m"];
var edge0="╔";
var edge1="╚";
for(var i=0;i<x;i+=1){
edge0~="══";
edge1~="══";
}
edge0~="╗\n";
edge1~="╝\n";
var vec=[];
setsize(vec,x);
for(var i=0;i<x;i+=1){
vec[i]=[];
setsize(vec[i],y);
for(var j=0;j<y;j+=1)
vec[i][j]=0;
}
var snake=list();
snake.push_back([int(x/2),int(y/3)]);
snake.push_back([int(x/2),int(y/3)+1]);
vec[int(x/2)][int(y/3)]=1;
vec[int(x/2)][int(y/3)+1]=1;
var move='w';
var gameover=0;
var setapple=func(){
var (cord_x,cord_y)=(int(rand()*x),int(rand()*y));
while(vec[cord_x][cord_y]!=0)
(cord_x,cord_y)=(int(rand()*x),int(rand()*y));
vec[cord_x][cord_y]=2;
}
setapple();
return {
print:func(){
var s="";
var (fx,fy)=snake.front();
for(var i=0;i<y;i+=1){
s~="║";
for(var j=0;j<x;j+=1){
if(fx==j and fy==i)
s~="\e[93m"~texture[vec[j][i]]~"\e[0m";
else
s~=texture[vec[j][i]];
}
s~='║\n';
}
print('\e[1;1H'~edge0~s~edge1);
},
next:func(){
var (fx,fy)=snake.front();
var eat=0;
if(move=="w" and fy-1>=0){
snake.push_front([fx,fy-1]);
if(vec[fx][fy-1]==1)
gameover=1;
elsif(vec[fx][fy-1]==2)
eat=1;
vec[fx][fy-1]=1;
}elsif(move=='a' and fx-1>=0){
snake.push_front([fx-1,fy]);
if(vec[fx-1][fy]==1)
gameover=1;
elsif(vec[fx-1][fy]==2)
eat=1;
vec[fx-1][fy]=1;
}elsif(move=='s' and fy+1<y){
snake.push_front([fx,fy+1]);
if(vec[fx][fy+1]==1)
gameover=1;
elsif(vec[fx][fy+1]==2)
eat=1;
vec[fx][fy+1]=1;
}elsif(move=='d' and fx+1<x){
snake.push_front([fx+1,fy]);
if(vec[fx+1][fy]==1)
gameover=1;
elsif(vec[fx+1][fy]==2)
eat=1;
vec[fx+1][fy]=1;
}else{
gameover=1;
}
if(!gameover and !eat){
var (bx,by)=snake.back();
vec[bx][by]=0;
snake.pop_back();
}
if(eat and snake.length()!=x*y)
setapple();
elsif(snake.length()==x*y)
gameover=2;
},
move:func(c){
move=c;
},
gameover:func(){
return gameover;
}
}
}
var co=coroutine.create(func(){
var t=maketimestamp();
while(1){
t.stamp();
while(t.elapsedMSec()<20);
coroutine.yield();
}
});
var main=func(){
if(os.platform()=="windows")
system("chcp 65001");
print("\ec");
var g=game(15,10);
g.print();
print("\rpress any key to start...");
libkey.getch();
print("\r \r");
var counter=20;
while(1){
var ch=libkey.nonblock();
if(ch!=nil){
if(ch=='q'[0])
break;
elsif(ch=='p'[0]){
print("\rpress any key to continue...");
libkey.getch();
print("\r \r");
}
g.move(chr(ch));
}
counter-=1;
if(!counter){
counter=20;
g.next();
if(g.gameover())
break;
g.print();
}
coroutine.resume(co);
}
println(g.gameover()<=1?"game over.":"you win!");
println("press 'q' to quit.");
while(libkey.getch()!='q'[0]);
}
main();

347
test/tetris.nas Normal file
View File

@@ -0,0 +1,347 @@
import.module.libkey;
var color=[
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",
"\e[91m","\e[92m","\e[93m","\e[94m","\e[95m","\e[96m",
];
var blocktype=[
[0,1,2,3 ],
[4,5,6,7 ],
[8,9,10,11],
[12,13 ],
[14 ],
[15,16 ],
[17,18 ]
];
var blockshape=[
# [][] [] [][][]
# [] [] [] []
# [] [][][] [][]
[[0,0],[1,0],[0,1],[0,2]],
[[0,0],[0,1],[1,1],[2,1]],
[[0,0],[0,1],[0,2],[-1,2]],
[[0,0],[1,0],[2,0],[2,1]],
# [][] [][][] []
# [] [] [] []
# [] [][] [][][]
[[0,0],[1,0],[1,1],[1,2]],
[[0,0],[1,0],[2,0],[0,1]],
[[0,0],[0,1],[0,2],[1,2]],
[[0,0],[0,1],[-1,1],[-2,1]],
# [] [] [][][] []
# [][][] [][] [] [][]
# [] []
[[0,0],[0,1],[-1,1],[1,1]],
[[0,0],[0,1],[-1,1],[0,2]],
[[0,0],[-1,0],[1,0],[0,1]],
[[0,0],[0,1],[0,2],[1,1]],
# [] [][][][]
# []
# []
# []
[[0,0],[0,1],[0,2],[0,3]],
[[0,0],[1,0],[2,0],[3,0]],
# [][]
# [][]
[[0,0],[1,0],[0,1],[1,1]],
# [] [][]
# [][] [][]
# []
[[0,0],[0,1],[-1,1],[-1,2]],
[[0,0],[1,0],[1,1],[2,1]],
# [] [][]
# [][] [][]
# []
[[0,0],[0,1],[1,1],[1,2]],
[[0,0],[1,0],[0,1],[-1,1]]
];
var color_count=0;
var counter=0;
var package=[0,1,2,3,4,5,6];
var exchange=func(){
for(var i=6;i>=0;i-=1){
var index=int(i*rand());
(package[i],package[index])=(package[index],package[i]);
}
}
var block={
x:0,
y:0,
rotate:0,
type:nil,
shape:nil,
color:nil,
new:func(x=0,y=0){
(me.x,me.y)=(x,y);
me.rotate=0;
me.type=blocktype[package[counter]];
counter+=1;
if(counter==7){
exchange();
counter=0;
}
me.shape=blockshape[me.type[me.rotate]];
me.color=color_count;
color_count+=1;
if(color_count>=size(color))
color_count=0;
return {parents:[block]};
}
};
var mapgen=func(mapx,mapy){
var (score,gameover)=(0,0);
var (empty,unset,full)=(0,1,2);
if(mapx<1 or mapy<1)
die("map_x or map_y must be greater than 1");
# use in print
var line="";
for(var i=0;i<mapx;i+=1)
line~="══";
var head="\e[32m╔"~line~"╗\e[0m\n";
var tail="\e[32m╚"~line~"╝\e[0m\n";
# generate new map
var map=[];
for(var y=0;y<mapy;y+=1){
var tmp=[];
for(var x=0;x<mapx;x+=1)
append(tmp,empty);
append(map,tmp);
}
var blk=nil;
var new_block=func(){
blk=block.new(int(mapx/2),0);
# check if has enough place to place a new block
foreach(var i;blk.shape)
if(map[blk.y+i[1]][blk.x+i[0]]>=full){
gameover=1;
return;
}
# update map
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=unset;
}
new_block(); # initialize the first block
# color print
var map_print=func(){
var s="\e[1;1H"~head;
for(var y=0;y<mapy;y+=1){
s~="\e[32m║\e[0m";
for(var x=0;x<mapx;x+=1){
var c=map[y][x];
if(c==empty)
s~=" ";
elsif(c==unset)
s~=color[blk.color]~"██\e[0m";
elsif(c>=full)
s~=color[c-full]~"██\e[0m";
}
s~="\e[32m║\e[0m\n";
}
s~=tail;
print(s,"\e[31ms\e[32mc\e[33mo\e[34mr\e[35me\e[36m: \e[0m",score,'\n');
}
var moveleft=func(){
var (x,y)=(blk.x-1,blk.y);
foreach(var i;blk.shape){
if(x+i[0]<0)
return;
if(map[y+i[1]][x+i[0]]>=full)
return;
}
# update block state and map
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=empty;
blk.x=x;
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=unset;
map_print();
}
var moveright=func(){
var (x,y)=(blk.x+1,blk.y);
foreach(var i;blk.shape){
if(x+i[0]>=mapx)
return;
if(map[y+i[1]][x+i[0]]>=full)
return;
}
# update block state and map
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=empty;
blk.x=x;
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=unset;
map_print();
}
var rotate=func(){
var (r,x,y)=(blk.rotate,blk.x,blk.y);
r=(r+1>=size(blk.type))?0:r+1;
var shape=blockshape[blk.type[r]];
foreach(var i;shape){
if(x+i[0]>=mapx or x+i[0]<0 or y+i[1]>=mapy or y+i[1]<0)
return;
if(map[y+i[1]][x+i[0]]>=full)
return;
}
# update block state and map
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=empty;
blk.rotate=r;
blk.shape=shape;
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=unset;
map_print();
}
var fall=func(){
var (x,y)=(blk.x,blk.y+1);
# check if falls to the edge of other blocks or map
var sethere=0;
foreach(var i;blk.shape)
if(y+i[1]>=mapy or map[y+i[1]][x+i[0]]>=full){
sethere=1;
break;
}
# set block here and generate a new block
if(sethere){
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=blk.color+full;
checkmap();
new_block();
map_print();
return;
}
# update block state and map
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=empty;
blk.y=y;
foreach(var i;blk.shape)
map[blk.y+i[1]][blk.x+i[0]]=unset;
map_print();
}
var checkmap=func(){
var lines=1;
for(var y=mapy-1;y>=0;y-=1){
# check if this line is full of blocks
var tmp=0;
for(var x=0;x<mapx;x+=1){
if(map[y][x]<full)
break;
tmp+=map[y][x];
}
# if is full, clear this line and
# all the lines above fall one block
if(x==mapx){
score+=lines*tmp;
lines*=2;
for(var t=y;t>=1;t-=1)
for(var x=0;x<mapx;x+=1)
map[t][x]=map[t-1][x];
for(var x=0;x<mapx;x+=1)
map[0][x]=empty;
y+=1;
}
}
map_print();
}
return {
print:map_print,
moveleft:moveleft,
moveright:moveright,
rotate:rotate,
fall:fall,
checkmap:checkmap,
gameover:func(){return gameover;}
};
}
var main=func(){
# windows use chcp 65001 to output unicode
if(os.platform()=="windows")
system("chcp 65001");
print(
"\ec\e[1:1H",
"╔═════════════════════════╗\n",
"║ TETRIS ║\n",
"╠═════════════════════════╣\n",
"║ w:rotate, a:move left ║\n",
"║ s:fall, d:move right ║\n",
"║ p:pause, q:quit ║\n",
"╠═════════════════════════╣\n",
"║ press any key to start ║\n",
"╚═════════════════════════╝\n"
);
rand(time(0));
exchange();
var map=mapgen(mapx:12,mapy:18);
libkey.getch();
print("\ec");
var counter=30;
while(1){
# nonblock input one character
var ch=libkey.nonblock();
if(ch){
if(ch=='a'[0]) # move left
map.moveleft();
elsif(ch=='d'[0]) # move right
map.moveright();
elsif(ch=='w'[0]) # rotate
map.rotate();
elsif(ch=='s'[0]) # move down
map.fall();
elsif(ch=='q'[0]) # quit the game
break;
if(ch=='p'[0]){ # pause the game
print("\rpress any key to continue...");
libkey.getch();
print("\r ");
}
map.checkmap();
if(map.gameover())
break;
}
if(!counter){
# automatically fall one block and check
map.fall();
map.checkmap();
if(map.gameover())
break;
counter=30;
}
unix.sleep(0.02);
counter-=1;
}
print(
map.gameover()?
"\e[31mg\e[32ma\e[33mm\e[34me \e[35mo\e[36mv\e[94me\e[31mr \e[32m~\e[0m\n":
"\e[31ms\e[32me\e[33me \e[34my\e[35mo\e[36mu \e[94m~\e[0m\n"
);
print(
"\e[31me\e[32mn\e[33mt\e[34me\e[35mr ",
"\e[36m'\e[94mq\e[95m' ",
"\e[35mt\e[36mo \e[94mq\e[95mu\e[91mi\e[92mt\e[0m\n"
);
while(libkey.getch()!='q'[0]);
};
main();

View File

@@ -1,4 +1,3 @@
import("lib.nas");
var trait={
get:func{return me.val;},

View File

@@ -1,4 +1,3 @@
import("lib.nas");
var table=[
['q0','0','1','R','q1'],
@@ -7,8 +6,44 @@ var table=[
['q2','0','1','R','q3'],
['q3',nil,nil,'S','q3']
];
var operand={
new:func(symbol,changed_symbol,move,next_state){
if(move!='L' and move!='R' and move!='S')
die("invalid move type:"+move);
return {
symbol:symbol,
changed_symbol:changed_symbol,
move:move,
next_state:next_state
};
}
};
var machine={
states:{},
add:func(state,operand){
if(!contains(me.states,state))
me.states[state]=[operand];
else{
foreach(var i;me.states[state])
if(i.symbol==operand.symbol or i.symbol==nil){
println(i);
die("conflict operand");
}
append(me.states[state],operand);
}
},
load:func(data){
foreach(var opr;data){
var (nstat,sym,csym,move,nextstat)=opr;
me.add(nstat,operand.new(sym,csym,move,nextstat));
}
}
};
var prt=func(state,pointer,paper,act=nil){
print(state,':',pointer,':',act!=nil?act:'','\n\t');
print(act!=nil?act:'','\n\t');
var s='';
foreach(var i;paper)
s~=i;
@@ -16,36 +51,50 @@ var prt=func(state,pointer,paper,act=nil){
for(var i=0;i<pointer;i+=1)
for(var j=0;j<size(paper[i]);j+=1)
s~=' ';
print(s,'^\n');
print(s,'^\n',state," ");
}
var run=func(table,node,start,stop){
var run=func(table,start,stop){
var paper=['0','1','1','1','0','1','0','a'];
var pointer=0;
foreach(var action;table){
if(!contains(node,action[0]))
node[action[0]]=nil;
if(!contains(node,action[4]))
node[action[4]]=nil;
}
print("nodes: ",keys(node),'\n');
if(!contains(node,start))
machine.load(table);
print("states: ",keys(machine.states),'\n');
if(!contains(machine.states,start))
die(start~" is not a valid node");
if(!contains(node,stop))
if(!contains(machine.states,stop))
die(stop~" is not a valid node");
var state=start;
var (state,pointer)=(start,0);
prt(state,pointer,paper);
while(state!=stop){
foreach(var action;table)
if(action[0]==state and (action[1]==paper[pointer] or action[1]==' ')){
paper[pointer]=action[2]==nil?paper[pointer]:action[2];
if(action[3]=='L') pointer-=1;
elsif(action[3]=='R') pointer+=1;
elsif(action[3]!='S') die("invalid action <"~action[3]~'>');
state=action[4];
if(!contains(machine.states,state))
die("no matching function for state:"~state);
var found=0;
foreach(var action;machine.states[state]){
var (sym,csym,move,next)=(
action.symbol,
action.changed_symbol,
action.move,
action.next_state
);
if(sym==paper[pointer] or sym==nil){
if(sym!=nil)
paper[pointer]=csym;
if(move=='L') pointer-=1;
elsif(move=='R') pointer+=1;
(state,found)=(next,1);
break;
}
prt(state,pointer,paper,action);
}
if(!found)
die("no matching function for state:"~state);
prt(state,pointer,paper,[sym,csym,move,next]);
}
}
run(table,{},'q0','q3');
run(table,'q0','q3');
print('\n');

49
test/utf8chk.nas Normal file
View File

@@ -0,0 +1,49 @@
var unicode测试=func(){
var 输出=print;
var 测试成功=[
"unicode: utf-8支持测试成功",
"目前仅支持utf-8以及ascii格式文件",
"注意: windows系统请开启chcp 65001代码页"
];
foreach(var 内容;测试成功)
输出(内容~"\n");
}
var emoji测试=func(){
var 💻=print;
var 🎤="\n";
var 🤣="🤣笑嘻了";
var 😅="😅差不多得了";
var 🤤="🤤收收味";
var 🥵="🥵太烧啦";
var 🥶="🥶捏麻麻滴冷死了";
var 🤢="🤢老八秘制小汉堡🍔";
var 🤓="🤓我是傻逼";
var 😭="😭你带我走吧😭😭😭";
var 👿="👿密麻麻石蜡";
var 🤡="🤡居然就是你";
var 💩="💩奥利给干了兄弟们";
var 🍾="🍾好似,开🍾咯";
var 🐘="🐘太🚬🐘了兄弟们";
var 📁=[🤣,😅,🤤,🥵,🥶,🤢,🤓,😭,👿,🤡,💩,🍾,🐘];
var 🗄️={
🤣:🤣,
😅:😅,
🤤:🤤,
🥵:🥵,
🥶:🥶,
🤢:🤢,
🤓:🤓,
😭:😭,
👿:👿,
🤡:🤡,
💩:💩,
🍾:🍾,
🐘:🐘
};
foreach(var 📄;📁)
💻(📄,🎤);
foreach(var 📄;keys(🗄️))
💻(📄,🗄️[📄],🎤);
}
unicode测试();
emoji测试();

104
test/wavecollapse.nas Normal file
View File

@@ -0,0 +1,104 @@
# wave collapse function 2022/4/10
# by ValKmjolnir
srand();
var interval=1/60;
var table=[
# c ,w,a,s,d
["═",0,1,0,1],
["═",0,1,0,1],
["═",0,1,0,1],
["║",1,0,1,0],
["╔",0,0,1,1],
["╗",0,1,1,0],
["╚",1,0,0,1],
["╝",1,1,0,0],
# ["╠",1,0,1,1],
# ["╣",1,1,1,0],
# ["╦",0,1,1,1],
# ["╩",1,1,0,1],
# ["╬",1,1,1,1],
[" ",0,0,0,0],
[" ",0,0,0,0],
[" ",0,0,0,0],
[" ",0,0,0,0],
[" ",0,0,0,0],
[" ",0,0,0,0]
];
var map=func(){
var (vec,x,s)=(nil,nil,size(table));
var generator=func(){
var tmp=[];
foreach(var elem;table)
if(elem[1]==vec[0][0][3] and elem[2]==0)
append(tmp,elem);
vec[1][0]=tmp[rand()*size(tmp)];
for(var j=1;j<x;j+=1){
if(vec[0][j][3]==0 and vec[1][j-1][4]==0 and rand()>0.5){
vec[1][j]=table[-1];
continue;
}
tmp=[];
foreach(var elem;table)
if(elem[2]==vec[1][j-1][4] and elem[1]==vec[0][j][3]){
if((j==x-1 and elem[4]==0) or j<x-1)
append(tmp,elem);
}
vec[1][j]=tmp[rand()*size(tmp)];
}
}
return {
new:func(_x=10){
x=_x;
vec=[[],[]];
for(var i=0;i<2;i+=1){
setsize(vec[i],x);
for(var j=0;j<x;j+=1)
vec[i][j]=table[-1];
}
var tmp=[];
foreach(var elem;table)
if(elem[1]==0 and elem[2]==0)
append(tmp,elem);
vec[0][0]=tmp[rand()*size(tmp)];
for(var i=1;i<x;i+=1){
tmp=[];
foreach(var elem;table)
if(elem[2]==vec[0][i-1][4] and elem[1]==0){
if((i==x-1 and elem[4]==0) or i<x-1)
append(tmp,elem);
}
vec[0][i]=tmp[rand()*size(tmp)];
}
me.print(0);
generator();
},
print:func(index){
var str="";
foreach(var _x;vec[index])
str~=_x[0];
str~="\n";
print(str);
},
next:func(){
(vec[0],vec[1])=(vec[1],vec[0]);
generator();
}
}
}();
if(os.platform()=="windows")
system("chcp 65001");
map.new(80);
for(var iter=0;iter<100;iter+=1){
map.print(1);
map.next();
unix.sleep(interval);
}

View File

@@ -1,7 +1,4 @@
# Y combinator by ValKmjolnir
import("lib.nas");
var fib=func(f){
return f(f);
}(