156 Commits
v9.0 ... next

Author SHA1 Message Date
ValKmjolnir
d121dcd630 🎨 improve format of code 2022-10-28 23:28:15 +08:00
ValKmjolnir
c705b75513 🚀 change module function parameter format to avoid warnings 2022-10-24 01:12:25 +08:00
ValKmjolnir
3ef8effe9a 🔥 change nasal_gc to gc 2022-10-23 01:29:20 +08:00
ValKmjolnir
3fd1b25f79 🔥 change class name.
nasal_lexer -> lexer
nasal_parse -> parse
nasal_codegen -> codegen
nasal_vm -> vm
nasal_gc -> gc
nasal_dbg -> debugger
nasal_import -> linker
nas_ref -> var
2022-10-21 01:29:29 +08:00
ValKmjolnir
025ff49ffc 🚀 add stl/csv.nas & ast name change 2022-10-19 00:54:21 +08:00
ValKmjolnir
7a93527948 add stl/json.nas & fix bug
bug: may cause program crash if stack overflow occurs on main stack
2022-10-08 21:34:47 +08:00
ValKmjolnir
405175061a 🚀 crashed coroutine will not make main thread crash. 2022-10-06 23:11:27 +08:00
Li Haokun
ae85791f01 Merge pull request #16 from sidi762/master
Build modules for macOS nightly builds and deliver everything (instead of just the binary) in the release
2022-10-06 17:23:14 +08:00
Sidi Liang
3cb5d0f2d9 Merge pull request #1 from sidi762/patch-1
Build modules for Mac nightly
2022-10-06 17:19:02 +08:00
Sidi Liang
a3f5dc01d3 Build modules for Mac nightly 2022-10-06 17:15:47 +08:00
ValKmjolnir
35d7772dd3 📝 update doc 2022-10-06 00:23:32 +08:00
ValKmjolnir
e25eb76e94 🚀 delete unnecessary codes & add stl/string.nas 2022-10-05 16:03:47 +08:00
ValKmjolnir
946aa020a5 🚀 add high resolution progress bar 2022-09-28 23:45:15 +08:00
ValKmjolnir
6ef22d3228 🚀 use std::ostream to unify nas_ref output 2022-09-23 21:45:08 +08:00
ValKmjolnir
c4cac512a6 🐛 fix compilation error of FindNextFile in other VS version. 2022-09-23 20:39:34 +08:00
ValKmjolnir
dc3770094a 🚀 update test file 2022-09-20 21:51:52 +08:00
ValKmjolnir
791de656c3 🚀 update ascii-art.nas and stl/padding.nas 2022-09-20 00:41:08 +08:00
ValKmjolnir
cff9f91bee 🚀 move pic directory into doc 2022-09-19 00:10:08 +08:00
ValKmjolnir
06f02ec0cb 📝 update doc 2022-09-17 18:23:27 +08:00
ValKmjolnir
24ae1c246f 📝 fix help info align 2022-09-15 23:37:46 +08:00
ValKmjolnir
0576459fbe 📝 add notes in test files & use system("color") to use ANSI esc seq 2022-09-14 23:15:36 +08:00
ValKmjolnir
518ce7fcb9 📝 add interesting gifs into docs 2022-09-13 23:00:48 +08:00
ValKmjolnir
0e682b7c07 📝 update docs 2022-09-13 22:14:17 +08:00
ValKmjolnir
26f4e1359f 🐛 fix bug that int() cannot convert numeric string 2022-09-12 22:45:35 +08:00
ValKmjolnir
aa5b1d3d66 🚀 import information output of dbg, vm and codegen 2022-09-11 17:22:00 +08:00
ValKmjolnir
6a6eab8db5 change parameter list of 2022-09-11 15:10:30 +08:00
ValKmjolnir
91b3074ce9 optimize stl/sort.nas and test/calc.nas 2022-09-10 15:45:25 +08:00
ValKmjolnir
10e579dabc 🐛 fix bug that word_collector.nas split some words incorrectly 2022-09-09 21:48:10 +08:00
ValKmjolnir
add5e0c2cd 📝 update test file & add new test file word_collector.nas 2022-09-09 01:00:09 +08:00
ValKmjolnir
1e0f0f8e7b 🐛 bug fix: fix sigsegv when parsing [1,2,3][]=1;, will report expected index value 2022-09-05 01:23:37 +08:00
ValKmjolnir
972ad49a4f improve error output info generated by codegen. 2022-09-05 00:41:36 +08:00
ValKmjolnir
a13e419518 📝 fix MSVC warning in nasal_builtin.h & improve error output. 2022-09-04 23:08:06 +08:00
ValKmjolnir
6c04487319 🚀 add new stl file padding.nas and update test files. 2022-09-04 17:53:00 +08:00
ValKmjolnir
5715c1df5f 🎨 improve error log output format. 2022-09-02 02:04:03 +08:00
ValKmjolnir
aa0023a23b 📝 use bold font in test/watchdog.nas 2022-09-01 00:04:14 +08:00
ValKmjolnir
f86ea2445f 🚀 change cpp standard to c++14 & add command line colorful info output.
and fix a bug that program may crash if there's an error when coroutine is running
2022-08-31 23:24:41 +08:00
ValKmjolnir
52a38709bb 📝 diable warning C4566 in MSVC 2022-08-31 01:06:57 +08:00
ValKmjolnir
b022b25cea 🚀 add colorful error info print. 2022-08-31 00:56:19 +08:00
ValKmjolnir
27ceeb517d 🐛 fix bug that make test failed 2022-08-29 00:48:06 +08:00
ValKmjolnir
8293f85c5b 🚀 add lib function exit() and add test file watchdog.nas to run the nasal file when it is changed. 2022-08-27 18:12:32 +08:00
ValKmjolnir
0e578b3e21 📝 update stl/file and test files. 2022-08-27 17:26:01 +08:00
ValKmjolnir
24ba300f3c 🔥 delete --opcnt and replace the function of -o by --optimize. 2022-08-25 19:54:03 +08:00
ValKmjolnir
5be6351b60 delete unnecessary prefix in die("") 2022-08-25 01:16:17 +08:00
ValKmjolnir
a91826607c optimize header file, now modules could generate smaller dynamic libs. 2022-08-24 22:08:47 +08:00
ValKmjolnir
987d3ce9e2 🚀 change indentation of nasal_ast::print & add new function
`nasal_ast::tree`.
2022-08-22 22:35:53 +08:00
ValKmjolnir
da8aa4744e 📝 add compilation time & update test/ppmgen.nas 2022-08-21 00:26:16 +08:00
ValKmjolnir
978957aca7 optimize str2num 2022-08-20 02:01:39 +08:00
ValKmjolnir
caf048aae4 several updates.
1. merge `--chkpath` `-cp` with `-d`

2. complete test/ppmgen.nas

3. update comments about why we don't use `atof` to convert string to
number

4. update .gitignore
2022-08-19 23:25:46 +08:00
ValKmjolnir
692f8ccefe update doc & notes, optimize code 2022-08-18 20:41:33 +08:00
ValKmjolnir
007b83bed5 📝 add test file ppmgen.nas 2022-08-17 21:09:22 +08:00
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
95 changed files with 13526 additions and 9957 deletions

1
.gitattributes vendored Normal file
View File

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

View File

@@ -17,7 +17,12 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: make
run: make
run: |
make
cd module
make all
cd ..
tar -czf nasal-mac-nightly.tgz .
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
@@ -30,5 +35,5 @@ jobs:
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next
# File to release
files: nasal
files: nasal-mac-nightly.tgz

20
.gitignore vendored
View File

@@ -31,6 +31,24 @@
*.out
*.app
# VS C++ sln
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
.vs
x64
# nasal executable
nasal
nasal.exe
# misc
.vscode
dump
dump
# macOS special cache directory
.DS_Store
# ppm picture generated by ppmgen.nas
*.ppm

1657
README.md

File diff suppressed because it is too large Load Diff

950
doc/README_zh.md Normal file
View File

@@ -0,0 +1,950 @@
# __Nasal 脚本语言__
![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.1-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++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码我们依然非常感谢Andy为我们带来了这样一个神奇且容易上手的编程语言。
该项目已使用 __MIT__ 协议开源 (2021/5/4)。
__我们为什么想要重新写一个nasal解释器?__
2019年暑假[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我在Flightgear中提供的nasal控制台窗口中进行调试很不方便仅仅是想检查语法错误也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误甚至是检查运行时的错误。
我编写了nasal的词法分析器和语法分析器以及一个全新的字节码虚拟机并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误提高了我们的工作效率。
你也可以使用这个语言来写一些与Flightgear运行环境无关的有趣的程序并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块使它成为项目中一个非常有用的工具。
## __编译__
![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。
`Windows` 用户通过 g++(`MinGW-w64`) 或者使用 MSVC(`Visual Studio`) 来进行编译。
`Linux/macOS/Unix` 用户可以使用 g++ 或者 clang++ 来进行编译 (建议您使用 `clang`)。
`Windows` 平台(`MinGW-w64`):
> mingw32-make nasal.exe
你也可以在`Visual Studio`中用这种方式来创建项目:[__点击跳转__](../doc/vs.md)。
`Linux/macOS/Unix` 平台:
> make nasal
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
> make nasal CXX=...
如果你觉得`-O3`编译的版本不是那么安全和稳定,你也可以选择生成稳定的版本:
> make stable-release
## __使用方法__
![usage](../doc/gif/help.gif)
如果你是 `Windows` 用户且想正常输出unicode在nasal代码里写这个来开启unicode代码页:
```javascript
if(os.platform()=="windows")
system("chcp 65001");
```
## __教程__
Nasal是非常容易上手的你甚至可以在15分钟之内看完这里的基本教程并且直接开始编写你想要的程序。
__如果你先前已经是C/C++, javascript选手那么几乎可以不用看这个教程……__ 在看完该教程之后,基本上你就完全掌握了这个语言:
<details><summary>基本类型</summary>
__`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行,该类型只能由虚拟机在抛出错误时产生。
__`nil`__ 是空类型。类似于null。
```javascript
var spc=nil;
```
__`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
```
__`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。
```javascript
var s='str';
var s="another string";
var s=`c`;
# 该语言也支持一些特别的转义字符:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度)
```javascript
var vec=[];
var vec=[0,nil,{},[],func(){return 0}];
append(vec,0,1,2);
```
__`hash`__ 使用哈希表 (类似于`python`中的`dict`)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。
```javascript
var hash={
member1:nil,
member2:"str",
"member3":"member\'s name can also be a string constant",
funct:func(){
return me.member2~me.member3;
}
};
```
__`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;
}
```
__`upval`__ 是存储闭包数据的特殊类型, 在 __`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 (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); # 从元组中初始化多个变量
```
</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`,可以试一试:
```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_native(builtin_print);
```
然后用C++完成这个函数的函数体:
```C++
var builtin_print(var* local,gc& ngc)
{
// 局部变量的下标其实是从1开始的
// 因为local[0]是保留给'me'的空间
var vec=local[1];
// 主要部分
// 一些必要的类型检查和输入合法性检测也要在这里写出
// 如果检测到问题用builtin_err函数来返回vm_null
// 并且狠狠地骂那些不好好写代码的混蛋(玩笑)
for(auto& i:vec.vec().elems)
switch(i.type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<i.str(); break;
case vm_vec: std::cout<<i.vec(); break;
case vm_hash: std::cout<<i.hash(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
}
std::cout<<std::flush;
// 最后一定要记得生成返回值,返回值必须是一个内置的类型,
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil;
}
```
当运行内置函数的时候内存分配器如果运行超过一次那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++
var builtin_keys(var* local,gc& ngc)
{
var hash=local[1];
if(hash.type!=vm_hash)
return nas_err("keys","\"hash\" must be hash");
// 使用gc.temp来存储gc管理的变量防止错误的回收
var res=ngc.temp=ngc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
vec.push_back(ngc.newstr(iter.first));
ngc.temp=nil;
return res;
}
```
这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针:
```C++
struct func
{
const char* name;
var (*func)(var*,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 fault(段错误)__。
在nasal文件中使用`import("文件名.nas")`可以导入该文件中你包装的所有内置函数,接下来你就可以使用他们了。
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
```javascript
import.dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
</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);
}
// 模块函数的参数列表一律以这个为准
var fib(var* args,usize size,gc* ngc){
// 传参会给予一个var指针指向一个vm_vec的data()
var num=args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
// nas_err会输出错误信息并返回错误类型让虚拟机终止执行
if(num.type!=vm_num)
return nas_err("extern_fib","\"num\" must be number");
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用ngc->alloc(type)
return {vm_num,fibonaci(num.tonum())};
}
// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针
// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的
// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致
// 类似 "extern "C" var fib" 的写法会得到编译错误
extern "C" mod get(const char* n){
string name=n;
if(name=="fib")
return fib;
return nullptr;
}
```
接着我们把`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
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解释器的不同之处__
![error](../doc/gif/error.gif)
<details><summary>必须用 var 定义变量</summary>
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。
在Andy的解释器中:
```javascript
foreach(i;[0,1,2,3])
print(i)
```
这个程序可以正常运行。然而这个`i`标识符实际上在这里是被第一次定义,而且没有使用`var`。我认为这样的设计很容易让使用者迷惑。他们可能都没有发现这里实际上是第一次定义`i`的地方。没有使用`var`的定义会让程序员认为这个`i`也许是在别的地方定义的。
所以在这个解释器中,我直接使用严格的语法检查方法来强行要求用户必须要使用`var`来定义新的变量或者迭代器。如果你忘了加这个关键字,那么你就会得到这个:
```javascript
code: undefined symbol "i"
--> test.nas:1:9
|
1 | foreach(i;[0,1,2,3])
| ^ undefined symbol "i"
code: undefined symbol "i"
--> test.nas:2:11
|
2 | print(i)
| ^ undefined symbol "i"
```
</details>
<details><summary>默认不定长参数</summary>
这个解释器在运行时,函数不会将超出参数表的那部分不定长参数放到默认的`arg`中。所以你如果不定义`arg`就使用它,那你只会得到`undefined symbol`。
```javascript
var f=func(){
println(arg)
}
f(1,2,3);
```
编译结果:
```javascript
code: undefined symbol "arg"
--> test.nas:2:15
|
2 | println(arg)
| ^ undefined symbol "arg"
```
</details>
## __堆栈追踪信息__
![stackoverflow](../doc/gif/stackoverflow.gif)
当解释器崩溃时,它会反馈错误产生过程的堆栈追踪信息:
<details><summary>内置函数 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>栈溢出</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>运行时错误</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>详细的崩溃信息</summary>
使用命令 __`-d`__ 或 __`--detail`__ 后trace back信息会包含更多的细节内容:
```javascript
hello
[vm] error: error occurred this line
[vm] error: native function error
trace back (main)
0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131)
0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4)
0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6)
vm stack (0x7fffe0ffed90 <sp+63>, limit 10, total 12)
0x0000004a | null |
0x00000049 | pc | 0x553
0x00000048 | addr | 0x7fffe0ffeda0
...
0x00000041 | nil |
registers (main)
[ pc ] | pc | 0xb0
[ global ] | addr | 0x7fffe0ffe9a0
[ localr ] | addr | 0x7fffe0ffedf0
[ memr ] | addr | 0x0
[ canary ] | addr | 0x7fffe1002990
[ top ] | addr | 0x7fffe0ffee40
[ funcr ] | func | <0x677cd0> entry:0xb0
[ upvalr ] | nil |
global (0x7fffe0ffe9a0 <sp+0>)
0x00000000 | func | <0x65fb00> entry:0x5
0x00000001 | func | <0x65fb20> entry:0xd
...
0x0000003d | func | <0x66bf00> entry:0x51f
0x0000003e | hash | <0x65ffa0> {5 val}
local (0x7fffe0ffedf0 <sp+45>)
0x00000000 | nil |
0x00000001 | str | <0x6cb630> error occurred t...
```
</details>
## __调试器__
![dbg](../doc/gif/dbg.gif)
在`v8.0`版本中我们添加了调试器。
使用这个命令`./nasal -dbg xxx.nas`来启用调试器,接下来调试器会打开文件并输出以下内容:
<details><summary>展开</summary>
```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 41 intg 0x41 (test/fib.nas:0)
0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6)
0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6)
0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6)
0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6)
0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7)
0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7)
0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 0)
>>
```
</details>
如果需要查看命令的使用方法,可以输入`h`获取帮助信息。
当运行调试器的时候,你可以看到现在的操作数栈上到底有些什么数据。
这些信息可以帮助你调试,同时也可以帮助你理解这个虚拟机是如何工作的:
<details><summary>展开</summary>
```javascript
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:
0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503)
0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498)
0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1)
0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1)
0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1)
0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1)
--> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3)
0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3)
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
0x00000047 | pc | 0x566
0x00000046 | addr | 0x0
0x00000045 | nil |
0x00000044 | num | 0
0x00000043 | nil |
0x00000042 | nil |
0x00000041 | func | <0x88d2f0> entry:0x5
>>
```
</details>

112
doc/benchmark.md Normal file
View File

@@ -0,0 +1,112 @@
# __Benchmark__
![benchmark](../doc/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](../doc/pic/mandelbrot.png)

670
doc/dev.md Normal file
View File

@@ -0,0 +1,670 @@
# __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 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__
![op](../doc/gif/opcode.gif)
### 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 `codegen::call_gen()` instead of `codegen::mcall_gen()`,
and the last child of the ast will be generated by `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 `vm`,
and now `vm` use `nas_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 `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 `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 `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 `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(var) |
| ... |
+----------------------+ <- 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(var) |
| ... |
+----------------------+ <- 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(var) |
| ... |
+----------------------+ <- 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(var) |
+----------------------+
| old pc(vm_ret) |
+----------------------+
| old localr(vm_addr) |
+----------------------+
| old upvalr(vm_upval) |
+----------------------+
| local scope(var) |
| ... |
+----------------------+ <- 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`.

603
doc/dev_zh.md Normal file
View File

@@ -0,0 +1,603 @@
# __开发历史记录__
## __目录__
* [__语法分析__](#语法分析)
* [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)
我改变想法了,树解释器给维护带来了太大的麻烦。如果想继续保留这个解释器,那么为了兼容性,字节码虚拟机的优化工作会更难推进。
## __字节码虚拟机__
![op](../doc/gif/opcode.gif)
### 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模块会优先使用`codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `codegen::mcall_gen()`,在最后一个调用处才会使用`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`,一个曾用来存储指针的栈,从`vm`中被移除。现在`vm`使用`nas_val** mem_addr`来暂存获取的内存地址。这不会导致严重的问题,因为内存空间是 __获取即使用__ 的。
### version 7.0 vm (last update 2021/10/8)
2021/6/26 update:
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后`vm`的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完`test/bigloop``test/pi`并且在linux平台虚拟机可以在0.8秒内执行完`test/fib`。你可以在下面的测试数据部分看到测试的结果。
这个分派方式使用了g++扩展"labels as values"clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了哈哈)
`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`不再由`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(var) |
| ... |
+----------------------+ <- 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(var) |
| ... |
+----------------------+ <- 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(var) |
| ... |
+----------------------+ <- local pointer stored in localr
| old funcr(vm_func) | <- old function stored in funcr
+----------------------+
```
当`builtin_coyield`执行完毕之后,栈又切换到了主操作数栈上,这时可以看到返回的`local[1]`实际上被`op_callb`放在了这里的栈顶:
```C++
+----------------------+(主操作数栈)
| return_value(var) |
+----------------------+
| old pc(vm_ret) |
+----------------------+
| old localr(vm_addr) |
+----------------------+
| old upvalr(vm_upval) |
+----------------------+
| local scope(var) |
| ... |
+----------------------+ <- 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`。

BIN
doc/gif/dbg.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

BIN
doc/gif/error.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
doc/gif/help.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

BIN
doc/gif/opcode.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
doc/gif/stackoverflow.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -0,0 +1,164 @@
<!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="/doc/pic/benchmark.png" width="450" height="350"></img>
<img src="/doc/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="/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>
<li><a href="/choice.nas">choice.nas</a></li>
</ul>
</td>
<td>
<ul>
<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>
<li><a href="/json.nas">json.nas</a></li>
</ul>
</td>
<td>
<ul>
<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="/mcpu.nas">mcpu.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="/ppmgen.nas">ppmgen.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>
</ul>
</td>
<td>
<ul>
<li><a href="/snake.nas">snake.nas</a></li>
<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="/watchdog.nas">watchdog.nas</a></li>
<li><a href="/wavecollapse.nas">wavecollapse.nas</a></li>
<li><a href="/word_collector.nas">word_collector.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>

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/pic/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

31
doc/vs.md Normal file
View File

@@ -0,0 +1,31 @@
# Create VS project | 创建 VS 工程
## First | 首先
Make sure you are using VS 2022.
确保你使用的是 VS 2022。
## How to Create VS project
1. Get code from this repo using `git`.
2. In Visual Studio, click `File`->`New`->`Project From Existing Code...`.
3. Select `Visual C++`->`Next`->choose `project file location`->write the `project name` at ease->`Finish`.
4. Remove cpp files in `module` to avoid compilation problems.(they should be compiled to dynamic libraries)
5. Click `Source Files` in `Search Solution Explorer` at left, right click `main.cpp`, compile.
## 如何创建VS工程
1.`git`从这个仓库获取代码。
2. 在VS的界面点击文件(F)->新建(N)->从现有代码创建项目(E)。
3. 选择创建`Visual C++`项目->下一步->项目文件位置选择你下载的代码存放的文件夹->填项目名称,随便写->完成。
4. 从项目中去掉 `module` 里的cpp文件以防止编译错误。(那些本应该编译到动态库)
5. 点开左侧解决方案资源管理器中的`Source Files`,右键点击`main.cpp`,编译。

277
lib.nas
View File

@@ -1,74 +1,74 @@
# 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 separator for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(separator,str){
return __builtin_split(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.
# int will get the integer of input number/string.
# 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 __builtin_floor(val);
return __floor(val);
}
# exit using std::exit
var exit=func(val=-1){
return __exit(val);
}
# abort using std::abort
var abort=func(){
__builtin_abort();
__abort();
}
# abs gets absolute number.
@@ -79,47 +79,47 @@ var abs=func(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);
@@ -128,18 +128,18 @@ var systime=func(){
# 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 __builtin_find(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
@@ -150,37 +150,37 @@ var subvec=func(vec,begin,length=nil){
# 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.
@@ -196,15 +196,13 @@ var srand=func(){
# values() gets all values in a hash.
var values=func(hash){
return __builtin_values(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){
@@ -267,9 +265,22 @@ var assert=func(condition,message="assertion failed!"){
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 __builtin_md5(str);
return __md5(str);
}
var io=
@@ -278,29 +289,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*.
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
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
@@ -325,35 +338,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.
@@ -361,42 +385,45 @@ var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
abs: func(x) {return x>0?x:-x; },
floor: func(x) {return __builtin_floor(x); },
pow: func(x,y){return __builtin_pow(x,y); },
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); },
max: func(x,y){return x>y?x:y; },
min: func(x,y){return x<y?x:y; }
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(){return __builtin_pipe;},
fork: func(){return __builtin_fork;},
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){return __builtin_waitpid;},
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(){return __builtin_environ();},
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.
@@ -404,13 +431,31 @@ var unix=
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.
@@ -418,35 +463,17 @@ 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");
@@ -466,4 +493,12 @@ var closure=func(function,level=1){
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; }
};

168
main.cpp
View File

@@ -1,54 +1,70 @@
#include "nasal.h"
#include "nasal_err.h"
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_opt.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"
const uint32_t VM_LEXINFO =0x01;
const uint32_t VM_ASTINFO =0x02;
const uint32_t VM_CODEINFO =0x04;
const uint32_t VM_EXECTIME =0x08;
const uint32_t VM_OPCALLNUM=0x10;
const uint32_t VM_EXEC =0x20;
const uint32_t VM_DBGINFO =0x40;
const uint32_t VM_DEBUG =0x80;
const uint32_t VM_OPTIMIZE =0x100;
#include <unordered_map>
using ch_clk=std::chrono::high_resolution_clock;
const u32 VM_TOKEN =0x01;
const u32 VM_AST =0x02;
const u32 VM_CODE =0x04;
const u32 VM_TIME =0x08;
const u32 VM_EXEC =0x10;
const u32 VM_DETAIL=0x20;
const u32 VM_DEBUG =0x40;
const u32 VM_OPT =0x80;
void help()
{
std::cout
std::clog
<<" ,--#-,\n"
<<"<3 / \\____\\ <3\n"
<<" |_|__A_|\n"
#ifdef _WIN32
<<"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"
<<" -l, --lex | view token info.\n"
<<" -a, --ast | view abstract syntax tree.\n"
<<" -c, --code | view bytecode.\n"
<<" -e, --exec | execute.\n"
<<" -t, --time | get the running time.\n"
<<" -d, --detail | get detail runtime crash info.\n"
<<" | get detail linker path-not-found 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 -e).\n"
<<" -o, --optimize | use optimizer(beta).\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"
<<"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
std::clog
<<" __ _\n"
<<" /\\ \\ \\__ _ ___ __ _| |\n"
<<" / \\/ / _` / __|/ _` | |\n"
<<" / /\\ / (_| \\__ \\ (_| | |\n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"nasal ver : "<<__nasver<<"\n"
<<"version : "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n"
<<"c++ std : "<<__cplusplus<<"\n"
<<"thanks to : https://github.com/andyross/nasal\n"
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
@@ -57,62 +73,59 @@ 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;
nasal_lexer lexer(nerr);
nasal_parse parse(nerr);
nasal_import linker(nerr);
nasal_codegen gen(nerr);
// back end
nasal_vm vm;
error err;
lexer lex(err);
parse parse(err);
linker ld(err);
codegen gen(err);
vm ctx;
// lexer scans file to get tokens
lexer.scan(file);
if(cmd&VM_LEXINFO)
lexer.print();
lex.scan(file);
if(cmd&VM_TOKEN)
lex.print();
// parser gets lexer's token list to compile
parse.compile(lexer);
parse.compile(lex);
// linker gets parser's ast and load import files to this ast
linker.link(parse,file);
ld.link(parse,file,cmd&VM_DETAIL);
// optimizer does simple optimization on ast
if(cmd&VM_OPTIMIZE)
optimize(parse.ast());
if(cmd&VM_ASTINFO)
if(cmd&VM_OPT)
optimize(parse.tree());
if(cmd&VM_AST)
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.compile(parse,ld);
if(cmd&VM_CODE)
gen.print();
// run bytecode
// run
if(cmd&VM_DEBUG)
debugger(err).run(gen,ld,argv);
else if(cmd&VM_TIME)
{
nasal_dbg debugger;
debugger.run(gen,linker);
}
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=ch_clk::now();
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
auto end=ch_clk::now();
std::clog<<"process exited after "<<(end-start).count()*1.0/ch_clk::duration::period::den<<"s.\n";
}
else if(cmd&VM_EXEC)
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
}
int main(int argc,const char* argv[])
i32 main(i32 argc,const char* argv[])
{
if(argc<=1)
{
@@ -121,36 +134,41 @@ 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();
std::clog<<"nasal "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n";
else if(s=="-h" || s=="--help")
help();
else if(s[0]!='-')
execute(s,VM_EXEC);
execute(s,{},VM_EXEC);
else
err();
return 0;
}
std::unordered_map<std::string,uint32_t> cmdlst={
{"--lex",VM_LEXINFO},{"-l",VM_LEXINFO},
{"--ast",VM_ASTINFO},{"-a",VM_ASTINFO},
{"--code",VM_CODEINFO},{"-c",VM_CODEINFO},
std::unordered_map<string,u32> cmdlst={
{"--lex",VM_TOKEN},{"-l",VM_TOKEN},
{"--ast",VM_AST},{"-a",VM_AST},
{"--code",VM_CODE},{"-c",VM_CODE},
{"--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},
{"--time",VM_TIME|VM_EXEC},{"-t",VM_TIME|VM_EXEC},
{"--detail",VM_DETAIL|VM_EXEC},{"-d",VM_DETAIL|VM_EXEC},
{"--optimize",VM_OPT},{"-o",VM_OPT},
{"--debug",VM_DEBUG},{"-dbg",VM_DEBUG}
};
uint32_t cmd=0;
for(int i=1;i<argc-1;++i)
u32 cmd=0;
string filename;
std::vector<string> vm_argv;
for(i32 i=1;i<argc;++i)
{
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:VM_EXEC);
return 0;
}

116
makefile
View File

@@ -1,47 +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
nasal.exe: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
g++ -std=c++11 -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
.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
STD=14
nasal:$(SRC)
$(CXX) -std=c++$(STD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
nasal.exe:$(SRC)
$(CXX) -std=c++$(STD) -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
stable-release:$(SRC)
$(CXX) -std=c++$(STD) -O2 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
stable-release-mingw:$(SRC)
$(CXX) -std=c++$(STD) -O2 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
test:nasal
@ ./nasal -op -e test/ascii-art.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 -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 -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 -o test/md5.nas
-@ ./nasal -op -t -d -o test/md5compare.nas
-@ ./nasal -op -d 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/props_sim.nas
@ ./nasal -op -e test/qrcode.nas
@ ./nasal -op -t -d test/quick_sort.nas
@ ./nasal -op -e test/scalar.nas
-@ ./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 -o test/ycombinator.nas
@ ./nasal -op -e test/wavecollapse.nas
@ ./nasal -o -e test/ascii-art.nas
@ ./nasal -o -c test/auto_crash.nas
@ ./nasal -o -a -c test/bf.nas
@ ./nasal -o -a -c test/bfconvertor.nas
@ ./nasal -o -e -d test/bfs.nas
@ ./nasal -o -t test/bigloop.nas
@ ./nasal -o -e test/bp.nas
@ ./nasal -o -e -d test/calc.nas
@ ./nasal -o -e test/choice.nas
@ ./nasal -o -e test/class.nas
@ ./nasal -o -d test/coroutine.nas
@ ./nasal -o -e test/diff.nas
-@ ./nasal -o -d test/exception.nas
@ ./nasal -o -t -d test/fib.nas
@ ./nasal -o -e test/filesystem.nas
@ ./nasal -o -e -d test/hexdump.nas
@ ./nasal -o -c test/httptest.nas
@ ./nasal -o -e test/json.nas
@ ./nasal -o -e test/leetcode1319.nas
@ ./nasal -o -e -d test/lexer.nas
@ ./nasal -o -e -d test/life.nas
@ ./nasal -o -t test/loop.nas
@ ./nasal -o -t -d test/mandel.nas
@ ./nasal -o -t -d test/mandelbrot.nas
@ ./nasal -o -t -d test/md5.nas
@ ./nasal -o -t -d test/md5compare.nas
-@ ./nasal -o -d test/module_test.nas
@ ./nasal -o -e test/nasal_test.nas
@ ./nasal -o -c test/occupation.nas
@ ./nasal -o -t -d test/pi.nas
@ ./nasal -o -c test/ppmgen.nas
@ ./nasal -o -t -d test/prime.nas
@ ./nasal -o -e test/qrcode.nas
@ ./nasal -o -t -d test/quick_sort.nas
@ ./nasal -o -e test/scalar.nas hello world
-@ ./nasal -o -c -t test/snake.nas
@ ./nasal -o -c -e test/trait.nas
-@ ./nasal -o -c -t test/tetris.nas
@ ./nasal -o -c -t -d test/turingmachine.nas
@ ./nasal -o -c -t -d test/ycombinator.nas
@ ./nasal -o -d test/wavecollapse.nas

View File

@@ -6,22 +6,24 @@ 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){
var fib(var* args,usize size,gc* ngc){
std::cout<<"[mod] this is the first test module of nasal\n";
if(!args.size())
return builtin_err("fib","lack arguments");
nasal_ref num=args[0];
if(!size)
return nas_err("fib","lack arguments");
var 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){
var quick_fib(var* args,usize size,gc* ngc){
std::cout<<"[mod] this is the first test module of nasal\n";
if(!args.size())
return builtin_err("fib","lack arguments");
nasal_ref num=args[0];
if(!size)
return nas_err("fib","lack arguments");
var 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;
@@ -31,4 +33,13 @@ extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
b=res;
}
return {vm_num,res};
}
extern "C" mod get(const char* n){
string name=n;
if(name=="fib")
return fib;
else if(name=="quick_fib")
return quick_fib;
return nullptr;
}

View File

@@ -1,4 +1,5 @@
#include "../nasal.h"
#include <unistd.h>
#include <iostream>
#ifdef _WIN32
#include <conio.h>
@@ -7,68 +8,88 @@
#include <termios.h>
#endif
class noecho_input{
private:
#ifndef _WIN32
static struct termios init_termios;
static struct termios new_termios;
static int peek_char=-1;
int kbhit(){
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;
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
}
return 0;
}
int getch(){
int ch=0;
if(peek_char!=-1){
ch=peek_char;
peek_char=-1;
~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;
}
ssize_t tmp=read(0,&ch,1);
return ch;
#else
return getch();
#endif
}
read(0,&ch,1);
return ch;
}
#endif
};
extern "C" nasal_ref nas_getch(std::vector<nasal_ref>& args,nasal_gc& gc){
return {vm_num,(double)getch()};
noecho_input this_window;
var nas_getch(var* args,usize size,gc* ngc){
return {vm_num,(double)this_window.noecho_getch()};
}
extern "C" nasal_ref nas_kbhit(std::vector<nasal_ref>& args,nasal_gc& gc){
return {vm_num,(double)kbhit()};
var nas_kbhit(var* args,usize size,gc* ngc){
return {vm_num,(double)this_window.noecho_kbhit()};
}
extern "C" nasal_ref nas_noblock(std::vector<nasal_ref>& args,nasal_gc& gc){
if(kbhit())
return {vm_num,(double)getch()};
var nas_noblock(var* args,usize size,gc* ngc){
if(this_window.noecho_kbhit())
return {vm_num,(double)this_window.noecho_getch()};
return nil;
}
extern "C" nasal_ref nas_init(std::vector<nasal_ref>& args,nasal_gc& gc){
#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
return nil;
}
extern "C" nasal_ref nas_close(std::vector<nasal_ref>& args,nasal_gc& gc){
#ifndef _WIN32
tcflush(0,TCIOFLUSH);
tcsetattr(0,TCSANOW,&init_termios);
#endif
return nil;
extern "C" mod get(const char* n){
string name=n;
if(name=="nas_getch")
return nas_getch;
else if(name=="nas_kbhit")
return nas_kbhit;
else if(name=="nas_noblock")
return nas_noblock;
return nullptr;
}

View File

@@ -1,12 +1,9 @@
import("lib.nas");
var libfib=func(){
var dl=dylib.dlopen("./module/libfib."~(os.platform()=="windows"?"dll":"so"));
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
{
return {
fib: func(x){return call(fib,x)},
qfib:func(x){return call(qfib,x)}
};

View File

@@ -1,47 +1,12 @@
import("lib.nas");
var libkey=func(){
var lib=dylib.dlopen("./module/libkey"~(os.platform()=="windows"?".dll":".so"));
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 init=dylib.dlsym(lib,"nas_init");
var cls=dylib.dlsym(lib,"nas_close");
var call=dylib.dlcall;
var is_init=0;
return {
init:func(){
# change io mode to no echo
call(init);
is_init=1;
},
kbhit:func(){
# check if kerboard is hit
# if keyboard is hit this function will return 1
# until getch() gets all the input characters
# and the input flow becomes empty
if(!is_init)
me.init();
return call(kb);
},
getch:func(){
# get input one character without echo
# block until get one input
if(!is_init)
me.init();
return call(gt);
},
nonblock:func(){
# nonblock input without echo
if(!is_init)
me.init();
return call(nb);
},
close:func(){
# must call this function before exiting the program
# this will change terminal mode to normal io mode
call(cls);
dylib.dlclose(lib);
}
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,23 +1,43 @@
.PHONY=clean all mingw-all
STD=14
libfib.so: fib.cpp
clang++ -c -O3 fib.cpp -fPIC -o fib.o
clang++ -shared -o libfib.so fib.o
rm fib.o
@ echo "[build] libfib.so"
@ $(CXX) -std=c++$(STD) -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
@ echo [build] libfib.dll
@ $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o
@ $(CXX) -shared -o libfib.dll fib.o
@ del fib.o
libkey.so: keyboard.cpp
clang++ -c -O3 keyboard.cpp -fPIC -o keyboard.o
clang++ -shared -o libkey.so keyboard.o
rm keyboard.o
@ echo "[build] libkey.so"
@ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
@ $(CXX) -shared -o libkey.so keyboard.o
@ rm keyboard.o
libkey.dll: keyboard.cpp
g++ -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
g++ -shared -o libkey.dll keyboard.o -static
@ echo [build] libkey.dll
@ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
@ $(CXX) -shared -o libkey.dll keyboard.o -static
@ del keyboard.o
libnasock.so: nasocket.cpp
@ echo "[build] libnasock.so"
@ $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
@ $(CXX) -shared -o libnasock.so nasocket.o
@ rm nasocket.o
libnasock.dll: nasocket.cpp
@ echo [build] libnasock.dll
@ $(CXX) -std=c++$(STD) -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
all: libfib.so libkey.so
@ echo "build done"
mingw-all: libfib.dll libkey.dll
@ echo "build done"
-@ 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

217
module/nasocket.cpp Normal file
View File

@@ -0,0 +1,217 @@
#include "../nasal.h"
#include <unistd.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
var nas_socket(var* args,usize size,gc* ngc){
if(args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
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};
}
var nas_closesocket(var* args,usize size,gc* ngc){
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
}
var nas_shutdown(var* args,usize size,gc* ngc){
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())};
}
var nas_bind(var* args,usize size,gc* ngc){
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))};
}
var nas_listen(var* args,usize size,gc* ngc){
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())};
}
var nas_connect(var* args,usize size,gc* ngc){
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))};
}
var nas_accept(var* args,usize size,gc* ngc){
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
var res=ngc->temp=ngc->alloc(vm_hash);
auto& hash=res.hash().elems;
hash["sd"]={vm_num,(double)client_sd};
hash["ip"]=ngc->newstr(inet_ntoa(client.sin_addr));
ngc->temp=nil;
return res;
}
var nas_send(var* args,usize size,gc* ngc){
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())};
}
var nas_sendto(var* args,usize size,gc* ngc){
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))};
}
var nas_recv(var* args,usize size,gc* ngc){
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");
var res=ngc->temp=ngc->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"]=ngc->newstr(buf);
delete[] buf;
ngc->temp=nil;
return res;
}
var nas_recvfrom(var* args,usize size,gc* ngc){
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);
var res=ngc->temp=ngc->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"]=ngc->newstr(buf);
delete[] buf;
hash["fromip"]=ngc->newstr(inet_ntoa(addr.sin_addr));
ngc->temp=nil;
return res;
}
var nas_errno(var* args,usize size,gc* ngc){
return ngc->newstr(strerror(errno));
}
extern "C" mod get(const char* n){
string name=n;
if(name=="nas_socket")
return nas_socket;
else if(name=="nas_closesocket")
return nas_closesocket;
else if(name=="nas_shutdown")
return nas_shutdown;
else if(name=="nas_bind")
return nas_bind;
else if(name=="nas_listen")
return nas_listen;
else if(name=="nas_connect")
return nas_connect;
else if(name=="nas_accept")
return nas_accept;
else if(name=="nas_send")
return nas_send;
else if(name=="nas_sendto")
return nas_sendto;
else if(name=="nas_recv")
return nas_recv;
else if(name=="nas_recvfrom")
return nas_recvfrom;
else if(name=="nas_errno")
return nas_errno;
return nullptr;
}

188
nasal.h
View File

@@ -1,82 +1,59 @@
#ifndef __NASAL_H__
#define __NASAL_H__
#pragma once
#define __nasver "9.0"
#include <unistd.h>
#ifndef __nasver
#define __nasver "10.1"
#endif
#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <list>
#include <stack>
#include <queue>
#include <vector>
#include <unordered_map>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
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;
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/wait.h>
#endif
const u32 STACK_DEPTH=1024;
#ifdef __linux__
#define PRTHEX64 "%lx"
#define PRTHEX64_8 "%.8lx"
#define PRTINT64 "%ld"
#else
#define PRTHEX64 "%llx"
#define PRTHEX64_8 "%.8llx"
#define PRTINT64 "%lld"
#endif
inline double hex_to_double(const char* str)
inline f64 hex2f(const char* str)
{
double ret=0;
f64 ret=0;
for(;*str;++str)
{
ret*=16;
if('0'<=*str && *str<='9')
ret+=(*str-'0');
ret=ret*16+(*str-'0');
else if('a'<=*str && *str<='f')
ret+=(*str-'a'+10);
ret=ret*16+(*str-'a'+10);
else if('A'<=*str && *str<='F')
ret+=(*str-'A'+10);
ret=ret*16+(*str-'A'+10);
else
return nan("");
}
return ret;
}
inline double oct_to_double(const char* str)
inline f64 oct2f(const char* str)
{
double ret=0;
for(;*str;++str)
{
ret*=8;
if('0'<=*str && *str<'8')
ret+=(*str-'0');
else
return nan("");
}
f64 ret=0;
while('0'<=*str && *str<'8')
ret=ret*8+(*str++-'0');
if(*str) return nan("");
return ret;
}
inline double dec_to_double(const char* str)
// we have the same reason not using atof here just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3, not another result that you may get in other languages.
inline f64 dec2f(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;
@@ -98,98 +75,81 @@ inline double dec_to_double(const char* str)
negative=(*str++=='-'? -1:1);
if(!*str) return nan("");
num_pow=0;
for(;*str;++str)
{
if('0'<=*str && *str<='9')
num_pow=num_pow*10+(*str-'0');
else
return nan("");
}
while('0'<=*str && *str<='9')
num_pow=num_pow*10+(*str++-'0');
if(*str) return nan("");
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=hex2f(str+2);
else if(str[0]=='0' && str[1]=='o')
ret_num=oct_to_double(str+2);
res=oct2f(str+2);
else
ret_num=dec_to_double(str);
return is_negative?-ret_num:ret_num;
res=dec2f(str);
return negative?-res:res;
}
int utf8_hdchk(char head)
i32 utf8_hdchk(const char head)
{
// RFC-2279 but in fact now we use RFC-3629 so nbytes is less than 4
uint8_t c=(uint8_t)head;
uint32_t nbytes=0;
// 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
nbytes=1;
return 1;
if((c>>4)==0x0e) // 1110 xxxx (10xx xxxx)^2
nbytes=2;
return 2;
if((c>>3)==0x1e) // 1111 0xxx (10xx xxxx)^3
nbytes=3;
// these should not be true
if((c>>2)==0x3e) // 1111 10xx (10xx xxxx)^4
nbytes=4;
if((c>>1)==0x7e) // 1111 110x (10xx xxxx)^5
nbytes=5;
if(c==0xfe) // 1111 1110 (10xx xxxx)^6
nbytes=6;
return nbytes;
return 3;
return 0;
}
std::string rawstr(const std::string& str)
string chrhex(const char c)
{
std::string ret("");
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, so we output the hex
// if 'chcp65001' is not enabled, we output the hex
if(i<=0)
{
ret+="\\x";
ret+="0123456789abcdef"[(i>>4)&15];
ret+="0123456789abcdef"[i&15];
ret+="\\x"+chrhex(i);
continue;
}
#endif
switch(i)
{
case '\0': ret+="\\0"; break;
case '\a': ret+="\\a"; break;
case '\b': ret+="\\b"; break;
case '\e': ret+="\\e"; 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;
case '\\': ret+="\\\\";break;
case '\'': ret+="\\\'";break;
case '\"': ret+="\\\"";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;
case '\033':ret+="\\e"; break;
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"
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_opt.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"
#endif
#include "nasal_gc.h" // declarations of var and nasal_gc

View File

@@ -1,7 +1,9 @@
#ifndef __NASAL_AST_H__
#define __NASAL_AST_H__
#pragma once
enum ast_node
#include <vector>
#include <cstring>
enum ast_node:u32
{
ast_null=0, // null node
ast_root, // mark the root node of ast
@@ -50,12 +52,12 @@ enum ast_node
ast_foreach, // foreach keyword
ast_while, // while
ast_iter, // iterator, used in forindex/foreach
ast_conditional, // mark a sub-tree of conditional expression
ast_cond, // mark a sub-tree of conditional expression
ast_if, // if keyword
ast_elsif, // elsif keyword
ast_else, // else keyword
ast_multi_id, // multi identifiers sub-tree
ast_multi_scalar,// multi value sub-tree
ast_tuple, // tuple, only used in multiple assignment
ast_def, // definition
ast_multi_assign,// multi assignment sub-tree
ast_continue, // continue keyword, only used in loop
@@ -76,7 +78,7 @@ const char* ast_name[]=
"func",
"hash",
"vec",
"hashmember",
"pair",
"call",
"callh",
"callv",
@@ -104,144 +106,151 @@ const char* ast_name[]=
"*",
"/",
"~",
"unary-",
"unary!",
"neg",
"!",
"trino",
"for",
"forindex",
"foreach",
"while",
"iter",
"conditional",
"cond",
"if",
"elsif",
"else",
"multi-id",
"multi-scalar",
"tuple",
"def",
"multi-assign",
"continue",
"break",
"return",
nullptr
"return"
};
class nasal_ast
class ast
{
private:
uint32_t _line;
uint32_t _type;
double _num;
std::string _str;
std::vector<nasal_ast> _child;
u32 nd_line;
u32 nd_col;
u32 nd_type;
f64 nd_num;
string nd_str;
std::vector<ast> nd_child;
public:
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):_line(l),_type(t),_num(0){}
nasal_ast(const nasal_ast&);
nasal_ast(nasal_ast&&);
void print(int,bool);
ast(const u32 l,const u32 c,const u32 t):
nd_line(l),nd_col(c),nd_type(t),nd_num(0){}
ast(const ast&);
ast(ast&&);
void print_tree();
void print(u32,bool,std::vector<string>&);
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();}
ast& operator=(const ast&);
ast& operator=(ast&&);
ast& operator[](usize n){return nd_child[n];}
const ast& operator[](usize n) const {return nd_child[n];}
usize size() const {return nd_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 add(ast&& node){nd_child.push_back(std::move(node));}
void add(const ast& node){nd_child.push_back(node);}
void set_line(const u32 l){nd_line=l;}
void set_type(const u32 t){nd_type=t;}
void set_str(const string& s){nd_str=s;}
void set_num(const f64 n){nd_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 const std::vector<nasal_ast>& child() const {return _child;}
inline std::vector<nasal_ast>& child(){return _child;}
inline u32 line() const {return nd_line;}
inline u32 col() const {return nd_col;}
inline u32 type() const {return nd_type;}
inline f64 num() const {return nd_num;}
inline const string& str() const {return nd_str;}
inline const std::vector<ast>& child() const {return nd_child;}
inline std::vector<ast>& child(){return nd_child;}
};
nasal_ast::nasal_ast(const nasal_ast& tmp):
_str(tmp._str),_child(tmp._child)
ast::ast(const ast& tmp):
nd_str(tmp.nd_str),nd_child(tmp.nd_child)
{
_line=tmp._line;
_type=tmp._type;
_num =tmp._num;
nd_line=tmp.nd_line;
nd_col=tmp.nd_col;
nd_type=tmp.nd_type;
nd_num =tmp.nd_num;
}
nasal_ast::nasal_ast(nasal_ast&& tmp)
ast::ast(ast&& tmp)
{
_line=tmp._line;
_type=tmp._type;
_num =tmp._num;
_str.swap(tmp._str);
_child.swap(tmp._child);
nd_line=tmp.nd_line;
nd_col=tmp.nd_col;
nd_type=tmp.nd_type;
nd_num =tmp.nd_num;
nd_str.swap(tmp.nd_str);
nd_child.swap(tmp.nd_child);
}
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
ast& ast::operator=(const ast& tmp)
{
_line=tmp._line;
_type=tmp._type;
_num=tmp._num;
_str=tmp._str;
_child=tmp._child;
nd_line=tmp.nd_line;
nd_col=tmp.nd_col;
nd_type=tmp.nd_type;
nd_num=tmp.nd_num;
nd_str=tmp.nd_str;
nd_child=tmp.nd_child;
return *this;
}
nasal_ast& nasal_ast::operator=(nasal_ast&& tmp)
ast& ast::operator=(ast&& tmp)
{
_line=tmp._line;
_type=tmp._type;
_num=tmp._num;
_str.swap(tmp._str);
_child.swap(tmp._child);
nd_line=tmp.nd_line;
nd_col=tmp.nd_col;
nd_type=tmp.nd_type;
nd_num=tmp.nd_num;
nd_str.swap(tmp.nd_str);
nd_child.swap(tmp.nd_child);
return *this;
}
void nasal_ast::clear()
void ast::clear()
{
_line=0;
_num=0;
_str="";
_type=ast_null;
_child.clear();
nd_line=nd_col=0;
nd_num=0;
nd_str.clear();
nd_type=ast_null;
nd_child.clear();
}
void nasal_ast::print(int depth,bool last=false)
void ast::print_tree()
{
static std::vector<std::string> intentation={};
for(auto& i:intentation)
std::vector<string> tmp;
print(0,false,tmp);
}
void ast::print(u32 depth,bool last,std::vector<string>& indent)
{
for(auto& i:indent)
std::cout<<i;
std::cout<<ast_name[_type];
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;
std::cout<<'\n';
std::cout<<ast_name[nd_type];
if(nd_type==ast_str || nd_type==ast_id ||
nd_type==ast_default || nd_type==ast_dynamic ||
nd_type==ast_callh)
std::cout<<":"<<rawstr(nd_str);
else if(nd_type==ast_num || nd_type==ast_file)
std::cout<<":"<<nd_num;
std::cout<<"\n";
if(last && depth)
intentation.back()=" ";
indent.back()=" ";
else if(!last && depth)
#ifdef _WIN32
intentation.back()="| ";
indent.back()="| ";
#else
intentation.back()="";
indent.back()="";
#endif
for(uint32_t i=0;i<_child.size();++i)
for(u32 i=0;i<nd_child.size();++i)
{
#ifdef _WIN32
intentation.push_back(i==_child.size()-1?"`-":"|-");
indent.push_back(i==nd_child.size()-1?"+-":"|-");
#else
intentation.push_back(i==_child.size()-1?"└─":"├─");
indent.push_back(i==nd_child.size()-1?"└─":"├─");
#endif
_child[i].print(depth+1,i==_child.size()-1);
intentation.pop_back();
nd_child[i].print(depth+1,i==nd_child.size()-1,indent);
indent.pop_back();
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,109 +1,143 @@
#ifndef __NASAL_DBG_H__
#define __NASAL_DBG_H__
#pragma once
#include "nasal_err.h"
#include "nasal_vm.h"
#include <algorithm>
class nasal_dbg:public nasal_vm
class debugger:public 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;
error& 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 callsort(const u64*);
void stepinfo();
void interact();
public:
nasal_dbg():
next_step(false),
bk_fidx(0),bk_line(0){}
debugger(error& err):
next(false),fsize(0),
bk_fidx(0),bk_line(0),
src(err){}
void run(
const nasal_codegen&,
const nasal_import&
const codegen&,
const linker&,
const std::vector<string>&
);
};
std::vector<std::string> nasal_dbg::parse(const std::string& cmd)
std::vector<string> debugger::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 debugger::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;
}
void nasal_dbg::err()
void debugger::err()
{
std::cerr
<<"incorrect command\n"
<<"input \'h\' to get help\n";
}
void nasal_dbg::help()
void debugger::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"
<<"\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"
<<"\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::stepinfo()
void debugger::callsort(const u64* arr)
{
uint32_t begin,end;
uint32_t line=bytecode[pc].line==0?0:bytecode[pc].line-1;
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 debugger::stepinfo()
{
u32 line=bytecode[pc].line==0?0:bytecode[pc].line-1;
u32 begin=(line>>3)==0?0:((line>>3)<<3);
u32 end=(1+(line>>3))<<3;
src.load(files[bytecode[pc].fidx]);
printf("\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");
std::cout<<"\nsource code:\n";
for(u32 i=begin;i<end && i<src.size();++i)
std::cout<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\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)
std::cout
<<(i==pc?back_white:reset)<<(i==pc?"--> ":" ")
<<codestream(bytecode[i],i,cnum,cstr,files)
<<reset<<"\n";
stackinfo(10);
}
void nasal_dbg::interact()
void debugger::interact()
{
// special operand
if(bytecode[pc].op==op_intg)
{
std::cout
<<"[debug] nasal debug mode\n"
<<bold_cyan<<"[debug] "<<reset
<<"nasal debug mode\n"
<<"input \'h\' to get help\n";
}
else if(bytecode[pc].op==op_exit)
@@ -111,15 +145,15 @@ void nasal_dbg::interact()
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)
@@ -131,19 +165,21 @@ void nasal_dbg::interact()
else if(res[0]=="c" || res[0]=="continue")
return;
else if(res[0]=="f" || res[0]=="file")
for(size_t i=0;i<files_size;++i)
printf("[%zu] %s\n",i,files[i].c_str());
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")
detail();
else if(res[0]=="n" || res[0]=="next")
{
next_step=true;
next=true;
return;
}
else if(res[0]=="q" || res[0]=="exit")
@@ -153,15 +189,15 @@ void nasal_dbg::interact()
}
else if(res.size()==3 && (res[0]=="bk" || res[0]=="break"))
{
bk_fidx=get_fileindex(res[1]);
bk_fidx=fileindex(res[1]);
if(bk_fidx==65535)
{
printf("cannot find file named \"%s\"\n",res[1].c_str());
std::cout<<"cannot find file named `"<<res[1]<<"`\n";
bk_fidx=0;
}
int tmp=atoi(res[2].c_str());
i32 tmp=atoi(res[2].c_str());
if(tmp<=0)
printf("incorrect line number \"%s\"\n",res[2].c_str());
std::cout<<"incorrect line number `"<<res[2]<<"`\n";
else
bk_line=tmp;
}
@@ -170,126 +206,195 @@ void nasal_dbg::interact()
}
}
void nasal_dbg::run(
const nasal_codegen& gen,
const nasal_import& linker)
void debugger::run(
const codegen& gen,
const linker& linker,
const std::vector<string>& argv)
{
detail_info=true;
init(gen.get_strs(),gen.get_nums(),gen.get_code(),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[]=
{
&&vmexit, &&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, &&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
};
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);
}
// goto the first operand
goto *code[pc];
#else
typedef void (debugger::*nafunc)();
const nafunc oprs[]=
{
nullptr, &debugger::o_intg,
&debugger::o_intl, &debugger::o_loadg,
&debugger::o_loadl, &debugger::o_loadu,
&debugger::o_pnum, &debugger::o_pnil,
&debugger::o_pstr, &debugger::o_newv,
&debugger::o_newh, &debugger::o_newf,
&debugger::o_happ, &debugger::o_para,
&debugger::o_deft, &debugger::o_dyn,
&debugger::o_unot, &debugger::o_usub,
&debugger::o_add, &debugger::o_sub,
&debugger::o_mul, &debugger::o_div,
&debugger::o_lnk, &debugger::o_addc,
&debugger::o_subc, &debugger::o_mulc,
&debugger::o_divc, &debugger::o_lnkc,
&debugger::o_addeq, &debugger::o_subeq,
&debugger::o_muleq, &debugger::o_diveq,
&debugger::o_lnkeq, &debugger::o_addeqc,
&debugger::o_subeqc, &debugger::o_muleqc,
&debugger::o_diveqc, &debugger::o_lnkeqc,
&debugger::o_meq, &debugger::o_eq,
&debugger::o_neq, &debugger::o_less,
&debugger::o_leq, &debugger::o_grt,
&debugger::o_geq, &debugger::o_lessc,
&debugger::o_leqc, &debugger::o_grtc,
&debugger::o_geqc, &debugger::o_pop,
&debugger::o_jmp, &debugger::o_jt,
&debugger::o_jf, &debugger::o_cnt,
&debugger::o_findex, &debugger::o_feach,
&debugger::o_callg, &debugger::o_calll,
&debugger::o_upval, &debugger::o_callv,
&debugger::o_callvi, &debugger::o_callh,
&debugger::o_callfv, &debugger::o_callfh,
&debugger::o_callb, &debugger::o_slcbeg,
&debugger::o_slcend, &debugger::o_slc,
&debugger::o_slc2, &debugger::o_mcallg,
&debugger::o_mcalll, &debugger::o_mupval,
&debugger::o_mcallv, &debugger::o_mcallh,
&debugger::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)
die("stack overflow");
++pc;
}
#endif
vmexit:
if(gc.top>=canary)
die("stack overflow");
gc.clear();
callsort(count);
ngc.clear();
imm.clear();
printf("[debug] debugger exited\n");
std::cout<<bold_cyan<<"[debug] "<<reset<<"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];\
die("stack overflow");\
goto *code[++pc];\
}
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

@@ -1,78 +1,160 @@
#ifndef __NASAL_ERR_H__
#define __NASAL_ERR_H__
#pragma once
#include <iostream>
#include <fstream>
#include <sstream> // MSVC need this to use std::getline
#include <cstring>
class file_line
#ifdef _WIN32
#include <windows.h> // use SetConsoleTextAttribute
struct for_reset
{
CONSOLE_SCREEN_BUFFER_INFO scr;
for_reset(){
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&scr);
}
}reset_ter_color;
#endif
std::ostream& back_white(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0xf0);
#else
s<<"\033[7m";
#endif
return s;
}
std::ostream& bold_red(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c);
#else
s<<"\033[91;1m";
#endif
return s;
}
std::ostream& bold_cyan(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03);
#else
s<<"\033[36;1m";
#endif
return s;
}
std::ostream& bold_orange(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0e);
#else
s<<"\033[93;1m";
#endif
return s;
}
std::ostream& bold_white(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0f);
#else
s<<"\033[0m\033[1m";
#endif
return s;
}
std::ostream& reset(std::ostream& s)
{
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),reset_ter_color.scr.wAttributes);
#else
s<<"\033[0m";
#endif
return s;
}
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;
if(file==f) return; // don't need to load a loaded file
file=f;
res.clear();
std::ifstream fin(f,std::ios::binary);
if(fin.fail())
std::ifstream in(f,std::ios::binary);
if(in.fail())
{
std::cerr<<"[src] cannot open file <"<<f<<">\n";
std::cerr<<bold_red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::exit(1);
}
std::string line;
while(!fin.eof())
string line;
while(!in.eof())
{
std::getline(fin,line);
std::getline(in,line);
res.push_back(line);
}
}
void clear()
{
std::vector<std::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 error:public fstreamline
{
private:
uint32_t error;
public:
nasal_err():error(0){}
void err(const char* stage,const std::string& info,const char end='\n')
u32 cnt;
string identation(usize len)
{
++error;
std::cerr<<"["<<stage<<"] "<<info<<end;
string tmp="";
tmp.resize(len,' ');
return tmp;
}
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
public:
error():cnt(0){}
void err(const char* stage,const string& info)
{
++error;
++cnt;
std::cerr<<bold_red<<stage<<": "
<<bold_white<<info<<reset<<"\n\n";
}
void err(const char* stage,u32 line,u32 column,const string& info)
{
++cnt;
const string& code=res[line-1];
const string ident=identation(std::to_string(line).length());
std::cerr<<bold_red<<stage<<": "
<<bold_white<<info<<reset<<"\n"
<<bold_cyan<<" --> "<<reset
<<bold_orange<<file<<":"<<line<<":"<<column<<"\n";
if(!line)
{
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
std::cerr<<"\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']);
std::cerr<<"^\n";
std::cerr<<bold_cyan<<ident<<" | "<<reset<<"\n"
<<bold_cyan<<line<<" | "<<reset<<code<<"\n"
<<bold_cyan<<ident<<" | "<<reset;
for(i32 i=0;i<(i32)column-1;++i)
std::cerr<<char(" \t"[code[i]=='\t']);
std::cerr<<bold_red<<"^ "<<info<<reset<<"\n\n";
}
void err(const char* stage,uint32_t line,const std::string& info)
void err(const char* stage,u32 line,const string& info)
{
++error;
++cnt;
const string ident=identation(std::to_string(line).length());
std::cerr<<bold_red<<stage<<": "
<<bold_white<<info<<reset<<"\n"
<<bold_cyan<<" --> "<<reset
<<bold_orange<<file<<":"<<line<<"\n";
if(!line)
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
else
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
{
std::cerr<<"\n";
return;
}
std::cerr<<bold_cyan<<ident<<" | "<<reset<<"\n"
<<bold_cyan<<line<<" | "<<reset<<res[line-1]<<"\n"
<<bold_cyan<<ident<<" | "<<reset<<"\n\n";
}
void chkerr(){if(error)std::exit(1);}
void chkerr(){if(cnt)std::exit(1);}
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,122 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
#pragma once
class nasal_import
#ifndef _MSC_VER
#include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#endif
#ifdef _MSC_VER
#define F_OK 0
#endif
class linker
{
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&);
void linker(nasal_ast&,nasal_ast&&);
nasal_ast file_import(nasal_ast&);
nasal_ast lib_import();
nasal_ast load(nasal_ast&,uint16_t);
error& err;
std::vector<string> files;
std::vector<string> envpath;
bool imptchk(const ast&);
bool exist(const string&);
void link(ast&,ast&&);
string path(const ast&);
string findf(const string&);
ast fimpt(ast&);
ast libimpt();
ast load(ast&,u16);
public:
nasal_import(nasal_err& e):lib_loaded(false),nerr(e){}
void link(nasal_parse&,const std::string&);
const std::vector<std::string>& get_file() const {return files;}
linker(error&);
void link(parse&,const string&,bool);
const std::vector<string>& filelist() const {return files;}
};
bool nasal_import::check_import(const nasal_ast& node)
linker::linker(error& e):show_path(false),lib_loaded(false),err(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 linker::path(const 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 linker::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)
{
err.err("link","cannot find file <"+fname+">");
return "";
}
string paths="";
for(auto& i:filepath)
paths+=" "+i+"\n";
err.err("link","cannot find file <"+fname+"> in these paths:\n"+paths);
return "";
}
bool linker::imptchk(const 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
@@ -30,15 +124,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 linker::exist(const string& file)
{
// avoid importing the same file
for(auto& fname:files)
@@ -48,104 +142,84 @@ bool nasal_import::check_exist(const std::string& file)
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
void linker::link(ast& root,ast&& add_root)
{
// add children of add_root to the back of root
for(auto& i:add_root.child())
root.add(std::move(i));
}
nasal_ast nasal_import::file_import(nasal_ast& node)
ast linker::fimpt(ast& node)
{
nasal_lexer lex(nerr);
nasal_parse par(nerr);
lexer lex(err);
parse par(err);
// 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))
return {0,ast_root};
if(access(filename.c_str(),F_OK)==-1)
{
nerr.err("link","cannot open file <"+filename+">");
return {0,ast_root};
}
filename=findf(filename);
if(!filename.length() || exist(filename))
return {0,0,ast_root};
// start importing...
lex.scan(filename);
par.compile(lex);
nasal_ast tmp=std::move(par.ast());
ast tmp=std::move(par.tree());
// check if tmp has 'import'
return load(tmp,files.size()-1);
}
nasal_ast nasal_import::lib_import()
ast linker::libimpt()
{
nasal_lexer lex(nerr);
nasal_parse par(nerr);
const std::vector<std::string> libpath=
{
"lib.nas",
"stl/lib.nas"
};
std::string filename="";
for(auto& i:libpath)
if(access(i.c_str(),F_OK)!=-1)
{
filename=i;
break;
}
lexer lex(err);
parse par(err);
string filename=findf("lib.nas");
if(!filename.length())
{
std::string paths="";
for(auto& i:libpath)
paths+=" "+i+"\n";
nerr.err("link","cannot find lib file in these paths:\n"+paths,' ');
nerr.chkerr();
return {0,ast_root};
}
return {0,0,ast_root};
// avoid infinite loading loop
if(check_exist(filename))
return {0,ast_root};
if(exist(filename))
return {0,0,ast_root};
// start importing...
lex.scan(filename);
par.compile(lex);
nasal_ast tmp=std::move(par.ast());
ast tmp=std::move(par.tree());
// check if tmp has 'import'
return load(tmp,files.size()-1);
}
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
ast linker::load(ast& root,u16 fileindex)
{
nasal_ast new_root(0,ast_root);
ast new_root(0,0,ast_root);
if(!lib_loaded)
{
linker(new_root,lib_import());
link(new_root,libimpt());
lib_loaded=true;
}
for(auto& i:root.child())
if(check_import(i))
linker(new_root,file_import(i));
{
if(imptchk(i))
link(new_root,fimpt(i));
else
break;
}
// add root to the back of new_root
nasal_ast file_head(0,ast_file);
ast file_head(0,0,ast_file);
file_head.set_num(fileindex);
new_root.add(std::move(file_head));
linker(new_root,std::move(root));
link(new_root,std::move(root));
return new_root;
}
void nasal_import::link(nasal_parse& parse,const std::string& self)
void linker::link(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);
parse.tree()=load(parse.tree(),0);
err.chkerr();
}
#endif

View File

@@ -1,44 +1,69 @@
#ifndef __NASAL_LEXER_H__
#define __NASAL_LEXER_H__
#pragma once
#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')
#define STR(c) (c=='\''||c=='\"'||c=='`')
// single operators have only one character
#define SINGLE_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
#define CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
#define NOTE(c) (c=='#')
#include <sstream>
#include <sys/stat.h>
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,
tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve,
tok_lbracket,tok_rbracket,
tok_lbrace,tok_rbrace,
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
tok_eq,
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
tok_eof // end of token list
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
enum tok:u32{
tok_null=0, // null token (default token type)
tok_num, // number literal
tok_str, // string literal
tok_id, // identifier
tok_for, // loop keyword for
tok_forindex,// loop keyword forindex
tok_foreach, // loop keyword foreach
tok_while, // loop keyword while
tok_var, // keyword for definition
tok_func, // keyword for definition of function
tok_break, // loop keyword break
tok_continue,// loop keyword continue
tok_ret, // function keyword return
tok_if, // condition expression keyword if
tok_elsif, // condition expression keyword elsif
tok_else, // condition expression keyword else
tok_nil, // nil literal
tok_lcurve, // (
tok_rcurve, // )
tok_lbracket,// [
tok_rbracket,// ]
tok_lbrace, // {
tok_rbrace, // }
tok_semi, // ;
tok_and, // operator and
tok_or, // operator or
tok_comma, // ,
tok_dot, // .
tok_ellipsis,// ...
tok_quesmark,// ?
tok_colon, // :
tok_add, // operator +
tok_sub, // operator -
tok_mult, // operator *
tok_div, // operator /
tok_link, // operator ~
tok_not, // operator !
tok_eq, // operator =
tok_addeq, // operator +=
tok_subeq, // operator -=
tok_multeq, // operator *=
tok_diveq, // operator /=
tok_lnkeq, // operator ~=
tok_cmpeq, // operator ==
tok_neq, // operator !=
tok_less, // operator <
tok_leq, // operator <=
tok_grt, // operator >
tok_geq, // operator >=
tok_eof // <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,97 +114,157 @@ 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,const std::string& s=""):str(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;
}
};
class nasal_lexer
class 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;
error& err;
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 utf8_gen();
std::string id_gen();
std::string num_gen();
std::string str_gen();
u32 get_type(const string&);
bool is_id(char);
bool is_hex(char);
bool is_oct(char);
bool is_dec(char);
bool is_str(char);
bool is_single_opr(char);
bool is_calc_opr(char);
void die(const string& info){err.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):line(0),column(0),ptr(0),nerr(e){}
void scan(const std::string&);
lexer(error& e):
line(1),column(0),
ptr(0),res(""),
err(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)
bool lexer::is_id(char c)
{
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
}
bool lexer::is_hex(char c)
{
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
}
bool lexer::is_oct(char c)
{
return '0'<=c&&c<='7';
}
bool lexer::is_dec(char c)
{
return '0'<=c&&c<='9';
}
bool lexer::is_str(char c)
{
return c=='\''||c=='\"'||c=='`';
}
bool lexer::is_single_opr(char c)
{
return (
c=='('||c==')'||c=='['||c==']'||
c=='{'||c=='}'||c==','||c==';'||
c=='|'||c==':'||c=='?'||c=='`'||
c=='&'||c=='@'||c=='%'||c=='$'||
c=='^'||c=='\\'
);
}
bool lexer::is_calc_opr(char c)
{
return c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~';
}
void 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.");
res="";
return;
err.err("lexer","<"+file+"> is not a regular file");
err.chkerr();
}
std::ifstream fin(file,std::ios::binary);
if(fin.fail())
nerr.err("lexer","failed to open <"+file+">.");
err.err("lexer","failed to open <"+file+">");
else
nerr.load(file);
err.load(file);
std::stringstream ss;
ss<<fin.rdbuf();
res=ss.str();
}
uint32_t nasal_lexer::get_type(const std::string& tk_str)
u32 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::utf8_gen()
string lexer::utf8_gen()
{
std::string str="";
string str="";
while(ptr<res.size() && res[ptr]<0)
{
std::string tmp="";
uint32_t nbytes=utf8_hdchk(res[ptr]);
string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
if(nbytes)
{
tmp+=res[ptr++];
for(uint32_t i=0;i<nbytes;++i,++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)
die("invalid utf-8 character here");
{
++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;
column+=2; // may have some problems because not all the unicode takes 2 space
}
else
{
++ptr;
++column;
}
}
return str;
}
std::string nasal_lexer::id_gen()
string lexer::id_gen()
{
std::string str="";
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
string str="";
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr])))
{
if(res[ptr]<0) // utf-8
str+=utf8_gen();
@@ -192,47 +277,47 @@ std::string nasal_lexer::id_gen()
return str;
}
std::string nasal_lexer::num_gen()
string 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]))
while(ptr<res.size() && is_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]))
while(ptr<res.size() && is_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="";
while(ptr<res.size() && DIGIT(res[ptr]))
string str="";
while(ptr<res.size() && is_dec(res[ptr]))
str+=res[ptr++];
if(ptr<res.size() && res[ptr]=='.')
{
str+=res[ptr++];
while(ptr<res.size() && DIGIT(res[ptr]))
while(ptr<res.size() && is_dec(res[ptr]))
str+=res[ptr++];
// "xxxx." is not a correct number
if(str.back()=='.')
{
column+=str.length();
die("invalid number:"+str);
die("invalid number `"+str+"`");
return "0";
}
}
@@ -241,13 +326,13 @@ std::string nasal_lexer::num_gen()
str+=res[ptr++];
if(ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+'))
str+=res[ptr++];
while(ptr<res.size() && DIGIT(res[ptr]))
while(ptr<res.size() && is_dec(res[ptr]))
str+=res[ptr++];
// "xxxe(-|+)" is not a correct number
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
{
column+=str.length();
die("invalid number:"+str);
die("invalid number `"+str+"`");
return "0";
}
}
@@ -255,10 +340,10 @@ std::string nasal_lexer::num_gen()
return str;
}
std::string nasal_lexer::str_gen()
string 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)
{
@@ -277,7 +362,7 @@ std::string nasal_lexer::str_gen()
case '0': str+='\0'; break;
case 'a': str+='\a'; break;
case 'b': str+='\b'; break;
case 'e': str+='\e'; break;
case 'e': str+='\033'; break;
case 't': str+='\t'; break;
case 'n': str+='\n'; break;
case 'v': str+='\v'; break;
@@ -296,26 +381,26 @@ std::string nasal_lexer::str_gen()
// check if this string ends with a " or '
if(ptr++>=res.size())
{
die("get EOF when generating string.");
die("get EOF when generating string");
return str;
}
++column;
if(begin=='`' && str.length()!=1)
die("\'`\' is used for string that includes one character.");
die("\'`\' is used for string that includes one character");
return str;
}
void nasal_lexer::scan(const std::string& file)
void 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;
@@ -326,29 +411,29 @@ void nasal_lexer::scan(const std::string& file)
}
}
if(ptr>=res.size()) break;
if(ID(res[ptr]))
if(is_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]))
else if(is_dec(res[ptr]))
{
str=num_gen(); // make sure column is correct
tokens.push_back({line,column,tok_num,str});
}
else if(STR(res[ptr]))
else if(is_str(res[ptr]))
{
str=str_gen(); // make sure column is correct
tokens.push_back({line,column,tok_str,str});
}
else if(SINGLE_OPERATOR(res[ptr]))
else if(is_single_opr(res[ptr]))
{
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;
}
@@ -361,7 +446,7 @@ void nasal_lexer::scan(const std::string& file)
column+=str.length();
tokens.push_back({line,column,get_type(str),str});
}
else if(CALC_OPERATOR(res[ptr]))
else if(is_calc_opr(res[ptr]))
{
// get calculation operator
str=res[ptr++];
@@ -370,24 +455,22 @@ void nasal_lexer::scan(const std::string& file)
column+=str.length();
tokens.push_back({line,column,get_type(str),str});
}
else if(NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
else if(res[ptr]=='#')// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n');
else
{
++column;
++ptr;
die("unknown character.");
char c=res[ptr++];
die("invalid character 0x"+chrhex(c));
}
}
tokens.push_back({line,column,tok_eof,"eof"});
tokens.push_back({line,column,tok_eof,"<eof>"});
res="";
nerr.chkerr();
err.chkerr();
}
void nasal_lexer::print()
void 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

@@ -1,7 +1,8 @@
#ifndef __NASAL_OPT_H__
#define __NASAL_OPT_H__
#pragma once
void const_str(nasal_ast& root)
#include <cmath>
void const_str(ast& root)
{
auto& vec=root.child();
root.set_str(vec[0].str()+vec[1].str());
@@ -9,10 +10,10 @@ void const_str(nasal_ast& root)
root.set_type(ast_str);
}
void const_num(nasal_ast& root)
void const_num(ast& root)
{
auto& vec=root.child();
double res;
f64 res;
switch(root.type())
{
case ast_add: res=vec[0].num()+vec[1].num(); break;
@@ -32,14 +33,14 @@ void const_num(nasal_ast& root)
root.set_type(ast_num);
}
void calc_const(nasal_ast& root)
void calc_const(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)
{
double res=-vec[0].num();
f64 res=-vec[0].num();
root.set_num(res);
root.child().clear();
root.set_type(ast_num);
@@ -47,24 +48,21 @@ void calc_const(nasal_ast& root)
}
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)
void optimize(ast& root)
{
for(auto& i:root.child())
calc_const(i);
}
#endif

File diff suppressed because it is too large Load Diff

1525
nasal_vm.h

File diff suppressed because it is too large Load Diff

16
stl/csv.nas Normal file
View File

@@ -0,0 +1,16 @@
# lib csv.nas
# ValKmjolnir 2022/10/15
var read_csv=func(path,delimeter=",",endline="\n"){
var context=io.fin(path);
context=split(endline,context);
forindex(var i;context){
context[i]=split(delimeter,context[i]);
}
if(size(context)<=1){
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
}
return {
property:context[0],
data:context[1:]
};
}

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

View File

@@ -1,12 +1,12 @@
# lib file.nas
# ValKmjolnir 2022/3/6
import("lib.nas");
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);},
@@ -23,4 +23,38 @@ var file={
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;
}
var recursive_find_files=func(path){
if(!io.exists(path))
return nil;
var dd=unix.opendir(path);
var res={
dir:path,
files:[]
};
while(var n=unix.readdir(dd)){
if(unix.isfile(path~"/"~n)){
append(res.files,n);
}elsif(unix.isdir(path~"/"~n) and n!="." and n!=".."){
var tmp=recursive_find_files(path~"/"~n);
if(tmp!=nil)
append(res.files,tmp);
}
}
unix.closedir(dd);
return res;
}

268
stl/json.nas Normal file
View File

@@ -0,0 +1,268 @@
# lib json.nas
# 2021 ValKmjolnir
var JSON=func(){
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"
];
var text='';
var line=1;
var text_size=0;
var ptr=0;
var token={content:'',type:''};
var content={};
var init=func(){
text='';
line=1;
text_size=0;
ptr=0;
content={};
token={content:'',type:''};
}
var isnum=func(c){
return '0'<=c and c<='9';
}
var isid=func(c){
var tmp=c[0];
return ('a'[0]<=tmp and tmp<='z'[0]) or
('A'[0]<=tmp and tmp<='Z'[0]) or
c=='_';
}
var check=func(){
var c=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;
}
var next=func(){
while(ptr<text_size and !check()){
if(text[ptr]=='\n')
line+=1;
ptr+=1;
}
if(ptr>=text_size){
token.content="eof";
token.type=j_eof;
return;
}
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;
}
var match=func(type){
if(token.type!=type)
print("line ",line,": expect ",j_content[type]," but get `",token.content,"`.\n");
next();
return;
}
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;
}
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;
}
};
}();

View File

@@ -1,74 +1,74 @@
# 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 separator for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(separator,str){
return __builtin_split(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.
# int will get the integer of input number/string.
# 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 __builtin_floor(val);
return __floor(val);
}
# exit using std::exit
var exit=func(val=-1){
return __exit(val);
}
# abort using std::abort
var abort=func(){
__builtin_abort();
__abort();
}
# abs gets absolute number.
@@ -79,47 +79,47 @@ var abs=func(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);
@@ -128,18 +128,18 @@ var systime=func(){
# 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 __builtin_find(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
@@ -150,37 +150,37 @@ var subvec=func(vec,begin,length=nil){
# 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.
@@ -196,15 +196,13 @@ var srand=func(){
# values() gets all values in a hash.
var values=func(hash){
return __builtin_values(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){
@@ -267,9 +265,22 @@ var assert=func(condition,message="assertion failed!"){
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 __builtin_md5(str);
return __md5(str);
}
var io=
@@ -278,29 +289,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*.
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
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
@@ -325,35 +338,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.
@@ -361,42 +385,45 @@ var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
abs: func(x) {return x>0?x:-x; },
floor: func(x) {return __builtin_floor(x); },
pow: func(x,y){return __builtin_pow(x,y); },
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); },
max: func(x,y){return x>y?x:y; },
min: func(x,y){return x<y?x:y; }
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(){return __builtin_pipe;},
fork: func(){return __builtin_fork;},
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){return __builtin_waitpid;},
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(){return __builtin_environ();},
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.
@@ -404,13 +431,31 @@ var unix=
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.
@@ -418,35 +463,17 @@ 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");
@@ -466,4 +493,12 @@ var closure=func(function,level=1){
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");
}
};
}();

View File

@@ -1,4 +1,4 @@
# lib module.nas
# module.nas
# ValKmjolnir 2022/3/5
# this provides safe usage of dylib
@@ -6,7 +6,7 @@
# all the invalid functions cannot be called
var module_call_func=func(fptr,args){
return __builtin_dlcall;
return __dlcall;
}
var extern={
new: func(fptr){

20
stl/padding.nas Normal file
View File

@@ -0,0 +1,20 @@
# padding.nas
# ValKmjolnir 2022/9/4
var leftpad=func(s,len,char=" "){
if(typeof(s)=="num")
s=str(s);
var strlen=size(s);
for(var i=strlen;i<len;i+=1)
s=char~s;
return s;
}
var rightpad=func(s,len,char=" "){
if(typeof(s)=="num")
s=str(s);
var strlen=size(s);
for(var i=strlen;i<len;i+=1)
s~=char;
return s;
}

352
stl/process_bar.nas Normal file
View File

@@ -0,0 +1,352 @@
# process_bar.nas
# ValKmjolnir 2022/6/14
# this file is inspired by a Python lib: alive_progress
var process_bar={
bar:nil,
high_resolution_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];
}
};
};
}();
# return a high resolution progress bar
# example:
# var bar=process_bar.high_resolution_bar(40);
# for(var i=0;i<=1;i+=0.001){
# print(bar.bar(i,40),'\r');
# unix.sleep(0.001);
# }
# println();
process_bar.high_resolution_bar=func(){
var block=["▏","▎","▍","▌","▋","▊","▉","█"];
return func(length){
return {
bar: func(number){
if(number>1)
number=1;
if(number<0)
number=0;
var block_len=number*length;
var complete_block=int(block_len);
var decimal=block_len-complete_block;
var progress=complete_block+(decimal!=0);
var s="|";
for(var i=0;i<complete_block;i+=1){
s~="█";
}
if(decimal!=0){
s~=block[int(decimal*10)/10*size(block)];
}
for(var i=0;i<length-progress;i+=1){
s~=" ";
}
s~="|";
return s;
}
};
};
}();
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,28 +1,23 @@
import("lib.nas");
var ResultTrait={
Ok:func(val){
me.ok=val;
me.flag=0;
return me;
},
Err:func(info){
me.err=info;
me.flag=1;
return me;
},
unwrap:func(){
if(me.flag)
die(me.err);
return me.ok;
}
};
# result.nas
# ValKmjolnir 2021
var Result=func(){
var (ok,err,flag)=(nil,"",1);
return{
ok:nil,
err:"",
flag:1,
parents:[ResultTrait]
Ok:func(val){
ok=val;
flag=0;
return me;
},
Err:func(info){
err=info;
flag=1;
return me;
},
unwrap:func(){
if(flag)
die(err);
return ok;
}
};
};

View File

@@ -1,20 +1,30 @@
# lib sort.nas
# sort.nas
# valkmjolnir 2021/4/2
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 and cmp(tmp,vec[right]))
right-=1;
while(left<right and cmp(vec[left],tmp))
left+=1;
if(left!=right)
(vec[left],vec[right])=(vec[right],vec[left]);
# please make sure the compare function has the function like <= or >=
# only using < or > may cause infinite loop or the program may crash
var sort=func(){
srand(); # be aware! this causes global changes
var quick_sort_core=func(vec,left,right,cmp){
if(left>=right) return nil;
var base=left+int(rand()*(right-left));
(vec[left],vec[base])=(vec[base],vec[left]);
var (i,j,tmp)=(left,right,vec[left]);
while(i!=j){
while(i<j and cmp(tmp,vec[j]))
j-=1;
vec[i]=vec[j];
while(i<j and cmp(vec[i],tmp))
i+=1;
vec[j]=vec[i];
}
vec[i]=tmp;
quick_sort_core(vec,left,i-1,cmp);
quick_sort_core(vec,i+1,right,cmp);
return nil;
}
(vec[L],vec[left])=(vec[left],tmp);
sort(vec,L,left-1,cmp);
sort(vec,left+1,R,cmp);
return nil;
}
return func(vec,cmp=func(a,b){return a<=b;}){
quick_sort_core(vec,0,size(vec)-1,cmp);
return nil;
}
}();

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;
}
};
}

9
stl/string.nas Normal file
View File

@@ -0,0 +1,9 @@
# string.nas
# ValKmjolnir 2022/10/5
var join=func(vec){
var res="";
foreach(var i;vec)
res~=i;
return res;
}

View File

@@ -1,37 +1,37 @@
import.stl.padding;
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)
@@ -47,60 +47,38 @@ var trans_ttf=func(string)
println(i);
return;
}
var curve1=func()
{
var shadow=["░","▒","▓","█","▀","▄","▐","▌"];
rand(100);
var s="";
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 curve1=func(line=4){
var table=["╚","═","╝","╔","║","╗"];
rand(100);
var s="";
for(var i=0;i<10;i+=1)
{
for(var j=0;j<40;j+=1)
for(var i=0;i<line;i+=1){
for(var j=0;j<45;j+=1)
s~=table[int(6*rand())];
s~='\n';
}
print(s);
}
var curve3=func()
{
var s=["","","","","",""];
var cnt=0;
foreach(var char;char_ttf)
{
cnt+=1;
forindex(var i;char)
s[i]~=char[i];
if(cnt==9)
{
forindex(var i;s)
{
println(s[i]);
s[i]='';
}
cnt=0;
}
var curve2=func(line=2){
var shadow=["░","▒","▓","█","▀","▄","▐","▌"];
rand(100);
var s="";
for(var i=0;i<line;i+=1){
for(var j=0;j<45;j+=1)
s~=shadow[int(8*rand())];
s~='\n';
}
return;
print(s);
}
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)
{
var curve3=func(line=2){
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,
0,1,2,3,4,5,6,7,8,
0,1,2,3,4,5,6,7,8
];
for(var loop=0;loop<line;loop+=1){
for(var i=size(arr)-1;i>=0;i-=1){
var rand_index=int(i*rand());
(arr[i],arr[rand_index])=(arr[rand_index],arr[i]);
}
@@ -113,19 +91,7 @@ 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 curve4=func(line=4){
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",
@@ -136,20 +102,47 @@ var curve6=func(){
"100","101","102","103","104","105","106","107"
];
rand(time(0));
for(var i=0;i<15;i+=1){
for(var i=0;i<line;i+=1){
for(var j=0;j<45;j+=1)
print("\e["~front[16*rand()]~";"~back[16*rand()]~shadow[8*rand()]);
print('\n');
}
}
var curve5=func(line=4){
var vec=["▀▄─","▄▀─","▀─▄","▄─▀"];
for(var (y,p)=(0,0);y!=line;y+=1){
for(var x=0;x!=15;x+=1)
print(vec[p]);
print("\n");
p+=1;
p=p>=4?0:p;
}
}
var ansi_escape_sequence=func(){
for(var i=0;i<=9;i+=1)
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
print("\n");
for(var i=30;i<=37;i+=1)
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
print("\n");
for(var i=40;i<=47;i+=1)
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
print("\n");
for(var i=90;i<=97;i+=1)
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
print("\n");
for(var i=100;i<=107;i+=1)
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
print("\n");
}
# enable unicode
if(os.platform()=="windows")
system("chcp 65001");
trans_ttf("just for test");
trans_ttf(" ValKmjolnir ");
trans_ttf("just for fun");
curve1();
curve2();
curve3();
curve4();
curve5();
curve6();
ansi_escape_sequence();

View File

@@ -1,5 +1,6 @@
# Road check and auto pilot by ValKmjolnir
import("test/props_sim.nas");
import.stl.fg_env;
var dt=0.01;
var intergral=0;
var derivative=0;
@@ -20,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);
@@ -42,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);
@@ -67,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

@@ -147,8 +147,23 @@ 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);
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];},
@@ -156,7 +171,7 @@ var funcs=[
func{if(paper[ptr])pc=inum[pc];},
func{if(!paper[ptr])pc=inum[pc];},
func{paper[ptr]=input()[0];},
func{print(chr(paper[ptr]));}
func{print(char[paper[ptr]]);}
];
var bf=func(program){
@@ -218,6 +233,10 @@ var bf=func(program){
die("lack ]");
return;
}
# enable ANSI escape sequence
if(os.platform()=="windows")
system("color");
len=size(code);
for(pc=0;pc<len;pc+=1)
funcs[code[pc]]();

View File

@@ -1,242 +0,0 @@
var mandelbrot=
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
>>>>>>>>>]+[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-]>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+
<<<<<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>>+>>>>>>>>>>>>>>>>>>>>>>>>>>
>+<<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+[>>>>>>[>>>>>>>[-]>>]<<<<<<<<<[<<<<<<<<<]>>
>>>>>[-]+<<<<<<++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<+++++++[-[->>>
>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[[-]>>>>>>[>>>>>
>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>
[>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<
<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>+++++++++++++++[[
>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[
>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[
-<<+>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<
<<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<
[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>
>>>>[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+
<<<<<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>
>>>>>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<
+>>>>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<
<]<+<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>
>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<
<<<]>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<
<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->
>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<
<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]<<<<<<<[->+>>>-<<<<]>>>>>>>>>+++++++++++++++++++
+++++++>>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>[<<<<<<<+<[-<+>>>>+<<[-]]>[-<<[->+>>>-
<<<<]>>>]>>>>>>>>>>>>>[>>[-]>[-]>[-]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>>>>>>[>>>>>
[-<<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>[-<<<<<<<<
<+>>>>>>>>>]>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>>]+>[-
]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>>>>>>>]<<<
<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+>>]<
<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[->>>>
>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-]<->>>
[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[>>>>>>[-<
<<<<+>>>>>]<<<<<[->>>>>+<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>+>>>>>>>>
]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<[->>[-<<+
>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>
[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-
]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>>
[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>++++++++
+++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>>>>>>[-<<<<<<<+
>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[
-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>>]>[-<<<<<<[->>>>>+<++<<<<]>>>>>[-<
<<<<+>>>>>]<->+>]<[->+<]<<<<<[->>>>>+<<<<<]>>>>>>[-]<<<<<<+>>>>[-<<<<->>>>]+<<<<
[->>>>->>>>>[>>[-<<->>]+<<[->>->[-<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]
+>>>>>>[>>>>>>>>>]>+<]]+>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<<<<<<<<<<[<<<<<
<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<
[<<<<<<<<<]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<
<<<+<[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<
<<<<<+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<
<<<<<<<<<<]>>>>[-]<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+<]>>>>>>>>]<<<
<<<<<+<[>[->>>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>[->>>>+<<<<]>]<[->>>>-<<<<<<<
<<<<<<<+>>>>>>>>>>]<]>>[->>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>>+<<<<
]<<<<<<<<<<<]>>>>>>+<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>>>>>>>>>]<<<<<<<<<
[>[->>>>>+<<<<[->>>>-<<<<<<<<<<<<<<+>>>>>>>>>>>[->>>+<<<]<]>[->>>-<<<<<<<<<<<<<<
+>>>>>>>>>>>]<<]>[->>>>+<<<[->>>-<<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>>+<<<]<<<<<<<
<<<<<]]>[-]>>[-]>[-]>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<
<<<+>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[
[>>>>>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+
[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>
[-<<+>>]<<[->>+>+<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<
<[>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[
>[-]<->>>[-<<<+>[<->-<<<<<<<+>>>>>>>]<[->+<]>>>]<<[->>+<<]<+<<<<<<<<<]>>>>>>>>>[
>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]>
>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>[-]>>>>+++++++++++++++[[>>>>>>>>>]<<<<<<<<<-<<<<<
<<<<[<<<<<<<<<]>>>>>>>>>-]+[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<
<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-
<<<+>>>]<<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>
>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>>>
[-<<<->>>]<<<[->>>+<<<]>>>>>>>>]<<<<<<<<+<[>[->+>[-<-<<<<<<<<<<+>>>>>>>>>>>>[-<<
+>>]<]>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<<<]>>[-<+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>>]<]>
[-<<+>>]<<<<<<<<<<<<<]]>>>>[-<<<<+>>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>
>>>>>>]<<<<<<<<+<[>[->+>>[-<<-<<<<<<<<<<+>>>>>>>>>>>[-<+>]>]<[-<-<<<<<<<<<<+>>>>
>>>>>>>]<<]>>>[-<<+>[-<-<<<<<<<<<<+>>>>>>>>>>>]>]<[-<+>]<<<<<<<<<<<<]>>>>>+<<<<<
]>>>>>>>>>[>>>[-]>[-]>[-]>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>>>[-<<<<<
<+>>>>>>]<<<<<<[->>>>>>+<<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>+>[-<-<<<<+>>>>
>]>>[-<<<<<<<[->>>>>+<++<<<<]>>>>>[-<<<<<+>>>>>]<->+>>]<<[->>+<<]<<<<<[->>>>>+<<
<<<]+>>>>[-<<<<->>>>]+<<<<[->>>>->>>>>[>>>[-<<<->>>]+<<<[->>>-<[-<<+>>]<<[->>+<<
<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>[-<<->>]+<<[->>->[-<<<+>>>]<
<<[->>>+<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<
<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-<<<+>>>]<<<[->>>+>>>>>>[>+>[-<->]<[->+
<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>[->>>+<<<]>]<[->>>-
<<<<<<<<<<<<<+>>>>>>>>>>]<]>>[->>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>]>]<[->>>+<<<
]<<<<<<<<<<<]>>>>>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]]>>>>[-<<<<+>
>>>]<<<<[->>>>+>>>>>[>+>>[-<<->>]<<[->>+<<]>>>>>>>>]<<<<<<<<+<[>[->>>>+<<<[->>>-
<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[
->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<<<<<]]>>>>[-]<<<<]>>>>[-<<<<+>>
>>]<<<<[->>>>+>[-]>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<+<<<<<]>>>>>>>>>[>>>>>>
>>>]<<<<<<<<<[>[->>>>+<<<[->>>-<<<<<<<<<<<<<+>>>>>>>>>>>[->>+<<]<]>[->>-<<<<<<<<
<<<<<+>>>>>>>>>>>]<<]>[->>>+<<[->>-<<<<<<<<<<<<<+>>>>>>>>>>>]<]>[->>+<<]<<<<<<<<
<<<<]]>>>>>>>>>[>>[-]>[-]>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>[-]>[-]>>>>>[>>>>>[-<<<<+
>>>>]<<<<[->>>>+<<<+<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<+>>>>>
]<<<<<[->>>>>+<<<+<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>
>>>>>]+>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+[>+>>
>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>[-<<<<+>>>>]<<<<[->>>>+<<<<<[->>[-<<+
>>]<<[->>+>>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>
[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<]>[->>>>>>>>>+<<<<<<<<<]<+>>>>>>>>]<<<<<<<<<[>[-
]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+<<<<<<<<<]>>>>>>>>>
[>+>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>->>>>>[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<
<<[->>>[-<<<+>>>]<<<[->>>+>+<<<<]+>>>>>>>>>]<<<<<<<<[<<<<<<<<<]]>>>>>>>>>[>>>>>>
>>>]<<<<<<<<<[>>[->>>>>>>>>+<<<<<<<<<]<<<<<<<<<<<]>>[->>>>>>>>>+<<<<<<<<<]<<+>>>
>>>>>]<<<<<<<<<[>[-]<->>>>[-<<<<+>[<->-<<<<<<+>>>>>>]<[->+<]>>>>]<<<[->>>+<<<]<+
<<<<<<<<<]>>>>>>>>>[>>>>[-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>]>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>+++++++++++++++[[>>>>>>>>
>]<<<<<<<<<-<<<<<<<<<[<<<<<<<<<]>>>>>>>>>-]+>>>>>>>>>>>>>>>>>>>>>+<<<[<<<<<<<<<]
>>>>>>>>>[>>>[-<<<->>>]+<<<[->>>->[-<<<<+>>>>]<<<<[->>>>+<<<<<<<<<<<<<[<<<<<<<<<
]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>[-<<<<->>>>]+<<<<[->>>>-<[-<<<+>>>]<<<[->>>+<
<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>
>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>->>[-<<<<+>>>>]<<<<[->>>>+<<[-]<<]>>]<<+>>>>[-<<<<
->>>>]+<<<<[->>>>-<<<<<<.>>]>>>>[-<<<<<<<.>>>>>>>]<<<[-]>[-]>[-]>[-]>[-]>[-]>>>[
>[-]>[-]>[-]>[-]>[-]>[-]>>>]<<<<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-]>>>>]<<<<<<<<<
[<<<<<<<<<]>+++++++++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+>>>>>>>>>+<<<<<<<<
<<<<<<[<<<<<<<<<]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+[-]>>[>>>>>>>>>]<<<<<
<<<<[>>>>>>>[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<[<<<<<<<<<]>>>>>>>[-]+>>>]<<<<
<<<<<<]]>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+>>[>+>>>>[-<<<<->>>>]<<<<[->>>
>+<<<<]>>>>>>>>]<<+<<<<<<<[>>>>>[->>+<<]<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<
<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<<<<<[->>>>>>+<<<<<<]<
+<<<<<<<<<]>>>>>>>-<<<<[-]+<<<]+>>>>>>>[-<<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>->>[>>
>>>[->>+<<]>>>>]<<<<<<<<<[>[-]<->>>>>>>[-<<<<<<<+>[<->-<<<+>>>]<[->+<]>>>>>>>]<<
<<<<[->>>>>>+<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>+<<<
<<[<<<<<<<<<]>>>>>>>>>[>>>>>[-<<<<<->>>>>]+<<<<<[->>>>>->>[-<<<<<<<+>>>>>>>]<<<<
<<<[->>>>>>>+<<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>[-<
<<<<<<->>>>>>>]+<<<<<<<[->>>>>>>-<<[-<<<<<+>>>>>]<<<<<[->>>>>+<<<<<<<<<<<<<<[<<<
<<<<<<]>>>[-]+>>>>>>[>>>>>>>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<
<<[<<<<<<<<<]>>>>[-]<<<+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>-<<<<<[<<<<<<<
<<]]>>>]<<<<.>>>>>>>>>>[>>>>>>[-]>>>]<<<<<<<<<[<<<<<<<<<]>++++++++++[-[->>>>>>>>
>+<<<<<<<<<]>>>>>>>>>]>>>>>+>>>>>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>>>>>>[-<<<<<<
<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+[-]>[>>>>>>>>>]<<<<<<<<<[>>>>>>>>[-<<<<<<<+>>>>>>
>]<<<<<<<[->>>>>>>+<<<<<<<<[<<<<<<<<<]>>>>>>>>[-]+>>]<<<<<<<<<<]]>>>>>>>>[-<<<<<
<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+>[>+>>>>>[-<<<<<->>>>>]<<<<<[->>>>>+<<<<<]>>>>>>
>>]<+<<<<<<<<[>>>>>>[->>+<<]<<<<<<<<<<<<<<<]>>>>>>>>>[>>>>>>>>>]<<<<<<<<<[>[-]<-
>>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]<+<<<<<<
<<<]>>>>>>>>-<<<<<[-]+<<<]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>->[>>>
>>>[->>+<<]>>>]<<<<<<<<<[>[-]<->>>>>>>>[-<<<<<<<<+>[<->-<<+>>]<[->+<]>>>>>>>>]<<
<<<<<[->>>>>>>+<<<<<<<]<+<<<<<<<<<]>+++++[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>
+>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<[<<<<<<<<<]>>>>>>>>>[>>>>>>[-<<<<<<->>>>>>]+<
<<<<<[->>>>>>->>[-<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<<<<<<<<<<[<<<<<<<
<<]>>>>[-]+>>>>>[>>>>>>>>>]>+<]]+>>>>>>>>[-<<<<<<<<->>>>>>>>]+<<<<<<<<[->>>>>>>>
-<<[-<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<<<<<<<<<<[<<<<<<<<<]>>>[-]+>>>>>>[>>>>>>
>>>]>[-]+<]]+>[-<[>>>>>>>>>]<<<<<<<<]>>>>>>>>]<<<<<<<<<[<<<<<<<<<]>>>>[-]<<<++++
+[-[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>]>>>>>->>>>>>>>>>>>>>>>>>>>>>>>>>>-<<<<<<[<<<<
<<<<<]]>>>]";
var paper=[];
var (ptr,pc)=(0,0);
var (code,inum,stack)=([],[],[]);
var (add,mov,jt,jf,in,out)=(0,1,2,3,4,5);
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"
];
var table=[];
func(){
var cnt=0;
for(var i=0;i<256;i+=1){
append(table,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(table[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]
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
@@ -152,20 +150,16 @@ var bf=func(program)
var stack=[];
var len=size(program);
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)
{
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,20 +1,17 @@
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()
{
var prt=func(){
var s="\e[0;0H+--------------------+\n";
for(var i=0;i<10;i+=1)
{
for(var i=0;i<10;i+=1){
s~="|";
for(var j=0;j<20;j+=1)
s~=pixel[map[i][j]];
@@ -25,30 +22,27 @@ var prt=func()
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;
}
@@ -59,7 +53,8 @@ var bfs=func(begin,end)
return;
}
# enable ANSI escape sequence
if(os.platform()=="windows")
system("chcp 65001");
system("color");
print("\ec");
bfs([0,0],[9,19]);

View File

@@ -1,7 +1,6 @@
rand(time(0));
var new_neuron=func()
{
var new_neuron=func(){
return {
in:0,
out:0,
@@ -11,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,5,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;
@@ -62,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;
@@ -81,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;
@@ -112,20 +104,23 @@ 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);
}
cnt+=1;
if(cnt>=3e5)
break;
}
print('finished after ',cnt,' epoch.\n');
foreach(var v;training_set)
{
if(cnt>=3e5){
print("failed to train, ",cnt," epoch.\n");
}else{
print('finished after ',cnt,' epoch.\n');
}
foreach(var v;training_set){
run(v);
print(v,': ',output[0].out,'\n');
}

View File

@@ -1,110 +1,142 @@
import.stl.padding;
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/file.nas ",
"stl/lib.nas ",
"stl/list.nas ",
"stl/module.nas ",
"stl/queue.nas ",
"stl/result.nas ",
"stl/sort.nas ",
"stl/stack.nas "
"fg_env.nas",
"file.nas",
"json.nas",
"lib.nas",
"list.nas",
"log.nas",
"module.nas",
"padding.nas",
"process_bar.nas",
"queue.nas",
"result.nas",
"sort.nas",
"stack.nas",
"string.nas"
];
var testfile=[
"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/diff.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/md5.nas ",
"test/md5compare.nas ",
"test/module_test.nas ",
"test/nasal_test.nas ",
"test/pi.nas ",
"test/prime.nas ",
"test/props_sim.nas ",
"test/props.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 "
"ascii-art.nas",
"auto_crash.nas",
"bf.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",
"mcpu.nas",
"md5.nas",
"md5compare.nas",
"module_test.nas",
"nasal_test.nas",
"occupation.nas",
"pi.nas",
"ppmgen.nas",
"prime.nas",
"qrcode.nas",
"quick_sort.nas",
"scalar.nas",
"snake.nas",
"tetris.nas",
"trait.nas",
"turingmachine.nas",
"utf8chk.nas",
"watchdog.nas",
"wavecollapse.nas",
"word_collector.nas",
"ycombinator.nas"
];
var module=[
"module/fib.cpp ",
"module/keyboard.cpp ",
"module/libfib.nas ",
"module/libkey.nas "
"fib.cpp",
"keyboard.cpp",
"nasocket.cpp",
"libfib.nas",
"libkey.nas",
"libsock.nas"
];
var getname=func(s){
var (len,ch)=(size(s),' '[0]);
for(var i=0;i<len and s[i]!=ch;i+=1);
return substr(s,0,i);
var longest=func(vec...){
var len=0;
foreach(var v;vec)
foreach(var f;v)
len=size(f)>len?size(f):len;
return len;
}
var padding_length=longest(source,lib,testfile,module);
var 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 column=func(number){
number=number>=1000?substr(str(number/1000),0,3)~'k':str(number);
return rightpad(number,6);
}
var calc=func(codetype,files,path=""){
println(codetype);
var (bytes,line,semi,line_cnt,semi_cnt)=(0,0,0,0,0);
var (bytes,ctx,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.exists(path~files[i])?io.fin(path~files[i]):"";
(line_cnt,semi_cnt)=(count(s,'\n'),count(s,';'));
println(files[i],'| ',line_cnt,' \tline | ',semi_cnt,' \tsemi');
println(rightpad(files[i],padding_length),'| ',
column(line_cnt),'line | ',
column(semi_cnt),'semi | ',
rightpad(str(int(size(s)/1024)),6),'kb | ',
md5(s),' |');
bytes+=size(s);
ctx~=s;
line+=line_cnt;
semi+=semi_cnt;
}
println('total: | ',line,' \tline | ',semi,' \tsemi');
println(' | ',bytes,'\tbytes| ',int(bytes/1024),' \tkb');
println(rightpad("total:",padding_length),'| ',
column(line),'line | ',
column(semi),'semi | ',
rightpad(str(int(bytes/1024)),6),'kb | ',
md5(ctx),' |\n');
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(rightpad("total:",padding_length),'| ',rightpad(str(all),6),'kb');

96
test/coroutine.nas Normal file
View File

@@ -0,0 +1,96 @@
# 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;
}
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])];
if(res[0]==nil or res[1]==nil or res[0][0]!=res[1][0])
die("different coroutines don't share the same local scope");
}
# 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){
var res=coroutine.resume(co);
if(res==nil or res[0]!=x or res[1]!=i)
die("coroutine should have the ability to get upvalues");
}
}();
# test crash in coroutines
var co=coroutine.create(func{
var b=func(){b()}
coroutine.yield(b);
b();
coroutine.yield(0);
});
println("coroutine yield: ",coroutine.resume(co));
println("coroutine state:\e[32m ",coroutine.status(co),"\e[0m");
println("coroutine error: ",coroutine.resume(co));
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
println("coroutine yield: ",coroutine.resume(co));
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
var co=coroutine.create(func{
var a=1;
var b=func(){
b();
}
coroutine.yield(b);
coroutine.yield(b());
});
println("coroutine yield: ",coroutine.resume(co));
println("coroutine state:\e[32m ",coroutine.status(co),"\e[0m");
println("coroutine error: ",coroutine.resume(co));
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
println("coroutine yield: ",coroutine.resume(co));
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
println("ok");
# pressure test
var productor=func(){
for(var i=0;;i+=1)
coroutine.yield(i);
}
var total=1000; # ms
var co=coroutine.create(productor);
var tm=maketimestamp();
if(os.platform()=="windows"){
system("chcp 65001");
system("color");
}
var counter=0;
var bar=process_bar.high_resolution_bar(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.");

View File

@@ -21,11 +21,8 @@ var myers=func(src,dst,show_table=0){
if(show_table){
var curve=[
["+---",
"| "],
[
"+---",
"| \\ "]
["+---","| "],
["+---","| \\ "]
];
var s="";
forindex(var y;dst){
@@ -41,9 +38,7 @@ var myers=func(src,dst,show_table=0){
print(s~"+\n");
}
var total=[];
var path=[];
var vec=[[0,0,-1]];
var (total,path,vec)=([],[],[[0,0,-1]]);
visited[0]=1;
while(size(vec)){
append(total,vec);
@@ -124,6 +119,6 @@ func(diff){
print("\n");
diff(
io.fin("test/bf.nas"),
io.fin("test/bfcolored.nas")
io.fin("test/bfconvertor.nas")
);
}(myers);

View File

@@ -1,8 +1,8 @@
var fib=func(x)
{
if(x<2) return x;
return fib(x-1)+fib(x-2);
if(x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
print(fib(i),'\n');

View File

@@ -1,16 +1,7 @@
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 files=func(path){
if(!io.exists(path))
return [];
var dd=unix.opendir(path);
var res=[];
while(var n=unix.readdir(dd))
append(res,n);
@@ -18,17 +9,29 @@ 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[33m[",f,"]\e[36m>\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('',".");
# enable unicode
if(os.platform()=="windows")
system("chcp 65001");
println("\e[33m[",unix.getcwd(),"]\e[36m>\e[0m");
prt([""],".");

View File

@@ -16,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)
@@ -42,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';

335
test/httptest.nas Normal file
View File

@@ -0,0 +1,335 @@
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);
if(socket.bind(sd,ip,port)<0){
println("failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
return;
}
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("./doc/pic/favicon.ico")));
elsif(path=="/license")
http.send(client,respond.ok(io.fin("./LICENSE")));
elsif(path=="/doc/pic/nasal.png" or path=="/doc/pic/benchmark.png" or path=="/doc/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,312 +1,4 @@
#lib json.nas
var JSON=func(){
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"
];
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='';
}
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;
}
var next=func()
{
while(ptr<text_size and !check())
{
if(text[ptr]=='\n')
line+=1;
ptr+=1;
}
if(ptr>=text_size)
{
token.content="eof";
token.type=j_eof;
return;
}
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;
}
var match=func(type)
{
if(token.type!=type)
print("line ",line,": expect ",j_content[type]," but get `",token.content,"`.\n");
next();
return;
}
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;
}
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_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;
}
};
}();
import.stl.json;
var ss=JSON.stringify({
vec:[0,1,2],
@@ -316,6 +8,10 @@ var ss=JSON.stringify({
m3:[114514],
m4:{year:1919,month:8,date:10}
},
emptyhash:{},
emptyvec:[],
empty:[{}],
empty_an:[[[[[[{}]]]]]],
function:func(){}
});
println(ss);

View File

@@ -4,28 +4,23 @@ var lexer=func(file)
var s=io.fin(file);
var len=size(s);
var line=0;
var gen=func(tok)
{
var gen=func(tok){
append(token,{
line:line,
token:tok
});
}
return
{
jmp_note:func()
{
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])
@@ -38,15 +33,12 @@ var lexer=func(file)
}
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';
@@ -63,9 +55,7 @@ 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]);
@@ -77,60 +67,48 @@ var lexer=func(file)
ptr+=1;
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;
}
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;
}
gen(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;
}
@@ -140,30 +118,23 @@ var lexer=func(file)
println("error number: ",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;
}
gen(tmp);
return;
}
elsif(c=='.')
{
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.')
{
}elsif(c=='.'){
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.'){
gen("...");
ptr+=3;
}
else
{
else{
gen(".");
ptr+=1;
}
@@ -174,16 +145,13 @@ var lexer=func(file)
ptr+=1;
return;
},
compile:func()
{
compile:func(){
line=1;
while(ptr<len)
{
while(ptr<len){
var c=s[ptr];
if(c=='#'[0])
me.jmp_note();
elsif(c=='\n'[0])
{
elsif(c=='\n'[0]){
line+=1;
ptr+=1;
}
@@ -204,7 +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.line,' | ',tok.token,')\n');

View File

@@ -1,18 +1,15 @@
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);
}
@@ -22,8 +19,7 @@ var new_map=func()
var prt=func()
{
var s='\e[0;0H';
foreach(var line;map)
{
foreach(var line;map){
foreach(var elem;line)
s~=elem~' ';
s~='\n';
@@ -32,10 +28,10 @@ var prt=func()
unix.sleep(1/144);
}
func()
{
func(){
# enable ANSI escape sequence
if(os.platform()=="windows")
system("chcp 65001");
system("color");
print("\ec");
rand(time(0));
map=new_map();
@@ -43,13 +39,11 @@ func()
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,6 +1,5 @@
for(;;)break;
for(;;)
{
for(;;){
var a=1;
break;
}
@@ -10,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,20 +1,16 @@
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;
}

172
test/mcpu.nas Normal file
View File

@@ -0,0 +1,172 @@
var inst={
inst_stop:0,
inst_mov_reg_reg:1,
inst_mov_reg_imm_low:2,
inst_mov_reg_imm_high:3,
inst_add:4,
inst_sub:5,
inst_mult:6,
inst_div:7,
inst_and:8,
inst_or:9,
inst_xor:10,
inst_not:11,
inst_nand:12,
inst_shl:13,
inst_shr:14,
inst_jmp:15,
inst_jt:16,
inst_jf:17,
inst_les:18,
inst_grt:19,
inst_leq:20,
inst_geq:21,
inst_eq:22,
inst_in:23,
inst_out:24
};
var hex=func(){
var vec=[];
foreach(var i;['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'])
foreach(var j;['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'])
append(vec,i~j);
return func(n){
return vec[n];
}
}();
var hex32=func(n){
return hex(bits.u32_and(n/math.pow(2,24),0xff))~
hex(bits.u32_and(n/math.pow(2,16),0xff))~
hex(bits.u32_and(n/math.pow(2,8),0xff))~
hex(bits.u32_and(n,0xff));
}
var machine=func(disk_file){
var reg=[];
var reg_size=32;
var pc=0;
var ir=[0,0,0,0]; # 32 bit instruction word
var mem=[];
var mem_size=1024*1024*1; # memory size, byte
var init=func(){
println("[",os.time(),"] init ",reg_size," registers.");
setsize(reg,reg_size); # 8 bit address wire
for(var i=0;i<reg_size;i+=1)
reg[i]=0;
println("[",os.time(),"] init memory, memory size: ",mem_size/1024/1024,"MB.");
setsize(mem,mem_size);
for(var i=0;i<mem_size;i+=1)
mem[i]=0;
println("[",os.time(),"] init completed.");
}
init();
var load=func(){
var vec=split(" ",disk_file);
println("[",os.time(),"] loading boot from disk: ",size(vec)," byte.");
forindex(var i;vec)
mem[i]=int("0x"~vec[i]);
println("[",os.time(),"] loading complete.");
}
load();
var ctx_info=func(){
var cnt=0;
println("pc : 0x",hex32(pc));
println("instr : 0x",hex(ir[0]),hex(ir[1]),hex(ir[2]),hex(ir[3]));
for(var i=0;i<reg_size;i+=1){
print("reg[",hex(i),"]: 0x",hex32(reg[i])," ");
if(cnt==3){
print("\n");
cnt=0;
}else{
cnt+=1;
}
}
}
var exec=func(info=1){
while(1){
ir=[mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]];
if(info)ctx_info();
var op=ir[0];
if(op==inst.inst_stop){
break;
}elsif(op==inst.inst_mov_reg_reg){
reg[ir[1]]=reg[ir[2]];
}elsif(op==inst.inst_mov_reg_imm_low){
reg[ir[1]]=bits.u32_and(reg[ir[1]],0xffff0000);
reg[ir[1]]=bits.u32_or(reg[ir[1]],ir[2]*math.pow(2,8)+ir[3]);
}elsif(op==inst.inst_mov_reg_imm_high){
reg[ir[1]]=bits.u32_and(reg[ir[1]],0x0000ffff);
reg[ir[1]]=bits.u32_or(reg[ir[1]],ir[2]*math.pow(2,24)+ir[3]*math.pow(2,16));
}elsif(op==inst.inst_add){
reg[ir[1]]=bits.u32_and(reg[ir[2]]+reg[ir[3]],0xffffffff);
}elsif(op==inst.inst_sub){
reg[ir[1]]=bits.u32_and(reg[ir[2]]-reg[ir[3]],0xffffffff);
}elsif(op==inst.inst_mult){
reg[ir[1]]=bits.u32_and(reg[ir[2]]*reg[ir[3]],0xffffffff);
}elsif(op==inst.inst_div){
reg[ir[1]]=bits.u32_and(reg[ir[2]]/reg[ir[3]],0xffffffff);
}elsif(op==inst.inst_and){
reg[ir[1]]=bits.u32_and(reg[ir[2]],reg[ir[3]]);
}elsif(op==inst.inst_or){
reg[ir[1]]=bits.u32_or(reg[ir[2]],reg[ir[3]]);
}elsif(op==inst.inst_xor){
reg[ir[1]]=bits.u32_xor(reg[ir[2]],reg[ir[3]]);
}elsif(op==inst.inst_not){
reg[ir[1]]=bits.u32_not(reg[ir[2]]);
}elsif(op==inst.inst_nand){
reg[ir[1]]=bits.u32_nand(reg[ir[2]],reg[ir[3]]);
}elsif(op==inst.inst_shl){
reg[ir[1]]=bits.u32_and(reg[ir[2]]*math.pow(2,reg[ir[3]]),0xffffffff);
}elsif(op==inst.inst_shr){
reg[ir[1]]=bits.u32_and(reg[ir[2]]/math.pow(2,reg[ir[3]]),0xffffffff);
}elsif(op==inst.inst_jmp){
pc=reg[ir[1]];
}elsif(op==inst.inst_jt){
pc=reg[ir[1]]?reg[ir[2]]:pc;
}elsif(op==inst.inst_jf){
pc=reg[ir[1]]?pc:reg[ir[2]];
}elsif(op==inst.inst_les){
reg[ir[1]]=reg[ir[2]]<reg[ir[3]];
}elsif(op==inst.inst_grt){
reg[ir[1]]=reg[ir[2]]>reg[ir[3]];
}elsif(op==inst.inst_leq){
reg[ir[1]]=reg[ir[2]]<=reg[ir[3]];
}elsif(op==inst.inst_geq){
reg[ir[1]]=reg[ir[2]]>=reg[ir[3]];
}elsif(op==inst.inst_eq){
reg[ir[1]]=reg[ir[2]]==reg[ir[3]];
}elsif(op==inst.inst_in){
reg[0]=0; # unfinished
}elsif(op==inst.inst_out){
println("reg[",ir[1],"]: 0x",hex32(reg[ir[1]]));
}
pc+=4;
}
};
return {exec:exec};
}(
hex(inst.inst_mov_reg_imm_high)~" 01 ca fe "~ # reg[1]=0xcafe0000
hex(inst.inst_mov_reg_imm_low)~" 01 ba be "~ # reg[1]=0xcafebabe
hex(inst.inst_out)~" 01 00 00 "~ # output reg[1]
hex(inst.inst_mov_reg_imm_low)~" 02 00 20 "~ # reg[2]=0x10
hex(inst.inst_jmp)~" 02 00 00 "~ # jmp *reg[2]
hex(inst.inst_out)~" 01 00 00 "~
hex(inst.inst_out)~" 01 00 00 "~
hex(inst.inst_out)~" 01 00 00 "~
hex(inst.inst_out)~" 01 00 00 "~
hex(inst.inst_out)~" 01 00 00 "~ # should jump here
hex(inst.inst_jf)~" 01 02 00 "~ # should not jump
hex(inst.inst_mov_reg_imm_low)~" 03 00 04 "~ # reg[3]=4
hex(inst.inst_mov_reg_imm_low)~" 04 00 00 "~ # reg[4]=0
hex(inst.inst_out)~" 04 00 00 "~ # output reg[4]
hex(inst.inst_grt)~" 00 04 03 "~ # reg[0]=reg[4]>reg[3]
hex(inst.inst_mov_reg_imm_low)~" 05 00 30 "~ # reg[5]=0x2c
hex(inst.inst_mov_reg_imm_low)~" 06 00 01 "~ # reg[6]=1
hex(inst.inst_add)~" 04 04 06 "~ # reg[4]+=reg[6]
hex(inst.inst_jf)~" 00 05 00 " # jmp *reg[5] if reg[0]!=true
);
machine.exec(0);

View File

@@ -4,47 +4,16 @@ var check=func(x){
return x-floor(x/0x100000000)*0x100000000;
}
var u32_bits_and=func(x,y){
(x,y)=(check(x),check(y));
var (res,op)=(0,1);
for(var i=0;i<32;i+=1){
var (tmpx,tmpy)=(x-floor(x/2)*2,y-floor(y/2)*2);
res+=op*(tmpx==1 and tmpy==1);
(x,y)=(floor(x/2),floor(y/2));
op*=2;
}
return res;
return bits.u32_and(check(x),check(y));
}
var u32_bits_or=func(x,y){
(x,y)=(check(x),check(y));
var (res,op)=(0,1);
for(var i=0;i<32;i+=1){
var (tmpx,tmpy)=(x-floor(x/2)*2,y-floor(y/2)*2);
res+=op*(tmpx==1 or tmpy==1);
(x,y)=(floor(x/2),floor(y/2));
op*=2;
}
return res;
return bits.u32_or(check(x),check(y));
}
var u32_bits_xor=func(x,y){
(x,y)=(check(x),check(y));
var (res,op)=(0,1);
for(var i=0;i<32;i+=1){
var (tmpx,tmpy)=(x-floor(x/2)*2,y-floor(y/2)*2);
res+=op*(tmpx!=tmpy);
(x,y)=(floor(x/2),floor(y/2));
op*=2;
}
return res;
return bits.u32_xor(check(x),check(y));
}
var u32_bits_not=func(x){
x=check(x);
var (res,op)=(0,1);
for(var i=0;i<32;i+=1){
res+=op*((x-floor(x/2)*2)==1?0:1);
x=floor(x/2);
op*=2;
}
return res;
return bits.u32_not(check(x));
}
var hex32str=func(){

View File

@@ -1,19 +1,7 @@
import("test/md5.nas");
import.test.md5;
import.stl.process_bar;
srand();
var progress_bar=func(){
var res=[];
setsize(res,51);
var (tmp,sp)=(" |"," | ");
res[0]=tmp~sp;
for(var i=1;i<=50;i+=1){
tmp~="#";
res[i]=tmp~substr(sp,i,52-i);
}
return res;
}();
var compare=func(){
var ch=[
"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+",
@@ -21,106 +9,85 @@ var compare=func(){
"^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?"
];
return func(begin,end){
var (total,prt_cnt,lastpercent,percent)=(end-begin,0,0,0);
var byte=0;
var total=end-begin;
var timestamp=maketimestamp();
timestamp.stamp();
var bar=process_bar.high_resolution_bar(40);
for(var i=begin;i<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));
}
percent=int((i-begin+1)/total*100);
if(percent-lastpercent>=2){
prt_cnt+=1;
lastpercent=percent;
}
print(progress_bar[prt_cnt],percent,"% (",i-begin+1,"/",total,")\t",res," max byte: ",end-1," \r");
print(" ",bar.bar((i-begin+1)/total)," (",i-begin+1,"/",total,")\t",res," byte: ",byte," time: ",timestamp.elapsedMSec()," \r");
}
print('\n');
print("\n");
};
}();
var filechecksum=func(){
var getname=func(s){
var (len,ch)=(size(s),' '[0]);
for(var i=0;i<len and s[i]!=ch;i+=1);
return substr(s,0,i);
}
var files=[
"./stl/file.nas ",
"./stl/lib.nas ",
"./stl/list.nas ",
"./stl/module.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/diff.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/md5.nas ",
"./test/md5compare.nas ",
"./test/module_test.nas ",
"./test/nasal_test.nas ",
"./test/pi.nas ",
"./test/prime.nas ",
"./test/props_sim.nas ",
"./test/props.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 "
"./stl/fg_env.nas", "./stl/file.nas",
"./stl/json.nas",
"./stl/lib.nas", "./stl/list.nas",
"./stl/log.nas", "./stl/module.nas",
"./stl/padding.nas", "./stl/process_bar.nas",
"./stl/queue.nas", "./stl/result.nas",
"./stl/sort.nas", "./stl/stack.nas",
"./stl/string.nas",
"./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/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/mcpu.nas",
"./test/md5.nas", "./test/md5compare.nas",
"./test/module_test.nas", "./test/nasal_test.nas",
"./test/occupation.nas", "./test/pi.nas",
"./test/ppmgen.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/watchdog.nas", "./test/wavecollapse.nas",
"./test/word_collector.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"
];
foreach(var i;files){
var f=io.fin(getname(i));
var (res0,res1)=(md5(f),_md5(f));
println(i,' ',res0,' ',!cmp(res0,res1),' ',size(f),' byte');
var byte=0;
var total=size(files);
var timestamp=maketimestamp();
timestamp.stamp();
var bar=process_bar.high_resolution_bar(40);
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," time: ",timestamp.elapsedMSec()," \r");
}
print("\n");
}
var randomchecksum=func(){
@@ -128,5 +95,7 @@ var randomchecksum=func(){
compare(i,i+512);
}
if(os.platform()=="windows")
system("chcp 65001");
filechecksum();
randomchecksum();

View File

@@ -3,7 +3,7 @@ var libfib=func(){
return {
open:func(){
if(dd==nil){
dd=dylib.dlopen("./module/libfib.so");
dd=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
fib=dylib.dlsym(dd,"fib");
qfib=dylib.dlsym(dd,"quick_fib");
}else{

View File

@@ -44,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

69
test/occupation.nas Normal file
View File

@@ -0,0 +1,69 @@
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(){
while(1){
var cpu0=cpu_stat();
for(var i=0;i<5;i+=1){
unix.sleep(0.1);
coroutine.yield(nil);
}
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;
coroutine.yield(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;
}
var co=coroutine.create(cpu_occupation);
var bar=process_bar.high_resolution_bar(30);
print("\ec");
while(1){
var mem=mem_occupation();
var mem_occ=(mem.MemTotal-mem.MemFree)/mem.MemTotal*100;
var cpu_occ=nil;
while((cpu_occ=coroutine.resume(co)[0])==nil){
var key=libkey.nonblock();
if(key!=nil and chr(key)=="q")
return;
}
println("\e[1;1H\e[1m Memory total(GB) : \e[0m\e[36m",mem.MemTotal/1024/1024,"\e[0m");
println("\e[2;1H\e[1m Memory free(GB) : \e[0m\e[36m",mem.MemFree/1024/1024,"\e[0m");
println("\e[3;1H\e[1m Memory occupation(%): \e[0m",mem_occ>60?"\e[91m":"\e[32m",bar.bar(mem_occ/100)~" ",mem_occ,"\e[0m ");
println("\e[4;1H\e[1m CPU occupation(%) : \e[0m",cpu_occ>90?"\e[91m":"\e[32m",bar.bar(cpu_occ/100)~" ",cpu_occ,"\e[0m ");
println("\e[5;1H Press 'q' to quit.");
}
}();

39
test/ppmgen.nas Normal file
View File

@@ -0,0 +1,39 @@
import.stl.process_bar;
var ppm=func(filename,width,height,RGB){
# P3 use ASCII number
# P6 use binary character
var fd=io.open(filename,"wb");
io.write(fd,"P3\n"~width~" "~height~"\n255\n");
for(var i=0;i<height;i+=1){
for(var j=0;j<width;j+=1)
io.write(fd,RGB(i,j));
io.write(fd,"\n");
}
io.close(fd);
}
var width=1280;
var height=720;
var bar=(os.platform()=="windows")?
process_bar.bar(front:"sharp",back:"point",sep:"line",length:50):
process_bar.high_resolution_bar(50);
var f=func(i,j){
var (yMin,yMax,xMin,xMax)=(-1.35,1.35,-3.3,1.5);
var (yDel,xDel)=(yMax-yMin,xMax-xMin);
var (y,x)=((i/height)*yDel+yMin,(j/width)*xDel+xMin);
var (x0,y0)=(x,y);
for(var iter=0;iter<25;iter+=1){
var (x1,y1)=((x0*x0)-(y0*y0)+x,2*x0*y0+y);
(x0,y0)=(x1,y1);
if((x0*x0)+(y0*y0)>4){
break;
}
}
var progress=(i*width+j+1)/(width*height);
print(bar.bar(progress)," ",progress*100,"% \r");
iter=iter==25?255:int(iter/25*255);
return iter~" "~iter~" "~iter~" ";
}
ppm("a.ppm",width,height,f);
println("\nfinished.");

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;
},
};

View File

@@ -1,179 +0,0 @@
var geodinfo=func(lat,lon){
return {};
}
var maketimer=func(interval,function){
return {
isRunning:0,
start:func(){
me.isRunning=1;
while(1){
unix.sleep(interval);
function();
}
},
stop:func(){
me.isRunning=0;
},
restart:func(interval){
},
singleShot:0,
simulatedTime:0
};
}
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());

View File

@@ -22,6 +22,7 @@ var code=[
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
];
# enable unicode
if(os.platform()=="windows")
system("chcp 65001");
var texture=[" ","██"];
@@ -38,7 +39,7 @@ var transfer=func(s){
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.bitand(1,len);
vec[i]=bits.u32_and(1,len);
len=int(len/2);
}
println(vec);
@@ -48,7 +49,7 @@ var transfer=func(s){
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.bitand(1,c);
tmp[j]=bits.u32_and(1,c);
c=int(c/2);
}
foreach(var j;tmp)

View File

@@ -1,24 +1,7 @@
var sort=func(vec,left,right)
{
if(left>=right) return;
var (L,R,tmp)=(left,right,vec[left]);
while(left<right)
{
while(left<right and tmp<=vec[right])
right-=1;
while(left<right and tmp>=vec[left])
left+=1;
if(left!=right)
(vec[left],vec[right])=(vec[right],vec[left]);
}
(vec[L],vec[left])=(vec[left],tmp);
sort(vec,L,left-1);
sort(vec,left+1,R);
return;
}
var vec=[];
rand(time(0));
for(var i=0;i<1e4;i+=1)
append(vec,int(rand()*1e5));
sort(vec,0,size(vec)-1);
import.stl.sort;
var vec=[];
rand(time(0));
for(var i=0;i<1e4;i+=1)
append(vec,int(rand()*1e5));
sort(vec);
println(vec);

View File

@@ -200,4 +200,8 @@ foreach(var i;a){
}
foreach(i;a){
;
}
}
println(runtime.argv());
func(a,b,c,d="只有红茶可以吗"){
println(a,' ',b,' ',c,' ',d);
}(c:1919810,b:514,a:114);

View File

@@ -1,4 +1,4 @@
import("./module/libkey.nas");
import.module.libkey;
var list=func(){
var (begin,end,len)=(nil,nil,0);
return{
@@ -165,8 +165,7 @@ var game=func(x,y){
gameover=2;
},
move:func(c){
if(c=='w' or c=='a' or c=='s' or c=='d')
move=c;
move=c;
},
gameover:func(){
return gameover;
@@ -174,17 +173,27 @@ var game=func(x,y){
}
}
var co=coroutine.create(func(){
var t=maketimestamp();
while(1){
t.stamp();
while(t.elapsedMSec()<20);
coroutine.yield();
}
});
var main=func(){
# enable unicode
if(os.platform()=="windows")
system("chcp 65001");
print("\ec");
libkey.init();
var g=game(15,10);
g.print();
print("\rpress any key to start...");
libkey.getch();
print("\r \r");
var counter=12;
var counter=20;
while(1){
var ch=libkey.nonblock();
if(ch!=nil){
@@ -197,20 +206,20 @@ var main=func(){
}
g.move(chr(ch));
}
unix.sleep(0.02);
counter-=1;
if(counter==0){
if(!counter){
counter=20;
g.next();
if(g.gameover())
break;
g.print();
}
coroutine.resume(co);
}
libkey.close();
println(g.gameover()<=1?"game over.":"you win!");
println("enter anything to quit.");
input();
println("press 'q' to quit.");
while(libkey.getch()!='q'[0]);
}
main();

View File

@@ -1,4 +1,4 @@
import("./module/libkey.nas");
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",
@@ -275,7 +275,6 @@ var main=func(){
if(os.platform()=="windows")
system("chcp 65001");
libkey.init();
print(
"\ec\e[1:1H",
"╔═════════════════════════╗\n",
@@ -331,7 +330,7 @@ var main=func(){
unix.sleep(0.02);
counter-=1;
}
libkey.close();
print(
map.gameover()?
"\e[31mg\e[32ma\e[33mm\e[34me \e[35mo\e[36mv\e[94me\e[31mr \e[32m~\e[0m\n":
@@ -339,10 +338,10 @@ var main=func(){
);
print(
"\e[31me\e[32mn\e[33mt\e[34me\e[35mr ",
"\e[36ma\e[94mn\e[95my\e[96mt\e[31mh\e[32mi\e[33mn\e[34mg ",
"\e[36m'\e[94mq\e[95m' ",
"\e[35mt\e[36mo \e[94mq\e[95mu\e[91mi\e[92mt\e[0m\n"
);
input();
while(libkey.getch()!='q'[0]);
};
main();

View File

@@ -25,8 +25,25 @@ var emoji测试=func(){
var 🍾="🍾好似,开🍾咯";
var 🐘="🐘太🚬🐘了兄弟们";
var 📁=[🤣,😅,🤤,🥵,🥶,🤢,🤓,😭,👿,🤡,💩,🍾,🐘];
var 🗄️={
🤣:🤣,
😅:😅,
🤤:🤤,
🥵:🥵,
🥶:🥶,
🤢:🤢,
🤓:🤓,
😭:😭,
👿:👿,
🤡:🤡,
💩:💩,
🍾:🍾,
🐘:🐘
};
foreach(var 📄;📁)
💻(📄,🎤);
foreach(var 📄;keys(🗄️))
💻(📄,🗄️[📄],🎤);
}
unicode测试();
emoji测试();

58
test/watchdog.nas Normal file
View File

@@ -0,0 +1,58 @@
var os_time=func(){
return "[\e[33;1m"~os.time()~"\e[0m] ";
}
var err_hd=func(){
return "[\e[91;1merror\e[0m] ";
}
var info_hd=func(){
return "[\e[96;1minfo\e[0m] ";
}
var modified_hd=func(){
return "[\e[92;1mmodified\e[0m] ";
}
var usage=func(){
println(os_time(),info_hd(),"\e[1musage: nasal watchdog.nas <filename> [\"argv\"]\e[0m");
}
var argv=runtime.argv();
if(size(argv)<1){
println(os_time(),err_hd(),"\e[1mneed correct file path to watch\e[0m");
usage();
exit(-1);
}
var filename=argv[0];
if(!io.exists(filename)){
println(os_time(),err_hd(),"\e[1mfile <",filename,"> does not exist\e[0m");
usage();
exit(-1);
}
var args=[];
if(size(argv)==2){
println(os_time(),info_hd(),"\e[1mwith argument(s) ",argv[1],"\e[0m");
args=split(" ",argv[1]);
}
var modified_time=fstat(filename).st_mtime;
println(os_time(),info_hd(),"\e[1mwatching ",filename," ..\e[0m");
while(1){
unix.sleep(1);
if(!io.exists(filename)){
println(os_time(),err_hd(),"\e[1mfile <",filename,"> does not exist\e[0m");
break;
}
var latest_modified_time=fstat(filename).st_mtime;
if(latest_modified_time!=modified_time){
modified_time=latest_modified_time;
println(os_time(),modified_hd(),"\e[1m",filename,"\e[0m");
var cmd=(os.platform()=="windows"?"":"./")~"nasal "~filename;
foreach(var i;args)
cmd~=" "~i;
var ret=system(cmd);
if(ret!=0){
println(os_time(),err_hd(),"\e[1mprocess returned ",ret,"\e[0m");
}else{
println(os_time(),info_hd(),"\e[1mprocess returned ",ret,"\e[0m");
}
}
}

View File

@@ -1,7 +1,7 @@
# wave collapse function 2022/4/10
# by ValKmjolnir
srand();
var interval=1/60;
var interval=1/120;
var table=[
# c ,w,a,s,d
["═",0,1,0,1],
@@ -94,6 +94,7 @@ var map=func(){
}
}();
# enable unicode
if(os.platform()=="windows")
system("chcp 65001");
map.new(80);

56
test/word_collector.nas Normal file
View File

@@ -0,0 +1,56 @@
import.stl.sort;
var to_lower=func(s){
var tmp="";
for(var i=0;i<size(s);i+=1){
var c=s[i];
if('a'[0]<=c and c<='z'[0])
tmp~=chr(c);
elsif('A'[0]<=c and c<='Z'[0])
tmp~=chr(c-'A'[0]+'a'[0]);
else
tmp~=chr(c);
}
return tmp;
}
var spliter=func(content){
var token={};
var len=size(content);
var s="";
for(var i=0;i<len;i+=1){
var n=content[i];
var c=chr(n);
if(('a'[0]<=n and n<='z'[0]) or ('A'[0]<=n and n<='Z'[0]) or n=='\''[0] or n=='-'[0]){
s~=c;
}elsif(size(s)){
if(s[0]!="-"[0] and s[0]!="'"[0] and s[-1]!="-"[0] and s[-1]!="'"[0])
token[to_lower(s)]+=1;
s="";
}
}
return token;
}
func(argv){
if(size(argv)<1){
println("no input files.");
exit(-1);
}
var all_exists=1;
foreach(var f;argv){
if(!io.exists(f)){
println("cannot open file <",f,">");
all_exists=0;
}
}
if(!all_exists){
exit(-1);
}
var file_content="";
foreach(var f;argv)
file_content~=io.fin(f)~" ";
var vec=keys(spliter(file_content));
sort(vec,func(a,b){return cmp(a,b)<=0;});
println(vec);
}(runtime.argv());