Compare commits
637 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8c233d1d4 | ||
|
|
0b179bf5ff | ||
|
|
d108e40e22 | ||
|
|
63b97fc5ea | ||
|
|
d661a6b92d | ||
|
|
5233cef16e | ||
|
|
3874856f2b | ||
|
|
56e3e4353b | ||
|
|
e3e501ab6a | ||
|
|
d14b816fa0 | ||
|
|
63b0112b9d | ||
|
|
982c5750ef | ||
|
|
d69dd0b03f | ||
|
|
7a10392bea | ||
|
|
5e2bb411e5 | ||
|
|
f8ecd2ae53 | ||
|
|
0a404694f6 | ||
|
|
6c8b4c6a87 | ||
|
|
85bc699905 | ||
|
|
38920382ea | ||
|
|
d0d9ce3b80 | ||
|
|
3e01239722 | ||
|
|
b77d7fafb1 | ||
|
|
36a2aa67ef | ||
|
|
c157d8a9b1 | ||
|
|
0c99eb15f0 | ||
|
|
cd4e0c1716 | ||
|
|
d89f290a8a | ||
|
|
298b54c9ec | ||
|
|
ec03f0aee0 | ||
|
|
a0e6047296 | ||
|
|
a801669888 | ||
|
|
ae5c76ecd5 | ||
|
|
93528babdb | ||
|
|
ba629e57d7 | ||
|
|
00449c2bfb | ||
|
|
939b4c4a65 | ||
|
|
6f259dc1ea | ||
|
|
98b0086656 | ||
|
|
88b039a82e | ||
|
|
fc2be49621 | ||
|
|
7d81246695 | ||
|
|
afaef55c68 | ||
|
|
e131c8da4b | ||
|
|
527cb5277b | ||
|
|
d690c779b8 | ||
|
|
52e6adfe28 | ||
|
|
18d59dfa94 | ||
|
|
044ccb4bc6 | ||
|
|
70a8be8e4c | ||
|
|
56a26b6ab6 | ||
|
|
5cc9824a62 | ||
|
|
56762d719d | ||
|
|
29b15b89f7 | ||
|
|
654a37ab88 | ||
|
|
227197996d | ||
|
|
fd7472b737 | ||
|
|
26a62dde9b | ||
|
|
788e0026c2 | ||
|
|
51a0ef6b0c | ||
|
|
31113ff69f | ||
|
|
fe65085a7a | ||
|
|
569d423772 | ||
|
|
25e202fbaf | ||
|
|
d479f13a5c | ||
|
|
d9cf9ff49e | ||
|
|
4b597000ba | ||
|
|
fe3847d69c | ||
|
|
eee30b7d8e | ||
|
|
0c183b5984 | ||
|
|
48d1d8ddef | ||
|
|
ea33a5ed81 | ||
|
|
4a7a2ce11e | ||
|
|
94b6e84693 | ||
|
|
eab84d5c0d | ||
|
|
bce8148f60 | ||
|
|
32e23741eb | ||
|
|
7e72661332 | ||
|
|
e2d8c5c495 | ||
|
|
3509655424 | ||
|
|
fd614a132c | ||
|
|
bff0d78d59 | ||
|
|
b4482792c8 | ||
|
|
b122afd1cd | ||
|
|
eca6141408 | ||
|
|
5c714d26aa | ||
|
|
8ca7ee1d90 | ||
|
|
27bc544bbe | ||
|
|
45f87925ae | ||
|
|
da43ec5193 | ||
|
|
917b4f6568 | ||
|
|
ac2744e24f | ||
|
|
ba6b7cd05c | ||
|
|
a7a2f47d1e | ||
|
|
597c0388cb | ||
|
|
af3ade5a41 | ||
|
|
b6886cc957 | ||
|
|
90ad1a53d7 | ||
|
|
8e4e4bfe89 | ||
|
|
5d9267d3b9 | ||
|
|
15f63210b4 | ||
|
|
e2ec8cb9a0 | ||
|
|
c516c0c3bf | ||
|
|
89f96d407b | ||
|
|
8a10f07ce7 | ||
|
|
fb3d9ed2f3 | ||
|
|
fe40bd7e3f | ||
|
|
9f66960244 | ||
|
|
2ce13e2134 | ||
|
|
0132ca0870 | ||
|
|
ac49c0d676 | ||
|
|
c5d6a6694b | ||
|
|
bcf274aed4 | ||
|
|
78b7bec786 | ||
|
|
7d8f199c8e | ||
|
|
c95810b46c | ||
|
|
ec1985b3cc | ||
|
|
da22dd03c0 | ||
|
|
19480f7f26 | ||
|
|
9bf4eacc62 | ||
|
|
afc8c54487 | ||
|
|
36f6dd6c96 | ||
|
|
63ec070d15 | ||
|
|
7f258dd17e | ||
|
|
dfb0c6ab52 | ||
|
|
d954a3fc5e | ||
|
|
7cc9ef436e | ||
|
|
774ad60c42 | ||
|
|
a69d05233b | ||
|
|
666fb8b25f | ||
|
|
92abad3384 | ||
|
|
f914678311 | ||
|
|
c858afbb76 | ||
|
|
94f7c941f6 | ||
|
|
cd21540710 | ||
|
|
a37ce6aadd | ||
|
|
db47ab4445 | ||
|
|
5519dc7a29 | ||
|
|
3852ce23a3 | ||
|
|
4e17de909a | ||
|
|
73450092f2 | ||
|
|
f8bac92548 | ||
|
|
7cdc5e40af | ||
|
|
bd12fe917a | ||
|
|
8161f3c514 | ||
|
|
dbc2c365b4 | ||
|
|
1678567c5d | ||
|
|
abe2464a67 | ||
|
|
f9cbd0b426 | ||
|
|
99434df819 | ||
|
|
cbee5f8705 | ||
|
|
e11793d340 | ||
|
|
461e5ac647 | ||
|
|
c8eb1f1d16 | ||
|
|
068184f451 | ||
|
|
368b057a91 | ||
|
|
f4deca4427 | ||
|
|
681f04b0d3 | ||
|
|
79c1e27027 | ||
|
|
84f9280cb4 | ||
|
|
e454b315fc | ||
|
|
1259b43389 | ||
|
|
acb2e3664c | ||
|
|
fdd7794a83 | ||
|
|
22b1841664 | ||
|
|
ac1ee4ddef | ||
|
|
aef7936292 | ||
|
|
d03273b4e1 | ||
|
|
8df02d25ad | ||
|
|
268a03864e | ||
|
|
48611c50f7 | ||
|
|
e6e89039b8 | ||
|
|
9d7f799ad3 | ||
|
|
dc6f3347b7 | ||
|
|
d5996d6a1d | ||
|
|
0d1069a724 | ||
|
|
175706126b | ||
|
|
cb547499ac | ||
|
|
de524938cf | ||
|
|
9c6996b54e | ||
|
|
eca978e2b3 | ||
|
|
9455a83df0 | ||
|
|
be318abb2e | ||
|
|
7e91c273f3 | ||
|
|
4c5ffb0240 | ||
|
|
54969681fc | ||
|
|
9196d7815f | ||
|
|
309a57070c | ||
|
|
7e4faed4a6 | ||
|
|
f9179188a5 | ||
|
|
97b3cefe75 | ||
|
|
ca527ec931 | ||
|
|
4fd69c6ce4 | ||
|
|
1ced201cb5 | ||
|
|
cedb5e12f5 | ||
|
|
aaccfbda11 | ||
|
|
0c216e5f16 | ||
|
|
feea901e4d | ||
|
|
b0f080070a | ||
|
|
d121dcd630 | ||
|
|
8aec98a2b3 | ||
|
|
2c36f722bb | ||
|
|
c705b75513 | ||
|
|
3ef8effe9a | ||
|
|
3fd1b25f79 | ||
|
|
025ff49ffc | ||
|
|
7a93527948 | ||
|
|
74b925715f | ||
|
|
b0c5dfada7 | ||
|
|
8ae5cc0071 | ||
|
|
8b1eebb310 | ||
|
|
568d821186 | ||
|
|
5b70449aee | ||
|
|
405175061a | ||
|
|
ae85791f01 | ||
|
|
3cb5d0f2d9 | ||
|
|
a3f5dc01d3 | ||
|
|
35d7772dd3 | ||
|
|
e25eb76e94 | ||
|
|
946aa020a5 | ||
|
|
6ef22d3228 | ||
|
|
c4cac512a6 | ||
|
|
dc3770094a | ||
|
|
791de656c3 | ||
|
|
cff9f91bee | ||
|
|
06f02ec0cb | ||
|
|
24ae1c246f | ||
|
|
0576459fbe | ||
|
|
518ce7fcb9 | ||
|
|
0e682b7c07 | ||
|
|
26f4e1359f | ||
|
|
aa5b1d3d66 | ||
|
|
6a6eab8db5 | ||
|
|
91b3074ce9 | ||
|
|
10e579dabc | ||
|
|
add5e0c2cd | ||
|
|
1e0f0f8e7b | ||
|
|
972ad49a4f | ||
|
|
a13e419518 | ||
|
|
6c04487319 | ||
|
|
5715c1df5f | ||
|
|
aa0023a23b | ||
|
|
f86ea2445f | ||
|
|
52a38709bb | ||
|
|
b022b25cea | ||
|
|
27ceeb517d | ||
|
|
8293f85c5b | ||
|
|
0e578b3e21 | ||
|
|
24ba300f3c | ||
|
|
5be6351b60 | ||
|
|
a91826607c | ||
|
|
987d3ce9e2 | ||
|
|
da8aa4744e | ||
|
|
978957aca7 | ||
|
|
caf048aae4 | ||
|
|
692f8ccefe | ||
|
|
007b83bed5 | ||
|
|
73278ea2dd | ||
|
|
732a00a1cd | ||
|
|
bb0a2e3669 | ||
|
|
2f43c47e77 | ||
|
|
d65868f1e9 | ||
|
|
170f34a5e2 | ||
|
|
0493e18d0e | ||
|
|
7ddb8593ad | ||
|
|
2937c10c77 | ||
|
|
feea8f8d66 | ||
|
|
e51878266a | ||
|
|
37841fc91e | ||
|
|
44bfd74ca9 | ||
|
|
04a45064c8 | ||
|
|
068743aa4c | ||
|
|
7ad1d69c64 | ||
|
|
52fcc9118c | ||
|
|
c0862704f0 | ||
|
|
854850d9b1 | ||
|
|
006ed644e6 | ||
|
|
33e584ab5c | ||
|
|
db896e932f | ||
|
|
3e8b9e4846 | ||
|
|
ea33ce020c | ||
|
|
ac8652f864 | ||
|
|
4c95d622f6 | ||
|
|
61dcfb9395 | ||
|
|
899b327608 | ||
|
|
c702f02a8a | ||
|
|
afbee18e26 | ||
|
|
c6129267aa | ||
|
|
6187395ce8 | ||
|
|
5dcf2ede66 | ||
|
|
99a131c552 | ||
|
|
46516485b5 | ||
|
|
e9fc70bba8 | ||
|
|
8ca0bc52c3 | ||
|
|
af761641d6 | ||
|
|
f68a512845 | ||
|
|
f7cd3a027b | ||
|
|
cc4ff38f28 | ||
|
|
cfbec9a3f1 | ||
|
|
d904123695 | ||
|
|
d8d457ce74 | ||
|
|
b3d65f0183 | ||
|
|
fc7e0a569e | ||
|
|
51f780e637 | ||
|
|
577ecd14df | ||
|
|
f1f48b4881 | ||
|
|
29084143d6 | ||
|
|
d1a7d32e7b | ||
|
|
8cc69c709f | ||
|
|
bcdc55a652 | ||
|
|
2ed20f6362 | ||
|
|
9890b46f02 | ||
|
|
11e9567b55 | ||
|
|
5d4cff0aa8 | ||
|
|
c1d13ecd85 | ||
|
|
824a31ef55 | ||
|
|
fb25a4973c | ||
|
|
75c46fa727 | ||
|
|
bd5044add2 | ||
|
|
b638708722 | ||
|
|
a04ed2a4aa | ||
|
|
82b33ffe4a | ||
|
|
2bc15697df | ||
|
|
92d68b357c | ||
|
|
452bb4a5d8 | ||
|
|
e8bd4664b3 | ||
|
|
5bc1925082 | ||
|
|
8b8fb79013 | ||
|
|
c993c77b78 | ||
|
|
a4a7f5075a | ||
|
|
4c0ef0fb99 | ||
|
|
9114ecd820 | ||
|
|
abbdb22478 | ||
|
|
4f62d2c7ea | ||
|
|
bb84c6d143 | ||
|
|
e27f773749 | ||
|
|
478d1aadf9 | ||
|
|
393b37d109 | ||
|
|
c2a23ecba6 | ||
|
|
ef3afa14b9 | ||
|
|
6b20e0f167 | ||
|
|
dea9c4ad3b | ||
|
|
f59c3c7787 | ||
|
|
0515e388da | ||
|
|
21b345af27 | ||
|
|
04806fc2c5 | ||
|
|
12d7dde42d | ||
|
|
53b85cd459 | ||
|
|
a2e2d5d8f6 | ||
|
|
cb0fee04a9 | ||
|
|
4e6cd82ccb | ||
|
|
c3a8e118f5 | ||
|
|
7ec8418740 | ||
|
|
04ab09586b | ||
|
|
ab37495960 | ||
|
|
882ecac100 | ||
|
|
36a7868b0b | ||
|
|
190014a3e5 | ||
|
|
ebfacd9197 | ||
|
|
99dca532f6 | ||
|
|
dcad554eba | ||
|
|
07eeaadf96 | ||
|
|
120ceb429a | ||
|
|
d567f5abf8 | ||
|
|
f8692f1e4e | ||
|
|
712a047a43 | ||
|
|
396d55a207 | ||
|
|
c5171c735a | ||
|
|
07857d980c | ||
|
|
51a1279110 | ||
|
|
de262980cc | ||
|
|
23a5c1b1ad | ||
|
|
fd8a148d0c | ||
|
|
9c7f5f1a6e | ||
|
|
f049e1f9fb | ||
|
|
c4b7712e53 | ||
|
|
7417d5e635 | ||
|
|
785572634b | ||
|
|
2dc8459cbf | ||
|
|
6e035d1951 | ||
|
|
4f5fd3de33 | ||
|
|
fc25dd69e1 | ||
|
|
ca073499ae | ||
|
|
022460755f | ||
|
|
b6f174e869 | ||
|
|
7b160c4589 | ||
|
|
4503239731 | ||
|
|
c12e812651 | ||
|
|
87cff700e8 | ||
|
|
bf5737ecfd | ||
|
|
651ae4ef77 | ||
|
|
e846e51175 | ||
|
|
399b2f0ce9 | ||
|
|
aed5e27409 | ||
|
|
a2b51fe212 | ||
|
|
92b684624d | ||
|
|
83a8632e8e | ||
|
|
41b5304712 | ||
|
|
c5a12ade5c | ||
|
|
7a939b417d | ||
|
|
dd7740f1fd | ||
|
|
617ad03d33 | ||
|
|
b66ebbef4b | ||
|
|
7f22b25909 | ||
|
|
86f6296268 | ||
|
|
cf722fd98c | ||
|
|
1dd3fd445c | ||
|
|
27e25f84ec | ||
|
|
e6457651d3 | ||
|
|
c4d52a88cd | ||
|
|
9bcad59e45 | ||
|
|
9a099f66cb | ||
|
|
b79d60fab5 | ||
|
|
82e9e97a26 | ||
|
|
6a1338bb23 | ||
|
|
3b8a092f36 | ||
|
|
3d86a32b12 | ||
|
|
f26719e1d3 | ||
|
|
e54ef9620f | ||
|
|
d8156e839b | ||
|
|
61666d275d | ||
|
|
99f595e16f | ||
|
|
debe32b187 | ||
|
|
ca9b8581b4 | ||
|
|
a0b341deb5 | ||
|
|
d3df356299 | ||
|
|
cd808a5e6d | ||
|
|
40f61a9dd4 | ||
|
|
f312250d27 | ||
|
|
3fac8aa665 | ||
|
|
243aafd417 | ||
|
|
6bc03601d9 | ||
|
|
f05acaecc7 | ||
|
|
9456a903d7 | ||
|
|
984deed883 | ||
|
|
557cb2ebcf | ||
|
|
9c055a9a23 | ||
|
|
13a09343e6 | ||
|
|
8c67e04cc4 | ||
|
|
e77bb73a82 | ||
|
|
05fc5db337 | ||
|
|
a4738e8c7d | ||
|
|
5f6051e333 | ||
|
|
51afe3dacd | ||
|
|
0291908675 | ||
|
|
5fba784d05 | ||
|
|
9139e34c0b | ||
|
|
e7f503fae1 | ||
|
|
980350d70a | ||
|
|
0ccd3c9bd0 | ||
|
|
3e7ba4d774 | ||
|
|
a176022840 | ||
|
|
24a1e39ad3 | ||
|
|
a09c6ae2f9 | ||
|
|
2a85e92d4a | ||
|
|
92646840e4 | ||
|
|
c55ce758ed | ||
|
|
aa301aefc3 | ||
|
|
d6d90ab7c8 | ||
|
|
bf780514e6 | ||
|
|
eaa54035ff | ||
|
|
baa4f5a258 | ||
|
|
df24db5b58 | ||
|
|
4def93b4ad | ||
|
|
0a407437a4 | ||
|
|
05ab4640da | ||
|
|
b92eb4b089 | ||
|
|
5778d1e38d | ||
|
|
479e5a2c52 | ||
|
|
2f455c52c1 | ||
|
|
78c1f9b7a9 | ||
|
|
c68b4c5947 | ||
|
|
40344455e6 | ||
|
|
630c99c39a | ||
|
|
46716620e3 | ||
|
|
70a43c2f03 | ||
|
|
1923fc74e4 | ||
|
|
f0ae63bce5 | ||
|
|
30650bb64f | ||
|
|
d87aef82b7 | ||
|
|
e79d1eb8a4 | ||
|
|
189d49fa4a | ||
|
|
6a543f2aa7 | ||
|
|
c27c5b70ee | ||
|
|
0a246d2fc7 | ||
|
|
0956a08bec | ||
|
|
5a80258d20 | ||
|
|
85bb502191 | ||
|
|
ead49a657e | ||
|
|
df5be35af8 | ||
|
|
19d5952210 | ||
|
|
afd87da5e7 | ||
|
|
9861ecd03e | ||
|
|
aa191a9feb | ||
|
|
b8ef3cf6b6 | ||
|
|
0a8655eb4d | ||
|
|
52b49edbcf | ||
|
|
6a35c58df4 | ||
|
|
cd08b2d1bb | ||
|
|
f8e2918561 | ||
|
|
e4ea34db51 | ||
|
|
4bfce37f40 | ||
|
|
fd0d836c03 | ||
|
|
183446d32a | ||
|
|
540aeb73f4 | ||
|
|
4f0acc4d63 | ||
|
|
56280db2c7 | ||
|
|
885b57cd52 | ||
|
|
bbee31ea55 | ||
|
|
1bfa7d2638 | ||
|
|
d4a9412947 | ||
|
|
1b240b293e | ||
|
|
e41f728589 | ||
|
|
577546763f | ||
|
|
58ea303202 | ||
|
|
818685c48d | ||
|
|
5d13261516 | ||
|
|
56289b5d22 | ||
|
|
1733ac0573 | ||
|
|
d71b4f09e2 | ||
|
|
13d40e886e | ||
|
|
618ce59233 | ||
|
|
071d8bd1ce | ||
|
|
c498d5c8c4 | ||
|
|
11971267dc | ||
|
|
418531a44a | ||
|
|
59dc0d1423 | ||
|
|
385f0af17e | ||
|
|
ef9b781961 | ||
|
|
80cc8e9db7 | ||
|
|
b2be386be8 | ||
|
|
e4c598cae6 | ||
|
|
5fe6681b0d | ||
|
|
35fc848672 | ||
|
|
e3f3bd7387 | ||
|
|
638ec1c3a3 | ||
|
|
90ac468aa9 | ||
|
|
65dfef0a33 | ||
|
|
76a2548e95 | ||
|
|
40b690b67b | ||
|
|
2b17f3d702 | ||
|
|
5b6c78783e | ||
|
|
fa618eb97f | ||
|
|
d0616ef028 | ||
|
|
91771297d3 | ||
|
|
4e1a3c5f2d | ||
|
|
df634cb1b2 | ||
|
|
aa797142d1 | ||
|
|
816be43a98 | ||
|
|
9ebabfe737 | ||
|
|
884b56ac09 | ||
|
|
61677101e4 | ||
|
|
7a93f5b89b | ||
|
|
9fe7a86a3b | ||
|
|
9da029b8fe | ||
|
|
8b8e72c879 | ||
|
|
590c595522 | ||
|
|
57d6bcdc52 | ||
|
|
0b2fe61e6e | ||
|
|
706659ba3d | ||
|
|
3c9a10d710 | ||
|
|
fd57e9a47c | ||
|
|
ab99d2d1ed | ||
|
|
ae0dae5956 | ||
|
|
00c6e3b4fd | ||
|
|
cdf7b92a8e | ||
|
|
0e979a6e7b | ||
|
|
dd144305da | ||
|
|
4f3ddf803a | ||
|
|
de305d26ad | ||
|
|
9f30f45774 | ||
|
|
1ae47807eb | ||
|
|
3deea632f8 | ||
|
|
9f2c31149a | ||
|
|
b25a1bc3f4 | ||
|
|
fd7677f94f | ||
|
|
2e31a70406 | ||
|
|
8e29a3ec5b | ||
|
|
a68bf85f04 | ||
|
|
aae9395d66 | ||
|
|
a463af53b7 | ||
|
|
6adb991c04 | ||
|
|
c5f4736984 | ||
|
|
1a233fbe15 | ||
|
|
a7d6518bff | ||
|
|
0700ce14f7 | ||
|
|
c88620920b | ||
|
|
125fc8a9fe | ||
|
|
b06e1bb5dd | ||
|
|
c7316e9780 | ||
|
|
be1bcdfe2c | ||
|
|
144e6f45da | ||
|
|
569d5c6c6a | ||
|
|
7087c67d79 | ||
|
|
c4e6a89959 | ||
|
|
f60f674845 | ||
|
|
c21d40c466 | ||
|
|
19b590f3bb | ||
|
|
a421470715 | ||
|
|
79dc13f419 | ||
|
|
2e8208a752 | ||
|
|
1c40cca673 | ||
|
|
b1a5a5f6c0 | ||
|
|
64961877de | ||
|
|
9c9bb52818 | ||
|
|
02148f4766 | ||
|
|
767711c93a | ||
|
|
78ba0641a6 | ||
|
|
80683c381f | ||
|
|
6aac46adaf | ||
|
|
388ef66308 | ||
|
|
8faa4ef2db | ||
|
|
441c02d0fb | ||
|
|
953ad80482 | ||
|
|
e2ee9cff4c | ||
|
|
996ac59c79 | ||
|
|
944f713ee9 | ||
|
|
7329c70492 | ||
|
|
b5514fd269 | ||
|
|
3be50116fa | ||
|
|
125d6d3a7d | ||
|
|
bb746dfbfb | ||
|
|
8069a1b659 | ||
|
|
f0cb8b6ef3 | ||
|
|
9474ac9ef0 | ||
|
|
b862aa91eb | ||
|
|
bc64d530be | ||
|
|
ea5116e963 | ||
|
|
cc4e697246 | ||
|
|
515bef3f5d | ||
|
|
05800fe518 | ||
|
|
33d37771ce | ||
|
|
89540475cf | ||
|
|
e03da2f737 | ||
|
|
4617eb8f17 | ||
|
|
fed1e20085 | ||
|
|
9f30440286 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.nas linguist-language=Nasal
|
||||
65
.github/workflows/c-cpp.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 16 * * *"
|
||||
push:
|
||||
branches: [ master,develop ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
mac-build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make -j4
|
||||
cd module
|
||||
make all -j4
|
||||
cd ..
|
||||
make test
|
||||
tar -czf nasal-mac-nightly.tgz .
|
||||
- name: Release file
|
||||
# You may pin to the exact commit or the version.
|
||||
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
|
||||
uses: marvinpinto/action-automatic-releases@v1.2.1
|
||||
with:
|
||||
# GitHub auth token
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Name of Release to add file to
|
||||
title: macOS Nightly build
|
||||
# Name of the tag for the release (will be associated with current branch)
|
||||
automatic_release_tag: next_macOS
|
||||
# File to release
|
||||
files: nasal-mac-nightly.tgz
|
||||
|
||||
linux-x86_64-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
make -j4
|
||||
cd module
|
||||
make all -j4
|
||||
cd ..
|
||||
make test
|
||||
touch nasal-linux-x86_64-nightly.tgz
|
||||
tar -czf nasal-linux-x86_64-nightly.tgz --exclude=nasal-linux-x86_64-nightly.tgz .
|
||||
- name: Release file
|
||||
# You may pin to the exact commit or the version.
|
||||
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
|
||||
uses: marvinpinto/action-automatic-releases@v1.2.1
|
||||
with:
|
||||
# GitHub auth token
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Name of Release to add file to
|
||||
title: Linux Nightly build
|
||||
# Name of the tag for the release (will be associated with current branch)
|
||||
automatic_release_tag: next_linux_x86_64
|
||||
# File to release
|
||||
files: nasal-linux-x86_64-nightly.tgz
|
||||
|
||||
29
.gitignore
vendored
@@ -30,3 +30,32 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# VS C++ sln
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
.vs
|
||||
x64
|
||||
CMakePresents.json
|
||||
|
||||
# nasal executable
|
||||
nasal
|
||||
nasal.exe
|
||||
|
||||
# misc
|
||||
.vscode
|
||||
dump
|
||||
fgfs.log
|
||||
.temp.*
|
||||
|
||||
# build dir
|
||||
build
|
||||
out
|
||||
|
||||
# macOS special cache directory
|
||||
.DS_Store
|
||||
|
||||
# ppm picture generated by ppmgen.nas
|
||||
*.ppm
|
||||
85
CMakeLists.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(nasal VERSION 10.1)
|
||||
|
||||
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
|
||||
|
||||
# -std=c++17 -Wshadow -Wall
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
||||
|
||||
add_compile_options(-fPIC)
|
||||
|
||||
# generate release executables
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
|
||||
# build nasal used object
|
||||
set(NASAL_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/coroutine.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/fg_props.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/bits_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/io_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/math_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/dylib_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/unix_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_dbg.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_err.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_import.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_lexer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/repl.cpp)
|
||||
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
|
||||
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
# build nasal
|
||||
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
|
||||
target_link_libraries(nasal nasal-object)
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
target_link_libraries(nasal dl)
|
||||
target_link_libraries(nasal pthread)
|
||||
endif()
|
||||
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
add_custom_command(
|
||||
TARGET nasal POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/build/nasal
|
||||
${CMAKE_SOURCE_DIR}/nasal
|
||||
)
|
||||
endif()
|
||||
|
||||
# build module
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
||||
|
||||
set(MODULE_USED_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
||||
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
|
||||
|
||||
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
|
||||
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(fib module-used-object)
|
||||
|
||||
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
|
||||
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(key module-used-object)
|
||||
|
||||
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
|
||||
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(mat module-used-object)
|
||||
|
||||
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
|
||||
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(nasock module-used-object)
|
||||
339
LICENSE
Normal file
@@ -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.
|
||||
1079
doc/README_zh.md
Normal file
112
doc/benchmark.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# __Benchmark__
|
||||
|
||||

|
||||
|
||||
## 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:
|
||||
|
||||

|
||||
699
doc/dev.md
Normal file
@@ -0,0 +1,699 @@
|
||||
# __Development History__
|
||||
|
||||
## __Contents__
|
||||
|
||||
* [__Parser__](#parser)
|
||||
* [v1.0](#version-10-parser-last-update-20191014)
|
||||
* [__Abstract Syntax Tree__](#abstract-syntax-tree)
|
||||
* [v1.2](#version-12-ast-last-update-20191031)
|
||||
* [v2.0](#version-20-ast-last-update-2020831)
|
||||
* [v3.0](#version-30-ast-last-update-20201023)
|
||||
* [v5.0](#version-50-ast-last-update-202137)
|
||||
* [v11.0](#version-110-ast-latest)
|
||||
* [__Bytecode VM__](#bytecode-virtual-machine)
|
||||
* [v4.0](#version-40-vm-last-update-20201217)
|
||||
* [v5.0](#version-50-vm-last-update-202137)
|
||||
* [v6.0](#version-60-vm-last-update-202161)
|
||||
* [v6.5](#version-65-vm-last-update-2021624)
|
||||
* [v7.0](#version-70-vm-last-update-2021108)
|
||||
* [v8.0](#version-80-vm-last-update-2022212)
|
||||
* [v9.0](#version-90-vm-last-update-2022518)
|
||||
* [v10.0](#version-100-vm-last-update-2022816)
|
||||
* [__Release Notes__](#release-notes)
|
||||
* [v8.0](#version-80-release)
|
||||
* [v11.0](#version-110-release)
|
||||
|
||||
## __Parser__
|
||||
|
||||
`LL(1)` parser with special check.
|
||||
|
||||
```javascript
|
||||
(var a,b,c)=[{b:nil},[1,2],func return 0;];
|
||||
(a.b,b[0],c)=(1,2,3);
|
||||
```
|
||||
|
||||
These two expressions have the same first set,so `LL(1)` is useless for this language. We add some special checks in it.
|
||||
|
||||
Problems mentioned above have been solved for a long time, but recently i found a new problem here:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
This will be recognized as this:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}(a,b,c)
|
||||
=(0,1,2);
|
||||
```
|
||||
|
||||
and causes fatal syntax error.
|
||||
And i tried this program in flightgear nasal console.
|
||||
It also found this is a syntax error.
|
||||
I think this is a serious design fault.
|
||||
To avoid this syntax error, change program like this, just add a semicolon:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z};
|
||||
^ here
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
### version 1.0 parser (last update 2019/10/14)
|
||||
|
||||
First fully functional version of parser.
|
||||
|
||||
Before version 1.0,i tried many times to create a correct parser.
|
||||
|
||||
Finally i learned `LL(1)` and `LL(k)` and wrote a parser for math formulas in version 0.16(last update 2019/9/14).
|
||||
|
||||
In version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)i was playing the parser happily and after that i wrote version 1.0.
|
||||
|
||||
__This project began at 2019/7/25__.
|
||||
|
||||
## __Abstract Syntax Tree__
|
||||
|
||||
### version 1.2 ast (last update 2019/10/31)
|
||||
|
||||
The ast has been completed in this version.
|
||||
|
||||
### version 2.0 ast (last update 2020/8/31)
|
||||
|
||||
A completed ast-interpreter with unfinished lib functions.
|
||||
|
||||
### version 3.0 ast (last update 2020/10/23)
|
||||
|
||||
The ast is refactored and is now easier to read and maintain.
|
||||
|
||||
Ast-interpreter uses new techniques so it can run codes more efficiently.
|
||||
|
||||
Now you can add your own functions as builtin-functions in this interpreter!
|
||||
|
||||
I decide to save the ast interpreter after releasing v4.0. Because it took me a long time to think and write...
|
||||
|
||||
### version 5.0 ast (last update 2021/3/7)
|
||||
|
||||
I change my mind.
|
||||
AST interpreter leaves me too much things to do.
|
||||
|
||||
If i continue saving this interpreter,
|
||||
it will be harder for me to make the bytecode vm become more efficient.
|
||||
|
||||
### version 11.0 ast (latest)
|
||||
|
||||
Change ast framework. Now we use visitor pattern.
|
||||
|
||||
## __Bytecode Virtual Machine__
|
||||
|
||||

|
||||
|
||||
### version 4.0 vm (last update 2020/12/17)
|
||||
|
||||
I have just finished the first version of bytecode-interpreter.
|
||||
|
||||
This interpreter is still in test.
|
||||
After this test, i will release version 4.0!
|
||||
|
||||
Now i am trying to search hidden bugs in this interpreter.
|
||||
Hope you could help me! :)
|
||||
|
||||
There's an example of byte code below:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 0
|
||||
.number 4e+006
|
||||
.number 1
|
||||
.symbol i
|
||||
0x00000000: pzero 0x00000000
|
||||
0x00000001: loadg 0x00000000 (i)
|
||||
0x00000002: callg 0x00000000 (i)
|
||||
0x00000003: pnum 0x00000001 (4e+006)
|
||||
0x00000004: less 0x00000000
|
||||
0x00000005: jf 0x0000000b
|
||||
0x00000006: pone 0x00000000
|
||||
0x00000007: mcallg 0x00000000 (i)
|
||||
0x00000008: addeq 0x00000000
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000002
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 5.0 vm (last update 2021/3/7)
|
||||
|
||||
I decide to optimize bytecode vm in this version.
|
||||
|
||||
Because it takes more than 1.5s to count i from `0` to `4000000-1`.This is not efficient at all!
|
||||
|
||||
2021/1/23 update: Now it can count from `0` to `4000000-1` in 1.5s.
|
||||
|
||||
### version 6.0 vm (last update 2021/6/1)
|
||||
|
||||
Use `loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll` to avoid branches.
|
||||
|
||||
Delete type `vm_scop`.
|
||||
|
||||
Use const `vm_num` to avoid frequently new & delete.
|
||||
|
||||
Change garbage collector from reference-counting to mark-sweep.
|
||||
|
||||
`vapp` and `newf` operand use .num to reduce the size of `exec_code`.
|
||||
|
||||
2021/4/3 update: Now it can count from `0` to `4e6-1` in 0.8s.
|
||||
|
||||
2021/4/19 update: Now it can count from `0` to `4e6-1` in 0.4s.
|
||||
|
||||
In this update i changed global and local scope from `unordered_map` to `vector`.
|
||||
|
||||
So the bytecode generator changed a lot.
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4e+006)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000c
|
||||
0x00000007: pone 0x00000000
|
||||
0x00000008: mcallg 0x00000000
|
||||
0x00000009: addeq 0x00000000
|
||||
0x0000000a: pop 0x00000000
|
||||
0x0000000b: jmp 0x00000003
|
||||
0x0000000c: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 6.5 vm (last update 2021/6/24)
|
||||
|
||||
2021/5/31 update:
|
||||
|
||||
Now gc can collect garbage correctly without re-collecting,
|
||||
which will cause fatal error.
|
||||
|
||||
Add `builtin_alloc` to avoid mark-sweep when running a built-in function,
|
||||
which will mark useful items as useless garbage to collect.
|
||||
|
||||
Better use setsize and assignment to get a big array,
|
||||
`append` is very slow in this situation.
|
||||
|
||||
2021/6/3 update:
|
||||
|
||||
Fixed a bug that gc still re-collects garbage,
|
||||
this time i use three mark states to make sure garbage is ready to be collected.
|
||||
|
||||
Change `callf` to `callfv` and `callfh`.
|
||||
And `callfv` fetches arguments from `val_stack` directly instead of using `vm_vec`,
|
||||
a not very efficient way.
|
||||
|
||||
Better use `callfv` instead of `callfh`,
|
||||
`callfh` will fetch a `vm_hash` from stack and parse it,
|
||||
making this process slow.
|
||||
|
||||
```javascript
|
||||
var f=func(x,y){return x+y;}
|
||||
f(1024,2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 1024
|
||||
.number 2048
|
||||
.symbol x
|
||||
.symbol y
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: newf 0x00000007
|
||||
0x00000002: intl 0x00000003
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: para 0x00000000 (x)
|
||||
0x00000005: para 0x00000001 (y)
|
||||
0x00000006: jmp 0x0000000b
|
||||
0x00000007: calll 0x00000001
|
||||
0x00000008: calll 0x00000002
|
||||
0x00000009: add 0x00000000
|
||||
0x0000000a: ret 0x00000000
|
||||
0x0000000b: loadg 0x00000000
|
||||
0x0000000c: callg 0x00000000
|
||||
0x0000000d: pnum 0x00000000 (1024)
|
||||
0x0000000e: pnum 0x00000001 (2048)
|
||||
0x0000000f: callfv 0x00000002
|
||||
0x00000010: pop 0x00000000
|
||||
0x00000011: nop 0x00000000
|
||||
```
|
||||
|
||||
2021/6/21 update: Now gc will not collect nullptr.
|
||||
And the function of assignment is complete,
|
||||
now these kinds of assignment is allowed:
|
||||
|
||||
```javascript
|
||||
var f=func()
|
||||
{
|
||||
var _=[{_:0},{_:1}];
|
||||
return func(x)
|
||||
{
|
||||
return _[x];
|
||||
}
|
||||
}
|
||||
var m=f();
|
||||
m(0)._=m(1)._=10;
|
||||
|
||||
[0,1,2][1:2][0]=0;
|
||||
```
|
||||
|
||||
In the old version,
|
||||
parser will check this left-value and tells that these kinds of left-value are not allowed(bad lvalue).
|
||||
|
||||
But now it can work.
|
||||
And you could see its use by reading the code above.
|
||||
To make sure this assignment works correctly,
|
||||
codegen will generate byte code by `codegen::call_gen()` instead of `codegen::mcall_gen()`,
|
||||
and the last child of the ast will be generated by `codegen::mcall_gen()`.
|
||||
So the bytecode is totally different now:
|
||||
|
||||
```x86asm
|
||||
.number 10
|
||||
.number 2
|
||||
.symbol _
|
||||
.symbol x
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: newf 0x00000005
|
||||
0x00000002: intl 0x00000002
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: jmp 0x00000017
|
||||
0x00000005: newh 0x00000000
|
||||
0x00000006: pzero 0x00000000
|
||||
0x00000007: happ 0x00000000 (_)
|
||||
0x00000008: newh 0x00000000
|
||||
0x00000009: pone 0x00000000
|
||||
0x0000000a: happ 0x00000000 (_)
|
||||
0x0000000b: newv 0x00000002
|
||||
0x0000000c: loadl 0x00000001
|
||||
0x0000000d: newf 0x00000012
|
||||
0x0000000e: intl 0x00000003
|
||||
0x0000000f: offset 0x00000002
|
||||
0x00000010: para 0x00000001 (x)
|
||||
0x00000011: jmp 0x00000016
|
||||
0x00000012: calll 0x00000001
|
||||
0x00000013: calll 0x00000002
|
||||
0x00000014: callv 0x00000000
|
||||
0x00000015: ret 0x00000000
|
||||
0x00000016: ret 0x00000000
|
||||
0x00000017: loadg 0x00000000
|
||||
0x00000018: callg 0x00000000
|
||||
0x00000019: callfv 0x00000000
|
||||
0x0000001a: loadg 0x00000001
|
||||
0x0000001b: pnum 0x00000000 (10.000000)
|
||||
0x0000001c: callg 0x00000001
|
||||
0x0000001d: pone 0x00000000
|
||||
0x0000001e: callfv 0x00000001
|
||||
0x0000001f: mcallh 0x00000000 (_)
|
||||
0x00000020: meq 0x00000000
|
||||
0x00000021: callg 0x00000001
|
||||
0x00000022: pzero 0x00000000
|
||||
0x00000023: callfv 0x00000001
|
||||
0x00000024: mcallh 0x00000000 (_)
|
||||
0x00000025: meq 0x00000000
|
||||
0x00000026: pop 0x00000000
|
||||
0x00000027: pzero 0x00000000
|
||||
0x00000028: pzero 0x00000000
|
||||
0x00000029: pone 0x00000000
|
||||
0x0000002a: pnum 0x00000001 (2.000000)
|
||||
0x0000002b: newv 0x00000003
|
||||
0x0000002c: slcbeg 0x00000000
|
||||
0x0000002d: pone 0x00000000
|
||||
0x0000002e: pnum 0x00000001 (2.000000)
|
||||
0x0000002f: slc2 0x00000000
|
||||
0x00000030: slcend 0x00000000
|
||||
0x00000031: pzero 0x00000000
|
||||
0x00000032: mcallv 0x00000000
|
||||
0x00000033: meq 0x00000000
|
||||
0x00000034: pop 0x00000000
|
||||
0x00000035: nop 0x00000000
|
||||
```
|
||||
|
||||
As you could see from the bytecode above,
|
||||
`mcall`/`mcallv`/`mcallh` operands' using frequency will reduce,
|
||||
`call`/`callv`/`callh`/`callfv`/`callfh` at the opposite.
|
||||
|
||||
And because of the new structure of `mcall`,
|
||||
`addr_stack`, a stack used to store the memory address,
|
||||
is deleted from `vm`,
|
||||
and now `vm` use `nas_val** mem_addr` to store the memory address.
|
||||
This will not cause fatal errors because the memory address is used __immediately__ after getting it.
|
||||
|
||||
### version 7.0 vm (last update 2021/10/8)
|
||||
|
||||
2021/6/26 update:
|
||||
|
||||
Instruction dispatch is changed from call-threading to computed-goto(with inline function).
|
||||
After changing the way of instruction dispatch,
|
||||
there is a great improvement in `vm`.
|
||||
Now vm can run test/bigloop and test/pi in 0.2s!
|
||||
And vm runs test/fib in 0.8s on linux.
|
||||
You could see the time use data below,
|
||||
in Test data section.
|
||||
|
||||
This version uses g++ extension "labels as values",
|
||||
which is also supported by clang++.
|
||||
(But i don't know if MSVC supports this)
|
||||
|
||||
There is also a change in `gc`:
|
||||
`std::vector` global is deleted,
|
||||
now the global values are all stored on stack(from `val_stack+0` to `val_stack+intg-1`).
|
||||
|
||||
2021/6/29 update:
|
||||
|
||||
Add some instructions that execute const values:
|
||||
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`.
|
||||
|
||||
Now the bytecode of test/bigloop.nas seems like this:
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
.number 1
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4000000)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000b
|
||||
0x00000007: mcallg 0x00000000
|
||||
0x00000008: addeqc 0x00000001 (1)
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000003
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
And this test file runs in 0.1s after this update.
|
||||
Most of the calculations are accelerated.
|
||||
|
||||
Also, assignment bytecode has changed a lot.
|
||||
Now the first identifier that called in assignment will use `op_load` to assign,
|
||||
instead of `op_meq`,`op_pop`.
|
||||
|
||||
```javascript
|
||||
var (a,b)=(1,2);
|
||||
a=b=0;
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 2
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: pone 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: pnum 0x00000000 (2)
|
||||
0x00000004: loadg 0x00000001
|
||||
0x00000005: pzero 0x00000000
|
||||
0x00000006: mcallg 0x00000001
|
||||
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
|
||||
0x00000008: loadg 0x00000000 (a=b use loadg)
|
||||
0x00000009: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 8.0 vm (last update 2022/2/12)
|
||||
|
||||
2021/10/8 update:
|
||||
|
||||
In this version vm_nil and vm_num now is not managed by `gc`,
|
||||
this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
|
||||
|
||||
New value type is added: `vm_obj`.
|
||||
This type is reserved for user to define their own value types.
|
||||
Related API will be added in the future.
|
||||
|
||||
Fully functional closure:
|
||||
Add new operands that get and set upvalues.
|
||||
Delete an old operand `op_offset`.
|
||||
|
||||
2021/10/13 update:
|
||||
|
||||
The format of output information of bytecodes changes to this:
|
||||
|
||||
```x86asm
|
||||
0x000002f2: newf 0x2f6
|
||||
0x000002f3: intl 0x2
|
||||
0x000002f4: para 0x3e ("x")
|
||||
0x000002f5: jmp 0x309
|
||||
0x000002f6: calll 0x1
|
||||
0x000002f7: lessc 0x0 (2)
|
||||
0x000002f8: jf 0x2fb
|
||||
0x000002f9: calll 0x1
|
||||
0x000002fa: ret
|
||||
0x000002fb: upval 0x0[0x1]
|
||||
0x000002fc: upval 0x0[0x1]
|
||||
0x000002fd: callfv 0x1
|
||||
0x000002fe: calll 0x1
|
||||
0x000002ff: subc 0x1d (1)
|
||||
0x00000300: callfv 0x1
|
||||
0x00000301: upval 0x0[0x1]
|
||||
0x00000302: upval 0x0[0x1]
|
||||
0x00000303: callfv 0x1
|
||||
0x00000304: calll 0x1
|
||||
0x00000305: subc 0x0 (2)
|
||||
0x00000306: callfv 0x1
|
||||
0x00000307: add
|
||||
0x00000308: ret
|
||||
0x00000309: ret
|
||||
0x0000030a: callfv 0x1
|
||||
0x0000030b: loadg 0x32
|
||||
```
|
||||
|
||||
2022/1/22 update:
|
||||
|
||||
Delete `op_pone` and `op_pzero`.
|
||||
Both of them are meaningless and will be replaced by `op_pnum`.
|
||||
|
||||
### version 9.0 vm (last update 2022/5/18)
|
||||
|
||||
2022/2/12 update:
|
||||
|
||||
Local values now are __stored on stack__.
|
||||
So function calling will be faster than before.
|
||||
Because in v8.0 when calling a function,
|
||||
new `vm_vec` will be allocated by `gc`, this makes gc doing mark-sweep too many times and spends a quite lot of time.
|
||||
In test file `test/bf.nas`, it takes too much time to test the file because this file has too many function calls(see test data below in table `version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`).
|
||||
|
||||
Upvalue now is generated when creating first new function in the local scope, using `vm_vec`.
|
||||
And after that when creating new functions, they share the same upvalue, and the upvalue will synchronize with the local scope each time creating a new function.
|
||||
|
||||
2022/3/27 update:
|
||||
|
||||
In this month's updates we change upvalue from `vm_vec` to `vm_upval`,
|
||||
a special gc-managed object,
|
||||
which has almost the same structure of that upvalue object in another programming language __`Lua`__.
|
||||
|
||||
Today we change the output format of bytecode.
|
||||
New output format looks like `objdump`:
|
||||
|
||||
```x86asm
|
||||
0x0000029b: 0a 00 00 00 00 newh
|
||||
|
||||
func <0x29c>:
|
||||
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
|
||||
0x0000029d: 02 00 00 00 02 intl 0x2
|
||||
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
|
||||
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
|
||||
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
|
||||
0x000002a1: 4a 00 00 00 00 ret
|
||||
<0x29c>;
|
||||
|
||||
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
|
||||
|
||||
func <0x2a3>:
|
||||
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
|
||||
0x000002a4: 02 00 00 00 03 intl 0x3
|
||||
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
|
||||
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
|
||||
0x000002a7: 32 00 00 02 aa jmp 0x2aa
|
||||
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
|
||||
0x000002a9: 4a 00 00 00 00 ret
|
||||
<0x2a3>;
|
||||
|
||||
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
|
||||
```
|
||||
|
||||
### version 10.0 vm (last update 2022/8/16)
|
||||
|
||||
2022/5/19 update:
|
||||
|
||||
Now we add coroutine in this runtime:
|
||||
|
||||
```javascript
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
```
|
||||
|
||||
`coroutine.create` is used to create a new coroutine object using a function.
|
||||
But this coroutine will not run immediately.
|
||||
|
||||
`coroutine.resume` is used to continue running a coroutine.
|
||||
|
||||
`coroutine.yield` is used to interrupt the running of a coroutine and throw some values.
|
||||
These values will be accepted and returned by `coroutine.resume`.
|
||||
And `coroutine.yield` it self returns `vm_nil` in the coroutine function.
|
||||
|
||||
`coroutine.status` is used to see the status of a coroutine.
|
||||
There are 3 types of status:`suspended` means waiting for running,`running` means is running,`dead` means finished running.
|
||||
|
||||
`coroutine.running` is used to judge if there is a coroutine running now.
|
||||
|
||||
__CAUTION:__ coroutine should not be created or running inside another coroutine.
|
||||
|
||||
__We will explain how resume and yield work here:__
|
||||
|
||||
When `op_callb` is called, the stack frame is like this:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| old pc(vm_ret) | <- top[0]
|
||||
+----------------------+
|
||||
| old localr(vm_addr) | <- top[-1]
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) | <- top[-2]
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
In `op_callb`'s progress, next step the stack frame is:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
Then we call `resume`, this function will change stack.
|
||||
As we can see, coroutine stack already has some values on it,
|
||||
but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`.
|
||||
|
||||
So for safe running, when first calling the coroutine, `resume` will return `gc.top[0]`.
|
||||
`op_callb` will do `top[0]=resume()`, so the value does not change.
|
||||
|
||||
```C++
|
||||
+----------------------+(coroutine stack)
|
||||
| pc:0(vm_ret) | <- now gc.top[0]
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
When we call `yield`, the function will do like this.
|
||||
And we find that `op_callb` has put the `nil` at the top.
|
||||
but where is the returned `local[1]` sent?
|
||||
|
||||
```C++
|
||||
+----------------------+(coroutine stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
When `builtin_coyield` is finished, the stack is set to main stack,
|
||||
and the returned `local[1]` in fact is set to the top of the main stack by `op_callb`:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| return_value(var) |
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
so the main progress feels the value on the top is the returned value of `resume`.
|
||||
but in fact the `resume`'s returned value is set on coroutine stack.
|
||||
so we conclude this:
|
||||
|
||||
```C++
|
||||
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
|
||||
yield (coroutine->main) return a vector. main.top[0] = vector;
|
||||
```
|
||||
|
||||
## __Release Notes__
|
||||
|
||||
### __version 8.0 release__
|
||||
|
||||
I made a __big mistake__ in `v8.0` release:
|
||||
|
||||
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
this will cause incorrect `stackoverflow` error.
|
||||
please change it to:
|
||||
|
||||
`canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
If do not change this line, only the debugger runs abnormally. this bug is fixed in `v9.0`.
|
||||
|
||||
Another bug is that in `nasal_err.h:class nasal_err`, we should add a constructor for this class:
|
||||
|
||||
```C++
|
||||
nasal_err(): error(0) {}
|
||||
```
|
||||
|
||||
This bug is fixed in `v9.0`. So we suggest that do not use `v8.0`.
|
||||
|
||||
### __version 11.0 release__
|
||||
|
||||
1. Use C++ `std=c++17`.
|
||||
|
||||
2. Change framework of ast, using visitor pattern.
|
||||
|
||||
3. New ast structure dump info format.
|
||||
|
||||
4. Change the way of module export, split library into different modules. Symbols begin with `_` will not be exported.
|
||||
|
||||
5. Change `stl` to `std`.
|
||||
|
||||
6. Add REPL interpreter.
|
||||
|
||||
7. Improve structure of virtual machine, split global symbol stack(stores global symbols' values) and value stack(using in process).
|
||||
|
||||
8. Delete operand `op_intg`, add operand `op_repl`.
|
||||
|
||||
9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`).
|
||||
|
||||
10. New ghost type register process.
|
||||
632
doc/dev_zh.md
Normal file
@@ -0,0 +1,632 @@
|
||||
# __开发历史记录__
|
||||
|
||||
## __目录__
|
||||
|
||||
* [__语法分析__](#语法分析)
|
||||
* [v1.0](#version-10-parser-last-update-20191014)
|
||||
* [__抽象语法树__](#抽象语法树)
|
||||
* [v1.2](#version-12-ast-last-update-20191031)
|
||||
* [v2.0](#version-20-ast-last-update-2020831)
|
||||
* [v3.0](#version-30-ast-last-update-20201023)
|
||||
* [v5.0](#version-50-ast-last-update-202137)
|
||||
* [v11.0](#version-110-ast-latest)
|
||||
* [__字节码虚拟机__](#字节码虚拟机)
|
||||
* [v4.0](#version-40-vm-last-update-20201217)
|
||||
* [v5.0](#version-50-vm-last-update-202137)
|
||||
* [v6.0](#version-60-vm-last-update-202161)
|
||||
* [v6.5](#version-65-vm-last-update-2021624)
|
||||
* [v7.0](#version-70-vm-last-update-2021108)
|
||||
* [v8.0](#version-80-vm-last-update-2022212)
|
||||
* [v9.0](#version-90-vm-last-update-2022518)
|
||||
* [v10.0](#version-100-vm-last-update-2022816)
|
||||
* [__发行日志__](#发行日志)
|
||||
* [v8.0](#version-80-release)
|
||||
* [v11.0](#version-110-release)
|
||||
|
||||
## __语法分析__
|
||||
|
||||
有特殊语法检查的`LL(1)`语法分析器。
|
||||
|
||||
```javascript
|
||||
(var a,b,c)=[{b:nil},[1,2],func return 0;];
|
||||
(a.b,b[0],c)=(1,2,3);
|
||||
```
|
||||
|
||||
这两个表达式有同一个first集,所以纯粹的`LL(1)`很难实现这个语言的语法分析。所以我们为其添加了特殊语法检查机制。本质上还是`LL(1)`的内核。
|
||||
|
||||
上面这个问题已经解决很久了,不过我最近发现了一个新的语法问题:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
这种写法会被错误识别合并成下面这种:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}(a,b,c)
|
||||
=(0,1,2);
|
||||
```
|
||||
|
||||
语法分析器会认为这是个严重的语法错误。我在Flightgear中也测试了这个代码,它内置的语法分析器也认为这是错误语法。当然我认为这是语法设计中的一个比较严重的缺漏。为了避免这个语法问题,只需要添加一个分号就可以了:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z};
|
||||
^ 就是这里
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
### version 1.0 parser (last update 2019/10/14)
|
||||
|
||||
第一版功能完备的nasal语法分析器完成了。
|
||||
|
||||
在version 1.0之前,我多次尝试构建一个正确的语法分析器但总是存在一些问题。
|
||||
|
||||
最终我学习了`LL(1)`和`LL(k)`文法并且在version 0.16(last update 2019/9/14)中完成了一个能识别数学算式的语法分析器。
|
||||
|
||||
在version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)中我只是抱着玩的心态在测试语法分析器,不过在那之后我还是完成了version 1.0的语法分析器。
|
||||
|
||||
__该项目于2019/7/25正式开始__。
|
||||
|
||||
## __抽象语法树__
|
||||
|
||||
### version 1.2 ast (last update 2019/10/31)
|
||||
|
||||
抽象语法树在这个版本初步完成。
|
||||
|
||||
### version 2.0 ast (last update 2020/8/31)
|
||||
|
||||
在这个版本我们基于抽象语法树实现了一个树解释器,并且完成了部分内置函数。
|
||||
|
||||
### version 3.0 ast (last update 2020/10/23)
|
||||
|
||||
我们重构了抽象语法树的代码,现在可以更容易地读懂代码并进行维护。
|
||||
|
||||
这个版本的树解释器用了新的优化方式,所以可以更高效地执行代码。
|
||||
|
||||
在这个版本用户已经可以自行添加内置函数。
|
||||
|
||||
我想在v4.0发布之后仍然保留这个树解释器,毕竟花了很长时间才写完这坨屎。
|
||||
|
||||
### version 5.0 ast (last update 2021/3/7)
|
||||
|
||||
我改变想法了,树解释器给维护带来了太大的麻烦。如果想继续保留这个解释器,那么为了兼容性,字节码虚拟机的优化工作会更难推进。
|
||||
|
||||
### version 11.0 ast (latest)
|
||||
|
||||
改变了语法树的设计模式,采用访问者模式。
|
||||
|
||||
## __字节码虚拟机__
|
||||
|
||||

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

|
||||
|
||||
## Introduction
|
||||
|
||||
In this nasal interpreter,
|
||||
we use this way below to construct namespaces:
|
||||
|
||||
- library is linked directly with the script
|
||||
- module is wraped by a function generated by linker, and return a hash
|
||||
|
||||
## Library
|
||||
|
||||
Library file is linked with script file directly, like this:
|
||||
|
||||
In `std/lib.nas`:
|
||||
|
||||
```nasal
|
||||
var a = 1;
|
||||
```
|
||||
|
||||
In `example.nas`:
|
||||
|
||||
```nasal
|
||||
var b = 1;
|
||||
```
|
||||
|
||||
At the link stage,
|
||||
in fact we put the ast of two files together to make a new ast,
|
||||
so the result is equal to:
|
||||
|
||||
```nasal
|
||||
var a = 1;
|
||||
var b = 1;
|
||||
```
|
||||
|
||||
## Module
|
||||
|
||||
Modules is wraped up by a function,
|
||||
and return a hash, for example:
|
||||
|
||||
In `std/example_module.nas`:
|
||||
|
||||
```nasal
|
||||
var a = 1;
|
||||
```
|
||||
|
||||
We analysed this file and generated the ast.
|
||||
Then we find all the global symbols.
|
||||
At last we use the information of all the globals symbols in this file to generate a hash to return.
|
||||
|
||||
So the result is equal to:
|
||||
|
||||
```nasal
|
||||
var example_module = func {
|
||||
# source code begin
|
||||
var a = 1;
|
||||
# source code end
|
||||
return {
|
||||
a: a
|
||||
};
|
||||
}();
|
||||
```
|
||||
170
doc/nasal-http-test-web.html
Normal file
@@ -0,0 +1,170 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title> nasal-http-test-web </title>
|
||||
<meta charset="utf-8">
|
||||
<meta author="ValKmjolnir">
|
||||
<style>
|
||||
body{
|
||||
background: white;
|
||||
width: 60%;
|
||||
}
|
||||
pre{
|
||||
background: #303030;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
h1,h2,h3{
|
||||
padding: 5px;
|
||||
background-color: #555588;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
}
|
||||
p{
|
||||
margin-left: 15px;
|
||||
}
|
||||
div.badges{
|
||||
text-align: center;
|
||||
}
|
||||
tr{
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Nasal | Not another scripting language!</h1>
|
||||
<div class="badges">
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github"><br/></img></a>
|
||||
</div>
|
||||
<h2> 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> Benchmark | 执行效率</h2>
|
||||
<img src="/doc/pic/benchmark.png" width="450" height="350"></img>
|
||||
<img src="/doc/pic/mandelbrot.png" width="450" height="350"><br /></img>
|
||||
<text>
|
||||
<p>
|
||||
Benchmark of different versions of nasal interpreter(left).
|
||||
Beautiful picture generated by brainfuck interpreter written in nasal(right).
|
||||
</p>
|
||||
<p>
|
||||
不同版本的nasal解释器执行效率图(左)。
|
||||
nasal运行brainfuck绘制的曼德勃罗集合(右)。
|
||||
</p>
|
||||
<p>
|
||||
Nasal can run this test file(test/bf.nas) to draw this picture in about 220 seconds.
|
||||
In fact this test file cost over 2200 seconds before ver 8.0 .
|
||||
</p>
|
||||
<p>
|
||||
Nasal现在可以在220秒内运行该文件(test/bf.nas)并绘制出这张图。
|
||||
在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。
|
||||
</p>
|
||||
<p>
|
||||
The figure below is the feigenbaum-figure generated by ppm script written in nasal.
|
||||
</p>
|
||||
<p>
|
||||
下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 图形。
|
||||
</p>
|
||||
</text>
|
||||
<img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
|
||||
<h2> Example | 样例代码</h2>
|
||||
<form method="get">
|
||||
<text style="margin-left: 15px;"> </text>
|
||||
<input type="text" name="filename" value="ascii-art.nas">
|
||||
<input type="submit" value="search">
|
||||
</form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/ascii-art.nas">ascii-art.nas</a></li>
|
||||
<li><a href="/auto_crash.nas">auto_crash.nas</a></li>
|
||||
<li><a href="/bf.nas">bf.nas</a></li>
|
||||
<li><a href="/bfconvertor.nas">bfconvertor.nas</a></li>
|
||||
<li><a href="/bfs.nas">bfs.nas</a></li>
|
||||
<li><a href="/bigloop.nas">bigloop.nas</a></li>
|
||||
<li><a href="/bp.nas">bp.nas</a></li>
|
||||
<li><a href="/calc.nas">calc.nas</a></li>
|
||||
<li><a href="/choice.nas">choice.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/class.nas">class.nas</a></li>
|
||||
<li><a href="/coroutine.nas">coroutine.nas</a></li>
|
||||
<li><a href="/diff.nas">diff.nas</a></li>
|
||||
<li><a href="/exception.nas">exception.nas</a></li>
|
||||
<li><a href="/fib.nas">fib.nas</a></li>
|
||||
<li><a href="/filesystem.nas">filesystem.nas</a></li>
|
||||
<li><a href="/hexdump.nas">hexdump.nas</a></li>
|
||||
<li><a href="/httptest.nas">httptest.nas</a></li>
|
||||
<li><a href="/json.nas">json.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/leetcode1319.nas">leetcode1319.nas</a></li>
|
||||
<li><a href="/lexer.nas">lexer.nas</a></li>
|
||||
<li><a href="/life.nas">life.nas</a></li>
|
||||
<li><a href="/loop.nas">loop.nas</a></li>
|
||||
<li><a href="/mandel.nas">mandel.nas</a></li>
|
||||
<li><a href="/mandelbrot.nas">mandelbrot.nas</a></li>
|
||||
<li><a href="/mcpu.nas">mcpu.nas</a></li>
|
||||
<li><a href="/md5.nas">md5.nas</a></li>
|
||||
<li><a href="/md5compare.nas">md5compare.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/module_test.nas">module_test.nas</a></li>
|
||||
<li><a href="/nasal_test.nas">nasal_test.nas</a></li>
|
||||
<li><a href="/occupation.nas">occupation.nas</a></li>
|
||||
<li><a href="/pi.nas">pi.nas</a></li>
|
||||
<li><a href="/ppmgen.nas">ppmgen.nas</a></li>
|
||||
<li><a href="/prime.nas">prime.nas</a></li>
|
||||
<li><a href="/qrcode.nas">qrcode.nas</a></li>
|
||||
<li><a href="/quick_sort.nas">quick_sort.nas</a></li>
|
||||
<li><a href="/scalar.nas">scalar.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/snake.nas">snake.nas</a></li>
|
||||
<li><a href="/tetris.nas">tetris.nas</a></li>
|
||||
<li><a href="/trait.nas">trait.nas</a></li>
|
||||
<li><a href="/turingmachine.nas">turingmachine.nas</a></li>
|
||||
<li><a href="/utf8chk.nas">utf8chk.nas</a></li>
|
||||
<li><a href="/watchdog.nas">watchdog.nas</a></li>
|
||||
<li><a href="/wavecollapse.nas">wavecollapse.nas</a></li>
|
||||
<li><a href="/word_collector.nas">word_collector.nas</a></li>
|
||||
<li><a href="/ycombinator.nas">ycombinator.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<h2> 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>
|
||||
@@ -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
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/pic/favicon.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/pic/feigenbaum.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
doc/pic/header.png
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
doc/pic/mandelbrot.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
doc/pic/social.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
167
lib.nas
@@ -1,167 +0,0 @@
|
||||
var import=func(filename)
|
||||
{
|
||||
nasal_call_import(filename);
|
||||
return nil;
|
||||
}
|
||||
var print=func(elements...)
|
||||
{
|
||||
nasal_call_builtin_std_cout(elements);
|
||||
return nil;
|
||||
};
|
||||
var append=func(vector,elements...)
|
||||
{
|
||||
nasal_call_builtin_push_back(vector,elements);
|
||||
return nil;
|
||||
}
|
||||
var setsize=func(vector,size)
|
||||
{
|
||||
nasal_call_builtin_set_size(vector,size);
|
||||
return nil;
|
||||
}
|
||||
var system=func(str)
|
||||
{
|
||||
nasal_call_builtin_system(str);
|
||||
return;
|
||||
}
|
||||
var input=func()
|
||||
{
|
||||
return nasal_call_builtin_input();
|
||||
}
|
||||
var sleep=func(duration)
|
||||
{
|
||||
nasal_call_builtin_sleep(duration);
|
||||
return;
|
||||
}
|
||||
var split=func(delimeter,string)
|
||||
{
|
||||
return nasal_call_builtin_split(delimeter,string);
|
||||
}
|
||||
var rand=func(seed=nil)
|
||||
{
|
||||
return nasal_call_builtin_rand(seed);
|
||||
}
|
||||
var id=func(thing)
|
||||
{
|
||||
return nasal_call_builtin_get_id(thing);
|
||||
}
|
||||
var int=func(value)
|
||||
{
|
||||
return nasal_call_builtin_trans_int(value);
|
||||
}
|
||||
var num=func(value)
|
||||
{
|
||||
return nasal_call_builtin_trans_num(value);
|
||||
}
|
||||
var pop=func(vector)
|
||||
{
|
||||
return nasal_call_builtin_pop_back(vector);
|
||||
}
|
||||
var str=func(number)
|
||||
{
|
||||
return nasal_call_builtin_trans_str(number);
|
||||
}
|
||||
var size=func(object)
|
||||
{
|
||||
return nasal_call_builtin_size(object);
|
||||
}
|
||||
var contains=func(hash,key)
|
||||
{
|
||||
return nasal_call_builtin_contains(hash,key);
|
||||
}
|
||||
var delete=func(hash,key)
|
||||
{
|
||||
nasal_call_builtin_delete(hash,key);
|
||||
return;
|
||||
}
|
||||
var keys=func(hash)
|
||||
{
|
||||
return nasal_call_builtin_get_keys(hash);
|
||||
}
|
||||
var time=func(begin_time)
|
||||
{
|
||||
return nasal_call_builtin_time(begin_time);
|
||||
}
|
||||
var die=func(str)
|
||||
{
|
||||
nasal_call_builtin_die(str);
|
||||
return nil;
|
||||
}
|
||||
var typeof=func(object)
|
||||
{
|
||||
return nasal_call_builtin_type(object);
|
||||
}
|
||||
var substr=func(str,begin,length)
|
||||
{
|
||||
return nasal_call_builtin_substr(str,begin,length);
|
||||
}
|
||||
|
||||
var io=
|
||||
{
|
||||
fin:func(filename)
|
||||
{
|
||||
return nasal_call_builtin_finput(filename);
|
||||
},
|
||||
fout:func(filename,str)
|
||||
{
|
||||
nasal_call_builtin_foutput(filename,str);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
var bits=
|
||||
{
|
||||
bitxor:func(a,b)
|
||||
{
|
||||
return nasal_call_builtin_xor(a,b);
|
||||
},
|
||||
bitand:func(a,b)
|
||||
{
|
||||
return nasal_call_builtin_and(a,b);
|
||||
},
|
||||
bitor:func(a,b)
|
||||
{
|
||||
return nasal_call_builtin_or(a,b);
|
||||
},
|
||||
bitnand:func(a,b)
|
||||
{
|
||||
return nasal_call_builtin_nand(a,b);
|
||||
},
|
||||
bitnot:func(a)
|
||||
{
|
||||
return nasal_call_builtin_not(a);
|
||||
}
|
||||
};
|
||||
|
||||
var math=
|
||||
{
|
||||
e:2.7182818284590452354,
|
||||
pi:3.14159265358979323846264338327950288,
|
||||
sin:func(x)
|
||||
{
|
||||
return nasal_call_builtin_sin(x);
|
||||
},
|
||||
cos:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cos(x);
|
||||
},
|
||||
tan:func(x)
|
||||
{
|
||||
return nasal_call_builtin_tan(x);
|
||||
},
|
||||
exp:func(x)
|
||||
{
|
||||
return nasal_call_builtin_exp(x);
|
||||
},
|
||||
ln:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cpp_math_ln(x);
|
||||
},
|
||||
sqrt:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cpp_math_sqrt(x);
|
||||
},
|
||||
atan2:func(x,y)
|
||||
{
|
||||
return nasal_call_builtin_cpp_atan2(x,y);
|
||||
},
|
||||
};
|
||||
221
main.cpp
@@ -1,221 +0,0 @@
|
||||
#include "nasal.h"
|
||||
|
||||
nasal_lexer lexer;
|
||||
nasal_parse parse;
|
||||
nasal_import import;
|
||||
std::string inputfile="null";
|
||||
nasal_runtime runtime;
|
||||
nasal_codegen code_generator;
|
||||
nasal_bytecode_vm bytevm;
|
||||
|
||||
void help()
|
||||
{
|
||||
std::cout<<">> [\"file\"] input a file name.\n";
|
||||
std::cout<<">> [help ] show help.\n";
|
||||
std::cout<<">> [clear ] clear the screen.\n";
|
||||
std::cout<<">> [del ] clear the input filename.\n";
|
||||
std::cout<<">> [lex ] use lexer to turn code into tokens.\n";
|
||||
std::cout<<">> [ast ] do parsing and check the abstract syntax tree.\n";
|
||||
std::cout<<">> [run ] run abstract syntax tree.\n";
|
||||
std::cout<<">> [code ] show byte code.\n";
|
||||
std::cout<<">> [exec ] execute program on bytecode vm.\n";
|
||||
std::cout<<">> [logo ] print logo of nasal .\n";
|
||||
std::cout<<">> [exit ] quit nasal interpreter.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
void logo()
|
||||
{
|
||||
std::cout<<" __ _ \n";
|
||||
std::cout<<" /\\ \\ \\__ _ ___ __ _| | \n";
|
||||
std::cout<<" / \\/ / _` / __|/ _` | | \n";
|
||||
std::cout<<" / /\\ / (_| \\__ \\ (_| | | \n";
|
||||
std::cout<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n";
|
||||
return;
|
||||
}
|
||||
|
||||
void del_func()
|
||||
{
|
||||
lexer.clear();
|
||||
parse.clear();
|
||||
inputfile="null";
|
||||
std::cout<<">> [Delete] complete.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
void die(std::string stage,std::string filename)
|
||||
{
|
||||
std::cout<<">> ["<<stage<<"] in <\""<<filename<<"\">: error(s) occurred,stop.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
void lex_func()
|
||||
{
|
||||
lexer.openfile(inputfile);
|
||||
lexer.scanner();
|
||||
if(lexer.get_error())
|
||||
{
|
||||
die("lexer",inputfile);
|
||||
return;
|
||||
}
|
||||
lexer.print_token();
|
||||
return;
|
||||
}
|
||||
|
||||
void ast_print()
|
||||
{
|
||||
lexer.openfile(inputfile);
|
||||
lexer.scanner();
|
||||
if(lexer.get_error())
|
||||
{
|
||||
die("lexer",inputfile);
|
||||
return;
|
||||
}
|
||||
parse.set_toklist(lexer.get_token_list());
|
||||
parse.main_process();
|
||||
if(parse.get_error())
|
||||
{
|
||||
die("parse",inputfile);
|
||||
return;
|
||||
}
|
||||
parse.get_root().print_ast(0);
|
||||
return;
|
||||
}
|
||||
|
||||
void runtime_start()
|
||||
{
|
||||
lexer.openfile(inputfile);
|
||||
lexer.scanner();
|
||||
if(lexer.get_error())
|
||||
{
|
||||
die("lexer",inputfile);
|
||||
return;
|
||||
}
|
||||
parse.set_toklist(lexer.get_token_list());
|
||||
parse.main_process();
|
||||
if(parse.get_error())
|
||||
{
|
||||
die("parse",inputfile);
|
||||
return;
|
||||
}
|
||||
import.link(parse.get_root());
|
||||
if(import.get_error())
|
||||
{
|
||||
die("import",inputfile);
|
||||
return;
|
||||
}
|
||||
runtime.set_root(import.get_root());
|
||||
runtime.run();
|
||||
return;
|
||||
}
|
||||
|
||||
void show_bytecode()
|
||||
{
|
||||
lexer.openfile(inputfile);
|
||||
lexer.scanner();
|
||||
if(lexer.get_error())
|
||||
{
|
||||
die("lexer",inputfile);
|
||||
return;
|
||||
}
|
||||
parse.set_toklist(lexer.get_token_list());
|
||||
parse.main_process();
|
||||
if(parse.get_error())
|
||||
{
|
||||
die("parse",inputfile);
|
||||
return;
|
||||
}
|
||||
import.link(parse.get_root());
|
||||
if(import.get_error())
|
||||
{
|
||||
die("import",inputfile);
|
||||
return;
|
||||
}
|
||||
code_generator.main_progress(import.get_root());
|
||||
code_generator.print_byte_code();
|
||||
return;
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
lexer.openfile(inputfile);
|
||||
lexer.scanner();
|
||||
if(lexer.get_error())
|
||||
{
|
||||
die("lexer",inputfile);
|
||||
return;
|
||||
}
|
||||
parse.set_toklist(lexer.get_token_list());
|
||||
parse.main_process();
|
||||
if(parse.get_error())
|
||||
{
|
||||
die("parse",inputfile);
|
||||
return;
|
||||
}
|
||||
import.link(parse.get_root());
|
||||
if(import.get_error())
|
||||
{
|
||||
die("import",inputfile);
|
||||
return;
|
||||
}
|
||||
code_generator.main_progress(import.get_root());
|
||||
bytevm.run(
|
||||
code_generator.get_string_table(),
|
||||
code_generator.get_number_table(),
|
||||
code_generator.get_exec_code()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string command;
|
||||
#ifdef _WIN32
|
||||
// use chcp 65001 to use unicode io
|
||||
system("chcp 65001");
|
||||
system("cls");
|
||||
#endif
|
||||
logo();
|
||||
std::cout<<">> Nasal interpreter ver 3.0 .\n";
|
||||
std::cout<<">> Code: https://github.com/ValKmjolnir/Nasal-Interpreter\n";
|
||||
std::cout<<">> Info: http://wiki.flightgear.org/Nasal_scripting_language\n";
|
||||
std::cout<<">> Input \"help\" to get help .\n";
|
||||
while(1)
|
||||
{
|
||||
std::cout<<">> ";
|
||||
std::cin>>command;
|
||||
if(command=="help")
|
||||
help();
|
||||
else if(command=="clear")
|
||||
{
|
||||
#ifdef _WIN32
|
||||
system("cls");
|
||||
#endif
|
||||
#ifdef _linux_
|
||||
system("clear");
|
||||
#endif
|
||||
#ifdef TARGET_OS_MAC
|
||||
system("clear");
|
||||
#endif
|
||||
}
|
||||
else if(command=="del")
|
||||
del_func();
|
||||
else if(command=="lex")
|
||||
lex_func();
|
||||
else if(command=="ast")
|
||||
ast_print();
|
||||
else if(command=="run")
|
||||
runtime_start();
|
||||
else if(command=="code")
|
||||
show_bytecode();
|
||||
else if(command=="exec")
|
||||
execute();
|
||||
else if(command=="logo")
|
||||
logo();
|
||||
else if(command=="exit")
|
||||
break;
|
||||
else
|
||||
inputfile=command;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
262
makefile
Normal file
@@ -0,0 +1,262 @@
|
||||
STD = c++17
|
||||
OS = $(shell uname)
|
||||
ifeq ($(OS), Darwin)
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
|
||||
else
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
|
||||
endif
|
||||
CPPFLAGS = -I .
|
||||
|
||||
NASAL_HEADER=\
|
||||
src/ast_dumper.h\
|
||||
src/ast_visitor.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_builtin.h\
|
||||
src/nasal_codegen.h\
|
||||
src/nasal_dbg.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_gc.h\
|
||||
src/nasal_import.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_opcode.h\
|
||||
src/nasal_parse.h\
|
||||
src/nasal_vm.h\
|
||||
src/nasal.h\
|
||||
src/optimizer.h\
|
||||
src/symbol_finder.h\
|
||||
src/fg_props.h\
|
||||
src/bits_lib.h\
|
||||
src/io_lib.h\
|
||||
src/math_lib.h\
|
||||
src/dylib_lib.h\
|
||||
src/unix_lib.h\
|
||||
src/coroutine.h\
|
||||
src/repl.h
|
||||
|
||||
NASAL_OBJECT=\
|
||||
build/nasal_err.o\
|
||||
build/nasal_ast.o\
|
||||
build/ast_visitor.o\
|
||||
build/bits_lib.o\
|
||||
build/ast_dumper.o\
|
||||
build/nasal_lexer.o\
|
||||
build/nasal_parse.o\
|
||||
build/nasal_import.o\
|
||||
build/optimizer.o\
|
||||
build/nasal_opcode.o\
|
||||
build/symbol_finder.o\
|
||||
build/nasal_codegen.o\
|
||||
build/nasal_misc.o\
|
||||
build/nasal_gc.o\
|
||||
build/nasal_builtin.o\
|
||||
build/fg_props.o\
|
||||
build/io_lib.o\
|
||||
build/math_lib.o\
|
||||
build/unix_lib.o\
|
||||
build/dylib_lib.o\
|
||||
build/coroutine.o\
|
||||
build/nasal_vm.o\
|
||||
build/nasal_dbg.o\
|
||||
build/repl.o\
|
||||
build/main.o
|
||||
|
||||
|
||||
# for test
|
||||
nasal: $(NASAL_OBJECT) | build
|
||||
@if [ OS = "Darwin" ]; then\
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread -stdlib=libc++ -static-libstdc++;\
|
||||
else\
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread;\
|
||||
fi
|
||||
|
||||
nasal.exe: $(NASAL_OBJECT) | build
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
||||
|
||||
build:
|
||||
@ if [ ! -d build ]; then mkdir build; fi
|
||||
|
||||
build/main.o: $(NASAL_HEADER) src/main.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/main.cpp -o build/main.o
|
||||
|
||||
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_misc.cpp -o build/nasal_misc.o
|
||||
|
||||
build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/repl.cpp -o build/repl.o
|
||||
|
||||
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
|
||||
|
||||
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
||||
|
||||
build/nasal_import.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_parse.h\
|
||||
src/nasal_import.h src/nasal_import.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_import.cpp -o build/nasal_import.o
|
||||
|
||||
build/nasal_lexer.o: \
|
||||
src/nasal.h\
|
||||
src/repl.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_lexer.h src/nasal_lexer.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_lexer.cpp -o build/nasal_lexer.o
|
||||
|
||||
build/nasal_ast.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h src/nasal_ast.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_ast.cpp -o build/nasal_ast.o
|
||||
|
||||
build/nasal_builtin.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
||||
|
||||
build/coroutine.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/coroutine.h src/coroutine.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
|
||||
|
||||
build/bits_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/bits_lib.h src/bits_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
|
||||
|
||||
|
||||
build/math_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/math_lib.h src/math_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
|
||||
|
||||
build/io_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/io_lib.h src/io_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
|
||||
|
||||
build/dylib_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/dylib_lib.h src/dylib_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
|
||||
|
||||
build/unix_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/unix_lib.h src/unix_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
|
||||
|
||||
build/fg_props.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_gc.h\
|
||||
src/fg_props.h src/fg_props.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
|
||||
|
||||
build/nasal_codegen.o: $(NASAL_HEADER) src/nasal_codegen.h src/nasal_codegen.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_codegen.cpp -o build/nasal_codegen.o
|
||||
|
||||
build/nasal_opcode.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_builtin.h\
|
||||
src/nasal_opcode.h src/nasal_opcode.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_opcode.cpp -o build/nasal_opcode.o
|
||||
|
||||
build/nasal_parse.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_parse.cpp -o build/nasal_parse.o
|
||||
|
||||
build/optimizer.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/optimizer.cpp -o build/optimizer.o
|
||||
|
||||
build/symbol_finder.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/symbol_finder.cpp -o build/symbol_finder.o
|
||||
|
||||
build/ast_visitor.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h src/ast_visitor.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/ast_visitor.cpp -o build/ast_visitor.o
|
||||
|
||||
build/ast_dumper.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/ast_dumper.h src/ast_dumper.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/ast_dumper.cpp -o build/ast_dumper.o
|
||||
|
||||
build/nasal_vm.o: $(NASAL_HEADER) src/nasal_vm.h src/nasal_vm.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_vm.cpp -o build/nasal_vm.o
|
||||
|
||||
build/nasal_dbg.o: $(NASAL_HEADER) src/nasal_dbg.h src/nasal_dbg.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_dbg.cpp -o build/nasal_dbg.o
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
|
||||
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
|
||||
@ rm $(NASAL_OBJECT)
|
||||
|
||||
.PHONY: test
|
||||
test:nasal
|
||||
@ ./nasal -e test/ascii-art.nas
|
||||
@ ./nasal -t -d test/bfs.nas
|
||||
@ ./nasal -t test/bigloop.nas
|
||||
@ ./nasal -t test/bp.nas
|
||||
@ ./nasal -d test/calc.nas
|
||||
@ ./nasal -e test/choice.nas
|
||||
@ ./nasal -e test/class.nas
|
||||
@ ./nasal -t -d test/console3D.nas 20
|
||||
@ ./nasal -e test/coroutine.nas
|
||||
@ ./nasal -t -d test/datalog.nas
|
||||
@ ./nasal -e test/diff.nas
|
||||
@ ./nasal -e test/donuts.nas 15
|
||||
-@ ./nasal -d test/exception.nas
|
||||
@ ./nasal -t -d test/fib.nas
|
||||
@ ./nasal -e test/filesystem.nas
|
||||
@ ./nasal -t -d test/globals_test.nas
|
||||
@ ./nasal -d test/hexdump.nas
|
||||
@ ./nasal -e test/json.nas
|
||||
@ ./nasal -e test/leetcode1319.nas
|
||||
@ ./nasal -d test/lexer.nas
|
||||
@ ./nasal -d test/life.nas
|
||||
@ ./nasal -t test/loop.nas
|
||||
@ ./nasal -t test/mandelbrot.nas
|
||||
@ ./nasal -t test/md5_self.nas
|
||||
@ ./nasal -t -d test/md5compare.nas
|
||||
@ ./nasal -d test/module_test.nas
|
||||
@ ./nasal -e test/nasal_test.nas
|
||||
@ ./nasal -t -d test/occupation.nas 2
|
||||
@ ./nasal -t -d test/pi.nas
|
||||
@ ./nasal -t -d test/prime.nas
|
||||
@ ./nasal -e test/qrcode.nas
|
||||
@ ./nasal -t -d test/quick_sort.nas
|
||||
@ ./nasal -e test/scalar.nas hello world
|
||||
@ ./nasal -e test/trait.nas
|
||||
@ ./nasal -t -d test/turingmachine.nas
|
||||
@ ./nasal -d test/wavecollapse.nas
|
||||
@ ./nasal test/word_collector.nas test/md5compare.nas
|
||||
@ ./nasal -t -d test/ycombinator.nas
|
||||
96
module/fib.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// module for test
|
||||
|
||||
#include <iostream>
|
||||
#include "../src/nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
namespace fib_module {
|
||||
|
||||
double fibonaci(double x) {
|
||||
if (x<=2) {
|
||||
return x;
|
||||
}
|
||||
return fibonaci(x-1)+fibonaci(x-2);
|
||||
}
|
||||
|
||||
var fib(var* args, usize size, gc* ngc) {
|
||||
if (!size) {
|
||||
return nas_err("fib", "lack arguments");
|
||||
}
|
||||
var num = args[0];
|
||||
return var::num(fibonaci(num.tonum()));
|
||||
}
|
||||
|
||||
var quick_fib(var* args, usize size, gc* ngc) {
|
||||
if (!size) {
|
||||
return nas_err("quick_fib","lack arguments");
|
||||
}
|
||||
double num = args[0].tonum();
|
||||
if (num<2) {
|
||||
return var::num(num);
|
||||
}
|
||||
double a = 1, b = 1, res = 0;
|
||||
for(double i = 1; i<num; ++i) {
|
||||
res = a+b;
|
||||
a = b;
|
||||
b = res;
|
||||
}
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
const auto ghost_for_test = "ghost_for_test";
|
||||
|
||||
void ghost_for_test_destructor(void* ptr) {
|
||||
std::cout << "ghost_for_test::destructor (0x";
|
||||
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
|
||||
delete static_cast<u32*>(ptr);
|
||||
std::cout << " delete 0x" << std::hex;
|
||||
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
|
||||
std::cout << "}\n";
|
||||
}
|
||||
|
||||
var create_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_obj);
|
||||
res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32);
|
||||
return res;
|
||||
}
|
||||
|
||||
var set_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = args[0];
|
||||
if (!res.objchk(ghost_for_test)) {
|
||||
std::cout << "set_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
f64 num = args[1].num();
|
||||
*((u32*)res.obj().ptr) = static_cast<u32>(num);
|
||||
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
var print_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = args[0];
|
||||
if (!res.objchk(ghost_for_test)) {
|
||||
std::cout << "print_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
std::cout << "print_new_ghost: " << res.obj() << " result = "
|
||||
<< *((u32*)res.obj().ptr) << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"fib", fib},
|
||||
{"quick_fib", quick_fib},
|
||||
{"create_ghost", create_new_ghost},
|
||||
{"set_ghost", set_new_ghost},
|
||||
{"print_ghost", print_new_ghost},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return fib_module::func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
111
module/keyboard.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "../src/nasal.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class noecho_input {
|
||||
private:
|
||||
#ifndef _WIN32
|
||||
struct termios init_termios;
|
||||
struct termios new_termios;
|
||||
int peek_char = -1;
|
||||
#endif
|
||||
public:
|
||||
noecho_input() {
|
||||
#ifndef _WIN32
|
||||
tcflush(0, TCIOFLUSH);
|
||||
tcgetattr(0, &init_termios);
|
||||
new_termios = init_termios;
|
||||
new_termios.c_lflag &= ~(ICANON|ECHO|ECHONL|ECHOE);
|
||||
// vmin=0 is nonblock input, but in wsl there is a bug that will block input
|
||||
// so we use fcntl to write the nonblock input
|
||||
new_termios.c_cc[VMIN] = 1;
|
||||
new_termios.c_cc[VTIME] = 0;
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
#endif
|
||||
}
|
||||
|
||||
~noecho_input() {
|
||||
#ifndef _WIN32
|
||||
tcflush(0, TCIOFLUSH);
|
||||
tcsetattr(0, TCSANOW, &init_termios);
|
||||
#endif
|
||||
}
|
||||
|
||||
int noecho_kbhit() {
|
||||
#ifndef _WIN32
|
||||
unsigned char ch = 0;
|
||||
int nread = 0;
|
||||
if (peek_char!=-1) {
|
||||
return 1;
|
||||
}
|
||||
int flag = fcntl(0, F_GETFL);
|
||||
fcntl(0, F_SETFL, flag|O_NONBLOCK);
|
||||
nread = read(0, &ch, 1);
|
||||
fcntl(0, F_SETFL, flag);
|
||||
if (nread==1) {
|
||||
peek_char = ch;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return kbhit();
|
||||
#endif
|
||||
}
|
||||
|
||||
int noecho_getch() {
|
||||
#ifndef _WIN32
|
||||
int ch = 0;
|
||||
if (peek_char!=-1) {
|
||||
ch = peek_char;
|
||||
peek_char = -1;
|
||||
return ch;
|
||||
}
|
||||
ssize_t tmp = read(0, &ch, 1);
|
||||
return ch;
|
||||
#else
|
||||
return getch();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
noecho_input this_window;
|
||||
|
||||
var nas_getch(var* args, usize size, gc* ngc) {
|
||||
return var::num(static_cast<double>(this_window.noecho_getch()));
|
||||
}
|
||||
|
||||
var nas_kbhit(var* args, usize size, gc* ngc) {
|
||||
return var::num(static_cast<double>(this_window.noecho_kbhit()));
|
||||
}
|
||||
|
||||
var nas_noblock(var* args, usize size, gc* ngc) {
|
||||
if (this_window.noecho_kbhit()) {
|
||||
return var::num(static_cast<double>(this_window.noecho_getch()));
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_getch", nas_getch},
|
||||
{"nas_kbhit", nas_kbhit},
|
||||
{"nas_noblock", nas_noblock},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
54
module/libfib.nas
Normal file
@@ -0,0 +1,54 @@
|
||||
import.std.dylib;
|
||||
|
||||
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
|
||||
var _fib = _dl.fib;
|
||||
|
||||
var _qfib = _dl.quick_fib;
|
||||
|
||||
var _create_ghost = _dl.create_ghost;
|
||||
|
||||
var _set_ghost = _dl.set_ghost;
|
||||
|
||||
var _print_ghost = _dl.print_ghost;
|
||||
|
||||
var _zero_call = dylib.limitcall(0);
|
||||
|
||||
var _call = dylib.limitcall(1);
|
||||
|
||||
var _test_call = dylib.limitcall(2);
|
||||
|
||||
|
||||
var fib = func(x) {
|
||||
return _call(_fib, x)
|
||||
}
|
||||
|
||||
|
||||
var qfib = func(x) {
|
||||
return _call(_qfib, x)
|
||||
}
|
||||
|
||||
|
||||
var create_ghost = func() {
|
||||
return _zero_call(_create_ghost)
|
||||
}
|
||||
|
||||
|
||||
var set_ghost = func(object, x) {
|
||||
return _test_call(_set_ghost, object, x)
|
||||
}
|
||||
|
||||
|
||||
var print_ghost = func(object) {
|
||||
return _call(_print_ghost, object)
|
||||
}
|
||||
|
||||
|
||||
var test_ghost=func() {
|
||||
var ghost = create_ghost();
|
||||
print_ghost(nil); # err
|
||||
print_ghost(ghost); # random
|
||||
set_ghost(nil, 114); # err
|
||||
set_ghost(ghost, 114); # success
|
||||
print_ghost(ghost); # 114
|
||||
}
|
||||
18
module/libkey.nas
Normal file
@@ -0,0 +1,18 @@
|
||||
import.std.dylib;
|
||||
|
||||
var (
|
||||
kbhit,
|
||||
getch,
|
||||
nonblock
|
||||
) = func {
|
||||
var lib = dylib.dlopen("libkey"~(os.platform()=="windows"? ".dll":".so"));
|
||||
var kb = lib.nas_kbhit;
|
||||
var gt = lib.nas_getch;
|
||||
var nb = lib.nas_noblock;
|
||||
var call = dylib.limitcall(0);
|
||||
return [
|
||||
func(){return call(kb);},
|
||||
func(){return call(gt);},
|
||||
func(){return call(nb);}
|
||||
];
|
||||
}();
|
||||
66
module/libmat.nas
Normal file
@@ -0,0 +1,66 @@
|
||||
import.std.dylib;
|
||||
|
||||
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));
|
||||
|
||||
var _vec2 = _dl.nas_vec2;
|
||||
|
||||
var _vec3 = _dl.nas_vec3;
|
||||
|
||||
var (_vec2add, _vec2sub, _vec2mul, _vec2div, _vec2neg, _vec2norm, _vec2len, _vec2dot)=(
|
||||
_dl.nas_vec2_add,
|
||||
_dl.nas_vec2_sub,
|
||||
_dl.nas_vec2_mult,
|
||||
_dl.nas_vec2_div,
|
||||
_dl.nas_vec2_neg,
|
||||
_dl.nas_vec2_norm,
|
||||
_dl.nas_vec2_len,
|
||||
_dl.nas_vec2_dot
|
||||
);
|
||||
var (_vec3add, _vec3sub, _vec3mul, _vec3div, _vec3neg, _vec3norm, _vec3len, _vec3dot)=(
|
||||
_dl.nas_vec3_add,
|
||||
_dl.nas_vec3_sub,
|
||||
_dl.nas_vec3_mult,
|
||||
_dl.nas_vec3_div,
|
||||
_dl.nas_vec3_neg,
|
||||
_dl.nas_vec3_norm,
|
||||
_dl.nas_vec3_len,
|
||||
_dl.nas_vec3_dot
|
||||
);
|
||||
var (_rotate_x, _rotate_y, _rotate_z)=(
|
||||
_dl.nas_rotate_x,
|
||||
_dl.nas_rotate_y,
|
||||
_dl.nas_rotate_z
|
||||
);
|
||||
var (_invoke_i, _invoke_ii, _invoke_iii)=(
|
||||
dylib.limitcall(1),
|
||||
dylib.limitcall(2),
|
||||
dylib.limitcall(3)
|
||||
);
|
||||
|
||||
var vec2 = {
|
||||
new: func(x, y) {return _invoke_ii(_vec2, x, y);},
|
||||
add: func(v0, v1) {return _invoke_ii(_vec2add, v0, v1);},
|
||||
sub: func(v0, v1) {return _invoke_ii(_vec2sub, v0, v1);},
|
||||
mul: func(v0, v1) {return _invoke_ii(_vec2mul, v0, v1);},
|
||||
div: func(v0, v1) {return _invoke_ii(_vec2div, v0, v1);},
|
||||
neg: func(v0) {return _invoke_i(_vec2neg, v0);},
|
||||
norm: func(v0) {return _invoke_i(_vec2norm, v0);},
|
||||
len: func(v0) {return _invoke_i(_vec2len, v0);},
|
||||
dot: func(v0, v1) {return _invoke_ii(_vec2dot, v0, v1);}
|
||||
};
|
||||
|
||||
var vec3 = {
|
||||
new: func(x, y, z) {return _invoke_iii(_vec3, x, y, z);},
|
||||
add: func(v0, v1) {return _invoke_ii(_vec3add, v0, v1);},
|
||||
sub: func(v0, v1) {return _invoke_ii(_vec3sub, v0, v1);},
|
||||
mul: func(v0, v1) {return _invoke_ii(_vec3mul, v0, v1);},
|
||||
div: func(v0, v1) {return _invoke_ii(_vec3div, v0, v1);},
|
||||
neg: func(v0) {return _invoke_i(_vec3neg, v0);},
|
||||
norm: func(v0) {return _invoke_i(_vec3norm, v0);},
|
||||
len: func(v0) {return _invoke_i(_vec3len, v0);},
|
||||
rx: func(v0, angle) {return _invoke_ii(_rotate_x, v0, angle);},
|
||||
ry: func(v0, angle) {return _invoke_ii(_rotate_y, v0, angle);},
|
||||
rz: func(v0, angle) {return _invoke_ii(_rotate_z, v0, angle);},
|
||||
dot: func(v0, v1) {return _invoke_ii(_vec3dot, v0, v1);}
|
||||
};
|
||||
|
||||
117
module/libsock.nas
Normal file
@@ -0,0 +1,117 @@
|
||||
import.std.dylib;
|
||||
|
||||
var socket=func(){
|
||||
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
|
||||
|
||||
var sock=lib.nas_socket;
|
||||
var closesocket=lib.nas_closesocket;
|
||||
var shutdown=lib.nas_shutdown;
|
||||
var bind=lib.nas_bind;
|
||||
var listen=lib.nas_listen;
|
||||
var connect=lib.nas_connect;
|
||||
var accept=lib.nas_accept;
|
||||
var send=lib.nas_send;
|
||||
var sendto=lib.nas_sendto;
|
||||
var recv=lib.nas_recv;
|
||||
var recvfrom=lib.nas_recvfrom;
|
||||
var errno=lib.nas_errno;
|
||||
|
||||
var (invoke,invoke_i,invoke_ii,invoke_iii,invoke_iiii,invoke_iiiii)=(
|
||||
dylib.limitcall(0),
|
||||
dylib.limitcall(1),
|
||||
dylib.limitcall(2),
|
||||
dylib.limitcall(3),
|
||||
dylib.limitcall(4),
|
||||
dylib.limitcall(5),
|
||||
);
|
||||
return {
|
||||
AF_UNSPEC:0,
|
||||
AF_UNIX:1,
|
||||
AF_INET:2,
|
||||
AF_IMPLINK:3,
|
||||
AF_PUP:4,
|
||||
AF_CHAOS:5,
|
||||
AF_IPX:6,
|
||||
AF_NS:6,
|
||||
AF_ISO:7,
|
||||
AF_OSI:7,
|
||||
AF_ECMA:8,
|
||||
AF_DATAKIT:9,
|
||||
AF_CCITT:10,
|
||||
AF_SNA:11,
|
||||
AF_DECnet:12,
|
||||
AF_DLI:13,
|
||||
AF_LAT:14,
|
||||
AF_HYLINK:15,
|
||||
AF_APPLETALK:16,
|
||||
AF_NETBIOS:17,
|
||||
AF_VOICEVIEW:18,
|
||||
AF_FIREFOX:19,
|
||||
AF_UNKNOWN1:20,
|
||||
AF_BAN:21,
|
||||
AF_MAX:22,
|
||||
|
||||
SOCK_STREAM:1,
|
||||
SOCK_DGRAM:2,
|
||||
SOCK_RAW:3,
|
||||
SOCK_RDM:4,
|
||||
SOCK_SEQPACKET:5,
|
||||
|
||||
IPPROTO_IP:0,IPPROTO_ICMP:1,IPPROTO_IGMP:2,IPPROTO_GGP:3,
|
||||
IPPROTO_TCP:6,IPPROTO_PUP:12,IPPROTO_UDP:17,IPPROTO_IDP:22,
|
||||
IPPROTO_ND:77,IPPROTO_RAW:255,IPPROTO_MAX:256,
|
||||
|
||||
IPPORT_ECHO:7,IPPORT_DISCARD:9,IPPORT_SYSTAT:11,IPPORT_DAYTIME:13,
|
||||
IPPORT_NETSTAT:15,IPPORT_FTP:21,IPPORT_TELNET:23,IPPORT_SMTP:25,
|
||||
IPPORT_TIMESERVER:37,IPPORT_NAMESERVER:42,IPPORT_WHOIS:43,IPPORT_MTP:57,
|
||||
IPPORT_TFTP:69,IPPORT_RJE:77,IPPORT_FINGER:79,IPPORT_TTYLINK:87,
|
||||
IPPORT_SUPDUP:95,IPPORT_EXECSERVER:512,IPPORT_LOGINSERVER:513,IPPORT_CMDSERVER:514,
|
||||
IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520,
|
||||
IPPORT_RESERVED:1024,
|
||||
|
||||
SHUT_RD :0x00,
|
||||
SHUT_WR :0x01,
|
||||
SHUT_RDWR:0x02,
|
||||
|
||||
MSG_OOB:0x1,
|
||||
MSG_PEEK:0x2,
|
||||
MSG_DONTROUTE:0x4,
|
||||
|
||||
socket:func(af,type,proto){
|
||||
return invoke_iii(sock,af,type,proto);
|
||||
},
|
||||
closesocket:func(sd){
|
||||
return invoke_i(closesocket,sd);
|
||||
},
|
||||
shutdown: func(sd,how){
|
||||
return invoke_ii(shutdown,sd,how);
|
||||
},
|
||||
bind: func(sd,ip,port){
|
||||
return invoke_iii(bind,sd,ip,port);
|
||||
},
|
||||
listen: func(sd,backlog){
|
||||
return invoke_ii(listen,sd,backlog);
|
||||
},
|
||||
connect: func(sd,hostname,port){
|
||||
return invoke_iii(connect,sd,hostname,port);
|
||||
},
|
||||
accept: func(sd){
|
||||
return invoke_i(accept,sd);
|
||||
},
|
||||
send: func(sd,buff,flags=0){
|
||||
return invoke_iii(send,sd,buff,flags);
|
||||
},
|
||||
sendto: func(sd,hostname,port,buff,flags=0){
|
||||
return invoke_iiiii(sendto,sd,hostname,port,buff,flags);
|
||||
},
|
||||
recv: func(sd,len,flags=0){
|
||||
return invoke_iii(recv,sd,len,flags);
|
||||
},
|
||||
recvfrom: func(sd,len,flags=0){
|
||||
return invoke_iii(recvfrom,sd,len,flags);
|
||||
},
|
||||
errno: func(){
|
||||
return invoke(errno);
|
||||
}
|
||||
};
|
||||
}();
|
||||
74
module/makefile
Normal file
@@ -0,0 +1,74 @@
|
||||
.PHONY=clean all winall
|
||||
|
||||
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
||||
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||
|
||||
used_header = ../src/nasal.h ../src/nasal_gc.h
|
||||
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
|
||||
|
||||
STD = c++17
|
||||
OS = $(shell uname)
|
||||
ifeq ($(OS), Darwin)
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
|
||||
else
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC
|
||||
endif
|
||||
|
||||
all: $(dynamic_libs_so)
|
||||
@ echo "[Compiling] done"
|
||||
winall: $(dynamic_libs_dll)
|
||||
@ echo [Compiling] done
|
||||
|
||||
libfib.so: fib.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libfib.so"
|
||||
@ $(CXX) $(CXXFLAGS) fib.cpp -o fib.o
|
||||
@ $(CXX) -shared -o libfib.so fib.o $(used_object)
|
||||
@ rm fib.o
|
||||
libfib.dll: fib.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libfib.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
|
||||
@ del fib.o
|
||||
|
||||
libkey.so: keyboard.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libkey.so"
|
||||
@ $(CXX) $(CXXFLAGS) keyboard.cpp -o keyboard.o
|
||||
@ $(CXX) -shared -o libkey.so keyboard.o $(used_object)
|
||||
@ rm keyboard.o
|
||||
libkey.dll: keyboard.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libkey.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
|
||||
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
|
||||
@ del keyboard.o
|
||||
|
||||
libnasock.so: nasocket.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libnasock.so"
|
||||
@ $(CXX) $(CXXFLAGS) nasocket.cpp -o nasocket.o
|
||||
@ $(CXX) -shared -o libnasock.so nasocket.o $(used_object)
|
||||
@ rm nasocket.o
|
||||
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libnasock.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
|
||||
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
|
||||
@ del nasocket.o
|
||||
|
||||
libmat.so: matrix.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libmat.so"
|
||||
@ $(CXX) $(CXXFLAGS) matrix.cpp -o matrix.o
|
||||
@ $(CXX) -shared -o libmat.so matrix.o $(used_object)
|
||||
@ rm matrix.o
|
||||
libmat.dll: matrix.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libmat.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
|
||||
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
|
||||
@ del matrix.o
|
||||
|
||||
clean:
|
||||
@ echo "[clean] libfib.so" && if [ -e libfib.so ]; then rm libfib.so; fi
|
||||
@ echo "[clean] libkey.so" && if [ -e libkey.so ]; then rm libkey.so; fi
|
||||
@ echo "[clean] libnasock.so" && if [ -e libnasock.so ]; then rm libnasock.so; fi
|
||||
@ echo "[clean] libmat.so" && if [ -e libmat.so ]; then rm libmat.so; fi
|
||||
@ echo "[clean] libfib.dll" &&if [ -e libfib.dll ]; then rm libfib.dll; fi
|
||||
@ echo "[clean] libkey.dll" &&if [ -e libkey.dll ]; then rm libkey.dll; fi
|
||||
@ echo "[clean] libnasock.dll" &&if [ -e libnasock.dll ]; then rm libnasock.dll; fi
|
||||
@ echo "[clean] libmat.dll" &&if [ -e libmat.dll ]; then rm libmat.dll; fi
|
||||
300
module/matrix.cpp
Normal file
@@ -0,0 +1,300 @@
|
||||
#include "../src/nasal.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_vec2(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(args[0]);
|
||||
res.vec().elems.push_back(args[1]);
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(args[0]);
|
||||
res.vec().elems.push_back(args[1]);
|
||||
res.vec().elems.push_back(args[2]);
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_add(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_sub(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_mult(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_div(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_neg(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(-v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_norm(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto t = std::sqrt(x*x+y*y);
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(x/t));
|
||||
res.vec().elems.push_back(var::num(y/t));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_len(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
return var::num(std::sqrt(x*x+y*y));
|
||||
}
|
||||
|
||||
var nas_vec2_dot(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num());
|
||||
}
|
||||
|
||||
var nas_vec3_add(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_sub(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_mult(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_div(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_neg(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(-v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[1].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_norm(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto z = v0[2].num();
|
||||
auto t = std::sqrt(x*x+y*y+z*z);
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(x/t));
|
||||
res.vec().elems.push_back(var::num(y/t));
|
||||
res.vec().elems.push_back(var::num(z/t));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_len(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto z = v0[2].num();
|
||||
return var::num(std::sqrt(x*x+y*y+z*z));
|
||||
}
|
||||
|
||||
var nas_rotate_x(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_rotate_y(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle)));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_rotate_z(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_dot(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num()+v0[2].num()*v1[2].num());
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_vec2", nas_vec2},
|
||||
{"nas_vec2_add", nas_vec2_add},
|
||||
{"nas_vec2_sub", nas_vec2_sub},
|
||||
{"nas_vec2_mult", nas_vec2_mult},
|
||||
{"nas_vec2_div", nas_vec2_div},
|
||||
{"nas_vec2_neg", nas_vec2_neg},
|
||||
{"nas_vec2_norm", nas_vec2_norm},
|
||||
{"nas_vec2_len", nas_vec2_len},
|
||||
{"nas_vec2_dot", nas_vec2_dot},
|
||||
{"nas_vec3", nas_vec3},
|
||||
{"nas_vec3_add", nas_vec3_add},
|
||||
{"nas_vec3_sub", nas_vec3_sub},
|
||||
{"nas_vec3_mult", nas_vec3_mult},
|
||||
{"nas_vec3_div", nas_vec3_div},
|
||||
{"nas_vec3_neg", nas_vec3_neg},
|
||||
{"nas_vec3_norm", nas_vec3_norm},
|
||||
{"nas_vec3_len", nas_vec3_len},
|
||||
{"nas_rotate_x", nas_rotate_x},
|
||||
{"nas_rotate_y", nas_rotate_y},
|
||||
{"nas_rotate_z", nas_rotate_z},
|
||||
{"nas_vec3_dot", nas_vec3_dot},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
252
module/nasocket.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "../src/nasal.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h>
|
||||
#pragma comment(lib,"ws2_32")
|
||||
|
||||
class WSAmanager {
|
||||
private:
|
||||
WSAData data;
|
||||
public:
|
||||
WSAmanager() {
|
||||
WSAStartup(0x1010, &data);
|
||||
}
|
||||
~WSAmanager() {
|
||||
WSACleanup();
|
||||
}
|
||||
};
|
||||
|
||||
static WSAmanager win;
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_socket(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
|
||||
return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number");
|
||||
int sd = socket(args[0].num(), args[1].num(), args[2].num());
|
||||
return var::num(static_cast<double>(sd));
|
||||
}
|
||||
|
||||
var nas_closesocket(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("closesocket", "\"sd\" should be number");
|
||||
#ifdef _WIN32
|
||||
return var::num(static_cast<double>(closesocket(args[0].num())));
|
||||
#else
|
||||
return var::num(static_cast<double>(close(args[0].num())));
|
||||
#endif
|
||||
}
|
||||
|
||||
var nas_shutdown(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("shutdown", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("shutdown", "\"how\" must be a number");
|
||||
return var::num(static_cast<double>(shutdown(args[0].num(), args[1].num())));
|
||||
}
|
||||
|
||||
var nas_bind(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("bind", "\"sd\" muse be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("bind", "\"ip\" should be a string including an ip with correct format");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("bind", "\"port\" must be a number");
|
||||
sockaddr_in server;
|
||||
memset(&server, 0, sizeof(sockaddr_in));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = inet_addr(args[1].str().c_str());
|
||||
server.sin_port = htons(args[2].num());
|
||||
return var::num(static_cast<double>(bind(
|
||||
args[0].num(),
|
||||
(sockaddr*)&server,
|
||||
sizeof(server)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_listen(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("listen", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("listen", "\"backlog\" must be a number");
|
||||
return var::num(static_cast<double>(listen(args[0].num(), args[1].num())));
|
||||
}
|
||||
|
||||
var nas_connect(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("connect", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("connect", "\"hostname\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("connect", "\"port\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(args[2].num());
|
||||
hostent* entry = gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
|
||||
return var::num(static_cast<double>(connect(
|
||||
args[0].num(),
|
||||
(sockaddr*)&addr,
|
||||
sizeof(sockaddr_in)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_accept(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("accept", "\"sd\" must be a number");
|
||||
sockaddr_in client;
|
||||
int socklen = sizeof(sockaddr_in);
|
||||
#ifdef _WIN32
|
||||
int client_sd = accept(args[0].num(), (sockaddr*)&client, &socklen);
|
||||
#else
|
||||
int client_sd = accept(args[0].num(), (sockaddr*)&client, (socklen_t*)&socklen);
|
||||
#endif
|
||||
var res=ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
hash["sd"] = var::num(static_cast<double>(client_sd));
|
||||
hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr));
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_send(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("send", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("send", "\"buff\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("send", "\"flags\" muse be a number");
|
||||
return var::num(static_cast<double>(send(
|
||||
args[0].num(),
|
||||
args[1].str().c_str(),
|
||||
args[1].str().length(),
|
||||
args[2].num()
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_sendto(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("sendto", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("sendto", "\"hostname\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("sendto", "\"port\" must be a number");
|
||||
if (args[3].type!=vm_str)
|
||||
return nas_err("sendto", "\"buff\" must be a string");
|
||||
if (args[4].type!=vm_num)
|
||||
return nas_err("sendto", "\"flags\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(args[2].num());
|
||||
hostent* entry = gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
|
||||
return var::num(static_cast<double>(sendto(
|
||||
args[0].num(),
|
||||
args[3].str().c_str(),
|
||||
args[3].str().length(),
|
||||
args[4].num(),
|
||||
(sockaddr*)&addr,
|
||||
sizeof(sockaddr_in)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_recv(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("recv", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("recv", "\"len\" must be a number");
|
||||
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recv", "\"len\" out of range");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("recv", "\"flags\" muse be a number");
|
||||
var res = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
char* buf = new char[static_cast<int>(args[1].num())];
|
||||
auto recvsize = recv(args[0].num(), buf,args[1].num(), args[2].num());
|
||||
hash["size"] = var::num(static_cast<double>(recvsize));
|
||||
buf[recvsize>=0? recvsize:0] = 0;
|
||||
hash["str"] = ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_recvfrom(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"len\" must be a number");
|
||||
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recvfrom", "\"len\" out of range");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"flags\" muse be a number");
|
||||
sockaddr_in addr;
|
||||
int socklen = sizeof(sockaddr_in);
|
||||
var res = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
char* buf = new char[static_cast<int>(args[1].num()+1)];
|
||||
#ifdef _WIN32
|
||||
auto recvsize = recvfrom(
|
||||
args[0].num(),
|
||||
buf,
|
||||
args[1].num(),
|
||||
args[2].num(),
|
||||
(sockaddr*)&addr,
|
||||
&socklen
|
||||
);
|
||||
#else
|
||||
auto recvsize = recvfrom(
|
||||
args[0].num(),
|
||||
buf,
|
||||
args[1].num(),
|
||||
args[2].num(),
|
||||
(sockaddr*)&addr,
|
||||
(socklen_t*)&socklen
|
||||
);
|
||||
#endif
|
||||
hash["size"] = var::num(static_cast<double>(recvsize));
|
||||
buf[recvsize>=0? recvsize:0] = 0;
|
||||
hash["str"] = ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
hash["fromip"] = ngc->newstr(inet_ntoa(addr.sin_addr));
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_errno(var* args, usize size, gc* ngc) {
|
||||
return ngc->newstr(strerror(errno));
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_socket", nas_socket},
|
||||
{"nas_closesocket", nas_closesocket},
|
||||
{"nas_shutdown", nas_shutdown},
|
||||
{"nas_bind", nas_bind},
|
||||
{"nas_listen", nas_listen},
|
||||
{"nas_connect", nas_connect},
|
||||
{"nas_accept", nas_accept},
|
||||
{"nas_send", nas_send},
|
||||
{"nas_sendto", nas_sendto},
|
||||
{"nas_recv", nas_recv},
|
||||
{"nas_recvfrom", nas_recvfrom},
|
||||
{"nas_errno", nas_errno},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
140
nasal.h
@@ -1,140 +0,0 @@
|
||||
#ifndef __NASAL_H__
|
||||
#define __NASAL_H__
|
||||
|
||||
#pragma GCC optimize(2)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
check if a string can be converted to a number
|
||||
if this string cannot be converted to a number,it will return nan
|
||||
*/
|
||||
inline double hex_to_double(std::string str)
|
||||
{
|
||||
double ret=0;
|
||||
for(int i=2;str[i];++i)
|
||||
{
|
||||
ret*=16;
|
||||
if('0'<=str[i] && str[i]<='9')
|
||||
ret+=(str[i]-'0');
|
||||
else if('a'<=str[i] && str[i]<='f')
|
||||
ret+=(str[i]-'a'+10);
|
||||
else if('A'<=str[i] && str[i]<='F')
|
||||
ret+=(str[i]-'A'+10);
|
||||
else
|
||||
return std::nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline double oct_to_double(std::string str)
|
||||
{
|
||||
double ret=0;
|
||||
for(int i=2;str[i];++i)
|
||||
{
|
||||
ret*=8;
|
||||
if('0'<=str[i] && str[i]<='8')
|
||||
ret+=(str[i]-'0');
|
||||
else
|
||||
return std::nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline double dec_to_double(std::string str,int len)
|
||||
{
|
||||
double ret=0;
|
||||
int i=0;
|
||||
while('0'<=str[i] && str[i]<='9' && i<len)
|
||||
ret=ret*10+(str[i++]-'0');
|
||||
if(i==len) return ret;
|
||||
if(str[i]!='.' && str[i]!='e' && str[i]!='E')
|
||||
return std::nan("");
|
||||
if(str[i]=='.')
|
||||
{
|
||||
++i;
|
||||
if(i==len) return std::nan("");
|
||||
double num_pow=0.1;
|
||||
while('0'<=str[i] && str[i]<='9' && i<len)
|
||||
{
|
||||
ret+=num_pow*(str[i++]-'0');
|
||||
num_pow*=0.1;
|
||||
}
|
||||
}
|
||||
if(i==len) return ret;
|
||||
if(str[i]!='e' && str[i]!='E')
|
||||
return std::nan("");
|
||||
++i;
|
||||
if(i==len) return std::nan("");
|
||||
double negative=(str[i]=='-'? -1:1);
|
||||
if(str[i]=='-' || str[i]=='+')
|
||||
++i;
|
||||
if(i==len) return std::nan("");
|
||||
double num_pow=0;
|
||||
for(;i<len;++i)
|
||||
{
|
||||
if('0'<=str[i] && str[i]<='9')
|
||||
num_pow=num_pow*10+(str[i]-'0');
|
||||
else
|
||||
return std::nan("");
|
||||
}
|
||||
return ret*std::pow(10,negative*num_pow);
|
||||
}
|
||||
double trans_string_to_number(std::string str)
|
||||
{
|
||||
double is_negative=1;
|
||||
int len=str.length();
|
||||
double ret_num=0;
|
||||
if(!len) return std::nan("");
|
||||
if(str[0]=='-' || str[0]=='+')
|
||||
{
|
||||
if(len==1) return std::nan("");
|
||||
is_negative=(str[0]=='-'?-1:1);
|
||||
str=str.substr(1,len--);
|
||||
}
|
||||
if(len>1 && str[0]=='0' && str[1]=='x')
|
||||
ret_num=hex_to_double(str);
|
||||
else if(len>1 && str[0]=='0' && str[1]=='o')
|
||||
ret_num=oct_to_double(str);
|
||||
else
|
||||
ret_num=dec_to_double(str,len);
|
||||
return is_negative*ret_num;
|
||||
}
|
||||
|
||||
/*
|
||||
trans_number_to_string:
|
||||
convert number to string
|
||||
*/
|
||||
std::string trans_number_to_string(double number)
|
||||
{
|
||||
std::string res;
|
||||
std::stringstream ss;
|
||||
ss<<number;
|
||||
ss>>res;
|
||||
return res;
|
||||
}
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "nasal_runtime.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_bytecode_vm.h"
|
||||
|
||||
#endif
|
||||
233
nasal_ast.h
@@ -1,233 +0,0 @@
|
||||
#ifndef __NASAL_AST_H__
|
||||
#define __NASAL_AST_H__
|
||||
|
||||
enum ast_node
|
||||
{
|
||||
ast_null=0,ast_root,ast_block,
|
||||
ast_nil,ast_number,ast_string,ast_identifier,ast_function,ast_hash,ast_vector,
|
||||
ast_hashmember,ast_call,ast_call_hash,ast_call_vec,ast_call_func,ast_subvec,
|
||||
ast_args,ast_default_arg,ast_dynamic_id,
|
||||
ast_and,ast_or,
|
||||
ast_equal,ast_add_equal,ast_sub_equal,ast_mult_equal,ast_div_equal,ast_link_equal,
|
||||
ast_cmp_equal,ast_cmp_not_equal,
|
||||
ast_less_than,ast_less_equal,
|
||||
ast_greater_than,ast_greater_equal,
|
||||
ast_add,ast_sub,ast_mult,ast_div,ast_link,
|
||||
ast_unary_sub,ast_unary_not,
|
||||
ast_trinocular,
|
||||
ast_for,ast_forindex,ast_foreach,ast_while,ast_new_iter,
|
||||
ast_conditional,ast_if,ast_elsif,ast_else,
|
||||
ast_multi_id,ast_multi_scalar,
|
||||
ast_definition,ast_multi_assign,
|
||||
ast_continue,ast_break,ast_return
|
||||
};
|
||||
|
||||
std::string ast_str(int type)
|
||||
{
|
||||
std::string str;
|
||||
switch(type)
|
||||
{
|
||||
case ast_null: str="null";break;
|
||||
case ast_root: str="root";break;
|
||||
case ast_block: str="block";break;
|
||||
case ast_nil: str="nil";break;
|
||||
case ast_number: str="number";break;
|
||||
case ast_string: str="string";break;
|
||||
case ast_identifier: str="id";break;
|
||||
case ast_function: str="function";break;
|
||||
case ast_hash: str="hash";break;
|
||||
case ast_vector: str="vector";break;
|
||||
case ast_hashmember: str="hashmember";break;
|
||||
case ast_call: str="call";break;
|
||||
case ast_call_hash: str="call_hash";break;
|
||||
case ast_call_vec: str="call_vector";break;
|
||||
case ast_call_func: str="call_func";break;
|
||||
case ast_subvec: str="subvec";break;
|
||||
case ast_args: str="arguments";break;
|
||||
case ast_default_arg: str="default_arg";break;
|
||||
case ast_dynamic_id: str="dynamic_id";break;
|
||||
case ast_and: str="and";break;
|
||||
case ast_or: str="or";break;
|
||||
case ast_equal: str="=";break;
|
||||
case ast_add_equal: str="+=";break;
|
||||
case ast_sub_equal: str="-=";break;
|
||||
case ast_mult_equal: str="*=";break;
|
||||
case ast_div_equal: str="/=";break;
|
||||
case ast_link_equal: str="~=";break;
|
||||
case ast_cmp_equal: str="==";break;
|
||||
case ast_cmp_not_equal:str="!=";break;
|
||||
case ast_less_than: str="<";break;
|
||||
case ast_less_equal: str="<=";break;
|
||||
case ast_greater_than: str=">";break;
|
||||
case ast_greater_equal:str=">=";break;
|
||||
case ast_add: str="+";break;
|
||||
case ast_sub: str="-";break;
|
||||
case ast_mult: str="*";break;
|
||||
case ast_div: str="/";break;
|
||||
case ast_link: str="~";break;
|
||||
case ast_unary_sub: str="unary-";break;
|
||||
case ast_unary_not: str="unary!";break;
|
||||
case ast_trinocular: str="trinocular";break;
|
||||
case ast_for: str="for";break;
|
||||
case ast_forindex: str="forindex";break;
|
||||
case ast_foreach: str="foreach";break;
|
||||
case ast_while: str="while";break;
|
||||
case ast_new_iter: str="new_iterator";break;
|
||||
case ast_conditional: str="conditional";break;
|
||||
case ast_if: str="if";break;
|
||||
case ast_elsif: str="elsif";break;
|
||||
case ast_else: str="else";break;
|
||||
case ast_multi_id: str="multi_id";break;
|
||||
case ast_multi_scalar: str="multi_scalar";break;
|
||||
case ast_definition: str="definition";break;
|
||||
case ast_multi_assign: str="multi_assignment";break;
|
||||
case ast_continue: str="continue";break;
|
||||
case ast_break: str="break";break;
|
||||
case ast_return: str="return";break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
class nasal_ast
|
||||
{
|
||||
private:
|
||||
int line;
|
||||
int type;
|
||||
std::string str;
|
||||
double num;
|
||||
std::vector<nasal_ast> children;
|
||||
public:
|
||||
nasal_ast(int,int);
|
||||
nasal_ast(const nasal_ast&);
|
||||
~nasal_ast();
|
||||
nasal_ast& operator=(const nasal_ast&);
|
||||
void clear();
|
||||
void set_line(int);
|
||||
void set_type(int);
|
||||
void set_str(std::string&);
|
||||
void set_num(double);
|
||||
void add_child(nasal_ast);
|
||||
int get_line();
|
||||
int get_type();
|
||||
std::string get_str();
|
||||
double get_num();
|
||||
std::vector<nasal_ast>& get_children();
|
||||
void print_ast(int);
|
||||
};
|
||||
|
||||
nasal_ast::nasal_ast(int init_line=0,int init_type=ast_null)
|
||||
{
|
||||
this->line=init_line;
|
||||
this->type=init_type;
|
||||
return;
|
||||
}
|
||||
|
||||
nasal_ast::nasal_ast(const nasal_ast& tmp)
|
||||
{
|
||||
this->line=tmp.line;
|
||||
this->type=tmp.type;
|
||||
this->str=tmp.str;
|
||||
this->num=tmp.num;
|
||||
this->children=tmp.children;
|
||||
return;
|
||||
}
|
||||
|
||||
nasal_ast::~nasal_ast()
|
||||
{
|
||||
this->children.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
|
||||
{
|
||||
this->line=tmp.line;
|
||||
this->type=tmp.type;
|
||||
this->str=tmp.str;
|
||||
this->num=tmp.num;
|
||||
this->children=tmp.children;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void nasal_ast::clear()
|
||||
{
|
||||
this->line=0;
|
||||
this->str="";
|
||||
this->num=0;
|
||||
this->type=ast_null;
|
||||
this->children.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_ast::set_line(int l)
|
||||
{
|
||||
this->line=l;
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_ast::set_type(int t)
|
||||
{
|
||||
this->type=t;
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_ast::set_str(std::string& s)
|
||||
{
|
||||
this->str=s;
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_ast::set_num(double n)
|
||||
{
|
||||
this->num=n;
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_ast::add_child(nasal_ast ast)
|
||||
{
|
||||
children.push_back(ast);
|
||||
return;
|
||||
}
|
||||
|
||||
int nasal_ast::get_line()
|
||||
{
|
||||
return this->line;
|
||||
}
|
||||
|
||||
int nasal_ast::get_type()
|
||||
{
|
||||
return this->type;
|
||||
}
|
||||
|
||||
std::string nasal_ast::get_str()
|
||||
{
|
||||
return this->str;
|
||||
}
|
||||
|
||||
double nasal_ast::get_num()
|
||||
{
|
||||
return this->num;
|
||||
}
|
||||
|
||||
std::vector<nasal_ast>& nasal_ast::get_children()
|
||||
{
|
||||
return this->children;
|
||||
}
|
||||
|
||||
void nasal_ast::print_ast(int depth)
|
||||
{
|
||||
std::string indentation="";
|
||||
for(int i=0;i<depth;++i) indentation+="| ";
|
||||
indentation+=ast_str(this->type);
|
||||
std::cout<<indentation;
|
||||
if(this->type==ast_string || this->type==ast_identifier || this->type==ast_dynamic_id || this->type==ast_call_hash)
|
||||
std::cout<<":"<<this->str;
|
||||
else if(this->type==ast_number)
|
||||
std::cout<<":"<<this->num;
|
||||
std::cout<<std::endl;
|
||||
int child_size=this->children.size();
|
||||
for(int i=0;i<child_size;++i)
|
||||
this->children[i].print_ast(depth+1);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
837
nasal_builtin.h
@@ -1,837 +0,0 @@
|
||||
#ifndef __NASAL_BUILTIN_H__
|
||||
#define __NASAL_BUILTIN_H__
|
||||
|
||||
// builtin functions must be called inside a outer function like this:
|
||||
// var print=func(elements...)
|
||||
// {
|
||||
// nasal_call_builtin_std_cout(elements);
|
||||
// return nil;
|
||||
// }
|
||||
// builtin function nasal_call_builtin_std_cout is wrapped up by print
|
||||
|
||||
// used to find values that builtin function uses
|
||||
#define in_builtin_find(value_name_string) (local_scope_addr>=0?nasal_vm.gc_get(local_scope_addr).get_closure().get_value_address(value_name_string):-1)
|
||||
// used to check found value's type
|
||||
// types are:vm_nil vm_number vm_string vm_vector vm_hash vm_function
|
||||
// dynamic values will be generated as vector by the outer function
|
||||
#define in_builtin_check(value_addr,value_type) (nasal_vm.gc_get(value_addr).get_type()==(value_type))
|
||||
|
||||
// declaration of builtin functions
|
||||
// to add new builtin function,declare it here and write the definition below
|
||||
int builtin_print(int,nasal_virtual_machine&);
|
||||
int builtin_append(int,nasal_virtual_machine&);
|
||||
int builtin_setsize(int,nasal_virtual_machine&);
|
||||
int builtin_system(int,nasal_virtual_machine&);
|
||||
int builtin_input(int,nasal_virtual_machine&);
|
||||
int builtin_sleep(int,nasal_virtual_machine&);
|
||||
int builtin_finput(int,nasal_virtual_machine&);
|
||||
int builtin_foutput(int,nasal_virtual_machine&);
|
||||
int builtin_split(int,nasal_virtual_machine&);
|
||||
int builtin_rand(int,nasal_virtual_machine&);
|
||||
int builtin_id(int,nasal_virtual_machine&);
|
||||
int builtin_int(int,nasal_virtual_machine&);
|
||||
int builtin_num(int,nasal_virtual_machine&);
|
||||
int builtin_pop(int,nasal_virtual_machine&);
|
||||
int builtin_str(int,nasal_virtual_machine&);
|
||||
int builtin_size(int,nasal_virtual_machine&);
|
||||
int builtin_xor(int,nasal_virtual_machine&);
|
||||
int builtin_and(int,nasal_virtual_machine&);
|
||||
int builtin_or(int,nasal_virtual_machine&);
|
||||
int builtin_nand(int,nasal_virtual_machine&);
|
||||
int builtin_not(int,nasal_virtual_machine&);
|
||||
int builtin_sin(int,nasal_virtual_machine&);
|
||||
int builtin_cos(int,nasal_virtual_machine&);
|
||||
int builtin_tan(int,nasal_virtual_machine&);
|
||||
int builtin_exp(int,nasal_virtual_machine&);
|
||||
int builtin_ln(int,nasal_virtual_machine&);
|
||||
int builtin_sqrt(int,nasal_virtual_machine&);
|
||||
int builtin_atan2(int,nasal_virtual_machine&);
|
||||
int builtin_time(int,nasal_virtual_machine&);
|
||||
int builtin_contains(int,nasal_virtual_machine&);
|
||||
int builtin_delete(int,nasal_virtual_machine&);
|
||||
int builtin_getkeys(int,nasal_virtual_machine&);
|
||||
int builtin_import(int,nasal_virtual_machine&);
|
||||
bool builtin_die_state;// used in builtin_die
|
||||
int builtin_die(int,nasal_virtual_machine&);
|
||||
int builtin_type(int,nasal_virtual_machine&);
|
||||
int builtin_substr(int,nasal_virtual_machine&);
|
||||
|
||||
// register builtin function's name and it's address here in this table below
|
||||
// this table must and with {"",NULL}
|
||||
struct FUNC_TABLE
|
||||
{
|
||||
std::string func_name;
|
||||
int (*func_pointer)(int x,nasal_virtual_machine& nasal_vm);
|
||||
} builtin_func_table[]=
|
||||
{
|
||||
{"nasal_call_builtin_std_cout", builtin_print},
|
||||
{"nasal_call_builtin_push_back", builtin_append},
|
||||
{"nasal_call_builtin_set_size", builtin_setsize},
|
||||
{"nasal_call_builtin_system", builtin_system},
|
||||
{"nasal_call_builtin_input", builtin_input},
|
||||
{"nasal_call_builtin_sleep", builtin_sleep},
|
||||
{"nasal_call_builtin_finput", builtin_finput},
|
||||
{"nasal_call_builtin_foutput", builtin_foutput},
|
||||
{"nasal_call_builtin_split", builtin_split},
|
||||
{"nasal_call_builtin_rand", builtin_rand},
|
||||
{"nasal_call_builtin_get_id", builtin_id},
|
||||
{"nasal_call_builtin_trans_int", builtin_int},
|
||||
{"nasal_call_builtin_trans_num", builtin_num},
|
||||
{"nasal_call_builtin_pop_back", builtin_pop},
|
||||
{"nasal_call_builtin_trans_str", builtin_str},
|
||||
{"nasal_call_builtin_size", builtin_size},
|
||||
{"nasal_call_builtin_xor", builtin_xor},
|
||||
{"nasal_call_builtin_and", builtin_and},
|
||||
{"nasal_call_builtin_or", builtin_or},
|
||||
{"nasal_call_builtin_nand", builtin_nand},
|
||||
{"nasal_call_builtin_not", builtin_not},
|
||||
{"nasal_call_builtin_sin", builtin_sin},
|
||||
{"nasal_call_builtin_cos", builtin_cos},
|
||||
{"nasal_call_builtin_tan", builtin_tan},
|
||||
{"nasal_call_builtin_exp", builtin_exp},
|
||||
{"nasal_call_builtin_cpp_math_ln", builtin_ln},
|
||||
{"nasal_call_builtin_cpp_math_sqrt", builtin_sqrt},
|
||||
{"nasal_call_builtin_cpp_atan2", builtin_atan2},
|
||||
{"nasal_call_builtin_time", builtin_time},
|
||||
{"nasal_call_builtin_contains", builtin_contains},
|
||||
{"nasal_call_builtin_delete", builtin_delete},
|
||||
{"nasal_call_builtin_get_keys", builtin_getkeys},
|
||||
{"nasal_call_import", builtin_import},
|
||||
{"nasal_call_builtin_die", builtin_die},
|
||||
{"nasal_call_builtin_type", builtin_type},
|
||||
{"nasal_call_builtin_substr", builtin_substr},
|
||||
{"", NULL}
|
||||
};
|
||||
|
||||
int builtin_print(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
// get arguments
|
||||
int vector_value_addr=in_builtin_find("elements");
|
||||
if(vector_value_addr<0 || !in_builtin_check(vector_value_addr,vm_vector))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_print: \"elements\" has wrong value type(must be vector).\n";
|
||||
return -1;
|
||||
}
|
||||
// main process
|
||||
nasal_vector& ref_vec=nasal_vm.gc_get(vector_value_addr).get_vector();
|
||||
int size=ref_vec.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
nasal_scalar& tmp=nasal_vm.gc_get(ref_vec.get_value_address(i));
|
||||
switch(tmp.get_type())
|
||||
{
|
||||
case vm_nil:std::cout<<"nil";break;
|
||||
case vm_number:std::cout<<tmp.get_number();break;
|
||||
case vm_string:std::cout<<tmp.get_string();break;
|
||||
case vm_vector:tmp.get_vector().print();break;
|
||||
case vm_hash:tmp.get_hash().print();break;
|
||||
case vm_function:std::cout<<"func(...){...}";break;
|
||||
}
|
||||
}
|
||||
std::cout<<"\n";
|
||||
// generate return value
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_append(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int vector_value_addr=in_builtin_find("vector");
|
||||
int elem_value_addr=in_builtin_find("elements");
|
||||
if(vector_value_addr<0 || !in_builtin_check(vector_value_addr,vm_vector))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_append: \"vector\" has wrong value type(must be vector).\n";
|
||||
return -1;
|
||||
}
|
||||
if(elem_value_addr<0 || !in_builtin_check(elem_value_addr,vm_vector))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_append: \"elements\" has wrong value type(must be vector).\n";
|
||||
return -1;
|
||||
}
|
||||
nasal_vector& ref_vector=nasal_vm.gc_get(vector_value_addr).get_vector();
|
||||
nasal_vector& ref_elements=nasal_vm.gc_get(elem_value_addr).get_vector();
|
||||
int size=ref_elements.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
int value_address=ref_elements.get_value_address(i);
|
||||
nasal_vm.add_reference(value_address);
|
||||
ref_vector.add_elem(value_address);
|
||||
}
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_setsize(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int vector_value_addr=in_builtin_find("vector");
|
||||
int size_value_addr=in_builtin_find("size");
|
||||
if(vector_value_addr<0 || nasal_vm.gc_get(vector_value_addr).get_type()!=vm_vector)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_setsize: \"vector\" has wrong value type(must be vector).\n";
|
||||
return -1;
|
||||
}
|
||||
if(size_value_addr<0)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_setsize: \"size\" has wrong value type(must be string or number).\n";
|
||||
return -1;
|
||||
}
|
||||
int type=nasal_vm.gc_get(size_value_addr).get_type();
|
||||
if(type!=vm_number && type!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_setsize: size is not a number.\n";
|
||||
return -1;
|
||||
}
|
||||
int number;
|
||||
if(type==vm_number)
|
||||
number=(int)nasal_vm.gc_get(size_value_addr).get_number();
|
||||
else
|
||||
{
|
||||
std::string str=nasal_vm.gc_get(size_value_addr).get_string();
|
||||
double tmp=trans_string_to_number(str);
|
||||
if(std::isnan(tmp))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_setsize: size is not a numerable string.\n";
|
||||
return -1;
|
||||
}
|
||||
number=(int)tmp;
|
||||
}
|
||||
if(number<0)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_setsize: size must be greater than -1.\n";
|
||||
return -1;
|
||||
}
|
||||
nasal_vector& ref_vector=nasal_vm.gc_get(vector_value_addr).get_vector();
|
||||
int vec_size=ref_vector.size();
|
||||
if(number<vec_size)
|
||||
for(int i=number;i<vec_size;++i)
|
||||
{
|
||||
int addr=ref_vector.del_elem();
|
||||
if(addr>=0)
|
||||
nasal_vm.del_reference(addr);
|
||||
}
|
||||
else if(number>vec_size)
|
||||
for(int i=vec_size;i<number;++i)
|
||||
{
|
||||
int new_val_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
ref_vector.add_elem(new_val_addr);
|
||||
}
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_system(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int str_value_addr=in_builtin_find("str");
|
||||
if(str_value_addr<0 || nasal_vm.gc_get(str_value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_system: \"str\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string str=nasal_vm.gc_get(str_value_addr).get_string();
|
||||
int size=str.length();
|
||||
char* command=new char[size+1];
|
||||
for(int i=0;i<size;++i)
|
||||
command[i]=str[i];
|
||||
command[size]='\0';
|
||||
system(command);
|
||||
delete []command;
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_input(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_string);
|
||||
std::string str;
|
||||
std::cin>>str;
|
||||
nasal_vm.gc_get(ret_addr).set_string(str);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_sleep(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("duration");
|
||||
if(value_addr<0 || (nasal_vm.gc_get(value_addr).get_type()!=vm_string && nasal_vm.gc_get(value_addr).get_type()!=vm_number))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_sleep: \"duration\" has wrong value type(must be string or number).\n";
|
||||
return -1;
|
||||
}
|
||||
unsigned long sleep_time=0;
|
||||
if(nasal_vm.gc_get(value_addr).get_type()==vm_string)
|
||||
{
|
||||
std::string str=nasal_vm.gc_get(value_addr).get_string();
|
||||
double number=trans_string_to_number(str);
|
||||
if(std::isnan(number))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_sleep: this is not a numerable string.\n";
|
||||
return -1;
|
||||
}sleep_time=(unsigned long)number;
|
||||
}
|
||||
else
|
||||
sleep_time=(unsigned long)nasal_vm.gc_get(value_addr).get_number();
|
||||
sleep(sleep_time); // sleep in unistd.h will make this progress sleep sleep_time seconds.
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_finput(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("filename");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_finput: \"filename\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string filename=nasal_vm.gc_get(value_addr).get_string();
|
||||
std::ifstream fin(filename);
|
||||
std::string file_content="";
|
||||
if(!fin.fail())
|
||||
while(!fin.eof())
|
||||
{
|
||||
char c=fin.get();
|
||||
if(fin.eof())
|
||||
break;
|
||||
file_content.push_back(c);
|
||||
}
|
||||
else
|
||||
file_content="";
|
||||
fin.close();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_string);
|
||||
nasal_vm.gc_get(ret_addr).set_string(file_content);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_foutput(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("filename");
|
||||
int str_value_addr=in_builtin_find("str");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_foutput: \"filename\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
if(str_value_addr<0 || nasal_vm.gc_get(str_value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_foutput: \"str\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string filename=nasal_vm.gc_get(value_addr).get_string();
|
||||
std::string file_content=nasal_vm.gc_get(str_value_addr).get_string();
|
||||
std::ofstream fout(filename);
|
||||
fout<<file_content;
|
||||
fout.close();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
int builtin_split(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int delimeter_value_addr=in_builtin_find("delimeter");
|
||||
int string_value_addr=in_builtin_find("string");
|
||||
if(delimeter_value_addr<0 || nasal_vm.gc_get(delimeter_value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_split: \"delimeter\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
if(string_value_addr<0 || nasal_vm.gc_get(string_value_addr).get_type()!=vm_string)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_split: \"string\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string delimeter=nasal_vm.gc_get(delimeter_value_addr).get_string();
|
||||
std::string source=nasal_vm.gc_get(string_value_addr).get_string();
|
||||
int delimeter_len=delimeter.length();
|
||||
int source_len=source.length();
|
||||
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_vector);
|
||||
nasal_vector& ref_vec=nasal_vm.gc_get(ret_addr).get_vector();
|
||||
std::string tmp="";
|
||||
|
||||
if(!delimeter_len)
|
||||
{
|
||||
for(int i=0;i<source_len;++i)
|
||||
{
|
||||
tmp+=source[i];
|
||||
int str_addr=nasal_vm.gc_alloc(vm_string);
|
||||
nasal_vm.gc_get(str_addr).set_string(tmp);
|
||||
ref_vec.add_elem(str_addr);
|
||||
tmp="";
|
||||
}
|
||||
return ret_addr;
|
||||
}
|
||||
|
||||
for(int i=0;i<source_len;++i)
|
||||
{
|
||||
bool check_delimeter=false;
|
||||
if(source[i]==delimeter[0])
|
||||
for(int j=0;j<delimeter_len;++j)
|
||||
{
|
||||
if(i+j>=source_len || source[i+j]!=delimeter[j])
|
||||
break;
|
||||
if(j==delimeter_len-1)
|
||||
check_delimeter=true;
|
||||
}
|
||||
if(check_delimeter)
|
||||
{
|
||||
int str_addr=nasal_vm.gc_alloc(vm_string);
|
||||
nasal_vm.gc_get(str_addr).set_string(tmp);
|
||||
ref_vec.add_elem(str_addr);
|
||||
tmp="";
|
||||
i+=delimeter_len-1;
|
||||
}
|
||||
else
|
||||
tmp+=source[i];
|
||||
}
|
||||
if(tmp.length())
|
||||
{
|
||||
int str_addr=nasal_vm.gc_alloc(vm_string);
|
||||
nasal_vm.gc_get(str_addr).set_string(tmp);
|
||||
ref_vec.add_elem(str_addr);
|
||||
tmp="";
|
||||
}
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_rand(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("seed");
|
||||
if(value_addr<0 || (nasal_vm.gc_get(value_addr).get_type()!=vm_number && nasal_vm.gc_get(value_addr).get_type()!=vm_nil))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_rand: \"seed\" has wrong value type(must be nil or number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(nasal_vm.gc_get(value_addr).get_type()==vm_number)
|
||||
{
|
||||
unsigned int number=(unsigned int)nasal_vm.gc_get(value_addr).get_number();
|
||||
srand(number);
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
double num=0;
|
||||
for(int i=0;i<5;++i)
|
||||
num=(num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(num);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_id(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("thing");
|
||||
if(value_addr<0)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_id: cannot find \"thing\".\n";
|
||||
return -1;
|
||||
}
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)value_addr);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_int(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("value");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_int: \"value\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number=(int)nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)number);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_num(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("value");
|
||||
if(value_addr<0 || !in_builtin_check(value_addr,vm_string))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_num: \"value\" has wrong value type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string str=nasal_vm.gc_get(value_addr).get_string();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(trans_string_to_number(str));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_pop(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("vector");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_vector)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_pop: \"vector\" has wrong value type(must be vector).\n";
|
||||
return -1;
|
||||
}
|
||||
int ret_addr=nasal_vm.gc_get(value_addr).get_vector().del_elem();
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_str(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("number");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_str: \"number\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_string(trans_number_to_string(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_size(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("object");
|
||||
if(value_addr<0)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_size: cannot find value \"object\".\n";
|
||||
return -1;
|
||||
}
|
||||
int type=nasal_vm.gc_get(value_addr).get_type();
|
||||
int number=-1;
|
||||
switch(type)
|
||||
{
|
||||
case vm_nil:
|
||||
case vm_number:
|
||||
case vm_function:
|
||||
case vm_closure:break;
|
||||
case vm_string:number=nasal_vm.gc_get(value_addr).get_string().length();break;
|
||||
case vm_vector:number=nasal_vm.gc_get(value_addr).get_vector().size();break;
|
||||
case vm_hash:number=nasal_vm.gc_get(value_addr).get_hash().size();break;
|
||||
}
|
||||
int ret_addr=-1;
|
||||
if(number<0)
|
||||
ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
else
|
||||
{
|
||||
ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)number);
|
||||
}
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_xor(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int a_addr=in_builtin_find("a");
|
||||
int b_addr=in_builtin_find("b");
|
||||
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_xor: \"a\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_xor: \"b\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
|
||||
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)(number_a^number_b));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_and(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int a_addr=in_builtin_find("a");
|
||||
int b_addr=in_builtin_find("b");
|
||||
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_and: \"a\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_and: \"b\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
|
||||
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)(number_a&number_b));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_or(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int a_addr=in_builtin_find("a");
|
||||
int b_addr=in_builtin_find("b");
|
||||
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_or: \"a\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_or: \"b\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
|
||||
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)(number_a|number_b));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_nand(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int a_addr=in_builtin_find("a");
|
||||
int b_addr=in_builtin_find("b");
|
||||
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_nand: \"a\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(b_addr<0 || nasal_vm.gc_get(b_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_nand: \"b\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number_a=(int)nasal_vm.gc_get(a_addr).get_number();
|
||||
int number_b=(int)nasal_vm.gc_get(b_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)(~(number_a&number_b)));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_not(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int a_addr=in_builtin_find("a");
|
||||
if(a_addr<0 || nasal_vm.gc_get(a_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_not: \"a\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
int number=(int)nasal_vm.gc_get(a_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)(~number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_sin(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_sin: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(sin(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_cos(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_cos: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(cos(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_tan(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_tan: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(tan(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_exp(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_exp: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(exp(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_ln(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_ln: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(log(number)/log(2.7182818284590452354));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_sqrt(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("x");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_sqrt: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double number=nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(sqrt(number));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_atan2(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int x_value_addr=in_builtin_find("x");
|
||||
int y_value_addr=in_builtin_find("y");
|
||||
if(x_value_addr<0 || nasal_vm.gc_get(x_value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_atan2: \"x\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(y_value_addr<0 || nasal_vm.gc_get(y_value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_atan2: \"y\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
double x=nasal_vm.gc_get(x_value_addr).get_number();
|
||||
double y=nasal_vm.gc_get(y_value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number(atan2(y,x));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_time(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("begin_time");
|
||||
if(value_addr<0 || nasal_vm.gc_get(value_addr).get_type()!=vm_number)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_time: \"begin_time\" has wrong value type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
time_t begin_time=(time_t)nasal_vm.gc_get(value_addr).get_number();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)time(&begin_time));
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_contains(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int hash_addr=in_builtin_find("hash");
|
||||
int key_addr=in_builtin_find("key");
|
||||
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_contains: \"hash\" has wrong type(must be hash).\n";
|
||||
return -1;
|
||||
}
|
||||
if(key_addr<0 || !in_builtin_check(key_addr,vm_string))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_contains: \"key\" has wrong type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string key=nasal_vm.gc_get(key_addr).get_string();
|
||||
bool contains=nasal_vm.gc_get(hash_addr).get_hash().check_contain(key);
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_number);
|
||||
nasal_vm.gc_get(ret_addr).set_number((double)contains);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_delete(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int hash_addr=in_builtin_find("hash");
|
||||
int key_addr=in_builtin_find("key");
|
||||
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_delete: \"hash\" has wrong type(must be hash).\n";
|
||||
return -1;
|
||||
}
|
||||
if(key_addr<0 || !in_builtin_check(key_addr,vm_string))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_delete: \"key\" has wrong type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string key=nasal_vm.gc_get(key_addr).get_string();
|
||||
nasal_vm.gc_get(hash_addr).get_hash().del_elem(key);
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_getkeys(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int hash_addr=in_builtin_find("hash");
|
||||
if(hash_addr<0 || !in_builtin_check(hash_addr,vm_hash))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_delete: \"hash\" has wrong type(must be hash).\n";
|
||||
return -1;
|
||||
}
|
||||
int ret_addr=nasal_vm.gc_get(hash_addr).get_hash().get_keys();
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_import(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
// this function is used in preprocessing.
|
||||
// this function will return nothing when running.
|
||||
std::cout<<">> [runtime] builtin_import: cannot use import when running.\n";
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_die(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int str_addr=in_builtin_find("str");
|
||||
if(str_addr<0 || !in_builtin_check(str_addr,vm_string))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_die: \"str\" has wrong type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
builtin_die_state=true;
|
||||
std::cout<<">> [runtime] error: "<<nasal_vm.gc_get(str_addr).get_string()<<'\n';
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_nil);
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_type(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int value_addr=in_builtin_find("object");
|
||||
if(value_addr<0)
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_type: cannot find \"object\".\n";
|
||||
return -1;
|
||||
}
|
||||
int type=nasal_vm.gc_get(value_addr).get_type();
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_string);
|
||||
switch(type)
|
||||
{
|
||||
case vm_nil: nasal_vm.gc_get(ret_addr).set_string("nil");break;
|
||||
case vm_number: nasal_vm.gc_get(ret_addr).set_string("number");break;
|
||||
case vm_string: nasal_vm.gc_get(ret_addr).set_string("string");break;
|
||||
case vm_vector: nasal_vm.gc_get(ret_addr).set_string("vector");break;
|
||||
case vm_hash: nasal_vm.gc_get(ret_addr).set_string("hash");break;
|
||||
case vm_function: nasal_vm.gc_get(ret_addr).set_string("function");break;
|
||||
}
|
||||
return ret_addr;
|
||||
}
|
||||
int builtin_substr(int local_scope_addr,nasal_virtual_machine& nasal_vm)
|
||||
{
|
||||
int str_addr=in_builtin_find("str");
|
||||
int begin_addr=in_builtin_find("begin");
|
||||
int length_addr=in_builtin_find("length");
|
||||
if(str_addr<0 || !in_builtin_check(str_addr,vm_string))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_substr: cannot find \"str\" or wrong type(must be string).\n";
|
||||
return -1;
|
||||
}
|
||||
if(begin_addr<0 || !in_builtin_check(begin_addr,vm_number))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_substr: cannot find \"begin\" or wrong type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
if(length_addr<0 || !in_builtin_check(length_addr,vm_number))
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_substr: cannot find \"length\" or wrong type(must be number).\n";
|
||||
return -1;
|
||||
}
|
||||
std::string str=nasal_vm.gc_get(str_addr).get_string();
|
||||
int begin=(int)nasal_vm.gc_get(begin_addr).get_number();
|
||||
int len=(int)nasal_vm.gc_get(length_addr).get_number();
|
||||
if(begin>=str.length() || begin+len>=str.length())
|
||||
{
|
||||
std::cout<<">> [runtime] builtin_substr: index out of range.\n";
|
||||
return -1;
|
||||
}
|
||||
std::string tmp="";
|
||||
for(int i=begin;i<begin+len;++i)
|
||||
tmp+=str[i];
|
||||
int ret_addr=nasal_vm.gc_alloc(vm_string);
|
||||
nasal_vm.gc_get(ret_addr).set_string(tmp);
|
||||
return ret_addr;
|
||||
}
|
||||
#endif
|
||||
1466
nasal_bytecode_vm.h
1267
nasal_codegen.h
744
nasal_gc.h
@@ -1,744 +0,0 @@
|
||||
#ifndef __NASAL_GC_H__
|
||||
#define __NASAL_GC_H__
|
||||
|
||||
enum runtime_scalar_type
|
||||
{
|
||||
vm_nil=0,
|
||||
vm_number,
|
||||
vm_string,
|
||||
vm_closure,
|
||||
vm_function,
|
||||
vm_vector,
|
||||
vm_hash
|
||||
};
|
||||
/*
|
||||
nasal_number: basic type(double)
|
||||
nasal_string: basic type(std::string)
|
||||
nasal_vector: elems[i] -> address in memory -> value address in gc
|
||||
nasal_hash: elems[key] -> address in memory -> value address in gc
|
||||
nasal_function: closure -> value address in gc(type: nasal_closure)
|
||||
nasal_closure: std::list<std::map<std::string,int>> -> std::map<std::string,int> -> (int) -> address in memory -> value address in gc
|
||||
*/
|
||||
|
||||
class nasal_virtual_machine;
|
||||
|
||||
class nasal_vector
|
||||
{
|
||||
private:
|
||||
// this int points to the space in nasal_vm::memory_manager_memory
|
||||
nasal_virtual_machine& vm;
|
||||
std::vector<int> elems;
|
||||
public:
|
||||
nasal_vector(nasal_virtual_machine&);
|
||||
~nasal_vector();
|
||||
void add_elem(int);
|
||||
int del_elem();
|
||||
int size();
|
||||
int get_value_address(int);
|
||||
int* get_mem_address(int);
|
||||
void print();
|
||||
};
|
||||
|
||||
class nasal_hash
|
||||
{
|
||||
private:
|
||||
// this int points to the space in nasal_vm::memory_manager_memory
|
||||
nasal_virtual_machine& vm;
|
||||
std::map<std::string,int> elems;
|
||||
public:
|
||||
nasal_hash(nasal_virtual_machine&);
|
||||
~nasal_hash();
|
||||
void add_elem(std::string,int);
|
||||
void del_elem(std::string);
|
||||
int size();
|
||||
int get_special_para(std::string);
|
||||
int get_value_address(std::string);
|
||||
int* get_mem_address(std::string);
|
||||
bool check_contain(std::string);
|
||||
int get_keys();
|
||||
void print();
|
||||
};
|
||||
|
||||
class nasal_function
|
||||
{
|
||||
private:
|
||||
nasal_virtual_machine& vm;
|
||||
int entry;
|
||||
int closure_addr;
|
||||
nasal_ast argument_list;
|
||||
nasal_ast function_expr;
|
||||
std::vector<std::string> para_name;
|
||||
std::string dynamic_para_name;
|
||||
std::vector<int> default_para_addr;
|
||||
public:
|
||||
nasal_function(nasal_virtual_machine&);
|
||||
~nasal_function();
|
||||
void set_entry(int);
|
||||
int get_entry();
|
||||
void add_para(std::string,int,bool);
|
||||
std::vector<std::string>& get_para();
|
||||
std::string get_dynamic_para();
|
||||
std::vector<int>& get_default();
|
||||
void set_closure_addr(int);
|
||||
int get_closure_addr();
|
||||
void set_arguments(nasal_ast&);
|
||||
nasal_ast& get_arguments();
|
||||
void set_run_block(nasal_ast&);
|
||||
nasal_ast& get_run_block();
|
||||
};
|
||||
|
||||
class nasal_closure
|
||||
{
|
||||
private:
|
||||
// int in std::map<std::string,int> points to the space in nasal_vm::memory_manager_memory
|
||||
// and this memory_manager_memory space stores an address to garbage_collector_memory
|
||||
// and this address points to an nasal_hash
|
||||
nasal_virtual_machine& vm;
|
||||
std::list<std::map<std::string,int> > elems;
|
||||
public:
|
||||
nasal_closure(nasal_virtual_machine&);
|
||||
~nasal_closure();
|
||||
void add_scope();
|
||||
void del_scope();
|
||||
void add_new_value(std::string,int);
|
||||
int get_value_address(std::string);
|
||||
int* get_mem_address(std::string);
|
||||
void set_closure(nasal_closure&);
|
||||
};
|
||||
|
||||
class nasal_scalar
|
||||
{
|
||||
protected:
|
||||
int type;
|
||||
void* scalar_ptr;
|
||||
public:
|
||||
nasal_scalar();
|
||||
~nasal_scalar();
|
||||
void clear();
|
||||
void set_type(int,nasal_virtual_machine&);
|
||||
void set_number(double);
|
||||
void set_string(std::string);
|
||||
int get_type();
|
||||
double get_number();
|
||||
std::string get_string();
|
||||
nasal_vector& get_vector();
|
||||
nasal_hash& get_hash();
|
||||
nasal_function& get_func();
|
||||
nasal_closure& get_closure();
|
||||
};
|
||||
|
||||
class nasal_virtual_machine
|
||||
{
|
||||
struct gc_unit
|
||||
{
|
||||
int ref_cnt;
|
||||
nasal_scalar elem;
|
||||
gc_unit()
|
||||
{
|
||||
ref_cnt=0;
|
||||
return;
|
||||
}
|
||||
};
|
||||
private:
|
||||
nasal_scalar error_returned_value;
|
||||
std::queue<int> garbage_collector_free_space;
|
||||
std::vector<gc_unit*> garbage_collector_memory;
|
||||
public:
|
||||
~nasal_virtual_machine();
|
||||
void clear();
|
||||
void debug();
|
||||
int gc_alloc(int); // garbage collector gives a new space
|
||||
nasal_scalar& gc_get(int); // get scalar that stored in gc
|
||||
void add_reference(int);
|
||||
void del_reference(int);
|
||||
};
|
||||
|
||||
/*functions of nasal_vector*/
|
||||
nasal_vector::nasal_vector(nasal_virtual_machine& nvm):vm(nvm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nasal_vector::~nasal_vector()
|
||||
{
|
||||
int size=elems.size();
|
||||
for(int i=0;i<size;++i)
|
||||
vm.del_reference(elems[i]);
|
||||
elems.clear();
|
||||
return;
|
||||
}
|
||||
void nasal_vector::add_elem(int value_address)
|
||||
{
|
||||
elems.push_back(value_address);
|
||||
return;
|
||||
}
|
||||
int nasal_vector::del_elem()
|
||||
{
|
||||
// pop back
|
||||
if(!elems.size())
|
||||
return -1;
|
||||
int ret=elems.back();
|
||||
elems.pop_back();
|
||||
return ret;
|
||||
}
|
||||
int nasal_vector::size()
|
||||
{
|
||||
return elems.size();
|
||||
}
|
||||
int nasal_vector::get_value_address(int index)
|
||||
{
|
||||
int vec_size=elems.size();
|
||||
int left_range=-vec_size;
|
||||
int right_range=vec_size-1;
|
||||
if(index<left_range || index>right_range)
|
||||
{
|
||||
std::cout<<">> [runtime] nasal_vector::get_value_address: index out of range: "<<index<<"\n";
|
||||
return -1;
|
||||
}
|
||||
return elems[(index+vec_size)%vec_size];
|
||||
}
|
||||
int* nasal_vector::get_mem_address(int index)
|
||||
{
|
||||
int vec_size=elems.size();
|
||||
int left_range=-vec_size;
|
||||
int right_range=vec_size-1;
|
||||
if(index<left_range || index>right_range)
|
||||
{
|
||||
std::cout<<">> [runtime] nasal_vector::get_mem_address: index out of range: "<<index<<"\n";
|
||||
return NULL;
|
||||
}
|
||||
return &elems[(index+vec_size)%vec_size];
|
||||
}
|
||||
void nasal_vector::print()
|
||||
{
|
||||
int size=elems.size();
|
||||
std::cout<<"[";
|
||||
if(!size)
|
||||
std::cout<<"]";
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
nasal_scalar& tmp=vm.gc_get(elems[i]);
|
||||
switch(tmp.get_type())
|
||||
{
|
||||
case vm_nil:std::cout<<"nil";break;
|
||||
case vm_number:std::cout<<tmp.get_number();break;
|
||||
case vm_string:std::cout<<tmp.get_string();break;
|
||||
case vm_vector:tmp.get_vector().print();break;
|
||||
case vm_hash:tmp.get_hash().print();break;
|
||||
case vm_function:std::cout<<"func(...){...}";break;
|
||||
}
|
||||
std::cout<<",]"[i==size-1];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*functions of nasal_hash*/
|
||||
nasal_hash::nasal_hash(nasal_virtual_machine& nvm):vm(nvm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nasal_hash::~nasal_hash()
|
||||
{
|
||||
for(std::map<std::string,int>::iterator iter=elems.begin();iter!=elems.end();++iter)
|
||||
vm.del_reference(iter->second);
|
||||
elems.clear();
|
||||
return;
|
||||
}
|
||||
void nasal_hash::add_elem(std::string key,int value_address)
|
||||
{
|
||||
if(elems.find(key)==elems.end())
|
||||
elems[key]=value_address;
|
||||
return;
|
||||
}
|
||||
void nasal_hash::del_elem(std::string key)
|
||||
{
|
||||
if(elems.find(key)!=elems.end())
|
||||
{
|
||||
vm.del_reference(elems[key]);
|
||||
elems.erase(key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int nasal_hash::size()
|
||||
{
|
||||
return elems.size();
|
||||
}
|
||||
int nasal_hash::get_special_para(std::string key)
|
||||
{
|
||||
if(elems.find(key)!=elems.end())
|
||||
return elems[key];
|
||||
return -1;
|
||||
}
|
||||
int nasal_hash::get_value_address(std::string key)
|
||||
{
|
||||
int ret_value_addr=-1;
|
||||
if(elems.find(key)!=elems.end())
|
||||
return elems[key];
|
||||
else if(elems.find("parents")!=elems.end())
|
||||
{
|
||||
int val_addr=elems["parents"];
|
||||
if(vm.gc_get(val_addr).get_type()==vm_vector)
|
||||
{
|
||||
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
|
||||
int size=vec_ref.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
int tmp_val_addr=vec_ref.get_value_address(i);
|
||||
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
|
||||
ret_value_addr=vm.gc_get(tmp_val_addr).get_hash().get_value_address(key);
|
||||
if(ret_value_addr>=0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret_value_addr;
|
||||
}
|
||||
int* nasal_hash::get_mem_address(std::string key)
|
||||
{
|
||||
int* mem_addr=NULL;
|
||||
if(elems.find(key)!=elems.end())
|
||||
return &elems[key];
|
||||
else if(elems.find("parents")!=elems.end())
|
||||
{
|
||||
int val_addr=elems["parents"];
|
||||
if(vm.gc_get(val_addr).get_type()==vm_vector)
|
||||
{
|
||||
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
|
||||
int size=vec_ref.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
int tmp_val_addr=vec_ref.get_value_address(i);
|
||||
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
|
||||
mem_addr=vm.gc_get(tmp_val_addr).get_hash().get_mem_address(key);
|
||||
if(mem_addr>0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mem_addr;
|
||||
}
|
||||
bool nasal_hash::check_contain(std::string key)
|
||||
{
|
||||
if(elems.find(key)!=elems.end())
|
||||
return true;
|
||||
if(elems.find("parents")!=elems.end())
|
||||
{
|
||||
bool result=false;
|
||||
int val_addr=elems["parents"];
|
||||
if(vm.gc_get(val_addr).get_type()==vm_vector)
|
||||
{
|
||||
nasal_vector& vec_ref=vm.gc_get(val_addr).get_vector();
|
||||
int size=vec_ref.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
int tmp_val_addr=vec_ref.get_value_address(i);
|
||||
if(vm.gc_get(tmp_val_addr).get_type()==vm_hash)
|
||||
result=vm.gc_get(tmp_val_addr).get_hash().check_contain(key);
|
||||
if(result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int nasal_hash::get_keys()
|
||||
{
|
||||
int ret_addr=vm.gc_alloc(vm_vector);
|
||||
nasal_vector& ref_vec=vm.gc_get(ret_addr).get_vector();
|
||||
for(std::map<std::string,int>::iterator iter=elems.begin();iter!=elems.end();++iter)
|
||||
{
|
||||
int str_addr=vm.gc_alloc(vm_string);
|
||||
vm.gc_get(str_addr).set_string(iter->first);
|
||||
ref_vec.add_elem(str_addr);
|
||||
}
|
||||
return ret_addr;
|
||||
}
|
||||
void nasal_hash::print()
|
||||
{
|
||||
std::cout<<"{";
|
||||
if(!elems.size())
|
||||
std::cout<<"}";
|
||||
for(std::map<std::string,int>::iterator i=elems.begin();i!=elems.end();++i)
|
||||
{
|
||||
std::cout<<i->first<<":";
|
||||
nasal_scalar& tmp=vm.gc_get(i->second);
|
||||
switch(tmp.get_type())
|
||||
{
|
||||
case vm_nil:std::cout<<"nil";break;
|
||||
case vm_number:std::cout<<tmp.get_number();break;
|
||||
case vm_string:std::cout<<tmp.get_string();break;
|
||||
case vm_vector:tmp.get_vector().print();break;
|
||||
case vm_hash:tmp.get_hash().print();break;
|
||||
case vm_function:std::cout<<"func(...){...}";break;
|
||||
}
|
||||
std::cout<<",}"[(++i)==elems.end()];
|
||||
--i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*functions of nasal_function*/
|
||||
nasal_function::nasal_function(nasal_virtual_machine& nvm):vm(nvm)
|
||||
{
|
||||
closure_addr=-1;
|
||||
dynamic_para_name="";
|
||||
argument_list.clear();
|
||||
function_expr.clear();
|
||||
return;
|
||||
}
|
||||
nasal_function::~nasal_function()
|
||||
{
|
||||
if(closure_addr>=0)
|
||||
vm.del_reference(closure_addr);
|
||||
for(int i=0;i<default_para_addr.size();++i)
|
||||
if(default_para_addr[i]>=0)
|
||||
vm.del_reference(default_para_addr[i]);
|
||||
argument_list.clear();
|
||||
function_expr.clear();
|
||||
return;
|
||||
}
|
||||
void nasal_function::set_entry(int etr)
|
||||
{
|
||||
entry=etr;
|
||||
return;
|
||||
}
|
||||
int nasal_function::get_entry()
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
void nasal_function::add_para(std::string name,int val_addr=-1,bool is_dynamic=false)
|
||||
{
|
||||
if(is_dynamic)
|
||||
{
|
||||
dynamic_para_name=name;
|
||||
return;
|
||||
}
|
||||
para_name.push_back(name);
|
||||
default_para_addr.push_back(val_addr);
|
||||
return;
|
||||
}
|
||||
std::vector<std::string>& nasal_function::get_para()
|
||||
{
|
||||
return para_name;
|
||||
}
|
||||
std::string nasal_function::get_dynamic_para()
|
||||
{
|
||||
return dynamic_para_name;
|
||||
}
|
||||
std::vector<int>& nasal_function::get_default()
|
||||
{
|
||||
return default_para_addr;
|
||||
}
|
||||
void nasal_function::set_closure_addr(int value_address)
|
||||
{
|
||||
if(closure_addr>=0)
|
||||
vm.del_reference(closure_addr);
|
||||
int new_closure=vm.gc_alloc(vm_closure);
|
||||
vm.gc_get(new_closure).get_closure().set_closure(vm.gc_get(value_address).get_closure());
|
||||
closure_addr=new_closure;
|
||||
return;
|
||||
}
|
||||
int nasal_function::get_closure_addr()
|
||||
{
|
||||
return closure_addr;
|
||||
}
|
||||
void nasal_function::set_arguments(nasal_ast& node)
|
||||
{
|
||||
argument_list=node;
|
||||
return;
|
||||
}
|
||||
nasal_ast& nasal_function::get_arguments()
|
||||
{
|
||||
return argument_list;
|
||||
}
|
||||
void nasal_function::set_run_block(nasal_ast& node)
|
||||
{
|
||||
function_expr=node;
|
||||
return;
|
||||
}
|
||||
nasal_ast& nasal_function::get_run_block()
|
||||
{
|
||||
return function_expr;
|
||||
}
|
||||
|
||||
/*functions of nasal_closure*/
|
||||
nasal_closure::nasal_closure(nasal_virtual_machine& nvm):vm(nvm)
|
||||
{
|
||||
std::map<std::string,int> new_scope;
|
||||
elems.push_back(new_scope);
|
||||
return;
|
||||
}
|
||||
nasal_closure::~nasal_closure()
|
||||
{
|
||||
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
|
||||
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
|
||||
vm.del_reference(j->second);
|
||||
elems.clear();
|
||||
return;
|
||||
}
|
||||
void nasal_closure::add_scope()
|
||||
{
|
||||
std::map<std::string,int> new_scope;
|
||||
elems.push_back(new_scope);
|
||||
return;
|
||||
}
|
||||
void nasal_closure::del_scope()
|
||||
{
|
||||
std::map<std::string,int>& last_scope=elems.back();
|
||||
for(std::map<std::string,int>::iterator i=last_scope.begin();i!=last_scope.end();++i)
|
||||
vm.del_reference(i->second);
|
||||
elems.pop_back();
|
||||
return;
|
||||
}
|
||||
void nasal_closure::add_new_value(std::string key,int value_address)
|
||||
{
|
||||
if(elems.back().find(key)!=elems.back().end())
|
||||
{
|
||||
// if this value already exists,delete the old value and update a new value
|
||||
int old_val_address=elems.back()[key];
|
||||
vm.del_reference(old_val_address);
|
||||
}
|
||||
elems.back()[key]=value_address;
|
||||
return;
|
||||
}
|
||||
int nasal_closure::get_value_address(std::string key)
|
||||
{
|
||||
int ret_address=-1;
|
||||
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
|
||||
if(i->find(key)!=i->end())
|
||||
ret_address=(*i)[key];
|
||||
return ret_address;
|
||||
}
|
||||
int* nasal_closure::get_mem_address(std::string key)
|
||||
{
|
||||
int* ret_address=NULL;
|
||||
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
|
||||
if(i->find(key)!=i->end())
|
||||
ret_address=&((*i)[key]);
|
||||
return ret_address;
|
||||
}
|
||||
void nasal_closure::set_closure(nasal_closure& tmp)
|
||||
{
|
||||
for(std::list<std::map<std::string,int> >::iterator i=elems.begin();i!=elems.end();++i)
|
||||
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
|
||||
vm.del_reference(j->second);
|
||||
elems.clear();
|
||||
for(std::list<std::map<std::string,int> >::iterator i=tmp.elems.begin();i!=tmp.elems.end();++i)
|
||||
{
|
||||
std::map<std::string,int> new_scope;
|
||||
elems.push_back(new_scope);
|
||||
for(std::map<std::string,int>::iterator j=i->begin();j!=i->end();++j)
|
||||
{
|
||||
int value_addr=j->second;
|
||||
vm.add_reference(value_addr);
|
||||
elems.back()[j->first]=value_addr;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*functions of nasal_scalar*/
|
||||
nasal_scalar::nasal_scalar()
|
||||
{
|
||||
this->type=vm_nil;
|
||||
this->scalar_ptr=(void*)NULL;
|
||||
return;
|
||||
}
|
||||
nasal_scalar::~nasal_scalar()
|
||||
{
|
||||
// must set type and scalar_ptr to default first
|
||||
// this operation will avoid SIGTRAP caused by circular reference
|
||||
// circular reference will cause using destructor repeatedly
|
||||
int tmp_type=this->type;
|
||||
void* tmp_ptr=this->scalar_ptr;
|
||||
|
||||
this->type=vm_nil;
|
||||
this->scalar_ptr=NULL;
|
||||
switch(tmp_type)
|
||||
{
|
||||
case vm_nil: break;
|
||||
case vm_number: delete (double*)(tmp_ptr); break;
|
||||
case vm_string: delete (std::string*)(tmp_ptr); break;
|
||||
case vm_vector: delete (nasal_vector*)(tmp_ptr); break;
|
||||
case vm_hash: delete (nasal_hash*)(tmp_ptr); break;
|
||||
case vm_function: delete (nasal_function*)(tmp_ptr); break;
|
||||
case vm_closure: delete (nasal_closure*)(tmp_ptr); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
void nasal_scalar::clear()
|
||||
{
|
||||
// must set type and scalar_ptr to default first
|
||||
// this operation will avoid SIGTRAP caused by circular reference
|
||||
// circular reference will cause using destructor repeatedly
|
||||
int tmp_type=this->type;
|
||||
void* tmp_ptr=this->scalar_ptr;
|
||||
|
||||
this->type=vm_nil;
|
||||
this->scalar_ptr=NULL;
|
||||
switch(tmp_type)
|
||||
{
|
||||
case vm_nil: break;
|
||||
case vm_number: delete (double*)(tmp_ptr); break;
|
||||
case vm_string: delete (std::string*)(tmp_ptr); break;
|
||||
case vm_vector: delete (nasal_vector*)(tmp_ptr); break;
|
||||
case vm_hash: delete (nasal_hash*)(tmp_ptr); break;
|
||||
case vm_function: delete (nasal_function*)(tmp_ptr); break;
|
||||
case vm_closure: delete (nasal_closure*)(tmp_ptr); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
void nasal_scalar::set_type(int nasal_scalar_type,nasal_virtual_machine& nvm)
|
||||
{
|
||||
this->type=nasal_scalar_type;
|
||||
switch(nasal_scalar_type)
|
||||
{
|
||||
case vm_nil: this->scalar_ptr=(void*)NULL; break;
|
||||
case vm_number: this->scalar_ptr=(void*)(new double); break;
|
||||
case vm_string: this->scalar_ptr=(void*)(new std::string); break;
|
||||
case vm_vector: this->scalar_ptr=(void*)(new nasal_vector(nvm)); break;
|
||||
case vm_hash: this->scalar_ptr=(void*)(new nasal_hash(nvm)); break;
|
||||
case vm_function: this->scalar_ptr=(void*)(new nasal_function(nvm)); break;
|
||||
case vm_closure: this->scalar_ptr=(void*)(new nasal_closure(nvm)); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
void nasal_scalar::set_number(double num)
|
||||
{
|
||||
*(double*)(this->scalar_ptr)=num;
|
||||
return;
|
||||
}
|
||||
void nasal_scalar::set_string(std::string str)
|
||||
{
|
||||
*(std::string*)(this->scalar_ptr)=str;
|
||||
return;
|
||||
}
|
||||
int nasal_scalar::get_type()
|
||||
{
|
||||
return this->type;
|
||||
}
|
||||
double nasal_scalar::get_number()
|
||||
{
|
||||
return *(double*)(this->scalar_ptr);
|
||||
}
|
||||
std::string nasal_scalar::get_string()
|
||||
{
|
||||
return *(std::string*)(this->scalar_ptr);
|
||||
}
|
||||
nasal_vector& nasal_scalar::get_vector()
|
||||
{
|
||||
return *(nasal_vector*)(this->scalar_ptr);
|
||||
}
|
||||
nasal_hash& nasal_scalar::get_hash()
|
||||
{
|
||||
return *(nasal_hash*)(this->scalar_ptr);
|
||||
}
|
||||
nasal_function& nasal_scalar::get_func()
|
||||
{
|
||||
return *(nasal_function*)(this->scalar_ptr);
|
||||
}
|
||||
nasal_closure& nasal_scalar::get_closure()
|
||||
{
|
||||
return *(nasal_closure*)(this->scalar_ptr);
|
||||
}
|
||||
|
||||
/*functions of nasal_virtual_machine*/
|
||||
nasal_virtual_machine::~nasal_virtual_machine()
|
||||
{
|
||||
int gc_mem_size=garbage_collector_memory.size();
|
||||
for(int i=0;i<gc_mem_size;++i)
|
||||
if(garbage_collector_memory[i]->ref_cnt)
|
||||
{
|
||||
garbage_collector_memory[i]->ref_cnt=0;
|
||||
garbage_collector_memory[i]->elem.clear();
|
||||
}
|
||||
for(int i=0;i<gc_mem_size;++i)
|
||||
delete garbage_collector_memory[i];
|
||||
while(!garbage_collector_free_space.empty())
|
||||
garbage_collector_free_space.pop();
|
||||
garbage_collector_memory.clear();
|
||||
return;
|
||||
}
|
||||
void nasal_virtual_machine::debug()
|
||||
{
|
||||
int gc_mem_size=garbage_collector_memory.size();
|
||||
for(int i=0;i<gc_mem_size;++i)
|
||||
if(garbage_collector_memory[i]->ref_cnt)
|
||||
{
|
||||
std::cout<<">> [debug] "<<i<<": "<<garbage_collector_memory[i]->ref_cnt<<" ";
|
||||
switch(garbage_collector_memory[i]->elem.get_type())
|
||||
{
|
||||
case vm_nil:std::cout<<"nil";break;
|
||||
case vm_number:std::cout<<"number "<<garbage_collector_memory[i]->elem.get_number();break;
|
||||
case vm_string:std::cout<<"string "<<garbage_collector_memory[i]->elem.get_string();break;
|
||||
case vm_vector:std::cout<<"vector";break;
|
||||
case vm_hash:std::cout<<"hash";break;
|
||||
case vm_function:std::cout<<"function";break;
|
||||
case vm_closure:std::cout<<"closure";break;
|
||||
}
|
||||
std::cout<<"\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
void nasal_virtual_machine::clear()
|
||||
{
|
||||
int gc_mem_size=garbage_collector_memory.size();
|
||||
for(int i=0;i<gc_mem_size;++i)
|
||||
if(garbage_collector_memory[i]->ref_cnt)
|
||||
{
|
||||
garbage_collector_memory[i]->ref_cnt=0;
|
||||
garbage_collector_memory[i]->elem.clear();
|
||||
}
|
||||
for(int i=0;i<gc_mem_size;++i)
|
||||
delete garbage_collector_memory[i];
|
||||
while(!garbage_collector_free_space.empty())
|
||||
garbage_collector_free_space.pop();
|
||||
garbage_collector_memory.clear();
|
||||
return;
|
||||
}
|
||||
int nasal_virtual_machine::gc_alloc(int val_type)
|
||||
{
|
||||
if(garbage_collector_free_space.empty())
|
||||
{
|
||||
int mem_size=garbage_collector_memory.size();
|
||||
garbage_collector_memory.resize(mem_size+64);
|
||||
for(int i=mem_size;i<mem_size+64;++i)
|
||||
{
|
||||
garbage_collector_memory[i]=new gc_unit;
|
||||
garbage_collector_free_space.push(i);
|
||||
}
|
||||
}
|
||||
int ret=garbage_collector_free_space.front();
|
||||
gc_unit& unit_ref=*garbage_collector_memory[ret];
|
||||
unit_ref.ref_cnt=1;
|
||||
unit_ref.elem.set_type(val_type,*this);
|
||||
garbage_collector_free_space.pop();
|
||||
return ret;
|
||||
}
|
||||
nasal_scalar& nasal_virtual_machine::gc_get(int value_address)
|
||||
{
|
||||
if(0<=value_address)
|
||||
return garbage_collector_memory[value_address]->elem;
|
||||
return error_returned_value;
|
||||
}
|
||||
void nasal_virtual_machine::add_reference(int value_address)
|
||||
{
|
||||
if(0<=value_address)
|
||||
++garbage_collector_memory[value_address]->ref_cnt;
|
||||
return;
|
||||
}
|
||||
void nasal_virtual_machine::del_reference(int value_address)
|
||||
{
|
||||
if(0<=value_address)
|
||||
--garbage_collector_memory[value_address]->ref_cnt;
|
||||
else
|
||||
return;
|
||||
if(!garbage_collector_memory[value_address]->ref_cnt)
|
||||
{
|
||||
garbage_collector_memory[value_address]->elem.clear();
|
||||
garbage_collector_free_space.push(value_address);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
177
nasal_import.h
@@ -1,177 +0,0 @@
|
||||
#ifndef __NASAL_IMPORT_H__
|
||||
#define __NASAL_IMPORT_H__
|
||||
|
||||
class nasal_import
|
||||
{
|
||||
private:
|
||||
nasal_lexer import_lex;
|
||||
nasal_parse import_par;
|
||||
nasal_ast import_ast;
|
||||
std::vector<std::string> filename_table;
|
||||
int error;
|
||||
void die(std::string,std::string);
|
||||
void init();
|
||||
bool check_import(nasal_ast&);
|
||||
bool check_exist(std::string);
|
||||
void linker(nasal_ast&,nasal_ast&);
|
||||
nasal_ast file_import(nasal_ast&);
|
||||
nasal_ast load(nasal_ast&);
|
||||
public:
|
||||
nasal_import();
|
||||
int get_error();
|
||||
void link(nasal_ast&);
|
||||
nasal_ast& get_root();
|
||||
};
|
||||
|
||||
nasal_import::nasal_import()
|
||||
{
|
||||
import_lex.clear();
|
||||
import_par.clear();
|
||||
import_ast.clear();
|
||||
filename_table.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_import::die(std::string filename,std::string error_stage)
|
||||
{
|
||||
++error;
|
||||
std::cout<<">> [import] in <\""<<filename<<"\">: error(s) occurred in "<<error_stage<<"."<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_import::init()
|
||||
{
|
||||
import_lex.clear();
|
||||
import_par.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
bool nasal_import::check_import(nasal_ast& node)
|
||||
{
|
||||
/*
|
||||
only this kind of node can be recognized as 'import':
|
||||
|
||||
call
|
||||
id:import
|
||||
call_func
|
||||
string:'filename'
|
||||
*/
|
||||
if(node.get_type()!=ast_call)
|
||||
return false;
|
||||
std::vector<nasal_ast>& ref_vec=node.get_children();
|
||||
if(ref_vec.size()!=2)
|
||||
return false;
|
||||
if(ref_vec[0].get_str()!="import")
|
||||
return false;
|
||||
if(ref_vec[1].get_type()!=ast_call_func)
|
||||
return false;
|
||||
if(ref_vec[1].get_children().size()!=1 || ref_vec[1].get_children()[0].get_type()!=ast_string)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nasal_import::check_exist(std::string filename)
|
||||
{
|
||||
// avoid importing the same file
|
||||
int size=filename_table.size();
|
||||
for(int i=0;i<size;++i)
|
||||
if(filename==filename_table[i])
|
||||
return true;
|
||||
filename_table.push_back(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
void nasal_import::linker(nasal_ast& root,nasal_ast& add_root)
|
||||
{
|
||||
// add children of add_root to the back of root
|
||||
std::vector<nasal_ast>& ref_vec=add_root.get_children();
|
||||
int size=ref_vec.size();
|
||||
for(int i=0;i<size;++i)
|
||||
root.add_child(ref_vec[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::file_import(nasal_ast& node)
|
||||
{
|
||||
// initializing
|
||||
nasal_ast tmp;
|
||||
tmp.set_line(0);
|
||||
tmp.set_type(ast_root);
|
||||
init();
|
||||
|
||||
// get filename and set node to ast_null
|
||||
std::string filename=node.get_children()[1].get_children()[0].get_str();
|
||||
node.clear();
|
||||
node.set_type(ast_null);
|
||||
|
||||
// avoid infinite loading loop
|
||||
if(check_exist(filename))
|
||||
return tmp;
|
||||
|
||||
// start importing...
|
||||
import_lex.openfile(filename);
|
||||
import_lex.scanner();
|
||||
if(import_lex.get_error())
|
||||
{
|
||||
this->die(filename,"lexer");
|
||||
return tmp;
|
||||
}
|
||||
import_par.set_toklist(import_lex.get_token_list());
|
||||
import_par.main_process();
|
||||
if(import_par.get_error())
|
||||
{
|
||||
this->die(filename,"parser");
|
||||
return tmp;
|
||||
}
|
||||
tmp=import_par.get_root();
|
||||
|
||||
// check if tmp has 'import'
|
||||
return load(tmp);
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::load(nasal_ast& root)
|
||||
{
|
||||
nasal_ast new_root;
|
||||
new_root.set_line(0);
|
||||
new_root.set_type(ast_root);
|
||||
|
||||
std::vector<nasal_ast>& ref_vec=root.get_children();
|
||||
int size=ref_vec.size();
|
||||
for(int i=0;i<size;++i)
|
||||
{
|
||||
if(check_import(ref_vec[i]))
|
||||
{
|
||||
nasal_ast tmp=file_import(ref_vec[i]);
|
||||
// add tmp to the back of new_root
|
||||
linker(new_root,tmp);
|
||||
}
|
||||
}
|
||||
// add root to the back of new_root
|
||||
linker(new_root,root);
|
||||
|
||||
// oops,i think it is not efficient if the root is too ... large?
|
||||
return new_root;
|
||||
}
|
||||
|
||||
void nasal_import::link(nasal_ast& root)
|
||||
{
|
||||
// initializing
|
||||
error=0;
|
||||
filename_table.clear();
|
||||
import_ast.clear();
|
||||
// scan root and import files,then generate a new ast and return to import_ast
|
||||
import_ast=load(root);
|
||||
return;
|
||||
}
|
||||
|
||||
nasal_ast& nasal_import::get_root()
|
||||
{
|
||||
return import_ast;
|
||||
}
|
||||
|
||||
int nasal_import::get_error()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif
|
||||
464
nasal_lexer.h
@@ -1,464 +0,0 @@
|
||||
#ifndef __NASAL_LEXER_H__
|
||||
#define __NASAL_LEXER_H__
|
||||
|
||||
#define IS_IDENTIFIER(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
|
||||
#define IS_HEX_NUMBER(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
|
||||
#define IS_OCT_NUMEBR(c) ('0'<=c&&c<='7')
|
||||
#define IS_DIGIT(c) ('0'<=c&&c<='9')
|
||||
#define IS_STRING(c) (c=='\''||c=='\"'||c=='`')
|
||||
// single operators have only one character
|
||||
#define IS_SINGLE_OPRATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
|
||||
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
|
||||
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
|
||||
#define IS_CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
|
||||
#define IS_NOTE(c) (c=='#')
|
||||
|
||||
enum token_type
|
||||
{
|
||||
tok_null=0,
|
||||
tok_number,tok_string,tok_identifier,
|
||||
tok_for,tok_forindex,tok_foreach,tok_while,
|
||||
tok_var,tok_func,tok_break,tok_continue,
|
||||
tok_return,tok_if,tok_elsif,tok_else,tok_nil,
|
||||
tok_left_curve,tok_right_curve,
|
||||
tok_left_bracket,tok_right_bracket,
|
||||
tok_left_brace,tok_right_brace,
|
||||
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
|
||||
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
|
||||
tok_equal,
|
||||
tok_add_equal,tok_sub_equal,tok_mult_equal,tok_div_equal,tok_link_equal,
|
||||
tok_cmp_equal,tok_cmp_not_equal,tok_less_than,tok_less_equal,tok_greater_than,tok_greater_equal
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
const char* str;
|
||||
int tok_type;
|
||||
}token_table[]=
|
||||
{
|
||||
{"for" ,tok_for },
|
||||
{"forindex",tok_forindex },
|
||||
{"foreach" ,tok_foreach },
|
||||
{"while" ,tok_while },
|
||||
{"var" ,tok_var },
|
||||
{"func" ,tok_func },
|
||||
{"break" ,tok_break },
|
||||
{"continue",tok_continue },
|
||||
{"return" ,tok_return },
|
||||
{"if" ,tok_if },
|
||||
{"elsif" ,tok_elsif },
|
||||
{"else" ,tok_else },
|
||||
{"nil" ,tok_nil },
|
||||
{"(" ,tok_left_curve },
|
||||
{")" ,tok_right_curve },
|
||||
{"[" ,tok_left_bracket },
|
||||
{"]" ,tok_right_bracket},
|
||||
{"{" ,tok_left_brace },
|
||||
{"}" ,tok_right_brace },
|
||||
{";" ,tok_semi },
|
||||
{"and" ,tok_and },
|
||||
{"or" ,tok_or },
|
||||
{"," ,tok_comma },
|
||||
{"." ,tok_dot },
|
||||
{"..." ,tok_ellipsis },
|
||||
{"?" ,tok_quesmark },
|
||||
{":" ,tok_colon },
|
||||
{"+" ,tok_add },
|
||||
{"-" ,tok_sub },
|
||||
{"*" ,tok_mult },
|
||||
{"/" ,tok_div },
|
||||
{"~" ,tok_link },
|
||||
{"!" ,tok_not },
|
||||
{"=" ,tok_equal },
|
||||
{"+=" ,tok_add_equal },
|
||||
{"-=" ,tok_sub_equal },
|
||||
{"*=" ,tok_mult_equal },
|
||||
{"/=" ,tok_div_equal },
|
||||
{"~=" ,tok_link_equal },
|
||||
{"==" ,tok_cmp_equal },
|
||||
{"!=" ,tok_cmp_not_equal},
|
||||
{"<" ,tok_less_than },
|
||||
{"<=" ,tok_less_equal },
|
||||
{">" ,tok_greater_than },
|
||||
{">=" ,tok_greater_equal},
|
||||
{NULL ,-1 }
|
||||
};
|
||||
|
||||
struct token
|
||||
{
|
||||
int line;
|
||||
int type;
|
||||
std::string str;
|
||||
};
|
||||
|
||||
class nasal_lexer
|
||||
{
|
||||
private:
|
||||
int error;
|
||||
int res_size;
|
||||
int line;
|
||||
int ptr;
|
||||
std::string line_code;
|
||||
std::vector<char> res;
|
||||
std::vector<token> token_list;
|
||||
std::string identifier_gen();
|
||||
std::string number_gen();
|
||||
std::string string_gen();
|
||||
public:
|
||||
void clear();
|
||||
void openfile(std::string);
|
||||
void die(std::string,int,int);
|
||||
void scanner();
|
||||
void print_token();
|
||||
int get_error();
|
||||
std::vector<token>& get_token_list();
|
||||
};
|
||||
|
||||
void nasal_lexer::clear()
|
||||
{
|
||||
error=0;
|
||||
res_size=0;
|
||||
line=0;
|
||||
ptr=0;
|
||||
line_code="";
|
||||
res.clear();
|
||||
token_list.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_lexer::openfile(std::string filename)
|
||||
{
|
||||
error=0;
|
||||
res.clear();
|
||||
std::ifstream fin(filename,std::ios::binary);
|
||||
if(fin.fail())
|
||||
{
|
||||
++error;
|
||||
std::cout<<">> [lexer] cannot open file \""<<filename<<"\".\n";
|
||||
fin.close();
|
||||
return;
|
||||
}
|
||||
while(!fin.eof())
|
||||
{
|
||||
char c=fin.get();
|
||||
if(fin.eof())
|
||||
break;
|
||||
res.push_back(c);
|
||||
}
|
||||
fin.close();
|
||||
res_size=res.size();
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_lexer::die(std::string error_info,int line=-1,int column=-1)
|
||||
{
|
||||
++error;
|
||||
std::cout<<">> [lexer] line "<<line<<" column "<<column<<": "<<error_info<<"\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::identifier_gen()
|
||||
{
|
||||
std::string token_str="";
|
||||
while(ptr<res_size && (IS_IDENTIFIER(res[ptr])||IS_DIGIT(res[ptr])))
|
||||
token_str+=res[ptr++];
|
||||
line_code+=token_str;
|
||||
return token_str;
|
||||
// after running this process, ptr will point to the next token's beginning character
|
||||
}
|
||||
|
||||
std::string nasal_lexer::number_gen()
|
||||
{
|
||||
bool scientific_notation=false;// numbers like 1e8 are scientific_notation
|
||||
std::string token_str="";
|
||||
// generate hex number
|
||||
if(res[ptr]=='0' && ptr+1<res_size && res[ptr+1]=='x')
|
||||
{
|
||||
token_str="0x";
|
||||
ptr+=2;
|
||||
while(ptr<res_size && IS_HEX_NUMBER(res[ptr]))
|
||||
token_str+=res[ptr++];
|
||||
line_code+=token_str;
|
||||
if(token_str=="0x")
|
||||
{
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
return token_str;
|
||||
}
|
||||
// generate oct number
|
||||
else if(res[ptr]=='0' && ptr+1<res_size && res[ptr+1]=='o')
|
||||
{
|
||||
token_str="0o";
|
||||
ptr+=2;
|
||||
while(ptr<res_size && IS_OCT_NUMEBR(res[ptr]))
|
||||
token_str+=res[ptr++];
|
||||
line_code+=token_str;
|
||||
if(token_str=="0o")
|
||||
{
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
return token_str;
|
||||
}
|
||||
// generate dec number
|
||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||
while(ptr<res_size && IS_DIGIT(res[ptr]))
|
||||
token_str+=res[ptr++];
|
||||
if(ptr<res_size && res[ptr]=='.')
|
||||
{
|
||||
token_str+=res[ptr++];
|
||||
// "xxxx." is not a correct number
|
||||
if(ptr>=res_size)
|
||||
{
|
||||
line_code+=token_str;
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
while(ptr<res_size && IS_DIGIT(res[ptr]))
|
||||
token_str+=res[ptr++];
|
||||
// "xxxx." is not a correct number
|
||||
if(token_str.back()=='.')
|
||||
{
|
||||
line_code+=token_str;
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
if(ptr<res_size && (res[ptr]=='e' || res[ptr]=='E'))
|
||||
{
|
||||
token_str+=res[ptr++];
|
||||
// "xxxe" is not a correct number
|
||||
if(ptr>=res_size)
|
||||
{
|
||||
line_code+=token_str;
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
if(ptr<res_size && (res[ptr]=='-' || res[ptr]=='+'))
|
||||
token_str+=res[ptr++];
|
||||
if(ptr>=res_size)
|
||||
{
|
||||
line_code+=token_str;
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
if(ptr<res_size && res[ptr]=='0')
|
||||
token_str+=res[ptr++];
|
||||
while(ptr<res_size && IS_DIGIT(res[ptr]))
|
||||
token_str+=res[ptr++];
|
||||
// "xxxe(-|+)" is not a correct number
|
||||
if(token_str.back()=='e' || token_str.back()=='E' || token_str.back()=='-' || token_str.back()=='+')
|
||||
{
|
||||
line_code+=token_str;
|
||||
die("["+line_code+"_] incorrect number.",line,line_code.length());
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
line_code+=token_str;
|
||||
return token_str;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::string_gen()
|
||||
{
|
||||
std::string token_str="";
|
||||
line_code+=res[ptr];
|
||||
char str_begin=res[ptr++];
|
||||
while(ptr<res_size && res[ptr]!=str_begin)
|
||||
{
|
||||
line_code+=res[ptr];
|
||||
if(res[ptr]=='\n')
|
||||
{
|
||||
line_code="";
|
||||
++line;
|
||||
}
|
||||
if(res[ptr]=='\\' && ptr+1<res_size)
|
||||
{
|
||||
++ptr;
|
||||
line_code+=res[ptr];
|
||||
switch(res[ptr])
|
||||
{
|
||||
case 'a':token_str.push_back('\a');break;
|
||||
case 'b':token_str.push_back('\b');break;
|
||||
case 'f':token_str.push_back('\f');break;
|
||||
case 'n':token_str.push_back('\n');break;
|
||||
case 'r':token_str.push_back('\r');break;
|
||||
case 't':token_str.push_back('\t');break;
|
||||
case 'v':token_str.push_back('\v');break;
|
||||
case '?':token_str.push_back('\?');break;
|
||||
case '0':token_str.push_back('\0');break;
|
||||
case '\\':token_str.push_back('\\');break;
|
||||
case '\'':token_str.push_back('\'');break;
|
||||
case '\"':token_str.push_back('\"');break;
|
||||
default: token_str.push_back(res[ptr]);break;
|
||||
}
|
||||
}
|
||||
else
|
||||
token_str+=res[ptr];
|
||||
++ptr;
|
||||
}
|
||||
// check if this string ends with a " or '
|
||||
if(ptr>=res_size)
|
||||
die("["+line_code+"_] get EOF when generating string.",line,line_code.length());
|
||||
++ptr;
|
||||
return token_str;
|
||||
}
|
||||
|
||||
void nasal_lexer::scanner()
|
||||
{
|
||||
token_list.clear();
|
||||
line=1;
|
||||
ptr=0;
|
||||
line_code="";
|
||||
|
||||
std::string token_str;
|
||||
while(ptr<res_size)
|
||||
{
|
||||
while(ptr<res_size && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
|
||||
{
|
||||
// these characters will be ignored, and '\n' will cause ++line
|
||||
line_code+=res[ptr];
|
||||
if(res[ptr]=='\n')
|
||||
{
|
||||
++line;
|
||||
line_code="";
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
if(ptr>=res_size) break;
|
||||
if(IS_IDENTIFIER(res[ptr]))
|
||||
{
|
||||
token_str=identifier_gen();
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.str=token_str;
|
||||
new_token.type=0;
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(token_str==token_table[i].str)
|
||||
{
|
||||
new_token.type=token_table[i].tok_type;
|
||||
break;
|
||||
}
|
||||
if(!new_token.type)
|
||||
new_token.type=tok_identifier;
|
||||
token_list.push_back(new_token);
|
||||
}
|
||||
else if(IS_DIGIT(res[ptr]))
|
||||
{
|
||||
token_str=number_gen();
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.str=token_str;
|
||||
new_token.type=tok_number;
|
||||
token_list.push_back(new_token);
|
||||
}
|
||||
else if(IS_STRING(res[ptr]))
|
||||
{
|
||||
token_str=string_gen();
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.type=tok_string;
|
||||
new_token.str=token_str;
|
||||
token_list.push_back(new_token);
|
||||
}
|
||||
else if(IS_SINGLE_OPRATOR(res[ptr]))
|
||||
{
|
||||
token_str="";
|
||||
token_str+=res[ptr];
|
||||
line_code+=res[ptr];
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.str=token_str;
|
||||
new_token.type=-1;
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(token_str==token_table[i].str)
|
||||
{
|
||||
new_token.type=token_table[i].tok_type;
|
||||
break;
|
||||
}
|
||||
if(new_token.type<0)
|
||||
die("["+line_code+"_] incorrect operator.",line,line_code.length());
|
||||
token_list.push_back(new_token);
|
||||
++ptr;
|
||||
}
|
||||
else if(res[ptr]=='.')
|
||||
{
|
||||
if(ptr+2<res_size && res[ptr+1]=='.' && res[ptr+2]=='.')
|
||||
{
|
||||
token_str="...";
|
||||
ptr+=3;
|
||||
}
|
||||
else
|
||||
{
|
||||
token_str=".";
|
||||
++ptr;
|
||||
}
|
||||
line_code+=token_str;
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.str=token_str;
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(token_str==token_table[i].str)
|
||||
{
|
||||
new_token.type=token_table[i].tok_type;
|
||||
break;
|
||||
}
|
||||
token_list.push_back(new_token);
|
||||
}
|
||||
else if(IS_CALC_OPERATOR(res[ptr]))
|
||||
{
|
||||
// get calculation operator
|
||||
token_str=res[ptr];
|
||||
++ptr;
|
||||
if(ptr<res.size() && res[ptr]=='=')
|
||||
{
|
||||
token_str+=res[ptr];
|
||||
++ptr;
|
||||
}
|
||||
line_code+=token_str;
|
||||
token new_token;
|
||||
new_token.line=line;
|
||||
new_token.str=token_str;
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(token_str==token_table[i].str)
|
||||
{
|
||||
new_token.type=token_table[i].tok_type;
|
||||
break;
|
||||
}
|
||||
token_list.push_back(new_token);
|
||||
}
|
||||
else if(IS_NOTE(res[ptr]))
|
||||
{
|
||||
// avoid note
|
||||
while(ptr<res_size && res[ptr]!='\n') ++ptr;
|
||||
// after this process ptr will point to a '\n'
|
||||
// don't ++ptr then the counter for line can work correctly
|
||||
}
|
||||
else
|
||||
{
|
||||
line_code+=res[ptr];
|
||||
die("["+line_code+"_] unknown character.",line,line_code.length());
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void nasal_lexer::print_token()
|
||||
{
|
||||
int size=token_list.size();
|
||||
for(int i=0;i<size;++i)
|
||||
std::cout<<"("<<token_list[i].line<<" | "<<token_list[i].str<<")\n";
|
||||
return;
|
||||
}
|
||||
|
||||
int nasal_lexer::get_error()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
std::vector<token>& nasal_lexer::get_token_list()
|
||||
{
|
||||
return token_list;
|
||||
}
|
||||
#endif
|
||||
1687
nasal_parse.h
1896
nasal_runtime.h
|
Before Width: | Height: | Size: 62 KiB |
485
src/ast_dumper.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
#include "ast_dumper.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_dumper::visit_null_expr(null_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "null" << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_nil_expr(nil_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "nil" << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_number_literal(number_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "number " << node->get_number();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_string_literal(string_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "string \"" << rawstr(node->get_content()) << "\"";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_identifier(identifier* node) {
|
||||
dump_indent();
|
||||
std::cout << "identifier " << node->get_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_bool_literal(bool_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "bool " << node->get_flag();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_vector_expr(vector_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "vector";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_hash_expr(hash_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "hash";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_members()) {
|
||||
if (i==node->get_members().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_hash_pair(hash_pair* node) {
|
||||
dump_indent();
|
||||
std::cout << "pair " << node->get_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_function(function* node) {
|
||||
dump_indent();
|
||||
std::cout << "function";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_code_block(code_block* node) {
|
||||
dump_indent();
|
||||
std::cout << "block";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_expressions()) {
|
||||
if (i==node->get_expressions().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_parameter(parameter* node) {
|
||||
dump_indent();
|
||||
std::cout << "parameter " << node->get_parameter_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_default_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_default_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
|
||||
dump_indent();
|
||||
std::cout << "ternary_operator";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_condition()->accept(this);
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_binary_operator(binary_operator* node) {
|
||||
if (node->get_optimized_number()) {
|
||||
node->get_optimized_number()->accept(this);
|
||||
return true;
|
||||
}
|
||||
if (node->get_optimized_string()) {
|
||||
node->get_optimized_string()->accept(this);
|
||||
return true;
|
||||
}
|
||||
dump_indent();
|
||||
std::cout << "binary_operator ";
|
||||
switch(node->get_operator_type()) {
|
||||
case binary_operator::binary_type::add: std::cout << "+"; break;
|
||||
case binary_operator::binary_type::sub: std::cout << "-"; break;
|
||||
case binary_operator::binary_type::mult: std::cout << "*"; break;
|
||||
case binary_operator::binary_type::div: std::cout << "/"; break;
|
||||
case binary_operator::binary_type::concat: std::cout << "~"; break;
|
||||
case binary_operator::binary_type::bitwise_and: std::cout << "&"; break;
|
||||
case binary_operator::binary_type::bitwise_or: std::cout << "|"; break;
|
||||
case binary_operator::binary_type::bitwise_xor: std::cout << "^"; break;
|
||||
case binary_operator::binary_type::cmpeq: std::cout << "=="; break;
|
||||
case binary_operator::binary_type::cmpneq: std::cout << "!="; break;
|
||||
case binary_operator::binary_type::grt: std::cout << ">"; break;
|
||||
case binary_operator::binary_type::geq: std::cout << ">="; break;
|
||||
case binary_operator::binary_type::less: std::cout << "<"; break;
|
||||
case binary_operator::binary_type::leq: std::cout << "<="; break;
|
||||
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
|
||||
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_unary_operator(unary_operator* node) {
|
||||
if (node->get_optimized_number()) {
|
||||
node->get_optimized_number()->accept(this);
|
||||
return true;
|
||||
}
|
||||
dump_indent();
|
||||
std::cout << "unary_operator ";
|
||||
switch(node->get_operator_type()) {
|
||||
case unary_operator::unary_type::negative: std::cout << "-"; break;
|
||||
case unary_operator::unary_type::logical_not: std::cout << "!"; break;
|
||||
case unary_operator::unary_type::bitwise_not: std::cout << "~"; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_expr(call_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_expr";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_calls().size()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
if (i==node->get_calls().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_hash(call_hash* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_hash " << node->get_field();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_vector(call_vector* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_vector";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_slices()) {
|
||||
if (i==node->get_slices().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_function(call_function* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_function";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_argument()) {
|
||||
if (i==node->get_argument().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_slice_vector(slice_vector* node) {
|
||||
dump_indent();
|
||||
std::cout << "slice";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_end()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_begin()->accept(this);
|
||||
if (node->get_end()) {
|
||||
set_last();
|
||||
node->get_end()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_definition_expr(definition_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "definition";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (node->get_variable_name()) {
|
||||
node->get_variable_name()->accept(this);
|
||||
} else {
|
||||
node->get_variables()->accept(this);
|
||||
}
|
||||
set_last();
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "assignment ";
|
||||
switch(node->get_assignment_type()) {
|
||||
case assignment_expr::assign_type::add_equal: std::cout << "+="; break;
|
||||
case assignment_expr::assign_type::sub_equal: std::cout << "-="; break;
|
||||
case assignment_expr::assign_type::mult_equal: std::cout << "*="; break;
|
||||
case assignment_expr::assign_type::div_equal: std::cout << "/="; break;
|
||||
case assignment_expr::assign_type::concat_equal: std::cout << "~="; break;
|
||||
case assignment_expr::assign_type::equal: std::cout << "="; break;
|
||||
case assignment_expr::assign_type::bitwise_and_equal: std::cout << "&="; break;
|
||||
case assignment_expr::assign_type::bitwise_or_equal: std::cout << "|="; break;
|
||||
case assignment_expr::assign_type::bitwise_xor_equal: std::cout << "^="; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
|
||||
dump_indent();
|
||||
std::cout << "multiple_identifier";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_variables()) {
|
||||
if (i==node->get_variables().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "tuple";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_multi_assign(multi_assign* node) {
|
||||
dump_indent();
|
||||
std::cout << "multiple_assignment";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_tuple()->accept(this);
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_while_expr(while_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "while";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_condition()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_for_expr(for_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "for";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_initial()->accept(this);
|
||||
node->get_condition()->accept(this);
|
||||
node->get_step()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_iter_expr(iter_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "iterator";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
set_last();
|
||||
if (node->get_name()) {
|
||||
node->get_name()->accept(this);
|
||||
} else {
|
||||
node->get_call()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_forei_expr(forei_expr* node) {
|
||||
dump_indent();
|
||||
if (node->get_loop_type()==forei_expr::forei_loop_type::foreach) {
|
||||
std::cout << "foreach";
|
||||
} else {
|
||||
std::cout << "forindex";
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_iterator()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_condition_expr(condition_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "condition";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_elsif_stataments().size() &&
|
||||
!node->get_else_statement()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
if (i==node->get_elsif_stataments().back() &&
|
||||
!node->get_else_statement()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
set_last();
|
||||
node->get_else_statement()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_if_expr(if_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "if";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (node->get_condition()) {
|
||||
node->get_condition()->accept(this);
|
||||
}
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_continue_expr(continue_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "continue";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_break_expr(break_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "break";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_return_expr(return_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "return";
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
85
src/ast_dumper.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_visitor.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_dumper:public ast_visitor {
|
||||
private:
|
||||
std::vector<std::string> indent;
|
||||
void push_indent() {
|
||||
if (indent.size()) {
|
||||
if (indent.back()=="|--") {
|
||||
indent.back() = "| ";
|
||||
} else if (indent.back()=="+--") {
|
||||
indent.back() = " ";
|
||||
}
|
||||
}
|
||||
indent.push_back("|--");
|
||||
}
|
||||
void pop_indent() {indent.pop_back();}
|
||||
void set_last() {indent.back() = "+--";}
|
||||
void dump_indent() {
|
||||
if (indent.size() && indent.back()=="| ") {
|
||||
indent.back() = "|--";
|
||||
}
|
||||
for(const auto& i : indent) {
|
||||
std::cout << i;
|
||||
}
|
||||
}
|
||||
std::string format_location(const span& location) {
|
||||
std::stringstream ss;
|
||||
ss << " -> ";
|
||||
ss << location.file << ":";
|
||||
ss << location.begin_line << ":" << location.begin_column + 1;
|
||||
ss << "\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
public:
|
||||
bool visit_null_expr(null_expr*) override;
|
||||
bool visit_nil_expr(nil_expr*) override;
|
||||
bool visit_number_literal(number_literal*) override;
|
||||
bool visit_string_literal(string_literal*) override;
|
||||
bool visit_identifier(identifier*) override;
|
||||
bool visit_bool_literal(bool_literal*) override;
|
||||
bool visit_vector_expr(vector_expr*) override;
|
||||
bool visit_hash_expr(hash_expr*) override;
|
||||
bool visit_hash_pair(hash_pair*) override;
|
||||
bool visit_function(function*) override;
|
||||
bool visit_code_block(code_block*) override;
|
||||
bool visit_parameter(parameter*) override;
|
||||
bool visit_ternary_operator(ternary_operator*) override;
|
||||
bool visit_binary_operator(binary_operator*) override;
|
||||
bool visit_unary_operator(unary_operator*) override;
|
||||
bool visit_call_expr(call_expr*) override;
|
||||
bool visit_call_hash(call_hash*) override;
|
||||
bool visit_call_vector(call_vector*) override;
|
||||
bool visit_call_function(call_function*) override;
|
||||
bool visit_slice_vector(slice_vector*) override;
|
||||
bool visit_definition_expr(definition_expr*) override;
|
||||
bool visit_assignment_expr(assignment_expr*) override;
|
||||
bool visit_multi_identifier(multi_identifier*) override;
|
||||
bool visit_tuple_expr(tuple_expr*) override;
|
||||
bool visit_multi_assign(multi_assign*) override;
|
||||
bool visit_while_expr(while_expr*) override;
|
||||
bool visit_for_expr(for_expr*) override;
|
||||
bool visit_iter_expr(iter_expr*) override;
|
||||
bool visit_forei_expr(forei_expr*) override;
|
||||
bool visit_condition_expr(condition_expr*) override;
|
||||
bool visit_if_expr(if_expr*) override;
|
||||
bool visit_continue_expr(continue_expr*) override;
|
||||
bool visit_break_expr(break_expr*) override;
|
||||
bool visit_return_expr(return_expr*) override;
|
||||
|
||||
public:
|
||||
void dump(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
238
src/ast_visitor.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_visitor::visit_expr(expr* node) {
|
||||
node->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call(call* node) {
|
||||
node->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_null_expr(null_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_nil_expr(nil_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_number_literal(number_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_string_literal(string_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_identifier(identifier* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_bool_literal(bool_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_vector_expr(vector_expr* node) {
|
||||
for(auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_hash_expr(hash_expr* node) {
|
||||
for(auto i : node->get_members()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_hash_pair(hash_pair* node) {
|
||||
if (node->get_value()) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_function(function* node) {
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_code_block(code_block* node) {
|
||||
for(auto i : node->get_expressions()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_parameter(parameter* node) {
|
||||
if (node->get_default_value()) {
|
||||
node->get_default_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_ternary_operator(ternary_operator* node) {
|
||||
node->get_condition()->accept(this);
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_binary_operator(binary_operator* node) {
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_unary_operator(unary_operator* node) {
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_expr(call_expr* node) {
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_hash(call_hash* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_vector(call_vector* node) {
|
||||
for(auto i : node->get_slices()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_function(call_function* node) {
|
||||
for(auto i : node->get_argument()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_slice_vector(slice_vector* node) {
|
||||
node->get_begin()->accept(this);
|
||||
if (node->get_end()) {
|
||||
node->get_end()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_definition_expr(definition_expr* node) {
|
||||
if (node->get_variable_name()) {
|
||||
node->get_variable_name()->accept(this);
|
||||
} else {
|
||||
node->get_variables()->accept(this);
|
||||
}
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_assignment_expr(assignment_expr* node) {
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_multi_identifier(multi_identifier* node) {
|
||||
for(auto i : node->get_variables()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_tuple_expr(tuple_expr* node) {
|
||||
for(auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_multi_assign(multi_assign* node) {
|
||||
node->get_tuple()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_while_expr(while_expr* node) {
|
||||
node->get_condition()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_for_expr(for_expr* node) {
|
||||
node->get_initial()->accept(this);
|
||||
node->get_condition()->accept(this);
|
||||
node->get_step()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_iter_expr(iter_expr* node) {
|
||||
if (node->get_name()) {
|
||||
node->get_name()->accept(this);
|
||||
} else {
|
||||
node->get_call()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_forei_expr(forei_expr* node) {
|
||||
node->get_iterator()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_condition_expr(condition_expr* node) {
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
node->get_else_statement()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_if_expr(if_expr* node) {
|
||||
if (node->get_condition()) {
|
||||
node->get_condition()->accept(this);
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_continue_expr(continue_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_break_expr(break_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_return_expr(return_expr* node) {
|
||||
if (node->get_value()) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
47
src/ast_visitor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_ast.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_visitor {
|
||||
public:
|
||||
virtual bool visit_expr(expr*);
|
||||
virtual bool visit_call(call*);
|
||||
virtual bool visit_null_expr(null_expr*);
|
||||
virtual bool visit_nil_expr(nil_expr*);
|
||||
virtual bool visit_number_literal(number_literal*);
|
||||
virtual bool visit_string_literal(string_literal*);
|
||||
virtual bool visit_identifier(identifier*);
|
||||
virtual bool visit_bool_literal(bool_literal*);
|
||||
virtual bool visit_vector_expr(vector_expr*);
|
||||
virtual bool visit_hash_expr(hash_expr*);
|
||||
virtual bool visit_hash_pair(hash_pair*);
|
||||
virtual bool visit_function(function*);
|
||||
virtual bool visit_code_block(code_block*);
|
||||
virtual bool visit_parameter(parameter*);
|
||||
virtual bool visit_ternary_operator(ternary_operator*);
|
||||
virtual bool visit_binary_operator(binary_operator*);
|
||||
virtual bool visit_unary_operator(unary_operator*);
|
||||
virtual bool visit_call_expr(call_expr*);
|
||||
virtual bool visit_call_hash(call_hash*);
|
||||
virtual bool visit_call_vector(call_vector*);
|
||||
virtual bool visit_call_function(call_function*);
|
||||
virtual bool visit_slice_vector(slice_vector*);
|
||||
virtual bool visit_definition_expr(definition_expr*);
|
||||
virtual bool visit_assignment_expr(assignment_expr*);
|
||||
virtual bool visit_multi_identifier(multi_identifier*);
|
||||
virtual bool visit_tuple_expr(tuple_expr*);
|
||||
virtual bool visit_multi_assign(multi_assign*);
|
||||
virtual bool visit_while_expr(while_expr*);
|
||||
virtual bool visit_for_expr(for_expr*);
|
||||
virtual bool visit_iter_expr(iter_expr*);
|
||||
virtual bool visit_forei_expr(forei_expr*);
|
||||
virtual bool visit_condition_expr(condition_expr*);
|
||||
virtual bool visit_if_expr(if_expr*);
|
||||
virtual bool visit_continue_expr(continue_expr*);
|
||||
virtual bool visit_break_expr(break_expr*);
|
||||
virtual bool visit_return_expr(return_expr*);
|
||||
};
|
||||
|
||||
}
|
||||
153
src/bits_lib.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "bits_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_u32xor(var* local, gc& ngc) {
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) ^
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32and(var* local, gc& ngc) {
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) &
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32or(var* local, gc& ngc) {
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) |
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32nand(var* local, gc& ngc) {
|
||||
return var::num(static_cast<f64>(~(
|
||||
static_cast<u32>(local[1].num()) &
|
||||
static_cast<u32>(local[2].num())
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_u32not(var* local, gc& ngc) {
|
||||
return var::num(static_cast<f64>(~static_cast<u32>(local[1].num())));
|
||||
}
|
||||
|
||||
var builtin_fld(var* local, gc& ngc) {
|
||||
// bits.fld(s,0,3);
|
||||
// if s stores 10100010(162)
|
||||
// will get 101(5)
|
||||
var str = local[1];
|
||||
var startbit = local[2];
|
||||
var length = local[3];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
||||
return nas_err("fld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||
return nas_err("fld", "\"startbit\",\"len\" must be number");
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("fld", "bitfield out of bounds");
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
}
|
||||
return var::num(static_cast<f64>(res));
|
||||
}
|
||||
|
||||
var builtin_sfld(var* local, gc& ngc) {
|
||||
// bits.sfld(s,0,3);
|
||||
// if s stores 10100010(162)
|
||||
// will get 101(5) then this will be signed extended to
|
||||
// 11111101(-3)
|
||||
var str = local[1];
|
||||
var startbit = local[2];
|
||||
var length = local[3];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
||||
return nas_err("sfld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||
return nas_err("sfld", "\"startbit\",\"len\" must be number");
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("sfld", "bitfield out of bounds");
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
}
|
||||
if (res&(1<<(len-1))) {
|
||||
res |= ~((1<<len)-1);
|
||||
}
|
||||
return var::num(static_cast<f64>(static_cast<i32>(res)));
|
||||
}
|
||||
|
||||
var builtin_setfld(var* local, gc& ngc) {
|
||||
// bits.setfld(s,0,8,69);
|
||||
// set 01000101(69) to string will get this:
|
||||
// 10100010(162)
|
||||
// so s[0]=162
|
||||
var str = local[1];
|
||||
var startbit = local[2];
|
||||
var length = local[3];
|
||||
var value = local[4];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
||||
return nas_err("setfld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
|
||||
return nas_err("setfld", "\"startbit\",\"len\",\"val\" must be number");
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
u64 val = static_cast<u64>(value.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("setfld", "bitfield out of bounds");
|
||||
}
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (val&(1<<(i-bit))) {
|
||||
s[i>>3] |= (1<<(7-(i&7)));
|
||||
} else {
|
||||
s[i>>3] &= ~(1<<(7-(i&7)));
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_buf(var* local, gc& ngc) {
|
||||
var length = local[1];
|
||||
if (length.type!=vm_num || length.num()<=0) {
|
||||
return nas_err("buf", "\"len\" must be number greater than 0");
|
||||
}
|
||||
var str = ngc.alloc(vm_str);
|
||||
auto& s = str.str();
|
||||
s.resize(length.num(), '\0');
|
||||
return str;
|
||||
}
|
||||
|
||||
nasal_builtin_table bits_native[] = {
|
||||
{"__u32xor", builtin_u32xor},
|
||||
{"__u32and", builtin_u32and},
|
||||
{"__u32or", builtin_u32or},
|
||||
{"__u32nand", builtin_u32nand},
|
||||
{"__u32not", builtin_u32not},
|
||||
{"__fld", builtin_fld},
|
||||
{"__sfld", builtin_sfld},
|
||||
{"__setfld", builtin_setfld},
|
||||
{"__buf", builtin_buf},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
21
src/bits_lib.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_u32xor(var*, gc&);
|
||||
var builtin_u32and(var*, gc&);
|
||||
var builtin_u32or(var*, gc&);
|
||||
var builtin_u32nand(var*, gc&);
|
||||
var builtin_u32not(var*, gc&);
|
||||
var builtin_fld(var*, gc&);
|
||||
var builtin_sfld(var*, gc&);
|
||||
var builtin_setfld(var*, gc&);
|
||||
var builtin_buf(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table bits_native[];
|
||||
|
||||
}
|
||||
120
src/coroutine.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "coroutine.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_cocreate(var* local, gc& ngc) {
|
||||
// +-------------+
|
||||
// | old pc | <- top[0]
|
||||
// +-------------+
|
||||
// | old localr | <- top[-1]
|
||||
// +-------------+
|
||||
// | old upvalr | <- top[-2]
|
||||
// +-------------+
|
||||
// | local scope |
|
||||
// | ... |
|
||||
// +-------------+ <- local pointer stored in localr
|
||||
// | old funcr | <- old function stored in funcr
|
||||
// +-------------+
|
||||
var func = local[1];
|
||||
if (func.type!=vm_func) {
|
||||
return nas_err("coroutine::create", "must use a function to create coroutine");
|
||||
}
|
||||
if (ngc.cort) {
|
||||
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
|
||||
}
|
||||
var co = ngc.alloc(vm_co);
|
||||
nas_co& cort = co.co();
|
||||
cort.ctx.pc = func.func().entry-1;
|
||||
|
||||
cort.ctx.top[0] = nil;
|
||||
cort.ctx.localr = cort.ctx.top+1;
|
||||
cort.ctx.top = cort.ctx.localr+func.func().lsize;
|
||||
cort.ctx.localr[0] = func.func().local[0];
|
||||
cort.ctx.top[0] = nil; // old upvalr
|
||||
cort.ctx.top++;
|
||||
cort.ctx.top[0] = var::addr((var*)nullptr); // old localr
|
||||
cort.ctx.top++;
|
||||
cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
|
||||
|
||||
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
|
||||
cort.status = nas_co::status::suspended;
|
||||
|
||||
return co;
|
||||
}
|
||||
|
||||
var builtin_coresume(var* local, gc& ngc) {
|
||||
if (ngc.cort) {
|
||||
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
|
||||
}
|
||||
var co = local[1];
|
||||
// return nil if is not a coroutine object
|
||||
if (co.type!=vm_co) {
|
||||
return nil;
|
||||
}
|
||||
// cannot resume a dead coroutine
|
||||
if (co.co().status==nas_co::status::dead) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// change to coroutine context
|
||||
ngc.ctxchg(co.co());
|
||||
|
||||
// fetch coroutine's stack top and return
|
||||
// so the coroutine's stack top in fact is not changed
|
||||
if (ngc.rctx->top[0].type==vm_ret) {
|
||||
// when first calling this coroutine, the stack top must be vm_ret
|
||||
return ngc.rctx->top[0];
|
||||
}
|
||||
|
||||
// after first calling the coroutine, each time coroutine.yield triggered
|
||||
// a new space will be reserved on stack with value nil
|
||||
// so we could fill this place with args
|
||||
|
||||
// the coroutine seems like coroutine.yield returns the value
|
||||
// but in fact coroutine.yield stop the coroutine
|
||||
// until main context calls the coroutine.resume
|
||||
return local[2];
|
||||
}
|
||||
|
||||
var builtin_coyield(var* local, gc& ngc) {
|
||||
if (!ngc.cort) {
|
||||
return nas_err("coroutine::yield", "no coroutine is running");
|
||||
}
|
||||
|
||||
// this will set to main stack top
|
||||
ngc.ctxreserve();
|
||||
|
||||
// then this will return value to main's stack top[0]
|
||||
// the procedure seems like coroutine.resume returns the value
|
||||
// but in fact coroutine.resume stop the main context
|
||||
// until coroutine calls the coroutine.yield
|
||||
return local[1];
|
||||
}
|
||||
|
||||
var builtin_costatus(var* local, gc& ngc) {
|
||||
var co = local[1];
|
||||
if (co.type!=vm_co) {
|
||||
return ngc.newstr("error");
|
||||
}
|
||||
switch(co.co().status) {
|
||||
case nas_co::status::suspended: return ngc.newstr("suspended");
|
||||
case nas_co::status::running: return ngc.newstr("running");
|
||||
case nas_co::status::dead: return ngc.newstr("dead");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_corun(var* local, gc& ngc) {
|
||||
return ngc.cort? one:zero;
|
||||
}
|
||||
|
||||
nasal_builtin_table coroutine_native[] = {
|
||||
{"__cocreate", builtin_cocreate},
|
||||
{"__coresume", builtin_coresume},
|
||||
{"__coyield", builtin_coyield},
|
||||
{"__costatus", builtin_costatus},
|
||||
{"__corun", builtin_corun},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
17
src/coroutine.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_cocreate(var*, gc&);
|
||||
var builtin_coresume(var*, gc&);
|
||||
var builtin_coyield(var*, gc&);
|
||||
var builtin_costatus(var*, gc&);
|
||||
var builtin_corun(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table coroutine_native[];
|
||||
|
||||
}
|
||||
117
src/dylib_lib.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include "dylib_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto dylib_type_name = "dylib";
|
||||
const auto func_addr_type_name = "faddr";
|
||||
|
||||
void dylib_destructor(void* ptr) {
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(static_cast<HMODULE>(ptr));
|
||||
#else
|
||||
dlclose(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void func_addr_destructor(void* ptr) {}
|
||||
|
||||
var builtin_dlopen(var* local, gc& ngc) {
|
||||
var dlname = local[1];
|
||||
if (dlname.type!=vm_str) {
|
||||
return nas_err("dlopen", "\"libname\" must be string");
|
||||
}
|
||||
#ifdef _WIN32
|
||||
wchar_t* str = new wchar_t[dlname.str().size()+1];
|
||||
if (!str) {
|
||||
return nas_err("dlopen", "malloc failed");
|
||||
}
|
||||
memset(str, 0, sizeof(wchar_t)*dlname.str().size()+1);
|
||||
mbstowcs(str, dlname.str().c_str(),dlname.str().size()+1);
|
||||
void* ptr = LoadLibraryA(dlname.str().c_str());
|
||||
delete []str;
|
||||
#else
|
||||
void* ptr = dlopen(dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY);
|
||||
#endif
|
||||
if (!ptr) {
|
||||
return nas_err("dlopen", "cannot open dynamic lib <"+dlname.str()+">");
|
||||
}
|
||||
var ret = ngc.temp = ngc.alloc(vm_hash);
|
||||
var lib = ngc.alloc(vm_obj);
|
||||
lib.obj().set(dylib_type_name, dylib_destructor, ptr);
|
||||
ret.hash().elems["lib"] = lib;
|
||||
|
||||
#ifdef _WIN32
|
||||
void* func = (void*)GetProcAddress(
|
||||
static_cast<HMODULE>(lib.obj().ptr),
|
||||
"get"
|
||||
);
|
||||
#else
|
||||
void* func = dlsym(lib.obj().ptr, "get");
|
||||
#endif
|
||||
if (!func) {
|
||||
return nas_err("dlopen", "cannot find <get> function");
|
||||
}
|
||||
// get function pointer by name
|
||||
module_func_info* tbl = reinterpret_cast<get_func_ptr>(func)();
|
||||
if (!tbl) {
|
||||
return nas_err("dlopen", "failed to get module functions");
|
||||
}
|
||||
for(u32 i = 0; tbl[i].name; ++i) {
|
||||
void* p = (void*)tbl[i].fd;
|
||||
var tmp = ngc.alloc(vm_obj);
|
||||
tmp.obj().set(func_addr_type_name, func_addr_destructor, p);
|
||||
ret.hash().elems[tbl[i].name] = tmp;
|
||||
}
|
||||
|
||||
ngc.temp = nil;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_dlclose(var* local, gc& ngc) {
|
||||
var libptr = local[1];
|
||||
if (!libptr.objchk(dylib_type_name)) {
|
||||
return nas_err("dlclose", "\"lib\" is not a valid dynamic lib");
|
||||
}
|
||||
libptr.obj().clear();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_dlcallv(var* local, gc& ngc) {
|
||||
var fp = local[1];
|
||||
var args = local[2];
|
||||
if (!fp.objchk(func_addr_type_name)) {
|
||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
||||
}
|
||||
auto& vec = args.vec().elems;
|
||||
return reinterpret_cast<module_func>(fp.obj().ptr)(
|
||||
vec.data(),
|
||||
vec.size(),
|
||||
&ngc
|
||||
);
|
||||
}
|
||||
|
||||
var builtin_dlcall(var* local, gc& ngc) {
|
||||
var fp = local[1];
|
||||
if (!fp.objchk(func_addr_type_name)) {
|
||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
||||
}
|
||||
|
||||
var* local_frame_start = local+2;
|
||||
usize local_frame_size = ngc.rctx->top-local_frame_start;
|
||||
// arguments' stored place begins at local +2
|
||||
return reinterpret_cast<module_func>(fp.obj().ptr)(
|
||||
local_frame_start,
|
||||
local_frame_size,
|
||||
&ngc
|
||||
);
|
||||
}
|
||||
|
||||
nasal_builtin_table dylib_lib_native[] = {
|
||||
{"__dlopen", builtin_dlopen},
|
||||
{"__dlclose", builtin_dlclose},
|
||||
{"__dlcallv", builtin_dlcallv},
|
||||
{"__dlcall", builtin_dlcall},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
26
src/dylib_lib.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void dylib_destructor(void*);
|
||||
void func_addr_destructor(void*);
|
||||
|
||||
var builtin_dlopen(var*, gc&);
|
||||
var builtin_dlclose(var*, gc&);
|
||||
var builtin_dlcallv(var*, gc&);
|
||||
var builtin_dlcall(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table dylib_lib_native[];
|
||||
|
||||
}
|
||||
41
src/fg_props.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "fg_props.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_logprint(var* local, gc& ngc) {
|
||||
var level = local[1];
|
||||
var elems = local[2];
|
||||
if (elems.type!=vm_vec) {
|
||||
return nas_err("logprint", "received argument is not vector.");
|
||||
}
|
||||
std::ofstream out("fgfs.log", std::ios::app);
|
||||
switch (static_cast<u32>(level.num())) {
|
||||
case SG_LOG_BULK: out << "[LOG_BULK]"; break;
|
||||
case SG_LOG_DEBUG: out << "[LOG_DEBUG]"; break;
|
||||
case SG_LOG_INFO: out << "[LOG_INFO]"; break;
|
||||
case SG_LOG_WARN: out << "[LOG_WARN]"; break;
|
||||
case SG_LOG_ALERT: out << "[LOG_ALERT]"; break;
|
||||
case SG_DEV_WARN: out << "[DEV_WARN]"; break;
|
||||
case SG_DEV_ALERT: out << "[DEV_ALERT]"; break;
|
||||
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
|
||||
default:
|
||||
return nas_err("logprint",
|
||||
"incorrect log level " +
|
||||
std::to_string(level.num())
|
||||
);
|
||||
}
|
||||
for(auto& i : elems.vec().elems) {
|
||||
out << i << " ";
|
||||
}
|
||||
out << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
nasal_builtin_table flight_gear_native[] = {
|
||||
{"_logprint", builtin_logprint},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
22
src/fg_props.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
#define SG_LOG_BULK 1
|
||||
#define SG_LOG_DEBUG 2
|
||||
#define SG_LOG_INFO 3
|
||||
#define SG_LOG_WARN 4
|
||||
#define SG_LOG_ALERT 5
|
||||
#define SG_DEV_WARN 7
|
||||
#define SG_DEV_ALERT 8
|
||||
#define SG_MANDATORY_INFO 9
|
||||
|
||||
var builtin_logprint(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table flight_gear_native[];
|
||||
|
||||
}
|
||||
213
src/io_lib.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
#include "io_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto file_type_name = "file";
|
||||
|
||||
void filehandle_destructor(void* ptr) {
|
||||
if (static_cast<FILE*>(ptr)==stdin) {
|
||||
return;
|
||||
}
|
||||
fclose(static_cast<FILE*>(ptr));
|
||||
}
|
||||
|
||||
var builtin_readfile(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_str) {
|
||||
return nas_err("io::readfile", "\"filename\" must be string");
|
||||
}
|
||||
std::ifstream in(val.str(), std::ios::binary);
|
||||
std::stringstream rd;
|
||||
if (!in.fail()) {
|
||||
rd << in.rdbuf();
|
||||
}
|
||||
return ngc.newstr(rd.str());
|
||||
}
|
||||
|
||||
var builtin_fout(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
var str = local[2];
|
||||
if (val.type!=vm_str) {
|
||||
return nas_err("io::fout", "\"filename\" must be string");
|
||||
}
|
||||
std::ofstream out(val.str());
|
||||
if (out.fail()) {
|
||||
return nas_err("io::fout", "cannot open <"+val.str()+">");
|
||||
}
|
||||
out << str;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_exists(var* local, gc& ngc) {
|
||||
if (local[1].type!=vm_str) {
|
||||
return zero;
|
||||
}
|
||||
return access(local[1].str().c_str(), F_OK)!=-1?one:zero;
|
||||
}
|
||||
|
||||
var builtin_open(var* local, gc& ngc) {
|
||||
var name = local[1];
|
||||
var mode = local[2];
|
||||
if (name.type!=vm_str) {
|
||||
return nas_err("open", "\"filename\" must be string");
|
||||
}
|
||||
if (mode.type!=vm_str) {
|
||||
return nas_err("open", "\"mode\" must be string");
|
||||
}
|
||||
FILE* res = fopen(name.str().c_str(), mode.str().c_str());
|
||||
if (!res) {
|
||||
return nas_err("open", "failed to open file <"+name.str()+">");
|
||||
}
|
||||
var ret = ngc.alloc(vm_obj);
|
||||
ret.obj().set(file_type_name, filehandle_destructor, res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_close(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("close", "not a valid filehandle");
|
||||
}
|
||||
fd.obj().clear();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_read(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
var buf = local[2];
|
||||
var len = local[3];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("read", "not a valid filehandle");
|
||||
}
|
||||
if (buf.type!=vm_str || buf.val.gcobj->unmut) {
|
||||
return nas_err("read", "\"buf\" must be mutable string");
|
||||
}
|
||||
if (len.type!=vm_num) {
|
||||
return nas_err("read", "\"len\" must be number");
|
||||
}
|
||||
if (len.num()<=0 || len.num()>=(1<<30)) {
|
||||
return nas_err("read", "\"len\" less than 1 or too large");
|
||||
}
|
||||
char* buff = new char[(usize)len.num()+1];
|
||||
if (!buff) {
|
||||
return nas_err("read", "malloc failed");
|
||||
}
|
||||
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().ptr));
|
||||
buf.str() = buff;
|
||||
buf.val.gcobj->unmut = true;
|
||||
delete []buff;
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
var builtin_write(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
var str = local[2];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("write", "not a valid filehandle");
|
||||
}
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("write", "\"str\" must be string");
|
||||
}
|
||||
return var::num(static_cast<f64>(fwrite(
|
||||
str.str().c_str(),
|
||||
1,
|
||||
str.str().length(),
|
||||
static_cast<FILE*>(fd.obj().ptr)
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_seek(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
var pos = local[2];
|
||||
var whence = local[3];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("seek", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(fseek(
|
||||
static_cast<FILE*>(fd.obj().ptr),
|
||||
pos.num(),
|
||||
whence.num()
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_tell(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("tell", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(ftell(static_cast<FILE*>(fd.obj().ptr))));
|
||||
}
|
||||
|
||||
var builtin_readln(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("readln", "not a valid filehandle");
|
||||
}
|
||||
var str = ngc.alloc(vm_str);
|
||||
char c;
|
||||
while((c = fgetc(static_cast<FILE*>(fd.obj().ptr)))!=EOF) {
|
||||
if (c=='\r') {
|
||||
continue;
|
||||
}
|
||||
if (c=='\n') {
|
||||
return str;
|
||||
}
|
||||
str.str() += c;
|
||||
}
|
||||
if (str.str().length()) {
|
||||
return str;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_stat(var* local, gc& ngc) {
|
||||
var name = local[1];
|
||||
if (name.type!=vm_str) {
|
||||
return nas_err("stat", "\"filename\" must be string");
|
||||
}
|
||||
struct stat buf;
|
||||
if (stat(name.str().c_str(),&buf)<0) {
|
||||
return nas_err("stat", "failed to open file <"+name.str()+">");
|
||||
}
|
||||
var ret = ngc.alloc(vm_vec);
|
||||
ret.vec().elems = {
|
||||
var::num(static_cast<f64>(buf.st_dev)),
|
||||
var::num(static_cast<f64>(buf.st_ino)),
|
||||
var::num(static_cast<f64>(buf.st_mode)),
|
||||
var::num(static_cast<f64>(buf.st_nlink)),
|
||||
var::num(static_cast<f64>(buf.st_uid)),
|
||||
var::num(static_cast<f64>(buf.st_gid)),
|
||||
var::num(static_cast<f64>(buf.st_rdev)),
|
||||
var::num(static_cast<f64>(buf.st_size)),
|
||||
var::num(static_cast<f64>(buf.st_atime)),
|
||||
var::num(static_cast<f64>(buf.st_mtime)),
|
||||
var::num(static_cast<f64>(buf.st_ctime))
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_eof(var* local, gc& ngc) {
|
||||
var fd = local[1];
|
||||
if (!fd.objchk(file_type_name)) {
|
||||
return nas_err("readln", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(feof(static_cast<FILE*>(fd.obj().ptr))));
|
||||
}
|
||||
|
||||
nasal_builtin_table io_lib_native[] = {
|
||||
{"__readfile", builtin_readfile},
|
||||
{"__fout", builtin_fout},
|
||||
{"__exists", builtin_exists},
|
||||
{"__open", builtin_open},
|
||||
{"__close", builtin_close},
|
||||
{"__read", builtin_read},
|
||||
{"__write", builtin_write},
|
||||
{"__seek", builtin_seek},
|
||||
{"__tell", builtin_tell},
|
||||
{"__readln", builtin_readln},
|
||||
{"__stat", builtin_stat},
|
||||
{"__eof", builtin_eof},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
32
src/io_lib.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define F_OK 0 // fuck msc
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void filehandle_destructor(void*);
|
||||
|
||||
var builtin_readfile(var*, gc&);
|
||||
var builtin_fout(var*, gc&);
|
||||
var builtin_exists(var*, gc&);
|
||||
var builtin_open(var*, gc&);
|
||||
var builtin_close(var*, gc&);
|
||||
var builtin_read(var*, gc&);
|
||||
var builtin_write(var*, gc&);
|
||||
var builtin_seek(var*, gc&);
|
||||
var builtin_tell(var*, gc&);
|
||||
var builtin_readln(var*, gc&);
|
||||
var builtin_stat(var*, gc&);
|
||||
var builtin_eof(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table io_lib_native[];
|
||||
|
||||
}
|
||||
231
src/main.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "ast_visitor.h"
|
||||
#include "ast_dumper.h"
|
||||
#include "symbol_finder.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
#include "repl.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
|
||||
const u32 VM_RAW_AST = 1;
|
||||
const u32 VM_AST = 1<<1;
|
||||
const u32 VM_CODE = 1<<2;
|
||||
const u32 VM_TIME = 1<<3;
|
||||
const u32 VM_EXEC = 1<<4;
|
||||
const u32 VM_DETAIL = 1<<5;
|
||||
const u32 VM_DEBUG = 1<<6;
|
||||
const u32 VM_SYMINFO = 1<<7;
|
||||
const u32 VM_PROFILE = 1<<8;
|
||||
const u32 VM_PROF_ALL = 1<<9;
|
||||
|
||||
std::ostream& help(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " ,--#-,\n"
|
||||
<< "<3 / \\____\\ <3\n"
|
||||
<< " |_|__A_|\n"
|
||||
#ifdef _WIN32
|
||||
<< "use command <chcp 65001> to use unicode.\n"
|
||||
#endif
|
||||
<< "\nnasal <option>\n"
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< " -r, --repl | use repl interpreter(experimental).\n"
|
||||
<< "\nnasal [option] <file> [argv]\n"
|
||||
<< "option:\n"
|
||||
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||
<< " --raw-ast | view ast without after-processing.\n"
|
||||
<< " -c, --code | view generated bytecode.\n"
|
||||
<< " -s, --symbol | show analysed symbol info.\n"
|
||||
<< " -e, --exec | execute directly.\n"
|
||||
<< " -t, --time | show execute time.\n"
|
||||
<< " -d, --detail | get detail info.\n"
|
||||
<< " -dbg, --debug | debug mode.\n"
|
||||
<< " --prof | show profiling result, available in debug mode.\n"
|
||||
<< " --prof-all | show profiling result of all files,"
|
||||
<< "available under debug mode.\n"
|
||||
<< "file:\n"
|
||||
<< " <filename> | execute file.\n"
|
||||
<< "argv:\n"
|
||||
<< " <args> | cmd arguments used in program.\n"
|
||||
<< "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& logo(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " __ _\n"
|
||||
<< " /\\ \\ \\__ _ ___ __ _| |\n"
|
||||
<< " / \\/ / _` / __|/ _` | |\n"
|
||||
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||
<< "std : c++ " << __cplusplus << "\n"
|
||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<< "\n"
|
||||
<< "input <nasal -h> to get help .\n\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& version(std::ostream& out) {
|
||||
std::srand(std::time(nullptr));
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
if (num<0.01) {
|
||||
nasal::parse::easter_egg();
|
||||
}
|
||||
out << "nasal interpreter version " << __nasver;
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void err() {
|
||||
std::cerr
|
||||
<< "invalid argument(s).\n"
|
||||
<< "use <nasal -h> to get help.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void execute(
|
||||
const std::string& file,
|
||||
const std::vector<std::string>& argv,
|
||||
const u32 cmd) {
|
||||
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
const auto den = clk::duration::period::den;
|
||||
|
||||
nasal::lexer lex;
|
||||
nasal::parse parse;
|
||||
nasal::linker ld;
|
||||
nasal::codegen gen;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lex.scan(file).chkerr();
|
||||
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lex).chkerr();
|
||||
if (cmd&VM_RAW_AST) {
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
// linker gets parser's ast and load import files to this ast
|
||||
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
||||
|
||||
// optimizer does simple optimization on ast
|
||||
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
|
||||
opt->do_optimization(parse.tree());
|
||||
if (cmd&VM_AST) {
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
// code generator gets parser's ast and import file list to generate code
|
||||
gen.compile(parse, ld, false).chkerr();
|
||||
if (cmd&VM_CODE) {
|
||||
gen.print(std::cout);
|
||||
}
|
||||
if (cmd&VM_SYMINFO) {
|
||||
gen.symbol_dump(std::cout);
|
||||
}
|
||||
|
||||
// run
|
||||
auto start = clk::now();
|
||||
if (cmd&VM_DEBUG) {
|
||||
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg);
|
||||
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
|
||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
|
||||
runtime->set_detail_report_info(cmd&VM_DETAIL);
|
||||
runtime->run(gen, ld, argv);
|
||||
}
|
||||
|
||||
// get running time
|
||||
auto end = clk::now();
|
||||
if (cmd&VM_TIME) {
|
||||
std::clog << "process exited after ";
|
||||
std::clog << (end-start).count()*1.0/den << "s.\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
i32 main(i32 argc, const char* argv[]) {
|
||||
// output version info
|
||||
if (argc<=1) {
|
||||
std::clog << logo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run directly or show help
|
||||
if (argc==2) {
|
||||
std::string s(argv[1]);
|
||||
if (s=="-h" || s=="--help") {
|
||||
std::clog << help;
|
||||
} else if (s=="-v" || s=="--version") {
|
||||
std::clog << version;
|
||||
} else if (s=="-r" || s=="--repl") {
|
||||
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl);
|
||||
repl->execute();
|
||||
} else if (s[0]!='-') {
|
||||
execute(s, {}, VM_EXEC);
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// execute with arguments
|
||||
const std::unordered_map<std::string, u32> cmdlst = {
|
||||
{"--raw-ast", VM_RAW_AST},
|
||||
{"--ast", VM_AST},
|
||||
{"-a", VM_AST},
|
||||
{"--code", VM_CODE},
|
||||
{"-c", VM_CODE},
|
||||
{"--symbol", VM_SYMINFO},
|
||||
{"-s", VM_SYMINFO},
|
||||
{"--exec", VM_EXEC},
|
||||
{"-e", VM_EXEC},
|
||||
{"--time", VM_TIME|VM_EXEC},
|
||||
{"-t", VM_TIME|VM_EXEC},
|
||||
{"--detail", VM_DETAIL|VM_EXEC},
|
||||
{"-d", VM_DETAIL|VM_EXEC},
|
||||
{"--debug", VM_DEBUG},
|
||||
{"-dbg", VM_DEBUG},
|
||||
{"--prof", VM_PROFILE},
|
||||
{"--prof-all", VM_PROF_ALL}
|
||||
};
|
||||
u32 cmd = 0;
|
||||
std::string filename = "";
|
||||
std::vector<std::string> vm_argv;
|
||||
for(i32 i = 1; i<argc; ++i) {
|
||||
if (cmdlst.count(argv[i])) {
|
||||
cmd |= cmdlst.at(argv[i]);
|
||||
} else if (!filename.length()) {
|
||||
filename = argv[i];
|
||||
} else {
|
||||
vm_argv.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
if (!filename.length()) {
|
||||
err();
|
||||
}
|
||||
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
|
||||
return 0;
|
||||
}
|
||||
77
src/math_lib.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "math_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_pow(var* local, gc& ngc) {
|
||||
var x = local[1];
|
||||
var y = local[2];
|
||||
if (x.type!=vm_num || y.type!=vm_num) {
|
||||
return var::num(std::nan(""));
|
||||
}
|
||||
return var::num(std::pow(x.num(), y.num()));
|
||||
}
|
||||
|
||||
var builtin_sin(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_cos(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_tan(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_exp(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_lg(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_ln(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_sqrt(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_atan2(var* local, gc& ngc) {
|
||||
var x = local[1];
|
||||
var y = local[2];
|
||||
if (x.type!=vm_num || y.type!=vm_num) {
|
||||
return var::num(std::nan(""));
|
||||
}
|
||||
return var::num(atan2(y.num(), x.num()));
|
||||
}
|
||||
|
||||
var builtin_isnan(var* local, gc& ngc) {
|
||||
var x = local[1];
|
||||
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
|
||||
}
|
||||
|
||||
nasal_builtin_table math_lib_native[] = {
|
||||
{"__pow", builtin_pow},
|
||||
{"__sin", builtin_sin},
|
||||
{"__cos", builtin_cos},
|
||||
{"__tan", builtin_tan},
|
||||
{"__exp", builtin_exp},
|
||||
{"__lg", builtin_lg},
|
||||
{"__ln", builtin_ln},
|
||||
{"__sqrt", builtin_sqrt},
|
||||
{"__atan2", builtin_atan2},
|
||||
{"__isnan", builtin_isnan},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
22
src/math_lib.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_pow(var*, gc&);
|
||||
var builtin_sin(var*, gc&);
|
||||
var builtin_cos(var*, gc&);
|
||||
var builtin_tan(var*, gc&);
|
||||
var builtin_exp(var*, gc&);
|
||||
var builtin_lg(var*, gc&);
|
||||
var builtin_ln(var*, gc&);
|
||||
var builtin_sqrt(var*, gc&);
|
||||
var builtin_atan2(var*, gc&);
|
||||
var builtin_isnan(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table math_lib_native[];
|
||||
|
||||
}
|
||||
61
src/nasal.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __nasver
|
||||
#define __nasver "11.0"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
// abbreviation of some useful basic type
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using usize = std::size_t;
|
||||
using f64 = double;
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool is_windows();
|
||||
bool is_linux();
|
||||
bool is_macos();
|
||||
bool is_x86();
|
||||
bool is_amd64();
|
||||
bool is_x86_64();
|
||||
bool is_arm();
|
||||
bool is_aarch64();
|
||||
bool is_ia64();
|
||||
bool is_powerpc();
|
||||
bool is_superh();
|
||||
|
||||
|
||||
// virtual machine stack depth
|
||||
// both global depth and value stack depth
|
||||
const u32 STACK_DEPTH = 4096;
|
||||
|
||||
f64 hex2f(const char*);
|
||||
f64 oct2f(const char*);
|
||||
|
||||
// we have the same reason not using atof here
|
||||
// just as andy's interpreter does.
|
||||
// it is not platform independent, and may have strange output.
|
||||
// so we write a new function here to convert str to number manually.
|
||||
// but this also makes 0.1+0.2==0.3,
|
||||
// not another result that you may get in other languages.
|
||||
f64 dec2f(const char*);
|
||||
|
||||
f64 str2num(const char*);
|
||||
i32 utf8_hdchk(const char);
|
||||
std::string chrhex(const char);
|
||||
std::string rawstr(const std::string&, const usize maxlen=0);
|
||||
|
||||
}
|
||||
|
||||
#include "nasal_gc.h"
|
||||
372
src/nasal_ast.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_expr(this);
|
||||
}
|
||||
|
||||
void call::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call(this);
|
||||
}
|
||||
|
||||
void null_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_null_expr(this);
|
||||
}
|
||||
|
||||
void nil_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_nil_expr(this);
|
||||
}
|
||||
|
||||
void number_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_number_literal(this);
|
||||
}
|
||||
|
||||
void string_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_string_literal(this);
|
||||
}
|
||||
|
||||
void identifier::accept(ast_visitor* visitor) {
|
||||
visitor->visit_identifier(this);
|
||||
}
|
||||
|
||||
void bool_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_bool_literal(this);
|
||||
}
|
||||
|
||||
vector_expr::~vector_expr() {
|
||||
for(auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void vector_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_vector_expr(this);
|
||||
}
|
||||
|
||||
hash_expr::~hash_expr() {
|
||||
for(auto i : members) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_hash_expr(this);
|
||||
}
|
||||
|
||||
hash_pair::~hash_pair() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_pair::accept(ast_visitor* visitor) {
|
||||
visitor->visit_hash_pair(this);
|
||||
}
|
||||
|
||||
function::~function() {
|
||||
for(auto i : parameter_list) {
|
||||
delete i;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void function::accept(ast_visitor* visitor) {
|
||||
visitor->visit_function(this);
|
||||
}
|
||||
|
||||
code_block::~code_block() {
|
||||
for(auto i : expressions) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void code_block::accept(ast_visitor* visitor) {
|
||||
visitor->visit_code_block(this);
|
||||
}
|
||||
|
||||
parameter::~parameter() {
|
||||
if (default_value) {
|
||||
delete default_value;
|
||||
}
|
||||
}
|
||||
|
||||
void parameter::accept(ast_visitor* visitor) {
|
||||
visitor->visit_parameter(this);
|
||||
}
|
||||
|
||||
ternary_operator::~ternary_operator() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
}
|
||||
|
||||
void ternary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_ternary_operator(this);
|
||||
}
|
||||
|
||||
binary_operator::~binary_operator() {
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
if (optimized_const_number) {
|
||||
delete optimized_const_number;
|
||||
}
|
||||
if (optimized_const_string) {
|
||||
delete optimized_const_string;
|
||||
}
|
||||
}
|
||||
|
||||
void binary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_binary_operator(this);
|
||||
}
|
||||
|
||||
unary_operator::~unary_operator() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
if (optimized_number) {
|
||||
delete optimized_number;
|
||||
}
|
||||
}
|
||||
|
||||
void unary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_unary_operator(this);
|
||||
}
|
||||
|
||||
call_expr::~call_expr() {
|
||||
if(first) {
|
||||
delete first;
|
||||
}
|
||||
for(auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_expr(this);
|
||||
}
|
||||
|
||||
void call_hash::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_hash(this);
|
||||
}
|
||||
|
||||
call_vector::~call_vector() {
|
||||
for(auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_vector::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_vector(this);
|
||||
}
|
||||
|
||||
call_function::~call_function() {
|
||||
for(auto i : args) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_function::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_function(this);
|
||||
}
|
||||
|
||||
slice_vector::~slice_vector() {
|
||||
if (begin) {
|
||||
delete begin;
|
||||
}
|
||||
if (end) {
|
||||
delete end;
|
||||
}
|
||||
}
|
||||
|
||||
void slice_vector::accept(ast_visitor* visitor) {
|
||||
visitor->visit_slice_vector(this);
|
||||
}
|
||||
|
||||
definition_expr::~definition_expr() {
|
||||
if (variable_name) {
|
||||
delete variable_name;
|
||||
}
|
||||
if (variables) {
|
||||
delete variables;
|
||||
}
|
||||
if (tuple) {
|
||||
delete tuple;
|
||||
}
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void definition_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_definition_expr(this);
|
||||
}
|
||||
|
||||
assignment_expr::~assignment_expr() {
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
}
|
||||
|
||||
void assignment_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_assignment_expr(this);
|
||||
}
|
||||
|
||||
multi_identifier::~multi_identifier() {
|
||||
for(auto i : variables) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_identifier::accept(ast_visitor* visitor) {
|
||||
visitor->visit_multi_identifier(this);
|
||||
}
|
||||
|
||||
tuple_expr::~tuple_expr() {
|
||||
for(auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void tuple_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_tuple_expr(this);
|
||||
}
|
||||
|
||||
multi_assign::~multi_assign() {
|
||||
if (tuple) {
|
||||
delete tuple;
|
||||
}
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_assign::accept(ast_visitor* visitor) {
|
||||
visitor->visit_multi_assign(this);
|
||||
}
|
||||
|
||||
while_expr::~while_expr() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void while_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_while_expr(this);
|
||||
}
|
||||
|
||||
for_expr::~for_expr() {
|
||||
if (initializing) {
|
||||
delete initializing;
|
||||
}
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (step) {
|
||||
delete step;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void for_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_for_expr(this);
|
||||
}
|
||||
|
||||
iter_expr::~iter_expr() {
|
||||
if (name) {
|
||||
delete name;
|
||||
}
|
||||
if (call) {
|
||||
delete call;
|
||||
}
|
||||
}
|
||||
|
||||
void iter_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_iter_expr(this);
|
||||
}
|
||||
|
||||
forei_expr::~forei_expr() {
|
||||
if (iterator) {
|
||||
delete iterator;
|
||||
}
|
||||
if (vector_node) {
|
||||
delete vector_node;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void forei_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_forei_expr(this);
|
||||
}
|
||||
|
||||
condition_expr::~condition_expr() {
|
||||
if (if_stmt) {
|
||||
delete if_stmt;
|
||||
}
|
||||
for(auto i : elsif_stmt) {
|
||||
delete i;
|
||||
}
|
||||
if (else_stmt) {
|
||||
delete else_stmt;
|
||||
}
|
||||
}
|
||||
|
||||
void condition_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_condition_expr(this);
|
||||
}
|
||||
|
||||
if_expr::~if_expr() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void if_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_if_expr(this);
|
||||
}
|
||||
|
||||
void continue_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_continue_expr(this);
|
||||
}
|
||||
|
||||
void break_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_break_expr(this);
|
||||
}
|
||||
|
||||
return_expr::~return_expr() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void return_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_return_expr(this);
|
||||
}
|
||||
|
||||
}
|
||||
675
src/nasal_ast.h
Normal file
@@ -0,0 +1,675 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class expr_type:u32 {
|
||||
ast_null = 0, // null node
|
||||
ast_block, // code block
|
||||
ast_nil, // nil keyword
|
||||
ast_num, // number, basic value type
|
||||
ast_str, // string, basic value type
|
||||
ast_id, // identifier
|
||||
ast_bool, // bools
|
||||
ast_func, // func keyword
|
||||
ast_hash, // hash, basic value type
|
||||
ast_vec, // vector, basic value type
|
||||
ast_pair, // pair of key and value in hashmap
|
||||
ast_call, // mark a sub-tree of calling an identifier
|
||||
ast_callh, // id.name
|
||||
ast_callv, // id[index]
|
||||
ast_callf, // id()
|
||||
ast_subvec, // id[index:index]
|
||||
ast_param, // function parameter
|
||||
ast_ternary, // ternary operator
|
||||
ast_binary, // binary operator
|
||||
ast_unary, // unary operator
|
||||
ast_for, // for keyword
|
||||
ast_forei, // foreach or forindex loop
|
||||
ast_while, // while
|
||||
ast_iter, // iterator, used in forindex/foreach
|
||||
ast_cond, // mark a sub-tree of conditional expression
|
||||
ast_if, // if keyword
|
||||
ast_multi_id, // multi identifiers sub-tree
|
||||
ast_tuple, // tuple, stores multiple scalars
|
||||
ast_def, // definition
|
||||
ast_assign, // assignment
|
||||
ast_multi_assign,// multiple assignment
|
||||
ast_continue, // continue keyword, only used in loop
|
||||
ast_break, // break keyword, only used in loop
|
||||
ast_ret // return keyword, only used in function block
|
||||
};
|
||||
|
||||
class ast_visitor;
|
||||
class hash_pair;
|
||||
class parameter;
|
||||
class slice_vector;
|
||||
class multi_identifier;
|
||||
class code_block;
|
||||
class if_expr;
|
||||
class tuple_expr;
|
||||
|
||||
class expr {
|
||||
protected:
|
||||
span nd_loc;
|
||||
expr_type nd_type;
|
||||
|
||||
public:
|
||||
expr(const span& location, expr_type node_type):
|
||||
nd_loc(location), nd_type(node_type) {}
|
||||
virtual ~expr() = default;
|
||||
void set_begin(u32 line, u32 column) {
|
||||
nd_loc.begin_line = line;
|
||||
nd_loc.begin_column = column;
|
||||
}
|
||||
const span& get_location() const {return nd_loc;}
|
||||
const u32 get_line() const {return nd_loc.begin_line;}
|
||||
expr_type get_type() const {return nd_type;}
|
||||
void update_location(const span& location) {
|
||||
nd_loc.end_line = location.end_line;
|
||||
nd_loc.end_column = location.end_column;
|
||||
}
|
||||
virtual void accept(ast_visitor*);
|
||||
};
|
||||
|
||||
class call:public expr {
|
||||
public:
|
||||
call(const span& location, expr_type node_type):
|
||||
expr(location, node_type) {}
|
||||
virtual ~call() = default;
|
||||
virtual void accept(ast_visitor*);
|
||||
};
|
||||
|
||||
class null_expr:public expr {
|
||||
public:
|
||||
null_expr(const span& location):
|
||||
expr(location, expr_type::ast_null) {}
|
||||
~null_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class nil_expr:public expr {
|
||||
public:
|
||||
nil_expr(const span& location):
|
||||
expr(location, expr_type::ast_nil) {}
|
||||
~nil_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class number_literal:public expr {
|
||||
private:
|
||||
f64 number;
|
||||
|
||||
public:
|
||||
number_literal(const span& location, const f64 num):
|
||||
expr(location, expr_type::ast_num), number(num) {}
|
||||
~number_literal() override = default;
|
||||
f64 get_number() const {return number;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class string_literal:public expr {
|
||||
private:
|
||||
std::string content;
|
||||
|
||||
public:
|
||||
string_literal(const span& location, const std::string& str):
|
||||
expr(location, expr_type::ast_str), content(str) {}
|
||||
~string_literal() override = default;
|
||||
const std::string get_content() const {return content;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class identifier:public expr {
|
||||
private:
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
identifier(const span& location, const std::string& str):
|
||||
expr(location, expr_type::ast_id), name(str) {}
|
||||
~identifier() override = default;
|
||||
const std::string& get_name() const {return name;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class bool_literal:public expr {
|
||||
private:
|
||||
bool flag;
|
||||
|
||||
public:
|
||||
bool_literal(const span& location, const bool bool_flag):
|
||||
expr(location, expr_type::ast_bool), flag(bool_flag) {}
|
||||
~bool_literal() override = default;
|
||||
bool get_flag() const {return flag;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class vector_expr:public expr {
|
||||
private:
|
||||
std::vector<expr*> elements;
|
||||
|
||||
public:
|
||||
vector_expr(const span& location):
|
||||
expr(location, expr_type::ast_vec) {}
|
||||
~vector_expr() override;
|
||||
void add_element(expr* node) {elements.push_back(node);}
|
||||
std::vector<expr*>& get_elements() {return elements;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class hash_expr:public expr {
|
||||
private:
|
||||
std::vector<hash_pair*> members;
|
||||
|
||||
public:
|
||||
hash_expr(const span& location):
|
||||
expr(location, expr_type::ast_hash) {}
|
||||
~hash_expr() override;
|
||||
void add_member(hash_pair* node) {members.push_back(node);}
|
||||
std::vector<hash_pair*>& get_members() {return members;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class hash_pair:public expr {
|
||||
private:
|
||||
std::string name;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
hash_pair(const span& location):
|
||||
expr(location, expr_type::ast_pair),
|
||||
value(nullptr) {}
|
||||
~hash_pair() override;
|
||||
void set_name(const std::string& field_name) {name = field_name;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
const std::string& get_name() const {return name;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class function:public expr {
|
||||
private:
|
||||
std::vector<parameter*> parameter_list;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
function(const span& location):
|
||||
expr(location, expr_type::ast_func),
|
||||
block(nullptr) {}
|
||||
~function() override;
|
||||
void add_parameter(parameter* node) {parameter_list.push_back(node);}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
std::vector<parameter*>& get_parameter_list() {return parameter_list;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class code_block:public expr {
|
||||
private:
|
||||
std::vector<expr*> expressions;
|
||||
|
||||
public:
|
||||
code_block(const span& location):
|
||||
expr(location, expr_type::ast_block) {}
|
||||
~code_block() override;
|
||||
void add_expression(expr* node) {expressions.push_back(node);}
|
||||
std::vector<expr*>& get_expressions() {return expressions;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class parameter:public expr {
|
||||
public:
|
||||
enum class param_type {
|
||||
normal_parameter,
|
||||
default_parameter,
|
||||
dynamic_parameter
|
||||
};
|
||||
|
||||
private:
|
||||
param_type type;
|
||||
std::string name;
|
||||
expr* default_value;
|
||||
|
||||
public:
|
||||
parameter(const span& location):
|
||||
expr(location, expr_type::ast_param),
|
||||
name(""), default_value(nullptr) {}
|
||||
~parameter() override;
|
||||
void set_parameter_type(param_type pt) {type = pt;}
|
||||
void set_parameter_name(const std::string& pname) {name = pname;}
|
||||
void set_default_value(expr* node) {default_value = node;}
|
||||
param_type get_parameter_type() {return type;}
|
||||
const std::string& get_parameter_name() const {return name;}
|
||||
expr* get_default_value() {return default_value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class ternary_operator:public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
expr* left;
|
||||
expr* right;
|
||||
|
||||
public:
|
||||
ternary_operator(const span& location):
|
||||
expr(location, expr_type::ast_ternary),
|
||||
condition(nullptr), left(nullptr), right(nullptr) {}
|
||||
~ternary_operator() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class binary_operator:public expr {
|
||||
public:
|
||||
enum class binary_type {
|
||||
add,
|
||||
sub,
|
||||
mult,
|
||||
div,
|
||||
concat,
|
||||
cmpeq,
|
||||
cmpneq,
|
||||
less,
|
||||
leq,
|
||||
grt,
|
||||
geq,
|
||||
bitwise_or,
|
||||
bitwise_xor,
|
||||
bitwise_and,
|
||||
condition_and,
|
||||
condition_or
|
||||
};
|
||||
|
||||
private:
|
||||
binary_type type;
|
||||
expr* left;
|
||||
expr* right;
|
||||
number_literal* optimized_const_number;
|
||||
string_literal* optimized_const_string;
|
||||
|
||||
public:
|
||||
binary_operator(const span& location):
|
||||
expr(location, expr_type::ast_binary),
|
||||
left(nullptr), right(nullptr),
|
||||
optimized_const_number(nullptr),
|
||||
optimized_const_string(nullptr) {}
|
||||
~binary_operator() override;
|
||||
void set_operator_type(binary_type operator_type) {type = operator_type;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
|
||||
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
|
||||
binary_type get_operator_type() const {return type;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
number_literal* get_optimized_number() {return optimized_const_number;}
|
||||
string_literal* get_optimized_string() {return optimized_const_string;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class unary_operator:public expr {
|
||||
public:
|
||||
enum class unary_type {
|
||||
negative,
|
||||
logical_not,
|
||||
bitwise_not
|
||||
};
|
||||
|
||||
private:
|
||||
unary_type type;
|
||||
expr* value;
|
||||
number_literal* optimized_number;
|
||||
|
||||
public:
|
||||
unary_operator(const span& location):
|
||||
expr(location, expr_type::ast_unary),
|
||||
value(nullptr), optimized_number(nullptr) {}
|
||||
~unary_operator() override;
|
||||
void set_operator_type(unary_type operator_type) {type = operator_type;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
void set_optimized_number(number_literal* node) {optimized_number = node;}
|
||||
unary_type get_operator_type() const {return type;}
|
||||
expr* get_value() {return value;}
|
||||
number_literal* get_optimized_number() {return optimized_number;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_expr:public expr {
|
||||
private:
|
||||
expr* first;
|
||||
std::vector<call*> calls;
|
||||
|
||||
public:
|
||||
call_expr(const span& location):
|
||||
expr(location, expr_type::ast_call),
|
||||
first(nullptr) {}
|
||||
~call_expr() override;
|
||||
void set_first(expr* node) {first = node;}
|
||||
void add_call(call* node) {calls.push_back(node);}
|
||||
expr* get_first() {return first;}
|
||||
auto& get_calls() {return calls;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_hash:public call {
|
||||
private:
|
||||
std::string field;
|
||||
|
||||
public:
|
||||
call_hash(const span& location, const std::string& name):
|
||||
call(location, expr_type::ast_callh),
|
||||
field(name) {}
|
||||
~call_hash() override = default;
|
||||
const std::string& get_field() const {return field;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_vector:public call {
|
||||
private:
|
||||
std::vector<slice_vector*> calls;
|
||||
|
||||
public:
|
||||
call_vector(const span& location):
|
||||
call(location, expr_type::ast_callv) {}
|
||||
~call_vector() override;
|
||||
void add_slice(slice_vector* node) {calls.push_back(node);}
|
||||
std::vector<slice_vector*>& get_slices() {return calls;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_function:public call {
|
||||
private:
|
||||
std::vector<expr*> args;
|
||||
|
||||
public:
|
||||
call_function(const span& location):
|
||||
call(location, expr_type::ast_callf) {}
|
||||
~call_function() override;
|
||||
void add_argument(expr* node) {args.push_back(node);}
|
||||
std::vector<expr*>& get_argument() {return args;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class slice_vector:public expr {
|
||||
private:
|
||||
expr* begin;
|
||||
expr* end;
|
||||
|
||||
public:
|
||||
slice_vector(const span& location):
|
||||
expr(location, expr_type::ast_subvec),
|
||||
begin(nullptr), end(nullptr) {}
|
||||
~slice_vector() override;
|
||||
void set_begin(expr* node) {begin = node;}
|
||||
void set_end(expr* node) {end = node;}
|
||||
expr* get_begin() {return begin;}
|
||||
expr* get_end() {return end;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class definition_expr:public expr {
|
||||
private:
|
||||
identifier* variable_name;
|
||||
multi_identifier* variables;
|
||||
tuple_expr* tuple;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
definition_expr(const span& location):
|
||||
expr(location, expr_type::ast_def),
|
||||
variable_name(nullptr), variables(nullptr),
|
||||
tuple(nullptr), value(nullptr) {}
|
||||
~definition_expr() override;
|
||||
void set_identifier(identifier* node) {variable_name = node;}
|
||||
void set_multi_define(multi_identifier* node) {variables = node;}
|
||||
void set_tuple(tuple_expr* node) {tuple = node;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
identifier* get_variable_name() {return variable_name;}
|
||||
multi_identifier* get_variables() {return variables;}
|
||||
tuple_expr* get_tuple() {return tuple;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class assignment_expr:public expr {
|
||||
public:
|
||||
enum class assign_type {
|
||||
equal,
|
||||
add_equal,
|
||||
sub_equal,
|
||||
mult_equal,
|
||||
div_equal,
|
||||
concat_equal,
|
||||
bitwise_and_equal,
|
||||
bitwise_or_equal,
|
||||
bitwise_xor_equal
|
||||
};
|
||||
|
||||
private:
|
||||
assign_type type;
|
||||
expr* left;
|
||||
expr* right;
|
||||
|
||||
public:
|
||||
assignment_expr(const span& location):
|
||||
expr(location, expr_type::ast_assign),
|
||||
left(nullptr), right(nullptr) {}
|
||||
~assignment_expr() override;
|
||||
void set_assignment_type(assign_type operator_type) {type = operator_type;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
assign_type get_assignment_type() const {return type;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class multi_identifier:public expr {
|
||||
private:
|
||||
std::vector<identifier*> variables;
|
||||
|
||||
public:
|
||||
multi_identifier(const span& location):
|
||||
expr(location, expr_type::ast_multi_id) {}
|
||||
~multi_identifier() override;
|
||||
void add_var(identifier* node) {variables.push_back(node);}
|
||||
std::vector<identifier*>& get_variables() {return variables;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class tuple_expr:public expr {
|
||||
private:
|
||||
std::vector<expr*> elements;
|
||||
|
||||
public:
|
||||
tuple_expr(const span& location):
|
||||
expr(location, expr_type::ast_tuple) {}
|
||||
~tuple_expr() override;
|
||||
void add_element(expr* node) {elements.push_back(node);}
|
||||
std::vector<expr*>& get_elements() {return elements;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class multi_assign:public expr {
|
||||
private:
|
||||
tuple_expr* tuple;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
multi_assign(const span& location):
|
||||
expr(location, expr_type::ast_multi_assign),
|
||||
tuple(nullptr), value(nullptr) {}
|
||||
~multi_assign() override;
|
||||
void set_tuple(tuple_expr* node) {tuple = node;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
tuple_expr* get_tuple() {return tuple;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class while_expr:public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
while_expr(const span& location):
|
||||
expr(location, expr_type::ast_while),
|
||||
condition(nullptr), block(nullptr) {}
|
||||
~while_expr() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_code_block (code_block* node) {block = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class for_expr:public expr {
|
||||
private:
|
||||
expr* initializing;
|
||||
expr* condition;
|
||||
expr* step;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
for_expr(const span& location):
|
||||
expr(location, expr_type::ast_for),
|
||||
initializing(nullptr), condition(nullptr),
|
||||
step(nullptr), block(nullptr) {}
|
||||
~for_expr() override;
|
||||
void set_initial(expr* node) {initializing = node;}
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_step(expr* node) {step = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
expr* get_initial() {return initializing;}
|
||||
expr* get_condition() {return condition;}
|
||||
expr* get_step() {return step;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class iter_expr:public expr {
|
||||
private:
|
||||
identifier* name;
|
||||
call_expr* call;
|
||||
|
||||
public:
|
||||
iter_expr(const span& location):
|
||||
expr(location, expr_type::ast_iter),
|
||||
name(nullptr), call(nullptr) {}
|
||||
~iter_expr() override;
|
||||
void set_name(identifier* node) {name = node;}
|
||||
void set_call(call_expr* node) {call = node;}
|
||||
identifier* get_name() {return name;}
|
||||
call_expr* get_call() {return call;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class forei_expr:public expr {
|
||||
public:
|
||||
enum class forei_loop_type {
|
||||
foreach,
|
||||
forindex
|
||||
};
|
||||
|
||||
private:
|
||||
forei_loop_type type;
|
||||
iter_expr* iterator;
|
||||
expr* vector_node;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
forei_expr(const span& location):
|
||||
expr(location, expr_type::ast_forei),
|
||||
type(forei_loop_type::foreach), iterator(nullptr),
|
||||
vector_node(nullptr), block(nullptr) {}
|
||||
~forei_expr() override;
|
||||
void set_loop_type(forei_loop_type ft) {type = ft;}
|
||||
void set_iterator(iter_expr* node) {iterator = node;}
|
||||
void set_value(expr* node) {vector_node = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
forei_loop_type get_loop_type() const {return type;}
|
||||
iter_expr* get_iterator() {return iterator;}
|
||||
expr* get_value() {return vector_node;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class condition_expr:public expr {
|
||||
private:
|
||||
if_expr* if_stmt;
|
||||
std::vector<if_expr*> elsif_stmt;
|
||||
if_expr* else_stmt;
|
||||
|
||||
public:
|
||||
condition_expr(const span& location):
|
||||
expr(location, expr_type::ast_cond),
|
||||
if_stmt(nullptr), else_stmt(nullptr) {}
|
||||
~condition_expr() override;
|
||||
void set_if_statement(if_expr* node) {if_stmt = node;}
|
||||
void add_elsif_statement(if_expr* node) {elsif_stmt.push_back(node);}
|
||||
void set_else_statement(if_expr* node) {else_stmt = node;}
|
||||
if_expr* get_if_statement() {return if_stmt;}
|
||||
std::vector<if_expr*>& get_elsif_stataments() {return elsif_stmt;}
|
||||
if_expr* get_else_statement() {return else_stmt;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class if_expr:public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
if_expr(const span& location):
|
||||
expr(location, expr_type::ast_if),
|
||||
condition(nullptr), block(nullptr) {}
|
||||
~if_expr() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class continue_expr:public expr {
|
||||
public:
|
||||
continue_expr(const span& location):
|
||||
expr(location, expr_type::ast_continue) {}
|
||||
~continue_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class break_expr:public expr {
|
||||
public:
|
||||
break_expr(const span& location):
|
||||
expr(location, expr_type::ast_break) {}
|
||||
~break_expr() = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class return_expr:public expr {
|
||||
private:
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
return_expr(const span& location):
|
||||
expr(location, expr_type::ast_ret),
|
||||
value(nullptr) {}
|
||||
~return_expr() override;
|
||||
void set_value(expr* node) {value = node;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
}
|
||||
669
src/nasal_builtin.cpp
Normal file
@@ -0,0 +1,669 @@
|
||||
#include "nasal_builtin.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_print(var* local, gc& ngc) {
|
||||
for(auto& i : local[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_println(var* local, gc& ngc) {
|
||||
for(auto& i : local[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_exit(var* local, gc& ngc) {
|
||||
std::exit(local[1].num());
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_abort(var* local, gc& ngc) {
|
||||
std::abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_append(var* local, gc& ngc) {
|
||||
var vec = local[1];
|
||||
var elem = local[2];
|
||||
if (vec.type!=vm_vec) {
|
||||
return nas_err("append", "\"vec\" must be vector");
|
||||
}
|
||||
auto& v = vec.vec().elems;
|
||||
for(auto& i : elem.vec().elems) {
|
||||
v.push_back(i);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_setsize(var* local, gc& ngc) {
|
||||
var vec = local[1];
|
||||
var size = local[2];
|
||||
if (vec.type!=vm_vec) {
|
||||
return nas_err("setsize", "\"vec\" must be vector");
|
||||
}
|
||||
if (size.type!=vm_num || size.num()<0) {
|
||||
return nil;
|
||||
}
|
||||
vec.vec().elems.resize(static_cast<i64>(size.num()), nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_system(var* local, gc& ngc) {
|
||||
var str = local[1];
|
||||
if (str.type!=vm_str) {
|
||||
return var::num(-1);
|
||||
}
|
||||
return var::num(static_cast<f64>(system(str.str().c_str())));
|
||||
}
|
||||
|
||||
var builtin_input(var* local, gc& ngc) {
|
||||
var end = local[1];
|
||||
var ret = ngc.alloc(vm_str);
|
||||
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
|
||||
std::cin >> ret.str();
|
||||
} else {
|
||||
std::getline(std::cin, ret.str(), end.str()[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_split(var* local, gc& ngc) {
|
||||
var delimeter = local[1];
|
||||
var str = local[2];
|
||||
if (delimeter.type!=vm_str) {
|
||||
return nas_err("split", "\"separator\" must be string");
|
||||
}
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("split", "\"str\" must be string");
|
||||
}
|
||||
const auto& deli = delimeter.str();
|
||||
const auto& s = str.str();
|
||||
|
||||
// avoid being sweeped
|
||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
|
||||
if (!deli.length()) {
|
||||
for(auto i : s) {
|
||||
vec.push_back(ngc.newstr(i));
|
||||
}
|
||||
ngc.temp = nil;
|
||||
return res;
|
||||
}
|
||||
usize last = 0;
|
||||
usize pos = s.find(deli, 0);
|
||||
while(pos!=std::string::npos) {
|
||||
if (pos>last) {
|
||||
vec.push_back(ngc.newstr(s.substr(last, pos-last)));
|
||||
}
|
||||
last = pos+deli.length();
|
||||
pos = s.find(deli, last);
|
||||
}
|
||||
if (last!=s.length()) {
|
||||
vec.push_back(ngc.newstr(s.substr(last)));
|
||||
}
|
||||
ngc.temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_rand(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_num && val.type!=vm_nil) {
|
||||
return nas_err("rand", "\"seed\" must be nil or number");
|
||||
}
|
||||
if (val.type==vm_num) {
|
||||
srand(static_cast<u32>(val.num()));
|
||||
return nil;
|
||||
}
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
return var::num(num);
|
||||
}
|
||||
|
||||
var builtin_id(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
std::stringstream ss;
|
||||
ss << "0";
|
||||
if (val.type>vm_num) {
|
||||
ss << "x" << std::hex;
|
||||
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
|
||||
}
|
||||
return ngc.newstr(ss.str());
|
||||
}
|
||||
|
||||
var builtin_int(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_num && val.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
return var::num(static_cast<f64>(static_cast<i32>(val.tonum())));
|
||||
}
|
||||
|
||||
var builtin_floor(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
return var::num(std::floor(val.num()));
|
||||
}
|
||||
|
||||
var builtin_num(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type==vm_num) {
|
||||
return val;
|
||||
}
|
||||
if (val.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
f64 res = val.tonum();
|
||||
if (std::isnan(res)) {
|
||||
return nil;
|
||||
}
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
var builtin_pop(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_vec) {
|
||||
return nas_err("pop", "\"vec\" must be vector");
|
||||
}
|
||||
auto& vec = val.vec().elems;
|
||||
if (vec.size()) {
|
||||
var tmp = vec.back();
|
||||
vec.pop_back();
|
||||
return tmp;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_str(var* local, gc& ngc) {
|
||||
return ngc.newstr(local[1].tostr());
|
||||
}
|
||||
|
||||
var builtin_size(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
f64 num = 0;
|
||||
switch(val.type) {
|
||||
case vm_num: num = val.num(); break;
|
||||
case vm_str: num = val.str().length(); break;
|
||||
case vm_vec: num = val.vec().size(); break;
|
||||
case vm_hash: num = val.hash().size(); break;
|
||||
case vm_map: num = val.map().mapper.size(); break;
|
||||
}
|
||||
return var::num(num);
|
||||
}
|
||||
|
||||
var builtin_time(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_num) {
|
||||
return nas_err("time", "\"begin\" must be number");
|
||||
}
|
||||
time_t begin = (time_t)val.num();
|
||||
return var::num(static_cast<f64>(time(&begin)));
|
||||
}
|
||||
|
||||
var builtin_contains(var* local, gc& ngc) {
|
||||
var hash = local[1];
|
||||
var key = local[2];
|
||||
if (hash.type!=vm_hash || key.type!=vm_str) {
|
||||
return zero;
|
||||
}
|
||||
return hash.hash().elems.count(key.str())? one:zero;
|
||||
}
|
||||
|
||||
var builtin_delete(var* local, gc& ngc) {
|
||||
var hash = local[1];
|
||||
var key = local[2];
|
||||
if (hash.type!=vm_hash) {
|
||||
return nas_err("delete", "\"hash\" must be hash");
|
||||
}
|
||||
if (key.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
if (hash.hash().elems.count(key.str())) {
|
||||
hash.hash().elems.erase(key.str());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_keys(var* local, gc& ngc) {
|
||||
var hash = local[1];
|
||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||
return nas_err("keys", "\"hash\" must be hash");
|
||||
}
|
||||
// avoid being sweeped
|
||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
if (hash.type==vm_hash) {
|
||||
for(const auto& iter : hash.hash().elems) {
|
||||
vec.push_back(ngc.newstr(iter.first));
|
||||
}
|
||||
} else {
|
||||
for(const auto& iter : hash.map().mapper) {
|
||||
vec.push_back(ngc.newstr(iter.first));
|
||||
}
|
||||
}
|
||||
ngc.temp=nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_die(var* local, gc& ngc) {
|
||||
return nas_err("error", local[1].tostr());
|
||||
}
|
||||
|
||||
var builtin_find(var* local, gc& ngc) {
|
||||
var needle = local[1];
|
||||
var haystack = local[2];
|
||||
usize pos = haystack.tostr().find(needle.tostr());
|
||||
if (pos==std::string::npos) {
|
||||
return var::num(-1.0);
|
||||
}
|
||||
return var::num(static_cast<f64>(pos));
|
||||
}
|
||||
|
||||
var builtin_type(var* local, gc& ngc) {
|
||||
switch(local[1].type) {
|
||||
case vm_none: return ngc.newstr("undefined");
|
||||
case vm_nil: return ngc.newstr("nil");
|
||||
case vm_num: return ngc.newstr("num");
|
||||
case vm_str: return ngc.newstr("str");
|
||||
case vm_vec: return ngc.newstr("vec");
|
||||
case vm_hash: return ngc.newstr("hash");
|
||||
case vm_func: return ngc.newstr("func");
|
||||
case vm_obj: return ngc.newstr("obj");
|
||||
case vm_co: return ngc.newstr("coroutine");
|
||||
case vm_map: return ngc.newstr("namespace");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_substr(var* local, gc& ngc) {
|
||||
var str = local[1];
|
||||
var beg = local[2];
|
||||
var len = local[3];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("substr", "\"str\" must be string");
|
||||
}
|
||||
if (beg.type!=vm_num || beg.num()<0) {
|
||||
return nas_err("substr", "\"begin\" should be number >= 0");
|
||||
}
|
||||
if (len.type!=vm_num || len.num()<0) {
|
||||
return nas_err("substr", "\"length\" should be number >= 0");
|
||||
}
|
||||
usize begin = (usize)beg.num();
|
||||
usize length = (usize)len.num();
|
||||
if (begin>=str.str().length()) {
|
||||
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
|
||||
}
|
||||
return ngc.newstr(str.str().substr(begin,length));
|
||||
}
|
||||
|
||||
var builtin_streq(var* local, gc& ngc) {
|
||||
var a = local[1];
|
||||
var b = local[2];
|
||||
return var::num(static_cast<f64>(
|
||||
(a.type!=vm_str || b.type!=vm_str)? 0:(a.str()==b.str())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_left(var* local, gc& ngc) {
|
||||
var str = local[1];
|
||||
var len = local[2];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("left", "\"string\" must be string");
|
||||
}
|
||||
if (len.type!=vm_num) {
|
||||
return nas_err("left", "\"length\" must be number");
|
||||
}
|
||||
if (len.num()<0) {
|
||||
return ngc.newstr("");
|
||||
}
|
||||
return ngc.newstr(str.str().substr(0, len.num()));
|
||||
}
|
||||
|
||||
var builtin_right(var* local, gc& ngc) {
|
||||
var str = local[1];
|
||||
var len = local[2];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("right", "\"string\" must be string");
|
||||
}
|
||||
if (len.type!=vm_num) {
|
||||
return nas_err("right", "\"length\" must be number");
|
||||
}
|
||||
i32 length = static_cast<i32>(len.num());
|
||||
i32 srclen = str.str().length();
|
||||
if (length>srclen) {
|
||||
length = srclen;
|
||||
}
|
||||
if (length<0) {
|
||||
length = 0;
|
||||
}
|
||||
return ngc.newstr(str.str().substr(srclen-length, srclen));
|
||||
}
|
||||
|
||||
var builtin_cmp(var* local, gc& ngc) {
|
||||
var a = local[1];
|
||||
var b = local[2];
|
||||
if (a.type!=vm_str || b.type!=vm_str) {
|
||||
return nas_err("cmp", "\"a\" and \"b\" must be string");
|
||||
}
|
||||
return var::num(static_cast<f64>(strcmp(
|
||||
a.str().c_str(),
|
||||
b.str().c_str()
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_chr(var* local, gc& ngc) {
|
||||
const char* extend[] = {
|
||||
"€"," ","‚","ƒ","„","…","†","‡",
|
||||
"ˆ","‰","Š","‹","Œ"," ","Ž"," ",
|
||||
" ","‘","’","“","”","•","–","—",
|
||||
"˜","™","š","›","œ"," ","ž","Ÿ",
|
||||
" ","¡","¢","£","¤","¥","¦","§",
|
||||
"¨","©","ª","«","¬"," ","®","¯",
|
||||
"°","±","²","³","´","µ","¶","·",
|
||||
"¸","¹","º","»","¼","½","¾","¿",
|
||||
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
|
||||
"È","É","Ê","Ë","Ì","Í","Î","Ï",
|
||||
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
|
||||
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
|
||||
"à","á","â","ã","ä","å","æ","ç",
|
||||
"è","é","ê","ë","ì","í","î","ï",
|
||||
"ð","ñ","ò","ó","ô","õ","ö","÷",
|
||||
"ø","ù","ú","û","ü","ý","þ","ÿ"
|
||||
};
|
||||
i32 num = local[1].num();
|
||||
if (0<=num && num<128) {
|
||||
return ngc.newstr((char)num);
|
||||
} else if (128<=num && num<256) {
|
||||
return ngc.newstr(extend[num-128]);
|
||||
}
|
||||
return ngc.newstr(" ");
|
||||
}
|
||||
|
||||
var builtin_char(var* local, gc& ngc) {
|
||||
return ngc.newstr((unsigned char)local[1].num());
|
||||
}
|
||||
|
||||
var builtin_values(var* local, gc& ngc) {
|
||||
var hash = local[1];
|
||||
if (hash.type!=vm_hash) {
|
||||
return nas_err("values", "\"hash\" must be hash");
|
||||
}
|
||||
var vec = ngc.alloc(vm_vec);
|
||||
auto& v = vec.vec().elems;
|
||||
for(auto& i : hash.hash().elems) {
|
||||
v.push_back(i.second);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
var builtin_sleep(var* local, gc& ngc) {
|
||||
var val = local[1];
|
||||
if (val.type!=vm_num) {
|
||||
return nil;
|
||||
}
|
||||
#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
// mingw-w64 win32 thread model has no std::this_thread
|
||||
// also msvc will use this
|
||||
Sleep(static_cast<i64>(val.num()*1e3));
|
||||
#else
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::microseconds(static_cast<i64>(val.num()*1e6))
|
||||
);
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_platform(var* local, gc& ngc) {
|
||||
if (is_windows()) {
|
||||
return ngc.newstr("windows");
|
||||
} else if (is_linux()) {
|
||||
return ngc.newstr("linux");
|
||||
} else if (is_macos()) {
|
||||
return ngc.newstr("macOS");
|
||||
}
|
||||
return ngc.newstr("unknown");
|
||||
}
|
||||
|
||||
var builtin_arch(var* local, gc& ngc) {
|
||||
if (is_x86()) {
|
||||
return ngc.newstr("x86");
|
||||
} else if (is_x86_64()) {
|
||||
return ngc.newstr("x86-64");
|
||||
} else if (is_amd64()) {
|
||||
return ngc.newstr("amd64");
|
||||
} else if (is_arm()) {
|
||||
return ngc.newstr("arm");
|
||||
} else if (is_aarch64()) {
|
||||
return ngc.newstr("aarch64");
|
||||
} else if (is_ia64()) {
|
||||
return ngc.newstr("ia64");
|
||||
} else if (is_powerpc()) {
|
||||
return ngc.newstr("powerpc");
|
||||
} else if (is_superh()) {
|
||||
return ngc.newstr("superh");
|
||||
}
|
||||
return ngc.newstr("unknown");
|
||||
}
|
||||
|
||||
// md5 related functions
|
||||
std::string tohex(u32 num) {
|
||||
const char str16[] = "0123456789abcdef";
|
||||
std::string str = "";
|
||||
for(u32 i = 0; i<4; i++, num >>= 8) {
|
||||
std::string tmp = "";
|
||||
for(u32 j = 0, b = num&0xff; j<2; j++, b >>= 4) {
|
||||
tmp.insert(0, 1, str16[b&0xf]);
|
||||
}
|
||||
str += tmp;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string md5(const std::string& src) {
|
||||
std::vector<u32> buff;
|
||||
usize num = ((src.length()+8)>>6)+1;
|
||||
usize buffsize = num<<4;
|
||||
buff.resize(buffsize,0);
|
||||
for(usize i = 0; i<src.length(); i++) {
|
||||
buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3);
|
||||
}
|
||||
buff[src.length()>>2] |= 0x80<<(((src.length()%4))<<3);
|
||||
buff[buffsize-2] = (src.length()<<3)&0xffffffff;
|
||||
buff[buffsize-1] = ((src.length()<<3)>>32)&0xffffffff;
|
||||
|
||||
// u32(abs(sin(i+1))*(2pow32))
|
||||
const u32 k[] = {
|
||||
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
|
||||
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
|
||||
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
|
||||
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
|
||||
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
|
||||
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
|
||||
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
|
||||
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
|
||||
};
|
||||
|
||||
// left shift bits
|
||||
const u32 s[] = {
|
||||
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
|
||||
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
|
||||
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
|
||||
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
|
||||
};
|
||||
|
||||
// index
|
||||
const u32 idx[] = {
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i
|
||||
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16;
|
||||
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16;
|
||||
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16;
|
||||
};
|
||||
|
||||
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
|
||||
#define md5f(x,y,z) (((x)&(y))|((~x)&(z)))
|
||||
#define md5g(x,y,z) (((x)&(z))|((y)&(~z)))
|
||||
#define md5h(x,y,z) ((x)^(y)^(z))
|
||||
#define md5i(x,y,z) ((y)^((x)|(~z)))
|
||||
|
||||
u32 atmp = 0x67452301, btmp = 0xefcdab89;
|
||||
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
|
||||
for(u32 i = 0; i<buffsize; i += 16) {
|
||||
u32 f, a = atmp, b = btmp, c = ctmp, d = dtmp;
|
||||
for(u32 j = 0; j<64; j++) {
|
||||
if (j<16) f = md5f(b, c, d);
|
||||
else if (j<32) f = md5g(b, c, d);
|
||||
else if (j<48) f = md5h(b, c, d);
|
||||
else f = md5i(b, c, d);
|
||||
u32 tmp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]);
|
||||
a = tmp;
|
||||
}
|
||||
atmp += a;
|
||||
btmp += b;
|
||||
ctmp += c;
|
||||
dtmp += d;
|
||||
}
|
||||
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
|
||||
}
|
||||
|
||||
var builtin_md5(var* local, gc& ngc) {
|
||||
var str = local[1];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("md5", "\"str\" must be string");
|
||||
}
|
||||
return ngc.newstr(md5(str.str()));
|
||||
}
|
||||
|
||||
var builtin_millisec(var* local, gc& ngc) {
|
||||
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
var builtin_gcextend(var* local, gc& ngc) {
|
||||
var type = local[1];
|
||||
if (type.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
auto& s = type.str();
|
||||
if (s=="str") {
|
||||
ngc.extend(vm_str);
|
||||
} else if (s=="vec") {
|
||||
ngc.extend(vm_vec);
|
||||
} else if (s=="hash") {
|
||||
ngc.extend(vm_hash);
|
||||
} else if (s=="func") {
|
||||
ngc.extend(vm_func);
|
||||
} else if (s=="upval") {
|
||||
ngc.extend(vm_upval);
|
||||
} else if (s=="obj") {
|
||||
ngc.extend(vm_obj);
|
||||
} else if (s=="co") {
|
||||
ngc.extend(vm_co);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_gcinfo(var* local, gc& ngc) {
|
||||
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
var res = ngc.alloc(vm_hash);
|
||||
|
||||
double total = 0;
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
total += ngc.gcnt[i];
|
||||
}
|
||||
// using ms
|
||||
auto& map = res.hash().elems;
|
||||
map["total"] = var::num(ngc.worktime*1.0/den*1000);
|
||||
map["average"] = var::num(ngc.worktime*1.0/den*1000/total);
|
||||
map["max_gc"] = var::num(ngc.max_time*1.0/den*1000);
|
||||
map["max_mark"] = var::num(ngc.max_mark_time*1.0/den*1000);
|
||||
map["max_sweep"] = var::num(ngc.max_sweep_time*1.0/den*1000);
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_logtime(var* local, gc& ngc) {
|
||||
time_t t = time(nullptr);
|
||||
tm* tm_t = localtime(&t);
|
||||
char s[64];
|
||||
snprintf(
|
||||
s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d",
|
||||
tm_t->tm_year+1900,
|
||||
tm_t->tm_mon+1,
|
||||
tm_t->tm_mday,
|
||||
tm_t->tm_hour,
|
||||
tm_t->tm_min,
|
||||
tm_t->tm_sec
|
||||
);
|
||||
return ngc.newstr(s);
|
||||
}
|
||||
|
||||
var builtin_ghosttype(var* local, gc& ngc) {
|
||||
var arg = local[1];
|
||||
if (arg.type!=vm_obj) {
|
||||
return nas_err("ghosttype", "this is not a ghost object.");
|
||||
}
|
||||
const auto& name = arg.obj().get_ghost_name();
|
||||
if (!name.length()) {
|
||||
return var::num(reinterpret_cast<u64>(arg.obj().ptr));
|
||||
}
|
||||
return ngc.newstr(name);
|
||||
}
|
||||
|
||||
nasal_builtin_table builtin[] = {
|
||||
{"__print", builtin_print},
|
||||
{"__println", builtin_println},
|
||||
{"__exit", builtin_exit},
|
||||
{"__abort", builtin_abort},
|
||||
{"__append", builtin_append},
|
||||
{"__setsize", builtin_setsize},
|
||||
{"__system", builtin_system},
|
||||
{"__input", builtin_input},
|
||||
{"__split", builtin_split},
|
||||
{"__rand", builtin_rand},
|
||||
{"__id", builtin_id},
|
||||
{"__int", builtin_int},
|
||||
{"__floor", builtin_floor},
|
||||
{"__num", builtin_num},
|
||||
{"__pop", builtin_pop},
|
||||
{"__str", builtin_str},
|
||||
{"__size", builtin_size},
|
||||
{"__time", builtin_time},
|
||||
{"__contains", builtin_contains},
|
||||
{"__delete", builtin_delete},
|
||||
{"__keys", builtin_keys},
|
||||
{"__die", builtin_die},
|
||||
{"__find", builtin_find},
|
||||
{"__type", builtin_type},
|
||||
{"__substr", builtin_substr},
|
||||
{"__streq", builtin_streq},
|
||||
{"__left", builtin_left},
|
||||
{"__right", builtin_right},
|
||||
{"__cmp", builtin_cmp},
|
||||
{"__chr", builtin_chr},
|
||||
{"__char", builtin_char},
|
||||
{"__values", builtin_values},
|
||||
{"__sleep", builtin_sleep},
|
||||
{"__platform", builtin_platform},
|
||||
{"__arch", builtin_arch},
|
||||
{"__md5", builtin_md5},
|
||||
{"__millisec", builtin_millisec},
|
||||
{"__gcextd", builtin_gcextend},
|
||||
{"__gcinfo", builtin_gcinfo},
|
||||
{"__logtime", builtin_logtime},
|
||||
{"__ghosttype", builtin_ghosttype},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
81
src/nasal_builtin.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4996)
|
||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
|
||||
// for environ
|
||||
#if defined __APPLE__
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_print(var*, gc&);
|
||||
var builtin_println(var*, gc&);
|
||||
var builtin_exit(var*, gc&);
|
||||
var builtin_abort(var*, gc&);
|
||||
var builtin_append(var*, gc&);
|
||||
var builtin_setsize(var*, gc&);
|
||||
var builtin_system(var*, gc&);
|
||||
var builtin_input(var*, gc&);
|
||||
var builtin_split(var*, gc&);
|
||||
var builtin_rand(var*, gc&);
|
||||
var builtin_id(var*, gc&);
|
||||
var builtin_int(var*, gc&);
|
||||
var builtin_floor(var*, gc&);
|
||||
var builtin_num(var*, gc&);
|
||||
var builtin_pop(var*, gc&);
|
||||
var builtin_str(var*, gc&);
|
||||
var builtin_size(var*, gc&);
|
||||
var builtin_time(var*, gc&);
|
||||
var builtin_contains(var*, gc&);
|
||||
var builtin_delete(var*, gc&);
|
||||
var builtin_keys(var*, gc&);
|
||||
var builtin_die(var*, gc&);
|
||||
var builtin_find(var*, gc&);
|
||||
var builtin_type(var*, gc&);
|
||||
var builtin_substr(var*, gc&);
|
||||
var builtin_streq(var*, gc&);
|
||||
var builtin_left(var*, gc&);
|
||||
var builtin_right(var*, gc&);
|
||||
var builtin_cmp(var*, gc&);
|
||||
var builtin_chr(var*, gc&);
|
||||
var builtin_char(var*, gc&);
|
||||
var builtin_values(var*, gc&);
|
||||
var builtin_sleep(var*, gc&);
|
||||
var builtin_platform(var*, gc&);
|
||||
var builtin_arch(var*, gc&);
|
||||
// md5 related functions
|
||||
std::string tohex(u32);
|
||||
std::string md5(const std::string&);
|
||||
var builtin_md5(var*, gc&);
|
||||
var builtin_millisec(var*, gc&);
|
||||
var builtin_gcextend(var*, gc&);
|
||||
var builtin_gcinfo(var*, gc&);
|
||||
var builtin_logtime(var*, gc&);
|
||||
var builtin_ghosttype(var*, gc&);
|
||||
|
||||
// register builtin function's name and it's address here in this table below
|
||||
// this table must end with {nullptr,nullptr}
|
||||
struct nasal_builtin_table {
|
||||
const char* name;
|
||||
var (*func)(var*, gc&);
|
||||
};
|
||||
|
||||
extern nasal_builtin_table builtin[];
|
||||
|
||||
}
|
||||
1340
src/nasal_codegen.cpp
Normal file
148
src/nasal_codegen.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_opcode.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
#include "symbol_finder.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
|
||||
#include "nasal_builtin.h"
|
||||
#include "coroutine.h"
|
||||
#include "bits_lib.h"
|
||||
#include "math_lib.h"
|
||||
#include "fg_props.h"
|
||||
#include "io_lib.h"
|
||||
#include "dylib_lib.h"
|
||||
#include "unix_lib.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class codegen {
|
||||
private:
|
||||
error err;
|
||||
|
||||
// repl output flag, will generate op_repl to output stack top value if true
|
||||
bool need_repl_output;
|
||||
|
||||
// file mapper for file -> index
|
||||
std::unordered_map<std::string, usize> file_map;
|
||||
void init_file_map(const std::vector<std::string>&);
|
||||
|
||||
// used for generate pop in return expression
|
||||
std::vector<u32> in_foreach_loop_level;
|
||||
|
||||
// constant numbers and strings
|
||||
std::unordered_map<f64, u32> const_number_map;
|
||||
std::unordered_map<std::string, u32> const_string_map;
|
||||
std::vector<f64> const_number_table;
|
||||
std::vector<std::string> const_string_table;
|
||||
|
||||
// native functions
|
||||
std::vector<nasal_builtin_table> native_function;
|
||||
std::unordered_map<std::string, usize> native_function_mapper;
|
||||
void load_native_function_table(nasal_builtin_table*);
|
||||
void init_native_function();
|
||||
|
||||
// generated opcodes
|
||||
std::vector<opcode> code;
|
||||
|
||||
// used to store jmp operands index, to fill the jump address back
|
||||
std::list<std::vector<i32>> continue_ptr;
|
||||
std::list<std::vector<i32>> break_ptr;
|
||||
|
||||
// symbol table
|
||||
// global : max STACK_DEPTH-1 values
|
||||
std::unordered_map<std::string, i32> global;
|
||||
std::unordered_map<std::string, std::unordered_set<std::string>> experimental_namespace;
|
||||
|
||||
// local : max 32768 upvalues 65536 values
|
||||
// but in fact local scope also has less than STACK_DEPTH value
|
||||
std::list<std::unordered_map<std::string, i32>> local;
|
||||
|
||||
void check_id_exist(identifier*);
|
||||
|
||||
void die(const std::string& info, const span& loc) {
|
||||
err.err("code", loc, info);
|
||||
}
|
||||
|
||||
void regist_num(const f64);
|
||||
void regist_str(const std::string&);
|
||||
void find_symbol(code_block*);
|
||||
void add_symbol(const std::string&);
|
||||
i32 local_symbol_find(const std::string&);
|
||||
i32 global_symbol_find(const std::string&);
|
||||
i32 upvalue_symbol_find(const std::string&);
|
||||
|
||||
void emit(u8, u32, const span&);
|
||||
|
||||
void num_gen(number_literal*);
|
||||
void str_gen(string_literal*);
|
||||
void bool_gen(bool_literal*);
|
||||
void vec_gen(vector_expr*);
|
||||
void hash_gen(hash_expr*);
|
||||
void func_gen(function*);
|
||||
void call_gen(call_expr*);
|
||||
void call_id(identifier*);
|
||||
void call_hash_gen(call_hash*);
|
||||
void call_vector_gen(call_vector*);
|
||||
void call_func_gen(call_function*);
|
||||
void mcall(expr*);
|
||||
void mcall_id(identifier*);
|
||||
void mcall_vec(call_vector*);
|
||||
void mcall_hash(call_hash*);
|
||||
void multi_def(definition_expr*);
|
||||
void single_def(definition_expr*);
|
||||
void def_gen(definition_expr*);
|
||||
void assignment_expression(assignment_expr*);
|
||||
void gen_assignment_equal_statement(assignment_expr*);
|
||||
void replace_left_assignment_with_load(const span&);
|
||||
void assignment_statement(assignment_expr*);
|
||||
void multi_assign_gen(multi_assign*);
|
||||
void cond_gen(condition_expr*);
|
||||
void loop_gen(expr*);
|
||||
void load_continue_break(i32, i32);
|
||||
void while_gen(while_expr*);
|
||||
void for_gen(for_expr*);
|
||||
void forei_gen(forei_expr*);
|
||||
void statement_generation(expr*);
|
||||
void or_gen(binary_operator*);
|
||||
void and_gen(binary_operator*);
|
||||
void unary_gen(unary_operator*);
|
||||
void binary_gen(binary_operator*);
|
||||
void trino_gen(ternary_operator*);
|
||||
void calc_gen(expr*);
|
||||
void repl_mode_info_output_gen(expr*);
|
||||
void block_gen(code_block*);
|
||||
void ret_gen(return_expr*);
|
||||
|
||||
public:
|
||||
const auto& strs() const {return const_string_table;}
|
||||
const auto& nums() const {return const_number_table;}
|
||||
const auto& natives() const {return native_function;}
|
||||
const auto& codes() const {return code;}
|
||||
const auto& globals() const {return global;}
|
||||
const auto& get_experimental_namespace() const {
|
||||
return experimental_namespace;
|
||||
}
|
||||
|
||||
public:
|
||||
codegen() = default;
|
||||
const error& compile(parse&, linker&, bool);
|
||||
void print(std::ostream&);
|
||||
void symbol_dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
}
|
||||
283
src/nasal_dbg.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void debug_prof_data::init_counter() {
|
||||
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
|
||||
operand_counter[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::load_file_line_counter(
|
||||
const std::vector<std::string>& file_list) {
|
||||
file_name_list = file_list;
|
||||
file_line_counter = {};
|
||||
file_contents = {};
|
||||
flstream fs;
|
||||
for(usize i =0; i<file_list.size(); ++i) {
|
||||
fs.load(file_list[i]);
|
||||
file_contents.push_back(fs.file_content());
|
||||
file_line_counter.push_back({});
|
||||
file_line_counter.back().resize(fs.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::init(const std::vector<std::string>& file_list) {
|
||||
init_counter();
|
||||
load_file_line_counter(file_list);
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_counter() const {
|
||||
typedef std::pair<u32, u64> op_count;
|
||||
std::vector<op_count> opcall;
|
||||
u64 total = 0;
|
||||
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
|
||||
total += operand_counter[i];
|
||||
opcall.push_back({i, operand_counter[i]});
|
||||
}
|
||||
std::sort(opcall.begin(), opcall.end(),
|
||||
[](const op_count& a, const op_count& b) {
|
||||
return a.second>b.second;
|
||||
}
|
||||
);
|
||||
std::clog << "\noperands call info (<1% ignored)\n";
|
||||
for(const auto& i : opcall) {
|
||||
u64 rate = i.second*100/total;
|
||||
if (!rate) {
|
||||
break;
|
||||
}
|
||||
std::clog << " " << opname[i.first] << " : ";
|
||||
std::clog << i.second << " (" << rate << "%)\n";
|
||||
}
|
||||
std::clog << " total : " << total << '\n';
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_code_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& context : file_line_counter) {
|
||||
for(const auto& count : context) {
|
||||
max_call_time = count>max_call_time? count:max_call_time;
|
||||
}
|
||||
}
|
||||
auto pad_length = std::to_string(max_call_time).length();
|
||||
for(usize i = 0; i<file_name_list.size(); ++i) {
|
||||
os << "\ncode profiling data of " << file_name_list[i] << ":\n";
|
||||
const auto& context = file_contents[i];
|
||||
const auto& counter = file_line_counter[i];
|
||||
for(usize j = 0; j<context.size(); ++j) {
|
||||
os << " " << std::right << std::setw(pad_length);
|
||||
os << std::setfill(' ');
|
||||
os << (counter[j]==0? "":std::to_string(counter[j]));
|
||||
os << " " << context[j] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_this_file_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& count : file_line_counter[0]) {
|
||||
max_call_time = count>max_call_time? count:max_call_time;
|
||||
}
|
||||
auto pad_length = std::to_string(max_call_time).length();
|
||||
|
||||
os << "\ncode profiling data of " << file_name_list[0] << ":\n";
|
||||
const auto& context = file_contents[0];
|
||||
const auto& counter = file_line_counter[0];
|
||||
for(usize i = 0; i<context.size(); ++i) {
|
||||
os << " " << std::right << std::setw(pad_length);
|
||||
os << std::setfill(' ');
|
||||
os << (counter[i]==0? "":std::to_string(counter[i]));
|
||||
os << " " << context[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> dbg::parse(const std::string& cmd) {
|
||||
std::vector<std::string> res;
|
||||
usize last = 0, pos = cmd.find(" ", 0);
|
||||
while(pos!=std::string::npos) {
|
||||
if (pos>last) {
|
||||
res.push_back(cmd.substr(last, pos-last));
|
||||
}
|
||||
last = pos+1;
|
||||
pos = cmd.find(" ", last);
|
||||
}
|
||||
if (last<cmd.length()) {
|
||||
res.push_back(cmd.substr(last));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
u16 dbg::file_index(const std::string& filename) const {
|
||||
for(u16 i = 0; i<fsize; ++i) {
|
||||
if (filename==files[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 65535;
|
||||
}
|
||||
|
||||
void dbg::err() {
|
||||
std::cerr
|
||||
<< "incorrect command\n"
|
||||
<< "input \'h\' to get help\n";
|
||||
}
|
||||
|
||||
void dbg::help() {
|
||||
std::clog
|
||||
<< "<option>\n"
|
||||
<< " h, help | get help\n"
|
||||
<< " bt, backtrace | get function call trace\n"
|
||||
<< " c, continue | run program until break point or exit\n"
|
||||
<< " f, file | see all the compiled files\n"
|
||||
<< " g, global | see global values\n"
|
||||
<< " l, local | see local values\n"
|
||||
<< " u, upval | see upvalue\n"
|
||||
<< " r, register | show vm register detail\n"
|
||||
<< " a, all | show global,local and upvalue\n"
|
||||
<< " n, next | execute next bytecode\n"
|
||||
<< " q, exit | exit debugger\n"
|
||||
<< "<option> <filename> <line>\n"
|
||||
<< " bk, break | set break point\n";
|
||||
}
|
||||
|
||||
void dbg::list_file() const {
|
||||
for(usize i = 0; i<fsize; ++i) {
|
||||
std::clog << "[" << i << "] " << files[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void dbg::step_info() {
|
||||
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
|
||||
u32 begin = (line>>3)==0? 0:((line>>3)<<3);
|
||||
u32 end = (1+(line>>3))<<3;
|
||||
src.load(files[bytecode[ctx.pc].fidx]);
|
||||
std::clog << "\nsource code:\n";
|
||||
for(u32 i = begin; i<end && i<src.size(); ++i) {
|
||||
std::clog << (i==line? back_white:reset);
|
||||
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
|
||||
}
|
||||
|
||||
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
|
||||
end = (1+(ctx.pc>>3))<<3;
|
||||
codestream::set(cnum, cstr, native.data(), files);
|
||||
std::clog << "next bytecode:\n";
|
||||
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
||||
std::clog
|
||||
<< (i==ctx.pc? back_white:reset)
|
||||
<< (i==ctx.pc? "--> ":" ")
|
||||
<< codestream(bytecode[i], i)
|
||||
<< reset << "\n";
|
||||
}
|
||||
stackinfo(10);
|
||||
}
|
||||
|
||||
void dbg::interact() {
|
||||
// special operand, end execution
|
||||
if (bytecode[ctx.pc].op==op_exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not need interact while doing profiling
|
||||
if (do_profiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
|
||||
bytecode[ctx.pc].line!=bk_line) && // break point
|
||||
!next) {// next step
|
||||
return;
|
||||
}
|
||||
|
||||
next = false;
|
||||
std::string cmd;
|
||||
step_info();
|
||||
while(true) {
|
||||
std::clog << ">> ";
|
||||
std::getline(std::cin, cmd);
|
||||
auto res=parse(cmd);
|
||||
if (res.size()==0) {
|
||||
step_info();
|
||||
} else if (res.size()==1) {
|
||||
switch(get_cmd_type(res[0])) {
|
||||
case dbg_cmd::cmd_help: help(); break;
|
||||
case dbg_cmd::cmd_backtrace: traceback(); break;
|
||||
case dbg_cmd::cmd_continue: return;
|
||||
case dbg_cmd::cmd_list_file: list_file(); break;
|
||||
case dbg_cmd::cmd_global: gstate(); break;
|
||||
case dbg_cmd::cmd_local: lstate(); break;
|
||||
case dbg_cmd::cmd_upval: ustate(); break;
|
||||
case dbg_cmd::cmd_register: reginfo(); break;
|
||||
case dbg_cmd::cmd_show_all: detail(); break;
|
||||
case dbg_cmd::cmd_next: next = true; return;
|
||||
case dbg_cmd::cmd_exit: std::exit(0);
|
||||
default: err(); break;
|
||||
}
|
||||
} else if (res.size()==3 &&
|
||||
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
|
||||
bk_fidx = file_index(res[1]);
|
||||
if (bk_fidx==65535) {
|
||||
std::clog << "cannot find file named `" << res[1] << "`\n";
|
||||
continue;
|
||||
}
|
||||
i32 tmp = atoi(res[2].c_str());
|
||||
if (tmp<=0) {
|
||||
std::clog << "incorrect line number `" << res[2] << "`\n";
|
||||
} else {
|
||||
bk_line = tmp;
|
||||
}
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbg::run(
|
||||
const codegen& gen,
|
||||
const linker& linker,
|
||||
const std::vector<std::string>& argv,
|
||||
bool profile,
|
||||
bool show_all_prof_result) {
|
||||
|
||||
set_detail_report_info(true);
|
||||
do_profiling = profile || show_all_prof_result;
|
||||
|
||||
const auto& file_list = linker.get_file_list();
|
||||
fsize = file_list.size();
|
||||
init(gen.strs(), gen.nums(), gen.natives(),
|
||||
gen.codes(), gen.globals(),
|
||||
file_list, argv);
|
||||
data.init(file_list);
|
||||
|
||||
std::vector<u32> code;
|
||||
std::vector<u16> code_file_index;
|
||||
std::vector<u32> code_line;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(i.op);
|
||||
code_file_index.push_back(i.fidx);
|
||||
code_line.push_back(i.line);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(operand_function[code[ctx.pc]]) {
|
||||
interact();
|
||||
data.add_operand_counter(code[ctx.pc]);
|
||||
data.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
|
||||
(this->*operand_function[code[ctx.pc]])();
|
||||
if (ctx.top>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
}
|
||||
++ctx.pc;
|
||||
}
|
||||
|
||||
data.dump_counter();
|
||||
if (do_profiling) {
|
||||
show_all_prof_result?
|
||||
data.dump_code_line_counter(std::clog):
|
||||
data.dump_this_file_line_counter(std::clog);
|
||||
}
|
||||
ngc.info();
|
||||
ngc.clear();
|
||||
imm.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
173
src/nasal_dbg.h
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_opcode.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class debug_prof_data {
|
||||
private:
|
||||
static const usize operand_size = op_code_type::op_ret + 1;
|
||||
u64 operand_counter[operand_size];
|
||||
std::vector<std::string> file_name_list;
|
||||
std::vector<std::vector<u64>> file_line_counter;
|
||||
std::vector<std::vector<std::string>> file_contents;
|
||||
|
||||
private:
|
||||
void init_counter();
|
||||
void load_file_line_counter(const std::vector<std::string>&);
|
||||
|
||||
public:
|
||||
void init(const std::vector<std::string>&);
|
||||
void dump_counter() const;
|
||||
void dump_code_line_counter(std::ostream&) const;
|
||||
void dump_this_file_line_counter(std::ostream&) const;
|
||||
void add_operand_counter(usize index) {
|
||||
operand_counter[index] += index<operand_size? 1:0;
|
||||
}
|
||||
void add_code_line_counter(usize fidx, usize line) {
|
||||
auto& vec = file_line_counter[fidx];
|
||||
vec[line==0? line: line-1]++;
|
||||
}
|
||||
};
|
||||
|
||||
class dbg:public vm {
|
||||
private:
|
||||
typedef void (dbg::*nasal_vm_func)();
|
||||
const nasal_vm_func operand_function[op_ret + 1] = {
|
||||
nullptr, &dbg::o_repl,
|
||||
&dbg::o_intl, &dbg::o_loadg,
|
||||
&dbg::o_loadl, &dbg::o_loadu,
|
||||
&dbg::o_pnum, &dbg::o_pnil,
|
||||
&dbg::o_pstr, &dbg::o_newv,
|
||||
&dbg::o_newh, &dbg::o_newf,
|
||||
&dbg::o_happ, &dbg::o_para,
|
||||
&dbg::o_deft, &dbg::o_dyn,
|
||||
&dbg::o_lnot, &dbg::o_usub,
|
||||
&dbg::o_bnot, &dbg::o_btor,
|
||||
&dbg::o_btxor, &dbg::o_btand,
|
||||
&dbg::o_add, &dbg::o_sub,
|
||||
&dbg::o_mul, &dbg::o_div,
|
||||
&dbg::o_lnk, &dbg::o_addc,
|
||||
&dbg::o_subc, &dbg::o_mulc,
|
||||
&dbg::o_divc, &dbg::o_lnkc,
|
||||
&dbg::o_addeq, &dbg::o_subeq,
|
||||
&dbg::o_muleq, &dbg::o_diveq,
|
||||
&dbg::o_lnkeq, &dbg::o_bandeq,
|
||||
&dbg::o_boreq, &dbg::o_bxoreq,
|
||||
&dbg::o_addeqc, &dbg::o_subeqc,
|
||||
&dbg::o_muleqc, &dbg::o_diveqc,
|
||||
&dbg::o_lnkeqc, &dbg::o_addecp,
|
||||
&dbg::o_subecp, &dbg::o_mulecp,
|
||||
&dbg::o_divecp, &dbg::o_lnkecp,
|
||||
&dbg::o_meq, &dbg::o_eq,
|
||||
&dbg::o_neq, &dbg::o_less,
|
||||
&dbg::o_leq, &dbg::o_grt,
|
||||
&dbg::o_geq, &dbg::o_lessc,
|
||||
&dbg::o_leqc, &dbg::o_grtc,
|
||||
&dbg::o_geqc, &dbg::o_pop,
|
||||
&dbg::o_jmp, &dbg::o_jt,
|
||||
&dbg::o_jf, &dbg::o_cnt,
|
||||
&dbg::o_findex, &dbg::o_feach,
|
||||
&dbg::o_callg, &dbg::o_calll,
|
||||
&dbg::o_upval, &dbg::o_callv,
|
||||
&dbg::o_callvi, &dbg::o_callh,
|
||||
&dbg::o_callfv, &dbg::o_callfh,
|
||||
&dbg::o_callb, &dbg::o_slcbeg,
|
||||
&dbg::o_slcend, &dbg::o_slc,
|
||||
&dbg::o_slc2, &dbg::o_mcallg,
|
||||
&dbg::o_mcalll, &dbg::o_mupval,
|
||||
&dbg::o_mcallv, &dbg::o_mcallh,
|
||||
&dbg::o_ret
|
||||
};
|
||||
|
||||
private:
|
||||
enum class dbg_cmd {
|
||||
cmd_error,
|
||||
cmd_help,
|
||||
cmd_backtrace,
|
||||
cmd_continue,
|
||||
cmd_list_file,
|
||||
cmd_global,
|
||||
cmd_local,
|
||||
cmd_upval,
|
||||
cmd_register,
|
||||
cmd_show_all,
|
||||
cmd_next,
|
||||
cmd_break_point,
|
||||
cmd_exit
|
||||
};
|
||||
|
||||
private:
|
||||
const std::unordered_map<std::string, dbg_cmd> command_table = {
|
||||
{"h", dbg_cmd::cmd_help},
|
||||
{"help", dbg_cmd::cmd_help},
|
||||
{"bt", dbg_cmd::cmd_backtrace},
|
||||
{"backtrace", dbg_cmd::cmd_backtrace},
|
||||
{"c", dbg_cmd::cmd_continue},
|
||||
{"continue", dbg_cmd::cmd_continue},
|
||||
{"f", dbg_cmd::cmd_list_file},
|
||||
{"file", dbg_cmd::cmd_list_file},
|
||||
{"g", dbg_cmd::cmd_global},
|
||||
{"global", dbg_cmd::cmd_global},
|
||||
{"l", dbg_cmd::cmd_local},
|
||||
{"local", dbg_cmd::cmd_local},
|
||||
{"u", dbg_cmd::cmd_upval},
|
||||
{"upval", dbg_cmd::cmd_upval},
|
||||
{"r", dbg_cmd::cmd_register},
|
||||
{"register", dbg_cmd::cmd_register},
|
||||
{"a", dbg_cmd::cmd_show_all},
|
||||
{"all", dbg_cmd::cmd_show_all},
|
||||
{"n", dbg_cmd::cmd_next},
|
||||
{"next", dbg_cmd::cmd_next},
|
||||
{"bk", dbg_cmd::cmd_break_point},
|
||||
{"break", dbg_cmd::cmd_break_point},
|
||||
{"q", dbg_cmd::cmd_exit},
|
||||
{"exit", dbg_cmd::cmd_exit}
|
||||
};
|
||||
dbg_cmd get_cmd_type(const std::string& cmd) const {
|
||||
return command_table.count(cmd)?
|
||||
command_table.at(cmd):dbg_cmd::cmd_error;
|
||||
}
|
||||
|
||||
private:
|
||||
bool next;
|
||||
usize fsize;
|
||||
u16 bk_fidx;
|
||||
u32 bk_line;
|
||||
error src;
|
||||
|
||||
private:
|
||||
debug_prof_data data;
|
||||
bool do_profiling;
|
||||
|
||||
private:
|
||||
std::vector<std::string> parse(const std::string&);
|
||||
u16 file_index(const std::string&) const;
|
||||
void err();
|
||||
void help();
|
||||
void list_file() const;
|
||||
void step_info();
|
||||
void interact();
|
||||
|
||||
public:
|
||||
dbg():
|
||||
next(false), fsize(0),
|
||||
bk_fidx(0), bk_line(0),
|
||||
do_profiling(false) {}
|
||||
void run(
|
||||
const codegen&,
|
||||
const linker&,
|
||||
const std::vector<std::string>&,
|
||||
bool,
|
||||
bool
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
199
src/nasal_err.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "nasal_err.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // use SetConsoleTextAttribute
|
||||
struct for_reset {
|
||||
CONSOLE_SCREEN_BUFFER_INFO scr;
|
||||
for_reset() {
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
||||
}
|
||||
static for_reset* singleton() {
|
||||
static for_reset windows_set;
|
||||
return &windows_set;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
std::ostream& back_white(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
||||
#else
|
||||
s << "\033[7m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& red(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
|
||||
#else
|
||||
s << "\033[91;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& cyan(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
|
||||
#else
|
||||
s << "\033[36;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& orange(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
|
||||
#else
|
||||
s << "\033[93;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& white(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
|
||||
#else
|
||||
s << "\033[0m\033[1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& reset(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
for_reset::singleton()->scr.wAttributes);
|
||||
#else
|
||||
s << "\033[0m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
void flstream::load(const std::string& f) {
|
||||
if (file==f) { // don't need to load a loaded file
|
||||
return;
|
||||
}
|
||||
|
||||
// update file name
|
||||
file = f;
|
||||
|
||||
// REPL: load from memory
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
const auto& source = repl::info::instance()->repl_file_source;
|
||||
res = {};
|
||||
size_t pos = 0, last = 0;
|
||||
while ((pos = source.find("\n", last))!=std::string::npos) {
|
||||
res.push_back(source.substr(last, pos - last));
|
||||
last = pos + 1;
|
||||
}
|
||||
if (last<source.length()) {
|
||||
res.push_back(source.substr(last));
|
||||
} else {
|
||||
res.push_back("");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
res.clear();
|
||||
std::ifstream in(f, std::ios::binary);
|
||||
if (in.fail()) {
|
||||
std::cerr << red << "src: " << reset << "cannot open <" << f << ">\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
while(!in.eof()) {
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
res.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
void error::err(const std::string& stage, const std::string& info) {
|
||||
++cnt;
|
||||
std::cerr << red << stage << ": " << white << info << reset << "\n\n";
|
||||
}
|
||||
|
||||
void error::warn(const std::string& stage, const std::string& info) {
|
||||
std::clog << orange << stage << ": " << white << info << reset << "\n\n";
|
||||
}
|
||||
|
||||
void error::err(
|
||||
const std::string& stage, const span& loc, const std::string& info) {
|
||||
// load error occurred file into string lines
|
||||
load(loc.file);
|
||||
|
||||
++cnt;
|
||||
|
||||
std::cerr
|
||||
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
|
||||
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
|
||||
<< reset << "\n";
|
||||
|
||||
const usize maxlen = std::to_string(loc.end_line).length();
|
||||
const std::string iden = identation(maxlen);
|
||||
|
||||
for(u32 line = loc.begin_line; line<=loc.end_line; ++line) {
|
||||
// skip line 0
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loc.begin_line<line && line<loc.end_line) {
|
||||
if (line==loc.begin_line+1) {
|
||||
std::cerr << cyan << iden << " | " << reset << "...\n";
|
||||
std::cerr << cyan << iden << " | " << reset << "\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this line has nothing, skip
|
||||
if (!res[line-1].length() && line!=loc.end_line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// line out of range
|
||||
if (line-1>=res.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& code = res[line-1];
|
||||
std::cerr << cyan << leftpad(line, maxlen) << " | " << reset << code << "\n";
|
||||
// output underline
|
||||
std::cerr << cyan << iden << " | " << reset;
|
||||
if (loc.begin_line==loc.end_line) {
|
||||
for(u32 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u32 i = loc.begin_column; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (line==loc.begin_line) {
|
||||
for(u32 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u32 i = loc.begin_column; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (loc.begin_line<line && line<loc.end_line) {
|
||||
for(u32 i = 0; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
} else {
|
||||
for(u32 i = 0; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
}
|
||||
if (line==loc.end_line) {
|
||||
std::cerr << reset;
|
||||
} else {
|
||||
std::cerr << reset << "\n";
|
||||
}
|
||||
}
|
||||
std::cerr << "\n\n";
|
||||
}
|
||||
|
||||
}
|
||||
71
src/nasal_err.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream> // MSVC need this to use std::getline
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct span {
|
||||
u32 begin_line;
|
||||
u32 begin_column;
|
||||
u32 end_line;
|
||||
u32 end_column;
|
||||
std::string file;
|
||||
};
|
||||
|
||||
std::ostream& back_white(std::ostream&);
|
||||
std::ostream& red(std::ostream&);
|
||||
std::ostream& cyan(std::ostream&);
|
||||
std::ostream& orange(std::ostream&);
|
||||
std::ostream& white(std::ostream&);
|
||||
std::ostream& reset(std::ostream&);
|
||||
|
||||
class flstream {
|
||||
protected:
|
||||
std::string file;
|
||||
std::vector<std::string> res;
|
||||
|
||||
public:
|
||||
flstream():file("") {}
|
||||
void load(const std::string&);
|
||||
const std::string& operator[](usize n) const {return res[n];}
|
||||
const auto& name() const {return file;}
|
||||
const auto& file_content() const {return res;}
|
||||
usize size() const {return res.size();}
|
||||
};
|
||||
|
||||
class error:public flstream {
|
||||
private:
|
||||
u32 cnt; // counter for errors
|
||||
|
||||
std::string identation(usize len) {
|
||||
return std::string(len,' ');
|
||||
}
|
||||
std::string leftpad(u32 num, usize len) {
|
||||
auto tmp = std::to_string(num);
|
||||
while(tmp.length()<len) {
|
||||
tmp=" "+tmp;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public:
|
||||
error():cnt(0) {}
|
||||
void err(const std::string&, const std::string&);
|
||||
void warn(const std::string&, const std::string&);
|
||||
void err(const std::string&, const span&, const std::string&);
|
||||
|
||||
void chkerr() const {
|
||||
if (cnt) {
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
u32 geterr() const {return cnt;}
|
||||
};
|
||||
|
||||
}
|
||||
732
src/nasal_gc.cpp
Normal file
@@ -0,0 +1,732 @@
|
||||
#include "nasal_gc.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_vec::get_val(const i32 n) {
|
||||
i32 size = elems.size();
|
||||
if (n<-size || n>=size) {
|
||||
return var::none();
|
||||
}
|
||||
return elems[n>=0? n:n+size];
|
||||
}
|
||||
|
||||
var* nas_vec::get_mem(const i32 n) {
|
||||
i32 size = elems.size();
|
||||
if (n<-size || n>=size) {
|
||||
return nullptr;
|
||||
}
|
||||
return &elems[n>=0? n:n+size];
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||
if (!vec.elems.size() || vec.printed) {
|
||||
out << (vec.elems.size()? "[..]":"[]");
|
||||
return out;
|
||||
}
|
||||
vec.printed = true;
|
||||
usize iter = 0, size = vec.elems.size();
|
||||
out << "[";
|
||||
for(auto& i:vec.elems) {
|
||||
out << i << ",]"[(++iter)==size];
|
||||
}
|
||||
vec.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
var nas_hash::get_val(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
return var::none();
|
||||
}
|
||||
var ret = var::none();
|
||||
var val = elems.at("parents");
|
||||
if (val.type!=vm_vec) {
|
||||
return ret;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
if (i.type==vm_hash) {
|
||||
ret = i.hash().get_val(key);
|
||||
}
|
||||
if (ret.type!=vm_none) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var* nas_hash::get_mem(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return &elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
return nullptr;
|
||||
}
|
||||
var* addr = nullptr;
|
||||
var val = elems.at("parents");
|
||||
if (val.type!=vm_vec) {
|
||||
return addr;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
if (i.type==vm_hash) {
|
||||
addr = i.hash().get_mem(key);
|
||||
}
|
||||
if (addr) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
||||
if (!hash.elems.size() || hash.printed) {
|
||||
out << (hash.elems.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
hash.printed = true;
|
||||
usize iter = 0, size = hash.elems.size();
|
||||
out << "{";
|
||||
for(auto& i : hash.elems) {
|
||||
out << i.first << ":" << i.second << ",}"[(++iter)==size];
|
||||
}
|
||||
hash.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
void nas_func::clear() {
|
||||
dpara = -1;
|
||||
local.clear();
|
||||
upval.clear();
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
void nas_ghost::set(
|
||||
const std::string& ghost_type_name,
|
||||
destructor destructor_pointer,
|
||||
void* ghost_pointer) {
|
||||
type_name = ghost_type_name;
|
||||
dtor_ptr = destructor_pointer;
|
||||
ptr = ghost_pointer;
|
||||
}
|
||||
|
||||
void nas_ghost::clear() {
|
||||
// do nothing if pointer is null
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do clear pointer if destructor function pointer is null
|
||||
if (!dtor_ptr) {
|
||||
type_name = "";
|
||||
ptr = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// do destruction
|
||||
dtor_ptr(ptr);
|
||||
type_name = "";
|
||||
ptr = nullptr;
|
||||
dtor_ptr = nullptr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
||||
out << "<object " << ghost.get_ghost_name();
|
||||
out << " at 0x" << std::hex;
|
||||
out << reinterpret_cast<u64>(ghost.ptr) << std::dec << ">";
|
||||
return out;
|
||||
}
|
||||
|
||||
void nas_co::clear() {
|
||||
if (!ctx.stack) {
|
||||
return;
|
||||
}
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = var::nil();
|
||||
}
|
||||
|
||||
ctx.pc = 0;
|
||||
ctx.localr = nullptr;
|
||||
ctx.memr = nullptr;
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||
ctx.top = ctx.stack;
|
||||
ctx.funcr = var::nil();
|
||||
ctx.upvalr = var::nil();
|
||||
|
||||
status = status::suspended;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
|
||||
out << "<coroutine at 0x" << std::hex;
|
||||
out << reinterpret_cast<u64>(&co) << std::dec << ">";
|
||||
return out;
|
||||
}
|
||||
|
||||
var nas_map::get_val(const std::string& key) {
|
||||
if (mapper.count(key)) {
|
||||
return *mapper.at(key);
|
||||
}
|
||||
return var::none();
|
||||
}
|
||||
|
||||
var* nas_map::get_mem(const std::string& key) {
|
||||
if (mapper.count(key)) {
|
||||
return mapper.at(key);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
|
||||
if (!mp.mapper.size() || mp.printed) {
|
||||
out << (mp.mapper.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
mp.printed = true;
|
||||
usize iter = 0, size = mp.mapper.size();
|
||||
out << "{";
|
||||
for(auto& i : mp.mapper) {
|
||||
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
|
||||
}
|
||||
mp.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
nas_val::nas_val(u8 val_type) {
|
||||
mark = gc_status::collected;
|
||||
type = val_type;
|
||||
unmut = 0;
|
||||
switch(val_type) {
|
||||
case vm_str: ptr.str = new std::string; break;
|
||||
case vm_vec: ptr.vec = new nas_vec; break;
|
||||
case vm_hash: ptr.hash = new nas_hash; break;
|
||||
case vm_func: ptr.func = new nas_func; break;
|
||||
case vm_upval: ptr.upval = new nas_upval; break;
|
||||
case vm_obj: ptr.obj = new nas_ghost; break;
|
||||
case vm_co: ptr.co = new nas_co; break;
|
||||
case vm_map: ptr.map = new nas_map; break;
|
||||
}
|
||||
}
|
||||
|
||||
nas_val::~nas_val() {
|
||||
switch(type) {
|
||||
case vm_str: delete ptr.str; break;
|
||||
case vm_vec: delete ptr.vec; break;
|
||||
case vm_hash: delete ptr.hash; break;
|
||||
case vm_func: delete ptr.func; break;
|
||||
case vm_upval:delete ptr.upval;break;
|
||||
case vm_obj: delete ptr.obj; break;
|
||||
case vm_co: delete ptr.co; break;
|
||||
case vm_map: delete ptr.map; break;
|
||||
}
|
||||
type=vm_nil;
|
||||
}
|
||||
|
||||
void nas_val::clear() {
|
||||
switch(type) {
|
||||
case vm_str: ptr.str->clear(); break;
|
||||
case vm_vec: ptr.vec->elems.clear(); break;
|
||||
case vm_hash: ptr.hash->elems.clear();break;
|
||||
case vm_func: ptr.func->clear(); break;
|
||||
case vm_upval:ptr.upval->clear(); break;
|
||||
case vm_obj: ptr.obj->clear(); break;
|
||||
case vm_co: ptr.co->clear(); break;
|
||||
case vm_map: ptr.map->clear(); break;
|
||||
}
|
||||
}
|
||||
|
||||
f64 var::tonum() {
|
||||
return type!=vm_str? val.num:str2num(str().c_str());
|
||||
}
|
||||
|
||||
std::string var::tostr() {
|
||||
if (type==vm_str) {
|
||||
return str();
|
||||
} else if (type==vm_num) {
|
||||
std::string tmp=std::to_string(num());
|
||||
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
||||
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
||||
return tmp;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, var& ref) {
|
||||
switch(ref.type) {
|
||||
case vm_none: out << "undefined"; break;
|
||||
case vm_nil: out << "nil"; break;
|
||||
case vm_num: out << ref.val.num; break;
|
||||
case vm_str: out << ref.str(); break;
|
||||
case vm_vec: out << ref.vec(); break;
|
||||
case vm_hash: out << ref.hash(); break;
|
||||
case vm_func: out << "func(..) {..}"; break;
|
||||
case vm_obj: out << ref.obj(); break;
|
||||
case vm_co: out << ref.co(); break;
|
||||
case vm_map: out << ref.map(); break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool var::objchk(const std::string& name) {
|
||||
return type==vm_obj && obj().type_name==name && obj().ptr;
|
||||
}
|
||||
|
||||
var var::none() {
|
||||
return {vm_none, static_cast<u32>(0)};
|
||||
}
|
||||
|
||||
var var::nil() {
|
||||
return {vm_nil, static_cast<u32>(0)};
|
||||
}
|
||||
|
||||
var var::ret(u32 pc) {
|
||||
return {vm_ret, pc};
|
||||
}
|
||||
|
||||
var var::cnt(i64 n) {
|
||||
return {vm_cnt, n};
|
||||
}
|
||||
|
||||
var var::num(f64 n) {
|
||||
return {vm_num, n};
|
||||
}
|
||||
|
||||
var var::gcobj(nas_val* p) {
|
||||
return {p->type, p};
|
||||
}
|
||||
|
||||
var var::addr(var* p) {
|
||||
return {vm_addr, p};
|
||||
}
|
||||
|
||||
var* var::addr() {
|
||||
return val.addr;
|
||||
}
|
||||
|
||||
u32 var::ret() {
|
||||
return val.ret;
|
||||
}
|
||||
|
||||
i64& var::cnt() {
|
||||
return val.cnt;
|
||||
}
|
||||
|
||||
f64 var::num() {
|
||||
return val.num;
|
||||
}
|
||||
|
||||
std::string& var::str() {
|
||||
return *val.gcobj->ptr.str;
|
||||
}
|
||||
|
||||
nas_vec& var::vec() {
|
||||
return *val.gcobj->ptr.vec;
|
||||
}
|
||||
|
||||
nas_hash& var::hash() {
|
||||
return *val.gcobj->ptr.hash;
|
||||
}
|
||||
|
||||
nas_func& var::func() {
|
||||
return *val.gcobj->ptr.func;
|
||||
}
|
||||
|
||||
nas_upval& var::upval() {
|
||||
return *val.gcobj->ptr.upval;
|
||||
}
|
||||
|
||||
nas_ghost& var::obj() {
|
||||
return *val.gcobj->ptr.obj;
|
||||
}
|
||||
|
||||
nas_co& var::co() {
|
||||
return *val.gcobj->ptr.co;
|
||||
}
|
||||
|
||||
nas_map& var::map() {
|
||||
return *val.gcobj->ptr.map;
|
||||
}
|
||||
|
||||
void gc::do_mark_sweep() {
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
auto begin = clk::now();
|
||||
mark();
|
||||
auto mark_end = clk::now();
|
||||
sweep();
|
||||
auto sweep_end = clk::now();
|
||||
|
||||
auto total_time = (sweep_end-begin).count();
|
||||
auto mark_time = (mark_end-begin).count();
|
||||
auto sweep_time = (sweep_end-mark_end).count();
|
||||
worktime += total_time;
|
||||
max_time = max_time<total_time? total_time:max_time;
|
||||
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
|
||||
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
|
||||
}
|
||||
|
||||
void gc::mark() {
|
||||
std::vector<var> bfs;
|
||||
mark_context_root(bfs);
|
||||
if (memory.size()>8192 && bfs.size()>4) {
|
||||
usize size = bfs.size();
|
||||
std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4);
|
||||
std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2);
|
||||
std::thread t2(&gc::concurrent_mark, this, std::ref(bfs), size/2, size/4*3);
|
||||
std::thread t3(&gc::concurrent_mark, this, std::ref(bfs), size/4*3, size);
|
||||
t0.join();
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
return;
|
||||
}
|
||||
|
||||
while(!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
std::vector<var> bfs;
|
||||
for(auto i = begin; i<end; ++i) {
|
||||
var value = vec[i];
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
while(!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_context_root(std::vector<var>& bfs_queue) {
|
||||
// scan global
|
||||
for(usize i = 0; i<main_context_global_size; ++i) {
|
||||
auto& val = main_context_global[i];
|
||||
if (val.type>vm_num) {
|
||||
bfs_queue.push_back(val);
|
||||
}
|
||||
}
|
||||
// scan now running context, this context maybe related to coroutine or main
|
||||
for(var* i = rctx->stack; i<=rctx->top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
bfs_queue.push_back(rctx->funcr);
|
||||
bfs_queue.push_back(rctx->upvalr);
|
||||
bfs_queue.push_back(temp);
|
||||
|
||||
if (!cort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coroutine is running, so scan main process stack from mctx
|
||||
for(var* i = mctx.stack; i<=mctx.top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
bfs_queue.push_back(mctx.funcr);
|
||||
bfs_queue.push_back(mctx.upvalr);
|
||||
}
|
||||
|
||||
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
||||
value.val.gcobj->mark = nas_val::gc_status::found;
|
||||
switch(value.type) {
|
||||
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
|
||||
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
|
||||
case vm_func: mark_func(bfs_queue, value.func()); break;
|
||||
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
|
||||
case vm_co: mark_co(bfs_queue, value.co()); break;
|
||||
case vm_map: mark_map(bfs_queue, value.map()); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
|
||||
for(auto& i : vec.elems) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
|
||||
for(auto& i : hash.elems) {
|
||||
if (i.second.type>vm_num) {
|
||||
bfs_queue.push_back(i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
|
||||
for(auto& i : function.local) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
for(auto& i : function.upval) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
||||
for(auto& i : upval.elems) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
||||
bfs_queue.push_back(co.ctx.funcr);
|
||||
bfs_queue.push_back(co.ctx.upvalr);
|
||||
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_map(std::vector<var>& bfs_queue, nas_map& mp) {
|
||||
for(const auto& i : mp.mapper) {
|
||||
if (i.second->type>vm_num) {
|
||||
bfs_queue.push_back(*i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::sweep() {
|
||||
for(auto i : memory) {
|
||||
if (i->mark==nas_val::gc_status::uncollected) {
|
||||
i->clear();
|
||||
unused[i->type-vm_str].push_back(i);
|
||||
i->mark = nas_val::gc_status::collected;
|
||||
} else if (i->mark==nas_val::gc_status::found) {
|
||||
i->mark = nas_val::gc_status::uncollected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::extend(u8 type) {
|
||||
const u8 index = type-vm_str;
|
||||
size[index] += incr[index];
|
||||
|
||||
for(u32 i = 0; i<incr[index]; ++i) {
|
||||
// no need to check, will be killed if memory is not enough
|
||||
nas_val* tmp = new nas_val(type);
|
||||
|
||||
// add to heap
|
||||
memory.push_back(tmp);
|
||||
unused[index].push_back(tmp);
|
||||
}
|
||||
|
||||
incr[index] = incr[index]+incr[index]/2;
|
||||
}
|
||||
|
||||
void gc::init(
|
||||
const std::vector<std::string>& constant_strings,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
// initialize counters
|
||||
worktime = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
size[i] = gcnt[i] = acnt[i] = 0;
|
||||
}
|
||||
|
||||
// coroutine pointer set to nullptr
|
||||
cort = nullptr;
|
||||
|
||||
// init constant strings
|
||||
strs.resize(constant_strings.size());
|
||||
for(u32 i = 0; i<strs.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (strs[i].type==vm_str && strs[i].str()==constant_strings[i]) {
|
||||
continue;
|
||||
}
|
||||
strs[i] = var::gcobj(new nas_val(vm_str));
|
||||
strs[i].val.gcobj->unmut = 1;
|
||||
strs[i].str() = constant_strings[i];
|
||||
}
|
||||
|
||||
// record arguments
|
||||
env_argv.resize(argv.size());
|
||||
for(usize i = 0; i<argv.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (env_argv[i].type==vm_str && env_argv[i].str()==argv[i]) {
|
||||
continue;
|
||||
}
|
||||
env_argv[i] = var::gcobj(new nas_val(vm_str));
|
||||
env_argv[i].val.gcobj->unmut = 1;
|
||||
env_argv[i].str() = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
void gc::clear() {
|
||||
for(auto i : memory) {
|
||||
delete i;
|
||||
}
|
||||
memory.clear();
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
unused[i].clear();
|
||||
}
|
||||
for(auto& i : strs) {
|
||||
delete i.val.gcobj;
|
||||
}
|
||||
strs.clear();
|
||||
env_argv.clear();
|
||||
}
|
||||
|
||||
void gc::info() const {
|
||||
|
||||
using std::left;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
|
||||
const char* used_table_name[] = {
|
||||
"object type", "gc count", "alloc count", "memory size",
|
||||
"detail", "time spend", "gc time", "avg time", "max gc",
|
||||
"max mark", "max sweep", nullptr
|
||||
};
|
||||
const char* name[] = {
|
||||
"string",
|
||||
"vector",
|
||||
"hashmap",
|
||||
"function",
|
||||
"upvalue",
|
||||
"object",
|
||||
"coroutine",
|
||||
"namespace",
|
||||
nullptr
|
||||
};
|
||||
|
||||
usize indent = 0, len = 0;
|
||||
for(usize i = 0; used_table_name[i]; ++i) {
|
||||
len = std::string(used_table_name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(usize i = 0; name[i]; ++i) {
|
||||
len = std::string(name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
len = std::to_string(gcnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(acnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(size[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
auto indent_string = std::string("--");
|
||||
for(usize i = 0; i<indent; ++i) {
|
||||
indent_string += "-";
|
||||
}
|
||||
auto last_line = indent_string + "+" +
|
||||
indent_string + "-" + indent_string + "-" + indent_string;
|
||||
indent_string = indent_string + "+" +
|
||||
indent_string + "+" + indent_string + "+" + indent_string;
|
||||
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
|
||||
double total = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
if (!gcnt[i] && !acnt[i] && !size[i]) {
|
||||
continue;
|
||||
}
|
||||
total += gcnt[i];
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
|
||||
std::clog << "\n";
|
||||
}
|
||||
std::clog << indent_string << "\n";
|
||||
|
||||
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " | " << worktime*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " | " << worktime*1.0/den*1000/total << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max gc";
|
||||
std::clog << " | " << max_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << last_line << "\n";
|
||||
}
|
||||
|
||||
var gc::alloc(u8 type) {
|
||||
const u8 index = type-vm_str;
|
||||
++acnt[index];
|
||||
if (unused[index].empty()) {
|
||||
++gcnt[index];
|
||||
do_mark_sweep();
|
||||
}
|
||||
if (unused[index].empty()) {
|
||||
extend(type);
|
||||
}
|
||||
var ret = var::gcobj(unused[index].back());
|
||||
ret.val.gcobj->mark = nas_val::gc_status::uncollected;
|
||||
unused[index].pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gc::ctxchg(nas_co& co) {
|
||||
// store running state to main context
|
||||
mctx = *rctx;
|
||||
|
||||
// restore coroutine context state
|
||||
*rctx = co.ctx;
|
||||
|
||||
// set coroutine pointer
|
||||
cort = &co;
|
||||
|
||||
// set coroutine state to running
|
||||
cort->status = nas_co::status::running;
|
||||
}
|
||||
|
||||
void gc::ctxreserve() {
|
||||
// pc=0 means this coroutine is finished
|
||||
cort->status = rctx->pc?
|
||||
nas_co::status::suspended:
|
||||
nas_co::status::dead;
|
||||
|
||||
// store running state to coroutine
|
||||
cort->ctx = *rctx;
|
||||
|
||||
// restore main context state
|
||||
*rctx = mctx;
|
||||
|
||||
// set coroutine pointer to nullptr
|
||||
cort = nullptr;
|
||||
}
|
||||
|
||||
var nas_err(const std::string& error_function_name, const std::string& info) {
|
||||
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
|
||||
return var::none();
|
||||
}
|
||||
|
||||
}
|
||||
394
src/nasal_gc.h
Normal file
@@ -0,0 +1,394 @@
|
||||
#pragma once
|
||||
|
||||
// avoid MSVC warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum vm_type:u8 {
|
||||
/* none-gc object */
|
||||
vm_none = 0,
|
||||
vm_cnt,
|
||||
vm_addr,
|
||||
vm_ret,
|
||||
vm_nil,
|
||||
vm_num,
|
||||
/* gc object */
|
||||
vm_str,
|
||||
vm_vec,
|
||||
vm_hash,
|
||||
vm_func,
|
||||
vm_upval,
|
||||
vm_obj,
|
||||
vm_co,
|
||||
vm_map // for globals and namespaces
|
||||
};
|
||||
|
||||
const u32 gc_type_size = vm_map-vm_str+1;
|
||||
|
||||
struct nas_vec; // vector
|
||||
struct nas_hash; // hashmap(dict)
|
||||
struct nas_func; // function(lambda)
|
||||
struct nas_upval; // upvalue
|
||||
struct nas_ghost; // objects
|
||||
struct nas_co; // coroutine
|
||||
struct nas_map; // mapper
|
||||
struct nas_val; // nas_val includes gc-managed types
|
||||
|
||||
struct var {
|
||||
public:
|
||||
u8 type = vm_none;
|
||||
union {
|
||||
u32 ret;
|
||||
i64 cnt;
|
||||
f64 num;
|
||||
var* addr;
|
||||
nas_val* gcobj;
|
||||
} val;
|
||||
|
||||
private:
|
||||
var(u8 t, u32 pc) {type = t; val.ret = pc;}
|
||||
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
|
||||
var(u8 t, f64 n) {type = t; val.num = n;}
|
||||
var(u8 t, var* p) {type = t; val.addr = p;}
|
||||
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
|
||||
|
||||
public:
|
||||
var() = default;
|
||||
var(const var&) = default;
|
||||
bool operator==(const var& nr) const {
|
||||
return type==nr.type && val.gcobj==nr.val.gcobj;
|
||||
}
|
||||
bool operator!=(const var& nr) const {
|
||||
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
||||
}
|
||||
|
||||
// number and string can be translated to each other
|
||||
f64 tonum();
|
||||
std::string tostr();
|
||||
bool objchk(const std::string&);
|
||||
|
||||
// create new var object
|
||||
static var none();
|
||||
static var nil();
|
||||
static var ret(u32);
|
||||
static var cnt(i64);
|
||||
static var num(f64);
|
||||
static var gcobj(nas_val*);
|
||||
static var addr(var*);
|
||||
|
||||
// get content
|
||||
var* addr();
|
||||
u32 ret();
|
||||
i64& cnt();
|
||||
f64 num();
|
||||
std::string& str();
|
||||
nas_vec& vec();
|
||||
nas_hash& hash();
|
||||
nas_func& func();
|
||||
nas_upval& upval();
|
||||
nas_ghost& obj();
|
||||
nas_co& co();
|
||||
nas_map& map();
|
||||
};
|
||||
|
||||
struct nas_vec {
|
||||
std::vector<var> elems;
|
||||
|
||||
// mark if this is printed, avoid stackoverflow
|
||||
bool printed;
|
||||
|
||||
nas_vec():printed(false) {}
|
||||
usize size() const {return elems.size();}
|
||||
var get_val(const i32);
|
||||
var* get_mem(const i32);
|
||||
};
|
||||
|
||||
struct nas_hash {
|
||||
std::unordered_map<std::string, var> elems;
|
||||
|
||||
// mark if this is printed, avoid stackoverflow
|
||||
bool printed;
|
||||
|
||||
nas_hash(): printed(false) {}
|
||||
usize size() const {return elems.size();}
|
||||
var get_val(const std::string&);
|
||||
var* get_mem(const std::string&);
|
||||
};
|
||||
|
||||
struct nas_func {
|
||||
i32 dpara; // dynamic parameter name index in hash.
|
||||
u32 entry; // pc will set to entry-1 to call this function
|
||||
u32 psize; // used to load default parameters to a new function
|
||||
u32 lsize; // used to expand memory space for local values on stack
|
||||
std::vector<var> local; // local scope with default value(var)
|
||||
std::vector<var> upval; // closure
|
||||
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
|
||||
|
||||
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nas_upval {
|
||||
public:
|
||||
/* on stack, use these variables */
|
||||
bool onstk;
|
||||
u32 size;
|
||||
var* stk;
|
||||
|
||||
/* not on stack, use this */
|
||||
std::vector<var> elems;
|
||||
|
||||
public:
|
||||
nas_upval(): onstk(true), size(0), stk(nullptr) {}
|
||||
|
||||
var& operator[](usize n) {
|
||||
return onstk? stk[n]:elems[n];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
onstk = true;
|
||||
elems.clear();
|
||||
size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct nas_ghost {
|
||||
private:
|
||||
using destructor=void (*)(void*);
|
||||
|
||||
public:
|
||||
std::string type_name;
|
||||
destructor dtor_ptr;
|
||||
void* ptr;
|
||||
|
||||
public:
|
||||
nas_ghost(): type_name(""), dtor_ptr(nullptr), ptr(nullptr) {}
|
||||
~nas_ghost() {clear();}
|
||||
void set(const std::string&, destructor, void*);
|
||||
void clear();
|
||||
|
||||
public:
|
||||
const std::string& get_ghost_name() const {
|
||||
return type_name;
|
||||
}
|
||||
};
|
||||
|
||||
struct context {
|
||||
u32 pc = 0;
|
||||
var* localr = nullptr;
|
||||
var* memr = nullptr;
|
||||
var funcr = var::nil();
|
||||
var upvalr = var::nil();
|
||||
var* canary = nullptr;
|
||||
var* stack = nullptr;
|
||||
var* top = nullptr;
|
||||
};
|
||||
|
||||
struct nas_co {
|
||||
enum class status:u32 {
|
||||
suspended,
|
||||
running,
|
||||
dead
|
||||
};
|
||||
|
||||
context ctx;
|
||||
status status;
|
||||
|
||||
nas_co() {
|
||||
ctx.stack = new var[STACK_DEPTH];
|
||||
clear();
|
||||
}
|
||||
~nas_co() {
|
||||
delete[] ctx.stack;
|
||||
}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nas_map {
|
||||
bool printed = false;
|
||||
std::unordered_map<std::string, var*> mapper;
|
||||
|
||||
nas_map() {}
|
||||
void clear() {
|
||||
mapper.clear();
|
||||
}
|
||||
|
||||
var get_val(const std::string&);
|
||||
var* get_mem(const std::string&);
|
||||
};
|
||||
|
||||
struct nas_val {
|
||||
enum class gc_status:u8 {
|
||||
uncollected = 0,
|
||||
collected,
|
||||
found
|
||||
};
|
||||
|
||||
gc_status mark;
|
||||
u8 type; // value type
|
||||
u8 unmut; // used to mark if a string is unmutable
|
||||
union {
|
||||
std::string* str;
|
||||
nas_vec* vec;
|
||||
nas_hash* hash;
|
||||
nas_func* func;
|
||||
nas_upval* upval;
|
||||
nas_ghost* obj;
|
||||
nas_co* co;
|
||||
nas_map* map;
|
||||
} ptr;
|
||||
|
||||
nas_val(u8);
|
||||
~nas_val();
|
||||
void clear();
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||
std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||
std::ostream& operator<<(std::ostream&, nas_map&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_co&);
|
||||
std::ostream& operator<<(std::ostream&, var&);
|
||||
|
||||
const var zero = var::num(0);
|
||||
const var one = var::num(1);
|
||||
const var nil = var::nil();
|
||||
|
||||
struct gc {
|
||||
/* main context temporary storage */
|
||||
context mctx;
|
||||
|
||||
/* global storage */
|
||||
var* main_context_global = nullptr;
|
||||
usize main_context_global_size = 0;
|
||||
|
||||
/* runtime context */
|
||||
context* rctx = nullptr;
|
||||
nas_co* cort = nullptr; // running coroutine
|
||||
|
||||
/* temporary space used in native/module functions */
|
||||
var temp = nil;
|
||||
|
||||
/* constants and memory pool */
|
||||
std::vector<var> strs = {}; // reserved address for const vm_str
|
||||
std::vector<var> env_argv = {}; // command line arguments
|
||||
std::vector<nas_val*> memory; // gc memory
|
||||
std::vector<nas_val*> unused[gc_type_size]; // gc free list
|
||||
|
||||
/* heap increase size */
|
||||
u32 incr[gc_type_size] = {
|
||||
128, // vm_str
|
||||
128, // vm_vec
|
||||
64, // vm_hash
|
||||
128, // vm_func
|
||||
256, // vm_upval
|
||||
16, // vm_obj
|
||||
16, // vm_co
|
||||
2, // vm_map
|
||||
};
|
||||
|
||||
/* values for analysis */
|
||||
u64 size[gc_type_size];
|
||||
u64 gcnt[gc_type_size];
|
||||
u64 acnt[gc_type_size];
|
||||
i64 worktime = 0;
|
||||
i64 max_time = 0;
|
||||
i64 max_mark_time = 0;
|
||||
i64 max_sweep_time = 0;
|
||||
|
||||
void set(context* _ctx, var* _global, usize _size) {
|
||||
rctx = _ctx;
|
||||
main_context_global = _global;
|
||||
main_context_global_size = _size;
|
||||
}
|
||||
|
||||
private:
|
||||
/* gc functions */
|
||||
void do_mark_sweep();
|
||||
void mark();
|
||||
void concurrent_mark(std::vector<var>&, usize, usize);
|
||||
void mark_context_root(std::vector<var>&);
|
||||
void mark_var(std::vector<var>&, var&);
|
||||
void mark_vec(std::vector<var>&, nas_vec&);
|
||||
void mark_hash(std::vector<var>&, nas_hash&);
|
||||
void mark_func(std::vector<var>&, nas_func&);
|
||||
void mark_upval(std::vector<var>&, nas_upval&);
|
||||
void mark_co(std::vector<var>&, nas_co&);
|
||||
void mark_map(std::vector<var>&, nas_map&);
|
||||
void sweep();
|
||||
|
||||
public:
|
||||
void extend(u8);
|
||||
void init(const std::vector<std::string>&, const std::vector<std::string>&);
|
||||
void clear();
|
||||
void info() const;
|
||||
var alloc(const u8);
|
||||
void ctxchg(nas_co&);
|
||||
void ctxreserve();
|
||||
|
||||
public:
|
||||
var newstr(char c) {
|
||||
var s = alloc(vm_str);
|
||||
s.str() = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const char* buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str() = buff;
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const std::string& buff) {
|
||||
var s=alloc(vm_str);
|
||||
s.str() = buff;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// use to print error log and return error value
|
||||
var nas_err(const std::string&, const std::string&);
|
||||
|
||||
// module function type
|
||||
typedef var (*module_func)(var*, usize, gc*);
|
||||
|
||||
// module function stores in tables with this type, end with {nullptr,nullptr}
|
||||
struct module_func_info {
|
||||
const char* name;
|
||||
module_func fd;
|
||||
};
|
||||
|
||||
// module function "get" type
|
||||
typedef module_func_info* (*get_func_ptr)();
|
||||
|
||||
}
|
||||
364
src/nasal_import.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
#include "nasal_import.h"
|
||||
#include "symbol_finder.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
linker::linker():
|
||||
show_path(false), lib_loaded(false),
|
||||
this_file(""), lib_path("") {
|
||||
char sep = is_windows()? ';':':';
|
||||
std::string PATH = getenv("PATH");
|
||||
usize last = 0, pos = PATH.find(sep, 0);
|
||||
while(pos!=std::string::npos) {
|
||||
std::string dirpath = PATH.substr(last, pos-last);
|
||||
if (dirpath.length()) {
|
||||
envpath.push_back(dirpath);
|
||||
}
|
||||
last = pos+1;
|
||||
pos = PATH.find(sep, last);
|
||||
}
|
||||
if (last!=PATH.length()) {
|
||||
envpath.push_back(PATH.substr(last));
|
||||
}
|
||||
}
|
||||
|
||||
std::string linker::get_path(call_expr* node) {
|
||||
if (node->get_calls()[0]->get_type()==expr_type::ast_callf) {
|
||||
auto tmp = (call_function*)node->get_calls()[0];
|
||||
return ((string_literal*)tmp->get_argument()[0])->get_content();
|
||||
}
|
||||
auto fpath = std::string(".");
|
||||
for(auto i : node->get_calls()) {
|
||||
fpath += (is_windows()? "\\":"/") + ((call_hash*)i)->get_field();
|
||||
}
|
||||
return fpath + ".nas";
|
||||
}
|
||||
|
||||
std::string linker::find_file(
|
||||
const std::string& filename, const span& location) {
|
||||
// first add file name itself into the file path
|
||||
std::vector<std::string> fpath = {filename};
|
||||
|
||||
// generate search path from environ path
|
||||
for(const auto& p : envpath) {
|
||||
fpath.push_back(p + (is_windows()? "\\":"/") + filename);
|
||||
}
|
||||
|
||||
// search file
|
||||
for(const auto& i : fpath) {
|
||||
if (access(i.c_str(), F_OK)!=-1) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// we will find lib.nas in nasal std directory
|
||||
if (filename=="lib.nas") {
|
||||
return is_windows()?
|
||||
find_file("std\\lib.nas", location):
|
||||
find_file("std/lib.nas", location);
|
||||
}
|
||||
if (!show_path) {
|
||||
err.err("link",
|
||||
"in <" + location.file + ">: " +
|
||||
"cannot find file <" + filename + ">, " +
|
||||
"use <-d> to get detail search path");
|
||||
return "";
|
||||
}
|
||||
std::string paths = "";
|
||||
for(const auto& i : fpath) {
|
||||
paths += " -> " + i + "\n";
|
||||
}
|
||||
err.err("link",
|
||||
"in <" + location.file + ">: " +
|
||||
"cannot find file <" + filename + "> in these paths:\n" + paths);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool linker::import_check(expr* node) {
|
||||
/*
|
||||
call
|
||||
|_id:import
|
||||
|_callh:std
|
||||
|_callh:file
|
||||
*/
|
||||
if (node->get_type()!=expr_type::ast_call) {
|
||||
return false;
|
||||
}
|
||||
auto tmp = (call_expr*)node;
|
||||
if (tmp->get_first()->get_type()!=expr_type::ast_id) {
|
||||
return false;
|
||||
}
|
||||
if (((identifier*)tmp->get_first())->get_name()!="import") {
|
||||
return false;
|
||||
}
|
||||
if (!tmp->get_calls().size()) {
|
||||
return false;
|
||||
}
|
||||
// import.xxx.xxx;
|
||||
if (tmp->get_calls()[0]->get_type()==expr_type::ast_callh) {
|
||||
for(auto i : tmp->get_calls()) {
|
||||
if (i->get_type()!=expr_type::ast_callh) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// import("xxx");
|
||||
if (tmp->get_calls().size()!=1) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
call
|
||||
|_id:import
|
||||
|_call_func
|
||||
|_string:'filename'
|
||||
*/
|
||||
if (tmp->get_calls()[0]->get_type()!=expr_type::ast_callf) {
|
||||
return false;
|
||||
}
|
||||
auto func_call = (call_function*)tmp->get_calls()[0];
|
||||
if (func_call->get_argument().size()!=1) {
|
||||
return false;
|
||||
}
|
||||
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool linker::exist(const std::string& file) {
|
||||
// avoid importing the same file
|
||||
for(const auto& fname : files) {
|
||||
if (file==fname) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
files.push_back(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
u16 linker::find(const std::string& file) {
|
||||
for(usize i = 0; i<files.size(); ++i) {
|
||||
if (files[i]==file) {
|
||||
return static_cast<u16>(i);
|
||||
}
|
||||
}
|
||||
std::cerr << "unreachable: using this method incorrectly\n";
|
||||
std::exit(-1);
|
||||
return UINT16_MAX;
|
||||
}
|
||||
|
||||
bool linker::check_self_import(const std::string& file) {
|
||||
for(const auto& i : module_load_stack) {
|
||||
if (file==i) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string linker::generate_self_import_path(const std::string& filename) {
|
||||
std::string res = "";
|
||||
for(const auto& i : module_load_stack) {
|
||||
res += "[" + i + "] -> ";
|
||||
}
|
||||
return res + "[" + filename + "]";
|
||||
}
|
||||
|
||||
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
|
||||
// add children of add_root to the back of root
|
||||
for(auto& i : old_tree_root->get_expressions()) {
|
||||
new_tree_root->add_expression(i);
|
||||
}
|
||||
// clean old root
|
||||
old_tree_root->get_expressions().clear();
|
||||
}
|
||||
|
||||
code_block* linker::import_regular_file(call_expr* node) {
|
||||
lexer lex;
|
||||
parse par;
|
||||
// get filename
|
||||
auto filename = get_path(node);
|
||||
// clear this node
|
||||
for(auto i : node->get_calls()) {
|
||||
delete i;
|
||||
}
|
||||
node->get_calls().clear();
|
||||
auto location = node->get_first()->get_location();
|
||||
delete node->get_first();
|
||||
node->set_first(new nil_expr(location));
|
||||
// this will make node to call_expr(nil),
|
||||
// will not be optimized when generating bytecodes
|
||||
|
||||
// avoid infinite loading loop
|
||||
filename = find_file(filename, node->get_location());
|
||||
if (!filename.length()) {
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
if (check_self_import(filename)) {
|
||||
err.err("link", "self-referenced module <" + filename + ">:\n" +
|
||||
" reference path: " + generate_self_import_path(filename));
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
exist(filename);
|
||||
|
||||
module_load_stack.push_back(filename);
|
||||
// start importing...
|
||||
if (lex.scan(filename).geterr()) {
|
||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
||||
}
|
||||
if (par.compile(lex).geterr()) {
|
||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
||||
}
|
||||
auto tmp = par.swap(nullptr);
|
||||
|
||||
// check if tmp has 'import'
|
||||
auto res = load(tmp, find(filename));
|
||||
module_load_stack.pop_back();
|
||||
return res;
|
||||
}
|
||||
|
||||
code_block* linker::import_nasal_lib() {
|
||||
lexer lex;
|
||||
parse par;
|
||||
auto filename = find_file("lib.nas", {0, 0, 0, 0, files[0]});
|
||||
if (!filename.length()) {
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
lib_path = filename;
|
||||
|
||||
// avoid infinite loading library
|
||||
if (exist(filename)) {
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
|
||||
// start importing...
|
||||
if (lex.scan(filename).geterr()) {
|
||||
err.err("link", "error occurred when analysing library <" + filename + ">");
|
||||
}
|
||||
if (par.compile(lex).geterr()) {
|
||||
err.err("link", "error occurred when analysing library <" + filename + ">");
|
||||
}
|
||||
auto tmp = par.swap(nullptr);
|
||||
|
||||
// check if tmp has 'import'
|
||||
return load(tmp, find(filename));
|
||||
}
|
||||
|
||||
std::string linker::generate_module_name(const std::string& filename) {
|
||||
auto error_name = "error_generated@[" + filename + "]";
|
||||
auto pos = filename.find_last_of(".nas");
|
||||
if (pos==std::string::npos) {
|
||||
return error_name;
|
||||
}
|
||||
pos -= 4;
|
||||
auto split_pos = filename.find_last_of("/");
|
||||
if (split_pos==std::string::npos) {
|
||||
split_pos = filename.find_last_of("\\");
|
||||
}
|
||||
auto res = split_pos==std::string::npos?
|
||||
filename.substr(0, pos + 1):
|
||||
filename.substr(split_pos + 1, pos - split_pos);
|
||||
if (!res.length()) {
|
||||
err.warn("link", "get empty module name from <" + filename + ">, " +
|
||||
"will not be easily accessed.");
|
||||
}
|
||||
if (res.length() && '0' <= res[0] && res[0] <= '9') {
|
||||
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
|
||||
"will not be easily accessed.");
|
||||
}
|
||||
if (res.length() && res.find(".")!=std::string::npos) {
|
||||
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
|
||||
"will not be easily accessed.");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
return_expr* linker::generate_module_return(code_block* block) {
|
||||
auto sf = new symbol_finder;
|
||||
auto res = new return_expr(block->get_location());
|
||||
auto value = new hash_expr(block->get_location());
|
||||
res->set_value(value);
|
||||
for(const auto& i : sf->do_find(block)) {
|
||||
auto pair = new hash_pair(block->get_location());
|
||||
// do not export symbol begins with '_'
|
||||
if (i.name.length() && i.name[0]=='_') {
|
||||
continue;
|
||||
}
|
||||
pair->set_name(i.name);
|
||||
pair->set_value(new identifier(block->get_location(), i.name));
|
||||
value->add_member(pair);
|
||||
}
|
||||
delete sf;
|
||||
return res;
|
||||
}
|
||||
|
||||
definition_expr* linker::generate_module_definition(code_block* block) {
|
||||
auto def = new definition_expr(block->get_location());
|
||||
def->set_identifier(new identifier(
|
||||
block->get_location(),
|
||||
generate_module_name(block->get_location().file)
|
||||
));
|
||||
|
||||
auto call = new call_expr(block->get_location());
|
||||
auto func = new function(block->get_location());
|
||||
func->set_code_block(block);
|
||||
func->get_code_block()->add_expression(generate_module_return(block));
|
||||
call->set_first(func);
|
||||
call->add_call(new call_function(block->get_location()));
|
||||
|
||||
def->set_value(call);
|
||||
return def;
|
||||
}
|
||||
|
||||
code_block* linker::load(code_block* program_root, u16 fileindex) {
|
||||
auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
|
||||
// load library, this ast will be linked with root directly
|
||||
// so no extra namespace is generated
|
||||
if (!lib_loaded) {
|
||||
auto nasal_lib_code_block = import_nasal_lib();
|
||||
// insert nasal lib code to the back of tree
|
||||
link(tree, nasal_lib_code_block);
|
||||
delete nasal_lib_code_block;
|
||||
lib_loaded = true;
|
||||
}
|
||||
|
||||
// load imported modules
|
||||
for(auto& import_ast_node : program_root->get_expressions()) {
|
||||
if (!import_check(import_ast_node)) {
|
||||
break;
|
||||
}
|
||||
auto module_code_block = import_regular_file((call_expr*)import_ast_node);
|
||||
// after importing the regular file as module, delete this node
|
||||
const auto loc = import_ast_node->get_location();
|
||||
delete import_ast_node;
|
||||
// and replace the node with null_expr node
|
||||
import_ast_node = new null_expr(loc);
|
||||
// then we generate a function warping the code block,
|
||||
// and export the necessary global symbols in this code block
|
||||
// by generate a return statement, with a hashmap return value
|
||||
tree->add_expression(generate_module_definition(module_code_block));
|
||||
}
|
||||
|
||||
// insert program root to the back of tree
|
||||
link(tree, program_root);
|
||||
return tree;
|
||||
}
|
||||
|
||||
const error& linker::link(
|
||||
parse& parse, const std::string& self, bool spath = false) {
|
||||
show_path = spath;
|
||||
// initializing file map
|
||||
this_file = self;
|
||||
files = {self};
|
||||
module_load_stack = {self};
|
||||
// scan root and import files
|
||||
// then generate a new ast and return to import_ast
|
||||
// the main file's index is 0
|
||||
auto new_tree_root = load(parse.tree(), 0);
|
||||
auto old_tree_root = parse.swap(new_tree_root);
|
||||
delete old_tree_root;
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
60
src/nasal_import.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define F_OK 0
|
||||
#endif
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "symbol_finder.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class linker {
|
||||
private:
|
||||
bool show_path;
|
||||
bool lib_loaded;
|
||||
std::string this_file;
|
||||
std::string lib_path;
|
||||
error err;
|
||||
std::vector<std::string> files;
|
||||
std::vector<std::string> module_load_stack;
|
||||
std::vector<std::string> envpath;
|
||||
|
||||
private:
|
||||
bool import_check(expr*);
|
||||
bool exist(const std::string&);
|
||||
u16 find(const std::string&);
|
||||
bool check_self_import(const std::string&);
|
||||
std::string generate_self_import_path(const std::string&);
|
||||
void link(code_block*, code_block*);
|
||||
std::string get_path(call_expr*);
|
||||
std::string find_file(const std::string&, const span&);
|
||||
code_block* import_regular_file(call_expr*);
|
||||
code_block* import_nasal_lib();
|
||||
std::string generate_module_name(const std::string&);
|
||||
return_expr* generate_module_return(code_block*);
|
||||
definition_expr* generate_module_definition(code_block*);
|
||||
code_block* load(code_block*, u16);
|
||||
|
||||
public:
|
||||
linker();
|
||||
const error& link(parse&, const std::string&, bool);
|
||||
const auto& get_file_list() const {return files;}
|
||||
const auto& get_this_file() const {return this_file;}
|
||||
const auto& get_lib_path() const {return lib_path;}
|
||||
};
|
||||
|
||||
}
|
||||
393
src/nasal_lexer.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#include "nasal_lexer.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool lexer::skip(char c) {
|
||||
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
|
||||
}
|
||||
|
||||
bool lexer::is_id(char c) {
|
||||
return (c=='_') || std::isalpha(c) || (c<0);
|
||||
}
|
||||
|
||||
bool lexer::is_hex(char c) {
|
||||
return std::isxdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_oct(char c) {
|
||||
return '0'<=c && c<='7';
|
||||
}
|
||||
|
||||
bool lexer::is_dec(char c) {
|
||||
return std::isdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_str(char c) {
|
||||
return c=='\'' || c=='\"' || c=='`';
|
||||
}
|
||||
|
||||
bool lexer::is_single_opr(char c) {
|
||||
return (
|
||||
c=='(' || c==')' || c=='[' || c==']' ||
|
||||
c=='{' || c=='}' || c==',' || c==';' ||
|
||||
c==':' || c=='?' || c=='`' || c=='@' ||
|
||||
c=='%' || c=='$' || c=='\\'
|
||||
);
|
||||
}
|
||||
|
||||
bool lexer::is_calc_opr(char c) {
|
||||
return (
|
||||
c=='=' || c=='+' || c=='-' || c=='*' ||
|
||||
c=='!' || c=='/' || c=='<' || c=='>' ||
|
||||
c=='~' || c=='|' || c=='&' || c=='^'
|
||||
);
|
||||
}
|
||||
|
||||
void lexer::skip_note() {
|
||||
// avoid note, after this process ptr will point to '\n'
|
||||
// so next loop line counter+1
|
||||
while(++ptr<res.size() && res[ptr]!='\n') {}
|
||||
}
|
||||
|
||||
void lexer::err_char() {
|
||||
++column;
|
||||
char c = res[ptr++];
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid character 0x"+chrhex(c)
|
||||
);
|
||||
++invalid_char;
|
||||
}
|
||||
|
||||
void lexer::open(const std::string& file) {
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
err.load(file);
|
||||
filename = file;
|
||||
res = repl::info::instance()->repl_file_source;
|
||||
return;
|
||||
}
|
||||
|
||||
// check file exsits and it is a regular file
|
||||
struct stat buffer;
|
||||
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
|
||||
err.err("lexer", "<"+file+"> is not a regular file");
|
||||
err.chkerr();
|
||||
}
|
||||
|
||||
// load
|
||||
filename = file;
|
||||
std::ifstream in(file, std::ios::binary);
|
||||
if (in.fail()) {
|
||||
err.err("lexer", "failed to open <" + file + ">");
|
||||
res = "";
|
||||
return;
|
||||
}
|
||||
err.load(file);
|
||||
std::stringstream ss;
|
||||
ss << in.rdbuf();
|
||||
res = ss.str();
|
||||
}
|
||||
|
||||
tok lexer::get_type(const std::string& str) {
|
||||
return typetbl.count(str)? typetbl.at(str):tok::null;
|
||||
}
|
||||
|
||||
std::string lexer::utf8_gen() {
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && res[ptr]<0) {
|
||||
std::string tmp = "";
|
||||
u32 nbytes = utf8_hdchk(res[ptr]);
|
||||
if (!nbytes) {
|
||||
++ptr;
|
||||
++column;
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp += res[ptr++];
|
||||
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
|
||||
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
|
||||
tmp += res[ptr];
|
||||
}
|
||||
}
|
||||
|
||||
// utf8 character's total length is 1+nbytes
|
||||
if (tmp.length()!=1+nbytes) {
|
||||
++column;
|
||||
std::string utf_info = "0x"+chrhex(tmp[0]);
|
||||
for(u32 i = 1; i<tmp.size(); ++i) {
|
||||
utf_info += " 0x"+chrhex(tmp[i]);
|
||||
}
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid utf-8 <"+utf_info+">"
|
||||
);
|
||||
++invalid_char;
|
||||
}
|
||||
str += tmp;
|
||||
// may have some problems because not all the unicode takes 2 space
|
||||
column += 2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
token lexer::id_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
|
||||
if (res[ptr]<0) { // utf-8
|
||||
str += utf8_gen();
|
||||
} else { // ascii
|
||||
str += res[ptr++];
|
||||
++column;
|
||||
}
|
||||
}
|
||||
tok type = get_type(str);
|
||||
return {
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
(type!=tok::null)? type:tok::id, str
|
||||
};
|
||||
}
|
||||
|
||||
token lexer::num_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
// generate hex number
|
||||
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
|
||||
std::string str = "0x";
|
||||
ptr += 2;
|
||||
while(ptr<res.size() && is_hex(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
// "0x"
|
||||
if (str.length()<3) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
|
||||
std::string str = "0o";
|
||||
ptr += 2;
|
||||
while(ptr<res.size() && is_oct(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
bool erfmt = false;
|
||||
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
|
||||
erfmt = true;
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
if (str.length()==2 || erfmt) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
}
|
||||
// generate dec number
|
||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
if (ptr<res.size() && res[ptr]=='.') {
|
||||
str += res[ptr++];
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxx." is not a correct number
|
||||
if (str.back()=='.') {
|
||||
column += str.length();
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||
}
|
||||
}
|
||||
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
|
||||
str += res[ptr++];
|
||||
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxe(-|+)" is not a correct number
|
||||
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
|
||||
column += str.length();
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||
}
|
||||
}
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
}
|
||||
|
||||
token lexer::str_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = "";
|
||||
const char begin = res[ptr];
|
||||
++column;
|
||||
while(++ptr<res.size() && res[ptr]!=begin) {
|
||||
++column;
|
||||
if (res[ptr]=='\n') {
|
||||
column = 0;
|
||||
++line;
|
||||
}
|
||||
if (res[ptr]=='\\' && ptr+1<res.size()) {
|
||||
++column;
|
||||
++ptr;
|
||||
switch(res[ptr]) {
|
||||
case '0': str += '\0'; break;
|
||||
case 'a': str += '\a'; break;
|
||||
case 'b': str += '\b'; break;
|
||||
case 'e': str += '\033'; break;
|
||||
case 't': str += '\t'; break;
|
||||
case 'n': str += '\n'; break;
|
||||
case 'v': str += '\v'; break;
|
||||
case 'f': str += '\f'; break;
|
||||
case 'r': str += '\r'; break;
|
||||
case '?': str += '\?'; break;
|
||||
case '\\':str += '\\'; break;
|
||||
case '\'':str += '\''; break;
|
||||
case '\"':str += '\"'; break;
|
||||
default: str += res[ptr];break;
|
||||
}
|
||||
if (res[ptr]=='\n') {
|
||||
column = 0;
|
||||
++line;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
str += res[ptr];
|
||||
}
|
||||
// check if this string ends with a " or '
|
||||
if (ptr++>=res.size()) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"get EOF when generating string"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||
}
|
||||
++column;
|
||||
|
||||
// if is not utf8, 1+utf8_hdchk should be 1
|
||||
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"\'`\' is used for string including one character"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||
}
|
||||
|
||||
token lexer::single_opr() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str(1, res[ptr]);
|
||||
++column;
|
||||
tok type = get_type(str);
|
||||
if (type==tok::null) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid operator `"+str+"`"
|
||||
);
|
||||
}
|
||||
++ptr;
|
||||
return {{begin_line, begin_column, line, column, filename}, type, str};
|
||||
}
|
||||
|
||||
token lexer::dots() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = ".";
|
||||
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
|
||||
str += "..";
|
||||
}
|
||||
ptr += str.length();
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||
}
|
||||
|
||||
token lexer::calc_opr() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
// get calculation operator
|
||||
std::string str(1, res[ptr++]);
|
||||
if (ptr<res.size() && res[ptr]=='=') {
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||
}
|
||||
|
||||
const error& lexer::scan(const std::string& file) {
|
||||
line = 1;
|
||||
column = 0;
|
||||
ptr = 0;
|
||||
toks = {};
|
||||
open(file);
|
||||
|
||||
while(ptr<res.size()) {
|
||||
while(ptr<res.size() && skip(res[ptr])) {
|
||||
// these characters will be ignored, and '\n' will cause ++line
|
||||
++column;
|
||||
if (res[ptr++]=='\n') {
|
||||
++line;
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
if (ptr>=res.size()) {
|
||||
break;
|
||||
}
|
||||
if (is_id(res[ptr])) {
|
||||
toks.push_back(id_gen());
|
||||
} else if (is_dec(res[ptr])) {
|
||||
toks.push_back(num_gen());
|
||||
} else if (is_str(res[ptr])) {
|
||||
toks.push_back(str_gen());
|
||||
} else if (is_single_opr(res[ptr])) {
|
||||
toks.push_back(single_opr());
|
||||
} else if (res[ptr]=='.') {
|
||||
toks.push_back(dots());
|
||||
} else if (is_calc_opr(res[ptr])) {
|
||||
toks.push_back(calc_opr());
|
||||
} else if (res[ptr]=='#') {
|
||||
skip_note();
|
||||
} else {
|
||||
err_char();
|
||||
}
|
||||
if (invalid_char>10) {
|
||||
err.err("lexer", "too many invalid characters, stop");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toks.size()) {
|
||||
// eof token's location is the last token's location
|
||||
toks.push_back({toks.back().loc, tok::eof, "<eof>"});
|
||||
} else {
|
||||
// if token sequence is empty, generate a default location
|
||||
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
||||
}
|
||||
res = "";
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
188
src/nasal_lexer.h
Normal file
@@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class tok:u32 {
|
||||
null=0, // null token (default token type)
|
||||
num, // number literal
|
||||
str, // string literal
|
||||
id, // identifier
|
||||
tktrue, // keyword true
|
||||
tkfalse, // keyword false
|
||||
rfor, // loop keyword for
|
||||
forindex, // loop keyword forindex
|
||||
foreach, // loop keyword foreach
|
||||
rwhile, // loop keyword while
|
||||
var, // keyword for definition
|
||||
func, // keyword for definition of function
|
||||
brk, // loop keyword break
|
||||
cont, // loop keyword continue
|
||||
ret, // function keyword return
|
||||
rif, // condition expression keyword if
|
||||
elsif, // condition expression keyword elsif
|
||||
relse, // condition expression keyword else
|
||||
tknil, // nil literal
|
||||
lcurve, // (
|
||||
rcurve, // )
|
||||
lbracket, // [
|
||||
rbracket, // ]
|
||||
lbrace, // {
|
||||
rbrace, // }
|
||||
semi, // ;
|
||||
opand, // operator and
|
||||
opor, // operator or
|
||||
comma, // ,
|
||||
dot, // .
|
||||
ellipsis, // ...
|
||||
quesmark, // ?
|
||||
colon, // :
|
||||
add, // operator +
|
||||
sub, // operator -
|
||||
mult, // operator *
|
||||
div, // operator /
|
||||
floater, // operator ~ and binary operator ~
|
||||
btand, // bitwise operator &
|
||||
btor, // bitwise operator |
|
||||
btxor, // bitwise operator ^
|
||||
opnot, // operator !
|
||||
eq, // operator =
|
||||
addeq, // operator +=
|
||||
subeq, // operator -=
|
||||
multeq, // operator *=
|
||||
diveq, // operator /=
|
||||
lnkeq, // operator ~=
|
||||
btandeq, // operator &=
|
||||
btoreq, // operator |=
|
||||
btxoreq, // operator ^=
|
||||
cmpeq, // operator ==
|
||||
neq, // operator !=
|
||||
less, // operator <
|
||||
leq, // operator <=
|
||||
grt, // operator >
|
||||
geq, // operator >=
|
||||
eof // <eof> end of token list
|
||||
};
|
||||
|
||||
struct token {
|
||||
span loc; // location
|
||||
tok type; // token type
|
||||
std::string str; // content
|
||||
token() = default;
|
||||
token(const token&) = default;
|
||||
};
|
||||
|
||||
class lexer {
|
||||
private:
|
||||
u32 line;
|
||||
u32 column;
|
||||
usize ptr;
|
||||
std::string filename;
|
||||
std::string res;
|
||||
|
||||
error err;
|
||||
u64 invalid_char;
|
||||
std::vector<token> toks;
|
||||
|
||||
const std::unordered_map<std::string, tok> typetbl {
|
||||
{"true" ,tok::tktrue },
|
||||
{"false" ,tok::tkfalse },
|
||||
{"for" ,tok::rfor },
|
||||
{"forindex",tok::forindex},
|
||||
{"foreach" ,tok::foreach },
|
||||
{"while" ,tok::rwhile },
|
||||
{"var" ,tok::var },
|
||||
{"func" ,tok::func },
|
||||
{"break" ,tok::brk },
|
||||
{"continue",tok::cont },
|
||||
{"return" ,tok::ret },
|
||||
{"if" ,tok::rif },
|
||||
{"elsif" ,tok::elsif },
|
||||
{"else" ,tok::relse },
|
||||
{"nil" ,tok::tknil },
|
||||
{"(" ,tok::lcurve },
|
||||
{")" ,tok::rcurve },
|
||||
{"[" ,tok::lbracket},
|
||||
{"]" ,tok::rbracket},
|
||||
{"{" ,tok::lbrace },
|
||||
{"}" ,tok::rbrace },
|
||||
{";" ,tok::semi },
|
||||
{"and" ,tok::opand },
|
||||
{"or" ,tok::opor },
|
||||
{"," ,tok::comma },
|
||||
{"." ,tok::dot },
|
||||
{"..." ,tok::ellipsis},
|
||||
{"?" ,tok::quesmark},
|
||||
{":" ,tok::colon },
|
||||
{"+" ,tok::add },
|
||||
{"-" ,tok::sub },
|
||||
{"*" ,tok::mult },
|
||||
{"/" ,tok::div },
|
||||
{"~" ,tok::floater },
|
||||
{"&" ,tok::btand },
|
||||
{"|" ,tok::btor },
|
||||
{"^" ,tok::btxor },
|
||||
{"!" ,tok::opnot },
|
||||
{"=" ,tok::eq },
|
||||
{"+=" ,tok::addeq },
|
||||
{"-=" ,tok::subeq },
|
||||
{"*=" ,tok::multeq },
|
||||
{"/=" ,tok::diveq },
|
||||
{"~=" ,tok::lnkeq },
|
||||
{"&=" ,tok::btandeq },
|
||||
{"|=" ,tok::btoreq },
|
||||
{"^=" ,tok::btxoreq },
|
||||
{"==" ,tok::cmpeq },
|
||||
{"!=" ,tok::neq },
|
||||
{"<" ,tok::less },
|
||||
{"<=" ,tok::leq },
|
||||
{">" ,tok::grt },
|
||||
{">=" ,tok::geq }
|
||||
};
|
||||
|
||||
tok get_type(const std::string&);
|
||||
bool skip(char);
|
||||
bool is_id(char);
|
||||
bool is_hex(char);
|
||||
bool is_oct(char);
|
||||
bool is_dec(char);
|
||||
bool is_str(char);
|
||||
bool is_single_opr(char);
|
||||
bool is_calc_opr(char);
|
||||
|
||||
void skip_note();
|
||||
void err_char();
|
||||
|
||||
void open(const std::string&);
|
||||
std::string utf8_gen();
|
||||
token id_gen();
|
||||
token num_gen();
|
||||
token str_gen();
|
||||
token single_opr();
|
||||
token dots();
|
||||
token calc_opr();
|
||||
public:
|
||||
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {}
|
||||
const error& scan(const std::string&);
|
||||
const std::vector<token>& result() const {return toks;}
|
||||
};
|
||||
|
||||
}
|
||||
234
src/nasal_misc.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool is_windows() {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_linux() {
|
||||
#if defined __linux__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_macos() {
|
||||
#if defined __APPLE__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_x86() {
|
||||
#if defined(__i386__) || defined(_M_IX86)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_amd64() {
|
||||
#if defined(__amd64__) || defined(_M_X64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_x86_64() {
|
||||
return is_amd64();
|
||||
}
|
||||
|
||||
bool is_arm() {
|
||||
#if defined(__arm__) || defined(_M_ARM)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_aarch64() {
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_ia64() {
|
||||
#if defined(__ia64__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_powerpc() {
|
||||
#if defined(__powerpc__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_superh() {
|
||||
#if defined(__sh__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
f64 hex2f(const char* str) {
|
||||
f64 ret = 0;
|
||||
for(; *str; ++str) {
|
||||
if ('0'<=*str && *str<='9') {
|
||||
ret = ret*16+(*str-'0');
|
||||
} else if ('a'<=*str && *str<='f') {
|
||||
ret = ret*16+(*str-'a'+10);
|
||||
} else if ('A'<=*str && *str<='F') {
|
||||
ret = ret*16+(*str-'A'+10);
|
||||
} else {
|
||||
return nan("");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
f64 oct2f(const char* str) {
|
||||
f64 ret = 0;
|
||||
while('0'<=*str && *str<'8') {
|
||||
ret = ret*8+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
return nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// we have the same reason not using atof here
|
||||
// just as andy's interpreter does.
|
||||
// it is not platform independent, and may have strange output.
|
||||
// so we write a new function here to convert str to number manually.
|
||||
// but this also makes 0.1+0.2==0.3,
|
||||
// not another result that you may get in other languages.
|
||||
f64 dec2f(const char* str) {
|
||||
f64 ret = 0, negative = 1, num_pow = 0;
|
||||
while('0'<=*str && *str<='9') {
|
||||
ret = ret*10+(*str++-'0');
|
||||
}
|
||||
if (!*str) {
|
||||
return ret;
|
||||
}
|
||||
if (*str=='.') {
|
||||
if (!*++str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0.1;
|
||||
while('0'<=*str && *str<='9') {
|
||||
ret += num_pow*(*str++-'0');
|
||||
num_pow *= 0.1;
|
||||
}
|
||||
if (!*str) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (*str!='e' && *str!='E') {
|
||||
return nan("");
|
||||
}
|
||||
if (!*++str) {
|
||||
return nan("");
|
||||
}
|
||||
if (*str=='-' || *str=='+') {
|
||||
negative = (*str++=='-'? -1:1);
|
||||
}
|
||||
if (!*str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0;
|
||||
while('0'<=*str && *str<='9') {
|
||||
num_pow = num_pow*10+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
return nan("");
|
||||
}
|
||||
return ret*std::pow(10, negative*num_pow);
|
||||
}
|
||||
|
||||
f64 str2num(const char* str) {
|
||||
bool negative = false;
|
||||
f64 res = 0;
|
||||
if (*str=='-' || *str=='+') {
|
||||
negative = (*str++=='-');
|
||||
}
|
||||
if (!*str) {
|
||||
return nan("");
|
||||
}
|
||||
if (str[0]=='0' && str[1]=='x') {
|
||||
res = hex2f(str+2);
|
||||
} else if (str[0]=='0' && str[1]=='o') {
|
||||
res = oct2f(str+2);
|
||||
} else {
|
||||
res = dec2f(str);
|
||||
}
|
||||
return negative? -res:res;
|
||||
}
|
||||
|
||||
i32 utf8_hdchk(const char head) {
|
||||
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
|
||||
const auto c = static_cast<u8>(head);
|
||||
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
|
||||
return 1;
|
||||
}
|
||||
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
|
||||
return 2;
|
||||
}
|
||||
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string chrhex(const char c) {
|
||||
const char hextbl[] = "0123456789abcdef";
|
||||
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
|
||||
}
|
||||
|
||||
std::string rawstr(const std::string& str, const usize maxlen) {
|
||||
std::string ret("");
|
||||
for(auto i : str) {
|
||||
// windows doesn't output unicode normally, so we output the hex
|
||||
if (is_windows() && i<=0) {
|
||||
ret += "\\x"+chrhex(i);
|
||||
continue;
|
||||
}
|
||||
switch(i) {
|
||||
case '\0': ret += "\\0"; break;
|
||||
case '\a': ret += "\\a"; break;
|
||||
case '\b': ret += "\\b"; break;
|
||||
case '\t': ret += "\\t"; break;
|
||||
case '\n': ret += "\\n"; break;
|
||||
case '\v': ret += "\\v"; break;
|
||||
case '\f': ret += "\\f"; break;
|
||||
case '\r': ret += "\\r"; break;
|
||||
case '\033':ret += "\\e"; break;
|
||||
case '\"': ret += "\\\""; break;
|
||||
case '\'': ret += "\\\'"; break;
|
||||
case '\\': ret += "\\\\"; break;
|
||||
default: ret += i; break;
|
||||
}
|
||||
}
|
||||
if (maxlen && ret.length()>maxlen) {
|
||||
ret = ret.substr(0, maxlen)+"...";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
123
src/nasal_opcode.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "nasal_opcode.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const char* opname[] = {
|
||||
"exit ", "repl ", "intl ", "loadg ",
|
||||
"loadl ", "loadu ", "pnum ", "pnil ",
|
||||
"pstr ", "newv ", "newh ", "newf ",
|
||||
"happ ", "para ", "def ", "dyn ",
|
||||
"lnot ", "usub ", "bitnot", "bitor ",
|
||||
"bitxor", "bitand", "add ", "sub ",
|
||||
"mult ", "div ", "lnk ", "addc ",
|
||||
"subc ", "multc ", "divc ", "lnkc ",
|
||||
"addeq ", "subeq ", "muleq ", "diveq ",
|
||||
"lnkeq ", "bandeq", "boreq ", "bxoreq",
|
||||
"addeqc", "subeqc", "muleqc", "diveqc",
|
||||
"lnkeqc", "addecp", "subecp", "mulecp",
|
||||
"divecp", "lnkecp", "meq ", "eq ",
|
||||
"neq ", "less ", "leq ", "grt ",
|
||||
"geq ", "lessc ", "leqc ", "grtc ",
|
||||
"geqc ", "pop ", "jmp ", "jt ",
|
||||
"jf ", "cnt ", "findx ", "feach ",
|
||||
"callg ", "calll ", "upval ", "callv ",
|
||||
"callvi", "callh ", "callfv", "callfh",
|
||||
"callb ", "slcbeg", "slcend", "slice ",
|
||||
"slice2", "mcallg", "mcalll", "mupval",
|
||||
"mcallv", "mcallh", "ret "
|
||||
};
|
||||
|
||||
void codestream::set(
|
||||
const f64* num_buff,
|
||||
const std::string* str_buff,
|
||||
const nasal_builtin_table* native_table_ptr,
|
||||
const std::string* file_list) {
|
||||
nums = num_buff;
|
||||
strs = str_buff;
|
||||
natives = native_table_ptr;
|
||||
files = file_list;
|
||||
}
|
||||
|
||||
void codestream::dump(std::ostream& out) const {
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::hex;
|
||||
using std::dec;
|
||||
auto op = code.op;
|
||||
auto num = code.num;
|
||||
out << hex << "0x"
|
||||
<< setw(6) << setfill('0') << index << " "
|
||||
<< setw(2) << setfill('0') << static_cast<u32>(op) << " "
|
||||
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
|
||||
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " "
|
||||
<< setw(2) << setfill('0') << (num&0xff) << " "
|
||||
<<opname[op]<<" "<<dec;
|
||||
switch(op) {
|
||||
case op_addeq: case op_subeq:
|
||||
case op_muleq: case op_diveq:
|
||||
case op_lnkeq: case op_meq:
|
||||
case op_btandeq: case op_btoreq:
|
||||
case op_btxoreq:
|
||||
out << hex << "0x" << num << dec << " sp-" << num; break;
|
||||
case op_addeqc: case op_subeqc:
|
||||
case op_muleqc:case op_diveqc:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << nums[num] << ")"; break;
|
||||
case op_lnkeqc:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(strs[num], 16) << ")"; break;
|
||||
case op_addecp: case op_subecp:
|
||||
case op_mulecp: case op_divecp:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << nums[num] << ") sp-1"; break;
|
||||
case op_lnkecp:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(strs[num], 16) << ") sp-1"; break;
|
||||
case op_addc: case op_subc:
|
||||
case op_mulc: case op_divc:
|
||||
case op_lessc: case op_leqc:
|
||||
case op_grtc: case op_geqc:
|
||||
case op_pnum:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << nums[num] << ")"; break;
|
||||
case op_callvi: case op_newv:
|
||||
case op_callfv: case op_repl:
|
||||
case op_intl: case op_findex:
|
||||
case op_feach: case op_newf:
|
||||
case op_jmp: case op_jt:
|
||||
case op_jf: case op_callg:
|
||||
case op_mcallg: case op_loadg:
|
||||
case op_calll: case op_mcalll:
|
||||
case op_loadl:
|
||||
out << hex << "0x" << num << dec; break;
|
||||
case op_callb:
|
||||
out << hex << "0x" << num << " <" << natives[num].name
|
||||
<< "@0x" << reinterpret_cast<u64>(natives[num].func)
|
||||
<< dec << ">"; break;
|
||||
case op_upval: case op_mupval:
|
||||
case op_loadu:
|
||||
out << hex << "0x" << ((num>>16)&0xffff)
|
||||
<< "[0x" << (num&0xffff) << "]" << dec; break;
|
||||
case op_happ: case op_pstr:
|
||||
case op_lnkc: case op_callh:
|
||||
case op_mcallh: case op_para:
|
||||
case op_deft: case op_dyn:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(strs[num], 16) << ")"; break;
|
||||
default:
|
||||
if (files) {
|
||||
out << hex << "0x" << num << dec;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (files) {
|
||||
out << "(" << files[code.fidx] << ":" << code.line << ")";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
|
||||
ins.dump(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
133
src/nasal_opcode.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum op_code_type:u8 {
|
||||
op_exit, // stop the virtual machine
|
||||
op_repl, // in repl mode: print value on stack top
|
||||
op_intl, // local scope size
|
||||
op_loadg, // load global value
|
||||
op_loadl, // load local value
|
||||
op_loadu, // load upvalue
|
||||
op_pnum, // push constant number to the stack
|
||||
op_pnil, // push constant nil to the stack
|
||||
op_pstr, // push constant std::string to the stack
|
||||
op_newv, // push new vector with initial values from stack
|
||||
op_newh, // push new hash to the stack
|
||||
op_newf, // push new function to the stack
|
||||
op_happ, // hash append
|
||||
op_para, // normal parameter
|
||||
op_deft, // default parameter
|
||||
op_dyn, // dynamic parameter
|
||||
op_lnot, // ! logical negation
|
||||
op_usub, // - negation
|
||||
op_bnot, // ~ bitwise not static_cast<i32>
|
||||
op_btor, // | bitwise or
|
||||
op_btxor, // ^ bitwise xor
|
||||
op_btand, // & bitwise and
|
||||
op_add, // +
|
||||
op_sub, // -
|
||||
op_mul, // *
|
||||
op_div, // /
|
||||
op_lnk, // ~
|
||||
op_addc, // + const
|
||||
op_subc, // - const
|
||||
op_mulc, // * const
|
||||
op_divc, // / const
|
||||
op_lnkc, // ~ const
|
||||
op_addeq, // += maybe pop stack top
|
||||
op_subeq, // -= maybe pop stack top
|
||||
op_muleq, // *= maybe pop stack top
|
||||
op_diveq, // /= maybe pop stack top
|
||||
op_lnkeq, // ~= maybe pop stack top
|
||||
op_btandeq,// &= maybe pop stack top
|
||||
op_btoreq, // |= maybe pop stack top
|
||||
op_btxoreq,// ^= maybe pop stack top
|
||||
op_addeqc, // += const don't pop stack top
|
||||
op_subeqc, // -= const don't pop stack top
|
||||
op_muleqc, // *= const don't pop stack top
|
||||
op_diveqc, // /= const don't pop stack top
|
||||
op_lnkeqc, // ~= const don't pop stack top
|
||||
op_addecp, // += const and pop stack top
|
||||
op_subecp, // -= const and pop stack top
|
||||
op_mulecp, // *= const and pop stack top
|
||||
op_divecp, // /= const and pop stack top
|
||||
op_lnkecp, // ~= concat const std::string and pop stack top
|
||||
op_meq, // = maybe pop stack top
|
||||
op_eq, // == compare operator
|
||||
op_neq, // != compare operator
|
||||
op_less, // < compare operator
|
||||
op_leq, // <= compare operator
|
||||
op_grt, // > compare operator
|
||||
op_geq, // >= compare operator
|
||||
op_lessc, // < const compare operator
|
||||
op_leqc, // <= const compare operator
|
||||
op_grtc, // > const compare operator
|
||||
op_geqc, // >= const compare operator
|
||||
op_pop, // pop a value out of stack top
|
||||
op_jmp, // jump absolute address with no condition
|
||||
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
|
||||
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
|
||||
op_cnt, // add counter for forindex/foreach
|
||||
op_findex, // index counter on the top of forindex_stack plus 1
|
||||
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
|
||||
op_callg, // get value in global scope
|
||||
op_calll, // get value in local scope
|
||||
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
|
||||
op_callv, // call vec[index]
|
||||
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
|
||||
op_callh, // call hash.label
|
||||
op_callfv, // call function(vector as parameters)
|
||||
op_callfh, // call function(hash as parameters)
|
||||
op_callb, // call native functions
|
||||
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
|
||||
op_slcend, // end of slice
|
||||
op_slc, // slice like vec[1]
|
||||
op_slc2, // slice like vec[nil:10]
|
||||
op_mcallg, // get memory space of value in global scope
|
||||
op_mcalll, // get memory space of value in local scope
|
||||
op_mupval, // get memory space of value in closure
|
||||
op_mcallv, // get memory space of vec[index]
|
||||
op_mcallh, // get memory space of hash.label
|
||||
op_ret // return
|
||||
};
|
||||
|
||||
struct opcode {
|
||||
u8 op; // opcode
|
||||
u16 fidx; // source code file index
|
||||
u32 num; // immediate num
|
||||
u32 line; // location line of source code
|
||||
opcode() = default;
|
||||
opcode(const opcode&) = default;
|
||||
opcode& operator=(const opcode&) = default;
|
||||
};
|
||||
|
||||
class codestream {
|
||||
private:
|
||||
opcode code;
|
||||
const u32 index;
|
||||
inline static const f64* nums = nullptr;
|
||||
inline static const std::string* strs = nullptr;
|
||||
inline static const nasal_builtin_table* natives = nullptr;
|
||||
inline static const std::string* files = nullptr;
|
||||
|
||||
public:
|
||||
codestream(const opcode& c, const u32 i): code(c), index(i) {}
|
||||
static void set(
|
||||
const f64*, const std::string*,
|
||||
const nasal_builtin_table*,
|
||||
const std::string* file_list = nullptr
|
||||
);
|
||||
void dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const codestream&);
|
||||
|
||||
extern const char* opname[];
|
||||
|
||||
}
|
||||
1063
src/nasal_parse.cpp
Normal file
158
src/nasal_parse.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class parse {
|
||||
|
||||
#define thisspan (toks[ptr].loc)
|
||||
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
|
||||
|
||||
private:
|
||||
u32 ptr;
|
||||
u32 in_func; // count function block
|
||||
u32 in_loop; // count loop block
|
||||
const token* toks;
|
||||
code_block* root;
|
||||
error err;
|
||||
|
||||
private:
|
||||
const std::unordered_map<tok, std::string> tokname {
|
||||
{tok::rfor ,"for" },
|
||||
{tok::forindex,"forindex"},
|
||||
{tok::foreach ,"foreach" },
|
||||
{tok::rwhile ,"while" },
|
||||
{tok::var ,"var" },
|
||||
{tok::func ,"func" },
|
||||
{tok::brk ,"break" },
|
||||
{tok::cont ,"continue"},
|
||||
{tok::ret ,"return" },
|
||||
{tok::rif ,"if" },
|
||||
{tok::elsif ,"elsif" },
|
||||
{tok::relse ,"else" },
|
||||
{tok::tknil ,"nil" },
|
||||
{tok::lcurve ,"(" },
|
||||
{tok::rcurve ,")" },
|
||||
{tok::lbracket,"[" },
|
||||
{tok::rbracket,"]" },
|
||||
{tok::lbrace ,"{" },
|
||||
{tok::rbrace ,"}" },
|
||||
{tok::semi ,";" },
|
||||
{tok::opand ,"and" },
|
||||
{tok::opor ,"or" },
|
||||
{tok::comma ,"," },
|
||||
{tok::dot ,"." },
|
||||
{tok::ellipsis,"..." },
|
||||
{tok::quesmark,"?" },
|
||||
{tok::colon ,":" },
|
||||
{tok::add ,"+" },
|
||||
{tok::sub ,"-" },
|
||||
{tok::mult ,"*" },
|
||||
{tok::div ,"/" },
|
||||
{tok::floater ,"~" },
|
||||
{tok::btand ,"&" },
|
||||
{tok::btor ,"|" },
|
||||
{tok::btxor ,"^" },
|
||||
{tok::opnot ,"!" },
|
||||
{tok::eq ,"=" },
|
||||
{tok::addeq ,"+=" },
|
||||
{tok::subeq ,"-=" },
|
||||
{tok::multeq ,"*=" },
|
||||
{tok::diveq ,"/=" },
|
||||
{tok::lnkeq ,"~=" },
|
||||
{tok::btandeq ,"&=" },
|
||||
{tok::btoreq ,"|=" },
|
||||
{tok::btxoreq ,"^=" },
|
||||
{tok::cmpeq ,"==" },
|
||||
{tok::neq ,"!=" },
|
||||
{tok::less ,"<" },
|
||||
{tok::leq ,"<=" },
|
||||
{tok::grt ,">" },
|
||||
{tok::geq ,">=" }
|
||||
};
|
||||
|
||||
private:
|
||||
void die(const span&,std::string);
|
||||
void next();
|
||||
void match(tok, const char* info=nullptr);
|
||||
bool lookahead(tok);
|
||||
bool is_call(tok);
|
||||
bool check_comma(const tok*);
|
||||
bool check_tuple();
|
||||
bool check_func_end(expr*);
|
||||
bool check_in_curve_multi_definition();
|
||||
bool check_special_call();
|
||||
bool need_semi_check(expr*);
|
||||
void update_location(expr*);
|
||||
|
||||
private:
|
||||
null_expr* null();
|
||||
nil_expr* nil();
|
||||
number_literal* num();
|
||||
string_literal* str();
|
||||
identifier* id();
|
||||
bool_literal* bools();
|
||||
vector_expr* vec();
|
||||
hash_expr* hash();
|
||||
hash_pair* pair();
|
||||
function* func();
|
||||
void params(function*);
|
||||
expr* lcurve_expr();
|
||||
expr* expression();
|
||||
code_block* expression_block();
|
||||
expr* calc();
|
||||
expr* bitwise_or();
|
||||
expr* bitwise_xor();
|
||||
expr* bitwise_and();
|
||||
expr* or_expr();
|
||||
expr* and_expr();
|
||||
expr* cmp_expr();
|
||||
expr* additive_expr();
|
||||
expr* multive_expr();
|
||||
unary_operator* unary();
|
||||
expr* scalar();
|
||||
call* call_scalar();
|
||||
call_hash* callh();
|
||||
call_vector* callv();
|
||||
call_function* callf();
|
||||
slice_vector* subvec();
|
||||
expr* definition();
|
||||
multi_identifier* incurve_def();
|
||||
multi_identifier* outcurve_def();
|
||||
multi_identifier* multi_id();
|
||||
tuple_expr* multi_scalar();
|
||||
multi_assign* multi_assignment();
|
||||
expr* loop();
|
||||
while_expr* while_loop();
|
||||
for_expr* for_loop();
|
||||
forei_expr* forei_loop();
|
||||
iter_expr* iter_gen();
|
||||
condition_expr* cond();
|
||||
continue_expr* continue_expression();
|
||||
break_expr* break_expression();
|
||||
return_expr* return_expression();
|
||||
|
||||
public:
|
||||
code_block* tree() {return root;}
|
||||
|
||||
// swap root pointer with another pointer(maybe nullptr)
|
||||
code_block* swap(code_block* another) {
|
||||
auto res = root;
|
||||
root = another;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
parse(): ptr(0), in_func(0), in_loop(0), toks(nullptr), root(nullptr) {}
|
||||
~parse() {delete root;}
|
||||
const error& compile(const lexer&);
|
||||
static void easter_egg();
|
||||
};
|
||||
|
||||
}
|
||||
468
src/nasal_vm.cpp
Normal file
@@ -0,0 +1,468 @@
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void vm::init(
|
||||
const std::vector<std::string>& strs,
|
||||
const std::vector<f64>& nums,
|
||||
const std::vector<nasal_builtin_table>& natives,
|
||||
const std::vector<opcode>& code,
|
||||
const std::unordered_map<std::string, i32>& global_symbol,
|
||||
const std::vector<std::string>& filenames,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
cnum = nums.data();
|
||||
cstr = strs.data();
|
||||
bytecode = code.data();
|
||||
files = filenames.data();
|
||||
global_size = global_symbol.size();
|
||||
|
||||
/* set native functions */
|
||||
native = natives;
|
||||
|
||||
/* set context and global */
|
||||
if (!is_repl_mode || first_exec_flag) {
|
||||
context_and_global_init();
|
||||
first_exec_flag = false;
|
||||
}
|
||||
|
||||
/* init gc */
|
||||
ngc.set(&ctx, global, global_size);
|
||||
ngc.init(strs, argv);
|
||||
|
||||
/* init vm globals */
|
||||
auto map_instance = ngc.alloc(vm_map);
|
||||
global[global_symbol.at("globals")] = map_instance;
|
||||
for(const auto& i : global_symbol) {
|
||||
map_instance.map().mapper[i.first] = global+i.second;
|
||||
}
|
||||
|
||||
/* init vm arg */
|
||||
auto arg_instance = ngc.alloc(vm_vec);
|
||||
global[global_symbol.at("arg")] = arg_instance;
|
||||
arg_instance.vec().elems = ngc.env_argv;
|
||||
}
|
||||
|
||||
void vm::context_and_global_init() {
|
||||
/* set canary and program counter */
|
||||
ctx.pc = 0;
|
||||
ctx.localr = nullptr;
|
||||
ctx.memr = nullptr;
|
||||
ctx.funcr = nil;
|
||||
ctx.upvalr = nil;
|
||||
|
||||
/* set canary = stack[STACK_DEPTH-1] */
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||
|
||||
/* nothing is on stack */
|
||||
ctx.top = ctx.stack - 1;
|
||||
|
||||
/* clear main stack and global */
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = nil;
|
||||
global[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void vm::valinfo(var& val) {
|
||||
const auto p = reinterpret_cast<u64>(val.val.gcobj);
|
||||
switch(val.type) {
|
||||
case vm_none: std::clog << "| null |"; break;
|
||||
case vm_ret: std::clog << "| pc | 0x" << std::hex
|
||||
<< val.ret() << std::dec; break;
|
||||
case vm_addr: std::clog << "| addr | 0x" << std::hex
|
||||
<< reinterpret_cast<u64>(val.addr())
|
||||
<< std::dec; break;
|
||||
case vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
|
||||
case vm_nil: std::clog << "| nil |"; break;
|
||||
case vm_num: std::clog << "| num | " << val.num(); break;
|
||||
case vm_str: std::clog << "| str | <0x" << std::hex << p
|
||||
<< "> " << rawstr(val.str(), 16)
|
||||
<< std::dec; break;
|
||||
case vm_func: std::clog << "| func | <0x" << std::hex << p
|
||||
<< "> entry:0x" << val.func().entry
|
||||
<< std::dec; break;
|
||||
case vm_upval:std::clog << "| upval| <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.upval().size
|
||||
<< " val]"; break;
|
||||
case vm_vec: std::clog << "| vec | <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.vec().size()
|
||||
<< " val]"; break;
|
||||
case vm_hash: std::clog << "| hash | <0x" << std::hex << p
|
||||
<< std::dec << "> {" << val.hash().size()
|
||||
<< " val}"; break;
|
||||
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
|
||||
<< "> obj:0x"
|
||||
<< reinterpret_cast<u64>(val.obj().ptr)
|
||||
<< std::dec; break;
|
||||
case vm_co: std::clog << "| co | <0x" << std::hex << p
|
||||
<< std::dec << "> coroutine"; break;
|
||||
case vm_map: std::clog << "| nmspc| <0x" << std::hex << p
|
||||
<< std::dec << "> namespace ["
|
||||
<< val.map().mapper.size() << " val]"; break;
|
||||
default: std::clog << "| err | <0x" << std::hex << p
|
||||
<< std::dec << "> unknown object"; break;
|
||||
}
|
||||
std::clog << "\n";
|
||||
}
|
||||
|
||||
void vm::traceback() {
|
||||
var* bottom = ctx.stack;
|
||||
var* top = ctx.top;
|
||||
|
||||
std::stack<u32> ret;
|
||||
for(var* i = bottom; i<=top; ++i) {
|
||||
if (i->type==vm_ret && i->ret()!=0) {
|
||||
ret.push(i->ret());
|
||||
}
|
||||
}
|
||||
ret.push(ctx.pc); // store the position program crashed
|
||||
|
||||
std::clog << "trace back ("
|
||||
<< (ngc.cort? "coroutine":"main")
|
||||
<< ")\n";
|
||||
codestream::set(cnum, cstr, native.data(), files);
|
||||
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||
if ((p = ret.top())==prev) {
|
||||
++same;
|
||||
continue;
|
||||
}
|
||||
if (same) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(6) << std::setfill('0')
|
||||
<< prev << std::dec << " "
|
||||
<< same << " same call(s)\n";
|
||||
}
|
||||
same = 0;
|
||||
std::clog << " " << codestream(bytecode[p], p) << "\n";
|
||||
}
|
||||
// the first called place has no same calls
|
||||
}
|
||||
|
||||
void vm::stackinfo(const u32 limit = 10) {
|
||||
var* top = ctx.top;
|
||||
var* bottom = ctx.stack;
|
||||
std::clog << "stack (0x" << std::hex << reinterpret_cast<u64>(bottom);
|
||||
std::clog << std::dec << ", limit " << limit << ", total ";
|
||||
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
||||
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(6) << std::setfill('0')
|
||||
<< static_cast<u64>(top-bottom) << std::dec
|
||||
<< " ";
|
||||
valinfo(top[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::reginfo() {
|
||||
std::clog << "registers (" << (ngc.cort? "coroutine":"main")
|
||||
<< ")\n" << std::hex
|
||||
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
|
||||
<< " [global] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(global) << "\n"
|
||||
<< " [local ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.localr) << "\n"
|
||||
<< " [memr ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.memr) << "\n"
|
||||
<< " [canary] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.canary) << "\n"
|
||||
<< " [top ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
||||
<< std::dec;
|
||||
std::clog << " [funcr ] "; valinfo(ctx.funcr);
|
||||
std::clog << " [upval ] "; valinfo(ctx.upvalr);
|
||||
}
|
||||
|
||||
void vm::gstate() {
|
||||
if (!global_size || global[0].type==vm_none) {
|
||||
return;
|
||||
}
|
||||
std::clog << "global (0x" << std::hex
|
||||
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
||||
for(usize i = 0; i<global_size; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
valinfo(global[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::lstate() {
|
||||
if (!ctx.localr || !ctx.funcr.func().lsize) {
|
||||
return;
|
||||
}
|
||||
const u32 lsize = ctx.funcr.func().lsize;
|
||||
std::clog << "local (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
||||
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
||||
<< ">)\n" << std::dec;
|
||||
for(u32 i = 0; i<lsize; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
valinfo(ctx.localr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::ustate() {
|
||||
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
|
||||
return;
|
||||
}
|
||||
std::clog << "upvalue\n";
|
||||
auto& upval = ctx.funcr.func().upval;
|
||||
for(u32 i = 0; i<upval.size(); ++i) {
|
||||
std::clog << " -> upval[" << i << "]:\n";
|
||||
auto& uv = upval[i].upval();
|
||||
for(u32 j = 0; j<uv.size; ++j) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << j << std::dec
|
||||
<< " ";
|
||||
valinfo(uv[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vm::detail() {
|
||||
reginfo();
|
||||
gstate();
|
||||
lstate();
|
||||
ustate();
|
||||
}
|
||||
|
||||
void vm::die(const std::string& str) {
|
||||
std::cerr << "[vm] error: " << str << "\n";
|
||||
traceback();
|
||||
stackinfo();
|
||||
|
||||
// show verbose crash info
|
||||
if (verbose) {
|
||||
detail();
|
||||
}
|
||||
|
||||
if (!ngc.cort) {
|
||||
// in main context, exit directly
|
||||
std::exit(1);
|
||||
} else {
|
||||
// in coroutine, shut down the coroutine and return to main context
|
||||
ctx.pc = 0; // mark coroutine 'dead'
|
||||
ngc.ctxreserve(); // switch context to main
|
||||
ctx.top[0] = nil; // generate return value 'nil'
|
||||
}
|
||||
}
|
||||
|
||||
void vm::run(
|
||||
const codegen& gen,
|
||||
const linker& linker,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
init(gen.strs(), gen.nums(), gen.natives(),
|
||||
gen.codes(), gen.globals(), linker.get_file_list(), argv);
|
||||
#ifndef _MSC_VER
|
||||
// using labels as values/computed goto
|
||||
const void* oprs[] = {
|
||||
&&vmexit, &&repl, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&deft, &&dyn,
|
||||
&&lnot, &&usub, &&bnot, &&btor,
|
||||
&&btxor, &&btand, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
|
||||
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
|
||||
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
|
||||
&&divecp, &&lnkecp, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&cnt, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbeg, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret
|
||||
};
|
||||
std::vector<const void*> code;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
// goto the first operand
|
||||
goto *code[ctx.pc];
|
||||
#else
|
||||
typedef void (vm::*nafunc)();
|
||||
const nafunc oprs[] = {
|
||||
nullptr, &vm::o_repl,
|
||||
&vm::o_intl, &vm::o_loadg,
|
||||
&vm::o_loadl, &vm::o_loadu,
|
||||
&vm::o_pnum, &vm::o_pnil,
|
||||
&vm::o_pstr, &vm::o_newv,
|
||||
&vm::o_newh, &vm::o_newf,
|
||||
&vm::o_happ, &vm::o_para,
|
||||
&vm::o_deft, &vm::o_dyn,
|
||||
&vm::o_lnot, &vm::o_usub,
|
||||
&vm::o_bnot, &vm::o_btor,
|
||||
&vm::o_btxor, &vm::o_btand,
|
||||
&vm::o_add, &vm::o_sub,
|
||||
&vm::o_mul, &vm::o_div,
|
||||
&vm::o_lnk, &vm::o_addc,
|
||||
&vm::o_subc, &vm::o_mulc,
|
||||
&vm::o_divc, &vm::o_lnkc,
|
||||
&vm::o_addeq, &vm::o_subeq,
|
||||
&vm::o_muleq, &vm::o_diveq,
|
||||
&vm::o_lnkeq, &vm::o_bandeq,
|
||||
&vm::o_boreq, &vm::o_bxoreq,
|
||||
&vm::o_addeqc, &vm::o_subeqc,
|
||||
&vm::o_muleqc, &vm::o_diveqc,
|
||||
&vm::o_lnkeqc, &vm::o_addecp,
|
||||
&vm::o_subecp, &vm::o_mulecp,
|
||||
&vm::o_divecp, &vm::o_lnkecp,
|
||||
&vm::o_meq, &vm::o_eq,
|
||||
&vm::o_neq, &vm::o_less,
|
||||
&vm::o_leq, &vm::o_grt,
|
||||
&vm::o_geq, &vm::o_lessc,
|
||||
&vm::o_leqc, &vm::o_grtc,
|
||||
&vm::o_geqc, &vm::o_pop,
|
||||
&vm::o_jmp, &vm::o_jt,
|
||||
&vm::o_jf, &vm::o_cnt,
|
||||
&vm::o_findex, &vm::o_feach,
|
||||
&vm::o_callg, &vm::o_calll,
|
||||
&vm::o_upval, &vm::o_callv,
|
||||
&vm::o_callvi, &vm::o_callh,
|
||||
&vm::o_callfv, &vm::o_callfh,
|
||||
&vm::o_callb, &vm::o_slcbeg,
|
||||
&vm::o_slcend, &vm::o_slc,
|
||||
&vm::o_slc2, &vm::o_mcallg,
|
||||
&vm::o_mcalll, &vm::o_mupval,
|
||||
&vm::o_mcallv, &vm::o_mcallh,
|
||||
&vm::o_ret
|
||||
};
|
||||
std::vector<nafunc> code;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(code[ctx.pc]) {
|
||||
(this->*code[ctx.pc])();
|
||||
if (ctx.top>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
}
|
||||
++ctx.pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
vmexit:
|
||||
if (verbose) {
|
||||
ngc.info();
|
||||
}
|
||||
imm.clear();
|
||||
if (!is_repl_mode) {
|
||||
ngc.clear();
|
||||
}
|
||||
return;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// may cause stackoverflow
|
||||
#define exec_check(op) {\
|
||||
op();\
|
||||
if (ctx.top<ctx.canary)\
|
||||
goto *code[++ctx.pc];\
|
||||
die("stack overflow");\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
// do not cause stackoverflow
|
||||
#define exec_nodie(op) {\
|
||||
op();\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
|
||||
repl: exec_nodie(o_repl ); // 0
|
||||
intl: exec_nodie(o_intl ); // -0
|
||||
loadg: exec_nodie(o_loadg ); // -1
|
||||
loadl: exec_nodie(o_loadl ); // -1
|
||||
loadu: exec_nodie(o_loadu ); // -1
|
||||
pnum: exec_check(o_pnum ); // +1
|
||||
pnil: exec_check(o_pnil ); // +1
|
||||
pstr: exec_check(o_pstr ); // +1
|
||||
newv: exec_check(o_newv ); // +1-imm[pc]
|
||||
newh: exec_check(o_newh ); // +1
|
||||
newf: exec_check(o_newf ); // +1
|
||||
happ: exec_nodie(o_happ ); // -1
|
||||
para: exec_nodie(o_para ); // -0
|
||||
deft: exec_nodie(o_deft ); // -1
|
||||
dyn: exec_nodie(o_dyn ); // -0
|
||||
lnot: exec_nodie(o_lnot ); // -0
|
||||
usub: exec_nodie(o_usub ); // -0
|
||||
bnot: exec_nodie(o_bnot ); // -0
|
||||
btor: exec_nodie(o_btor ); // -1
|
||||
btxor: exec_nodie(o_btxor ); // -1
|
||||
btand: exec_nodie(o_btand ); // -1
|
||||
add: exec_nodie(o_add ); // -1
|
||||
sub: exec_nodie(o_sub ); // -1
|
||||
mul: exec_nodie(o_mul ); // -1
|
||||
div: exec_nodie(o_div ); // -1
|
||||
lnk: exec_nodie(o_lnk ); // -1
|
||||
addc: exec_nodie(o_addc ); // -0
|
||||
subc: exec_nodie(o_subc ); // -0
|
||||
mulc: exec_nodie(o_mulc ); // -0
|
||||
divc: exec_nodie(o_divc ); // -0
|
||||
lnkc: exec_nodie(o_lnkc ); // -0
|
||||
addeq: exec_nodie(o_addeq ); // -1
|
||||
subeq: exec_nodie(o_subeq ); // -1
|
||||
muleq: exec_nodie(o_muleq ); // -1
|
||||
diveq: exec_nodie(o_diveq ); // -1
|
||||
lnkeq: exec_nodie(o_lnkeq ); // -1
|
||||
bandeq: exec_nodie(o_bandeq); // -1
|
||||
boreq: exec_nodie(o_boreq ); // -1
|
||||
bxoreq: exec_nodie(o_bxoreq); // -1
|
||||
addeqc: exec_nodie(o_addeqc); // -0
|
||||
subeqc: exec_nodie(o_subeqc); // -0
|
||||
muleqc: exec_nodie(o_muleqc); // -0
|
||||
diveqc: exec_nodie(o_diveqc); // -0
|
||||
lnkeqc: exec_nodie(o_lnkeqc); // -0
|
||||
addecp: exec_nodie(o_addecp); // -1
|
||||
subecp: exec_nodie(o_subecp); // -1
|
||||
mulecp: exec_nodie(o_mulecp); // -1
|
||||
divecp: exec_nodie(o_divecp); // -1
|
||||
lnkecp: exec_nodie(o_lnkecp); // -1
|
||||
meq: exec_nodie(o_meq ); // -1
|
||||
eq: exec_nodie(o_eq ); // -1
|
||||
neq: exec_nodie(o_neq ); // -1
|
||||
less: exec_nodie(o_less ); // -1
|
||||
leq: exec_nodie(o_leq ); // -1
|
||||
grt: exec_nodie(o_grt ); // -1
|
||||
geq: exec_nodie(o_geq ); // -1
|
||||
lessc: exec_nodie(o_lessc ); // -0
|
||||
leqc: exec_nodie(o_leqc ); // -0
|
||||
grtc: exec_nodie(o_grtc ); // -0
|
||||
geqc: exec_nodie(o_geqc ); // -0
|
||||
pop: exec_nodie(o_pop ); // -1
|
||||
jmp: exec_nodie(o_jmp ); // -0
|
||||
jt: exec_nodie(o_jt ); // -0
|
||||
jf: exec_nodie(o_jf ); // -1
|
||||
cnt: exec_nodie(o_cnt ); // -0
|
||||
findex: exec_check(o_findex); // +1
|
||||
feach: exec_check(o_feach ); // +1
|
||||
callg: exec_check(o_callg ); // +1
|
||||
calll: exec_check(o_calll ); // +1
|
||||
upval: exec_check(o_upval ); // +1
|
||||
callv: exec_nodie(o_callv ); // -0
|
||||
callvi: exec_nodie(o_callvi); // -0
|
||||
callh: exec_nodie(o_callh ); // -0
|
||||
callfv: exec_nodie(o_callfv); // check in the function
|
||||
callfh: exec_nodie(o_callfh); // check in the function
|
||||
callb: exec_nodie(o_callb ); // -0
|
||||
slcbeg: exec_check(o_slcbeg); // +1
|
||||
slcend: exec_nodie(o_slcend); // -1
|
||||
slc: exec_nodie(o_slc ); // -1
|
||||
slc2: exec_nodie(o_slc2 ); // -2
|
||||
mcallg: exec_check(o_mcallg); // +1
|
||||
mcalll: exec_check(o_mcalll); // +1
|
||||
mupval: exec_check(o_mupval); // +1
|
||||
mcallv: exec_nodie(o_mcallv); // -0
|
||||
mcallh: exec_nodie(o_mcallh); // -0
|
||||
ret: exec_nodie(o_ret ); // -2
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
991
src/nasal_vm.h
Normal file
@@ -0,0 +1,991 @@
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <stack>
|
||||
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_codegen.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class vm {
|
||||
protected:
|
||||
|
||||
/* registers of vm */
|
||||
context ctx;
|
||||
|
||||
/* constants */
|
||||
const f64* cnum = nullptr; // constant numbers
|
||||
const std::string* cstr = nullptr; // constant symbols and strings
|
||||
std::vector<u32> imm; // immediate number table
|
||||
std::vector<nasal_builtin_table> native;
|
||||
|
||||
/* garbage collector */
|
||||
gc ngc;
|
||||
|
||||
/* main stack */
|
||||
var* global = nullptr;
|
||||
usize global_size = 0;
|
||||
|
||||
/* values used for debugger */
|
||||
const std::string* files = nullptr; // file name list
|
||||
const opcode* bytecode = nullptr; // bytecode buffer address
|
||||
|
||||
/* variables for repl mode */
|
||||
bool is_repl_mode = false;
|
||||
bool first_exec_flag = true;
|
||||
bool allow_repl_output = false;
|
||||
|
||||
/* vm initializing function */
|
||||
void init(
|
||||
const std::vector<std::string>&,
|
||||
const std::vector<f64>&,
|
||||
const std::vector<nasal_builtin_table>&,
|
||||
const std::vector<opcode>&,
|
||||
const std::unordered_map<std::string, i32>&,
|
||||
const std::vector<std::string>&,
|
||||
const std::vector<std::string>&
|
||||
);
|
||||
void context_and_global_init();
|
||||
|
||||
/* debug functions */
|
||||
bool verbose = false;
|
||||
void valinfo(var&);
|
||||
void traceback();
|
||||
void stackinfo(const u32);
|
||||
void reginfo();
|
||||
void gstate();
|
||||
void lstate();
|
||||
void ustate();
|
||||
void detail();
|
||||
void die(const std::string&);
|
||||
|
||||
/* vm calculation functions*/
|
||||
inline bool cond(var&);
|
||||
|
||||
/* vm operands */
|
||||
inline void o_repl();
|
||||
inline void o_intl();
|
||||
inline void o_loadg();
|
||||
inline void o_loadl();
|
||||
inline void o_loadu();
|
||||
inline void o_pnum();
|
||||
inline void o_pnil();
|
||||
inline void o_pstr();
|
||||
inline void o_newv();
|
||||
inline void o_newh();
|
||||
inline void o_newf();
|
||||
inline void o_happ();
|
||||
inline void o_para();
|
||||
inline void o_deft();
|
||||
inline void o_dyn();
|
||||
inline void o_lnot();
|
||||
inline void o_usub();
|
||||
inline void o_bnot();
|
||||
inline void o_btor();
|
||||
inline void o_btxor();
|
||||
inline void o_btand();
|
||||
inline void o_add();
|
||||
inline void o_sub();
|
||||
inline void o_mul();
|
||||
inline void o_div();
|
||||
inline void o_lnk();
|
||||
inline void o_addc();
|
||||
inline void o_subc();
|
||||
inline void o_mulc();
|
||||
inline void o_divc();
|
||||
inline void o_lnkc();
|
||||
inline void o_addeq();
|
||||
inline void o_subeq();
|
||||
inline void o_muleq();
|
||||
inline void o_diveq();
|
||||
inline void o_lnkeq();
|
||||
inline void o_bandeq();
|
||||
inline void o_boreq();
|
||||
inline void o_bxoreq();
|
||||
inline void o_addeqc();
|
||||
inline void o_subeqc();
|
||||
inline void o_muleqc();
|
||||
inline void o_diveqc();
|
||||
inline void o_lnkeqc();
|
||||
inline void o_addecp();
|
||||
inline void o_subecp();
|
||||
inline void o_mulecp();
|
||||
inline void o_divecp();
|
||||
inline void o_lnkecp();
|
||||
inline void o_meq();
|
||||
inline void o_eq();
|
||||
inline void o_neq();
|
||||
inline void o_less();
|
||||
inline void o_leq();
|
||||
inline void o_grt();
|
||||
inline void o_geq();
|
||||
inline void o_lessc();
|
||||
inline void o_leqc();
|
||||
inline void o_grtc();
|
||||
inline void o_geqc();
|
||||
inline void o_pop();
|
||||
inline void o_jmp();
|
||||
inline void o_jt();
|
||||
inline void o_jf();
|
||||
inline void o_cnt();
|
||||
inline void o_findex();
|
||||
inline void o_feach();
|
||||
inline void o_callg();
|
||||
inline void o_calll();
|
||||
inline void o_upval();
|
||||
inline void o_callv();
|
||||
inline void o_callvi();
|
||||
inline void o_callh();
|
||||
inline void o_callfv();
|
||||
inline void o_callfh();
|
||||
inline void o_callb();
|
||||
inline void o_slcbeg();
|
||||
inline void o_slcend();
|
||||
inline void o_slc();
|
||||
inline void o_slc2();
|
||||
inline void o_mcallg();
|
||||
inline void o_mcalll();
|
||||
inline void o_mupval();
|
||||
inline void o_mcallv();
|
||||
inline void o_mcallh();
|
||||
inline void o_ret();
|
||||
|
||||
public:
|
||||
|
||||
/* constructor of vm instance */
|
||||
vm() {
|
||||
ctx.stack = new var[STACK_DEPTH];
|
||||
global = new var[STACK_DEPTH];
|
||||
}
|
||||
~vm() {
|
||||
delete[] ctx.stack;
|
||||
delete[] global;
|
||||
}
|
||||
|
||||
/* execution entry */
|
||||
void run(
|
||||
const codegen&,
|
||||
const linker&,
|
||||
const std::vector<std::string>&
|
||||
);
|
||||
|
||||
/* set detail report info flag */
|
||||
void set_detail_report_info(bool flag) {verbose = flag;}
|
||||
/* set repl mode flag */
|
||||
void set_repl_mode_flag(bool flag) {is_repl_mode = flag;}
|
||||
/* set repl output flag */
|
||||
void set_allow_repl_output_flag(bool flag) {allow_repl_output = flag;}
|
||||
};
|
||||
|
||||
inline bool vm::cond(var& val) {
|
||||
if (val.type==vm_num) {
|
||||
return val.num();
|
||||
} else if (val.type==vm_str) {
|
||||
const f64 num = str2num(val.str().c_str());
|
||||
return std::isnan(num)? !val.str().empty():num;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void vm::o_repl() {
|
||||
// reserved for repl mode stack top value output
|
||||
if (allow_repl_output) {
|
||||
std::cout << ctx.top[0] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_intl() {
|
||||
ctx.top[0].func().local.resize(imm[ctx.pc], nil);
|
||||
ctx.top[0].func().lsize = imm[ctx.pc];
|
||||
}
|
||||
|
||||
inline void vm::o_loadg() {
|
||||
global[imm[ctx.pc]] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_loadl() {
|
||||
ctx.localr[imm[ctx.pc]] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_loadu() {
|
||||
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_pnum() {
|
||||
(++ctx.top)[0] = var::num(cnum[imm[ctx.pc]]);
|
||||
}
|
||||
|
||||
inline void vm::o_pnil() {
|
||||
(++ctx.top)[0] = nil;
|
||||
}
|
||||
|
||||
inline void vm::o_pstr() {
|
||||
(++ctx.top)[0] = ngc.strs[imm[ctx.pc]];
|
||||
}
|
||||
|
||||
inline void vm::o_newv() {
|
||||
var newv = ngc.alloc(vm_vec);
|
||||
auto& vec = newv.vec().elems;
|
||||
vec.resize(imm[ctx.pc]);
|
||||
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
|
||||
ctx.top = ctx.top-imm[ctx.pc]+1;
|
||||
for(u32 i = 0; i<imm[ctx.pc]; ++i) {
|
||||
vec[i] = ctx.top[i];
|
||||
}
|
||||
ctx.top[0] = newv;
|
||||
}
|
||||
|
||||
inline void vm::o_newh() {
|
||||
(++ctx.top)[0] = ngc.alloc(vm_hash);
|
||||
}
|
||||
|
||||
inline void vm::o_newf() {
|
||||
(++ctx.top)[0] = ngc.alloc(vm_func);
|
||||
auto& func = ctx.top[0].func();
|
||||
func.entry = imm[ctx.pc];
|
||||
func.psize = 1;
|
||||
|
||||
/* this means you create a new function in local scope */
|
||||
if (ctx.localr) {
|
||||
func.upval = ctx.funcr.func().upval;
|
||||
// function created in the same local scope shares one closure
|
||||
// so this size & stk setting has no problem
|
||||
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
||||
upval.upval().size = ctx.funcr.func().lsize;
|
||||
upval.upval().stk = ctx.localr;
|
||||
func.upval.push_back(upval);
|
||||
ctx.upvalr = upval;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_happ() {
|
||||
ctx.top[-1].hash().elems[cstr[imm[ctx.pc]]] = ctx.top[0];
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_para() {
|
||||
auto& func = ctx.top[0].func();
|
||||
// func->size has 1 place reserved for "me"
|
||||
func.keys[imm[ctx.pc]] = func.psize;
|
||||
func.local[func.psize++] = var::none();
|
||||
}
|
||||
|
||||
inline void vm::o_deft() {
|
||||
var val = ctx.top[0];
|
||||
auto& func = (--ctx.top)[0].func();
|
||||
// func->size has 1 place reserved for "me"
|
||||
func.keys[imm[ctx.pc]] = func.psize;
|
||||
func.local[func.psize++] = val;
|
||||
}
|
||||
|
||||
inline void vm::o_dyn() {
|
||||
ctx.top[0].func().dpara = imm[ctx.pc];
|
||||
}
|
||||
|
||||
inline void vm::o_lnot() {
|
||||
var val = ctx.top[0];
|
||||
switch(val.type) {
|
||||
case vm_nil: ctx.top[0] = one; break;
|
||||
case vm_num: ctx.top[0] = val.num()? zero:one; break;
|
||||
case vm_str: {
|
||||
const f64 num = str2num(val.str().c_str());
|
||||
if (std::isnan(num)) {
|
||||
ctx.top[0] = var::num(static_cast<f64>(val.str().empty()));
|
||||
} else {
|
||||
ctx.top[0] = num? zero:one;
|
||||
}
|
||||
} break;
|
||||
default: die("incorrect value type"); return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_usub() {
|
||||
ctx.top[0] = var::num(-ctx.top[0].tonum());
|
||||
}
|
||||
|
||||
inline void vm::o_bnot() {
|
||||
ctx.top[0] = var::num(~static_cast<int32_t>(ctx.top[0].num()));
|
||||
}
|
||||
|
||||
inline void vm::o_btor() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].tonum())|
|
||||
static_cast<i32>(ctx.top[0].tonum())
|
||||
);
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_btxor() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].tonum())^
|
||||
static_cast<i32>(ctx.top[0].tonum())
|
||||
);
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_btand() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].tonum())&
|
||||
static_cast<i32>(ctx.top[0].tonum())
|
||||
);
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
#define op_calc(type)\
|
||||
ctx.top[-1] = var::num(ctx.top[-1].tonum() type ctx.top[0].tonum());\
|
||||
--ctx.top;
|
||||
|
||||
inline void vm::o_add() {op_calc(+);}
|
||||
inline void vm::o_sub() {op_calc(-);}
|
||||
inline void vm::o_mul() {op_calc(*);}
|
||||
inline void vm::o_div() {op_calc(/);}
|
||||
inline void vm::o_lnk() {
|
||||
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
|
||||
ngc.temp = ngc.alloc(vm_vec);
|
||||
for(auto i : ctx.top[-1].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
for(auto i : ctx.top[0].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
ctx.top[-1] = ngc.temp;
|
||||
ngc.temp = nil;
|
||||
--ctx.top;
|
||||
return;
|
||||
}
|
||||
ctx.top[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
#define op_calc_const(type)\
|
||||
ctx.top[0] = var::num(ctx.top[0].tonum() type cnum[imm[ctx.pc]]);
|
||||
|
||||
inline void vm::o_addc() {op_calc_const(+);}
|
||||
inline void vm::o_subc() {op_calc_const(-);}
|
||||
inline void vm::o_mulc() {op_calc_const(*);}
|
||||
inline void vm::o_divc() {op_calc_const(/);}
|
||||
inline void vm::o_lnkc() {
|
||||
ctx.top[0] = ngc.newstr(ctx.top[0].tostr()+cstr[imm[ctx.pc]]);
|
||||
}
|
||||
|
||||
// top[0] stores the value of memr[0], to avoid being garbage-collected
|
||||
// so when the calculation ends, top-=1, then top-=imm[pc]
|
||||
// because this return value is meaningless if on stack when imm[pc] = 1
|
||||
// like this: func{a+=c;}(); the result of 'a+c' will no be used later, imm[pc] = 1
|
||||
// but if b+=a+=c; the result of 'a+c' will be used later, imm[pc] = 0
|
||||
#define op_calc_eq(type)\
|
||||
ctx.top[-1] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type ctx.top[-1].tonum());\
|
||||
ctx.memr = nullptr;\
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
|
||||
inline void vm::o_addeq() {op_calc_eq(+);}
|
||||
inline void vm::o_subeq() {op_calc_eq(-);}
|
||||
inline void vm::o_muleq() {op_calc_eq(*);}
|
||||
inline void vm::o_diveq() {op_calc_eq(/);}
|
||||
inline void vm::o_lnkeq() {
|
||||
ctx.top[-1] = ctx.memr[0] = ngc.newstr(
|
||||
ctx.memr[0].tostr()+ctx.top[-1].tostr()
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
}
|
||||
|
||||
inline void vm::o_bandeq() {
|
||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||
static_cast<i32>(ctx.memr[0].tonum())&
|
||||
static_cast<i32>(ctx.top[-1].tonum())
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
}
|
||||
|
||||
inline void vm::o_boreq() {
|
||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||
static_cast<i32>(ctx.memr[0].tonum())|
|
||||
static_cast<i32>(ctx.top[-1].tonum())
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
}
|
||||
|
||||
inline void vm::o_bxoreq() {
|
||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||
static_cast<i32>(ctx.memr[0].tonum())^
|
||||
static_cast<i32>(ctx.top[-1].tonum())
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
}
|
||||
|
||||
// top[0] stores the value of memr[0], to avoid being garbage-collected
|
||||
// so when the calculation ends, top-=imm[pc]>>31
|
||||
// because this return value is meaningless if on stack when imm[pc]>>31=1
|
||||
// like this: func{a+=1;}(); the result of 'a+1' will no be used later, imm[pc]>>31=1
|
||||
// but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0
|
||||
#define op_calc_eq_const(type)\
|
||||
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
|
||||
ctx.memr = nullptr;
|
||||
|
||||
inline void vm::o_addeqc() {op_calc_eq_const(+);}
|
||||
inline void vm::o_subeqc() {op_calc_eq_const(-);}
|
||||
inline void vm::o_muleqc() {op_calc_eq_const(*);}
|
||||
inline void vm::o_diveqc() {op_calc_eq_const(/);}
|
||||
inline void vm::o_lnkeqc() {
|
||||
ctx.top[0] = ctx.memr[0] = ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]);
|
||||
ctx.memr = nullptr;
|
||||
}
|
||||
|
||||
#define op_calc_eq_const_and_pop(type)\
|
||||
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
|
||||
ctx.memr = nullptr;\
|
||||
--ctx.top;
|
||||
|
||||
inline void vm::o_addecp() {op_calc_eq_const_and_pop(+);}
|
||||
inline void vm::o_subecp() {op_calc_eq_const_and_pop(-);}
|
||||
inline void vm::o_mulecp() {op_calc_eq_const_and_pop(*);}
|
||||
inline void vm::o_divecp() {op_calc_eq_const_and_pop(/);}
|
||||
inline void vm::o_lnkecp() {
|
||||
ctx.top[0] = ctx.memr[0] = ngc.newstr(
|
||||
ctx.memr[0].tostr()+cstr[imm[ctx.pc]]
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_meq() {
|
||||
// pop old memr[0] and replace it
|
||||
// the reason why we should get memr and push the old value on stack
|
||||
// is that when lnkeq/lnkeqc is called, there will be
|
||||
// a new gc object vm_str which is returned by gc::alloc
|
||||
// this may cause gc, so we should temporarily put it on stack
|
||||
ctx.memr[0] = ctx.top[-1];
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
}
|
||||
|
||||
inline void vm::o_eq() {
|
||||
var val2 = ctx.top[0];
|
||||
var val1 = (--ctx.top)[0];
|
||||
if (val1.type==vm_nil && val2.type==vm_nil) {
|
||||
ctx.top[0] = one;
|
||||
} else if (val1.type==vm_str && val2.type==vm_str) {
|
||||
ctx.top[0] = (val1.str()==val2.str())? one:zero;
|
||||
} else if ((val1.type==vm_num || val2.type==vm_num)
|
||||
&& val1.type!=vm_nil && val2.type!=vm_nil) {
|
||||
ctx.top[0] = (val1.tonum()==val2.tonum())? one:zero;
|
||||
} else {
|
||||
ctx.top[0] = (val1==val2)? one:zero;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_neq() {
|
||||
var val2 = ctx.top[0];
|
||||
var val1 = (--ctx.top)[0];
|
||||
if (val1.type==vm_nil && val2.type==vm_nil) {
|
||||
ctx.top[0] = zero;
|
||||
} else if (val1.type==vm_str && val2.type==vm_str) {
|
||||
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
|
||||
} else if ((val1.type==vm_num || val2.type==vm_num)
|
||||
&& val1.type!=vm_nil && val2.type!=vm_nil) {
|
||||
ctx.top[0] = (val1.tonum()!=val2.tonum())? one:zero;
|
||||
} else {
|
||||
ctx.top[0] = (val1!=val2)? one:zero;
|
||||
}
|
||||
}
|
||||
|
||||
#define op_cmp(type)\
|
||||
--ctx.top;\
|
||||
ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero;
|
||||
|
||||
inline void vm::o_less() {op_cmp(<);}
|
||||
inline void vm::o_leq() {op_cmp(<=);}
|
||||
inline void vm::o_grt() {op_cmp(>);}
|
||||
inline void vm::o_geq() {op_cmp(>=);}
|
||||
|
||||
#define op_cmp_const(type)\
|
||||
ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero;
|
||||
|
||||
inline void vm::o_lessc() {op_cmp_const(<);}
|
||||
inline void vm::o_leqc() {op_cmp_const(<=);}
|
||||
inline void vm::o_grtc() {op_cmp_const(>);}
|
||||
inline void vm::o_geqc() {op_cmp_const(>=);}
|
||||
|
||||
inline void vm::o_pop() {
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_jmp() {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
}
|
||||
|
||||
inline void vm::o_jt() {
|
||||
// jump true needs to reserve the result on stack
|
||||
// because conditional expression in nasal has return value
|
||||
if (cond(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_jf() {
|
||||
// jump false doesn't need to reserve result
|
||||
if (!cond(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
}
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_cnt() {
|
||||
if (ctx.top[0].type!=vm_vec) {
|
||||
die("must use vector in forindex/foreach");
|
||||
return;
|
||||
}
|
||||
(++ctx.top)[0] = var::cnt(-1);
|
||||
}
|
||||
|
||||
inline void vm::o_findex() {
|
||||
if ((usize)(++ctx.top[0].cnt())>=ctx.top[-1].vec().size()) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
return;
|
||||
}
|
||||
ctx.top[1] = var::num(ctx.top[0].cnt());
|
||||
++ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_feach() {
|
||||
auto& ref = ctx.top[-1].vec().elems;
|
||||
if ((usize)(++ctx.top[0].cnt())>=ref.size()) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
return;
|
||||
}
|
||||
ctx.top[1] = ref[ctx.top[0].cnt()];
|
||||
++ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_callg() {
|
||||
// get main stack directly
|
||||
(++ctx.top)[0] = global[imm[ctx.pc]];
|
||||
}
|
||||
|
||||
inline void vm::o_calll() {
|
||||
(++ctx.top)[0] = ctx.localr[imm[ctx.pc]];
|
||||
}
|
||||
|
||||
inline void vm::o_upval() {
|
||||
(++ctx.top)[0] = ctx.funcr.func()
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff];
|
||||
}
|
||||
|
||||
inline void vm::o_callv() {
|
||||
var val = ctx.top[0];
|
||||
var vec = (--ctx.top)[0];
|
||||
if (vec.type==vm_vec) {
|
||||
ctx.top[0] = vec.vec().get_val(val.tonum());
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
die("out of range:"+std::to_string(val.tonum()));
|
||||
return;
|
||||
}
|
||||
} else if (vec.type==vm_hash) {
|
||||
if (val.type!=vm_str) {
|
||||
die("must use string as the key");
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = vec.hash().get_val(val.str());
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
die("cannot find member \""+val.str()+"\"");
|
||||
return;
|
||||
} else if (ctx.top[0].type==vm_func) {
|
||||
ctx.top[0].func().local[0] = val; // 'me'
|
||||
}
|
||||
} else if (vec.type==vm_str) {
|
||||
auto& str = vec.str();
|
||||
i32 num = val.tonum();
|
||||
i32 len = str.length();
|
||||
if (num<-len || num>=len) {
|
||||
die("out of range:"+std::to_string(val.tonum()));
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = var::num(
|
||||
static_cast<f64>(static_cast<u8>(str[num>=0? num:num+len]))
|
||||
);
|
||||
} else if (vec.type==vm_map) {
|
||||
if (val.type!=vm_str) {
|
||||
die("must use string as the key");
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = vec.map().get_val(val.str());
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
die("cannot find symbol \""+val.str()+"\"");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
die("must call a vector/hash/string");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_callvi() {
|
||||
var val = ctx.top[0];
|
||||
if (val.type!=vm_vec) {
|
||||
die("must use a vector");
|
||||
return;
|
||||
}
|
||||
// cannot use operator[],because this may cause overflow
|
||||
(++ctx.top)[0] = val.vec().get_val(imm[ctx.pc]);
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
die("out of range:"+std::to_string(imm[ctx.pc]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_callh() {
|
||||
var val = ctx.top[0];
|
||||
if (val.type!=vm_hash && val.type!=vm_map) {
|
||||
die("must call a hash");
|
||||
return;
|
||||
}
|
||||
const auto& str = cstr[imm[ctx.pc]];
|
||||
if (val.type==vm_hash) {
|
||||
ctx.top[0] = val.hash().get_val(str);
|
||||
} else {
|
||||
ctx.top[0] = val.map().get_val(str);
|
||||
}
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
val.type==vm_hash?
|
||||
die("member \"" + str + "\" does not exist"):
|
||||
die("cannot find symbol \"" + str + "\"");
|
||||
return;
|
||||
} else if (ctx.top[0].type==vm_func) {
|
||||
ctx.top[0].func().local[0] = val; // 'me'
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_callfv() {
|
||||
u32 argc = imm[ctx.pc]; // arguments counter
|
||||
var* local = ctx.top-argc+1; // arguments begin address
|
||||
if (local[-1].type!=vm_func) {
|
||||
die("must call a function");
|
||||
return;
|
||||
}
|
||||
auto& func = local[-1].func();
|
||||
|
||||
// swap funcr with local[-1]
|
||||
var tmp = local[-1];
|
||||
local[-1] = ctx.funcr;
|
||||
ctx.funcr = tmp;
|
||||
|
||||
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
||||
if (ctx.top-argc+func.lsize+3>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
return;
|
||||
}
|
||||
// parameter size is func->psize-1, 1 is reserved for "me"
|
||||
u32 psize = func.psize-1;
|
||||
if (argc<psize && func.local[argc+1].type==vm_none) {
|
||||
die("lack argument(s)");
|
||||
return;
|
||||
}
|
||||
|
||||
var dynamic = nil;
|
||||
if (func.dpara>=0) { // load dynamic arguments
|
||||
dynamic = ngc.alloc(vm_vec);
|
||||
for(u32 i = psize; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
} else if (psize<argc) {
|
||||
// load arguments to "arg", located at stack+1
|
||||
dynamic = ngc.alloc(vm_vec);
|
||||
for(u32 i = psize; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
}
|
||||
// should reset stack top after allocating vector
|
||||
// because this may cause gc
|
||||
// then all the available values the vector needs
|
||||
// are all outside the stack top and may be
|
||||
// collected incorrectly
|
||||
ctx.top = local+func.lsize;
|
||||
|
||||
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC
|
||||
for(u32 i = min_size; i>=1; --i) { // load arguments
|
||||
local[i] = local[i-1];
|
||||
}
|
||||
local[0] = func.local[0];// load "me"
|
||||
|
||||
// load local scope & default arguments
|
||||
for(u32 i = min_size+1; i<func.lsize; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
(++ctx.top)[0] = var::ret(ctx.pc);
|
||||
ctx.pc = func.entry-1;
|
||||
ctx.localr = local;
|
||||
ctx.upvalr = nil;
|
||||
}
|
||||
|
||||
inline void vm::o_callfh() {
|
||||
auto& hash = ctx.top[0].hash().elems;
|
||||
if (ctx.top[-1].type!=vm_func) {
|
||||
die("must call a function");
|
||||
return;
|
||||
}
|
||||
auto& func = ctx.top[-1].func();
|
||||
var tmp = ctx.top[-1];
|
||||
ctx.top[-1] = ctx.funcr;
|
||||
ctx.funcr = tmp;
|
||||
|
||||
// top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
||||
if (ctx.top+func.lsize+2>= ctx.canary) {
|
||||
die("stack overflow");
|
||||
return;
|
||||
}
|
||||
if (func.dpara>=0) {
|
||||
die("special call cannot use dynamic argument");
|
||||
return;
|
||||
}
|
||||
|
||||
var* local = ctx.top;
|
||||
ctx.top += func.lsize;
|
||||
for(u32 i = 0; i<func.lsize; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
|
||||
for(const auto& i : func.keys) {
|
||||
auto& key = cstr[i.first];
|
||||
if (hash.count(key)) {
|
||||
local[i.second] = hash[key];
|
||||
} else if (local[i.second].type==vm_none) {
|
||||
die("lack argument(s): \""+key+"\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
(++ctx.top)[0] = var::ret(ctx.pc); // rewrite top with vm_ret
|
||||
ctx.pc=func.entry-1;
|
||||
ctx.localr = local;
|
||||
ctx.upvalr = nil;
|
||||
}
|
||||
|
||||
inline void vm::o_callb() {
|
||||
// reserve place for native function return,
|
||||
// this code is written for coroutine
|
||||
(++ctx.top)[0] = nil;
|
||||
|
||||
// if running a native function about coroutine
|
||||
// (top) will be set to another context.top, instead of main_context.top
|
||||
var tmp = (*native[imm[ctx.pc]].func)(ctx.localr, ngc);
|
||||
|
||||
// so we use tmp variable to store this return value
|
||||
// and set it to top[0] later
|
||||
ctx.top[0] = tmp;
|
||||
|
||||
// if get none, this means errors occurred when calling this native function
|
||||
if (ctx.top[0].type==vm_none) {
|
||||
die("error occurred in native function");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_slcbeg() {
|
||||
// +--------------+
|
||||
// | slice_vector | <-- top[0]
|
||||
// +--------------+
|
||||
// | resource_vec | <-- top[-1]
|
||||
// +--------------+
|
||||
(++ctx.top)[0] = ngc.alloc(vm_vec);
|
||||
if (ctx.top[-1].type!=vm_vec) {
|
||||
die("must slice a vector");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_slcend() {
|
||||
ctx.top[-1] = ctx.top[0];
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_slc() {
|
||||
var val = (ctx.top--)[0];
|
||||
var res = ctx.top[-1].vec().get_val(val.tonum());
|
||||
if (res.type==vm_none) {
|
||||
die("index " + std::to_string(val.tonum()) + " out of range");
|
||||
return;
|
||||
}
|
||||
ctx.top[0].vec().elems.push_back(res);
|
||||
}
|
||||
|
||||
inline void vm::o_slc2() {
|
||||
var val2 = (ctx.top--)[0];
|
||||
var val1 = (ctx.top--)[0];
|
||||
auto& ref = ctx.top[-1].vec().elems;
|
||||
auto& aim = ctx.top[0].vec().elems;
|
||||
|
||||
u8 type1 = val1.type,type2=val2.type;
|
||||
i32 num1 = val1.tonum();
|
||||
i32 num2 = val2.tonum();
|
||||
i32 size = ref.size();
|
||||
if (type1==vm_nil && type2==vm_nil) {
|
||||
num1 = 0;
|
||||
num2 = size-1;
|
||||
} else if (type1==vm_nil && type2!=vm_nil) {
|
||||
num1 = num2<0? -size:0;
|
||||
} else if (type1!=vm_nil && type2==vm_nil) {
|
||||
num2 = num1<0? -1:size-1;
|
||||
}
|
||||
|
||||
if (num1<-size || num1>=size || num2<-size || num2>=size) {
|
||||
die("index " + std::to_string(num1) + ":" +
|
||||
std::to_string(num2) + " out of range");
|
||||
return;
|
||||
} else if (num1<=num2) {
|
||||
for(i32 i = num1; i<=num2; ++i) {
|
||||
aim.push_back(i>=0? ref[i]:ref[i+size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_mcallg() {
|
||||
ctx.memr = global+imm[ctx.pc];
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
|
||||
inline void vm::o_mcalll() {
|
||||
ctx.memr = ctx.localr+imm[ctx.pc];
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
|
||||
inline void vm::o_mupval() {
|
||||
ctx.memr = &(
|
||||
ctx.funcr.func()
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff]);
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
}
|
||||
|
||||
inline void vm::o_mcallv() {
|
||||
var val = ctx.top[0]; // index
|
||||
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
|
||||
if (vec.type==vm_vec) {
|
||||
ctx.memr = vec.vec().get_mem(val.tonum());
|
||||
if (!ctx.memr) {
|
||||
die("index "+std::to_string(val.tonum())+" out of range");
|
||||
return;
|
||||
}
|
||||
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
|
||||
if (val.type!=vm_str) {
|
||||
die("key must be string");
|
||||
return;
|
||||
}
|
||||
auto& ref = vec.hash();
|
||||
auto& str = val.str();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
ref.elems[str] = nil;
|
||||
ctx.memr = ref.get_mem(str);
|
||||
}
|
||||
} else if (vec.type==vm_map) {
|
||||
if (val.type!=vm_str) {
|
||||
die("key must be string");
|
||||
return;
|
||||
}
|
||||
auto& ref = vec.map();
|
||||
auto& str = val.str();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
die("cannot find symbol \"" + str + "\"");
|
||||
}
|
||||
} else {
|
||||
die("cannot get memory space in this type");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_mcallh() {
|
||||
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
|
||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||
die("must call a hash");
|
||||
return;
|
||||
}
|
||||
auto& str = cstr[imm[ctx.pc]];
|
||||
if (hash.type==vm_map) {
|
||||
ctx.memr = hash.map().get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
die("cannot find symbol \"" + str + "\"");
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto& ref = hash.hash();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) { // create a new key
|
||||
ref.elems[str] = nil;
|
||||
ctx.memr = ref.get_mem(str);
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_ret() {
|
||||
/* +-------------+
|
||||
* | return value| <- top[0]
|
||||
* +-------------+
|
||||
* | old pc | <- top[-1]
|
||||
* +-------------+
|
||||
* | old localr | <- top[-2]
|
||||
* +-------------+
|
||||
* | old upvalr | <- top[-3]
|
||||
* +-------------+
|
||||
* | local scope |
|
||||
* +-------------+ <- local pointer stored in localr
|
||||
* | old funcr | <- old function stored in funcr
|
||||
* +-------------+
|
||||
*/
|
||||
var ret = ctx.top[0];
|
||||
var* local = ctx.localr;
|
||||
var func = ctx.funcr;
|
||||
var up = ctx.upvalr;
|
||||
|
||||
ctx.pc = ctx.top[-1].ret();
|
||||
ctx.localr = ctx.top[-2].addr();
|
||||
ctx.upvalr = ctx.top[-3];
|
||||
|
||||
ctx.top = local-1;
|
||||
ctx.funcr = ctx.top[0];
|
||||
ctx.top[0] = ret; // rewrite func with returned value
|
||||
|
||||
if (up.type==vm_upval) { // synchronize upvalue
|
||||
auto& upval = up.upval();
|
||||
auto size = func.func().lsize;
|
||||
upval.onstk = false;
|
||||
upval.elems.resize(size);
|
||||
for(u32 i = 0; i<size; ++i) {
|
||||
upval.elems[i] = local[i];
|
||||
}
|
||||
}
|
||||
|
||||
// cannot use gc.cort to judge,
|
||||
// because there maybe another function call inside but return here
|
||||
// coroutine function ends with setting pc to 0
|
||||
if (!ctx.pc) {
|
||||
ngc.ctxreserve();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
139
src/optimizer.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "optimizer.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void optimizer::const_string(
|
||||
binary_operator* node,
|
||||
string_literal* left_node,
|
||||
string_literal* right_node) {
|
||||
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
|
||||
return;
|
||||
}
|
||||
const auto& left = left_node->get_content();
|
||||
const auto& right = right_node->get_content();
|
||||
node->set_optimized_string(
|
||||
new string_literal(node->get_location(), left+right));
|
||||
}
|
||||
|
||||
void optimizer::const_number(
|
||||
binary_operator* node,
|
||||
number_literal* left_node,
|
||||
number_literal* right_node) {
|
||||
const auto left = left_node->get_number();
|
||||
const auto right = right_node->get_number();
|
||||
f64 res;
|
||||
switch(node->get_operator_type()) {
|
||||
case binary_operator::binary_type::add: res = left+right; break;
|
||||
case binary_operator::binary_type::sub: res = left-right; break;
|
||||
case binary_operator::binary_type::mult: res = left*right; break;
|
||||
case binary_operator::binary_type::div: res = left/right; break;
|
||||
case binary_operator::binary_type::less: res = left<right; break;
|
||||
case binary_operator::binary_type::leq: res = left<=right; break;
|
||||
case binary_operator::binary_type::grt: res = left>right; break;
|
||||
case binary_operator::binary_type::geq: res = left>=right; break;
|
||||
case binary_operator::binary_type::bitwise_or:
|
||||
res = static_cast<i32>(left)|static_cast<i32>(right); break;
|
||||
case binary_operator::binary_type::bitwise_xor:
|
||||
res = static_cast<i32>(left)^static_cast<i32>(right); break;
|
||||
case binary_operator::binary_type::bitwise_and:
|
||||
res = static_cast<i32>(left)&static_cast<i32>(right); break;
|
||||
default: return;
|
||||
}
|
||||
if (std::isinf(res) || std::isnan(res)) {
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res));
|
||||
}
|
||||
|
||||
void optimizer::const_number(
|
||||
unary_operator* node,
|
||||
number_literal* value_node) {
|
||||
auto res = value_node->get_number();
|
||||
switch(node->get_operator_type()) {
|
||||
case unary_operator::unary_type::negative:
|
||||
res = -res; break;
|
||||
case unary_operator::unary_type::bitwise_not:
|
||||
res = ~static_cast<i32>(res); break;
|
||||
case unary_operator::unary_type::logical_not:
|
||||
res = !res; break;
|
||||
}
|
||||
if (std::isinf(res) || std::isnan(res)) {
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res));
|
||||
}
|
||||
|
||||
bool optimizer::visit_binary_operator(binary_operator* node) {
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
number_literal* left_num_node = nullptr;
|
||||
number_literal* right_num_node = nullptr;
|
||||
string_literal* left_str_node = nullptr;
|
||||
string_literal* right_str_node = nullptr;
|
||||
if (node->get_left()->get_type()==expr_type::ast_num) {
|
||||
left_num_node = (number_literal*)node->get_left();
|
||||
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
|
||||
((binary_operator*)node->get_left())->get_optimized_number()) {
|
||||
left_num_node = ((binary_operator*)node->get_left())->get_optimized_number();
|
||||
} else if (node->get_left()->get_type()==expr_type::ast_unary &&
|
||||
((unary_operator*)node->get_left())->get_optimized_number()) {
|
||||
left_num_node = ((unary_operator*)node->get_left())->get_optimized_number();
|
||||
}
|
||||
if (node->get_right()->get_type()==expr_type::ast_num) {
|
||||
right_num_node = (number_literal*)node->get_right();
|
||||
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
|
||||
((binary_operator*)node->get_right())->get_optimized_number()) {
|
||||
right_num_node = ((binary_operator*)node->get_right())->get_optimized_number();
|
||||
} else if (node->get_right()->get_type()==expr_type::ast_unary &&
|
||||
((unary_operator*)node->get_right())->get_optimized_number()) {
|
||||
right_num_node = ((unary_operator*)node->get_right())->get_optimized_number();
|
||||
}
|
||||
|
||||
if (node->get_left()->get_type()==expr_type::ast_str) {
|
||||
left_str_node = (string_literal*)node->get_left();
|
||||
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
|
||||
((binary_operator*)node->get_left())->get_optimized_string()) {
|
||||
left_str_node = ((binary_operator*)node->get_left())->get_optimized_string();
|
||||
}
|
||||
if (node->get_right()->get_type()==expr_type::ast_str) {
|
||||
right_str_node = (string_literal*)node->get_right();
|
||||
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
|
||||
((binary_operator*)node->get_right())->get_optimized_string()) {
|
||||
right_str_node = ((binary_operator*)node->get_right())->get_optimized_string();
|
||||
}
|
||||
if (left_num_node && right_num_node) {
|
||||
const_number(node, left_num_node, right_num_node);
|
||||
return true;
|
||||
}
|
||||
if (left_str_node && right_str_node) {
|
||||
const_string(node, left_str_node, right_str_node);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optimizer::visit_unary_operator(unary_operator* node) {
|
||||
node->get_value()->accept(this);
|
||||
number_literal* value_node = nullptr;
|
||||
if (node->get_value()->get_type()==expr_type::ast_num) {
|
||||
value_node = (number_literal*)node->get_value();
|
||||
} else if (node->get_value()->get_type()==expr_type::ast_binary &&
|
||||
((binary_operator*)node->get_value())->get_optimized_number()) {
|
||||
value_node = ((binary_operator*)node->get_value())->get_optimized_number();
|
||||
} else if (node->get_value()->get_type()==expr_type::ast_unary &&
|
||||
((unary_operator*)node->get_value())->get_optimized_number()) {
|
||||
value_node = ((unary_operator*)node->get_value())->get_optimized_number();
|
||||
}
|
||||
if (value_node) {
|
||||
const_number(node, value_node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void optimizer::do_optimization(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
|
||||
}
|
||||
24
src/optimizer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class optimizer:public ast_visitor {
|
||||
private:
|
||||
void const_string(binary_operator*, string_literal*, string_literal*);
|
||||
void const_number(binary_operator*, number_literal*, number_literal*);
|
||||
void const_number(unary_operator*, number_literal*);
|
||||
|
||||
public:
|
||||
bool visit_binary_operator(binary_operator*) override;
|
||||
bool visit_unary_operator(unary_operator*) override;
|
||||
|
||||
public:
|
||||
void do_optimization(code_block*);
|
||||
};
|
||||
|
||||
}
|
||||
167
src/repl.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "repl.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
namespace repl {
|
||||
|
||||
void repl::add_command_history(const std::string& history) {
|
||||
if (command_history.size() && command_history.back()==history) {
|
||||
return;
|
||||
}
|
||||
command_history.push_back(history);
|
||||
if (command_history.size()>1000) {
|
||||
command_history.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
std::string repl::readline(std::string prompt = ">>> ") {
|
||||
auto line = std::string("");
|
||||
std::cout << prompt;
|
||||
std::getline(std::cin, line,'\n');
|
||||
return line;
|
||||
}
|
||||
|
||||
void repl::update_temp_file() {
|
||||
auto content = std::string("");
|
||||
for(const auto& i : source) {
|
||||
content += i + "\n";
|
||||
}
|
||||
info::instance()->repl_file_source = content + " ";
|
||||
}
|
||||
|
||||
bool repl::check_need_more_input() {
|
||||
while(true) {
|
||||
update_temp_file();
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 in_curve = 0;
|
||||
i64 in_bracket = 0;
|
||||
i64 in_brace = 0;
|
||||
for(const auto& t : nasal_lexer->result()) {
|
||||
switch(t.type) {
|
||||
case tok::lcurve: ++in_curve; break;
|
||||
case tok::rcurve: --in_curve; break;
|
||||
case tok::lbracket: ++in_bracket; break;
|
||||
case tok::rbracket: --in_bracket; break;
|
||||
case tok::lbrace: ++in_brace; break;
|
||||
case tok::rbrace: --in_brace; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (in_curve<=0 && in_bracket<=0 && in_brace<=0) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto line = readline("... ");
|
||||
add_command_history(line);
|
||||
source.back() += "\n" + line;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::help() {
|
||||
std::cout << ".h, .help | show help\n";
|
||||
std::cout << ".e, .exit | quit the REPL\n";
|
||||
std::cout << ".q, .quit | quit the REPL\n";
|
||||
std::cout << ".c, .clear | clear the screen\n";
|
||||
std::cout << ".s, .source | show source code\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
bool repl::run() {
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
auto nasal_parser = std::unique_ptr<parse>(new parse);
|
||||
auto nasal_linker = std::unique_ptr<linker>(new linker);
|
||||
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer);
|
||||
auto nasal_codegen = std::unique_ptr<codegen>(new codegen);
|
||||
|
||||
update_temp_file();
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_parser->compile(*nasal_lexer).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nasal_opt->do_optimization(nasal_parser->tree());
|
||||
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
runtime.run(*nasal_codegen, *nasal_linker, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::execute() {
|
||||
source = {};
|
||||
// mark we are in repl mode
|
||||
info::instance()->in_repl_mode = true;
|
||||
std::cout << "[nasal-repl] Initializating enviroment...\n";
|
||||
// run on pass for initializing basic modules, without output
|
||||
run();
|
||||
// allow output now
|
||||
runtime.set_allow_repl_output_flag(true);
|
||||
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
||||
|
||||
// finish initialization, output version info
|
||||
std::cout << "Nasal REPL interpreter version " << __nasver;
|
||||
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
help();
|
||||
|
||||
while(true) {
|
||||
auto line = readline();
|
||||
if (!line.length()) {
|
||||
continue;
|
||||
}
|
||||
add_command_history(line);
|
||||
|
||||
if (line == ".e" || line == ".exit") {
|
||||
break;
|
||||
} else if (line == ".q" || line == ".quit") {
|
||||
break;
|
||||
} else if (line == ".h" || line == ".help") {
|
||||
help();
|
||||
continue;
|
||||
} else if (line == ".c" || line == ".clear") {
|
||||
std::cout << "\033c";
|
||||
continue;
|
||||
} else if (line == ".s" || line == ".source") {
|
||||
update_temp_file();
|
||||
std::cout << info::instance()->repl_file_source << "\n";
|
||||
continue;
|
||||
} else if (line[0] == "."[0]) {
|
||||
std::cout << "no such command \"" << line;
|
||||
std::cout << "\", input \".help\" for help\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
source.push_back(line);
|
||||
if (!check_need_more_input()) {
|
||||
source.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// run program
|
||||
if (!run()) {
|
||||
source.pop_back();
|
||||
}
|
||||
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
55
src/repl.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <deque>
|
||||
|
||||
namespace nasal {
|
||||
namespace repl {
|
||||
|
||||
struct info {
|
||||
bool in_repl_mode = false;
|
||||
std::string repl_file_name = "<nasal-repl>";
|
||||
std::string repl_file_source = "";
|
||||
|
||||
// singleton
|
||||
static info* instance() {
|
||||
static info info;
|
||||
return &info;
|
||||
}
|
||||
};
|
||||
|
||||
class repl {
|
||||
private:
|
||||
std::vector<std::string> source;
|
||||
std::deque<std::string> command_history;
|
||||
vm runtime;
|
||||
|
||||
private:
|
||||
void add_command_history(const std::string&);
|
||||
std::string readline(std::string);
|
||||
bool check_need_more_input();
|
||||
void update_temp_file();
|
||||
void help();
|
||||
bool run();
|
||||
|
||||
public:
|
||||
repl() {
|
||||
// set repl mode
|
||||
runtime.set_repl_mode_flag(true);
|
||||
// no detail report info
|
||||
runtime.set_detail_report_info(false);
|
||||
// set empty history
|
||||
command_history = {""};
|
||||
}
|
||||
void execute();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
47
src/symbol_finder.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "symbol_finder.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool symbol_finder::visit_definition_expr(definition_expr* node) {
|
||||
if (node->get_variable_name()) {
|
||||
symbols.push_back({
|
||||
node->get_variable_name()->get_name(),
|
||||
node->get_variable_name()->get_location()
|
||||
});
|
||||
} else {
|
||||
for(auto i : node->get_variables()->get_variables()) {
|
||||
symbols.push_back({
|
||||
i->get_name(),
|
||||
i->get_location()
|
||||
});
|
||||
}
|
||||
}
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool symbol_finder::visit_function(function* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool symbol_finder::visit_iter_expr(iter_expr* node) {
|
||||
if (node->get_name()) {
|
||||
symbols.push_back({
|
||||
node->get_name()->get_name(),
|
||||
node->get_name()->get_location()
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<symbol_finder::symbol_info>& symbol_finder::do_find(code_block* root) {
|
||||
symbols.clear();
|
||||
root->accept(this);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/symbol_finder.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class symbol_finder:public ast_visitor {
|
||||
public:
|
||||
struct symbol_info {
|
||||
std::string name;
|
||||
span location;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<symbol_info> symbols;
|
||||
|
||||
public:
|
||||
bool visit_definition_expr(definition_expr*) override;
|
||||
bool visit_function(function*) override;
|
||||
bool visit_iter_expr(iter_expr*) override;
|
||||
const std::vector<symbol_finder::symbol_info>& do_find(code_block*);
|
||||
};
|
||||
|
||||
}
|
||||
155
src/unix_lib.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "unix_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto dir_type_name = "dir";
|
||||
|
||||
void dir_entry_destructor(void* ptr) {
|
||||
#ifndef _MSC_VER
|
||||
closedir(static_cast<DIR*>(ptr));
|
||||
#else
|
||||
FindClose(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_pipe(var* local, gc& ngc) {
|
||||
#ifndef _WIN32
|
||||
i32 fd[2];
|
||||
var res = ngc.alloc(vm_vec);
|
||||
if (pipe(fd)==-1) {
|
||||
return nas_err("pipe", "failed to create pipe");
|
||||
}
|
||||
res.vec().elems.push_back(var::num(static_cast<f64>(fd[0])));
|
||||
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
|
||||
return res;
|
||||
#endif
|
||||
return nas_err("pipe", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_fork(var* local, gc& ngc) {
|
||||
#ifndef _WIN32
|
||||
f64 res=fork();
|
||||
if (res<0) {
|
||||
return nas_err("fork", "failed to fork a process");
|
||||
}
|
||||
return var::num(static_cast<f64>(res));
|
||||
#endif
|
||||
return nas_err("fork", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_waitpid(var* local, gc& ngc) {
|
||||
var pid = local[1];
|
||||
var nohang = local[2];
|
||||
if (pid.type!=vm_num || nohang.type!=vm_num) {
|
||||
return nas_err("waitpid", "pid and nohang must be number");
|
||||
}
|
||||
#ifndef _WIN32
|
||||
i32 ret_pid, status;
|
||||
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
|
||||
var vec = ngc.alloc(vm_vec);
|
||||
vec.vec().elems.push_back(var::num(static_cast<f64>(ret_pid)));
|
||||
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
|
||||
return vec;
|
||||
#endif
|
||||
return nas_err("waitpid", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_opendir(var* local, gc& ngc) {
|
||||
var path = local[1];
|
||||
if (path.type!=vm_str) {
|
||||
return nas_err("opendir", "\"path\" must be string");
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
WIN32_FIND_DATAA data;
|
||||
HANDLE p;
|
||||
p = FindFirstFileA((path.str()+"\\*.*").c_str(), &data);
|
||||
if (p==INVALID_HANDLE_VALUE) {
|
||||
return nas_err("opendir", "cannot open dir <"+path.str()+">");
|
||||
}
|
||||
#else
|
||||
DIR* p = opendir(path.str().c_str());
|
||||
if (!p) {
|
||||
return nas_err("opendir", "cannot open dir <"+path.str()+">");
|
||||
}
|
||||
#endif
|
||||
var ret = ngc.alloc(vm_obj);
|
||||
ret.obj().set(dir_type_name, dir_entry_destructor, p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_readdir(var* local, gc& ngc) {
|
||||
var handle = local[1];
|
||||
if (!handle.objchk(dir_type_name)) {
|
||||
return nas_err("readdir", "not a valid dir handle");
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
WIN32_FIND_DATAA data;
|
||||
if (!FindNextFileA(handle.obj().ptr,&data)) {
|
||||
return nil;
|
||||
}
|
||||
return ngc.newstr(data.cFileName);
|
||||
#else
|
||||
dirent* p = readdir(static_cast<DIR*>(handle.obj().ptr));
|
||||
return p? ngc.newstr(p->d_name):nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_closedir(var* local, gc& ngc) {
|
||||
var handle = local[1];
|
||||
if (!handle.objchk(dir_type_name)) {
|
||||
return nas_err("closedir", "not a valid dir handle");
|
||||
}
|
||||
handle.obj().clear();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_chdir(var* local, gc& ngc) {
|
||||
var path = local[1];
|
||||
if (path.type!=vm_str) {
|
||||
return var::num(-1.0);
|
||||
}
|
||||
return var::num(static_cast<f64>(chdir(path.str().c_str())));
|
||||
}
|
||||
|
||||
var builtin_environ(var* local, gc& ngc) {
|
||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
for(char** env = environ; *env; ++env) {
|
||||
vec.push_back(ngc.newstr(*env));
|
||||
}
|
||||
ngc.temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_getcwd(var* local, gc& ngc) {
|
||||
char buf[1024];
|
||||
if (!getcwd(buf, sizeof(buf))) {
|
||||
return nil;
|
||||
}
|
||||
return ngc.newstr(buf);
|
||||
}
|
||||
|
||||
var builtin_getenv(var* local, gc& ngc) {
|
||||
var envvar = local[1];
|
||||
if (envvar.type!=vm_str) {
|
||||
return nas_err("getenv", "\"envvar\" must be string");
|
||||
}
|
||||
char* res = getenv(envvar.str().c_str());
|
||||
return res? ngc.newstr(res):nil;
|
||||
}
|
||||
|
||||
nasal_builtin_table unix_lib_native[] = {
|
||||
{"__pipe", builtin_pipe},
|
||||
{"__fork", builtin_fork},
|
||||
{"__waitpid", builtin_waitpid},
|
||||
{"__opendir", builtin_opendir},
|
||||
{"__readdir", builtin_readdir},
|
||||
{"__closedir", builtin_closedir},
|
||||
{"__chdir", builtin_chdir},
|
||||
{"__environ", builtin_environ},
|
||||
{"__getcwd", builtin_getcwd},
|
||||
{"__getenv", builtin_getenv},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
40
src/unix_lib.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#else
|
||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void dir_entry_destructor(void*);
|
||||
|
||||
var builtin_pipe(var*, gc&);
|
||||
var builtin_fork(var*, gc&);
|
||||
var builtin_waitpid(var*, gc&);
|
||||
var builtin_opendir(var*, gc&);
|
||||
var builtin_readdir(var*, gc&);
|
||||
var builtin_closedir(var*, gc&);
|
||||
var builtin_chdir(var*, gc&);
|
||||
var builtin_environ(var*, gc&);
|
||||
var builtin_getcwd(var*, gc&);
|
||||
var builtin_getenv(var*, gc&);
|
||||
|
||||
extern nasal_builtin_table unix_lib_native[];
|
||||
|
||||
}
|
||||
125
std/bits.nas
Normal file
@@ -0,0 +1,125 @@
|
||||
# bits.nas
|
||||
# 2023 by ValKmjolnir
|
||||
# functions that do bitwise calculation.
|
||||
# carefully use it, all the calculations are based on integer.
|
||||
|
||||
# u32 xor
|
||||
var u32_xor = func(a, b) {
|
||||
return __u32xor(a, b);
|
||||
}
|
||||
|
||||
# u32 and
|
||||
var u32_and = func(a, b) {
|
||||
return __u32and(a, b);
|
||||
}
|
||||
|
||||
# u32 or
|
||||
var u32_or = func(a, b) {
|
||||
return __u32or(a, b);
|
||||
}
|
||||
|
||||
# u32 nand
|
||||
var u32_nand = func(a, b) {
|
||||
return __u32nand(a, b);
|
||||
}
|
||||
|
||||
# u32 not
|
||||
var u32_not = func(a) {
|
||||
return __u32not(a);
|
||||
}
|
||||
|
||||
# get bit data from a special string. for example:
|
||||
# bits.fld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5).
|
||||
var fld = func(str, startbit, len) {
|
||||
return __fld;
|
||||
}
|
||||
|
||||
# get sign-extended data from a special string. for example:
|
||||
# bits.sfld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5) then this will be signed extended to
|
||||
# 11111101(-3).
|
||||
var sfld = func(str, startbit, len) {
|
||||
return __sfld;
|
||||
}
|
||||
|
||||
# set value into a special string to store it. little-endian, for example:
|
||||
# bits.setfld(s,0,8,69);
|
||||
# set 01000101(69) to string will get this:
|
||||
# 10100010(162)
|
||||
# so s[0]=162.
|
||||
var setfld = func(str, startbit, len, val) {
|
||||
return __setfld;
|
||||
}
|
||||
|
||||
# get a special string filled by '\0' to use in setfld.
|
||||
var buf = func(len) {
|
||||
return __buf;
|
||||
}
|
||||
|
||||
var bit = func() {
|
||||
var res = [var __ = 1];
|
||||
var append = func(vec, arg...) {
|
||||
return __append;
|
||||
}
|
||||
for(var i = 1; i<32; i += 1) {
|
||||
append(res, __ += __);
|
||||
}
|
||||
return res;
|
||||
}();
|
||||
|
||||
var test = func(n, b) {
|
||||
n /= bit[b];
|
||||
return int(n) != int(n/2)*2;
|
||||
}
|
||||
|
||||
# returns number <n> with bit <b> set
|
||||
var set = func(n, b) {
|
||||
return n+!test(n, b)*bit[b];
|
||||
}
|
||||
|
||||
|
||||
# returns number <n> with bit <b> cleared
|
||||
var clear = func(n, b) {
|
||||
return n-test(n, b)*bit[b];
|
||||
}
|
||||
|
||||
|
||||
# returns number <n> with bit <b> toggled
|
||||
var toggle = func(n, b) {
|
||||
return test(n, b) ? (n-bit[b]):(n+bit[b]);
|
||||
}
|
||||
|
||||
|
||||
# returns number <n> with bit <b> set to value <v>
|
||||
var switch = func(n, b, v) {
|
||||
return n-(test(n, b)-!!v)*bit[b];
|
||||
}
|
||||
|
||||
|
||||
# returns number <n> as bit string, zero-padded to <len> digits:
|
||||
# bits.string(6) -> "110"
|
||||
# bits.string(6, 8) -> "00000110"
|
||||
var string = func(n, len = 1) {
|
||||
var s = "";
|
||||
while (n) {
|
||||
var v = int(n/2);
|
||||
s = (v+v!=n)~s;
|
||||
n = v;
|
||||
}
|
||||
for (var i = size(s); i<len; i += 1)
|
||||
s = '0'~s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
# returns bit string <s> as number: bits.value("110") -> 6
|
||||
var value = func(s) {
|
||||
var n = 0;
|
||||
var len = size(s);
|
||||
for (var i = 0; i<len; i += 1)
|
||||
n += n+(s[i]!= `0`);
|
||||
return n;
|
||||
}
|
||||
24
std/coroutine.nas
Normal file
@@ -0,0 +1,24 @@
|
||||
# coroutine.nas
|
||||
# 2023 by ValKmjolnir
|
||||
|
||||
# in fact it is not multi-threaded, maybe in the future i could make it
|
||||
|
||||
var create = func(function) {
|
||||
return __cocreate;
|
||||
}
|
||||
|
||||
var resume = func(co, args...) {
|
||||
return __coresume;
|
||||
}
|
||||
|
||||
var yield = func(args...) {
|
||||
return __coyield;
|
||||
}
|
||||
|
||||
var status = func(co) {
|
||||
return __costatus;
|
||||
}
|
||||
|
||||
var running = func() {
|
||||
return __corun;
|
||||
}
|
||||
16
std/csv.nas
Normal file
@@ -0,0 +1,16 @@
|
||||
# lib csv.nas
|
||||
# ValKmjolnir 2022/10/15
|
||||
var read = func(path, delimeter=",", endline="\n"){
|
||||
var context = io.readfile(path);
|
||||
context = split(endline, context);
|
||||
forindex(var i;context){
|
||||
context[i] = split(delimeter,context[i]);
|
||||
}
|
||||
if(size(context)<=1){
|
||||
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
|
||||
}
|
||||
return {
|
||||
property: context[0],
|
||||
data: context[1:]
|
||||
};
|
||||
}
|
||||