637 Commits
v4.0 ... v11.0

Author SHA1 Message Date
Li Haokun
c8c233d1d4 Merge pull request #32 from ValKmjolnir/develop
📝 update release notes and tutorials
2023-10-07 22:53:52 +08:00
ValKmjolnir
0b179bf5ff 📝 update release notes and tutorials 2023-10-07 22:51:30 +08:00
Li Haokun
d108e40e22 Merge pull request #31 from ValKmjolnir/develop
📝 update docs and makefile
2023-10-07 21:49:09 +08:00
ValKmjolnir
63b97fc5ea Merge branch 'master' into develop 2023-10-07 17:35:22 +08:00
Li Haokun
d661a6b92d Merge pull request #30 from sidi762/master
Updated makefile to ensure the macOS binaries build by GitHub action are backward compatible to macOS 10.15
2023-10-07 17:13:11 +08:00
Sidi Liang
5233cef16e Update makefile 2023-10-07 14:41:46 +08:00
Sidi Liang
3874856f2b Specify mmacosx-version-min in makefile
So apparently the static linking isn't working
2023-10-07 14:34:12 +08:00
Sidi Liang
56e3e4353b Update makefile 2023-10-07 14:25:37 +08:00
Sidi Liang
e3e501ab6a Update makefile
Updated makefile to have the C++ standard libraries statically linked, so that older macOSs can still execute the program
2023-10-07 13:45:44 +08:00
ValKmjolnir
d14b816fa0 📝 add notes 2023-10-07 00:55:20 +08:00
ValKmjolnir
63b0112b9d 📝 update README 2023-10-04 11:52:09 +08:00
Li Haokun
982c5750ef Merge pull request #29 from ValKmjolnir/develop
 improve codegen and repl output
2023-10-02 00:52:05 +08:00
ValKmjolnir
d69dd0b03f change intg to repl output operand 2023-10-02 00:46:12 +08:00
ValKmjolnir
7a10392bea 📝 improve note and code in codegen 2023-09-27 00:26:13 +08:00
ValKmjolnir
5e2bb411e5 optimize codegen 2023-09-24 00:41:29 +08:00
ValKmjolnir
f8ecd2ae53 🔥 change codegen::gen to codegen::emit 2023-09-23 01:08:45 +08:00
ValKmjolnir
0a404694f6 🐛 fix memory leak in ast destructor 2023-09-20 21:11:13 +08:00
ValKmjolnir
6c8b4c6a87 use incremental gc const initialization 2023-09-20 00:46:23 +08:00
ValKmjolnir
85bc699905 add command history in repl 2023-09-19 00:24:45 +08:00
Li Haokun
38920382ea Merge pull request #28 from ValKmjolnir/develop
 finish simple repl interpreter (experimental)
2023-09-18 00:50:54 +08:00
ValKmjolnir
d0d9ce3b80 🔥 delete tools/repl.nas 2023-09-17 22:49:06 +08:00
ValKmjolnir
3e01239722 finish basic function of repl 2023-09-17 17:56:59 +08:00
ValKmjolnir
b77d7fafb1 using c++ cast 2023-09-15 00:04:19 +08:00
ValKmjolnir
36a2aa67ef using c++ cast (wip) 2023-09-14 00:34:31 +08:00
ValKmjolnir
c157d8a9b1 🎨 add namespace 2023-09-08 18:17:07 +08:00
Li Haokun
0c99eb15f0 Merge pull request #27 from ValKmjolnir/develop
🎨 bug fix & add experimental REPL & optimize framework of vm
2023-09-08 00:39:14 +08:00
ValKmjolnir
cd4e0c1716 🎨 add namespace 2023-09-08 00:33:57 +08:00
ValKmjolnir
d89f290a8a fix out of bound bug & delete fatal in lexer 2023-09-07 23:14:17 +08:00
ValKmjolnir
298b54c9ec 🐛 fix out of bound bug 2023-09-06 19:29:59 +08:00
ValKmjolnir
ec03f0aee0 repl does not write temp file now 2023-09-06 00:12:01 +08:00
ValKmjolnir
a0e6047296 update test/datalog.nas 2023-08-31 00:32:01 +08:00
ValKmjolnir
a801669888 add experimental REPL (cpp) 2023-08-27 17:46:10 +08:00
ValKmjolnir
ae5c76ecd5 maybe do not need o_intg anymore 2023-08-27 00:25:49 +08:00
ValKmjolnir
93528babdb optimize framework of vm 2023-08-24 23:24:04 +08:00
ValKmjolnir
ba629e57d7 optimize lexer 2023-08-23 00:59:35 +08:00
ValKmjolnir
00449c2bfb optimize code 2023-08-21 21:35:34 +08:00
ValKmjolnir
939b4c4a65 Merge branch 'master' into develop 2023-08-20 22:37:41 +08:00
ValKmjolnir
6f259dc1ea fix old ambiguous syntax 2023-08-20 22:35:24 +08:00
ValKmjolnir
98b0086656 🐛 fix segfault in var x = (var a,b) = (1,2) 2023-08-20 16:48:53 +08:00
ValKmjolnir
88b039a82e 📝 add experimental repl (for fun) 2023-08-19 21:36:12 +08:00
ValKmjolnir
fc2be49621 add check for symbol definition 2023-08-13 22:12:07 +08:00
ValKmjolnir
7d81246695 📝 optimize code format 2023-08-13 00:31:11 +08:00
Li Haokun
afaef55c68 Merge pull request #26 from ValKmjolnir/develop
 simpler ghost type check mechanism & split lib
2023-08-12 11:54:13 +08:00
ValKmjolnir
e131c8da4b split dylib and unix lib 2023-08-11 00:48:57 +08:00
ValKmjolnir
527cb5277b optimize vm 2023-08-10 00:40:00 +08:00
ValKmjolnir
d690c779b8 split io lib 2023-08-09 00:31:23 +08:00
ValKmjolnir
52e6adfe28 🔥 delete ghost type table 2023-08-08 20:50:47 +08:00
Li Haokun
18d59dfa94 Merge pull request #25 from ValKmjolnir/develop
 support `globals` `arg` and module imports
2023-08-07 19:06:30 +08:00
ValKmjolnir
044ccb4bc6 add profiling tools in debug mode 2023-08-06 20:02:12 +08:00
ValKmjolnir
70a8be8e4c symbol begins with '_' will not be exported 2023-08-05 00:08:28 +08:00
ValKmjolnir
56a26b6ab6 update std/math 2023-08-03 22:55:59 +08:00
ValKmjolnir
5cc9824a62 add new functions in bits & math lib 2023-08-02 19:22:45 +08:00
ValKmjolnir
56762d719d split bits lib 2023-08-01 00:36:25 +08:00
ValKmjolnir
29b15b89f7 split coroutine lib from builtin 2023-07-31 00:25:36 +08:00
ValKmjolnir
654a37ab88 split math lib into new file 2023-07-30 00:19:38 +08:00
ValKmjolnir
227197996d native functions can be loaded from differen tables 2023-07-29 23:45:44 +08:00
ValKmjolnir
fd7472b737 📝 add bits.nas & dylib.nas & unix.nas 2023-07-25 00:22:11 +08:00
ValKmjolnir
26a62dde9b 🐛 fix 2 bugs.
1. unary optimized number not generated bug
2. self-referenced module path not displayed correctly
2023-07-24 19:50:30 +08:00
ValKmjolnir
788e0026c2 optimize location info gen in codegen
and refactor lib.nas, add multiple native modules
2023-07-23 23:57:25 +08:00
ValKmjolnir
51a0ef6b0c 📝 add logprint & change libs 2023-07-23 18:30:32 +08:00
ValKmjolnir
31113ff69f 📝 delete std/sort.nas & update test file 2023-07-23 00:30:14 +08:00
ValKmjolnir
fe65085a7a add check for self-imported module 2023-07-21 23:40:59 +08:00
ValKmjolnir
569d423772 📝 update 2023-07-21 00:33:04 +08:00
ValKmjolnir
25e202fbaf add check for generated module name 2023-07-20 19:56:27 +08:00
ValKmjolnir
d479f13a5c support nasal module 2023-07-20 00:54:57 +08:00
ValKmjolnir
d9cf9ff49e update 2023-07-19 00:43:00 +08:00
ValKmjolnir
4b597000ba optimize error report info 2023-07-17 00:38:33 +08:00
ValKmjolnir
fe3847d69c add experimental namespace table 2023-07-16 21:31:51 +08:00
ValKmjolnir
eee30b7d8e add concurrent mark for experiment 2023-07-16 00:37:11 +08:00
ValKmjolnir
0c183b5984 optimize gc 2023-07-13 23:59:41 +08:00
ValKmjolnir
48d1d8ddef 🐛 fix compilation error in vs 2022 2023-07-12 00:16:27 +08:00
Li Haokun
ea33a5ed81 Merge pull request #24 from ValKmjolnir/develop
🐛 fix bug of argument "arg"
2023-07-11 19:13:42 +08:00
ValKmjolnir
4a7a2ce11e 🐛 fix bug in special argument "arg"
if defining a parameter named "arg", this will cause
error value of the last local variable of this function
2023-07-11 19:09:33 +08:00
ValKmjolnir
94b6e84693 📝 optimize code 2023-07-11 00:05:20 +08:00
ValKmjolnir
eab84d5c0d optimize gc 2023-07-10 20:34:21 +08:00
ValKmjolnir
bce8148f60 update 2023-07-10 00:11:46 +08:00
ValKmjolnir
32e23741eb add concat for vector 2023-07-09 16:45:10 +08:00
ValKmjolnir
7e72661332 🐛 complete function of arg in all scopes 2023-07-09 16:21:09 +08:00
Li Haokun
e2d8c5c495 Merge pull request #23 from ValKmjolnir/develop: add special variable "arg"
 add special variable "arg"
2023-07-09 01:06:04 +08:00
ValKmjolnir
3509655424 add special variable "arg" 2023-07-09 00:59:17 +08:00
ValKmjolnir
fd614a132c 📝 add new test file feigenbaum.nas 2023-07-07 19:55:29 +08:00
Li Haokun
bff0d78d59 🧑‍💻 Merge pull request #22 from ValKmjolnir/develop: add important symbol "globals"
🧑‍💻 add important symbol "globals"
2023-07-06 22:14:37 +08:00
ValKmjolnir
b4482792c8 🧑‍💻 add important symbol "globals" 2023-07-06 22:10:07 +08:00
Li Haokun
b122afd1cd Merge pull request #21 from ValKmjolnir/develop
👾 Update docs & optimize codes
2023-07-05 00:29:30 +08:00
ValKmjolnir
eca6141408 optimize codegen 2023-07-05 00:24:34 +08:00
ValKmjolnir
5c714d26aa update 2023-07-04 00:33:57 +08:00
Li Haokun
8ca7ee1d90 Merge pull request #20 from ValKmjolnir/develop
🎇 refactor ast structure & build process
2023-07-02 22:16:09 +08:00
ValKmjolnir
27bc544bbe 📝 rename files 2023-07-02 22:02:31 +08:00
ValKmjolnir
45f87925ae 📝 change license to GPLv2 2023-07-02 21:37:14 +08:00
ValKmjolnir
da43ec5193 📝 update docks 2023-07-02 20:39:28 +08:00
ValKmjolnir
917b4f6568 🐛 fix compile error in visual studio 2023-07-02 20:29:41 +08:00
ValKmjolnir
ac2744e24f change build scripts 2023-07-02 19:48:36 +08:00
ValKmjolnir
ba6b7cd05c change dir stl -> std 2023-07-02 16:17:56 +08:00
ValKmjolnir
a7a2f47d1e 🚀 switch build script to new nasal 2023-07-02 00:32:13 +08:00
ValKmjolnir
597c0388cb 🚀 update 2023-07-01 23:21:52 +08:00
ValKmjolnir
af3ade5a41 🐛 fix codegen bug in condition & mcall 2023-07-01 18:41:55 +08:00
ValKmjolnir
b6886cc957 finish new codegen 2023-07-01 16:38:37 +08:00
ValKmjolnir
90ad1a53d7 update 2023-07-01 00:55:00 +08:00
ValKmjolnir
8e4e4bfe89 update 2023-06-30 00:25:58 +08:00
ValKmjolnir
5d9267d3b9 add symbol_finder & codegen 2023-06-29 00:30:50 +08:00
ValKmjolnir
15f63210b4 add codegen/opcode/optimizer 2023-06-28 00:34:54 +08:00
ValKmjolnir
e2ec8cb9a0 🚀 add function ghosttype 2023-06-27 22:54:13 +08:00
ValKmjolnir
c516c0c3bf add new import & use c++17 2023-06-26 23:59:09 +08:00
ValKmjolnir
89f96d407b 🐛 fix bug 2023-06-26 19:35:56 +08:00
ValKmjolnir
8a10f07ce7 change parameter::name to std::string 2023-06-26 19:35:21 +08:00
ValKmjolnir
fb3d9ed2f3 update 2023-06-25 22:47:36 +08:00
ValKmjolnir
fe40bd7e3f finish ast dumper 2023-06-25 22:25:13 +08:00
ValKmjolnir
9f66960244 add ast dumper 2023-06-25 00:31:11 +08:00
ValKmjolnir
2ce13e2134 change ast used in parser 2023-06-24 16:50:43 +08:00
ValKmjolnir
0132ca0870 update 2023-06-24 00:30:50 +08:00
ValKmjolnir
ac49c0d676 update 2023-06-21 00:03:46 +08:00
ValKmjolnir
c5d6a6694b update new parser 2023-06-20 00:01:50 +08:00
ValKmjolnir
bcf274aed4 update 2023-06-19 00:10:14 +08:00
ValKmjolnir
78b7bec786 update 2023-06-18 00:14:21 +08:00
ValKmjolnir
7d8f199c8e update 2023-06-17 00:15:07 +08:00
ValKmjolnir
c95810b46c add nasal_new_lexer 2023-06-16 22:23:46 +08:00
ValKmjolnir
ec1985b3cc update 2023-06-14 00:01:15 +08:00
ValKmjolnir
da22dd03c0 add nasal_new_parse 2023-06-12 00:03:18 +08:00
ValKmjolnir
19480f7f26 add new ast nodes 2023-06-07 00:10:00 +08:00
ValKmjolnir
9bf4eacc62 prepare to use new ast 2023-06-05 00:11:37 +08:00
ValKmjolnir
afc8c54487 gc::extend uses std::nothrow 2023-05-18 18:45:56 +08:00
Li Haokun
36f6dd6c96 Merge pull request #19 from ValKmjolnir/develop
 add ghost_type_table for type registration & add cmake for VS to build
2023-05-11 20:59:49 +08:00
ValKmjolnir
63ec070d15 📝 update doc for vs build 2023-05-11 20:12:59 +08:00
ValKmjolnir
7f258dd17e vs could build with cmakelists.txt 2023-05-11 19:58:25 +08:00
ValKmjolnir
dfb0c6ab52 🐛 fix header in module for vs to build 2023-05-11 19:41:39 +08:00
ValKmjolnir
d954a3fc5e optimize ghost table structure 2023-05-11 19:26:34 +08:00
ValKmjolnir
7cc9ef436e improve function of ghost_register_table 2023-05-11 00:24:35 +08:00
ValKmjolnir
774ad60c42 add global_ghost_type_table for test 2023-05-10 00:48:04 +08:00
ValKmjolnir
a69d05233b prepare for ghost type 2023-05-09 00:09:15 +08:00
ValKmjolnir
666fb8b25f 🐛 fix codegen skip or/and expr bug 2023-05-08 19:23:50 +08:00
ValKmjolnir
92abad3384 add cmakelists.txt 2023-05-06 21:12:40 +08:00
ValKmjolnir
f914678311 add readline & os.arch
delete test file mandel.nas

change io.fin => io.readfile
2023-04-28 23:11:17 +08:00
ValKmjolnir
c858afbb76 🐛 fix compilation error
in windows mingw-w64 win32 thread model,
but we do not suggest you to use this,
please use posix thread model
2023-04-23 00:02:39 +08:00
ValKmjolnir
94f7c941f6 🐛 fix compilation error in VS 2023-04-12 19:44:13 +08:00
ValKmjolnir
cd21540710 🎨 improve code style 2023-04-12 01:11:31 +08:00
ValKmjolnir
a37ce6aadd 🐛 fix compilation warnings on macOS 2023-04-11 00:28:12 +08:00
ValKmjolnir
db47ab4445 🎨 improve gc code structure 2023-04-10 00:24:50 +08:00
ValKmjolnir
5519dc7a29 merge runtime regs into struct context 2023-04-08 00:18:58 +08:00
ValKmjolnir
3852ce23a3 split opcode to nasal_opcode.h 2023-04-04 00:29:17 +08:00
ValKmjolnir
4e17de909a update test/jsonrpc.nas 2023-03-25 21:26:49 +08:00
ValKmjolnir
73450092f2 add test/jsonrpc.nas and fix bug
bug: in nasocket.cpp, recv and recvfrom do not place 0 after reciving
data into the buffer.
2023-03-23 00:09:51 +08:00
ValKmjolnir
f8bac92548 📝 update test file datalog.nas 2023-03-14 00:16:54 +08:00
ValKmjolnir
7cdc5e40af visual update 2023-03-11 18:15:27 +08:00
ValKmjolnir
bd12fe917a add new operand calculate const and pop 2023-03-10 22:27:06 +08:00
ValKmjolnir
8161f3c514 optimize codegen 2023-03-09 23:00:25 +08:00
ValKmjolnir
dbc2c365b4 update error report for parser&codegen 2023-03-08 23:53:02 +08:00
ValKmjolnir
1678567c5d add multi-line error report and span 2023-03-08 01:12:01 +08:00
ValKmjolnir
abe2464a67 📝 shorten the running time of test process 2023-03-02 00:36:48 +08:00
ValKmjolnir
f9cbd0b426 add error report info in stl/mat 2023-03-02 00:26:52 +08:00
ValKmjolnir
99434df819 🚀 add more matrix operation in stl/mat.nas
and bp example test
2023-03-01 23:37:13 +08:00
ValKmjolnir
cbee5f8705 add std lib mat.nas 2023-03-01 00:41:34 +08:00
ValKmjolnir
e11793d340 🚀 add operator ^= &= |=
fix bug of parsing expressions beginning with floater
2023-02-28 00:30:27 +08:00
ValKmjolnir
461e5ac647 🔥 remove i32 bitwise native functions
now we use bitwise operators instead
2023-02-17 00:42:57 +08:00
ValKmjolnir
c8eb1f1d16 🚀 add bitwise operators or, xor, and 2023-02-16 00:24:32 +08:00
ValKmjolnir
068184f451 🐛 fix codegen for binary not on some ast nodes 2023-02-12 15:33:38 +08:00
ValKmjolnir
368b057a91 🚀 add binary negation 2023-02-12 15:22:05 +08:00
Li Haokun
f4deca4427 Merge pull request #18 from sidi762/master
Update CI to include tests
2023-02-12 00:28:31 +08:00
Sidi Liang
681f04b0d3 Update CI to include tests 2023-02-11 13:10:59 +08:00
ValKmjolnir
79c1e27027 🐛 fix bug in test/calc.nas 2023-02-03 19:01:00 +08:00
ValKmjolnir
84f9280cb4 🚀 add test file auto-push.nas 2023-02-02 21:28:11 +08:00
ValKmjolnir
e454b315fc 🚀 add file::find_all_files_with_extension 2023-02-02 21:24:21 +08:00
ValKmjolnir
1259b43389 🐛 fix bug that cannot parse return true/false 2023-02-02 19:35:42 +08:00
ValKmjolnir
acb2e3664c 🐛 fix bugs in stl/json, now it runs correctly 2023-01-31 19:39:22 +08:00
ValKmjolnir
fdd7794a83 📝 update readme 2023-01-31 19:01:36 +08:00
ValKmjolnir
22b1841664 update test/ascii-art.nas 2023-01-30 19:13:08 +08:00
ValKmjolnir
ac1ee4ddef change sprintf to snprintf in logtime() 2023-01-24 23:10:01 +08:00
ValKmjolnir
aef7936292 optimize test files
httptest.nas: when clicking mandel.nas link, this may cause memory overflow in new gc
2023-01-23 23:14:34 +08:00
ValKmjolnir
d03273b4e1 add keyword support 'true' and 'false'
true is now number 1.0

false is number 0.0
2023-01-16 17:44:56 +08:00
ValKmjolnir
8df02d25ad add detail location info in tokens
prepare for multi-line error reporter module
2023-01-10 22:22:27 +08:00
ValKmjolnir
268a03864e use new way of constructing var objects 2023-01-05 22:42:17 +08:00
ValKmjolnir
48611c50f7 🚀 coroutine.resume can pass arguments 2023-01-02 18:53:58 +08:00
ValKmjolnir
e6e89039b8 optimize code & add comments 2023-01-02 01:00:16 +08:00
ValKmjolnir
9d7f799ad3 update test file 'life.nas' 2022-12-27 18:25:58 +08:00
ValKmjolnir
dc6f3347b7 🎨 change clean command in makefile 2022-12-25 17:59:14 +08:00
ValKmjolnir
d5996d6a1d 🎨 change clean command in makefiles 2022-12-25 17:58:05 +08:00
ValKmjolnir
0d1069a724 optimize test file 2022-12-10 01:24:46 +08:00
ValKmjolnir
175706126b 🐛 fix dynamic arguments load bug in nasal_vm 2022-12-09 00:56:53 +08:00
ValKmjolnir
cb547499ac optimize gc 2022-12-07 01:17:40 +08:00
ValKmjolnir
de524938cf add runtime.gc.extend 2022-12-04 20:28:51 +08:00
ValKmjolnir
9c6996b54e update test files 2022-12-03 01:24:56 +08:00
ValKmjolnir
eca978e2b3 little optimization 2022-11-30 01:11:26 +08:00
ValKmjolnir
9455a83df0 optimize lexer 2022-11-28 21:16:39 +08:00
ValKmjolnir
be318abb2e change enum tok to enum class tok 2022-11-27 23:47:58 +08:00
ValKmjolnir
7e91c273f3 🎨 add header pic 2022-11-26 23:36:12 +08:00
ValKmjolnir
4c5ffb0240 🎨 change code format 2022-11-26 22:49:22 +08:00
ValKmjolnir
54969681fc move lvalue check from parse to codegen 2022-11-20 17:06:13 +08:00
ValKmjolnir
9196d7815f 🎨 improve error report 2022-11-19 22:47:19 +08:00
ValKmjolnir
309a57070c 🚀 change module loading 2022-11-15 21:23:34 +08:00
ValKmjolnir
7e4faed4a6 🚀 add new native function char 2022-11-12 17:03:56 +08:00
ValKmjolnir
f9179188a5 optimize test/console3D.nas 2022-11-11 00:37:14 +08:00
ValKmjolnir
97b3cefe75 add new ways of calling dylib function 2022-11-11 00:11:01 +08:00
ValKmjolnir
ca527ec931 🚀 add new module matrix.cpp (in dev) 2022-11-06 23:30:18 +08:00
ValKmjolnir
4fd69c6ce4 🚀 optimize code 2022-11-06 18:20:10 +08:00
ValKmjolnir
1ced201cb5 🚀 add gc time info 2022-11-04 21:36:54 +08:00
ValKmjolnir
cedb5e12f5 🚀 add test file console3D.nas 2022-11-04 01:18:38 +08:00
ValKmjolnir
aaccfbda11 🐛 fix ast print bug & delete some macros 2022-10-30 18:40:03 +08:00
ValKmjolnir
0c216e5f16 🐛 safer builtin_logtime 2022-10-30 01:30:05 +08:00
ValKmjolnir
feea901e4d 🎨 improve code format
delete option -v, --version, -l, --lex
2022-10-30 01:26:26 +08:00
Li Haokun
b0f080070a Merge pull request #17 from sidi762/master
[Review needed] Added linux nightly build
2022-10-28 23:39:37 +08:00
ValKmjolnir
d121dcd630 🎨 improve format of code 2022-10-28 23:28:15 +08:00
Sidi Liang
8aec98a2b3 Update c-cpp.yml 2022-10-24 01:32:05 +08:00
Sidi Liang
2c36f722bb Merge branch 'ValKmjolnir:master' into master 2022-10-24 01:29:46 +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
Sidi Liang
74b925715f Unable to place two builds under one releases, thus dividing them 2022-10-07 01:30:17 +08:00
Sidi Liang
b0c5dfada7 I'm stupid 2022-10-07 01:23:15 +08:00
Sidi Liang
8ae5cc0071 fix tar failing on linux (again) 2022-10-07 01:21:40 +08:00
Sidi Liang
8b1eebb310 Fix tar failing on linux 2022-10-07 01:20:14 +08:00
Sidi Liang
568d821186 fix file name 2022-10-07 01:14:06 +08:00
Sidi Liang
5b70449aee Added linux nightly build 2022-10-07 01:13:24 +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
ValKmjolnir
d567f5abf8 📝 update test/module_test.nas 2022-05-17 21:48:08 +08:00
ValKmjolnir
f8692f1e4e 📝 update test files 2022-05-17 18:22:24 +08:00
ValKmjolnir
712a047a43 🐛 now builtin_md5 uses unsigned char instead of char 2022-05-15 20:35:20 +08:00
ValKmjolnir
396d55a207 fix some warnings 2022-05-09 18:42:40 +08:00
ValKmjolnir
c5171c735a 💬 change code related to macros 'PRTHEX' 2022-05-08 19:43:27 +08:00
ValKmjolnir
07857d980c 📝 update test/utf8chk.nas 2022-05-07 17:18:29 +08:00
ValKmjolnir
51a1279110 add utf-8 identifier check in nasal_lexer & fix printf format at windows platform & add test file utf8chk.nas 2022-05-07 16:50:13 +08:00
ValKmjolnir
de262980cc 🎨 improve rawstr() and builtin_getcwd(..), change format output in nasal_vm::valinfo() 2022-05-07 02:22:49 +08:00
ValKmjolnir
23a5c1b1ad 🐛 bug fix 2022-05-06 20:58:02 +08:00
ValKmjolnir
fd8a148d0c bytecode info print and debugger will show the front 16 characters of strings whose length is greater than 16 2022-05-02 16:56:03 +08:00
ValKmjolnir
9c7f5f1a6e update test file diff.nas& add math.max math.min 2022-05-01 21:10:23 +08:00
ValKmjolnir
f049e1f9fb add test file diff.nas 2022-04-30 20:00:15 +08:00
ValKmjolnir
c4b7712e53 update README.md 2022-04-27 12:49:29 +08:00
ValKmjolnir
7417d5e635 change makefile 2022-04-26 22:53:35 +08:00
ValKmjolnir
785572634b change visual settings in -c/-dbg 2022-04-23 01:51:49 +08:00
ValKmjolnir
2dc8459cbf use effective way to check if file exists 2022-04-21 20:51:47 +08:00
ValKmjolnir
6e035d1951 add lib load code to automatically load lib.nas 2022-04-21 19:45:16 +08:00
ValKmjolnir
4f5fd3de33 op_addeq~op_lnkeq and op_addeqc~op_lnkeqc and op_meq operands now can do pop, this will decrease the frequency of calling op_pop 2022-04-17 17:20:18 +08:00
ValKmjolnir
fc25dd69e1 fix bug in codegen: foreach/forindex(id;vec/hash) may cause segmentation fault because of incorrect generated operand 2022-04-16 19:45:55 +08:00
ValKmjolnir
ca073499ae optimize codes 2022-04-13 19:08:06 +08:00
ValKmjolnir
022460755f optimize codes 2022-04-12 18:26:54 +08:00
ValKmjolnir
b6f174e869 update test/wavecollapse.nas 2022-04-10 16:47:27 +08:00
ValKmjolnir
7b160c4589 add test file wavecollapse.nas 2022-04-10 15:34:20 +08:00
ValKmjolnir
4503239731 change STACK_MAX_DEPTH to nasal_gc::stack_depth 2022-04-09 23:14:28 +08:00
ValKmjolnir
c12e812651 change test/md5compare.nas 2022-04-06 22:56:21 +08:00
ValKmjolnir
87cff700e8 change module/libmd5 to native function builtin_md5() in nasal_builtin.h 2022-04-06 22:30:49 +08:00
ValKmjolnir
bf5737ecfd add native function abort(), assert() 2022-04-06 21:25:20 +08:00
ValKmjolnir
651ae4ef77 add native function isa(object,class) 2022-04-05 22:33:55 +08:00
ValKmjolnir
e846e51175 add native function: srand, values, find. 2022-04-05 22:15:05 +08:00
ValKmjolnir
399b2f0ce9 bug fixed & add systime, finished vecindex 2022-04-04 19:08:48 +08:00
ValKmjolnir
aed5e27409 bug fix & optimize test code & add lib function: subvec, floor, abs, isfunc, isghost, isint, isnum, isscalar, isstr, isvec, vecindex(unfinished) 2022-04-04 18:38:25 +08:00
ValKmjolnir
a2b51fe212 optimize libmd5.nas & test/md5.nas 2022-04-03 18:10:00 +08:00
ValKmjolnir
92b684624d change module/makefile and test/md5compare.nas 2022-04-01 22:52:04 +08:00
ValKmjolnir
83a8632e8e update test/md5compare.nas & optimize test/md5.nas 2022-03-31 19:31:00 +08:00
ValKmjolnir
41b5304712 update test/md5compare.nas
and CAUTION: now the cpp&nas md5 program may not calculate strings including unicode
2022-03-30 21:42:30 +08:00
ValKmjolnir
c5a12ade5c update md5compare.nas 2022-03-29 22:54:17 +08:00
ValKmjolnir
7a939b417d fix bug in test/md5.nas & add test file md5compare.nas 2022-03-29 22:36:28 +08:00
ValKmjolnir
dd7740f1fd update test/md5.nas(still has bug) & add libmd5 in module(written in C++) 2022-03-28 17:14:11 +08:00
ValKmjolnir
617ad03d33 add math.pow 2022-03-27 16:14:55 +08:00
ValKmjolnir
b66ebbef4b fix bytecode output format bug 2022-03-27 13:10:35 +08:00
ValKmjolnir
7f22b25909 update README.md 2022-03-27 01:36:38 +08:00
ValKmjolnir
86f6296268 change opcode print format 2022-03-27 01:20:17 +08:00
ValKmjolnir
cf722fd98c fix bug: windows ps/cmd output unicode in bytecodes abnormally.
so we use the hex format to print them.
2022-03-23 22:54:07 +08:00
ValKmjolnir
1dd3fd445c add test/snake.nas 2022-03-20 22:08:11 +08:00
ValKmjolnir
27e25f84ec update main.cpp and bug fix 2022-03-20 18:28:45 +08:00
ValKmjolnir
e6457651d3 add unix.waitpid 2022-03-16 18:44:38 +08:00
ValKmjolnir
c4d52a88cd update docs & fix bug in nasal_builtin.h 2022-03-16 15:30:34 +08:00
ValKmjolnir
9bcad59e45 change enum obj_type to nasal_obj::obj_type 2022-03-15 22:51:14 +08:00
ValKmjolnir
9a099f66cb update docs 2022-03-14 20:29:49 +08:00
ValKmjolnir
b79d60fab5 add destructors for obj_file, obj_dylib, obj_dir & bug fix 2022-03-13 00:11:50 +08:00
ValKmjolnir
82e9e97a26 add destructor for obj_file & change arguments in test/tetris.nas 2022-03-12 23:37:16 +08:00
ValKmjolnir
6a1338bb23 update test/tetris.nas 2022-03-11 23:42:09 +08:00
ValKmjolnir
3b8a092f36 fix bug in test/json.nas 2022-03-11 15:26:38 +08:00
ValKmjolnir
3d86a32b12 update fully functional test/json.nas 2022-03-11 15:11:24 +08:00
ValKmjolnir
f26719e1d3 visual update 2022-03-10 16:05:49 +08:00
ValKmjolnir
e54ef9620f change nasal_ref value option functions' output types from pointers to references 2022-03-09 22:54:54 +08:00
ValKmjolnir
d8156e839b add unix.fork&unix.pipe(do not work on windows platform) 2022-03-09 19:03:12 +08:00
ValKmjolnir
61666d275d delete lstk&fstk, store local address and function on stack and in vm registers 2022-03-08 17:30:40 +08:00
ValKmjolnir
99f595e16f update num->string algorithm, now the to_string will output strings with no tailing zeros 2022-03-08 13:40:27 +08:00
ValKmjolnir
debe32b187 safer stl/module.nas & add stl/file.nas encapsulated from lib.nas/io 2022-03-06 15:17:39 +08:00
ValKmjolnir
ca9b8581b4 add module.nas to safely use dylib 2022-03-05 21:52:29 +08:00
ValKmjolnir
a0b341deb5 try fix bug 'use of undeclared identifier 'environ'' on MacOS 2022-03-05 19:24:33 +08:00
ValKmjolnir
d3df356299 add unix.environ() & use LPCWSTR in dylib.dlopen on Windows platform 2022-03-05 19:15:52 +08:00
ValKmjolnir
cd808a5e6d update detailed-info 2022-03-03 19:00:23 +08:00
ValKmjolnir
40f61a9dd4 fix bug of upval_state 2022-03-02 19:28:17 +08:00
ValKmjolnir
f312250d27 update 2022-03-01 14:36:05 +08:00
ValKmjolnir
3fac8aa665 fix bug in test/tetris.nas 2022-02-25 23:34:02 +08:00
ValKmjolnir
243aafd417 fix bug of -o not working. 2022-02-23 21:56:45 +08:00
ValKmjolnir
6bc03601d9 update test/tetris.nas 2022-02-23 01:09:31 +08:00
ValKmjolnir
f05acaecc7 fix bug in libkey and test/tetris.nas 2022-02-22 18:01:49 +08:00
ValKmjolnir
9456a903d7 finish tetris.nas 2022-02-21 17:10:13 +08:00
ValKmjolnir
984deed883 finish basic functions of tetris 2022-02-21 00:47:32 +08:00
ValKmjolnir
557cb2ebcf finish map drawing in test/tetris.nas 2022-02-20 17:58:13 +08:00
ValKmjolnir
9c055a9a23 add new third-lib libkey that includes function: kbhit,getch,nonblock 2022-02-19 16:55:54 +08:00
ValKmjolnir
13a09343e6 update README.md 2022-02-18 15:59:27 +08:00
ValKmjolnir
8c67e04cc4 update test file 2022-02-18 15:36:34 +08:00
ValKmjolnir
e77bb73a82 add special character \e. 2022-02-18 01:58:49 +08:00
ValKmjolnir
05fc5db337 change parameter name in native function split. 2022-02-17 23:34:58 +08:00
ValKmjolnir
a4738e8c7d delete operand op_nop 2022-02-16 23:27:22 +08:00
ValKmjolnir
5f6051e333 change tutorial's place in README.md 2022-02-15 23:42:17 +08:00
ValKmjolnir
51afe3dacd fixed bug when -O0 the program crashes with code 01.
add `nasal_err():error(0){}` in `nasal_err.h:47` as the constructor.
2022-02-14 17:40:19 +08:00
ValKmjolnir
0291908675 update README.md 2022-02-14 17:29:26 +08:00
ValKmjolnir
5fba784d05 add new gc type vm_upval 2022-02-13 22:40:54 +08:00
ValKmjolnir
9139e34c0b update README & bug fixed 2022-02-13 16:10:02 +08:00
ValKmjolnir
e7f503fae1 now local values are stored on stack. upvalue is generated when creating a new function in local scope.
may include some bugs inside. but all test files has passed the test so i decide to push it.
2022-02-12 23:12:30 +08:00
ValKmjolnir
980350d70a prepare for function-call optimization 2022-02-12 18:03:50 +08:00
ValKmjolnir
0ccd3c9bd0 update test/calc.nas & print function of nasal_vec/nasal_hash 2022-02-11 17:04:27 +08:00
ValKmjolnir
3e7ba4d774 little update 2022-02-10 22:20:18 +08:00
ValKmjolnir
a176022840 add notes in lib.nas & add runtime.gc() & add total instruction count in '-o'. 2022-02-09 15:53:09 +08:00
ValKmjolnir
24a1e39ad3 add garbage collector and memory allocator info. use '-d' to activate this function. 2022-02-08 23:40:52 +08:00
ValKmjolnir
a09c6ae2f9 update/optimize test files 2022-02-07 23:41:50 +08:00
ValKmjolnir
2a85e92d4a optimize test file & little update. 2022-02-06 19:55:45 +08:00
ValKmjolnir
92646840e4 reuse codes in nasal_vec::print and nasal_hash::print
now they all use nasal_ref::print
2022-02-05 23:55:56 +08:00
ValKmjolnir
c55ce758ed change increment arguments to a more efficiency level & change gc.nil, gc.one, gc.zero to constant nil, one, zero 2022-02-04 01:51:30 +08:00
ValKmjolnir
aa301aefc3 change increment argument in nasal_gc, because new vm doesn't need to alloc new objects frequently.
vm_str  2048 -> 512

vm_func 1024 -> 512

vm_vec  8192 -> 512
2022-02-03 22:43:51 +08:00
ValKmjolnir
d6d90ab7c8 change 'global' & 'local' in codegen to std::unordered_map & std::vector<std::unordered_map>> 2022-02-02 23:52:00 +08:00
ValKmjolnir
bf780514e6 add constant string calculation in optimizer 2022-02-01 21:20:36 +08:00
ValKmjolnir
eaa54035ff change name of enum:ast_hashmember->ast_pair, ast_new_iter->ast_iter
change ast_hashmember to ast_pair is because this type in fact is the same as std::pair<std::string,nasal_ref> in C++
2022-01-31 17:22:44 +08:00
ValKmjolnir
baa4f5a258 change error info of native function __builtin_import 2022-01-31 14:48:51 +08:00
ValKmjolnir
df24db5b58 optimize nasal_import::check_import 2022-01-30 23:26:30 +08:00
ValKmjolnir
4def93b4ad bug fixed in nasal_vec::print and nasal_hash::print
if vec's or hash's size is 0, a bug will occur because ++depth. now the ++depth has been put behind the size()
2022-01-29 19:51:43 +08:00
ValKmjolnir
0a407437a4 change native function num()
num now returns nil if the argument is not a number or numerable string.
2022-01-27 21:41:06 +08:00
ValKmjolnir
05ab4640da add notes of some native functions in lib.nas 2022-01-26 23:51:25 +08:00
ValKmjolnir
b92eb4b089 update README.md 2022-01-25 15:02:57 +08:00
ValKmjolnir
5778d1e38d update new test data 2022-01-24 15:19:27 +08:00
ValKmjolnir
479e5a2c52 fix errors in README.md 2022-01-22 22:19:14 +08:00
ValKmjolnir
2f455c52c1 fix error in README.md 2022-01-22 22:09:00 +08:00
ValKmjolnir
78c1f9b7a9 add contents in README.md 2022-01-22 22:05:42 +08:00
ValKmjolnir
c68b4c5947 add new option -op & --optimize to use optimizer | delete bytecode op_pone & op_pzero 2022-01-22 13:37:49 +08:00
ValKmjolnir
40344455e6 add optimizer
optimizer now does one work: calculate const number before generating bytecode
2022-01-22 00:41:08 +08:00
ValKmjolnir
630c99c39a optimize codes 2022-01-16 22:48:00 +08:00
ValKmjolnir
46716620e3 add test file 'turingmachine.nas' & change output format of ast & bug fixed 2021-12-28 20:23:47 +08:00
ValKmjolnir
70a43c2f03 bug fixed 2021-12-25 15:56:08 +08:00
ValKmjolnir
1923fc74e4 update README.md 2021-12-23 21:46:53 +08:00
ValKmjolnir
f0ae63bce5 lexer,parser,import,codegen use the same error module. 2021-12-23 21:15:50 +08:00
ValKmjolnir
30650bb64f update error info (except parser)
prepare for nasal_err module
2021-12-23 14:09:54 +08:00
ValKmjolnir
d87aef82b7 debug info now shows both source code and bytecode 2021-12-21 19:57:23 +08:00
ValKmjolnir
e79d1eb8a4 add debugger function: break point & next step 2021-12-21 15:27:38 +08:00
ValKmjolnir
189d49fa4a add debugger framework
with function:backtrace,run,help,show global/local/upvalue,exit
2021-12-20 21:33:22 +08:00
Li Haokun
6a543f2aa7 Merge pull request #7 from sidi762/master
Update c-cpp.yml to solve failures
2021-12-17 00:31:16 +08:00
ValKmjolnir
c27c5b70ee little update 2021-12-16 23:53:13 +08:00
Sidi Liang
0a246d2fc7 Merge branch 'ValKmjolnir:master' into master 2021-12-13 08:49:45 +08:00
Sidi Liang
0956a08bec Update c-cpp.yml 2021-12-13 08:46:20 +08:00
ValKmjolnir
5a80258d20 bug fixed & add os.platform 2021-12-08 18:46:32 +08:00
Li Haokun
85bb502191 Merge pull request #6 from sidi762/master
New Github Action for nightly builds on macOS
2021-12-07 10:37:39 +08:00
Sidi Liang
ead49a657e Change the asset name to 'nasal' 2021-12-06 23:00:25 +08:00
Sidi Liang
df5be35af8 Attempt to make nightly builds for macOS 2021-12-06 22:55:18 +08:00
Sidi Liang
19d5952210 Update c-cpp.yml 2021-12-06 22:47:37 +08:00
Sidi Liang
afd87da5e7 Try out GitHub Actions 2021-12-06 22:36:08 +08:00
ValKmjolnir
9861ecd03e add dylib.dlopen dylib.dlsym dylib.dlclose dylib.dlcall
now you could add your own modules into nasal without changing the source code!
2021-12-03 19:31:03 +08:00
ValKmjolnir
aa191a9feb delete ret stack/ add math.lg
now return address is stored on value stack
2021-12-02 22:23:22 +08:00
ValKmjolnir
b8ef3cf6b6 delete op_cntpop & counter stack
now the iterator will be type vm_cnt and be stored on value stack.
2021-11-25 18:13:31 +08:00
ValKmjolnir
0a8655eb4d fixed bug of in_foreach and in_forindex 2021-11-22 14:24:28 +08:00
ValKmjolnir
52b49edbcf add unix.isdir and unix.isfile 2021-11-15 22:47:52 +08:00
ValKmjolnir
6a35c58df4 2 bugs fixed:
empty string will be true in conditional expressions,but now it is false(and string that is not empty and is not numerable will be true)

foreach(var i;func(){return []}()); will cause sigsegv because codegen generates error op_pop and op_cntpop,now the counter in_foreach and in_forindex change after generating
foreach/forindex expression before block generation.
2021-11-14 23:05:34 +08:00
ValKmjolnir
cd08b2d1bb change code structure 2021-11-02 22:44:42 +08:00
ValKmjolnir
f8e2918561 add unix.opendir unix.readdir unix.closedir 2021-10-31 23:11:04 +08:00
ValKmjolnir
e4ea34db51 add unix.time unix.chdir unix.sleep unix.getcwd unix.getenv 2021-10-29 19:52:49 +08:00
ValKmjolnir
4bfce37f40 add bits lib 2021-10-28 21:49:08 +08:00
ValKmjolnir
fd0d836c03 add io lib & bug fixed 2021-10-27 23:05:25 +08:00
ValKmjolnir
183446d32a bug fixed 2021-10-26 22:34:02 +08:00
ValKmjolnir
540aeb73f4 optimize nasal_ast and fix bug in opr_slc2 2021-10-20 20:54:23 +08:00
ValKmjolnir
4f0acc4d63 fix dynamic para error 2021-10-18 22:05:31 +08:00
ValKmjolnir
56280db2c7 fix sigsegv error 2021-10-18 21:50:25 +08:00
ValKmjolnir
885b57cd52 add upvalue info into detail crash info 2021-10-18 19:59:41 +08:00
ValKmjolnir
bbee31ea55 add detail crash info 2021-10-17 22:57:45 +08:00
ValKmjolnir
1bfa7d2638 update 2021-10-16 23:36:43 +08:00
ValKmjolnir
d4a9412947 optimize code structure 2021-10-16 21:08:57 +08:00
ValKmjolnir
1b240b293e update variable name in nasal_lexer 2021-10-16 14:07:55 +08:00
ValKmjolnir
e41f728589 update 2021-10-15 22:21:57 +08:00
ValKmjolnir
577546763f change function name and cli format 2021-10-14 23:22:28 +08:00
ValKmjolnir
58ea303202 complete simple tutorial 2021-10-14 13:42:07 +08:00
ValKmjolnir
818685c48d change output format of information of bytecodes 2021-10-13 22:59:15 +08:00
ValKmjolnir
5d13261516 optimize source code 2021-10-12 18:26:10 +08:00
ValKmjolnir
56289b5d22 fully functional closure & add benchmark 2021-10-10 14:29:23 +08:00
ValKmjolnir
1733ac0573 vm_nil,vm_num changed to no-gcobject 2021-10-08 23:18:26 +08:00
ValKmjolnir
d71b4f09e2 prepare for version 8.0 2021-10-08 15:32:18 +08:00
ValKmjolnir
13d40e886e update 2021-09-25 23:25:31 +08:00
Li Haokun
618ce59233 bad access bug fixed 2021-09-13 19:55:03 +08:00
Li Haokun
071d8bd1ce update 2021-09-10 19:13:42 +08:00
Li Haokun
c498d5c8c4 add fg constants & change int(), num() 2021-09-02 19:10:49 +08:00
Li Haokun
11971267dc add math.nan math.inf 2021-09-01 19:15:32 +08:00
Li Haokun
418531a44a add result.nas 2021-09-01 19:14:03 +08:00
Li Haokun
59dc0d1423 add isnan 2021-08-27 17:43:01 +08:00
Li Haokun
385f0af17e nothing changed 2021-08-24 19:34:52 +08:00
Li Haokun
ef9b781961 security holes fixed 2021-08-19 17:54:36 +08:00
ValKmjolnir
80cc8e9db7 closure bugs fixed 2021-08-17 01:18:35 +08:00
Li Haokun
b2be386be8 update hexdump.nas 2021-08-13 15:10:20 +08:00
Li Haokun
e4c598cae6 add hexdump.nas 2021-08-12 20:01:51 +08:00
Li Haokun
5fe6681b0d update lexer 2021-08-12 19:00:17 +08:00
Li Haokun
35fc848672 fully functional closure 2021-08-11 14:54:17 +08:00
Li Haokun
e3f3bd7387 update readme 2021-08-10 17:55:49 +08:00
ValKmjolnir
638ec1c3a3 use same indentation 2021-08-09 21:30:18 +08:00
Li Haokun
90ac468aa9 update 2021-08-09 19:13:39 +08:00
ValKmjolnir
65dfef0a33 update 2021-08-09 01:02:27 +08:00
Li Haokun
76a2548e95 update 2021-08-06 18:57:06 +08:00
Li Haokun
40b690b67b update 2021-08-05 19:02:41 +08:00
Li Haokun
2b17f3d702 update debug info 2021-08-04 14:32:56 +08:00
ValKmjolnir
5b6c78783e update README 2021-08-04 00:03:49 +08:00
Li Haokun
fa618eb97f variables can be used before definition
change program to command line
change trace back info
change print function of nasal_vec and nasal_hash
2021-08-03 18:55:11 +08:00
ValKmjolnir
d0616ef028 update 2021-08-01 22:37:42 +08:00
ValKmjolnir
91771297d3 add test file 2021-08-01 22:34:02 +08:00
ValKmjolnir
4e1a3c5f2d syntax bug fixed
syntax like:
var f=func(){}
(var a,b,c)=(1,2,3);
will be incorrectly recognized like:
var f=func(){}(var a,b,c)

this bug is fixed now.
2021-08-01 02:11:27 +08:00
ValKmjolnir
df634cb1b2 update readme:difference between this and andy's interpreter 2021-08-01 01:54:14 +08:00
Li Haokun
aa797142d1 update parser
one bug found, waiting to be fixed
2021-07-28 18:22:40 +08:00
Li Haokun
816be43a98 update 2021-07-24 19:59:56 +08:00
Li Haokun
9ebabfe737 fixed bug in nasal_parse 2021-07-21 17:38:11 +08:00
ValKmjolnir
884b56ac09 bug fixed & raw string print 2021-07-21 00:20:25 +08:00
Li Haokun
61677101e4 show vm stack top's info when error occurs 2021-07-20 19:21:05 +08:00
Li Haokun
7a93f5b89b update 2021-07-19 17:04:45 +08:00
Li Haokun
9fe7a86a3b add trace back info 2021-07-16 17:18:13 +08:00
ValKmjolnir
9da029b8fe prepare for debugger 2021-07-16 02:17:53 +08:00
ValKmjolnir
8b8e72c879 update 2021-07-14 01:24:15 +08:00
ValKmjolnir
590c595522 delete slice_stack 2021-07-07 14:46:46 +08:00
ValKmjolnir
57d6bcdc52 add const compare instructions 2021-07-03 15:22:23 +08:00
ValKmjolnir
0b2fe61e6e add instruction & changes in codegen
add some instructions that execute const values.
the first symbol called in assignment will use op_load instead of op_meq,op_pop to assign.
2021-06-29 17:18:05 +08:00
ValKmjolnir
706659ba3d change instruction dispatch to computed-goto
bug fixed
prepare for version 7.0
2021-06-26 14:53:10 +08:00
ValKmjolnir
3c9a10d710 avoid unnecessary deep copy by using rvalue ref
and test file update.
bug fixed in test/lexer.nas
2021-06-24 22:10:08 +08:00
ValKmjolnir
fd57e9a47c performance optimization of vm/lex/parse/test 2021-06-24 00:26:26 +08:00
ValKmjolnir
ab99d2d1ed change mcall to call->mcall&allow differen lvalue in assignment 2021-06-21 16:46:47 +08:00
Valk Richard Li
ae0dae5956 update 2021-06-21 01:02:09 +08:00
Valk Richard Li
00c6e3b4fd nothing changed 2021-06-20 01:27:01 +08:00
Valk Richard Li
cdf7b92a8e add statistical information 2021-06-19 00:32:10 +08:00
Valk Richard Li
0e979a6e7b bug fixed & delete operand vapp 2021-06-15 00:49:32 +08:00
Valk Richard Li
dd144305da update test file 2021-06-14 00:27:00 +08:00
Valk Richard Li
4f3ddf803a add tips 2021-06-13 01:01:32 +08:00
Valk Richard Li
de305d26ad fixed an error in README 2021-06-12 01:23:06 +08:00
Valk Richard Li
9f30f45774 Update README.md 2021-06-11 15:28:25 +08:00
Valk Richard Li
1ae47807eb Add command line parameters & chr supports extended ASCII 2021-06-11 15:16:06 +08:00
Valk Richard Li
3deea632f8 front end optimization&stack overflow prompt
change parameter type to avoid unnecessary copies of string.
change stack depth from 65536<<4 to 16+(65536<<2).
now you could know stack overflow when it happens
2021-06-07 23:53:43 +08:00
Valk Richard Li
9f2c31149a bug fixed
fixed SIGSEGV when failed to load file in nasal_lexer::openfile
2021-06-06 19:17:02 +08:00
Valk Richard Li
b25a1bc3f4 more efficient str2num 2021-06-05 20:42:58 +08:00
Valk Richard Li
fd7677f94f update README.md(some history of this project)
AND a question of admins' brains of gitee
2021-06-05 17:15:07 +08:00
Valk Richard Li
2e31a70406 Update README.md 2021-06-03 21:59:15 +08:00
Valk Richard Li
8e29a3ec5b bug fixed & more efficient callfv
I changed callfv's way of calling a function with arguments in vm_vec.
now callfv fetches arguments from val_stack directly,so it runs test/fib.nas from 2.4s to 1.9s.

delete operand callf,add operands callfv & callfh.

also,i check val_stack's top to make sure there is not a stack overflow.
2021-06-03 21:49:31 +08:00
Valk Richard Li
a68bf85f04 bug fixed
a gc bug which causes  fatal error.
add member value collect to make sure that nasal_val is not collected repeatedly.
use builtin_alloc in builtin function to avoid incorrect collection of value in use(gc_alloc).
change free_list to free_list[vm_type_size] to avoid too many calls of new/delete(but seems useless?)
but the most important thing is fixing this bug.
2021-05-31 19:10:59 +08:00
Valk Richard Li
aae9395d66 bug fixed
add operand op_offset to make sure arguments are loaded at correct places,before this commit,arguments are not loaded from a correct offset,which will cause SIGSEGV when calling a function that has complex closure
2021-05-17 21:15:57 +08:00
Valk Richard Li
a463af53b7 add license & other changes
parser recognizes syntax errors more accurately.
change some for loop to standard c++11 for(auto iter:obj)
add MIT license
change info in README.md
2021-05-04 17:39:24 +08:00
Valk Richard Li
6adb991c04 parser reports syntax error accurately
still need improvement
2021-05-04 01:13:53 +08:00
Valk Richard Li
c5f4736984 change scope from unordered_map to vector 2021-04-19 19:12:41 +08:00
Valk Richard Li
1a233fbe15 update 2021-04-12 13:21:13 +08:00
Valk Richard Li
a7d6518bff add test file & update README 2021-04-04 23:35:13 +08:00
Valk Richard Li
0700ce14f7 change function of vapp & README update 2021-04-03 23:47:14 +08:00
Valk Richard Li
c88620920b add stl & more efficient scope 2021-04-02 22:19:29 +08:00
Valk Richard Li
125fc8a9fe bug fixed & test file changes
compare operators now run more efficiently.
2021-03-31 20:59:13 +08:00
Valk Richard Li
b06e1bb5dd add static symbol check & test file update 2021-03-30 15:55:38 +08:00
Valk Richard Li
c7316e9780 change id name & add test file 2021-03-30 00:12:48 +08:00
Valk Richard Li
be1bcdfe2c bug fixed 2021-03-28 17:39:24 +08:00
Valk Richard Li
144e6f45da gc changed to mark-sweep 2021-03-27 01:08:05 +08:00
Valk Richard Li
569d5c6c6a update 2021-03-11 23:18:04 +08:00
Valk Richard Li
7087c67d79 add constant table in vm 2021-03-08 19:45:33 +08:00
Valk Richard Li
c4e6a89959 update test files and prepare the release of v5.0 2021-03-07 16:33:43 +08:00
Valk Richard Li
f60f674845 delete op_entry 2021-03-04 15:53:34 +08:00
Valk Richard Li
c21d40c466 change test files 2021-03-03 09:20:42 +08:00
Valk Richard Li
19b590f3bb add test files 2021-03-03 01:09:57 +08:00
Valk Richard Li
a421470715 change map to unordered_map 2021-03-01 15:54:58 +08:00
Valk Richard Li
79dc13f419 update 2021-02-27 22:45:51 +08:00
Valk Richard Li
2e8208a752 add getParent & getPath 2021-02-25 22:30:28 +08:00
Valk Richard Li
1c40cca673 add tutorial of how to add built-in function 2021-02-23 23:33:01 +08:00
Valk Richard Li
b1a5a5f6c0 clear memory footprint when codegen complete 2021-02-23 22:53:28 +08:00
Valk Richard Li
64961877de Add props lib & bug fixed(op_ret in foreach/index got SIGSEGV) 2021-02-18 23:49:29 +08:00
Valk Richard Li
9c9bb52818 codegen will not generate 'jmp' if 'if' and 'elsif' is the last condition 2021-02-18 11:45:47 +08:00
Valk Richard Li
02148f4766 bug fixed & more efficient function call
fixed a bug when different hash calling the same function,'me' will be set to the latest called hash's address.
changed the way of pushing the scope to the stack by copying a new scope from the function's scope address.
use map as the nasal_scop instead of list<map>,it'll be more efficient.
2021-02-17 19:31:44 +08:00
Valk Richard Li
767711c93a change identifier name to avoid misunderstanding 2021-02-15 15:29:55 +08:00
Valk Richard Li
78ba0641a6 change the way of calling built-in functions
change std::map to struct array,use index to call built-in functions.this may be more efficient.
2021-02-14 17:36:42 +08:00
Valk Richard Li
80683c381f bug fixed 2021-02-13 23:57:53 +08:00
Li Haokun
6aac46adaf Merge pull request #5 from sidi762/master
Added left, right, streq, cmp, chr to library functions
2021-02-13 07:24:17 -08:00
Sidi Liang
388ef66308 Library: added comment for chr 2021-02-13 21:43:07 +08:00
Sidi Liang
8faa4ef2db Library: Added core library function chr according to FlightGear Nasal 2021-02-13 21:40:10 +08:00
Sidi Liang
441c02d0fb Library: Added core library function streq and cmp according to FlightGear Nasal 2021-02-13 21:09:13 +08:00
Sidi Liang
953ad80482 Library: Added core library function left and right according to FlightGear Nasal 2021-02-13 20:19:49 +08:00
Valk Richard Li
e2ee9cff4c update 2021-02-13 14:37:21 +08:00
Valk Richard Li
996ac59c79 update parser to LL(1) 2021-02-13 13:28:20 +08:00
Valk Richard Li
944f713ee9 update 2021-02-13 11:09:31 +08:00
Valk Richard Li
7329c70492 identifiers' name changed 2021-02-12 23:48:51 +08:00
Valk Richard Li
b5514fd269 update 2021-02-12 22:27:41 +08:00
Valk Richard Li
3be50116fa update 2021-02-10 00:12:22 +08:00
Valk Richard Li
125d6d3a7d update 2021-01-23 19:21:37 +08:00
Valk Richard Li
bb746dfbfb update 2021-01-23 17:57:05 +08:00
Valk Richard Li
8069a1b659 update 2021-01-06 22:12:19 +08:00
Valk Richard Li
f0cb8b6ef3 update 2021-01-06 21:07:34 +08:00
Valk Richard Li
9474ac9ef0 update & bug fixed 2021-01-05 23:17:32 +08:00
Valk Richard Li
b862aa91eb update 2021-01-05 01:55:17 +08:00
Valk Richard Li
bc64d530be update 2021-01-02 23:57:21 +08:00
Valk Richard Li
ea5116e963 update 2020-12-29 19:09:20 +08:00
Valk Richard Li
cc4e697246 bug fixed 2020-12-26 19:05:04 +08:00
Valk Richard Li
515bef3f5d bug fixed 2020-12-25 22:06:42 +08:00
Valk Richard Li
05800fe518 bug fixed 2020-12-25 13:22:57 +08:00
Valk Richard Li
33d37771ce update 2020-12-24 23:53:31 +08:00
Valk Richard Li
89540475cf update 2020-12-20 20:18:02 +08:00
Valk Richard Li
e03da2f737 update 2020-12-20 13:39:24 +08:00
Valk Richard Li
4617eb8f17 update 2020-12-19 23:47:04 +08:00
Valk Richard Li
fed1e20085 update 2020-12-19 21:02:02 +08:00
Valk Richard Li
9f30440286 preparation for v5.0 2020-12-19 01:26:15 +08:00
200 changed files with 26703 additions and 22194 deletions

1
.gitattributes vendored Normal file
View File

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

65
.github/workflows/c-cpp.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: C/C++ CI
on:
schedule:
- cron: "0 16 * * *"
push:
branches: [ master,develop ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
mac-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: make
run: |
make -j4
cd module
make all -j4
cd ..
make test
tar -czf nasal-mac-nightly.tgz .
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
with:
# GitHub auth token
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Name of Release to add file to
title: macOS Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_macOS
# File to release
files: nasal-mac-nightly.tgz
linux-x86_64-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: make
run: |
make -j4
cd module
make all -j4
cd ..
make test
touch nasal-linux-x86_64-nightly.tgz
tar -czf nasal-linux-x86_64-nightly.tgz --exclude=nasal-linux-x86_64-nightly.tgz .
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
with:
# GitHub auth token
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Name of Release to add file to
title: Linux Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_linux_x86_64
# File to release
files: nasal-linux-x86_64-nightly.tgz

29
.gitignore vendored
View File

@@ -30,3 +30,32 @@
*.exe
*.out
*.app
# VS C++ sln
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
.vs
x64
CMakePresents.json
# nasal executable
nasal
nasal.exe
# misc
.vscode
dump
fgfs.log
.temp.*
# build dir
build
out
# macOS special cache directory
.DS_Store
# ppm picture generated by ppmgen.nas
*.ppm

85
CMakeLists.txt Normal file
View File

@@ -0,0 +1,85 @@
cmake_minimum_required(VERSION 3.10)
project(nasal VERSION 10.1)
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
# -std=c++17 -Wshadow -Wall
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
add_compile_options(-fPIC)
# generate release executables
set(CMAKE_BUILD_TYPE "Release")
# build nasal used object
set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
${CMAKE_SOURCE_DIR}/src/coroutine.cpp
${CMAKE_SOURCE_DIR}/src/fg_props.cpp
${CMAKE_SOURCE_DIR}/src/bits_lib.cpp
${CMAKE_SOURCE_DIR}/src/io_lib.cpp
${CMAKE_SOURCE_DIR}/src/math_lib.cpp
${CMAKE_SOURCE_DIR}/src/dylib_lib.cpp
${CMAKE_SOURCE_DIR}/src/unix_lib.cpp
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
${CMAKE_SOURCE_DIR}/src/nasal_dbg.cpp
${CMAKE_SOURCE_DIR}/src/nasal_err.cpp
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_import.cpp
${CMAKE_SOURCE_DIR}/src/nasal_lexer.cpp
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
${CMAKE_SOURCE_DIR}/src/repl.cpp)
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
# build nasal
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
target_link_libraries(nasal nasal-object)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(nasal dl)
target_link_libraries(nasal pthread)
endif()
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
add_custom_command(
TARGET nasal POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/build/nasal
${CMAKE_SOURCE_DIR}/nasal
)
endif()
# build module
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
set(MODULE_USED_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(fib module-used-object)
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(key module-used-object)
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(mat module-used-object)
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(nasock module-used-object)

339
LICENSE Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

1143
README.md

File diff suppressed because it is too large Load Diff

1079
doc/README_zh.md Normal file

File diff suppressed because it is too large Load Diff

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)

699
doc/dev.md Normal file
View File

@@ -0,0 +1,699 @@
# __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)
* [v11.0](#version-110-ast-latest)
* [__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-last-update-2022816)
* [__Release Notes__](#release-notes)
* [v8.0](#version-80-release)
* [v11.0](#version-110-release)
## __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.
### version 11.0 ast (latest)
Change ast framework. Now we use visitor pattern.
## __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 (last update 2022/8/16)
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, when first calling the coroutine, `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`.
### __version 11.0 release__
1. Use C++ `std=c++17`.
2. Change framework of ast, using visitor pattern.
3. New ast structure dump info format.
4. Change the way of module export, split library into different modules. Symbols begin with `_` will not be exported.
5. Change `stl` to `std`.
6. Add REPL interpreter.
7. Improve structure of virtual machine, split global symbol stack(stores global symbols' values) and value stack(using in process).
8. Delete operand `op_intg`, add operand `op_repl`.
9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`).
10. New ghost type register process.

632
doc/dev_zh.md Normal file
View File

@@ -0,0 +1,632 @@
# __开发历史记录__
## __目录__
* [__语法分析__](#语法分析)
* [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)
* [v11.0](#version-110-ast-latest)
* [__字节码虚拟机__](#字节码虚拟机)
* [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-last-update-2022816)
* [__发行日志__](#发行日志)
* [v8.0](#version-80-release)
* [v11.0](#version-110-release)
## __语法分析__
有特殊语法检查的`LL(1)`语法分析器。
```javascript
(var a,b,c)=[{b:nil},[1,2],func return 0;];
(a.b,b[0],c)=(1,2,3);
```
这两个表达式有同一个first集所以纯粹的`LL(1)`很难实现这个语言的语法分析。所以我们为其添加了特殊语法检查机制。本质上还是`LL(1)`的内核。
上面这个问题已经解决很久了,不过我最近发现了一个新的语法问题:
```javascript
var f=func(x,y,z){return x+y+z}
(a,b,c)=(0,1,2);
```
这种写法会被错误识别合并成下面这种:
```javascript
var f=func(x,y,z){return x+y+z}(a,b,c)
=(0,1,2);
```
语法分析器会认为这是个严重的语法错误。我在Flightgear中也测试了这个代码它内置的语法分析器也认为这是错误语法。当然我认为这是语法设计中的一个比较严重的缺漏。为了避免这个语法问题只需要添加一个分号就可以了:
```javascript
var f=func(x,y,z){return x+y+z};
^ 就是这里
(a,b,c)=(0,1,2);
```
### version 1.0 parser (last update 2019/10/14)
第一版功能完备的nasal语法分析器完成了。
在version 1.0之前,我多次尝试构建一个正确的语法分析器但总是存在一些问题。
最终我学习了`LL(1)``LL(k)`文法并且在version 0.16(last update 2019/9/14)中完成了一个能识别数学算式的语法分析器。
在version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)中我只是抱着玩的心态在测试语法分析器不过在那之后我还是完成了version 1.0的语法分析器。
__该项目于2019/7/25正式开始__
## __抽象语法树__
### version 1.2 ast (last update 2019/10/31)
抽象语法树在这个版本初步完成。
### version 2.0 ast (last update 2020/8/31)
在这个版本我们基于抽象语法树实现了一个树解释器,并且完成了部分内置函数。
### version 3.0 ast (last update 2020/10/23)
我们重构了抽象语法树的代码,现在可以更容易地读懂代码并进行维护。
这个版本的树解释器用了新的优化方式,所以可以更高效地执行代码。
在这个版本用户已经可以自行添加内置函数。
我想在v4.0发布之后仍然保留这个树解释器,毕竟花了很长时间才写完这坨屎。
### version 5.0 ast (last update 2021/3/7)
我改变想法了,树解释器给维护带来了太大的麻烦。如果想继续保留这个解释器,那么为了兼容性,字节码虚拟机的优化工作会更难推进。
### version 11.0 ast (latest)
改变了语法树的设计模式,采用访问者模式。
## __字节码虚拟机__
![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 (last update 2022/8/16)
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`。
### __version 11.0 release__
1. 使用C++标准 `std=c++17`。
2. 改变语法树设计模式,采用访问者模式。
3. 全新的语法树结构输出格式。
4. 改变了导出模块的方式,把主要的库分成了多个模块。以`_`开头的变量不会被导出。
5. 文件夹`stl`更名为`std`。
6. 添加交互式解释器 (REPL)。
7. 优化虚拟机结构, 将全局数据栈 (存储全局变量的数据) 和操作数据栈 (用于运算) 分离。
8. 删除`op_intg`指令,添加`op_repl`指令。
9. 添加`CMakeLists.txt` (可在`Visual Studio`中使用)。
10. 全新的自定义类型注册流程。

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

64
doc/namespace.md Normal file
View File

@@ -0,0 +1,64 @@
# Nasal Namespace
![feigenbaum](./pic/feigenbaum.png)
## Introduction
In this nasal interpreter,
we use this way below to construct namespaces:
- library is linked directly with the script
- module is wraped by a function generated by linker, and return a hash
## Library
Library file is linked with script file directly, like this:
In `std/lib.nas`:
```nasal
var a = 1;
```
In `example.nas`:
```nasal
var b = 1;
```
At the link stage,
in fact we put the ast of two files together to make a new ast,
so the result is equal to:
```nasal
var a = 1;
var b = 1;
```
## Module
Modules is wraped up by a function,
and return a hash, for example:
In `std/example_module.nas`:
```nasal
var a = 1;
```
We analysed this file and generated the ast.
Then we find all the global symbols.
At last we use the information of all the globals symbols in this file to generate a hash to return.
So the result is equal to:
```nasal
var example_module = func {
# source code begin
var a = 1;
# source code end
return {
a: a
};
}();
```

View File

@@ -0,0 +1,170 @@
<!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-GPLv2-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">GPL-2.0 license</a>.<br/>
</p>
<p>
这个解释器是由<a href="https://github.com/ValKmjolnir">ValKmjolnir</a>用C++11编写的完全没有复用Andy Ross版解释器的代码。但是我们仍然非常感谢Andy为我们带来了这么一款有趣的编程语言。
现在这个项目使用<a href="/license">GPL-2.0协议</a>开源。
</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/bf.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/bf.nas)并绘制出这张图。
在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。
</p>
<p>
The figure below is the feigenbaum-figure generated by ppm script written in nasal.
</p>
<p>
下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 图形。
</p>
</text>
<img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
<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

@@ -10,6 +10,7 @@ nil ::= nil;
id ::= identifier;
number::= number;
string::= string;
bool ::= true | false;
vector::=
'[' {calculation ','} ']'
;
@@ -20,7 +21,7 @@ hashmember::=
id|string ':' calculation
;
function::=
func argument_list expressions
func {argument_list} exprs|expr
;
argument_list::=
'(' [{id ','} ([id '...']|{id '=' scalar ','})] ')'
@@ -35,13 +36,22 @@ expr::=
|continue_expr
|break_expr
;
expressions::=
exprs::=
'{' {expr} '}'
;
calculation::=
calculation '?' calculation ':' calculation
|or_expr
|calculation ('=' | '+=' | '-=' | '*=' | '/=' | '~=') calculation
|bitwise_or
|calculation ('=' | '+=' | '-=' | '*=' | '/=' | '~=' | '^=' | '&=' | '|=') calculation
;
bitwise_or::=
bitwise_xor '|' bitwise_xor
;
bitwise_xor::=
bitwise_and '^' bitwise_and
;
bitwise_and::=
or_expr '&' or_expr
;
or_expr::=
and_expr or and_expr
@@ -59,15 +69,16 @@ multive_expr::=
(unary|scalar) ('*' | '/') (unary|scalar)
;
unary::=
('-'|'!') (unary|scalar)
('-'|'!'|'~') (unary|scalar)
;
scalar::=
function {call_scalar}
|[func] identifier {call_scalar}
|vector {call_scalar}
|hash {call_scalar}
|number
|string
|nil
|bool
|'(' calculation ')' {call_scalar}
;
call_scalar::=
@@ -108,18 +119,18 @@ loop::=
|forei_loop
;
while_loop::=
while '(' calculation ')' expressions
while '(' calculation ')' exprs
;
for_loop::=
for '(' [definition|calculation] ';' [calculation] ';' [calculation] ')' expressions
for '(' [definition|calculation] ';' [calculation] ';' [calculation] ')' exprs
;
forei_loop::=
(forindex | foreach) '(' (definition | calculation) ';' calculation ')' expressions
(forindex | foreach) '(' (definition | calculation) ';' calculation ')' exprs
;
conditional::=
if '(' calculation ')' expressions
{elsif '(' calculation ')' expressions}
[else expressions]
if '(' calculation ')' exprs
{elsif '(' calculation ')' exprs}
[else exprs]
;
continue_expr::=
continue

BIN
doc/pic/benchmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/pic/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
doc/pic/feigenbaum.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
doc/pic/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

BIN
doc/pic/mandelbrot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
doc/pic/social.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

167
lib.nas
View File

@@ -1,167 +0,0 @@
var import=func(filename)
{
nasal_call_import(filename);
return nil;
}
var print=func(elements...)
{
nasal_call_builtin_std_cout(elements);
return nil;
};
var append=func(vector,elements...)
{
nasal_call_builtin_push_back(vector,elements);
return nil;
}
var setsize=func(vector,size)
{
nasal_call_builtin_set_size(vector,size);
return nil;
}
var system=func(str)
{
nasal_call_builtin_system(str);
return;
}
var input=func()
{
return nasal_call_builtin_input();
}
var sleep=func(duration)
{
nasal_call_builtin_sleep(duration);
return;
}
var split=func(delimeter,string)
{
return nasal_call_builtin_split(delimeter,string);
}
var rand=func(seed=nil)
{
return nasal_call_builtin_rand(seed);
}
var id=func(thing)
{
return nasal_call_builtin_get_id(thing);
}
var int=func(value)
{
return nasal_call_builtin_trans_int(value);
}
var num=func(value)
{
return nasal_call_builtin_trans_num(value);
}
var pop=func(vector)
{
return nasal_call_builtin_pop_back(vector);
}
var str=func(number)
{
return nasal_call_builtin_trans_str(number);
}
var size=func(object)
{
return nasal_call_builtin_size(object);
}
var contains=func(hash,key)
{
return nasal_call_builtin_contains(hash,key);
}
var delete=func(hash,key)
{
nasal_call_builtin_delete(hash,key);
return;
}
var keys=func(hash)
{
return nasal_call_builtin_get_keys(hash);
}
var time=func(begin_time)
{
return nasal_call_builtin_time(begin_time);
}
var die=func(str)
{
nasal_call_builtin_die(str);
return nil;
}
var typeof=func(object)
{
return nasal_call_builtin_type(object);
}
var substr=func(str,begin,length)
{
return nasal_call_builtin_substr(str,begin,length);
}
var io=
{
fin:func(filename)
{
return nasal_call_builtin_finput(filename);
},
fout:func(filename,str)
{
nasal_call_builtin_foutput(filename,str);
return;
}
};
var bits=
{
bitxor:func(a,b)
{
return nasal_call_builtin_xor(a,b);
},
bitand:func(a,b)
{
return nasal_call_builtin_and(a,b);
},
bitor:func(a,b)
{
return nasal_call_builtin_or(a,b);
},
bitnand:func(a,b)
{
return nasal_call_builtin_nand(a,b);
},
bitnot:func(a)
{
return nasal_call_builtin_not(a);
}
};
var math=
{
e:2.7182818284590452354,
pi:3.14159265358979323846264338327950288,
sin:func(x)
{
return nasal_call_builtin_sin(x);
},
cos:func(x)
{
return nasal_call_builtin_cos(x);
},
tan:func(x)
{
return nasal_call_builtin_tan(x);
},
exp:func(x)
{
return nasal_call_builtin_exp(x);
},
ln:func(x)
{
return nasal_call_builtin_cpp_math_ln(x);
},
sqrt:func(x)
{
return nasal_call_builtin_cpp_math_sqrt(x);
},
atan2:func(x,y)
{
return nasal_call_builtin_cpp_atan2(x,y);
},
};

221
main.cpp
View File

@@ -1,221 +0,0 @@
#include "nasal.h"
nasal_lexer lexer;
nasal_parse parse;
nasal_import import;
std::string inputfile="null";
nasal_runtime runtime;
nasal_codegen code_generator;
nasal_bytecode_vm bytevm;
void help()
{
std::cout<<">> [\"file\"] input a file name.\n";
std::cout<<">> [help ] show help.\n";
std::cout<<">> [clear ] clear the screen.\n";
std::cout<<">> [del ] clear the input filename.\n";
std::cout<<">> [lex ] use lexer to turn code into tokens.\n";
std::cout<<">> [ast ] do parsing and check the abstract syntax tree.\n";
std::cout<<">> [run ] run abstract syntax tree.\n";
std::cout<<">> [code ] show byte code.\n";
std::cout<<">> [exec ] execute program on bytecode vm.\n";
std::cout<<">> [logo ] print logo of nasal .\n";
std::cout<<">> [exit ] quit nasal interpreter.\n";
return;
}
void logo()
{
std::cout<<" __ _ \n";
std::cout<<" /\\ \\ \\__ _ ___ __ _| | \n";
std::cout<<" / \\/ / _` / __|/ _` | | \n";
std::cout<<" / /\\ / (_| \\__ \\ (_| | | \n";
std::cout<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n";
return;
}
void del_func()
{
lexer.clear();
parse.clear();
inputfile="null";
std::cout<<">> [Delete] complete.\n";
return;
}
void die(std::string stage,std::string filename)
{
std::cout<<">> ["<<stage<<"] in <\""<<filename<<"\">: error(s) occurred,stop.\n";
return;
}
void lex_func()
{
lexer.openfile(inputfile);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",inputfile);
return;
}
lexer.print_token();
return;
}
void ast_print()
{
lexer.openfile(inputfile);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",inputfile);
return;
}
parse.set_toklist(lexer.get_token_list());
parse.main_process();
if(parse.get_error())
{
die("parse",inputfile);
return;
}
parse.get_root().print_ast(0);
return;
}
void runtime_start()
{
lexer.openfile(inputfile);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",inputfile);
return;
}
parse.set_toklist(lexer.get_token_list());
parse.main_process();
if(parse.get_error())
{
die("parse",inputfile);
return;
}
import.link(parse.get_root());
if(import.get_error())
{
die("import",inputfile);
return;
}
runtime.set_root(import.get_root());
runtime.run();
return;
}
void show_bytecode()
{
lexer.openfile(inputfile);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",inputfile);
return;
}
parse.set_toklist(lexer.get_token_list());
parse.main_process();
if(parse.get_error())
{
die("parse",inputfile);
return;
}
import.link(parse.get_root());
if(import.get_error())
{
die("import",inputfile);
return;
}
code_generator.main_progress(import.get_root());
code_generator.print_byte_code();
return;
}
void execute()
{
lexer.openfile(inputfile);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",inputfile);
return;
}
parse.set_toklist(lexer.get_token_list());
parse.main_process();
if(parse.get_error())
{
die("parse",inputfile);
return;
}
import.link(parse.get_root());
if(import.get_error())
{
die("import",inputfile);
return;
}
code_generator.main_progress(import.get_root());
bytevm.run(
code_generator.get_string_table(),
code_generator.get_number_table(),
code_generator.get_exec_code()
);
return;
}
int main()
{
std::string command;
#ifdef _WIN32
// use chcp 65001 to use unicode io
system("chcp 65001");
system("cls");
#endif
logo();
std::cout<<">> Nasal interpreter ver 3.0 .\n";
std::cout<<">> Code: https://github.com/ValKmjolnir/Nasal-Interpreter\n";
std::cout<<">> Info: http://wiki.flightgear.org/Nasal_scripting_language\n";
std::cout<<">> Input \"help\" to get help .\n";
while(1)
{
std::cout<<">> ";
std::cin>>command;
if(command=="help")
help();
else if(command=="clear")
{
#ifdef _WIN32
system("cls");
#endif
#ifdef _linux_
system("clear");
#endif
#ifdef TARGET_OS_MAC
system("clear");
#endif
}
else if(command=="del")
del_func();
else if(command=="lex")
lex_func();
else if(command=="ast")
ast_print();
else if(command=="run")
runtime_start();
else if(command=="code")
show_bytecode();
else if(command=="exec")
execute();
else if(command=="logo")
logo();
else if(command=="exit")
break;
else
inputfile=command;
}
return 0;
}

262
makefile Normal file
View File

@@ -0,0 +1,262 @@
STD = c++17
OS = $(shell uname)
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
else
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
endif
CPPFLAGS = -I .
NASAL_HEADER=\
src/ast_dumper.h\
src/ast_visitor.h\
src/nasal_ast.h\
src/nasal_builtin.h\
src/nasal_codegen.h\
src/nasal_dbg.h\
src/nasal_err.h\
src/nasal_gc.h\
src/nasal_import.h\
src/nasal_lexer.h\
src/nasal_opcode.h\
src/nasal_parse.h\
src/nasal_vm.h\
src/nasal.h\
src/optimizer.h\
src/symbol_finder.h\
src/fg_props.h\
src/bits_lib.h\
src/io_lib.h\
src/math_lib.h\
src/dylib_lib.h\
src/unix_lib.h\
src/coroutine.h\
src/repl.h
NASAL_OBJECT=\
build/nasal_err.o\
build/nasal_ast.o\
build/ast_visitor.o\
build/bits_lib.o\
build/ast_dumper.o\
build/nasal_lexer.o\
build/nasal_parse.o\
build/nasal_import.o\
build/optimizer.o\
build/nasal_opcode.o\
build/symbol_finder.o\
build/nasal_codegen.o\
build/nasal_misc.o\
build/nasal_gc.o\
build/nasal_builtin.o\
build/fg_props.o\
build/io_lib.o\
build/math_lib.o\
build/unix_lib.o\
build/dylib_lib.o\
build/coroutine.o\
build/nasal_vm.o\
build/nasal_dbg.o\
build/repl.o\
build/main.o
# for test
nasal: $(NASAL_OBJECT) | build
@if [ OS = "Darwin" ]; then\
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread -stdlib=libc++ -static-libstdc++;\
else\
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread;\
fi
nasal.exe: $(NASAL_OBJECT) | build
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
build:
@ if [ ! -d build ]; then mkdir build; fi
build/main.o: $(NASAL_HEADER) src/main.cpp | build
$(CXX) $(CXXFLAGS) src/main.cpp -o build/main.o
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_misc.cpp -o build/nasal_misc.o
build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
$(CXX) $(CXXFLAGS) src/repl.cpp -o build/repl.o
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
build/nasal_import.o: \
src/nasal.h\
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_parse.h\
src/nasal_import.h src/nasal_import.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_import.cpp -o build/nasal_import.o
build/nasal_lexer.o: \
src/nasal.h\
src/repl.h\
src/nasal_err.h\
src/nasal_lexer.h src/nasal_lexer.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_lexer.cpp -o build/nasal_lexer.o
build/nasal_ast.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h src/nasal_ast.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_ast.cpp -o build/nasal_ast.o
build/nasal_builtin.o: \
src/nasal.h\
src/nasal_gc.h\
src/nasal_builtin.h src/nasal_builtin.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
build/coroutine.o: \
src/nasal.h\
src/nasal_gc.h\
src/coroutine.h src/coroutine.cpp | build
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
build/bits_lib.o: \
src/nasal.h\
src/nasal_gc.h\
src/bits_lib.h src/bits_lib.cpp | build
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
build/math_lib.o: \
src/nasal.h\
src/nasal_gc.h\
src/math_lib.h src/math_lib.cpp | build
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
build/io_lib.o: \
src/nasal.h\
src/nasal_gc.h\
src/io_lib.h src/io_lib.cpp | build
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
build/dylib_lib.o: \
src/nasal.h\
src/nasal_gc.h\
src/dylib_lib.h src/dylib_lib.cpp | build
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
build/unix_lib.o: \
src/nasal.h\
src/nasal_gc.h\
src/unix_lib.h src/unix_lib.cpp | build
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
build/fg_props.o: \
src/nasal.h\
src/nasal_gc.h\
src/fg_props.h src/fg_props.cpp | build
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
build/nasal_codegen.o: $(NASAL_HEADER) src/nasal_codegen.h src/nasal_codegen.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_codegen.cpp -o build/nasal_codegen.o
build/nasal_opcode.o: \
src/nasal.h\
src/nasal_builtin.h\
src/nasal_opcode.h src/nasal_opcode.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_opcode.cpp -o build/nasal_opcode.o
build/nasal_parse.o: \
src/nasal.h\
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_err.h\
src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
$(CXX) $(CXXFLAGS) src/nasal_parse.cpp -o build/nasal_parse.o
build/optimizer.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
$(CXX) $(CXXFLAGS) src/optimizer.cpp -o build/optimizer.o
build/symbol_finder.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
$(CXX) $(CXXFLAGS) src/symbol_finder.cpp -o build/symbol_finder.o
build/ast_visitor.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h src/ast_visitor.cpp | build
$(CXX) $(CXXFLAGS) src/ast_visitor.cpp -o build/ast_visitor.o
build/ast_dumper.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/ast_dumper.h src/ast_dumper.cpp | build
$(CXX) $(CXXFLAGS) src/ast_dumper.cpp -o build/ast_dumper.o
build/nasal_vm.o: $(NASAL_HEADER) src/nasal_vm.h src/nasal_vm.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_vm.cpp -o build/nasal_vm.o
build/nasal_dbg.o: $(NASAL_HEADER) src/nasal_dbg.h src/nasal_dbg.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_dbg.cpp -o build/nasal_dbg.o
.PHONY: clean
clean:
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
@ rm $(NASAL_OBJECT)
.PHONY: test
test:nasal
@ ./nasal -e test/ascii-art.nas
@ ./nasal -t -d test/bfs.nas
@ ./nasal -t test/bigloop.nas
@ ./nasal -t test/bp.nas
@ ./nasal -d test/calc.nas
@ ./nasal -e test/choice.nas
@ ./nasal -e test/class.nas
@ ./nasal -t -d test/console3D.nas 20
@ ./nasal -e test/coroutine.nas
@ ./nasal -t -d test/datalog.nas
@ ./nasal -e test/diff.nas
@ ./nasal -e test/donuts.nas 15
-@ ./nasal -d test/exception.nas
@ ./nasal -t -d test/fib.nas
@ ./nasal -e test/filesystem.nas
@ ./nasal -t -d test/globals_test.nas
@ ./nasal -d test/hexdump.nas
@ ./nasal -e test/json.nas
@ ./nasal -e test/leetcode1319.nas
@ ./nasal -d test/lexer.nas
@ ./nasal -d test/life.nas
@ ./nasal -t test/loop.nas
@ ./nasal -t test/mandelbrot.nas
@ ./nasal -t test/md5_self.nas
@ ./nasal -t -d test/md5compare.nas
@ ./nasal -d test/module_test.nas
@ ./nasal -e test/nasal_test.nas
@ ./nasal -t -d test/occupation.nas 2
@ ./nasal -t -d test/pi.nas
@ ./nasal -t -d test/prime.nas
@ ./nasal -e test/qrcode.nas
@ ./nasal -t -d test/quick_sort.nas
@ ./nasal -e test/scalar.nas hello world
@ ./nasal -e test/trait.nas
@ ./nasal -t -d test/turingmachine.nas
@ ./nasal -d test/wavecollapse.nas
@ ./nasal test/word_collector.nas test/md5compare.nas
@ ./nasal -t -d test/ycombinator.nas

96
module/fib.cpp Normal file
View File

@@ -0,0 +1,96 @@
// module for test
#include <iostream>
#include "../src/nasal.h"
namespace nasal {
namespace fib_module {
double fibonaci(double x) {
if (x<=2) {
return x;
}
return fibonaci(x-1)+fibonaci(x-2);
}
var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib", "lack arguments");
}
var num = args[0];
return var::num(fibonaci(num.tonum()));
}
var quick_fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("quick_fib","lack arguments");
}
double num = args[0].tonum();
if (num<2) {
return var::num(num);
}
double a = 1, b = 1, res = 0;
for(double i = 1; i<num; ++i) {
res = a+b;
a = b;
b = res;
}
return var::num(res);
}
const auto ghost_for_test = "ghost_for_test";
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<u32*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj);
res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.objchk(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*((u32*)res.obj().ptr) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.objchk(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.obj() << " result = "
<< *((u32*)res.obj().ptr) << "\n";
return nil;
}
module_func_info func_tbl[] = {
{"fib", fib},
{"quick_fib", quick_fib},
{"create_ghost", create_new_ghost},
{"set_ghost", set_new_ghost},
{"print_ghost", print_new_ghost},
{nullptr, nullptr}
};
}
extern "C" module_func_info* get() {
return fib_module::func_tbl;
}
}

111
module/keyboard.cpp Normal file
View File

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

54
module/libfib.nas Normal file
View File

@@ -0,0 +1,54 @@
import.std.dylib;
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var _fib = _dl.fib;
var _qfib = _dl.quick_fib;
var _create_ghost = _dl.create_ghost;
var _set_ghost = _dl.set_ghost;
var _print_ghost = _dl.print_ghost;
var _zero_call = dylib.limitcall(0);
var _call = dylib.limitcall(1);
var _test_call = dylib.limitcall(2);
var fib = func(x) {
return _call(_fib, x)
}
var qfib = func(x) {
return _call(_qfib, x)
}
var create_ghost = func() {
return _zero_call(_create_ghost)
}
var set_ghost = func(object, x) {
return _test_call(_set_ghost, object, x)
}
var print_ghost = func(object) {
return _call(_print_ghost, object)
}
var test_ghost=func() {
var ghost = create_ghost();
print_ghost(nil); # err
print_ghost(ghost); # random
set_ghost(nil, 114); # err
set_ghost(ghost, 114); # success
print_ghost(ghost); # 114
}

18
module/libkey.nas Normal file
View File

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

66
module/libmat.nas Normal file
View File

@@ -0,0 +1,66 @@
import.std.dylib;
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));
var _vec2 = _dl.nas_vec2;
var _vec3 = _dl.nas_vec3;
var (_vec2add, _vec2sub, _vec2mul, _vec2div, _vec2neg, _vec2norm, _vec2len, _vec2dot)=(
_dl.nas_vec2_add,
_dl.nas_vec2_sub,
_dl.nas_vec2_mult,
_dl.nas_vec2_div,
_dl.nas_vec2_neg,
_dl.nas_vec2_norm,
_dl.nas_vec2_len,
_dl.nas_vec2_dot
);
var (_vec3add, _vec3sub, _vec3mul, _vec3div, _vec3neg, _vec3norm, _vec3len, _vec3dot)=(
_dl.nas_vec3_add,
_dl.nas_vec3_sub,
_dl.nas_vec3_mult,
_dl.nas_vec3_div,
_dl.nas_vec3_neg,
_dl.nas_vec3_norm,
_dl.nas_vec3_len,
_dl.nas_vec3_dot
);
var (_rotate_x, _rotate_y, _rotate_z)=(
_dl.nas_rotate_x,
_dl.nas_rotate_y,
_dl.nas_rotate_z
);
var (_invoke_i, _invoke_ii, _invoke_iii)=(
dylib.limitcall(1),
dylib.limitcall(2),
dylib.limitcall(3)
);
var vec2 = {
new: func(x, y) {return _invoke_ii(_vec2, x, y);},
add: func(v0, v1) {return _invoke_ii(_vec2add, v0, v1);},
sub: func(v0, v1) {return _invoke_ii(_vec2sub, v0, v1);},
mul: func(v0, v1) {return _invoke_ii(_vec2mul, v0, v1);},
div: func(v0, v1) {return _invoke_ii(_vec2div, v0, v1);},
neg: func(v0) {return _invoke_i(_vec2neg, v0);},
norm: func(v0) {return _invoke_i(_vec2norm, v0);},
len: func(v0) {return _invoke_i(_vec2len, v0);},
dot: func(v0, v1) {return _invoke_ii(_vec2dot, v0, v1);}
};
var vec3 = {
new: func(x, y, z) {return _invoke_iii(_vec3, x, y, z);},
add: func(v0, v1) {return _invoke_ii(_vec3add, v0, v1);},
sub: func(v0, v1) {return _invoke_ii(_vec3sub, v0, v1);},
mul: func(v0, v1) {return _invoke_ii(_vec3mul, v0, v1);},
div: func(v0, v1) {return _invoke_ii(_vec3div, v0, v1);},
neg: func(v0) {return _invoke_i(_vec3neg, v0);},
norm: func(v0) {return _invoke_i(_vec3norm, v0);},
len: func(v0) {return _invoke_i(_vec3len, v0);},
rx: func(v0, angle) {return _invoke_ii(_rotate_x, v0, angle);},
ry: func(v0, angle) {return _invoke_ii(_rotate_y, v0, angle);},
rz: func(v0, angle) {return _invoke_ii(_rotate_z, v0, angle);},
dot: func(v0, v1) {return _invoke_ii(_vec3dot, v0, v1);}
};

117
module/libsock.nas Normal file
View File

@@ -0,0 +1,117 @@
import.std.dylib;
var socket=func(){
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
var sock=lib.nas_socket;
var closesocket=lib.nas_closesocket;
var shutdown=lib.nas_shutdown;
var bind=lib.nas_bind;
var listen=lib.nas_listen;
var connect=lib.nas_connect;
var accept=lib.nas_accept;
var send=lib.nas_send;
var sendto=lib.nas_sendto;
var recv=lib.nas_recv;
var recvfrom=lib.nas_recvfrom;
var errno=lib.nas_errno;
var (invoke,invoke_i,invoke_ii,invoke_iii,invoke_iiii,invoke_iiiii)=(
dylib.limitcall(0),
dylib.limitcall(1),
dylib.limitcall(2),
dylib.limitcall(3),
dylib.limitcall(4),
dylib.limitcall(5),
);
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 invoke_iii(sock,af,type,proto);
},
closesocket:func(sd){
return invoke_i(closesocket,sd);
},
shutdown: func(sd,how){
return invoke_ii(shutdown,sd,how);
},
bind: func(sd,ip,port){
return invoke_iii(bind,sd,ip,port);
},
listen: func(sd,backlog){
return invoke_ii(listen,sd,backlog);
},
connect: func(sd,hostname,port){
return invoke_iii(connect,sd,hostname,port);
},
accept: func(sd){
return invoke_i(accept,sd);
},
send: func(sd,buff,flags=0){
return invoke_iii(send,sd,buff,flags);
},
sendto: func(sd,hostname,port,buff,flags=0){
return invoke_iiiii(sendto,sd,hostname,port,buff,flags);
},
recv: func(sd,len,flags=0){
return invoke_iii(recv,sd,len,flags);
},
recvfrom: func(sd,len,flags=0){
return invoke_iii(recvfrom,sd,len,flags);
},
errno: func(){
return invoke(errno);
}
};
}();

74
module/makefile Normal file
View File

@@ -0,0 +1,74 @@
.PHONY=clean all winall
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
used_header = ../src/nasal.h ../src/nasal_gc.h
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
STD = c++17
OS = $(shell uname)
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
else
CXXFLAGS = -std=$(STD) -c -O3 -fPIC
endif
all: $(dynamic_libs_so)
@ echo "[Compiling] done"
winall: $(dynamic_libs_dll)
@ echo [Compiling] done
libfib.so: fib.cpp $(used_header) $(used_object)
@ echo "[Compiling] libfib.so"
@ $(CXX) $(CXXFLAGS) fib.cpp -o fib.o
@ $(CXX) -shared -o libfib.so fib.o $(used_object)
@ rm fib.o
libfib.dll: fib.cpp $(used_header) $(used_object)
@ echo [Compiling] libfib.dll
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
@ del fib.o
libkey.so: keyboard.cpp $(used_header) $(used_object)
@ echo "[Compiling] libkey.so"
@ $(CXX) $(CXXFLAGS) keyboard.cpp -o keyboard.o
@ $(CXX) -shared -o libkey.so keyboard.o $(used_object)
@ rm keyboard.o
libkey.dll: keyboard.cpp $(used_header) $(used_object)
@ echo [Compiling] libkey.dll
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
@ del keyboard.o
libnasock.so: nasocket.cpp $(used_header) $(used_object)
@ echo "[Compiling] libnasock.so"
@ $(CXX) $(CXXFLAGS) nasocket.cpp -o nasocket.o
@ $(CXX) -shared -o libnasock.so nasocket.o $(used_object)
@ rm nasocket.o
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
@ echo [Compiling] libnasock.dll
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
@ del nasocket.o
libmat.so: matrix.cpp $(used_header) $(used_object)
@ echo "[Compiling] libmat.so"
@ $(CXX) $(CXXFLAGS) matrix.cpp -o matrix.o
@ $(CXX) -shared -o libmat.so matrix.o $(used_object)
@ rm matrix.o
libmat.dll: matrix.cpp $(used_header) $(used_object)
@ echo [Compiling] libmat.dll
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
@ del matrix.o
clean:
@ echo "[clean] libfib.so" && if [ -e libfib.so ]; then rm libfib.so; fi
@ echo "[clean] libkey.so" && if [ -e libkey.so ]; then rm libkey.so; fi
@ echo "[clean] libnasock.so" && if [ -e libnasock.so ]; then rm libnasock.so; fi
@ echo "[clean] libmat.so" && if [ -e libmat.so ]; then rm libmat.so; fi
@ echo "[clean] libfib.dll" &&if [ -e libfib.dll ]; then rm libfib.dll; fi
@ echo "[clean] libkey.dll" &&if [ -e libkey.dll ]; then rm libkey.dll; fi
@ echo "[clean] libnasock.dll" &&if [ -e libnasock.dll ]; then rm libnasock.dll; fi
@ echo "[clean] libmat.dll" &&if [ -e libmat.dll ]; then rm libmat.dll; fi

300
module/matrix.cpp Normal file
View File

@@ -0,0 +1,300 @@
#include "../src/nasal.h"
#include <cmath>
namespace nasal {
var nas_vec2(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(args[0]);
res.vec().elems.push_back(args[1]);
return res;
}
var nas_vec3(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(args[0]);
res.vec().elems.push_back(args[1]);
res.vec().elems.push_back(args[2]);
return res;
}
var nas_vec2_add(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
return res;
}
var nas_vec2_sub(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
return res;
}
var nas_vec2_mult(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
return res;
}
var nas_vec2_div(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
return res;
}
var nas_vec2_neg(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
return res;
}
var nas_vec2_norm(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
return nil;
auto x = v0[0].num();
auto y = v0[1].num();
auto t = std::sqrt(x*x+y*y);
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
return res;
}
var nas_vec2_len(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
return nil;
auto x = v0[0].num();
auto y = v0[1].num();
return var::num(std::sqrt(x*x+y*y));
}
var nas_vec2_dot(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num());
}
var nas_vec3_add(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num()));
return res;
}
var nas_vec3_sub(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num()));
return res;
}
var nas_vec3_mult(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num()));
return res;
}
var nas_vec3_div(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num()));
return res;
}
var nas_vec3_neg(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
res.vec().elems.push_back(var::num(-v0[2].num()));
return res;
}
var nas_vec3_norm(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto x = v0[0].num();
auto y = v0[1].num();
auto z = v0[2].num();
auto t = std::sqrt(x*x+y*y+z*z);
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
res.vec().elems.push_back(var::num(z/t));
return res;
}
var nas_vec3_len(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto x = v0[0].num();
auto y = v0[1].num();
auto z = v0[2].num();
return var::num(std::sqrt(x*x+y*y+z*z));
}
var nas_rotate_x(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()));
res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
return res;
}
var nas_rotate_y(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[1].num()));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle)));
return res;
}
var nas_rotate_z(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()));
return res;
}
var nas_vec3_dot(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num()+v0[2].num()*v1[2].num());
}
module_func_info func_tbl[] = {
{"nas_vec2", nas_vec2},
{"nas_vec2_add", nas_vec2_add},
{"nas_vec2_sub", nas_vec2_sub},
{"nas_vec2_mult", nas_vec2_mult},
{"nas_vec2_div", nas_vec2_div},
{"nas_vec2_neg", nas_vec2_neg},
{"nas_vec2_norm", nas_vec2_norm},
{"nas_vec2_len", nas_vec2_len},
{"nas_vec2_dot", nas_vec2_dot},
{"nas_vec3", nas_vec3},
{"nas_vec3_add", nas_vec3_add},
{"nas_vec3_sub", nas_vec3_sub},
{"nas_vec3_mult", nas_vec3_mult},
{"nas_vec3_div", nas_vec3_div},
{"nas_vec3_neg", nas_vec3_neg},
{"nas_vec3_norm", nas_vec3_norm},
{"nas_vec3_len", nas_vec3_len},
{"nas_rotate_x", nas_rotate_x},
{"nas_rotate_y", nas_rotate_y},
{"nas_rotate_z", nas_rotate_z},
{"nas_vec3_dot", nas_vec3_dot},
{nullptr, nullptr}
};
extern "C" module_func_info* get() {
return func_tbl;
}
}

252
module/nasocket.cpp Normal file
View File

@@ -0,0 +1,252 @@
#include "../src/nasal.h"
#ifndef _MSC_VER
#include <unistd.h>
#endif
#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
namespace nasal {
var nas_socket(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number");
int sd = socket(args[0].num(), args[1].num(), args[2].num());
return var::num(static_cast<double>(sd));
}
var nas_closesocket(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
return nas_err("closesocket", "\"sd\" should be number");
#ifdef _WIN32
return var::num(static_cast<double>(closesocket(args[0].num())));
#else
return var::num(static_cast<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 var::num(static_cast<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 var::num(static_cast<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 var::num(static_cast<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 var::num(static_cast<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"] = var::num(static_cast<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 var::num(static_cast<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 var::num(static_cast<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[static_cast<int>(args[1].num())];
auto recvsize = recv(args[0].num(), buf,args[1].num(), args[2].num());
hash["size"] = var::num(static_cast<double>(recvsize));
buf[recvsize>=0? recvsize:0] = 0;
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[static_cast<int>(args[1].num()+1)];
#ifdef _WIN32
auto recvsize = recvfrom(
args[0].num(),
buf,
args[1].num(),
args[2].num(),
(sockaddr*)&addr,
&socklen
);
#else
auto recvsize = recvfrom(
args[0].num(),
buf,
args[1].num(),
args[2].num(),
(sockaddr*)&addr,
(socklen_t*)&socklen
);
#endif
hash["size"] = var::num(static_cast<double>(recvsize));
buf[recvsize>=0? recvsize:0] = 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));
}
module_func_info func_tbl[] = {
{"nas_socket", nas_socket},
{"nas_closesocket", nas_closesocket},
{"nas_shutdown", nas_shutdown},
{"nas_bind", nas_bind},
{"nas_listen", nas_listen},
{"nas_connect", nas_connect},
{"nas_accept", nas_accept},
{"nas_send", nas_send},
{"nas_sendto", nas_sendto},
{"nas_recv", nas_recv},
{"nas_recvfrom", nas_recvfrom},
{"nas_errno", nas_errno},
{nullptr, nullptr}
};
extern "C" module_func_info* get() {
return func_tbl;
}
}

140
nasal.h
View File

@@ -1,140 +0,0 @@
#ifndef __NASAL_H__
#define __NASAL_H__
#pragma GCC optimize(2)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <thread>
#include <list>
#include <stack>
#include <queue>
#include <vector>
#include <map>
/*
check if a string can be converted to a number
if this string cannot be converted to a number,it will return nan
*/
inline double hex_to_double(std::string str)
{
double ret=0;
for(int i=2;str[i];++i)
{
ret*=16;
if('0'<=str[i] && str[i]<='9')
ret+=(str[i]-'0');
else if('a'<=str[i] && str[i]<='f')
ret+=(str[i]-'a'+10);
else if('A'<=str[i] && str[i]<='F')
ret+=(str[i]-'A'+10);
else
return std::nan("");
}
return ret;
}
inline double oct_to_double(std::string str)
{
double ret=0;
for(int i=2;str[i];++i)
{
ret*=8;
if('0'<=str[i] && str[i]<='8')
ret+=(str[i]-'0');
else
return std::nan("");
}
return ret;
}
inline double dec_to_double(std::string str,int len)
{
double ret=0;
int i=0;
while('0'<=str[i] && str[i]<='9' && i<len)
ret=ret*10+(str[i++]-'0');
if(i==len) return ret;
if(str[i]!='.' && str[i]!='e' && str[i]!='E')
return std::nan("");
if(str[i]=='.')
{
++i;
if(i==len) return std::nan("");
double num_pow=0.1;
while('0'<=str[i] && str[i]<='9' && i<len)
{
ret+=num_pow*(str[i++]-'0');
num_pow*=0.1;
}
}
if(i==len) return ret;
if(str[i]!='e' && str[i]!='E')
return std::nan("");
++i;
if(i==len) return std::nan("");
double negative=(str[i]=='-'? -1:1);
if(str[i]=='-' || str[i]=='+')
++i;
if(i==len) return std::nan("");
double num_pow=0;
for(;i<len;++i)
{
if('0'<=str[i] && str[i]<='9')
num_pow=num_pow*10+(str[i]-'0');
else
return std::nan("");
}
return ret*std::pow(10,negative*num_pow);
}
double trans_string_to_number(std::string str)
{
double is_negative=1;
int len=str.length();
double ret_num=0;
if(!len) return std::nan("");
if(str[0]=='-' || str[0]=='+')
{
if(len==1) return std::nan("");
is_negative=(str[0]=='-'?-1:1);
str=str.substr(1,len--);
}
if(len>1 && str[0]=='0' && str[1]=='x')
ret_num=hex_to_double(str);
else if(len>1 && str[0]=='0' && str[1]=='o')
ret_num=oct_to_double(str);
else
ret_num=dec_to_double(str,len);
return is_negative*ret_num;
}
/*
trans_number_to_string:
convert number to string
*/
std::string trans_number_to_string(double number)
{
std::string res;
std::stringstream ss;
ss<<number;
ss>>res;
return res;
}
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "nasal_runtime.h"
#include "nasal_codegen.h"
#include "nasal_bytecode_vm.h"
#endif

View File

@@ -1,233 +0,0 @@
#ifndef __NASAL_AST_H__
#define __NASAL_AST_H__
enum ast_node
{
ast_null=0,ast_root,ast_block,
ast_nil,ast_number,ast_string,ast_identifier,ast_function,ast_hash,ast_vector,
ast_hashmember,ast_call,ast_call_hash,ast_call_vec,ast_call_func,ast_subvec,
ast_args,ast_default_arg,ast_dynamic_id,
ast_and,ast_or,
ast_equal,ast_add_equal,ast_sub_equal,ast_mult_equal,ast_div_equal,ast_link_equal,
ast_cmp_equal,ast_cmp_not_equal,
ast_less_than,ast_less_equal,
ast_greater_than,ast_greater_equal,
ast_add,ast_sub,ast_mult,ast_div,ast_link,
ast_unary_sub,ast_unary_not,
ast_trinocular,
ast_for,ast_forindex,ast_foreach,ast_while,ast_new_iter,
ast_conditional,ast_if,ast_elsif,ast_else,
ast_multi_id,ast_multi_scalar,
ast_definition,ast_multi_assign,
ast_continue,ast_break,ast_return
};
std::string ast_str(int type)
{
std::string str;
switch(type)
{
case ast_null: str="null";break;
case ast_root: str="root";break;
case ast_block: str="block";break;
case ast_nil: str="nil";break;
case ast_number: str="number";break;
case ast_string: str="string";break;
case ast_identifier: str="id";break;
case ast_function: str="function";break;
case ast_hash: str="hash";break;
case ast_vector: str="vector";break;
case ast_hashmember: str="hashmember";break;
case ast_call: str="call";break;
case ast_call_hash: str="call_hash";break;
case ast_call_vec: str="call_vector";break;
case ast_call_func: str="call_func";break;
case ast_subvec: str="subvec";break;
case ast_args: str="arguments";break;
case ast_default_arg: str="default_arg";break;
case ast_dynamic_id: str="dynamic_id";break;
case ast_and: str="and";break;
case ast_or: str="or";break;
case ast_equal: str="=";break;
case ast_add_equal: str="+=";break;
case ast_sub_equal: str="-=";break;
case ast_mult_equal: str="*=";break;
case ast_div_equal: str="/=";break;
case ast_link_equal: str="~=";break;
case ast_cmp_equal: str="==";break;
case ast_cmp_not_equal:str="!=";break;
case ast_less_than: str="<";break;
case ast_less_equal: str="<=";break;
case ast_greater_than: str=">";break;
case ast_greater_equal:str=">=";break;
case ast_add: str="+";break;
case ast_sub: str="-";break;
case ast_mult: str="*";break;
case ast_div: str="/";break;
case ast_link: str="~";break;
case ast_unary_sub: str="unary-";break;
case ast_unary_not: str="unary!";break;
case ast_trinocular: str="trinocular";break;
case ast_for: str="for";break;
case ast_forindex: str="forindex";break;
case ast_foreach: str="foreach";break;
case ast_while: str="while";break;
case ast_new_iter: str="new_iterator";break;
case ast_conditional: str="conditional";break;
case ast_if: str="if";break;
case ast_elsif: str="elsif";break;
case ast_else: str="else";break;
case ast_multi_id: str="multi_id";break;
case ast_multi_scalar: str="multi_scalar";break;
case ast_definition: str="definition";break;
case ast_multi_assign: str="multi_assignment";break;
case ast_continue: str="continue";break;
case ast_break: str="break";break;
case ast_return: str="return";break;
}
return str;
}
class nasal_ast
{
private:
int line;
int type;
std::string str;
double num;
std::vector<nasal_ast> children;
public:
nasal_ast(int,int);
nasal_ast(const nasal_ast&);
~nasal_ast();
nasal_ast& operator=(const nasal_ast&);
void clear();
void set_line(int);
void set_type(int);
void set_str(std::string&);
void set_num(double);
void add_child(nasal_ast);
int get_line();
int get_type();
std::string get_str();
double get_num();
std::vector<nasal_ast>& get_children();
void print_ast(int);
};
nasal_ast::nasal_ast(int init_line=0,int init_type=ast_null)
{
this->line=init_line;
this->type=init_type;
return;
}
nasal_ast::nasal_ast(const nasal_ast& tmp)
{
this->line=tmp.line;
this->type=tmp.type;
this->str=tmp.str;
this->num=tmp.num;
this->children=tmp.children;
return;
}
nasal_ast::~nasal_ast()
{
this->children.clear();
return;
}
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
{
this->line=tmp.line;
this->type=tmp.type;
this->str=tmp.str;
this->num=tmp.num;
this->children=tmp.children;
return *this;
}
void nasal_ast::clear()
{
this->line=0;
this->str="";
this->num=0;
this->type=ast_null;
this->children.clear();
return;
}
void nasal_ast::set_line(int l)
{
this->line=l;
return;
}
void nasal_ast::set_type(int t)
{
this->type=t;
return;
}
void nasal_ast::set_str(std::string& s)
{
this->str=s;
return;
}
void nasal_ast::set_num(double n)
{
this->num=n;
return;
}
void nasal_ast::add_child(nasal_ast ast)
{
children.push_back(ast);
return;
}
int nasal_ast::get_line()
{
return this->line;
}
int nasal_ast::get_type()
{
return this->type;
}
std::string nasal_ast::get_str()
{
return this->str;
}
double nasal_ast::get_num()
{
return this->num;
}
std::vector<nasal_ast>& nasal_ast::get_children()
{
return this->children;
}
void nasal_ast::print_ast(int depth)
{
std::string indentation="";
for(int i=0;i<depth;++i) indentation+="| ";
indentation+=ast_str(this->type);
std::cout<<indentation;
if(this->type==ast_string || this->type==ast_identifier || this->type==ast_dynamic_id || this->type==ast_call_hash)
std::cout<<":"<<this->str;
else if(this->type==ast_number)
std::cout<<":"<<this->num;
std::cout<<std::endl;
int child_size=this->children.size();
for(int i=0;i<child_size;++i)
this->children[i].print_ast(depth+1);
return;
}
#endif

View File

@@ -1,837 +0,0 @@
#ifndef __NASAL_BUILTIN_H__
#define __NASAL_BUILTIN_H__
// builtin functions must be called inside a outer function like this:
// var print=func(elements...)
// {
// nasal_call_builtin_std_cout(elements);
// return nil;
// }
// builtin function nasal_call_builtin_std_cout is wrapped up by print
// used to find values that builtin function uses
#define in_builtin_find(value_name_string) (local_scope_addr>=0?nasal_vm.gc_get(local_scope_addr).get_closure().get_value_address(value_name_string):-1)
// used to check found value's type
// types are:vm_nil vm_number vm_string vm_vector vm_hash vm_function
// dynamic values will be generated as vector by the outer function
#define in_builtin_check(value_addr,value_type) (nasal_vm.gc_get(value_addr).get_type()==(value_type))
// declaration of builtin functions
// to add new builtin function,declare it here and write the definition below
int builtin_print(int,nasal_virtual_machine&);
int builtin_append(int,nasal_virtual_machine&);
int builtin_setsize(int,nasal_virtual_machine&);
int builtin_system(int,nasal_virtual_machine&);
int builtin_input(int,nasal_virtual_machine&);
int builtin_sleep(int,nasal_virtual_machine&);
int builtin_finput(int,nasal_virtual_machine&);
int builtin_foutput(int,nasal_virtual_machine&);
int builtin_split(int,nasal_virtual_machine&);
int builtin_rand(int,nasal_virtual_machine&);
int builtin_id(int,nasal_virtual_machine&);
int builtin_int(int,nasal_virtual_machine&);
int builtin_num(int,nasal_virtual_machine&);
int builtin_pop(int,nasal_virtual_machine&);
int builtin_str(int,nasal_virtual_machine&);
int builtin_size(int,nasal_virtual_machine&);
int builtin_xor(int,nasal_virtual_machine&);
int builtin_and(int,nasal_virtual_machine&);
int builtin_or(int,nasal_virtual_machine&);
int builtin_nand(int,nasal_virtual_machine&);
int builtin_not(int,nasal_virtual_machine&);
int builtin_sin(int,nasal_virtual_machine&);
int builtin_cos(int,nasal_virtual_machine&);
int builtin_tan(int,nasal_virtual_machine&);
int builtin_exp(int,nasal_virtual_machine&);
int builtin_ln(int,nasal_virtual_machine&);
int builtin_sqrt(int,nasal_virtual_machine&);
int builtin_atan2(int,nasal_virtual_machine&);
int builtin_time(int,nasal_virtual_machine&);
int builtin_contains(int,nasal_virtual_machine&);
int builtin_delete(int,nasal_virtual_machine&);
int builtin_getkeys(int,nasal_virtual_machine&);
int builtin_import(int,nasal_virtual_machine&);
bool builtin_die_state;// used in builtin_die
int builtin_die(int,nasal_virtual_machine&);
int builtin_type(int,nasal_virtual_machine&);
int builtin_substr(int,nasal_virtual_machine&);
// register builtin function's name and it's address here in this table below
// this table must and with {"",NULL}
struct FUNC_TABLE
{
std::string func_name;
int (*func_pointer)(int x,nasal_virtual_machine& nasal_vm);
} builtin_func_table[]=
{
{"nasal_call_builtin_std_cout", builtin_print},
{"nasal_call_builtin_push_back", builtin_append},
{"nasal_call_builtin_set_size", builtin_setsize},
{"nasal_call_builtin_system", builtin_system},
{"nasal_call_builtin_input", builtin_input},
{"nasal_call_builtin_sleep", builtin_sleep},
{"nasal_call_builtin_finput", builtin_finput},
{"nasal_call_builtin_foutput", builtin_foutput},
{"nasal_call_builtin_split", builtin_split},
{"nasal_call_builtin_rand", builtin_rand},
{"nasal_call_builtin_get_id", builtin_id},
{"nasal_call_builtin_trans_int", builtin_int},
{"nasal_call_builtin_trans_num", builtin_num},
{"nasal_call_builtin_pop_back", builtin_pop},
{"nasal_call_builtin_trans_str", builtin_str},
{"nasal_call_builtin_size", builtin_size},
{"nasal_call_builtin_xor", builtin_xor},
{"nasal_call_builtin_and", builtin_and},
{"nasal_call_builtin_or", builtin_or},
{"nasal_call_builtin_nand", builtin_nand},
{"nasal_call_builtin_not", builtin_not},
{"nasal_call_builtin_sin", builtin_sin},
{"nasal_call_builtin_cos", builtin_cos},
{"nasal_call_builtin_tan", builtin_tan},
{"nasal_call_builtin_exp", builtin_exp},
{"nasal_call_builtin_cpp_math_ln", builtin_ln},
{"nasal_call_builtin_cpp_math_sqrt", builtin_sqrt},
{"nasal_call_builtin_cpp_atan2", builtin_atan2},
{"nasal_call_builtin_time", builtin_time},
{"nasal_call_builtin_contains", builtin_contains},
{"nasal_call_builtin_delete", builtin_delete},
{"nasal_call_builtin_get_keys", builtin_getkeys},
{"nasal_call_import", builtin_import},
{"nasal_call_builtin_die", builtin_die},
{"nasal_call_builtin_type", builtin_type},
{"nasal_call_builtin_substr", builtin_substr},
{"", NULL}
};
int builtin_print(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
// get arguments
int vector_value_addr=in_builtin_find("elements");
if(vector_value_addr<0 || !in_builtin_check(vector_value_addr,vm_vector))
{
std::cout<<">> [runtime] builtin_print: \"elements\" has wrong value type(must be vector).\n";
return -1;
}
// main process
nasal_vector& ref_vec=nasal_vm.gc_get(vector_value_addr).get_vector();
int size=ref_vec.size();
for(int i=0;i<size;++i)
{
nasal_scalar& tmp=nasal_vm.gc_get(ref_vec.get_value_address(i));
switch(tmp.get_type())
{
case vm_nil:std::cout<<"nil";break;
case vm_number:std::cout<<tmp.get_number();break;
case vm_string:std::cout<<tmp.get_string();break;
case vm_vector:tmp.get_vector().print();break;
case vm_hash:tmp.get_hash().print();break;
case vm_function:std::cout<<"func(...){...}";break;
}
}
std::cout<<"\n";
// generate return value
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_append(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int vector_value_addr=in_builtin_find("vector");
int elem_value_addr=in_builtin_find("elements");
if(vector_value_addr<0 || !in_builtin_check(vector_value_addr,vm_vector))
{
std::cout<<">> [runtime] builtin_append: \"vector\" has wrong value type(must be vector).\n";
return -1;
}
if(elem_value_addr<0 || !in_builtin_check(elem_value_addr,vm_vector))
{
std::cout<<">> [runtime] builtin_append: \"elements\" has wrong value type(must be vector).\n";
return -1;
}
nasal_vector& ref_vector=nasal_vm.gc_get(vector_value_addr).get_vector();
nasal_vector& ref_elements=nasal_vm.gc_get(elem_value_addr).get_vector();
int size=ref_elements.size();
for(int i=0;i<size;++i)
{
int value_address=ref_elements.get_value_address(i);
nasal_vm.add_reference(value_address);
ref_vector.add_elem(value_address);
}
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_setsize(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int vector_value_addr=in_builtin_find("vector");
int size_value_addr=in_builtin_find("size");
if(vector_value_addr<0 || nasal_vm.gc_get(vector_value_addr).get_type()!=vm_vector)
{
std::cout<<">> [runtime] builtin_setsize: \"vector\" has wrong value type(must be vector).\n";
return -1;
}
if(size_value_addr<0)
{
std::cout<<">> [runtime] builtin_setsize: \"size\" has wrong value type(must be string or number).\n";
return -1;
}
int type=nasal_vm.gc_get(size_value_addr).get_type();
if(type!=vm_number && type!=vm_string)
{
std::cout<<">> [runtime] builtin_setsize: size is not a number.\n";
return -1;
}
int number;
if(type==vm_number)
number=(int)nasal_vm.gc_get(size_value_addr).get_number();
else
{
std::string str=nasal_vm.gc_get(size_value_addr).get_string();
double tmp=trans_string_to_number(str);
if(std::isnan(tmp))
{
std::cout<<">> [runtime] builtin_setsize: size is not a numerable string.\n";
return -1;
}
number=(int)tmp;
}
if(number<0)
{
std::cout<<">> [runtime] builtin_setsize: size must be greater than -1.\n";
return -1;
}
nasal_vector& ref_vector=nasal_vm.gc_get(vector_value_addr).get_vector();
int vec_size=ref_vector.size();
if(number<vec_size)
for(int i=number;i<vec_size;++i)
{
int addr=ref_vector.del_elem();
if(addr>=0)
nasal_vm.del_reference(addr);
}
else if(number>vec_size)
for(int i=vec_size;i<number;++i)
{
int new_val_addr=nasal_vm.gc_alloc(vm_nil);
ref_vector.add_elem(new_val_addr);
}
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_system(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int str_value_addr=in_builtin_find("str");
if(str_value_addr<0 || nasal_vm.gc_get(str_value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_system: \"str\" has wrong value type(must be string).\n";
return -1;
}
std::string str=nasal_vm.gc_get(str_value_addr).get_string();
int size=str.length();
char* command=new char[size+1];
for(int i=0;i<size;++i)
command[i]=str[i];
command[size]='\0';
system(command);
delete []command;
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_input(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int ret_addr=nasal_vm.gc_alloc(vm_string);
std::string str;
std::cin>>str;
nasal_vm.gc_get(ret_addr).set_string(str);
return ret_addr;
}
int builtin_sleep(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("duration");
if(value_addr<0 || (nasal_vm.gc_get(value_addr).get_type()!=vm_string && nasal_vm.gc_get(value_addr).get_type()!=vm_number))
{
std::cout<<">> [runtime] builtin_sleep: \"duration\" has wrong value type(must be string or number).\n";
return -1;
}
unsigned long sleep_time=0;
if(nasal_vm.gc_get(value_addr).get_type()==vm_string)
{
std::string str=nasal_vm.gc_get(value_addr).get_string();
double number=trans_string_to_number(str);
if(std::isnan(number))
{
std::cout<<">> [runtime] builtin_sleep: this is not a numerable string.\n";
return -1;
}sleep_time=(unsigned long)number;
}
else
sleep_time=(unsigned long)nasal_vm.gc_get(value_addr).get_number();
sleep(sleep_time); // sleep in unistd.h will make this progress sleep sleep_time seconds.
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_finput(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("filename");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_finput: \"filename\" has wrong value type(must be string).\n";
return -1;
}
std::string filename=nasal_vm.gc_get(value_addr).get_string();
std::ifstream fin(filename);
std::string file_content="";
if(!fin.fail())
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
file_content.push_back(c);
}
else
file_content="";
fin.close();
int ret_addr=nasal_vm.gc_alloc(vm_string);
nasal_vm.gc_get(ret_addr).set_string(file_content);
return ret_addr;
}
int builtin_foutput(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("filename");
int str_value_addr=in_builtin_find("str");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_foutput: \"filename\" has wrong value type(must be string).\n";
return -1;
}
if(str_value_addr<0 || nasal_vm.gc_get(str_value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_foutput: \"str\" has wrong value type(must be string).\n";
return -1;
}
std::string filename=nasal_vm.gc_get(value_addr).get_string();
std::string file_content=nasal_vm.gc_get(str_value_addr).get_string();
std::ofstream fout(filename);
fout<<file_content;
fout.close();
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_split(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int delimeter_value_addr=in_builtin_find("delimeter");
int string_value_addr=in_builtin_find("string");
if(delimeter_value_addr<0 || nasal_vm.gc_get(delimeter_value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_split: \"delimeter\" has wrong value type(must be string).\n";
return -1;
}
if(string_value_addr<0 || nasal_vm.gc_get(string_value_addr).get_type()!=vm_string)
{
std::cout<<">> [runtime] builtin_split: \"string\" has wrong value type(must be string).\n";
return -1;
}
std::string delimeter=nasal_vm.gc_get(delimeter_value_addr).get_string();
std::string source=nasal_vm.gc_get(string_value_addr).get_string();
int delimeter_len=delimeter.length();
int source_len=source.length();
int ret_addr=nasal_vm.gc_alloc(vm_vector);
nasal_vector& ref_vec=nasal_vm.gc_get(ret_addr).get_vector();
std::string tmp="";
if(!delimeter_len)
{
for(int i=0;i<source_len;++i)
{
tmp+=source[i];
int str_addr=nasal_vm.gc_alloc(vm_string);
nasal_vm.gc_get(str_addr).set_string(tmp);
ref_vec.add_elem(str_addr);
tmp="";
}
return ret_addr;
}
for(int i=0;i<source_len;++i)
{
bool check_delimeter=false;
if(source[i]==delimeter[0])
for(int j=0;j<delimeter_len;++j)
{
if(i+j>=source_len || source[i+j]!=delimeter[j])
break;
if(j==delimeter_len-1)
check_delimeter=true;
}
if(check_delimeter)
{
int str_addr=nasal_vm.gc_alloc(vm_string);
nasal_vm.gc_get(str_addr).set_string(tmp);
ref_vec.add_elem(str_addr);
tmp="";
i+=delimeter_len-1;
}
else
tmp+=source[i];
}
if(tmp.length())
{
int str_addr=nasal_vm.gc_alloc(vm_string);
nasal_vm.gc_get(str_addr).set_string(tmp);
ref_vec.add_elem(str_addr);
tmp="";
}
return ret_addr;
}
int builtin_rand(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("seed");
if(value_addr<0 || (nasal_vm.gc_get(value_addr).get_type()!=vm_number && nasal_vm.gc_get(value_addr).get_type()!=vm_nil))
{
std::cout<<">> [runtime] builtin_rand: \"seed\" has wrong value type(must be nil or number).\n";
return -1;
}
if(nasal_vm.gc_get(value_addr).get_type()==vm_number)
{
unsigned int number=(unsigned int)nasal_vm.gc_get(value_addr).get_number();
srand(number);
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
double num=0;
for(int i=0;i<5;++i)
num=(num+rand())*(1.0/(RAND_MAX+1.0));
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(num);
return ret_addr;
}
int builtin_id(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("thing");
if(value_addr<0)
{
std::cout<<">> [runtime] builtin_id: cannot find \"thing\".\n";
return -1;
}
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)value_addr);
return ret_addr;
}
int builtin_int(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("value");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_int: \"value\" has wrong value type(must be number).\n";
return -1;
}
int number=(int)nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)number);
return ret_addr;
}
int builtin_num(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("value");
if(value_addr<0 || !in_builtin_check(value_addr,vm_string))
{
std::cout<<">> [runtime] builtin_num: \"value\" has wrong value type(must be string).\n";
return -1;
}
std::string str=nasal_vm.gc_get(value_addr).get_string();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(trans_string_to_number(str));
return ret_addr;
}
int builtin_pop(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("vector");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_vector)
{
std::cout<<">> [runtime] builtin_pop: \"vector\" has wrong value type(must be vector).\n";
return -1;
}
int ret_addr=nasal_vm.gc_get(value_addr).get_vector().del_elem();
return ret_addr;
}
int builtin_str(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("number");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_str: \"number\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_string(trans_number_to_string(number));
return ret_addr;
}
int builtin_size(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("object");
if(value_addr<0)
{
std::cout<<">> [runtime] builtin_size: cannot find value \"object\".\n";
return -1;
}
int type=nasal_vm.gc_get(value_addr).get_type();
int number=-1;
switch(type)
{
case vm_nil:
case vm_number:
case vm_function:
case vm_closure:break;
case vm_string:number=nasal_vm.gc_get(value_addr).get_string().length();break;
case vm_vector:number=nasal_vm.gc_get(value_addr).get_vector().size();break;
case vm_hash:number=nasal_vm.gc_get(value_addr).get_hash().size();break;
}
int ret_addr=-1;
if(number<0)
ret_addr=nasal_vm.gc_alloc(vm_nil);
else
{
ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)number);
}
return ret_addr;
}
int builtin_xor(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int a_addr=in_builtin_find("a");
int b_addr=in_builtin_find("b");
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_xor: \"a\" has wrong value type(must be number).\n";
return -1;
}
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_xor: \"b\" has wrong value type(must be number).\n";
return -1;
}
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)(number_a^number_b));
return ret_addr;
}
int builtin_and(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int a_addr=in_builtin_find("a");
int b_addr=in_builtin_find("b");
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_and: \"a\" has wrong value type(must be number).\n";
return -1;
}
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_and: \"b\" has wrong value type(must be number).\n";
return -1;
}
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)(number_a&number_b));
return ret_addr;
}
int builtin_or(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int a_addr=in_builtin_find("a");
int b_addr=in_builtin_find("b");
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_or: \"a\" has wrong value type(must be number).\n";
return -1;
}
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_or: \"b\" has wrong value type(must be number).\n";
return -1;
}
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)(number_a|number_b));
return ret_addr;
}
int builtin_nand(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int a_addr=in_builtin_find("a");
int b_addr=in_builtin_find("b");
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_nand: \"a\" has wrong value type(must be number).\n";
return -1;
}
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_nand: \"b\" has wrong value type(must be number).\n";
return -1;
}
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)(~(number_a&number_b)));
return ret_addr;
}
int builtin_not(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int a_addr=in_builtin_find("a");
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_not: \"a\" has wrong value type(must be number).\n";
return -1;
}
int number=(int)nasal_vm.gc_get(a_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)(~number));
return ret_addr;
}
int builtin_sin(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_sin: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(sin(number));
return ret_addr;
}
int builtin_cos(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_cos: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(cos(number));
return ret_addr;
}
int builtin_tan(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_tan: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(tan(number));
return ret_addr;
}
int builtin_exp(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_exp: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(exp(number));
return ret_addr;
}
int builtin_ln(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_ln: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(log(number)/log(2.7182818284590452354));
return ret_addr;
}
int builtin_sqrt(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("x");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_sqrt: \"x\" has wrong value type(must be number).\n";
return -1;
}
double number=nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(sqrt(number));
return ret_addr;
}
int builtin_atan2(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int x_value_addr=in_builtin_find("x");
int y_value_addr=in_builtin_find("y");
if(x_value_addr<0 || nasal_vm.gc_get(x_value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_atan2: \"x\" has wrong value type(must be number).\n";
return -1;
}
if(y_value_addr<0 || nasal_vm.gc_get(y_value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_atan2: \"y\" has wrong value type(must be number).\n";
return -1;
}
double x=nasal_vm.gc_get(x_value_addr).get_number();
double y=nasal_vm.gc_get(y_value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number(atan2(y,x));
return ret_addr;
}
int builtin_time(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("begin_time");
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
{
std::cout<<">> [runtime] builtin_time: \"begin_time\" has wrong value type(must be number).\n";
return -1;
}
time_t begin_time=(time_t)nasal_vm.gc_get(value_addr).get_number();
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)time(&begin_time));
return ret_addr;
}
int builtin_contains(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int hash_addr=in_builtin_find("hash");
int key_addr=in_builtin_find("key");
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
{
std::cout<<">> [runtime] builtin_contains: \"hash\" has wrong type(must be hash).\n";
return -1;
}
if(key_addr<0 || !in_builtin_check(key_addr,vm_string))
{
std::cout<<">> [runtime] builtin_contains: \"key\" has wrong type(must be string).\n";
return -1;
}
std::string key=nasal_vm.gc_get(key_addr).get_string();
bool contains=nasal_vm.gc_get(hash_addr).get_hash().check_contain(key);
int ret_addr=nasal_vm.gc_alloc(vm_number);
nasal_vm.gc_get(ret_addr).set_number((double)contains);
return ret_addr;
}
int builtin_delete(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int hash_addr=in_builtin_find("hash");
int key_addr=in_builtin_find("key");
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
{
std::cout<<">> [runtime] builtin_delete: \"hash\" has wrong type(must be hash).\n";
return -1;
}
if(key_addr<0 || !in_builtin_check(key_addr,vm_string))
{
std::cout<<">> [runtime] builtin_delete: \"key\" has wrong type(must be string).\n";
return -1;
}
std::string key=nasal_vm.gc_get(key_addr).get_string();
nasal_vm.gc_get(hash_addr).get_hash().del_elem(key);
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_getkeys(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int hash_addr=in_builtin_find("hash");
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
{
std::cout<<">> [runtime] builtin_delete: \"hash\" has wrong type(must be hash).\n";
return -1;
}
int ret_addr=nasal_vm.gc_get(hash_addr).get_hash().get_keys();
return ret_addr;
}
int builtin_import(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
// this function is used in preprocessing.
// this function will return nothing when running.
std::cout<<">> [runtime] builtin_import: cannot use import when running.\n";
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_die(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int str_addr=in_builtin_find("str");
if(str_addr<0 || !in_builtin_check(str_addr,vm_string))
{
std::cout<<">> [runtime] builtin_die: \"str\" has wrong type(must be string).\n";
return -1;
}
builtin_die_state=true;
std::cout<<">> [runtime] error: "<<nasal_vm.gc_get(str_addr).get_string()<<'\n';
int ret_addr=nasal_vm.gc_alloc(vm_nil);
return ret_addr;
}
int builtin_type(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int value_addr=in_builtin_find("object");
if(value_addr<0)
{
std::cout<<">> [runtime] builtin_type: cannot find \"object\".\n";
return -1;
}
int type=nasal_vm.gc_get(value_addr).get_type();
int ret_addr=nasal_vm.gc_alloc(vm_string);
switch(type)
{
case vm_nil: nasal_vm.gc_get(ret_addr).set_string("nil");break;
case vm_number: nasal_vm.gc_get(ret_addr).set_string("number");break;
case vm_string: nasal_vm.gc_get(ret_addr).set_string("string");break;
case vm_vector: nasal_vm.gc_get(ret_addr).set_string("vector");break;
case vm_hash: nasal_vm.gc_get(ret_addr).set_string("hash");break;
case vm_function: nasal_vm.gc_get(ret_addr).set_string("function");break;
}
return ret_addr;
}
int builtin_substr(int local_scope_addr,nasal_virtual_machine& nasal_vm)
{
int str_addr=in_builtin_find("str");
int begin_addr=in_builtin_find("begin");
int length_addr=in_builtin_find("length");
if(str_addr<0 || !in_builtin_check(str_addr,vm_string))
{
std::cout<<">> [runtime] builtin_substr: cannot find \"str\" or wrong type(must be string).\n";
return -1;
}
if(begin_addr<0 || !in_builtin_check(begin_addr,vm_number))
{
std::cout<<">> [runtime] builtin_substr: cannot find \"begin\" or wrong type(must be number).\n";
return -1;
}
if(length_addr<0 || !in_builtin_check(length_addr,vm_number))
{
std::cout<<">> [runtime] builtin_substr: cannot find \"length\" or wrong type(must be number).\n";
return -1;
}
std::string str=nasal_vm.gc_get(str_addr).get_string();
int begin=(int)nasal_vm.gc_get(begin_addr).get_number();
int len=(int)nasal_vm.gc_get(length_addr).get_number();
if(begin>=str.length() || begin+len>=str.length())
{
std::cout<<">> [runtime] builtin_substr: index out of range.\n";
return -1;
}
std::string tmp="";
for(int i=begin;i<begin+len;++i)
tmp+=str[i];
int ret_addr=nasal_vm.gc_alloc(vm_string);
nasal_vm.gc_get(ret_addr).set_string(tmp);
return ret_addr;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,744 +0,0 @@
#ifndef __NASAL_GC_H__
#define __NASAL_GC_H__
enum runtime_scalar_type
{
vm_nil=0,
vm_number,
vm_string,
vm_closure,
vm_function,
vm_vector,
vm_hash
};
/*
nasal_number: basic type(double)
nasal_string: basic type(std::string)
nasal_vector: elems[i] -> address in memory -> value address in gc
nasal_hash: elems[key] -> address in memory -> value address in gc
nasal_function: closure -> value address in gc(type: nasal_closure)
nasal_closure: std::list<std::map<std::string,int>> -> std::map<std::string,int> -> (int) -> address in memory -> value address in gc
*/
class nasal_virtual_machine;
class nasal_vector
{
private:
// this int points to the space in nasal_vm::memory_manager_memory
nasal_virtual_machine& vm;
std::vector<int> elems;
public:
nasal_vector(nasal_virtual_machine&);
~nasal_vector();
void add_elem(int);
int del_elem();
int size();
int get_value_address(int);
int* get_mem_address(int);
void print();
};
class nasal_hash
{
private:
// this int points to the space in nasal_vm::memory_manager_memory
nasal_virtual_machine& vm;
std::map<std::string,int> elems;
public:
nasal_hash(nasal_virtual_machine&);
~nasal_hash();
void add_elem(std::string,int);
void del_elem(std::string);
int size();
int get_special_para(std::string);
int get_value_address(std::string);
int* get_mem_address(std::string);
bool check_contain(std::string);
int get_keys();
void print();
};
class nasal_function
{
private:
nasal_virtual_machine& vm;
int entry;
int closure_addr;
nasal_ast argument_list;
nasal_ast function_expr;
std::vector<std::string> para_name;
std::string dynamic_para_name;
std::vector<int> default_para_addr;
public:
nasal_function(nasal_virtual_machine&);
~nasal_function();
void set_entry(int);
int get_entry();
void add_para(std::string,int,bool);
std::vector<std::string>& get_para();
std::string get_dynamic_para();
std::vector<int>& get_default();
void set_closure_addr(int);
int get_closure_addr();
void set_arguments(nasal_ast&);
nasal_ast& get_arguments();
void set_run_block(nasal_ast&);
nasal_ast& get_run_block();
};
class nasal_closure
{
private:
// int in std::map<std::string,int> points to the space in nasal_vm::memory_manager_memory
// and this memory_manager_memory space stores an address to garbage_collector_memory
// and this address points to an nasal_hash
nasal_virtual_machine& vm;
std::list<std::map<std::string,int> > elems;
public:
nasal_closure(nasal_virtual_machine&);
~nasal_closure();
void add_scope();
void del_scope();
void add_new_value(std::string,int);
int get_value_address(std::string);
int* get_mem_address(std::string);
void set_closure(nasal_closure&);
};
class nasal_scalar
{
protected:
int type;
void* scalar_ptr;
public:
nasal_scalar();
~nasal_scalar();
void clear();
void set_type(int,nasal_virtual_machine&);
void set_number(double);
void set_string(std::string);
int get_type();
double get_number();
std::string get_string();
nasal_vector& get_vector();
nasal_hash& get_hash();
nasal_function& get_func();
nasal_closure& get_closure();
};
class nasal_virtual_machine
{
struct gc_unit
{
int ref_cnt;
nasal_scalar elem;
gc_unit()
{
ref_cnt=0;
return;
}
};
private:
nasal_scalar error_returned_value;
std::queue<int> garbage_collector_free_space;
std::vector<gc_unit*> garbage_collector_memory;
public:
~nasal_virtual_machine();
void clear();
void debug();
int gc_alloc(int); // garbage collector gives a new space
nasal_scalar& gc_get(int); // get scalar that stored in gc
void add_reference(int);
void del_reference(int);
};
/*functions of nasal_vector*/
nasal_vector::nasal_vector(nasal_virtual_machine& nvm):vm(nvm)
{
return;
}
nasal_vector::~nasal_vector()
{
int size=elems.size();
for(int i=0;i<size;++i)
vm.del_reference(elems[i]);
elems.clear();
return;
}
void nasal_vector::add_elem(int value_address)
{
elems.push_back(value_address);
return;
}
int nasal_vector::del_elem()
{
// pop back
if(!elems.size())
return -1;
int ret=elems.back();
elems.pop_back();
return ret;
}
int nasal_vector::size()
{
return elems.size();
}
int nasal_vector::get_value_address(int index)
{
int vec_size=elems.size();
int left_range=-vec_size;
int right_range=vec_size-1;
if(index<left_range || index>right_range)
{
std::cout<<">> [runtime] nasal_vector::get_value_address: index out of range: "<<index<<"\n";
return -1;
}
return elems[(index+vec_size)%vec_size];
}
int* nasal_vector::get_mem_address(int index)
{
int vec_size=elems.size();
int left_range=-vec_size;
int right_range=vec_size-1;
if(index<left_range || index>right_range)
{
std::cout<<">> [runtime] nasal_vector::get_mem_address: index out of range: "<<index<<"\n";
return NULL;
}
return &elems[(index+vec_size)%vec_size];
}
void nasal_vector::print()
{
int size=elems.size();
std::cout<<"[";
if(!size)
std::cout<<"]";
for(int i=0;i<size;++i)
{
nasal_scalar& tmp=vm.gc_get(elems[i]);
switch(tmp.get_type())
{
case vm_nil:std::cout<<"nil";break;
case vm_number:std::cout<<tmp.get_number();break;
case vm_string:std::cout<<tmp.get_string();break;
case vm_vector:tmp.get_vector().print();break;
case vm_hash:tmp.get_hash().print();break;
case vm_function:std::cout<<"func(...){...}";break;
}
std::cout<<",]"[i==size-1];
}
return;
}
/*functions of nasal_hash*/
nasal_hash::nasal_hash(nasal_virtual_machine& nvm):vm(nvm)
{
return;
}
nasal_hash::~nasal_hash()
{
for(std::map<std::string,int>::iterator iter=elems.begin();iter!=elems.end();++iter)
vm.del_reference(iter->second);
elems.clear();
return;
}
void nasal_hash::add_elem(std::string key,int value_address)
{
if(elems.find(key)==elems.end())
elems[key]=value_address;
return;
}
void nasal_hash::del_elem(std::string key)
{
if(elems.find(key)!=elems.end())
{
vm.del_reference(elems[key]);
elems.erase(key);
}
return;
}
int nasal_hash::size()
{
return elems.size();
}
int nasal_hash::get_special_para(std::string key)
{
if(elems.find(key)!=elems.end())
return elems[key];
return -1;
}
int nasal_hash::get_value_address(std::string key)
{
int ret_value_addr=-1;
if(elems.find(key)!=elems.end())
return elems[key];
else if(elems.find("parents")!=elems.end())
{
int val_addr=elems["parents"];
if(vm.gc_get(val_addr).get_type()==vm_vector)
{
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
int tmp_val_addr=vec_ref.get_value_address(i);
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
ret_value_addr=vm.gc_get(tmp_val_addr).get_hash().get_value_address(key);
if(ret_value_addr>=0)
break;
}
}
}
return ret_value_addr;
}
int* nasal_hash::get_mem_address(std::string key)
{
int* mem_addr=NULL;
if(elems.find(key)!=elems.end())
return &elems[key];
else if(elems.find("parents")!=elems.end())
{
int val_addr=elems["parents"];
if(vm.gc_get(val_addr).get_type()==vm_vector)
{
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
int tmp_val_addr=vec_ref.get_value_address(i);
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
mem_addr=vm.gc_get(tmp_val_addr).get_hash().get_mem_address(key);
if(mem_addr>0)
break;
}
}
}
return mem_addr;
}
bool nasal_hash::check_contain(std::string key)
{
if(elems.find(key)!=elems.end())
return true;
if(elems.find("parents")!=elems.end())
{
bool result=false;
int val_addr=elems["parents"];
if(vm.gc_get(val_addr).get_type()==vm_vector)
{
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
int tmp_val_addr=vec_ref.get_value_address(i);
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
result=vm.gc_get(tmp_val_addr).get_hash().check_contain(key);
if(result)
break;
}
}
return result;
}
return false;
}
int nasal_hash::get_keys()
{
int ret_addr=vm.gc_alloc(vm_vector);
nasal_vector& ref_vec=vm.gc_get(ret_addr).get_vector();
for(std::map<std::string,int>::iterator iter=elems.begin();iter!=elems.end();++iter)
{
int str_addr=vm.gc_alloc(vm_string);
vm.gc_get(str_addr).set_string(iter->first);
ref_vec.add_elem(str_addr);
}
return ret_addr;
}
void nasal_hash::print()
{
std::cout<<"{";
if(!elems.size())
std::cout<<"}";
for(std::map<std::string,int>::iterator i=elems.begin();i!=elems.end();++i)
{
std::cout<<i->first<<":";
nasal_scalar& tmp=vm.gc_get(i->second);
switch(tmp.get_type())
{
case vm_nil:std::cout<<"nil";break;
case vm_number:std::cout<<tmp.get_number();break;
case vm_string:std::cout<<tmp.get_string();break;
case vm_vector:tmp.get_vector().print();break;
case vm_hash:tmp.get_hash().print();break;
case vm_function:std::cout<<"func(...){...}";break;
}
std::cout<<",}"[(++i)==elems.end()];
--i;
}
return;
}
/*functions of nasal_function*/
nasal_function::nasal_function(nasal_virtual_machine& nvm):vm(nvm)
{
closure_addr=-1;
dynamic_para_name="";
argument_list.clear();
function_expr.clear();
return;
}
nasal_function::~nasal_function()
{
if(closure_addr>=0)
vm.del_reference(closure_addr);
for(int i=0;i<default_para_addr.size();++i)
if(default_para_addr[i]>=0)
vm.del_reference(default_para_addr[i]);
argument_list.clear();
function_expr.clear();
return;
}
void nasal_function::set_entry(int etr)
{
entry=etr;
return;
}
int nasal_function::get_entry()
{
return entry;
}
void nasal_function::add_para(std::string name,int val_addr=-1,bool is_dynamic=false)
{
if(is_dynamic)
{
dynamic_para_name=name;
return;
}
para_name.push_back(name);
default_para_addr.push_back(val_addr);
return;
}
std::vector<std::string>& nasal_function::get_para()
{
return para_name;
}
std::string nasal_function::get_dynamic_para()
{
return dynamic_para_name;
}
std::vector<int>& nasal_function::get_default()
{
return default_para_addr;
}
void nasal_function::set_closure_addr(int value_address)
{
if(closure_addr>=0)
vm.del_reference(closure_addr);
int new_closure=vm.gc_alloc(vm_closure);
vm.gc_get(new_closure).get_closure().set_closure(vm.gc_get(value_address).get_closure());
closure_addr=new_closure;
return;
}
int nasal_function::get_closure_addr()
{
return closure_addr;
}
void nasal_function::set_arguments(nasal_ast& node)
{
argument_list=node;
return;
}
nasal_ast& nasal_function::get_arguments()
{
return argument_list;
}
void nasal_function::set_run_block(nasal_ast& node)
{
function_expr=node;
return;
}
nasal_ast& nasal_function::get_run_block()
{
return function_expr;
}
/*functions of nasal_closure*/
nasal_closure::nasal_closure(nasal_virtual_machine& nvm):vm(nvm)
{
std::map<std::string,int> new_scope;
elems.push_back(new_scope);
return;
}
nasal_closure::~nasal_closure()
{
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
vm.del_reference(j->second);
elems.clear();
return;
}
void nasal_closure::add_scope()
{
std::map<std::string,int> new_scope;
elems.push_back(new_scope);
return;
}
void nasal_closure::del_scope()
{
std::map<std::string,int>& last_scope=elems.back();
for(std::map<std::string,int>::iterator i=last_scope.begin();i!=last_scope.end();++i)
vm.del_reference(i->second);
elems.pop_back();
return;
}
void nasal_closure::add_new_value(std::string key,int value_address)
{
if(elems.back().find(key)!=elems.back().end())
{
// if this value already exists,delete the old value and update a new value
int old_val_address=elems.back()[key];
vm.del_reference(old_val_address);
}
elems.back()[key]=value_address;
return;
}
int nasal_closure::get_value_address(std::string key)
{
int ret_address=-1;
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
if(i->find(key)!=i->end())
ret_address=(*i)[key];
return ret_address;
}
int* nasal_closure::get_mem_address(std::string key)
{
int* ret_address=NULL;
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
if(i->find(key)!=i->end())
ret_address=&((*i)[key]);
return ret_address;
}
void nasal_closure::set_closure(nasal_closure& tmp)
{
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
vm.del_reference(j->second);
elems.clear();
for(std::list<std::map<std::string,int> >::iterator i=tmp.elems.begin();i!=tmp.elems.end();++i)
{
std::map<std::string,int> new_scope;
elems.push_back(new_scope);
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
{
int value_addr=j->second;
vm.add_reference(value_addr);
elems.back()[j->first]=value_addr;
}
}
return;
}
/*functions of nasal_scalar*/
nasal_scalar::nasal_scalar()
{
this->type=vm_nil;
this->scalar_ptr=(void*)NULL;
return;
}
nasal_scalar::~nasal_scalar()
{
// must set type and scalar_ptr to default first
// this operation will avoid SIGTRAP caused by circular reference
// circular reference will cause using destructor repeatedly
int tmp_type=this->type;
void* tmp_ptr=this->scalar_ptr;
this->type=vm_nil;
this->scalar_ptr=NULL;
switch(tmp_type)
{
case vm_nil: break;
case vm_number: delete (double*)(tmp_ptr); break;
case vm_string: delete (std::string*)(tmp_ptr); break;
case vm_vector: delete (nasal_vector*)(tmp_ptr); break;
case vm_hash: delete (nasal_hash*)(tmp_ptr); break;
case vm_function: delete (nasal_function*)(tmp_ptr); break;
case vm_closure: delete (nasal_closure*)(tmp_ptr); break;
}
return;
}
void nasal_scalar::clear()
{
// must set type and scalar_ptr to default first
// this operation will avoid SIGTRAP caused by circular reference
// circular reference will cause using destructor repeatedly
int tmp_type=this->type;
void* tmp_ptr=this->scalar_ptr;
this->type=vm_nil;
this->scalar_ptr=NULL;
switch(tmp_type)
{
case vm_nil: break;
case vm_number: delete (double*)(tmp_ptr); break;
case vm_string: delete (std::string*)(tmp_ptr); break;
case vm_vector: delete (nasal_vector*)(tmp_ptr); break;
case vm_hash: delete (nasal_hash*)(tmp_ptr); break;
case vm_function: delete (nasal_function*)(tmp_ptr); break;
case vm_closure: delete (nasal_closure*)(tmp_ptr); break;
}
return;
}
void nasal_scalar::set_type(int nasal_scalar_type,nasal_virtual_machine& nvm)
{
this->type=nasal_scalar_type;
switch(nasal_scalar_type)
{
case vm_nil: this->scalar_ptr=(void*)NULL; break;
case vm_number: this->scalar_ptr=(void*)(new double); break;
case vm_string: this->scalar_ptr=(void*)(new std::string); break;
case vm_vector: this->scalar_ptr=(void*)(new nasal_vector(nvm)); break;
case vm_hash: this->scalar_ptr=(void*)(new nasal_hash(nvm)); break;
case vm_function: this->scalar_ptr=(void*)(new nasal_function(nvm)); break;
case vm_closure: this->scalar_ptr=(void*)(new nasal_closure(nvm)); break;
}
return;
}
void nasal_scalar::set_number(double num)
{
*(double*)(this->scalar_ptr)=num;
return;
}
void nasal_scalar::set_string(std::string str)
{
*(std::string*)(this->scalar_ptr)=str;
return;
}
int nasal_scalar::get_type()
{
return this->type;
}
double nasal_scalar::get_number()
{
return *(double*)(this->scalar_ptr);
}
std::string nasal_scalar::get_string()
{
return *(std::string*)(this->scalar_ptr);
}
nasal_vector& nasal_scalar::get_vector()
{
return *(nasal_vector*)(this->scalar_ptr);
}
nasal_hash& nasal_scalar::get_hash()
{
return *(nasal_hash*)(this->scalar_ptr);
}
nasal_function& nasal_scalar::get_func()
{
return *(nasal_function*)(this->scalar_ptr);
}
nasal_closure& nasal_scalar::get_closure()
{
return *(nasal_closure*)(this->scalar_ptr);
}
/*functions of nasal_virtual_machine*/
nasal_virtual_machine::~nasal_virtual_machine()
{
int gc_mem_size=garbage_collector_memory.size();
for(int i=0;i<gc_mem_size;++i)
if(garbage_collector_memory[i]->ref_cnt)
{
garbage_collector_memory[i]->ref_cnt=0;
garbage_collector_memory[i]->elem.clear();
}
for(int i=0;i<gc_mem_size;++i)
delete garbage_collector_memory[i];
while(!garbage_collector_free_space.empty())
garbage_collector_free_space.pop();
garbage_collector_memory.clear();
return;
}
void nasal_virtual_machine::debug()
{
int gc_mem_size=garbage_collector_memory.size();
for(int i=0;i<gc_mem_size;++i)
if(garbage_collector_memory[i]->ref_cnt)
{
std::cout<<">> [debug] "<<i<<": "<<garbage_collector_memory[i]->ref_cnt<<" ";
switch(garbage_collector_memory[i]->elem.get_type())
{
case vm_nil:std::cout<<"nil";break;
case vm_number:std::cout<<"number "<<garbage_collector_memory[i]->elem.get_number();break;
case vm_string:std::cout<<"string "<<garbage_collector_memory[i]->elem.get_string();break;
case vm_vector:std::cout<<"vector";break;
case vm_hash:std::cout<<"hash";break;
case vm_function:std::cout<<"function";break;
case vm_closure:std::cout<<"closure";break;
}
std::cout<<"\n";
}
return;
}
void nasal_virtual_machine::clear()
{
int gc_mem_size=garbage_collector_memory.size();
for(int i=0;i<gc_mem_size;++i)
if(garbage_collector_memory[i]->ref_cnt)
{
garbage_collector_memory[i]->ref_cnt=0;
garbage_collector_memory[i]->elem.clear();
}
for(int i=0;i<gc_mem_size;++i)
delete garbage_collector_memory[i];
while(!garbage_collector_free_space.empty())
garbage_collector_free_space.pop();
garbage_collector_memory.clear();
return;
}
int nasal_virtual_machine::gc_alloc(int val_type)
{
if(garbage_collector_free_space.empty())
{
int mem_size=garbage_collector_memory.size();
garbage_collector_memory.resize(mem_size+64);
for(int i=mem_size;i<mem_size+64;++i)
{
garbage_collector_memory[i]=new gc_unit;
garbage_collector_free_space.push(i);
}
}
int ret=garbage_collector_free_space.front();
gc_unit& unit_ref=*garbage_collector_memory[ret];
unit_ref.ref_cnt=1;
unit_ref.elem.set_type(val_type,*this);
garbage_collector_free_space.pop();
return ret;
}
nasal_scalar& nasal_virtual_machine::gc_get(int value_address)
{
if(0<=value_address)
return garbage_collector_memory[value_address]->elem;
return error_returned_value;
}
void nasal_virtual_machine::add_reference(int value_address)
{
if(0<=value_address)
++garbage_collector_memory[value_address]->ref_cnt;
return;
}
void nasal_virtual_machine::del_reference(int value_address)
{
if(0<=value_address)
--garbage_collector_memory[value_address]->ref_cnt;
else
return;
if(!garbage_collector_memory[value_address]->ref_cnt)
{
garbage_collector_memory[value_address]->elem.clear();
garbage_collector_free_space.push(value_address);
}
return;
}
#endif

View File

@@ -1,177 +0,0 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
class nasal_import
{
private:
nasal_lexer import_lex;
nasal_parse import_par;
nasal_ast import_ast;
std::vector<std::string> filename_table;
int error;
void die(std::string,std::string);
void init();
bool check_import(nasal_ast&);
bool check_exist(std::string);
void linker(nasal_ast&,nasal_ast&);
nasal_ast file_import(nasal_ast&);
nasal_ast load(nasal_ast&);
public:
nasal_import();
int get_error();
void link(nasal_ast&);
nasal_ast& get_root();
};
nasal_import::nasal_import()
{
import_lex.clear();
import_par.clear();
import_ast.clear();
filename_table.clear();
return;
}
void nasal_import::die(std::string filename,std::string error_stage)
{
++error;
std::cout<<">> [import] in <\""<<filename<<"\">: error(s) occurred in "<<error_stage<<"."<<std::endl;
return;
}
void nasal_import::init()
{
import_lex.clear();
import_par.clear();
return;
}
bool nasal_import::check_import(nasal_ast& node)
{
/*
only this kind of node can be recognized as 'import':
call
id:import
call_func
string:'filename'
*/
if(node.get_type()!=ast_call)
return false;
std::vector<nasal_ast>& ref_vec=node.get_children();
if(ref_vec.size()!=2)
return false;
if(ref_vec[0].get_str()!="import")
return false;
if(ref_vec[1].get_type()!=ast_call_func)
return false;
if(ref_vec[1].get_children().size()!=1 || ref_vec[1].get_children()[0].get_type()!=ast_string)
return false;
return true;
}
bool nasal_import::check_exist(std::string filename)
{
// avoid importing the same file
int size=filename_table.size();
for(int i=0;i<size;++i)
if(filename==filename_table[i])
return true;
filename_table.push_back(filename);
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast& add_root)
{
// add children of add_root to the back of root
std::vector<nasal_ast>& ref_vec=add_root.get_children();
int size=ref_vec.size();
for(int i=0;i<size;++i)
root.add_child(ref_vec[i]);
return;
}
nasal_ast nasal_import::file_import(nasal_ast& node)
{
// initializing
nasal_ast tmp;
tmp.set_line(0);
tmp.set_type(ast_root);
init();
// get filename and set node to ast_null
std::string filename=node.get_children()[1].get_children()[0].get_str();
node.clear();
node.set_type(ast_null);
// avoid infinite loading loop
if(check_exist(filename))
return tmp;
// start importing...
import_lex.openfile(filename);
import_lex.scanner();
if(import_lex.get_error())
{
this->die(filename,"lexer");
return tmp;
}
import_par.set_toklist(import_lex.get_token_list());
import_par.main_process();
if(import_par.get_error())
{
this->die(filename,"parser");
return tmp;
}
tmp=import_par.get_root();
// check if tmp has 'import'
return load(tmp);
}
nasal_ast nasal_import::load(nasal_ast& root)
{
nasal_ast new_root;
new_root.set_line(0);
new_root.set_type(ast_root);
std::vector<nasal_ast>& ref_vec=root.get_children();
int size=ref_vec.size();
for(int i=0;i<size;++i)
{
if(check_import(ref_vec[i]))
{
nasal_ast tmp=file_import(ref_vec[i]);
// add tmp to the back of new_root
linker(new_root,tmp);
}
}
// add root to the back of new_root
linker(new_root,root);
// oops,i think it is not efficient if the root is too ... large?
return new_root;
}
void nasal_import::link(nasal_ast& root)
{
// initializing
error=0;
filename_table.clear();
import_ast.clear();
// scan root and import files,then generate a new ast and return to import_ast
import_ast=load(root);
return;
}
nasal_ast& nasal_import::get_root()
{
return import_ast;
}
int nasal_import::get_error()
{
return error;
}
#endif

View File

@@ -1,464 +0,0 @@
#ifndef __NASAL_LEXER_H__
#define __NASAL_LEXER_H__
#define IS_IDENTIFIER(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
#define IS_HEX_NUMBER(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
#define IS_OCT_NUMEBR(c) ('0'<=c&&c<='7')
#define IS_DIGIT(c) ('0'<=c&&c<='9')
#define IS_STRING(c) (c=='\''||c=='\"'||c=='`')
// single operators have only one character
#define IS_SINGLE_OPRATOR(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 IS_CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
#define IS_NOTE(c) (c=='#')
enum token_type
{
tok_null=0,
tok_number,tok_string,tok_identifier,
tok_for,tok_forindex,tok_foreach,tok_while,
tok_var,tok_func,tok_break,tok_continue,
tok_return,tok_if,tok_elsif,tok_else,tok_nil,
tok_left_curve,tok_right_curve,
tok_left_bracket,tok_right_bracket,
tok_left_brace,tok_right_brace,
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_equal,
tok_add_equal,tok_sub_equal,tok_mult_equal,tok_div_equal,tok_link_equal,
tok_cmp_equal,tok_cmp_not_equal,tok_less_than,tok_less_equal,tok_greater_than,tok_greater_equal
};
struct
{
const char* str;
int tok_type;
}token_table[]=
{
{"for" ,tok_for },
{"forindex",tok_forindex },
{"foreach" ,tok_foreach },
{"while" ,tok_while },
{"var" ,tok_var },
{"func" ,tok_func },
{"break" ,tok_break },
{"continue",tok_continue },
{"return" ,tok_return },
{"if" ,tok_if },
{"elsif" ,tok_elsif },
{"else" ,tok_else },
{"nil" ,tok_nil },
{"(" ,tok_left_curve },
{")" ,tok_right_curve },
{"[" ,tok_left_bracket },
{"]" ,tok_right_bracket},
{"{" ,tok_left_brace },
{"}" ,tok_right_brace },
{";" ,tok_semi },
{"and" ,tok_and },
{"or" ,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_equal },
{"+=" ,tok_add_equal },
{"-=" ,tok_sub_equal },
{"*=" ,tok_mult_equal },
{"/=" ,tok_div_equal },
{"~=" ,tok_link_equal },
{"==" ,tok_cmp_equal },
{"!=" ,tok_cmp_not_equal},
{"<" ,tok_less_than },
{"<=" ,tok_less_equal },
{">" ,tok_greater_than },
{">=" ,tok_greater_equal},
{NULL ,-1 }
};
struct token
{
int line;
int type;
std::string str;
};
class nasal_lexer
{
private:
int error;
int res_size;
int line;
int ptr;
std::string line_code;
std::vector<char> res;
std::vector<token> token_list;
std::string identifier_gen();
std::string number_gen();
std::string string_gen();
public:
void clear();
void openfile(std::string);
void die(std::string,int,int);
void scanner();
void print_token();
int get_error();
std::vector<token>& get_token_list();
};
void nasal_lexer::clear()
{
error=0;
res_size=0;
line=0;
ptr=0;
line_code="";
res.clear();
token_list.clear();
return;
}
void nasal_lexer::openfile(std::string filename)
{
error=0;
res.clear();
std::ifstream fin(filename,std::ios::binary);
if(fin.fail())
{
++error;
std::cout<<">> [lexer] cannot open file \""<<filename<<"\".\n";
fin.close();
return;
}
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
res.push_back(c);
}
fin.close();
res_size=res.size();
return;
}
void nasal_lexer::die(std::string error_info,int line=-1,int column=-1)
{
++error;
std::cout<<">> [lexer] line "<<line<<" column "<<column<<": "<<error_info<<"\n";
return;
}
std::string nasal_lexer::identifier_gen()
{
std::string token_str="";
while(ptr<res_size && (IS_IDENTIFIER(res[ptr])||IS_DIGIT(res[ptr])))
token_str+=res[ptr++];
line_code+=token_str;
return token_str;
// after running this process, ptr will point to the next token's beginning character
}
std::string nasal_lexer::number_gen()
{
bool scientific_notation=false;// numbers like 1e8 are scientific_notation
std::string token_str="";
// generate hex number
if(res[ptr]=='0' && ptr+1<res_size && res[ptr+1]=='x')
{
token_str="0x";
ptr+=2;
while(ptr<res_size && IS_HEX_NUMBER(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str=="0x")
{
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
return token_str;
}
// generate oct number
else if(res[ptr]=='0' && ptr+1<res_size && res[ptr+1]=='o')
{
token_str="0o";
ptr+=2;
while(ptr<res_size && IS_OCT_NUMEBR(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str=="0o")
{
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
return token_str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
if(ptr<res_size && res[ptr]=='.')
{
token_str+=res[ptr++];
// "xxxx." is not a correct number
if(ptr>=res_size)
{
line_code+=token_str;
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxx." is not a correct number
if(token_str.back()=='.')
{
line_code+=token_str;
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
}
if(ptr<res_size && (res[ptr]=='e' || res[ptr]=='E'))
{
token_str+=res[ptr++];
// "xxxe" is not a correct number
if(ptr>=res_size)
{
line_code+=token_str;
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
if(ptr<res_size && (res[ptr]=='-' || res[ptr]=='+'))
token_str+=res[ptr++];
if(ptr>=res_size)
{
line_code+=token_str;
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
if(ptr<res_size && res[ptr]=='0')
token_str+=res[ptr++];
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxe(-|+)" is not a correct number
if(token_str.back()=='e' || token_str.back()=='E' || token_str.back()=='-' || token_str.back()=='+')
{
line_code+=token_str;
die("["+line_code+"_] incorrect number.",line,line_code.length());
return "0";
}
}
line_code+=token_str;
return token_str;
}
std::string nasal_lexer::string_gen()
{
std::string token_str="";
line_code+=res[ptr];
char str_begin=res[ptr++];
while(ptr<res_size && res[ptr]!=str_begin)
{
line_code+=res[ptr];
if(res[ptr]=='\n')
{
line_code="";
++line;
}
if(res[ptr]=='\\' && ptr+1<res_size)
{
++ptr;
line_code+=res[ptr];
switch(res[ptr])
{
case 'a':token_str.push_back('\a');break;
case 'b':token_str.push_back('\b');break;
case 'f':token_str.push_back('\f');break;
case 'n':token_str.push_back('\n');break;
case 'r':token_str.push_back('\r');break;
case 't':token_str.push_back('\t');break;
case 'v':token_str.push_back('\v');break;
case '?':token_str.push_back('\?');break;
case '0':token_str.push_back('\0');break;
case '\\':token_str.push_back('\\');break;
case '\'':token_str.push_back('\'');break;
case '\"':token_str.push_back('\"');break;
default: token_str.push_back(res[ptr]);break;
}
}
else
token_str+=res[ptr];
++ptr;
}
// check if this string ends with a " or '
if(ptr>=res_size)
die("["+line_code+"_] get EOF when generating string.",line,line_code.length());
++ptr;
return token_str;
}
void nasal_lexer::scanner()
{
token_list.clear();
line=1;
ptr=0;
line_code="";
std::string token_str;
while(ptr<res_size)
{
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
line_code+=res[ptr];
if(res[ptr]=='\n')
{
++line;
line_code="";
}
++ptr;
}
if(ptr>=res_size) break;
if(IS_IDENTIFIER(res[ptr]))
{
token_str=identifier_gen();
token new_token;
new_token.line=line;
new_token.str=token_str;
new_token.type=0;
for(int i=0;token_table[i].str;++i)
if(token_str==token_table[i].str)
{
new_token.type=token_table[i].tok_type;
break;
}
if(!new_token.type)
new_token.type=tok_identifier;
token_list.push_back(new_token);
}
else if(IS_DIGIT(res[ptr]))
{
token_str=number_gen();
token new_token;
new_token.line=line;
new_token.str=token_str;
new_token.type=tok_number;
token_list.push_back(new_token);
}
else if(IS_STRING(res[ptr]))
{
token_str=string_gen();
token new_token;
new_token.line=line;
new_token.type=tok_string;
new_token.str=token_str;
token_list.push_back(new_token);
}
else if(IS_SINGLE_OPRATOR(res[ptr]))
{
token_str="";
token_str+=res[ptr];
line_code+=res[ptr];
token new_token;
new_token.line=line;
new_token.str=token_str;
new_token.type=-1;
for(int i=0;token_table[i].str;++i)
if(token_str==token_table[i].str)
{
new_token.type=token_table[i].tok_type;
break;
}
if(new_token.type<0)
die("["+line_code+"_] incorrect operator.",line,line_code.length());
token_list.push_back(new_token);
++ptr;
}
else if(res[ptr]=='.')
{
if(ptr+2<res_size && res[ptr+1]=='.' && res[ptr+2]=='.')
{
token_str="...";
ptr+=3;
}
else
{
token_str=".";
++ptr;
}
line_code+=token_str;
token new_token;
new_token.line=line;
new_token.str=token_str;
for(int i=0;token_table[i].str;++i)
if(token_str==token_table[i].str)
{
new_token.type=token_table[i].tok_type;
break;
}
token_list.push_back(new_token);
}
else if(IS_CALC_OPERATOR(res[ptr]))
{
// get calculation operator
token_str=res[ptr];
++ptr;
if(ptr<res.size() && res[ptr]=='=')
{
token_str+=res[ptr];
++ptr;
}
line_code+=token_str;
token new_token;
new_token.line=line;
new_token.str=token_str;
for(int i=0;token_table[i].str;++i)
if(token_str==token_table[i].str)
{
new_token.type=token_table[i].tok_type;
break;
}
token_list.push_back(new_token);
}
else if(IS_NOTE(res[ptr]))
{
// avoid note
while(ptr<res_size && res[ptr]!='\n') ++ptr;
// after this process ptr will point to a '\n'
// don't ++ptr then the counter for line can work correctly
}
else
{
line_code+=res[ptr];
die("["+line_code+"_] unknown character.",line,line_code.length());
++ptr;
}
}
return;
}
void nasal_lexer::print_token()
{
int size=token_list.size();
for(int i=0;i<size;++i)
std::cout<<"("<<token_list[i].line<<" | "<<token_list[i].str<<")\n";
return;
}
int nasal_lexer::get_error()
{
return error;
}
std::vector<token>& nasal_lexer::get_token_list()
{
return token_list;
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

485
src/ast_dumper.cpp Normal file
View File

@@ -0,0 +1,485 @@
#include "ast_dumper.h"
#include <iostream>
namespace nasal {
bool ast_dumper::visit_null_expr(null_expr* node) {
dump_indent();
std::cout << "null" << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_nil_expr(nil_expr* node) {
dump_indent();
std::cout << "nil" << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_number_literal(number_literal* node) {
dump_indent();
std::cout << "number " << node->get_number();
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_string_literal(string_literal* node) {
dump_indent();
std::cout << "string \"" << rawstr(node->get_content()) << "\"";
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_identifier(identifier* node) {
dump_indent();
std::cout << "identifier " << node->get_name();
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_bool_literal(bool_literal* node) {
dump_indent();
std::cout << "bool " << node->get_flag();
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_vector_expr(vector_expr* node) {
dump_indent();
std::cout << "vector";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_elements()) {
if (i==node->get_elements().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_hash_expr(hash_expr* node) {
dump_indent();
std::cout << "hash";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_members()) {
if (i==node->get_members().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_hash_pair(hash_pair* node) {
dump_indent();
std::cout << "pair " << node->get_name();
std::cout << format_location(node->get_location());
if (node->get_value()) {
push_indent();
set_last();
node->get_value()->accept(this);
pop_indent();
}
return true;
}
bool ast_dumper::visit_function(function* node) {
dump_indent();
std::cout << "function";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_parameter_list()) {
i->accept(this);
}
set_last();
node->get_code_block()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_code_block(code_block* node) {
dump_indent();
std::cout << "block";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_expressions()) {
if (i==node->get_expressions().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_parameter(parameter* node) {
dump_indent();
std::cout << "parameter " << node->get_parameter_name();
std::cout << format_location(node->get_location());
if (node->get_default_value()) {
push_indent();
set_last();
node->get_default_value()->accept(this);
pop_indent();
}
return true;
}
bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
dump_indent();
std::cout << "ternary_operator";
std::cout << format_location(node->get_location());
push_indent();
node->get_condition()->accept(this);
node->get_left()->accept(this);
set_last();
node->get_right()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_binary_operator(binary_operator* node) {
if (node->get_optimized_number()) {
node->get_optimized_number()->accept(this);
return true;
}
if (node->get_optimized_string()) {
node->get_optimized_string()->accept(this);
return true;
}
dump_indent();
std::cout << "binary_operator ";
switch(node->get_operator_type()) {
case binary_operator::binary_type::add: std::cout << "+"; break;
case binary_operator::binary_type::sub: std::cout << "-"; break;
case binary_operator::binary_type::mult: std::cout << "*"; break;
case binary_operator::binary_type::div: std::cout << "/"; break;
case binary_operator::binary_type::concat: std::cout << "~"; break;
case binary_operator::binary_type::bitwise_and: std::cout << "&"; break;
case binary_operator::binary_type::bitwise_or: std::cout << "|"; break;
case binary_operator::binary_type::bitwise_xor: std::cout << "^"; break;
case binary_operator::binary_type::cmpeq: std::cout << "=="; break;
case binary_operator::binary_type::cmpneq: std::cout << "!="; break;
case binary_operator::binary_type::grt: std::cout << ">"; break;
case binary_operator::binary_type::geq: std::cout << ">="; break;
case binary_operator::binary_type::less: std::cout << "<"; break;
case binary_operator::binary_type::leq: std::cout << "<="; break;
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
}
std::cout << format_location(node->get_location());
push_indent();
node->get_left()->accept(this);
set_last();
node->get_right()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_unary_operator(unary_operator* node) {
if (node->get_optimized_number()) {
node->get_optimized_number()->accept(this);
return true;
}
dump_indent();
std::cout << "unary_operator ";
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative: std::cout << "-"; break;
case unary_operator::unary_type::logical_not: std::cout << "!"; break;
case unary_operator::unary_type::bitwise_not: std::cout << "~"; break;
}
std::cout << format_location(node->get_location());
push_indent();
set_last();
node->get_value()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_call_expr(call_expr* node) {
dump_indent();
std::cout << "call_expr";
std::cout << format_location(node->get_location());
push_indent();
if (!node->get_calls().size()) {
set_last();
}
node->get_first()->accept(this);
for(auto i : node->get_calls()) {
if (i==node->get_calls().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_call_hash(call_hash* node) {
dump_indent();
std::cout << "call_hash " << node->get_field();
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_call_vector(call_vector* node) {
dump_indent();
std::cout << "call_vector";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_slices()) {
if (i==node->get_slices().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_call_function(call_function* node) {
dump_indent();
std::cout << "call_function";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_argument()) {
if (i==node->get_argument().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_slice_vector(slice_vector* node) {
dump_indent();
std::cout << "slice";
std::cout << format_location(node->get_location());
push_indent();
if (!node->get_end()) {
set_last();
}
node->get_begin()->accept(this);
if (node->get_end()) {
set_last();
node->get_end()->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_definition_expr(definition_expr* node) {
dump_indent();
std::cout << "definition";
std::cout << format_location(node->get_location());
push_indent();
if (node->get_variable_name()) {
node->get_variable_name()->accept(this);
} else {
node->get_variables()->accept(this);
}
set_last();
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
dump_indent();
std::cout << "assignment ";
switch(node->get_assignment_type()) {
case assignment_expr::assign_type::add_equal: std::cout << "+="; break;
case assignment_expr::assign_type::sub_equal: std::cout << "-="; break;
case assignment_expr::assign_type::mult_equal: std::cout << "*="; break;
case assignment_expr::assign_type::div_equal: std::cout << "/="; break;
case assignment_expr::assign_type::concat_equal: std::cout << "~="; break;
case assignment_expr::assign_type::equal: std::cout << "="; break;
case assignment_expr::assign_type::bitwise_and_equal: std::cout << "&="; break;
case assignment_expr::assign_type::bitwise_or_equal: std::cout << "|="; break;
case assignment_expr::assign_type::bitwise_xor_equal: std::cout << "^="; break;
}
std::cout << format_location(node->get_location());
push_indent();
node->get_left()->accept(this);
set_last();
node->get_right()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
dump_indent();
std::cout << "multiple_identifier";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_variables()) {
if (i==node->get_variables().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
dump_indent();
std::cout << "tuple";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_elements()) {
if (i==node->get_elements().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_multi_assign(multi_assign* node) {
dump_indent();
std::cout << "multiple_assignment";
std::cout << format_location(node->get_location());
push_indent();
node->get_tuple()->accept(this);
set_last();
node->get_value()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_while_expr(while_expr* node) {
dump_indent();
std::cout << "while";
std::cout << format_location(node->get_location());
push_indent();
node->get_condition()->accept(this);
set_last();
node->get_code_block()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_for_expr(for_expr* node) {
dump_indent();
std::cout << "for";
std::cout << format_location(node->get_location());
push_indent();
node->get_initial()->accept(this);
node->get_condition()->accept(this);
node->get_step()->accept(this);
set_last();
node->get_code_block()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_iter_expr(iter_expr* node) {
dump_indent();
std::cout << "iterator";
std::cout << format_location(node->get_location());
push_indent();
set_last();
if (node->get_name()) {
node->get_name()->accept(this);
} else {
node->get_call()->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_forei_expr(forei_expr* node) {
dump_indent();
if (node->get_loop_type()==forei_expr::forei_loop_type::foreach) {
std::cout << "foreach";
} else {
std::cout << "forindex";
}
std::cout << format_location(node->get_location());
push_indent();
node->get_iterator()->accept(this);
node->get_value()->accept(this);
set_last();
node->get_code_block()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_condition_expr(condition_expr* node) {
dump_indent();
std::cout << "condition";
std::cout << format_location(node->get_location());
push_indent();
if (!node->get_elsif_stataments().size() &&
!node->get_else_statement()) {
set_last();
}
node->get_if_statement()->accept(this);
for(auto i : node->get_elsif_stataments()) {
if (i==node->get_elsif_stataments().back() &&
!node->get_else_statement()) {
set_last();
}
i->accept(this);
}
if (node->get_else_statement()) {
set_last();
node->get_else_statement()->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_if_expr(if_expr* node) {
dump_indent();
std::cout << "if";
std::cout << format_location(node->get_location());
push_indent();
if (node->get_condition()) {
node->get_condition()->accept(this);
}
set_last();
node->get_code_block()->accept(this);
pop_indent();
return true;
}
bool ast_dumper::visit_continue_expr(continue_expr* node) {
dump_indent();
std::cout << "continue";
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_break_expr(break_expr* node) {
dump_indent();
std::cout << "break";
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_return_expr(return_expr* node) {
dump_indent();
std::cout << "return";
std::cout << format_location(node->get_location());
if (node->get_value()) {
push_indent();
set_last();
node->get_value()->accept(this);
pop_indent();
}
return true;
}
}

85
src/ast_dumper.h Normal file
View File

@@ -0,0 +1,85 @@
#pragma once
#include "ast_visitor.h"
#include <iostream>
#include <cstring>
#include <sstream>
namespace nasal {
class ast_dumper:public ast_visitor {
private:
std::vector<std::string> indent;
void push_indent() {
if (indent.size()) {
if (indent.back()=="|--") {
indent.back() = "| ";
} else if (indent.back()=="+--") {
indent.back() = " ";
}
}
indent.push_back("|--");
}
void pop_indent() {indent.pop_back();}
void set_last() {indent.back() = "+--";}
void dump_indent() {
if (indent.size() && indent.back()=="| ") {
indent.back() = "|--";
}
for(const auto& i : indent) {
std::cout << i;
}
}
std::string format_location(const span& location) {
std::stringstream ss;
ss << " -> ";
ss << location.file << ":";
ss << location.begin_line << ":" << location.begin_column + 1;
ss << "\n";
return ss.str();
}
public:
bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override;
bool visit_string_literal(string_literal*) override;
bool visit_identifier(identifier*) override;
bool visit_bool_literal(bool_literal*) override;
bool visit_vector_expr(vector_expr*) override;
bool visit_hash_expr(hash_expr*) override;
bool visit_hash_pair(hash_pair*) override;
bool visit_function(function*) override;
bool visit_code_block(code_block*) override;
bool visit_parameter(parameter*) override;
bool visit_ternary_operator(ternary_operator*) override;
bool visit_binary_operator(binary_operator*) override;
bool visit_unary_operator(unary_operator*) override;
bool visit_call_expr(call_expr*) override;
bool visit_call_hash(call_hash*) override;
bool visit_call_vector(call_vector*) override;
bool visit_call_function(call_function*) override;
bool visit_slice_vector(slice_vector*) override;
bool visit_definition_expr(definition_expr*) override;
bool visit_assignment_expr(assignment_expr*) override;
bool visit_multi_identifier(multi_identifier*) override;
bool visit_tuple_expr(tuple_expr*) override;
bool visit_multi_assign(multi_assign*) override;
bool visit_while_expr(while_expr*) override;
bool visit_for_expr(for_expr*) override;
bool visit_iter_expr(iter_expr*) override;
bool visit_forei_expr(forei_expr*) override;
bool visit_condition_expr(condition_expr*) override;
bool visit_if_expr(if_expr*) override;
bool visit_continue_expr(continue_expr*) override;
bool visit_break_expr(break_expr*) override;
bool visit_return_expr(return_expr*) override;
public:
void dump(code_block* root) {
root->accept(this);
}
};
}

238
src/ast_visitor.cpp Normal file
View File

@@ -0,0 +1,238 @@
#include "ast_visitor.h"
namespace nasal {
bool ast_visitor::visit_expr(expr* node) {
node->accept(this);
return true;
}
bool ast_visitor::visit_call(call* node) {
node->accept(this);
return true;
}
bool ast_visitor::visit_null_expr(null_expr* node) {
return true;
}
bool ast_visitor::visit_nil_expr(nil_expr* node) {
return true;
}
bool ast_visitor::visit_number_literal(number_literal* node) {
return true;
}
bool ast_visitor::visit_string_literal(string_literal* node) {
return true;
}
bool ast_visitor::visit_identifier(identifier* node) {
return true;
}
bool ast_visitor::visit_bool_literal(bool_literal* node) {
return true;
}
bool ast_visitor::visit_vector_expr(vector_expr* node) {
for(auto i : node->get_elements()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_hash_expr(hash_expr* node) {
for(auto i : node->get_members()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_hash_pair(hash_pair* node) {
if (node->get_value()) {
node->get_value()->accept(this);
}
return true;
}
bool ast_visitor::visit_function(function* node) {
for(auto i : node->get_parameter_list()) {
i->accept(this);
}
node->get_code_block()->accept(this);
return true;
}
bool ast_visitor::visit_code_block(code_block* node) {
for(auto i : node->get_expressions()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_parameter(parameter* node) {
if (node->get_default_value()) {
node->get_default_value()->accept(this);
}
return true;
}
bool ast_visitor::visit_ternary_operator(ternary_operator* node) {
node->get_condition()->accept(this);
node->get_left()->accept(this);
node->get_right()->accept(this);
return true;
}
bool ast_visitor::visit_binary_operator(binary_operator* node) {
node->get_left()->accept(this);
node->get_right()->accept(this);
return true;
}
bool ast_visitor::visit_unary_operator(unary_operator* node) {
node->get_value()->accept(this);
return true;
}
bool ast_visitor::visit_call_expr(call_expr* node) {
node->get_first()->accept(this);
for(auto i : node->get_calls()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_call_hash(call_hash* node) {
return true;
}
bool ast_visitor::visit_call_vector(call_vector* node) {
for(auto i : node->get_slices()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_call_function(call_function* node) {
for(auto i : node->get_argument()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_slice_vector(slice_vector* node) {
node->get_begin()->accept(this);
if (node->get_end()) {
node->get_end()->accept(this);
}
return true;
}
bool ast_visitor::visit_definition_expr(definition_expr* node) {
if (node->get_variable_name()) {
node->get_variable_name()->accept(this);
} else {
node->get_variables()->accept(this);
}
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
return true;
}
bool ast_visitor::visit_assignment_expr(assignment_expr* node) {
node->get_left()->accept(this);
node->get_right()->accept(this);
return true;
}
bool ast_visitor::visit_multi_identifier(multi_identifier* node) {
for(auto i : node->get_variables()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_tuple_expr(tuple_expr* node) {
for(auto i : node->get_elements()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_multi_assign(multi_assign* node) {
node->get_tuple()->accept(this);
node->get_value()->accept(this);
return true;
}
bool ast_visitor::visit_while_expr(while_expr* node) {
node->get_condition()->accept(this);
node->get_code_block()->accept(this);
return true;
}
bool ast_visitor::visit_for_expr(for_expr* node) {
node->get_initial()->accept(this);
node->get_condition()->accept(this);
node->get_step()->accept(this);
node->get_code_block()->accept(this);
return true;
}
bool ast_visitor::visit_iter_expr(iter_expr* node) {
if (node->get_name()) {
node->get_name()->accept(this);
} else {
node->get_call()->accept(this);
}
return true;
}
bool ast_visitor::visit_forei_expr(forei_expr* node) {
node->get_iterator()->accept(this);
node->get_value()->accept(this);
node->get_code_block()->accept(this);
return true;
}
bool ast_visitor::visit_condition_expr(condition_expr* node) {
node->get_if_statement()->accept(this);
for(auto i : node->get_elsif_stataments()) {
i->accept(this);
}
if (node->get_else_statement()) {
node->get_else_statement()->accept(this);
}
return true;
}
bool ast_visitor::visit_if_expr(if_expr* node) {
if (node->get_condition()) {
node->get_condition()->accept(this);
}
node->get_code_block()->accept(this);
return true;
}
bool ast_visitor::visit_continue_expr(continue_expr* node) {
return true;
}
bool ast_visitor::visit_break_expr(break_expr* node) {
return true;
}
bool ast_visitor::visit_return_expr(return_expr* node) {
if (node->get_value()) {
node->get_value()->accept(this);
}
return true;
}
}

47
src/ast_visitor.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
#include "nasal_ast.h"
namespace nasal {
class ast_visitor {
public:
virtual bool visit_expr(expr*);
virtual bool visit_call(call*);
virtual bool visit_null_expr(null_expr*);
virtual bool visit_nil_expr(nil_expr*);
virtual bool visit_number_literal(number_literal*);
virtual bool visit_string_literal(string_literal*);
virtual bool visit_identifier(identifier*);
virtual bool visit_bool_literal(bool_literal*);
virtual bool visit_vector_expr(vector_expr*);
virtual bool visit_hash_expr(hash_expr*);
virtual bool visit_hash_pair(hash_pair*);
virtual bool visit_function(function*);
virtual bool visit_code_block(code_block*);
virtual bool visit_parameter(parameter*);
virtual bool visit_ternary_operator(ternary_operator*);
virtual bool visit_binary_operator(binary_operator*);
virtual bool visit_unary_operator(unary_operator*);
virtual bool visit_call_expr(call_expr*);
virtual bool visit_call_hash(call_hash*);
virtual bool visit_call_vector(call_vector*);
virtual bool visit_call_function(call_function*);
virtual bool visit_slice_vector(slice_vector*);
virtual bool visit_definition_expr(definition_expr*);
virtual bool visit_assignment_expr(assignment_expr*);
virtual bool visit_multi_identifier(multi_identifier*);
virtual bool visit_tuple_expr(tuple_expr*);
virtual bool visit_multi_assign(multi_assign*);
virtual bool visit_while_expr(while_expr*);
virtual bool visit_for_expr(for_expr*);
virtual bool visit_iter_expr(iter_expr*);
virtual bool visit_forei_expr(forei_expr*);
virtual bool visit_condition_expr(condition_expr*);
virtual bool visit_if_expr(if_expr*);
virtual bool visit_continue_expr(continue_expr*);
virtual bool visit_break_expr(break_expr*);
virtual bool visit_return_expr(return_expr*);
};
}

153
src/bits_lib.cpp Normal file
View File

@@ -0,0 +1,153 @@
#include "bits_lib.h"
namespace nasal {
var builtin_u32xor(var* local, gc& ngc) {
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) ^
static_cast<u32>(local[2].num())
));
}
var builtin_u32and(var* local, gc& ngc) {
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
));
}
var builtin_u32or(var* local, gc& ngc) {
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) |
static_cast<u32>(local[2].num())
));
}
var builtin_u32nand(var* local, gc& ngc) {
return var::num(static_cast<f64>(~(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
)));
}
var builtin_u32not(var* local, gc& ngc) {
return var::num(static_cast<f64>(~static_cast<u32>(local[1].num())));
}
var builtin_fld(var* local, gc& ngc) {
// bits.fld(s,0,3);
// if s stores 10100010(162)
// will get 101(5)
var str = local[1];
var startbit = local[2];
var length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("fld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("fld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
if (bit+len>8*str.str().length()) {
return nas_err("fld", "bitfield out of bounds");
}
u32 res = 0;
auto& s = str.str();
for(u32 i = bit; i<bit+len; ++i) {
if (s[i>>3]&(1<<(7-(i&7)))) {
res |= 1<<(bit+len-i-1);
}
}
return var::num(static_cast<f64>(res));
}
var builtin_sfld(var* local, gc& ngc) {
// bits.sfld(s,0,3);
// if s stores 10100010(162)
// will get 101(5) then this will be signed extended to
// 11111101(-3)
var str = local[1];
var startbit = local[2];
var length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("sfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("sfld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
if (bit+len>8*str.str().length()) {
return nas_err("sfld", "bitfield out of bounds");
}
u32 res = 0;
auto& s = str.str();
for(u32 i = bit; i<bit+len; ++i) {
if (s[i>>3]&(1<<(7-(i&7)))) {
res |= 1<<(bit+len-i-1);
}
}
if (res&(1<<(len-1))) {
res |= ~((1<<len)-1);
}
return var::num(static_cast<f64>(static_cast<i32>(res)));
}
var builtin_setfld(var* local, gc& ngc) {
// bits.setfld(s,0,8,69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
var str = local[1];
var startbit = local[2];
var length = local[3];
var value = local[4];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("setfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
return nas_err("setfld", "\"startbit\",\"len\",\"val\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
u64 val = static_cast<u64>(value.num());
if (bit+len>8*str.str().length()) {
return nas_err("setfld", "bitfield out of bounds");
}
auto& s = str.str();
for(u32 i = bit; i<bit+len; ++i) {
if (val&(1<<(i-bit))) {
s[i>>3] |= (1<<(7-(i&7)));
} else {
s[i>>3] &= ~(1<<(7-(i&7)));
}
}
return nil;
}
var builtin_buf(var* local, gc& ngc) {
var length = local[1];
if (length.type!=vm_num || length.num()<=0) {
return nas_err("buf", "\"len\" must be number greater than 0");
}
var str = ngc.alloc(vm_str);
auto& s = str.str();
s.resize(length.num(), '\0');
return str;
}
nasal_builtin_table bits_native[] = {
{"__u32xor", builtin_u32xor},
{"__u32and", builtin_u32and},
{"__u32or", builtin_u32or},
{"__u32nand", builtin_u32nand},
{"__u32not", builtin_u32not},
{"__fld", builtin_fld},
{"__sfld", builtin_sfld},
{"__setfld", builtin_setfld},
{"__buf", builtin_buf},
{nullptr, nullptr}
};
}

21
src/bits_lib.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
namespace nasal {
var builtin_u32xor(var*, gc&);
var builtin_u32and(var*, gc&);
var builtin_u32or(var*, gc&);
var builtin_u32nand(var*, gc&);
var builtin_u32not(var*, gc&);
var builtin_fld(var*, gc&);
var builtin_sfld(var*, gc&);
var builtin_setfld(var*, gc&);
var builtin_buf(var*, gc&);
extern nasal_builtin_table bits_native[];
}

120
src/coroutine.cpp Normal file
View File

@@ -0,0 +1,120 @@
#include "coroutine.h"
namespace nasal {
var builtin_cocreate(var* local, gc& ngc) {
// +-------------+
// | old pc | <- top[0]
// +-------------+
// | old localr | <- top[-1]
// +-------------+
// | old upvalr | <- top[-2]
// +-------------+
// | local scope |
// | ... |
// +-------------+ <- local pointer stored in localr
// | old funcr | <- old function stored in funcr
// +-------------+
var func = local[1];
if (func.type!=vm_func) {
return nas_err("coroutine::create", "must use a function to create coroutine");
}
if (ngc.cort) {
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
}
var co = ngc.alloc(vm_co);
nas_co& cort = co.co();
cort.ctx.pc = func.func().entry-1;
cort.ctx.top[0] = nil;
cort.ctx.localr = cort.ctx.top+1;
cort.ctx.top = cort.ctx.localr+func.func().lsize;
cort.ctx.localr[0] = func.func().local[0];
cort.ctx.top[0] = nil; // old upvalr
cort.ctx.top++;
cort.ctx.top[0] = var::addr((var*)nullptr); // old localr
cort.ctx.top++;
cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
cort.status = nas_co::status::suspended;
return co;
}
var builtin_coresume(var* local, gc& ngc) {
if (ngc.cort) {
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
}
var co = local[1];
// return nil if is not a coroutine object
if (co.type!=vm_co) {
return nil;
}
// cannot resume a dead coroutine
if (co.co().status==nas_co::status::dead) {
return nil;
}
// change to coroutine context
ngc.ctxchg(co.co());
// fetch coroutine's stack top and return
// so the coroutine's stack top in fact is not changed
if (ngc.rctx->top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc.rctx->top[0];
}
// after first calling the coroutine, each time coroutine.yield triggered
// a new space will be reserved on stack with value nil
// so we could fill this place with args
// the coroutine seems like coroutine.yield returns the value
// but in fact coroutine.yield stop the coroutine
// until main context calls the coroutine.resume
return local[2];
}
var builtin_coyield(var* local, gc& ngc) {
if (!ngc.cort) {
return nas_err("coroutine::yield", "no coroutine is running");
}
// this will set to main stack top
ngc.ctxreserve();
// then this will return value to main's stack top[0]
// the procedure seems like coroutine.resume returns the value
// but in fact coroutine.resume stop the main context
// until coroutine calls the coroutine.yield
return local[1];
}
var builtin_costatus(var* local, gc& ngc) {
var co = local[1];
if (co.type!=vm_co) {
return ngc.newstr("error");
}
switch(co.co().status) {
case nas_co::status::suspended: return ngc.newstr("suspended");
case nas_co::status::running: return ngc.newstr("running");
case nas_co::status::dead: return ngc.newstr("dead");
}
return nil;
}
var builtin_corun(var* local, gc& ngc) {
return ngc.cort? one:zero;
}
nasal_builtin_table coroutine_native[] = {
{"__cocreate", builtin_cocreate},
{"__coresume", builtin_coresume},
{"__coyield", builtin_coyield},
{"__costatus", builtin_costatus},
{"__corun", builtin_corun},
{nullptr, nullptr}
};
}

17
src/coroutine.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
namespace nasal {
var builtin_cocreate(var*, gc&);
var builtin_coresume(var*, gc&);
var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&);
extern nasal_builtin_table coroutine_native[];
}

117
src/dylib_lib.cpp Normal file
View File

@@ -0,0 +1,117 @@
#include "dylib_lib.h"
namespace nasal {
const auto dylib_type_name = "dylib";
const auto func_addr_type_name = "faddr";
void dylib_destructor(void* ptr) {
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(ptr));
#else
dlclose(ptr);
#endif
}
void func_addr_destructor(void* ptr) {}
var builtin_dlopen(var* local, gc& ngc) {
var dlname = local[1];
if (dlname.type!=vm_str) {
return nas_err("dlopen", "\"libname\" must be string");
}
#ifdef _WIN32
wchar_t* str = new wchar_t[dlname.str().size()+1];
if (!str) {
return nas_err("dlopen", "malloc failed");
}
memset(str, 0, sizeof(wchar_t)*dlname.str().size()+1);
mbstowcs(str, dlname.str().c_str(),dlname.str().size()+1);
void* ptr = LoadLibraryA(dlname.str().c_str());
delete []str;
#else
void* ptr = dlopen(dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY);
#endif
if (!ptr) {
return nas_err("dlopen", "cannot open dynamic lib <"+dlname.str()+">");
}
var ret = ngc.temp = ngc.alloc(vm_hash);
var lib = ngc.alloc(vm_obj);
lib.obj().set(dylib_type_name, dylib_destructor, ptr);
ret.hash().elems["lib"] = lib;
#ifdef _WIN32
void* func = (void*)GetProcAddress(
static_cast<HMODULE>(lib.obj().ptr),
"get"
);
#else
void* func = dlsym(lib.obj().ptr, "get");
#endif
if (!func) {
return nas_err("dlopen", "cannot find <get> function");
}
// get function pointer by name
module_func_info* tbl = reinterpret_cast<get_func_ptr>(func)();
if (!tbl) {
return nas_err("dlopen", "failed to get module functions");
}
for(u32 i = 0; tbl[i].name; ++i) {
void* p = (void*)tbl[i].fd;
var tmp = ngc.alloc(vm_obj);
tmp.obj().set(func_addr_type_name, func_addr_destructor, p);
ret.hash().elems[tbl[i].name] = tmp;
}
ngc.temp = nil;
return ret;
}
var builtin_dlclose(var* local, gc& ngc) {
var libptr = local[1];
if (!libptr.objchk(dylib_type_name)) {
return nas_err("dlclose", "\"lib\" is not a valid dynamic lib");
}
libptr.obj().clear();
return nil;
}
var builtin_dlcallv(var* local, gc& ngc) {
var fp = local[1];
var args = local[2];
if (!fp.objchk(func_addr_type_name)) {
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
}
auto& vec = args.vec().elems;
return reinterpret_cast<module_func>(fp.obj().ptr)(
vec.data(),
vec.size(),
&ngc
);
}
var builtin_dlcall(var* local, gc& ngc) {
var fp = local[1];
if (!fp.objchk(func_addr_type_name)) {
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
}
var* local_frame_start = local+2;
usize local_frame_size = ngc.rctx->top-local_frame_start;
// arguments' stored place begins at local +2
return reinterpret_cast<module_func>(fp.obj().ptr)(
local_frame_start,
local_frame_size,
&ngc
);
}
nasal_builtin_table dylib_lib_native[] = {
{"__dlopen", builtin_dlopen},
{"__dlclose", builtin_dlclose},
{"__dlcallv", builtin_dlcallv},
{"__dlcall", builtin_dlcall},
{nullptr, nullptr}
};
}

26
src/dylib_lib.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/wait.h>
#endif
namespace nasal {
void dylib_destructor(void*);
void func_addr_destructor(void*);
var builtin_dlopen(var*, gc&);
var builtin_dlclose(var*, gc&);
var builtin_dlcallv(var*, gc&);
var builtin_dlcall(var*, gc&);
extern nasal_builtin_table dylib_lib_native[];
}

41
src/fg_props.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "fg_props.h"
#include <fstream>
namespace nasal {
var builtin_logprint(var* local, gc& ngc) {
var level = local[1];
var elems = local[2];
if (elems.type!=vm_vec) {
return nas_err("logprint", "received argument is not vector.");
}
std::ofstream out("fgfs.log", std::ios::app);
switch (static_cast<u32>(level.num())) {
case SG_LOG_BULK: out << "[LOG_BULK]"; break;
case SG_LOG_DEBUG: out << "[LOG_DEBUG]"; break;
case SG_LOG_INFO: out << "[LOG_INFO]"; break;
case SG_LOG_WARN: out << "[LOG_WARN]"; break;
case SG_LOG_ALERT: out << "[LOG_ALERT]"; break;
case SG_DEV_WARN: out << "[DEV_WARN]"; break;
case SG_DEV_ALERT: out << "[DEV_ALERT]"; break;
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
default:
return nas_err("logprint",
"incorrect log level " +
std::to_string(level.num())
);
}
for(auto& i : elems.vec().elems) {
out << i << " ";
}
out << "\n";
return nil;
}
nasal_builtin_table flight_gear_native[] = {
{"_logprint", builtin_logprint},
{nullptr, nullptr}
};
}

22
src/fg_props.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
namespace nasal {
#define SG_LOG_BULK 1
#define SG_LOG_DEBUG 2
#define SG_LOG_INFO 3
#define SG_LOG_WARN 4
#define SG_LOG_ALERT 5
#define SG_DEV_WARN 7
#define SG_DEV_ALERT 8
#define SG_MANDATORY_INFO 9
var builtin_logprint(var*, gc&);
extern nasal_builtin_table flight_gear_native[];
}

213
src/io_lib.cpp Normal file
View File

@@ -0,0 +1,213 @@
#include "io_lib.h"
namespace nasal {
const auto file_type_name = "file";
void filehandle_destructor(void* ptr) {
if (static_cast<FILE*>(ptr)==stdin) {
return;
}
fclose(static_cast<FILE*>(ptr));
}
var builtin_readfile(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_str) {
return nas_err("io::readfile", "\"filename\" must be string");
}
std::ifstream in(val.str(), std::ios::binary);
std::stringstream rd;
if (!in.fail()) {
rd << in.rdbuf();
}
return ngc.newstr(rd.str());
}
var builtin_fout(var* local, gc& ngc) {
var val = local[1];
var str = local[2];
if (val.type!=vm_str) {
return nas_err("io::fout", "\"filename\" must be string");
}
std::ofstream out(val.str());
if (out.fail()) {
return nas_err("io::fout", "cannot open <"+val.str()+">");
}
out << str;
return nil;
}
var builtin_exists(var* local, gc& ngc) {
if (local[1].type!=vm_str) {
return zero;
}
return access(local[1].str().c_str(), F_OK)!=-1?one:zero;
}
var builtin_open(var* local, gc& ngc) {
var name = local[1];
var mode = local[2];
if (name.type!=vm_str) {
return nas_err("open", "\"filename\" must be string");
}
if (mode.type!=vm_str) {
return nas_err("open", "\"mode\" must be string");
}
FILE* res = fopen(name.str().c_str(), mode.str().c_str());
if (!res) {
return nas_err("open", "failed to open file <"+name.str()+">");
}
var ret = ngc.alloc(vm_obj);
ret.obj().set(file_type_name, filehandle_destructor, res);
return ret;
}
var builtin_close(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("close", "not a valid filehandle");
}
fd.obj().clear();
return nil;
}
var builtin_read(var* local, gc& ngc) {
var fd = local[1];
var buf = local[2];
var len = local[3];
if (!fd.objchk(file_type_name)) {
return nas_err("read", "not a valid filehandle");
}
if (buf.type!=vm_str || buf.val.gcobj->unmut) {
return nas_err("read", "\"buf\" must be mutable string");
}
if (len.type!=vm_num) {
return nas_err("read", "\"len\" must be number");
}
if (len.num()<=0 || len.num()>=(1<<30)) {
return nas_err("read", "\"len\" less than 1 or too large");
}
char* buff = new char[(usize)len.num()+1];
if (!buff) {
return nas_err("read", "malloc failed");
}
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().ptr));
buf.str() = buff;
buf.val.gcobj->unmut = true;
delete []buff;
return var::num(res);
}
var builtin_write(var* local, gc& ngc) {
var fd = local[1];
var str = local[2];
if (!fd.objchk(file_type_name)) {
return nas_err("write", "not a valid filehandle");
}
if (str.type!=vm_str) {
return nas_err("write", "\"str\" must be string");
}
return var::num(static_cast<f64>(fwrite(
str.str().c_str(),
1,
str.str().length(),
static_cast<FILE*>(fd.obj().ptr)
)));
}
var builtin_seek(var* local, gc& ngc) {
var fd = local[1];
var pos = local[2];
var whence = local[3];
if (!fd.objchk(file_type_name)) {
return nas_err("seek", "not a valid filehandle");
}
return var::num(static_cast<f64>(fseek(
static_cast<FILE*>(fd.obj().ptr),
pos.num(),
whence.num()
)));
}
var builtin_tell(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("tell", "not a valid filehandle");
}
return var::num(static_cast<f64>(ftell(static_cast<FILE*>(fd.obj().ptr))));
}
var builtin_readln(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("readln", "not a valid filehandle");
}
var str = ngc.alloc(vm_str);
char c;
while((c = fgetc(static_cast<FILE*>(fd.obj().ptr)))!=EOF) {
if (c=='\r') {
continue;
}
if (c=='\n') {
return str;
}
str.str() += c;
}
if (str.str().length()) {
return str;
}
return nil;
}
var builtin_stat(var* local, gc& ngc) {
var name = local[1];
if (name.type!=vm_str) {
return nas_err("stat", "\"filename\" must be string");
}
struct stat buf;
if (stat(name.str().c_str(),&buf)<0) {
return nas_err("stat", "failed to open file <"+name.str()+">");
}
var ret = ngc.alloc(vm_vec);
ret.vec().elems = {
var::num(static_cast<f64>(buf.st_dev)),
var::num(static_cast<f64>(buf.st_ino)),
var::num(static_cast<f64>(buf.st_mode)),
var::num(static_cast<f64>(buf.st_nlink)),
var::num(static_cast<f64>(buf.st_uid)),
var::num(static_cast<f64>(buf.st_gid)),
var::num(static_cast<f64>(buf.st_rdev)),
var::num(static_cast<f64>(buf.st_size)),
var::num(static_cast<f64>(buf.st_atime)),
var::num(static_cast<f64>(buf.st_mtime)),
var::num(static_cast<f64>(buf.st_ctime))
};
return ret;
}
var builtin_eof(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("readln", "not a valid filehandle");
}
return var::num(static_cast<f64>(feof(static_cast<FILE*>(fd.obj().ptr))));
}
nasal_builtin_table io_lib_native[] = {
{"__readfile", builtin_readfile},
{"__fout", builtin_fout},
{"__exists", builtin_exists},
{"__open", builtin_open},
{"__close", builtin_close},
{"__read", builtin_read},
{"__write", builtin_write},
{"__seek", builtin_seek},
{"__tell", builtin_tell},
{"__readln", builtin_readln},
{"__stat", builtin_stat},
{"__eof", builtin_eof},
{nullptr, nullptr}
};
}

32
src/io_lib.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include <sys/stat.h>
#ifdef _MSC_VER
#define F_OK 0 // fuck msc
#endif
namespace nasal {
void filehandle_destructor(void*);
var builtin_readfile(var*, gc&);
var builtin_fout(var*, gc&);
var builtin_exists(var*, gc&);
var builtin_open(var*, gc&);
var builtin_close(var*, gc&);
var builtin_read(var*, gc&);
var builtin_write(var*, gc&);
var builtin_seek(var*, gc&);
var builtin_tell(var*, gc&);
var builtin_readln(var*, gc&);
var builtin_stat(var*, gc&);
var builtin_eof(var*, gc&);
extern nasal_builtin_table io_lib_native[];
}

231
src/main.cpp Normal file
View File

@@ -0,0 +1,231 @@
#include "nasal.h"
#include "nasal_err.h"
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "ast_visitor.h"
#include "ast_dumper.h"
#include "symbol_finder.h"
#include "optimizer.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"
#include "repl.h"
#include <unordered_map>
#include <thread>
#include <cstdlib>
const u32 VM_RAW_AST = 1;
const u32 VM_AST = 1<<1;
const u32 VM_CODE = 1<<2;
const u32 VM_TIME = 1<<3;
const u32 VM_EXEC = 1<<4;
const u32 VM_DETAIL = 1<<5;
const u32 VM_DEBUG = 1<<6;
const u32 VM_SYMINFO = 1<<7;
const u32 VM_PROFILE = 1<<8;
const u32 VM_PROF_ALL = 1<<9;
std::ostream& help(std::ostream& out) {
out
<< "\n"
<< " ,--#-,\n"
<< "<3 / \\____\\ <3\n"
<< " |_|__A_|\n"
#ifdef _WIN32
<< "use command <chcp 65001> to use unicode.\n"
#endif
<< "\nnasal <option>\n"
<< "option:\n"
<< " -h, --help | get help.\n"
<< " -v, --version | get version.\n"
<< " -r, --repl | use repl interpreter(experimental).\n"
<< "\nnasal [option] <file> [argv]\n"
<< "option:\n"
<< " -a, --ast | view ast after link/optimize process.\n"
<< " --raw-ast | view ast without after-processing.\n"
<< " -c, --code | view generated bytecode.\n"
<< " -s, --symbol | show analysed symbol info.\n"
<< " -e, --exec | execute directly.\n"
<< " -t, --time | show execute time.\n"
<< " -d, --detail | get detail info.\n"
<< " -dbg, --debug | debug mode.\n"
<< " --prof | show profiling result, available in debug mode.\n"
<< " --prof-all | show profiling result of all files,"
<< "available under debug mode.\n"
<< "file:\n"
<< " <filename> | execute file.\n"
<< "argv:\n"
<< " <args> | cmd arguments used in program.\n"
<< "\n";
return out;
}
std::ostream& logo(std::ostream& out) {
out
<< "\n"
<< " __ _\n"
<< " /\\ \\ \\__ _ ___ __ _| |\n"
<< " / \\/ / _` / __|/ _` | |\n"
<< " / /\\ / (_| \\__ \\ (_| | |\n"
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\n"
<< "std : c++ " << __cplusplus << "\n"
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
<< "\n"
<< "input <nasal -h> to get help .\n\n";
return out;
}
std::ostream& version(std::ostream& out) {
std::srand(std::time(nullptr));
f64 num = 0;
for(u32 i = 0; i<5; ++i) {
num = (num+rand())*(1.0/(RAND_MAX+1.0));
}
if (num<0.01) {
nasal::parse::easter_egg();
}
out << "nasal interpreter version " << __nasver;
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
return out;
}
[[noreturn]]
void err() {
std::cerr
<< "invalid argument(s).\n"
<< "use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(
const std::string& file,
const std::vector<std::string>& argv,
const u32 cmd) {
using clk = std::chrono::high_resolution_clock;
const auto den = clk::duration::period::den;
nasal::lexer lex;
nasal::parse parse;
nasal::linker ld;
nasal::codegen gen;
// lexer scans file to get tokens
lex.scan(file).chkerr();
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
if (cmd&VM_RAW_AST) {
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
dumper->dump(parse.tree());
}
// linker gets parser's ast and load import files to this ast
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
// optimizer does simple optimization on ast
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
opt->do_optimization(parse.tree());
if (cmd&VM_AST) {
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
dumper->dump(parse.tree());
}
// code generator gets parser's ast and import file list to generate code
gen.compile(parse, ld, false).chkerr();
if (cmd&VM_CODE) {
gen.print(std::cout);
}
if (cmd&VM_SYMINFO) {
gen.symbol_dump(std::cout);
}
// run
auto start = clk::now();
if (cmd&VM_DEBUG) {
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg);
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
runtime->set_detail_report_info(cmd&VM_DETAIL);
runtime->run(gen, ld, argv);
}
// get running time
auto end = clk::now();
if (cmd&VM_TIME) {
std::clog << "process exited after ";
std::clog << (end-start).count()*1.0/den << "s.\n\n";
}
}
i32 main(i32 argc, const char* argv[]) {
// output version info
if (argc<=1) {
std::clog << logo;
return 0;
}
// run directly or show help
if (argc==2) {
std::string s(argv[1]);
if (s=="-h" || s=="--help") {
std::clog << help;
} else if (s=="-v" || s=="--version") {
std::clog << version;
} else if (s=="-r" || s=="--repl") {
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl);
repl->execute();
} else if (s[0]!='-') {
execute(s, {}, VM_EXEC);
} else {
err();
}
return 0;
}
// execute with arguments
const std::unordered_map<std::string, u32> cmdlst = {
{"--raw-ast", VM_RAW_AST},
{"--ast", VM_AST},
{"-a", VM_AST},
{"--code", VM_CODE},
{"-c", VM_CODE},
{"--symbol", VM_SYMINFO},
{"-s", VM_SYMINFO},
{"--exec", VM_EXEC},
{"-e", VM_EXEC},
{"--time", VM_TIME|VM_EXEC},
{"-t", VM_TIME|VM_EXEC},
{"--detail", VM_DETAIL|VM_EXEC},
{"-d", VM_DETAIL|VM_EXEC},
{"--debug", VM_DEBUG},
{"-dbg", VM_DEBUG},
{"--prof", VM_PROFILE},
{"--prof-all", VM_PROF_ALL}
};
u32 cmd = 0;
std::string filename = "";
std::vector<std::string> vm_argv;
for(i32 i = 1; i<argc; ++i) {
if (cmdlst.count(argv[i])) {
cmd |= cmdlst.at(argv[i]);
} else if (!filename.length()) {
filename = argv[i];
} else {
vm_argv.push_back(argv[i]);
}
}
if (!filename.length()) {
err();
}
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
return 0;
}

77
src/math_lib.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "math_lib.h"
namespace nasal {
var builtin_pow(var* local, gc& ngc) {
var x = local[1];
var y = local[2];
if (x.type!=vm_num || y.type!=vm_num) {
return var::num(std::nan(""));
}
return var::num(std::pow(x.num(), y.num()));
}
var builtin_sin(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
}
var builtin_cos(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
}
var builtin_tan(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
}
var builtin_exp(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
}
var builtin_lg(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
}
var builtin_ln(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
}
var builtin_sqrt(var* local, gc& ngc) {
var val = local[1];
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
}
var builtin_atan2(var* local, gc& ngc) {
var x = local[1];
var y = local[2];
if (x.type!=vm_num || y.type!=vm_num) {
return var::num(std::nan(""));
}
return var::num(atan2(y.num(), x.num()));
}
var builtin_isnan(var* local, gc& ngc) {
var x = local[1];
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
}
nasal_builtin_table math_lib_native[] = {
{"__pow", builtin_pow},
{"__sin", builtin_sin},
{"__cos", builtin_cos},
{"__tan", builtin_tan},
{"__exp", builtin_exp},
{"__lg", builtin_lg},
{"__ln", builtin_ln},
{"__sqrt", builtin_sqrt},
{"__atan2", builtin_atan2},
{"__isnan", builtin_isnan},
{nullptr, nullptr}
};
}

22
src/math_lib.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
namespace nasal {
var builtin_pow(var*, gc&);
var builtin_sin(var*, gc&);
var builtin_cos(var*, gc&);
var builtin_tan(var*, gc&);
var builtin_exp(var*, gc&);
var builtin_lg(var*, gc&);
var builtin_ln(var*, gc&);
var builtin_sqrt(var*, gc&);
var builtin_atan2(var*, gc&);
var builtin_isnan(var*, gc&);
extern nasal_builtin_table math_lib_native[];
}

61
src/nasal.h Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#ifndef __nasver
#define __nasver "11.0"
#endif
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cmath>
#include <vector>
// abbreviation of some useful basic type
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;
namespace nasal {
bool is_windows();
bool is_linux();
bool is_macos();
bool is_x86();
bool is_amd64();
bool is_x86_64();
bool is_arm();
bool is_aarch64();
bool is_ia64();
bool is_powerpc();
bool is_superh();
// virtual machine stack depth
// both global depth and value stack depth
const u32 STACK_DEPTH = 4096;
f64 hex2f(const char*);
f64 oct2f(const char*);
// we have the same reason not using atof here
// just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char*);
f64 str2num(const char*);
i32 utf8_hdchk(const char);
std::string chrhex(const char);
std::string rawstr(const std::string&, const usize maxlen=0);
}
#include "nasal_gc.h"

372
src/nasal_ast.cpp Normal file
View File

@@ -0,0 +1,372 @@
#include "nasal_ast.h"
#include "ast_visitor.h"
namespace nasal {
void expr::accept(ast_visitor* visitor) {
visitor->visit_expr(this);
}
void call::accept(ast_visitor* visitor) {
visitor->visit_call(this);
}
void null_expr::accept(ast_visitor* visitor) {
visitor->visit_null_expr(this);
}
void nil_expr::accept(ast_visitor* visitor) {
visitor->visit_nil_expr(this);
}
void number_literal::accept(ast_visitor* visitor) {
visitor->visit_number_literal(this);
}
void string_literal::accept(ast_visitor* visitor) {
visitor->visit_string_literal(this);
}
void identifier::accept(ast_visitor* visitor) {
visitor->visit_identifier(this);
}
void bool_literal::accept(ast_visitor* visitor) {
visitor->visit_bool_literal(this);
}
vector_expr::~vector_expr() {
for(auto i : elements) {
delete i;
}
}
void vector_expr::accept(ast_visitor* visitor) {
visitor->visit_vector_expr(this);
}
hash_expr::~hash_expr() {
for(auto i : members) {
delete i;
}
}
void hash_expr::accept(ast_visitor* visitor) {
visitor->visit_hash_expr(this);
}
hash_pair::~hash_pair() {
if (value) {
delete value;
}
}
void hash_pair::accept(ast_visitor* visitor) {
visitor->visit_hash_pair(this);
}
function::~function() {
for(auto i : parameter_list) {
delete i;
}
if (block) {
delete block;
}
}
void function::accept(ast_visitor* visitor) {
visitor->visit_function(this);
}
code_block::~code_block() {
for(auto i : expressions) {
delete i;
}
}
void code_block::accept(ast_visitor* visitor) {
visitor->visit_code_block(this);
}
parameter::~parameter() {
if (default_value) {
delete default_value;
}
}
void parameter::accept(ast_visitor* visitor) {
visitor->visit_parameter(this);
}
ternary_operator::~ternary_operator() {
if (condition) {
delete condition;
}
if (left) {
delete left;
}
if (right) {
delete right;
}
}
void ternary_operator::accept(ast_visitor* visitor) {
visitor->visit_ternary_operator(this);
}
binary_operator::~binary_operator() {
if (left) {
delete left;
}
if (right) {
delete right;
}
if (optimized_const_number) {
delete optimized_const_number;
}
if (optimized_const_string) {
delete optimized_const_string;
}
}
void binary_operator::accept(ast_visitor* visitor) {
visitor->visit_binary_operator(this);
}
unary_operator::~unary_operator() {
if (value) {
delete value;
}
if (optimized_number) {
delete optimized_number;
}
}
void unary_operator::accept(ast_visitor* visitor) {
visitor->visit_unary_operator(this);
}
call_expr::~call_expr() {
if(first) {
delete first;
}
for(auto i : calls) {
delete i;
}
}
void call_expr::accept(ast_visitor* visitor) {
visitor->visit_call_expr(this);
}
void call_hash::accept(ast_visitor* visitor) {
visitor->visit_call_hash(this);
}
call_vector::~call_vector() {
for(auto i : calls) {
delete i;
}
}
void call_vector::accept(ast_visitor* visitor) {
visitor->visit_call_vector(this);
}
call_function::~call_function() {
for(auto i : args) {
delete i;
}
}
void call_function::accept(ast_visitor* visitor) {
visitor->visit_call_function(this);
}
slice_vector::~slice_vector() {
if (begin) {
delete begin;
}
if (end) {
delete end;
}
}
void slice_vector::accept(ast_visitor* visitor) {
visitor->visit_slice_vector(this);
}
definition_expr::~definition_expr() {
if (variable_name) {
delete variable_name;
}
if (variables) {
delete variables;
}
if (tuple) {
delete tuple;
}
if (value) {
delete value;
}
}
void definition_expr::accept(ast_visitor* visitor) {
visitor->visit_definition_expr(this);
}
assignment_expr::~assignment_expr() {
if (left) {
delete left;
}
if (right) {
delete right;
}
}
void assignment_expr::accept(ast_visitor* visitor) {
visitor->visit_assignment_expr(this);
}
multi_identifier::~multi_identifier() {
for(auto i : variables) {
delete i;
}
}
void multi_identifier::accept(ast_visitor* visitor) {
visitor->visit_multi_identifier(this);
}
tuple_expr::~tuple_expr() {
for(auto i : elements) {
delete i;
}
}
void tuple_expr::accept(ast_visitor* visitor) {
visitor->visit_tuple_expr(this);
}
multi_assign::~multi_assign() {
if (tuple) {
delete tuple;
}
if (value) {
delete value;
}
}
void multi_assign::accept(ast_visitor* visitor) {
visitor->visit_multi_assign(this);
}
while_expr::~while_expr() {
if (condition) {
delete condition;
}
if (block) {
delete block;
}
}
void while_expr::accept(ast_visitor* visitor) {
visitor->visit_while_expr(this);
}
for_expr::~for_expr() {
if (initializing) {
delete initializing;
}
if (condition) {
delete condition;
}
if (step) {
delete step;
}
if (block) {
delete block;
}
}
void for_expr::accept(ast_visitor* visitor) {
visitor->visit_for_expr(this);
}
iter_expr::~iter_expr() {
if (name) {
delete name;
}
if (call) {
delete call;
}
}
void iter_expr::accept(ast_visitor* visitor) {
visitor->visit_iter_expr(this);
}
forei_expr::~forei_expr() {
if (iterator) {
delete iterator;
}
if (vector_node) {
delete vector_node;
}
if (block) {
delete block;
}
}
void forei_expr::accept(ast_visitor* visitor) {
visitor->visit_forei_expr(this);
}
condition_expr::~condition_expr() {
if (if_stmt) {
delete if_stmt;
}
for(auto i : elsif_stmt) {
delete i;
}
if (else_stmt) {
delete else_stmt;
}
}
void condition_expr::accept(ast_visitor* visitor) {
visitor->visit_condition_expr(this);
}
if_expr::~if_expr() {
if (condition) {
delete condition;
}
if (block) {
delete block;
}
}
void if_expr::accept(ast_visitor* visitor) {
visitor->visit_if_expr(this);
}
void continue_expr::accept(ast_visitor* visitor) {
visitor->visit_continue_expr(this);
}
void break_expr::accept(ast_visitor* visitor) {
visitor->visit_break_expr(this);
}
return_expr::~return_expr() {
if (value) {
delete value;
}
}
void return_expr::accept(ast_visitor* visitor) {
visitor->visit_return_expr(this);
}
}

675
src/nasal_ast.h Normal file
View File

@@ -0,0 +1,675 @@
#pragma once
#include "nasal.h"
#include "nasal_err.h"
#include <vector>
#include <unordered_map>
namespace nasal {
enum class expr_type:u32 {
ast_null = 0, // null node
ast_block, // code block
ast_nil, // nil keyword
ast_num, // number, basic value type
ast_str, // string, basic value type
ast_id, // identifier
ast_bool, // bools
ast_func, // func keyword
ast_hash, // hash, basic value type
ast_vec, // vector, basic value type
ast_pair, // pair of key and value in hashmap
ast_call, // mark a sub-tree of calling an identifier
ast_callh, // id.name
ast_callv, // id[index]
ast_callf, // id()
ast_subvec, // id[index:index]
ast_param, // function parameter
ast_ternary, // ternary operator
ast_binary, // binary operator
ast_unary, // unary operator
ast_for, // for keyword
ast_forei, // foreach or forindex loop
ast_while, // while
ast_iter, // iterator, used in forindex/foreach
ast_cond, // mark a sub-tree of conditional expression
ast_if, // if keyword
ast_multi_id, // multi identifiers sub-tree
ast_tuple, // tuple, stores multiple scalars
ast_def, // definition
ast_assign, // assignment
ast_multi_assign,// multiple assignment
ast_continue, // continue keyword, only used in loop
ast_break, // break keyword, only used in loop
ast_ret // return keyword, only used in function block
};
class ast_visitor;
class hash_pair;
class parameter;
class slice_vector;
class multi_identifier;
class code_block;
class if_expr;
class tuple_expr;
class expr {
protected:
span nd_loc;
expr_type nd_type;
public:
expr(const span& location, expr_type node_type):
nd_loc(location), nd_type(node_type) {}
virtual ~expr() = default;
void set_begin(u32 line, u32 column) {
nd_loc.begin_line = line;
nd_loc.begin_column = column;
}
const span& get_location() const {return nd_loc;}
const u32 get_line() const {return nd_loc.begin_line;}
expr_type get_type() const {return nd_type;}
void update_location(const span& location) {
nd_loc.end_line = location.end_line;
nd_loc.end_column = location.end_column;
}
virtual void accept(ast_visitor*);
};
class call:public expr {
public:
call(const span& location, expr_type node_type):
expr(location, node_type) {}
virtual ~call() = default;
virtual void accept(ast_visitor*);
};
class null_expr:public expr {
public:
null_expr(const span& location):
expr(location, expr_type::ast_null) {}
~null_expr() override = default;
void accept(ast_visitor*) override;
};
class nil_expr:public expr {
public:
nil_expr(const span& location):
expr(location, expr_type::ast_nil) {}
~nil_expr() override = default;
void accept(ast_visitor*) override;
};
class number_literal:public expr {
private:
f64 number;
public:
number_literal(const span& location, const f64 num):
expr(location, expr_type::ast_num), number(num) {}
~number_literal() override = default;
f64 get_number() const {return number;}
void accept(ast_visitor*) override;
};
class string_literal:public expr {
private:
std::string content;
public:
string_literal(const span& location, const std::string& str):
expr(location, expr_type::ast_str), content(str) {}
~string_literal() override = default;
const std::string get_content() const {return content;}
void accept(ast_visitor*) override;
};
class identifier:public expr {
private:
std::string name;
public:
identifier(const span& location, const std::string& str):
expr(location, expr_type::ast_id), name(str) {}
~identifier() override = default;
const std::string& get_name() const {return name;}
void accept(ast_visitor*) override;
};
class bool_literal:public expr {
private:
bool flag;
public:
bool_literal(const span& location, const bool bool_flag):
expr(location, expr_type::ast_bool), flag(bool_flag) {}
~bool_literal() override = default;
bool get_flag() const {return flag;}
void accept(ast_visitor*) override;
};
class vector_expr:public expr {
private:
std::vector<expr*> elements;
public:
vector_expr(const span& location):
expr(location, expr_type::ast_vec) {}
~vector_expr() override;
void add_element(expr* node) {elements.push_back(node);}
std::vector<expr*>& get_elements() {return elements;}
void accept(ast_visitor*) override;
};
class hash_expr:public expr {
private:
std::vector<hash_pair*> members;
public:
hash_expr(const span& location):
expr(location, expr_type::ast_hash) {}
~hash_expr() override;
void add_member(hash_pair* node) {members.push_back(node);}
std::vector<hash_pair*>& get_members() {return members;}
void accept(ast_visitor*) override;
};
class hash_pair:public expr {
private:
std::string name;
expr* value;
public:
hash_pair(const span& location):
expr(location, expr_type::ast_pair),
value(nullptr) {}
~hash_pair() override;
void set_name(const std::string& field_name) {name = field_name;}
void set_value(expr* node) {value = node;}
const std::string& get_name() const {return name;}
expr* get_value() {return value;}
void accept(ast_visitor*) override;
};
class function:public expr {
private:
std::vector<parameter*> parameter_list;
code_block* block;
public:
function(const span& location):
expr(location, expr_type::ast_func),
block(nullptr) {}
~function() override;
void add_parameter(parameter* node) {parameter_list.push_back(node);}
void set_code_block(code_block* node) {block = node;}
std::vector<parameter*>& get_parameter_list() {return parameter_list;}
code_block* get_code_block() {return block;}
void accept(ast_visitor*) override;
};
class code_block:public expr {
private:
std::vector<expr*> expressions;
public:
code_block(const span& location):
expr(location, expr_type::ast_block) {}
~code_block() override;
void add_expression(expr* node) {expressions.push_back(node);}
std::vector<expr*>& get_expressions() {return expressions;}
void accept(ast_visitor*) override;
};
class parameter:public expr {
public:
enum class param_type {
normal_parameter,
default_parameter,
dynamic_parameter
};
private:
param_type type;
std::string name;
expr* default_value;
public:
parameter(const span& location):
expr(location, expr_type::ast_param),
name(""), default_value(nullptr) {}
~parameter() override;
void set_parameter_type(param_type pt) {type = pt;}
void set_parameter_name(const std::string& pname) {name = pname;}
void set_default_value(expr* node) {default_value = node;}
param_type get_parameter_type() {return type;}
const std::string& get_parameter_name() const {return name;}
expr* get_default_value() {return default_value;}
void accept(ast_visitor*) override;
};
class ternary_operator:public expr {
private:
expr* condition;
expr* left;
expr* right;
public:
ternary_operator(const span& location):
expr(location, expr_type::ast_ternary),
condition(nullptr), left(nullptr), right(nullptr) {}
~ternary_operator() override;
void set_condition(expr* node) {condition = node;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
expr* get_condition() {return condition;}
expr* get_left() {return left;}
expr* get_right() {return right;}
void accept(ast_visitor*) override;
};
class binary_operator:public expr {
public:
enum class binary_type {
add,
sub,
mult,
div,
concat,
cmpeq,
cmpneq,
less,
leq,
grt,
geq,
bitwise_or,
bitwise_xor,
bitwise_and,
condition_and,
condition_or
};
private:
binary_type type;
expr* left;
expr* right;
number_literal* optimized_const_number;
string_literal* optimized_const_string;
public:
binary_operator(const span& location):
expr(location, expr_type::ast_binary),
left(nullptr), right(nullptr),
optimized_const_number(nullptr),
optimized_const_string(nullptr) {}
~binary_operator() override;
void set_operator_type(binary_type operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
binary_type get_operator_type() const {return type;}
expr* get_left() {return left;}
expr* get_right() {return right;}
number_literal* get_optimized_number() {return optimized_const_number;}
string_literal* get_optimized_string() {return optimized_const_string;}
void accept(ast_visitor*) override;
};
class unary_operator:public expr {
public:
enum class unary_type {
negative,
logical_not,
bitwise_not
};
private:
unary_type type;
expr* value;
number_literal* optimized_number;
public:
unary_operator(const span& location):
expr(location, expr_type::ast_unary),
value(nullptr), optimized_number(nullptr) {}
~unary_operator() override;
void set_operator_type(unary_type operator_type) {type = operator_type;}
void set_value(expr* node) {value = node;}
void set_optimized_number(number_literal* node) {optimized_number = node;}
unary_type get_operator_type() const {return type;}
expr* get_value() {return value;}
number_literal* get_optimized_number() {return optimized_number;}
void accept(ast_visitor*) override;
};
class call_expr:public expr {
private:
expr* first;
std::vector<call*> calls;
public:
call_expr(const span& location):
expr(location, expr_type::ast_call),
first(nullptr) {}
~call_expr() override;
void set_first(expr* node) {first = node;}
void add_call(call* node) {calls.push_back(node);}
expr* get_first() {return first;}
auto& get_calls() {return calls;}
void accept(ast_visitor*) override;
};
class call_hash:public call {
private:
std::string field;
public:
call_hash(const span& location, const std::string& name):
call(location, expr_type::ast_callh),
field(name) {}
~call_hash() override = default;
const std::string& get_field() const {return field;}
void accept(ast_visitor*) override;
};
class call_vector:public call {
private:
std::vector<slice_vector*> calls;
public:
call_vector(const span& location):
call(location, expr_type::ast_callv) {}
~call_vector() override;
void add_slice(slice_vector* node) {calls.push_back(node);}
std::vector<slice_vector*>& get_slices() {return calls;}
void accept(ast_visitor*) override;
};
class call_function:public call {
private:
std::vector<expr*> args;
public:
call_function(const span& location):
call(location, expr_type::ast_callf) {}
~call_function() override;
void add_argument(expr* node) {args.push_back(node);}
std::vector<expr*>& get_argument() {return args;}
void accept(ast_visitor*) override;
};
class slice_vector:public expr {
private:
expr* begin;
expr* end;
public:
slice_vector(const span& location):
expr(location, expr_type::ast_subvec),
begin(nullptr), end(nullptr) {}
~slice_vector() override;
void set_begin(expr* node) {begin = node;}
void set_end(expr* node) {end = node;}
expr* get_begin() {return begin;}
expr* get_end() {return end;}
void accept(ast_visitor*) override;
};
class definition_expr:public expr {
private:
identifier* variable_name;
multi_identifier* variables;
tuple_expr* tuple;
expr* value;
public:
definition_expr(const span& location):
expr(location, expr_type::ast_def),
variable_name(nullptr), variables(nullptr),
tuple(nullptr), value(nullptr) {}
~definition_expr() override;
void set_identifier(identifier* node) {variable_name = node;}
void set_multi_define(multi_identifier* node) {variables = node;}
void set_tuple(tuple_expr* node) {tuple = node;}
void set_value(expr* node) {value = node;}
identifier* get_variable_name() {return variable_name;}
multi_identifier* get_variables() {return variables;}
tuple_expr* get_tuple() {return tuple;}
expr* get_value() {return value;}
void accept(ast_visitor*) override;
};
class assignment_expr:public expr {
public:
enum class assign_type {
equal,
add_equal,
sub_equal,
mult_equal,
div_equal,
concat_equal,
bitwise_and_equal,
bitwise_or_equal,
bitwise_xor_equal
};
private:
assign_type type;
expr* left;
expr* right;
public:
assignment_expr(const span& location):
expr(location, expr_type::ast_assign),
left(nullptr), right(nullptr) {}
~assignment_expr() override;
void set_assignment_type(assign_type operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
assign_type get_assignment_type() const {return type;}
expr* get_left() {return left;}
expr* get_right() {return right;}
void accept(ast_visitor*) override;
};
class multi_identifier:public expr {
private:
std::vector<identifier*> variables;
public:
multi_identifier(const span& location):
expr(location, expr_type::ast_multi_id) {}
~multi_identifier() override;
void add_var(identifier* node) {variables.push_back(node);}
std::vector<identifier*>& get_variables() {return variables;}
void accept(ast_visitor*) override;
};
class tuple_expr:public expr {
private:
std::vector<expr*> elements;
public:
tuple_expr(const span& location):
expr(location, expr_type::ast_tuple) {}
~tuple_expr() override;
void add_element(expr* node) {elements.push_back(node);}
std::vector<expr*>& get_elements() {return elements;}
void accept(ast_visitor*) override;
};
class multi_assign:public expr {
private:
tuple_expr* tuple;
expr* value;
public:
multi_assign(const span& location):
expr(location, expr_type::ast_multi_assign),
tuple(nullptr), value(nullptr) {}
~multi_assign() override;
void set_tuple(tuple_expr* node) {tuple = node;}
void set_value(expr* node) {value = node;}
tuple_expr* get_tuple() {return tuple;}
expr* get_value() {return value;}
void accept(ast_visitor*) override;
};
class while_expr:public expr {
private:
expr* condition;
code_block* block;
public:
while_expr(const span& location):
expr(location, expr_type::ast_while),
condition(nullptr), block(nullptr) {}
~while_expr() override;
void set_condition(expr* node) {condition = node;}
void set_code_block (code_block* node) {block = node;}
expr* get_condition() {return condition;}
code_block* get_code_block() {return block;}
void accept(ast_visitor*) override;
};
class for_expr:public expr {
private:
expr* initializing;
expr* condition;
expr* step;
code_block* block;
public:
for_expr(const span& location):
expr(location, expr_type::ast_for),
initializing(nullptr), condition(nullptr),
step(nullptr), block(nullptr) {}
~for_expr() override;
void set_initial(expr* node) {initializing = node;}
void set_condition(expr* node) {condition = node;}
void set_step(expr* node) {step = node;}
void set_code_block(code_block* node) {block = node;}
expr* get_initial() {return initializing;}
expr* get_condition() {return condition;}
expr* get_step() {return step;}
code_block* get_code_block() {return block;}
void accept(ast_visitor*) override;
};
class iter_expr:public expr {
private:
identifier* name;
call_expr* call;
public:
iter_expr(const span& location):
expr(location, expr_type::ast_iter),
name(nullptr), call(nullptr) {}
~iter_expr() override;
void set_name(identifier* node) {name = node;}
void set_call(call_expr* node) {call = node;}
identifier* get_name() {return name;}
call_expr* get_call() {return call;}
void accept(ast_visitor*) override;
};
class forei_expr:public expr {
public:
enum class forei_loop_type {
foreach,
forindex
};
private:
forei_loop_type type;
iter_expr* iterator;
expr* vector_node;
code_block* block;
public:
forei_expr(const span& location):
expr(location, expr_type::ast_forei),
type(forei_loop_type::foreach), iterator(nullptr),
vector_node(nullptr), block(nullptr) {}
~forei_expr() override;
void set_loop_type(forei_loop_type ft) {type = ft;}
void set_iterator(iter_expr* node) {iterator = node;}
void set_value(expr* node) {vector_node = node;}
void set_code_block(code_block* node) {block = node;}
forei_loop_type get_loop_type() const {return type;}
iter_expr* get_iterator() {return iterator;}
expr* get_value() {return vector_node;}
code_block* get_code_block() {return block;}
void accept(ast_visitor*) override;
};
class condition_expr:public expr {
private:
if_expr* if_stmt;
std::vector<if_expr*> elsif_stmt;
if_expr* else_stmt;
public:
condition_expr(const span& location):
expr(location, expr_type::ast_cond),
if_stmt(nullptr), else_stmt(nullptr) {}
~condition_expr() override;
void set_if_statement(if_expr* node) {if_stmt = node;}
void add_elsif_statement(if_expr* node) {elsif_stmt.push_back(node);}
void set_else_statement(if_expr* node) {else_stmt = node;}
if_expr* get_if_statement() {return if_stmt;}
std::vector<if_expr*>& get_elsif_stataments() {return elsif_stmt;}
if_expr* get_else_statement() {return else_stmt;}
void accept(ast_visitor*) override;
};
class if_expr:public expr {
private:
expr* condition;
code_block* block;
public:
if_expr(const span& location):
expr(location, expr_type::ast_if),
condition(nullptr), block(nullptr) {}
~if_expr() override;
void set_condition(expr* node) {condition = node;}
void set_code_block(code_block* node) {block = node;}
expr* get_condition() {return condition;}
code_block* get_code_block() {return block;}
void accept(ast_visitor*) override;
};
class continue_expr:public expr {
public:
continue_expr(const span& location):
expr(location, expr_type::ast_continue) {}
~continue_expr() override = default;
void accept(ast_visitor*) override;
};
class break_expr:public expr {
public:
break_expr(const span& location):
expr(location, expr_type::ast_break) {}
~break_expr() = default;
void accept(ast_visitor*) override;
};
class return_expr:public expr {
private:
expr* value;
public:
return_expr(const span& location):
expr(location, expr_type::ast_ret),
value(nullptr) {}
~return_expr() override;
void set_value(expr* node) {value = node;}
expr* get_value() {return value;}
void accept(ast_visitor*) override;
};
}

669
src/nasal_builtin.cpp Normal file
View File

@@ -0,0 +1,669 @@
#include "nasal_builtin.h"
#include <chrono>
namespace nasal {
var builtin_print(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
return nil;
}
var builtin_println(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
std::cout << i;
}
std::cout << std::endl;
return nil;
}
var builtin_exit(var* local, gc& ngc) {
std::exit(local[1].num());
return nil;
}
var builtin_abort(var* local, gc& ngc) {
std::abort();
return nil;
}
var builtin_append(var* local, gc& ngc) {
var vec = local[1];
var elem = local[2];
if (vec.type!=vm_vec) {
return nas_err("append", "\"vec\" must be vector");
}
auto& v = vec.vec().elems;
for(auto& i : elem.vec().elems) {
v.push_back(i);
}
return nil;
}
var builtin_setsize(var* local, gc& ngc) {
var vec = local[1];
var size = local[2];
if (vec.type!=vm_vec) {
return nas_err("setsize", "\"vec\" must be vector");
}
if (size.type!=vm_num || size.num()<0) {
return nil;
}
vec.vec().elems.resize(static_cast<i64>(size.num()), nil);
return nil;
}
var builtin_system(var* local, gc& ngc) {
var str = local[1];
if (str.type!=vm_str) {
return var::num(-1);
}
return var::num(static_cast<f64>(system(str.str().c_str())));
}
var builtin_input(var* local, gc& ngc) {
var end = local[1];
var ret = ngc.alloc(vm_str);
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
std::cin >> ret.str();
} else {
std::getline(std::cin, ret.str(), end.str()[0]);
}
return ret;
}
var builtin_split(var* local, gc& ngc) {
var delimeter = local[1];
var str = local[2];
if (delimeter.type!=vm_str) {
return nas_err("split", "\"separator\" must be string");
}
if (str.type!=vm_str) {
return nas_err("split", "\"str\" must be string");
}
const auto& deli = delimeter.str();
const auto& s = str.str();
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto& vec = res.vec().elems;
if (!deli.length()) {
for(auto i : s) {
vec.push_back(ngc.newstr(i));
}
ngc.temp = nil;
return res;
}
usize last = 0;
usize pos = s.find(deli, 0);
while(pos!=std::string::npos) {
if (pos>last) {
vec.push_back(ngc.newstr(s.substr(last, pos-last)));
}
last = pos+deli.length();
pos = s.find(deli, last);
}
if (last!=s.length()) {
vec.push_back(ngc.newstr(s.substr(last)));
}
ngc.temp = nil;
return res;
}
var builtin_rand(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num && val.type!=vm_nil) {
return nas_err("rand", "\"seed\" must be nil or number");
}
if (val.type==vm_num) {
srand(static_cast<u32>(val.num()));
return nil;
}
f64 num = 0;
for(u32 i = 0; i<5; ++i) {
num = (num+rand())*(1.0/(RAND_MAX+1.0));
}
return var::num(num);
}
var builtin_id(var* local, gc& ngc) {
var val = local[1];
std::stringstream ss;
ss << "0";
if (val.type>vm_num) {
ss << "x" << std::hex;
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
}
return ngc.newstr(ss.str());
}
var builtin_int(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num && val.type!=vm_str) {
return nil;
}
return var::num(static_cast<f64>(static_cast<i32>(val.tonum())));
}
var builtin_floor(var* local, gc& ngc) {
var val = local[1];
return var::num(std::floor(val.num()));
}
var builtin_num(var* local, gc& ngc) {
var val = local[1];
if (val.type==vm_num) {
return val;
}
if (val.type!=vm_str) {
return nil;
}
f64 res = val.tonum();
if (std::isnan(res)) {
return nil;
}
return var::num(res);
}
var builtin_pop(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_vec) {
return nas_err("pop", "\"vec\" must be vector");
}
auto& vec = val.vec().elems;
if (vec.size()) {
var tmp = vec.back();
vec.pop_back();
return tmp;
}
return nil;
}
var builtin_str(var* local, gc& ngc) {
return ngc.newstr(local[1].tostr());
}
var builtin_size(var* local, gc& ngc) {
var val = local[1];
f64 num = 0;
switch(val.type) {
case vm_num: num = val.num(); break;
case vm_str: num = val.str().length(); break;
case vm_vec: num = val.vec().size(); break;
case vm_hash: num = val.hash().size(); break;
case vm_map: num = val.map().mapper.size(); break;
}
return var::num(num);
}
var builtin_time(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num) {
return nas_err("time", "\"begin\" must be number");
}
time_t begin = (time_t)val.num();
return var::num(static_cast<f64>(time(&begin)));
}
var builtin_contains(var* local, gc& ngc) {
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash || key.type!=vm_str) {
return zero;
}
return hash.hash().elems.count(key.str())? one:zero;
}
var builtin_delete(var* local, gc& ngc) {
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash) {
return nas_err("delete", "\"hash\" must be hash");
}
if (key.type!=vm_str) {
return nil;
}
if (hash.hash().elems.count(key.str())) {
hash.hash().elems.erase(key.str());
}
return nil;
}
var builtin_keys(var* local, gc& ngc) {
var hash = local[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc.newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc.newstr(iter.first));
}
}
ngc.temp=nil;
return res;
}
var builtin_die(var* local, gc& ngc) {
return nas_err("error", local[1].tostr());
}
var builtin_find(var* local, gc& ngc) {
var needle = local[1];
var haystack = local[2];
usize pos = haystack.tostr().find(needle.tostr());
if (pos==std::string::npos) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(pos));
}
var builtin_type(var* local, gc& ngc) {
switch(local[1].type) {
case vm_none: return ngc.newstr("undefined");
case vm_nil: return ngc.newstr("nil");
case vm_num: return ngc.newstr("num");
case vm_str: return ngc.newstr("str");
case vm_vec: return ngc.newstr("vec");
case vm_hash: return ngc.newstr("hash");
case vm_func: return ngc.newstr("func");
case vm_obj: return ngc.newstr("obj");
case vm_co: return ngc.newstr("coroutine");
case vm_map: return ngc.newstr("namespace");
}
return nil;
}
var builtin_substr(var* local, gc& ngc) {
var str = local[1];
var beg = local[2];
var len = local[3];
if (str.type!=vm_str) {
return nas_err("substr", "\"str\" must be string");
}
if (beg.type!=vm_num || beg.num()<0) {
return nas_err("substr", "\"begin\" should be number >= 0");
}
if (len.type!=vm_num || len.num()<0) {
return nas_err("substr", "\"length\" should be number >= 0");
}
usize begin = (usize)beg.num();
usize length = (usize)len.num();
if (begin>=str.str().length()) {
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
}
return ngc.newstr(str.str().substr(begin,length));
}
var builtin_streq(var* local, gc& ngc) {
var a = local[1];
var b = local[2];
return var::num(static_cast<f64>(
(a.type!=vm_str || b.type!=vm_str)? 0:(a.str()==b.str())
));
}
var builtin_left(var* local, gc& ngc) {
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
return nas_err("left", "\"string\" must be string");
}
if (len.type!=vm_num) {
return nas_err("left", "\"length\" must be number");
}
if (len.num()<0) {
return ngc.newstr("");
}
return ngc.newstr(str.str().substr(0, len.num()));
}
var builtin_right(var* local, gc& ngc) {
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
return nas_err("right", "\"string\" must be string");
}
if (len.type!=vm_num) {
return nas_err("right", "\"length\" must be number");
}
i32 length = static_cast<i32>(len.num());
i32 srclen = str.str().length();
if (length>srclen) {
length = srclen;
}
if (length<0) {
length = 0;
}
return ngc.newstr(str.str().substr(srclen-length, srclen));
}
var builtin_cmp(var* local, gc& ngc) {
var a = local[1];
var b = local[2];
if (a.type!=vm_str || b.type!=vm_str) {
return nas_err("cmp", "\"a\" and \"b\" must be string");
}
return var::num(static_cast<f64>(strcmp(
a.str().c_str(),
b.str().c_str()
)));
}
var builtin_chr(var* local, gc& ngc) {
const char* extend[] = {
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
i32 num = local[1].num();
if (0<=num && num<128) {
return ngc.newstr((char)num);
} else if (128<=num && num<256) {
return ngc.newstr(extend[num-128]);
}
return ngc.newstr(" ");
}
var builtin_char(var* local, gc& ngc) {
return ngc.newstr((unsigned char)local[1].num());
}
var builtin_values(var* local, gc& ngc) {
var hash = local[1];
if (hash.type!=vm_hash) {
return nas_err("values", "\"hash\" must be hash");
}
var vec = ngc.alloc(vm_vec);
auto& v = vec.vec().elems;
for(auto& i : hash.hash().elems) {
v.push_back(i.second);
}
return vec;
}
var builtin_sleep(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num) {
return nil;
}
#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS)
// mingw-w64 win32 thread model has no std::this_thread
// also msvc will use this
Sleep(static_cast<i64>(val.num()*1e3));
#else
std::this_thread::sleep_for(
std::chrono::microseconds(static_cast<i64>(val.num()*1e6))
);
#endif
return nil;
}
var builtin_platform(var* local, gc& ngc) {
if (is_windows()) {
return ngc.newstr("windows");
} else if (is_linux()) {
return ngc.newstr("linux");
} else if (is_macos()) {
return ngc.newstr("macOS");
}
return ngc.newstr("unknown");
}
var builtin_arch(var* local, gc& ngc) {
if (is_x86()) {
return ngc.newstr("x86");
} else if (is_x86_64()) {
return ngc.newstr("x86-64");
} else if (is_amd64()) {
return ngc.newstr("amd64");
} else if (is_arm()) {
return ngc.newstr("arm");
} else if (is_aarch64()) {
return ngc.newstr("aarch64");
} else if (is_ia64()) {
return ngc.newstr("ia64");
} else if (is_powerpc()) {
return ngc.newstr("powerpc");
} else if (is_superh()) {
return ngc.newstr("superh");
}
return ngc.newstr("unknown");
}
// md5 related functions
std::string tohex(u32 num) {
const char str16[] = "0123456789abcdef";
std::string str = "";
for(u32 i = 0; i<4; i++, num >>= 8) {
std::string tmp = "";
for(u32 j = 0, b = num&0xff; j<2; j++, b >>= 4) {
tmp.insert(0, 1, str16[b&0xf]);
}
str += tmp;
}
return str;
}
std::string md5(const std::string& src) {
std::vector<u32> buff;
usize num = ((src.length()+8)>>6)+1;
usize buffsize = num<<4;
buff.resize(buffsize,0);
for(usize i = 0; i<src.length(); i++) {
buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3);
}
buff[src.length()>>2] |= 0x80<<(((src.length()%4))<<3);
buff[buffsize-2] = (src.length()<<3)&0xffffffff;
buff[buffsize-1] = ((src.length()<<3)>>32)&0xffffffff;
// u32(abs(sin(i+1))*(2pow32))
const u32 k[] = {
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};
// left shift bits
const u32 s[] = {
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
};
// index
const u32 idx[] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16;
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16;
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16;
};
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x,y,z) (((x)&(y))|((~x)&(z)))
#define md5g(x,y,z) (((x)&(z))|((y)&(~z)))
#define md5h(x,y,z) ((x)^(y)^(z))
#define md5i(x,y,z) ((y)^((x)|(~z)))
u32 atmp = 0x67452301, btmp = 0xefcdab89;
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
for(u32 i = 0; i<buffsize; i += 16) {
u32 f, a = atmp, b = btmp, c = ctmp, d = dtmp;
for(u32 j = 0; j<64; j++) {
if (j<16) f = md5f(b, c, d);
else if (j<32) f = md5g(b, c, d);
else if (j<48) f = md5h(b, c, d);
else f = md5i(b, c, d);
u32 tmp = d;
d = c;
c = b;
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]);
a = tmp;
}
atmp += a;
btmp += b;
ctmp += c;
dtmp += d;
}
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
}
var builtin_md5(var* local, gc& ngc) {
var str = local[1];
if (str.type!=vm_str) {
return nas_err("md5", "\"str\" must be string");
}
return ngc.newstr(md5(str.str()));
}
var builtin_millisec(var* local, gc& ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return var::num(res);
}
var builtin_gcextend(var* local, gc& ngc) {
var type = local[1];
if (type.type!=vm_str) {
return nil;
}
auto& s = type.str();
if (s=="str") {
ngc.extend(vm_str);
} else if (s=="vec") {
ngc.extend(vm_vec);
} else if (s=="hash") {
ngc.extend(vm_hash);
} else if (s=="func") {
ngc.extend(vm_func);
} else if (s=="upval") {
ngc.extend(vm_upval);
} else if (s=="obj") {
ngc.extend(vm_obj);
} else if (s=="co") {
ngc.extend(vm_co);
}
return nil;
}
var builtin_gcinfo(var* local, gc& ngc) {
auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc.alloc(vm_hash);
double total = 0;
for(u32 i = 0; i<gc_type_size; ++i) {
total += ngc.gcnt[i];
}
// using ms
auto& map = res.hash().elems;
map["total"] = var::num(ngc.worktime*1.0/den*1000);
map["average"] = var::num(ngc.worktime*1.0/den*1000/total);
map["max_gc"] = var::num(ngc.max_time*1.0/den*1000);
map["max_mark"] = var::num(ngc.max_mark_time*1.0/den*1000);
map["max_sweep"] = var::num(ngc.max_sweep_time*1.0/den*1000);
return res;
}
var builtin_logtime(var* local, gc& ngc) {
time_t t = time(nullptr);
tm* tm_t = localtime(&t);
char s[64];
snprintf(
s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d",
tm_t->tm_year+1900,
tm_t->tm_mon+1,
tm_t->tm_mday,
tm_t->tm_hour,
tm_t->tm_min,
tm_t->tm_sec
);
return ngc.newstr(s);
}
var builtin_ghosttype(var* local, gc& ngc) {
var arg = local[1];
if (arg.type!=vm_obj) {
return nas_err("ghosttype", "this is not a ghost object.");
}
const auto& name = arg.obj().get_ghost_name();
if (!name.length()) {
return var::num(reinterpret_cast<u64>(arg.obj().ptr));
}
return ngc.newstr(name);
}
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{"__println", builtin_println},
{"__exit", builtin_exit},
{"__abort", builtin_abort},
{"__append", builtin_append},
{"__setsize", builtin_setsize},
{"__system", builtin_system},
{"__input", builtin_input},
{"__split", builtin_split},
{"__rand", builtin_rand},
{"__id", builtin_id},
{"__int", builtin_int},
{"__floor", builtin_floor},
{"__num", builtin_num},
{"__pop", builtin_pop},
{"__str", builtin_str},
{"__size", builtin_size},
{"__time", builtin_time},
{"__contains", builtin_contains},
{"__delete", builtin_delete},
{"__keys", builtin_keys},
{"__die", builtin_die},
{"__find", builtin_find},
{"__type", builtin_type},
{"__substr", builtin_substr},
{"__streq", builtin_streq},
{"__left", builtin_left},
{"__right", builtin_right},
{"__cmp", builtin_cmp},
{"__chr", builtin_chr},
{"__char", builtin_char},
{"__values", builtin_values},
{"__sleep", builtin_sleep},
{"__platform", builtin_platform},
{"__arch", builtin_arch},
{"__md5", builtin_md5},
{"__millisec", builtin_millisec},
{"__gcextd", builtin_gcextend},
{"__gcinfo", builtin_gcinfo},
{"__logtime", builtin_logtime},
{"__ghosttype", builtin_ghosttype},
{nullptr, nullptr}
};
}

81
src/nasal_builtin.h Normal file
View File

@@ -0,0 +1,81 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#ifdef _MSC_VER
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4996)
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#endif
#include <sstream>
#include <cmath>
#include <thread>
// for environ
#if defined __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#endif
namespace nasal {
var builtin_print(var*, gc&);
var builtin_println(var*, gc&);
var builtin_exit(var*, gc&);
var builtin_abort(var*, gc&);
var builtin_append(var*, gc&);
var builtin_setsize(var*, gc&);
var builtin_system(var*, gc&);
var builtin_input(var*, gc&);
var builtin_split(var*, gc&);
var builtin_rand(var*, gc&);
var builtin_id(var*, gc&);
var builtin_int(var*, gc&);
var builtin_floor(var*, gc&);
var builtin_num(var*, gc&);
var builtin_pop(var*, gc&);
var builtin_str(var*, gc&);
var builtin_size(var*, gc&);
var builtin_time(var*, gc&);
var builtin_contains(var*, gc&);
var builtin_delete(var*, gc&);
var builtin_keys(var*, gc&);
var builtin_die(var*, gc&);
var builtin_find(var*, gc&);
var builtin_type(var*, gc&);
var builtin_substr(var*, gc&);
var builtin_streq(var*, gc&);
var builtin_left(var*, gc&);
var builtin_right(var*, gc&);
var builtin_cmp(var*, gc&);
var builtin_chr(var*, gc&);
var builtin_char(var*, gc&);
var builtin_values(var*, gc&);
var builtin_sleep(var*, gc&);
var builtin_platform(var*, gc&);
var builtin_arch(var*, gc&);
// md5 related functions
std::string tohex(u32);
std::string md5(const std::string&);
var builtin_md5(var*, gc&);
var builtin_millisec(var*, gc&);
var builtin_gcextend(var*, gc&);
var builtin_gcinfo(var*, gc&);
var builtin_logtime(var*, gc&);
var builtin_ghosttype(var*, gc&);
// register builtin function's name and it's address here in this table below
// this table must end with {nullptr,nullptr}
struct nasal_builtin_table {
const char* name;
var (*func)(var*, gc&);
};
extern nasal_builtin_table builtin[];
}

1340
src/nasal_codegen.cpp Normal file

File diff suppressed because it is too large Load Diff

148
src/nasal_codegen.h Normal file
View File

@@ -0,0 +1,148 @@
#pragma once
#include "nasal_err.h"
#include "nasal_opcode.h"
#include "nasal_ast.h"
#include "ast_visitor.h"
#include "symbol_finder.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_builtin.h"
#include "coroutine.h"
#include "bits_lib.h"
#include "math_lib.h"
#include "fg_props.h"
#include "io_lib.h"
#include "dylib_lib.h"
#include "unix_lib.h"
#include <iomanip>
#include <list>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#endif
namespace nasal {
class codegen {
private:
error err;
// repl output flag, will generate op_repl to output stack top value if true
bool need_repl_output;
// file mapper for file -> index
std::unordered_map<std::string, usize> file_map;
void init_file_map(const std::vector<std::string>&);
// used for generate pop in return expression
std::vector<u32> in_foreach_loop_level;
// constant numbers and strings
std::unordered_map<f64, u32> const_number_map;
std::unordered_map<std::string, u32> const_string_map;
std::vector<f64> const_number_table;
std::vector<std::string> const_string_table;
// native functions
std::vector<nasal_builtin_table> native_function;
std::unordered_map<std::string, usize> native_function_mapper;
void load_native_function_table(nasal_builtin_table*);
void init_native_function();
// generated opcodes
std::vector<opcode> code;
// used to store jmp operands index, to fill the jump address back
std::list<std::vector<i32>> continue_ptr;
std::list<std::vector<i32>> break_ptr;
// symbol table
// global : max STACK_DEPTH-1 values
std::unordered_map<std::string, i32> global;
std::unordered_map<std::string, std::unordered_set<std::string>> experimental_namespace;
// local : max 32768 upvalues 65536 values
// but in fact local scope also has less than STACK_DEPTH value
std::list<std::unordered_map<std::string, i32>> local;
void check_id_exist(identifier*);
void die(const std::string& info, const span& loc) {
err.err("code", loc, info);
}
void regist_num(const f64);
void regist_str(const std::string&);
void find_symbol(code_block*);
void add_symbol(const std::string&);
i32 local_symbol_find(const std::string&);
i32 global_symbol_find(const std::string&);
i32 upvalue_symbol_find(const std::string&);
void emit(u8, u32, const span&);
void num_gen(number_literal*);
void str_gen(string_literal*);
void bool_gen(bool_literal*);
void vec_gen(vector_expr*);
void hash_gen(hash_expr*);
void func_gen(function*);
void call_gen(call_expr*);
void call_id(identifier*);
void call_hash_gen(call_hash*);
void call_vector_gen(call_vector*);
void call_func_gen(call_function*);
void mcall(expr*);
void mcall_id(identifier*);
void mcall_vec(call_vector*);
void mcall_hash(call_hash*);
void multi_def(definition_expr*);
void single_def(definition_expr*);
void def_gen(definition_expr*);
void assignment_expression(assignment_expr*);
void gen_assignment_equal_statement(assignment_expr*);
void replace_left_assignment_with_load(const span&);
void assignment_statement(assignment_expr*);
void multi_assign_gen(multi_assign*);
void cond_gen(condition_expr*);
void loop_gen(expr*);
void load_continue_break(i32, i32);
void while_gen(while_expr*);
void for_gen(for_expr*);
void forei_gen(forei_expr*);
void statement_generation(expr*);
void or_gen(binary_operator*);
void and_gen(binary_operator*);
void unary_gen(unary_operator*);
void binary_gen(binary_operator*);
void trino_gen(ternary_operator*);
void calc_gen(expr*);
void repl_mode_info_output_gen(expr*);
void block_gen(code_block*);
void ret_gen(return_expr*);
public:
const auto& strs() const {return const_string_table;}
const auto& nums() const {return const_number_table;}
const auto& natives() const {return native_function;}
const auto& codes() const {return code;}
const auto& globals() const {return global;}
const auto& get_experimental_namespace() const {
return experimental_namespace;
}
public:
codegen() = default;
const error& compile(parse&, linker&, bool);
void print(std::ostream&);
void symbol_dump(std::ostream&) const;
};
}

283
src/nasal_dbg.cpp Normal file
View File

@@ -0,0 +1,283 @@
#include "nasal_dbg.h"
namespace nasal {
void debug_prof_data::init_counter() {
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
operand_counter[i] = 0;
}
}
void debug_prof_data::load_file_line_counter(
const std::vector<std::string>& file_list) {
file_name_list = file_list;
file_line_counter = {};
file_contents = {};
flstream fs;
for(usize i =0; i<file_list.size(); ++i) {
fs.load(file_list[i]);
file_contents.push_back(fs.file_content());
file_line_counter.push_back({});
file_line_counter.back().resize(fs.size(), 0);
}
}
void debug_prof_data::init(const std::vector<std::string>& file_list) {
init_counter();
load_file_line_counter(file_list);
}
void debug_prof_data::dump_counter() const {
typedef std::pair<u32, u64> op_count;
std::vector<op_count> opcall;
u64 total = 0;
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
total += operand_counter[i];
opcall.push_back({i, operand_counter[i]});
}
std::sort(opcall.begin(), opcall.end(),
[](const op_count& a, const op_count& b) {
return a.second>b.second;
}
);
std::clog << "\noperands call info (<1% ignored)\n";
for(const auto& i : opcall) {
u64 rate = i.second*100/total;
if (!rate) {
break;
}
std::clog << " " << opname[i.first] << " : ";
std::clog << i.second << " (" << rate << "%)\n";
}
std::clog << " total : " << total << '\n';
}
void debug_prof_data::dump_code_line_counter(std::ostream& os) const {
u64 max_call_time = 0;
for(const auto& context : file_line_counter) {
for(const auto& count : context) {
max_call_time = count>max_call_time? count:max_call_time;
}
}
auto pad_length = std::to_string(max_call_time).length();
for(usize i = 0; i<file_name_list.size(); ++i) {
os << "\ncode profiling data of " << file_name_list[i] << ":\n";
const auto& context = file_contents[i];
const auto& counter = file_line_counter[i];
for(usize j = 0; j<context.size(); ++j) {
os << " " << std::right << std::setw(pad_length);
os << std::setfill(' ');
os << (counter[j]==0? "":std::to_string(counter[j]));
os << " " << context[j] << "\n";
}
}
}
void debug_prof_data::dump_this_file_line_counter(std::ostream& os) const {
u64 max_call_time = 0;
for(const auto& count : file_line_counter[0]) {
max_call_time = count>max_call_time? count:max_call_time;
}
auto pad_length = std::to_string(max_call_time).length();
os << "\ncode profiling data of " << file_name_list[0] << ":\n";
const auto& context = file_contents[0];
const auto& counter = file_line_counter[0];
for(usize i = 0; i<context.size(); ++i) {
os << " " << std::right << std::setw(pad_length);
os << std::setfill(' ');
os << (counter[i]==0? "":std::to_string(counter[i]));
os << " " << context[i] << "\n";
}
}
std::vector<std::string> dbg::parse(const std::string& cmd) {
std::vector<std::string> res;
usize last = 0, pos = cmd.find(" ", 0);
while(pos!=std::string::npos) {
if (pos>last) {
res.push_back(cmd.substr(last, pos-last));
}
last = pos+1;
pos = cmd.find(" ", last);
}
if (last<cmd.length()) {
res.push_back(cmd.substr(last));
}
return res;
}
u16 dbg::file_index(const std::string& filename) const {
for(u16 i = 0; i<fsize; ++i) {
if (filename==files[i]) {
return i;
}
}
return 65535;
}
void dbg::err() {
std::cerr
<< "incorrect command\n"
<< "input \'h\' to get help\n";
}
void dbg::help() {
std::clog
<< "<option>\n"
<< " h, help | get help\n"
<< " bt, backtrace | get function call trace\n"
<< " c, continue | run program until break point or exit\n"
<< " f, file | see all the compiled files\n"
<< " g, global | see global values\n"
<< " l, local | see local values\n"
<< " u, upval | see upvalue\n"
<< " r, register | show vm register detail\n"
<< " a, all | show global,local and upvalue\n"
<< " n, next | execute next bytecode\n"
<< " q, exit | exit debugger\n"
<< "<option> <filename> <line>\n"
<< " bk, break | set break point\n";
}
void dbg::list_file() const {
for(usize i = 0; i<fsize; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n";
}
}
void dbg::step_info() {
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u32 begin = (line>>3)==0? 0:((line>>3)<<3);
u32 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog << "\nsource code:\n";
for(u32 i = begin; i<end && i<src.size(); ++i) {
std::clog << (i==line? back_white:reset);
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
}
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3;
codestream::set(cnum, cstr, native.data(), files);
std::clog << "next bytecode:\n";
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog
<< (i==ctx.pc? back_white:reset)
<< (i==ctx.pc? "--> ":" ")
<< codestream(bytecode[i], i)
<< reset << "\n";
}
stackinfo(10);
}
void dbg::interact() {
// special operand, end execution
if (bytecode[ctx.pc].op==op_exit) {
return;
}
// do not need interact while doing profiling
if (do_profiling) {
return;
}
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
bytecode[ctx.pc].line!=bk_line) && // break point
!next) {// next step
return;
}
next = false;
std::string cmd;
step_info();
while(true) {
std::clog << ">> ";
std::getline(std::cin, cmd);
auto res=parse(cmd);
if (res.size()==0) {
step_info();
} else if (res.size()==1) {
switch(get_cmd_type(res[0])) {
case dbg_cmd::cmd_help: help(); break;
case dbg_cmd::cmd_backtrace: traceback(); break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: gstate(); break;
case dbg_cmd::cmd_local: lstate(); break;
case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); break;
case dbg_cmd::cmd_next: next = true; return;
case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
bk_fidx = file_index(res[1]);
if (bk_fidx==65535) {
std::clog << "cannot find file named `" << res[1] << "`\n";
continue;
}
i32 tmp = atoi(res[2].c_str());
if (tmp<=0) {
std::clog << "incorrect line number `" << res[2] << "`\n";
} else {
bk_line = tmp;
}
} else {
err();
}
}
}
void dbg::run(
const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv,
bool profile,
bool show_all_prof_result) {
set_detail_report_info(true);
do_profiling = profile || show_all_prof_result;
const auto& file_list = linker.get_file_list();
fsize = file_list.size();
init(gen.strs(), gen.nums(), gen.natives(),
gen.codes(), gen.globals(),
file_list, argv);
data.init(file_list);
std::vector<u32> code;
std::vector<u16> code_file_index;
std::vector<u32> code_line;
for(auto& i : gen.codes()) {
code.push_back(i.op);
code_file_index.push_back(i.fidx);
code_line.push_back(i.line);
imm.push_back(i.num);
}
while(operand_function[code[ctx.pc]]) {
interact();
data.add_operand_counter(code[ctx.pc]);
data.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
(this->*operand_function[code[ctx.pc]])();
if (ctx.top>=ctx.canary) {
die("stack overflow");
}
++ctx.pc;
}
data.dump_counter();
if (do_profiling) {
show_all_prof_result?
data.dump_code_line_counter(std::clog):
data.dump_this_file_line_counter(std::clog);
}
ngc.info();
ngc.clear();
imm.clear();
return;
}
}

173
src/nasal_dbg.h Normal file
View File

@@ -0,0 +1,173 @@
#pragma once
#include "nasal_import.h"
#include "nasal_err.h"
#include "nasal_opcode.h"
#include "nasal_vm.h"
#include <cstring>
#include <algorithm>
#include <unordered_map>
namespace nasal {
class debug_prof_data {
private:
static const usize operand_size = op_code_type::op_ret + 1;
u64 operand_counter[operand_size];
std::vector<std::string> file_name_list;
std::vector<std::vector<u64>> file_line_counter;
std::vector<std::vector<std::string>> file_contents;
private:
void init_counter();
void load_file_line_counter(const std::vector<std::string>&);
public:
void init(const std::vector<std::string>&);
void dump_counter() const;
void dump_code_line_counter(std::ostream&) const;
void dump_this_file_line_counter(std::ostream&) const;
void add_operand_counter(usize index) {
operand_counter[index] += index<operand_size? 1:0;
}
void add_code_line_counter(usize fidx, usize line) {
auto& vec = file_line_counter[fidx];
vec[line==0? line: line-1]++;
}
};
class dbg:public vm {
private:
typedef void (dbg::*nasal_vm_func)();
const nasal_vm_func operand_function[op_ret + 1] = {
nullptr, &dbg::o_repl,
&dbg::o_intl, &dbg::o_loadg,
&dbg::o_loadl, &dbg::o_loadu,
&dbg::o_pnum, &dbg::o_pnil,
&dbg::o_pstr, &dbg::o_newv,
&dbg::o_newh, &dbg::o_newf,
&dbg::o_happ, &dbg::o_para,
&dbg::o_deft, &dbg::o_dyn,
&dbg::o_lnot, &dbg::o_usub,
&dbg::o_bnot, &dbg::o_btor,
&dbg::o_btxor, &dbg::o_btand,
&dbg::o_add, &dbg::o_sub,
&dbg::o_mul, &dbg::o_div,
&dbg::o_lnk, &dbg::o_addc,
&dbg::o_subc, &dbg::o_mulc,
&dbg::o_divc, &dbg::o_lnkc,
&dbg::o_addeq, &dbg::o_subeq,
&dbg::o_muleq, &dbg::o_diveq,
&dbg::o_lnkeq, &dbg::o_bandeq,
&dbg::o_boreq, &dbg::o_bxoreq,
&dbg::o_addeqc, &dbg::o_subeqc,
&dbg::o_muleqc, &dbg::o_diveqc,
&dbg::o_lnkeqc, &dbg::o_addecp,
&dbg::o_subecp, &dbg::o_mulecp,
&dbg::o_divecp, &dbg::o_lnkecp,
&dbg::o_meq, &dbg::o_eq,
&dbg::o_neq, &dbg::o_less,
&dbg::o_leq, &dbg::o_grt,
&dbg::o_geq, &dbg::o_lessc,
&dbg::o_leqc, &dbg::o_grtc,
&dbg::o_geqc, &dbg::o_pop,
&dbg::o_jmp, &dbg::o_jt,
&dbg::o_jf, &dbg::o_cnt,
&dbg::o_findex, &dbg::o_feach,
&dbg::o_callg, &dbg::o_calll,
&dbg::o_upval, &dbg::o_callv,
&dbg::o_callvi, &dbg::o_callh,
&dbg::o_callfv, &dbg::o_callfh,
&dbg::o_callb, &dbg::o_slcbeg,
&dbg::o_slcend, &dbg::o_slc,
&dbg::o_slc2, &dbg::o_mcallg,
&dbg::o_mcalll, &dbg::o_mupval,
&dbg::o_mcallv, &dbg::o_mcallh,
&dbg::o_ret
};
private:
enum class dbg_cmd {
cmd_error,
cmd_help,
cmd_backtrace,
cmd_continue,
cmd_list_file,
cmd_global,
cmd_local,
cmd_upval,
cmd_register,
cmd_show_all,
cmd_next,
cmd_break_point,
cmd_exit
};
private:
const std::unordered_map<std::string, dbg_cmd> command_table = {
{"h", dbg_cmd::cmd_help},
{"help", dbg_cmd::cmd_help},
{"bt", dbg_cmd::cmd_backtrace},
{"backtrace", dbg_cmd::cmd_backtrace},
{"c", dbg_cmd::cmd_continue},
{"continue", dbg_cmd::cmd_continue},
{"f", dbg_cmd::cmd_list_file},
{"file", dbg_cmd::cmd_list_file},
{"g", dbg_cmd::cmd_global},
{"global", dbg_cmd::cmd_global},
{"l", dbg_cmd::cmd_local},
{"local", dbg_cmd::cmd_local},
{"u", dbg_cmd::cmd_upval},
{"upval", dbg_cmd::cmd_upval},
{"r", dbg_cmd::cmd_register},
{"register", dbg_cmd::cmd_register},
{"a", dbg_cmd::cmd_show_all},
{"all", dbg_cmd::cmd_show_all},
{"n", dbg_cmd::cmd_next},
{"next", dbg_cmd::cmd_next},
{"bk", dbg_cmd::cmd_break_point},
{"break", dbg_cmd::cmd_break_point},
{"q", dbg_cmd::cmd_exit},
{"exit", dbg_cmd::cmd_exit}
};
dbg_cmd get_cmd_type(const std::string& cmd) const {
return command_table.count(cmd)?
command_table.at(cmd):dbg_cmd::cmd_error;
}
private:
bool next;
usize fsize;
u16 bk_fidx;
u32 bk_line;
error src;
private:
debug_prof_data data;
bool do_profiling;
private:
std::vector<std::string> parse(const std::string&);
u16 file_index(const std::string&) const;
void err();
void help();
void list_file() const;
void step_info();
void interact();
public:
dbg():
next(false), fsize(0),
bk_fidx(0), bk_line(0),
do_profiling(false) {}
void run(
const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool
);
};
}

199
src/nasal_err.cpp Normal file
View File

@@ -0,0 +1,199 @@
#include "nasal_err.h"
#include "repl.h"
namespace nasal {
#ifdef _WIN32
#include <windows.h> // use SetConsoleTextAttribute
struct for_reset {
CONSOLE_SCREEN_BUFFER_INFO scr;
for_reset() {
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
}
static for_reset* singleton() {
static for_reset windows_set;
return &windows_set;
}
};
#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& red(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else
s << "\033[91;1m";
#endif
return s;
}
std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else
s << "\033[36;1m";
#endif
return s;
}
std::ostream& orange(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else
s << "\033[93;1m";
#endif
return s;
}
std::ostream& 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),
for_reset::singleton()->scr.wAttributes);
#else
s << "\033[0m";
#endif
return s;
}
void flstream::load(const std::string& f) {
if (file==f) { // don't need to load a loaded file
return;
}
// update file name
file = f;
// REPL: load from memory
if (repl::info::instance()->in_repl_mode &&
repl::info::instance()->repl_file_name==file) {
const auto& source = repl::info::instance()->repl_file_source;
res = {};
size_t pos = 0, last = 0;
while ((pos = source.find("\n", last))!=std::string::npos) {
res.push_back(source.substr(last, pos - last));
last = pos + 1;
}
if (last<source.length()) {
res.push_back(source.substr(last));
} else {
res.push_back("");
}
return;
}
res.clear();
std::ifstream in(f, std::ios::binary);
if (in.fail()) {
std::cerr << red << "src: " << reset << "cannot open <" << f << ">\n";
std::exit(1);
}
while(!in.eof()) {
std::string line;
std::getline(in, line);
res.push_back(line);
}
}
void error::err(const std::string& stage, const std::string& info) {
++cnt;
std::cerr << red << stage << ": " << white << info << reset << "\n\n";
}
void error::warn(const std::string& stage, const std::string& info) {
std::clog << orange << stage << ": " << white << info << reset << "\n\n";
}
void error::err(
const std::string& stage, const span& loc, const std::string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
<< reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen);
for(u32 line = loc.begin_line; line<=loc.end_line; ++line) {
// skip line 0
if (!line) {
continue;
}
if (loc.begin_line<line && line<loc.end_line) {
if (line==loc.begin_line+1) {
std::cerr << cyan << iden << " | " << reset << "...\n";
std::cerr << cyan << iden << " | " << reset << "\n";
}
continue;
}
// if this line has nothing, skip
if (!res[line-1].length() && line!=loc.end_line) {
continue;
}
// line out of range
if (line-1>=res.size()) {
continue;
}
const auto& code = res[line-1];
std::cerr << cyan << leftpad(line, maxlen) << " | " << reset << code << "\n";
// output underline
std::cerr << cyan << iden << " | " << reset;
if (loc.begin_line==loc.end_line) {
for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 i = loc.begin_column; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
}
} else if (line==loc.begin_line) {
for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 i = loc.begin_column; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
}
} else if (loc.begin_line<line && line<loc.end_line) {
for(u32 i = 0; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
} else {
for(u32 i = 0; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
}
if (line==loc.end_line) {
std::cerr << reset;
} else {
std::cerr << reset << "\n";
}
}
std::cerr << "\n\n";
}
}

71
src/nasal_err.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream> // MSVC need this to use std::getline
#include <cstring>
#include <vector>
#include "nasal.h"
namespace nasal {
struct span {
u32 begin_line;
u32 begin_column;
u32 end_line;
u32 end_column;
std::string file;
};
std::ostream& back_white(std::ostream&);
std::ostream& red(std::ostream&);
std::ostream& cyan(std::ostream&);
std::ostream& orange(std::ostream&);
std::ostream& white(std::ostream&);
std::ostream& reset(std::ostream&);
class flstream {
protected:
std::string file;
std::vector<std::string> res;
public:
flstream():file("") {}
void load(const std::string&);
const std::string& operator[](usize n) const {return res[n];}
const auto& name() const {return file;}
const auto& file_content() const {return res;}
usize size() const {return res.size();}
};
class error:public flstream {
private:
u32 cnt; // counter for errors
std::string identation(usize len) {
return std::string(len,' ');
}
std::string leftpad(u32 num, usize len) {
auto tmp = std::to_string(num);
while(tmp.length()<len) {
tmp=" "+tmp;
}
return tmp;
}
public:
error():cnt(0) {}
void err(const std::string&, const std::string&);
void warn(const std::string&, const std::string&);
void err(const std::string&, const span&, const std::string&);
void chkerr() const {
if (cnt) {
std::exit(1);
}
}
u32 geterr() const {return cnt;}
};
}

732
src/nasal_gc.cpp Normal file
View File

@@ -0,0 +1,732 @@
#include "nasal_gc.h"
namespace nasal {
var nas_vec::get_val(const i32 n) {
i32 size = elems.size();
if (n<-size || n>=size) {
return var::none();
}
return elems[n>=0? n:n+size];
}
var* nas_vec::get_mem(const i32 n) {
i32 size = elems.size();
if (n<-size || n>=size) {
return nullptr;
}
return &elems[n>=0? n:n+size];
}
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
if (!vec.elems.size() || vec.printed) {
out << (vec.elems.size()? "[..]":"[]");
return out;
}
vec.printed = true;
usize iter = 0, size = vec.elems.size();
out << "[";
for(auto& i:vec.elems) {
out << i << ",]"[(++iter)==size];
}
vec.printed = false;
return out;
}
var nas_hash::get_val(const std::string& key) {
if (elems.count(key)) {
return elems.at(key);
} else if (!elems.count("parents")) {
return var::none();
}
var ret = var::none();
var val = elems.at("parents");
if (val.type!=vm_vec) {
return ret;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
ret = i.hash().get_val(key);
}
if (ret.type!=vm_none) {
return ret;
}
}
return ret;
}
var* nas_hash::get_mem(const std::string& key) {
if (elems.count(key)) {
return &elems.at(key);
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr = nullptr;
var val = elems.at("parents");
if (val.type!=vm_vec) {
return addr;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
addr = i.hash().get_mem(key);
}
if (addr) {
return addr;
}
}
return addr;
}
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
if (!hash.elems.size() || hash.printed) {
out << (hash.elems.size()? "{..}":"{}");
return out;
}
hash.printed = true;
usize iter = 0, size = hash.elems.size();
out << "{";
for(auto& i : hash.elems) {
out << i.first << ":" << i.second << ",}"[(++iter)==size];
}
hash.printed = false;
return out;
}
void nas_func::clear() {
dpara = -1;
local.clear();
upval.clear();
keys.clear();
}
void nas_ghost::set(
const std::string& ghost_type_name,
destructor destructor_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
dtor_ptr = destructor_pointer;
ptr = ghost_pointer;
}
void nas_ghost::clear() {
// do nothing if pointer is null
if (!ptr) {
return;
}
// do clear pointer if destructor function pointer is null
if (!dtor_ptr) {
type_name = "";
ptr = nullptr;
return;
}
// do destruction
dtor_ptr(ptr);
type_name = "";
ptr = nullptr;
dtor_ptr = nullptr;
}
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
out << "<object " << ghost.get_ghost_name();
out << " at 0x" << std::hex;
out << reinterpret_cast<u64>(ghost.ptr) << std::dec << ">";
return out;
}
void nas_co::clear() {
if (!ctx.stack) {
return;
}
for(u32 i = 0; i<STACK_DEPTH; ++i) {
ctx.stack[i] = var::nil();
}
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.canary = ctx.stack+STACK_DEPTH-1;
ctx.top = ctx.stack;
ctx.funcr = var::nil();
ctx.upvalr = var::nil();
status = status::suspended;
}
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
out << "<coroutine at 0x" << std::hex;
out << reinterpret_cast<u64>(&co) << std::dec << ">";
return out;
}
var nas_map::get_val(const std::string& key) {
if (mapper.count(key)) {
return *mapper.at(key);
}
return var::none();
}
var* nas_map::get_mem(const std::string& key) {
if (mapper.count(key)) {
return mapper.at(key);
}
return nullptr;
}
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
if (!mp.mapper.size() || mp.printed) {
out << (mp.mapper.size()? "{..}":"{}");
return out;
}
mp.printed = true;
usize iter = 0, size = mp.mapper.size();
out << "{";
for(auto& i : mp.mapper) {
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
}
mp.printed = false;
return out;
}
nas_val::nas_val(u8 val_type) {
mark = gc_status::collected;
type = val_type;
unmut = 0;
switch(val_type) {
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
case vm_map: ptr.map = new nas_map; break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_upval:delete ptr.upval;break;
case vm_obj: delete ptr.obj; break;
case vm_co: delete ptr.co; break;
case vm_map: delete ptr.map; break;
}
type=vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_str: ptr.str->clear(); break;
case vm_vec: ptr.vec->elems.clear(); break;
case vm_hash: ptr.hash->elems.clear();break;
case vm_func: ptr.func->clear(); break;
case vm_upval:ptr.upval->clear(); break;
case vm_obj: ptr.obj->clear(); break;
case vm_co: ptr.co->clear(); break;
case vm_map: ptr.map->clear(); break;
}
}
f64 var::tonum() {
return type!=vm_str? val.num:str2num(str().c_str());
}
std::string var::tostr() {
if (type==vm_str) {
return str();
} else if (type==vm_num) {
std::string tmp=std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp;
}
return "";
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out << "undefined"; break;
case vm_nil: out << "nil"; break;
case vm_num: out << ref.val.num; break;
case vm_str: out << ref.str(); break;
case vm_vec: out << ref.vec(); break;
case vm_hash: out << ref.hash(); break;
case vm_func: out << "func(..) {..}"; break;
case vm_obj: out << ref.obj(); break;
case vm_co: out << ref.co(); break;
case vm_map: out << ref.map(); break;
}
return out;
}
bool var::objchk(const std::string& name) {
return type==vm_obj && obj().type_name==name && obj().ptr;
}
var var::none() {
return {vm_none, static_cast<u32>(0)};
}
var var::nil() {
return {vm_nil, static_cast<u32>(0)};
}
var var::ret(u32 pc) {
return {vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_cnt, n};
}
var var::num(f64 n) {
return {vm_num, n};
}
var var::gcobj(nas_val* p) {
return {p->type, p};
}
var var::addr(var* p) {
return {vm_addr, p};
}
var* var::addr() {
return val.addr;
}
u32 var::ret() {
return val.ret;
}
i64& var::cnt() {
return val.cnt;
}
f64 var::num() {
return val.num;
}
std::string& var::str() {
return *val.gcobj->ptr.str;
}
nas_vec& var::vec() {
return *val.gcobj->ptr.vec;
}
nas_hash& var::hash() {
return *val.gcobj->ptr.hash;
}
nas_func& var::func() {
return *val.gcobj->ptr.func;
}
nas_upval& var::upval() {
return *val.gcobj->ptr.upval;
}
nas_ghost& var::obj() {
return *val.gcobj->ptr.obj;
}
nas_co& var::co() {
return *val.gcobj->ptr.co;
}
nas_map& var::map() {
return *val.gcobj->ptr.map;
}
void gc::do_mark_sweep() {
using clk = std::chrono::high_resolution_clock;
auto begin = clk::now();
mark();
auto mark_end = clk::now();
sweep();
auto sweep_end = clk::now();
auto total_time = (sweep_end-begin).count();
auto mark_time = (mark_end-begin).count();
auto sweep_time = (sweep_end-mark_end).count();
worktime += total_time;
max_time = max_time<total_time? total_time:max_time;
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
}
void gc::mark() {
std::vector<var> bfs;
mark_context_root(bfs);
if (memory.size()>8192 && bfs.size()>4) {
usize size = bfs.size();
std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4);
std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2);
std::thread t2(&gc::concurrent_mark, this, std::ref(bfs), size/2, size/4*3);
std::thread t3(&gc::concurrent_mark, this, std::ref(bfs), size/4*3, size);
t0.join();
t1.join();
t2.join();
t3.join();
return;
}
while(!bfs.empty()) {
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
mark_var(bfs, value);
}
}
void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
std::vector<var> bfs;
for(auto i = begin; i<end; ++i) {
var value = vec[i];
if (value.type<=vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
mark_var(bfs, value);
}
while(!bfs.empty()) {
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
mark_var(bfs, value);
}
}
void gc::mark_context_root(std::vector<var>& bfs_queue) {
// scan global
for(usize i = 0; i<main_context_global_size; ++i) {
auto& val = main_context_global[i];
if (val.type>vm_num) {
bfs_queue.push_back(val);
}
}
// scan now running context, this context maybe related to coroutine or main
for(var* i = rctx->stack; i<=rctx->top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(rctx->funcr);
bfs_queue.push_back(rctx->upvalr);
bfs_queue.push_back(temp);
if (!cort) {
return;
}
// coroutine is running, so scan main process stack from mctx
for(var* i = mctx.stack; i<=mctx.top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(mctx.funcr);
bfs_queue.push_back(mctx.upvalr);
}
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
value.val.gcobj->mark = nas_val::gc_status::found;
switch(value.type) {
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
case vm_func: mark_func(bfs_queue, value.func()); break;
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
case vm_co: mark_co(bfs_queue, value.co()); break;
case vm_map: mark_map(bfs_queue, value.map()); break;
default: break;
}
}
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i : vec.elems) {
if (i.type>vm_num) {
bfs_queue.push_back(i);
}
}
}
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i : hash.elems) {
if (i.second.type>vm_num) {
bfs_queue.push_back(i.second);
}
}
}
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
for(auto& i : function.local) {
if (i.type>vm_num) {
bfs_queue.push_back(i);
}
}
for(auto& i : function.upval) {
bfs_queue.push_back(i);
}
}
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
for(auto& i : upval.elems) {
if (i.type>vm_num) {
bfs_queue.push_back(i);
}
}
}
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
bfs_queue.push_back(co.ctx.funcr);
bfs_queue.push_back(co.ctx.upvalr);
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
}
void gc::mark_map(std::vector<var>& bfs_queue, nas_map& mp) {
for(const auto& i : mp.mapper) {
if (i.second->type>vm_num) {
bfs_queue.push_back(*i.second);
}
}
}
void gc::sweep() {
for(auto i : memory) {
if (i->mark==nas_val::gc_status::uncollected) {
i->clear();
unused[i->type-vm_str].push_back(i);
i->mark = nas_val::gc_status::collected;
} else if (i->mark==nas_val::gc_status::found) {
i->mark = nas_val::gc_status::uncollected;
}
}
}
void gc::extend(u8 type) {
const u8 index = type-vm_str;
size[index] += incr[index];
for(u32 i = 0; i<incr[index]; ++i) {
// no need to check, will be killed if memory is not enough
nas_val* tmp = new nas_val(type);
// add to heap
memory.push_back(tmp);
unused[index].push_back(tmp);
}
incr[index] = incr[index]+incr[index]/2;
}
void gc::init(
const std::vector<std::string>& constant_strings,
const std::vector<std::string>& argv
) {
// initialize counters
worktime = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
size[i] = gcnt[i] = acnt[i] = 0;
}
// coroutine pointer set to nullptr
cort = nullptr;
// init constant strings
strs.resize(constant_strings.size());
for(u32 i = 0; i<strs.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode
if (strs[i].type==vm_str && strs[i].str()==constant_strings[i]) {
continue;
}
strs[i] = var::gcobj(new nas_val(vm_str));
strs[i].val.gcobj->unmut = 1;
strs[i].str() = constant_strings[i];
}
// record arguments
env_argv.resize(argv.size());
for(usize i = 0; i<argv.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode
if (env_argv[i].type==vm_str && env_argv[i].str()==argv[i]) {
continue;
}
env_argv[i] = var::gcobj(new nas_val(vm_str));
env_argv[i].val.gcobj->unmut = 1;
env_argv[i].str() = argv[i];
}
}
void gc::clear() {
for(auto i : memory) {
delete i;
}
memory.clear();
for(u8 i = 0; i<gc_type_size; ++i) {
unused[i].clear();
}
for(auto& i : strs) {
delete i.val.gcobj;
}
strs.clear();
env_argv.clear();
}
void gc::info() const {
using std::left;
using std::setw;
using std::setfill;
const char* used_table_name[] = {
"object type", "gc count", "alloc count", "memory size",
"detail", "time spend", "gc time", "avg time", "max gc",
"max mark", "max sweep", nullptr
};
const char* name[] = {
"string",
"vector",
"hashmap",
"function",
"upvalue",
"object",
"coroutine",
"namespace",
nullptr
};
usize indent = 0, len = 0;
for(usize i = 0; used_table_name[i]; ++i) {
len = std::string(used_table_name[i]).length();
indent = indent<len? len:indent;
}
for(usize i = 0; name[i]; ++i) {
len = std::string(name[i]).length();
indent = indent<len? len:indent;
}
for(u32 i = 0; i<gc_type_size; ++i) {
len = std::to_string(gcnt[i]).length();
indent = indent<len? len:indent;
len = std::to_string(acnt[i]).length();
indent = indent<len? len:indent;
len = std::to_string(size[i]).length();
indent = indent<len? len:indent;
}
auto indent_string = std::string("--");
for(usize i = 0; i<indent; ++i) {
indent_string += "-";
}
auto last_line = indent_string + "+" +
indent_string + "-" + indent_string + "-" + indent_string;
indent_string = indent_string + "+" +
indent_string + "+" + indent_string + "+" + indent_string;
std::clog << "\n" << indent_string << "\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "object type";
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
std::clog << "\n" << indent_string << "\n";
double total = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
if (!gcnt[i] && !acnt[i] && !size[i]) {
continue;
}
total += gcnt[i];
std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
std::clog << "\n";
}
std::clog << indent_string << "\n";
auto den = std::chrono::high_resolution_clock::duration::period::den;
std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << "\n" << indent_string << "\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "gc time";
std::clog << " | " << worktime*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "avg time";
std::clog << " | " << worktime*1.0/den*1000/total << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max gc";
std::clog << " | " << max_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max mark";
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
std::clog << last_line << "\n";
}
var gc::alloc(u8 type) {
const u8 index = type-vm_str;
++acnt[index];
if (unused[index].empty()) {
++gcnt[index];
do_mark_sweep();
}
if (unused[index].empty()) {
extend(type);
}
var ret = var::gcobj(unused[index].back());
ret.val.gcobj->mark = nas_val::gc_status::uncollected;
unused[index].pop_back();
return ret;
}
void gc::ctxchg(nas_co& co) {
// store running state to main context
mctx = *rctx;
// restore coroutine context state
*rctx = co.ctx;
// set coroutine pointer
cort = &co;
// set coroutine state to running
cort->status = nas_co::status::running;
}
void gc::ctxreserve() {
// pc=0 means this coroutine is finished
cort->status = rctx->pc?
nas_co::status::suspended:
nas_co::status::dead;
// store running state to coroutine
cort->ctx = *rctx;
// restore main context state
*rctx = mctx;
// set coroutine pointer to nullptr
cort = nullptr;
}
var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
return var::none();
}
}

394
src/nasal_gc.h Normal file
View File

@@ -0,0 +1,394 @@
#pragma once
// avoid MSVC warnings
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#ifndef _MSC_VER
#include <unistd.h>
#include <dirent.h>
#else
#include <io.h>
#include <direct.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <iomanip>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <algorithm>
#include <thread>
#include <cstring>
#include <sstream>
#include "nasal.h"
namespace nasal {
enum vm_type:u8 {
/* none-gc object */
vm_none = 0,
vm_cnt,
vm_addr,
vm_ret,
vm_nil,
vm_num,
/* gc object */
vm_str,
vm_vec,
vm_hash,
vm_func,
vm_upval,
vm_obj,
vm_co,
vm_map // for globals and namespaces
};
const u32 gc_type_size = vm_map-vm_str+1;
struct nas_vec; // vector
struct nas_hash; // hashmap(dict)
struct nas_func; // function(lambda)
struct nas_upval; // upvalue
struct nas_ghost; // objects
struct nas_co; // coroutine
struct nas_map; // mapper
struct nas_val; // nas_val includes gc-managed types
struct var {
public:
u8 type = vm_none;
union {
u32 ret;
i64 cnt;
f64 num;
var* addr;
nas_val* gcobj;
} val;
private:
var(u8 t, u32 pc) {type = t; val.ret = pc;}
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
var(u8 t, f64 n) {type = t; val.num = n;}
var(u8 t, var* p) {type = t; val.addr = p;}
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
public:
var() = default;
var(const var&) = default;
bool operator==(const var& nr) const {
return type==nr.type && val.gcobj==nr.val.gcobj;
}
bool operator!=(const var& nr) const {
return type!=nr.type || val.gcobj!=nr.val.gcobj;
}
// number and string can be translated to each other
f64 tonum();
std::string tostr();
bool objchk(const std::string&);
// create new var object
static var none();
static var nil();
static var ret(u32);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
// get content
var* addr();
u32 ret();
i64& cnt();
f64 num();
std::string& str();
nas_vec& vec();
nas_hash& hash();
nas_func& func();
nas_upval& upval();
nas_ghost& obj();
nas_co& co();
nas_map& map();
};
struct nas_vec {
std::vector<var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_vec():printed(false) {}
usize size() const {return elems.size();}
var get_val(const i32);
var* get_mem(const i32);
};
struct nas_hash {
std::unordered_map<std::string, var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_hash(): printed(false) {}
usize size() const {return elems.size();}
var get_val(const std::string&);
var* get_mem(const std::string&);
};
struct nas_func {
i32 dpara; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
u32 psize; // used to load default parameters to a new function
u32 lsize; // used to expand memory space for local values on stack
std::vector<var> local; // local scope with default value(var)
std::vector<var> upval; // closure
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool onstk;
u32 size;
var* stk;
/* not on stack, use this */
std::vector<var> elems;
public:
nas_upval(): onstk(true), size(0), stk(nullptr) {}
var& operator[](usize n) {
return onstk? stk[n]:elems[n];
}
void clear() {
onstk = true;
elems.clear();
size = 0;
}
};
struct nas_ghost {
private:
using destructor=void (*)(void*);
public:
std::string type_name;
destructor dtor_ptr;
void* ptr;
public:
nas_ghost(): type_name(""), dtor_ptr(nullptr), ptr(nullptr) {}
~nas_ghost() {clear();}
void set(const std::string&, destructor, void*);
void clear();
public:
const std::string& get_ghost_name() const {
return type_name;
}
};
struct context {
u32 pc = 0;
var* localr = nullptr;
var* memr = nullptr;
var funcr = var::nil();
var upvalr = var::nil();
var* canary = nullptr;
var* stack = nullptr;
var* top = nullptr;
};
struct nas_co {
enum class status:u32 {
suspended,
running,
dead
};
context ctx;
status status;
nas_co() {
ctx.stack = new var[STACK_DEPTH];
clear();
}
~nas_co() {
delete[] ctx.stack;
}
void clear();
};
struct nas_map {
bool printed = false;
std::unordered_map<std::string, var*> mapper;
nas_map() {}
void clear() {
mapper.clear();
}
var get_val(const std::string&);
var* get_mem(const std::string&);
};
struct nas_val {
enum class gc_status:u8 {
uncollected = 0,
collected,
found
};
gc_status mark;
u8 type; // value type
u8 unmut; // used to mark if a string is unmutable
union {
std::string* str;
nas_vec* vec;
nas_hash* hash;
nas_func* func;
nas_upval* upval;
nas_ghost* obj;
nas_co* co;
nas_map* map;
} ptr;
nas_val(u8);
~nas_val();
void clear();
};
std::ostream& operator<<(std::ostream&, nas_vec&);
std::ostream& operator<<(std::ostream&, nas_hash&);
std::ostream& operator<<(std::ostream&, nas_map&);
std::ostream& operator<<(std::ostream&, const nas_ghost&);
std::ostream& operator<<(std::ostream&, const nas_co&);
std::ostream& operator<<(std::ostream&, var&);
const var zero = var::num(0);
const var one = var::num(1);
const var nil = var::nil();
struct gc {
/* main context temporary storage */
context mctx;
/* global storage */
var* main_context_global = nullptr;
usize main_context_global_size = 0;
/* runtime context */
context* rctx = nullptr;
nas_co* cort = nullptr; // running coroutine
/* temporary space used in native/module functions */
var temp = nil;
/* constants and memory pool */
std::vector<var> strs = {}; // reserved address for const vm_str
std::vector<var> env_argv = {}; // command line arguments
std::vector<nas_val*> memory; // gc memory
std::vector<nas_val*> unused[gc_type_size]; // gc free list
/* heap increase size */
u32 incr[gc_type_size] = {
128, // vm_str
128, // vm_vec
64, // vm_hash
128, // vm_func
256, // vm_upval
16, // vm_obj
16, // vm_co
2, // vm_map
};
/* values for analysis */
u64 size[gc_type_size];
u64 gcnt[gc_type_size];
u64 acnt[gc_type_size];
i64 worktime = 0;
i64 max_time = 0;
i64 max_mark_time = 0;
i64 max_sweep_time = 0;
void set(context* _ctx, var* _global, usize _size) {
rctx = _ctx;
main_context_global = _global;
main_context_global_size = _size;
}
private:
/* gc functions */
void do_mark_sweep();
void mark();
void concurrent_mark(std::vector<var>&, usize, usize);
void mark_context_root(std::vector<var>&);
void mark_var(std::vector<var>&, var&);
void mark_vec(std::vector<var>&, nas_vec&);
void mark_hash(std::vector<var>&, nas_hash&);
void mark_func(std::vector<var>&, nas_func&);
void mark_upval(std::vector<var>&, nas_upval&);
void mark_co(std::vector<var>&, nas_co&);
void mark_map(std::vector<var>&, nas_map&);
void sweep();
public:
void extend(u8);
void init(const std::vector<std::string>&, const std::vector<std::string>&);
void clear();
void info() const;
var alloc(const u8);
void ctxchg(nas_co&);
void ctxreserve();
public:
var newstr(char c) {
var s = alloc(vm_str);
s.str() = c;
return s;
}
var newstr(const char* buff) {
var s=alloc(vm_str);
s.str() = buff;
return s;
}
var newstr(const std::string& buff) {
var s=alloc(vm_str);
s.str() = buff;
return s;
}
};
// use to print error log and return error value
var nas_err(const std::string&, const std::string&);
// module function type
typedef var (*module_func)(var*, usize, gc*);
// module function stores in tables with this type, end with {nullptr,nullptr}
struct module_func_info {
const char* name;
module_func fd;
};
// module function "get" type
typedef module_func_info* (*get_func_ptr)();
}

364
src/nasal_import.cpp Normal file
View File

@@ -0,0 +1,364 @@
#include "nasal_import.h"
#include "symbol_finder.h"
namespace nasal {
linker::linker():
show_path(false), lib_loaded(false),
this_file(""), lib_path("") {
char sep = is_windows()? ';':':';
std::string PATH = getenv("PATH");
usize last = 0, pos = PATH.find(sep, 0);
while(pos!=std::string::npos) {
std::string dirpath = PATH.substr(last, pos-last);
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));
}
}
std::string linker::get_path(call_expr* node) {
if (node->get_calls()[0]->get_type()==expr_type::ast_callf) {
auto tmp = (call_function*)node->get_calls()[0];
return ((string_literal*)tmp->get_argument()[0])->get_content();
}
auto fpath = std::string(".");
for(auto i : node->get_calls()) {
fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field();
}
return fpath + ".nas";
}
std::string linker::find_file(
const std::string& filename, const span& location) {
// first add file name itself into the file path
std::vector<std::string> fpath = {filename};
// generate search path from environ path
for(const auto& p : envpath) {
fpath.push_back(p + (is_windows()? "\\":"/") + filename);
}
// search file
for(const auto& i : fpath) {
if (access(i.c_str(), F_OK)!=-1) {
return i;
}
}
// we will find lib.nas in nasal std directory
if (filename=="lib.nas") {
return is_windows()?
find_file("std\\lib.nas", location):
find_file("std/lib.nas", location);
}
if (!show_path) {
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename + ">, " +
"use <-d> to get detail search path");
return "";
}
std::string paths = "";
for(const auto& i : fpath) {
paths += " -> " + i + "\n";
}
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename + "> in these paths:\n" + paths);
return "";
}
bool linker::import_check(expr* node) {
/*
call
|_id:import
|_callh:std
|_callh:file
*/
if (node->get_type()!=expr_type::ast_call) {
return false;
}
auto tmp = (call_expr*)node;
if (tmp->get_first()->get_type()!=expr_type::ast_id) {
return false;
}
if (((identifier*)tmp->get_first())->get_name()!="import") {
return false;
}
if (!tmp->get_calls().size()) {
return false;
}
// import.xxx.xxx;
if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) {
for(auto i : tmp->get_calls()) {
if (i->get_type()!=expr_type::ast_callh) {
return false;
}
}
return true;
}
// import("xxx");
if (tmp->get_calls().size()!=1) {
return false;
}
/*
call
|_id:import
|_call_func
|_string:'filename'
*/
if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) {
return false;
}
auto func_call = (call_function*)tmp->get_calls()[0];
if (func_call->get_argument().size()!=1) {
return false;
}
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
return false;
}
return true;
}
bool linker::exist(const std::string& file) {
// avoid importing the same file
for(const auto& fname : files) {
if (file==fname) {
return true;
}
}
files.push_back(file);
return false;
}
u16 linker::find(const std::string& file) {
for(usize i = 0; i<files.size(); ++i) {
if (files[i]==file) {
return static_cast<u16>(i);
}
}
std::cerr << "unreachable: using this method incorrectly\n";
std::exit(-1);
return UINT16_MAX;
}
bool linker::check_self_import(const std::string& file) {
for(const auto& i : module_load_stack) {
if (file==i) {
return true;
}
}
return false;
}
std::string linker::generate_self_import_path(const std::string& filename) {
std::string res = "";
for(const auto& i : module_load_stack) {
res += "[" + i + "] -> ";
}
return res + "[" + filename + "]";
}
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
// add children of add_root to the back of root
for(auto& i : old_tree_root->get_expressions()) {
new_tree_root->add_expression(i);
}
// clean old root
old_tree_root->get_expressions().clear();
}
code_block* linker::import_regular_file(call_expr* node) {
lexer lex;
parse par;
// get filename
auto filename = get_path(node);
// clear this node
for(auto i : node->get_calls()) {
delete i;
}
node->get_calls().clear();
auto location = node->get_first()->get_location();
delete node->get_first();
node->set_first(new nil_expr(location));
// this will make node to call_expr(nil),
// will not be optimized when generating bytecodes
// avoid infinite loading loop
filename = find_file(filename, node->get_location());
if (!filename.length()) {
return new code_block({0, 0, 0, 0, filename});
}
if (check_self_import(filename)) {
err.err("link", "self-referenced module <" + filename + ">:\n" +
" reference path: " + generate_self_import_path(filename));
return new code_block({0, 0, 0, 0, filename});
}
exist(filename);
module_load_stack.push_back(filename);
// start importing...
if (lex.scan(filename).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
}
if (par.compile(lex).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
}
auto tmp = par.swap(nullptr);
// check if tmp has 'import'
auto res = load(tmp, find(filename));
module_load_stack.pop_back();
return res;
}
code_block* linker::import_nasal_lib() {
lexer lex;
parse par;
auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]});
if (!filename.length()) {
return new code_block({0, 0, 0, 0, filename});
}
lib_path = filename;
// avoid infinite loading library
if (exist(filename)) {
return new code_block({0, 0, 0, 0, filename});
}
// start importing...
if (lex.scan(filename).geterr()) {
err.err("link", "error occurred when analysing library <" + filename + ">");
}
if (par.compile(lex).geterr()) {
err.err("link", "error occurred when analysing library <" + filename + ">");
}
auto tmp = par.swap(nullptr);
// check if tmp has 'import'
return load(tmp, find(filename));
}
std::string linker::generate_module_name(const std::string& filename) {
auto error_name = "error_generated@[" + filename + "]";
auto pos = filename.find_last_of(".nas");
if (pos==std::string::npos) {
return error_name;
}
pos -= 4;
auto split_pos = filename.find_last_of("/");
if (split_pos==std::string::npos) {
split_pos = filename.find_last_of("\\");
}
auto res = split_pos==std::string::npos?
filename.substr(0, pos + 1):
filename.substr(split_pos + 1, pos - split_pos);
if (!res.length()) {
err.warn("link", "get empty module name from <" + filename + ">, " +
"will not be easily accessed.");
}
if (res.length() && '0' <= res[0] && res[0] <= '9') {
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
"will not be easily accessed.");
}
if (res.length() && res.find(".")!=std::string::npos) {
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
"will not be easily accessed.");
}
return res;
}
return_expr* linker::generate_module_return(code_block* block) {
auto sf = new symbol_finder;
auto res = new return_expr(block->get_location());
auto value = new hash_expr(block->get_location());
res->set_value(value);
for(const auto& i : sf->do_find(block)) {
auto pair = new hash_pair(block->get_location());
// do not export symbol begins with '_'
if (i.name.length() && i.name[0]=='_') {
continue;
}
pair->set_name(i.name);
pair->set_value(new identifier(block->get_location(), i.name));
value->add_member(pair);
}
delete sf;
return res;
}
definition_expr* linker::generate_module_definition(code_block* block) {
auto def = new definition_expr(block->get_location());
def->set_identifier(new identifier(
block->get_location(),
generate_module_name(block->get_location().file)
));
auto call = new call_expr(block->get_location());
auto func = new function(block->get_location());
func->set_code_block(block);
func->get_code_block()->add_expression(generate_module_return(block));
call->set_first(func);
call->add_call(new call_function(block->get_location()));
def->set_value(call);
return def;
}
code_block* linker::load(code_block* program_root, u16 fileindex) {
auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
// load library, this ast will be linked with root directly
// so no extra namespace is generated
if (!lib_loaded) {
auto nasal_lib_code_block = import_nasal_lib();
// insert nasal lib code to the back of tree
link(tree, nasal_lib_code_block);
delete nasal_lib_code_block;
lib_loaded = true;
}
// load imported modules
for(auto& import_ast_node : program_root->get_expressions()) {
if (!import_check(import_ast_node)) {
break;
}
auto module_code_block = import_regular_file((call_expr*)import_ast_node);
// after importing the regular file as module, delete this node
const auto loc = import_ast_node->get_location();
delete import_ast_node;
// and replace the node with null_expr node
import_ast_node = new null_expr(loc);
// then we generate a function warping the code block,
// and export the necessary global symbols in this code block
// by generate a return statement, with a hashmap return value
tree->add_expression(generate_module_definition(module_code_block));
}
// insert program root to the back of tree
link(tree, program_root);
return tree;
}
const error& linker::link(
parse& parse, const std::string& self, bool spath = false) {
show_path = spath;
// initializing file map
this_file = self;
files = {self};
module_load_stack = {self};
// scan root and import files
// then generate a new ast and return to import_ast
// the main file's index is 0
auto new_tree_root = load(parse.tree(), 0);
auto old_tree_root = parse.swap(new_tree_root);
delete old_tree_root;
return err;
}
}

60
src/nasal_import.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#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
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
#include "symbol_finder.h"
#include <vector>
namespace nasal {
class linker {
private:
bool show_path;
bool lib_loaded;
std::string this_file;
std::string lib_path;
error err;
std::vector<std::string> files;
std::vector<std::string> module_load_stack;
std::vector<std::string> envpath;
private:
bool import_check(expr*);
bool exist(const std::string&);
u16 find(const std::string&);
bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&);
void link(code_block*, code_block*);
std::string get_path(call_expr*);
std::string find_file(const std::string&, const span&);
code_block* import_regular_file(call_expr*);
code_block* import_nasal_lib();
std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*);
definition_expr* generate_module_definition(code_block*);
code_block* load(code_block*, u16);
public:
linker();
const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return files;}
const auto& get_this_file() const {return this_file;}
const auto& get_lib_path() const {return lib_path;}
};
}

393
src/nasal_lexer.cpp Normal file
View File

@@ -0,0 +1,393 @@
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include "nasal_lexer.h"
#include "repl.h"
namespace nasal {
bool lexer::skip(char c) {
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
}
bool lexer::is_id(char c) {
return (c=='_') || std::isalpha(c) || (c<0);
}
bool lexer::is_hex(char c) {
return std::isxdigit(c);
}
bool lexer::is_oct(char c) {
return '0'<=c && c<='7';
}
bool lexer::is_dec(char c) {
return std::isdigit(c);
}
bool lexer::is_str(char c) {
return c=='\'' || c=='\"' || c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='(' || c==')' || c=='[' || c==']' ||
c=='{' || c=='}' || c==',' || c==';' ||
c==':' || c=='?' || c=='`' || c=='@' ||
c=='%' || c=='$' || c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='=' || c=='+' || c=='-' || c=='*' ||
c=='!' || c=='/' || c=='<' || c=='>' ||
c=='~' || c=='|' || c=='&' || c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to '\n'
// so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c = res[ptr++];
err.err("lexer",
{line, column-1, line, column, filename},
"invalid character 0x"+chrhex(c)
);
++invalid_char;
}
void lexer::open(const std::string& file) {
if (repl::info::instance()->in_repl_mode &&
repl::info::instance()->repl_file_name==file) {
err.load(file);
filename = file;
res = repl::info::instance()->repl_file_source;
return;
}
// check file exsits and it is a regular file
struct stat buffer;
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename = file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <" + file + ">");
res = "";
return;
}
err.load(file);
std::stringstream ss;
ss << in.rdbuf();
res = ss.str();
}
tok lexer::get_type(const std::string& str) {
return typetbl.count(str)? typetbl.at(str):tok::null;
}
std::string lexer::utf8_gen() {
std::string str = "";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp = "";
u32 nbytes = utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp += res[ptr++];
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp += res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
std::string utf_info = "0x"+chrhex(tmp[0]);
for(u32 i = 1; i<tmp.size(); ++i) {
utf_info += " 0x"+chrhex(tmp[i]);
}
err.err("lexer",
{line, column-1, line, column, filename},
"invalid utf-8 <"+utf_info+">"
);
++invalid_char;
}
str += tmp;
// may have some problems because not all the unicode takes 2 space
column += 2;
}
return str;
}
token lexer::id_gen() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str += utf8_gen();
} else { // ascii
str += res[ptr++];
++column;
}
}
tok type = get_type(str);
return {
{begin_line, begin_column, line, column, filename},
(type!=tok::null)? type:tok::id, str
};
}
token lexer::num_gen() {
u32 begin_line = line;
u32 begin_column = column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str = "0x";
ptr += 2;
while(ptr<res.size() && is_hex(res[ptr])) {
str += res[ptr++];
}
column += str.length();
// "0x"
if (str.length()<3) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
std::string str = "0o";
ptr += 2;
while(ptr<res.size() && is_oct(res[ptr])) {
str += res[ptr++];
}
bool erfmt = false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
erfmt = true;
str += res[ptr++];
}
column += str.length();
if (str.length()==2 || erfmt) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::num, 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() && is_dec(res[ptr])) {
str += res[ptr++];
}
if (ptr<res.size() && res[ptr]=='.') {
str += 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();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
str += res[ptr++];
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
str += 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();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
column += str.length();
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
token lexer::str_gen() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
const char begin = res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin) {
++column;
if (res[ptr]=='\n') {
column = 0;
++line;
}
if (res[ptr]=='\\' && ptr+1<res.size()) {
++column;
++ptr;
switch(res[ptr]) {
case '0': str += '\0'; break;
case 'a': str += '\a'; break;
case 'b': str += '\b'; break;
case 'e': str += '\033'; break;
case 't': str += '\t'; break;
case 'n': str += '\n'; break;
case 'v': str += '\v'; break;
case 'f': str += '\f'; break;
case 'r': str += '\r'; break;
case '?': str += '\?'; break;
case '\\':str += '\\'; break;
case '\'':str += '\''; break;
case '\"':str += '\"'; break;
default: str += res[ptr];break;
}
if (res[ptr]=='\n') {
column = 0;
++line;
}
continue;
}
str += res[ptr];
}
// check if this string ends with a " or '
if (ptr++>=res.size()) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"get EOF when generating string"
);
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
++column;
// if is not utf8, 1+utf8_hdchk should be 1
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"\'`\' is used for string including one character"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
token lexer::single_opr() {
u32 begin_line = line;
u32 begin_column = column;
std::string str(1, res[ptr]);
++column;
tok type = get_type(str);
if (type==tok::null) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid operator `"+str+"`"
);
}
++ptr;
return {{begin_line, begin_column, line, column, filename}, type, str};
}
token lexer::dots() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = ".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str += "..";
}
ptr += str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
token lexer::calc_opr() {
u32 begin_line = line;
u32 begin_column = column;
// get calculation operator
std::string str(1, res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') {
str += res[ptr++];
}
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
const error& lexer::scan(const std::string& file) {
line = 1;
column = 0;
ptr = 0;
toks = {};
open(file);
while(ptr<res.size()) {
while(ptr<res.size() && skip(res[ptr])) {
// these characters will be ignored, and '\n' will cause ++line
++column;
if (res[ptr++]=='\n') {
++line;
column = 0;
}
}
if (ptr>=res.size()) {
break;
}
if (is_id(res[ptr])) {
toks.push_back(id_gen());
} else if (is_dec(res[ptr])) {
toks.push_back(num_gen());
} else if (is_str(res[ptr])) {
toks.push_back(str_gen());
} else if (is_single_opr(res[ptr])) {
toks.push_back(single_opr());
} else if (res[ptr]=='.') {
toks.push_back(dots());
} else if (is_calc_opr(res[ptr])) {
toks.push_back(calc_opr());
} else if (res[ptr]=='#') {
skip_note();
} else {
err_char();
}
if (invalid_char>10) {
err.err("lexer", "too many invalid characters, stop");
break;
}
}
if (toks.size()) {
// eof token's location is the last token's location
toks.push_back({toks.back().loc, tok::eof, "<eof>"});
} else {
// if token sequence is empty, generate a default location
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
}
res = "";
return err;
}
}

188
src/nasal_lexer.h Normal file
View File

@@ -0,0 +1,188 @@
#pragma once
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include <cstring>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <sys/stat.h>
#include "nasal.h"
#include "nasal_err.h"
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
namespace nasal {
enum class tok:u32 {
null=0, // null token (default token type)
num, // number literal
str, // string literal
id, // identifier
tktrue, // keyword true
tkfalse, // keyword false
rfor, // loop keyword for
forindex, // loop keyword forindex
foreach, // loop keyword foreach
rwhile, // loop keyword while
var, // keyword for definition
func, // keyword for definition of function
brk, // loop keyword break
cont, // loop keyword continue
ret, // function keyword return
rif, // condition expression keyword if
elsif, // condition expression keyword elsif
relse, // condition expression keyword else
tknil, // nil literal
lcurve, // (
rcurve, // )
lbracket, // [
rbracket, // ]
lbrace, // {
rbrace, // }
semi, // ;
opand, // operator and
opor, // operator or
comma, // ,
dot, // .
ellipsis, // ...
quesmark, // ?
colon, // :
add, // operator +
sub, // operator -
mult, // operator *
div, // operator /
floater, // operator ~ and binary operator ~
btand, // bitwise operator &
btor, // bitwise operator |
btxor, // bitwise operator ^
opnot, // operator !
eq, // operator =
addeq, // operator +=
subeq, // operator -=
multeq, // operator *=
diveq, // operator /=
lnkeq, // operator ~=
btandeq, // operator &=
btoreq, // operator |=
btxoreq, // operator ^=
cmpeq, // operator ==
neq, // operator !=
less, // operator <
leq, // operator <=
grt, // operator >
geq, // operator >=
eof // <eof> end of token list
};
struct token {
span loc; // location
tok type; // token type
std::string str; // content
token() = default;
token(const token&) = default;
};
class lexer {
private:
u32 line;
u32 column;
usize ptr;
std::string filename;
std::string res;
error err;
u64 invalid_char;
std::vector<token> toks;
const std::unordered_map<std::string, tok> typetbl {
{"true" ,tok::tktrue },
{"false" ,tok::tkfalse },
{"for" ,tok::rfor },
{"forindex",tok::forindex},
{"foreach" ,tok::foreach },
{"while" ,tok::rwhile },
{"var" ,tok::var },
{"func" ,tok::func },
{"break" ,tok::brk },
{"continue",tok::cont },
{"return" ,tok::ret },
{"if" ,tok::rif },
{"elsif" ,tok::elsif },
{"else" ,tok::relse },
{"nil" ,tok::tknil },
{"(" ,tok::lcurve },
{")" ,tok::rcurve },
{"[" ,tok::lbracket},
{"]" ,tok::rbracket},
{"{" ,tok::lbrace },
{"}" ,tok::rbrace },
{";" ,tok::semi },
{"and" ,tok::opand },
{"or" ,tok::opor },
{"," ,tok::comma },
{"." ,tok::dot },
{"..." ,tok::ellipsis},
{"?" ,tok::quesmark},
{":" ,tok::colon },
{"+" ,tok::add },
{"-" ,tok::sub },
{"*" ,tok::mult },
{"/" ,tok::div },
{"~" ,tok::floater },
{"&" ,tok::btand },
{"|" ,tok::btor },
{"^" ,tok::btxor },
{"!" ,tok::opnot },
{"=" ,tok::eq },
{"+=" ,tok::addeq },
{"-=" ,tok::subeq },
{"*=" ,tok::multeq },
{"/=" ,tok::diveq },
{"~=" ,tok::lnkeq },
{"&=" ,tok::btandeq },
{"|=" ,tok::btoreq },
{"^=" ,tok::btxoreq },
{"==" ,tok::cmpeq },
{"!=" ,tok::neq },
{"<" ,tok::less },
{"<=" ,tok::leq },
{">" ,tok::grt },
{">=" ,tok::geq }
};
tok get_type(const std::string&);
bool skip(char);
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 skip_note();
void err_char();
void open(const std::string&);
std::string utf8_gen();
token id_gen();
token num_gen();
token str_gen();
token single_opr();
token dots();
token calc_opr();
public:
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {}
const error& scan(const std::string&);
const std::vector<token>& result() const {return toks;}
};
}

234
src/nasal_misc.cpp Normal file
View File

@@ -0,0 +1,234 @@
#include "nasal.h"
namespace nasal {
bool is_windows() {
#if defined(_WIN32) || defined(_WIN64)
return true;
#else
return false;
#endif
}
bool is_linux() {
#if defined __linux__
return true;
#else
return false;
#endif
}
bool is_macos() {
#if defined __APPLE__
return true;
#else
return false;
#endif
}
bool is_x86() {
#if defined(__i386__) || defined(_M_IX86)
return true;
#else
return false;
#endif
}
bool is_amd64() {
#if defined(__amd64__) || defined(_M_X64)
return true;
#else
return false;
#endif
}
bool is_x86_64() {
return is_amd64();
}
bool is_arm() {
#if defined(__arm__) || defined(_M_ARM)
return true;
#else
return false;
#endif
}
bool is_aarch64() {
#if defined(__aarch64__) || defined(_M_ARM64)
return true;
#else
return false;
#endif
}
bool is_ia64() {
#if defined(__ia64__)
return true;
#else
return false;
#endif
}
bool is_powerpc() {
#if defined(__powerpc__)
return true;
#else
return false;
#endif
}
bool is_superh() {
#if defined(__sh__)
return true;
#else
return false;
#endif
}
f64 hex2f(const char* str) {
f64 ret = 0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
ret = ret*16+(*str-'0');
} else if ('a'<=*str && *str<='f') {
ret = ret*16+(*str-'a'+10);
} else if ('A'<=*str && *str<='F') {
ret = ret*16+(*str-'A'+10);
} else {
return nan("");
}
}
return ret;
}
f64 oct2f(const char* str) {
f64 ret = 0;
while('0'<=*str && *str<'8') {
ret = ret*8+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret;
}
// we have the same reason not using atof here
// just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char* str) {
f64 ret = 0, negative = 1, num_pow = 0;
while('0'<=*str && *str<='9') {
ret = ret*10+(*str++-'0');
}
if (!*str) {
return ret;
}
if (*str=='.') {
if (!*++str) {
return nan("");
}
num_pow = 0.1;
while('0'<=*str && *str<='9') {
ret += num_pow*(*str++-'0');
num_pow *= 0.1;
}
if (!*str) {
return ret;
}
}
if (*str!='e' && *str!='E') {
return nan("");
}
if (!*++str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative = (*str++=='-'? -1:1);
}
if (!*str) {
return nan("");
}
num_pow = 0;
while('0'<=*str && *str<='9') {
num_pow = num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret*std::pow(10, negative*num_pow);
}
f64 str2num(const char* str) {
bool negative = false;
f64 res = 0;
if (*str=='-' || *str=='+') {
negative = (*str++=='-');
}
if (!*str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res = hex2f(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res = oct2f(str+2);
} else {
res = dec2f(str);
}
return negative? -res:res;
}
i32 utf8_hdchk(const char head) {
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const auto c = static_cast<u8>(head);
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
std::string chrhex(const char c) {
const char hextbl[] = "0123456789abcdef";
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
}
std::string rawstr(const std::string& str, const usize maxlen) {
std::string ret("");
for(auto i : str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
ret += "\\x"+chrhex(i);
continue;
}
switch(i) {
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;
}
}

123
src/nasal_opcode.cpp Normal file
View File

@@ -0,0 +1,123 @@
#include "nasal_opcode.h"
namespace nasal {
const char* opname[] = {
"exit ", "repl ", "intl ", "loadg ",
"loadl ", "loadu ", "pnum ", "pnil ",
"pstr ", "newv ", "newh ", "newf ",
"happ ", "para ", "def ", "dyn ",
"lnot ", "usub ", "bitnot", "bitor ",
"bitxor", "bitand", "add ", "sub ",
"mult ", "div ", "lnk ", "addc ",
"subc ", "multc ", "divc ", "lnkc ",
"addeq ", "subeq ", "muleq ", "diveq ",
"lnkeq ", "bandeq", "boreq ", "bxoreq",
"addeqc", "subeqc", "muleqc", "diveqc",
"lnkeqc", "addecp", "subecp", "mulecp",
"divecp", "lnkecp", "meq ", "eq ",
"neq ", "less ", "leq ", "grt ",
"geq ", "lessc ", "leqc ", "grtc ",
"geqc ", "pop ", "jmp ", "jt ",
"jf ", "cnt ", "findx ", "feach ",
"callg ", "calll ", "upval ", "callv ",
"callvi", "callh ", "callfv", "callfh",
"callb ", "slcbeg", "slcend", "slice ",
"slice2", "mcallg", "mcalll", "mupval",
"mcallv", "mcallh", "ret "
};
void codestream::set(
const f64* num_buff,
const std::string* str_buff,
const nasal_builtin_table* native_table_ptr,
const std::string* file_list) {
nums = num_buff;
strs = str_buff;
natives = native_table_ptr;
files = file_list;
}
void codestream::dump(std::ostream& out) const {
using std::setw;
using std::setfill;
using std::hex;
using std::dec;
auto op = code.op;
auto num = code.num;
out << hex << "0x"
<< setw(6) << setfill('0') << index << " "
<< setw(2) << setfill('0') << static_cast<u32>(op) << " "
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " "
<< setw(2) << setfill('0') << (num&0xff) << " "
<<opname[op]<<" "<<dec;
switch(op) {
case op_addeq: case op_subeq:
case op_muleq: case op_diveq:
case op_lnkeq: case op_meq:
case op_btandeq: case op_btoreq:
case op_btxoreq:
out << hex << "0x" << num << dec << " sp-" << num; break;
case op_addeqc: case op_subeqc:
case op_muleqc:case op_diveqc:
out << hex << "0x" << num << dec
<< " (" << nums[num] << ")"; break;
case op_lnkeqc:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out << hex << "0x" << num << dec
<< " (" << nums[num] << ") sp-1"; break;
case op_lnkecp:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ") sp-1"; break;
case op_addc: case op_subc:
case op_mulc: case op_divc:
case op_lessc: case op_leqc:
case op_grtc: case op_geqc:
case op_pnum:
out << hex << "0x" << num << dec
<< " (" << nums[num] << ")"; break;
case op_callvi: case op_newv:
case op_callfv: case op_repl:
case op_intl: case op_findex:
case op_feach: case op_newf:
case op_jmp: case op_jt:
case op_jf: case op_callg:
case op_mcallg: case op_loadg:
case op_calll: case op_mcalll:
case op_loadl:
out << hex << "0x" << num << dec; break;
case op_callb:
out << hex << "0x" << num << " <" << natives[num].name
<< "@0x" << reinterpret_cast<u64>(natives[num].func)
<< dec << ">"; break;
case op_upval: case op_mupval:
case op_loadu:
out << hex << "0x" << ((num>>16)&0xffff)
<< "[0x" << (num&0xffff) << "]" << dec; break;
case op_happ: case op_pstr:
case op_lnkc: case op_callh:
case op_mcallh: case op_para:
case op_deft: case op_dyn:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
default:
if (files) {
out << hex << "0x" << num << dec;
}
break;
}
if (files) {
out << "(" << files[code.fidx] << ":" << code.line << ")";
}
}
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
ins.dump(out);
return out;
}
}

133
src/nasal_opcode.h Normal file
View File

@@ -0,0 +1,133 @@
#pragma once
#include "nasal.h"
#include "nasal_builtin.h"
#include <iostream>
namespace nasal {
enum op_code_type:u8 {
op_exit, // stop the virtual machine
op_repl, // in repl mode: print value on stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant std::string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq,// &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq,// ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const std::string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // == compare operator
op_neq, // != compare operator
op_less, // < compare operator
op_leq, // <= compare operator
op_grt, // > compare operator
op_geq, // >= compare operator
op_lessc, // < const compare operator
op_leqc, // <= const compare operator
op_grtc, // > const compare operator
op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call native functions
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
};
struct opcode {
u8 op; // opcode
u16 fidx; // source code file index
u32 num; // immediate num
u32 line; // location line of source code
opcode() = default;
opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default;
};
class codestream {
private:
opcode code;
const u32 index;
inline static const f64* nums = nullptr;
inline static const std::string* strs = nullptr;
inline static const nasal_builtin_table* natives = nullptr;
inline static const std::string* files = nullptr;
public:
codestream(const opcode& c, const u32 i): code(c), index(i) {}
static void set(
const f64*, const std::string*,
const nasal_builtin_table*,
const std::string* file_list = nullptr
);
void dump(std::ostream&) const;
};
std::ostream& operator<<(std::ostream&, const codestream&);
extern const char* opname[];
}

1063
src/nasal_parse.cpp Normal file

File diff suppressed because it is too large Load Diff

158
src/nasal_parse.h Normal file
View File

@@ -0,0 +1,158 @@
#pragma once
#include <unordered_map>
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_err.h"
namespace nasal {
class parse {
#define thisspan (toks[ptr].loc)
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
private:
u32 ptr;
u32 in_func; // count function block
u32 in_loop; // count loop block
const token* toks;
code_block* root;
error err;
private:
const std::unordered_map<tok, std::string> tokname {
{tok::rfor ,"for" },
{tok::forindex,"forindex"},
{tok::foreach ,"foreach" },
{tok::rwhile ,"while" },
{tok::var ,"var" },
{tok::func ,"func" },
{tok::brk ,"break" },
{tok::cont ,"continue"},
{tok::ret ,"return" },
{tok::rif ,"if" },
{tok::elsif ,"elsif" },
{tok::relse ,"else" },
{tok::tknil ,"nil" },
{tok::lcurve ,"(" },
{tok::rcurve ,")" },
{tok::lbracket,"[" },
{tok::rbracket,"]" },
{tok::lbrace ,"{" },
{tok::rbrace ,"}" },
{tok::semi ,";" },
{tok::opand ,"and" },
{tok::opor ,"or" },
{tok::comma ,"," },
{tok::dot ,"." },
{tok::ellipsis,"..." },
{tok::quesmark,"?" },
{tok::colon ,":" },
{tok::add ,"+" },
{tok::sub ,"-" },
{tok::mult ,"*" },
{tok::div ,"/" },
{tok::floater ,"~" },
{tok::btand ,"&" },
{tok::btor ,"|" },
{tok::btxor ,"^" },
{tok::opnot ,"!" },
{tok::eq ,"=" },
{tok::addeq ,"+=" },
{tok::subeq ,"-=" },
{tok::multeq ,"*=" },
{tok::diveq ,"/=" },
{tok::lnkeq ,"~=" },
{tok::btandeq ,"&=" },
{tok::btoreq ,"|=" },
{tok::btxoreq ,"^=" },
{tok::cmpeq ,"==" },
{tok::neq ,"!=" },
{tok::less ,"<" },
{tok::leq ,"<=" },
{tok::grt ,">" },
{tok::geq ,">=" }
};
private:
void die(const span&,std::string);
void next();
void match(tok, const char* info=nullptr);
bool lookahead(tok);
bool is_call(tok);
bool check_comma(const tok*);
bool check_tuple();
bool check_func_end(expr*);
bool check_in_curve_multi_definition();
bool check_special_call();
bool need_semi_check(expr*);
void update_location(expr*);
private:
null_expr* null();
nil_expr* nil();
number_literal* num();
string_literal* str();
identifier* id();
bool_literal* bools();
vector_expr* vec();
hash_expr* hash();
hash_pair* pair();
function* func();
void params(function*);
expr* lcurve_expr();
expr* expression();
code_block* expression_block();
expr* calc();
expr* bitwise_or();
expr* bitwise_xor();
expr* bitwise_and();
expr* or_expr();
expr* and_expr();
expr* cmp_expr();
expr* additive_expr();
expr* multive_expr();
unary_operator* unary();
expr* scalar();
call* call_scalar();
call_hash* callh();
call_vector* callv();
call_function* callf();
slice_vector* subvec();
expr* definition();
multi_identifier* incurve_def();
multi_identifier* outcurve_def();
multi_identifier* multi_id();
tuple_expr* multi_scalar();
multi_assign* multi_assignment();
expr* loop();
while_expr* while_loop();
for_expr* for_loop();
forei_expr* forei_loop();
iter_expr* iter_gen();
condition_expr* cond();
continue_expr* continue_expression();
break_expr* break_expression();
return_expr* return_expression();
public:
code_block* tree() {return root;}
// swap root pointer with another pointer(maybe nullptr)
code_block* swap(code_block* another) {
auto res = root;
root = another;
return res;
}
public:
parse(): ptr(0), in_func(0), in_loop(0), toks(nullptr), root(nullptr) {}
~parse() {delete root;}
const error& compile(const lexer&);
static void easter_egg();
};
}

468
src/nasal_vm.cpp Normal file
View File

@@ -0,0 +1,468 @@
#include "nasal_vm.h"
namespace nasal {
void vm::init(
const std::vector<std::string>& strs,
const std::vector<f64>& nums,
const std::vector<nasal_builtin_table>& natives,
const std::vector<opcode>& code,
const std::unordered_map<std::string, i32>& global_symbol,
const std::vector<std::string>& filenames,
const std::vector<std::string>& argv
) {
cnum = nums.data();
cstr = strs.data();
bytecode = code.data();
files = filenames.data();
global_size = global_symbol.size();
/* set native functions */
native = natives;
/* set context and global */
if (!is_repl_mode || first_exec_flag) {
context_and_global_init();
first_exec_flag = false;
}
/* init gc */
ngc.set(&ctx, global, global_size);
ngc.init(strs, argv);
/* init vm globals */
auto map_instance = ngc.alloc(vm_map);
global[global_symbol.at("globals")] = map_instance;
for(const auto& i : global_symbol) {
map_instance.map().mapper[i.first] = global+i.second;
}
/* init vm arg */
auto arg_instance = ngc.alloc(vm_vec);
global[global_symbol.at("arg")] = arg_instance;
arg_instance.vec().elems = ngc.env_argv;
}
void vm::context_and_global_init() {
/* set canary and program counter */
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.funcr = nil;
ctx.upvalr = nil;
/* set canary = stack[STACK_DEPTH-1] */
ctx.canary = ctx.stack+STACK_DEPTH-1;
/* nothing is on stack */
ctx.top = ctx.stack - 1;
/* clear main stack and global */
for(u32 i = 0; i<STACK_DEPTH; ++i) {
ctx.stack[i] = nil;
global[i] = nil;
}
}
void vm::valinfo(var& val) {
const auto p = reinterpret_cast<u64>(val.val.gcobj);
switch(val.type) {
case vm_none: std::clog << "| null |"; break;
case vm_ret: std::clog << "| pc | 0x" << std::hex
<< val.ret() << std::dec; break;
case vm_addr: std::clog << "| addr | 0x" << std::hex
<< reinterpret_cast<u64>(val.addr())
<< std::dec; break;
case vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
case vm_nil: std::clog << "| nil |"; break;
case vm_num: std::clog << "| num | " << val.num(); break;
case vm_str: std::clog << "| str | <0x" << std::hex << p
<< "> " << rawstr(val.str(), 16)
<< std::dec; break;
case vm_func: std::clog << "| func | <0x" << std::hex << p
<< "> entry:0x" << val.func().entry
<< std::dec; break;
case vm_upval:std::clog << "| upval| <0x" << std::hex << p
<< std::dec << "> [" << val.upval().size
<< " val]"; break;
case vm_vec: std::clog << "| vec | <0x" << std::hex << p
<< std::dec << "> [" << val.vec().size()
<< " val]"; break;
case vm_hash: std::clog << "| hash | <0x" << std::hex << p
<< std::dec << "> {" << val.hash().size()
<< " val}"; break;
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
<< "> obj:0x"
<< reinterpret_cast<u64>(val.obj().ptr)
<< std::dec; break;
case vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
case vm_map: std::clog << "| nmspc| <0x" << std::hex << p
<< std::dec << "> namespace ["
<< val.map().mapper.size() << " val]"; break;
default: std::clog << "| err | <0x" << std::hex << p
<< std::dec << "> unknown object"; break;
}
std::clog << "\n";
}
void vm::traceback() {
var* bottom = ctx.stack;
var* top = ctx.top;
std::stack<u32> ret;
for(var* i = bottom; i<=top; ++i) {
if (i->type==vm_ret && i->ret()!=0) {
ret.push(i->ret());
}
}
ret.push(ctx.pc); // store the position program crashed
std::clog << "trace back ("
<< (ngc.cort? "coroutine":"main")
<< ")\n";
codestream::set(cnum, cstr, native.data(), files);
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
if ((p = ret.top())==prev) {
++same;
continue;
}
if (same) {
std::clog << " 0x" << std::hex
<< std::setw(6) << std::setfill('0')
<< prev << std::dec << " "
<< same << " same call(s)\n";
}
same = 0;
std::clog << " " << codestream(bytecode[p], p) << "\n";
}
// the first called place has no same calls
}
void vm::stackinfo(const u32 limit = 10) {
var* top = ctx.top;
var* bottom = ctx.stack;
std::clog << "stack (0x" << std::hex << reinterpret_cast<u64>(bottom);
std::clog << std::dec << ", limit " << limit << ", total ";
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
std::clog << " 0x" << std::hex
<< std::setw(6) << std::setfill('0')
<< static_cast<u64>(top-bottom) << std::dec
<< " ";
valinfo(top[0]);
}
}
void vm::reginfo() {
std::clog << "registers (" << (ngc.cort? "coroutine":"main")
<< ")\n" << std::hex
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
<< " [global] | addr | 0x"
<< reinterpret_cast<u64>(global) << "\n"
<< " [local ] | addr | 0x"
<< reinterpret_cast<u64>(ctx.localr) << "\n"
<< " [memr ] | addr | 0x"
<< reinterpret_cast<u64>(ctx.memr) << "\n"
<< " [canary] | addr | 0x"
<< reinterpret_cast<u64>(ctx.canary) << "\n"
<< " [top ] | addr | 0x"
<< reinterpret_cast<u64>(ctx.top) << "\n"
<< std::dec;
std::clog << " [funcr ] "; valinfo(ctx.funcr);
std::clog << " [upval ] "; valinfo(ctx.upvalr);
}
void vm::gstate() {
if (!global_size || global[0].type==vm_none) {
return;
}
std::clog << "global (0x" << std::hex
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
for(usize i = 0; i<global_size; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << i << std::dec
<< " ";
valinfo(global[i]);
}
}
void vm::lstate() {
if (!ctx.localr || !ctx.funcr.func().lsize) {
return;
}
const u32 lsize = ctx.funcr.func().lsize;
std::clog << "local (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
<< ">)\n" << std::dec;
for(u32 i = 0; i<lsize; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << i << std::dec
<< " ";
valinfo(ctx.localr[i]);
}
}
void vm::ustate() {
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
return;
}
std::clog << "upvalue\n";
auto& upval = ctx.funcr.func().upval;
for(u32 i = 0; i<upval.size(); ++i) {
std::clog << " -> upval[" << i << "]:\n";
auto& uv = upval[i].upval();
for(u32 j = 0; j<uv.size; ++j) {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << j << std::dec
<< " ";
valinfo(uv[j]);
}
}
}
void vm::detail() {
reginfo();
gstate();
lstate();
ustate();
}
void vm::die(const std::string& str) {
std::cerr << "[vm] error: " << str << "\n";
traceback();
stackinfo();
// show verbose crash info
if (verbose) {
detail();
}
if (!ngc.cort) {
// in main context, exit directly
std::exit(1);
} else {
// in coroutine, shut down the coroutine and return to main context
ctx.pc = 0; // mark coroutine 'dead'
ngc.ctxreserve(); // switch context to main
ctx.top[0] = nil; // generate return value 'nil'
}
}
void vm::run(
const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv
) {
init(gen.strs(), gen.nums(), gen.natives(),
gen.codes(), gen.globals(), linker.get_file_list(), argv);
#ifndef _MSC_VER
// using labels as values/computed goto
const void* oprs[] = {
&&vmexit, &&repl, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil,
&&pstr, &&newv, &&newh, &&newf,
&&happ, &&para, &&deft, &&dyn,
&&lnot, &&usub, &&bnot, &&btor,
&&btxor, &&btand, &&add, &&sub,
&&mul, &&div, &&lnk, &&addc,
&&subc, &&mulc, &&divc, &&lnkc,
&&addeq, &&subeq, &&muleq, &&diveq,
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
&&divecp, &&lnkecp, &&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.codes()) {
code.push_back(oprs[i.op]);
imm.push_back(i.num);
}
// goto the first operand
goto *code[ctx.pc];
#else
typedef void (vm::*nafunc)();
const nafunc oprs[] = {
nullptr, &vm::o_repl,
&vm::o_intl, &vm::o_loadg,
&vm::o_loadl, &vm::o_loadu,
&vm::o_pnum, &vm::o_pnil,
&vm::o_pstr, &vm::o_newv,
&vm::o_newh, &vm::o_newf,
&vm::o_happ, &vm::o_para,
&vm::o_deft, &vm::o_dyn,
&vm::o_lnot, &vm::o_usub,
&vm::o_bnot, &vm::o_btor,
&vm::o_btxor, &vm::o_btand,
&vm::o_add, &vm::o_sub,
&vm::o_mul, &vm::o_div,
&vm::o_lnk, &vm::o_addc,
&vm::o_subc, &vm::o_mulc,
&vm::o_divc, &vm::o_lnkc,
&vm::o_addeq, &vm::o_subeq,
&vm::o_muleq, &vm::o_diveq,
&vm::o_lnkeq, &vm::o_bandeq,
&vm::o_boreq, &vm::o_bxoreq,
&vm::o_addeqc, &vm::o_subeqc,
&vm::o_muleqc, &vm::o_diveqc,
&vm::o_lnkeqc, &vm::o_addecp,
&vm::o_subecp, &vm::o_mulecp,
&vm::o_divecp, &vm::o_lnkecp,
&vm::o_meq, &vm::o_eq,
&vm::o_neq, &vm::o_less,
&vm::o_leq, &vm::o_grt,
&vm::o_geq, &vm::o_lessc,
&vm::o_leqc, &vm::o_grtc,
&vm::o_geqc, &vm::o_pop,
&vm::o_jmp, &vm::o_jt,
&vm::o_jf, &vm::o_cnt,
&vm::o_findex, &vm::o_feach,
&vm::o_callg, &vm::o_calll,
&vm::o_upval, &vm::o_callv,
&vm::o_callvi, &vm::o_callh,
&vm::o_callfv, &vm::o_callfh,
&vm::o_callb, &vm::o_slcbeg,
&vm::o_slcend, &vm::o_slc,
&vm::o_slc2, &vm::o_mcallg,
&vm::o_mcalll, &vm::o_mupval,
&vm::o_mcallv, &vm::o_mcallh,
&vm::o_ret
};
std::vector<nafunc> code;
for(auto& i : gen.codes()) {
code.push_back(oprs[i.op]);
imm.push_back(i.num);
}
while(code[ctx.pc]) {
(this->*code[ctx.pc])();
if (ctx.top>=ctx.canary) {
die("stack overflow");
}
++ctx.pc;
}
#endif
vmexit:
if (verbose) {
ngc.info();
}
imm.clear();
if (!is_repl_mode) {
ngc.clear();
}
return;
#ifndef _MSC_VER
// may cause stackoverflow
#define exec_check(op) {\
op();\
if (ctx.top<ctx.canary)\
goto *code[++ctx.pc];\
die("stack overflow");\
goto *code[++ctx.pc];\
}
// do not cause stackoverflow
#define exec_nodie(op) {\
op();\
goto *code[++ctx.pc];\
}
repl: exec_nodie(o_repl ); // 0
intl: exec_nodie(o_intl ); // -0
loadg: exec_nodie(o_loadg ); // -1
loadl: exec_nodie(o_loadl ); // -1
loadu: exec_nodie(o_loadu ); // -1
pnum: exec_check(o_pnum ); // +1
pnil: exec_check(o_pnil ); // +1
pstr: exec_check(o_pstr ); // +1
newv: exec_check(o_newv ); // +1-imm[pc]
newh: exec_check(o_newh ); // +1
newf: exec_check(o_newf ); // +1
happ: exec_nodie(o_happ ); // -1
para: exec_nodie(o_para ); // -0
deft: exec_nodie(o_deft ); // -1
dyn: exec_nodie(o_dyn ); // -0
lnot: exec_nodie(o_lnot ); // -0
usub: exec_nodie(o_usub ); // -0
bnot: exec_nodie(o_bnot ); // -0
btor: exec_nodie(o_btor ); // -1
btxor: exec_nodie(o_btxor ); // -1
btand: exec_nodie(o_btand ); // -1
add: exec_nodie(o_add ); // -1
sub: exec_nodie(o_sub ); // -1
mul: exec_nodie(o_mul ); // -1
div: exec_nodie(o_div ); // -1
lnk: exec_nodie(o_lnk ); // -1
addc: exec_nodie(o_addc ); // -0
subc: exec_nodie(o_subc ); // -0
mulc: exec_nodie(o_mulc ); // -0
divc: exec_nodie(o_divc ); // -0
lnkc: exec_nodie(o_lnkc ); // -0
addeq: exec_nodie(o_addeq ); // -1
subeq: exec_nodie(o_subeq ); // -1
muleq: exec_nodie(o_muleq ); // -1
diveq: exec_nodie(o_diveq ); // -1
lnkeq: exec_nodie(o_lnkeq ); // -1
bandeq: exec_nodie(o_bandeq); // -1
boreq: exec_nodie(o_boreq ); // -1
bxoreq: exec_nodie(o_bxoreq); // -1
addeqc: exec_nodie(o_addeqc); // -0
subeqc: exec_nodie(o_subeqc); // -0
muleqc: exec_nodie(o_muleqc); // -0
diveqc: exec_nodie(o_diveqc); // -0
lnkeqc: exec_nodie(o_lnkeqc); // -0
addecp: exec_nodie(o_addecp); // -1
subecp: exec_nodie(o_subecp); // -1
mulecp: exec_nodie(o_mulecp); // -1
divecp: exec_nodie(o_divecp); // -1
lnkecp: exec_nodie(o_lnkecp); // -1
meq: exec_nodie(o_meq ); // -1
eq: exec_nodie(o_eq ); // -1
neq: exec_nodie(o_neq ); // -1
less: exec_nodie(o_less ); // -1
leq: exec_nodie(o_leq ); // -1
grt: exec_nodie(o_grt ); // -1
geq: exec_nodie(o_geq ); // -1
lessc: exec_nodie(o_lessc ); // -0
leqc: exec_nodie(o_leqc ); // -0
grtc: exec_nodie(o_grtc ); // -0
geqc: exec_nodie(o_geqc ); // -0
pop: exec_nodie(o_pop ); // -1
jmp: exec_nodie(o_jmp ); // -0
jt: exec_nodie(o_jt ); // -0
jf: exec_nodie(o_jf ); // -1
cnt: exec_nodie(o_cnt ); // -0
findex: exec_check(o_findex); // +1
feach: exec_check(o_feach ); // +1
callg: exec_check(o_callg ); // +1
calll: exec_check(o_calll ); // +1
upval: exec_check(o_upval ); // +1
callv: exec_nodie(o_callv ); // -0
callvi: exec_nodie(o_callvi); // -0
callh: exec_nodie(o_callh ); // -0
callfv: exec_nodie(o_callfv); // check in the function
callfh: exec_nodie(o_callfh); // check in the function
callb: exec_nodie(o_callb ); // -0
slcbeg: exec_check(o_slcbeg); // +1
slcend: exec_nodie(o_slcend); // -1
slc: exec_nodie(o_slc ); // -1
slc2: exec_nodie(o_slc2 ); // -2
mcallg: exec_check(o_mcallg); // +1
mcalll: exec_check(o_mcalll); // +1
mupval: exec_check(o_mupval); // +1
mcallv: exec_nodie(o_mcallv); // -0
mcallh: exec_nodie(o_mcallh); // -0
ret: exec_nodie(o_ret ); // -2
#endif
}
}

991
src/nasal_vm.h Normal file
View File

@@ -0,0 +1,991 @@
#pragma once
#include <iomanip>
#include <stack>
#include "nasal_import.h"
#include "nasal_gc.h"
#include "nasal_codegen.h"
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
namespace nasal {
class vm {
protected:
/* registers of vm */
context ctx;
/* constants */
const f64* cnum = nullptr; // constant numbers
const std::string* cstr = nullptr; // constant symbols and strings
std::vector<u32> imm; // immediate number table
std::vector<nasal_builtin_table> native;
/* garbage collector */
gc ngc;
/* main stack */
var* global = nullptr;
usize global_size = 0;
/* values used for debugger */
const std::string* files = nullptr; // file name list
const opcode* bytecode = nullptr; // bytecode buffer address
/* variables for repl mode */
bool is_repl_mode = false;
bool first_exec_flag = true;
bool allow_repl_output = false;
/* vm initializing function */
void init(
const std::vector<std::string>&,
const std::vector<f64>&,
const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&,
const std::unordered_map<std::string, i32>&,
const std::vector<std::string>&,
const std::vector<std::string>&
);
void context_and_global_init();
/* debug functions */
bool verbose = false;
void valinfo(var&);
void traceback();
void stackinfo(const u32);
void reginfo();
void gstate();
void lstate();
void ustate();
void detail();
void die(const std::string&);
/* vm calculation functions*/
inline bool cond(var&);
/* vm operands */
inline void o_repl();
inline void o_intl();
inline void o_loadg();
inline void o_loadl();
inline void o_loadu();
inline void o_pnum();
inline void o_pnil();
inline void o_pstr();
inline void o_newv();
inline void o_newh();
inline void o_newf();
inline void o_happ();
inline void o_para();
inline void o_deft();
inline void o_dyn();
inline void o_lnot();
inline void o_usub();
inline void o_bnot();
inline void o_btor();
inline void o_btxor();
inline void o_btand();
inline void o_add();
inline void o_sub();
inline void o_mul();
inline void o_div();
inline void o_lnk();
inline void o_addc();
inline void o_subc();
inline void o_mulc();
inline void o_divc();
inline void o_lnkc();
inline void o_addeq();
inline void o_subeq();
inline void o_muleq();
inline void o_diveq();
inline void o_lnkeq();
inline void o_bandeq();
inline void o_boreq();
inline void o_bxoreq();
inline void o_addeqc();
inline void o_subeqc();
inline void o_muleqc();
inline void o_diveqc();
inline void o_lnkeqc();
inline void o_addecp();
inline void o_subecp();
inline void o_mulecp();
inline void o_divecp();
inline void o_lnkecp();
inline void o_meq();
inline void o_eq();
inline void o_neq();
inline void o_less();
inline void o_leq();
inline void o_grt();
inline void o_geq();
inline void o_lessc();
inline void o_leqc();
inline void o_grtc();
inline void o_geqc();
inline void o_pop();
inline void o_jmp();
inline void o_jt();
inline void o_jf();
inline void o_cnt();
inline void o_findex();
inline void o_feach();
inline void o_callg();
inline void o_calll();
inline void o_upval();
inline void o_callv();
inline void o_callvi();
inline void o_callh();
inline void o_callfv();
inline void o_callfh();
inline void o_callb();
inline void o_slcbeg();
inline void o_slcend();
inline void o_slc();
inline void o_slc2();
inline void o_mcallg();
inline void o_mcalll();
inline void o_mupval();
inline void o_mcallv();
inline void o_mcallh();
inline void o_ret();
public:
/* constructor of vm instance */
vm() {
ctx.stack = new var[STACK_DEPTH];
global = new var[STACK_DEPTH];
}
~vm() {
delete[] ctx.stack;
delete[] global;
}
/* execution entry */
void run(
const codegen&,
const linker&,
const std::vector<std::string>&
);
/* set detail report info flag */
void set_detail_report_info(bool flag) {verbose = flag;}
/* set repl mode flag */
void set_repl_mode_flag(bool flag) {is_repl_mode = flag;}
/* set repl output flag */
void set_allow_repl_output_flag(bool flag) {allow_repl_output = flag;}
};
inline bool vm::cond(var& val) {
if (val.type==vm_num) {
return val.num();
} else if (val.type==vm_str) {
const f64 num = str2num(val.str().c_str());
return std::isnan(num)? !val.str().empty():num;
}
return false;
}
inline void vm::o_repl() {
// reserved for repl mode stack top value output
if (allow_repl_output) {
std::cout << ctx.top[0] << "\n";
}
}
inline void vm::o_intl() {
ctx.top[0].func().local.resize(imm[ctx.pc], nil);
ctx.top[0].func().lsize = imm[ctx.pc];
}
inline void vm::o_loadg() {
global[imm[ctx.pc]] = (ctx.top--)[0];
}
inline void vm::o_loadl() {
ctx.localr[imm[ctx.pc]] = (ctx.top--)[0];
}
inline void vm::o_loadu() {
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
}
inline void vm::o_pnum() {
(++ctx.top)[0] = var::num(cnum[imm[ctx.pc]]);
}
inline void vm::o_pnil() {
(++ctx.top)[0] = nil;
}
inline void vm::o_pstr() {
(++ctx.top)[0] = ngc.strs[imm[ctx.pc]];
}
inline void vm::o_newv() {
var newv = ngc.alloc(vm_vec);
auto& vec = newv.vec().elems;
vec.resize(imm[ctx.pc]);
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
ctx.top = ctx.top-imm[ctx.pc]+1;
for(u32 i = 0; i<imm[ctx.pc]; ++i) {
vec[i] = ctx.top[i];
}
ctx.top[0] = newv;
}
inline void vm::o_newh() {
(++ctx.top)[0] = ngc.alloc(vm_hash);
}
inline void vm::o_newf() {
(++ctx.top)[0] = ngc.alloc(vm_func);
auto& func = ctx.top[0].func();
func.entry = imm[ctx.pc];
func.psize = 1;
/* this means you create a new function in local scope */
if (ctx.localr) {
func.upval = ctx.funcr.func().upval;
// function created in the same local scope shares one closure
// so this size & stk setting has no problem
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
upval.upval().size = ctx.funcr.func().lsize;
upval.upval().stk = ctx.localr;
func.upval.push_back(upval);
ctx.upvalr = upval;
}
}
inline void vm::o_happ() {
ctx.top[-1].hash().elems[cstr[imm[ctx.pc]]] = ctx.top[0];
--ctx.top;
}
inline void vm::o_para() {
auto& func = ctx.top[0].func();
// func->size has 1 place reserved for "me"
func.keys[imm[ctx.pc]] = func.psize;
func.local[func.psize++] = var::none();
}
inline void vm::o_deft() {
var val = ctx.top[0];
auto& func = (--ctx.top)[0].func();
// func->size has 1 place reserved for "me"
func.keys[imm[ctx.pc]] = func.psize;
func.local[func.psize++] = val;
}
inline void vm::o_dyn() {
ctx.top[0].func().dpara = imm[ctx.pc];
}
inline void vm::o_lnot() {
var val = ctx.top[0];
switch(val.type) {
case vm_nil: ctx.top[0] = one; break;
case vm_num: ctx.top[0] = val.num()? zero:one; break;
case vm_str: {
const f64 num = str2num(val.str().c_str());
if (std::isnan(num)) {
ctx.top[0] = var::num(static_cast<f64>(val.str().empty()));
} else {
ctx.top[0] = num? zero:one;
}
} break;
default: die("incorrect value type"); return;
}
}
inline void vm::o_usub() {
ctx.top[0] = var::num(-ctx.top[0].tonum());
}
inline void vm::o_bnot() {
ctx.top[0] = var::num(~static_cast<int32_t>(ctx.top[0].num()));
}
inline void vm::o_btor() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())|
static_cast<i32>(ctx.top[0].tonum())
);
--ctx.top;
}
inline void vm::o_btxor() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())^
static_cast<i32>(ctx.top[0].tonum())
);
--ctx.top;
}
inline void vm::o_btand() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())&
static_cast<i32>(ctx.top[0].tonum())
);
--ctx.top;
}
#define op_calc(type)\
ctx.top[-1] = var::num(ctx.top[-1].tonum() type ctx.top[0].tonum());\
--ctx.top;
inline void vm::o_add() {op_calc(+);}
inline void vm::o_sub() {op_calc(-);}
inline void vm::o_mul() {op_calc(*);}
inline void vm::o_div() {op_calc(/);}
inline void vm::o_lnk() {
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
ngc.temp = ngc.alloc(vm_vec);
for(auto i : ctx.top[-1].vec().elems) {
ngc.temp.vec().elems.push_back(i);
}
for(auto i : ctx.top[0].vec().elems) {
ngc.temp.vec().elems.push_back(i);
}
ctx.top[-1] = ngc.temp;
ngc.temp = nil;
--ctx.top;
return;
}
ctx.top[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
--ctx.top;
}
#define op_calc_const(type)\
ctx.top[0] = var::num(ctx.top[0].tonum() type cnum[imm[ctx.pc]]);
inline void vm::o_addc() {op_calc_const(+);}
inline void vm::o_subc() {op_calc_const(-);}
inline void vm::o_mulc() {op_calc_const(*);}
inline void vm::o_divc() {op_calc_const(/);}
inline void vm::o_lnkc() {
ctx.top[0] = ngc.newstr(ctx.top[0].tostr()+cstr[imm[ctx.pc]]);
}
// top[0] stores the value of memr[0], to avoid being garbage-collected
// so when the calculation ends, top-=1, then top-=imm[pc]
// because this return value is meaningless if on stack when imm[pc] = 1
// like this: func{a+=c;}(); the result of 'a+c' will no be used later, imm[pc] = 1
// but if b+=a+=c; the result of 'a+c' will be used later, imm[pc] = 0
#define op_calc_eq(type)\
ctx.top[-1] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type ctx.top[-1].tonum());\
ctx.memr = nullptr;\
ctx.top -= imm[ctx.pc]+1;
inline void vm::o_addeq() {op_calc_eq(+);}
inline void vm::o_subeq() {op_calc_eq(-);}
inline void vm::o_muleq() {op_calc_eq(*);}
inline void vm::o_diveq() {op_calc_eq(/);}
inline void vm::o_lnkeq() {
ctx.top[-1] = ctx.memr[0] = ngc.newstr(
ctx.memr[0].tostr()+ctx.top[-1].tostr()
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
}
inline void vm::o_bandeq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())&
static_cast<i32>(ctx.top[-1].tonum())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
}
inline void vm::o_boreq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())|
static_cast<i32>(ctx.top[-1].tonum())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
}
inline void vm::o_bxoreq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())^
static_cast<i32>(ctx.top[-1].tonum())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
}
// top[0] stores the value of memr[0], to avoid being garbage-collected
// so when the calculation ends, top-=imm[pc]>>31
// because this return value is meaningless if on stack when imm[pc]>>31=1
// like this: func{a+=1;}(); the result of 'a+1' will no be used later, imm[pc]>>31=1
// but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0
#define op_calc_eq_const(type)\
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
ctx.memr = nullptr;
inline void vm::o_addeqc() {op_calc_eq_const(+);}
inline void vm::o_subeqc() {op_calc_eq_const(-);}
inline void vm::o_muleqc() {op_calc_eq_const(*);}
inline void vm::o_diveqc() {op_calc_eq_const(/);}
inline void vm::o_lnkeqc() {
ctx.top[0] = ctx.memr[0] = ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]);
ctx.memr = nullptr;
}
#define op_calc_eq_const_and_pop(type)\
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
ctx.memr = nullptr;\
--ctx.top;
inline void vm::o_addecp() {op_calc_eq_const_and_pop(+);}
inline void vm::o_subecp() {op_calc_eq_const_and_pop(-);}
inline void vm::o_mulecp() {op_calc_eq_const_and_pop(*);}
inline void vm::o_divecp() {op_calc_eq_const_and_pop(/);}
inline void vm::o_lnkecp() {
ctx.top[0] = ctx.memr[0] = ngc.newstr(
ctx.memr[0].tostr()+cstr[imm[ctx.pc]]
);
ctx.memr = nullptr;
--ctx.top;
}
inline void vm::o_meq() {
// pop old memr[0] and replace it
// the reason why we should get memr and push the old value on stack
// is that when lnkeq/lnkeqc is called, there will be
// a new gc object vm_str which is returned by gc::alloc
// this may cause gc, so we should temporarily put it on stack
ctx.memr[0] = ctx.top[-1];
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
}
inline void vm::o_eq() {
var val2 = ctx.top[0];
var val1 = (--ctx.top)[0];
if (val1.type==vm_nil && val2.type==vm_nil) {
ctx.top[0] = one;
} else if (val1.type==vm_str && val2.type==vm_str) {
ctx.top[0] = (val1.str()==val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
ctx.top[0] = (val1.tonum()==val2.tonum())? one:zero;
} else {
ctx.top[0] = (val1==val2)? one:zero;
}
}
inline void vm::o_neq() {
var val2 = ctx.top[0];
var val1 = (--ctx.top)[0];
if (val1.type==vm_nil && val2.type==vm_nil) {
ctx.top[0] = zero;
} else if (val1.type==vm_str && val2.type==vm_str) {
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
ctx.top[0] = (val1.tonum()!=val2.tonum())? one:zero;
} else {
ctx.top[0] = (val1!=val2)? one:zero;
}
}
#define op_cmp(type)\
--ctx.top;\
ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero;
inline void vm::o_less() {op_cmp(<);}
inline void vm::o_leq() {op_cmp(<=);}
inline void vm::o_grt() {op_cmp(>);}
inline void vm::o_geq() {op_cmp(>=);}
#define op_cmp_const(type)\
ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero;
inline void vm::o_lessc() {op_cmp_const(<);}
inline void vm::o_leqc() {op_cmp_const(<=);}
inline void vm::o_grtc() {op_cmp_const(>);}
inline void vm::o_geqc() {op_cmp_const(>=);}
inline void vm::o_pop() {
--ctx.top;
}
inline void vm::o_jmp() {
ctx.pc = imm[ctx.pc]-1;
}
inline void vm::o_jt() {
// jump true needs to reserve the result on stack
// because conditional expression in nasal has return value
if (cond(ctx.top[0])) {
ctx.pc = imm[ctx.pc]-1;
}
}
inline void vm::o_jf() {
// jump false doesn't need to reserve result
if (!cond(ctx.top[0])) {
ctx.pc = imm[ctx.pc]-1;
}
--ctx.top;
}
inline void vm::o_cnt() {
if (ctx.top[0].type!=vm_vec) {
die("must use vector in forindex/foreach");
return;
}
(++ctx.top)[0] = var::cnt(-1);
}
inline void vm::o_findex() {
if ((usize)(++ctx.top[0].cnt())>=ctx.top[-1].vec().size()) {
ctx.pc = imm[ctx.pc]-1;
return;
}
ctx.top[1] = var::num(ctx.top[0].cnt());
++ctx.top;
}
inline void vm::o_feach() {
auto& ref = ctx.top[-1].vec().elems;
if ((usize)(++ctx.top[0].cnt())>=ref.size()) {
ctx.pc = imm[ctx.pc]-1;
return;
}
ctx.top[1] = ref[ctx.top[0].cnt()];
++ctx.top;
}
inline void vm::o_callg() {
// get main stack directly
(++ctx.top)[0] = global[imm[ctx.pc]];
}
inline void vm::o_calll() {
(++ctx.top)[0] = ctx.localr[imm[ctx.pc]];
}
inline void vm::o_upval() {
(++ctx.top)[0] = ctx.funcr.func()
.upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff];
}
inline void vm::o_callv() {
var val = ctx.top[0];
var vec = (--ctx.top)[0];
if (vec.type==vm_vec) {
ctx.top[0] = vec.vec().get_val(val.tonum());
if (ctx.top[0].type==vm_none) {
die("out of range:"+std::to_string(val.tonum()));
return;
}
} else if (vec.type==vm_hash) {
if (val.type!=vm_str) {
die("must use string as the key");
return;
}
ctx.top[0] = vec.hash().get_val(val.str());
if (ctx.top[0].type==vm_none) {
die("cannot find member \""+val.str()+"\"");
return;
} else if (ctx.top[0].type==vm_func) {
ctx.top[0].func().local[0] = val; // 'me'
}
} else if (vec.type==vm_str) {
auto& str = vec.str();
i32 num = val.tonum();
i32 len = str.length();
if (num<-len || num>=len) {
die("out of range:"+std::to_string(val.tonum()));
return;
}
ctx.top[0] = var::num(
static_cast<f64>(static_cast<u8>(str[num>=0? num:num+len]))
);
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
die("must use string as the key");
return;
}
ctx.top[0] = vec.map().get_val(val.str());
if (ctx.top[0].type==vm_none) {
die("cannot find symbol \""+val.str()+"\"");
return;
}
} else {
die("must call a vector/hash/string");
return;
}
}
inline void vm::o_callvi() {
var val = ctx.top[0];
if (val.type!=vm_vec) {
die("must use a vector");
return;
}
// cannot use operator[],because this may cause overflow
(++ctx.top)[0] = val.vec().get_val(imm[ctx.pc]);
if (ctx.top[0].type==vm_none) {
die("out of range:"+std::to_string(imm[ctx.pc]));
return;
}
}
inline void vm::o_callh() {
var val = ctx.top[0];
if (val.type!=vm_hash && val.type!=vm_map) {
die("must call a hash");
return;
}
const auto& str = cstr[imm[ctx.pc]];
if (val.type==vm_hash) {
ctx.top[0] = val.hash().get_val(str);
} else {
ctx.top[0] = val.map().get_val(str);
}
if (ctx.top[0].type==vm_none) {
val.type==vm_hash?
die("member \"" + str + "\" does not exist"):
die("cannot find symbol \"" + str + "\"");
return;
} else if (ctx.top[0].type==vm_func) {
ctx.top[0].func().local[0] = val; // 'me'
}
}
inline void vm::o_callfv() {
u32 argc = imm[ctx.pc]; // arguments counter
var* local = ctx.top-argc+1; // arguments begin address
if (local[-1].type!=vm_func) {
die("must call a function");
return;
}
auto& func = local[-1].func();
// swap funcr with local[-1]
var tmp = local[-1];
local[-1] = ctx.funcr;
ctx.funcr = tmp;
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top-argc+func.lsize+3>=ctx.canary) {
die("stack overflow");
return;
}
// parameter size is func->psize-1, 1 is reserved for "me"
u32 psize = func.psize-1;
if (argc<psize && func.local[argc+1].type==vm_none) {
die("lack argument(s)");
return;
}
var dynamic = nil;
if (func.dpara>=0) { // load dynamic arguments
dynamic = ngc.alloc(vm_vec);
for(u32 i = psize; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
} else if (psize<argc) {
// load arguments to "arg", located at stack+1
dynamic = ngc.alloc(vm_vec);
for(u32 i = psize; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
}
// should reset stack top after allocating vector
// because this may cause gc
// then all the available values the vector needs
// are all outside the stack top and may be
// collected incorrectly
ctx.top = local+func.lsize;
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC
for(u32 i = min_size; i>=1; --i) { // load arguments
local[i] = local[i-1];
}
local[0] = func.local[0];// load "me"
// load local scope & default arguments
for(u32 i = min_size+1; i<func.lsize; ++i) {
local[i] = func.local[i];
}
local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr);
(++ctx.top)[0] = var::ret(ctx.pc);
ctx.pc = func.entry-1;
ctx.localr = local;
ctx.upvalr = nil;
}
inline void vm::o_callfh() {
auto& hash = ctx.top[0].hash().elems;
if (ctx.top[-1].type!=vm_func) {
die("must call a function");
return;
}
auto& func = ctx.top[-1].func();
var tmp = ctx.top[-1];
ctx.top[-1] = ctx.funcr;
ctx.funcr = tmp;
// top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top+func.lsize+2>= ctx.canary) {
die("stack overflow");
return;
}
if (func.dpara>=0) {
die("special call cannot use dynamic argument");
return;
}
var* local = ctx.top;
ctx.top += func.lsize;
for(u32 i = 0; i<func.lsize; ++i) {
local[i] = func.local[i];
}
for(const auto& i : func.keys) {
auto& key = cstr[i.first];
if (hash.count(key)) {
local[i.second] = hash[key];
} else if (local[i.second].type==vm_none) {
die("lack argument(s): \""+key+"\"");
return;
}
}
ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr);
(++ctx.top)[0] = var::ret(ctx.pc); // rewrite top with vm_ret
ctx.pc=func.entry-1;
ctx.localr = local;
ctx.upvalr = nil;
}
inline void vm::o_callb() {
// reserve place for native function return,
// this code is written for coroutine
(++ctx.top)[0] = nil;
// if running a native function about coroutine
// (top) will be set to another context.top, instead of main_context.top
var tmp = (*native[imm[ctx.pc]].func)(ctx.localr, ngc);
// so we use tmp variable to store this return value
// and set it to top[0] later
ctx.top[0] = tmp;
// if get none, this means errors occurred when calling this native function
if (ctx.top[0].type==vm_none) {
die("error occurred in native function");
return;
}
}
inline void vm::o_slcbeg() {
// +--------------+
// | slice_vector | <-- top[0]
// +--------------+
// | resource_vec | <-- top[-1]
// +--------------+
(++ctx.top)[0] = ngc.alloc(vm_vec);
if (ctx.top[-1].type!=vm_vec) {
die("must slice a vector");
return;
}
}
inline void vm::o_slcend() {
ctx.top[-1] = ctx.top[0];
--ctx.top;
}
inline void vm::o_slc() {
var val = (ctx.top--)[0];
var res = ctx.top[-1].vec().get_val(val.tonum());
if (res.type==vm_none) {
die("index " + std::to_string(val.tonum()) + " out of range");
return;
}
ctx.top[0].vec().elems.push_back(res);
}
inline void vm::o_slc2() {
var val2 = (ctx.top--)[0];
var val1 = (ctx.top--)[0];
auto& ref = ctx.top[-1].vec().elems;
auto& aim = ctx.top[0].vec().elems;
u8 type1 = val1.type,type2=val2.type;
i32 num1 = val1.tonum();
i32 num2 = val2.tonum();
i32 size = ref.size();
if (type1==vm_nil && type2==vm_nil) {
num1 = 0;
num2 = size-1;
} else if (type1==vm_nil && type2!=vm_nil) {
num1 = num2<0? -size:0;
} else if (type1!=vm_nil && type2==vm_nil) {
num2 = num1<0? -1:size-1;
}
if (num1<-size || num1>=size || num2<-size || num2>=size) {
die("index " + std::to_string(num1) + ":" +
std::to_string(num2) + " out of range");
return;
} else if (num1<=num2) {
for(i32 i = num1; i<=num2; ++i) {
aim.push_back(i>=0? ref[i]:ref[i+size]);
}
}
}
inline void vm::o_mcallg() {
ctx.memr = global+imm[ctx.pc];
(++ctx.top)[0] = ctx.memr[0];
// push value in this memory space on stack
// to avoid being garbage collected
}
inline void vm::o_mcalll() {
ctx.memr = ctx.localr+imm[ctx.pc];
(++ctx.top)[0] = ctx.memr[0];
// push value in this memory space on stack
// to avoid being garbage collected
}
inline void vm::o_mupval() {
ctx.memr = &(
ctx.funcr.func()
.upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff]);
(++ctx.top)[0] = ctx.memr[0];
// push value in this memory space on stack
// to avoid being garbage collected
}
inline void vm::o_mcallv() {
var val = ctx.top[0]; // index
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
if (vec.type==vm_vec) {
ctx.memr = vec.vec().get_mem(val.tonum());
if (!ctx.memr) {
die("index "+std::to_string(val.tonum())+" out of range");
return;
}
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
if (val.type!=vm_str) {
die("key must be string");
return;
}
auto& ref = vec.hash();
auto& str = val.str();
ctx.memr = ref.get_mem(str);
if (!ctx.memr) {
ref.elems[str] = nil;
ctx.memr = ref.get_mem(str);
}
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
die("key must be string");
return;
}
auto& ref = vec.map();
auto& str = val.str();
ctx.memr = ref.get_mem(str);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
}
} else {
die("cannot get memory space in this type");
return;
}
}
inline void vm::o_mcallh() {
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
if (hash.type!=vm_hash && hash.type!=vm_map) {
die("must call a hash");
return;
}
auto& str = cstr[imm[ctx.pc]];
if (hash.type==vm_map) {
ctx.memr = hash.map().get_mem(str);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
}
return;
}
auto& ref = hash.hash();
ctx.memr = ref.get_mem(str);
if (!ctx.memr) { // create a new key
ref.elems[str] = nil;
ctx.memr = ref.get_mem(str);
}
}
inline void vm::o_ret() {
/* +-------------+
* | return value| <- top[0]
* +-------------+
* | old pc | <- top[-1]
* +-------------+
* | old localr | <- top[-2]
* +-------------+
* | old upvalr | <- top[-3]
* +-------------+
* | local scope |
* +-------------+ <- local pointer stored in localr
* | old funcr | <- old function stored in funcr
* +-------------+
*/
var ret = ctx.top[0];
var* local = ctx.localr;
var func = ctx.funcr;
var up = ctx.upvalr;
ctx.pc = ctx.top[-1].ret();
ctx.localr = ctx.top[-2].addr();
ctx.upvalr = ctx.top[-3];
ctx.top = local-1;
ctx.funcr = ctx.top[0];
ctx.top[0] = ret; // rewrite func with returned value
if (up.type==vm_upval) { // synchronize upvalue
auto& upval = up.upval();
auto size = func.func().lsize;
upval.onstk = false;
upval.elems.resize(size);
for(u32 i = 0; i<size; ++i) {
upval.elems[i] = local[i];
}
}
// cannot use gc.cort to judge,
// because there maybe another function call inside but return here
// coroutine function ends with setting pc to 0
if (!ctx.pc) {
ngc.ctxreserve();
}
}
}

139
src/optimizer.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include "optimizer.h"
namespace nasal {
void optimizer::const_string(
binary_operator* node,
string_literal* left_node,
string_literal* right_node) {
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
return;
}
const auto& left = left_node->get_content();
const auto& right = right_node->get_content();
node->set_optimized_string(
new string_literal(node->get_location(), left+right));
}
void optimizer::const_number(
binary_operator* node,
number_literal* left_node,
number_literal* right_node) {
const auto left = left_node->get_number();
const auto right = right_node->get_number();
f64 res;
switch(node->get_operator_type()) {
case binary_operator::binary_type::add: res = left+right; break;
case binary_operator::binary_type::sub: res = left-right; break;
case binary_operator::binary_type::mult: res = left*right; break;
case binary_operator::binary_type::div: res = left/right; break;
case binary_operator::binary_type::less: res = left<right; break;
case binary_operator::binary_type::leq: res = left<=right; break;
case binary_operator::binary_type::grt: res = left>right; break;
case binary_operator::binary_type::geq: res = left>=right; break;
case binary_operator::binary_type::bitwise_or:
res = static_cast<i32>(left)|static_cast<i32>(right); break;
case binary_operator::binary_type::bitwise_xor:
res = static_cast<i32>(left)^static_cast<i32>(right); break;
case binary_operator::binary_type::bitwise_and:
res = static_cast<i32>(left)&static_cast<i32>(right); break;
default: return;
}
if (std::isinf(res) || std::isnan(res)) {
return;
}
node->set_optimized_number(
new number_literal(node->get_location(), res));
}
void optimizer::const_number(
unary_operator* node,
number_literal* value_node) {
auto res = value_node->get_number();
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative:
res = -res; break;
case unary_operator::unary_type::bitwise_not:
res = ~static_cast<i32>(res); break;
case unary_operator::unary_type::logical_not:
res = !res; break;
}
if (std::isinf(res) || std::isnan(res)) {
return;
}
node->set_optimized_number(
new number_literal(node->get_location(), res));
}
bool optimizer::visit_binary_operator(binary_operator* node) {
node->get_left()->accept(this);
node->get_right()->accept(this);
number_literal* left_num_node = nullptr;
number_literal* right_num_node = nullptr;
string_literal* left_str_node = nullptr;
string_literal* right_str_node = nullptr;
if (node->get_left()->get_type()==expr_type::ast_num) {
left_num_node = (number_literal*)node->get_left();
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_number()) {
left_num_node = ((binary_operator*)node->get_left())->get_optimized_number();
} else if (node->get_left()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_left())->get_optimized_number()) {
left_num_node = ((unary_operator*)node->get_left())->get_optimized_number();
}
if (node->get_right()->get_type()==expr_type::ast_num) {
right_num_node = (number_literal*)node->get_right();
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_number()) {
right_num_node = ((binary_operator*)node->get_right())->get_optimized_number();
} else if (node->get_right()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_right())->get_optimized_number()) {
right_num_node = ((unary_operator*)node->get_right())->get_optimized_number();
}
if (node->get_left()->get_type()==expr_type::ast_str) {
left_str_node = (string_literal*)node->get_left();
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_string()) {
left_str_node = ((binary_operator*)node->get_left())->get_optimized_string();
}
if (node->get_right()->get_type()==expr_type::ast_str) {
right_str_node = (string_literal*)node->get_right();
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_string()) {
right_str_node = ((binary_operator*)node->get_right())->get_optimized_string();
}
if (left_num_node && right_num_node) {
const_number(node, left_num_node, right_num_node);
return true;
}
if (left_str_node && right_str_node) {
const_string(node, left_str_node, right_str_node);
return true;
}
return true;
}
bool optimizer::visit_unary_operator(unary_operator* node) {
node->get_value()->accept(this);
number_literal* value_node = nullptr;
if (node->get_value()->get_type()==expr_type::ast_num) {
value_node = (number_literal*)node->get_value();
} else if (node->get_value()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_value())->get_optimized_number()) {
value_node = ((binary_operator*)node->get_value())->get_optimized_number();
} else if (node->get_value()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_value())->get_optimized_number()) {
value_node = ((unary_operator*)node->get_value())->get_optimized_number();
}
if (value_node) {
const_number(node, value_node);
}
return true;
}
void optimizer::do_optimization(code_block* root) {
root->accept(this);
}
}

24
src/optimizer.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <cmath>
#include "nasal_ast.h"
#include "ast_visitor.h"
namespace nasal {
class optimizer:public ast_visitor {
private:
void const_string(binary_operator*, string_literal*, string_literal*);
void const_number(binary_operator*, number_literal*, number_literal*);
void const_number(unary_operator*, number_literal*);
public:
bool visit_binary_operator(binary_operator*) override;
bool visit_unary_operator(unary_operator*) override;
public:
void do_optimization(code_block*);
};
}

167
src/repl.cpp Normal file
View File

@@ -0,0 +1,167 @@
#include "repl.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "optimizer.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
namespace nasal {
namespace repl {
void repl::add_command_history(const std::string& history) {
if (command_history.size() && command_history.back()==history) {
return;
}
command_history.push_back(history);
if (command_history.size()>1000) {
command_history.pop_front();
}
}
std::string repl::readline(std::string prompt = ">>> ") {
auto line = std::string("");
std::cout << prompt;
std::getline(std::cin, line,'\n');
return line;
}
void repl::update_temp_file() {
auto content = std::string("");
for(const auto& i : source) {
content += i + "\n";
}
info::instance()->repl_file_source = content + " ";
}
bool repl::check_need_more_input() {
while(true) {
update_temp_file();
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
return false;
}
i64 in_curve = 0;
i64 in_bracket = 0;
i64 in_brace = 0;
for(const auto& t : nasal_lexer->result()) {
switch(t.type) {
case tok::lcurve: ++in_curve; break;
case tok::rcurve: --in_curve; break;
case tok::lbracket: ++in_bracket; break;
case tok::rbracket: --in_bracket; break;
case tok::lbrace: ++in_brace; break;
case tok::rbrace: --in_brace; break;
default: break;
}
}
if (in_curve<=0 && in_bracket<=0 && in_brace<=0) {
break;
}
auto line = readline("... ");
add_command_history(line);
source.back() += "\n" + line;
}
return true;
}
void repl::help() {
std::cout << ".h, .help | show help\n";
std::cout << ".e, .exit | quit the REPL\n";
std::cout << ".q, .quit | quit the REPL\n";
std::cout << ".c, .clear | clear the screen\n";
std::cout << ".s, .source | show source code\n";
std::cout << "\n";
}
bool repl::run() {
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
auto nasal_parser = std::unique_ptr<parse>(new parse);
auto nasal_linker = std::unique_ptr<linker>(new linker);
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer);
auto nasal_codegen = std::unique_ptr<codegen>(new codegen);
update_temp_file();
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
return false;
}
if (nasal_parser->compile(*nasal_lexer).geterr()) {
return false;
}
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
return false;
}
nasal_opt->do_optimization(nasal_parser->tree());
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true).geterr()) {
return false;
}
runtime.run(*nasal_codegen, *nasal_linker, {});
return true;
}
void repl::execute() {
source = {};
// mark we are in repl mode
info::instance()->in_repl_mode = true;
std::cout << "[nasal-repl] Initializating enviroment...\n";
// run on pass for initializing basic modules, without output
run();
// allow output now
runtime.set_allow_repl_output_flag(true);
std::cout << "[nasal-repl] Initialization complete.\n\n";
// finish initialization, output version info
std::cout << "Nasal REPL interpreter version " << __nasver;
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
help();
while(true) {
auto line = readline();
if (!line.length()) {
continue;
}
add_command_history(line);
if (line == ".e" || line == ".exit") {
break;
} else if (line == ".q" || line == ".quit") {
break;
} else if (line == ".h" || line == ".help") {
help();
continue;
} else if (line == ".c" || line == ".clear") {
std::cout << "\033c";
continue;
} else if (line == ".s" || line == ".source") {
update_temp_file();
std::cout << info::instance()->repl_file_source << "\n";
continue;
} else if (line[0] == "."[0]) {
std::cout << "no such command \"" << line;
std::cout << "\", input \".help\" for help\n";
continue;
}
source.push_back(line);
if (!check_need_more_input()) {
source.pop_back();
continue;
}
// run program
if (!run()) {
source.pop_back();
}
std::cout << "\n";
}
}
}
}

55
src/repl.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#include "nasal.h"
#include "nasal_vm.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <sstream>
#include <deque>
namespace nasal {
namespace repl {
struct info {
bool in_repl_mode = false;
std::string repl_file_name = "<nasal-repl>";
std::string repl_file_source = "";
// singleton
static info* instance() {
static info info;
return &info;
}
};
class repl {
private:
std::vector<std::string> source;
std::deque<std::string> command_history;
vm runtime;
private:
void add_command_history(const std::string&);
std::string readline(std::string);
bool check_need_more_input();
void update_temp_file();
void help();
bool run();
public:
repl() {
// set repl mode
runtime.set_repl_mode_flag(true);
// no detail report info
runtime.set_detail_report_info(false);
// set empty history
command_history = {""};
}
void execute();
};
}
}

47
src/symbol_finder.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include "symbol_finder.h"
namespace nasal {
bool symbol_finder::visit_definition_expr(definition_expr* node) {
if (node->get_variable_name()) {
symbols.push_back({
node->get_variable_name()->get_name(),
node->get_variable_name()->get_location()
});
} else {
for(auto i : node->get_variables()->get_variables()) {
symbols.push_back({
i->get_name(),
i->get_location()
});
}
}
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
return true;
}
bool symbol_finder::visit_function(function* node) {
return true;
}
bool symbol_finder::visit_iter_expr(iter_expr* node) {
if (node->get_name()) {
symbols.push_back({
node->get_name()->get_name(),
node->get_name()->get_location()
});
}
return true;
}
const std::vector<symbol_finder::symbol_info>& symbol_finder::do_find(code_block* root) {
symbols.clear();
root->accept(this);
return symbols;
}
}

29
src/symbol_finder.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include "nasal_ast.h"
#include "ast_visitor.h"
#include <cstring>
#include <sstream>
#include <vector>
namespace nasal {
class symbol_finder:public ast_visitor {
public:
struct symbol_info {
std::string name;
span location;
};
private:
std::vector<symbol_info> symbols;
public:
bool visit_definition_expr(definition_expr*) override;
bool visit_function(function*) override;
bool visit_iter_expr(iter_expr*) override;
const std::vector<symbol_finder::symbol_info>& do_find(code_block*);
};
}

155
src/unix_lib.cpp Normal file
View File

@@ -0,0 +1,155 @@
#include "unix_lib.h"
namespace nasal {
const auto dir_type_name = "dir";
void dir_entry_destructor(void* ptr) {
#ifndef _MSC_VER
closedir(static_cast<DIR*>(ptr));
#else
FindClose(ptr);
#endif
}
var builtin_pipe(var* local, gc& ngc) {
#ifndef _WIN32
i32 fd[2];
var res = ngc.alloc(vm_vec);
if (pipe(fd)==-1) {
return nas_err("pipe", "failed to create pipe");
}
res.vec().elems.push_back(var::num(static_cast<f64>(fd[0])));
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
return res;
#endif
return nas_err("pipe", "not supported on windows");
}
var builtin_fork(var* local, gc& ngc) {
#ifndef _WIN32
f64 res=fork();
if (res<0) {
return nas_err("fork", "failed to fork a process");
}
return var::num(static_cast<f64>(res));
#endif
return nas_err("fork", "not supported on windows");
}
var builtin_waitpid(var* local, gc& ngc) {
var pid = local[1];
var nohang = local[2];
if (pid.type!=vm_num || nohang.type!=vm_num) {
return nas_err("waitpid", "pid and nohang must be number");
}
#ifndef _WIN32
i32 ret_pid, status;
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
var vec = ngc.alloc(vm_vec);
vec.vec().elems.push_back(var::num(static_cast<f64>(ret_pid)));
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
return vec;
#endif
return nas_err("waitpid", "not supported on windows");
}
var builtin_opendir(var* local, gc& ngc) {
var path = local[1];
if (path.type!=vm_str) {
return nas_err("opendir", "\"path\" must be string");
}
#ifdef _MSC_VER
WIN32_FIND_DATAA data;
HANDLE p;
p = FindFirstFileA((path.str()+"\\*.*").c_str(), &data);
if (p==INVALID_HANDLE_VALUE) {
return nas_err("opendir", "cannot open dir <"+path.str()+">");
}
#else
DIR* p = opendir(path.str().c_str());
if (!p) {
return nas_err("opendir", "cannot open dir <"+path.str()+">");
}
#endif
var ret = ngc.alloc(vm_obj);
ret.obj().set(dir_type_name, dir_entry_destructor, p);
return ret;
}
var builtin_readdir(var* local, gc& ngc) {
var handle = local[1];
if (!handle.objchk(dir_type_name)) {
return nas_err("readdir", "not a valid dir handle");
}
#ifdef _MSC_VER
WIN32_FIND_DATAA data;
if (!FindNextFileA(handle.obj().ptr,&data)) {
return nil;
}
return ngc.newstr(data.cFileName);
#else
dirent* p = readdir(static_cast<DIR*>(handle.obj().ptr));
return p? ngc.newstr(p->d_name):nil;
#endif
}
var builtin_closedir(var* local, gc& ngc) {
var handle = local[1];
if (!handle.objchk(dir_type_name)) {
return nas_err("closedir", "not a valid dir handle");
}
handle.obj().clear();
return nil;
}
var builtin_chdir(var* local, gc& ngc) {
var path = local[1];
if (path.type!=vm_str) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(chdir(path.str().c_str())));
}
var builtin_environ(var* local, gc& ngc) {
var res = ngc.temp = ngc.alloc(vm_vec);
auto& vec = res.vec().elems;
for(char** env = environ; *env; ++env) {
vec.push_back(ngc.newstr(*env));
}
ngc.temp = nil;
return res;
}
var builtin_getcwd(var* local, gc& ngc) {
char buf[1024];
if (!getcwd(buf, sizeof(buf))) {
return nil;
}
return ngc.newstr(buf);
}
var builtin_getenv(var* local, gc& ngc) {
var envvar = local[1];
if (envvar.type!=vm_str) {
return nas_err("getenv", "\"envvar\" must be string");
}
char* res = getenv(envvar.str().c_str());
return res? ngc.newstr(res):nil;
}
nasal_builtin_table unix_lib_native[] = {
{"__pipe", builtin_pipe},
{"__fork", builtin_fork},
{"__waitpid", builtin_waitpid},
{"__opendir", builtin_opendir},
{"__readdir", builtin_readdir},
{"__closedir", builtin_closedir},
{"__chdir", builtin_chdir},
{"__environ", builtin_environ},
{"__getcwd", builtin_getcwd},
{"__getenv", builtin_getenv},
{nullptr, nullptr}
};
}

40
src/unix_lib.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#ifndef _MSC_VER
#include <unistd.h>
#include <dirent.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#include <direct.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/wait.h>
#endif
namespace nasal {
void dir_entry_destructor(void*);
var builtin_pipe(var*, gc&);
var builtin_fork(var*, gc&);
var builtin_waitpid(var*, gc&);
var builtin_opendir(var*, gc&);
var builtin_readdir(var*, gc&);
var builtin_closedir(var*, gc&);
var builtin_chdir(var*, gc&);
var builtin_environ(var*, gc&);
var builtin_getcwd(var*, gc&);
var builtin_getenv(var*, gc&);
extern nasal_builtin_table unix_lib_native[];
}

125
std/bits.nas Normal file
View File

@@ -0,0 +1,125 @@
# bits.nas
# 2023 by ValKmjolnir
# functions that do bitwise calculation.
# carefully use it, all the calculations are based on integer.
# u32 xor
var u32_xor = func(a, b) {
return __u32xor(a, b);
}
# u32 and
var u32_and = func(a, b) {
return __u32and(a, b);
}
# u32 or
var u32_or = func(a, b) {
return __u32or(a, b);
}
# u32 nand
var u32_nand = func(a, b) {
return __u32nand(a, b);
}
# u32 not
var 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).
var 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).
var 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.
var setfld = func(str, startbit, len, val) {
return __setfld;
}
# get a special string filled by '\0' to use in setfld.
var buf = func(len) {
return __buf;
}
var bit = func() {
var res = [var __ = 1];
var append = func(vec, arg...) {
return __append;
}
for(var i = 1; i<32; i += 1) {
append(res, __ += __);
}
return res;
}();
var test = func(n, b) {
n /= bit[b];
return int(n) != int(n/2)*2;
}
# returns number <n> with bit <b> set
var set = func(n, b) {
return n+!test(n, b)*bit[b];
}
# returns number <n> with bit <b> cleared
var clear = func(n, b) {
return n-test(n, b)*bit[b];
}
# returns number <n> with bit <b> toggled
var toggle = func(n, b) {
return test(n, b) ? (n-bit[b]):(n+bit[b]);
}
# returns number <n> with bit <b> set to value <v>
var switch = func(n, b, v) {
return n-(test(n, b)-!!v)*bit[b];
}
# returns number <n> as bit string, zero-padded to <len> digits:
# bits.string(6) -> "110"
# bits.string(6, 8) -> "00000110"
var string = func(n, len = 1) {
var s = "";
while (n) {
var v = int(n/2);
s = (v+v!=n)~s;
n = v;
}
for (var i = size(s); i<len; i += 1)
s = '0'~s;
return s;
}
# returns bit string <s> as number: bits.value("110") -> 6
var value = func(s) {
var n = 0;
var len = size(s);
for (var i = 0; i<len; i += 1)
n += n+(s[i]!= `0`);
return n;
}

24
std/coroutine.nas Normal file
View File

@@ -0,0 +1,24 @@
# coroutine.nas
# 2023 by ValKmjolnir
# in fact it is not multi-threaded, maybe in the future i could make it
var create = func(function) {
return __cocreate;
}
var resume = func(co, args...) {
return __coresume;
}
var yield = func(args...) {
return __coyield;
}
var status = func(co) {
return __costatus;
}
var running = func() {
return __corun;
}

16
std/csv.nas Normal file
View File

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

Some files were not shown because too many files have changed in this diff Show More