592 Commits
v6.5 ... v11.1

Author SHA1 Message Date
ValK
97adfc9ea4 Merge pull request #34 from ValKmjolnir/develop
📝 code improvement
2023-11-05 21:38:34 +08:00
ValKmjolnir
e8c8a6446b use reinterpret_cast and static_cast 2023-11-05 21:25:20 +08:00
ValKmjolnir
336139dcae 🐛 fix segfault in multi-assign/multi-def codegen 2023-11-05 20:30:54 +08:00
ValKmjolnir
8a160dc7f2 use reinterpret_cast as pointer cast 2023-11-05 00:10:26 +08:00
ValKmjolnir
c946e9debd 📝 update documents 2023-11-04 00:09:59 +08:00
ValKmjolnir
2f58a7c223 📝 update documents 2023-11-02 23:01:47 +08:00
ValKmjolnir
5174551aa1 improve import 2023-11-02 00:12:45 +08:00
ValKmjolnir
49f8cefca0 avoid repeatedly importing the same modules 2023-11-01 23:49:15 +08:00
ValKmjolnir
ccbe341dc5 add import logic for use statement 2023-11-01 00:37:02 +08:00
ValKmjolnir
4757dd220a 📝 add check for use statement 2023-10-31 19:53:01 +08:00
ValKmjolnir
88e4b86ccb add new module import syntax rule 2023-10-31 00:36:49 +08:00
ValKmjolnir
c35ad2e6fa add stdin, stdout, stderr in io module 2023-10-30 23:20:49 +08:00
ValKmjolnir
4c8e1dfe91 add new test file burningship.nas 2023-10-29 00:01:14 +08:00
ValK
d6e1408edb Merge pull request #33 from ValKmjolnir/develop
🐛 fix bug in debugger and symbol_finder & code improvement
2023-10-27 00:16:01 +08:00
ValKmjolnir
ef4af8f195 🐛 fix error codegen in foreach/forindex loop
undefined symbol was recognized as defined in symbol_finder, now it is
fixed :)
2023-10-26 22:40:20 +08:00
ValKmjolnir
bbed29eb65 add native function ceil 2023-10-26 00:04:20 +08:00
ValKmjolnir
d56e1bff2c change json.JSON to json 2023-10-25 00:32:42 +08:00
ValKmjolnir
9f7484596a 🐛 fix error report bug in import.cpp 2023-10-24 00:16:48 +08:00
ValKmjolnir
f7f4a38b47 🐛 fix abnormal debugger output on windows 2023-10-23 00:02:36 +08:00
ValKmjolnir
07a652cc37 🐛 fix compilation error using CMake 2023-10-22 23:52:39 +08:00
ValKmjolnir
9629108a1e improve readability of some codes 2023-10-22 23:45:10 +08:00
ValKmjolnir
1e1ab37e83 values can get data from namespace type 2023-10-21 18:13:52 +08:00
ValKmjolnir
dfcccd4523 🐛 fix invalid debug mode problem 2023-10-21 18:00:11 +08:00
ValKmjolnir
820c05c986 change way of calling native functions 2023-10-21 00:31:39 +08:00
ValKmjolnir
7f8a0b6445 improve code & add new test file 2023-10-20 00:24:17 +08:00
ValKmjolnir
54317a39a7 improve error info of out-of-range 2023-10-18 00:29:53 +08:00
ValKmjolnir
a298aa3a63 add detail error info in callh 2023-10-17 00:44:45 +08:00
ValKmjolnir
1580b31122 add function call trace info 2023-10-15 23:49:11 +08:00
ValKmjolnir
aab7decd70 split type definition from gc.h 2023-10-15 21:46:53 +08:00
ValKmjolnir
8290b7df9f 🐛 fix mingw make error 2023-10-14 21:30:33 +08:00
ValKmjolnir
ecfb679218 improve error info when lack arguments
in function call
2023-10-14 00:39:25 +08:00
ValKmjolnir
80f9fc5842 can convert minimum double from string 2023-10-11 00:20:02 +08:00
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
191 changed files with 26950 additions and 7164 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

87
CMakeLists.txt Normal file
View File

@@ -0,0 +1,87 @@
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_type.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_type.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)

352
LICENSE
View File

@@ -1,21 +1,339 @@
MIT License
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (c) 2021 ValKmjolnir
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.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
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.

1557
README.md

File diff suppressed because it is too large Load Diff

1145
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)

710
doc/dev.md Normal file
View File

@@ -0,0 +1,710 @@
# __Development History__
![buringship](./pic/burningship.png)
## __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)
* [v11.1](#version-111-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.
### __version 11.1 release__
1. Bug fix: debugger in v11.0 is malfunctional.
2. Bug fix: symbol_finder does not check definition in foreach/forindex loop.
3. Change extension syntax `import.xx.xx` to `use xx.xx`.

643
doc/dev_zh.md Normal file
View File

@@ -0,0 +1,643 @@
# __开发历史记录__
![buringship](./pic/burningship.png)
## __目录__
* [__语法分析__](#语法分析)
* [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)
* [v11.1](#version-111-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. 全新的自定义类型注册流程。
### __version 11.1 release__
1. Bug 修复: 修复 v11.0 的 debugger 无法启动的问题。
2. Bug 修复: symbol_finder 不检查 foreach/forindex 中的迭代变量声明的问题。
3. 扩展语法 `import.xx.xx` 改为 `use xx.xx`。

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

88
doc/namespace.md Normal file
View File

@@ -0,0 +1,88 @@
# 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
};
}();
```
## Import a module
Here is a module named `std/example_module.nas`:
```nasal
var a = 1;
```
Then there's a script file named `test.nas`, import module in this file using this way:
```nasal
use std.example_module;
println(example_module.a); # 1
```
Or this way:
```nasal
import("std/example_module.nas");
println(example_module.a); # 1
```

View File

@@ -0,0 +1,172 @@
<!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>
<img src="/doc/pic/social.png" width="900" height="400" style="margin-left: 15px;"><br /></img>
<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-v11.2-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 and burningship-figure generated by ppm script written in nasal.
</p>
<p>
下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 和 burningship 图形。
</p>
</text>
<img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
<img src="/doc/pic/burningship.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,16 +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::=
@@ -109,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/burningship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 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

144
lib.nas
View File

@@ -1,144 +0,0 @@
var import=func(filename)
{
return __builtin_import(filename);
}
var print=func(elems...)
{
return __builtin_print(elems);
};
var println=func(elems...)
{
__builtin_print(elems);
elems=['\n'];
return __builtin_print(elems);
}
var append=func(vec,elems...)
{
return __builtin_append(vec,elems);
}
var setsize=func(vec,size)
{
return __builtin_setsize(vec,size);
}
var system=func(str)
{
return __builtin_system(str);
}
var input=func()
{
return __builtin_input();
}
var sleep=func(duration)
{
return __builtin_sleep(duration);
}
var split=func(deli,str)
{
return __builtin_split(deli,str);
}
var rand=func(seed=nil)
{
return __builtin_rand(seed);
}
var id=func(object)
{
return __builtin_id(object);
}
var int=func(val)
{
return __builtin_int(val);
}
var num=func(val)
{
return __builtin_num(val);
}
var pop=func(vec)
{
return __builtin_pop(vec);
}
var str=func(num)
{
return __builtin_str(num);
}
var size=func(object)
{
return __builtin_size(object);
}
var contains=func(hash,key)
{
return __builtin_contains(hash,key);
}
var delete=func(hash,key)
{
return __builtin_delete(hash,key);
}
var keys=func(hash)
{
return __builtin_keys(hash);
}
var time=func(begin_time)
{
return __builtin_time(begin_time);
}
var die=func(str)
{
return __builtin_die(str);
}
var typeof=func(object)
{
return __builtin_type(object);
}
var substr=func(str,begin,len)
{
return __builtin_substr(str,begin,len);
}
var streq=func(a,b)
{
return __builtin_streq(a,b);
}
var left=func(str,len)
{
return __builtin_left(str,len);
}
var right=func(str,len)
{
return __builtin_right(str,len);
}
var cmp=func(a,b)
{
return __builtin_cmp(a,b);
}
var chr=func(code)
{
return __builtin_chr(code);
}
var io=
{
fin: func(filename){return __builtin_fin(filename);},
fout:func(filename,str){return __builtin_fout(filename,str);}
};
var bits=
{
bitxor: func(a,b){return __builtin_xor(a,b); },
bitand: func(a,b){return __builtin_and(a,b); },
bitor: func(a,b){return __builtin_or(a,b); },
bitnand: func(a,b){return __builtin_nand(a,b);},
bitnot: func(a) {return __builtin_not(a); }
};
var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); },
exp: func(x) {return __builtin_exp(x); },
ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);}
};
var D2R=math.pi/180;

173
main.cpp
View File

@@ -1,173 +0,0 @@
#include "nasal.h"
void help_interact()
{
std::cout
<<">> [ ] input a file name to execute. \n"
<<">> [help ] show help. \n"
<<">> [clear] clear the screen. \n"
<<">> [lex ] view tokens. \n"
<<">> [ast ] view abstract syntax tree. \n"
<<">> [code ] view byte code. \n"
<<">> [exec ] execute program on bytecode vm.\n"
<<">> [logo ] print logo of nasal . \n"
<<">> [exit ] quit nasal interpreter. \n";
return;
}
void help_cmd()
{
std::cout
#ifdef _WIN32
<<"use command \'chcp 65001\' if want to use unicode.\n"
#endif
<<"nasal | use interactive interpreter.\n"
<<"nasal -h -help | get help.\n"
<<"nasal -v -version | get version of nasal interpreter.\n"
<<"nasal filename | execute script file.\n";
return;
}
void info()
{
std::cout
<<">> Nasal interpreter ver 6.5.\n"
<<">> Thanks to https://github.com/andyross/nasal\n"
<<">> Code: https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<<">> Code: https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
<<">> Info: http://wiki.flightgear.org/Nasal_scripting_language\n"
<<">> Input \"help\" to get help .\n";
return;
}
void logo()
{
std::cout
<<" __ _ \n"
<<" /\\ \\ \\__ _ ___ __ _| | \n"
<<" / \\/ / _` / __|/ _` | | \n"
<<" / /\\ / (_| \\__ \\ (_| | | \n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n";
return;
}
void die(const char* stage,std::string& filename)
{
std::cout<<">> ["<<stage<<"] in <\""<<filename<<"\">: error(s) occurred,stop.\n";
return;
}
void execute(std::string& file,std::string& command)
{
nasal_lexer lexer;
nasal_parse parse;
nasal_import import;
nasal_codegen codegen;
nasal_vm vm;
lexer.openfile(file);
lexer.scanner();
if(lexer.get_error())
{
die("lexer",file);
return;
}
if(command=="lex")
{
lexer.print_token();
return;
}
parse.set_toklist(lexer.get_token_list());
lexer.get_token_list().clear();
parse.main_process();
if(parse.get_error())
{
die("parse",file);
return;
}
if(command=="ast")
{
parse.get_root().print_ast(0);
return;
}
import.link(parse.get_root());
parse.get_root().clear();
if(import.get_error())
{
die("import",file);
return;
}
codegen.main_progress(import.get_root());
if(codegen.get_error())
{
die("codegen",file);
return;
}
if(command=="code")
{
codegen.print_byte_code();
return;
}
vm.init(
codegen.get_str_table(),
codegen.get_num_table(),
codegen.get_exec_code()
);
vm.run();
vm.clear();
return;
}
void interact()
{
#ifdef _WIN32
// use chcp 65001 to use unicode io
system("chcp 65001");
#endif
std::string command,file="null";
logo();
info();
while(1)
{
std::cout<<">> ";
std::cin>>command;
if(command=="help")
help_interact();
else if(command=="clear")
{
#ifdef _WIN32
system("cls");
#else
int rs=system("clear");
#endif
}
else if(command=="logo")
logo();
else if(command=="exit")
return;
else if(command=="lex" || command=="ast" || command=="code" || command=="exec")
execute(file,command);
else
file=command;
}
}
int main(int argc,const char* argv[])
{
std::string command,file="null";
if(argc==1)
interact();
else if(argc==2 && (!strcmp(argv[1],"-v") || !strcmp(argv[1],"-version")))
{
logo();
std::cout<<"Nasal interpreter ver 6.5\n";
}
else if(argc==2 && (!strcmp(argv[1],"-h") || !strcmp(argv[1],"-help")))
help_cmd();
else if(argc==2 && argv[1][0]!='-')
{
file=argv[1];
command="exec";
execute(file,command);
return 0;
}
else
{
std::cout
<<"invalid command.\n"
<<"use nasal -h to get help.\n";
}
return 0;
}

277
makefile Normal file
View File

@@ -0,0 +1,277 @@
STD = c++17
ifndef OS
OS = $(shell uname)
endif
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
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_type.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_type.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_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o
build/nasal_gc.o: src/nasal.h src/nasal_type.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_type.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_type.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_type.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_type.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_type.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_type.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_type.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_type.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

98
module/fib.cpp Normal file
View File

@@ -0,0 +1,98 @@
// module for test
#include <iostream>
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.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.to_num()));
}
var quick_fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("quick_fib","lack arguments");
}
double num = args[0].to_num();
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.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = 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.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.ghost().pointer) << "\n";
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;
}
}

113
module/keyboard.cpp Normal file
View File

@@ -0,0 +1,113 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.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 @@
use 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 @@
use 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 @@
use 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 @@
use 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);
}
};
}();

77
module/makefile Normal file
View File

@@ -0,0 +1,77 @@
.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_type.h ../src/nasal_gc.h
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
STD = c++17
ifndef OS
OS = $(shell uname)
endif
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

302
module/matrix.cpp Normal file
View File

@@ -0,0 +1,302 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.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;
}
}

262
module/nasocket.cpp Normal file
View File

@@ -0,0 +1,262 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.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(),
reinterpret_cast<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(),
reinterpret_cast<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(),
reinterpret_cast<sockaddr*>(&client),
&socklen
);
#else
int client_sd = accept(
args[0].num(),
reinterpret_cast<sockaddr*>(&client),
reinterpret_cast<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(),
reinterpret_cast<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(),
reinterpret_cast<sockaddr*>(&addr),
&socklen
);
#else
auto recvsize = recvfrom(
args[0].num(),
buf,
args[1].num(),
args[2].num(),
reinterpret_cast<sockaddr*>(&addr),
reinterpret_cast<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;
}
}

128
nasal.h
View File

@@ -1,128 +0,0 @@
#ifndef __NASAL_H__
#define __NASAL_H__
#pragma GCC optimize(2)
#include <stdint.h>
#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 <unordered_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(const char* str)
{
double ret=0;
for(;*str;++str)
{
ret*=16;
if('0'<=*str && *str<='9')
ret+=(*str-'0');
else if('a'<=*str && *str<='f')
ret+=(*str-'a'+10);
else if('A'<=*str && *str<='F')
ret+=(*str-'A'+10);
else
return nan("");
}
return ret;
}
inline double oct_to_double(const char* str)
{
double ret=0;
for(;*str;++str)
{
ret*=8;
if('0'<=*str && *str<='8')
ret+=(*str-'0');
else
return nan("");
}
return ret;
}
inline double dec_to_double(const char* str)
{
double 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;
for(;*str;++str)
{
if('0'<=*str && *str<='9')
num_pow=num_pow*10+(*str-'0');
else
return nan("");
}
return ret*std::pow(10,negative*num_pow);
}
double str2num(const char* str)
{
bool is_negative=false;
double ret_num=0;
if(*str=='-' || *str=='+')
is_negative=(*str++=='-');
if(!*str)
return nan("");
if(str[0]=='0' && str[1]=='x')
ret_num=hex_to_double(str+2);
else if(str[0]=='0' && str[1]=='o')
ret_num=oct_to_double(str+2);
else
ret_num=dec_to_double(str);
return is_negative?-ret_num:ret_num;
}
/*
trans_number_to_string:
convert number to string
*/
std::string num2str(double number)
{
std::ostringstream ss;
ss<<number;
return ss.str();
}
#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_codegen.h"
#include "nasal_vm.h"
#endif

View File

@@ -1,142 +0,0 @@
#ifndef __NASAL_AST_H__
#define __NASAL_AST_H__
enum ast_node
{
ast_null=0,
ast_root,ast_block,
ast_nil,ast_num,ast_str,ast_id,ast_func,ast_hash,ast_vec,
ast_hashmember,ast_call,ast_callh,ast_callv,ast_callf,ast_subvec,
ast_args,ast_default_arg,ast_dynamic_id,
ast_and,ast_or,
ast_equal,ast_addeq,ast_subeq,ast_multeq,ast_diveq,ast_lnkeq,
ast_cmpeq,ast_neq,
ast_less,ast_leq,
ast_grt,ast_geq,
ast_add,ast_sub,ast_mult,ast_div,ast_link,
ast_neg,ast_not,
ast_trino,
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_def,ast_multi_assign,
ast_continue,ast_break,ast_ret
};
const char* ast_name[]=
{
"null",
"root","block",
"nil","num","str","id","func","hash","vec",
"hashmember","call","callh","callv","callf","subvec",
"args","deflt_arg","dyn_id",
"and","or",
"=","+=","-=","*=","/=","~=",
"==","!=",
"<","<=",
">",">=",
"+","-","*","/","~",
"unary-","unary!",
"trino",
"for","forindex","foreach","while","iter",
"conditional","if","elsif","else",
"multi_id","multi_scalar",
"def","multi_assign",
"continue","break","return"
};
class nasal_ast
{
private:
int line;
int type;
double num;
std::string str;
std::vector<nasal_ast> children;
public:
nasal_ast(){line=0;type=ast_null;}
nasal_ast(int l,int t){line=l;type=t;}
nasal_ast(const nasal_ast&);
nasal_ast(nasal_ast&&);
nasal_ast& operator=(const nasal_ast&);
nasal_ast& operator=(nasal_ast&&);
void print_ast(int);
void clear();
void add_child(nasal_ast&& ast){children.push_back(std::move(ast));}
void set_line(int l){line=l;}
void set_type(int t){type=t;}
void set_str(std::string& s){str=s;}
void set_num(double n){num=n;}
int get_line(){return line;}
int get_type(){return type;}
double get_num(){return num;}
std::string& get_str(){return str;}
std::vector<nasal_ast>& get_children(){return children;}
};
nasal_ast::nasal_ast(const nasal_ast& tmp)
{
line=tmp.line;
type=tmp.type;
str =tmp.str;
num =tmp.num;
children=tmp.children;
return;
}
nasal_ast::nasal_ast(nasal_ast&& tmp)
{
line=tmp.line;
type=tmp.type;
str.swap(tmp.str);
num =tmp.num;
children.swap(tmp.children);
return;
}
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
{
line=tmp.line;
type=tmp.type;
str=tmp.str;
num=tmp.num;
children=tmp.children;
return *this;
}
nasal_ast& nasal_ast::operator=(nasal_ast&& tmp)
{
line=tmp.line;
type=tmp.type;
str.swap(tmp.str);
num=tmp.num;
children.swap(tmp.children);
return *this;
}
void nasal_ast::clear()
{
line=0;
str="";
num=0;
type=ast_null;
children.clear();
return;
}
void nasal_ast::print_ast(int depth)
{
for(int i=0;i<depth;++i)
std::cout<<"| ";
std::cout<<ast_name[type];
if(type==ast_str || type==ast_id || type==ast_default_arg || type==ast_dynamic_id || type==ast_callh)
std::cout<<":"<<str;
else if(type==ast_num)
std::cout<<":"<<num;
std::cout<<'\n';
for(auto& i:children)
i.print_ast(depth+1);
return;
}
#endif

View File

@@ -1,846 +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...)
{
__builtin_std_cout(elements);
return nil;
}
builtin function __builtin_std_cout is wrapped up by print
*/
// declaration of builtin functions
// to add new builtin function,declare it here and write the definition below
nasal_val* builtin_print(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_append(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_setsize(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_system(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_input(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_sleep(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_fin(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_fout(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_split(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_rand(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_id(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_int(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_num(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_pop(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_str(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_size(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_xor(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_and(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_or(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_nand(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_not(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_sin(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_cos(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_tan(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_exp(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_ln(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_sqrt(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_atan2(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_time(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_contains(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_delete(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_keys(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_import(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_die(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_type(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_substr(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_streq(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_left(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_right(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_cmp(std::vector<nasal_val*>&,nasal_gc&);
nasal_val* builtin_chr(std::vector<nasal_val*>&,nasal_gc&);
void builtin_err(const char* func_name,std::string info)
{
std::cout<<">> [vm] "<<func_name<<": "<<info<<".\n";
return;
}
// register builtin function's name and it's address here in this table below
// this table must end with {"",nullptr}
struct FUNC_TABLE
{
const char* name;
nasal_val* (*func)(std::vector<nasal_val*>&,nasal_gc&);
} builtin_func[]=
{
{"__builtin_print", builtin_print },
{"__builtin_append", builtin_append },
{"__builtin_setsize", builtin_setsize },
{"__builtin_system", builtin_system },
{"__builtin_input", builtin_input },
{"__builtin_sleep", builtin_sleep },
{"__builtin_fin", builtin_fin },
{"__builtin_fout", builtin_fout },
{"__builtin_split", builtin_split },
{"__builtin_rand", builtin_rand },
{"__builtin_id", builtin_id },
{"__builtin_int", builtin_int },
{"__builtin_num", builtin_num },
{"__builtin_pop", builtin_pop },
{"__builtin_str", builtin_str },
{"__builtin_size", builtin_size },
{"__builtin_xor", builtin_xor },
{"__builtin_and", builtin_and },
{"__builtin_or", builtin_or },
{"__builtin_nand", builtin_nand },
{"__builtin_not", builtin_not },
{"__builtin_sin", builtin_sin },
{"__builtin_cos", builtin_cos },
{"__builtin_tan", builtin_tan },
{"__builtin_exp", builtin_exp },
{"__builtin_ln", builtin_ln },
{"__builtin_sqrt", builtin_sqrt },
{"__builtin_atan2", builtin_atan2 },
{"__builtin_time", builtin_time },
{"__builtin_contains",builtin_contains},
{"__builtin_delete", builtin_delete },
{"__builtin_keys", builtin_keys },
{"__builtin_import", builtin_import },
{"__builtin_die", builtin_die },
{"__builtin_type", builtin_type },
{"__builtin_substr", builtin_substr },
{"__builtin_streq", builtin_streq },
{"__builtin_left", builtin_left },
{"__builtin_right", builtin_right },
{"__builtin_cmp", builtin_cmp },
{"__builtin_chr", builtin_chr },
{nullptr, nullptr }
};
nasal_val* builtin_print(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
// get arguments
// local_scope[0] is reserved for 'me'
nasal_val* vec_addr=local_scope[1];
// main process
for(auto i:vec_addr->ptr.vec->elems)
switch(i->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
// generate return value
return gc.nil_addr;
}
nasal_val* builtin_append(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* vec_addr=local_scope[1];
nasal_val* elem_addr=local_scope[2];
if(vec_addr->type!=vm_vec)
{
builtin_err("append","\"vector\" must be vector");
return nullptr;
}
std::vector<nasal_val*>& ref_vec=vec_addr->ptr.vec->elems;
for(auto i:elem_addr->ptr.vec->elems)
ref_vec.push_back(i);
return gc.nil_addr;
}
nasal_val* builtin_setsize(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* vec_addr=local_scope[1];
nasal_val* size_addr=local_scope[2];
if(vec_addr->type!=vm_vec)
{
builtin_err("setsize","\"vector\" must be vector");
return nullptr;
}
if(size_addr->type!=vm_num)
{
builtin_err("setsize","\"size\" is not a number");
return nullptr;
}
int num=size_addr->ptr.num;
if(num<0)
{
builtin_err("setsize","\"size\" must be greater than -1");
return nullptr;
}
vec_addr->ptr.vec->elems.resize(num,gc.nil_addr);
return gc.nil_addr;
}
nasal_val* builtin_system(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* ret_addr=gc.gc_alloc(vm_num);
nasal_val* str_addr=local_scope[1];
if(str_addr->type!=vm_str)
{
builtin_err("system","\"str\" must be string");
return nullptr;
}
ret_addr->ptr.num=(double)system(str_addr->ptr.str->data());
return ret_addr;
}
nasal_val* builtin_input(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* ret_addr=gc.gc_alloc(vm_str);
std::cin>>*ret_addr->ptr.str;
return ret_addr;
}
nasal_val* builtin_sleep(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("sleep","\"duration\" must be number");
return nullptr;
}
// sleep in unistd.h will make this progress sleep sleep_time seconds.
sleep((unsigned int)val_addr->ptr.num);
return gc.nil_addr;
}
nasal_val* builtin_fin(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_str)
{
builtin_err("io.fin","\"filename\" must be string");
return nullptr;
}
std::string& filename=*val_addr->ptr.str;
std::ifstream fin(filename);
nasal_val* ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str="";
if(!fin.fail())
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
ret_addr->ptr.str->push_back(c);
}
else
builtin_err("io.fin","cannot open \""+filename+"\"");
fin.close();
return ret_addr;
}
nasal_val* builtin_fout(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
nasal_val* str_addr=local_scope[2];
if(val_addr->type!=vm_str)
{
builtin_err("io.fout","\"filename\" must be string");
return nullptr;
}
if(str_addr->type!=vm_str)
{
builtin_err("io.fout","\"str\" must be string");
return nullptr;
}
std::ofstream fout(*val_addr->ptr.str);
if(fout.fail())
{
builtin_err("io.fout","cannot open \""+*val_addr->ptr.str+"\"");
return nullptr;
}
fout<<*str_addr->ptr.str;
fout.close();
return gc.nil_addr;
}
nasal_val* builtin_split(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* delimeter_val_addr=local_scope[1];
nasal_val* string_val_addr=local_scope[2];
if(delimeter_val_addr->type!=vm_str)
{
builtin_err("split","\"delimeter\" must be string");
return nullptr;
}
if(string_val_addr->type!=vm_str)
{
builtin_err("split","\"string\" must be string");
return nullptr;
}
std::string delimeter=*delimeter_val_addr->ptr.str;
std::string source=*string_val_addr->ptr.str;
int delimeter_len=delimeter.length();
int source_len=source.length();
nasal_val* ret_addr=gc.builtin_alloc(vm_vec);
std::vector<nasal_val*>& ref_vec=ret_addr->ptr.vec->elems;
std::string tmp="";
if(!delimeter_len)
{
for(int i=0;i<source_len;++i)
{
nasal_val* str_addr=gc.builtin_alloc(vm_str);
*str_addr->ptr.str=source[i];
ref_vec.push_back(str_addr);
}
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)
{
if(tmp.length())
{
nasal_val* str_addr=gc.builtin_alloc(vm_str);
*str_addr->ptr.str=tmp;
ref_vec.push_back(str_addr);
tmp="";
}
i+=delimeter_len-1;
}
else
tmp+=source[i];
}
if(tmp.length())
{
nasal_val* str_addr=gc.builtin_alloc(vm_str);
*str_addr->ptr.str=tmp;
ref_vec.push_back(str_addr);
tmp="";
}
return ret_addr;
}
nasal_val* builtin_rand(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num && val_addr->type!=vm_nil)
{
builtin_err("rand","\"seed\" must be nil or number");
return nullptr;
}
if(val_addr->type==vm_num)
{
srand((unsigned int)val_addr->ptr.num);
return gc.nil_addr;
}
double num=0;
for(int i=0;i<5;++i)
num=(num+rand())*(1.0/(RAND_MAX+1.0));
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=num;
return ret_addr;
}
nasal_val* builtin_id(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
nasal_val* ret_addr=gc.gc_alloc(vm_str);
char buf[32];
sprintf(buf,"0x%p",val_addr);
*ret_addr->ptr.str=buf;
return ret_addr;
}
nasal_val* builtin_int(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("int","\"value\" must be number");
return nullptr;
}
int number=(int)val_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(double)number;
return ret_addr;
}
nasal_val* builtin_num(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_str)
{
builtin_err("num","\"value\" must be string");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=val_addr->to_number();
return ret_addr;
}
nasal_val* builtin_pop(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_vec)
{
builtin_err("pop","\"vector\" must be vector");
return nullptr;
}
if(val_addr->ptr.vec->elems.size())
{
nasal_val* tmp=val_addr->ptr.vec->elems.back();
val_addr->ptr.vec->elems.pop_back();
return tmp;
}
return gc.nil_addr;
}
nasal_val* builtin_str(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("str","\"number\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=val_addr->to_string();
return ret_addr;
}
nasal_val* builtin_size(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
nasal_val* ret_addr=gc.gc_alloc(vm_num);
switch(val_addr->type)
{
case vm_nil: ret_addr->ptr.num=0; break;
case vm_num: ret_addr->ptr.num=val_addr->ptr.num; break;
case vm_func: ret_addr->ptr.num=0; break;
case vm_str: ret_addr->ptr.num=val_addr->ptr.str->length(); break;
case vm_vec: ret_addr->ptr.num=val_addr->ptr.vec->elems.size(); break;
case vm_hash: ret_addr->ptr.num=val_addr->ptr.hash->elems.size();break;
}
return ret_addr;
}
nasal_val* builtin_xor(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
if(a_addr->type!=vm_num)
{
builtin_err("xor","\"a\" must be number");
return nullptr;
}
if(b_addr->type!=vm_num)
{
builtin_err("xor","\"b\" must be number");
return nullptr;
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a^number_b);
return ret_addr;
}
nasal_val* builtin_and(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
if(a_addr->type!=vm_num)
{
builtin_err("and","\"a\" must be number");
return nullptr;
}
if(b_addr->type!=vm_num)
{
builtin_err("and","\"b\" must be number");
return nullptr;
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a&number_b);
return ret_addr;
}
nasal_val* builtin_or(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
if(a_addr->type!=vm_num)
{
builtin_err("or","\"a\" must be number");
return nullptr;
}
if(b_addr->type!=vm_num)
{
builtin_err("or","\"b\" must be number");
return nullptr;
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a|number_b);
return ret_addr;
}
nasal_val* builtin_nand(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
if(a_addr->type!=vm_num)
{
builtin_err("nand","\"a\" must be number");
return nullptr;
}
if(b_addr->type!=vm_num)
{
builtin_err("nand","\"b\" must be number");
return nullptr;
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(~(number_a&number_b));
return ret_addr;
}
nasal_val* builtin_not(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
if(a_addr->type!=vm_num)
{
builtin_err("not","\"a\" must be number");
return nullptr;
}
int number=(int)a_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(~number);
return ret_addr;
}
nasal_val* builtin_sin(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("sin","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=sin(val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_cos(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("cos","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=cos(val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_tan(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("tan","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=tan(val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_exp(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("exp","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=exp(val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_ln(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("ln","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(log(val_addr->ptr.num)/log(2.7182818284590452354));
return ret_addr;
}
nasal_val* builtin_sqrt(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("sqrt","\"x\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=sqrt(val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_atan2(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* x_val_addr=local_scope[1];
nasal_val* y_val_addr=local_scope[2];
if(x_val_addr->type!=vm_num)
{
builtin_err("atan2","\"x\" must be number");
return nullptr;
}
if(y_val_addr->type!=vm_num)
{
builtin_err("atan2","\"y\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=atan2(y_val_addr->ptr.num,x_val_addr->ptr.num);
return ret_addr;
}
nasal_val* builtin_time(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
if(val_addr->type!=vm_num)
{
builtin_err("time","\"begin_time\" must be number");
return nullptr;
}
time_t begin_time=(time_t)val_addr->ptr.num;
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=time(&begin_time);
return ret_addr;
}
nasal_val* builtin_contains(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* hash_addr=local_scope[1];
nasal_val* key_addr=local_scope[2];
if(hash_addr->type!=vm_hash)
{
builtin_err("contains","\"hash\" must be hash");
return nullptr;
}
if(key_addr->type!=vm_str)
{
builtin_err("contains","\"key\" must be string");
return nullptr;
}
return hash_addr->ptr.hash->elems.count(*key_addr->ptr.str)?gc.one_addr:gc.zero_addr;
}
nasal_val* builtin_delete(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* hash_addr=local_scope[1];
nasal_val* key_addr=local_scope[2];
if(hash_addr->type!=vm_hash)
{
builtin_err("delete","\"hash\" must be hash");
return nullptr;
}
if(key_addr->type!=vm_str)
{
builtin_err("delete","\"key\" must be string");
return nullptr;
}
if(hash_addr->ptr.hash->elems.count(*key_addr->ptr.str))
hash_addr->ptr.hash->elems.erase(*key_addr->ptr.str);
return gc.nil_addr;
}
nasal_val* builtin_keys(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* hash_addr=local_scope[1];
if(hash_addr->type!=vm_hash)
{
builtin_err("keys","\"hash\" must be hash");
return nullptr;
}
nasal_val* ret_addr=gc.builtin_alloc(vm_vec);
std::vector<nasal_val*>& ref_vec=ret_addr->ptr.vec->elems;
for(auto iter:hash_addr->ptr.hash->elems)
{
nasal_val* str_addr=gc.builtin_alloc(vm_str);
*str_addr->ptr.str=iter.first;
ref_vec.push_back(str_addr);
}
return ret_addr;
}
nasal_val* builtin_import(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
// this function is used in preprocessing.
// this function will return nothing when running.
builtin_err("import","must use this function in global scope");
return nullptr;
}
nasal_val* builtin_die(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* str_addr=local_scope[1];
if(str_addr->type!=vm_str)
{
builtin_err("die","\"str\" must be string");
return nullptr;
}
std::cout<<">> [vm] error: "<<*str_addr->ptr.str<<'\n';
return nullptr;
}
nasal_val* builtin_type(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* val_addr=local_scope[1];
nasal_val* ret_addr=gc.gc_alloc(vm_str);
switch(val_addr->type)
{
case vm_nil: *ret_addr->ptr.str="nil"; break;
case vm_num: *ret_addr->ptr.str="number"; break;
case vm_str: *ret_addr->ptr.str="string"; break;
case vm_vec: *ret_addr->ptr.str="vector"; break;
case vm_hash: *ret_addr->ptr.str="hash"; break;
case vm_func: *ret_addr->ptr.str="function"; break;
}
return ret_addr;
}
nasal_val* builtin_substr(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* str_addr=local_scope[1];
nasal_val* beg_addr=local_scope[2];
nasal_val* len_addr=local_scope[3];
if(str_addr->type!=vm_str)
{
builtin_err("substr","\"str\" must be string");
return nullptr;
}
if(beg_addr->type!=vm_num)
{
builtin_err("substr","\"begin\" must be number");
return nullptr;
}
if(len_addr->type!=vm_num)
{
builtin_err("substr","\"length\" must be number");
return nullptr;
}
std::string& str=*str_addr->ptr.str;
int beg=(int)beg_addr->ptr.num;
int len=(int)len_addr->ptr.num;
if(beg>=str.length() || beg+len>=str.length())
{
builtin_err("susbtr","index out of range");
return nullptr;
}
if(len<0)
len=0;
nasal_val* ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(beg,len);
return ret_addr;
}
nasal_val* builtin_streq(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(a_addr->type!=vm_str || b_addr->type!=vm_str)?0:(*a_addr->ptr.str==*b_addr->ptr.str);
return ret_addr;
}
nasal_val* builtin_left(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* str_addr=local_scope[1];
nasal_val* len_addr=local_scope[2];
if(str_addr->type!=vm_str)
{
builtin_err("left","\"string\" must be string");
return nullptr;
}
if(len_addr->type!=vm_num)
{
builtin_err("left","\"length\" must be number");
return nullptr;
}
std::string& str=*str_addr->ptr.str;
int len=(int)len_addr->ptr.num;
if(len<0)
len=0;
nasal_val* ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(0, len);
return ret_addr;
}
nasal_val* builtin_right(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* str_addr=local_scope[1];
nasal_val* len_addr=local_scope[2];
if(str_addr->type!=vm_str)
{
builtin_err("right","\"string\" must be string");
return nullptr;
}
if(len_addr->type!=vm_num)
{
builtin_err("right","\"length\" must be number");
return nullptr;
}
std::string& str=*str_addr->ptr.str;
int len=(int)len_addr->ptr.num;
int srclen=str.length();
if(len>srclen)
len=srclen;
if(len<0)
len=0;
nasal_val* ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(srclen-len, srclen);
return ret_addr;
}
nasal_val* builtin_cmp(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
nasal_val* a_addr=local_scope[1];
nasal_val* b_addr=local_scope[2];
if(a_addr->type!=vm_str)
{
builtin_err("cmp","\"a\" must be string");
return nullptr;
}
if(b_addr->type!=vm_str)
{
builtin_err("cmp","\"b\" must be string");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=strcmp(a_addr->ptr.str->data(),b_addr->ptr.str->data());
return ret_addr;
}
nasal_val* builtin_chr(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
{
const char* extend[]={
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
nasal_val* code_addr=local_scope[1];
if(code_addr->type!=vm_num)
{
builtin_err("chr","\"code\" must be number");
return nullptr;
}
nasal_val* ret_addr=gc.gc_alloc(vm_str);
int num=code_addr->ptr.num;
if(0<=num && num<128)
*ret_addr->ptr.str=(char)num;
else if(128<=num && num<256)
*ret_addr->ptr.str=extend[num-128];
else
*ret_addr->ptr.str=" ";
return ret_addr;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,412 +0,0 @@
#ifndef __NASAL_GC_H__
#define __NASAL_GC_H__
enum nasal_type
{
vm_nil=0,
vm_num,
vm_str,
vm_func,
vm_vec,
vm_hash,
vm_type_size
};
// change parameters here to make your own efficient gc
// better set bigger number on vm_num and vm_vec
const int increment[vm_type_size]=
{
0, // vm_nil,in fact it is not in use
65536,// vm_num
256, // vm_str
16, // vm_func
2048, // vm_vec
8 // vm_hash
};
struct nasal_val;//declaration of nasal_val
struct nasal_vec// 24 bytes
{
std::vector<nasal_val*> elems;
void print();
nasal_val* get_val(int);
nasal_val** get_mem(int);
};
struct nasal_hash// 56 bytes
{
std::unordered_map<std::string,nasal_val*> elems;
void print();
nasal_val* get_val(std::string&);
nasal_val** get_mem(std::string&);
};
struct nasal_func// 120 bytes
{
int32_t dynpara;// dynamic parameter name index in hash
uint32_t offset; // arguments will be loaded into local scope from this offset
uint32_t entry; // pc will set to entry-1 to call this function
std::vector<nasal_val*> default_para;// default value(nasal_val*)
std::unordered_map<std::string,int> key_table;// parameter name hash
std::vector<nasal_val*> closure;// closure will be loaded to gc.local.back() as the local scope
nasal_func();
void clear();
};
struct nasal_val// 16 bytes
{
#define GC_UNCOLLECTED 0
#define GC_COLLECTED 1
#define GC_FOUND 2
uint8_t mark;
uint16_t type;
union
{
double num;
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
nasal_func* func;
}ptr;
nasal_val(int);
~nasal_val();
double to_number();
std::string to_string();
};
/*functions of nasal_vec*/
nasal_val* nasal_vec::get_val(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return elems[index>=0?index:index+vec_size];
}
nasal_val** nasal_vec::get_mem(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return &elems[index>=0?index:index+vec_size];
}
void nasal_vec::print()
{
std::cout<<'[';
for(auto i:elems)
{
switch(i->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
std::cout<<',';
}
std::cout<<']';
return;
}
/*functions of nasal_hash*/
nasal_val* nasal_hash::get_val(std::string& key)
{
nasal_val* ret_addr=nullptr;
if(elems.count(key))
return elems[key];
else if(elems.count("parents"))
{
nasal_val* val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
{
if(i->type==vm_hash)
ret_addr=i->ptr.hash->get_val(key);
if(ret_addr)
return ret_addr;
}
}
return nullptr;
}
nasal_val** nasal_hash::get_mem(std::string& key)
{
nasal_val** mem_addr=nullptr;
if(elems.count(key))
return &elems[key];
else if(elems.count("parents"))
{
nasal_val* val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
{
if(i->type==vm_hash)
mem_addr=i->ptr.hash->get_mem(key);
if(mem_addr)
return mem_addr;
}
}
return nullptr;
}
void nasal_hash::print()
{
std::cout<<'{';
for(auto& i:elems)
{
std::cout<<i.first<<':';
nasal_val* tmp=i.second;
switch(tmp->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->ptr.num; break;
case vm_str: std::cout<<*tmp->ptr.str; break;
case vm_vec: tmp->ptr.vec->print(); break;
case vm_hash: tmp->ptr.hash->print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
std::cout<<',';
}
std::cout<<'}';
return;
}
/*functions of nasal_func*/
nasal_func::nasal_func()
{
dynpara=-1;
return;
}
void nasal_func::clear()
{
dynpara=-1;
default_para.clear();
key_table.clear();
closure.clear();
return;
}
/*functions of nasal_val*/
nasal_val::nasal_val(int val_type)
{
mark=GC_COLLECTED;
type=val_type;
switch(type)
{
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec; break;
case vm_hash: ptr.hash=new nasal_hash; break;
case vm_func: ptr.func=new nasal_func; break;
}
return;
}
nasal_val::~nasal_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;
}
type=vm_nil;
return;
}
double nasal_val::to_number()
{
if(type==vm_str)
return str2num(ptr.str->c_str());
return ptr.num;
}
std::string nasal_val::to_string()
{
if(type==vm_str)
return *ptr.str;
else if(type==vm_num)
return num2str(ptr.num);
return "";
}
struct nasal_gc
{
#define STACK_MAX_DEPTH (65536)
nasal_val* zero_addr; // reserved address of nasal_val,type vm_num, 0
nasal_val* one_addr; // reserved address of nasal_val,type vm_num, 1
nasal_val* nil_addr; // reserved address of nasal_val,type vm_nil
nasal_val* val_stack[STACK_MAX_DEPTH+16];// 16 reserved to avoid stack overflow
nasal_val** stack_top; // stack top
std::vector<nasal_val*> num_addrs; // reserved address for const vm_num
std::vector<nasal_val*> str_addrs; // reserved address for const vm_str
std::vector<nasal_val*> slice_stack; // slice stack for vec[val,val,val:val]
std::vector<nasal_val*> memory; // gc memory
std::queue <nasal_val*> free_list[vm_type_size]; // gc free list
std::vector<nasal_val*> global;
std::list<std::vector<nasal_val*>> local;
void mark();
void sweep();
void gc_init(std::vector<double>&,std::vector<std::string>&);
void gc_clear();
nasal_val* gc_alloc(int);
nasal_val* builtin_alloc(int);
};
/* gc functions */
void nasal_gc::mark()
{
std::queue<nasal_val*> bfs;
for(auto i:global)
bfs.push(i);
for(auto& i:local)
for(auto j:i)
bfs.push(j);
for(auto i:slice_stack)
bfs.push(i);
for(nasal_val** i=val_stack;i<=stack_top;++i)
bfs.push(*i);
while(!bfs.empty())
{
nasal_val* tmp=bfs.front();
bfs.pop();
if(tmp->mark) continue;
tmp->mark=GC_FOUND;
if(tmp->type==vm_vec)
for(auto i:tmp->ptr.vec->elems)
bfs.push(i);
else if(tmp->type==vm_hash)
for(auto& i:tmp->ptr.hash->elems)
bfs.push(i.second);
else if(tmp->type==vm_func)
{
for(auto i:tmp->ptr.func->closure)
bfs.push(i);
for(auto i:tmp->ptr.func->default_para)
if(i)
bfs.push(i);
}
}
return;
}
void nasal_gc::sweep()
{
for(auto i:memory)
{
if(i->mark==GC_UNCOLLECTED)
{
switch(i->type)
{
case vm_str: i->ptr.str->clear(); break;
case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash:i->ptr.hash->elems.clear();break;
case vm_func:i->ptr.func->clear(); break;
}
free_list[i->type].push(i);
i->mark=GC_COLLECTED;
}
else if(i->mark==GC_FOUND)
i->mark=GC_UNCOLLECTED;
}
return;
}
void nasal_gc::gc_init(std::vector<double>& nums,std::vector<std::string>& strs)
{
for(int i=vm_num;i<vm_type_size;++i)
for(int j=0;j<increment[i];++j)
{
nasal_val* tmp=new nasal_val(i);
memory.push_back(tmp);
free_list[i].push(tmp);
}
stack_top=val_stack; // set stack_top to val_stack
zero_addr=new nasal_val(vm_num); // init constant 0
zero_addr->ptr.num=0;
one_addr=new nasal_val(vm_num); // init constant 1
one_addr->ptr.num=1;
nil_addr=new nasal_val(vm_nil); // init nil
*val_stack=nil_addr; // the first space will not store any values,but gc checks
// init constant numbers
num_addrs.resize(nums.size());
for(int i=0;i<nums.size();++i)
{
num_addrs[i]=new nasal_val(vm_num);
num_addrs[i]->ptr.num=nums[i];
}
// init constant strings
str_addrs.resize(strs.size());
for(int i=0;i<strs.size();++i)
{
str_addrs[i]=new nasal_val(vm_str);
*str_addrs[i]->ptr.str=strs[i];
}
return;
}
void nasal_gc::gc_clear()
{
for(auto i:memory)
delete i;
memory.clear();
for(int i=0;i<vm_type_size;++i)
while(!free_list[i].empty())
free_list[i].pop();
global.clear();
local.clear();
slice_stack.clear();
delete nil_addr;
delete one_addr;
delete zero_addr;
for(auto i:num_addrs)
delete i;
num_addrs.clear();
for(auto i:str_addrs)
delete i;
str_addrs.clear();
return;
}
nasal_val* nasal_gc::gc_alloc(int type)
{
if(free_list[type].empty())
{
mark();
sweep();
}
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_val* ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
nasal_val* nasal_gc::builtin_alloc(int type)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc_alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_val* ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
#endif

View File

@@ -1,129 +0,0 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
class nasal_import
{
private:
int error;
nasal_lexer import_lex;
nasal_parse import_par;
nasal_ast import_ast;
std::vector<std::string> filename_table;
void die(std::string&,const char*);
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:
int get_error(){return error;}
void link(nasal_ast&);
nasal_ast& get_root(){return import_ast;}
};
void nasal_import::die(std::string& filename,const char* error_stage)
{
++error;
std::cout<<">> [import] in <\""<<filename<<"\">: error(s) occurred in "<<error_stage<<".\n";
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_callf)
return false;
if(ref_vec[1].get_children().size()!=1 || ref_vec[1].get_children()[0].get_type()!=ast_str)
return false;
return true;
}
bool nasal_import::check_exist(std::string& file)
{
// avoid importing the same file
for(auto& fname:filename_table)
if(file==fname)
return true;
filename_table.push_back(file);
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
{
// add children of add_root to the back of root
for(auto& i:add_root.get_children())
root.add_child(std::move(i));
return;
}
nasal_ast nasal_import::file_import(nasal_ast& node)
{
// initializing
nasal_ast tmp(0,ast_root);
// get filename and set node to ast_null
std::string filename=node.get_children()[1].get_children()[0].get_str();
node.clear();
// avoid infinite loading loop
if(check_exist(filename))
return tmp;
// start importing...
import_lex.openfile(filename);
import_lex.scanner();
if(import_lex.get_error())
{
die(filename,"lexer");
return tmp;
}
import_par.set_toklist(import_lex.get_token_list());
import_lex.get_token_list().clear();
import_par.main_process();
if(import_par.get_error())
{
die(filename,"parser");
return tmp;
}
tmp=std::move(import_par.get_root());
import_par.get_root().clear();
// check if tmp has 'import'
return load(tmp);
}
nasal_ast nasal_import::load(nasal_ast& root)
{
nasal_ast new_root(0,ast_root);
for(auto& i:root.get_children())
if(check_import(i))
linker(new_root,file_import(i));
// add root to the back of new_root
linker(new_root,std::move(root));
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;
}
#endif

View File

@@ -1,371 +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_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
#define 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_num,tok_str,tok_id,
tok_for,tok_forindex,tok_foreach,tok_while,
tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve,
tok_lbracket,tok_rbracket,
tok_lbrace,tok_rbrace,
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
tok_eq,
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
tok_eof
};
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_ret },
{"if" ,tok_if },
{"elsif" ,tok_elsif },
{"else" ,tok_else },
{"nil" ,tok_nil },
{"(" ,tok_lcurve },
{")" ,tok_rcurve },
{"[" ,tok_lbracket },
{"]" ,tok_rbracket },
{"{" ,tok_lbrace },
{"}" ,tok_rbrace },
{";" ,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_eq },
{"+=" ,tok_addeq },
{"-=" ,tok_subeq },
{"*=" ,tok_multeq },
{"/=" ,tok_diveq },
{"~=" ,tok_lnkeq },
{"==" ,tok_cmpeq },
{"!=" ,tok_neq },
{"<" ,tok_less },
{"<=" ,tok_leq },
{">" ,tok_grt },
{">=" ,tok_geq },
{nullptr ,-1 }
};
struct token
{
int line;
int type;
std::string str;
token(int l=0,int t=tok_null,std::string s=""){line=l;type=t;str=s;return;}
};
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;
int get_tok_type(std::string&);
void die(const char*);
std::string id_gen();
std::string num_gen();
std::string str_gen();
public:
void openfile(std::string&);
void scanner();
void print_token();
int get_error(){return error;}
std::vector<token>& get_token_list(){return token_list;}
};
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();
return;
}
int nasal_lexer::get_tok_type(std::string& tk_str)
{
for(int i=0;token_table[i].str;++i)
if(tk_str==token_table[i].str)
return token_table[i].tok_type;
return tok_null;
}
void nasal_lexer::die(const char* error_info)
{
++error;
std::cout<<">> [lexer] line "<<line<<" column "<<line_code.length()<<": \n"<<line_code<<"\n";
for(auto i:line_code)
std::cout<<(i=='\t'?'\t':' ');
std::cout<<"^"<<error_info<<'\n';
return;
}
std::string nasal_lexer::id_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::num_gen()
{
// generate hex number
if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='x')
{
std::string 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("incorrect number.");
return token_str;
}
// generate oct number
else if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='o')
{
std::string 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("incorrect number.");
return token_str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string token_str="";
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
if(ptr<res_size && res[ptr]=='.')
{
token_str+=res[ptr++];
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("incorrect number.");
return "0";
}
}
if(ptr<res_size && (res[ptr]=='e' || res[ptr]=='E'))
{
token_str+=res[ptr++];
if(ptr<res_size && (res[ptr]=='-' || res[ptr]=='+'))
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("incorrect number.");
return "0";
}
}
line_code+=token_str;
return token_str;
}
std::string nasal_lexer::str_gen()
{
std::string token_str="";
char str_begin=res[ptr];
line_code+=str_begin;
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)
{
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;
}
continue;
}
token_str+=res[ptr];
}
// check if this string ends with a " or '
if(ptr++>=res_size)
die("get EOF when generating string.");
if(str_begin=='`' && token_str.length()!=1)
die("\'`\' is used for string that includes one character.");
return token_str;
}
void nasal_lexer::scanner()
{
token_list.clear();
line=1;
ptr=0;
line_code="";
res_size=res.size();
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="";
}
}
if(ptr>=res_size) break;
if(IS_IDENTIFIER(res[ptr]))
{
token_str=id_gen();
token new_token(line,get_tok_type(token_str),token_str);
if(!new_token.type)
new_token.type=tok_id;
token_list.push_back(new_token);
}
else if(IS_DIGIT(res[ptr]))
{
token_str=num_gen();
token_list.push_back({line,tok_num,token_str});
}
else if(IS_STRING(res[ptr]))
{
token_str=str_gen();
token_list.push_back({line,tok_str,token_str});
}
else if(IS_SINGLE_OPERATOR(res[ptr]))
{
token_str=res[ptr];
line_code+=res[ptr];
int type=get_tok_type(token_str);
if(!type)
die("incorrect operator.");
token_list.push_back({line,type,token_str});
++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_list.push_back({line,get_tok_type(token_str),token_str});
}
else if(IS_CALC_OPERATOR(res[ptr]))
{
// get calculation operator
token_str=res[ptr++];
if(ptr<res_size && res[ptr]=='=')
token_str+=res[ptr++];
line_code+=token_str;
token_list.push_back({line,get_tok_type(token_str),token_str});
}
else if(IS_NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res_size && res[ptr]!='\n');
else
{
line_code+=res[ptr++];
die("unknown character.");
}
}
token_list.push_back({line,tok_eof,""});
res.clear();
return;
}
void nasal_lexer::print_token()
{
for(auto tok:token_list)
std::cout<<"("<<tok.line<<" | "<<tok.str<<")\n";
return;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,850 +0,0 @@
#ifndef __NASAL_VM_H__
#define __NASAL_VM_H__
class nasal_vm
{
private:
/* reference from nasal_gc */
nasal_val**& stack_top;// stack top
/* values of nasal_vm */
uint32_t pc; // program counter
bool loop_mark;// when mark is false,break the main loop
std::stack<int> ret; // ptr stack stores address for function to return
std::stack<int> counter; // iterator stack for forindex/foreach
std::vector<std::string> str_table;// symbols used in process
std::vector<void (nasal_vm::*)()>
exec_code;//function pointer
std::vector<uint32_t> imm; // immediate number
nasal_val** mem_addr; // used for mem_call
nasal_gc gc; // garbage collector
void die(std::string);
bool condition(nasal_val*);
void opr_nop();
void opr_intg();
void opr_intl();
void opr_offset();
void opr_loadg();
void opr_loadl();
void opr_pnum();
void opr_pone();
void opr_pzero();
void opr_pnil();
void opr_pstr();
void opr_newv();
void opr_newh();
void opr_newf();
void opr_happ();
void opr_para();
void opr_defpara();
void opr_dynpara();
void opr_unot();
void opr_usub();
void opr_add();
void opr_sub();
void opr_mul();
void opr_div();
void opr_lnk();
void opr_addeq();
void opr_subeq();
void opr_muleq();
void opr_diveq();
void opr_lnkeq();
void opr_meq();
void opr_eq();
void opr_neq();
void opr_less();
void opr_leq();
void opr_grt();
void opr_geq();
void opr_pop();
void opr_jmp();
void opr_jt();
void opr_jf();
void opr_counter();
void opr_cntpop();
void opr_findex();
void opr_feach();
void opr_callg();
void opr_calll();
void opr_callv();
void opr_callvi();
void opr_callh();
void opr_callfv();
void opr_callfh();
void opr_callb();
void opr_slcbegin();
void opr_slcend();
void opr_slc();
void opr_slc2();
void opr_mcallg();
void opr_mcalll();
void opr_mcallv();
void opr_mcallh();
void opr_ret();
public:
nasal_vm():stack_top(gc.stack_top){};
void init(
std::vector<std::string>&,
std::vector<double>&,
std::vector<opcode>&);
void clear();
void run();
};
void nasal_vm::init(
std::vector<std::string>& strs,
std::vector<double>& nums,
std::vector<opcode>& exec)
{
gc.gc_init(nums,strs);
gc.val_stack[STACK_MAX_DEPTH-1]=nullptr;
str_table=strs; // get constant strings & symbols
void (nasal_vm::*opr_table[])()=
{
&nasal_vm::opr_nop,
&nasal_vm::opr_intg,
&nasal_vm::opr_intl,
&nasal_vm::opr_offset,
&nasal_vm::opr_loadg,
&nasal_vm::opr_loadl,
&nasal_vm::opr_pnum,
&nasal_vm::opr_pone,
&nasal_vm::opr_pzero,
&nasal_vm::opr_pnil,
&nasal_vm::opr_pstr,
&nasal_vm::opr_newv,
&nasal_vm::opr_newh,
&nasal_vm::opr_newf,
&nasal_vm::opr_happ,
&nasal_vm::opr_para,
&nasal_vm::opr_defpara,
&nasal_vm::opr_dynpara,
&nasal_vm::opr_unot,
&nasal_vm::opr_usub,
&nasal_vm::opr_add,
&nasal_vm::opr_sub,
&nasal_vm::opr_mul,
&nasal_vm::opr_div,
&nasal_vm::opr_lnk,
&nasal_vm::opr_addeq,
&nasal_vm::opr_subeq,
&nasal_vm::opr_muleq,
&nasal_vm::opr_diveq,
&nasal_vm::opr_lnkeq,
&nasal_vm::opr_meq,
&nasal_vm::opr_eq,
&nasal_vm::opr_neq,
&nasal_vm::opr_less,
&nasal_vm::opr_leq,
&nasal_vm::opr_grt,
&nasal_vm::opr_geq,
&nasal_vm::opr_pop,
&nasal_vm::opr_jmp,
&nasal_vm::opr_jt,
&nasal_vm::opr_jf,
&nasal_vm::opr_counter,
&nasal_vm::opr_cntpop,
&nasal_vm::opr_findex,
&nasal_vm::opr_feach,
&nasal_vm::opr_callg,
&nasal_vm::opr_calll,
&nasal_vm::opr_callv,
&nasal_vm::opr_callvi,
&nasal_vm::opr_callh,
&nasal_vm::opr_callfv,
&nasal_vm::opr_callfh,
&nasal_vm::opr_callb,
&nasal_vm::opr_slcbegin,
&nasal_vm::opr_slcend,
&nasal_vm::opr_slc,
&nasal_vm::opr_slc2,
&nasal_vm::opr_mcallg,
&nasal_vm::opr_mcalll,
&nasal_vm::opr_mcallv,
&nasal_vm::opr_mcallh,
&nasal_vm::opr_ret
};
for(auto& i:exec)
{
exec_code.push_back(opr_table[i.op]);
imm.push_back(i.num);
}
loop_mark=true; // set loop mark to true
return;
}
void nasal_vm::clear()
{
gc.gc_clear();
while(!ret.empty())
ret.pop();
while(!counter.empty())
counter.pop();
str_table.clear();
exec_code.clear();
return;
}
void nasal_vm::die(std::string str)
{
printf(">> [vm] 0x%.8x: %s\n",pc,str.c_str());
loop_mark=false;
return;
}
bool nasal_vm::condition(nasal_val* val_addr)
{
if(val_addr->type==vm_num)
return val_addr->ptr.num;
else if(val_addr->type==vm_str)
{
std::string& str=*val_addr->ptr.str;
double num=str2num(str.c_str());
if(std::isnan(num))
return str.empty();
return num;
}
return false;
}
void nasal_vm::opr_nop()
{
loop_mark=false;
return;
}
void nasal_vm::opr_intg()
{
gc.global.resize(imm[pc],gc.nil_addr);
return;
}
void nasal_vm::opr_intl()
{
stack_top[0]->ptr.func->closure.resize(imm[pc],gc.nil_addr);
return;
}
void nasal_vm::opr_offset()
{
stack_top[0]->ptr.func->offset=imm[pc];
return;
}
void nasal_vm::opr_loadg()
{
gc.global[imm[pc]]=(stack_top--)[0];
return;
}
void nasal_vm::opr_loadl()
{
gc.local.back()[imm[pc]]=(stack_top--)[0];
return;
}
void nasal_vm::opr_pnum()
{
(++stack_top)[0]=gc.num_addrs[imm[pc]];
return;
}
void nasal_vm::opr_pone()
{
(++stack_top)[0]=gc.one_addr;
return;
}
void nasal_vm::opr_pzero()
{
(++stack_top)[0]=gc.zero_addr;
return;
}
void nasal_vm::opr_pnil()
{
(++stack_top)[0]=gc.nil_addr;
return;
}
void nasal_vm::opr_pstr()
{
(++stack_top)[0]=gc.str_addrs[imm[pc]];
return;
}
void nasal_vm::opr_newv()
{
nasal_val* vec_addr=gc.gc_alloc(vm_vec);
nasal_val** begin=stack_top-imm[pc]+1;
auto& vec=vec_addr->ptr.vec->elems;// stack_top-imm[pc] stores the vector
vec.resize(imm[pc]);
for(int i=0;i<imm[pc];++i)
vec[i]=begin[i];
begin[0]=vec_addr;
stack_top=begin;
return;
}
void nasal_vm::opr_newh()
{
(++stack_top)[0]=gc.gc_alloc(vm_hash);
return;
}
void nasal_vm::opr_newf()
{
nasal_val* val=gc.gc_alloc(vm_func);
val->ptr.func->entry=imm[pc];
if(gc.local.empty())
val->ptr.func->closure.push_back(gc.nil_addr);// me
else
val->ptr.func->closure=gc.local.back();// local contains 'me'
(++stack_top)[0]=val;
return;
}
void nasal_vm::opr_happ()
{
nasal_val* val=stack_top[0];
(--stack_top)[0]->ptr.hash->elems[str_table[imm[pc]]]=val;
return;
}
void nasal_vm::opr_para()
{
nasal_func* func=stack_top[0]->ptr.func;
int size=func->key_table.size();
func->key_table[str_table[imm[pc]]]=size;
func->default_para.push_back(nullptr);
return;
}
void nasal_vm::opr_defpara()
{
nasal_val* def_val=stack_top[0];
nasal_func* func=(--stack_top)[0]->ptr.func;
int size=func->key_table.size();
func->key_table[str_table[imm[pc]]]=size;
func->default_para.push_back(def_val);
return;
}
void nasal_vm::opr_dynpara()
{
stack_top[0]->ptr.func->dynpara=imm[pc];
return;
}
void nasal_vm::opr_unot()
{
nasal_val* val=stack_top[0];
int type=val->type;
if(type==vm_nil)
stack_top[0]=gc.one_addr;
else if(type==vm_num)
stack_top[0]=val->ptr.num?gc.zero_addr:gc.one_addr;
else if(type==vm_str)
{
double num=str2num(val->ptr.str->c_str());
if(std::isnan(num))
stack_top[0]=val->ptr.str->empty()?gc.one_addr:gc.zero_addr;
else
stack_top[0]=num?gc.zero_addr:gc.one_addr;
}
else
die("unot: incorrect value type");
return;
}
void nasal_vm::opr_usub()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=-stack_top[0]->to_number();
stack_top[0]=new_val;
return;
}
void nasal_vm::opr_add()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[-1]->to_number()+stack_top[0]->to_number();
(--stack_top)[0]=new_val;
return;
}
void nasal_vm::opr_sub()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[-1]->to_number()-stack_top[0]->to_number();
(--stack_top)[0]=new_val;
return;
}
void nasal_vm::opr_mul()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[-1]->to_number()*stack_top[0]->to_number();
(--stack_top)[0]=new_val;
return;
}
void nasal_vm::opr_div()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=stack_top[-1]->to_number()/stack_top[0]->to_number();
(--stack_top)[0]=new_val;
return;
}
void nasal_vm::opr_lnk()
{
nasal_val* new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=stack_top[-1]->to_string()+stack_top[0]->to_string();
(--stack_top)[0]=new_val;
return;
}
void nasal_vm::opr_addeq()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()+stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_subeq()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()-stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_muleq()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()*stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_diveq()
{
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()/stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_lnkeq()
{
nasal_val* new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=mem_addr[0]->to_string()+stack_top[-1]->to_string();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_meq()
{
mem_addr[0]=(--stack_top)[0];
return;
}
void nasal_vm::opr_eq()
{
nasal_val* val2=stack_top[0];
nasal_val* val1=(--stack_top)[0];
int a_type=val1->type;
int b_type=val2->type;
if(a_type==vm_nil && b_type==vm_nil)
stack_top[0]=gc.one_addr;
else if(a_type==vm_str && b_type==vm_str)
stack_top[0]=(*val1->ptr.str==*val2->ptr.str)?gc.one_addr:gc.zero_addr;
else if(a_type==vm_num || b_type==vm_num)
stack_top[0]=(val1->to_number()==val2->to_number())?gc.one_addr:gc.zero_addr;
else
stack_top[0]=(val1==val2)?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_neq()
{
nasal_val* val2=stack_top[0];
nasal_val* val1=(--stack_top)[0];
int a_type=val1->type;
int b_type=val2->type;
if(a_type==vm_nil && b_type==vm_nil)
stack_top[0]=gc.zero_addr;
else if(a_type==vm_str && b_type==vm_str)
stack_top[0]=(*val1->ptr.str!=*val2->ptr.str)?gc.one_addr:gc.zero_addr;
else if(a_type==vm_num || b_type==vm_num)
stack_top[0]=(val1->to_number()!=val2->to_number())?gc.one_addr:gc.zero_addr;
else
stack_top[0]=(val1!=val2)?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_less()
{
--stack_top;
stack_top[0]=(stack_top[0]->to_number()<stack_top[1]->to_number())?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_leq()
{
--stack_top;
stack_top[0]=(stack_top[0]->to_number()<=stack_top[1]->to_number())?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_grt()
{
--stack_top;
stack_top[0]=(stack_top[0]->to_number()>stack_top[1]->to_number())?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_geq()
{
--stack_top;
stack_top[0]=(stack_top[0]->to_number()>=stack_top[1]->to_number())?gc.one_addr:gc.zero_addr;
return;
}
void nasal_vm::opr_pop()
{
--stack_top;
return;
}
void nasal_vm::opr_jmp()
{
pc=imm[pc]-1;
return;
}
void nasal_vm::opr_jt()
{
if(condition(stack_top[0]))
pc=imm[pc]-1;
return;
}
void nasal_vm::opr_jf()
{
if(!condition(stack_top[0]))
pc=imm[pc]-1;
--stack_top;
return;
}
void nasal_vm::opr_counter()
{
counter.push(-1);
if(stack_top[0]->type!=vm_vec)
die("cnt: must use vector in forindex/foreach");
return;
}
void nasal_vm::opr_cntpop()
{
counter.pop();
return;
}
void nasal_vm::opr_findex()
{
if(++counter.top()>=stack_top[0]->ptr.vec->elems.size())
{
pc=imm[pc]-1;
return;
}
(++stack_top)[0]=gc.gc_alloc(vm_num);
stack_top[0]->ptr.num=counter.top();
return;
}
void nasal_vm::opr_feach()
{
std::vector<nasal_val*>& ref=stack_top[0]->ptr.vec->elems;
if(++counter.top()>=ref.size())
{
pc=imm[pc]-1;
return;
}
(++stack_top)[0]=ref[counter.top()];
return;
}
void nasal_vm::opr_callg()
{
(++stack_top)[0]=gc.global[imm[pc]];
return;
}
void nasal_vm::opr_calll()
{
(++stack_top)[0]=gc.local.back()[imm[pc]];
return;
}
void nasal_vm::opr_callv()
{
nasal_val* val=stack_top[0];
nasal_val* vec_addr=(--stack_top)[0];
int type=vec_addr->type;
if(type==vm_vec)
{
stack_top[0]=vec_addr->ptr.vec->get_val(val->to_number());
if(!stack_top[0])
die("callv: index out of range:"+num2str(val->to_number()));
}
else if(type==vm_hash)
{
if(val->type!=vm_str)
{
die("callv: must use string as the key");
return;
}
stack_top[0]=vec_addr->ptr.hash->get_val(*val->ptr.str);
if(!stack_top[0])
{
die("callv: cannot find member \""+*val->ptr.str+"\" of this hash");
return;
}
if(stack_top[0]->type==vm_func)
stack_top[0]->ptr.func->closure[0]=val;// me
}
else if(type==vm_str)
{
std::string& str=*vec_addr->ptr.str;
int num=val->to_number();
int str_size=str.length();
if(num<-str_size || num>=str_size)
{
die("callv: index out of range:"+num2str(val->to_number()));
return;
}
stack_top[0]=gc.gc_alloc(vm_num);
stack_top[0]->ptr.num=(str[num>=0? num:num+str_size]);
}
else
die("callv: must call a vector/hash/string");
return;
}
void nasal_vm::opr_callvi()
{
nasal_val* val=stack_top[0];
if(val->type!=vm_vec)
{
die("callvi: multi-definition/multi-assignment must use a vector");
return;
}
// cannot use operator[],because this may cause overflow
(++stack_top)[0]=val->ptr.vec->get_val(imm[pc]);
if(!stack_top[0])
die("callvi: index out of range:"+num2str(imm[pc]));
return;
}
void nasal_vm::opr_callh()
{
nasal_val* val=stack_top[0];
if(val->type!=vm_hash)
{
die("callh: must call a hash");
return;
}
stack_top[0]=val->ptr.hash->get_val(str_table[imm[pc]]);
if(!stack_top[0])
{
die("callh: member \""+str_table[imm[pc]]+"\" does not exist");
return;
}
if(stack_top[0]->type==vm_func)
stack_top[0]->ptr.func->closure[0]=val;// me
return;
}
void nasal_vm::opr_callfv()
{
// get parameter list and function value
int args_size=imm[pc];
nasal_val** vec=stack_top-args_size+1;
nasal_val* func_addr=vec[-1];
if(func_addr->type!=vm_func)
{
die("callfv: must call a function");
return;
}
// push new local scope
auto& ref_func=*func_addr->ptr.func;
gc.local.push_back(ref_func.closure);
// load parameters
auto& ref_default=ref_func.default_para;
auto& ref_closure=gc.local.back();
int offset=ref_func.offset;
int para_size=ref_func.key_table.size();
// load arguments
if(args_size<para_size && !ref_default[args_size])
{
// if the first default value is not nullptr,then values after it are not nullptr
die("callfv: lack argument(s)");
return;
}
// if args_size>para_size,for 0 to args_size will cause corruption
for(int i=0;i<para_size;++i)
ref_closure[i+offset]=(i<args_size)?vec[i]:ref_default[i];
// load dynamic argument if args_size>=para_size
if(ref_func.dynpara>=0)
{
nasal_val* vec_addr=gc.gc_alloc(vm_vec);
for(int i=para_size;i<args_size;++i)
vec_addr->ptr.vec->elems.push_back(vec[i]);
ref_closure[para_size+offset]=vec_addr;
}
stack_top-=args_size;// pop arguments
ret.push(pc);
pc=ref_func.entry-1;
return;
}
void nasal_vm::opr_callfh()
{
// get parameter list and function value
auto& ref_hash=stack_top[0]->ptr.hash->elems;
nasal_val* func_addr=stack_top[-1];
if(func_addr->type!=vm_func)
{
die("callfh: must call a function");
return;
}
// push new local scope
auto& ref_func=*func_addr->ptr.func;
gc.local.push_back(ref_func.closure);
// load parameters
auto& ref_default=ref_func.default_para;
auto& ref_closure=gc.local.back();
if(ref_func.dynpara>=0)
{
die("callfh: special call cannot use dynamic argument");
return;
}
int offset=ref_func.offset;
for(auto& i:ref_func.key_table)
{
if(ref_hash.count(i.first))
ref_closure[i.second+offset]=ref_hash[i.first];
else if(ref_default[i.second])
ref_closure[i.second+offset]=ref_default[i.second];
else
{
die("callfh: lack argument(s): \""+i.first+"\"");
return;
}
}
--stack_top;// pop hash
ret.push(pc);
pc=ref_func.entry-1;
return;
}
void nasal_vm::opr_callb()
{
loop_mark=(++stack_top)[0]=(*builtin_func[imm[pc]].func)(gc.local.back(),gc);
return;
}
void nasal_vm::opr_slcbegin()
{
gc.slice_stack.push_back(gc.gc_alloc(vm_vec));
if(stack_top[0]->type!=vm_vec)
die("slcbegin: must slice a vector");
return;
}
void nasal_vm::opr_slcend()
{
stack_top[0]=gc.slice_stack.back();
gc.slice_stack.pop_back();
return;
}
void nasal_vm::opr_slc()
{
nasal_val* val=(stack_top--)[0];
nasal_val* res=stack_top[0]->ptr.vec->get_val(val->to_number());
if(!res)
die("slc: index out of range:"+num2str(val->to_number()));
gc.slice_stack.back()->ptr.vec->elems.push_back(res);
return;
}
void nasal_vm::opr_slc2()
{
nasal_val* val2=(stack_top--)[0];
nasal_val* val1=(stack_top--)[0];
std::vector<nasal_val*>& ref=stack_top[0]->ptr.vec->elems;
std::vector<nasal_val*>& aim=gc.slice_stack.back()->ptr.vec->elems;
int type1=val1->type,type2=val2->type;
int num1=val1->to_number();
int num2=val2->to_number();
int ref_size=ref.size();
if(type1==vm_nil && type2==vm_nil)
{
num1=0;
num2=ref_size-1;
}
else if(type1==vm_nil && type2!=vm_nil)
num1=num2<0? -ref_size:0;
else if(type1!=vm_nil && type2==vm_nil)
num2=num1<0? -1:ref_size-1;
if(num1>=num2)
{
die("slc2: begin index must be less than end index");
return;
}
else if(num1<-ref_size || num1>=ref_size)
{
die("slc2: begin index out of range: "+num2str(num1));
return;
}
else if(num2<-ref_size || num2>=ref_size)
{
die("slc2: end index out of range: "+num2str(num2));
return;
}
for(int i=num1;i<=num2;++i)
aim.push_back(i>=0?ref[i]:ref[i+ref_size]);
return;
}
void nasal_vm::opr_mcallg()
{
mem_addr=&gc.global[imm[pc]];
(++stack_top)[0]=mem_addr[0];
return;
}
void nasal_vm::opr_mcalll()
{
mem_addr=&gc.local.back()[imm[pc]];
(++stack_top)[0]=mem_addr[0];
return;
}
void nasal_vm::opr_mcallv()
{
nasal_val* val=stack_top[0];
nasal_val* vec_addr=(--stack_top)[0];
int type=vec_addr->type;
if(type==vm_vec)
{
mem_addr=vec_addr->ptr.vec->get_mem(val->to_number());
if(!mem_addr)
die("mcallv: index out of range:"+num2str(val->to_number()));
}
else if(type==vm_hash)
{
if(val->type!=vm_str)
{
die("mcallv: must use string as the key");
return;
}
nasal_hash& ref=*vec_addr->ptr.hash;
std::string& str=*val->ptr.str;
mem_addr=ref.get_mem(str);
if(!mem_addr)
{
ref.elems[str]=gc.nil_addr;
mem_addr=ref.get_mem(str);
}
}
else
die("mcallv: cannot get memory space in other types");
return;
}
void nasal_vm::opr_mcallh()
{
nasal_val* hash_addr=stack_top[0];
if(hash_addr->type!=vm_hash)
{
die("mcallh: must call a hash");
return;
}
nasal_hash& ref=*hash_addr->ptr.hash;
std::string& str=str_table[imm[pc]];
mem_addr=ref.get_mem(str);
if(!mem_addr) // create a new key
{
ref.elems[str]=gc.nil_addr;
mem_addr=ref.get_mem(str);
}
return;
}
void nasal_vm::opr_ret()
{
gc.local.pop_back();// delete local scope
pc=ret.top();ret.pop();// fetch pc
(--stack_top)[0]->ptr.func->closure[0]=gc.nil_addr;// set 'me' to nil
stack_top[0]=stack_top[1];// rewrite nasal_func with returned value
return;
}
void nasal_vm::run()
{
clock_t begin_time=clock();
for(pc=0;loop_mark&&!gc.val_stack[STACK_MAX_DEPTH-1];++pc)
(this->*exec_code[pc])();
if(gc.val_stack[STACK_MAX_DEPTH-1])
die("stack overflow");
std::cout<<">> [vm] process exited after "<<((double)(clock()-begin_time))/CLOCKS_PER_SEC<<"s.\n";
return;
}
#endif

152
props.nas
View File

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

503
src/ast_dumper.cpp Normal file
View File

@@ -0,0 +1,503 @@
#include "ast_dumper.h"
#include <iostream>
namespace nasal {
bool ast_dumper::visit_use_stmt(use_stmt* node) {
dump_indent();
std::cout << "use" << format_location(node->get_location());
push_indent();
for(auto i : node->get_path()) {
if (i==node->get_path().back()) {
set_last();
}
i->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_null_expr(null_expr* node) {
dump_indent();
std::cout << "null" << format_location(node->get_location());
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();
if (node->is_definition()) {
std::cout << "iterator_definition";
} else {
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;
}
}

87
src/ast_dumper.h Normal file
View File

@@ -0,0 +1,87 @@
#pragma once
#include "ast_visitor.h"
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
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_use_stmt(use_stmt*) override;
bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override;
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);
}
};
}

245
src/ast_visitor.cpp Normal file
View File

@@ -0,0 +1,245 @@
#include "ast_visitor.h"
namespace nasal {
bool ast_visitor::visit_expr(expr* node) {
node->accept(this);
return true;
}
bool ast_visitor::visit_use_stmt(use_stmt* node) {
for(auto i : node->get_path()) {
i->accept(this);
}
return true;
}
bool ast_visitor::visit_call(call* node) {
node->accept(this);
return true;
}
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;
}
}

48
src/ast_visitor.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include "nasal_ast.h"
namespace nasal {
class ast_visitor {
public:
virtual bool visit_expr(expr*);
virtual bool visit_use_stmt(use_stmt*);
virtual bool visit_call(call*);
virtual bool visit_null_expr(null_expr*);
virtual bool visit_nil_expr(nil_expr*);
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*);
};
}

164
src/bits_lib.cpp Normal file
View File

@@ -0,0 +1,164 @@
#include "bits_lib.h"
namespace nasal {
var builtin_u32xor(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) ^
static_cast<u32>(local[2].num())
));
}
var builtin_u32and(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
));
}
var builtin_u32or(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) |
static_cast<u32>(local[2].num())
));
}
var builtin_u32nand(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(~(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
)));
}
var builtin_u32not(context* ctx, gc* ngc) {
return var::num(static_cast<f64>(
~static_cast<u32>(ctx->localr[1].num())
));
}
var builtin_fld(context* ctx, gc* ngc) {
// bits.fld(s,0,3);
// if s stores 10100010(162)
// will get 101(5)
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::fld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("bits::fld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
if (bit+len>8*str.str().length()) {
return nas_err("bits::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(context* ctx, 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)
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::sfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("bits::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("bits::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(context* ctx, gc* ngc) {
// bits.setfld(s,0,8,69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
auto value = local[4];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::setfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
return nas_err("bits::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("bits::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(context* ctx, gc* ngc) {
var length = ctx->localr[1];
if (length.type!=vm_num || length.num()<=0) {
return nas_err("bits::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(context*, gc*);
var builtin_u32and(context*, gc*);
var builtin_u32or(context*, gc*);
var builtin_u32nand(context*, gc*);
var builtin_u32not(context*, gc*);
var builtin_fld(context*, gc*);
var builtin_sfld(context*, gc*);
var builtin_setfld(context*, gc*);
var builtin_buf(context*, gc*);
extern nasal_builtin_table bits_native[];
}

141
src/coroutine.cpp Normal file
View File

@@ -0,0 +1,141 @@
#include "coroutine.h"
namespace nasal {
var builtin_cocreate(context* ctx, 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
// +-------------+
// ```
auto coroutine_function = ctx->localr[1];
if (coroutine_function.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"
);
}
auto coroutine_object = ngc->alloc(vm_co);
auto& coroutine = coroutine_object.co();
coroutine.ctx.pc = coroutine_function.func().entry-1;
coroutine.ctx.top[0] = nil;
coroutine.ctx.localr = coroutine.ctx.top+1;
coroutine.ctx.top = coroutine.ctx.localr +
coroutine_function.func().local_size;
coroutine.ctx.localr[0] = coroutine_function.func().local[0];
// store old upvalr on stack
coroutine.ctx.top[0] = nil;
coroutine.ctx.top++;
// store old localr on stack
coroutine.ctx.top[0] = var::addr(nullptr);
coroutine.ctx.top++;
// store old pc on stack
// set to zero to make op_ret recognizing this as coroutine function
coroutine.ctx.top[0] = var::ret(0);
// make sure the coroutine function can use correct upvalues
coroutine.ctx.funcr = coroutine_function;
coroutine.status = nas_co::status::suspended;
return coroutine_object;
}
var builtin_coresume(context* ctx, gc* ngc) {
if (ngc->cort) {
return nas_err(
"coroutine::resume",
"cannot start another coroutine when one is running"
);
}
auto main_local_frame = ctx->localr;
auto coroutine_object = main_local_frame[1];
// return nil if is not a coroutine object or coroutine exited
if (coroutine_object.type!=vm_co ||
coroutine_object.co().status==nas_co::status::dead) {
return nil;
}
// change to coroutine context
ngc->context_change(&coroutine_object.co());
// fetch coroutine's stack top and return
// then coroutine's stack top will catch this return value
// so the coroutine's stack top in fact is not changed
if (ngc->running_context->top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc->running_context->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 main_local_frame[2];
}
var builtin_coyield(context* ctx, gc* ngc) {
if (!ngc->cort) {
return nas_err("coroutine::yield", "no coroutine is running");
}
// get coroutine local frame
auto coroutine_local_frame = ctx->localr;
// vm context will set to main context
ngc->context_reserve();
// 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 coroutine_local_frame[1];
}
var builtin_costatus(context* ctx, gc* ngc) {
auto coroutine_object = ctx->localr[1];
if (coroutine_object.type!=vm_co) {
return ngc->newstr("error");
}
switch(coroutine_object.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(context* ctx, 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(context*, gc*);
var builtin_coresume(context*, gc*);
var builtin_coyield(context*, gc*);
var builtin_costatus(context*, gc*);
var builtin_corun(context*, gc*);
extern nasal_builtin_table coroutine_native[];
}

140
src/dylib_lib.cpp Normal file
View File

@@ -0,0 +1,140 @@
#include "dylib_lib.h"
namespace nasal {
const auto dynamic_library_type_name = "dylib";
const auto function_address_type_name = "faddr";
void dynamic_library_destructor(void* pointer) {
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(pointer));
#else
dlclose(pointer);
#endif
}
var builtin_dlopen(context* ctx, gc* ngc) {
auto dlname = ctx->localr[1];
if (dlname.type!=vm_str) {
return nas_err("dylib::dlopen", "\"libname\" must be string");
}
// get library pointer
#ifdef _WIN32
wchar_t* wide_string = new wchar_t[dlname.str().size()+1];
if (!wide_string) {
return nas_err("dylib::dlopen", "malloc failed");
}
memset(wide_string, 0, sizeof(wchar_t) * dlname.str().size() + 1);
mbstowcs(wide_string, dlname.str().c_str(), dlname.str().size() + 1);
// load library by using wide string name
void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str());
delete []wide_string;
#else
void* dynamic_library_pointer = dlopen(
dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY
);
#endif
// check library pointer and insert into returned hashmap
if (!dynamic_library_pointer) {
return nas_err("dylib::dlopen",
"cannot open dynamic lib <" + dlname.str() + ">"
);
}
auto return_hash = ngc->temp = ngc->alloc(vm_hash);
auto library_object = ngc->alloc(vm_obj);
library_object.ghost().set(
dynamic_library_type_name,
dynamic_library_destructor,
dynamic_library_pointer
);
return_hash.hash().elems["lib"] = library_object;
// get "get" function, to get the register table
#ifdef _WIN32
void* register_table_get_function = reinterpret_cast<void*>(GetProcAddress(
static_cast<HMODULE>(library_object.ghost().pointer), "get"
));
#else
void* register_table_get_function = dlsym(
library_object.ghost().pointer, "get"
);
#endif
if (!register_table_get_function) {
return nas_err("dylib::dlopen", "cannot find <get> function");
}
// get function pointer by name
auto table = reinterpret_cast<get_func_ptr>(register_table_get_function)();
if (!table) {
return nas_err("dylib::dlopen", "failed to get module functions");
}
for(u32 i = 0; table[i].name; ++i) {
auto function_pointer = reinterpret_cast<void*>(table[i].fd);
auto function_object = ngc->alloc(vm_obj);
function_object.ghost().set(
function_address_type_name,
nullptr,
function_pointer
);
return_hash.hash().elems[table[i].name] = function_object;
}
ngc->temp = nil;
return return_hash;
}
var builtin_dlclose(context* ctx, gc* ngc) {
auto library_pointer = ctx->localr[1];
if (!library_pointer.object_check(dynamic_library_type_name)) {
return nas_err("dylib::dlclose", "\"lib\" is not a valid dynamic lib");
}
library_pointer.ghost().clear();
return nil;
}
var builtin_dlcallv(context* ctx, gc* ngc) {
auto function_object = ctx->localr[1];
auto arguments = ctx->localr[2];
if (!function_object.object_check(function_address_type_name)) {
return nas_err("dylib::dlcall",
"\"ptr\" is not a valid function pointer"
);
}
auto& vec = arguments.vec().elems;
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
vec.data(),
vec.size(),
ngc
);
}
var builtin_dlcall(context* ctx, gc* ngc) {
auto function_object = ctx->localr[1];
if (!function_object.object_check(function_address_type_name)) {
return nas_err("dylib::dlcall",
"\"ptr\" is not a valid function pointer"
);
}
// function pointer is at ctx->localr[1]
// so arguments starts from ctx->localr[2]
var* local_frame_start = ctx->localr + 2;
usize local_frame_size = ngc->running_context->top - local_frame_start;
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
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}
};
}

25
src/dylib_lib.h Normal file
View File

@@ -0,0 +1,25 @@
#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 dynamic_library_destructor(void*);
var builtin_dlopen(context*, gc*);
var builtin_dlclose(context*, gc*);
var builtin_dlcallv(context*, gc*);
var builtin_dlcall(context*, 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(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto level = local[1];
auto elems = local[2];
if (elems.type!=vm_vec) {
return nas_err("fg_env::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("fg_env::logprint",
"incorrect log level " + std::to_string(level.num())
);
}
for(auto& value : elems.vec().elems) {
out << value << " ";
}
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(context*, gc*);
extern nasal_builtin_table flight_gear_native[];
}

245
src/io_lib.cpp Normal file
View File

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

41
src/io_lib.h Normal file
View File

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

233
src/main.cpp Normal file
View File

@@ -0,0 +1,233 @@
#include "nasal.h"
#include "nasal_type.h"
#include "nasal_gc.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(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[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(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
}
var builtin_cos(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
}
var builtin_tan(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
}
var builtin_exp(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
}
var builtin_lg(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
}
var builtin_ln(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
}
var builtin_sqrt(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
}
var builtin_atan2(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (x.type!=vm_num || y.type!=vm_num) {
return var::num(std::nan(""));
}
return var::num(atan2(y.num(), x.num()));
}
var builtin_isnan(context* ctx, gc* ngc) {
auto x = ctx->localr[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(context*, gc*);
var builtin_sin(context*, gc*);
var builtin_cos(context*, gc*);
var builtin_tan(context*, gc*);
var builtin_exp(context*, gc*);
var builtin_lg(context*, gc*);
var builtin_ln(context*, gc*);
var builtin_sqrt(context*, gc*);
var builtin_atan2(context*, gc*);
var builtin_isnan(context*, gc*);
extern nasal_builtin_table math_lib_native[];
}

58
src/nasal.h Normal file
View File

@@ -0,0 +1,58 @@
#pragma once
#ifndef __nasver
#define __nasver "11.1"
#endif
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <sstream>
#include <cmath>
// 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);
}

382
src/nasal_ast.cpp Normal file
View File

@@ -0,0 +1,382 @@
#include "nasal_ast.h"
#include "ast_visitor.h"
namespace nasal {
void expr::accept(ast_visitor* visitor) {
visitor->visit_expr(this);
}
use_stmt::~use_stmt() {
for(auto i : path) {
delete i;
}
}
void use_stmt::accept(ast_visitor* visitor) {
visitor->visit_use_stmt(this);
}
void call::accept(ast_visitor* visitor) {
visitor->visit_call(this);
}
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);
}
}

694
src/nasal_ast.h Normal file
View File

@@ -0,0 +1,694 @@
#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_use, // use statement
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 identifier;
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 use_stmt: public expr {
private:
std::vector<identifier*> path;
public:
use_stmt(const span& location):
expr(location, expr_type::ast_use) {}
~use_stmt() override;
void accept(ast_visitor*) override;
void add_path(identifier* node) {path.push_back(node);}
const auto& get_path() const {return path;}
};
class call: public expr {
public:
call(const span& location, expr_type node_type):
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:
bool is_iterator_definition;
identifier* name;
call_expr* call;
public:
iter_expr(const span& location):
expr(location, expr_type::ast_iter),
is_iterator_definition(false),
name(nullptr), call(nullptr) {}
~iter_expr() override;
void set_name(identifier* node) {name = node;}
void set_call(call_expr* node) {call = node;}
void set_is_definition(bool flag) {is_iterator_definition = flag;}
identifier* get_name() {return name;}
call_expr* get_call() {return call;}
bool is_definition() const {return is_iterator_definition;}
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;
};
}

693
src/nasal_builtin.cpp Normal file
View File

@@ -0,0 +1,693 @@
#include "nasal_builtin.h"
#include <chrono>
namespace nasal {
var builtin_print(context* ctx, gc* ngc) {
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
return nil;
}
var builtin_println(context* ctx, gc* ngc) {
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::endl;
return nil;
}
var builtin_exit(context* ctx, gc* ngc) {
std::exit(ctx->localr[1].num());
return nil;
}
var builtin_abort(context* ctx, gc* ngc) {
std::abort();
return nil;
}
var builtin_append(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
return var::num(-1);
}
return var::num(static_cast<f64>(system(str.str().c_str())));
}
var builtin_input(context* ctx, gc* ngc) {
auto local = ctx->localr;
var end = local[1];
var ret = ngc->alloc(vm_str);
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
std::cin >> ret.str();
} else {
std::getline(std::cin, ret.str(), end.str()[0]);
}
return ret;
}
var builtin_split(context* ctx, gc* ngc) {
auto local = ctx->localr;
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
auto 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(context* ctx, gc* ngc) {
auto val = ctx->localr[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(context* ctx, gc* ngc) {
auto val = ctx->localr[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(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num && val.type!=vm_str) {
return nil;
}
return var::num(static_cast<f64>(static_cast<i32>(val.to_num())));
}
var builtin_floor(context* ctx, gc* ngc) {
auto value = ctx->localr[1];
return var::num(std::floor(value.num()));
}
var builtin_ceil(context* ctx, gc* ngc) {
auto value = ctx->localr[1];
return var::num(std::ceil(value.num()));
}
var builtin_num(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type==vm_num) {
return val;
}
if (val.type!=vm_str) {
return nil;
}
auto res = val.to_num();
if (std::isnan(res)) {
return nil;
}
return var::num(res);
}
var builtin_pop(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_vec) {
return nas_err("pop", "\"vec\" must be vector");
}
auto& vec = val.vec().elems;
if (vec.size()) {
auto tmp = vec.back();
vec.pop_back();
return tmp;
}
return nil;
}
var builtin_str(context* ctx, gc* ngc) {
return ngc->newstr(ctx->localr[1].to_str());
}
var builtin_size(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
f64 num = 0;
switch(val.type) {
case vm_num: num = val.num(); break;
case vm_str: num = val.str().length(); break;
case vm_vec: num = val.vec().size(); break;
case vm_hash: num = val.hash().size(); break;
case vm_map: num = val.map().mapper.size(); break;
}
return var::num(num);
}
var builtin_time(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num) {
return nas_err("time", "\"begin\" must be number");
}
auto begin = static_cast<time_t>(val.num());
return var::num(static_cast<f64>(time(&begin)));
}
var builtin_contains(context* ctx, gc* ngc) {
auto local = ctx->localr;
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash || key.type!=vm_str) {
return zero;
}
return hash.hash().elems.count(key.str())? one:zero;
}
var builtin_delete(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// avoid being sweeped
auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc->newstr(iter.first));
}
}
ngc->temp = nil;
return res;
}
var builtin_die(context* ctx, gc* ngc) {
return nas_err("error", ctx->localr[1].to_str());
}
var builtin_find(context* ctx, gc* ngc) {
auto local = ctx->localr;
var needle = local[1];
var haystack = local[2];
usize pos = haystack.to_str().find(needle.to_str());
if (pos==std::string::npos) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(pos));
}
var builtin_type(context* ctx, gc* ngc) {
switch(ctx->localr[1].type) {
case vm_none: return ngc->newstr("undefined");
case vm_nil: return ngc->newstr("nil");
case vm_num: return ngc->newstr("num");
case vm_str: return ngc->newstr("str");
case vm_vec: return ngc->newstr("vec");
case vm_hash: return ngc->newstr("hash");
case vm_func: return ngc->newstr("func");
case vm_obj: return ngc->newstr("obj");
case vm_co: return ngc->newstr("coroutine");
case vm_map: return ngc->newstr("namespace");
}
return nil;
}
var builtin_substr(context* ctx, gc* ngc) {
auto local = ctx->localr;
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");
}
auto begin = static_cast<usize>(beg.num());
auto length = static_cast<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(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto local = ctx->localr;
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(context* ctx, gc* ngc) {
auto local = ctx->localr;
var a = local[1];
var b = local[2];
if (a.type!=vm_str || b.type!=vm_str) {
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(context* ctx, gc* ngc) {
const char* extend[] = {
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) {
return ngc->newstr(static_cast<char>(num));
} else if (128<=num && num<256) {
return ngc->newstr(extend[num-128]);
}
return ngc->newstr(" ");
}
var builtin_char(context* ctx, gc* ngc) {
return ngc->newstr(static_cast<unsigned char>(ctx->localr[1].num()));
}
var builtin_values(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("values", "\"hash\" must be hash or namespace");
}
auto vec = ngc->alloc(vm_vec);
auto& v = vec.vec().elems;
if (hash.type==vm_hash) {
for(auto& i : hash.hash().elems) {
v.push_back(i.second);
}
} else {
for(auto& i : hash.map().mapper) {
v.push_back(*i.second);
}
}
return vec;
}
var builtin_sleep(context* ctx, gc* ngc) {
auto val = ctx->localr[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(context* ctx, 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(context* ctx, 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(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
return nas_err("md5", "\"str\" must be string");
}
return ngc->newstr(md5(str.str()));
}
var builtin_millisec(context* ctx, gc* ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return var::num(res);
}
var builtin_gcextend(context* ctx, gc* ngc) {
auto type = ctx->localr[1];
if (type.type!=vm_str) {
return nil;
}
const 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(context* ctx, 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(context* ctx, 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(context* ctx, gc* ngc) {
auto arg = ctx->localr[1];
if (arg.type!=vm_obj) {
return nas_err("ghosttype", "this is not a ghost object.");
}
const auto& name = arg.ghost().get_ghost_name();
if (!name.length()) {
return var::num(reinterpret_cast<u64>(arg.ghost().pointer));
}
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},
{"__ceil", builtin_ceil},
{"__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}
};
}

87
src/nasal_builtin.h Normal file
View File

@@ -0,0 +1,87 @@
#pragma once
#include "nasal.h"
#include "nasal_type.h"
#include "nasal_gc.h"
#ifdef _WIN32
#include <windows.h>
#endif
#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(context*, gc*);
var builtin_println(context*, gc*);
var builtin_exit(context*, gc*);
var builtin_abort(context*, gc*);
var builtin_append(context*, gc*);
var builtin_setsize(context*, gc*);
var builtin_system(context*, gc*);
var builtin_input(context*, gc*);
var builtin_split(context*, gc*);
var builtin_rand(context*, gc*);
var builtin_id(context*, gc*);
var builtin_int(context*, gc*);
var builtin_floor(context*, gc*);
var builtin_ceil(context*, gc*);
var builtin_num(context*, gc*);
var builtin_pop(context*, gc*);
var builtin_str(context*, gc*);
var builtin_size(context*, gc*);
var builtin_time(context*, gc*);
var builtin_contains(context*, gc*);
var builtin_delete(context*, gc*);
var builtin_keys(context*, gc*);
var builtin_die(context*, gc*);
var builtin_find(context*, gc*);
var builtin_type(context*, gc*);
var builtin_substr(context*, gc*);
var builtin_streq(context*, gc*);
var builtin_left(context*, gc*);
var builtin_right(context*, gc*);
var builtin_cmp(context*, gc*);
var builtin_chr(context*, gc*);
var builtin_char(context*, gc*);
var builtin_values(context*, gc*);
var builtin_sleep(context*, gc*);
var builtin_platform(context*, gc*);
var builtin_arch(context*, gc*);
// md5 related functions
std::string tohex(u32);
std::string md5(const std::string&);
var builtin_md5(context*, gc*);
var builtin_millisec(context*, gc*);
var builtin_gcextend(context*, gc*);
var builtin_gcinfo(context*, gc*);
var builtin_logtime(context*, gc*);
var builtin_ghosttype(context*, 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)(context*, gc*);
};
extern nasal_builtin_table builtin[];
}

1423
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_number(const f64);
void regist_string(const std::string&);
void find_symbol(code_block*);
void regist_symbol(const std::string&);
i32 local_symbol_find(const std::string&);
i32 global_symbol_find(const std::string&);
i32 upvalue_symbol_find(const std::string&);
void emit(u8, u32, const span&);
void number_gen(number_literal*);
void string_gen(string_literal*);
void bool_gen(bool_literal*);
void vector_gen(vector_expr*);
void hash_gen(hash_expr*);
void func_gen(function*);
void call_gen(call_expr*);
void call_identifier(identifier*);
void call_hash_gen(call_hash*);
void call_vector_gen(call_vector*);
void call_func_gen(call_function*);
void mcall(expr*);
void mcall_identifier(identifier*);
void mcall_vec(call_vector*);
void mcall_hash(call_hash*);
void multi_def(definition_expr*);
void single_def(definition_expr*);
void definition_gen(definition_expr*);
void assignment_expression(assignment_expr*);
void gen_assignment_equal_statement(assignment_expr*);
void replace_left_assignment_with_load(const span&);
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;
};
}

292
src/nasal_dbg.cpp Normal file
View File

@@ -0,0 +1,292 @@
#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(const_number, const_string, native_function.data(), files);
std::clog << "\nnext 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";
}
stack_info(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;
}
// is not break point and is not next stop command
const auto& code = bytecode[ctx.pc];
if ((code.fidx!=break_file_index || code.line!=break_line) && !next) {
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:
function_call_trace();
trace_back();
break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: global_state(); break;
case dbg_cmd::cmd_local: local_state(); break;
case dbg_cmd::cmd_upval: upvalue_state(); break;
case dbg_cmd::cmd_register: register_info(); break;
case dbg_cmd::cmd_show_all: all_state_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) {
break_file_index = file_index(res[1]);
if (break_file_index==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 {
break_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 break_file_index;
u32 break_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(true), fsize(0),
break_file_index(0), break_line(0),
do_profiling(false) {}
void run(
const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool
);
};
}

198
src/nasal_err.cpp Normal file
View File

@@ -0,0 +1,198 @@
#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 windows_system_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),
windows_system_set.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;}
};
}

385
src/nasal_gc.cpp Normal file
View File

@@ -0,0 +1,385 @@
#include "nasal_gc.h"
namespace nasal {
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 = running_context->stack; i<=running_context->top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(running_context->funcr);
bfs_queue.push_back(running_context->upvalr);
bfs_queue.push_back(temp);
if (!cort) {
return;
}
// coroutine is running, so scan main process stack from mctx
for(var* i = main_context.stack; i<=main_context.top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(main_context.funcr);
bfs_queue.push_back(main_context.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->unmutable = 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->unmutable = 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::context_change(nas_co* co) {
// store running state to main context
main_context = *running_context;
// restore coroutine context state
*running_context = co->ctx;
// set coroutine pointer
cort = co;
// set coroutine state to running
cort->status = nas_co::status::running;
}
void gc::context_reserve() {
// pc=0 means this coroutine is finished
cort->status = running_context->pc?
nas_co::status::suspended:
nas_co::status::dead;
// store running state to coroutine
cort->ctx = *running_context;
// restore main context state
*running_context = main_context;
// set coroutine pointer to nullptr
cort = nullptr;
}
}

126
src/nasal_gc.h Normal file
View File

@@ -0,0 +1,126 @@
#pragma once
// avoid MSVC warnings
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include <iomanip>
#include <vector>
#include <chrono>
#include <thread>
#include <cstring>
#include <sstream>
#include "nasal.h"
#include "nasal_type.h"
namespace nasal {
struct gc {
/* main context temporary storage */
context main_context;
/* global storage */
var* main_context_global = nullptr;
usize main_context_global_size = 0;
/* runtime context */
context* running_context = 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) {
running_context = _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 context_change(nas_co*);
void context_reserve();
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() = std::string(buff);
return s;
}
var newstr(const std::string& buff) {
var s = alloc(vm_str);
s.str() = buff;
return s;
}
};
// 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)();
}

399
src/nasal_import.cpp Normal file
View File

@@ -0,0 +1,399 @@
#include "nasal_import.h"
#include "symbol_finder.h"
#include <memory>
#include <unordered_set>
namespace nasal {
linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
const auto seperator= is_windows()? ';':':';
const auto PATH = std::string(getenv("PATH"));
usize last = 0, position = PATH.find(seperator, 0);
while(position!=std::string::npos) {
std::string dirpath = PATH.substr(last, position-last);
if (dirpath.length()) {
envpath.push_back(dirpath);
}
last = position+1;
position = PATH.find(seperator, last);
}
if (last!=PATH.length()) {
envpath.push_back(PATH.substr(last));
}
}
std::string linker::get_path(expr* node) {
if (node->get_type()==expr_type::ast_use) {
auto file_relative_path = std::string("");
const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
for(auto i : path) {
file_relative_path += i->get_name();
if (i!=path.back()) {
file_relative_path += (is_windows()? "\\":"/");
}
}
return file_relative_path + ".nas";
}
auto call_node = reinterpret_cast<call_expr*>(node);
auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]);
auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]);
return content->get_content();
}
std::string linker::find_real_file_path(
const std::string& filename, const span& location) {
// first add file name itself into the file path
std::vector<std::string> path_list = {filename};
// generate search path from environ path
for(const auto& p : envpath) {
path_list.push_back(p + (is_windows()? "\\":"/") + filename);
}
// search file
for(const auto& path : path_list) {
if (access(path.c_str(), F_OK)!=-1) {
return path;
}
}
// we will find lib.nas in nasal std directory
if (filename=="lib.nas") {
return is_windows()?
find_real_file_path("std\\lib.nas", location):
find_real_file_path("std/lib.nas", location);
}
if (!show_path_flag) {
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename + ">, " +
"use <-d> to get detail search path"
);
return "";
}
auto path_list_info = std::string("");
for(const auto& path : path_list) {
path_list_info += " -> " + path + "\n";
}
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename +
"> in these paths:\n" + path_list_info
);
return "";
}
bool linker::import_check(expr* node) {
if (node->get_type()==expr_type::ast_use) {
return true;
}
/*
call
|_id:import
|_call_func
|_string:'filename'
*/
if (node->get_type()!=expr_type::ast_call) {
return false;
}
auto call_node = reinterpret_cast<call_expr*>(node);
auto first_expr = call_node->get_first();
if (first_expr->get_type()!=expr_type::ast_id) {
return false;
}
if (reinterpret_cast<identifier*>(first_expr)->get_name()!="import") {
return false;
}
if (!call_node->get_calls().size()) {
return false;
}
// import("xxx");
if (call_node->get_calls().size()!=1) {
return false;
}
auto maybe_func_call = call_node->get_calls()[0];
if (maybe_func_call->get_type()!=expr_type::ast_callf) {
return false;
}
auto func_call = reinterpret_cast<call_function*>(maybe_func_call);
if (func_call->get_argument().size()!=1) {
return false;
}
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
return false;
}
return true;
}
bool linker::check_exist_or_record_file(const std::string& file) {
// avoid importing the same file
for(const auto& name : imported_files) {
if (file==name) {
return true;
}
}
imported_files.push_back(file);
return false;
}
bool linker::check_self_import(const std::string& file) {
for(const auto& name : module_load_stack) {
if (file==name) {
return true;
}
}
return false;
}
std::string linker::generate_self_import_path(const std::string& filename) {
std::string res = "";
for(const auto& i : module_load_stack) {
res += "[" + i + "] -> ";
}
return res + "[" + filename + "]";
}
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
// add children of add_root to the back of root
for(auto& i : old_tree_root->get_expressions()) {
new_tree_root->add_expression(i);
}
// clean old root
old_tree_root->get_expressions().clear();
}
code_block* linker::import_regular_file(
expr* node, std::unordered_set<std::string>& used_modules) {
// get filename
auto filename = get_path(node);
// avoid infinite loading loop
filename = find_real_file_path(filename, node->get_location());
// if get empty string(error) or this file is used before, do not parse
if (!filename.length() || used_modules.count(filename)) {
return new code_block({0, 0, 0, 0, filename});
}
// check self import, avoid infinite loading loop
if (check_self_import(filename)) {
err.err("link",
"self-referenced module <" + filename + ">:\n" +
" reference path: " + generate_self_import_path(filename)
);
return new code_block({0, 0, 0, 0, filename});
}
check_exist_or_record_file(filename);
module_load_stack.push_back(filename);
// start importing...
lexer nasal_lexer;
parse nasal_parser;
if (nasal_lexer.scan(filename).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename});
}
if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename});
}
// swap result out
auto parse_result = nasal_parser.swap(nullptr);
// check if parse result has 'import'
auto result = load(parse_result, filename);
module_load_stack.pop_back();
return result;
}
code_block* linker::import_nasal_lib() {
auto path = find_real_file_path(
"lib.nas", {0, 0, 0, 0, this_file}
);
if (!path.length()) {
return new code_block({0, 0, 0, 0, path});
}
// avoid infinite loading library
if (check_exist_or_record_file(path)) {
return new code_block({0, 0, 0, 0, path});
}
// start importing...
lexer nasal_lexer;
parse nasal_parser;
if (nasal_lexer.scan(path).geterr()) {
err.err("link",
"error occurred when analysing library <" + path + ">"
);
return new code_block({0, 0, 0, 0, path});
}
if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link",
"error occurred when analysing library <" + path + ">"
);
return new code_block({0, 0, 0, 0, path});
}
// swap result out
auto parse_result = nasal_parser.swap(nullptr);
// check if library has 'import' (in fact it should not)
return load(parse_result, path);
}
std::string linker::generate_module_name(const std::string& file_path) {
auto error_name = "module@[" + file_path + "]";
if (!file_path.length()) {
return error_name;
}
// check file suffix and get file suffix position
auto suffix_position = file_path.find(".nas");
if (suffix_position==std::string::npos) {
err.warn("link",
"get invalid module name from <" + file_path + ">, " +
"will not be easily accessed. " +
"\".nas\" suffix is required."
);
return error_name;
}
if (suffix_position+4!=file_path.length()) {
err.warn("link",
"get invalid module name from <" + file_path + ">, " +
"will not be easily accessed. " +
"only one \".nas\" suffix is required in the path."
);
return error_name;
}
// only get the file name as module name, directory path is not included
auto split_position = file_path.find_last_of("/");
// find "\\" in windows platform
if (split_position==std::string::npos) {
split_position = file_path.find_last_of("\\");
}
// split file path to get module name
auto module_name = split_position==std::string::npos?
file_path.substr(0, suffix_position):
file_path.substr(split_position+1, suffix_position-split_position-1);
// check validation of module name
if (!module_name.length()) {
err.warn("link",
"get empty module name from <" + file_path + ">, " +
"will not be easily accessed."
);
return module_name;
}
if (std::isdigit(module_name[0]) ||
module_name.find(".")!=std::string::npos ||
module_name.find("-")!=std::string::npos) {
err.warn("link",
"get module <" + module_name + "> from <" + file_path + ">, " +
"will not be easily accessed."
);
}
return module_name;
}
return_expr* linker::generate_module_return(code_block* block) {
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
auto result = new return_expr(block->get_location());
auto value = new hash_expr(block->get_location());
result->set_value(value);
for(const auto& i : finder->do_find(block)) {
auto pair = new hash_pair(block->get_location());
// do not export symbol begins with '_'
if (i.name.length() && i.name[0]=='_') {
continue;
}
pair->set_name(i.name);
pair->set_value(new identifier(block->get_location(), i.name));
value->add_member(pair);
}
return result;
}
definition_expr* linker::generate_module_definition(code_block* block) {
auto def = new definition_expr(block->get_location());
def->set_identifier(new identifier(
block->get_location(),
generate_module_name(block->get_location().file)
));
auto call = new call_expr(block->get_location());
auto func = new function(block->get_location());
func->set_code_block(block);
func->get_code_block()->add_expression(generate_module_return(block));
call->set_first(func);
call->add_call(new call_function(block->get_location()));
def->set_value(call);
return def;
}
code_block* linker::load(code_block* program_root, const std::string& filename) {
auto tree = new code_block({0, 0, 0, 0, filename});
// load library, this ast will be linked with root directly
// so no extra namespace is generated
if (!library_loaded) {
auto nasal_lib_code_block = import_nasal_lib();
// insert nasal lib code to the back of tree
link(tree, nasal_lib_code_block);
delete nasal_lib_code_block;
library_loaded = true;
}
// load imported modules
std::unordered_set<std::string> used_modules = {};
for(auto& import_node : program_root->get_expressions()) {
if (!import_check(import_node)) {
break;
}
// parse file and get ast
auto module_code_block = import_regular_file(import_node, used_modules);
auto replace_node = new null_expr(import_node->get_location());
// after importing the regular file as module, delete this node
delete import_node;
// and replace the node with null_expr node
import_node = replace_node;
// avoid repeatedly importing the same module
const auto& module_path = module_code_block->get_location().file;
if (used_modules.count(module_path)) {
delete module_code_block;
continue;
}
// then we generate a function warping the code block,
// and export the necessary global symbols in this code block
// by generate a return statement, with a hashmap return value
used_modules.insert(module_path);
tree->add_expression(generate_module_definition(module_code_block));
}
// insert program root to the back of tree
link(tree, program_root);
return tree;
}
const error& linker::link(
parse& parse, const std::string& self, bool spath = false) {
// switch for showing path when errors occur
show_path_flag = spath;
// initializing file map
this_file = self;
imported_files = {self};
module_load_stack = {self};
// scan root and import files
// then generate a new ast and return to import_ast
auto new_tree_root = load(parse.tree(), self);
auto old_tree_root = parse.swap(new_tree_root);
delete old_tree_root;
return err;
}
}

59
src/nasal_import.h Normal file
View File

@@ -0,0 +1,59 @@
#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 <cstring>
#include <sstream>
#include <vector>
#include <unordered_set>
namespace nasal {
class linker {
private:
bool show_path_flag;
bool library_loaded;
std::string this_file;
error err;
std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack;
std::vector<std::string> envpath;
private:
bool import_check(expr*);
bool check_exist_or_record_file(const std::string&);
bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&);
void link(code_block*, code_block*);
std::string get_path(expr*);
std::string find_real_file_path(const std::string&, const span&);
code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
code_block* import_nasal_lib();
std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*);
definition_expr* generate_module_definition(code_block*);
code_block* load(code_block*, const std::string&);
public:
linker();
const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return imported_files;}
};
}

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

190
src/nasal_lexer.h Normal file
View File

@@ -0,0 +1,190 @@
#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
use, // keyword use
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 {
{"use" ,tok::use },
{"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;}
};
}

237
src/nasal_misc.cpp Normal file
View File

@@ -0,0 +1,237 @@
#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, num_pow = 0;
bool negative = false;
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++=='-');
}
if (!*str) {
return nan("");
}
num_pow = 0;
while('0'<=*str && *str<='9') {
num_pow = num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
}
return negative?
ret*std::pow(10, 1-num_pow)*0.1:
ret*std::pow(10, num_pow-1)*10;
}
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* number_list,
const std::string* string_list,
const nasal_builtin_table* native_table,
const std::string* file_list) {
const_number = number_list;
const_string = string_list;
natives = native_table;
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
<< " (" << const_number[num] << ")"; break;
case op_lnkeqc:
out << hex << "0x" << num << dec
<< " (" << rawstr(const_string[num], 16) << ")"; break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out << hex << "0x" << num << dec
<< " (" << const_number[num] << ") sp-1"; break;
case op_lnkecp:
out << hex << "0x" << num << dec
<< " (" << rawstr(const_string[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
<< " (" << const_number[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(const_string[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* const_number = nullptr;
inline static const std::string* const_string = 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[];
}

1086
src/nasal_parse.cpp Normal file

File diff suppressed because it is too large Load Diff

160
src/nasal_parse.h Normal file
View File

@@ -0,0 +1,160 @@
#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::use ,"use" },
{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:
use_stmt* use_stmt_gen();
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();
};
}

355
src/nasal_type.cpp Normal file
View File

@@ -0,0 +1,355 @@
#include "nasal_type.h"
#include <cstring>
#include <sstream>
namespace nasal {
var nas_vec::get_value(const i32 index) {
i32 size = elems.size();
if (index<-size || index>=size) {
return var::none();
}
return elems[index>=0? index:index+size];
}
var* nas_vec::get_memory(const i32 index) {
i32 size = elems.size();
if (index<-size || index>=size) {
return nullptr;
}
return &elems[index>=0? index:index+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_value(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_value(key);
}
if (ret.type!=vm_none) {
return ret;
}
}
return ret;
}
var* nas_hash::get_memory(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_memory(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() {
dynamic_parameter_index = -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;
destructor_function = destructor_pointer;
pointer = ghost_pointer;
}
void nas_ghost::clear() {
// do nothing if pointer is null
if (!pointer) {
return;
}
// do clear pointer if destructor function pointer is null
if (!destructor_function) {
type_name = "";
pointer = nullptr;
return;
}
// do destruction
destructor_function(pointer);
type_name = "";
pointer = nullptr;
destructor_function = 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.pointer) << 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_value(const std::string& key) {
if (mapper.count(key)) {
return *mapper.at(key);
}
return var::none();
}
var* nas_map::get_memory(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;
unmutable = 0;
switch(val_type) {
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
case vm_map: ptr.map = new nas_map; break;
}
}
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::to_num() {
return type!=vm_str? val.num:str2num(str().c_str());
}
std::string var::to_str() {
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.ghost(); break;
case vm_co: out << ref.co(); break;
case vm_map: out << ref.map(); break;
}
return out;
}
bool var::object_check(const std::string& name) {
return type==vm_obj && ghost().type_name==name && ghost().pointer;
}
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() const {
return val.ret;
}
i64& var::cnt() {
return val.cnt;
}
f64 var::num() const {
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::ghost() {
return *val.gcobj->ptr.obj;
}
nas_co& var::co() {
return *val.gcobj->ptr.co;
}
nas_map& var::map() {
return *val.gcobj->ptr.map;
}
var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
return var::none();
}
}

269
src/nasal_type.h Normal file
View File

@@ -0,0 +1,269 @@
#pragma once
#include "nasal.h"
#include <vector>
#include <unordered_map>
namespace nasal {
enum vm_type:u8 {
/* none-gc object */
vm_none = 0, // error type
vm_cnt, // counter for forindex/foreach loop
vm_addr, // var* address
vm_ret, // return addres(program counter)
vm_nil, // nil
vm_num, // number
/* gc object */
vm_str, // string
vm_vec, // vector
vm_hash, // hashmap(dict)
vm_func, // function(lambda)
vm_upval, // upvalue
vm_obj, // ghost type
vm_co, // coroutine
vm_map, // for globals and namespaces
/* mark type range */
vm_type_size_max
};
// size of gc object type
const u32 gc_type_size = vm_type_size_max-vm_str;
// basic types
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
// union type
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 to_num();
std::string to_str();
bool object_check(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 value
var* addr();
u32 ret() const;
i64& cnt();
f64 num() const;
std::string& str();
nas_vec& vec();
nas_hash& hash();
nas_func& func();
nas_upval& upval();
nas_ghost& ghost();
nas_co& co();
nas_map& map();
};
struct nas_vec {
std::vector<var> elems;
// mark if this is printed, avoid stack overflow
bool printed = false;
usize size() const {return elems.size();}
var get_value(const i32);
var* get_memory(const i32);
};
struct nas_hash {
std::unordered_map<std::string, var> elems;
// mark if this is printed, avoid stack overflow
bool printed = false;
usize size() const {return elems.size();}
var get_value(const std::string&);
var* get_memory(const std::string&);
};
struct nas_func {
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
u32 parameter_size; // used to load default parameters to a new function
u32 local_size; // 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
// parameter table, u32 begins from 1
std::unordered_map<std::string, u32> keys;
nas_func():
dynamic_parameter_index(-1), entry(0),
parameter_size(0), local_size(0) {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool on_stack;
u32 size;
var* stack_frame_offset;
/* not on stack, use this */
std::vector<var> elems;
public:
nas_upval(): on_stack(true), size(0), stack_frame_offset(nullptr) {}
var& operator[](usize n) {
return on_stack? stack_frame_offset[n]:elems[n];
}
void clear() {
on_stack = true;
elems.clear();
size = 0;
}
};
struct nas_ghost {
private:
using destructor = void (*)(void*);
public:
std::string type_name;
destructor destructor_function;
void* pointer;
public:
nas_ghost():
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
~nas_ghost() {clear();}
void set(const std::string&, destructor, void*);
void clear();
public:
const auto& 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;
void clear() {
mapper.clear();
}
var get_value(const std::string&);
var* get_memory(const std::string&);
};
struct nas_val {
enum class gc_status:u8 {
uncollected = 0,
collected,
found
};
gc_status mark;
u8 type; // value type
u8 unmutable; // 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();
// use to print error log and return error value
var nas_err(const std::string&, const std::string&);
}

625
src/nasal_vm.cpp Normal file
View File

@@ -0,0 +1,625 @@
#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
) {
const_number = nums.data();
const_string = strs.data();
bytecode = code.data();
files = filenames.data();
global_size = global_symbol.size();
/* set native functions */
native_function = 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::value_info(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.ghost().pointer)
<< std::dec; break;
case vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
case vm_map: std::clog << "| nmspc| <0x" << std::hex << p
<< std::dec << "> namespace ["
<< val.map().mapper.size() << " val]"; break;
default: std::clog << "| err | <0x" << std::hex << p
<< std::dec << "> unknown object"; break;
}
std::clog << "\n";
}
void vm::function_detail_info(const nas_func& func) {
std::clog << "func@0x";
std::clog << std::hex << reinterpret_cast<u64>(&func) << std::dec;
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& key : func.keys) {
argument_list[key.second-1] = key.first;
}
std::clog << "(";
for(const auto& key : argument_list) {
std::clog << key;
if (key != argument_list.back()) {
std::clog << ", ";
}
}
if (func.dynamic_parameter_index>=0) {
std::clog << (argument_list.size()? ", ":"");
std::clog << const_string[func.dynamic_parameter_index] << "...";
}
std::clog << ") ";
std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
}
void vm::function_call_trace() {
var* bottom = ctx.stack;
var* top = ctx.top;
// generate trace back
std::stack<const nas_func*> functions;
for(var* i = bottom; i<=top; ++i) {
if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) {
functions.push(&i->func());
}
}
if (functions.empty()) {
return;
}
std::clog << "\ncall trace " << (ngc.cort? "(coroutine)":"(main)") << "\n";
const nas_func* last = nullptr;
u32 same = 0;
for(auto func = last; !functions.empty(); functions.pop()) {
func = functions.top();
if (last==func) {
++same;
continue;
} else if (same) {
std::clog << " --> " << same << " same call(s)\n";
same = 0;
}
last = func;
std::clog << " call ";
function_detail_info(*func);
std::clog << "\n";
}
if (same) {
std::clog << " --> " << same << " same call(s)\n";
}
}
void vm::trace_back() {
// var* bottom = ctx.stack;
// var* top = ctx.top;
// generate trace back
std::stack<u32> ret;
for(var* i = ctx.stack; i<=ctx.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 << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n";
codestream::set(const_number, const_string, native_function.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::stack_info(const u32 limit = 10) {
var* top = ctx.top;
var* bottom = ctx.stack;
std::clog << "\nstack (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
<< " ";
value_info(top[0]);
}
}
void vm::register_info() {
std::clog << "\nregisters (" << (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 ] "; value_info(ctx.funcr);
std::clog << " [upval ] "; value_info(ctx.upvalr);
}
void vm::global_state() {
if (!global_size || global[0].type==vm_none) {
return;
}
std::clog << "\nglobal (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
<< " ";
value_info(global[i]);
}
}
void vm::local_state() {
if (!ctx.localr || !ctx.funcr.func().local_size) {
return;
}
const u32 lsize = ctx.funcr.func().local_size;
std::clog << "\nlocal (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
<< " ";
value_info(ctx.localr[i]);
}
}
void vm::upvalue_state() {
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
return;
}
std::clog << "\nupvalue\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
<< " ";
value_info(uv[j]);
}
}
}
void vm::all_state_detail() {
register_info();
global_state();
local_state();
upvalue_state();
}
std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
auto result = std::string("lack argument(s) when calling function:\n func(");
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& i : func.keys) {
argument_list[i.second-1] = i.first;
}
for(u32 i = 0; i<argument_list.size(); ++i) {
result += argument_list[i];
if (i<argc) {
result += "[get]";
}
if (i!=argument_list.size()-1) {
result += ", ";
}
}
if (func.dynamic_parameter_index>=0) {
result += argument_list.size()? ", ":"";
result += const_string[func.dynamic_parameter_index] + "[dynamic]";
}
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
std::string vm::report_special_call_lack_arguments(
var* local, const nas_func& func) const {
auto result = std::string("lack argument(s) when calling function:\n func(");
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& i : func.keys) {
argument_list[i.second-1] = i.first;
}
for(const auto& key : argument_list) {
if (local[func.keys.at(key)].type==vm_none) {
result += key + ", ";
} else {
result += key + "[get], ";
}
}
result = result.substr(0, result.length()-2);
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
std::string vm::report_key_not_found(
const std::string& not_found, const nas_hash& hash) const {
auto result = "member \"" + not_found + "\" doesn't exist in hash {";
for(const auto& i : hash.elems) {
result += i.first + ", ";
}
if (hash.elems.size()) {
result = result.substr(0, result.length()-2);
}
result += "}";
return result;
}
std::string vm::report_out_of_range(f64 index, usize real_size) const {
auto result = "index out of range: " + std::to_string(index);
result += " but max size is " + std::to_string(real_size);
if (!real_size) {
return result;
}
result += ", index range is -" + std::to_string(real_size);
result += "~" + std::to_string(real_size-1);
return result;
}
std::string vm::type_name_string(const var& value) const {
switch(value.type) {
case vm_none: return "none";
case vm_cnt: return "counter";
case vm_addr: return "address";
case vm_ret: return "program counter";
case vm_nil: return "nil";
case vm_num: return "number";
case vm_str: return "string";
case vm_vec: return "vector";
case vm_hash: return "hash";
case vm_func: return "function";
case vm_upval: return "upvalue";
case vm_obj: return "ghost type";
case vm_co: return "coroutine";
case vm_map: return "namespace";
}
return "unknown";
}
void vm::die(const std::string& str) {
std::cerr << "[vm] error: " << str << "\n";
function_call_trace();
trace_back();
stack_info();
// show verbose crash info
if (verbose) {
all_state_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.context_reserve(); // 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
}
}

1031
src/nasal_vm.h Normal file

File diff suppressed because it is too large Load Diff

155
src/optimizer.cpp Normal file
View File

@@ -0,0 +1,155 @@
#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) {
auto left_node = node->get_left();
auto right_node = node->get_right();
left_node->accept(this);
right_node->accept(this);
number_literal* left_num_node = nullptr;
number_literal* right_num_node = nullptr;
string_literal* left_str_node = nullptr;
string_literal* right_str_node = nullptr;
if (left_node->get_type()==expr_type::ast_num) {
left_num_node = reinterpret_cast<number_literal*>(left_node);
} else if (left_node->get_type()==expr_type::ast_binary &&
reinterpret_cast<binary_operator*>(left_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<binary_operator*>(left_node);
left_num_node = optimized->get_optimized_number();
} else if (left_node->get_type()==expr_type::ast_unary &&
reinterpret_cast<unary_operator*>(left_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(left_node);
left_num_node = optimized->get_optimized_number();
}
if (right_node->get_type()==expr_type::ast_num) {
right_num_node = reinterpret_cast<number_literal*>(right_node);
} else if (right_node->get_type()==expr_type::ast_binary &&
reinterpret_cast<binary_operator*>(right_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<binary_operator*>(right_node);
right_num_node = optimized->get_optimized_number();
} else if (right_node->get_type()==expr_type::ast_unary &&
reinterpret_cast<unary_operator*>(right_node)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(right_node);
right_num_node = optimized->get_optimized_number();
}
if (left_node->get_type()==expr_type::ast_str) {
left_str_node = reinterpret_cast<string_literal*>(left_node);
} else if (left_node->get_type()==expr_type::ast_binary &&
reinterpret_cast<binary_operator*>(left_node)->get_optimized_string()) {
auto optimized = reinterpret_cast<binary_operator*>(left_node);
left_str_node = optimized->get_optimized_string();
}
if (right_node->get_type()==expr_type::ast_str) {
right_str_node = reinterpret_cast<string_literal*>(right_node);
} else if (right_node->get_type()==expr_type::ast_binary &&
reinterpret_cast<binary_operator*>(right_node)->get_optimized_string()) {
auto optimized = reinterpret_cast<binary_operator*>(right_node);
right_str_node = optimized->get_optimized_string();
}
if (left_num_node && right_num_node) {
const_number(node, left_num_node, right_num_node);
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) {
auto value = node->get_value();
value->accept(this);
number_literal* num_node = nullptr;
if (value->get_type()==expr_type::ast_num) {
num_node = reinterpret_cast<number_literal*>(value);
} else if (value->get_type()==expr_type::ast_binary &&
reinterpret_cast<binary_operator*>(value)->get_optimized_number()) {
auto optimized = reinterpret_cast<binary_operator*>(value);
num_node = optimized->get_optimized_number();
} else if (value->get_type()==expr_type::ast_unary &&
reinterpret_cast<unary_operator*>(value)->get_optimized_number()) {
auto optimized = reinterpret_cast<unary_operator*>(value);
num_node = optimized->get_optimized_number();
}
if (num_node) {
const_number(node, num_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*);
};
}

170
src/repl.cpp Normal file
View File

@@ -0,0 +1,170 @@
#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
if (!run()) {
std::cout << "[nasal-repl] Initialization failed.\n\n";
std::exit(-1);
}
// 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->is_definition() && 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(context* ctx, gc* ngc) {
#ifndef _WIN32
i32 fd[2];
var res = ngc->alloc(vm_vec);
if (pipe(fd)==-1) {
return nas_err("unix::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("unix::pipe", "not supported on windows");
}
var builtin_fork(context* ctx, gc* ngc) {
#ifndef _WIN32
f64 res=fork();
if (res<0) {
return nas_err("unix::fork", "failed to fork a process");
}
return var::num(static_cast<f64>(res));
#endif
return nas_err("unix::fork", "not supported on windows");
}
var builtin_waitpid(context* ctx, gc* ngc) {
auto pid = ctx->localr[1];
auto nohang = ctx->localr[2];
if (pid.type!=vm_num || nohang.type!=vm_num) {
return nas_err("unix::waitpid", "pid and nohang must be number");
}
#ifndef _WIN32
i32 ret_pid, status;
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
var vec = ngc->alloc(vm_vec);
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("unix::waitpid", "not supported on windows");
}
var builtin_opendir(context* ctx, gc* ngc) {
auto path = ctx->localr[1];
if (path.type!=vm_str) {
return nas_err("unix::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("unix::opendir", "cannot open dir <"+path.str()+">");
}
#else
DIR* p = opendir(path.str().c_str());
if (!p) {
return nas_err("unix::opendir", "cannot open dir <"+path.str()+">");
}
#endif
var ret = ngc->alloc(vm_obj);
ret.ghost().set(dir_type_name, dir_entry_destructor, p);
return ret;
}
var builtin_readdir(context* ctx, gc* ngc) {
auto handle = ctx->localr[1];
if (!handle.object_check(dir_type_name)) {
return nas_err("unix::readdir", "not a valid dir handle");
}
#ifdef _MSC_VER
WIN32_FIND_DATAA data;
if (!FindNextFileA(handle.ghost().pointer, &data)) {
return nil;
}
return ngc->newstr(data.cFileName);
#else
dirent* p = readdir(static_cast<DIR*>(handle.ghost().pointer));
return p? ngc->newstr(p->d_name):nil;
#endif
}
var builtin_closedir(context* ctx, gc* ngc) {
auto handle = ctx->localr[1];
if (!handle.object_check(dir_type_name)) {
return nas_err("unix::closedir", "not a valid dir handle");
}
handle.ghost().clear();
return nil;
}
var builtin_chdir(context* ctx, gc* ngc) {
auto path = ctx->localr[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(context* ctx, 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(context* ctx, gc* ngc) {
char buf[1024];
if (!getcwd(buf, sizeof(buf))) {
return nil;
}
return ngc->newstr(buf);
}
var builtin_getenv(context* ctx, gc* ngc) {
auto envvar = ctx->localr[1];
if (envvar.type!=vm_str) {
return nas_err("unix::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(context*, gc*);
var builtin_fork(context*, gc*);
var builtin_waitpid(context*, gc*);
var builtin_opendir(context*, gc*);
var builtin_readdir(context*, gc*);
var builtin_closedir(context*, gc*);
var builtin_chdir(context*, gc*);
var builtin_environ(context*, gc*);
var builtin_getcwd(context*, gc*);
var builtin_getenv(context*, 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;
}

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