756 Commits
v5.0 ... v11.2

Author SHA1 Message Date
ValK
0ffa62e5cc Merge pull request #45 from ValKmjolnir/develop
📝 add tutorial about new operators
2024-06-07 00:21:42 +08:00
ValKmjolnir
0f80dd7588 📝 rename enums 2024-06-07 00:11:54 +08:00
ValKmjolnir
b09e2a7875 📝 update tutorial about new operators 2024-06-06 23:45:27 +08:00
ValK
5aa99f396b Merge pull request #44 from ValKmjolnir/develop
 support `??` and `?.` operators & fix utf8 output on MSVC
2024-06-06 00:03:56 +08:00
ValKmjolnir
f62d747b23 🐛 really enable utf8 on MSVC 2024-06-06 00:00:48 +08:00
ValKmjolnir
ce9f28274e 🐛 fix compilation error in MSVC 2024-06-05 23:42:56 +08:00
ValKmjolnir
f83b693f65 📝 code improvement 2024-06-05 23:38:48 +08:00
ValKmjolnir
d535c8f7c4 📝 use the same function table 2024-06-05 23:12:38 +08:00
ValKmjolnir
958f669348 🐛 fix typo oprand => operand 2024-06-05 22:59:41 +08:00
ValKmjolnir
1436693cd6 add codegen for ?. 2024-06-05 01:13:39 +08:00
ValKmjolnir
4adf9541b9 add codegen for ?? operator 2024-06-05 00:01:46 +08:00
ValKmjolnir
43c229fc72 add parse process for ?? & ?. 2024-06-04 00:49:01 +08:00
ValKmjolnir
32c0b93e05 📝 rename tokname => token_name_mapper 2024-06-04 00:14:47 +08:00
ValKmjolnir
5e2268e4c5 add token ?? and ?. 2024-06-03 23:29:40 +08:00
ValKmjolnir
5a165d3255 beautiful unicode output info 2024-06-02 23:56:49 +08:00
ValK
0cf8e3bd23 Merge pull request #43 from ValKmjolnir/develop
🎨 rename & file structure change & improve ast/gc dump format
2024-06-02 22:51:28 +08:00
ValKmjolnir
0f61f8e18e 🐛 fix config error in cmake file 2024-06-02 22:47:53 +08:00
ValKmjolnir
18285c1c5a 🐛 fix link error 2024-06-02 22:39:14 +08:00
ValKmjolnir
b020ebf5b5 🐛 fix weird error on mac build 2024-06-02 22:36:41 +08:00
ValKmjolnir
627cb9d773 Merge branch 'master' into develop 2024-06-02 22:33:59 +08:00
ValKmjolnir
886703f3bd 📝 improve output format of gc dump 2024-06-02 22:32:36 +08:00
ValKmjolnir
f6d4d79c51 add windows code page manager 2024-06-02 22:08:17 +08:00
ValKmjolnir
4ac4675491 📝 delete nasal_misc.cpp 2024-06-02 19:58:50 +08:00
ValKmjolnir
05605c3570 📝 move functions from nasal_misc => util 2024-06-02 16:53:03 +08:00
ValKmjolnir
c840d70a9c 📝 add src/util 2024-06-02 16:35:48 +08:00
ValKmjolnir
764a0c6b4b 📝 move repl.* => repl/* 2024-06-02 16:10:23 +08:00
ValKmjolnir
a7dfd34120 📝 rename nasal_builtin.* 2024-06-02 16:06:18 +08:00
ValKmjolnir
2c6a0fd84d using new cli parser 2024-06-02 00:33:21 +08:00
ValKmjolnir
40a53a4224 add cli module and test/wavecity.nas 2024-06-01 18:52:22 +08:00
ValKmjolnir
3b71c5fee4 📝 change output info width from 6 -> 8 2024-06-01 00:41:05 +08:00
ValKmjolnir
b02168fc55 📝 delete system call in test files 2024-05-22 23:41:14 +08:00
ValKmjolnir
f9f2cf6d47 📝 change eol to lf 2024-05-22 20:29:13 +08:00
ValKmjolnir
6a155f56e5 📝 update doc about enabling 65001 code page 2024-05-22 20:02:37 +08:00
ValKmjolnir
971583b1c7 support utf8 ast dump on windows 2024-05-22 19:23:09 +08:00
ValKmjolnir
b6a7b7f46d 📝 rename flstream => filestream 2024-05-22 00:08:36 +08:00
ValKmjolnir
7590a286c3 more beautiful ast dump 2024-05-21 23:51:44 +08:00
ValKmjolnir
77a14699f4 📝 rename lexical tokens 2024-05-21 23:31:21 +08:00
ValKmjolnir
2c851613ce 📝 update docs 2024-05-17 00:10:24 +08:00
ValK
b32d4f8f82 Merge pull request #42 from ValKmjolnir/develop
📝 update nightly build ci
2024-05-16 19:39:54 +08:00
ValKmjolnir
ad87298737 📝 update ci 2024-05-16 19:35:23 +08:00
ValK
4f0afb31db Merge pull request #41 from ValKmjolnir/develop
📝 adjust CI & improve maintainability
2024-05-16 00:17:18 +08:00
ValKmjolnir
ba6f48c991 📝 adjust ci 2024-05-16 00:13:48 +08:00
ValKmjolnir
250667268a 📝 adjust ci 2024-05-16 00:10:10 +08:00
ValKmjolnir
2bb7ebcb58 🐛 fix syntax error in yaml 2024-05-16 00:08:09 +08:00
ValKmjolnir
7ab40e57e3 📝 try new release action 2024-05-16 00:06:45 +08:00
ValKmjolnir
939257d684 📝 try using action created by myself 2024-05-15 23:49:08 +08:00
ValKmjolnir
4550478caf 📝 test new action 2024-05-15 23:26:45 +08:00
ValKmjolnir
bdedd0301d 📝 test CI 2024-05-15 23:16:50 +08:00
ValKmjolnir
5a02a5bd99 📝 test new release 2024-05-15 23:14:42 +08:00
ValKmjolnir
1f43eae4fa 🔥 expand stack depth to 65535 2024-05-15 22:59:36 +08:00
ValKmjolnir
290ed122ba 🔥 change action/checkout to v3 2024-05-15 00:33:23 +08:00
ValKmjolnir
00a655a9fc 📝 change opcode dump format 2024-05-15 00:27:31 +08:00
ValKmjolnir
8759d12eda 🐛 delete omp.h header 2024-05-14 00:23:00 +08:00
ValKmjolnir
9b168b5d52 📝 move andy_gc_test to test dir 2024-05-14 00:16:02 +08:00
ValKmjolnir
ef09946fd6 str() can be used on complex types 2024-05-13 21:59:05 +08:00
ValKmjolnir
230848a6e1 📝 improve maintainability 2024-05-13 00:06:37 +08:00
ValKmjolnir
a11e0726bb 🔥 add check for imported files' quantity 2024-05-12 23:29:48 +08:00
ValKmjolnir
96731d180f append code size from u32 to u64 2024-05-12 22:33:51 +08:00
ValKmjolnir
8e38764df0 📝 change span data from u32 to u64 2024-05-12 19:34:05 +08:00
ValKmjolnir
eed59cfe07 📝 update README: add download links 2024-05-12 12:11:31 +08:00
ValKmjolnir
ac1960ea27 📝 adjust CI 2024-05-12 11:51:28 +08:00
ValKmjolnir
a34f90cbd1 📝 fix typo: unmutable -> immutable 2024-05-12 00:21:18 +08:00
ValK
1df1ae4a43 Merge pull request #40 from ValKmjolnir/develop
🐛 fix typo in cli help message
2024-04-12 00:08:41 +08:00
ValKmjolnir
1c011f0aad 📝 adjust size of pic in README 2024-04-11 23:45:48 +08:00
ValKmjolnir
cc6ad76eaa 🐛 fix typo in help info 2024-04-11 21:48:42 +08:00
ValKmjolnir
dbbcb134f2 📝 improve visual effect of test/langtons-ant 2024-03-28 23:21:57 +08:00
ValK
539e4c4964 Merge pull request #39 from ValKmjolnir/develop
👍 add regex lib & CI will pack executable in nightly build release
2024-03-03 23:40:59 +08:00
ValKmjolnir
b7bc36bfde 🔥 trigger CI 2024-03-03 23:37:08 +08:00
ValKmjolnir
974d413537 🐛 fix CI 2024-03-03 23:29:31 +08:00
ValKmjolnir
7cd249f049 📝 change CI config 2024-03-03 23:27:44 +08:00
ValKmjolnir
cc04720f12 🔥 change file structure & add pack.py 2024-03-03 23:12:16 +08:00
ValKmjolnir
4da8bbbd40 📝 add action badge in README 2024-03-01 23:05:04 +08:00
ValKmjolnir
22b9bce298 add regex lib 2024-03-01 22:39:43 +08:00
ValK
2e321fc4d6 Merge pull request #38 from ValKmjolnir/develop
 optimize import module & add limited execution mode (disable unsafe system api)
2024-02-21 19:59:42 +08:00
ValKmjolnir
b3e6b5784a add limited execution mode 2024-02-21 19:55:46 +08:00
ValKmjolnir
38c6fe2c5c add test file langtons-ant.nas 2024-01-16 23:02:48 +08:00
ValKmjolnir
4cceb63053 📝 optimize format in nasal_err.h 2023-12-24 16:20:45 +08:00
ValKmjolnir
1831fc4245 📝 update link in welcome info 2023-12-24 00:00:46 +08:00
ValKmjolnir
d70864fb2e 📝 add os and arch info in welcome info 2023-12-23 12:36:13 +08:00
ValKmjolnir
7ce8d3af25 optimize import module 2023-12-22 00:15:31 +08:00
ValKmjolnir
d3840edd73 avoid infinite recursion when loading module 2023-12-18 23:43:37 +08:00
ValK
a63bb6c35a Merge pull request #37 from ValKmjolnir/develop
 add json cpp lib & argparse module
2023-12-12 00:14:55 +08:00
ValKmjolnir
6e819391aa 🐛 avoid stackoverflow in json stringify 2023-12-12 00:05:38 +08:00
ValKmjolnir
c59743b2ed merge module/json.cpp into builtins 2023-12-10 23:55:18 +08:00
ValKmjolnir
49ebdc8e19 finish argparse module 2023-12-10 17:25:16 +08:00
ValKmjolnir
734aec1bc2 finish parse trigger command 2023-12-10 00:18:46 +08:00
ValKmjolnir
43ff63dc83 📝 update README 2023-12-09 00:07:44 +08:00
ValKmjolnir
ca7666c220 finish parse in argparse.nas 2023-12-08 00:07:14 +08:00
ValKmjolnir
8e0d1e18e1 🐛 fix bug in json.cpp & add std/argparse.nas 2023-12-07 00:04:41 +08:00
ValKmjolnir
a2c738d4c7 🐛 delete dlclose in module_test.nas 2023-12-06 21:17:53 +08:00
ValKmjolnir
2a36db1cf5 🐛 try fixing segfault in module_test.nas 2023-12-06 21:02:56 +08:00
ValKmjolnir
65dbe51c05 🐛 trigger CI 2023-12-06 20:53:25 +08:00
ValKmjolnir
3e226b6f73 🐛 close test for module_test 2023-12-06 20:48:28 +08:00
ValKmjolnir
fea52d9c38 🐛 trigger CI 2023-12-06 20:47:28 +08:00
ValKmjolnir
eb753c56b8 🐛 trigger CI 2023-12-06 20:38:20 +08:00
ValKmjolnir
f70ebc86b2 🐛 trigger CI 2023-12-06 20:31:01 +08:00
ValKmjolnir
8666580f14 🐛 trigger CI 2023-12-06 20:21:11 +08:00
ValKmjolnir
d56434ae28 optimize lib.nas 2023-12-06 20:10:07 +08:00
ValKmjolnir
f747d91efe optimize code 2023-12-05 23:54:26 +08:00
ValKmjolnir
3b1659e7ee add namespace fs 2023-12-03 21:14:14 +08:00
ValKmjolnir
2c03d59b6f 🐛 fix error loading bug in MSVC version 2023-12-02 21:48:20 +08:00
ValKmjolnir
d42e4a5897 📝 change CRLF to LF 2023-12-02 19:42:21 +08:00
ValKmjolnir
476fbdb859 show function entry file location
in call trace info
2023-12-01 19:31:47 +08:00
ValKmjolnir
14ec9d2a34 Merge branch 'master' into develop 2023-12-01 18:48:13 +08:00
ValK
1148ef1a08 Merge pull request #36 from sidi762/master
📝 Update contact info in README.md
2023-12-01 18:46:12 +08:00
Sidi Liang
aca8d7a011 Update contact info in README.md 2023-12-01 14:35:26 +08:00
ValKmjolnir
bd3ae8c440 optimize error handling in json module 2023-11-29 23:24:29 +08:00
ValKmjolnir
135665a5df 🐛 fix generation bug in json module 2023-11-28 23:54:58 +08:00
ValKmjolnir
f2ae974e1f change CMakeLists for json module 2023-11-28 00:19:14 +08:00
ValKmjolnir
4f44559a9d add cpp module json 2023-11-28 00:18:13 +08:00
ValKmjolnir
d55ba26c83 add cpp module json 2023-11-28 00:17:48 +08:00
ValKmjolnir
8c3c8d3d62 add ghost type timestamp 2023-11-27 20:25:02 +08:00
ValKmjolnir
2bb9655422 add recursive file searcher methods 2023-11-24 00:17:47 +08:00
ValKmjolnir
cacf0ae86a avoid compilation warnings 2023-11-23 21:35:11 +08:00
ValKmjolnir
01ceaf7e66 replace vm_type comparison with .is_xxx 2023-11-23 00:20:52 +08:00
ValKmjolnir
be84388f5b change enum vm_type to enum class 2023-11-22 22:23:15 +08:00
ValKmjolnir
c453cca0a6 add gc mark function pointer in ghost 2023-11-22 00:36:51 +08:00
ValKmjolnir
56e93e703e add test/juliaset.nas 2023-11-20 00:09:18 +08:00
ValKmjolnir
5a0d8dec20 add to_num & to_char in std.string 2023-11-18 00:12:05 +08:00
ValK
7e5c935356 Merge pull request #35 from ValKmjolnir/develop
📝 updatedocuments and scripts
2023-11-17 00:19:35 +08:00
ValKmjolnir
8582c0f221 📝 update readme 2023-11-17 00:13:17 +08:00
ValKmjolnir
28a42346b7 📝 update scripts 2023-11-16 23:19:03 +08:00
ValKmjolnir
a03739ebb2 📝 update documents and pics 2023-11-15 00:36:25 +08:00
ValKmjolnir
7d35e5bbed 📝 update readme 2023-11-08 22:23:09 +08:00
ValKmjolnir
0b66227667 add new std file phi.nas 2023-11-08 22:15:33 +08:00
ValKmjolnir
fee646965e 📝 add notes in tools/fgfs_props_getter.nas 2023-11-08 00:18:30 +08:00
ValKmjolnir
d461cbb05c add POST method to edit prop tree 2023-11-07 23:55:35 +08:00
ValKmjolnir
7b05a0a244 package getprop using socket prop getter 2023-11-07 19:13:38 +08:00
ValKmjolnir
433743f790 add tool for getting property tree from fgfs
by using --httpd in fgfs
2023-11-06 22:18:48 +08:00
ValKmjolnir
2ea9e03522 🐛 fix bug in recvfrom 2023-11-06 00:22:11 +08:00
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
ValKmjolnir
3c9a10d710 avoid unnecessary deep copy by using rvalue ref
and test file update.
bug fixed in test/lexer.nas
2021-06-24 22:10:08 +08:00
ValKmjolnir
fd57e9a47c performance optimization of vm/lex/parse/test 2021-06-24 00:26:26 +08:00
ValKmjolnir
ab99d2d1ed change mcall to call->mcall&allow differen lvalue in assignment 2021-06-21 16:46:47 +08:00
Valk Richard Li
ae0dae5956 update 2021-06-21 01:02:09 +08:00
Valk Richard Li
00c6e3b4fd nothing changed 2021-06-20 01:27:01 +08:00
Valk Richard Li
cdf7b92a8e add statistical information 2021-06-19 00:32:10 +08:00
Valk Richard Li
0e979a6e7b bug fixed & delete operand vapp 2021-06-15 00:49:32 +08:00
Valk Richard Li
dd144305da update test file 2021-06-14 00:27:00 +08:00
Valk Richard Li
4f3ddf803a add tips 2021-06-13 01:01:32 +08:00
Valk Richard Li
de305d26ad fixed an error in README 2021-06-12 01:23:06 +08:00
Valk Richard Li
9f30f45774 Update README.md 2021-06-11 15:28:25 +08:00
Valk Richard Li
1ae47807eb Add command line parameters & chr supports extended ASCII 2021-06-11 15:16:06 +08:00
Valk Richard Li
3deea632f8 front end optimization&stack overflow prompt
change parameter type to avoid unnecessary copies of string.
change stack depth from 65536<<4 to 16+(65536<<2).
now you could know stack overflow when it happens
2021-06-07 23:53:43 +08:00
Valk Richard Li
9f2c31149a bug fixed
fixed SIGSEGV when failed to load file in nasal_lexer::openfile
2021-06-06 19:17:02 +08:00
Valk Richard Li
b25a1bc3f4 more efficient str2num 2021-06-05 20:42:58 +08:00
Valk Richard Li
fd7677f94f update README.md(some history of this project)
AND a question of admins' brains of gitee
2021-06-05 17:15:07 +08:00
Valk Richard Li
2e31a70406 Update README.md 2021-06-03 21:59:15 +08:00
Valk Richard Li
8e29a3ec5b bug fixed & more efficient callfv
I changed callfv's way of calling a function with arguments in vm_vec.
now callfv fetches arguments from val_stack directly,so it runs test/fib.nas from 2.4s to 1.9s.

delete operand callf,add operands callfv & callfh.

also,i check val_stack's top to make sure there is not a stack overflow.
2021-06-03 21:49:31 +08:00
Valk Richard Li
a68bf85f04 bug fixed
a gc bug which causes  fatal error.
add member value collect to make sure that nasal_val is not collected repeatedly.
use builtin_alloc in builtin function to avoid incorrect collection of value in use(gc_alloc).
change free_list to free_list[vm_type_size] to avoid too many calls of new/delete(but seems useless?)
but the most important thing is fixing this bug.
2021-05-31 19:10:59 +08:00
Valk Richard Li
aae9395d66 bug fixed
add operand op_offset to make sure arguments are loaded at correct places,before this commit,arguments are not loaded from a correct offset,which will cause SIGSEGV when calling a function that has complex closure
2021-05-17 21:15:57 +08:00
Valk Richard Li
a463af53b7 add license & other changes
parser recognizes syntax errors more accurately.
change some for loop to standard c++11 for(auto iter:obj)
add MIT license
change info in README.md
2021-05-04 17:39:24 +08:00
Valk Richard Li
6adb991c04 parser reports syntax error accurately
still need improvement
2021-05-04 01:13:53 +08:00
Valk Richard Li
c5f4736984 change scope from unordered_map to vector 2021-04-19 19:12:41 +08:00
Valk Richard Li
1a233fbe15 update 2021-04-12 13:21:13 +08:00
Valk Richard Li
a7d6518bff add test file & update README 2021-04-04 23:35:13 +08:00
Valk Richard Li
0700ce14f7 change function of vapp & README update 2021-04-03 23:47:14 +08:00
Valk Richard Li
c88620920b add stl & more efficient scope 2021-04-02 22:19:29 +08:00
Valk Richard Li
125fc8a9fe bug fixed & test file changes
compare operators now run more efficiently.
2021-03-31 20:59:13 +08:00
Valk Richard Li
b06e1bb5dd add static symbol check & test file update 2021-03-30 15:55:38 +08:00
Valk Richard Li
c7316e9780 change id name & add test file 2021-03-30 00:12:48 +08:00
Valk Richard Li
be1bcdfe2c bug fixed 2021-03-28 17:39:24 +08:00
Valk Richard Li
144e6f45da gc changed to mark-sweep 2021-03-27 01:08:05 +08:00
Valk Richard Li
569d5c6c6a update 2021-03-11 23:18:04 +08:00
Valk Richard Li
7087c67d79 add constant table in vm 2021-03-08 19:45:33 +08:00
226 changed files with 30017 additions and 18762 deletions

1
.gitattributes vendored Normal file
View File

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

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

@@ -0,0 +1,69 @@
name: Build/Test/Package CI
on:
schedule:
- cron: "0 16 * * *"
push:
branches: [ master, develop ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
mac-aarch64-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Update Tag
run: |
git fetch --tags origin
git tag -f next_macOS
git push -f origin next_macOS
- name: Build
run: |
make -j4
cd module
make all -j4
cd ..
- name: Test
run: make test
- name: Package
run: python3 tools/pack.py
- name: Release
uses: softprops/action-gh-release@v2.0.5
with:
name: macOS nightly build
tag_name: next_macOS
prerelease: true
draft: false
files: |
nasal-Darwin.tar
linux-x86_64-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update Tag
run: |
git fetch --tags origin
git tag -f next_linux_x86_64
git push -f origin next_linux_x86_64
- name: Build
run: |
make -j4
cd module
make all -j4
cd ..
- name: Test
run: make test
- name: Package
run: python3 tools/pack.py
- name: Release
uses: softprops/action-gh-release@v2.0.5
with:
name: linux nightly build
tag_name: next_linux_x86_64
prerelease: true
draft: false
files: |
nasal-Linux.tar

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

99
CMakeLists.txt Normal file
View File

@@ -0,0 +1,99 @@
cmake_minimum_required(VERSION 3.10)
project(nasal VERSION 11.2)
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)
# MSVC needs this command option to really enable utf-8 output
if(MSVC)
add_compile_options(/utf-8)
endif()
# generate release executables
set(CMAKE_BUILD_TYPE "Release")
# build nasal used object
set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/cli/cli.cpp
${CMAKE_SOURCE_DIR}/src/natives/builtin.cpp
${CMAKE_SOURCE_DIR}/src/natives/coroutine.cpp
${CMAKE_SOURCE_DIR}/src/natives/fg_props.cpp
${CMAKE_SOURCE_DIR}/src/natives/bits_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/io_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/json_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/math_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/dylib_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/regex_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/unix_lib.cpp
${CMAKE_SOURCE_DIR}/src/repl/repl.cpp
${CMAKE_SOURCE_DIR}/src/util/fs.cpp
${CMAKE_SOURCE_DIR}/src/util/util.cpp
${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_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_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)
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)
# link ldl and lpthread
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)
# copy nasal from build dir to the outside dir
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/util/util.cpp
${CMAKE_SOURCE_DIR}/src/util/fs.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})
target_include_directories(module-used-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(fib module-used-object)
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(key module-used-object)
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(mat module-used-object)
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(nasock module-used-object)

339
LICENSE Normal file
View File

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

687
README.md
View File

@@ -1,362 +1,451 @@
# Nasal Script Language
# __Nasal - Modern Interpreter__
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language) is a script language that used in [FlightGear](https://www.flightgear.org/).
<img src="./doc/pic/header.png" style="width:600px"></img>
The interpreter is still in development(now it works well --2021/2/15). We really need your support!
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
[![C/C++ CI](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
Also,i am a member of [FGPRC](https://www.fgprc.org/), welcome to join us!
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
# Why Writing Nasal Interpreter
## __Contents__
Nasal is a script language first used in Flightgear.
* [__Introduction__](#introduction)
* [__Download__](#download)
* [__Compile__](#compile)
* [__Usage__](#how-to-use)
* [__Tutorial__](./doc/tutorial.md)
* [__Release Notes__](./doc/dev.md#release-notes)
* [__Development History__](./doc/dev.md)
* [__Benchmark__](./doc/benchmark.md)
* [__Difference__](#difference-between-andys-and-this-interpreter)
* [__Trace Back Info__](#trace-back-info)
* [__Debugger__](#debugger)
* [__REPL__](#repl)
But in last summer holiday, members in FGPRC told me that it is hard to debug with nasal-console in Flightgear, especially when checking syntax errors.
__Contact us if having great ideas to share!__
So i tried to write a new interpreter to help them checking syntax error and even, runtime error.
* __lhk101lhk101@qq.com__ (ValKmjolnir)
I wrote the lexer, parser and runtime(bytecode virtual machine/ast-runtime virtual machine) to help checking errors.
* __sidi.liang@gmail.com__ (Sidi)
They found it easier for them to check errors before copying nasal-codes in nasal-console in Flightgear to test.
## __Introduction__
# How to Compile
![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
> g++ -std=c++11 main.cpp -o main.exe
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
The designer is [Andy Ross](https://github.com/andyross).
This interpreter is rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`).
We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
# Parser
Old version of this project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5). Now it uses __GPL v2 license__ (since 2023/6).
LL(k) parser.
### __Why writing this Nasal interpreter?__
2019 summer,
members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear,
especially when checking syntax errors.
So i wrote a new interpreter to help checking syntax error and runtime error.
I wrote the lexer, parser and
bytecode virtual machine to help checking errors.
We found it much easier to debug.
You could also use this language to write some
interesting programs and run them without the lib of Flightgear.
You could add your own modules to make
the interpreter a useful tool in your own projects.
## __Download__
Nightly build could be found here.
Windows nightly build is not supported yet,
please wait or just compile it by yourself, a Cmake file is given for Visual Studio to compile this project easily:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [WIP]
## __Compile__
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
Better download the latest update source of the interpreter and build it! It's quite easy to build this interpreter, what you need are only two things: C++ compiler and the `make`. There is no third-party library used in this project.
### __Windows (MinGW-w64)__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
Make sure thread model is `posix thread model`, otherwise no thread library exists.
> mingw32-make nasal.exe -j4
### __Windows (Visual Studio)__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
There is a [__CMakelists.txt__](./CMakeLists.txt) to create project.
### __Linux / macOS / Unix__
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU) ![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
> make -j
You could choose which compiler you want to use:
> make nasal CXX=... -j
## __How to Use__
![usage](./doc/gif/help.gif)
If your system is `Windows` and you want to output unicode, you could write this in nasal code:
```javascript
(var a,b,c)=[{b:nil},[1,2],func{return 0;}];
(a.b,b[0],c)=(1,2,3);
if (os.platform()=="windows") {
system("chcp 65001");
}
```
have the same first set,so LL(1) is useless for this language.
Maybe in the future i can refactor it to LL(1) with special checks.
# Abstract Syntax Tree
## Version 1.2
The ast has been completed in this version.
## Version 2.0
A completed ast-interpreter with unfinished lib functions.
## Version 3.0
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
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.
# Byte Code Interpreter
## Version 4.0
I have just finished the first version of byte-code-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:
Or use `std.runtime.windows.set_utf8_output()`:
```javascript
for(var i=0;i<4000000;i+=1);
use std.runtime;
runtime.windows.set_utf8_output();
```
```asm
.number 0
.number 4e+006
.number 1
.symbol i
0x00000000: pzero 0x00000000
0x00000001: load 0x00000000 (i)
0x00000002: call 0x00000000 (i)
0x00000003: pnum 0x00000001 (4e+006)
0x00000004: less 0x00000000
0x00000005: jf 0x0000000b
0x00000006: pone 0x00000000
0x00000007: mcall 0x00000000 (i)
0x00000008: addeq 0x00000000
0x00000009: pop 0x00000000
0x0000000a: jmp 0x00000002
0x0000000b: nop 0x00000000
```
## __Difference Between Andy's and This Interpreter__
## Version 5.0
![error](./doc/gif/error.gif)
I decide to optimize bytecode vm in this version.
<details><summary>Must use `var` to define variables</summary>
Because it takes more than 1.5s to count i from 0 to 4000000-1.This is not efficient at all!
This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
And flightgear's nasal interpreter also has the same rule.
So do not use variable without using `var` to declare it.
2021/1/23 update: Now it can count from 0 to 4000000-1 in 1.5s.
# How to Use Nasal to Program
## basic value type
nasal has 6 value types.Number,string,vector,hash,function,nil.
Number has 3 formats.Dec,hex and oct;
String has 3 formats.But the third one is often used to declare a character.
Vector has unlimited length and can store all types of values.
Hash is a hashmap that stores values with strings/identifiers as the key.
Function is also a value type in nasal.
In Andy's interpreter:
```javascript
var spc=nil;
foreach(i; [0, 1, 2, 3])
print(i)
```
var a=1;
var a=2.71828;
var a=2.147e16;
var a=1e-10;
var a=0x7fffffff;
var a=0xAA55;
var a=0o170001;
This program can run normally.
But take a look at the iterator `i`,
it is defined in foreach without using keyword `var`.
I think this design will make programmers feeling confused that they maybe hard to find the `i` is defined here.
Without `var`, they may think this `i` is defined anywhere else.
var b='str';
var b="another string";
var b=`c`;
So in this interpreter i use a more strict syntax to force users to use `var` to define iterator of forindex and foreach.
If you forget to add the keyword `var`, you will get this:
var c=[];
var c=[0,nil,{},[],func(){return 0;}];
append(c,0,1,2);
```javascript
code: undefined symbol "i"
--> test.nas:1:9
|
1 | foreach(i; [0, 1, 2, 3])
| ^ undefined symbol "i"
var d={
member1:nil,
member2:'str',
'member3':'member\'s name can also be a string constant',
"member4":"also this",
function:func()
{
var a=me.member2~me.member3;
return a;
code: undefined symbol "i"
--> test.nas:2:11
|
2 | print(i)
| ^ undefined symbol "i"
```
</details>
## __Trace Back Info__
![stackoverflow](./doc/gif/stackoverflow.gif)
When interpreter crashes,
it will print trace back information:
<details><summary>Native function `die`</summary>
Function `die` is used to throw error and crash immediately.
```javascript
func() {
println("hello");
die("error occurred this line");
return;
}();
```
```javascript
hello
[vm] error: error occurred this line
[vm] error: error occurred in native function
call trace (main)
call func@0x557513935710() {entry: 0x850}
trace back (main)
0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x5575138e8c40, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x5575138e8c50
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x5575138d9190> error occurred t...
0x000007 | nil |
0x000006 | func | <0x5575139356f0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
```
</details>
<details><summary>Stack overflow</summary>
Here is an example of stack overflow:
```javascript
func(f) {
return f(f);
}(
func(f) {
f(f);
}
};
var f=func(x,y,z)
{
return nil;
}
var f=func
{
return 1024;
}
var f=func(x,y,z,default_parameter1=1,default_parameter2=2)
{
return x+y+z+default_parameter1+default_parameter2;
}
var f=func(x,y,z,dynamic_parameter...)
{
var sum=0;
foreach(var i;dynamic_parameter)
sum+=i;
return sum+x+y+z;
}
)();
```
## operators
```javascript
1+2;
1-2;
1*2;
1/2;
'str1'~'str2';
(1+2)*(3+4)
[vm] error: stack overflow
1+1 and 0;
1+2*3 or 0;
1<0;
1>0;
1<=0;
1>=0;
1==0;
1!=0;
call trace (main)
call func@0x564106058620(f) {entry: 0x859}
--> 583 same call(s)
call func@0x5641060586c0(f) {entry: 0x851}
-1;
!0;
trace back (main)
0x000859 45 00 00 01 calll 0x1(a.nas:5)
0x00085b 4a 00 00 01 callfv 0x1(a.nas:5)
0x00085b 582 same call(s)
0x000853 4a 00 00 01 callfv 0x1(a.nas:2)
0x00085f 4a 00 00 01 callfv 0x1(a.nas:3)
a=b=c=d=1;
a+=1;
a-=1;
a*=1;
a/=1;
a~='string';
stack (0x56410600be00, limit 10, total 4096)
0x000fff | func | <0x564106058600> entry:0x859
0x000ffe | pc | 0x85b
0x000ffd | addr | 0x56410601bd20
0x000ffc | nil |
0x000ffb | nil |
0x000ffa | func | <0x564106058600> entry:0x859
0x000ff9 | nil |
0x000ff8 | func | <0x564106058600> entry:0x859
0x000ff7 | pc | 0x85b
0x000ff6 | addr | 0x56410601bcb0
```
## definition
</details>
<details><summary>Normal vm error crash info</summary>
Error will be thrown if there's a fatal error when executing:
```javascript
var a=1;
var (a,b,c)=[0,1,2];
var (a,b,c)=(0,1,2);
(var a,b,c)=[0,1,2];
(var a,b,c)=(0,1,2);
func() {
return 0;
}()[1];
```
## multi-assignment
```javascript
(a,b[0],c.d)=[0,1,2];
(a,b[1],c.e)=(0,1,2);
[vm] error: must call a vector/hash/string but get number
trace back (main)
0x000854 47 00 00 00 callv 0x0(a.nas:3)
stack (0x564993f462b0, limit 10, total 1)
0x000000 | num | 0
```
## conditional expression
</details>
<details><summary>Detailed crash info</summary>
Use command __`-d`__ or __`--detail`__ the trace back info will show more details:
```javascript
if(1)
{
;
}
elsif(2)
{
;
}
else if(3)
{
;
}
else
{
;
}
hello
[vm] error: error occurred this line
[vm] error: error occurred in native function
call trace (main)
call func@0x55dcb5b8fbf0() {entry: 0x850}
trace back (main)
0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x55dcb5b43120, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x55dcb5b43130
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x55dcb5b33670> error occurred t...
0x000007 | nil |
0x000006 | func | <0x55dcb5b8fbd0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
registers (main)
[pc ] | pc | 0x547
[global] | addr | 0x55dcb5b53130
[local ] | addr | 0x55dcb5b43190
[memr ] | addr | 0x0
[canary] | addr | 0x55dcb5b53110
[top ] | addr | 0x55dcb5b431f0
[funcr ] | func | <0x55dcb5b65620> entry:0x547
[upval ] | nil |
global (0x55dcb5b53130)
0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val]
0x000001 | vec | <0x55dcb5b64c20> [0 val]
...
0x00005e | func | <0x55dcb5b8fc70> entry:0x846
local (0x55dcb5b43190 <+7>)
0x000000 | nil |
0x000001 | str | <0x55dcb5b33670> error occurred t...
0x000002 | nil |
```
## loop
</details>
## __Debugger__
![dbg](./doc/gif/dbg.gif)
We added a debugger in `v8.0`.
Use command `./nasal -dbg xxx.nas` to use the debugger,
and the debugger will print this:
<details><summary>Click to unfold</summary>
```javascript
while(condition)
continue;
for(var i=0;i<10;i+=1)
break;
forindex(var i;elem)
print(elem[i]);
foreach(var i;elem)
print(i);
```
## subvec
Use index to search one element in the string will get the ascii number of this character.If you want to get the character,use built-in function chr().
```javascript
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil];
"hello world"[0];
```
## special function call
This is of great use but is not very efficient.
```javascript
a(x:0,y:1,z:2);
```
## closure
```javascript
var f=func()
{
var a=1;
return func(){return a;};
}
print(f()());
```
## built-in functions
Must import lib.nas or has these functions' definitions inside your code.
Also you could add builtin functions of your own(written in C/C++) to help you calculate things more quickly.(Advanced usage)
Check built-in functions in lib.nas!
If you want to add your own built-in functions,define the function in nasal_builtin.h.
Definition:
```C++
nasal_val* builtin_chr(nasal_val*,nasal_gc&);
```
Then complete this function using C++:
```C++
nasal_val* builtin_print(nasal_val* local_scope_addr,nasal_gc& gc)
{
// get arguments by using in_builtin_find
nasal_val* vector_value_addr=in_builtin_find("elements");
// main process
// also check type here,if get a type error,use builtin_error_occurred and return nullptr
nasal_vec& ref_vec=vector_value_addr->get_vector();
int size=ref_vec.size();
for(int i=0;i<size;++i)
{
nasal_val* tmp=ref_vec[i];
switch(tmp->get_type())
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->get_number(); break;
case vm_str: std::cout<<tmp->get_string(); break;
case vm_vec: tmp->get_vector().print(); break;
case vm_hash: tmp->get_hash().print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
source code:
--> var fib = func(x) {
if (x<2) return x;
return fib(x-1)+fib(x-2);
}
// if a nasal value is not in use,use gc::del_reference to delete it
// if get a new reference of a nasal value,use gc::add_reference
// generate return value,use gc::gc_alloc(type) to make a new value
return gc.gc_alloc(vm_nil);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
next bytecode:
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
--> 0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
vm stack (0x7fca7e9f1010, limit 16, total 0)
>>
```
After that, write the built-in function's name(in nasal) and the function's pointer in this table:
</details>
```C++
struct FUNC_TABLE
{
std::string func_name;
nasal_val* (*func_pointer)(nasal_val* x,nasal_gc& gc);
} builtin_func_table[]=
{
{"nasal_call_builtin_std_cout",builtin_print},
{"",NULL}
};
```
If want help, input `h` to get help.
At last,warp the 'nasal_call_builtin_std_cout' in a nasal file:
When running the debugger, you could see what is on stack.
This will help you debugging or learning how the vm works:
<details><summary>Click to unfold</summary>
```javascript
var print=func(elements...)
{
nasal_call_builtin_std_cout(elements);
return nil;
};
source code:
var fib = func(x) {
--> if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
next bytecode:
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
--> 0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
vm stack (0x7fca7e9f1010, limit 16, total 8)
0x000007 | pc | 0x3c7
0x000006 | addr | 0x0
0x000005 | nil |
0x000004 | nil |
0x000003 | num | 0
0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x5573f66ef5f0> func(elems...) {..}
>>
```
In version 5.0,if you don't warp built-in function in a normal nasal function,this built-in function may cause a fault when searching arguments,which will cause SIGSEGV segmentation error(maybe).
</details>
Use import("") to get the nasal file including your biult-in functions,then you could use it.
## REPL
We added experimental repl interpreter in v11.0.
Use this command to use the repl interpreter:
> nasal -r
Then enjoy!
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>>
```
Try import `std/json.nas`~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>> use std.json;
{stringify:func(..) {..},parse:func(..) {..}}
>>>
```

435
doc/README_zh.md Normal file
View File

@@ -0,0 +1,435 @@
# __Nasal - Modern Interpreter__
<img src="../doc/pic/header.png" style="width:600px"></img>
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
[![C/C++ CI](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
## __目录__
* [__简介__](#简介)
* [__下载__](#下载)
* [__编译__](#编译)
* [__使用方法__](#使用方法)
* [__教程__](../doc/tutorial_zh.md)
* [__发行日志__](../doc/dev_zh.md#发行日志)
* [__开发历史__](../doc/dev_zh.md)
* [__测试数据__](../doc/benchmark.md)
* [__特殊之处__](#与andy解释器的不同之处)
* [__堆栈追踪信息__](#堆栈追踪信息)
* [__调试器__](#调试器)
* [__交互解释器__](#交互解释器)
__如果有好的意见或建议欢迎联系我们!__
* __lhk101lhk101@qq.com__ (ValKmjolnir)
* __sidi.liang@gmail.com__ (Sidi)
## __简介__
![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。
该语言的设计者为 [Andy Ross](https://github.com/andyross)。
该解释器由 [ValKmjolnir](https://github.com/ValKmjolnir) 使用 `C++`(`-std=c++17`)重新实现。非常感谢 Andy 为我们设计了这个神奇且简洁的编程语言: [Andy Ross 的 nasal 解释器](https://github.com/andyross/nasal)。
该项目旧版本使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始新版本使用 __GPL v2__ 协议。
### __为什么重新写 Nasal 解释器?__
2019 年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在 Flightgear 中提供的 nasal 控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。
我编写了 nasal 的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行 nasal 程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率。
你也可以使用这个语言来写一些与 Flightgear 运行环境无关的有趣的程序,并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块,使它成为项目中一个非常有用的工具。
## __下载__
现在支持下载预览版(Nightly Build)。
Windows 平台的预览版解释器现在还没配置相关流水线,
请耐心等候或者直接在本地编译。
我们提供了一份 Cmake 文件,可以很方便地在 Visual Studio 中编译:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [施工中...]
## __编译__
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
推荐下载最新代码包编译,这个项目非常小巧, 没有使用任何第三方库,因此编译起来非常轻松,
只需要这两样东西: C++ 编译器以及make程序。
### __Windows 平台 (MinGW-w64)__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
确保 thread model 是 `posix thread model`, 否则没有 thread 库。
> mingw32-make nasal.exe -j4
### __Windows 平台 (Vistual Studio)__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中创建项目。
### __Linux / macOS / Unix 平台__
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
> make -j
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
> make nasal CXX=... -j
## __使用方法__
![usage](../doc/gif/help.gif)
如果你是 `Windows` 用户且想正常输出 unicode可以这样开启 unicode 代码页:
```javascript
if (os.platform()=="windows") {
system("chcp 65001");
}
```
或者使用 `std.runtime.windows.set_utf8_output()`:
```javascript
use std.runtime;
runtime.windows.set_utf8_output();
```
## __与andy解释器的不同之处__
![error](../doc/gif/error.gif)
<details><summary>必须用 var 定义变量</summary>
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。
同样的flightgear 内置的 nasal 解释器也采取了类似的措施,所以使用变量前务必用 `var` 先进行声明。
在Andy的解释器中:
```javascript
foreach(i; [0, 1, 2, 3])
print(i)
```
这个程序可以正常运行。然而这个`i`标识符实际上在这里是被第一次定义,而且没有使用`var`。我认为这样的设计很容易让使用者迷惑。他们可能都没有发现这里实际上是第一次定义`i`的地方。没有使用`var`的定义会让程序员认为这个`i`也许是在别的地方定义的。
所以在这个解释器中,我直接使用严格的语法检查方法来强行要求用户必须要使用`var`来定义新的变量或者迭代器。如果你忘了加这个关键字,那么你就会得到这个:
```javascript
code: undefined symbol "i"
--> test.nas:1:9
|
1 | foreach(i; [0, 1, 2, 3])
| ^ undefined symbol "i"
code: undefined symbol "i"
--> test.nas:2:11
|
2 | print(i)
| ^ undefined symbol "i"
```
</details>
## __堆栈追踪信息__
![stackoverflow](../doc/gif/stackoverflow.gif)
当解释器崩溃时,它会反馈错误产生过程的堆栈追踪信息:
<details><summary>内置函数 die</summary>
`die`函数用于直接抛出错误并终止执行。
```javascript
func() {
println("hello");
die("error occurred this line");
return;
}();
```
```javascript
hello
[vm] error: error occurred this line
[vm] error: error occurred in native function
call trace (main)
call func@0x557513935710() {entry: 0x850}
trace back (main)
0x000547 4c 00 00 16 callb 0x16 <__die@0x557512441780>(std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x5575138e8c40, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x5575138e8c50
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x5575138d9190> error occurred t...
0x000007 | nil |
0x000006 | func | <0x5575139356f0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
```
</details>
<details><summary>栈溢出</summary>
这是一个会导致栈溢出的例子:
```javascript
func(f) {
return f(f);
}(
func(f) {
f(f);
}
)();
```
```javascript
[vm] error: stack overflow
call trace (main)
call func@0x564106058620(f) {entry: 0x859}
--> 583 same call(s)
call func@0x5641060586c0(f) {entry: 0x851}
trace back (main)
0x000859 45 00 00 01 calll 0x1(a.nas:5)
0x00085b 4a 00 00 01 callfv 0x1(a.nas:5)
0x00085b 582 same call(s)
0x000853 4a 00 00 01 callfv 0x1(a.nas:2)
0x00085f 4a 00 00 01 callfv 0x1(a.nas:3)
stack (0x56410600be00, limit 10, total 4096)
0x000fff | func | <0x564106058600> entry:0x859
0x000ffe | pc | 0x85b
0x000ffd | addr | 0x56410601bd20
0x000ffc | nil |
0x000ffb | nil |
0x000ffa | func | <0x564106058600> entry:0x859
0x000ff9 | nil |
0x000ff8 | func | <0x564106058600> entry:0x859
0x000ff7 | pc | 0x85b
0x000ff6 | addr | 0x56410601bcb0
```
</details>
<details><summary>运行时错误</summary>
如果在执行的时候出现错误,程序会直接终止执行:
```javascript
func() {
return 0;
}()[1];
```
```javascript
[vm] error: must call a vector/hash/string but get number
trace back (main)
0x000854 47 00 00 00 callv 0x0(a.nas:3)
stack (0x564993f462b0, limit 10, total 1)
0x000000 | num | 0
```
</details>
<details><summary>详细的崩溃信息</summary>
使用命令 __`-d`__ 或 __`--detail`__ 后trace back信息会包含更多的细节内容:
```javascript
hello
[vm] error: error occurred this line
[vm] error: error occurred in native function
call trace (main)
call func@0x55dcb5b8fbf0() {entry: 0x850}
trace back (main)
0x000547 4c 00 00 16 callb 0x16 <__die@0x55dcb3c41780>(std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x55dcb5b43120, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x55dcb5b43130
0x00000a | nil |
0x000009 | nil |
0x000008 | str | <0x55dcb5b33670> error occurred t...
0x000007 | nil |
0x000006 | func | <0x55dcb5b8fbd0> entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
registers (main)
[pc ] | pc | 0x547
[global] | addr | 0x55dcb5b53130
[local ] | addr | 0x55dcb5b43190
[memr ] | addr | 0x0
[canary] | addr | 0x55dcb5b53110
[top ] | addr | 0x55dcb5b431f0
[funcr ] | func | <0x55dcb5b65620> entry:0x547
[upval ] | nil |
global (0x55dcb5b53130)
0x000000 | nmspc| <0x55dcb5b33780> namespace [95 val]
0x000001 | vec | <0x55dcb5b64c20> [0 val]
...
0x00005e | func | <0x55dcb5b8fc70> entry:0x846
local (0x55dcb5b43190 <+7>)
0x000000 | nil |
0x000001 | str | <0x55dcb5b33670> error occurred t...
0x000002 | nil |
```
</details>
## __调试器__
![dbg](../doc/gif/dbg.gif)
`v8.0`版本中我们添加了调试器。
使用这个命令`./nasal -dbg xxx.nas`来启用调试器,接下来调试器会打开文件并输出以下内容:
<details><summary>展开</summary>
```javascript
source code:
--> var fib = func(x) {
if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
next bytecode:
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
--> 0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
vm stack (0x7fca7e9f1010, limit 16, total 0)
>>
```
</details>
如果需要查看命令的使用方法,可以输入`h`获取帮助信息。
当运行调试器的时候,你可以看到现在的操作数栈上到底有些什么数据。
这些信息可以帮助你调试,同时也可以帮助你理解这个虚拟机是如何工作的:
<details><summary>展开</summary>
```javascript
source code:
var fib = func(x) {
--> if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
print(fib(i),'\n');
next bytecode:
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
--> 0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
vm stack (0x7fca7e9f1010, limit 16, total 8)
0x000007 | pc | 0x3c7
0x000006 | addr | 0x0
0x000005 | nil |
0x000004 | nil |
0x000003 | num | 0
0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x5573f66ef5f0> func(elems...) {..}
>>
```
</details>
## 交互解释器
v11.0 版本新增了交互式解释器 (REPL),使用如下命令开启:
> nasal -r
接下来就可以随便玩了~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>>
```
试试引入 `std/json.nas` 模块 ~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>> use std.json;
{stringify:func(..) {..},parse:func(..) {..}}
>>>
```

122
doc/benchmark.md Normal file
View File

@@ -0,0 +1,122 @@
# __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)
## More Nasal Generated Pictures
|Mandelbrot Set|Mandelbrot Set|Julia Set|
|:----:|:----:|:----:|
|[mandelbrotset.nas](../test/mandelbrotset.nas)|[mandelbrotset.nas](../test/mandelbrotset.nas)|[juliaset.nas](../test/juliaset.nas)|
|![mandelbrotset](../doc/pic/mandelbrotset.png)|![mandelbrotset_reverse](../doc/pic/mandelbrotset_reverse.png)|![juliaset](../doc/pic/juliaset.png)|
|__Burning Ship__|__Burning Ship__|__Feigenbaum__|
|[burningship.nas](../test/burningship.nas)|[burningship.nas](../test/burningship.nas)|[feigenbaum.nas](../test/feigenbaum.nas)|
|![burningship](../doc/pic/burningship.png)|![burningship_reverse](../doc/pic/burningship_reverse.png)|![feigenbaum](../doc/pic/feigenbaum.png)|

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

93
doc/namespace.md Normal file
View File

@@ -0,0 +1,93 @@
# 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;
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;
var _a = 1;
# source code end
return {
a: a
# _a begins with underscore so do not export
};
}();
```
## 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="/mandelbrot.nas">mandelbrot.nas</a></li>
<li><a href="/mandelbrotset.nas">mandelbrotset.nas</a></li>
<li><a href="/mcpu.nas">mcpu.nas</a></li>
<li><a href="/md5_self.nas">md5_self.nas</a></li>
<li><a href="/md5compare.nas">md5compare.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/module_test.nas">module_test.nas</a></li>
<li><a href="/nasal_test.nas">nasal_test.nas</a></li>
<li><a href="/occupation.nas">occupation.nas</a></li>
<li><a href="/pi.nas">pi.nas</a></li>
<li><a href="/ppmgen.nas">ppmgen.nas</a></li>
<li><a href="/prime.nas">prime.nas</a></li>
<li><a href="/qrcode.nas">qrcode.nas</a></li>
<li><a href="/quick_sort.nas">quick_sort.nas</a></li>
<li><a href="/scalar.nas">scalar.nas</a></li>
</ul>
</td>
<td>
<ul>
<li><a href="/snake.nas">snake.nas</a></li>
<li><a href="/tetris.nas">tetris.nas</a></li>
<li><a href="/trait.nas">trait.nas</a></li>
<li><a href="/turingmachine.nas">turingmachine.nas</a></li>
<li><a href="/utf8chk.nas">utf8chk.nas</a></li>
<li><a href="/watchdog.nas">watchdog.nas</a></li>
<li><a href="/wavecollapse.nas">wavecollapse.nas</a></li>
<li><a href="/word_collector.nas">word_collector.nas</a></li>
<li><a href="/ycombinator.nas">ycombinator.nas</a></li>
</ul>
</td>
</tr>
</table>
</body>
<h2>&nbsp;Shutdown | 关闭服务器</h2>
<text>
<p>Click <a href="/shutdown">here</a> to shutdown http server.</p>
<p>Don't click <a href="/teapot">me</a> besauce i am just a teapot.</p>
</text>
</html>

View File

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

BIN
doc/pic/benchmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
doc/pic/burningship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

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/juliaset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
doc/pic/mandelbrot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
doc/pic/mandelbrotset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

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

766
doc/tutorial.md Normal file
View File

@@ -0,0 +1,766 @@
# __Tutorial__
![mandelbrotset](../doc/pic/mandelbrotset.png)
Nasal is __easy__ to learn.
After reading this tutorial about 15 minutes,
You could totally use nasal.
## __Contents__
* [__Basic Type__](#basic-type)
* [__Operators__](#operators)
* [__Definition__](#definition)
* [__Multi-Assignment__](#multi-assignment)
* [__Conditional Expression__](#conditional-expression)
* [__Loop__](#loop)
* [__Subvec__](#subvec)
* [__Special function call__](#special-function-call)
* [__Lambda__](#lambda)
* [__Closure__](#closure)
* [__Trait__](#trait)
* [__Multi-Files/Modules Import__](#multi-filesmodules-import)
* [__Native Functions and Module Import__](#native-functions-and-module-import)
* [__C++ Modules (for lib developers)__](#c-modules-for-lib-developers)
* [__Ghost Type (for lib developers)__](#ghost-type-for-lib-developers)
## Basic Type
__`none`__ is error type used to interrupt the execution.
This type is not created by user program.
__`nil`__ is a null type. Just like `null`.
```javascript
var spc = nil;
```
__`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store.
```javascript
# this language use '#' to write notes
var n = 2.71828; # dec
var n = 2.147e16; # dec
var n = 1e-10; # dec
var n = 0xAA55; # hex
var n = 0o170001; # oct
# caution: true and false also useful in nasal now
var n = true; # in fact n is now 1.0
var n = false; # in face n is now 0.0
```
__`str`__ has 3 formats. The third one is used to declare a character.
```javascript
var s = 'str';
var s = "another string";
var s = `c`;
# some special characters is allowed in this language:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vec`__ has unlimited length and can store all types of values.
```javascript
var vec = [];
var vec = [0, nil, {}, [], func(){return 0}];
append(vec, 0, 1, 2);
```
__`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key.
```javascript
var hash = {
member1: nil,
member2: "str",
"member3": "member\'s name can also be a string constant",
funct: func() {
return me.member2~me.member3;
}
};
```
__`func`__ is a function type (in fact it is `lambda`).
```javascript
var f = func(x, y, z) {
return nil;
}
# function could be declared without parameters and `(`, `)`
var f = func {
return 114514;
}
var f = func(x, y, z, deft = 1) {
return x+y+z+deft;
}
var f = func(args...) {
var sum = 0;
foreach(var i; args) {
sum += i;
}
return sum;
}
```
__`upval`__ is used to store upvalues, used in __`vm`__ to make sure closure runs correctly.
__`ghost`__ is used to store other complex `C/C++` data types.
This type is created by native-function of nasal. If want to define a new data type, see how to add native-functions by editing code.
## Operators
Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that joints strings.
```javascript
1+2-(1+3)*(2+4)/(16-9);
"str1"~"str2";
```
For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values.
`and` `or` have the same function as C/C++ `&&` `||`.
```javascript
1+1 and (1<0 or 1>0);
1<=0 and 1>=0;
1==0 or 1!=0;
```
Unary operators `-` `!` have the same function as C/C++.
```javascript
-1;
!0;
```
Bitwise operators `~` `|` `&` `^` have the same function as C/C++.
```javascript
# these operators will:
# 1. convert f64 to i32 (static_cast<int32_t>)
# 2. do the bitwise function
~0x80000000; # not 2147483647
0x8|0x1; # or
0x1&0x2; # and
0x8^0x1; # xor
```
Operators `=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=` are used in assignment expressions.
```javascript
a = b = c = d = 1;
a += 1;
a -= 1;
a *= 1;
a /= 1;
a ~= "string";
a ^= 0xff;
a &= 0xca;
a |= 0xba;
```
Operator `??` is used to check left hand side value is `nil` or not, if not,
return the right hand side value:
```javascript
print(nil??"should get this string");
```
The example above will print `should get this string`.
Operator `?.` is used to get hash member if left hand side value is not `nil`.
And if left hand side value is not `nil` and not `hash`,
will cause error and exit.
```javascript
var a = nil;
print(a?.try_get); # nil
var a = {try_get: "congrats!"};
print(a?.try_get); # "congrats!"
```
## Definition
As follows.
```javascript
var a = 1; # define single variable
var (a, b, c) = [0, 1, 2]; # define multiple variables from a vector
var (a, b, c) = (0, 1, 2); # define multiple variables from a tuple
```
Nasal has many special global symbols:
```javascript
globals; # hashmap including all global symbols and their values
arg; # in global scope, arg is the command line arguments
# in local scope, arg is the dynamic arguments of this function call
```
For example:
```javascript
var a = 1;
println(globals); # will print {a:1}
```
```javascript
# nasal a b c
println(arg); # will print ["a", "b", "c"]
func() {
println(arg);
}(1, 2, 3); # will print [1, 2, 3]
```
## Multi-assignment
The last one is often used to swap two variables.
```javascript
(a, b[0], c.d) = [0, 1, 2];
(a, b[1], c.e) = (0, 1, 2);
(a, b) = (b, a);
```
## Conditional expression
In nasal there's a new key word `elsif`.
It has the same functions as `else if`.
```javascript
if (1) {
;
} elsif (2) {
;
} else if (3) {
;
} else {
;
}
```
## Loop
While loop and for loop is simalar to C/C++.
```javascript
while(condition) {
continue;
}
for(var i = 0; i<10; i += 1) {
break;
}
```
Nasal has another two kinds of loops that iterates through a vector:
`forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`.
```javascript
forindex(var i; elem) {
print(elem[i]);
}
```
`foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`.
```javascript
foreach(var i; elem) {
print(i);
}
```
## Subvec
Nasal provides this special syntax to help user generate a new vector by getting values by one index or getting values by indexes in a range from an old vector.
If there's only one index in the bracket, then we will get the value directly.
Use index to search one element in the string will get the __ascii number__ of this character.
If you want to get the character, use built-in function `chr()`.
```javascript
a[0];
a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0];
```
## Special function call
This is not very efficient,
because hashmap use string as the key to compare.
But if it really useful, the efficientcy may not be so important...
```javascript
f(x:0, y:nil, z:[]);
```
## Lambda
Also functions have this kind of use:
```javascript
func(x, y) {
return x+y
}(0, 1);
func(x) {
return 1/(1+math.exp(-x));
}(0.5);
```
There's an interesting test file `y-combinator.nas`,
try it for fun:
```javascript
var fib = func(f) {
return f(f);
}(
func(f) {
return func(x) {
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
}
}
);
```
## Closure
Closure means you could get the variable that is not in the local scope of a function that you called.
Here is an example, result is `1`:
```javascript
var f = func() {
var a = 1;
return func() {return a;};
}
print(f()());
```
Using closure makes it easier to OOP.
```javascript
var student = func(n, a) {
var (name, age) = (n, a);
return {
print_info: func() {println(name, ' ', age);},
set_age: func(a) {age = a;},
get_age: func() {return age;},
set_name: func(n) {name = n;},
get_name: func() {return name;}
};
}
```
## Trait
Also there's another way to OOP, that is `trait`.
When a hash has a member named `parents` and the value type is vector,
then when you are trying to find a member that is not in this hash,
virtual machine will search the member in `parents`.
If there is a hash that has the member, you will get the member's value.
Using this mechanism, we could OOP like this, the result is `114514`:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
a.set(114514);
println(a.get());
```
First virtual machine cannot find member `set` in hash `a`, but in `a.parents` there's a hash `trait` has the member `set`, so we get the `set`.
variable `me` points to hash `a`, so we change the `a.val`.
And `get` has the same process.
And we must remind you that if you do this:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
var b = class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c = a.get;
var d = b.get;
println(c());
println(c());
println(d());
println(d());
```
You will get this result now:
```bash
114
514
514
514
514
514
```
Because `a.get` will set `me=a` in the `trait.get`. Then `b.get` do the `me=b`. So in fact c is `b.get` too after running `var d=b.get`.
If you want to use this trick to make the program running more efficiently, you must know this special mechanism.
## Multi-Files/Modules Import</summary>
See more details in [namespace.md](./namespace.md)
## Native functions and module import
This part shows how we add native functions in this interpreter.
If you are interested in this part, this may help you.
And...
__CAUTION:__ If you want to add your own functions __without__ changing the source code, see the __`module`__ after this part.
If you really want to change source code, check built-in functions in `lib.nas` and see the example below.
Definition:
```C++
// you could also use a macro to define one.
var builtin_print(context*, gc*);
```
Then complete this function using C++:
```C++
var builtin_print(context* ctx, gc* ngc) {
// find value with index begin from 1
// because local[0] is reserved for value 'me'
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
// generate return value,
// use ngc::alloc(type) to make a new value
// or use reserved reference nil/one/zero
return nil;
}
```
When running a builtin function, alloc will run more than one time, this may cause mark-sweep in `gc::alloc`.
The value got before will be collected, but stil in use in this builtin function, this will cause a fatal error.
So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
```C++
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");
}
// use gc.temp to store the gc-managed-value, to 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;
}
```
After that, register the built-in function's name(in nasal) and the function's pointer in this table:
```C++
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{nullptr, nullptr}
};
```
At last,warp the `__print` in a nasal file:
```javascript
var print = func(elems...) {
return __print(elems);
};
```
In fact the arguments that `__print` uses are not necessary.
So writting it like this is also right:
```javascript
var print = func(elems...) {
return __print;
};
```
If you don't warp built-in function in a normal nasal function,
this native function may cause __segmentation fault__ when searching arguments.
Use `import("filename.nas")` to get the nasal file including your built-in functions, then you could use it.
Also there's another way of importing nasal files, the two way of importing have the same function:
```javascript
use dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
## C++ Modules (for lib developers)
If there is only one way to add your own functions into nasal,
that is really inconvenient.
Luckily, we have developed some useful native-functions to help you add modules that created by you.
Functions used to load dynamic libraries are added to `std/dylib.nas`:
```javascript
var dlopen = func(libname) {
...
}
var dlclose = func(lib) {
...
}
var dlcall = func(ptr, args...) {
...
}
var limitcall = func(arg_size = 0) {
...
}
```
As you could see, these functions are used to load dynamic libraries into the nasal runtime and execute.
Let's see how they work.
First, write a cpp file that you want to generate the dynamic lib, take the `fib.cpp` as the example(example codes are in `./module`):
```C++
// add header file nasal.h to get api
#include "nasal.h"
double fibonaci(double x) {
if (x<=2) {
return x;
}
return fibonaci(x-1)+fibonaci(x-2);
}
// module functions' parameter list example
var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib", "lack arguments");
}
// the arguments are generated into a vm_vec: args
// get values from the vector that must be used here
var num = args[0];
// if you want your function safer, try this
// nas_err will print the error info on screen
// and return vm_null for runtime to interrupt
if(num.type!=vm_num) {
return nas_err("extern_fib", "\"num\" must be number");
}
// ok, you must know that vm_num now is not managed by gc
// if want to return a gc object, use ngc->alloc(type)
// usage of gc is the same as adding a native function
return var::num(fibonaci(num.tonum()));
}
// then put function name and address into this table
// make sure the end of the table is {nullptr,nullptr}
module_func_info func_tbl[] = {
{"fib", fib},
{nullptr, nullptr}
};
// must write this function, this will help nasal to
// get the function pointer by name
// the reason why using this way to get function pointer
// is because `var` has constructors, which is not compatiable in C
// so "extern "C" var fib" may get compilation warnings
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}
```
Next, compile this `fib.cpp` into dynamic lib.
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac(`.so` & `.dylib`): same as Linux.
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib);
```
`dylib.dlopen` is used to load dynamic library and get the function address.
`dylib.dlcall` is used to call the function, the first argument is the function address, make sure this argument is `vm_obj` and `type=obj_extern`.
`dylib.dlclose` is used to unload the library, at the moment that you call the function, all the function addresses that got from it are invalid.
`dylib.limitcall` is used to get `dlcall` function that has limited parameter size, this function will prove the performance of your code because it does not use `vm_vec` to store the arguments, instead it uses local scope to store them, so this could avoid frequently garbage collecting. And the code above could also be written like this:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i += 1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib);
```
If get this, Congratulations!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
## Ghost Type (for lib developers)
It's quite easy to create a new ghost by yourself now.
Look at the example below:
```c++
const auto ghost_for_test = "ghost_for_test";
// declare destructor for ghost type
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);
// create ghost type
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];
// check ghost type by the type name
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;
}
```
We use this function to create a new ghost type:
`void nas_ghost::set(const std::string&, nasal::nas_ghost::destructor, void*);`
`const std::string&` is the name of the ghost type.
`nasal::nas_ghost::destructor` is the pointer of the destructor of the ghost type.
`void*` is the pointer of the ghost type instance.
And we use this function to check if value is the correct ghost type:
`bool var::object_check(const std::string&);`
The parameter is the name of the ghost type.

745
doc/tutorial_zh.md Normal file
View File

@@ -0,0 +1,745 @@
# __教程__
![mandelbrotset](../doc/pic/mandelbrotset.png)
Nasal非常容易上手你可以在15分钟之内看完基本教程并直接开始编写程序。
## __目录__
* [__基本类型__](#基本类型)
* [__运算符__](#运算符)
* [__定义变量__](#定义变量)
* [__多变量赋值__](#多变量赋值)
* [__条件语句__](#条件语句)
* [__循环语句__](#循环语句)
* [__生成子列表(subvec)__](#生成子列表subvec)
* [__特殊函数调用语法__](#特殊函数调用语法)
* [__Lambda 表达式__](#lambda表达式)
* [__闭包__](#闭包)
* [__特性与继承__](#特性与继承)
* [__多文件/模块导入__](#多文件模块导入)
* [__原生内置函数以及模块导入__](#原生内置函数以及模块导入)
* [__C++ 模块(开发者教程)__](#c-模块开发者教程)
* [__自定义类型(开发者教程)__](#自定义类型开发者教程)
## 基本类型
__`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行,该类型只能由虚拟机在抛出错误时产生。
__`nil`__ 是空类型。类似于null。
```javascript
var spc = nil;
```
__`num`__ 有三种形式:十进制十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。
```javascript
# 该语言用 '#' 来作为注释的开头
var n = 2.71828; # dec 十进制
var n = 2.147e16; # dec 十进制
var n = 1e-10; # dec 十进制
var n = 0xAA55; # hex 十六进制
var n = 0o170001; # oct 八进制
# 注意: true false 关键字在现在的 nasal 里也是可用的
var n = true; # n 实际上是数字 1.0
var n = false; # n 实际上是数字 0.0
```
__`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。
```javascript
var s = 'str';
var s = "another string";
var s = `c`;
# 该语言也支持一些特别的转义字符:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度)
```javascript
var vec = [];
var vec = [0, nil, {}, [], func(){return 0}];
append(vec, 0, 1, 2);
```
__`hash`__ 使用哈希表 (类似于`python`中的`dict`)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。
```javascript
var hash = {
member1: nil,
member2: "str",
"member3": "member\'s name can also be a string constant",
funct: func() {
return me.member2~me.member3;
}
};
```
__`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式)
```javascript
var f = func(x, y, z) {
return nil;
}
# 函数声明可以没有参数列表以及 `(`, `)`
var f = func {
return 114514;
}
var f = func(x, y, z, deft = 1) {
return x+y+z+deft;
}
var f = func(args...) {
var sum = 0;
foreach(var i; args) {
sum += i;
}
return sum;
}
```
__`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。
__`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。
## 运算符
Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的运算符 `~`,用于拼接字符串。
```javascript
1+2-(1+3)*(2+4)/(16-9);
"str1"~"str2";
```
对于条件语句,可以使用`==` `!=` `<` `>` `<=` `>=`来比较数据。`and` `or` 与C/C++中 `&&` `||`运算符一致。
```javascript
1+1 and (1<0 or 1>0);
1<=0 and 1>=0;
1==0 or 1!=0;
```
单目运算符`-` `!`与C/C++中的运算符功能类似。
```javascript
-1;
!0;
```
位运算符`~` `|` `&` `^`与C/C++中的运算符功能类似。
```javascript
# 运行过程:
# 1. f64 强转为 i32 (static_cast<int32_t>)
# 2. 执行位运算符
~0x80000000; # 按位取反 2147483647
0x8|0x1; # 按位或
0x1&0x2; # 按位与
0x8^0x1; # 按位异或
```
赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。
```javascript
a = b = c = d = 1;
a += 1;
a -= 1;
a *= 1;
a /= 1;
a ~= "string";
a ^= 0xff;
a &= 0xca;
a |= 0xba;
```
`??` 运算符用于检查左侧值是否为 `nil`,如果不是则返回右侧的值:
```javascript
print(nil??"should get this string");
```
上面的例子会输出 `should get this string`
`?.` 运算符用于先检查左侧不为 `nil`,如果左侧不是 `nil` 则尝试获取 hash 的字段。
当然如果左侧此时也不是 `hash` 类型,则报错退出。
```javascript
var a = nil;
print(a?.try_get); # nil
var a = {try_get: "congrats!"};
print(a?.try_get); # "congrats!"
```
## 定义变量
如下所示。
```javascript
var a = 1; # 定义单个变量
var (a, b, c) = [0, 1, 2]; # 从数组中初始化多个变量
var (a, b, c) = (0, 1, 2); # 从元组中初始化多个变量
```
Nasal 有很多特别的全局变量:
```javascript
globals; # 包含所有全局声明变量名和对应数据的哈希表
arg; # 在全局作用域arg 是包含命令行参数的数组
# 在局部作用域arg 是函数调用时的动态参数数组
```
具体实例:
```javascript
var a = 1;
println(globals); # 输出 {a:1}
```
```javascript
# nasal a b c
println(arg); # 输出 ["a", "b", "c"]
func() {
println(arg);
}(1, 2, 3); # 输出 [1, 2, 3]
```
## 多变量赋值
最后这个语句通常用于交换两个变量的数据类似于Python中的操作。
```javascript
(a, b[0], c.d) = [0, 1, 2];
(a, b[1], c.e) = (0, 1, 2);
(a, b) = (b, a);
```
## 条件语句
nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。
```javascript
if (1) {
;
} elsif (2) {
;
} else if (3) {
;
} else {
;
}
```
## 循环语句
while循环和for循环大体上与C/C++是一致的。
```javascript
while(condition) {
continue;
}
for(var i = 0; i<10; i += 1) {
break;
}
```
同时nasal还有另外两种直接遍历列表的循环方式:
`forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。
```javascript
forindex(var i; elem) {
print(elem[i]);
}
```
`foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`.
```javascript
foreach(var i; elem) {
print(i);
}
```
## 生成子列表(subvec)
nasal提供了下面第一句的类似语法来从列表中随机或者按照一个区间获取数据并且拼接生成一个新的列表。当然如果中括号内只有一个下标的话你会直接获得这个下标对应的数据而不是一个子列表。如果直接对string使用下标来获取内容的话会得到对应字符的 __ascii值__。如果你想进一步获得这个字符串,可以尝试使用内置函数`chr()`
```javascript
a[0];
a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0];
```
## 特殊函数调用语法
这种调用方式不是很高效,因为哈希表会使用字符串比对来找到数据存放的位置。
然而如果它用起来非常舒适,那效率也显得不是非常重要了……
```javascript
f(x:0, y:nil, z:[]);
```
## lambda表达式
函数有这样一种直接编写函数体并且立即调用的方式:
```javascript
func(x, y) {
return x+y;
}(0, 1);
func(x) {
return 1/(1+math.exp(-x));
}(0.5);
```
测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试:
```javascript
var fib = func(f) {
return f(f);
}(
func(f) {
return func(x) {
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
}
}
);
```
## 闭包
闭包是一种特别的作用域,你可以从这个作用域中获取其保存的所有变量,
而这些变量原本不是你当前运行的函数的局部作用域中的。
下面这个例子里,结果是`1`:
```javascript
var f = func() {
var a = 1;
return func() {return a;};
}
print(f()());
```
如果善用闭包,你可以使用它来进行面向对象编程。
```javascript
var student = func(n, a) {
var (name, age) = (n, a);
return {
print_info: func() {println(name, ' ', age);},
set_age: func(a) {age = a;},
get_age: func() {return age;},
set_name: func(n) {name = n;},
get_name: func() {return name;}
};
}
```
## 特性与继承
当然,也有另外一种办法来面向对象编程,那就是利用`trait`
当一个hash类型中有一个成员的key是`parents`,并且该成员是一个数组的话,
那么当你试图从这个hash中寻找一个它自己没有的成员名时虚拟机会进一步搜索`parents`数组。
如果该数组中有一个hash类型有一个成员的key与当前你搜索的成员名一致
那么你会得到这个成员对应的值。
使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
a.set(114514);
println(a.get());
```
首先虚拟机会发现在`a`中找不到成员`set`,但是在`a.parents`中有个hash类型`trait`存在该成员,所以返回了这个成员的值。
成员`me`指向的是`a`自身,类似于一些语言中的`this`,所以我们通过这个函数,实际上修改了`a.val``get`函数的调用实际上也经过了相同的过程。
不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
var b = class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c = a.get;
var d = b.get;
println(c());
println(c());
println(d());
println(d());
```
那么你会发现现在虚拟机会输出这个结果:
```bash
114
514
514
514
514
514
```
因为执行`a.get`时在`trait.get`函数的属性中进行了`me=a`的操作。而`b.get`则执行了`me=b`的操作。所以在运行`var d=b.get`后实际上c也变成`b.get`了。
如果你想要用这种小技巧来让程序运行更高效的话,最好是要知道这里存在这样一个机制。
## 多文件/模块导入
详情可见 [namespace.md](./namespace.md)
## 原生内置函数以及模块导入
这个部分对于纯粹的使用者来说是不需要了解的,
它将告诉你我们是如何为解释器添加新的内置函数的。
如果你对此很感兴趣,那么这个部分可能会帮到你,并且……
__警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。
如果你确实是想修改源码来搞一个自己私人订制的解释器 ———— “我他妈就是想自己私人订制,你们他妈的管得着吗?”,
参考源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,下面是其中一个样例:
定义新的内置函数:
```C++
// 你可以使用这个宏来直接定义一个新的内置函数
var builtin_print(context*, gc*);
```
然后用C++完成这个函数的函数体:
```C++
var builtin_print(context* ctx, gc* ngc) {
// 局部变量的下标其实是从 1 开始的
// 因为 local[0] 是保留给 'me' 的空间
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
// 最后生成返回值,返回值必须是一个内置的类型,
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil;
}
```
当运行内置函数的时候内存分配器如果运行超过一次那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++
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");
}
// 使用gc.temp来存储gc管理的变量防止错误的回收
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;
}
```
这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针:
```C++
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{nullptr, nullptr}
};
```
最后将其包装到nasal文件中:
```javascript
var print = func(elems...) {
return __print(elems);
};
```
事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对:
```javascript
var print = func(elems...) {
return __print;
};
```
如果你不把内置函数包装到一个普通的nasal函数中那么直接调用这个内置函数会在参数传入阶段出现 __segmentation fault(段错误)__。
在nasal文件中使用`import("文件名.nas")`可以导入该文件中你包装的所有内置函数,接下来你就可以使用他们了。
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
```javascript
use dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
## C++ 模块(开发者教程)
如果只有上文中那种方式来添加你自定义的函数到nasal中这肯定是非常麻烦的。
因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。
用于加载动态库的函数在`std/dylib.nas`中:
```javascript
var dlopen = func(libname) {
...
}
var dlclose = func(lib) {
...
}
var dlcall = func(ptr, args...) {
...
}
var limitcall = func(arg_size = 0) {
...
}
```
这些函数是用来加载动态库的这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。
首先用C++写个项目,并且编译成动态库。我们就拿`fib.cpp`作为例子来说明(样例代码可以在`./module`中找到):
```C++
// 这个头文件得加上因为我们需要拿到nasal的api
#include "nasal.h"
double fibonaci(double x) {
if (x<=2) {
return x;
}
return fibonaci(x-1)+fibonaci(x-2);
}
// 模块函数的参数列表一律以这个为准
var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib", "lack arguments");
}
// 传参会给予一个var指针指向一个vm_vec的data()
var num = args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
// nas_err会输出错误信息并返回错误类型让虚拟机终止执行
if(num.type!=vm_num) {
return nas_err("extern_fib", "\"num\" must be number");
}
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用ngc->alloc(type)
return var::num(fibonaci(num.tonum()));
}
// 然后将函数名字和函数地址放到一个表里,一定要记住表尾是{nullptr,nullptr}
module_func_info func_tbl[] = {
{"fib", fib},
{nullptr, nullptr}
};
// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针
// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的
// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致
// 类似 "extern "C" var fib" 的写法会得到编译错误
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}
```
接着我们把`fib.cpp`编译成动态库。
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac(`.so` & `.dylib`): 和Linux下操作相同。
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
好了那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。
下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib);
```
`dylib.dlopen`用于加载动态库并从动态库中获得函数地址。
`dylib.dlcall`用于调用函数,第一个参数是动态库函数的地址,这是个特殊类型,一定要保证这个参数是`vm_obj`类型并且`type=obj_extern`。
`dylib.dlclose`用于卸载动态库,当然,在这个函数调用之后,所有从该库中获取的函数都作废。
`dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i += 1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib);
```
如果得到如下运行结果,恭喜你!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
## 自定义类型(开发者教程)
创建一个自定义类型很容易。下面是使用示例:
```c++
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;
}
```
我们使用下面这个函数来创建一个自定义类型:
`void nas_ghost::set(const std::string&, nasal::nas_ghost::destructor, void*);`
`const std::string&` 是自定义类型的类型名。
`nasal::nas_ghost::destructor` 是自定义类型的析构函数指针。
`void*` 是指向自定义类型实例的指针。
我们使用下面的这个函数检测是否是正确的自定义类型:
`bool var::object_check(const std::string&);`
参数是自定义类型的类型名。

157
lib.nas
View File

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

194
main.cpp
View File

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

328
makefile Normal file
View File

@@ -0,0 +1,328 @@
STD = c++17
ifndef OS
OS = $(shell uname)
endif
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15 -I src
else
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -I src
endif
NASAL_HEADER = \
src/ast_dumper.h\
src/ast_visitor.h\
src/nasal_ast.h\
src/natives/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/cli/cli.h\
src/natives/fg_props.h\
src/natives/bits_lib.h\
src/natives/io_lib.h\
src/natives/math_lib.h\
src/natives/dylib_lib.h\
src/natives/json_lib.h\
src/natives/unix_lib.h\
src/natives/coroutine.h\
src/natives/regex_lib.h\
src/repl/repl.h\
src/util/fs.h\
src/util/util.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_gc.o\
build/builtin.o\
build/fg_props.o\
build/io_lib.o\
build/math_lib.o\
build/unix_lib.o\
build/dylib_lib.o\
build/json_lib.o\
build/coroutine.o\
build/nasal_type.o\
build/nasal_vm.o\
build/nasal_dbg.o\
build/regex_lib.o\
build/repl.o\
build/cli.o\
build/fs.o\
build/util.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/cli.o: src/cli/cli.h src/cli/cli.cpp | build
$(CXX) $(CXXFLAGS) src/cli/cli.cpp -o build/cli.o
build/util.o: src/util/util.h src/util/util.cpp | build
$(CXX) $(CXXFLAGS) src/util/util.cpp -o build/util.o
build/fs.o: src/nasal.h src/util/util.h src/util/fs.h src/util/fs.cpp | build
$(CXX) $(CXXFLAGS) src/util/fs.cpp -o build/fs.o
build/repl.o: $(NASAL_HEADER) src/repl/repl.h src/repl/repl.cpp | build
$(CXX) $(CXXFLAGS) src/repl/repl.cpp -o build/repl.o
build/nasal_err.o: src/nasal.h src/repl/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/util/util.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/util/util.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/util/util.h\
src/util/fs.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/repl.h\
src/util/util.h\
src/util/fs.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/builtin.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/util/util.h\
src/natives/builtin.h\
src/natives/builtin.cpp | build
$(CXX) $(CXXFLAGS) src/natives/builtin.cpp -o build/builtin.o
build/coroutine.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/coroutine.h src/natives/coroutine.cpp | build
$(CXX) $(CXXFLAGS) src/natives/coroutine.cpp -o build/coroutine.o
build/bits_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/bits_lib.h src/natives/bits_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/bits_lib.cpp -o build/bits_lib.o
build/math_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/math_lib.h src/natives/math_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/math_lib.cpp -o build/math_lib.o
build/io_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/util/fs.h\
src/natives/io_lib.h src/natives/io_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/io_lib.cpp -o build/io_lib.o
build/dylib_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/dylib_lib.h src/natives/dylib_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/dylib_lib.cpp -o build/dylib_lib.o
build/json_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/util/util.h\
src/natives/json_lib.h src/natives/json_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/json_lib.cpp -o build/json_lib.o
build/unix_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/unix_lib.h src/natives/unix_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/unix_lib.cpp -o build/unix_lib.o
build/regex_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/regex_lib.h src/natives/regex_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/regex_lib.cpp -o build/regex_lib.o
build/fg_props.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/fg_props.h src/natives/fg_props.cpp | build
$(CXX) $(CXXFLAGS) src/natives/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/natives/builtin.h\
src/util/util.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/util/util.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/util/util.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 -t -d test/andy_gc_test.nas
@ ./nasal test/argparse_test.nas
@ ./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 -t -d test/regex_test.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 -d test/wavecity.nas
@ ./nasal test/word_collector.nas test/md5compare.nas
@ ./nasal -t -d test/ycombinator.nas

131
module/fib.cpp Normal file
View File

@@ -0,0 +1,131 @@
// 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";
struct ghost_obj {
u32 number = 0;
var test_string = nil;
};
// if the dynamic library is closed, the pointer of this function will be unsafe
// make sure gc deletes the object before closing the dynamic library
// or just do not close the dynamic library...
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<ghost_obj*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n";
}
void ghost_for_test_gc_marker(void* ptr, std::vector<var>* bfs_queue) {
std::cout << "ghost_for_test::mark (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
bfs_queue->push_back(static_cast<ghost_obj*>(ptr)->test_string);
std::cout << " mark 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << "->test_string;\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_type::vm_ghost);
res.ghost().set(
ghost_for_test,
ghost_for_test_destructor,
ghost_for_test_gc_marker,
new ghost_obj
);
return res;
}
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<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n";
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string = ngc->newstr("just for test");
std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n";
return nil;
}
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() << " number = "
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number
<< " test_string = "
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string
<< "\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}
};
}
NASAL_EXTERN module_func_info* get() {
return fib_module::func_tbl;
}
}

118
module/keyboard.cpp Normal file
View File

@@ -0,0 +1,118 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.h"
#include <iostream>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4996)
#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 wsl1 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}
};
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}
}

58
module/libfib.nas Normal file
View File

@@ -0,0 +1,58 @@
use std.dylib;
use std.os;
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("\n");
print_ghost(ghost); # random
print("\n");
set_ghost(nil, 114); # err
print("\n");
set_ghost(ghost, 114); # success
print("\n");
for(var i = 0; i<256; i+=1) {
var temp = []; # try to trigger gc
}
print("\n");
print_ghost(ghost); # 114
print("\n");
}

19
module/libkey.nas Normal file
View File

@@ -0,0 +1,19 @@
use std.dylib;
use std.os;
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);}
];
}();

67
module/libmat.nas Normal file
View File

@@ -0,0 +1,67 @@
use std.dylib;
use std.os;
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);}
};

120
module/libnasock.nas Normal file
View File

@@ -0,0 +1,120 @@
use std.dylib;
use std.os;
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,
SOCKET_ERROR: -1,
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,
MSG_DONTWAIT: 0x40,
socket: func(af, type, proto = 0) {
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/util/util.h ../src/nasal_type.h ../src/nasal_gc.h
used_object = ../build/util.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_type::vm_vec);
res.vec().elems.push_back(args[0]);
res.vec().elems.push_back(args[1]);
return res;
}
var nas_vec3(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_type::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].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
return res;
}
var nas_vec2_sub(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
return res;
}
var nas_vec2_mult(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
return res;
}
var nas_vec2_div(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
return res;
}
var nas_vec2_neg(var* args, usize size, gc* ngc) {
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
return res;
}
var nas_vec2_norm(var* args, usize size, gc* ngc) {
if (!args[0].is_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_type::vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
return res;
}
var nas_vec2_len(var* args, usize size, gc* ngc) {
if (!args[0].is_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].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
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].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num()));
return res;
}
var nas_vec3_sub(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num()));
return res;
}
var nas_vec3_mult(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num()));
return res;
}
var nas_vec3_div(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num()));
return res;
}
var nas_vec3_neg(var* args, usize size, gc* ngc) {
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
res.vec().elems.push_back(var::num(-v0[2].num()));
return res;
}
var nas_vec3_norm(var* args, usize size, gc* ngc) {
if (!args[0].is_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_type::vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
res.vec().elems.push_back(var::num(z/t));
return res;
}
var nas_vec3_len(var* args, usize size, gc* ngc) {
if (!args[0].is_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].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()));
res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
return res;
}
var nas_rotate_y(var* args, usize size, gc* ngc) {
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[1].num()));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle)));
return res;
}
var nas_rotate_z(var* args, usize size, gc* ngc) {
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()));
return res;
}
var nas_vec3_dot(var* args, usize size, gc* ngc) {
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
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}
};
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}
}

269
module/nasocket.cpp Normal file
View File

@@ -0,0 +1,269 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.h"
#ifndef _MSC_VER
#include <unistd.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4996)
#endif
#ifdef _WIN32
// load socket library on windows platform
#include <winsock.h>
#pragma comment(lib,"ws2_32")
class WSAmanager {
private:
WSAData data;
public:
WSAmanager() {
WSAStartup(0x1010, &data);
}
~WSAmanager() {
WSACleanup();
}
};
// use static object to do WSAStartup and 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].is_num() || !args[1].is_num() || !args[2].is_num())
return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number");
int sd = socket(args[0].num(), args[1].num(), args[2].num());
return var::num(static_cast<double>(sd));
}
var nas_closesocket(var* args, usize size, gc* ngc) {
if (!args[0].is_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].is_num())
return nas_err("shutdown", "\"sd\" must be a number");
if (!args[1].is_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].is_num())
return nas_err("bind", "\"sd\" muse be a number");
if (!args[1].is_str())
return nas_err("bind", "\"ip\" should be a string including an ip with correct format");
if (!args[2].is_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].is_num())
return nas_err("listen", "\"sd\" must be a number");
if (!args[1].is_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].is_num())
return nas_err("connect", "\"sd\" must be a number");
if (!args[1].is_str())
return nas_err("connect", "\"hostname\" must be a string");
if (!args[2].is_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].is_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_type::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].is_num())
return nas_err("send", "\"sd\" must be a number");
if (!args[1].is_str())
return nas_err("send", "\"buff\" must be a string");
if (!args[2].is_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].is_num())
return nas_err("sendto", "\"sd\" must be a number");
if (!args[1].is_str())
return nas_err("sendto", "\"hostname\" must be a string");
if (!args[2].is_num())
return nas_err("sendto", "\"port\" must be a number");
if (!args[3].is_str())
return nas_err("sendto", "\"buff\" must be a string");
if (!args[4].is_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].is_num())
return nas_err("recv", "\"sd\" must be a number");
if (!args[1].is_num())
return nas_err("recv", "\"len\" must be a number");
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
return nas_err("recv", "\"len\" out of range");
if (!args[2].is_num())
return nas_err("recv", "\"flags\" muse be a number");
var res = ngc->temp = ngc->alloc(vm_type::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].is_num())
return nas_err("recvfrom", "\"sd\" must be a number");
if (!args[1].is_num())
return nas_err("recvfrom", "\"len\" must be a number");
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
return nas_err("recvfrom", "\"len\" out of range");
if (!args[2].is_num())
return nas_err("recvfrom", "\"flags\" muse be a number");
sockaddr_in addr;
int socklen = sizeof(sockaddr_in);
var res = ngc->temp = ngc->alloc(vm_type::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));
hash["port"] = var::num(ntohs(addr.sin_port));
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}
};
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}
}

139
nasal.h
View File

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

View File

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

View File

@@ -1,882 +0,0 @@
#ifndef __NASAL_BUILTIN_H__
#define __NASAL_BUILTIN_H__
// builtin functions must be called inside a outer function like this:
// var print=func(elements...)
// {
// nasal_call_builtin_std_cout(elements);
// return nil;
// }
// builtin function nasal_call_builtin_std_cout is wrapped up by print
std::unordered_map<std::string,int> builtin_use_string_table;
// used to find values that builtin function uses
#define in_builtin_find(value_name_string) \
(\
local_scope_addr\
->get_closure()\
.get_value_address\
(builtin_use_string_table[value_name_string])\
)
// declaration of builtin functions
// to add new builtin function,declare it here and write the definition below
nasal_val* builtin_print(nasal_val*,nasal_gc&);
nasal_val* builtin_append(nasal_val*,nasal_gc&);
nasal_val* builtin_setsize(nasal_val*,nasal_gc&);
nasal_val* builtin_system(nasal_val*,nasal_gc&);
nasal_val* builtin_input(nasal_val*,nasal_gc&);
nasal_val* builtin_sleep(nasal_val*,nasal_gc&);
nasal_val* builtin_finput(nasal_val*,nasal_gc&);
nasal_val* builtin_foutput(nasal_val*,nasal_gc&);
nasal_val* builtin_split(nasal_val*,nasal_gc&);
nasal_val* builtin_rand(nasal_val*,nasal_gc&);
nasal_val* builtin_id(nasal_val*,nasal_gc&);
nasal_val* builtin_int(nasal_val*,nasal_gc&);
nasal_val* builtin_num(nasal_val*,nasal_gc&);
nasal_val* builtin_pop(nasal_val*,nasal_gc&);
nasal_val* builtin_str(nasal_val*,nasal_gc&);
nasal_val* builtin_size(nasal_val*,nasal_gc&);
nasal_val* builtin_xor(nasal_val*,nasal_gc&);
nasal_val* builtin_and(nasal_val*,nasal_gc&);
nasal_val* builtin_or(nasal_val*,nasal_gc&);
nasal_val* builtin_nand(nasal_val*,nasal_gc&);
nasal_val* builtin_not(nasal_val*,nasal_gc&);
nasal_val* builtin_sin(nasal_val*,nasal_gc&);
nasal_val* builtin_cos(nasal_val*,nasal_gc&);
nasal_val* builtin_tan(nasal_val*,nasal_gc&);
nasal_val* builtin_exp(nasal_val*,nasal_gc&);
nasal_val* builtin_ln(nasal_val*,nasal_gc&);
nasal_val* builtin_sqrt(nasal_val*,nasal_gc&);
nasal_val* builtin_atan2(nasal_val*,nasal_gc&);
nasal_val* builtin_time(nasal_val*,nasal_gc&);
nasal_val* builtin_contains(nasal_val*,nasal_gc&);
nasal_val* builtin_delete(nasal_val*,nasal_gc&);
nasal_val* builtin_getkeys(nasal_val*,nasal_gc&);
nasal_val* builtin_import(nasal_val*,nasal_gc&);
bool builtin_die_state;// used in builtin_die
nasal_val* builtin_die(nasal_val*,nasal_gc&);
nasal_val* builtin_type(nasal_val*,nasal_gc&);
nasal_val* builtin_substr(nasal_val*,nasal_gc&);
nasal_val* builtin_streq(nasal_val*,nasal_gc&);
nasal_val* builtin_left(nasal_val*,nasal_gc&);
nasal_val* builtin_right(nasal_val*,nasal_gc&);
nasal_val* builtin_cmp(nasal_val*,nasal_gc&);
nasal_val* builtin_chr(nasal_val*,nasal_gc&);
void builtin_error_occurred(std::string func_name,std::string info)
{
builtin_die_state=true;
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 {"",NULL}
struct FUNC_TABLE
{
std::string func_name;
nasal_val* (*func_pointer)(nasal_val* x,nasal_gc& gc);
} builtin_func_table[]=
{
{"nasal_call_builtin_std_cout", builtin_print},
{"nasal_call_builtin_push_back", builtin_append},
{"nasal_call_builtin_set_size", builtin_setsize},
{"nasal_call_builtin_system", builtin_system},
{"nasal_call_builtin_input", builtin_input},
{"nasal_call_builtin_sleep", builtin_sleep},
{"nasal_call_builtin_finput", builtin_finput},
{"nasal_call_builtin_foutput", builtin_foutput},
{"nasal_call_builtin_split", builtin_split},
{"nasal_call_builtin_rand", builtin_rand},
{"nasal_call_builtin_get_id", builtin_id},
{"nasal_call_builtin_trans_int", builtin_int},
{"nasal_call_builtin_trans_num", builtin_num},
{"nasal_call_builtin_pop_back", builtin_pop},
{"nasal_call_builtin_trans_str", builtin_str},
{"nasal_call_builtin_size", builtin_size},
{"nasal_call_builtin_xor", builtin_xor},
{"nasal_call_builtin_and", builtin_and},
{"nasal_call_builtin_or", builtin_or},
{"nasal_call_builtin_nand", builtin_nand},
{"nasal_call_builtin_not", builtin_not},
{"nasal_call_builtin_sin", builtin_sin},
{"nasal_call_builtin_cos", builtin_cos},
{"nasal_call_builtin_tan", builtin_tan},
{"nasal_call_builtin_exp", builtin_exp},
{"nasal_call_builtin_cpp_math_ln", builtin_ln},
{"nasal_call_builtin_cpp_math_sqrt", builtin_sqrt},
{"nasal_call_builtin_cpp_atan2", builtin_atan2},
{"nasal_call_builtin_time", builtin_time},
{"nasal_call_builtin_contains", builtin_contains},
{"nasal_call_builtin_delete", builtin_delete},
{"nasal_call_builtin_get_keys", builtin_getkeys},
{"nasal_call_import", builtin_import},
{"nasal_call_builtin_die", builtin_die},
{"nasal_call_builtin_type", builtin_type},
{"nasal_call_builtin_substr", builtin_substr},
{"nasal_call_builtin_streq", builtin_streq},
{"nasal_call_builtin_left", builtin_left},
{"nasal_call_builtin_right", builtin_right},
{"nasal_call_builtin_cmp", builtin_cmp},
{"nasal_call_builtin_chr", builtin_chr},
{"", NULL}
};
nasal_val* builtin_print(nasal_val* local_scope_addr,nasal_gc& gc)
{
// get arguments
nasal_val* vector_value_addr=in_builtin_find("elements");
// main process
nasal_vec& ref_vec=vector_value_addr->get_vector();
int size=ref_vec.size();
for(int i=0;i<size;++i)
{
nasal_val* tmp=ref_vec[i];
switch(tmp->get_type())
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->get_number(); break;
case vm_str: std::cout<<tmp->get_string(); break;
case vm_vec: tmp->get_vector().print(); break;
case vm_hash: tmp->get_hash().print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
}
// generate return value
return gc.gc_alloc(vm_nil);
}
nasal_val* builtin_append(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* vector_value_addr=in_builtin_find("vector");
nasal_val* elem_value_addr=in_builtin_find("elements");
if(vector_value_addr->get_type()!=vm_vec)
{
builtin_error_occurred("append","\"vector\" must be vector");
return NULL;
}
nasal_vec& ref_vector=vector_value_addr->get_vector();
nasal_vec& ref_elements=elem_value_addr->get_vector();
int size=ref_elements.size();
for(int i=0;i<size;++i)
{
nasal_val* value_address=ref_elements.get_value_address(i);
gc.add_reference(value_address);
ref_vector.add_elem(value_address);
}
return gc.gc_alloc(vm_nil);
}
nasal_val* builtin_setsize(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* vector_value_addr=in_builtin_find("vector");
nasal_val* size_value_addr=in_builtin_find("size");
if(vector_value_addr->get_type()!=vm_vec)
{
builtin_error_occurred("setsize","\"vector\" must be vector");
return NULL;
}
int type=size_value_addr->get_type();
if(type!=vm_num)
{
builtin_error_occurred("setsize","\"size\" is not a number");
return NULL;
}
int number=size_value_addr->get_number();
if(number<0)
{
builtin_error_occurred("setsize","\"size\" must be greater than -1");
return NULL;
}
nasal_vec& ref_vector=vector_value_addr->get_vector();
int vec_size=ref_vector.size();
if(number<vec_size)
for(int i=number;i<vec_size;++i)
{
nasal_val* addr=ref_vector.del_elem();
if(addr) gc.del_reference(addr);
}
else if(number>vec_size)
for(int i=vec_size;i<number;++i)
{
nasal_val* new_val_addr=gc.gc_alloc(vm_nil);
ref_vector.add_elem(new_val_addr);
}
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_system(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* str_value_addr=in_builtin_find("str");
if(str_value_addr->get_type()!=vm_str)
{
builtin_error_occurred("system","\"str\" must be string");
return NULL;
}
std::string str=str_value_addr->get_string();
system(str.data());
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_input(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* ret_addr=gc.gc_alloc(vm_str);
std::string str;
std::cin>>str;
ret_addr->set_string(str);
return ret_addr;
}
nasal_val* builtin_sleep(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("duration");
int type=value_addr->get_type();
if(type!=vm_num)
{
builtin_error_occurred("sleep","\"duration\" must be number");
return NULL;
}
// sleep in unistd.h will make this progress sleep sleep_time seconds.
sleep((unsigned long)value_addr->get_number());
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_finput(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("filename");
if(value_addr->get_type()!=vm_str)
{
builtin_error_occurred("io.fin","\"filename\" must be string");
return NULL;
}
std::string filename=value_addr->get_string();
std::ifstream fin(filename);
std::string file_content="";
if(!fin.fail())
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
file_content.push_back(c);
}
else
builtin_error_occurred("io.fin","cannot open \""+filename+"\".");
fin.close();
nasal_val* ret_addr=gc.gc_alloc(vm_str);
ret_addr->set_string(file_content);
return ret_addr;
}
nasal_val* builtin_foutput(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("filename");
nasal_val* str_value_addr=in_builtin_find("str");
if(value_addr->get_type()!=vm_str)
{
builtin_error_occurred("io.fout","\"filename\" must be string");
return NULL;
}
if(str_value_addr->get_type()!=vm_str)
{
builtin_error_occurred("io.fout","\"str\" must be string");
return NULL;
}
std::string filename=value_addr->get_string();
std::string file_content=str_value_addr->get_string();
std::ofstream fout(filename);
fout<<file_content;
fout.close();
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_split(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* delimeter_value_addr=in_builtin_find("delimeter");
nasal_val* string_value_addr=in_builtin_find("string");
if(delimeter_value_addr->get_type()!=vm_str)
{
builtin_error_occurred("split","\"delimeter\" must be string");
return NULL;
}
if(string_value_addr->get_type()!=vm_str)
{
builtin_error_occurred("split","\"string\" must be string");
return NULL;
}
std::string delimeter=delimeter_value_addr->get_string();
std::string source=string_value_addr->get_string();
int delimeter_len=delimeter.length();
int source_len=source.length();
nasal_val* ret_addr=gc.gc_alloc(vm_vec);
nasal_vec& ref_vec=ret_addr->get_vector();
std::string tmp="";
if(!delimeter_len)
{
for(int i=0;i<source_len;++i)
{
tmp=source[i];
nasal_val* str_addr=gc.gc_alloc(vm_str);
str_addr->set_string(tmp);
ref_vec.add_elem(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.gc_alloc(vm_str);
str_addr->set_string(tmp);
ref_vec.add_elem(str_addr);
tmp="";
}
i+=delimeter_len-1;
}
else
tmp+=source[i];
}
if(tmp.length())
{
nasal_val* str_addr=gc.gc_alloc(vm_str);
str_addr->set_string(tmp);
ref_vec.add_elem(str_addr);
tmp="";
}
return ret_addr;
}
nasal_val* builtin_rand(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("seed");
if(value_addr->get_type()!=vm_num && value_addr->get_type()!=vm_nil)
{
builtin_error_occurred("rand","\"seed\" must be nil or number");
return NULL;
}
if(value_addr->get_type()==vm_num)
{
unsigned int number=(unsigned int)value_addr->get_number();
srand(number);
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
double num=0;
for(int i=0;i<5;++i)
num=(num+rand())*(1.0/(RAND_MAX+1.0));
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(num);
return ret_addr;
}
nasal_val* builtin_id(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("thing");
nasal_val* ret_addr=gc.gc_alloc(vm_str);
char buf[32];
sprintf(buf,"0x%p",value_addr);
ret_addr->set_string(buf);
return ret_addr;
}
nasal_val* builtin_int(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("value");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("int","\"value\" must be number");
return NULL;
}
int number=(int)value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)number);
return ret_addr;
}
nasal_val* builtin_num(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("value");
if(value_addr->get_type()!=vm_str)
{
builtin_error_occurred("num","\"value\" must be string");
return NULL;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(value_addr->to_number());
return ret_addr;
}
nasal_val* builtin_pop(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("vector");
if(value_addr->get_type()!=vm_vec)
{
builtin_error_occurred("pop","\"vector\" must be vector");
return NULL;
}
nasal_val* ret_addr=value_addr->get_vector().del_elem();
return ret_addr;
}
nasal_val* builtin_str(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("number");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("str","\"number\" must be number");
return NULL;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_string(value_addr->to_string());
return ret_addr;
}
nasal_val* builtin_size(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("object");
int type=value_addr->get_type();
int number=-1;
switch(type)
{
case vm_nil:
case vm_num:
case vm_func:
case vm_scop: return gc.gc_alloc(vm_nil); break;
case vm_str: number=value_addr->get_string().length(); break;
case vm_vec: number=value_addr->get_vector().size(); break;
case vm_hash: number=value_addr->get_hash().size(); break;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)number);
return ret_addr;
}
nasal_val* builtin_xor(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
if(a_addr->get_type()!=vm_num)
{
builtin_error_occurred("xor","\"a\" must be number");
return NULL;
}
if(b_addr->get_type()!=vm_num)
{
builtin_error_occurred("xor","\"b\" must be number");
return NULL;
}
int number_a=(int)a_addr->get_number();
int number_b=(int)b_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)(number_a^number_b));
return ret_addr;
}
nasal_val* builtin_and(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
if(a_addr->get_type()!=vm_num)
{
builtin_error_occurred("and","\"a\" must be number");
return NULL;
}
if(b_addr->get_type()!=vm_num)
{
builtin_error_occurred("and","\"b\" must be number");
return NULL;
}
int number_a=(int)a_addr->get_number();
int number_b=(int)b_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)(number_a&number_b));
return ret_addr;
}
nasal_val* builtin_or(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
if(a_addr->get_type()!=vm_num)
{
builtin_error_occurred("or","\"a\" must be number");
return NULL;
}
if(b_addr->get_type()!=vm_num)
{
builtin_error_occurred("or","\"b\" must be number");
return NULL;
}
int number_a=(int)a_addr->get_number();
int number_b=(int)b_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)(number_a|number_b));
return ret_addr;
}
nasal_val* builtin_nand(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
if(a_addr->get_type()!=vm_num)
{
builtin_error_occurred("nand","\"a\" must be number");
return NULL;
}
if(b_addr->get_type()!=vm_num)
{
builtin_error_occurred("nand","\"b\" must be number");
return NULL;
}
int number_a=(int)a_addr->get_number();
int number_b=(int)b_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)(~(number_a&number_b)));
return ret_addr;
}
nasal_val* builtin_not(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
if(a_addr->get_type()!=vm_num)
{
builtin_error_occurred("not","\"a\" must be number");
return NULL;
}
int number=(int)a_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)(~number));
return ret_addr;
}
nasal_val* builtin_sin(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("sin","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(sin(number));
return ret_addr;
}
nasal_val* builtin_cos(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("cos","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(cos(number));
return ret_addr;
}
nasal_val* builtin_tan(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("tan","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(tan(number));
return ret_addr;
}
nasal_val* builtin_exp(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("exp","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(exp(number));
return ret_addr;
}
nasal_val* builtin_ln(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("ln","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(log(number)/log(2.7182818284590452354));
return ret_addr;
}
nasal_val* builtin_sqrt(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("x");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("sqrt","\"x\" must be number");
return NULL;
}
double number=value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(sqrt(number));
return ret_addr;
}
nasal_val* builtin_atan2(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* x_value_addr=in_builtin_find("x");
nasal_val* y_value_addr=in_builtin_find("y");
if(x_value_addr->get_type()!=vm_num)
{
builtin_error_occurred("atan2","\"x\" must be number");
return NULL;
}
if(y_value_addr->get_type()!=vm_num)
{
builtin_error_occurred("atan2","\"y\" must be number");
return NULL;
}
double x=x_value_addr->get_number();
double y=y_value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(atan2(y,x));
return ret_addr;
}
nasal_val* builtin_time(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("begin_time");
if(value_addr->get_type()!=vm_num)
{
builtin_error_occurred("time","\"begin_time\" must be number");
return NULL;
}
time_t begin_time=(time_t)value_addr->get_number();
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)time(&begin_time));
return ret_addr;
}
nasal_val* builtin_contains(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* hash_addr=in_builtin_find("hash");
nasal_val* key_addr=in_builtin_find("key");
if(hash_addr->get_type()!=vm_hash)
{
builtin_error_occurred("contains","\"hash\" must be hash");
return NULL;
}
if(key_addr->get_type()!=vm_str)
{
builtin_error_occurred("contains","\"key\" must be string");
return NULL;
}
bool contains=hash_addr->get_hash().check_contain(key_addr->get_string());
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number((double)contains);
return ret_addr;
}
nasal_val* builtin_delete(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* hash_addr=in_builtin_find("hash");
nasal_val* key_addr=in_builtin_find("key");
if(hash_addr->get_type()!=vm_hash)
{
builtin_error_occurred("delete","\"hash\" must be hash");
return NULL;
}
if(key_addr->get_type()!=vm_str)
{
builtin_error_occurred("delete","\"key\" must be string");
return NULL;
}
hash_addr->get_hash().del_elem(key_addr->get_string());
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_getkeys(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* hash_addr=in_builtin_find("hash");
if(hash_addr->get_type()!=vm_hash)
{
builtin_error_occurred("keys","\"hash\" must be hash");
return NULL;
}
nasal_val* ret_addr=hash_addr->get_hash().get_keys();
return ret_addr;
}
nasal_val* builtin_import(nasal_val* local_scope_addr,nasal_gc& gc)
{
// this function is used in preprocessing.
// this function will return nothing when running.
builtin_error_occurred("import","cannot use import when running");
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_die(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* str_addr=in_builtin_find("str");
if(str_addr->get_type()!=vm_str)
{
builtin_error_occurred("die","\"str\" must be string");
return NULL;
}
builtin_die_state=true;
std::cout<<">> [vm] error: "<<str_addr->get_string()<<'\n';
nasal_val* ret_addr=gc.gc_alloc(vm_nil);
return ret_addr;
}
nasal_val* builtin_type(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* value_addr=in_builtin_find("object");
int type=value_addr->get_type();
nasal_val* ret_addr=gc.gc_alloc(vm_str);
switch(type)
{
case vm_nil: ret_addr->set_string("nil");break;
case vm_num: ret_addr->set_string("number");break;
case vm_str: ret_addr->set_string("string");break;
case vm_vec: ret_addr->set_string("vector");break;
case vm_hash: ret_addr->set_string("hash");break;
case vm_func: ret_addr->set_string("function");break;
}
return ret_addr;
}
nasal_val* builtin_substr(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* str_addr=in_builtin_find("str");
nasal_val* begin_addr=in_builtin_find("begin");
nasal_val* length_addr=in_builtin_find("length");
if(str_addr->get_type()!=vm_str)
{
builtin_error_occurred("substr","\"str\" must be string");
return NULL;
}
if(begin_addr->get_type()!=vm_num)
{
builtin_error_occurred("substr","\"begin\" must be number");
return NULL;
}
if(length_addr->get_type()!=vm_num)
{
builtin_error_occurred("substr","\"length\" must be number");
return NULL;
}
std::string str=str_addr->get_string();
int begin=(int)begin_addr->get_number();
int len=(int)length_addr->get_number();
if(begin+len>str.length())
{
builtin_error_occurred("susbtr","index out of range");
return NULL;
}
std::string tmp="";
for(int i=begin;i<begin+len;++i)
tmp+=str[i];
nasal_val* ret_addr=gc.gc_alloc(vm_str);
ret_addr->set_string(tmp);
return ret_addr;
}
nasal_val* builtin_streq(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
nasal_val* ret_addr=gc.gc_alloc(vm_num);
if(a_addr->get_type()!=vm_str || b_addr->get_type()!=vm_str)
{
ret_addr->set_number((double)0);
return ret_addr;
}
int ret=(a_addr->get_string()==b_addr->get_string());
ret_addr->set_number((double)ret);
return ret_addr;
}
nasal_val* builtin_left(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* string_addr=in_builtin_find("string");
nasal_val* length_addr=in_builtin_find("length");
if(string_addr->get_type()!=vm_str)
{
builtin_error_occurred("left","\"string\" must be string");
return NULL;
}
if(length_addr->get_type()!=vm_num)
{
builtin_error_occurred("left","\"length\" must be number");
return NULL;
}
std::string str=string_addr->get_string();
int len=(int)length_addr->get_number();
if(len<0)
len=0;
std::string tmp=str.substr(0, len);
nasal_val* ret_addr=gc.gc_alloc(vm_str);
ret_addr->set_string(tmp);
return ret_addr;
}
nasal_val* builtin_right(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* string_addr=in_builtin_find("string");
nasal_val* length_addr=in_builtin_find("length");
if(string_addr->get_type()!=vm_str)
{
builtin_error_occurred("right","\"string\" must be string");
return NULL;
}
if(length_addr->get_type()!=vm_num)
{
builtin_error_occurred("right","\"length\" must be number");
return NULL;
}
std::string str=string_addr->get_string();
int len=(int)length_addr->get_number();
int srclen=str.length();
if(len>srclen)
len=srclen;
if(len<0)
len=0;
std::string tmp=str.substr(srclen-len, srclen);
nasal_val* ret_addr=gc.gc_alloc(vm_str);
ret_addr->set_string(tmp);
return ret_addr;
}
nasal_val* builtin_cmp(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* a_addr=in_builtin_find("a");
nasal_val* b_addr=in_builtin_find("b");
if(a_addr->get_type()!=vm_str)
{
builtin_error_occurred("cmp","\"a\" must be string");
return NULL;
}
if(b_addr->get_type()!=vm_str)
{
builtin_error_occurred("cmp","\"b\" must be string");
return NULL;
}
nasal_val* ret_addr=gc.gc_alloc(vm_num);
ret_addr->set_number(strcmp(a_addr->get_string().data(),b_addr->get_string().data()));
return ret_addr;
}
nasal_val* builtin_chr(nasal_val* local_scope_addr,nasal_gc& gc)
{
nasal_val* code_addr=in_builtin_find("code");
if(code_addr->get_type()!=vm_num)
{
builtin_error_occurred("chr","\"code\" must be number");
return NULL;
}
char chr[2]={(char)code_addr->get_number(),'\0'};
nasal_val* ret_addr=gc.gc_alloc(vm_str);
ret_addr->set_string(chr);
return ret_addr;
}
#endif

View File

@@ -1,988 +0,0 @@
#ifndef __NASAL_CODEGEN_H__
#define __NASAL_CODEGEN_H__
enum op_code
{
op_nop,
op_load,// load value in value_stack to identifier
op_pushnum,
op_pushone,
op_pushzero,
op_pushnil,
op_pushstr,
op_newvec,op_newhash,op_newfunc,
op_vecapp,op_hashapp,
op_para,op_defpara,op_dynpara,
op_unot,op_usub,
op_add,op_sub,op_mul,op_div,op_lnk,
op_addeq,op_subeq,op_muleq,op_diveq,op_lnkeq,op_meq,
op_eq,op_neq,op_less,op_leq,op_grt,op_geq,
op_pop,
op_jmp, // jump with no condition
op_jmptrue, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jmpfalse, // used in conditional/loop,jmp when condition is false and POP STACK
op_counter, // add counter for forindex/foreach
op_cntpop, // pop counter
op_forindex, // index counter on the top of forindex_stack plus 1
op_foreach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_call, // call identifier
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callf, // call function(parameters)
op_builtincall,// call builtin-function
op_slicebegin, // begin of slice like: vec[1,2,3:6,0,-1]
op_sliceend, // end of slice
op_slice, // slice like vec[1]
op_slice2, // slice like vec[nil:10]
op_mcall, // get memory of identifier
op_mcallv, // get memory of vec[index]
op_mcallh, // get memory of hash.label
op_return // return
};
struct
{
int type;
const char* name;
}code_table[]=
{
{op_nop, "nop "},
{op_load, "load "},
{op_pushnum, "pnum "},
{op_pushone, "pone "},
{op_pushzero, "pzero "},
{op_pushnil, "pnil "},
{op_pushstr, "pstr "},
{op_newvec, "newv "},
{op_newhash, "newh "},
{op_newfunc, "newf "},
{op_vecapp, "vapp "},
{op_hashapp, "happ "},
{op_para, "para "},
{op_defpara, "deflt "},
{op_dynpara, "dyn "},
{op_unot, "not "},
{op_usub, "usub "},
{op_add, "add "},
{op_sub, "sub "},
{op_mul, "mult "},
{op_div, "div "},
{op_lnk, "link "},
{op_addeq, "addeq "},
{op_subeq, "subeq "},
{op_muleq, "muleq "},
{op_diveq, "diveq "},
{op_lnkeq, "linkeq"},
{op_meq, "memeq "},
{op_eq, "eq "},
{op_neq, "neq "},
{op_less, "less "},
{op_leq, "leq "},
{op_grt, "grt "},
{op_geq, "geq "},
{op_pop, "pop "},
{op_jmp, "jmp "},
{op_jmptrue, "jt "},
{op_jmpfalse, "jf "},
{op_counter, "cnt "},
{op_cntpop, "cntpop"},
{op_forindex, "findx "},
{op_foreach, "feach "},
{op_call, "call "},
{op_callv, "callv "},
{op_callvi, "callvi"},
{op_callh, "callh "},
{op_callf, "callf "},
{op_builtincall, "callb "},
{op_slicebegin, "slcbeg"},
{op_sliceend, "slcend"},
{op_slice, "slc "},
{op_slice2, "slc2 "},
{op_mcall, "mcall "},
{op_mcallv, "mcallv"},
{op_mcallh, "mcallh"},
{op_return, "ret "},
{-1, NULL},
};
struct opcode
{
unsigned char op;
unsigned int index;
opcode(unsigned char _op=op_nop,unsigned int _index=0)
{
op=_op;
index=_index;
return;
}
opcode& operator=(const opcode& tmp)
{
op=tmp.op;
index=tmp.index;
return *this;
}
};
class nasal_codegen
{
private:
std::unordered_map<double,int> number_table;
std::unordered_map<std::string,int> string_table;
std::vector<double> number_result_table;
std::vector<std::string> string_result_table;
std::vector<opcode> exec_code;
std::list<std::vector<int> > continue_ptr;
std::list<std::vector<int> > break_ptr;
int error;
int in_forindex;
int in_foreach;
void regist_number(double);
void regist_string(std::string);
void gen(unsigned char,unsigned int);
void pop_gen();
void nil_gen();
void number_gen(nasal_ast&);
void string_gen(nasal_ast&);
void vector_gen(nasal_ast&);
void hash_gen(nasal_ast&);
void function_gen(nasal_ast&);
void call_gen(nasal_ast&);
void call_id(nasal_ast&);
void call_hash(nasal_ast&);
void call_vec(nasal_ast&);
void call_func(nasal_ast&);
void mem_call(nasal_ast&);
void mem_call_id(nasal_ast&);
void mem_call_vec(nasal_ast&);
void mem_call_hash(nasal_ast&);
void multi_def(nasal_ast&);
void single_def(nasal_ast&);
void definition_gen(nasal_ast&);
void multi_assignment_gen(nasal_ast&);
void conditional_gen(nasal_ast&);
void loop_gen(nasal_ast&);
void load_continue_break(int,int);
void while_gen(nasal_ast&);
void for_gen(nasal_ast&);
void forindex_gen(nasal_ast&);
void foreach_gen(nasal_ast&);
void or_gen(nasal_ast&);
void and_gen(nasal_ast&);
void trino_gen(nasal_ast&);
void calculation_gen(nasal_ast&);
void block_gen(nasal_ast&);
void return_gen(nasal_ast&);
public:
nasal_codegen();
void main_progress(nasal_ast&);
void print_op(int);
void print_byte_code();
std::vector<std::string>& get_string_table();
std::vector<double>& get_number_table();
std::vector<opcode>& get_exec_code();
};
nasal_codegen::nasal_codegen()
{
error=0;
in_foreach=0;
in_forindex=0;
return;
}
void nasal_codegen::regist_number(double num)
{
int size=number_table.size();
if(number_table.find(num)==number_table.end())
number_table[num]=size;
return;
}
void nasal_codegen::regist_string(std::string str)
{
int size=string_table.size();
if(string_table.find(str)==string_table.end())
string_table[str]=size;
return;
}
void nasal_codegen::gen(unsigned char op,unsigned int index)
{
opcode opr(op,index);
exec_code.push_back(opr);
return;
}
void nasal_codegen::pop_gen()
{
opcode op(op_pop,0);
exec_code.push_back(op);
return;
}
void nasal_codegen::nil_gen()
{
opcode op(op_pushnil,0);
exec_code.push_back(op);
return;
}
void nasal_codegen::number_gen(nasal_ast& ast)
{
double num=ast.get_num();
regist_number(num);
if(num==1) gen(op_pushone,0);
else if(num==0) gen(op_pushzero,0);
else gen(op_pushnum,number_table[num]);
return;
}
void nasal_codegen::string_gen(nasal_ast& ast)
{
std::string str=ast.get_str();
regist_string(str);
gen(op_pushstr,string_table[str]);
return;
}
void nasal_codegen::vector_gen(nasal_ast& ast)
{
int size=ast.get_children().size();
gen(op_newvec,0);
for(int i=0;i<size;++i)
{
calculation_gen(ast.get_children()[i]);
gen(op_vecapp,0);
}
return;
}
void nasal_codegen::hash_gen(nasal_ast& ast)
{
int size=ast.get_children().size();
gen(op_newhash,0);
for(int i=0;i<size;++i)
{
calculation_gen(ast.get_children()[i].get_children()[1]);
std::string str=ast.get_children()[i].get_children()[0].get_str();
regist_string(str);
gen(op_hashapp,string_table[str]);
}
return;
}
void nasal_codegen::function_gen(nasal_ast& ast)
{
int newfunc_label=exec_code.size();
gen(op_newfunc,0);
nasal_ast& ref_arg=ast.get_children()[0];
int arg_size=ref_arg.get_children().size();
for(int i=0;i<arg_size;++i)
{
nasal_ast& tmp=ref_arg.get_children()[i];
if(tmp.get_type()==ast_id)
{
std::string str=tmp.get_str();
regist_string(str);
gen(op_para,string_table[str]);
}
else if(tmp.get_type()==ast_default_arg)
{
calculation_gen(tmp.get_children()[1]);
std::string str=tmp.get_children()[0].get_str();
regist_string(str);
gen(op_defpara,string_table[str]);
}
else if(tmp.get_type()==ast_dynamic_id)
{
std::string str=tmp.get_str();
regist_string(str);
gen(op_dynpara,string_table[str]);
}
}
exec_code[newfunc_label].index=exec_code.size()+1;
int ptr=exec_code.size();
gen(op_jmp,0);
nasal_ast& block=ast.get_children()[1];
block_gen(block);
if(!block.get_children().size() || block.get_children().back().get_type()!=ast_return)
{
nil_gen();
gen(op_return,0);
}
exec_code[ptr].index=exec_code.size();
return;
}
void nasal_codegen::call_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[0]);
if(exec_code.back().op==op_builtincall)
return;
int child_size=ast.get_children().size();
for(int i=1;i<child_size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
switch(tmp.get_type())
{
case ast_callh:call_hash(tmp);break;
case ast_callv: call_vec(tmp); break;
case ast_callf:call_func(tmp);break;
}
}
return;
}
void nasal_codegen::call_id(nasal_ast& ast)
{
std::string str=ast.get_str();
regist_string(str);
for(int i=0;builtin_func_table[i].func_pointer;++i)
if(builtin_func_table[i].func_name==str)
{
gen(op_builtincall,i);
return;
}
gen(op_call,string_table[str]);
return;
}
void nasal_codegen::call_hash(nasal_ast& ast)
{
std::string str=ast.get_str();
regist_string(str);
gen(op_callh,string_table[str]);
return;
}
void nasal_codegen::call_vec(nasal_ast& ast)
{
if(ast.get_children().size()==1 && ast.get_children()[0].get_type()!=ast_subvec)
{
calculation_gen(ast.get_children()[0]);
gen(op_callv,0);
return;
}
gen(op_slicebegin,0);
int size=ast.get_children().size();
for(int i=0;i<size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
if(tmp.get_type()!=ast_subvec)
{
calculation_gen(tmp);
gen(op_slice,0);
}
else
{
calculation_gen(tmp.get_children()[0]);
calculation_gen(tmp.get_children()[1]);
gen(op_slice2,0);
}
}
gen(op_sliceend,0);
return;
}
void nasal_codegen::call_func(nasal_ast& ast)
{
if(!ast.get_children().size())
gen(op_newvec,0);
else if(ast.get_children()[0].get_type()==ast_hashmember)
hash_gen(ast);
else
vector_gen(ast);
gen(op_callf,0);
return;
}
void nasal_codegen::mem_call(nasal_ast& ast)
{
if(ast.get_type()==ast_id)
mem_call_id(ast);
else
mem_call_id(ast.get_children()[0]);
int child_size=ast.get_children().size();
for(int i=1;i<child_size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
if(tmp.get_type()==ast_callh)
mem_call_hash(tmp);
else if(tmp.get_type()==ast_callv)
mem_call_vec(tmp);
}
return;
}
void nasal_codegen::mem_call_id(nasal_ast& ast)
{
std::string str=ast.get_str();
regist_string(str);
gen(op_mcall,string_table[str]);
return;
}
void nasal_codegen::mem_call_vec(nasal_ast& ast)
{
calculation_gen(ast.get_children()[0]);
gen(op_mcallv,0);
return;
}
void nasal_codegen::mem_call_hash(nasal_ast& ast)
{
std::string str=ast.get_str();
regist_string(str);
gen(op_mcallh,string_table[str]);
return;
}
void nasal_codegen::single_def(nasal_ast& ast)
{
calculation_gen(ast.get_children()[1]);
std::string str=ast.get_children()[0].get_str();
regist_string(str);
gen(op_load,string_table[str]);
return;
}
void nasal_codegen::multi_def(nasal_ast& ast)
{
int size=ast.get_children()[0].get_children().size();
if(ast.get_children()[1].get_type()==ast_multi_scalar)
{
for(int i=0;i<size;++i)
{
calculation_gen(ast.get_children()[1].get_children()[i]);
std::string str=ast.get_children()[0].get_children()[i].get_str();
regist_string(str);
gen(op_load,string_table[str]);
}
}
else
{
calculation_gen(ast.get_children()[1]);
for(int i=0;i<size;++i)
{
gen(op_callvi,i);
std::string str=ast.get_children()[0].get_children()[i].get_str();
regist_string(str);
gen(op_load,string_table[str]);
}
pop_gen();
}
return;
}
void nasal_codegen::definition_gen(nasal_ast& ast)
{
if(ast.get_children()[0].get_type()==ast_id)
single_def(ast);
else
multi_def(ast);
return;
}
void nasal_codegen::multi_assignment_gen(nasal_ast& ast)
{
int size=ast.get_children()[0].get_children().size();
if(ast.get_children()[1].get_type()==ast_multi_scalar)
{
for(int i=size-1;i>=0;--i)
calculation_gen(ast.get_children()[1].get_children()[i]);
for(int i=0;i<size;++i)
{
mem_call(ast.get_children()[0].get_children()[i]);
gen(op_meq,0);
pop_gen();
}
}
else
{
calculation_gen(ast.get_children()[1]);
for(int i=0;i<size;++i)
{
gen(op_callvi,i);
mem_call(ast.get_children()[0].get_children()[i]);
gen(op_meq,0);
pop_gen();
}
pop_gen();
}
return;
}
void nasal_codegen::conditional_gen(nasal_ast& ast)
{
int size=ast.get_children().size();
std::vector<int> jmp_label;
for(int i=0;i<size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
if(tmp.get_type()==ast_if || tmp.get_type()==ast_elsif)
{
calculation_gen(tmp.get_children()[0]);
int ptr=exec_code.size();
gen(op_jmpfalse,0);
block_gen(tmp.get_children()[1]);
jmp_label.push_back(exec_code.size());
// without 'else' the last condition doesn't need to jmp
if(i!=size-1)
gen(op_jmp,0);
exec_code[ptr].index=exec_code.size();
}
else
{
block_gen(tmp.get_children()[0]);
break;
}
}
for(std::vector<int>::iterator i=jmp_label.begin();i!=jmp_label.end();++i)
exec_code[*i].index=exec_code.size();
return;
}
void nasal_codegen::loop_gen(nasal_ast& ast)
{
std::vector<int> new_level;
continue_ptr.push_front(new_level);
break_ptr.push_front(new_level);
switch(ast.get_type())
{
case ast_while: while_gen(ast); break;
case ast_for: for_gen(ast); break;
case ast_forindex: ++in_forindex;forindex_gen(ast);--in_forindex; break;
case ast_foreach: ++in_foreach; foreach_gen(ast); --in_foreach; break;
}
return;
}
void nasal_codegen::load_continue_break(int continue_place,int break_place)
{
for(int i=0;i<continue_ptr.front().size();++i)
exec_code[continue_ptr.front()[i]].index=continue_place;
for(int i=0;i<break_ptr.front().size();++i)
exec_code[break_ptr.front()[i]].index=break_place;
continue_ptr.pop_front();
break_ptr.pop_front();
return;
}
void nasal_codegen::while_gen(nasal_ast& ast)
{
int loop_ptr=exec_code.size();
calculation_gen(ast.get_children()[0]);
int condition_ptr=exec_code.size();
gen(op_jmpfalse,0);
block_gen(ast.get_children()[1]);
gen(op_jmp,loop_ptr);
exec_code[condition_ptr].index=exec_code.size();
load_continue_break(exec_code.size()-1,exec_code.size());
return;
}
void nasal_codegen::for_gen(nasal_ast& ast)
{
opcode op;
switch(ast.get_children()[0].get_type())
{
case ast_null:break;
case ast_definition:definition_gen(ast.get_children()[0]);break;
case ast_multi_assign:multi_assignment_gen(ast.get_children()[0]);break;
case ast_nil:case ast_num:case ast_str:case ast_func:break;
case ast_vec:case ast_hash:
case ast_call:
case ast_equal:case ast_addeq:case ast_subeq:case ast_multeq:case ast_diveq:case ast_lnkeq:
case ast_neg:case ast_not:
case ast_add:case ast_sub:case ast_mult:case ast_div:case ast_link:
case ast_cmpeq:case ast_neq:case ast_leq:case ast_less:case ast_geq:case ast_grt:
case ast_trino:calculation_gen(ast.get_children()[0]);pop_gen();break;
}
int jmp_place=exec_code.size();
if(ast.get_children()[1].get_type()==ast_null)
gen(op_pushone,0);
else
calculation_gen(ast.get_children()[1]);
int label_exit=exec_code.size();
gen(op_jmpfalse,0);
block_gen(ast.get_children()[3]);
int continue_place=exec_code.size();
switch(ast.get_children()[2].get_type())
{
case ast_null:break;
case ast_definition:definition_gen(ast.get_children()[2]);break;
case ast_multi_assign:multi_assignment_gen(ast.get_children()[2]);break;
case ast_nil:case ast_num:case ast_str:case ast_func:break;
case ast_vec:case ast_hash:
case ast_call:
case ast_equal:case ast_addeq:case ast_subeq:case ast_multeq:case ast_diveq:case ast_lnkeq:
case ast_neg:case ast_not:
case ast_add:case ast_sub:case ast_mult:case ast_div:case ast_link:
case ast_cmpeq:case ast_neq:case ast_leq:case ast_less:case ast_geq:case ast_grt:
case ast_trino:calculation_gen(ast.get_children()[2]);pop_gen();break;
}
gen(op_jmp,jmp_place);
exec_code[label_exit].index=exec_code.size();
load_continue_break(continue_place,exec_code.size());
return;
}
void nasal_codegen::forindex_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[1]);
gen(op_counter,0);
int ptr=exec_code.size();
gen(op_forindex,0);
if(ast.get_children()[0].get_type()==ast_new_iter)
{
std::string str=ast.get_children()[0].get_children()[0].get_str();
regist_string(str);
gen(op_load,string_table[str]);
}
else
{
mem_call(ast.get_children()[0]);
gen(op_meq,0);
pop_gen();
}
block_gen(ast.get_children()[2]);
gen(op_jmp,ptr);
exec_code[ptr].index=exec_code.size();
load_continue_break(exec_code.size()-1,exec_code.size());
pop_gen();// pop vector
gen(op_cntpop,0);
return;
}
void nasal_codegen::foreach_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[1]);
gen(op_counter,0);
int ptr=exec_code.size();
gen(op_foreach,0);
if(ast.get_children()[0].get_type()==ast_new_iter)
{
std::string str=ast.get_children()[0].get_children()[0].get_str();
regist_string(str);
gen(op_load,string_table[str]);
}
else
{
mem_call(ast.get_children()[0]);
gen(op_meq,0);
pop_gen();
}
block_gen(ast.get_children()[2]);
gen(op_jmp,ptr);
exec_code[ptr].index=exec_code.size();
load_continue_break(exec_code.size()-1,exec_code.size());
pop_gen();// pop vector
gen(op_cntpop,0);
return;
}
void nasal_codegen::or_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[0]);
int l1=exec_code.size();
gen(op_jmptrue,0);
pop_gen();
calculation_gen(ast.get_children()[1]);
int l2=exec_code.size();
gen(op_jmptrue,0);
pop_gen();
nil_gen();
exec_code[l1].index=exec_code[l2].index=exec_code.size();
return;
}
void nasal_codegen::and_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[0]);
gen(op_jmptrue,exec_code.size()+2);
int lfalse=exec_code.size();
gen(op_jmp,0);
pop_gen();// jt jumps here
calculation_gen(ast.get_children()[1]);
gen(op_jmptrue,exec_code.size()+3);
exec_code[lfalse].index=exec_code.size();
pop_gen();
nil_gen();
//jt jumps here
return;
}
void nasal_codegen::trino_gen(nasal_ast& ast)
{
calculation_gen(ast.get_children()[0]);
int lfalse=exec_code.size();
gen(op_jmpfalse,0);
calculation_gen(ast.get_children()[1]);
int lexit=exec_code.size();
gen(op_jmp,0);
exec_code[lfalse].index=exec_code.size();
calculation_gen(ast.get_children()[2]);
exec_code[lexit].index=exec_code.size();
return;
}
void nasal_codegen::calculation_gen(nasal_ast& ast)
{
switch(ast.get_type())
{
case ast_nil: nil_gen(); break;
case ast_num: number_gen(ast); break;
case ast_str: string_gen(ast); break;
case ast_id: call_id(ast); break;
case ast_vec: vector_gen(ast); break;
case ast_hash: hash_gen(ast); break;
case ast_func: function_gen(ast); break;
case ast_call: call_gen(ast); break;
case ast_equal:
calculation_gen(ast.get_children()[1]);
mem_call(ast.get_children()[0]);
gen(op_meq,0);
break;
// ast_addeq(22)~ast_lnkeq(26) op_addeq(23)~op_lnkeq(27)
case ast_addeq:case ast_subeq:case ast_multeq:case ast_diveq:case ast_lnkeq:
calculation_gen(ast.get_children()[1]);
mem_call(ast.get_children()[0]);
gen(ast.get_type()-ast_addeq+op_addeq,0);
break;
case ast_or:or_gen(ast);break;
case ast_and:and_gen(ast);break;
// ast_add(33)~ast_link(37) op_add(18)~op_lnk(22)
case ast_add:case ast_sub:case ast_mult:case ast_div:case ast_link:
calculation_gen(ast.get_children()[0]);
calculation_gen(ast.get_children()[1]);
gen(ast.get_type()-ast_add+op_add,0);
break;
// ast_cmpeq(27)~ast_geq(32) op_eq(29)~op_geq(34)
case ast_cmpeq:case ast_neq:case ast_less:case ast_leq:case ast_grt:case ast_geq:
calculation_gen(ast.get_children()[0]);
calculation_gen(ast.get_children()[1]);
gen(ast.get_type()-ast_cmpeq+op_eq,0);
break;
case ast_trino:trino_gen(ast);break;
case ast_neg:
calculation_gen(ast.get_children()[0]);
gen(op_usub,0);
break;
case ast_not:
calculation_gen(ast.get_children()[0]);
gen(op_unot,0);
break;
case ast_definition:
single_def(ast);
call_id(ast.get_children()[0]);
break;
}
return;
}
void nasal_codegen::block_gen(nasal_ast& ast)
{
int size=ast.get_children().size();
for(int i=0;i<size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
switch(tmp.get_type())
{
case ast_null:case ast_nil:case ast_num:case ast_str:case ast_func:break;
case ast_definition:definition_gen(tmp);break;
case ast_multi_assign:multi_assignment_gen(tmp);break;
case ast_conditional:conditional_gen(tmp);break;
case ast_continue:
continue_ptr.front().push_back(exec_code.size());
gen(op_jmp,0);
break;
case ast_break:
break_ptr.front().push_back(exec_code.size());
gen(op_jmp,0);
break;
case ast_while:
case ast_for:
case ast_forindex:
case ast_foreach:loop_gen(tmp);break;
case ast_id:
case ast_vec:
case ast_hash:
case ast_call:
case ast_equal:
case ast_addeq:
case ast_subeq:
case ast_multeq:
case ast_diveq:
case ast_lnkeq:
case ast_neg:
case ast_not:
case ast_add:
case ast_sub:
case ast_mult:
case ast_div:
case ast_link:
case ast_cmpeq:
case ast_neq:
case ast_leq:
case ast_less:
case ast_geq:
case ast_grt:
case ast_or:
case ast_and:
case ast_trino:calculation_gen(tmp);pop_gen();break;
case ast_return:return_gen(tmp);break;
}
}
return;
}
void nasal_codegen::return_gen(nasal_ast& ast)
{
for(int i=0;i<in_foreach;++i)
{
gen(op_pop,0);
gen(op_cntpop,0);
}
for(int i=0;i<in_forindex;++i)
{
gen(op_pop,0);
gen(op_cntpop,0);
}
if(ast.get_children().size())
calculation_gen(ast.get_children()[0]);
else
nil_gen();
gen(op_return,0);
return;
}
void nasal_codegen::main_progress(nasal_ast& ast)
{
error=0;
number_table.clear();
string_table.clear();
exec_code.clear();
int size=ast.get_children().size();
for(int i=0;i<size;++i)
{
nasal_ast& tmp=ast.get_children()[i];
switch(tmp.get_type())
{
case ast_null:case ast_nil:case ast_num:case ast_str:case ast_func:break;
case ast_definition:definition_gen(tmp);break;
case ast_multi_assign:multi_assignment_gen(tmp);break;
case ast_conditional:conditional_gen(tmp);break;
case ast_while:
case ast_for:
case ast_forindex:
case ast_foreach:loop_gen(tmp);break;
case ast_id:
case ast_vec:
case ast_hash:
case ast_call:
case ast_equal:
case ast_addeq:
case ast_subeq:
case ast_multeq:
case ast_diveq:
case ast_lnkeq:
case ast_neg:
case ast_not:
case ast_add:
case ast_sub:
case ast_mult:
case ast_div:
case ast_link:
case ast_cmpeq:
case ast_neq:
case ast_leq:
case ast_less:
case ast_geq:
case ast_grt:
case ast_or:
case ast_and:
case ast_trino:calculation_gen(tmp);pop_gen();break;
}
}
gen(op_nop,0);
number_result_table.resize(number_table.size());
string_result_table.resize(string_table.size());
for(auto i=number_table.begin();i!=number_table.end();++i)
number_result_table[i->second]=i->first;
for(auto i=string_table.begin();i!=string_table.end();++i)
string_result_table[i->second]=i->first;
return;
}
void nasal_codegen::print_op(int index)
{
// print opcode ptr
printf("0x%.8x: ",index);
// print opcode name
for(int i=0;code_table[i].name;++i)
if(exec_code[index].op==code_table[i].type)
{
std::cout<<code_table[i].name<<" ";
break;
}
// print opcode index
printf("0x%.8x ",exec_code[index].index);
// print detail info
switch(exec_code[index].op)
{
case op_pushnum:std::cout<<'('<<number_result_table[exec_code[index].index]<<')';break;
case op_builtincall:std::cout<<'('<<builtin_func_table[exec_code[index].index].func_name<<')';break;
case op_hashapp:
case op_call:
case op_mcall:
case op_pushstr:
case op_callh:
case op_mcallh:
case op_para:
case op_defpara:
case op_dynpara:
case op_load:std::cout<<'('<<string_result_table[exec_code[index].index]<<')';break;
}
std::cout<<'\n';
return;
}
void nasal_codegen::print_byte_code()
{
for(int i=0;i<number_result_table.size();++i)
std::cout<<".number "<<number_result_table[i]<<'\n';
for(int i=0;i<string_result_table.size();++i)
std::cout<<".symbol "<<string_result_table[i]<<'\n';
int size=exec_code.size();
for(int i=0;i<size;++i)
print_op(i);
return;
}
std::vector<std::string>& nasal_codegen::get_string_table()
{
return string_result_table;
}
std::vector<double>& nasal_codegen::get_number_table()
{
return number_result_table;
}
std::vector<opcode>& nasal_codegen::get_exec_code()
{
return exec_code;
}
#endif

View File

@@ -1,659 +0,0 @@
#ifndef __NASAL_GC_H__
#define __NASAL_GC_H__
enum runtime_scalar_type
{
vm_nil=0,
vm_num,
vm_str,
vm_scop,
vm_func,
vm_vec,
vm_hash
};
class nasal_gc;
class nasal_vec;
class nasal_hash;
class nasal_func;
class nasal_scop;
class nasal_val;
class nasal_vec
{
private:
nasal_gc& gc;
std::vector<nasal_val*> elems;
public:
nasal_vec(nasal_gc&);
~nasal_vec();
int size();
void add_elem(nasal_val*);
void print();
nasal_val* del_elem();
nasal_val* operator[](const int);
nasal_val* get_value_address(int);
nasal_val** get_mem_address(int);
};
class nasal_hash
{
private:
nasal_gc& gc;
std::unordered_map<std::string,nasal_val*> elems;
public:
nasal_hash(nasal_gc&);
~nasal_hash();
int size();
bool check_contain(std::string);
void add_elem(std::string,nasal_val*);
void del_elem(std::string);
void print();
nasal_val* get_special_para(std::string);
nasal_val* get_value_address(std::string);
nasal_val** get_mem_address(std::string);
nasal_val* get_keys();
};
class nasal_func
{
private:
nasal_gc& gc;
int entry;
nasal_val* closure_addr;
std::vector<int> para;
int dynpara;
std::vector<nasal_val*> default_para_addr;
public:
nasal_func(nasal_gc&);
~nasal_func();
int get_entry();
int get_dynamic_para();
void set_entry(int);
void add_para(int,nasal_val*,bool);
void set_closure_addr(nasal_val*);
void set_new_closure();
nasal_val* get_closure_addr();
std::vector<int>& get_para();
std::vector<nasal_val*>& get_default();
};
class nasal_scop
{
private:
nasal_gc& gc;
std::unordered_map<int,nasal_val*> elems;
public:
nasal_scop(nasal_gc&);
~nasal_scop();
void set_closure(nasal_scop&);
void add_new_value(int,nasal_val*);
nasal_val* get_value_address(int);
nasal_val** get_mem_address(int);
};
class nasal_val
{
protected:
int type;
union
{
double num;
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
nasal_func* func;
nasal_scop* cls;
}ptr;
public:
int ref_cnt;
nasal_val();
nasal_val(int,nasal_gc&);
~nasal_val();
void clear();
void set_type(int,nasal_gc&);
void set_number(double);
void set_string(std::string);
int get_type();
double to_number();
double get_number();
std::string to_string();
std::string get_string();
nasal_vec& get_vector();
nasal_hash& get_hash();
nasal_func& get_func();
nasal_scop& get_closure();
};
class nasal_gc
{
private:
std::queue<nasal_val*> free_space;
std::vector<nasal_val*> memory;
public:
~nasal_gc();
void clear();
nasal_val* gc_alloc(int);
void add_reference(nasal_val*);
void del_reference(nasal_val*);
};
/*functions of nasal_vec*/
nasal_vec::nasal_vec(nasal_gc& ngc):gc(ngc)
{
return;
}
nasal_vec::~nasal_vec()
{
int size=elems.size();
for(int i=0;i<size;++i)
gc.del_reference(elems[i]);
return;
}
void nasal_vec::add_elem(nasal_val* value_address)
{
elems.push_back(value_address);
return;
}
nasal_val* nasal_vec::del_elem()
{
// pop back
if(!elems.size())
return NULL;
nasal_val* ret=elems.back();
elems.pop_back();
return ret;
}
int nasal_vec::size()
{
return elems.size();
}
nasal_val* nasal_vec::operator[](const int index)
{
return elems[index];
}
nasal_val* nasal_vec::get_value_address(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
{
std::cout<<">> [gc] nasal_vec::get_value_address: index out of range: "<<index<<"\n";
return NULL;
}
int idx[2]={index+vec_size,index};
return elems[idx[index>=0]];
}
nasal_val** nasal_vec::get_mem_address(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
{
std::cout<<">> [gc] nasal_vec::get_mem_address: index out of range: "<<index<<"\n";
return NULL;
}
int idx[2]={index+vec_size,index};
return &elems[idx[index>=0]];
}
void nasal_vec::print()
{
int size=elems.size();
std::cout<<"[";
if(!size)
std::cout<<"]";
for(int i=0;i<size;++i)
{
nasal_val* tmp=elems[i];
switch(tmp->get_type())
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->get_number(); break;
case vm_str: std::cout<<tmp->get_string(); break;
case vm_vec: tmp->get_vector().print(); break;
case vm_hash: tmp->get_hash().print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
std::cout<<",]"[i==size-1];
}
return;
}
/*functions of nasal_hash*/
nasal_hash::nasal_hash(nasal_gc& ngc):gc(ngc)
{
return;
}
nasal_hash::~nasal_hash()
{
for(auto iter=elems.begin();iter!=elems.end();++iter)
gc.del_reference(iter->second);
return;
}
void nasal_hash::add_elem(std::string key,nasal_val* value_address)
{
if(!elems.count(key))
elems[key]=value_address;
return;
}
void nasal_hash::del_elem(std::string key)
{
if(!elems.count(key))
{
gc.del_reference(elems[key]);
elems.erase(key);
}
return;
}
int nasal_hash::size()
{
return elems.size();
}
nasal_val* nasal_hash::get_special_para(std::string key)
{
if(elems.count(key))
return elems[key];
return NULL;
}
nasal_val* nasal_hash::get_value_address(std::string key)
{
nasal_val* ret_value_addr=NULL;
if(elems.count(key))
return elems[key];
else if(elems.count("parents"))
{
nasal_val* val_addr=elems["parents"];
if(val_addr->get_type()==vm_vec)
{
nasal_vec& vec_ref=val_addr->get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
nasal_val* tmp_val_addr=vec_ref.get_value_address(i);
if(tmp_val_addr->get_type()==vm_hash)
ret_value_addr=tmp_val_addr->get_hash().get_value_address(key);
if(ret_value_addr)
break;
}
}
}
return ret_value_addr;
}
nasal_val** nasal_hash::get_mem_address(std::string key)
{
nasal_val** mem_addr=NULL;
if(elems.count(key))
return &elems[key];
else if(elems.count("parents"))
{
nasal_val* val_addr=elems["parents"];
if(val_addr->get_type()==vm_vec)
{
nasal_vec& vec_ref=val_addr->get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
nasal_val* tmp_val_addr=vec_ref.get_value_address(i);
if(tmp_val_addr->get_type()==vm_hash)
mem_addr=tmp_val_addr->get_hash().get_mem_address(key);
if(mem_addr)
break;
}
}
}
return mem_addr;
}
bool nasal_hash::check_contain(std::string key)
{
if(elems.count(key))
return true;
if(elems.count("parents"))
{
bool result=false;
nasal_val* val_addr=elems["parents"];
if(val_addr->get_type()==vm_vec)
{
nasal_vec& vec_ref=val_addr->get_vector();
int size=vec_ref.size();
for(int i=0;i<size;++i)
{
nasal_val* tmp_val_addr=vec_ref.get_value_address(i);
if(tmp_val_addr->get_type()==vm_hash)
result=tmp_val_addr->get_hash().check_contain(key);
if(result)
break;
}
}
return result;
}
return false;
}
nasal_val* nasal_hash::get_keys()
{
nasal_val* ret_addr=gc.gc_alloc(vm_vec);
nasal_vec& ref_vec=ret_addr->get_vector();
for(auto iter=elems.begin();iter!=elems.end();++iter)
{
nasal_val* str_addr=gc.gc_alloc(vm_str);
str_addr->set_string(iter->first);
ref_vec.add_elem(str_addr);
}
return ret_addr;
}
void nasal_hash::print()
{
std::cout<<'{';
if(!elems.size())
{
std::cout<<'}';
return;
}
for(auto i=elems.begin();i!=elems.end();++i)
{
std::cout<<i->first<<':';
nasal_val* tmp=i->second;
switch(tmp->get_type())
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->get_number(); break;
case vm_str: std::cout<<tmp->get_string(); break;
case vm_vec: tmp->get_vector().print(); break;
case vm_hash: tmp->get_hash().print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
}
std::cout<<',';
}
std::cout<<'}';
return;
}
/*functions of nasal_func*/
nasal_func::nasal_func(nasal_gc& ngc):gc(ngc)
{
closure_addr=NULL;
dynpara=-1;
return;
}
nasal_func::~nasal_func()
{
if(closure_addr)
gc.del_reference(closure_addr);
for(int i=0;i<default_para_addr.size();++i)
if(default_para_addr[i])
gc.del_reference(default_para_addr[i]);
return;
}
void nasal_func::set_entry(int etr)
{
entry=etr;
return;
}
int nasal_func::get_entry()
{
return entry;
}
void nasal_func::add_para(int name_index,nasal_val* val_addr=NULL,bool is_dynamic=false)
{
if(is_dynamic)
{
dynpara=name_index;
return;
}
para.push_back(name_index);
default_para_addr.push_back(val_addr);
return;
}
std::vector<int>& nasal_func::get_para()
{
return para;
}
int nasal_func::get_dynamic_para()
{
return dynpara;
}
std::vector<nasal_val*>& nasal_func::get_default()
{
return default_para_addr;
}
void nasal_func::set_closure_addr(nasal_val* value_address)
{
nasal_val* new_closure=gc.gc_alloc(vm_scop);
new_closure->get_closure().set_closure(value_address->get_closure());
closure_addr=new_closure;
return;
}
void nasal_func::set_new_closure()
{
closure_addr=gc.gc_alloc(vm_scop);
return;
}
nasal_val* nasal_func::get_closure_addr()
{
return closure_addr;
}
/*functions of nasal_scop*/
nasal_scop::nasal_scop(nasal_gc& ngc):gc(ngc)
{
return;
}
nasal_scop::~nasal_scop()
{
for(auto i=elems.begin();i!=elems.end();++i)
gc.del_reference(i->second);
return;
}
void nasal_scop::add_new_value(int key,nasal_val* value_address)
{
if(elems.count(key))
{
// if this value already exists,delete the old value and update a new value
gc.del_reference(elems[key]);
}
elems[key]=value_address;
return;
}
nasal_val* nasal_scop::get_value_address(int key)
{
if(elems.count(key))
return elems[key];
return NULL;
}
nasal_val** nasal_scop::get_mem_address(int key)
{
if(elems.count(key))
return &(elems[key]);
return NULL;
}
void nasal_scop::set_closure(nasal_scop& tmp)
{
elems=tmp.elems;
for(auto i=elems.begin();i!=elems.end();++i)
gc.add_reference(i->second);
return;
}
/*functions of nasal_val*/
nasal_val::nasal_val()
{
ref_cnt=1;
type=vm_nil;
return;
}
nasal_val::nasal_val(int nasal_val_type,nasal_gc& nvm)
{
ref_cnt=1;
type=nasal_val_type;
switch(nasal_val_type)
{
case vm_nil: break;
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec(nvm); break;
case vm_hash: ptr.hash=new nasal_hash(nvm); break;
case vm_func: ptr.func=new nasal_func(nvm); break;
case vm_scop: ptr.cls=new nasal_scop(nvm); break;
}
return;
}
nasal_val::~nasal_val()
{
// must set type and scalar_ptr to default first
// this operation will avoid SIGTRAP caused by circular reference
// circular reference will cause using destructor repeatedly
int tmp_type=type;
type=vm_nil;
switch(tmp_type)
{
case vm_nil: break;
case vm_num: break;
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_scop: delete ptr.cls; break;
}
return;
}
void nasal_val::clear()
{
// must set type and scalar_ptr to default first
// this operation will avoid SIGTRAP caused by circular reference
// circular reference will cause using destructor repeatedly
int tmp_type=type;
type=vm_nil;
switch(tmp_type)
{
case vm_nil: break;
case vm_num: break;
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_scop: delete ptr.cls; break;
}
return;
}
void nasal_val::set_type(int nasal_val_type,nasal_gc& nvm)
{
type=nasal_val_type;
switch(nasal_val_type)
{
case vm_nil: break;
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec(nvm); break;
case vm_hash: ptr.hash=new nasal_hash(nvm); break;
case vm_func: ptr.func=new nasal_func(nvm); break;
case vm_scop: ptr.cls=new nasal_scop(nvm); break;
}
return;
}
void nasal_val::set_number(double num)
{
ptr.num=num;
return;
}
void nasal_val::set_string(std::string str)
{
*ptr.str=str;
return;
}
int nasal_val::get_type()
{
return type;
}
double nasal_val::to_number()
{
if(type==vm_num)
return ptr.num;
else if(type==vm_str)
return trans_string_to_number(*ptr.str);
return 0;
}
double nasal_val::get_number()
{
return ptr.num;
}
std::string nasal_val::to_string()
{
if(type==vm_str)
return *ptr.str;
else if(type==vm_num)
return trans_number_to_string(ptr.num);
return "";
}
std::string nasal_val::get_string()
{
return *ptr.str;
}
nasal_vec& nasal_val::get_vector()
{
return *ptr.vec;
}
nasal_hash& nasal_val::get_hash()
{
return *ptr.hash;
}
nasal_func& nasal_val::get_func()
{
return *ptr.func;
}
nasal_scop& nasal_val::get_closure()
{
return *ptr.cls;
}
/*functions of nasal_gc*/
nasal_gc::~nasal_gc()
{
int gc_mem_size=memory.size();
for(int i=0;i<gc_mem_size;++i)
memory[i]->clear();
for(int i=0;i<gc_mem_size;++i)
delete memory[i];
while(!free_space.empty())
free_space.pop();
return;
}
void nasal_gc::clear()
{
int gc_mem_size=memory.size();
for(int i=0;i<gc_mem_size;++i)
memory[i]->clear();
for(int i=0;i<gc_mem_size;++i)
delete memory[i];
while(!free_space.empty())
free_space.pop();
memory.clear();
return;
}
nasal_val* nasal_gc::gc_alloc(int val_type)
{
if(free_space.empty())
{
nasal_val* new_unit=new nasal_val(val_type,*this);
memory.push_back(new_unit);
return new_unit;
}
nasal_val* ret=free_space.front();
free_space.pop();
ret->ref_cnt=1;
ret->set_type(val_type,*this);
return ret;
}
void nasal_gc::add_reference(nasal_val* value_address)
{
++value_address->ref_cnt;
return;
}
void nasal_gc::del_reference(nasal_val* value_address)
{
--value_address->ref_cnt;
if(!value_address->ref_cnt)
{
value_address->clear();
free_space.push(value_address);
}
return;
}
#endif

View File

@@ -1,152 +0,0 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
class nasal_import
{
private:
nasal_lexer import_lex;
nasal_parse import_par;
nasal_ast import_ast;
std::vector<std::string> filename_table;
int error;
void die(std::string,std::string);
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();
void link(nasal_ast&);
nasal_ast& get_root();
};
void nasal_import::die(std::string filename,std::string error_stage)
{
++error;
std::cout<<">> [import] in <\""<<filename<<"\">: error(s) occurred in "<<error_stage<<"."<<std::endl;
return;
}
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 filename)
{
// avoid importing the same file
int size=filename_table.size();
for(int i=0;i<size;++i)
if(filename==filename_table[i])
return true;
filename_table.push_back(filename);
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast& add_root)
{
// add children of add_root to the back of root
std::vector<nasal_ast>& ref_vec=add_root.get_children();
int size=ref_vec.size();
for(int i=0;i<size;++i)
root.add_child(ref_vec[i]);
return;
}
nasal_ast nasal_import::file_import(nasal_ast& node)
{
// initializing
nasal_ast tmp(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())
{
this->die(filename,"lexer");
return tmp;
}
import_par.set_toklist(import_lex.get_token_list());
import_par.main_process();
if(import_par.get_error())
{
this->die(filename,"parser");
return tmp;
}
tmp=import_par.get_root();
// check if tmp has 'import'
return load(tmp);
}
nasal_ast nasal_import::load(nasal_ast& root)
{
nasal_ast new_root(0,ast_root);
std::vector<nasal_ast>& ref_vec=root.get_children();
int size=ref_vec.size();
for(int i=0;i<size;++i)
{
if(check_import(ref_vec[i]))
{
nasal_ast tmp=file_import(ref_vec[i]);
// add tmp to the back of new_root
linker(new_root,tmp);
}
}
// add root to the back of new_root
linker(new_root,root);
// oops,i think it is not efficient if the root is too ... large?
return new_root;
}
void nasal_import::link(nasal_ast& root)
{
// initializing
error=0;
filename_table.clear();
import_ast.clear();
// scan root and import files,then generate a new ast and return to import_ast
import_ast=load(root);
return;
}
nasal_ast& nasal_import::get_root()
{
return import_ast;
}
int nasal_import::get_error()
{
return error;
}
#endif

View File

@@ -1,411 +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 },
{NULL ,-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_token_type(std::string);
void die(std::string,std::string,int,int);
std::string identifier_gen();
std::string number_gen();
std::string string_gen();
public:
void openfile(std::string);
void scanner();
void print_token();
int get_error();
std::vector<token>& get_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();
res_size=res.size();
return;
}
int nasal_lexer::get_token_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(std::string code,std::string error_info,int line=-1,int column=-1)
{
++error;
std::cout<<">> [lexer] line "<<line<<" column "<<column<<": \n"<<code<<"\n";
for(int i=0;i<column-1;++i)
std::cout<<(code[i]=='\t'?'\t':' ');
std::cout<<"^ "<<error_info<<'\n';
return;
}
std::string nasal_lexer::identifier_gen()
{
std::string token_str="";
while(ptr<res_size && (IS_IDENTIFIER(res[ptr])||IS_DIGIT(res[ptr])))
token_str+=res[ptr++];
line_code+=token_str;
return token_str;
// after running this process, ptr will point to the next token's beginning character
}
std::string nasal_lexer::number_gen()
{
bool scientific_notation=false;// numbers like 1e8 are scientific_notation
std::string token_str="";
// generate hex number
if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='x')
{
token_str="0x";
ptr+=2;
while(ptr<res_size && IS_HEX_NUMBER(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str=="0x")
{
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
return token_str;
}
// generate oct number
else if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='o')
{
token_str="0o";
ptr+=2;
while(ptr<res_size && IS_OCT_NUMEBR(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str=="0o")
{
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
return token_str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
if(ptr<res_size && res[ptr]=='.')
{
token_str+=res[ptr++];
// "xxxx." is not a correct number
if(ptr>=res_size)
{
line_code+=token_str;
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxx." is not a correct number
if(token_str.back()=='.')
{
line_code+=token_str;
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
}
if(ptr<res_size && (res[ptr]=='e' || res[ptr]=='E'))
{
token_str+=res[ptr++];
// "xxxe" is not a correct number
if(ptr>=res_size)
{
line_code+=token_str;
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
if(ptr<res_size && (res[ptr]=='-' || res[ptr]=='+'))
token_str+=res[ptr++];
if(ptr>=res_size)
{
line_code+=token_str;
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxe(-|+)" is not a correct number
if(token_str.back()=='e' || token_str.back()=='E' || token_str.back()=='-' || token_str.back()=='+')
{
line_code+=token_str;
die(line_code,"incorrect number.",line,line_code.length());
return "0";
}
}
line_code+=token_str;
return token_str;
}
std::string nasal_lexer::string_gen()
{
std::string token_str="";
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(line_code,"get EOF when generating string.",line,line_code.length());
return token_str;
}
void nasal_lexer::scanner()
{
token_list.clear();
line=1;
ptr=0;
line_code="";
std::string token_str;
while(ptr<res_size)
{
while(ptr<res_size && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
{
// these characters will be ignored, and '\n' will cause ++line
line_code+=res[ptr];
if(res[ptr++]=='\n')
{
++line;
line_code="";
}
}
if(ptr>=res_size) break;
if(IS_IDENTIFIER(res[ptr]))
{
token_str=identifier_gen();
token new_token(line,get_token_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=number_gen();
token new_token(line,tok_num,token_str);
token_list.push_back(new_token);
}
else if(IS_STRING(res[ptr]))
{
token_str=string_gen();
token new_token(line,tok_str,token_str);
token_list.push_back(new_token);
}
else if(IS_SINGLE_OPERATOR(res[ptr]))
{
token_str="";
token_str+=res[ptr];
line_code+=res[ptr];
token new_token(line,get_token_type(token_str),token_str);
if(!new_token.type)
die(line_code,"incorrect operator.",line,line_code.length());
token_list.push_back(new_token);
++ptr;
}
else if(res[ptr]=='.')
{
if(ptr+2<res_size && res[ptr+1]=='.' && res[ptr+2]=='.')
{
token_str="...";
ptr+=3;
}
else
{
token_str=".";
++ptr;
}
line_code+=token_str;
token new_token(line,get_token_type(token_str),token_str);
token_list.push_back(new_token);
}
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 new_token(line,get_token_type(token_str),token_str);
token_list.push_back(new_token);
}
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(line_code,"unknown character.",line,line_code.length());
}
}
token tk(line,tok_eof,"");
token_list.push_back(tk);
return;
}
void nasal_lexer::print_token()
{
int size=token_list.size();
for(int i=0;i<size;++i)
std::cout<<"("<<token_list[i].line<<" | "<<token_list[i].str<<")\n";
return;
}
int nasal_lexer::get_error()
{
return error;
}
std::vector<token>& nasal_lexer::get_token_list()
{
return token_list;
}
#endif

File diff suppressed because it is too large Load Diff

1183
nasal_vm.h

File diff suppressed because it is too large Load Diff

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());

518
src/ast_dumper.cpp Normal file
View File

@@ -0,0 +1,518 @@
#include "ast_dumper.h"
#include "util/util.h"
#include <iostream>
namespace nasal {
bool ast_dumper::visit_use_stmt(use_stmt* node) {
dump_indent();
std::cout << "use" << format_location(node);
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);
return true;
}
bool ast_dumper::visit_nil_expr(nil_expr* node) {
dump_indent();
std::cout << "nil" << format_location(node);
return true;
}
bool ast_dumper::visit_number_literal(number_literal* node) {
dump_indent();
std::cout << "number " << node->get_number();
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_string_literal(string_literal* node) {
dump_indent();
std::cout << "string \"" << util::rawstr(node->get_content()) << "\"";
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_identifier(identifier* node) {
dump_indent();
std::cout << "identifier " << node->get_name();
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_bool_literal(bool_literal* node) {
dump_indent();
std::cout << "bool " << node->get_flag();
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_vector_expr(vector_expr* node) {
dump_indent();
std::cout << "vector";
std::cout << format_location(node);
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);
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);
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);
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);
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 ";
switch(node->get_parameter_type()) {
case parameter::kind::normal_parameter: std::cout << "[normal]"; break;
case parameter::kind::dynamic_parameter: std::cout << "[dynamic]"; break;
case parameter::kind::default_parameter: std::cout << "[default]"; break;
}
std::cout << " " << node->get_parameter_name();
std::cout << format_location(node);
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);
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::kind::add: std::cout << "+"; break;
case binary_operator::kind::sub: std::cout << "-"; break;
case binary_operator::kind::mult: std::cout << "*"; break;
case binary_operator::kind::div: std::cout << "/"; break;
case binary_operator::kind::concat: std::cout << "~"; break;
case binary_operator::kind::bitwise_and: std::cout << "&"; break;
case binary_operator::kind::bitwise_or: std::cout << "|"; break;
case binary_operator::kind::bitwise_xor: std::cout << "^"; break;
case binary_operator::kind::cmpeq: std::cout << "=="; break;
case binary_operator::kind::cmpneq: std::cout << "!="; break;
case binary_operator::kind::grt: std::cout << ">"; break;
case binary_operator::kind::geq: std::cout << ">="; break;
case binary_operator::kind::less: std::cout << "<"; break;
case binary_operator::kind::leq: std::cout << "<="; break;
case binary_operator::kind::condition_and: std::cout << "and"; break;
case binary_operator::kind::condition_or: std::cout << "or"; break;
case binary_operator::kind::null_chain: std::cout << "??"; break;
}
std::cout << "\"" << format_location(node);
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::kind::negative: std::cout << "-"; break;
case unary_operator::kind::logical_not: std::cout << "!"; break;
case unary_operator::kind::bitwise_not: std::cout << "~"; break;
}
std::cout << "\"" << format_location(node);
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);
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);
return true;
}
bool ast_dumper::visit_null_access(null_access* node) {
dump_indent();
std::cout << "null_access " << node->get_field();
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_call_vector(call_vector* node) {
dump_indent();
std::cout << "call_vector";
std::cout << format_location(node);
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);
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);
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);
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::kind::add_equal: std::cout << "+="; break;
case assignment_expr::kind::sub_equal: std::cout << "-="; break;
case assignment_expr::kind::mult_equal: std::cout << "*="; break;
case assignment_expr::kind::div_equal: std::cout << "/="; break;
case assignment_expr::kind::concat_equal: std::cout << "~="; break;
case assignment_expr::kind::equal: std::cout << "="; break;
case assignment_expr::kind::bitwise_and_equal: std::cout << "&="; break;
case assignment_expr::kind::bitwise_or_equal: std::cout << "|="; break;
case assignment_expr::kind::bitwise_xor_equal: std::cout << "^="; break;
}
std::cout << format_location(node);
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);
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);
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);
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);
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);
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);
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::kind::foreach) {
std::cout << "foreach";
} else {
std::cout << "forindex";
}
std::cout << format_location(node);
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);
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);
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);
return true;
}
bool ast_dumper::visit_break_expr(break_expr* node) {
dump_indent();
std::cout << "break";
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_return_expr(return_expr* node) {
dump_indent();
std::cout << "return";
std::cout << format_location(node);
if (node->get_value()) {
push_indent();
set_last();
node->get_value()->accept(this);
pop_indent();
}
return true;
}
}

101
src/ast_dumper.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include "ast_visitor.h"
#include "util/util.h"
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
namespace nasal {
class ast_dumper: public ast_visitor {
private:
std::vector<std::string> indent;
private:
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(expr* node) {
std::stringstream ss;
ss << " ⇒ [";
node->get_location().dump_begin(ss);
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_null_access(null_access*) 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) {
util::windows_code_page_manager wm;
wm.set_utf8_output();
root->accept(this);
wm.restore_code_page();
}
};
}

249
src/ast_visitor.cpp Normal file
View File

@@ -0,0 +1,249 @@
#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_null_access(null_access* 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;
}
}

49
src/ast_visitor.h Normal file
View File

@@ -0,0 +1,49 @@
#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_null_access(null_access*);
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*);
};
}

62
src/cli/cli.cpp Normal file
View File

@@ -0,0 +1,62 @@
#include "cli/cli.h"
#include <iostream>
namespace nasal::cli {
cli_config parse(const std::vector<std::string>& args) {
cli_config result;
for(const auto& arg : args) {
if (cli_options.count(arg)) {
result.options.insert(cli_options.at(arg));
} else if (!result.input_file_path.length()) {
result.input_file_path = arg;
} else {
result.nasal_vm_args.push_back(arg);
}
}
if (result.input_file_path.length() && result.options.empty()) {
result.options.insert(option::cli_execute);
}
return result;
}
std::ostream& help(std::ostream& out) {
out
<< "\n"
<< " ,--#-,\n"
<< "<3 / \\____\\ <3\n"
<< " |_|__A_|\n"
<< "\nnasal <option>\n"
<< "option:\n"
<< " -h, --help | get help.\n"
<< " -v, --version | get version.\n"
<< " -r, --repl | use repl interpreter.\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"
<< " -f, --ref-file | get referenced files.\n"
<< " -dbg, --debug | debug mode.\n"
<< " --prof | show profiling result, "
<< "available under debug mode.\n"
<< " --prof-all | show profiling result of all files, "
<< "available under debug mode.\n"
<< " --limit | use limited execution mode "
<< "(readonly api enabled).\n"
<< "file:\n"
<< " <filename> | execute file.\n"
<< "argv:\n"
<< " <args> | cmd arguments used in program.\n"
<< "\n";
return out;
}
}

73
src/cli/cli.h Normal file
View File

@@ -0,0 +1,73 @@
#pragma once
#include <cstring>
#include <sstream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <iostream>
namespace nasal::cli {
enum class option {
cli_help,
cli_version,
cli_repl_mode,
cli_view_ast,
cli_view_raw_ast,
cli_view_code,
cli_show_symbol,
cli_execute,
cli_show_execute_time,
cli_detail_info,
cli_show_referenced_file,
cli_debug_mode,
cli_profile,
cli_profile_all,
cli_limit_mode
};
struct cli_config {
std::string input_file_path = "";
std::unordered_set<option> options = {};
std::vector<std::string> nasal_vm_args = {};
bool has(option opt) const {
return options.count(opt);
}
};
const std::unordered_map<std::string, option> cli_options = {
{"-h", option::cli_help},
{"--help", option::cli_help},
{"-v", option::cli_version},
{"--version", option::cli_version},
{"-r", option::cli_repl_mode},
{"--repl", option::cli_repl_mode},
{"-a", option::cli_view_ast},
{"--ast", option::cli_view_ast},
{"--raw-ast", option::cli_view_raw_ast},
{"-c", option::cli_view_code},
{"--code", option::cli_view_code},
{"-s", option::cli_show_symbol},
{"--symbol", option::cli_show_symbol},
{"-e", option::cli_execute},
{"--exec", option::cli_execute},
{"-t", option::cli_show_execute_time},
{"--time", option::cli_show_execute_time},
{"-d", option::cli_detail_info},
{"--detail", option::cli_detail_info},
{"-f", option::cli_show_referenced_file},
{"--ref-file", option::cli_show_referenced_file},
{"-dbg", option::cli_debug_mode},
{"--debug", option::cli_debug_mode},
{"--prof", option::cli_profile},
{"--prof-all", option::cli_profile_all},
{"--limit", option::cli_limit_mode}
};
cli_config parse(const std::vector<std::string>&);
std::ostream& help(std::ostream&);
}

181
src/main.cpp Normal file
View File

@@ -0,0 +1,181 @@
#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 "util/util.h"
#include "repl/repl.h"
#include "cli/cli.h"
#include <thread>
#include <cstdlib>
std::ostream& logo(std::ostream& out) {
out
<< "\n"
<< " __ _\n"
<< " /\\ \\ \\__ _ ___ __ _| |\n"
<< " / \\/ / _` / __|/ _` | |\n"
<< " / /\\ / (_| \\__ \\ (_| | |\n"
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<< "\n"
<< "ver : " << __nasver__
<< " " << nasal::util::get_platform()
<< " " << nasal::util::get_arch()
<< " (" << __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"
<< "presented by fgprc members:\n"
<< " - http://fgprc.org\n"
<< " - http://fgprc.org.cn\n"
<< "\n"
<< "input <nasal -h> to get help .\n\n";
return out;
}
std::ostream& version(std::ostream& out) {
std::srand(static_cast<u32>(std::time(nullptr)));
f64 num = 0;
for(u32 i = 0; i<5; ++i) {
num = (num+rand())*(1.0/(RAND_MAX+1.0));
}
// give you 5% to see this easter egg
if (num<0.05) {
nasal::parse::easter_egg();
}
out << "nasal version " << __nasver__;
out << " " << nasal::util::get_platform();
out << " " << nasal::util::get_arch();
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
return out;
}
[[noreturn]]
void err() {
std::cerr << "invalid argument(s), use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(const nasal::cli::cli_config& config) {
using option = nasal::cli::option;
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(config.input_file_path).chkerr();
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
if (config.has(option::cli_view_raw_ast)) {
nasal::ast_dumper().dump(parse.tree());
}
// linker gets parser's ast and load import files to this ast
ld.link(parse, config.has(option::cli_detail_info)).chkerr();
if (config.has(option::cli_show_referenced_file)) {
if (ld.get_file_list().size()) {
std::cout << "referenced file(s):\n";
}
for(const auto& file: ld.get_file_list()) {
std::cout << " " << file << "\n";
}
}
// optimizer does simple optimization on ast
auto opt = std::make_unique<nasal::optimizer>();
opt->do_optimization(parse.tree());
if (config.has(option::cli_view_ast)) {
nasal::ast_dumper().dump(parse.tree());
}
// code generator gets parser's ast and import file list to generate code
gen.compile(parse, ld, false, config.has(option::cli_limit_mode)).chkerr();
if (config.has(option::cli_view_code)) {
gen.print(std::cout);
}
if (config.has(option::cli_show_symbol)) {
gen.symbol_dump(std::cout);
}
// run
const auto start = clk::now();
if (config.has(option::cli_debug_mode)) {
auto debugger = std::make_unique<nasal::dbg>();
debugger->run(
gen,
ld,
config.nasal_vm_args,
config.has(option::cli_profile),
config.has(option::cli_profile_all)
);
} else if (config.has(option::cli_show_execute_time) ||
config.has(option::cli_detail_info) ||
config.has(option::cli_limit_mode) ||
config.has(option::cli_execute)) {
auto runtime = std::make_unique<nasal::vm>();
runtime->set_detail_report_info(config.has(option::cli_detail_info));
runtime->set_limit_mode_flag(config.has(option::cli_limit_mode));
runtime->run(gen, ld, config.nasal_vm_args);
}
// get running time
const auto end = clk::now();
if (config.has(option::cli_show_execute_time)) {
std::clog << "process exited after ";
std::clog << static_cast<f64>((end-start).count())/den << "s.\n\n";
}
}
i32 main(i32 argc, const char* argv[]) {
// output version info
if (argc<=1) {
std::clog << logo;
return 0;
}
// the first argument is the executable itself, ignore it
const auto config = nasal::cli::parse({argv+1, argv+argc});
// run directly or show help
if (argc==2) {
if (config.has(nasal::cli::option::cli_help)) {
std::clog << nasal::cli::help;
} else if (config.has(nasal::cli::option::cli_version)) {
std::clog << version;
} else if (config.has(nasal::cli::option::cli_repl_mode)) {
auto repl = std::make_unique<nasal::repl::repl>();
repl->execute();
} else if (config.input_file_path.size()) {
execute(config);
} else {
err();
}
return 0;
}
// execute with arguments
execute(config);
return 0;
}

21
src/nasal.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#ifndef __nasver__
#define __nasver__ "11.2"
#endif
#include <cstddef>
#include <cstdint>
// 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;
// virtual machine stack depth, both global depth and value stack depth
const u32 VM_STACK_DEPTH = UINT16_MAX;

386
src/nasal_ast.cpp Normal file
View File

@@ -0,0 +1,386 @@
#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);
}
void null_access::accept(ast_visitor* visitor) {
visitor->visit_null_access(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);
}
}

709
src/nasal_ast.h Normal file
View File

@@ -0,0 +1,709 @@
#pragma once
#include "nasal.h"
#include "nasal_err.h"
#include <vector>
#include <unordered_map>
namespace nasal {
enum class expr_type {
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_null_access, // 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(u64 line, u64 column) {
nd_loc.begin_line = line;
nd_loc.begin_column = column;
}
const auto& get_location() const { return nd_loc; }
const auto get_line() const { return nd_loc.begin_line; }
auto 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 kind {
normal_parameter,
default_parameter,
dynamic_parameter
};
private:
kind 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(kind pt) {type = pt;}
void set_parameter_name(const std::string& pname) {name = pname;}
void set_default_value(expr* node) {default_value = node;}
auto get_parameter_type() {return type;}
const auto& get_parameter_name() const {return name;}
auto 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 kind {
add,
sub,
mult,
div,
concat,
cmpeq,
cmpneq,
less,
leq,
grt,
geq,
bitwise_or,
bitwise_xor,
bitwise_and,
condition_and,
condition_or,
null_chain
};
private:
kind 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(kind 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;}
auto get_operator_type() const {return type;}
auto get_left() {return left;}
auto get_right() {return right;}
auto get_optimized_number() {return optimized_const_number;}
auto get_optimized_string() {return optimized_const_string;}
void accept(ast_visitor*) override;
};
class unary_operator: public expr {
public:
enum class kind {
negative,
logical_not,
bitwise_not
};
private:
kind 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(kind operator_type) {type = operator_type;}
void set_value(expr* node) {value = node;}
void set_optimized_number(number_literal* node) {optimized_number = node;}
auto get_operator_type() const {return type;}
auto get_value() {return value;}
auto 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 null_access: public call {
private:
std::string field;
public:
null_access(const span& location, const std::string& name):
call(location, expr_type::ast_null_access),
field(name) {}
~null_access() 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 kind {
equal,
add_equal,
sub_equal,
mult_equal,
div_equal,
concat_equal,
bitwise_and_equal,
bitwise_or_equal,
bitwise_xor_equal
};
private:
kind 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(kind operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
auto get_assignment_type() const {return type;}
auto get_left() {return left;}
auto 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 kind {
foreach,
forindex
};
private:
kind type;
iter_expr* iterator;
expr* vector_node;
code_block* block;
public:
forei_expr(const span& location):
expr(location, expr_type::ast_forei),
type(kind::foreach), iterator(nullptr),
vector_node(nullptr), block(nullptr) {}
~forei_expr() override;
void set_loop_type(kind 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;}
auto get_loop_type() const {return type;}
auto get_iterator() {return iterator;}
auto get_value() {return vector_node;}
auto 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;
};
}

1494
src/nasal_codegen.cpp Normal file

File diff suppressed because it is too large Load Diff

173
src/nasal_codegen.h Normal file
View File

@@ -0,0 +1,173 @@
#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 "natives/builtin.h"
#include "natives/coroutine.h"
#include "natives/bits_lib.h"
#include "natives/math_lib.h"
#include "natives/fg_props.h"
#include "natives/io_lib.h"
#include "natives/json_lib.h"
#include "natives/dylib_lib.h"
#include "natives/regex_lib.h"
#include "natives/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 flag_need_repl_output = false;
// limit mode flag
bool flag_limited_mode = false;
// under limited mode, unsafe system api will be banned
const std::unordered_set<std::string> unsafe_system_api = {
// builtin
"__system", "__input",
// io
"__fout", "__open", "__write", "__stat"
// bits
"__fld", "__sfld", "__setfld",
"__buf",
// fg
"__logprint",
// dylib
"__dlopen", "__dlclose", "__dlcallv", "__dlcall",
// unix
"__pipe", "__fork", "__waitpid", "__chdir",
"__environ", "__getcwd", "__getenv"
};
// 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, u64> const_number_map;
std::unordered_map<std::string, u64> 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<u64>> continue_ptr;
std::list<std::vector<u64>> break_ptr;
// symbol table
// global : max VM_STACK_DEPTH-1 values
std::unordered_map<std::string, u64> global;
// nasal namespace
// stores all global symbols of each file
std::unordered_map<std::string, std::unordered_set<std::string>> nasal_namespace;
// local : max 32768 upvalues 65536 values
// but in fact local scope also has less than VM_STACK_DEPTH value
std::list<std::unordered_map<std::string, u64>> 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&);
i64 local_symbol_find(const std::string&);
i64 global_symbol_find(const std::string&);
i64 upvalue_symbol_find(const std::string&);
void emit(u8, u64, 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 null_access_gen(null_access*);
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(u64, u64);
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 null_chain_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;}
public:
codegen() = default;
const error& compile(parse&, linker&, bool, bool);
void print(std::ostream&);
void symbol_dump(std::ostream&) const;
};
}

296
src/nasal_dbg.cpp Normal file
View File

@@ -0,0 +1,296 @@
#include "nasal_dbg.h"
namespace nasal {
void operand_line_counter::init_counter() {
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
operand_counter[i] = 0;
}
}
void operand_line_counter::load_file_line_counter(
const std::vector<std::string>& file_list) {
file_name_list = file_list;
file_line_counter = {};
file_contents = {};
filestream 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 operand_line_counter::init(const std::vector<std::string>& file_list) {
init_counter();
load_file_line_counter(file_list);
}
void operand_line_counter::dump_operand_count() const {
typedef std::pair<u32, u64> op_count;
std::vector<op_count> opcall;
u64 total = 0;
for(usize i = 0; i<operand_line_counter::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 << " ";
std::clog << operand_name_table.at(static_cast<op_code_type>(i.first));
std::clog << " : " << i.second << " (" << rate << "%)\n";
}
std::clog << " total : " << total << '\n';
}
void operand_line_counter::dump_all_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 operand_line_counter::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<file_list_size; ++i) {
if (filename==files[i]) {
return i;
}
}
return UINT16_MAX;
}
void dbg::err() const {
std::cerr
<< "incorrect command\n"
<< "input \'h\' to get help\n";
}
void dbg::help() const {
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<file_list_size; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n";
}
}
void dbg::step_info() {
u64 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u64 begin = (line>>3)==0? 0:((line>>3)<<3);
u64 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog << "\nsource code:\n";
for(u64 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(u64 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(16);
}
void dbg::interact() {
// special operand, end execution
if (bytecode[ctx.pc].op==op_exit) {
return;
}
// do not need interact while doing profiling
if (do_operand_count) {
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 cmd_kind::cmd_help: help(); break;
case cmd_kind::cmd_backtrace:
function_call_trace();
trace_back();
break;
case cmd_kind::cmd_continue: return;
case cmd_kind::cmd_list_file: list_file(); break;
case cmd_kind::cmd_global: global_state(); break;
case cmd_kind::cmd_local: local_state(); break;
case cmd_kind::cmd_upval: upvalue_state(); break;
case cmd_kind::cmd_register: register_info(); break;
case cmd_kind::cmd_show_all: all_state_detail(); break;
case cmd_kind::cmd_next: next = true; return;
case cmd_kind::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==cmd_kind::cmd_break_point) {
break_file_index = file_index(res[1]);
if (break_file_index==UINT16_MAX) {
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_operand_count = profile || show_all_prof_result;
const auto& file_list = linker.get_file_list();
file_list_size = file_list.size();
vm_init_enrty(
gen.strs(),
gen.nums(),
gen.natives(),
gen.codes(),
gen.globals(),
file_list,
argv
);
counter.init(file_list);
std::vector<u8> code;
std::vector<u16> code_file_index;
std::vector<u64> code_line;
for(const 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();
counter.add_operand_counter(code[ctx.pc]);
counter.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;
}
counter.dump_operand_count();
if (do_operand_count) {
show_all_prof_result?
counter.dump_all_code_line_counter(std::clog):
counter.dump_this_file_line_counter(std::clog);
}
ngc.info();
ngc.clear();
imm.clear();
return;
}
}

123
src/nasal_dbg.h Normal file
View File

@@ -0,0 +1,123 @@
#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 {
// count detail operand calling
// and show them before each line of the source file
class operand_line_counter {
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_operand_count() const;
void dump_all_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:
enum class cmd_kind {
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, cmd_kind> command_table = {
{"h", cmd_kind::cmd_help},
{"help", cmd_kind::cmd_help},
{"bt", cmd_kind::cmd_backtrace},
{"backtrace", cmd_kind::cmd_backtrace},
{"c", cmd_kind::cmd_continue},
{"continue", cmd_kind::cmd_continue},
{"f", cmd_kind::cmd_list_file},
{"file", cmd_kind::cmd_list_file},
{"g", cmd_kind::cmd_global},
{"global", cmd_kind::cmd_global},
{"l", cmd_kind::cmd_local},
{"local", cmd_kind::cmd_local},
{"u", cmd_kind::cmd_upval},
{"upval", cmd_kind::cmd_upval},
{"r", cmd_kind::cmd_register},
{"register", cmd_kind::cmd_register},
{"a", cmd_kind::cmd_show_all},
{"all", cmd_kind::cmd_show_all},
{"n", cmd_kind::cmd_next},
{"next", cmd_kind::cmd_next},
{"bk", cmd_kind::cmd_break_point},
{"break", cmd_kind::cmd_break_point},
{"q", cmd_kind::cmd_exit},
{"exit", cmd_kind::cmd_exit}
};
cmd_kind get_cmd_type(const std::string& cmd) const {
return command_table.count(cmd)?
command_table.at(cmd):cmd_kind::cmd_error;
}
private:
bool next;
usize file_list_size;
u16 break_file_index;
u64 break_line;
error src;
private:
operand_line_counter counter;
bool do_operand_count;
private:
std::vector<std::string> parse(const std::string&);
u16 file_index(const std::string&) const;
void err() const;
void help() const;
void list_file() const;
void step_info();
void interact();
public:
dbg(): next(true), file_list_size(0),
break_file_index(0), break_line(0),
do_operand_count(false) {}
void run(const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool);
};
}

199
src/nasal_err.cpp Normal file
View File

@@ -0,0 +1,199 @@
#include "nasal_err.h"
#include "repl/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 filestream::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";
std::cerr << cyan << " --> " << red;
loc.dump_begin(std::cerr);
std::cerr << reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen);
for(u64 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(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u64 i = loc.begin_column; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
}
} else if (line==loc.begin_line) {
for(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u64 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(u64 i = 0; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
} else {
for(u64 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";
}
}

75
src/nasal_err.h Normal file
View File

@@ -0,0 +1,75 @@
#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 {
u64 begin_line;
u64 begin_column;
u64 end_line;
u64 end_column;
std::string file;
void dump_begin(std::ostream& out) const {
out << file << ":" << begin_line << ":" << begin_column + 1;
}
};
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 filestream {
protected:
std::string file;
std::vector<std::string> res;
public:
filestream(): 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 filestream {
private:
u32 cnt; // counter for errors
std::string identation(usize len) {
return std::string(len, ' ');
}
std::string leftpad(u64 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);
}
}
auto geterr() const { return cnt; }
};
}

443
src/nasal_gc.cpp Normal file
View File

@@ -0,0 +1,443 @@
#include "nasal_gc.h"
#include "util/util.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);
// concurrent mark, experimental
if (memory.size()>UINT16_MAX && bfs.size()>32) {
flag_concurrent_mark_triggered = true;
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;
}
// normal mark
while(!bfs.empty()) {
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_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_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_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_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_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_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_type::vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_type::vm_hash: mark_hash(bfs_queue, value.hash()); break;
case vm_type::vm_func: mark_func(bfs_queue, value.func()); break;
case vm_type::vm_upval: mark_upval(bfs_queue, value.upval()); break;
case vm_type::vm_ghost: mark_ghost(bfs_queue, value.ghost()); break;
case vm_type::vm_co: mark_co(bfs_queue, value.co()); break;
case vm_type::vm_map: mark_map(bfs_queue, value.map()); break;
default: break;
}
}
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i : vec.elems) {
if (i.type>vm_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_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_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_type::vm_num) {
bfs_queue.push_back(i);
}
}
}
void gc::mark_ghost(std::vector<var>& bfs_queue, nas_ghost& ghost) {
if (!ghost.gc_mark_function) {
return;
}
ghost.gc_mark_function(ghost.pointer, &bfs_queue);
}
void gc::mark_co(std::vector<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_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_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[static_cast<u8>(i->type)-static_cast<u8>(vm_type::vm_str)].push_back(i);
i->mark = nas_val::gc_status::collected;
} else if (i->mark==nas_val::gc_status::found) {
i->mark = nas_val::gc_status::uncollected;
}
}
}
void gc::extend(const vm_type type) {
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str);
size[index] += incr[index];
for(u64 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);
}
// if incr[index] = 1, this will always be 1
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(u64 i = 0; i<strs.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode
if (strs[i].is_str() && strs[i].str()==constant_strings[i]) {
continue;
}
strs[i] = var::gcobj(new nas_val(vm_type::vm_str));
strs[i].val.gcobj->immutable = 1;
strs[i].str() = constant_strings[i];
}
// record arguments
env_argv.resize(argv.size());
for(u64 i = 0; i<argv.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode
if (env_argv[i].is_str() && env_argv[i].str()==argv[i]) {
continue;
}
env_argv[i] = var::gcobj(new nas_val(vm_type::vm_str));
env_argv[i].val.gcobj->immutable = 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 {
util::windows_code_page_manager wm;
wm.set_utf8_output();
using std::left;
using std::setw;
using std::setfill;
using std::setprecision;
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",
"ghost",
"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 += "";
}
const auto first_line = "" + indent_string + "" +
indent_string + "" +
indent_string + "" +
indent_string + "";
const auto mid_line = "" + indent_string + "" +
indent_string + "" +
indent_string + "" +
indent_string + "";
const auto another_mid_line = "" + indent_string + "" +
indent_string + "" +
indent_string + "" +
indent_string + "";
const auto last_line = "" + indent_string + "" +
indent_string + "" +
indent_string + "" +
indent_string + "";
std::clog << "\n" << first_line << "\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" << mid_line << "\n";
double total = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
if (!gcnt[i] && !acnt[i] && !size[i]) {
continue;
}
total += static_cast<f64>(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 << mid_line << "\n";
const 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" << another_mid_line << "\n";
const auto gc_time = worktime*1.0/den*1000;
std::clog << "" << left << setw(indent) << setfill(' ') << "gc time";
std::clog << "" << setw(indent-3) << setprecision(4) << gc_time << " ms";
std::clog << setw(indent*2+7) << " " << "\n";
const auto avg_time = worktime*1.0/den*1000/total;
std::clog << "" << left << setw(indent) << setfill(' ') << "avg time";
std::clog << "" << setw(indent-3) << setprecision(4) << avg_time << " ms";
std::clog << setw(indent*2+7) << " " << "\n";
const auto max_gc = max_time*1.0/den*1000;
std::clog << "" << left << setw(indent) << setfill(' ') << "max gc";
std::clog << "" << setw(indent-3) << setprecision(4) << max_gc << " ms";
std::clog << setw(indent*2+7) << " " << "\n";
const auto max_mark = max_mark_time*1.0/den*1000;
std::clog << "" << left << setw(indent) << setfill(' ') << "max mark";
std::clog << "" << setw(indent-3) << setprecision(4) << max_mark << " ms";
std::clog << setw(indent*2+7) << " " << "\n";
const auto max_sweep = max_sweep_time*1.0/den*1000;
std::clog << "" << left << setw(indent) << setfill(' ') << "max sweep";
std::clog << "" << setw(indent-3) << setprecision(4) << max_sweep << " ms";
std::clog << setw(indent*2+7) << " " << "\n";
std::clog << "" << left << setw(indent) << setfill(' ') << "concurrent";
std::clog << "" << setw(indent)
<< (flag_concurrent_mark_triggered? "true":"false");
std::clog << setw(indent*2+7) << " " << "\n";
std::clog << last_line << "\n";
wm.restore_code_page();
}
var gc::alloc(const vm_type type) {
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_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;
}
}

138
src/nasal_gc.h Normal file
View File

@@ -0,0 +1,138 @@
#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 <iostream>
#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 */
u64 incr[gc_type_size] = {
128, // vm_str
128, // vm_vec
64, // vm_hash
256, // 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;
bool flag_concurrent_mark_triggered = false;
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_ghost(std::vector<var>&, nas_ghost&);
void mark_co(std::vector<var>&, nas_co&);
void mark_map(std::vector<var>&, nas_map&);
void sweep();
public:
void extend(const vm_type);
void init(const std::vector<std::string>&, const std::vector<std::string>&);
void clear();
void info() const;
var alloc(const vm_type);
void context_change(nas_co*);
void context_reserve();
public:
var newstr(char c) {
var s = alloc(vm_type::vm_str);
s.str() = c;
return s;
}
var newstr(const char* buff) {
var s = alloc(vm_type::vm_str);
s.str() = std::string(buff);
return s;
}
var newstr(const std::string& buff) {
var s = alloc(vm_type::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)();
// avoid error loading function bug in MSVC version nasal.exe
#ifdef _MSC_VER
// and fuck MSVC again
#define NASAL_EXTERN extern "C" __declspec(dllexport)
#else
#define NASAL_EXTERN extern "C"
#endif
}

421
src/nasal_import.cpp Normal file
View File

@@ -0,0 +1,421 @@
#include "nasal_import.h"
#include "symbol_finder.h"
#include "util/util.h"
#include "util/fs.h"
#include <memory>
#include <unordered_set>
namespace nasal {
linker::linker(): show_path_flag(false), this_file("") {
const auto seperator = util::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 += (util::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<fs::path> path_list = {filename};
// generate search path from environ path
for(const auto& p : envpath) {
path_list.push_back(fs::path(p)/filename);
}
// search file
for(const auto& path : path_list) {
if (fs::exists(path)) {
return path.str();
}
}
// we will find lib.nas in nasal std directory
if (filename=="lib.nas") {
return util::is_windows()?
find_real_file_path("std\\lib.nas", location):
find_real_file_path("std/lib.nas", location);
}
if (!show_path_flag) {
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename + ">, " +
"use <-d> to get detail search path"
);
return "";
}
auto path_list_info = std::string("");
for(const auto& path : path_list) {
path_list_info += " -> " + path.str() + "\n";
}
err.err("link",
"in <" + location.file + ">: " +
"cannot find file <" + filename +
"> in these paths:\n" + path_list_info
);
return "";
}
bool linker::import_check(expr* node) {
if (node->get_type()==expr_type::ast_use) {
return true;
}
/*
call
|_id:import
|_call_func
|_string:'filename'
*/
if (node->get_type()!=expr_type::ast_call) {
return false;
}
auto call_node = reinterpret_cast<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::merge_tree(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",
node->get_location(),
"self-referenced module <" + filename + ">, " +
"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);
// avoid stack overflow
if (module_load_stack.size()>MAX_RECURSION_DEPTH) {
err.err("link",
node->get_location(),
"too deep module import stack (>" +
std::to_string(MAX_RECURSION_DEPTH) + ")."
);
return new code_block({0, 0, 0, 0, filename});
}
// start importing...
lexer nasal_lexer;
parse nasal_parser;
if (nasal_lexer.scan(filename).geterr()) {
err.err("link",
node->get_location(),
"error occurred when analysing <" + filename + ">"
);
return new code_block({0, 0, 0, 0, filename});
}
if (nasal_parser.compile(nasal_lexer).geterr()) {
err.err("link",
node->get_location(),
"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'
load(parse_result, filename);
module_load_stack.pop_back();
return parse_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)
load(parse_result, path);
return parse_result;
}
std::string linker::generate_module_name(const std::string& file_path) {
// import("...") may trigger this error module name
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::make_unique<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) {
// generate ast node like this:
// var {module_name} = (func() {
// ... # module itself
// })();
auto def = new definition_expr(block->get_location());
def->set_identifier(new identifier(
block->get_location(),
generate_module_name(block->get_location().file)
));
// (func() {...})();
auto call = new call_expr(block->get_location());
// func() {...}
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;
}
void linker::load(code_block* program_root, const std::string& filename) {
// 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);
// avoid repeatedly importing the same module in one file
const auto& module_path = module_code_block->get_location().file;
if (used_modules.count(module_path)) {
delete module_code_block;
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;
continue;
}
used_modules.insert(module_path);
delete import_node;
// 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
import_node = generate_module_definition(module_code_block);
}
}
const error& linker::link(parse& parse, bool spath = false) {
// switch for showing path when errors occur
show_path_flag = spath;
// initializing file map
this_file = parse.tree()->get_location().file;
imported_files = {this_file};
module_load_stack = {this_file};
// scan root and import files
// then generate a new ast and return to import_ast
// dfs load file
auto library = import_nasal_lib();
// load used modules of this file
load(parse.tree(), this_file);
// then insert the whole tree into library tree root
merge_tree(library, parse.tree());
// swap tree root, and delete old root
delete parse.swap(library);
if (imported_files.size()>=UINT16_MAX) {
err.err("link",
"too many imported files: " +
std::to_string(imported_files.size())
);
}
return err;
}
}

56
src/nasal_import.h Normal file
View File

@@ -0,0 +1,56 @@
#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
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
#include "symbol_finder.h"
#include "util/fs.h"
#include <cstring>
#include <sstream>
#include <vector>
#include <unordered_set>
namespace nasal {
class linker {
private:
const u32 MAX_RECURSION_DEPTH = 256;
bool show_path_flag;
std::string this_file;
error err;
std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack;
std::vector<fs::path> 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 merge_tree(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*);
void load(code_block*, const std::string&);
public:
linker();
const error& link(parse&, bool);
const auto& get_file_list() const {return imported_files;}
};
}

458
src/nasal_lexer.cpp Normal file
View File

@@ -0,0 +1,458 @@
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include "nasal_lexer.h"
#include "repl/repl.h"
#include "util/util.h"
#include "util/fs.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_quesmark(char c) {
return c=='?';
}
bool lexer::is_single_opr(char c) {
return (
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" + util::char_to_hex(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;
}
if (file.empty()) {
err.err("lexer", "empty input file");
err.chkerr();
}
// check file exsits and it is a regular file
if (!fs::is_regular(file)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename = file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <" + file + ">");
res = "";
return;
}
err.load(file);
std::stringstream ss;
ss << in.rdbuf();
res = ss.str();
}
tok lexer::get_type(const std::string& str) {
// search token type from mapper
// if cannot find, just return null
return token_mapper.count(str)? token_mapper.at(str):tok::tk_null;
}
std::string lexer::utf8_gen() {
std::string str = "";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp = "";
u32 nbytes = util::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" + util::char_to_hex(tmp[0]);
for(u32 i = 1; i<tmp.size(); ++i) {
utf_info += " 0x" + util::char_to_hex(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() {
u64 begin_line = line;
u64 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::tk_null)? type:tok::tk_id,
str
};
}
token lexer::num_gen() {
u64 begin_line = line;
u64 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::tk_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::tk_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::tk_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::tk_num,
"0"
};
}
}
column += str.length();
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
str
};
}
token lexer::str_gen() {
u64 begin_line = line;
u64 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::tk_str,
str
};
}
++column;
// if is not utf8, 1+utf8_hdchk should be 1
if (begin=='`' && str.length()!=1+util::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::tk_str,
str
};
}
token lexer::quesmark_gen() {
u64 begin_line = line;
u64 begin_column = column;
std::string str(1, res[ptr]);
++column;
++ptr;
if (ptr < res.size() && (res[ptr]=='?' || res[ptr]=='.')) {
str += res[ptr];
++column;
++ptr;
}
return {
{begin_line, begin_column, line, column, filename},
get_type(str),
str
};
}
token lexer::single_opr() {
u64 begin_line = line;
u64 begin_column = column;
std::string str(1, res[ptr]);
++column;
tok type = get_type(str);
if (type==tok::tk_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() {
u64 begin_line = line;
u64 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() {
u64 begin_line = line;
u64 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_quesmark(res[ptr])) {
toks.push_back(quesmark_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::tk_eof, "<eof>"});
} else {
// if token sequence is empty, generate a default location
toks.push_back({
{line, column, line, column, filename},
tok::tk_eof,
"<eof>"
});
}
res = "";
return err;
}
}

198
src/nasal_lexer.h Normal file
View File

@@ -0,0 +1,198 @@
#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 "nasal.h"
#include "nasal_err.h"
namespace nasal {
enum class tok {
tk_null = 0, // null token (default token type)
tk_num, // number literal
tk_str, // string literal
tk_id, // identifier
tk_true, // keyword true
tk_false, // keyword false
tk_use, // keyword use
tk_for, // loop keyword for
tk_forindex, // loop keyword forindex
tk_foreach, // loop keyword foreach
tk_while, // loop keyword while
tk_var, // keyword for definition
tk_func, // keyword for definition of function
tk_brk, // loop keyword break
tk_cont, // loop keyword continue
tk_ret, // function keyword return
tk_if, // condition expression keyword if
tk_elsif, // condition expression keyword elsif
tk_else, // condition expression keyword else
tk_nil, // nil literal
tk_lcurve, // (
tk_rcurve, // )
tk_lbracket, // [
tk_rbracket, // ]
tk_lbrace, // {
tk_rbrace, // }
tk_semi, // ;
tk_and, // operator and
tk_or, // operator or
tk_comma, // ,
tk_dot, // .
tk_ellipsis, // ...
tk_quesmark, // ?
tk_quesques, // ??
tk_quesdot, // ?.
tk_colon, // :
tk_add, // operator +
tk_sub, // operator -
tk_mult, // operator *
tk_div, // operator /
tk_floater, // operator ~ and binary operator ~
tk_btand, // bitwise operator &
tk_btor, // bitwise operator |
tk_btxor, // bitwise operator ^
tk_not, // operator !
tk_eq, // operator =
tk_addeq, // operator +=
tk_subeq, // operator -=
tk_multeq, // operator *=
tk_diveq, // operator /=
tk_lnkeq, // operator ~=
tk_btandeq, // operator &=
tk_btoreq, // operator |=
tk_btxoreq, // operator ^=
tk_cmpeq, // operator ==
tk_neq, // operator !=
tk_less, // operator <
tk_leq, // operator <=
tk_grt, // operator >
tk_geq, // operator >=
tk_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:
u64 line;
u64 column;
usize ptr;
std::string filename;
std::string res;
private:
error err;
u64 invalid_char;
std::vector<token> toks;
private:
const std::unordered_map<std::string, tok> token_mapper = {
{"use" , tok::tk_use },
{"true" , tok::tk_true },
{"false" , tok::tk_false },
{"for" , tok::tk_for },
{"forindex", tok::tk_forindex},
{"foreach" , tok::tk_foreach },
{"while" , tok::tk_while },
{"var" , tok::tk_var },
{"func" , tok::tk_func },
{"break" , tok::tk_brk },
{"continue", tok::tk_cont },
{"return" , tok::tk_ret },
{"if" , tok::tk_if },
{"elsif" , tok::tk_elsif },
{"else" , tok::tk_else },
{"nil" , tok::tk_nil },
{"(" , tok::tk_lcurve },
{")" , tok::tk_rcurve },
{"[" , tok::tk_lbracket},
{"]" , tok::tk_rbracket},
{"{" , tok::tk_lbrace },
{"}" , tok::tk_rbrace },
{";" , tok::tk_semi },
{"and" , tok::tk_and },
{"or" , tok::tk_or },
{"," , tok::tk_comma },
{"." , tok::tk_dot },
{"..." , tok::tk_ellipsis},
{"?" , tok::tk_quesmark},
{"??" , tok::tk_quesques},
{"?." , tok::tk_quesdot },
{":" , tok::tk_colon },
{"+" , tok::tk_add },
{"-" , tok::tk_sub },
{"*" , tok::tk_mult },
{"/" , tok::tk_div },
{"~" , tok::tk_floater },
{"&" , tok::tk_btand },
{"|" , tok::tk_btor },
{"^" , tok::tk_btxor },
{"!" , tok::tk_not },
{"=" , tok::tk_eq },
{"+=" , tok::tk_addeq },
{"-=" , tok::tk_subeq },
{"*=" , tok::tk_multeq },
{"/=" , tok::tk_diveq },
{"~=" , tok::tk_lnkeq },
{"&=" , tok::tk_btandeq },
{"|=" , tok::tk_btoreq },
{"^=" , tok::tk_btxoreq },
{"==" , tok::tk_cmpeq },
{"!=" , tok::tk_neq },
{"<" , tok::tk_less },
{"<=" , tok::tk_leq },
{">" , tok::tk_grt },
{">=" , tok::tk_geq }
};
private:
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_quesmark(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 quesmark_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 auto& result() const {return toks;}
};
}

140
src/nasal_opcode.cpp Normal file
View File

@@ -0,0 +1,140 @@
#include "nasal_opcode.h"
#include "util/util.h"
namespace nasal {
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;
const auto op = code.op;
const auto num = code.num;
// dump operand index and bytecode(hex format)
out << hex << "0x"
<< setw(8) << setfill('0') << index << " "
<< setw(2) << setfill('0') << static_cast<u32>(op) << ":" << dec;
// dump immediate number(hex format)
for(i32 i = 64-8; i>=0; i -= 8) {
out << hex << setw(2) << setfill('0') << ((num>>i)&0xff) << dec << " ";
}
// dump operand name
out << " " << operand_name_table.at(static_cast<op_code_type>(op)) << " ";
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;
out << " (" << const_number[num] << ")";
break;
case op_lnkeqc:
out << hex << "0x" << num << dec;
out << " (" << util::rawstr(const_string[num], 16) << ")";
break;
case op_addecp:
case op_subecp:
case op_mulecp:
case op_divecp:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ") sp-1";
break;
case op_lnkecp:
out << hex << "0x" << num << dec;
out << " (" << util::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;
out << " (" << 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;
out << " (" << util::rawstr(const_string[num], 16) << ")";
break;
default:
if (files) {
out << hex << "0x" << num << dec;
}
break;
}
// if file list is loaded, dump file location info
if (files) {
out << " (" << files[code.fidx] << ":" << code.line << ")";
}
}
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
ins.dump(out);
return out;
}
}

222
src/nasal_opcode.h Normal file
View File

@@ -0,0 +1,222 @@
#pragma once
#include "nasal.h"
#include "natives/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_dup, // copy value on stack top
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
};
static std::unordered_map<op_code_type, std::string> operand_name_table = {
{op_code_type::op_exit, "exit "},
{op_code_type::op_repl, "repl "},
{op_code_type::op_intl, "intl "},
{op_code_type::op_loadg, "loadg "},
{op_code_type::op_loadl, "loadl "},
{op_code_type::op_loadu, "loadu "},
{op_code_type::op_dup, "dup "},
{op_code_type::op_pnum, "pnum "},
{op_code_type::op_pnil, "pnil "},
{op_code_type::op_pstr, "pstr "},
{op_code_type::op_newv, "newv "},
{op_code_type::op_newh, "newh "},
{op_code_type::op_newf, "newf "},
{op_code_type::op_happ, "happ "},
{op_code_type::op_para, "para "},
{op_code_type::op_deft, "def "},
{op_code_type::op_dyn, "dyn "},
{op_code_type::op_lnot, "lnot "},
{op_code_type::op_usub, "usub "},
{op_code_type::op_bnot, "bitnot"},
{op_code_type::op_btor, "bitor "},
{op_code_type::op_btxor, "bitxor"},
{op_code_type::op_btand, "bitand"},
{op_code_type::op_add, "add "},
{op_code_type::op_sub, "sub "},
{op_code_type::op_mul, "mult "},
{op_code_type::op_div, "div "},
{op_code_type::op_lnk, "lnk "},
{op_code_type::op_addc, "addc "},
{op_code_type::op_subc, "subc "},
{op_code_type::op_mulc, "multc "},
{op_code_type::op_divc, "divc "},
{op_code_type::op_lnkc, "lnkc "},
{op_code_type::op_addeq, "addeq "},
{op_code_type::op_subeq, "subeq "},
{op_code_type::op_muleq, "muleq "},
{op_code_type::op_diveq, "diveq "},
{op_code_type::op_lnkeq, "lnkeq "},
{op_code_type::op_btandeq, "bandeq"},
{op_code_type::op_btoreq, "boreq "},
{op_code_type::op_btxoreq, "bxoreq"},
{op_code_type::op_addeqc, "addeqc"},
{op_code_type::op_subeqc, "subeqc"},
{op_code_type::op_muleqc, "muleqc"},
{op_code_type::op_diveqc, "diveqc"},
{op_code_type::op_lnkeqc, "lnkeqc"},
{op_code_type::op_addecp, "addecp"},
{op_code_type::op_subecp, "subecp"},
{op_code_type::op_mulecp, "mulecp"},
{op_code_type::op_divecp, "divecp"},
{op_code_type::op_lnkecp, "lnkecp"},
{op_code_type::op_meq, "meq "},
{op_code_type::op_eq, "eq "},
{op_code_type::op_neq, "neq "},
{op_code_type::op_less, "less "},
{op_code_type::op_leq, "leq "},
{op_code_type::op_grt, "grt "},
{op_code_type::op_geq, "geq "},
{op_code_type::op_lessc, "lessc "},
{op_code_type::op_leqc, "leqc "},
{op_code_type::op_grtc, "grtc "},
{op_code_type::op_geqc, "geqc "},
{op_code_type::op_pop, "pop "},
{op_code_type::op_jmp, "jmp "},
{op_code_type::op_jt, "jt "},
{op_code_type::op_jf, "jf "},
{op_code_type::op_cnt, "cnt "},
{op_code_type::op_findex, "findx "},
{op_code_type::op_feach, "feach "},
{op_code_type::op_callg, "callg "},
{op_code_type::op_calll, "calll "},
{op_code_type::op_upval, "upval "},
{op_code_type::op_callv, "callv "},
{op_code_type::op_callvi, "callvi"},
{op_code_type::op_callh, "callh "},
{op_code_type::op_callfv, "callfv"},
{op_code_type::op_callfh, "callfh"},
{op_code_type::op_callb, "callb "},
{op_code_type::op_slcbeg, "slcbeg"},
{op_code_type::op_slcend, "slcend"},
{op_code_type::op_slc, "slice "},
{op_code_type::op_slc2, "slice2"},
{op_code_type::op_mcallg, "mcallg"},
{op_code_type::op_mcalll, "mcalll"},
{op_code_type::op_mupval, "mupval"},
{op_code_type::op_mcallv, "mcallv"},
{op_code_type::op_mcallh, "mcallh"},
{op_code_type::op_ret, "ret "}
};
struct opcode {
u8 op; // opcode
u16 fidx; // source code file index
u64 num; // immediate num
u64 line; // location line of source code
opcode() = default;
opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default;
};
class codestream {
private:
opcode code;
const u64 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 u64 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&);
}

1145
src/nasal_parse.cpp Normal file

File diff suppressed because it is too large Load Diff

168
src/nasal_parse.h Normal file
View File

@@ -0,0 +1,168 @@
#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:
u64 ptr;
u64 in_func_depth; // count function block
u64 in_loop_depth; // count loop block
const token* toks;
code_block* root;
error err;
private:
const std::unordered_map<tok, std::string> token_name_mapper = {
{tok::tk_true , "true" },
{tok::tk_false , "false" },
{tok::tk_use , "use" },
{tok::tk_for , "for" },
{tok::tk_forindex, "forindex"},
{tok::tk_foreach , "foreach" },
{tok::tk_while , "while" },
{tok::tk_var , "var" },
{tok::tk_func , "func" },
{tok::tk_brk , "break" },
{tok::tk_cont , "continue"},
{tok::tk_ret , "return" },
{tok::tk_if , "if" },
{tok::tk_elsif , "elsif" },
{tok::tk_else , "else" },
{tok::tk_nil , "nil" },
{tok::tk_lcurve , "(" },
{tok::tk_rcurve , ")" },
{tok::tk_lbracket, "[" },
{tok::tk_rbracket, "]" },
{tok::tk_lbrace , "{" },
{tok::tk_rbrace , "}" },
{tok::tk_semi , ";" },
{tok::tk_and , "and" },
{tok::tk_or , "or" },
{tok::tk_comma , "," },
{tok::tk_dot , "." },
{tok::tk_ellipsis, "..." },
{tok::tk_quesmark, "?" },
{tok::tk_quesques, "??" },
{tok::tk_quesdot , "?." },
{tok::tk_colon , ":" },
{tok::tk_add , "+" },
{tok::tk_sub , "-" },
{tok::tk_mult , "*" },
{tok::tk_div , "/" },
{tok::tk_floater , "~" },
{tok::tk_btand , "&" },
{tok::tk_btor , "|" },
{tok::tk_btxor , "^" },
{tok::tk_not , "!" },
{tok::tk_eq , "=" },
{tok::tk_addeq , "+=" },
{tok::tk_subeq , "-=" },
{tok::tk_multeq , "*=" },
{tok::tk_diveq , "/=" },
{tok::tk_lnkeq , "~=" },
{tok::tk_btandeq , "&=" },
{tok::tk_btoreq , "|=" },
{tok::tk_btxoreq , "^=" },
{tok::tk_cmpeq , "==" },
{tok::tk_neq , "!=" },
{tok::tk_less , "<" },
{tok::tk_leq , "<=" },
{tok::tk_grt , ">" },
{tok::tk_geq , ">=" }
};
private:
void die(const span&, const 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* null_chain_expr();
expr* additive_expr();
expr* multive_expr();
unary_operator* unary();
expr* scalar();
call* call_scalar();
call_hash* callh();
null_access* null_access_call();
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_depth(0),
in_loop_depth(0), toks(nullptr),
root(nullptr) {}
~parse() {delete root;}
const error& compile(const lexer&);
static void easter_egg();
};
}

392
src/nasal_type.cpp Normal file
View File

@@ -0,0 +1,392 @@
#include "nasal_type.h"
#include "util/util.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();
}
auto ret = var::none();
auto& val = elems.at("parents");
if (!val.is_vec()) {
return ret;
}
for(auto& i : val.vec().elems) {
if (i.is_hash()) {
ret = i.hash().get_value(key);
}
if (!ret.is_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.is_vec()) {
return addr;
}
for(auto& i : val.vec().elems) {
// recursively search key in `parents`
if (i.is_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;
}
std::ostream& operator<<(std::ostream& out, nas_func& func) {
out << "func(";
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;
}
for(const auto& key : argument_list) {
out << key;
if (key != argument_list.back()) {
out << ", ";
}
}
if (func.dynamic_parameter_index>=0) {
out << (argument_list.size()? ", ":"");
out << func.dynamic_parameter_name << "...";
}
out << ") {..}";
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,
marker gc_marker_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
destructor_function = destructor_pointer;
gc_mark_function = gc_marker_pointer;
pointer = ghost_pointer;
}
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;
gc_mark_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<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = var::nil();
}
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.canary = ctx.stack+VM_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(vm_type val_type) {
mark = gc_status::collected;
type = val_type;
immutable = 0;
switch(val_type) {
case vm_type::vm_str: ptr.str = new std::string; break;
case vm_type::vm_vec: ptr.vec = new nas_vec; break;
case vm_type::vm_hash: ptr.hash = new nas_hash; break;
case vm_type::vm_func: ptr.func = new nas_func; break;
case vm_type::vm_upval: ptr.upval = new nas_upval; break;
case vm_type::vm_ghost: ptr.obj = new nas_ghost; break;
case vm_type::vm_co: ptr.co = new nas_co; break;
case vm_type::vm_map: ptr.map = new nas_map; break;
default: break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_type::vm_str: delete ptr.str; break;
case vm_type::vm_vec: delete ptr.vec; break;
case vm_type::vm_hash: delete ptr.hash; break;
case vm_type::vm_func: delete ptr.func; break;
case vm_type::vm_upval: delete ptr.upval; break;
case vm_type::vm_ghost: delete ptr.obj; break;
case vm_type::vm_co: delete ptr.co; break;
case vm_type::vm_map: delete ptr.map; break;
default: break;
}
type = vm_type::vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_type::vm_str: ptr.str->clear(); break;
case vm_type::vm_vec: ptr.vec->elems.clear(); break;
case vm_type::vm_hash: ptr.hash->elems.clear(); break;
case vm_type::vm_func: ptr.func->clear(); break;
case vm_type::vm_upval: ptr.upval->clear(); break;
case vm_type::vm_ghost: ptr.obj->clear(); break;
case vm_type::vm_co: ptr.co->clear(); break;
case vm_type::vm_map: ptr.map->clear(); break;
default: break;
}
}
f64 var::to_num() {
return type!=vm_type::vm_str? val.num:util::str_to_num(str().c_str());
}
std::string var::to_str() {
if (type==vm_type::vm_str) {
return str();
} else if (type==vm_type::vm_num) {
auto 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;
}
std::stringstream ss;
ss << *this;
return ss.str();
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_type::vm_none: out << "undefined"; break;
case vm_type::vm_nil: out << "nil"; break;
case vm_type::vm_num: out << ref.val.num; break;
case vm_type::vm_str: out << ref.str(); break;
case vm_type::vm_vec: out << ref.vec(); break;
case vm_type::vm_hash: out << ref.hash(); break;
case vm_type::vm_func: out << ref.func(); break;
case vm_type::vm_ghost: out << ref.ghost(); break;
case vm_type::vm_co: out << ref.co(); break;
case vm_type::vm_map: out << ref.map(); break;
default: break;
}
return out;
}
bool var::object_check(const std::string& name) {
return is_ghost() && ghost().type_name==name && ghost().pointer;
}
var var::none() {
return {vm_type::vm_none, static_cast<u64>(0)};
}
var var::nil() {
return {vm_type::vm_nil, static_cast<u64>(0)};
}
var var::ret(u64 pc) {
return {vm_type::vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_type::vm_cnt, n};
}
var var::num(f64 n) {
return {vm_type::vm_num, n};
}
var var::gcobj(nas_val* p) {
return {p->type, p};
}
var var::addr(var* p) {
return {vm_type::vm_addr, p};
}
var* var::addr() {
return val.addr;
}
u64 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();
}
}

302
src/nasal_type.h Normal file
View File

@@ -0,0 +1,302 @@
#pragma once
#include "nasal.h"
#include <cstring>
#include <sstream>
#include <iostream>
#include <vector>
#include <unordered_map>
namespace nasal {
enum class 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_ghost, // 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 =
static_cast<u32>(vm_type::vm_type_size_max) -
static_cast<u32>(vm_type::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:
vm_type type = vm_type::vm_none;
union {
u64 ret;
i64 cnt;
f64 num;
var* addr;
nas_val* gcobj;
} val;
private:
var(vm_type t, u64 pc) {type = t; val.ret = pc;}
var(vm_type t, i64 ct) {type = t; val.cnt = ct;}
var(vm_type t, f64 n) {type = t; val.num = n;}
var(vm_type t, var* p) {type = t; val.addr = p;}
var(vm_type t, nas_val* p) {type = t; val.gcobj = p;}
public:
var() = default;
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&);
public:
// create new var object
static var none();
static var nil();
static var ret(u64);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
public:
// get value
var* addr();
u64 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();
public:
bool is_none() const { return type==vm_type::vm_none; }
bool is_cnt() const { return type==vm_type::vm_cnt; }
bool is_addr() const { return type==vm_type::vm_addr; }
bool is_ret() const { return type==vm_type::vm_ret; }
bool is_nil() const { return type==vm_type::vm_nil; }
bool is_num() const { return type==vm_type::vm_num; }
bool is_str() const { return type==vm_type::vm_str; }
bool is_vec() const { return type==vm_type::vm_vec; }
bool is_hash() const { return type==vm_type::vm_hash; }
bool is_func() const { return type==vm_type::vm_func; }
bool is_upval() const { return type==vm_type::vm_upval; }
bool is_ghost() const { return type==vm_type::vm_ghost; }
bool is_coroutine() const { return type==vm_type::vm_co; }
bool is_map() const { return type==vm_type::vm_map; }
};
struct nas_vec {
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 {
i64 dynamic_parameter_index; // dynamic parameter name index in hash.
u64 entry; // pc will set to entry-1 to call this function
u32 parameter_size; // used to load default parameters to a new function
u64 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;
// dynamic parameter name
std::string dynamic_parameter_name;
nas_func():
dynamic_parameter_index(-1), entry(0),
parameter_size(0), local_size(0),
dynamic_parameter_name("") {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool on_stack;
u64 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*);
using marker = void (*)(void*, std::vector<var>*);
public:
std::string type_name;
destructor destructor_function;
marker gc_mark_function;
void* pointer;
public:
nas_ghost():
type_name(""), destructor_function(nullptr),
gc_mark_function(nullptr), pointer(nullptr) {}
~nas_ghost() { clear(); }
void set(const std::string&, destructor, marker, void*);
void clear();
public:
const auto& get_ghost_name() const { return type_name; }
};
struct context {
u64 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[VM_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;
vm_type type; // value type
u8 immutable; // used to mark if a string is immutable
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(vm_type);
~nas_val();
void clear();
};
std::ostream& operator<<(std::ostream&, nas_vec&);
std::ostream& operator<<(std::ostream&, nas_hash&);
std::ostream& operator<<(std::ostream&, nas_func&);
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&);
}

658
src/nasal_vm.cpp Normal file
View File

@@ -0,0 +1,658 @@
#include "nasal_vm.h"
#include "util/util.h"
namespace nasal {
void vm::vm_init_enrty(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, u64>& 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_type::vm_map);
global[global_symbol.at("globals")] = map_instance;
for(const auto& i : global_symbol) {
map_instance.map().mapper[i.first] = global+i.second;
}
/* init vm arg */
auto arg_instance = ngc.alloc(vm_type::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[VM_STACK_DEPTH-1] */
ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
/* nothing is on stack */
ctx.top = ctx.stack - 1;
/* clear main stack and global */
for(u32 i = 0; i<VM_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_type::vm_none: std::clog << "| null |"; break;
case vm_type::vm_ret: std::clog << "| pc | 0x" << std::hex
<< val.ret() << std::dec; break;
case vm_type::vm_addr: std::clog << "| addr | 0x" << std::hex
<< reinterpret_cast<u64>(val.addr())
<< std::dec; break;
case vm_type::vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
case vm_type::vm_nil: std::clog << "| nil |"; break;
case vm_type::vm_num: std::clog << "| num | " << val.num(); break;
case vm_type::vm_str: std::clog << "| str | <0x" << std::hex << p
<< "> \"" << util::rawstr(val.str(), 16)
<< "\"" << std::dec; break;
case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p
<< std::dec << "> " << val.func();
break;
case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p
<< std::dec << "> [" << val.upval().size
<< " val]"; break;
case vm_type::vm_vec: std::clog << "| vec | <0x" << std::hex << p
<< std::dec << "> [" << val.vec().size()
<< " val]"; break;
case vm_type::vm_hash: std::clog << "| hash | <0x" << std::hex << p
<< std::dec << "> {" << val.hash().size()
<< " val}"; break;
case vm_type::vm_ghost:std::clog << "| obj | <0x" << std::hex << p
<< "> obj:0x"
<< reinterpret_cast<u64>(val.ghost().pointer)
<< std::dec; break;
case vm_type::vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
case vm_type::vm_map: std::clog << "| nmspc| <0x" << std::hex << p
<< std::dec << "> namespace ["
<< val.map().mapper.size()
<< " val]"; break;
default: std::clog << "| err | <0x" << std::hex << p
<< std::dec << "> unknown object"; break;
}
std::clog << "\n";
}
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 << func.dynamic_parameter_name << "...";
}
std::clog << ") ";
const auto& code = bytecode[func.entry];
std::clog << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
}
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->is_func() && i-1>=bottom && (i-1)->is_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->is_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(8) << 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 u64 limit = 16) {
var* top = ctx.top;
var* bottom = ctx.stack;
const auto stack_address = reinterpret_cast<u64>(bottom);
std::clog << "\nvm stack (0x" << std::hex << stack_address << std::dec;
std::clog << ", 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(8) << 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::clog << 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].is_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(8)
<< 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(8)
<< std::setfill('0') << i << std::dec
<< " ";
value_info(ctx.localr[i]);
}
}
void vm::upvalue_state() {
if (ctx.funcr.is_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(8)
<< 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;
const auto& code = bytecode[func.entry];
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
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)].is_none()) {
result += key + ", ";
} else {
result += key + "[get], ";
}
}
result = result.substr(0, result.length()-2);
result += ") ";
std::stringstream out;
const auto& code = bytecode[func.entry];
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
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_type::vm_none: return "none";
case vm_type::vm_cnt: return "counter";
case vm_type::vm_addr: return "address";
case vm_type::vm_ret: return "program counter";
case vm_type::vm_nil: return "nil";
case vm_type::vm_num: return "number";
case vm_type::vm_str: return "string";
case vm_type::vm_vec: return "vector";
case vm_type::vm_hash: return "hash";
case vm_type::vm_func: return "function";
case vm_type::vm_upval: return "upvalue";
case vm_type::vm_ghost: return "ghost type";
case vm_type::vm_co: return "coroutine";
case vm_type::vm_map: return "namespace";
default: break;
}
return "unknown";
}
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) {
vm_init_enrty(
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,
&&dup,
&&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(const auto& i : gen.codes()) {
code.push_back(oprs[i.op]);
imm.push_back(i.num);
}
// goto the first operand
goto *code[ctx.pc];
#else
std::vector<nasal_vm_func> code;
for(const auto& i : gen.codes()) {
code.push_back(operand_function[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
// all nasal programs should end here
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
dup: exec_check(o_dup ); // +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
}
}

1172
src/nasal_vm.h Normal file

File diff suppressed because it is too large Load Diff

164
src/natives/bits_lib.cpp Normal file
View File

@@ -0,0 +1,164 @@
#include "natives/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.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::fld", "\"str\" must be mutable string");
}
if (!startbit.is_num() || !length.is_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.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::sfld", "\"str\" must be mutable string");
}
if (!startbit.is_num() || !length.is_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.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::setfld", "\"str\" must be mutable string");
}
if (!startbit.is_num() || !length.is_num() || !value.is_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.is_num() || length.num()<=0) {
return nas_err("bits::buf", "\"len\" must be number greater than 0");
}
var str = ngc->alloc(vm_type::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/natives/bits_lib.h Normal file
View File

@@ -0,0 +1,21 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/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[];
}

785
src/natives/builtin.cpp Normal file
View File

@@ -0,0 +1,785 @@
#include "natives/builtin.h"
#include "util/util.h"
#include <chrono>
#ifdef _WIN32
#include <windows.h>
#endif
namespace nasal {
var builtin_unsafe(context* ctx, gc* ngc) {
return nas_err(
"unsafe_redirect",
"you are using unsafe system api under limited mode!"
);
}
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.is_vec()) {
return nas_err("native::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.is_vec()) {
return nas_err("native::setsize", "\"vec\" must be vector");
}
if (!size.is_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.is_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_type::vm_str);
if (!end.is_str() || end.str().length()>1 || !end.str().length()) {
std::cin >> ret.str();
} else {
std::getline(std::cin, ret.str(), end.str()[0]);
}
return ret;
}
var builtin_split(context* ctx, gc* ngc) {
auto local = ctx->localr;
var delimeter = local[1];
var str = local[2];
if (!delimeter.is_str()) {
return nas_err("native::split", "\"separator\" must be string");
}
if (!str.is_str()) {
return nas_err("native::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_type::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.is_num() && !val.is_nil()) {
return nas_err("native::rand", "\"seed\" must be nil or number");
}
if (val.is_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_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.is_num() && !val.is_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.is_num()) {
return val;
}
if (!val.is_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.is_vec()) {
return nas_err("native::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];
usize num = 0;
switch(val.type) {
case vm_type::vm_num: return val;
case vm_type::vm_str: num = val.str().length(); break;
case vm_type::vm_vec: num = val.vec().size(); break;
case vm_type::vm_hash: num = val.hash().size(); break;
case vm_type::vm_map: num = val.map().mapper.size(); break;
default: break;
}
return var::num(static_cast<f64>(num));
}
var builtin_time(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (!val.is_num()) {
return nas_err("native::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.is_hash() || !key.is_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.is_hash()) {
return nas_err("native::delete", "\"hash\" must be hash");
}
if (!key.is_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.is_hash() && !hash.is_map()) {
return nas_err("native::keys", "\"hash\" must be hash");
}
// avoid being sweeped
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
auto& vec = res.vec().elems;
if (hash.is_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("native::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_type::vm_none: return ngc->newstr("undefined");
case vm_type::vm_nil: return ngc->newstr("nil");
case vm_type::vm_num: return ngc->newstr("num");
case vm_type::vm_str: return ngc->newstr("str");
case vm_type::vm_vec: return ngc->newstr("vec");
case vm_type::vm_hash: return ngc->newstr("hash");
case vm_type::vm_func: return ngc->newstr("func");
case vm_type::vm_ghost: return ngc->newstr("ghost");
case vm_type::vm_co: return ngc->newstr("coroutine");
case vm_type::vm_map: return ngc->newstr("namespace");
default: break;
}
return nil;
}
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.is_str()) {
return nas_err("native::substr", "\"str\" must be string");
}
if (!beg.is_num() || beg.num()<0) {
return nas_err("native::substr", "\"begin\" should be number >= 0");
}
if (!len.is_num() || len.num()<0) {
return nas_err("native::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("native::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.is_str() || !b.is_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.is_str()) {
return nas_err("native::left", "\"string\" must be string");
}
if (!len.is_num()) {
return nas_err("native::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.is_str()) {
return nas_err("native::right", "\"string\" must be string");
}
if (!len.is_num()) {
return nas_err("native::right", "\"length\" must be number");
}
i32 length = static_cast<i32>(len.num());
i32 srclen = static_cast<i32>(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.is_str() || !b.is_str()) {
return nas_err("native::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.is_hash() && !hash.is_map()) {
return nas_err("native::values", "\"hash\" must be hash or namespace");
}
auto vec = ngc->alloc(vm_type::vm_vec);
auto& v = vec.vec().elems;
if (hash.is_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.is_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) {
return ngc->newstr(util::get_platform());
}
var builtin_arch(context* ctx, gc* ngc) {
return ngc->newstr(util::get_arch());
}
// 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.is_str()) {
return nas_err("native::md5", "\"str\" must be string");
}
return ngc->newstr(md5(str.str()));
}
class time_stamp {
private:
std::chrono::high_resolution_clock::time_point stamp;
public:
time_stamp() {
stamp = std::chrono::high_resolution_clock::now();
}
void make_stamp() {
stamp = std::chrono::high_resolution_clock::now();
}
auto elapsed_milliseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
}
auto elapsed_microseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
}
};
void time_stamp_destructor(void* ptr) {
delete static_cast<time_stamp*>(ptr);
}
var builtin_maketimestamp(context* ctx, gc* ngc) {
auto res = ngc->alloc(vm_type::vm_ghost);
res.ghost().set(
"nasal-time-stamp",
time_stamp_destructor,
nullptr,
new time_stamp
);
return res;
}
var builtin_time_stamp(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return nil;
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
stamp->make_stamp();
return nil;
}
var builtin_elapsed_millisecond(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return var::num(-1);
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(static_cast<f64>(stamp->elapsed_milliseconds()));
}
var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return var::num(-1);
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(static_cast<f64>(stamp->elapsed_microseconds()));
}
var builtin_gcextend(context* ctx, gc* ngc) {
auto type = ctx->localr[1];
if (!type.is_str()) {
return nil;
}
const auto& s = type.str();
if (s=="str") {
ngc->extend(vm_type::vm_str);
} else if (s=="vec") {
ngc->extend(vm_type::vm_vec);
} else if (s=="hash") {
ngc->extend(vm_type::vm_hash);
} else if (s=="func") {
ngc->extend(vm_type::vm_func);
} else if (s=="upval") {
ngc->extend(vm_type::vm_upval);
} else if (s=="ghost") {
ngc->extend(vm_type::vm_ghost);
} else if (s=="co") {
ngc->extend(vm_type::vm_co);
}
return nil;
}
var builtin_gcinfo(context* ctx, gc* ngc) {
const auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc->alloc(vm_type::vm_hash);
f64 total = 0;
for(u32 i = 0; i<gc_type_size; ++i) {
total += static_cast<f64>(ngc->gcnt[i]);
}
auto& map = res.hash().elems;
const auto worktime = static_cast<f64>(ngc->worktime);
const auto max_time = static_cast<f64>(ngc->max_time);
const auto max_mark_time = static_cast<f64>(ngc->max_mark_time);
const auto max_sweep_time = static_cast<f64>(ngc->max_sweep_time);
// using ms
map["total"] = var::num(worktime/den*1000);
map["average"] = var::num(worktime/den*1000/total);
map["max_gc"] = var::num(max_time/den*1000);
map["max_mark"] = var::num(max_mark_time/den*1000);
map["max_sweep"] = var::num(max_sweep_time/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.is_ghost()) {
return nas_err("native::ghosttype", "this is not a ghost object.");
}
const auto& name = arg.ghost().get_ghost_name();
// https://wiki.flightgear.org/Nasal_library#ghosttype()
// tolds us if no name has been set,
// return a unique id (the pointer to the instance)
if (!name.length()) {
std::stringstream ss;
ss << "0x" << std::hex;
ss << reinterpret_cast<u64>(arg.ghost().pointer) << std::dec;
return ngc->newstr(ss.str());
}
return ngc->newstr(name);
}
var builtin_set_utf8_output(context* ctx, gc* ngc) {
#ifdef _WIN32
// allow 65001 code page
SetConsoleOutputCP(CP_UTF8);
#endif
// do nothing on other platforms
return nil;
}
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},
{"__maketimestamp", builtin_maketimestamp},
{"__time_stamp", builtin_time_stamp},
{"__elapsed_millisecond", builtin_elapsed_millisecond},
{"__elapsed_microsecond", builtin_elapsed_microsecond},
{"__gcextd", builtin_gcextend},
{"__gcinfo", builtin_gcinfo},
{"__logtime", builtin_logtime},
{"__ghosttype", builtin_ghosttype},
{"__set_utf8_output", builtin_set_utf8_output},
{nullptr, nullptr}
};
}

96
src/natives/builtin.h Normal file
View File

@@ -0,0 +1,96 @@
#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_unsafe(context*, gc*);
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_maketimestamp(context*, gc*);
var builtin_time_stamp(context*, gc*);
var builtin_elapsed_millisecond(context*, gc*);
var builtin_elapsed_microsecond(context*, gc*);
var builtin_gcextend(context*, gc*);
var builtin_gcinfo(context*, gc*);
var builtin_logtime(context*, gc*);
var builtin_ghosttype(context*, gc*);
// only useful on windows platform
var builtin_set_utf8_output(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[];
}

141
src/natives/coroutine.cpp Normal file
View File

@@ -0,0 +1,141 @@
#include "natives/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.is_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_type::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.is_coroutine() ||
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].is_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.is_coroutine()) {
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/natives/coroutine.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/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[];
}

142
src/natives/dylib_lib.cpp Normal file
View File

@@ -0,0 +1,142 @@
#include "natives/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.is_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_type::vm_hash);
auto library_object = ngc->alloc(vm_type::vm_ghost);
library_object.ghost().set(
dynamic_library_type_name,
dynamic_library_destructor,
nullptr,
dynamic_library_pointer
);
return_hash.hash().elems["lib"] = library_object;
// 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_type::vm_ghost);
function_object.ghost().set(
function_address_type_name,
nullptr,
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/natives/dylib_lib.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/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/natives/fg_props.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "natives/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.is_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/natives/fg_props.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/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[];
}

249
src/natives/io_lib.cpp Normal file
View File

@@ -0,0 +1,249 @@
#include "natives/io_lib.h"
#include "util/fs.h"
#include <fstream>
#include <sys/stat.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.is_str()) {
return nas_err("io::readfile", "\"filename\" must be string");
}
std::ifstream in(filename.str(), std::ios::binary);
std::stringstream rd;
if (!in.fail()) {
rd << in.rdbuf();
}
return ngc->newstr(rd.str());
}
var builtin_fout(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto filename = local[1];
auto source = local[2];
if (!filename.is_str()) {
return nas_err("io::fout", "\"filename\" must be string");
}
std::ofstream out(filename.str());
if (out.fail()) {
return nas_err("io::fout", "cannot open <" + filename.str() + ">");
}
out << source;
return nil;
}
var builtin_exists(context* ctx, gc* ngc) {
auto filename = ctx->localr[1];
if (!filename.is_str()) {
return zero;
}
return fs::exists(filename.str())? one:zero;
}
var builtin_open(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto name = local[1];
auto mode = local[2];
if (!name.is_str()) {
return nas_err("io::open", "\"filename\" must be string");
}
if (!mode.is_str()) {
return nas_err("io::open", "\"mode\" must be string");
}
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
if (!file_descriptor) {
return nas_err("io::open", "failed to open file <" + name.str() + ">");
}
var return_object = ngc->alloc(vm_type::vm_ghost);
return_object.ghost().set(
file_type_name, filehandle_destructor, nullptr, file_descriptor
);
return return_object;
}
var builtin_close(context* ctx, gc* ngc) {
var file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::close", "not a valid filehandle");
}
file_descriptor.ghost().clear();
return nil;
}
var builtin_read(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto file_descriptor = local[1];
auto buffer = local[2];
auto length = local[3];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::read", "not a valid filehandle");
}
if (!buffer.is_str() || buffer.val.gcobj->immutable) {
return nas_err("io::read", "\"buf\" must be mutable string");
}
if (!length.is_num()) {
return nas_err("io::read", "\"len\" must be number");
}
if (length.num()<=0 || length.num()>=(1<<30)) {
return nas_err("io::read", "\"len\" less than 1 or too large");
}
auto temp_buffer = new char[static_cast<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->immutable = true;
delete []temp_buffer;
return var::num(read_size);
}
var builtin_write(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto file_descriptor = local[1];
auto source = local[2];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::write", "not a valid filehandle");
}
if (!source.is_str()) {
return nas_err("io::write", "\"str\" must be string");
}
return var::num(static_cast<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_type::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.is_str()) {
return nas_err("io::stat", "\"filename\" must be string");
}
struct stat buffer;
if (stat(name.str().c_str(), &buffer)<0) {
return nas_err("io::stat", "failed to open file <" + name.str() + ">");
}
auto result = ngc->alloc(vm_type::vm_vec);
result.vec().elems = {
var::num(static_cast<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_type::vm_ghost);
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdin);
return file_descriptor;
}
var builtin_stdout(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_type::vm_ghost);
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stdout);
return file_descriptor;
}
var builtin_stderr(context* ctx, gc* ngc) {
auto file_descriptor = ngc->alloc(vm_type::vm_ghost);
file_descriptor.ghost().set(file_type_name, nullptr, nullptr, stderr);
return file_descriptor;
}
nasal_builtin_table io_lib_native[] = {
{"__readfile", builtin_readfile},
{"__fout", builtin_fout},
{"__exists", builtin_exists},
{"__open", builtin_open},
{"__close", builtin_close},
{"__read", builtin_read},
{"__write", builtin_write},
{"__seek", builtin_seek},
{"__tell", builtin_tell},
{"__readln", builtin_readln},
{"__stat", builtin_stat},
{"__eof", builtin_eof},
{"__stdin", builtin_stdin},
{"__stdout", builtin_stdout},
{"__stderr", builtin_stderr},
{nullptr, nullptr}
};
}

35
src/natives/io_lib.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/builtin.h"
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <io.h>
#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[];
}

406
src/natives/json_lib.cpp Normal file
View File

@@ -0,0 +1,406 @@
#include "natives/json_lib.h"
#include "util/util.h"
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
namespace nasal {
enum class json_token_type {
tok_eof,
tok_lbrace,
tok_rbrace,
tok_lbrkt,
tok_rbrkt,
tok_comma,
tok_colon,
tok_str,
tok_num,
tok_id,
tok_bool
};
std::string get_content(json_token_type type) {
switch(type) {
case json_token_type::tok_eof: return "eof";
case json_token_type::tok_lbrace: return "`{`";
case json_token_type::tok_rbrace: return "`}`";
case json_token_type::tok_lbrkt: return "`[`";
case json_token_type::tok_rbrkt: return "`]`";
case json_token_type::tok_comma: return "`,`";
case json_token_type::tok_colon: return "`:`";
case json_token_type::tok_str: return "string";
case json_token_type::tok_num: return "number";
case json_token_type::tok_id: return "identifier";
case json_token_type::tok_bool: return "boolean";
}
// unreachable
return "";
}
struct token {
json_token_type type;
std::string content;
};
class json {
private:
std::string text = "";
usize line = 1;
usize ptr = 0;
token this_token;
var temp_stack = nil;
std::string info = "";
private:
std::string var_generate(var&);
std::string vector_generate(nas_vec&);
std::string hash_generate(nas_hash&);
private:
bool is_num(char c) {
return std::isdigit(c);
}
bool is_id(char c) {
return std::isalpha(c) || c=='_';
}
bool check(char c) {
return c=='{' || c=='}' || c=='[' || c==']' ||
c==':' || c==',' || c=='"' || c=='\'' ||
c=='-' || c=='+' || is_num(c) || is_id(c);
}
void next();
void match(json_token_type);
void vector_member(nas_vec&, gc*);
var vector_object_generate(gc*);
void hash_member(nas_hash&, gc*);
var hash_object_generate(gc*);
void check_eof();
std::string& error_info() {
return info;
}
public:
std::string stringify(var&);
var parse(const std::string&, gc*);
const std::string& get_error() { return error_info(); }
};
std::string json::var_generate(var& value) {
switch(value.type) {
case vm_type::vm_num: {
std::stringstream out;
out << value.num();
if (std::isnan(value.num())) {
error_info() += "json::stringify: cannot generate number nan\n";
}
if (std::isinf(value.num())) {
error_info() += "json::stringify: cannot generate number inf\n";
}
return out.str();
}
case vm_type::vm_str: return "\"" + value.str() + "\"";
case vm_type::vm_vec: return vector_generate(value.vec());
case vm_type::vm_hash: return hash_generate(value.hash());
case vm_type::vm_func:
error_info() += "json::stringify: cannot generate function\n"; break;
case vm_type::vm_ghost:
error_info() += "json::stringify: cannot generate ghost type\n"; break;
case vm_type::vm_map:
error_info() += "json::stringify: cannot generate namespace type\n"; break;
default: break;
}
return "\"undefined\"";
}
std::string json::vector_generate(nas_vec& vect) {
// avoid stackoverflow
if (vect.printed) {
error_info() += "json::stringify: get vector containing itself\n";
return "undefined";
}
vect.printed = true;
std::string out = "[";
for(auto& i : vect.elems) {
out += var_generate(i) + ",";
}
if (out.back()==',') {
out.pop_back();
}
out += "]";
vect.printed = false;
return out;
}
std::string json::hash_generate(nas_hash& hash) {
// avoid stackoverflow
if (hash.printed) {
error_info() += "json::stringify: get hash containing itself\n";
return "undefined";
}
hash.printed = true;
std::string out = "{";
for(auto& i : hash.elems) {
out += "\"" + i.first + "\":";
out += var_generate(i.second) + ",";
}
if (out.back()==',') {
out.pop_back();
}
out += "}";
hash.printed = false;
return out;
}
std::string json::stringify(var& object) {
error_info() = "";
if (object.is_vec()) {
return vector_generate(object.vec());
} else if (object.is_hash()) {
return hash_generate(object.hash());
}
return "[]";
}
void json::next() {
while(ptr<text.length() && !check(text[ptr])) {
if (text[ptr]=='\n') {
++line;
} else if (text[ptr]!=' ' && text[ptr]!='\t' && text[ptr]!='\r') {
error_info() += "json::parse: line " + std::to_string(line);
error_info() += ": invalid character `0x";
error_info() += util::char_to_hex(text[ptr]);
error_info() += "`\n";
}
++ptr;
}
if (ptr>=text.length()) {
this_token = {json_token_type::tok_eof, "eof"};
return;
}
auto c = text[ptr];
switch(c) {
case '{': this_token = {json_token_type::tok_lbrace, "{"}; ++ptr; return;
case '}': this_token = {json_token_type::tok_rbrace, "}"}; ++ptr; return;
case '[': this_token = {json_token_type::tok_lbrkt, "["}; ++ptr; return;
case ']': this_token = {json_token_type::tok_rbrkt, "]"}; ++ptr; return;
case ',': this_token = {json_token_type::tok_comma, ","}; ++ptr; return;
case ':': this_token = {json_token_type::tok_colon, ":"}; ++ptr; return;
default: break;
}
if (is_num(c) || c=='-' || c=='+') {
auto temp = std::string(1, c);
++ptr;
while(ptr<text.length() && (
is_num(text[ptr]) ||
text[ptr]=='.' ||
text[ptr]=='e' ||
text[ptr]=='-' ||
text[ptr]=='+')) {
temp += text[ptr];
++ptr;
}
--ptr;
this_token = {json_token_type::tok_num, temp};
} else if (is_id(c)) {
auto temp = std::string(1, c);
++ptr;
while(ptr<text.length() && (is_id(text[ptr]) || is_num(text[ptr]))) {
temp += text[ptr];
++ptr;
}
--ptr;
if (temp=="true" || temp=="false") {
this_token = {json_token_type::tok_bool, temp};
} else {
this_token = {json_token_type::tok_id, temp};
}
} else if (c=='"' || c=='\'') {
auto begin = c;
auto temp = std::string("");
++ptr;
while(ptr<text.length() && text[ptr]!=begin) {
temp += text[ptr];
++ptr;
if (text[ptr-1]=='\\' && ptr<text.length()) {
temp += text[ptr];
++ptr;
}
}
this_token = {json_token_type::tok_str, temp};
}
++ptr;
return;
}
void json::match(json_token_type type) {
if (this_token.type!=type) {
error_info() += "json::parse: line " + std::to_string(line);
error_info() += ": expect " + get_content(type) + " but get `";
error_info() += this_token.content + "`.\n";
}
next();
return;
}
void json::vector_member(nas_vec& vec, gc* ngc) {
if (this_token.type==json_token_type::tok_lbrace) {
vec.elems.push_back(hash_object_generate(ngc));
} else if (this_token.type==json_token_type::tok_lbrkt) {
vec.elems.push_back(vector_object_generate(ngc));
} else if (this_token.type==json_token_type::tok_str) {
vec.elems.push_back(ngc->newstr(this_token.content));
next();
} else if (this_token.type==json_token_type::tok_num) {
vec.elems.push_back(var::num(util::str_to_num(this_token.content.c_str())));
next();
}
}
var json::vector_object_generate(gc* ngc) {
auto vect_object = ngc->alloc(vm_type::vm_vec);
temp_stack.vec().elems.push_back(vect_object);
match(json_token_type::tok_lbrkt);
vector_member(vect_object.vec(), ngc);
while(this_token.type==json_token_type::tok_comma) {
match(json_token_type::tok_comma);
vector_member(vect_object.vec(), ngc);
}
match(json_token_type::tok_rbrkt);
temp_stack.vec().elems.pop_back();
return vect_object;
}
void json::hash_member(nas_hash& hash, gc* ngc) {
const auto name = this_token.content;
if (this_token.type==json_token_type::tok_rbrace) {
return;
}
if (this_token.type==json_token_type::tok_str) {
match(json_token_type::tok_str);
} else {
match(json_token_type::tok_id);
}
match(json_token_type::tok_colon);
if (this_token.type==json_token_type::tok_lbrace) {
hash.elems.insert({name, hash_object_generate(ngc)});
} else if (this_token.type==json_token_type::tok_lbrkt) {
hash.elems.insert({name, vector_object_generate(ngc)});
} else if (this_token.type==json_token_type::tok_str ||
this_token.type==json_token_type::tok_bool) {
hash.elems.insert({name, ngc->newstr(this_token.content)});
next();
} else if (this_token.type==json_token_type::tok_num) {
hash.elems.insert({name, var::num(util::str_to_num(this_token.content.c_str()))});
next();
}
}
var json::hash_object_generate(gc* ngc) {
auto hash_object = ngc->alloc(vm_type::vm_hash);
temp_stack.vec().elems.push_back(hash_object);
match(json_token_type::tok_lbrace);
hash_member(hash_object.hash(), ngc);
while(this_token.type==json_token_type::tok_comma) {
match(json_token_type::tok_comma);
hash_member(hash_object.hash(), ngc);
}
match(json_token_type::tok_rbrace);
temp_stack.vec().elems.pop_back();
return hash_object;
}
void json::check_eof() {
next();
if (this_token.type==json_token_type::tok_eof) {
return;
}
while (this_token.type!=json_token_type::tok_eof) {
error_info() += "json::parse: line " + std::to_string(line);
error_info() += ": expect " + get_content(json_token_type::tok_eof);
error_info() += " but get `" + this_token.content + "`.\n";
next();
}
}
var json::parse(const std::string& input, gc* ngc) {
line = 1;
ptr = 0;
this_token = {json_token_type::tok_eof, ""};
error_info() = "";
if (input.empty()) {
error_info() += "json::parse: empty string.\n";
return nil;
}
text = input;
next();
if (this_token.type==json_token_type::tok_lbrkt) {
ngc->temp = temp_stack = ngc->alloc(vm_type::vm_vec);
auto result = vector_object_generate(ngc);
check_eof();
ngc->temp = temp_stack = nil;
return result;
} else {
ngc->temp = temp_stack = ngc->alloc(vm_type::vm_vec);
auto result = hash_object_generate(ngc);
check_eof();
ngc->temp = temp_stack = nil;
return result;
}
return nil;
}
void json_destructor(void* ptr) {
delete static_cast<json*>(ptr);
}
var builtin_json_new(context* ctx, gc* ngc) {
var res = ngc->alloc(vm_type::vm_ghost);
res.ghost().set("nasal::json", json_destructor, nullptr, new json);
return res;
}
var builtin_json_stringify(context* ctx, gc* ngc) {
auto json_object = ctx->localr[1];
auto object = ctx->localr[2];
if (!json_object.object_check("nasal::json")) {
return nas_err("json::stringify", "expect a json object.");
}
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
return ngc->newstr(json_ptr->stringify(object));
}
var builtin_json_parse(context* ctx, gc* ngc) {
auto json_object = ctx->localr[1];
auto input_string = ctx->localr[2];
if (!json_object.object_check("nasal::json")) {
return nas_err("json::parse", "expect a json object.");
}
if (!input_string.is_str()) {
return nas_err("json::parse", "require string as the input.");
}
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
return json_ptr->parse(input_string.str(), ngc);
}
var builtin_json_get_error(context* ctx, gc* ngc) {
auto json_object = ctx->localr[1];
if (!json_object.object_check("nasal::json")) {
return nas_err("json::get_error", "expect a json object.");
}
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
return ngc->newstr(json_ptr->get_error());
}
nasal_builtin_table json_lib_native[] = {
{"_json_new", builtin_json_new},
{"_json_stringify", builtin_json_stringify},
{"_json_parse", builtin_json_parse},
{"_json_get_error", builtin_json_get_error},
{nullptr, nullptr}
};
}

16
src/natives/json_lib.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/builtin.h"
namespace nasal {
var builtin_json_new(context*, gc*);
var builtin_json_stringify(context*, gc*);
var builtin_json_parse(context*, gc*);
var builtin_json_get_error(context*, gc*);
extern nasal_builtin_table json_lib_native[];
}

77
src/natives/math_lib.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "natives/math_lib.h"
namespace nasal {
var builtin_pow(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (!x.is_num() || !y.is_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.is_num()? sin(val.num()):std::nan(""));
}
var builtin_cos(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? cos(val.num()):std::nan(""));
}
var builtin_tan(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? tan(val.num()):std::nan(""));
}
var builtin_exp(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? exp(val.num()):std::nan(""));
}
var builtin_lg(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? log(val.num())/log(10.0):std::nan(""));
}
var builtin_ln(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? log(val.num()):std::nan(""));
}
var builtin_sqrt(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.is_num()? sqrt(val.num()):std::nan(""));
}
var builtin_atan2(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (!x.is_num() || !y.is_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.is_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/natives/math_lib.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/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[];
}

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