Compare commits
480 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97adfc9ea4 | ||
|
|
e8c8a6446b | ||
|
|
336139dcae | ||
|
|
8a160dc7f2 | ||
|
|
c946e9debd | ||
|
|
2f58a7c223 | ||
|
|
5174551aa1 | ||
|
|
49f8cefca0 | ||
|
|
ccbe341dc5 | ||
|
|
4757dd220a | ||
|
|
88e4b86ccb | ||
|
|
c35ad2e6fa | ||
|
|
4c8e1dfe91 | ||
|
|
d6e1408edb | ||
|
|
ef4af8f195 | ||
|
|
bbed29eb65 | ||
|
|
d56e1bff2c | ||
|
|
9f7484596a | ||
|
|
f7f4a38b47 | ||
|
|
07a652cc37 | ||
|
|
9629108a1e | ||
|
|
1e1ab37e83 | ||
|
|
dfcccd4523 | ||
|
|
820c05c986 | ||
|
|
7f8a0b6445 | ||
|
|
54317a39a7 | ||
|
|
a298aa3a63 | ||
|
|
1580b31122 | ||
|
|
aab7decd70 | ||
|
|
8290b7df9f | ||
|
|
ecfb679218 | ||
|
|
80f9fc5842 | ||
|
|
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 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.nas linguist-language=Nasal
|
||||
47
.github/workflows/c-cpp.yml
vendored
@@ -4,20 +4,24 @@ on:
|
||||
schedule:
|
||||
- cron: "0 16 * * *"
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ master,develop ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
mac-build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: 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
|
||||
@@ -28,7 +32,34 @@ jobs:
|
||||
# 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
|
||||
automatic_release_tag: next_macOS
|
||||
# File to release
|
||||
files: nasal
|
||||
|
||||
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
|
||||
|
||||
|
||||
27
.gitignore
vendored
@@ -31,6 +31,31 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# VS C++ sln
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
.vs
|
||||
x64
|
||||
CMakePresents.json
|
||||
|
||||
# nasal executable
|
||||
nasal
|
||||
nasal.exe
|
||||
|
||||
# misc
|
||||
.vscode
|
||||
dump
|
||||
dump
|
||||
fgfs.log
|
||||
.temp.*
|
||||
|
||||
# build dir
|
||||
build
|
||||
out
|
||||
|
||||
# macOS special cache directory
|
||||
.DS_Store
|
||||
|
||||
# ppm picture generated by ppmgen.nas
|
||||
*.ppm
|
||||
87
CMakeLists.txt
Normal file
@@ -0,0 +1,87 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(nasal VERSION 10.1)
|
||||
|
||||
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
|
||||
|
||||
# -std=c++17 -Wshadow -Wall
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
||||
|
||||
add_compile_options(-fPIC)
|
||||
|
||||
# generate release executables
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
|
||||
# build nasal used object
|
||||
set(NASAL_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/coroutine.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/fg_props.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/bits_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/io_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/math_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/dylib_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/unix_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_dbg.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_err.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_import.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_lexer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/repl.cpp)
|
||||
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
|
||||
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
# build nasal
|
||||
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
|
||||
target_link_libraries(nasal nasal-object)
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
target_link_libraries(nasal dl)
|
||||
target_link_libraries(nasal pthread)
|
||||
endif()
|
||||
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
add_custom_command(
|
||||
TARGET nasal POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/build/nasal
|
||||
${CMAKE_SOURCE_DIR}/nasal
|
||||
)
|
||||
endif()
|
||||
|
||||
# build module
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
||||
|
||||
set(MODULE_USED_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
||||
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
|
||||
|
||||
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
|
||||
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(fib module-used-object)
|
||||
|
||||
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
|
||||
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(key module-used-object)
|
||||
|
||||
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
|
||||
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(mat module-used-object)
|
||||
|
||||
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
|
||||
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(nasock module-used-object)
|
||||
352
LICENSE
@@ -1,21 +1,339 @@
|
||||
MIT License
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (c) 2021 ValKmjolnir
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Preamble
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
1145
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:
|
||||
|
||||

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

|
||||
|
||||
### version 4.0 vm (last update 2020/12/17)
|
||||
|
||||
I have just finished the first version of bytecode-interpreter.
|
||||
|
||||
This interpreter is still in test.
|
||||
After this test, i will release version 4.0!
|
||||
|
||||
Now i am trying to search hidden bugs in this interpreter.
|
||||
Hope you could help me! :)
|
||||
|
||||
There's an example of byte code below:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 0
|
||||
.number 4e+006
|
||||
.number 1
|
||||
.symbol i
|
||||
0x00000000: pzero 0x00000000
|
||||
0x00000001: loadg 0x00000000 (i)
|
||||
0x00000002: callg 0x00000000 (i)
|
||||
0x00000003: pnum 0x00000001 (4e+006)
|
||||
0x00000004: less 0x00000000
|
||||
0x00000005: jf 0x0000000b
|
||||
0x00000006: pone 0x00000000
|
||||
0x00000007: mcallg 0x00000000 (i)
|
||||
0x00000008: addeq 0x00000000
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000002
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 5.0 vm (last update 2021/3/7)
|
||||
|
||||
I decide to optimize bytecode vm in this version.
|
||||
|
||||
Because it takes more than 1.5s to count i from `0` to `4000000-1`.This is not efficient at all!
|
||||
|
||||
2021/1/23 update: Now it can count from `0` to `4000000-1` in 1.5s.
|
||||
|
||||
### version 6.0 vm (last update 2021/6/1)
|
||||
|
||||
Use `loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll` to avoid branches.
|
||||
|
||||
Delete type `vm_scop`.
|
||||
|
||||
Use const `vm_num` to avoid frequently new & delete.
|
||||
|
||||
Change garbage collector from reference-counting to mark-sweep.
|
||||
|
||||
`vapp` and `newf` operand use .num to reduce the size of `exec_code`.
|
||||
|
||||
2021/4/3 update: Now it can count from `0` to `4e6-1` in 0.8s.
|
||||
|
||||
2021/4/19 update: Now it can count from `0` to `4e6-1` in 0.4s.
|
||||
|
||||
In this update i changed global and local scope from `unordered_map` to `vector`.
|
||||
|
||||
So the bytecode generator changed a lot.
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4e+006)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000c
|
||||
0x00000007: pone 0x00000000
|
||||
0x00000008: mcallg 0x00000000
|
||||
0x00000009: addeq 0x00000000
|
||||
0x0000000a: pop 0x00000000
|
||||
0x0000000b: jmp 0x00000003
|
||||
0x0000000c: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 6.5 vm (last update 2021/6/24)
|
||||
|
||||
2021/5/31 update:
|
||||
|
||||
Now gc can collect garbage correctly without re-collecting,
|
||||
which will cause fatal error.
|
||||
|
||||
Add `builtin_alloc` to avoid mark-sweep when running a built-in function,
|
||||
which will mark useful items as useless garbage to collect.
|
||||
|
||||
Better use setsize and assignment to get a big array,
|
||||
`append` is very slow in this situation.
|
||||
|
||||
2021/6/3 update:
|
||||
|
||||
Fixed a bug that gc still re-collects garbage,
|
||||
this time i use three mark states to make sure garbage is ready to be collected.
|
||||
|
||||
Change `callf` to `callfv` and `callfh`.
|
||||
And `callfv` fetches arguments from `val_stack` directly instead of using `vm_vec`,
|
||||
a not very efficient way.
|
||||
|
||||
Better use `callfv` instead of `callfh`,
|
||||
`callfh` will fetch a `vm_hash` from stack and parse it,
|
||||
making this process slow.
|
||||
|
||||
```javascript
|
||||
var f=func(x,y){return x+y;}
|
||||
f(1024,2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 1024
|
||||
.number 2048
|
||||
.symbol x
|
||||
.symbol y
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: newf 0x00000007
|
||||
0x00000002: intl 0x00000003
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: para 0x00000000 (x)
|
||||
0x00000005: para 0x00000001 (y)
|
||||
0x00000006: jmp 0x0000000b
|
||||
0x00000007: calll 0x00000001
|
||||
0x00000008: calll 0x00000002
|
||||
0x00000009: add 0x00000000
|
||||
0x0000000a: ret 0x00000000
|
||||
0x0000000b: loadg 0x00000000
|
||||
0x0000000c: callg 0x00000000
|
||||
0x0000000d: pnum 0x00000000 (1024)
|
||||
0x0000000e: pnum 0x00000001 (2048)
|
||||
0x0000000f: callfv 0x00000002
|
||||
0x00000010: pop 0x00000000
|
||||
0x00000011: nop 0x00000000
|
||||
```
|
||||
|
||||
2021/6/21 update: Now gc will not collect nullptr.
|
||||
And the function of assignment is complete,
|
||||
now these kinds of assignment is allowed:
|
||||
|
||||
```javascript
|
||||
var f=func()
|
||||
{
|
||||
var _=[{_:0},{_:1}];
|
||||
return func(x)
|
||||
{
|
||||
return _[x];
|
||||
}
|
||||
}
|
||||
var m=f();
|
||||
m(0)._=m(1)._=10;
|
||||
|
||||
[0,1,2][1:2][0]=0;
|
||||
```
|
||||
|
||||
In the old version,
|
||||
parser will check this left-value and tells that these kinds of left-value are not allowed(bad lvalue).
|
||||
|
||||
But now it can work.
|
||||
And you could see its use by reading the code above.
|
||||
To make sure this assignment works correctly,
|
||||
codegen will generate byte code by `codegen::call_gen()` instead of `codegen::mcall_gen()`,
|
||||
and the last child of the ast will be generated by `codegen::mcall_gen()`.
|
||||
So the bytecode is totally different now:
|
||||
|
||||
```x86asm
|
||||
.number 10
|
||||
.number 2
|
||||
.symbol _
|
||||
.symbol x
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: newf 0x00000005
|
||||
0x00000002: intl 0x00000002
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: jmp 0x00000017
|
||||
0x00000005: newh 0x00000000
|
||||
0x00000006: pzero 0x00000000
|
||||
0x00000007: happ 0x00000000 (_)
|
||||
0x00000008: newh 0x00000000
|
||||
0x00000009: pone 0x00000000
|
||||
0x0000000a: happ 0x00000000 (_)
|
||||
0x0000000b: newv 0x00000002
|
||||
0x0000000c: loadl 0x00000001
|
||||
0x0000000d: newf 0x00000012
|
||||
0x0000000e: intl 0x00000003
|
||||
0x0000000f: offset 0x00000002
|
||||
0x00000010: para 0x00000001 (x)
|
||||
0x00000011: jmp 0x00000016
|
||||
0x00000012: calll 0x00000001
|
||||
0x00000013: calll 0x00000002
|
||||
0x00000014: callv 0x00000000
|
||||
0x00000015: ret 0x00000000
|
||||
0x00000016: ret 0x00000000
|
||||
0x00000017: loadg 0x00000000
|
||||
0x00000018: callg 0x00000000
|
||||
0x00000019: callfv 0x00000000
|
||||
0x0000001a: loadg 0x00000001
|
||||
0x0000001b: pnum 0x00000000 (10.000000)
|
||||
0x0000001c: callg 0x00000001
|
||||
0x0000001d: pone 0x00000000
|
||||
0x0000001e: callfv 0x00000001
|
||||
0x0000001f: mcallh 0x00000000 (_)
|
||||
0x00000020: meq 0x00000000
|
||||
0x00000021: callg 0x00000001
|
||||
0x00000022: pzero 0x00000000
|
||||
0x00000023: callfv 0x00000001
|
||||
0x00000024: mcallh 0x00000000 (_)
|
||||
0x00000025: meq 0x00000000
|
||||
0x00000026: pop 0x00000000
|
||||
0x00000027: pzero 0x00000000
|
||||
0x00000028: pzero 0x00000000
|
||||
0x00000029: pone 0x00000000
|
||||
0x0000002a: pnum 0x00000001 (2.000000)
|
||||
0x0000002b: newv 0x00000003
|
||||
0x0000002c: slcbeg 0x00000000
|
||||
0x0000002d: pone 0x00000000
|
||||
0x0000002e: pnum 0x00000001 (2.000000)
|
||||
0x0000002f: slc2 0x00000000
|
||||
0x00000030: slcend 0x00000000
|
||||
0x00000031: pzero 0x00000000
|
||||
0x00000032: mcallv 0x00000000
|
||||
0x00000033: meq 0x00000000
|
||||
0x00000034: pop 0x00000000
|
||||
0x00000035: nop 0x00000000
|
||||
```
|
||||
|
||||
As you could see from the bytecode above,
|
||||
`mcall`/`mcallv`/`mcallh` operands' using frequency will reduce,
|
||||
`call`/`callv`/`callh`/`callfv`/`callfh` at the opposite.
|
||||
|
||||
And because of the new structure of `mcall`,
|
||||
`addr_stack`, a stack used to store the memory address,
|
||||
is deleted from `vm`,
|
||||
and now `vm` use `nas_val** mem_addr` to store the memory address.
|
||||
This will not cause fatal errors because the memory address is used __immediately__ after getting it.
|
||||
|
||||
### version 7.0 vm (last update 2021/10/8)
|
||||
|
||||
2021/6/26 update:
|
||||
|
||||
Instruction dispatch is changed from call-threading to computed-goto(with inline function).
|
||||
After changing the way of instruction dispatch,
|
||||
there is a great improvement in `vm`.
|
||||
Now vm can run test/bigloop and test/pi in 0.2s!
|
||||
And vm runs test/fib in 0.8s on linux.
|
||||
You could see the time use data below,
|
||||
in Test data section.
|
||||
|
||||
This version uses g++ extension "labels as values",
|
||||
which is also supported by clang++.
|
||||
(But i don't know if MSVC supports this)
|
||||
|
||||
There is also a change in `gc`:
|
||||
`std::vector` global is deleted,
|
||||
now the global values are all stored on stack(from `val_stack+0` to `val_stack+intg-1`).
|
||||
|
||||
2021/6/29 update:
|
||||
|
||||
Add some instructions that execute const values:
|
||||
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`.
|
||||
|
||||
Now the bytecode of test/bigloop.nas seems like this:
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
.number 1
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4000000)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000b
|
||||
0x00000007: mcallg 0x00000000
|
||||
0x00000008: addeqc 0x00000001 (1)
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000003
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
And this test file runs in 0.1s after this update.
|
||||
Most of the calculations are accelerated.
|
||||
|
||||
Also, assignment bytecode has changed a lot.
|
||||
Now the first identifier that called in assignment will use `op_load` to assign,
|
||||
instead of `op_meq`,`op_pop`.
|
||||
|
||||
```javascript
|
||||
var (a,b)=(1,2);
|
||||
a=b=0;
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 2
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: pone 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: pnum 0x00000000 (2)
|
||||
0x00000004: loadg 0x00000001
|
||||
0x00000005: pzero 0x00000000
|
||||
0x00000006: mcallg 0x00000001
|
||||
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
|
||||
0x00000008: loadg 0x00000000 (a=b use loadg)
|
||||
0x00000009: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 8.0 vm (last update 2022/2/12)
|
||||
|
||||
2021/10/8 update:
|
||||
|
||||
In this version vm_nil and vm_num now is not managed by `gc`,
|
||||
this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
|
||||
|
||||
New value type is added: `vm_obj`.
|
||||
This type is reserved for user to define their own value types.
|
||||
Related API will be added in the future.
|
||||
|
||||
Fully functional closure:
|
||||
Add new operands that get and set upvalues.
|
||||
Delete an old operand `op_offset`.
|
||||
|
||||
2021/10/13 update:
|
||||
|
||||
The format of output information of bytecodes changes to this:
|
||||
|
||||
```x86asm
|
||||
0x000002f2: newf 0x2f6
|
||||
0x000002f3: intl 0x2
|
||||
0x000002f4: para 0x3e ("x")
|
||||
0x000002f5: jmp 0x309
|
||||
0x000002f6: calll 0x1
|
||||
0x000002f7: lessc 0x0 (2)
|
||||
0x000002f8: jf 0x2fb
|
||||
0x000002f9: calll 0x1
|
||||
0x000002fa: ret
|
||||
0x000002fb: upval 0x0[0x1]
|
||||
0x000002fc: upval 0x0[0x1]
|
||||
0x000002fd: callfv 0x1
|
||||
0x000002fe: calll 0x1
|
||||
0x000002ff: subc 0x1d (1)
|
||||
0x00000300: callfv 0x1
|
||||
0x00000301: upval 0x0[0x1]
|
||||
0x00000302: upval 0x0[0x1]
|
||||
0x00000303: callfv 0x1
|
||||
0x00000304: calll 0x1
|
||||
0x00000305: subc 0x0 (2)
|
||||
0x00000306: callfv 0x1
|
||||
0x00000307: add
|
||||
0x00000308: ret
|
||||
0x00000309: ret
|
||||
0x0000030a: callfv 0x1
|
||||
0x0000030b: loadg 0x32
|
||||
```
|
||||
|
||||
2022/1/22 update:
|
||||
|
||||
Delete `op_pone` and `op_pzero`.
|
||||
Both of them are meaningless and will be replaced by `op_pnum`.
|
||||
|
||||
### version 9.0 vm (last update 2022/5/18)
|
||||
|
||||
2022/2/12 update:
|
||||
|
||||
Local values now are __stored on stack__.
|
||||
So function calling will be faster than before.
|
||||
Because in v8.0 when calling a function,
|
||||
new `vm_vec` will be allocated by `gc`, this makes gc doing mark-sweep too many times and spends a quite lot of time.
|
||||
In test file `test/bf.nas`, it takes too much time to test the file because this file has too many function calls(see test data below in table `version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`).
|
||||
|
||||
Upvalue now is generated when creating first new function in the local scope, using `vm_vec`.
|
||||
And after that when creating new functions, they share the same upvalue, and the upvalue will synchronize with the local scope each time creating a new function.
|
||||
|
||||
2022/3/27 update:
|
||||
|
||||
In this month's updates we change upvalue from `vm_vec` to `vm_upval`,
|
||||
a special gc-managed object,
|
||||
which has almost the same structure of that upvalue object in another programming language __`Lua`__.
|
||||
|
||||
Today we change the output format of bytecode.
|
||||
New output format looks like `objdump`:
|
||||
|
||||
```x86asm
|
||||
0x0000029b: 0a 00 00 00 00 newh
|
||||
|
||||
func <0x29c>:
|
||||
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
|
||||
0x0000029d: 02 00 00 00 02 intl 0x2
|
||||
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
|
||||
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
|
||||
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
|
||||
0x000002a1: 4a 00 00 00 00 ret
|
||||
<0x29c>;
|
||||
|
||||
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
|
||||
|
||||
func <0x2a3>:
|
||||
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
|
||||
0x000002a4: 02 00 00 00 03 intl 0x3
|
||||
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
|
||||
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
|
||||
0x000002a7: 32 00 00 02 aa jmp 0x2aa
|
||||
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
|
||||
0x000002a9: 4a 00 00 00 00 ret
|
||||
<0x2a3>;
|
||||
|
||||
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
|
||||
```
|
||||
|
||||
### version 10.0 vm (last update 2022/8/16)
|
||||
|
||||
2022/5/19 update:
|
||||
|
||||
Now we add coroutine in this runtime:
|
||||
|
||||
```javascript
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
```
|
||||
|
||||
`coroutine.create` is used to create a new coroutine object using a function.
|
||||
But this coroutine will not run immediately.
|
||||
|
||||
`coroutine.resume` is used to continue running a coroutine.
|
||||
|
||||
`coroutine.yield` is used to interrupt the running of a coroutine and throw some values.
|
||||
These values will be accepted and returned by `coroutine.resume`.
|
||||
And `coroutine.yield` it self returns `vm_nil` in the coroutine function.
|
||||
|
||||
`coroutine.status` is used to see the status of a coroutine.
|
||||
There are 3 types of status:`suspended` means waiting for running,`running` means is running,`dead` means finished running.
|
||||
|
||||
`coroutine.running` is used to judge if there is a coroutine running now.
|
||||
|
||||
__CAUTION:__ coroutine should not be created or running inside another coroutine.
|
||||
|
||||
__We will explain how resume and yield work here:__
|
||||
|
||||
When `op_callb` is called, the stack frame is like this:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| old pc(vm_ret) | <- top[0]
|
||||
+----------------------+
|
||||
| old localr(vm_addr) | <- top[-1]
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) | <- top[-2]
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
In `op_callb`'s progress, next step the stack frame is:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
Then we call `resume`, this function will change stack.
|
||||
As we can see, coroutine stack already has some values on it,
|
||||
but if we first enter it, the stack top will be `vm_ret`, and the return `pc` is `0`.
|
||||
|
||||
So for safe running, when first calling the coroutine, `resume` will return `gc.top[0]`.
|
||||
`op_callb` will do `top[0]=resume()`, so the value does not change.
|
||||
|
||||
```C++
|
||||
+----------------------+(coroutine stack)
|
||||
| pc:0(vm_ret) | <- now gc.top[0]
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
When we call `yield`, the function will do like this.
|
||||
And we find that `op_callb` has put the `nil` at the top.
|
||||
but where is the returned `local[1]` sent?
|
||||
|
||||
```C++
|
||||
+----------------------+(coroutine stack)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
When `builtin_coyield` is finished, the stack is set to main stack,
|
||||
and the returned `local[1]` in fact is set to the top of the main stack by `op_callb`:
|
||||
|
||||
```C++
|
||||
+----------------------+(main stack)
|
||||
| return_value(var) |
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
so the main progress feels the value on the top is the returned value of `resume`.
|
||||
but in fact the `resume`'s returned value is set on coroutine stack.
|
||||
so we conclude this:
|
||||
|
||||
```C++
|
||||
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
|
||||
yield (coroutine->main) return a vector. main.top[0] = vector;
|
||||
```
|
||||
|
||||
## __Release Notes__
|
||||
|
||||
### __version 8.0 release__
|
||||
|
||||
I made a __big mistake__ in `v8.0` release:
|
||||
|
||||
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
this will cause incorrect `stackoverflow` error.
|
||||
please change it to:
|
||||
|
||||
`canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
If do not change this line, only the debugger runs abnormally. this bug is fixed in `v9.0`.
|
||||
|
||||
Another bug is that in `nasal_err.h:class nasal_err`, we should add a constructor for this class:
|
||||
|
||||
```C++
|
||||
nasal_err(): error(0) {}
|
||||
```
|
||||
|
||||
This bug is fixed in `v9.0`. So we suggest that do not use `v8.0`.
|
||||
|
||||
### __version 11.0 release__
|
||||
|
||||
1. Use C++ `std=c++17`.
|
||||
|
||||
2. Change framework of ast, using visitor pattern.
|
||||
|
||||
3. New ast structure dump info format.
|
||||
|
||||
4. Change the way of module export, split library into different modules. Symbols begin with `_` will not be exported.
|
||||
|
||||
5. Change `stl` to `std`.
|
||||
|
||||
6. Add REPL interpreter.
|
||||
|
||||
7. Improve structure of virtual machine, split global symbol stack(stores global symbols' values) and value stack(using in process).
|
||||
|
||||
8. Delete operand `op_intg`, add operand `op_repl`.
|
||||
|
||||
9. Add `CMakeLists.txt` for cmake user(including `Visual Studio`).
|
||||
|
||||
10. New ghost type register process.
|
||||
|
||||
### __version 11.1 release__
|
||||
|
||||
1. Bug fix: debugger in v11.0 is malfunctional.
|
||||
|
||||
2. Bug fix: symbol_finder does not check definition in foreach/forindex loop.
|
||||
|
||||
3. Change extension syntax `import.xx.xx` to `use xx.xx`.
|
||||
643
doc/dev_zh.md
Normal file
@@ -0,0 +1,643 @@
|
||||
# __开发历史记录__
|
||||
|
||||

|
||||
|
||||
## __目录__
|
||||
|
||||
* [__语法分析__](#语法分析)
|
||||
* [v1.0](#version-10-parser-last-update-20191014)
|
||||
* [__抽象语法树__](#抽象语法树)
|
||||
* [v1.2](#version-12-ast-last-update-20191031)
|
||||
* [v2.0](#version-20-ast-last-update-2020831)
|
||||
* [v3.0](#version-30-ast-last-update-20201023)
|
||||
* [v5.0](#version-50-ast-last-update-202137)
|
||||
* [v11.0](#version-110-ast-latest)
|
||||
* [__字节码虚拟机__](#字节码虚拟机)
|
||||
* [v4.0](#version-40-vm-last-update-20201217)
|
||||
* [v5.0](#version-50-vm-last-update-202137)
|
||||
* [v6.0](#version-60-vm-last-update-202161)
|
||||
* [v6.5](#version-65-vm-last-update-2021624)
|
||||
* [v7.0](#version-70-vm-last-update-2021108)
|
||||
* [v8.0](#version-80-vm-last-update-2022212)
|
||||
* [v9.0](#version-90-vm-last-update-2022518)
|
||||
* [v10.0](#version-100-vm-last-update-2022816)
|
||||
* [__发行日志__](#发行日志)
|
||||
* [v8.0](#version-80-release)
|
||||
* [v11.0](#version-110-release)
|
||||
* [v11.1](#version-111-release)
|
||||
|
||||
## __语法分析__
|
||||
|
||||
有特殊语法检查的`LL(1)`语法分析器。
|
||||
|
||||
```javascript
|
||||
(var a,b,c)=[{b:nil},[1,2],func return 0;];
|
||||
(a.b,b[0],c)=(1,2,3);
|
||||
```
|
||||
|
||||
这两个表达式有同一个first集,所以纯粹的`LL(1)`很难实现这个语言的语法分析。所以我们为其添加了特殊语法检查机制。本质上还是`LL(1)`的内核。
|
||||
|
||||
上面这个问题已经解决很久了,不过我最近发现了一个新的语法问题:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
这种写法会被错误识别合并成下面这种:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}(a,b,c)
|
||||
=(0,1,2);
|
||||
```
|
||||
|
||||
语法分析器会认为这是个严重的语法错误。我在Flightgear中也测试了这个代码,它内置的语法分析器也认为这是错误语法。当然我认为这是语法设计中的一个比较严重的缺漏。为了避免这个语法问题,只需要添加一个分号就可以了:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z};
|
||||
^ 就是这里
|
||||
(a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
### version 1.0 parser (last update 2019/10/14)
|
||||
|
||||
第一版功能完备的nasal语法分析器完成了。
|
||||
|
||||
在version 1.0之前,我多次尝试构建一个正确的语法分析器但总是存在一些问题。
|
||||
|
||||
最终我学习了`LL(1)`和`LL(k)`文法并且在version 0.16(last update 2019/9/14)中完成了一个能识别数学算式的语法分析器。
|
||||
|
||||
在version 0.17(2019/9/15) 0.18(2019/9/18) 0.19(2019/10/1)中我只是抱着玩的心态在测试语法分析器,不过在那之后我还是完成了version 1.0的语法分析器。
|
||||
|
||||
__该项目于2019/7/25正式开始__。
|
||||
|
||||
## __抽象语法树__
|
||||
|
||||
### version 1.2 ast (last update 2019/10/31)
|
||||
|
||||
抽象语法树在这个版本初步完成。
|
||||
|
||||
### version 2.0 ast (last update 2020/8/31)
|
||||
|
||||
在这个版本我们基于抽象语法树实现了一个树解释器,并且完成了部分内置函数。
|
||||
|
||||
### version 3.0 ast (last update 2020/10/23)
|
||||
|
||||
我们重构了抽象语法树的代码,现在可以更容易地读懂代码并进行维护。
|
||||
|
||||
这个版本的树解释器用了新的优化方式,所以可以更高效地执行代码。
|
||||
|
||||
在这个版本用户已经可以自行添加内置函数。
|
||||
|
||||
我想在v4.0发布之后仍然保留这个树解释器,毕竟花了很长时间才写完这坨屎。
|
||||
|
||||
### version 5.0 ast (last update 2021/3/7)
|
||||
|
||||
我改变想法了,树解释器给维护带来了太大的麻烦。如果想继续保留这个解释器,那么为了兼容性,字节码虚拟机的优化工作会更难推进。
|
||||
|
||||
### version 11.0 ast (latest)
|
||||
|
||||
改变了语法树的设计模式,采用访问者模式。
|
||||
|
||||
## __字节码虚拟机__
|
||||
|
||||

|
||||
|
||||
### version 4.0 vm (last update 2020/12/17)
|
||||
|
||||
我在这个版本实现了第一版字节码虚拟机。不过这个虚拟机仍然在测试中,在这次测试结束之后,我会发布v4.0发行版。
|
||||
|
||||
现在我在找一些隐藏很深的bug。如果有人想帮忙的话,非常欢迎!:)
|
||||
|
||||
下面是生成的字节码的样例:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 0
|
||||
.number 4e+006
|
||||
.number 1
|
||||
.symbol i
|
||||
0x00000000: pzero 0x00000000
|
||||
0x00000001: loadg 0x00000000 (i)
|
||||
0x00000002: callg 0x00000000 (i)
|
||||
0x00000003: pnum 0x00000001 (4e+006)
|
||||
0x00000004: less 0x00000000
|
||||
0x00000005: jf 0x0000000b
|
||||
0x00000006: pone 0x00000000
|
||||
0x00000007: mcallg 0x00000000 (i)
|
||||
0x00000008: addeq 0x00000000
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000002
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 5.0 vm (last update 2021/3/7)
|
||||
|
||||
从这个版本起,我决定持续优化字节码虚拟机。
|
||||
|
||||
毕竟现在这玩意从`0`数到`4000000-1`要花费1.5秒。这效率完全不能忍。
|
||||
|
||||
2021/1/23 update: 现在它确实可以在1.5秒内从`0`数到`4000000-1`了。
|
||||
|
||||
### version 6.0 vm (last update 2021/6/1)
|
||||
|
||||
使用`loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll`指令来减少分支语句的调用。
|
||||
|
||||
删除了`vm_scop`类型。
|
||||
|
||||
添加作为常量的`vm_num`来减少内存分配的开销。
|
||||
|
||||
将垃圾收集器从引用计数改为了标记清理。
|
||||
|
||||
`vapp`和`newf`开始使用先前未被使用的.num段来压缩字节码生成数量,减少生成的`exec_code`的大小。
|
||||
|
||||
2021/4/3 update: 从`0`数到`4e6-1`只需要不到0.8秒了。
|
||||
|
||||
2021/4/19 update: 从`0`数到`4e6-1`只需要不到0.4秒了。
|
||||
|
||||
在这次的更新中,我把全局变量和局部变量的存储结构从`unordered_map`变为了`vector`,从而提升执行效率。所以现在生成的字节码大变样了。
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4e+006)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000c
|
||||
0x00000007: pone 0x00000000
|
||||
0x00000008: mcallg 0x00000000
|
||||
0x00000009: addeq 0x00000000
|
||||
0x0000000a: pop 0x00000000
|
||||
0x0000000b: jmp 0x00000003
|
||||
0x0000000c: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 6.5 vm (last update 2021/6/24)
|
||||
|
||||
2021/5/31 update:
|
||||
|
||||
现在垃圾收集器不会错误地重复收集未使用变量了。
|
||||
|
||||
添加了`builtin_alloc`以防止在运行内置函数的时候错误触发标记清除。
|
||||
|
||||
建议在获取大空间数组的时候尽量使用setsize,因为`append`在被频繁调用时可能会频繁触发垃圾收集器。
|
||||
|
||||
2021/6/3 update:
|
||||
|
||||
修复了垃圾收集器还是他妈的会重复收集的bug,这次我设计了三个标记状态来保证垃圾是被正确收集了。
|
||||
|
||||
将`callf`指令拆分为`callfv`和`callfh`。并且`callfv`将直接从`val_stack`获取传参,而不是先通过一个`vm_vec`把参数收集起来再传入,后者是非常低效的做法。
|
||||
|
||||
建议更多使用`callfv`而不是`callfh`,因为`callfh`只能从栈上获取参数并整合为`vm_hash`之后才能传给该指令进行处理,拖慢执行速度。
|
||||
|
||||
```javascript
|
||||
var f=func(x,y){return x+y;}
|
||||
f(1024,2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 1024
|
||||
.number 2048
|
||||
.symbol x
|
||||
.symbol y
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: newf 0x00000007
|
||||
0x00000002: intl 0x00000003
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: para 0x00000000 (x)
|
||||
0x00000005: para 0x00000001 (y)
|
||||
0x00000006: jmp 0x0000000b
|
||||
0x00000007: calll 0x00000001
|
||||
0x00000008: calll 0x00000002
|
||||
0x00000009: add 0x00000000
|
||||
0x0000000a: ret 0x00000000
|
||||
0x0000000b: loadg 0x00000000
|
||||
0x0000000c: callg 0x00000000
|
||||
0x0000000d: pnum 0x00000000 (1024)
|
||||
0x0000000e: pnum 0x00000001 (2048)
|
||||
0x0000000f: callfv 0x00000002
|
||||
0x00000010: pop 0x00000000
|
||||
0x00000011: nop 0x00000000
|
||||
```
|
||||
|
||||
2021/6/21 update:
|
||||
|
||||
现在垃圾收集器不会收集空指针了。并且调用链中含有函数调用的赋值语句现在也可以执行了,下面这些赋值方式是合法的:
|
||||
|
||||
```javascript
|
||||
var f=func()
|
||||
{
|
||||
var _=[{_:0},{_:1}];
|
||||
return func(x)
|
||||
{
|
||||
return _[x];
|
||||
}
|
||||
}
|
||||
var m=f();
|
||||
m(0)._=m(1)._=10;
|
||||
|
||||
[0,1,2][1:2][0]=0;
|
||||
```
|
||||
|
||||
在老版本中,语法分析器会检查左值,并且在检测到有特别调用的情况下直接告知用户这种左值是不被接受的(bad lvalue)。但是现在它可以正常运作了。为了保证这种赋值语句能正常执行,codegen模块会优先使用`codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `codegen::mcall_gen()`,在最后一个调用处才会使用`codegen::mcall_gen()`。
|
||||
|
||||
所以现在生成的相关字节码也完全不同了:
|
||||
|
||||
```x86asm
|
||||
.number 10
|
||||
.number 2
|
||||
.symbol _
|
||||
.symbol x
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: newf 0x00000005
|
||||
0x00000002: intl 0x00000002
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: jmp 0x00000017
|
||||
0x00000005: newh 0x00000000
|
||||
0x00000006: pzero 0x00000000
|
||||
0x00000007: happ 0x00000000 (_)
|
||||
0x00000008: newh 0x00000000
|
||||
0x00000009: pone 0x00000000
|
||||
0x0000000a: happ 0x00000000 (_)
|
||||
0x0000000b: newv 0x00000002
|
||||
0x0000000c: loadl 0x00000001
|
||||
0x0000000d: newf 0x00000012
|
||||
0x0000000e: intl 0x00000003
|
||||
0x0000000f: offset 0x00000002
|
||||
0x00000010: para 0x00000001 (x)
|
||||
0x00000011: jmp 0x00000016
|
||||
0x00000012: calll 0x00000001
|
||||
0x00000013: calll 0x00000002
|
||||
0x00000014: callv 0x00000000
|
||||
0x00000015: ret 0x00000000
|
||||
0x00000016: ret 0x00000000
|
||||
0x00000017: loadg 0x00000000
|
||||
0x00000018: callg 0x00000000
|
||||
0x00000019: callfv 0x00000000
|
||||
0x0000001a: loadg 0x00000001
|
||||
0x0000001b: pnum 0x00000000 (10.000000)
|
||||
0x0000001c: callg 0x00000001
|
||||
0x0000001d: pone 0x00000000
|
||||
0x0000001e: callfv 0x00000001
|
||||
0x0000001f: mcallh 0x00000000 (_)
|
||||
0x00000020: meq 0x00000000
|
||||
0x00000021: callg 0x00000001
|
||||
0x00000022: pzero 0x00000000
|
||||
0x00000023: callfv 0x00000001
|
||||
0x00000024: mcallh 0x00000000 (_)
|
||||
0x00000025: meq 0x00000000
|
||||
0x00000026: pop 0x00000000
|
||||
0x00000027: pzero 0x00000000
|
||||
0x00000028: pzero 0x00000000
|
||||
0x00000029: pone 0x00000000
|
||||
0x0000002a: pnum 0x00000001 (2.000000)
|
||||
0x0000002b: newv 0x00000003
|
||||
0x0000002c: slcbeg 0x00000000
|
||||
0x0000002d: pone 0x00000000
|
||||
0x0000002e: pnum 0x00000001 (2.000000)
|
||||
0x0000002f: slc2 0x00000000
|
||||
0x00000030: slcend 0x00000000
|
||||
0x00000031: pzero 0x00000000
|
||||
0x00000032: mcallv 0x00000000
|
||||
0x00000033: meq 0x00000000
|
||||
0x00000034: pop 0x00000000
|
||||
0x00000035: nop 0x00000000
|
||||
```
|
||||
|
||||
从上面这些字节码可以看出,`mcall`/`mcallv`/`mcallh`指令的使用频率比以前减小了一些,而`call`/`callv`/`callh`/`callfv`/`callfh`则相反。
|
||||
|
||||
并且因为新的数据结构,`mcall`指令以及`addr_stack`,一个曾用来存储指针的栈,从`vm`中被移除。现在`vm`使用`nas_val** mem_addr`来暂存获取的内存地址。这不会导致严重的问题,因为内存空间是 __获取即使用__ 的。
|
||||
|
||||
### version 7.0 vm (last update 2021/10/8)
|
||||
|
||||
2021/6/26 update:
|
||||
|
||||
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后,`vm`的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完`test/bigloop`和`test/pi`!并且在linux平台虚拟机可以在0.8秒内执行完`test/fib`。你可以在下面的测试数据部分看到测试的结果。
|
||||
|
||||
这个分派方式使用了g++扩展"labels as values",clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了,哈哈)
|
||||
|
||||
`gc`中也有部分改动:
|
||||
全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0`到`val_stack+intg-1`)。
|
||||
|
||||
2021/6/29 update:
|
||||
|
||||
添加了一些直接用常量进行运算的指令:
|
||||
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`。
|
||||
|
||||
现在test/bigloop.nas的字节码是这样的:
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
.number 1
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4000000)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000b
|
||||
0x00000007: mcallg 0x00000000
|
||||
0x00000008: addeqc 0x00000001 (1)
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000003
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
在这次更新之后,这个测试文件可以在0.1秒内运行结束。大多数的运算操作速度都有提升。
|
||||
|
||||
并且赋值相关的字节码也有一些改动。现在赋值语句只包含一个标识符时,会优先调用`op_load`来赋值,而不是使用`op_meq`和`op_pop`。
|
||||
|
||||
```javascript
|
||||
var (a,b)=(1,2);
|
||||
a=b=0;
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 2
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: pone 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: pnum 0x00000000 (2)
|
||||
0x00000004: loadg 0x00000001
|
||||
0x00000005: pzero 0x00000000
|
||||
0x00000006: mcallg 0x00000001
|
||||
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
|
||||
0x00000008: loadg 0x00000000 (a=b use loadg)
|
||||
0x00000009: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 8.0 vm (last update 2022/2/12)
|
||||
|
||||
2021/10/8 update:
|
||||
|
||||
从这个版本开始`vm_nil`和`vm_num`不再由`gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
|
||||
|
||||
添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。
|
||||
|
||||
功能完备的闭包:添加了读写闭包数据的指令。删除了老的指令`op_offset`。
|
||||
|
||||
2021/10/13 update:
|
||||
|
||||
字节码信息输出格式修改为如下形式:
|
||||
|
||||
```x86asm
|
||||
0x000002f2: newf 0x2f6
|
||||
0x000002f3: intl 0x2
|
||||
0x000002f4: para 0x3e ("x")
|
||||
0x000002f5: jmp 0x309
|
||||
0x000002f6: calll 0x1
|
||||
0x000002f7: lessc 0x0 (2)
|
||||
0x000002f8: jf 0x2fb
|
||||
0x000002f9: calll 0x1
|
||||
0x000002fa: ret
|
||||
0x000002fb: upval 0x0[0x1]
|
||||
0x000002fc: upval 0x0[0x1]
|
||||
0x000002fd: callfv 0x1
|
||||
0x000002fe: calll 0x1
|
||||
0x000002ff: subc 0x1d (1)
|
||||
0x00000300: callfv 0x1
|
||||
0x00000301: upval 0x0[0x1]
|
||||
0x00000302: upval 0x0[0x1]
|
||||
0x00000303: callfv 0x1
|
||||
0x00000304: calll 0x1
|
||||
0x00000305: subc 0x0 (2)
|
||||
0x00000306: callfv 0x1
|
||||
0x00000307: add
|
||||
0x00000308: ret
|
||||
0x00000309: ret
|
||||
0x0000030a: callfv 0x1
|
||||
0x0000030b: loadg 0x32
|
||||
```
|
||||
|
||||
2022/1/22 update:
|
||||
|
||||
删除`op_pone`和`op_pzero`。这两个指令在目前已经没有实际意义,并且已经被`op_pnum`替代。
|
||||
|
||||
### version 9.0 vm (last update 2022/5/18)
|
||||
|
||||
2022/2/12 update:
|
||||
|
||||
局部变量现在也被 __存储在栈上__。
|
||||
所以函数调用比以前也会快速很多。
|
||||
在v8.0如果你想调用一个函数,
|
||||
新的`vm_vec`将被分配出来用于模拟局部作用域,这个操作会导致标记清除过程会被频繁触发并且浪费太多的执行时间。
|
||||
在测试文件`test/bf.nas`中,这种调用方式使得大部分时间都被浪费了,因为这个测试文件包含大量且频繁的函数调用(详细数据请看测试数据一节中`version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`)。
|
||||
|
||||
现在闭包会在第一次在局部作用域创建新函数的时候产生,使用`vm_vec`。
|
||||
在那之后如果再创建新的函数,则他们会共享同一个闭包,这些闭包会在每次于局部作用域创建新函数时同步。
|
||||
|
||||
2022/3/27 update:
|
||||
|
||||
在这个月的更新中我们把闭包的数据结构从`vm_vec`换成了一个新的对象`vm_upval`,这种类型有着和另外一款编程语言 __`Lua`__ 中闭包相类似的结构。
|
||||
|
||||
同时我们也修改了字节码的输出格式。新的格式看起来像是 `objdump`:
|
||||
|
||||
```x86asm
|
||||
0x0000029b: 0a 00 00 00 00 newh
|
||||
|
||||
func <0x29c>:
|
||||
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
|
||||
0x0000029d: 02 00 00 00 02 intl 0x2
|
||||
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
|
||||
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
|
||||
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
|
||||
0x000002a1: 4a 00 00 00 00 ret
|
||||
<0x29c>;
|
||||
|
||||
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
|
||||
|
||||
func <0x2a3>:
|
||||
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
|
||||
0x000002a4: 02 00 00 00 03 intl 0x3
|
||||
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
|
||||
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
|
||||
0x000002a7: 32 00 00 02 aa jmp 0x2aa
|
||||
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
|
||||
0x000002a9: 4a 00 00 00 00 ret
|
||||
<0x2a3>;
|
||||
|
||||
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
|
||||
```
|
||||
|
||||
### version 10.0 vm (last update 2022/8/16)
|
||||
|
||||
2022/5/19 update:
|
||||
|
||||
在这个版本中我们给nasal加入了协程:
|
||||
|
||||
```javascript
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
```
|
||||
|
||||
`coroutine.create`用于创建新的协程对象。不过创建之后协程并不会直接运行。
|
||||
|
||||
`coroutine.resume`用于继续运行一个协程。
|
||||
|
||||
`coroutine.yield`用于中断一个协程的运行过程并且抛出一些数据。这些数据会被`coroutine.resume`接收并返回。而在协程函数中`coroutine.yield`本身只返回`vm_nil`。
|
||||
|
||||
`coroutine.status`用于查看协程的状态。协程有三种不同的状态:`suspended`挂起,`running`运行中,`dead`结束运行。
|
||||
|
||||
`coroutine.running`用于判断当前是否有协程正在运行。
|
||||
|
||||
__注意:__ 协程不能在其他正在运行的协程中创建。
|
||||
|
||||
__接下来我们解释这个协程的运行原理:__
|
||||
|
||||
当`op_callb`被执行时,栈帧如下所示:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| old pc(vm_ret) | <- top[0]
|
||||
+----------------------+
|
||||
| old localr(vm_addr) | <- top[-1]
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) | <- top[-2]
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
在`op_callb`执行过程中,下一步的栈帧如下:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
接着我们调用`resume`,这个函数会替换操作数栈。我们会看到,协程的操作数栈上已经保存了一些数据,但是我们首次进入协程执行时,这个操作数栈的栈顶将会是`vm_ret`,并且返回的`pc`值是`0`。
|
||||
|
||||
首次调用时,为了保证栈顶的数据不会被破坏,`resume`会返回`gc.top[0]`。`op_callb`将会执行`top[0]=resume()`,所以栈顶的数据虽然被覆盖了一次,但是实际上还是原来的数据。
|
||||
|
||||
```C++
|
||||
+----------------------+(协程操作数栈)
|
||||
| pc:0(vm_ret) | <- now gc.top[0]
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
当我们调用`yield`的时候,该函数会执行出这个情况,我们发现`op_callb` 已经把`nil`放在的栈顶。但是应该返回的`local[1]`到底发送到哪里去了?
|
||||
|
||||
```C++
|
||||
+----------------------+(协程操作数栈)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
当`builtin_coyield`执行完毕之后,栈又切换到了主操作数栈上,这时可以看到返回的`local[1]`实际上被`op_callb`放在了这里的栈顶:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| return_value(var) |
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
所以主程序会认为顶部这个返回值好像是`resume`返回的。而实际上`resume`的返回值在协程的操作数栈顶。综上所述:
|
||||
|
||||
```C++
|
||||
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
|
||||
yield (coroutine->main) return a vector. main.top[0] = vector;
|
||||
```
|
||||
|
||||
## __发行日志__
|
||||
|
||||
### __version 8.0 release__
|
||||
|
||||
这个版本的发行版有个 __严重的问题__:
|
||||
|
||||
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
这个会导致不正确的`stackoverflow`报错。因为它覆盖了原有的变量。
|
||||
请修改为:
|
||||
|
||||
`canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
如果不修改这一行,调试器运行肯定是不正常的。在`v9.0`第一个commit中我们修复了这个问题。
|
||||
|
||||
另外一个bug在 `nasal_err.h:class nasal_err`这边,要给这个类添加一个构造函数来进行初始化,否则会出问题:
|
||||
|
||||
```C++
|
||||
nasal_err(): error(0) {}
|
||||
```
|
||||
|
||||
同样这个也在`v9.0`中修复了。所以我们建议不要使用`v8.0`。
|
||||
|
||||
### __version 11.0 release__
|
||||
|
||||
1. 使用C++标准 `std=c++17`。
|
||||
|
||||
2. 改变语法树设计模式,采用访问者模式。
|
||||
|
||||
3. 全新的语法树结构输出格式。
|
||||
|
||||
4. 改变了导出模块的方式,把主要的库分成了多个模块。以`_`开头的变量不会被导出。
|
||||
|
||||
5. 文件夹`stl`更名为`std`。
|
||||
|
||||
6. 添加交互式解释器 (REPL)。
|
||||
|
||||
7. 优化虚拟机结构, 将全局数据栈 (存储全局变量的数据) 和操作数据栈 (用于运算) 分离。
|
||||
|
||||
8. 删除`op_intg`指令,添加`op_repl`指令。
|
||||
|
||||
9. 添加`CMakeLists.txt` (可在`Visual Studio`中使用)。
|
||||
|
||||
10. 全新的自定义类型注册流程。
|
||||
|
||||
### __version 11.1 release__
|
||||
|
||||
1. Bug 修复: 修复 v11.0 的 debugger 无法启动的问题。
|
||||
|
||||
2. Bug 修复: symbol_finder 不检查 foreach/forindex 中的迭代变量声明的问题。
|
||||
|
||||
3. 扩展语法 `import.xx.xx` 改为 `use xx.xx`。
|
||||
BIN
doc/gif/dbg.gif
Normal file
|
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 |
88
doc/namespace.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 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
|
||||
};
|
||||
}();
|
||||
```
|
||||
|
||||
## Import a module
|
||||
|
||||
Here is a module named `std/example_module.nas`:
|
||||
|
||||
```nasal
|
||||
var a = 1;
|
||||
```
|
||||
|
||||
Then there's a script file named `test.nas`, import module in this file using this way:
|
||||
|
||||
```nasal
|
||||
use std.example_module;
|
||||
|
||||
println(example_module.a); # 1
|
||||
```
|
||||
|
||||
Or this way:
|
||||
|
||||
```nasal
|
||||
import("std/example_module.nas");
|
||||
|
||||
println(example_module.a); # 1
|
||||
```
|
||||
172
doc/nasal-http-test-web.html
Normal file
@@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title> nasal-http-test-web </title>
|
||||
<meta charset="utf-8">
|
||||
<meta author="ValKmjolnir">
|
||||
<style>
|
||||
body{
|
||||
background: white;
|
||||
width: 60%;
|
||||
}
|
||||
pre{
|
||||
background: #303030;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
h1,h2,h3{
|
||||
padding: 5px;
|
||||
background-color: #555588;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
}
|
||||
p{
|
||||
margin-left: 15px;
|
||||
}
|
||||
div.badges{
|
||||
text-align: center;
|
||||
}
|
||||
tr{
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Nasal | Not another scripting language!</h1>
|
||||
<img src="/doc/pic/social.png" width="900" height="400" style="margin-left: 15px;"><br /></img>
|
||||
<div class="badges">
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github"><br/></img></a>
|
||||
</div>
|
||||
<h2> 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 and burningship-figure generated by ppm script written in nasal.
|
||||
</p>
|
||||
<p>
|
||||
下方是使用 nasal 的 ppm 生成脚本生成的 feigenbaum 和 burningship 图形。
|
||||
</p>
|
||||
</text>
|
||||
<img src="/doc/pic/feigenbaum.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
|
||||
<img src="/doc/pic/burningship.png" width="900" height="550" style="margin-left: 15px;"><br /></img>
|
||||
<h2> 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 ','} ']'
|
||||
;
|
||||
@@ -40,8 +41,17 @@ exprs::=
|
||||
;
|
||||
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,7 +69,7 @@ multive_expr::=
|
||||
(unary|scalar) ('*' | '/') (unary|scalar)
|
||||
;
|
||||
unary::=
|
||||
('-'|'!') (unary|scalar)
|
||||
('-'|'!'|'~') (unary|scalar)
|
||||
;
|
||||
scalar::=
|
||||
function {call_scalar}
|
||||
@@ -68,6 +78,7 @@ scalar::=
|
||||
|number
|
||||
|string
|
||||
|nil
|
||||
|bool
|
||||
|'(' calculation ')' {call_scalar}
|
||||
;
|
||||
call_scalar::=
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
doc/pic/burningship.png
Normal file
|
After Width: | Height: | Size: 161 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 |
331
lib.nas
@@ -1,331 +0,0 @@
|
||||
# lib.nas
|
||||
|
||||
# import is used to link another file, this lib function will do nothing.
|
||||
# because nasal_import will recognize this and link files before generating bytecode.
|
||||
var import=func(filename){
|
||||
return __builtin_import(filename);
|
||||
}
|
||||
|
||||
# print is used to print all things in nasal, try and see how it works.
|
||||
# this function uses std::cout/printf to output logs.
|
||||
var print=func(elems...){
|
||||
return __builtin_print(elems);
|
||||
}
|
||||
|
||||
# append is used to add values into a vector.
|
||||
var append=func(vec,elems...){
|
||||
return __builtin_append(vec,elems);
|
||||
}
|
||||
|
||||
# setsize is used to change the size of vector.
|
||||
# if the size is larger than before,
|
||||
# this function will fill vm_nil into uninitialized space.
|
||||
var setsize=func(vec,size){
|
||||
return __builtin_setsize(vec,size);
|
||||
}
|
||||
|
||||
# system has the same use in C.
|
||||
var system=func(str){
|
||||
return __builtin_system(str);
|
||||
}
|
||||
|
||||
# input uses std::cin and returns what we input.
|
||||
var input=func(){
|
||||
return __builtin_input();
|
||||
}
|
||||
|
||||
# split a string by delimiter for example:
|
||||
# split("ll","hello world") -> ["he","o world"]
|
||||
# this function will return a vector.
|
||||
var split=func(deli,str){
|
||||
return __builtin_split(deli,str);
|
||||
}
|
||||
|
||||
# rand has the same function as the rand in C
|
||||
# if seed is nil, it will return the random number.
|
||||
# if seed is not nil, it will be initialized by this seed.
|
||||
var rand=func(seed=nil){
|
||||
return __builtin_rand(seed);
|
||||
}
|
||||
|
||||
# id will return the pointer of an gc-object.
|
||||
# if this object is not managed by gc, it will return 0.
|
||||
var id=func(object){
|
||||
return __builtin_id(object);
|
||||
}
|
||||
|
||||
# int will get the integer of input number.
|
||||
# but carefully use it, because int has range between -2147483648~2147483647
|
||||
var int=func(val){
|
||||
return __builtin_int(val);
|
||||
}
|
||||
|
||||
# num will change all the other types into number.
|
||||
# mostly used to change a numerable string.
|
||||
var num=func(val){
|
||||
return __builtin_num(val);
|
||||
}
|
||||
|
||||
# pop used to pop the last element in a vector.
|
||||
# this function will return the value that poped if vector has element(s).
|
||||
# if the vector is empty, it will return nil.
|
||||
var pop=func(vec){
|
||||
return __builtin_pop(vec);
|
||||
}
|
||||
|
||||
# str is used to change number into string.
|
||||
var str=func(num){
|
||||
return __builtin_str(num);
|
||||
}
|
||||
|
||||
# size can get the size of a string/vector/hashmap.
|
||||
# in fact it can also get the size of number, and the result is the number itself.
|
||||
# so don't do useless things, though it really works.
|
||||
var size=func(object){
|
||||
return __builtin_size(object);
|
||||
}
|
||||
|
||||
# contains is used to check if a key exists in a hashmap/dict.
|
||||
var contains=func(hash,key){
|
||||
return __builtin_contains(hash,key);
|
||||
}
|
||||
|
||||
# delete is used to delete a pair in a hashmap/dict by key.
|
||||
var delete=func(hash,key){
|
||||
return __builtin_delete(hash,key);
|
||||
}
|
||||
|
||||
# keys is used to get all keys in a hashmap/dict.
|
||||
# this function will return a vector.
|
||||
var keys=func(hash){
|
||||
return __builtin_keys(hash);
|
||||
}
|
||||
|
||||
# time has the same function in C.
|
||||
var time=func(begin){
|
||||
return __builtin_time(begin);
|
||||
}
|
||||
|
||||
# die is a special native function.
|
||||
# use it at where you want the program to crash immediately.
|
||||
var die=func(str){
|
||||
return __builtin_die(str);
|
||||
}
|
||||
|
||||
# typeof is used to get the type of an object.
|
||||
# this function returns a string.
|
||||
var typeof=func(object){
|
||||
return __builtin_type(object);
|
||||
}
|
||||
|
||||
# substr will get the sub-string.
|
||||
# it gets the string, the begin index and sub-string's length as arguments.
|
||||
var substr=func(str,begin,len){
|
||||
return __builtin_substr(str,begin,len);
|
||||
}
|
||||
|
||||
# streq is used to compare if two strings are the same.
|
||||
var streq=func(a,b){
|
||||
return __builtin_streq(a,b);
|
||||
}
|
||||
|
||||
# left is used to get the sub-string like substr.
|
||||
# but the begin index is 0.
|
||||
var left=func(str,len){
|
||||
return __builtin_left(str,len);
|
||||
}
|
||||
|
||||
# right i used to get the sub-string like substr.
|
||||
# but the begin index is strlen-len.
|
||||
var right=func(str,len){
|
||||
return __builtin_right(str,len);
|
||||
}
|
||||
|
||||
# cmp is used to compare two strings.
|
||||
# normal string will not be correctly compared by operators < > <= >=
|
||||
# because these operators will turn strings into numbers then compare.
|
||||
var cmp=func(a,b){
|
||||
return __builtin_cmp(a,b);
|
||||
}
|
||||
|
||||
# chr is used to get the character by ascii-number.
|
||||
# for example chr(65) -> 'A'
|
||||
var chr=func(code){
|
||||
return __builtin_chr(code);
|
||||
}
|
||||
|
||||
# println has the same function as print.
|
||||
# but it will output a '\n' after using print.
|
||||
var println=func(elems...){
|
||||
__builtin_print(elems);
|
||||
elems=['\n'];
|
||||
return __builtin_print(elems);
|
||||
}
|
||||
|
||||
var io=
|
||||
{
|
||||
SEEK_SET:0,
|
||||
SEEK_CUR:1,
|
||||
SEEK_END:2,
|
||||
# get content of a file by filename. returns a string.
|
||||
fin: func(filename){return __builtin_fin(filename);},
|
||||
# input a string as the content of a file.
|
||||
fout: func(filename,str){return __builtin_fout(filename,str);},
|
||||
# same as C fopen. open file and get the FILE*.
|
||||
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
|
||||
# same as C fclose. close file by FILE*.
|
||||
close: func(filehandle){return __builtin_close(filehandle);},
|
||||
# same as C fread. read file by FILE*.
|
||||
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
|
||||
# same as C fwrite. write file by FILE*.
|
||||
write: func(filehandle,str){return __builtin_write(filehandle,str);},
|
||||
# same as C fseek. seek place by FILE*.
|
||||
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
|
||||
# same as C ftell.
|
||||
tell: func(filehandle){return __builtin_tell(filehandle);},
|
||||
# read file by lines. use FILE*.
|
||||
# get nil if EOF
|
||||
readln:func(filehandle){return __builtin_readln(filehandle);},
|
||||
# same as C stat.
|
||||
stat: func(filename){return __builtin_stat(filename);},
|
||||
# same as C feof. check if FILE* gets the end of file(EOF).
|
||||
eof: func(filehandle){return __builtin_eof(filehandle);}
|
||||
};
|
||||
|
||||
# get file status. using data from io.stat
|
||||
var fstat=func(filename){
|
||||
var s=io.stat(filename);
|
||||
return {
|
||||
st_dev: s[0],
|
||||
st_ino: s[1],
|
||||
st_mode: s[2],
|
||||
st_nlink:s[3],
|
||||
st_uid: s[4],
|
||||
st_gid: s[5],
|
||||
st_rdev: s[6],
|
||||
st_size: s[7],
|
||||
st_atime:s[8],
|
||||
st_mtime:s[9],
|
||||
st_ctime:s[10]
|
||||
};
|
||||
}
|
||||
|
||||
# functions that do bitwise calculation.
|
||||
# carefully use it, all the calculations are based on integer.
|
||||
var bits=
|
||||
{
|
||||
# xor
|
||||
bitxor: func(a,b){return __builtin_xor(a,b); },
|
||||
# and
|
||||
bitand: func(a,b){return __builtin_and(a,b); },
|
||||
# or
|
||||
bitor: func(a,b){return __builtin_or(a,b); },
|
||||
# nand
|
||||
bitnand: func(a,b){return __builtin_nand(a,b);},
|
||||
# not
|
||||
bitnot: func(a) {return __builtin_not(a); },
|
||||
# get bit data from a special string. for example:
|
||||
# bits.fld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5).
|
||||
fld: func(str,startbit,len){return __builtin_fld;},
|
||||
# get sign-extended data from a special string. for example:
|
||||
# bits.sfld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5) then this will be signed extended to
|
||||
# 11111101(-3).
|
||||
sfld: func(str,startbit,len){return __builtin_sfld;},
|
||||
# set value into a special string to store it. little-endian, for example:
|
||||
# bits.setfld(s,0,8,69);
|
||||
# set 01000101(69) to string will get this:
|
||||
# 10100010(162)
|
||||
# so s[0]=162.
|
||||
setfld: func(str,startbit,len,val){return __builtin_setfld;},
|
||||
# get a special string filled by '\0' to use in setfld.
|
||||
buf: func(len){return __builtin_buf;}
|
||||
};
|
||||
|
||||
# mostly used math functions and special constants, you know.
|
||||
var math=
|
||||
{
|
||||
e: 2.7182818284590452354,
|
||||
pi: 3.14159265358979323846264338327950288,
|
||||
inf: 1/0,
|
||||
nan: 0/0,
|
||||
sin: func(x) {return __builtin_sin(x); },
|
||||
cos: func(x) {return __builtin_cos(x); },
|
||||
tan: func(x) {return __builtin_tan(x); },
|
||||
exp: func(x) {return __builtin_exp(x); },
|
||||
lg: func(x) {return __builtin_lg(x); },
|
||||
ln: func(x) {return __builtin_ln(x); },
|
||||
sqrt: func(x) {return __builtin_sqrt(x); },
|
||||
atan2: func(x,y){return __builtin_atan2(x,y);},
|
||||
isnan: func(x) {return __builtin_isnan(x); }
|
||||
};
|
||||
|
||||
var unix=
|
||||
{
|
||||
pipe: func(){die("not supported yet");},
|
||||
fork: func(){die("not supported yet");},
|
||||
dup2: func(fd0,fd1){die("not supported yet");},
|
||||
exec: func(filename,argv,envp){die("not supported yet");},
|
||||
waitpid: func(pid,nohang=0){die("not supported yet");},
|
||||
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
|
||||
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
|
||||
opendir: func(path){return __builtin_opendir;},
|
||||
readdir: func(handle){return __builtin_readdir;},
|
||||
closedir: func(handle){return __builtin_closedir;},
|
||||
time: func(){return time(0);},
|
||||
sleep: func(secs){return __builtin_sleep(secs);},
|
||||
chdir: func(path){return __builtin_chdir(path);},
|
||||
environ: func(){die("not supported yet");},
|
||||
getcwd: func(){return __builtin_getcwd();},
|
||||
getenv: func(envvar){return __builtin_getenv(envvar);}
|
||||
};
|
||||
|
||||
# dylib is the core hashmap for developers to load their own library.
|
||||
var dylib=
|
||||
{
|
||||
# open dynamic lib.
|
||||
dlopen: func(libname){return __builtin_dlopen;},
|
||||
# load symbol from an open dynamic lib.
|
||||
dlsym: func(lib,sym){return __builtin_dlsym; },
|
||||
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
|
||||
dlclose: func(lib){return __builtin_dlclose; },
|
||||
# call the loaded symbol.
|
||||
dlcall: func(funcptr,args...){return __builtin_dlcall}
|
||||
};
|
||||
|
||||
# os is used to use or get some os-related info/functions.
|
||||
# windows/macOS/linux are supported.
|
||||
var os=
|
||||
{
|
||||
# get a string that tell which os it runs on.
|
||||
platform: func(){return __builtin_platform;}
|
||||
};
|
||||
|
||||
# runtime gives us some functions that we could manage it manually.
|
||||
var runtime=
|
||||
{
|
||||
# do garbage collection manually.
|
||||
# carefully use it because using it frequently may make program running slower.
|
||||
gc: func(){return __builtin_gc;}
|
||||
};
|
||||
|
||||
# important global constants
|
||||
var D2R=math.pi/180;
|
||||
var FPS2KT=0.5925;
|
||||
var FT2M=0.3048;
|
||||
var GAL2L=3.7854;
|
||||
var IN2M=0.0254;
|
||||
var KG2LB=2.2046;
|
||||
var KT2FPS=1.6878;
|
||||
var KT2MPS=0.5144;
|
||||
var L2GAL=0.2642;
|
||||
var LB2KG=0.4536;
|
||||
var M2FT=3.2808;
|
||||
var M2IN=39.3701;
|
||||
var M2NM=0.00054;
|
||||
var MPS2KT=1.9438;
|
||||
var NM2M=1852;
|
||||
var R2D=180/math.pi;
|
||||
162
main.cpp
@@ -1,162 +0,0 @@
|
||||
#include "nasal.h"
|
||||
|
||||
const uint32_t VM_LEXINFO =1;
|
||||
const uint32_t VM_ASTINFO =2;
|
||||
const uint32_t VM_CODEINFO =4;
|
||||
const uint32_t VM_EXECTIME =8;
|
||||
const uint32_t VM_OPCALLNUM=16;
|
||||
const uint32_t VM_EXEC =32;
|
||||
const uint32_t VM_DBGINFO =64;
|
||||
const uint32_t VM_DEBUG =128;
|
||||
const uint32_t VM_OPTIMIZE =256;
|
||||
|
||||
void help()
|
||||
{
|
||||
std::cout
|
||||
#ifdef _WIN32
|
||||
<<"use command \'chcp 65001\' if want to use unicode.\n"
|
||||
#endif
|
||||
<<"nasal <option>\n"
|
||||
<<"option:\n"
|
||||
<<" -h, --help | get help.\n"
|
||||
<<" -v, --version | get version of nasal interpreter.\n\n"
|
||||
<<"nasal <file>\n"
|
||||
<<"file:\n"
|
||||
<<" input file name to execute script file.\n\n"
|
||||
<<"nasal [options...] <file>\n"
|
||||
<<"option:\n"
|
||||
<<" -l, --lex | view token info.\n"
|
||||
<<" -a, --ast | view abstract syntax tree.\n"
|
||||
<<" -c, --code | view bytecode.\n"
|
||||
<<" -e, --exec | execute.\n"
|
||||
<<" -t, --time | execute and get the running time.\n"
|
||||
<<" -o, --opcnt | execute and count used operands.\n"
|
||||
<<" -d, --detail | execute and get detail crash info.\n"
|
||||
<<" | get garbage collector info if didn't crash.\n"
|
||||
<<" -op, --optimize| use optimizer(beta).\n"
|
||||
<<" | if want to use -op and run, please use -op -e/-t/-o/-d.\n"
|
||||
<<" -dbg, --debug | debug mode (this will ignore -t -o -d).\n"
|
||||
<<"file:\n"
|
||||
<<" input file name to execute script file.\n";
|
||||
}
|
||||
|
||||
void logo()
|
||||
{
|
||||
std::cout
|
||||
<<" __ _ \n"
|
||||
<<" /\\ \\ \\__ _ ___ __ _| | \n"
|
||||
<<" / \\/ / _` / __|/ _` | | \n"
|
||||
<<" / /\\ / (_| \\__ \\ (_| | | \n"
|
||||
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<<"nasal interpreter ver 8.0\n"
|
||||
<<"thanks to : https://github.com/andyross/nasal\n"
|
||||
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<<"lang info : http://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<<"input <nasal -h> to get help .\n";
|
||||
}
|
||||
|
||||
void err()
|
||||
{
|
||||
std::cout
|
||||
<<"invalid argument(s).\n"
|
||||
<<"use <nasal -h> to get help.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void execute(const std::string& file,const uint32_t cmd)
|
||||
{
|
||||
// front end use the same error module
|
||||
nasal_err nerr;
|
||||
nasal_lexer lexer(nerr);
|
||||
nasal_parse parse(nerr);
|
||||
nasal_import linker(nerr);
|
||||
nasal_codegen gen(nerr);
|
||||
// back end
|
||||
nasal_vm vm;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lexer.scan(file);
|
||||
if(cmd&VM_LEXINFO)
|
||||
lexer.print();
|
||||
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lexer);
|
||||
// linker gets parser's ast and load import files to this ast
|
||||
linker.link(parse,file);
|
||||
if(cmd&VM_ASTINFO)
|
||||
parse.print();
|
||||
|
||||
// optimizer does simple optimization on ast
|
||||
if(cmd&VM_OPTIMIZE)
|
||||
optimize(parse.ast());
|
||||
|
||||
// code generator gets parser's ast and linker's import file list to generate code
|
||||
gen.compile(parse,linker);
|
||||
if(cmd&VM_CODEINFO)
|
||||
gen.print();
|
||||
|
||||
// run bytecode
|
||||
if(cmd&VM_DEBUG)
|
||||
{
|
||||
nasal_dbg debugger;
|
||||
debugger.run(gen,linker);
|
||||
}
|
||||
else if(cmd&VM_EXECTIME)
|
||||
{
|
||||
clock_t t=clock();
|
||||
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
|
||||
std::cout<<"process exited after "<<((double)(clock()-t))/CLOCKS_PER_SEC<<"s.\n";
|
||||
}
|
||||
else if(cmd&VM_EXEC)
|
||||
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
|
||||
}
|
||||
|
||||
int main(int argc,const char* argv[])
|
||||
{
|
||||
if(argc<=1)
|
||||
{
|
||||
logo();
|
||||
return 0;
|
||||
}
|
||||
if(argc==2)
|
||||
{
|
||||
std::string s(argv[1]);
|
||||
if(s=="-v" || s=="--version")
|
||||
logo();
|
||||
else if(s=="-h" || s=="--help")
|
||||
help();
|
||||
else if(s[0]!='-')
|
||||
execute(s,VM_EXEC);
|
||||
else
|
||||
err();
|
||||
return 0;
|
||||
}
|
||||
uint32_t cmd=0;
|
||||
for(int i=1;i<argc-1;++i)
|
||||
{
|
||||
std::string s(argv[i]);
|
||||
if(s=="--lex" || s=="-l")
|
||||
cmd|=VM_LEXINFO;
|
||||
else if(s=="--ast" || s=="-a")
|
||||
cmd|=VM_ASTINFO;
|
||||
else if(s=="--code" || s=="-c")
|
||||
cmd|=VM_CODEINFO;
|
||||
else if(s=="--exec" || s=="-e")
|
||||
cmd|=VM_EXEC;
|
||||
else if(s=="--opcnt" || s=="-o")
|
||||
cmd|=VM_OPCALLNUM|VM_EXEC;
|
||||
else if(s=="--time" || s=="-t")
|
||||
cmd|=VM_EXECTIME;
|
||||
else if(s=="--detail" || s=="-d")
|
||||
cmd|=VM_DBGINFO|VM_EXEC;
|
||||
else if(s=="--optimize" || s=="-op")
|
||||
cmd|=VM_OPTIMIZE;
|
||||
else if(s=="--debug" || s=="-dbg")
|
||||
cmd|=VM_DEBUG;
|
||||
else
|
||||
err();
|
||||
}
|
||||
execute(argv[argc-1],cmd);
|
||||
return 0;
|
||||
}
|
||||
311
makefile
@@ -1,34 +1,277 @@
|
||||
.PHONY=test
|
||||
nasal:main.cpp nasal_ast.h nasal_err.h nasal_builtin.h nasal_opt.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal_dbg.h nasal.h
|
||||
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
||||
test:nasal
|
||||
./nasal -op -e test/ascii-art.nas
|
||||
./nasal -op -c test/bf.nas
|
||||
./nasal -op -c test/bfconvertor.nas
|
||||
./nasal -op -e -d test/bfs.nas
|
||||
./nasal -op -t test/bigloop.nas
|
||||
./nasal -op -e test/bp.nas
|
||||
./nasal -op -e -d test/calc.nas
|
||||
./nasal -op -e test/choice.nas
|
||||
./nasal -op -e test/class.nas
|
||||
./nasal -op -c test/exception.nas
|
||||
./nasal -op -t -d test/fib.nas
|
||||
./nasal -op -e test/filesystem.nas
|
||||
./nasal -op -e -d test/hexdump.nas
|
||||
./nasal -op -e test/json.nas
|
||||
./nasal -op -e test/leetcode1319.nas
|
||||
./nasal -op -e -d test/lexer.nas
|
||||
./nasal -op -e -d test/life.nas
|
||||
./nasal -op -t test/loop.nas
|
||||
./nasal -op -t -d test/mandel.nas
|
||||
./nasal -op -t -d test/mandelbrot.nas
|
||||
./nasal -op -c test/module_test.nas
|
||||
./nasal -op -e test/nasal_test.nas
|
||||
./nasal -op -t -d test/pi.nas
|
||||
./nasal -op -t -d test/prime.nas
|
||||
./nasal -op -t -d test/quick_sort.nas
|
||||
./nasal -op -e test/scalar.nas
|
||||
./nasal -op -e test/trait.nas
|
||||
./nasal -op -t -d test/turingmachine.nas
|
||||
./nasal -op -t -d test/ycombinator.nas
|
||||
|
||||
STD = c++17
|
||||
|
||||
ifndef OS
|
||||
OS = $(shell uname)
|
||||
endif
|
||||
ifeq ($(OS), Darwin)
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
|
||||
else
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
|
||||
endif
|
||||
|
||||
NASAL_HEADER=\
|
||||
src/ast_dumper.h\
|
||||
src/ast_visitor.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_builtin.h\
|
||||
src/nasal_codegen.h\
|
||||
src/nasal_dbg.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_gc.h\
|
||||
src/nasal_import.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_opcode.h\
|
||||
src/nasal_parse.h\
|
||||
src/nasal_vm.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal.h\
|
||||
src/optimizer.h\
|
||||
src/symbol_finder.h\
|
||||
src/fg_props.h\
|
||||
src/bits_lib.h\
|
||||
src/io_lib.h\
|
||||
src/math_lib.h\
|
||||
src/dylib_lib.h\
|
||||
src/unix_lib.h\
|
||||
src/coroutine.h\
|
||||
src/repl.h
|
||||
|
||||
NASAL_OBJECT=\
|
||||
build/nasal_err.o\
|
||||
build/nasal_ast.o\
|
||||
build/ast_visitor.o\
|
||||
build/bits_lib.o\
|
||||
build/ast_dumper.o\
|
||||
build/nasal_lexer.o\
|
||||
build/nasal_parse.o\
|
||||
build/nasal_import.o\
|
||||
build/optimizer.o\
|
||||
build/nasal_opcode.o\
|
||||
build/symbol_finder.o\
|
||||
build/nasal_codegen.o\
|
||||
build/nasal_misc.o\
|
||||
build/nasal_gc.o\
|
||||
build/nasal_builtin.o\
|
||||
build/fg_props.o\
|
||||
build/io_lib.o\
|
||||
build/math_lib.o\
|
||||
build/unix_lib.o\
|
||||
build/dylib_lib.o\
|
||||
build/coroutine.o\
|
||||
build/nasal_type.o\
|
||||
build/nasal_vm.o\
|
||||
build/nasal_dbg.o\
|
||||
build/repl.o\
|
||||
build/main.o
|
||||
|
||||
|
||||
# for test
|
||||
nasal: $(NASAL_OBJECT) | build
|
||||
@if [ OS = "Darwin" ]; then\
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread -stdlib=libc++ -static-libstdc++;\
|
||||
else\
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl -lpthread;\
|
||||
fi
|
||||
|
||||
nasal.exe: $(NASAL_OBJECT) | build
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
||||
|
||||
build:
|
||||
@ if [ ! -d build ]; then mkdir build; fi
|
||||
|
||||
build/main.o: $(NASAL_HEADER) src/main.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/main.cpp -o build/main.o
|
||||
|
||||
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_misc.cpp -o build/nasal_misc.o
|
||||
|
||||
build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/repl.cpp -o build/repl.o
|
||||
|
||||
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
|
||||
|
||||
build/nasal_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o
|
||||
|
||||
build/nasal_gc.o: src/nasal.h src/nasal_type.h src/nasal_gc.h src/nasal_gc.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
||||
|
||||
build/nasal_import.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_parse.h\
|
||||
src/nasal_import.h src/nasal_import.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_import.cpp -o build/nasal_import.o
|
||||
|
||||
build/nasal_lexer.o: \
|
||||
src/nasal.h\
|
||||
src/repl.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_lexer.h src/nasal_lexer.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_lexer.cpp -o build/nasal_lexer.o
|
||||
|
||||
build/nasal_ast.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h src/nasal_ast.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_ast.cpp -o build/nasal_ast.o
|
||||
|
||||
build/nasal_builtin.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
||||
|
||||
build/coroutine.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/coroutine.h src/coroutine.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
|
||||
|
||||
build/bits_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/bits_lib.h src/bits_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
|
||||
|
||||
|
||||
build/math_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/math_lib.h src/math_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
|
||||
|
||||
build/io_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/io_lib.h src/io_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
|
||||
|
||||
build/dylib_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/dylib_lib.h src/dylib_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
|
||||
|
||||
build/unix_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/unix_lib.h src/unix_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
|
||||
|
||||
build/fg_props.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/fg_props.h src/fg_props.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
|
||||
|
||||
build/nasal_codegen.o: $(NASAL_HEADER) src/nasal_codegen.h src/nasal_codegen.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_codegen.cpp -o build/nasal_codegen.o
|
||||
|
||||
build/nasal_opcode.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_builtin.h\
|
||||
src/nasal_opcode.h src/nasal_opcode.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_opcode.cpp -o build/nasal_opcode.o
|
||||
|
||||
build/nasal_parse.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_ast.h\
|
||||
src/nasal_lexer.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_parse.cpp -o build/nasal_parse.o
|
||||
|
||||
build/optimizer.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/optimizer.cpp -o build/optimizer.o
|
||||
|
||||
build/symbol_finder.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
|
||||
$(CXX) $(CXXFLAGS) src/symbol_finder.cpp -o build/symbol_finder.o
|
||||
|
||||
build/ast_visitor.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h src/ast_visitor.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/ast_visitor.cpp -o build/ast_visitor.o
|
||||
|
||||
build/ast_dumper.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_ast.h\
|
||||
src/ast_visitor.h\
|
||||
src/ast_dumper.h src/ast_dumper.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/ast_dumper.cpp -o build/ast_dumper.o
|
||||
|
||||
build/nasal_vm.o: $(NASAL_HEADER) src/nasal_vm.h src/nasal_vm.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_vm.cpp -o build/nasal_vm.o
|
||||
|
||||
build/nasal_dbg.o: $(NASAL_HEADER) src/nasal_dbg.h src/nasal_dbg.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_dbg.cpp -o build/nasal_dbg.o
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
|
||||
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
|
||||
@ rm $(NASAL_OBJECT)
|
||||
|
||||
.PHONY: test
|
||||
test:nasal
|
||||
@ ./nasal -e test/ascii-art.nas
|
||||
@ ./nasal -t -d test/bfs.nas
|
||||
@ ./nasal -t test/bigloop.nas
|
||||
@ ./nasal -t test/bp.nas
|
||||
@ ./nasal -d test/calc.nas
|
||||
@ ./nasal -e test/choice.nas
|
||||
@ ./nasal -e test/class.nas
|
||||
@ ./nasal -t -d test/console3D.nas 20
|
||||
@ ./nasal -e test/coroutine.nas
|
||||
@ ./nasal -t -d test/datalog.nas
|
||||
@ ./nasal -e test/diff.nas
|
||||
@ ./nasal -e test/donuts.nas 15
|
||||
-@ ./nasal -d test/exception.nas
|
||||
@ ./nasal -t -d test/fib.nas
|
||||
@ ./nasal -e test/filesystem.nas
|
||||
@ ./nasal -t -d test/globals_test.nas
|
||||
@ ./nasal -d test/hexdump.nas
|
||||
@ ./nasal -e test/json.nas
|
||||
@ ./nasal -e test/leetcode1319.nas
|
||||
@ ./nasal -d test/lexer.nas
|
||||
@ ./nasal -d test/life.nas
|
||||
@ ./nasal -t test/loop.nas
|
||||
@ ./nasal -t test/mandelbrot.nas
|
||||
@ ./nasal -t test/md5_self.nas
|
||||
@ ./nasal -t -d test/md5compare.nas
|
||||
@ ./nasal -d test/module_test.nas
|
||||
@ ./nasal -e test/nasal_test.nas
|
||||
@ ./nasal -t -d test/occupation.nas 2
|
||||
@ ./nasal -t -d test/pi.nas
|
||||
@ ./nasal -t -d test/prime.nas
|
||||
@ ./nasal -e test/qrcode.nas
|
||||
@ ./nasal -t -d test/quick_sort.nas
|
||||
@ ./nasal -e test/scalar.nas hello world
|
||||
@ ./nasal -e test/trait.nas
|
||||
@ ./nasal -t -d test/turingmachine.nas
|
||||
@ ./nasal -d test/wavecollapse.nas
|
||||
@ ./nasal test/word_collector.nas test/md5compare.nas
|
||||
@ ./nasal -t -d test/ycombinator.nas
|
||||
|
||||
126
module/fib.cpp
@@ -1,30 +1,98 @@
|
||||
#include <iostream>
|
||||
#include "../nasal.h"
|
||||
|
||||
double fibonaci(double x){
|
||||
if(x<=2)
|
||||
return x;
|
||||
return fibonaci(x-1)+fibonaci(x-2);
|
||||
}
|
||||
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
|
||||
std::cout<<"[mod] this is the first test module of nasal\n";
|
||||
nasal_ref num=args[0];
|
||||
if(num.type!=vm_num)
|
||||
return builtin_err("extern_fib","\"num\" must be number");
|
||||
return {vm_num,fibonaci(num.to_number())};
|
||||
}
|
||||
extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
|
||||
std::cout<<"[mod] this is the first test module of nasal\n";
|
||||
nasal_ref num=args[0];
|
||||
if(num.type!=vm_num)
|
||||
return builtin_err("extern_quick_fib","\"num\" must be number");
|
||||
if(num.num()<2)
|
||||
return num;
|
||||
double a=1,b=1,res=0;
|
||||
for(double i=1;i<num.num();i+=1){
|
||||
res=a+b;
|
||||
a=b;
|
||||
b=res;
|
||||
}
|
||||
return {vm_num,res};
|
||||
// module for test
|
||||
|
||||
#include <iostream>
|
||||
#include "../src/nasal.h"
|
||||
#include "../src/nasal_type.h"
|
||||
#include "../src/nasal_gc.h"
|
||||
|
||||
namespace nasal {
|
||||
namespace fib_module {
|
||||
|
||||
double fibonaci(double x) {
|
||||
if (x<=2) {
|
||||
return x;
|
||||
}
|
||||
return fibonaci(x-1)+fibonaci(x-2);
|
||||
}
|
||||
|
||||
var fib(var* args, usize size, gc* ngc) {
|
||||
if (!size) {
|
||||
return nas_err("fib", "lack arguments");
|
||||
}
|
||||
var num = args[0];
|
||||
return var::num(fibonaci(num.to_num()));
|
||||
}
|
||||
|
||||
var quick_fib(var* args, usize size, gc* ngc) {
|
||||
if (!size) {
|
||||
return nas_err("quick_fib","lack arguments");
|
||||
}
|
||||
double num = args[0].to_num();
|
||||
if (num<2) {
|
||||
return var::num(num);
|
||||
}
|
||||
double a = 1, b = 1, res = 0;
|
||||
for(double i = 1; i<num; ++i) {
|
||||
res = a+b;
|
||||
a = b;
|
||||
b = res;
|
||||
}
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
const auto ghost_for_test = "ghost_for_test";
|
||||
|
||||
void ghost_for_test_destructor(void* ptr) {
|
||||
std::cout << "ghost_for_test::destructor (0x";
|
||||
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
|
||||
delete static_cast<u32*>(ptr);
|
||||
std::cout << " delete 0x" << std::hex;
|
||||
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
|
||||
std::cout << "}\n";
|
||||
}
|
||||
|
||||
var create_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_obj);
|
||||
res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
|
||||
return res;
|
||||
}
|
||||
|
||||
var set_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = args[0];
|
||||
if (!res.object_check(ghost_for_test)) {
|
||||
std::cout << "set_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
f64 num = args[1].num();
|
||||
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
|
||||
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
var print_new_ghost(var* args, usize size, gc* ngc) {
|
||||
var res = args[0];
|
||||
if (!res.object_check(ghost_for_test)) {
|
||||
std::cout << "print_new_ghost: not ghost for test type.\n";
|
||||
return nil;
|
||||
}
|
||||
std::cout << "print_new_ghost: " << res.ghost() << " result = "
|
||||
<< *((u32*)res.ghost().pointer) << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"fib", fib},
|
||||
{"quick_fib", quick_fib},
|
||||
{"create_ghost", create_new_ghost},
|
||||
{"set_ghost", set_new_ghost},
|
||||
{"print_ghost", print_new_ghost},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return fib_module::func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
113
module/keyboard.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "../src/nasal.h"
|
||||
#include "../src/nasal_type.h"
|
||||
#include "../src/nasal_gc.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class noecho_input {
|
||||
private:
|
||||
#ifndef _WIN32
|
||||
struct termios init_termios;
|
||||
struct termios new_termios;
|
||||
int peek_char = -1;
|
||||
#endif
|
||||
public:
|
||||
noecho_input() {
|
||||
#ifndef _WIN32
|
||||
tcflush(0, TCIOFLUSH);
|
||||
tcgetattr(0, &init_termios);
|
||||
new_termios = init_termios;
|
||||
new_termios.c_lflag &= ~(ICANON|ECHO|ECHONL|ECHOE);
|
||||
// vmin=0 is nonblock input, but in wsl there is a bug that will block input
|
||||
// so we use fcntl to write the nonblock input
|
||||
new_termios.c_cc[VMIN] = 1;
|
||||
new_termios.c_cc[VTIME] = 0;
|
||||
tcsetattr(0, TCSANOW, &new_termios);
|
||||
#endif
|
||||
}
|
||||
|
||||
~noecho_input() {
|
||||
#ifndef _WIN32
|
||||
tcflush(0, TCIOFLUSH);
|
||||
tcsetattr(0, TCSANOW, &init_termios);
|
||||
#endif
|
||||
}
|
||||
|
||||
int noecho_kbhit() {
|
||||
#ifndef _WIN32
|
||||
unsigned char ch = 0;
|
||||
int nread = 0;
|
||||
if (peek_char!=-1) {
|
||||
return 1;
|
||||
}
|
||||
int flag = fcntl(0, F_GETFL);
|
||||
fcntl(0, F_SETFL, flag|O_NONBLOCK);
|
||||
nread = read(0, &ch, 1);
|
||||
fcntl(0, F_SETFL, flag);
|
||||
if (nread==1) {
|
||||
peek_char = ch;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return kbhit();
|
||||
#endif
|
||||
}
|
||||
|
||||
int noecho_getch() {
|
||||
#ifndef _WIN32
|
||||
int ch = 0;
|
||||
if (peek_char!=-1) {
|
||||
ch = peek_char;
|
||||
peek_char = -1;
|
||||
return ch;
|
||||
}
|
||||
ssize_t tmp = read(0, &ch, 1);
|
||||
return ch;
|
||||
#else
|
||||
return getch();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
noecho_input this_window;
|
||||
|
||||
var nas_getch(var* args, usize size, gc* ngc) {
|
||||
return var::num(static_cast<double>(this_window.noecho_getch()));
|
||||
}
|
||||
|
||||
var nas_kbhit(var* args, usize size, gc* ngc) {
|
||||
return var::num(static_cast<double>(this_window.noecho_kbhit()));
|
||||
}
|
||||
|
||||
var nas_noblock(var* args, usize size, gc* ngc) {
|
||||
if (this_window.noecho_kbhit()) {
|
||||
return var::num(static_cast<double>(this_window.noecho_getch()));
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_getch", nas_getch},
|
||||
{"nas_kbhit", nas_kbhit},
|
||||
{"nas_noblock", nas_noblock},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
54
module/libfib.nas
Normal file
@@ -0,0 +1,54 @@
|
||||
use std.dylib;
|
||||
|
||||
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
|
||||
var _fib = _dl.fib;
|
||||
|
||||
var _qfib = _dl.quick_fib;
|
||||
|
||||
var _create_ghost = _dl.create_ghost;
|
||||
|
||||
var _set_ghost = _dl.set_ghost;
|
||||
|
||||
var _print_ghost = _dl.print_ghost;
|
||||
|
||||
var _zero_call = dylib.limitcall(0);
|
||||
|
||||
var _call = dylib.limitcall(1);
|
||||
|
||||
var _test_call = dylib.limitcall(2);
|
||||
|
||||
|
||||
var fib = func(x) {
|
||||
return _call(_fib, x)
|
||||
}
|
||||
|
||||
|
||||
var qfib = func(x) {
|
||||
return _call(_qfib, x)
|
||||
}
|
||||
|
||||
|
||||
var create_ghost = func() {
|
||||
return _zero_call(_create_ghost)
|
||||
}
|
||||
|
||||
|
||||
var set_ghost = func(object, x) {
|
||||
return _test_call(_set_ghost, object, x)
|
||||
}
|
||||
|
||||
|
||||
var print_ghost = func(object) {
|
||||
return _call(_print_ghost, object)
|
||||
}
|
||||
|
||||
|
||||
var test_ghost=func() {
|
||||
var ghost = create_ghost();
|
||||
print_ghost(nil); # err
|
||||
print_ghost(ghost); # random
|
||||
set_ghost(nil, 114); # err
|
||||
set_ghost(ghost, 114); # success
|
||||
print_ghost(ghost); # 114
|
||||
}
|
||||
18
module/libkey.nas
Normal file
@@ -0,0 +1,18 @@
|
||||
use std.dylib;
|
||||
|
||||
var (
|
||||
kbhit,
|
||||
getch,
|
||||
nonblock
|
||||
) = func {
|
||||
var lib = dylib.dlopen("libkey"~(os.platform()=="windows"? ".dll":".so"));
|
||||
var kb = lib.nas_kbhit;
|
||||
var gt = lib.nas_getch;
|
||||
var nb = lib.nas_noblock;
|
||||
var call = dylib.limitcall(0);
|
||||
return [
|
||||
func(){return call(kb);},
|
||||
func(){return call(gt);},
|
||||
func(){return call(nb);}
|
||||
];
|
||||
}();
|
||||
66
module/libmat.nas
Normal file
@@ -0,0 +1,66 @@
|
||||
use std.dylib;
|
||||
|
||||
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));
|
||||
|
||||
var _vec2 = _dl.nas_vec2;
|
||||
|
||||
var _vec3 = _dl.nas_vec3;
|
||||
|
||||
var (_vec2add, _vec2sub, _vec2mul, _vec2div, _vec2neg, _vec2norm, _vec2len, _vec2dot)=(
|
||||
_dl.nas_vec2_add,
|
||||
_dl.nas_vec2_sub,
|
||||
_dl.nas_vec2_mult,
|
||||
_dl.nas_vec2_div,
|
||||
_dl.nas_vec2_neg,
|
||||
_dl.nas_vec2_norm,
|
||||
_dl.nas_vec2_len,
|
||||
_dl.nas_vec2_dot
|
||||
);
|
||||
var (_vec3add, _vec3sub, _vec3mul, _vec3div, _vec3neg, _vec3norm, _vec3len, _vec3dot)=(
|
||||
_dl.nas_vec3_add,
|
||||
_dl.nas_vec3_sub,
|
||||
_dl.nas_vec3_mult,
|
||||
_dl.nas_vec3_div,
|
||||
_dl.nas_vec3_neg,
|
||||
_dl.nas_vec3_norm,
|
||||
_dl.nas_vec3_len,
|
||||
_dl.nas_vec3_dot
|
||||
);
|
||||
var (_rotate_x, _rotate_y, _rotate_z)=(
|
||||
_dl.nas_rotate_x,
|
||||
_dl.nas_rotate_y,
|
||||
_dl.nas_rotate_z
|
||||
);
|
||||
var (_invoke_i, _invoke_ii, _invoke_iii)=(
|
||||
dylib.limitcall(1),
|
||||
dylib.limitcall(2),
|
||||
dylib.limitcall(3)
|
||||
);
|
||||
|
||||
var vec2 = {
|
||||
new: func(x, y) {return _invoke_ii(_vec2, x, y);},
|
||||
add: func(v0, v1) {return _invoke_ii(_vec2add, v0, v1);},
|
||||
sub: func(v0, v1) {return _invoke_ii(_vec2sub, v0, v1);},
|
||||
mul: func(v0, v1) {return _invoke_ii(_vec2mul, v0, v1);},
|
||||
div: func(v0, v1) {return _invoke_ii(_vec2div, v0, v1);},
|
||||
neg: func(v0) {return _invoke_i(_vec2neg, v0);},
|
||||
norm: func(v0) {return _invoke_i(_vec2norm, v0);},
|
||||
len: func(v0) {return _invoke_i(_vec2len, v0);},
|
||||
dot: func(v0, v1) {return _invoke_ii(_vec2dot, v0, v1);}
|
||||
};
|
||||
|
||||
var vec3 = {
|
||||
new: func(x, y, z) {return _invoke_iii(_vec3, x, y, z);},
|
||||
add: func(v0, v1) {return _invoke_ii(_vec3add, v0, v1);},
|
||||
sub: func(v0, v1) {return _invoke_ii(_vec3sub, v0, v1);},
|
||||
mul: func(v0, v1) {return _invoke_ii(_vec3mul, v0, v1);},
|
||||
div: func(v0, v1) {return _invoke_ii(_vec3div, v0, v1);},
|
||||
neg: func(v0) {return _invoke_i(_vec3neg, v0);},
|
||||
norm: func(v0) {return _invoke_i(_vec3norm, v0);},
|
||||
len: func(v0) {return _invoke_i(_vec3len, v0);},
|
||||
rx: func(v0, angle) {return _invoke_ii(_rotate_x, v0, angle);},
|
||||
ry: func(v0, angle) {return _invoke_ii(_rotate_y, v0, angle);},
|
||||
rz: func(v0, angle) {return _invoke_ii(_rotate_z, v0, angle);},
|
||||
dot: func(v0, v1) {return _invoke_ii(_vec3dot, v0, v1);}
|
||||
};
|
||||
|
||||
117
module/libsock.nas
Normal file
@@ -0,0 +1,117 @@
|
||||
use std.dylib;
|
||||
|
||||
var socket=func(){
|
||||
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
|
||||
|
||||
var sock=lib.nas_socket;
|
||||
var closesocket=lib.nas_closesocket;
|
||||
var shutdown=lib.nas_shutdown;
|
||||
var bind=lib.nas_bind;
|
||||
var listen=lib.nas_listen;
|
||||
var connect=lib.nas_connect;
|
||||
var accept=lib.nas_accept;
|
||||
var send=lib.nas_send;
|
||||
var sendto=lib.nas_sendto;
|
||||
var recv=lib.nas_recv;
|
||||
var recvfrom=lib.nas_recvfrom;
|
||||
var errno=lib.nas_errno;
|
||||
|
||||
var (invoke,invoke_i,invoke_ii,invoke_iii,invoke_iiii,invoke_iiiii)=(
|
||||
dylib.limitcall(0),
|
||||
dylib.limitcall(1),
|
||||
dylib.limitcall(2),
|
||||
dylib.limitcall(3),
|
||||
dylib.limitcall(4),
|
||||
dylib.limitcall(5),
|
||||
);
|
||||
return {
|
||||
AF_UNSPEC:0,
|
||||
AF_UNIX:1,
|
||||
AF_INET:2,
|
||||
AF_IMPLINK:3,
|
||||
AF_PUP:4,
|
||||
AF_CHAOS:5,
|
||||
AF_IPX:6,
|
||||
AF_NS:6,
|
||||
AF_ISO:7,
|
||||
AF_OSI:7,
|
||||
AF_ECMA:8,
|
||||
AF_DATAKIT:9,
|
||||
AF_CCITT:10,
|
||||
AF_SNA:11,
|
||||
AF_DECnet:12,
|
||||
AF_DLI:13,
|
||||
AF_LAT:14,
|
||||
AF_HYLINK:15,
|
||||
AF_APPLETALK:16,
|
||||
AF_NETBIOS:17,
|
||||
AF_VOICEVIEW:18,
|
||||
AF_FIREFOX:19,
|
||||
AF_UNKNOWN1:20,
|
||||
AF_BAN:21,
|
||||
AF_MAX:22,
|
||||
|
||||
SOCK_STREAM:1,
|
||||
SOCK_DGRAM:2,
|
||||
SOCK_RAW:3,
|
||||
SOCK_RDM:4,
|
||||
SOCK_SEQPACKET:5,
|
||||
|
||||
IPPROTO_IP:0,IPPROTO_ICMP:1,IPPROTO_IGMP:2,IPPROTO_GGP:3,
|
||||
IPPROTO_TCP:6,IPPROTO_PUP:12,IPPROTO_UDP:17,IPPROTO_IDP:22,
|
||||
IPPROTO_ND:77,IPPROTO_RAW:255,IPPROTO_MAX:256,
|
||||
|
||||
IPPORT_ECHO:7,IPPORT_DISCARD:9,IPPORT_SYSTAT:11,IPPORT_DAYTIME:13,
|
||||
IPPORT_NETSTAT:15,IPPORT_FTP:21,IPPORT_TELNET:23,IPPORT_SMTP:25,
|
||||
IPPORT_TIMESERVER:37,IPPORT_NAMESERVER:42,IPPORT_WHOIS:43,IPPORT_MTP:57,
|
||||
IPPORT_TFTP:69,IPPORT_RJE:77,IPPORT_FINGER:79,IPPORT_TTYLINK:87,
|
||||
IPPORT_SUPDUP:95,IPPORT_EXECSERVER:512,IPPORT_LOGINSERVER:513,IPPORT_CMDSERVER:514,
|
||||
IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520,
|
||||
IPPORT_RESERVED:1024,
|
||||
|
||||
SHUT_RD :0x00,
|
||||
SHUT_WR :0x01,
|
||||
SHUT_RDWR:0x02,
|
||||
|
||||
MSG_OOB:0x1,
|
||||
MSG_PEEK:0x2,
|
||||
MSG_DONTROUTE:0x4,
|
||||
|
||||
socket:func(af,type,proto){
|
||||
return invoke_iii(sock,af,type,proto);
|
||||
},
|
||||
closesocket:func(sd){
|
||||
return invoke_i(closesocket,sd);
|
||||
},
|
||||
shutdown: func(sd,how){
|
||||
return invoke_ii(shutdown,sd,how);
|
||||
},
|
||||
bind: func(sd,ip,port){
|
||||
return invoke_iii(bind,sd,ip,port);
|
||||
},
|
||||
listen: func(sd,backlog){
|
||||
return invoke_ii(listen,sd,backlog);
|
||||
},
|
||||
connect: func(sd,hostname,port){
|
||||
return invoke_iii(connect,sd,hostname,port);
|
||||
},
|
||||
accept: func(sd){
|
||||
return invoke_i(accept,sd);
|
||||
},
|
||||
send: func(sd,buff,flags=0){
|
||||
return invoke_iii(send,sd,buff,flags);
|
||||
},
|
||||
sendto: func(sd,hostname,port,buff,flags=0){
|
||||
return invoke_iiiii(sendto,sd,hostname,port,buff,flags);
|
||||
},
|
||||
recv: func(sd,len,flags=0){
|
||||
return invoke_iii(recv,sd,len,flags);
|
||||
},
|
||||
recvfrom: func(sd,len,flags=0){
|
||||
return invoke_iii(recvfrom,sd,len,flags);
|
||||
},
|
||||
errno: func(){
|
||||
return invoke(errno);
|
||||
}
|
||||
};
|
||||
}();
|
||||
@@ -1,9 +1,77 @@
|
||||
.PHONY=clean
|
||||
libfib.so: fib.cpp
|
||||
clang++ -c -O3 fib.cpp -fPIC -o fib.o
|
||||
clang++ -shared -o libfib.so fib.o
|
||||
libfib.dll: fib.cpp
|
||||
g++ -c -O3 fib.cpp -fPIC -o fib.o
|
||||
g++ -shared -o libfib.dll fib.o
|
||||
.PHONY=clean all winall
|
||||
|
||||
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
||||
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||
|
||||
used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h
|
||||
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
|
||||
|
||||
STD = c++17
|
||||
|
||||
ifndef OS
|
||||
OS = $(shell uname)
|
||||
endif
|
||||
ifeq ($(OS), Darwin)
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
|
||||
else
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC
|
||||
endif
|
||||
|
||||
all: $(dynamic_libs_so)
|
||||
@ echo "[Compiling] done"
|
||||
winall: $(dynamic_libs_dll)
|
||||
@ echo [Compiling] done
|
||||
|
||||
libfib.so: fib.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libfib.so"
|
||||
@ $(CXX) $(CXXFLAGS) fib.cpp -o fib.o
|
||||
@ $(CXX) -shared -o libfib.so fib.o $(used_object)
|
||||
@ rm fib.o
|
||||
libfib.dll: fib.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libfib.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
|
||||
@ del fib.o
|
||||
|
||||
libkey.so: keyboard.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libkey.so"
|
||||
@ $(CXX) $(CXXFLAGS) keyboard.cpp -o keyboard.o
|
||||
@ $(CXX) -shared -o libkey.so keyboard.o $(used_object)
|
||||
@ rm keyboard.o
|
||||
libkey.dll: keyboard.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libkey.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
|
||||
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
|
||||
@ del keyboard.o
|
||||
|
||||
libnasock.so: nasocket.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libnasock.so"
|
||||
@ $(CXX) $(CXXFLAGS) nasocket.cpp -o nasocket.o
|
||||
@ $(CXX) -shared -o libnasock.so nasocket.o $(used_object)
|
||||
@ rm nasocket.o
|
||||
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libnasock.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
|
||||
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
|
||||
@ del nasocket.o
|
||||
|
||||
libmat.so: matrix.cpp $(used_header) $(used_object)
|
||||
@ echo "[Compiling] libmat.so"
|
||||
@ $(CXX) $(CXXFLAGS) matrix.cpp -o matrix.o
|
||||
@ $(CXX) -shared -o libmat.so matrix.o $(used_object)
|
||||
@ rm matrix.o
|
||||
libmat.dll: matrix.cpp $(used_header) $(used_object)
|
||||
@ echo [Compiling] libmat.dll
|
||||
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
|
||||
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
|
||||
@ del matrix.o
|
||||
|
||||
clean:
|
||||
rm *.o *.so *.dll *.dylib
|
||||
@ echo "[clean] libfib.so" && if [ -e libfib.so ]; then rm libfib.so; fi
|
||||
@ echo "[clean] libkey.so" && if [ -e libkey.so ]; then rm libkey.so; fi
|
||||
@ echo "[clean] libnasock.so" && if [ -e libnasock.so ]; then rm libnasock.so; fi
|
||||
@ echo "[clean] libmat.so" && if [ -e libmat.so ]; then rm libmat.so; fi
|
||||
@ echo "[clean] libfib.dll" &&if [ -e libfib.dll ]; then rm libfib.dll; fi
|
||||
@ echo "[clean] libkey.dll" &&if [ -e libkey.dll ]; then rm libkey.dll; fi
|
||||
@ echo "[clean] libnasock.dll" &&if [ -e libnasock.dll ]; then rm libnasock.dll; fi
|
||||
@ echo "[clean] libmat.dll" &&if [ -e libmat.dll ]; then rm libmat.dll; fi
|
||||
|
||||
302
module/matrix.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#include "../src/nasal.h"
|
||||
#include "../src/nasal_type.h"
|
||||
#include "../src/nasal_gc.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_vec2(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(args[0]);
|
||||
res.vec().elems.push_back(args[1]);
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3(var* args, usize size, gc* ngc) {
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(args[0]);
|
||||
res.vec().elems.push_back(args[1]);
|
||||
res.vec().elems.push_back(args[2]);
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_add(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_sub(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_mult(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_div(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_neg(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(-v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[1].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_norm(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto t = std::sqrt(x*x+y*y);
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(x/t));
|
||||
res.vec().elems.push_back(var::num(y/t));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec2_len(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=2)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
return var::num(std::sqrt(x*x+y*y));
|
||||
}
|
||||
|
||||
var nas_vec2_dot(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=2 || v1.size()!=2)
|
||||
return nil;
|
||||
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num());
|
||||
}
|
||||
|
||||
var nas_vec3_add(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_sub(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_mult(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_div(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_neg(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(-v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[1].num()));
|
||||
res.vec().elems.push_back(var::num(-v0[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_norm(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto z = v0[2].num();
|
||||
auto t = std::sqrt(x*x+y*y+z*z);
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(x/t));
|
||||
res.vec().elems.push_back(var::num(y/t));
|
||||
res.vec().elems.push_back(var::num(z/t));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_len(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto x = v0[0].num();
|
||||
auto y = v0[1].num();
|
||||
auto z = v0[2].num();
|
||||
return var::num(std::sqrt(x*x+y*y+z*z));
|
||||
}
|
||||
|
||||
var nas_rotate_x(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_rotate_y(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[1].num()));
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle)));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_rotate_z(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
if (v0.size()!=3)
|
||||
return nil;
|
||||
auto angle = args[1].num();
|
||||
var res = ngc->alloc(vm_vec);
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
|
||||
res.vec().elems.push_back(var::num(v0[2].num()));
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_vec3_dot(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
|
||||
return nil;
|
||||
auto& v0 = args[0].vec().elems;
|
||||
auto& v1 = args[1].vec().elems;
|
||||
if (v0.size()!=3 || v1.size()!=3)
|
||||
return nil;
|
||||
return var::num(v0[0].num()*v1[0].num()+v0[1].num()*v1[1].num()+v0[2].num()*v1[2].num());
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_vec2", nas_vec2},
|
||||
{"nas_vec2_add", nas_vec2_add},
|
||||
{"nas_vec2_sub", nas_vec2_sub},
|
||||
{"nas_vec2_mult", nas_vec2_mult},
|
||||
{"nas_vec2_div", nas_vec2_div},
|
||||
{"nas_vec2_neg", nas_vec2_neg},
|
||||
{"nas_vec2_norm", nas_vec2_norm},
|
||||
{"nas_vec2_len", nas_vec2_len},
|
||||
{"nas_vec2_dot", nas_vec2_dot},
|
||||
{"nas_vec3", nas_vec3},
|
||||
{"nas_vec3_add", nas_vec3_add},
|
||||
{"nas_vec3_sub", nas_vec3_sub},
|
||||
{"nas_vec3_mult", nas_vec3_mult},
|
||||
{"nas_vec3_div", nas_vec3_div},
|
||||
{"nas_vec3_neg", nas_vec3_neg},
|
||||
{"nas_vec3_norm", nas_vec3_norm},
|
||||
{"nas_vec3_len", nas_vec3_len},
|
||||
{"nas_rotate_x", nas_rotate_x},
|
||||
{"nas_rotate_y", nas_rotate_y},
|
||||
{"nas_rotate_z", nas_rotate_z},
|
||||
{"nas_vec3_dot", nas_vec3_dot},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
262
module/nasocket.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
#include "../src/nasal.h"
|
||||
#include "../src/nasal_type.h"
|
||||
#include "../src/nasal_gc.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h>
|
||||
#pragma comment(lib,"ws2_32")
|
||||
|
||||
class WSAmanager {
|
||||
private:
|
||||
WSAData data;
|
||||
public:
|
||||
WSAmanager() {
|
||||
WSAStartup(0x1010, &data);
|
||||
}
|
||||
~WSAmanager() {
|
||||
WSACleanup();
|
||||
}
|
||||
};
|
||||
|
||||
static WSAmanager win;
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_socket(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
|
||||
return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number");
|
||||
int sd = socket(args[0].num(), args[1].num(), args[2].num());
|
||||
return var::num(static_cast<double>(sd));
|
||||
}
|
||||
|
||||
var nas_closesocket(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("closesocket", "\"sd\" should be number");
|
||||
#ifdef _WIN32
|
||||
return var::num(static_cast<double>(closesocket(args[0].num())));
|
||||
#else
|
||||
return var::num(static_cast<double>(close(args[0].num())));
|
||||
#endif
|
||||
}
|
||||
|
||||
var nas_shutdown(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("shutdown", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("shutdown", "\"how\" must be a number");
|
||||
return var::num(static_cast<double>(shutdown(args[0].num(), args[1].num())));
|
||||
}
|
||||
|
||||
var nas_bind(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("bind", "\"sd\" muse be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("bind", "\"ip\" should be a string including an ip with correct format");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("bind", "\"port\" must be a number");
|
||||
sockaddr_in server;
|
||||
memset(&server, 0, sizeof(sockaddr_in));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = inet_addr(args[1].str().c_str());
|
||||
server.sin_port = htons(args[2].num());
|
||||
return var::num(static_cast<double>(bind(
|
||||
args[0].num(),
|
||||
reinterpret_cast<sockaddr*>(&server),
|
||||
sizeof(server)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_listen(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("listen", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("listen", "\"backlog\" must be a number");
|
||||
return var::num(static_cast<double>(listen(args[0].num(), args[1].num())));
|
||||
}
|
||||
|
||||
var nas_connect(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("connect", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("connect", "\"hostname\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("connect", "\"port\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(args[2].num());
|
||||
hostent* entry = gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
|
||||
return var::num(static_cast<double>(connect(
|
||||
args[0].num(),
|
||||
reinterpret_cast<sockaddr*>(&addr),
|
||||
sizeof(sockaddr_in)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_accept(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("accept", "\"sd\" must be a number");
|
||||
sockaddr_in client;
|
||||
int socklen = sizeof(sockaddr_in);
|
||||
#ifdef _WIN32
|
||||
int client_sd = accept(
|
||||
args[0].num(),
|
||||
reinterpret_cast<sockaddr*>(&client),
|
||||
&socklen
|
||||
);
|
||||
#else
|
||||
int client_sd = accept(
|
||||
args[0].num(),
|
||||
reinterpret_cast<sockaddr*>(&client),
|
||||
reinterpret_cast<socklen_t*>(&socklen)
|
||||
);
|
||||
#endif
|
||||
var res = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
hash["sd"] = var::num(static_cast<double>(client_sd));
|
||||
hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr));
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_send(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("send", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("send", "\"buff\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("send", "\"flags\" muse be a number");
|
||||
return var::num(static_cast<double>(send(
|
||||
args[0].num(),
|
||||
args[1].str().c_str(),
|
||||
args[1].str().length(),
|
||||
args[2].num()
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_sendto(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("sendto", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_str)
|
||||
return nas_err("sendto", "\"hostname\" must be a string");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("sendto", "\"port\" must be a number");
|
||||
if (args[3].type!=vm_str)
|
||||
return nas_err("sendto", "\"buff\" must be a string");
|
||||
if (args[4].type!=vm_num)
|
||||
return nas_err("sendto", "\"flags\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(args[2].num());
|
||||
hostent* entry = gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
|
||||
return var::num(static_cast<double>(sendto(
|
||||
args[0].num(),
|
||||
args[3].str().c_str(),
|
||||
args[3].str().length(),
|
||||
args[4].num(),
|
||||
reinterpret_cast<sockaddr*>(&addr),
|
||||
sizeof(sockaddr_in)
|
||||
)));
|
||||
}
|
||||
|
||||
var nas_recv(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("recv", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("recv", "\"len\" must be a number");
|
||||
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recv", "\"len\" out of range");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("recv", "\"flags\" muse be a number");
|
||||
var res = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
char* buf = new char[static_cast<int>(args[1].num())];
|
||||
auto recvsize = recv(args[0].num(), buf,args[1].num(), args[2].num());
|
||||
hash["size"] = var::num(static_cast<double>(recvsize));
|
||||
buf[recvsize>=0? recvsize:0] = 0;
|
||||
hash["str"] = ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_recvfrom(var* args, usize size, gc* ngc) {
|
||||
if (args[0].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"sd\" must be a number");
|
||||
if (args[1].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"len\" must be a number");
|
||||
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recvfrom", "\"len\" out of range");
|
||||
if (args[2].type!=vm_num)
|
||||
return nas_err("recvfrom", "\"flags\" muse be a number");
|
||||
sockaddr_in addr;
|
||||
int socklen = sizeof(sockaddr_in);
|
||||
var res = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto& hash = res.hash().elems;
|
||||
char* buf = new char[static_cast<int>(args[1].num()+1)];
|
||||
#ifdef _WIN32
|
||||
auto recvsize = recvfrom(
|
||||
args[0].num(),
|
||||
buf,
|
||||
args[1].num(),
|
||||
args[2].num(),
|
||||
reinterpret_cast<sockaddr*>(&addr),
|
||||
&socklen
|
||||
);
|
||||
#else
|
||||
auto recvsize = recvfrom(
|
||||
args[0].num(),
|
||||
buf,
|
||||
args[1].num(),
|
||||
args[2].num(),
|
||||
reinterpret_cast<sockaddr*>(&addr),
|
||||
reinterpret_cast<socklen_t*>(&socklen)
|
||||
);
|
||||
#endif
|
||||
hash["size"] = var::num(static_cast<double>(recvsize));
|
||||
buf[recvsize>=0? recvsize:0] = 0;
|
||||
hash["str"] = ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
hash["fromip"] = ngc->newstr(inet_ntoa(addr.sin_addr));
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_errno(var* args, usize size, gc* ngc) {
|
||||
return ngc->newstr(strerror(errno));
|
||||
}
|
||||
|
||||
module_func_info func_tbl[] = {
|
||||
{"nas_socket", nas_socket},
|
||||
{"nas_closesocket", nas_closesocket},
|
||||
{"nas_shutdown", nas_shutdown},
|
||||
{"nas_bind", nas_bind},
|
||||
{"nas_listen", nas_listen},
|
||||
{"nas_connect", nas_connect},
|
||||
{"nas_accept", nas_accept},
|
||||
{"nas_send", nas_send},
|
||||
{"nas_sendto", nas_sendto},
|
||||
{"nas_recv", nas_recv},
|
||||
{"nas_recvfrom", nas_recvfrom},
|
||||
{"nas_errno", nas_errno},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
extern "C" module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
}
|
||||
142
nasal.h
@@ -1,142 +0,0 @@
|
||||
#ifndef __NASAL_H__
|
||||
#define __NASAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
inline double hex_to_double(const char* str)
|
||||
{
|
||||
double ret=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
ret*=16;
|
||||
if('0'<=*str && *str<='9')
|
||||
ret+=(*str-'0');
|
||||
else if('a'<=*str && *str<='f')
|
||||
ret+=(*str-'a'+10);
|
||||
else if('A'<=*str && *str<='F')
|
||||
ret+=(*str-'A'+10);
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline double oct_to_double(const char* str)
|
||||
{
|
||||
double ret=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
ret*=8;
|
||||
if('0'<=*str && *str<'8')
|
||||
ret+=(*str-'0');
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline double dec_to_double(const char* str)
|
||||
{
|
||||
double ret=0,negative=1,num_pow=0;
|
||||
while('0'<=*str && *str<='9')
|
||||
ret=ret*10+(*str++-'0');
|
||||
if(!*str) return ret;
|
||||
if(*str=='.')
|
||||
{
|
||||
if(!*++str) return nan("");
|
||||
num_pow=0.1;
|
||||
while('0'<=*str && *str<='9')
|
||||
{
|
||||
ret+=num_pow*(*str++-'0');
|
||||
num_pow*=0.1;
|
||||
}
|
||||
if(!*str) return ret;
|
||||
}
|
||||
if(*str!='e' && *str!='E')
|
||||
return nan("");
|
||||
if(!*++str) return nan("");
|
||||
if(*str=='-' || *str=='+')
|
||||
negative=(*str++=='-'? -1:1);
|
||||
if(!*str) return nan("");
|
||||
num_pow=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
if('0'<=*str && *str<='9')
|
||||
num_pow=num_pow*10+(*str-'0');
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
return ret*std::pow(10,negative*num_pow);
|
||||
}
|
||||
double str2num(const char* str)
|
||||
{
|
||||
bool is_negative=false;
|
||||
double ret_num=0;
|
||||
if(*str=='-' || *str=='+')
|
||||
is_negative=(*str++=='-');
|
||||
if(!*str)
|
||||
return nan("");
|
||||
if(str[0]=='0' && str[1]=='x')
|
||||
ret_num=hex_to_double(str+2);
|
||||
else if(str[0]=='0' && str[1]=='o')
|
||||
ret_num=oct_to_double(str+2);
|
||||
else
|
||||
ret_num=dec_to_double(str);
|
||||
return is_negative?-ret_num:ret_num;
|
||||
}
|
||||
|
||||
std::string rawstr(const std::string& str)
|
||||
{
|
||||
std::string ret("");
|
||||
for(auto i:str)
|
||||
switch(i)
|
||||
{
|
||||
case '\a': ret+="\\a";break;
|
||||
case '\b': ret+="\\b";break;
|
||||
case '\f': ret+="\\f";break;
|
||||
case '\n': ret+="\\n";break;
|
||||
case '\r': ret+="\\r";break;
|
||||
case '\t': ret+="\\t";break;
|
||||
case '\v': ret+="\\v";break;
|
||||
case '\0': ret+="\\0";break;
|
||||
default: ret+=i; break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_opt.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
#endif
|
||||
248
nasal_ast.h
@@ -1,248 +0,0 @@
|
||||
#ifndef __NASAL_AST_H__
|
||||
#define __NASAL_AST_H__
|
||||
|
||||
enum ast_node
|
||||
{
|
||||
ast_null=0, // null node
|
||||
ast_root, // mark the root node of ast
|
||||
ast_block, // expression block
|
||||
ast_file, // used to store which file the sub-tree is on, only used in main block
|
||||
ast_nil, // nil keyword
|
||||
ast_num, // number, basic value type
|
||||
ast_str, // string, basic value type
|
||||
ast_id, // identifier
|
||||
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_args, // mark a sub-tree of function parameters
|
||||
ast_default, // default parameter
|
||||
ast_dynamic, // dynamic parameter
|
||||
ast_and, // and keyword
|
||||
ast_or, // or keyword
|
||||
ast_equal, // =
|
||||
ast_addeq, // +=
|
||||
ast_subeq, // -=
|
||||
ast_multeq, // *=
|
||||
ast_diveq, // /=
|
||||
ast_lnkeq, // ~=
|
||||
ast_cmpeq, // ==
|
||||
ast_neq, // !=
|
||||
ast_less, // <
|
||||
ast_leq, // <=
|
||||
ast_grt, // >
|
||||
ast_geq, // >=
|
||||
ast_add, // +
|
||||
ast_sub, // -
|
||||
ast_mult, // *
|
||||
ast_div, // /
|
||||
ast_link, // ~
|
||||
ast_neg, // -
|
||||
ast_not, // ~
|
||||
ast_trino, // ?:
|
||||
ast_for, // for keyword
|
||||
ast_forindex, // forindex keyword
|
||||
ast_foreach, // foreach keyword
|
||||
ast_while, // while
|
||||
ast_iter, // iterator, used in forindex/foreach
|
||||
ast_conditional, // mark a sub-tree of conditional expression
|
||||
ast_if, // if keyword
|
||||
ast_elsif, // elsif keyword
|
||||
ast_else, // else keyword
|
||||
ast_multi_id, // multi identifiers sub-tree
|
||||
ast_multi_scalar,// multi value sub-tree
|
||||
ast_def, // definition
|
||||
ast_multi_assign,// multi assignment sub-tree
|
||||
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
|
||||
};
|
||||
|
||||
const char* ast_name[]=
|
||||
{
|
||||
"null",
|
||||
"root",
|
||||
"block",
|
||||
"file",
|
||||
"nil",
|
||||
"num",
|
||||
"str",
|
||||
"id",
|
||||
"func",
|
||||
"hash",
|
||||
"vec",
|
||||
"hashmember",
|
||||
"call",
|
||||
"callh",
|
||||
"callv",
|
||||
"callf",
|
||||
"subvec",
|
||||
"args",
|
||||
"default",
|
||||
"dynamic",
|
||||
"and",
|
||||
"or",
|
||||
"=",
|
||||
"+=",
|
||||
"-=",
|
||||
"*=",
|
||||
"/=",
|
||||
"~=",
|
||||
"==",
|
||||
"!=",
|
||||
"<",
|
||||
"<=",
|
||||
">",
|
||||
">=",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"~",
|
||||
"unary-",
|
||||
"unary!",
|
||||
"trino",
|
||||
"for",
|
||||
"forindex",
|
||||
"foreach",
|
||||
"while",
|
||||
"iter",
|
||||
"conditional",
|
||||
"if",
|
||||
"elsif",
|
||||
"else",
|
||||
"multi_id",
|
||||
"multi_scalar",
|
||||
"def",
|
||||
"multi_assign",
|
||||
"continue",
|
||||
"break",
|
||||
"return",
|
||||
nullptr
|
||||
};
|
||||
|
||||
class nasal_ast
|
||||
{
|
||||
private:
|
||||
uint32_t _line;
|
||||
uint32_t _type;
|
||||
double _num;
|
||||
std::string _str;
|
||||
std::vector<nasal_ast> _child;
|
||||
public:
|
||||
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):_line(l),_type(t){}
|
||||
nasal_ast(const nasal_ast&);
|
||||
nasal_ast(nasal_ast&&);
|
||||
void print(int,bool);
|
||||
void clear();
|
||||
|
||||
nasal_ast& operator=(const nasal_ast&);
|
||||
nasal_ast& operator=(nasal_ast&&);
|
||||
nasal_ast& operator[](const int index){return _child[index];}
|
||||
const nasal_ast& operator[](const int index) const {return _child[index];}
|
||||
size_t size() const {return _child.size();}
|
||||
|
||||
void add(nasal_ast&& ast){_child.push_back(std::move(ast));}
|
||||
void add(const nasal_ast& ast){_child.push_back(ast);}
|
||||
void set_line(const uint32_t l){_line=l;}
|
||||
void set_type(const uint32_t t){_type=t;}
|
||||
void set_str(const std::string& s){_str=s;}
|
||||
void set_num(const double n){_num=n;}
|
||||
|
||||
inline uint32_t line() const {return _line;}
|
||||
inline uint32_t type() const {return _type;}
|
||||
inline double num() const {return _num;}
|
||||
inline const std::string& str() const {return _str;}
|
||||
inline const std::vector<nasal_ast>& child() const {return _child;}
|
||||
inline std::vector<nasal_ast>& child(){return _child;}
|
||||
};
|
||||
|
||||
nasal_ast::nasal_ast(const nasal_ast& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num =tmp._num;
|
||||
_str =tmp._str;
|
||||
_child=tmp._child;
|
||||
}
|
||||
|
||||
nasal_ast::nasal_ast(nasal_ast&& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num =tmp._num;
|
||||
_str.swap(tmp._str);
|
||||
_child.swap(tmp._child);
|
||||
}
|
||||
|
||||
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num=tmp._num;
|
||||
_str=tmp._str;
|
||||
_child=tmp._child;
|
||||
return *this;
|
||||
}
|
||||
|
||||
nasal_ast& nasal_ast::operator=(nasal_ast&& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num=tmp._num;
|
||||
_str.swap(tmp._str);
|
||||
_child.swap(tmp._child);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void nasal_ast::clear()
|
||||
{
|
||||
_line=0;
|
||||
_num=0;
|
||||
_str="";
|
||||
_type=ast_null;
|
||||
_child.clear();
|
||||
}
|
||||
|
||||
void nasal_ast::print(int depth,bool last=false)
|
||||
{
|
||||
static std::vector<std::string> intentation={};
|
||||
for(auto& i:intentation)
|
||||
std::cout<<i;
|
||||
std::cout<<ast_name[_type];
|
||||
if(
|
||||
_type==ast_str ||
|
||||
_type==ast_id ||
|
||||
_type==ast_default ||
|
||||
_type==ast_dynamic ||
|
||||
_type==ast_callh)
|
||||
std::cout<<":"<<rawstr(_str);
|
||||
else if(_type==ast_num || _type==ast_file)
|
||||
std::cout<<":"<<_num;
|
||||
std::cout<<'\n';
|
||||
if(last && depth)
|
||||
intentation.back()=" ";
|
||||
else if(!last && depth)
|
||||
#ifdef _WIN32
|
||||
intentation.back()="| ";
|
||||
#else
|
||||
intentation.back()="│ ";
|
||||
#endif
|
||||
for(uint32_t i=0;i<_child.size();++i)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
intentation.push_back(i==_child.size()-1?"`-":"|-");
|
||||
#else
|
||||
intentation.push_back(i==_child.size()-1?"└─":"├─");
|
||||
#endif
|
||||
_child[i].print(depth+1,i==_child.size()-1);
|
||||
intentation.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1082
nasal_builtin.h
1196
nasal_codegen.h
306
nasal_dbg.h
@@ -1,306 +0,0 @@
|
||||
#ifndef __NASAL_DBG_H__
|
||||
#define __NASAL_DBG_H__
|
||||
|
||||
#include "nasal_vm.h"
|
||||
|
||||
class nasal_dbg:public nasal_vm
|
||||
{
|
||||
private:
|
||||
bool next_step;
|
||||
uint16_t bk_fidx;
|
||||
uint32_t bk_line;
|
||||
file_line src;
|
||||
|
||||
std::vector<std::string> parse(const std::string&);
|
||||
uint16_t get_fileindex(const std::string&);
|
||||
void err();
|
||||
void help();
|
||||
void stepinfo();
|
||||
void interact();
|
||||
public:
|
||||
nasal_dbg():
|
||||
next_step(false),
|
||||
bk_fidx(0),bk_line(0){}
|
||||
void run(
|
||||
const nasal_codegen&,
|
||||
const nasal_import&
|
||||
);
|
||||
};
|
||||
|
||||
std::vector<std::string> nasal_dbg::parse(const std::string& cmd)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::string tmp="";
|
||||
for(uint32_t i=0;i<cmd.length();++i)
|
||||
{
|
||||
if(cmd[i]==' ' && tmp.length())
|
||||
{
|
||||
res.push_back(tmp);
|
||||
tmp="";
|
||||
continue;
|
||||
}
|
||||
tmp+=cmd[i];
|
||||
}
|
||||
if(tmp.length())
|
||||
res.push_back(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t nasal_dbg::get_fileindex(const std::string& filename)
|
||||
{
|
||||
for(uint16_t i=0;i<files_size;++i)
|
||||
if(filename==files[i])
|
||||
return i;
|
||||
return 65535;
|
||||
}
|
||||
|
||||
void nasal_dbg::err()
|
||||
{
|
||||
std::cerr
|
||||
<<"incorrect command\n"
|
||||
<<"input \'h\' to get help\n";
|
||||
}
|
||||
|
||||
void nasal_dbg::help()
|
||||
{
|
||||
std::cout
|
||||
<<"<option>\n"
|
||||
<<"\th, help | get help\n"
|
||||
<<"\tbt, backtrace | get function call trace\n"
|
||||
<<"\tc, continue | run program until break point or exit\n"
|
||||
<<"\tg, global | see global values\n"
|
||||
<<"\tl, local | see local values\n"
|
||||
<<"\tu, upval | see upvalue\n"
|
||||
<<"\ta, all | show global,local and upvalue\n"
|
||||
<<"\tn, next | execute next bytecode\n"
|
||||
<<"\tq, exit | exit debugger\n"
|
||||
<<"<option> <filename> <line>\n"
|
||||
<<"\tbk, break | set break point\n";
|
||||
}
|
||||
|
||||
void nasal_dbg::stepinfo()
|
||||
{
|
||||
uint32_t begin,end;
|
||||
uint32_t line=bytecode[pc].line==0?0:bytecode[pc].line-1;
|
||||
src.load(files[bytecode[pc].fidx]);
|
||||
printf("\nsource code:\n");
|
||||
begin=(line>>3)==0?0:((line>>3)<<3);
|
||||
end=(1+(line>>3))<<3;
|
||||
for(uint32_t i=begin;i<end && i<src.size();++i)
|
||||
printf("%s\t%s\n",i==line?"-->":" ",src[i].c_str());
|
||||
printf("next bytecode:\n");
|
||||
begin=(pc>>3)==0?0:((pc>>3)<<3);
|
||||
end=(1+(pc>>3))<<3;
|
||||
for(uint32_t i=begin;i<end && bytecode[i].op!=op_exit;++i)
|
||||
bytecodeinfo(i==pc?"-->\t":" \t",i);
|
||||
stackinfo(5);
|
||||
}
|
||||
|
||||
void nasal_dbg::interact()
|
||||
{
|
||||
// special operand
|
||||
if(bytecode[pc].op==op_intg)
|
||||
{
|
||||
std::cout
|
||||
<<"[debug] nasal debug mode\n"
|
||||
<<"input \'h\' to get help\n";
|
||||
}
|
||||
else if(bytecode[pc].op==op_nop || bytecode[pc].op==op_exit)
|
||||
return;
|
||||
|
||||
if(
|
||||
(bytecode[pc].fidx!=bk_fidx || bytecode[pc].line!=bk_line) && // break point
|
||||
!next_step // next step
|
||||
)return;
|
||||
|
||||
next_step=false;
|
||||
std::string cmd;
|
||||
stepinfo();
|
||||
while(1)
|
||||
{
|
||||
printf(">> ");
|
||||
std::getline(std::cin,cmd);
|
||||
auto res=parse(cmd);
|
||||
if(res.size()==1)
|
||||
{
|
||||
if(res[0]=="h" || res[0]=="help")
|
||||
help();
|
||||
else if(res[0]=="bt" || res[0]=="backtrace")
|
||||
traceback();
|
||||
else if(res[0]=="c" || res[0]=="continue")
|
||||
return;
|
||||
else if(res[0]=="g" || res[0]=="global")
|
||||
global_state();
|
||||
else if(res[0]=="l" || res[0]=="local")
|
||||
local_state();
|
||||
else if(res[0]=="u" || res[0]=="upval")
|
||||
upval_state();
|
||||
else if(res[0]=="a" || res[0]=="all")
|
||||
{
|
||||
global_state();
|
||||
local_state();
|
||||
upval_state();
|
||||
}
|
||||
else if(res[0]=="n" || res[0]=="next")
|
||||
{
|
||||
next_step=true;
|
||||
return;
|
||||
}
|
||||
else if(res[0]=="q" || res[0]=="exit")
|
||||
std::exit(0);
|
||||
else
|
||||
err();
|
||||
}
|
||||
else if(res.size()==3)
|
||||
{
|
||||
if(res[0]=="bk" || res[0]=="break")
|
||||
{
|
||||
bk_fidx=get_fileindex(res[1]);
|
||||
if(bk_fidx==65535)
|
||||
{
|
||||
printf("cannot find file named \"%s\"\n",res[1].c_str());
|
||||
bk_fidx=0;
|
||||
}
|
||||
int tmp=atoi(res[2].c_str());
|
||||
if(tmp<=0)
|
||||
printf("incorrect line number \"%s\"\n",res[2].c_str());
|
||||
else
|
||||
bk_line=tmp;
|
||||
}
|
||||
else
|
||||
err();
|
||||
}
|
||||
else
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
void nasal_dbg::run(
|
||||
const nasal_codegen& gen,
|
||||
const nasal_import& linker)
|
||||
{
|
||||
detail_info=true;
|
||||
init(gen.get_strs(),gen.get_nums(),linker.get_file());
|
||||
const void* opr_table[]=
|
||||
{
|
||||
&&nop, &&intg, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&defpara,&&dynpara,
|
||||
&&unot, &&usub, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
|
||||
&&diveqc, &&lnkeqc, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&counter, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbegin, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret, &&vmexit
|
||||
};
|
||||
bytecode=gen.get_code().data();
|
||||
std::vector<const void*> code;
|
||||
for(auto& i:gen.get_code())
|
||||
{
|
||||
code.push_back(opr_table[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
|
||||
// set canary and program counter
|
||||
auto canary=gc.stack+STACK_MAX_DEPTH-1;
|
||||
pc=0;
|
||||
// goto the first operand
|
||||
goto *code[pc];
|
||||
|
||||
vmexit:
|
||||
if(gc.top>=canary)
|
||||
die("stack overflow");
|
||||
gc.clear();
|
||||
imm.clear();
|
||||
printf("[debug] debugger exited\n");
|
||||
return;
|
||||
#define dbg(op) {interact();op();if(gc.top<canary)goto *code[++pc];goto vmexit;}
|
||||
|
||||
nop: dbg(opr_nop );
|
||||
intg: dbg(opr_intg );
|
||||
intl: dbg(opr_intl );
|
||||
loadg: dbg(opr_loadg );
|
||||
loadl: dbg(opr_loadl );
|
||||
loadu: dbg(opr_loadu );
|
||||
pnum: dbg(opr_pnum );
|
||||
pnil: dbg(opr_pnil );
|
||||
pstr: dbg(opr_pstr );
|
||||
newv: dbg(opr_newv );
|
||||
newh: dbg(opr_newh );
|
||||
newf: dbg(opr_newf );
|
||||
happ: dbg(opr_happ );
|
||||
para: dbg(opr_para );
|
||||
defpara: dbg(opr_defpara );
|
||||
dynpara: dbg(opr_dynpara );
|
||||
unot: dbg(opr_unot );
|
||||
usub: dbg(opr_usub );
|
||||
add: dbg(opr_add );
|
||||
sub: dbg(opr_sub );
|
||||
mul: dbg(opr_mul );
|
||||
div: dbg(opr_div );
|
||||
lnk: dbg(opr_lnk );
|
||||
addc: dbg(opr_addc );
|
||||
subc: dbg(opr_subc );
|
||||
mulc: dbg(opr_mulc );
|
||||
divc: dbg(opr_divc );
|
||||
lnkc: dbg(opr_lnkc );
|
||||
addeq: dbg(opr_addeq );
|
||||
subeq: dbg(opr_subeq );
|
||||
muleq: dbg(opr_muleq );
|
||||
diveq: dbg(opr_diveq );
|
||||
lnkeq: dbg(opr_lnkeq );
|
||||
addeqc: dbg(opr_addeqc );
|
||||
subeqc: dbg(opr_subeqc );
|
||||
muleqc: dbg(opr_muleqc );
|
||||
diveqc: dbg(opr_diveqc );
|
||||
lnkeqc: dbg(opr_lnkeqc );
|
||||
meq: dbg(opr_meq );
|
||||
eq: dbg(opr_eq );
|
||||
neq: dbg(opr_neq );
|
||||
less: dbg(opr_less );
|
||||
leq: dbg(opr_leq );
|
||||
grt: dbg(opr_grt );
|
||||
geq: dbg(opr_geq );
|
||||
lessc: dbg(opr_lessc );
|
||||
leqc: dbg(opr_leqc );
|
||||
grtc: dbg(opr_grtc );
|
||||
geqc: dbg(opr_geqc );
|
||||
pop: dbg(opr_pop );
|
||||
jmp: dbg(opr_jmp );
|
||||
jt: dbg(opr_jt );
|
||||
jf: dbg(opr_jf );
|
||||
counter: dbg(opr_counter );
|
||||
findex: dbg(opr_findex );
|
||||
feach: dbg(opr_feach );
|
||||
callg: dbg(opr_callg );
|
||||
calll: dbg(opr_calll );
|
||||
upval: dbg(opr_upval );
|
||||
callv: dbg(opr_callv );
|
||||
callvi: dbg(opr_callvi );
|
||||
callh: dbg(opr_callh );
|
||||
callfv: dbg(opr_callfv );
|
||||
callfh: dbg(opr_callfh );
|
||||
callb: dbg(opr_callb );
|
||||
slcbegin:dbg(opr_slcbegin);
|
||||
slcend: dbg(opr_slcend );
|
||||
slc: dbg(opr_slc );
|
||||
slc2: dbg(opr_slc2 );
|
||||
mcallg: dbg(opr_mcallg );
|
||||
mcalll: dbg(opr_mcalll );
|
||||
mupval: dbg(opr_mupval );
|
||||
mcallv: dbg(opr_mcallv );
|
||||
mcallh: dbg(opr_mcallh );
|
||||
ret: dbg(opr_ret );
|
||||
}
|
||||
|
||||
#endif
|
||||
77
nasal_err.h
@@ -1,77 +0,0 @@
|
||||
#ifndef __NASAL_ERR_H__
|
||||
#define __NASAL_ERR_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
class file_line
|
||||
{
|
||||
protected:
|
||||
std::string file;
|
||||
std::vector<std::string> res;
|
||||
public:
|
||||
void load(const std::string& f)
|
||||
{
|
||||
if(file==f) // don't need to load a loaded file
|
||||
return;
|
||||
file=f;
|
||||
res.clear();
|
||||
std::ifstream fin(f,std::ios::binary);
|
||||
if(fin.fail())
|
||||
{
|
||||
std::cerr<<"[src] cannot open file <"<<f<<">\n";
|
||||
std::exit(1);
|
||||
}
|
||||
std::string line;
|
||||
while(!fin.eof())
|
||||
{
|
||||
std::getline(fin,line);
|
||||
res.push_back(line);
|
||||
}
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
std::vector<std::string> tmp;
|
||||
res.swap(tmp);
|
||||
}
|
||||
const std::string& operator[](const uint32_t line){return res[line];}
|
||||
const std::string& name(){return file;}
|
||||
size_t size(){return res.size();}
|
||||
};
|
||||
|
||||
class nasal_err:public file_line
|
||||
{
|
||||
private:
|
||||
uint32_t error;
|
||||
public:
|
||||
void err(const char* stage,const std::string& info)
|
||||
{
|
||||
++error;
|
||||
std::cerr<<"["<<stage<<"] "<<info<<'\n';
|
||||
}
|
||||
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
|
||||
{
|
||||
++error;
|
||||
if(!line)
|
||||
{
|
||||
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
|
||||
return;
|
||||
}
|
||||
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<res[line-1]<<'\n';
|
||||
for(int i=0;i<(int)column-1;++i)
|
||||
std::cerr<<char(" \t"[res[line-1][i]=='\t']);
|
||||
std::cerr<<"^\n";
|
||||
}
|
||||
void err(const char* stage,uint32_t line,const std::string& info)
|
||||
{
|
||||
++error;
|
||||
if(!line)
|
||||
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
|
||||
else
|
||||
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
|
||||
}
|
||||
void chkerr(){if(error)std::exit(1);}
|
||||
};
|
||||
|
||||
#endif
|
||||
474
nasal_gc.h
@@ -1,474 +0,0 @@
|
||||
#ifndef __NASAL_GC_H__
|
||||
#define __NASAL_GC_H__
|
||||
|
||||
enum nasal_type
|
||||
{
|
||||
/* none-gc object */
|
||||
vm_none=0,
|
||||
vm_cnt,
|
||||
vm_ret,
|
||||
vm_nil,
|
||||
vm_num,
|
||||
/* gc object */
|
||||
vm_str,
|
||||
vm_func,
|
||||
vm_vec,
|
||||
vm_hash,
|
||||
vm_obj,
|
||||
vm_type_size
|
||||
};
|
||||
|
||||
// change parameters here to make your own efficient gc
|
||||
// better set bigger number on vm_vec
|
||||
const uint32_t increment[vm_type_size]=
|
||||
{
|
||||
/* none-gc object */
|
||||
0, // vm_none, error type
|
||||
0, // vm_count, used in foreach/forindex
|
||||
0, // vm_ret, used to store call-return address
|
||||
0, // vm_nil
|
||||
0, // vm_num
|
||||
/* gc object */
|
||||
256, // vm_str
|
||||
512, // vm_func
|
||||
512, // vm_vec
|
||||
128, // vm_hash
|
||||
16 // vm_obj
|
||||
};
|
||||
|
||||
struct nasal_vec; // vector
|
||||
struct nasal_hash;// hashmap(dict)
|
||||
struct nasal_func;// function(lambda)
|
||||
struct nasal_obj; // special objects
|
||||
struct nasal_val; // nasal_val includes gc-managed types
|
||||
|
||||
struct nasal_ref
|
||||
{
|
||||
uint8_t type;
|
||||
union
|
||||
{
|
||||
uint32_t ret;
|
||||
int64_t cnt;
|
||||
double num;
|
||||
nasal_val* gcobj;
|
||||
}value;
|
||||
|
||||
nasal_ref(const uint8_t t=vm_none):type(t){}
|
||||
nasal_ref(const uint8_t t,const uint32_t n):type(t){value.ret=n;}
|
||||
nasal_ref(const uint8_t t,const int64_t n):type(t){value.cnt=n;}
|
||||
nasal_ref(const uint8_t t,const double n):type(t){value.num=n;}
|
||||
nasal_ref(const uint8_t t,nasal_val* n):type(t){value.gcobj=n;}
|
||||
nasal_ref(const nasal_ref& nr):type(nr.type),value(nr.value){}
|
||||
nasal_ref& operator=(const nasal_ref& nr)
|
||||
{
|
||||
type=nr.type;
|
||||
value=nr.value;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const nasal_ref& nr){return type==nr.type && value.gcobj==nr.value.gcobj;}
|
||||
bool operator!=(const nasal_ref& nr){return type!=nr.type || value.gcobj!=nr.value.gcobj;}
|
||||
// number and string can be translated to each other
|
||||
double to_number();
|
||||
std::string to_string();
|
||||
void print();
|
||||
inline uint32_t ret ();
|
||||
inline int64_t& cnt ();
|
||||
inline double& num ();
|
||||
inline std::string* str ();
|
||||
inline nasal_vec* vec ();
|
||||
inline nasal_hash* hash();
|
||||
inline nasal_func* func();
|
||||
inline nasal_obj* obj ();
|
||||
};
|
||||
|
||||
struct nasal_vec
|
||||
{
|
||||
std::vector<nasal_ref> elems;
|
||||
|
||||
void print();
|
||||
nasal_ref get_val(const int);
|
||||
nasal_ref* get_mem(const int);
|
||||
};
|
||||
|
||||
struct nasal_hash
|
||||
{
|
||||
std::unordered_map<std::string,nasal_ref> elems;
|
||||
|
||||
void print();
|
||||
nasal_ref get_val(const std::string&);
|
||||
nasal_ref* get_mem(const std::string&);
|
||||
};
|
||||
|
||||
struct nasal_func
|
||||
{
|
||||
int32_t dynpara; // dynamic parameter name index in hash.
|
||||
uint32_t entry; // pc will set to entry-1 to call this function
|
||||
uint32_t psize; // used to load default parameters to a new function
|
||||
uint32_t lsize; // used to expand memory space for local values on stack
|
||||
std::vector<nasal_ref> local; // local scope with default value(nasal_ref)
|
||||
std::vector<nasal_ref> upvalue; // closure
|
||||
std::unordered_map<std::string,size_t> keys; // parameter name table, size_t begins from 1
|
||||
|
||||
nasal_func():dynpara(-1){}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nasal_obj
|
||||
{
|
||||
uint32_t type;
|
||||
void* ptr;
|
||||
nasal_obj():ptr(nullptr){}
|
||||
void clear(){ptr=nullptr;}
|
||||
};
|
||||
|
||||
const uint8_t GC_UNCOLLECTED=0;
|
||||
const uint8_t GC_COLLECTED =1;
|
||||
const uint8_t GC_FOUND =2;
|
||||
struct nasal_val
|
||||
{
|
||||
uint8_t mark;
|
||||
uint8_t type;
|
||||
uint8_t unmut; // used to mark if a string is unmutable
|
||||
union
|
||||
{
|
||||
std::string* str;
|
||||
nasal_vec* vec;
|
||||
nasal_hash* hash;
|
||||
nasal_func* func;
|
||||
nasal_obj* obj;
|
||||
}ptr;
|
||||
|
||||
nasal_val(uint8_t);
|
||||
~nasal_val();
|
||||
};
|
||||
|
||||
nasal_ref nasal_vec::get_val(const int index)
|
||||
{
|
||||
int size=elems.size();
|
||||
if(index<-size || index>=size)
|
||||
return {vm_none};
|
||||
return elems[index>=0?index:index+size];
|
||||
}
|
||||
nasal_ref* nasal_vec::get_mem(const int index)
|
||||
{
|
||||
int size=elems.size();
|
||||
if(index<-size || index>=size)
|
||||
return nullptr;
|
||||
return &elems[index>=0?index:index+size];
|
||||
}
|
||||
void nasal_vec::print()
|
||||
{
|
||||
static int depth=0;
|
||||
if(!elems.size() || depth>8)
|
||||
{
|
||||
std::cout<<(elems.size()?"[..]":"[]");
|
||||
return;
|
||||
}
|
||||
++depth;
|
||||
size_t iter=0;
|
||||
std::cout<<'[';
|
||||
for(auto& i:elems)
|
||||
{
|
||||
i.print();
|
||||
std::cout<<",]"[(++iter)==elems.size()];
|
||||
}
|
||||
--depth;
|
||||
}
|
||||
|
||||
nasal_ref nasal_hash::get_val(const std::string& key)
|
||||
{
|
||||
if(elems.count(key))
|
||||
return elems[key];
|
||||
else if(elems.count("parents"))
|
||||
{
|
||||
nasal_ref ret(vm_none);
|
||||
nasal_ref val=elems["parents"];
|
||||
if(val.type==vm_vec)
|
||||
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 {vm_none};
|
||||
}
|
||||
nasal_ref* nasal_hash::get_mem(const std::string& key)
|
||||
{
|
||||
if(elems.count(key))
|
||||
return &elems[key];
|
||||
else if(elems.count("parents"))
|
||||
{
|
||||
nasal_ref* addr=nullptr;
|
||||
nasal_ref val=elems["parents"];
|
||||
if(val.type==vm_vec)
|
||||
for(auto& i:val.vec()->elems)
|
||||
{
|
||||
if(i.type==vm_hash)
|
||||
addr=i.hash()->get_mem(key);
|
||||
if(addr)
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void nasal_hash::print()
|
||||
{
|
||||
static int depth=0;
|
||||
if(!elems.size() || depth>8)
|
||||
{
|
||||
std::cout<<(elems.size()?"{..}":"{}");
|
||||
return;
|
||||
}
|
||||
++depth;
|
||||
size_t iter=0;
|
||||
std::cout<<'{';
|
||||
for(auto& i:elems)
|
||||
{
|
||||
std::cout<<i.first<<':';
|
||||
i.second.print();
|
||||
std::cout<<",}"[(++iter)==elems.size()];
|
||||
}
|
||||
--depth;
|
||||
}
|
||||
|
||||
void nasal_func::clear()
|
||||
{
|
||||
dynpara=-1;
|
||||
local.clear();
|
||||
upvalue.clear();
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
nasal_val::nasal_val(uint8_t val_type)
|
||||
{
|
||||
mark=GC_COLLECTED;
|
||||
type=val_type;
|
||||
unmut=0;
|
||||
switch(val_type)
|
||||
{
|
||||
case vm_str: ptr.str=new std::string; break;
|
||||
case vm_vec: ptr.vec=new nasal_vec; break;
|
||||
case vm_hash: ptr.hash=new nasal_hash; break;
|
||||
case vm_func: ptr.func=new nasal_func; break;
|
||||
case vm_obj: ptr.obj=new nasal_obj; break;
|
||||
}
|
||||
}
|
||||
nasal_val::~nasal_val()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case vm_str: delete ptr.str; break;
|
||||
case vm_vec: delete ptr.vec; break;
|
||||
case vm_hash: delete ptr.hash; break;
|
||||
case vm_func: delete ptr.func; break;
|
||||
case vm_obj: delete ptr.obj; break;
|
||||
}
|
||||
type=vm_nil;
|
||||
}
|
||||
double nasal_ref::to_number()
|
||||
{
|
||||
return type!=vm_str?value.num:str2num(str()->c_str());
|
||||
}
|
||||
std::string nasal_ref::to_string()
|
||||
{
|
||||
if(type==vm_str)
|
||||
return *str();
|
||||
else if(type==vm_num)
|
||||
return std::to_string(num());
|
||||
return "";
|
||||
}
|
||||
void nasal_ref::print()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case vm_none: std::cout<<"undefined"; break;
|
||||
case vm_nil: std::cout<<"nil"; break;
|
||||
case vm_num: std::cout<<value.num; break;
|
||||
case vm_str: std::cout<<*(this->str());break;
|
||||
case vm_vec: this->vec()->print(); break;
|
||||
case vm_hash: this->hash()->print(); break;
|
||||
case vm_func: std::cout<<"func(..){..}";break;
|
||||
case vm_obj: std::cout<<"<object>"; break;
|
||||
}
|
||||
}
|
||||
inline uint32_t nasal_ref::ret (){return value.ret; }
|
||||
inline int64_t& nasal_ref::cnt (){return value.cnt; }
|
||||
inline double& nasal_ref::num (){return value.num; }
|
||||
inline std::string* nasal_ref::str (){return value.gcobj->ptr.str; }
|
||||
inline nasal_vec* nasal_ref::vec (){return value.gcobj->ptr.vec; }
|
||||
inline nasal_hash* nasal_ref::hash(){return value.gcobj->ptr.hash;}
|
||||
inline nasal_func* nasal_ref::func(){return value.gcobj->ptr.func;}
|
||||
inline nasal_obj* nasal_ref::obj (){return value.gcobj->ptr.obj; }
|
||||
|
||||
const uint32_t STACK_MAX_DEPTH=4095;
|
||||
const nasal_ref zero={vm_num,(double)0};
|
||||
const nasal_ref one ={vm_num,(double)1};
|
||||
const nasal_ref nil ={vm_nil,nullptr};
|
||||
struct nasal_gc
|
||||
{
|
||||
nasal_ref stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow
|
||||
nasal_ref* top; // stack top
|
||||
std::vector<nasal_ref> strs; // reserved address for const vm_str
|
||||
std::vector<nasal_val*> memory; // gc memory
|
||||
std::queue<nasal_val*> free_list[vm_type_size]; // gc free list
|
||||
std::vector<nasal_ref> local;
|
||||
uint64_t size[vm_type_size];
|
||||
uint64_t count[vm_type_size];
|
||||
void mark();
|
||||
void sweep();
|
||||
void init(const std::vector<std::string>&);
|
||||
void clear();
|
||||
void info();
|
||||
nasal_ref alloc(const uint8_t);
|
||||
nasal_ref builtin_alloc(const uint8_t);
|
||||
};
|
||||
|
||||
/* gc functions */
|
||||
void nasal_gc::mark()
|
||||
{
|
||||
std::queue<nasal_ref> bfs;
|
||||
for(auto& i:local)
|
||||
bfs.push(i);
|
||||
for(nasal_ref* i=stack;i<=top;++i)
|
||||
bfs.push(*i);
|
||||
while(!bfs.empty())
|
||||
{
|
||||
nasal_ref tmp=bfs.front();
|
||||
bfs.pop();
|
||||
if(tmp.type<=vm_num || tmp.value.gcobj->mark) continue;
|
||||
tmp.value.gcobj->mark=GC_FOUND;
|
||||
switch(tmp.type)
|
||||
{
|
||||
case vm_vec:
|
||||
for(auto& i:tmp.vec()->elems)
|
||||
bfs.push(i);
|
||||
break;
|
||||
case vm_hash:
|
||||
for(auto& i:tmp.hash()->elems)
|
||||
bfs.push(i.second);
|
||||
break;
|
||||
case vm_func:
|
||||
for(auto& i:tmp.func()->local)
|
||||
bfs.push(i);
|
||||
for(auto& i:tmp.func()->upvalue)
|
||||
bfs.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void nasal_gc::sweep()
|
||||
{
|
||||
for(auto i:memory)
|
||||
{
|
||||
if(i->mark==GC_UNCOLLECTED)
|
||||
{
|
||||
switch(i->type)
|
||||
{
|
||||
case vm_str: i->ptr.str->clear(); break;
|
||||
case vm_vec: i->ptr.vec->elems.clear(); break;
|
||||
case vm_hash:i->ptr.hash->elems.clear();break;
|
||||
case vm_func:i->ptr.func->clear(); break;
|
||||
case vm_obj: i->ptr.obj->clear(); break;
|
||||
}
|
||||
free_list[i->type].push(i);
|
||||
i->mark=GC_COLLECTED;
|
||||
}
|
||||
else if(i->mark==GC_FOUND)
|
||||
i->mark=GC_UNCOLLECTED;
|
||||
}
|
||||
}
|
||||
void nasal_gc::init(const std::vector<std::string>& s)
|
||||
{
|
||||
for(uint8_t i=0;i<vm_type_size;++i)
|
||||
size[i]=count[i]=0;
|
||||
for(uint8_t i=vm_str;i<vm_type_size;++i)
|
||||
for(uint32_t j=0;j<increment[i];++j)
|
||||
{
|
||||
nasal_val* tmp=new nasal_val(i);
|
||||
memory.push_back(tmp);
|
||||
free_list[i].push(tmp);
|
||||
}
|
||||
top=stack;
|
||||
// init constant strings
|
||||
strs.resize(s.size());
|
||||
for(uint32_t i=0;i<strs.size();++i)
|
||||
{
|
||||
strs[i]={vm_str,new nasal_val(vm_str)};
|
||||
strs[i].value.gcobj->unmut=1;
|
||||
*strs[i].str()=s[i];
|
||||
}
|
||||
}
|
||||
void nasal_gc::clear()
|
||||
{
|
||||
for(auto i:memory)
|
||||
delete i;
|
||||
memory.clear();
|
||||
for(uint8_t i=0;i<vm_type_size;++i)
|
||||
while(!free_list[i].empty())
|
||||
free_list[i].pop();
|
||||
local.clear();
|
||||
for(auto& i:strs)
|
||||
delete i.value.gcobj;
|
||||
strs.clear();
|
||||
}
|
||||
void nasal_gc::info()
|
||||
{
|
||||
const char* name[]={
|
||||
"null","cnt ","ret ",
|
||||
"nil ","num ","str ",
|
||||
"func","vec ","hash","obj "
|
||||
};
|
||||
std::cout<<"\ngarbage collector info:\n";
|
||||
for(uint8_t i=vm_str;i<vm_type_size;++i)
|
||||
std::cout<<name[i]<<" | "<<count[i]<<"\n";
|
||||
std::cout<<"\nmemory allocator info(max size):\n";
|
||||
for(uint8_t i=vm_str;i<vm_type_size;++i)
|
||||
std::cout<<name[i]<<" | "<<(size[i]+1)*increment[i]<<"(+"<<size[i]<<")\n";
|
||||
}
|
||||
nasal_ref nasal_gc::alloc(uint8_t type)
|
||||
{
|
||||
if(free_list[type].empty())
|
||||
{
|
||||
++count[type];
|
||||
mark();
|
||||
sweep();
|
||||
}
|
||||
if(free_list[type].empty())
|
||||
{
|
||||
++size[type];
|
||||
for(uint32_t i=0;i<increment[type];++i)
|
||||
{
|
||||
nasal_val* tmp=new nasal_val(type);
|
||||
memory.push_back(tmp);
|
||||
free_list[type].push(tmp);
|
||||
}
|
||||
}
|
||||
nasal_ref ret={type,free_list[type].front()};
|
||||
ret.value.gcobj->mark=GC_UNCOLLECTED;
|
||||
free_list[type].pop();
|
||||
return ret;
|
||||
}
|
||||
nasal_ref nasal_gc::builtin_alloc(uint8_t type)
|
||||
{
|
||||
// when running a builtin function,alloc will run more than one time
|
||||
// this may cause mark-sweep in gc::alloc
|
||||
// and the value got before will be collected,this is a fatal error
|
||||
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
|
||||
if(free_list[type].empty())
|
||||
{
|
||||
++size[type];
|
||||
for(uint32_t i=0;i<increment[type];++i)
|
||||
{
|
||||
nasal_val* tmp=new nasal_val(type);
|
||||
memory.push_back(tmp);
|
||||
free_list[type].push(tmp);
|
||||
}
|
||||
}
|
||||
nasal_ref ret={type,free_list[type].front()};
|
||||
ret.value.gcobj->mark=GC_UNCOLLECTED;
|
||||
free_list[type].pop();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
#ifndef __NASAL_IMPORT_H__
|
||||
#define __NASAL_IMPORT_H__
|
||||
|
||||
class nasal_import
|
||||
{
|
||||
private:
|
||||
nasal_err& nerr;
|
||||
std::vector<std::string> files;
|
||||
bool check_import(const nasal_ast&);
|
||||
bool check_exist(const std::string&);
|
||||
void linker(nasal_ast&,nasal_ast&&);
|
||||
nasal_ast file_import(nasal_ast&);
|
||||
nasal_ast load(nasal_ast&,uint16_t);
|
||||
public:
|
||||
nasal_import(nasal_err& e):nerr(e){}
|
||||
void link(nasal_parse&,const std::string&);
|
||||
const std::vector<std::string>& get_file() const {return files;}
|
||||
};
|
||||
|
||||
bool nasal_import::check_import(const nasal_ast& node)
|
||||
{
|
||||
/*
|
||||
only this kind of node can be recognized as 'import':
|
||||
call
|
||||
|_id:import
|
||||
|_call_func
|
||||
|_string:'filename'
|
||||
*/
|
||||
return (
|
||||
node.type()==ast_call &&
|
||||
node.size()==2 &&
|
||||
node[0].str()=="import" &&
|
||||
node[1].type()==ast_callf &&
|
||||
node[1].size()==1 &&
|
||||
node[1][0].type()==ast_str
|
||||
);
|
||||
}
|
||||
|
||||
bool nasal_import::check_exist(const std::string& file)
|
||||
{
|
||||
// avoid importing the same file
|
||||
for(auto& fname:files)
|
||||
if(file==fname)
|
||||
return true;
|
||||
files.push_back(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
|
||||
{
|
||||
// add children of add_root to the back of root
|
||||
for(auto& i:add_root.child())
|
||||
root.add(std::move(i));
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::file_import(nasal_ast& node)
|
||||
{
|
||||
nasal_lexer lex(nerr);
|
||||
nasal_parse par(nerr);
|
||||
// get filename and set node to ast_null
|
||||
std::string filename=node[1][0].str();
|
||||
node.clear();
|
||||
|
||||
// avoid infinite loading loop
|
||||
if(check_exist(filename))
|
||||
return {0,ast_root};
|
||||
|
||||
// start importing...
|
||||
lex.scan(filename);
|
||||
par.compile(lex);
|
||||
nasal_ast tmp=std::move(par.ast());
|
||||
// check if tmp has 'import'
|
||||
return load(tmp,files.size()-1);
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
|
||||
{
|
||||
nasal_ast new_root(0,ast_root);
|
||||
for(auto& i:root.child())
|
||||
if(check_import(i))
|
||||
linker(new_root,file_import(i));
|
||||
// add root to the back of new_root
|
||||
nasal_ast file_head(0,ast_file);
|
||||
file_head.set_num(fileindex);
|
||||
new_root.add(std::move(file_head));
|
||||
linker(new_root,std::move(root));
|
||||
return new_root;
|
||||
}
|
||||
|
||||
void nasal_import::link(nasal_parse& parse,const std::string& self)
|
||||
{
|
||||
// initializing
|
||||
files={self};
|
||||
// scan root and import files,then generate a new ast and return to import_ast
|
||||
// the main file's index is 0
|
||||
parse.ast()=load(parse.ast(),0);
|
||||
}
|
||||
|
||||
#endif
|
||||
354
nasal_lexer.h
@@ -1,354 +0,0 @@
|
||||
#ifndef __NASAL_LEXER_H__
|
||||
#define __NASAL_LEXER_H__
|
||||
|
||||
#define ID(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
|
||||
#define HEX(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
|
||||
#define OCT(c) ('0'<=c&&c<='7')
|
||||
#define DIGIT(c) ('0'<=c&&c<='9')
|
||||
#define STR(c) (c=='\''||c=='\"'||c=='`')
|
||||
// single operators have only one character
|
||||
#define SINGLE_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
|
||||
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
|
||||
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
|
||||
#define CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
|
||||
#define NOTE(c) (c=='#')
|
||||
|
||||
enum token_type
|
||||
{
|
||||
tok_null=0,// null token default token type
|
||||
tok_num, // number basic token type
|
||||
tok_str, // string basic token type
|
||||
tok_id, // identifier basic token type
|
||||
tok_for,tok_forindex,tok_foreach,tok_while,
|
||||
tok_var,tok_func,tok_break,tok_continue,
|
||||
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
|
||||
tok_lcurve,tok_rcurve,
|
||||
tok_lbracket,tok_rbracket,
|
||||
tok_lbrace,tok_rbrace,
|
||||
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
|
||||
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
|
||||
tok_eq,
|
||||
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
|
||||
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
|
||||
tok_eof // end of token list
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
const char* str;
|
||||
const uint32_t tok_type;
|
||||
}token_table[]=
|
||||
{
|
||||
{"for" ,tok_for },
|
||||
{"forindex",tok_forindex },
|
||||
{"foreach" ,tok_foreach },
|
||||
{"while" ,tok_while },
|
||||
{"var" ,tok_var },
|
||||
{"func" ,tok_func },
|
||||
{"break" ,tok_break },
|
||||
{"continue",tok_continue },
|
||||
{"return" ,tok_ret },
|
||||
{"if" ,tok_if },
|
||||
{"elsif" ,tok_elsif },
|
||||
{"else" ,tok_else },
|
||||
{"nil" ,tok_nil },
|
||||
{"(" ,tok_lcurve },
|
||||
{")" ,tok_rcurve },
|
||||
{"[" ,tok_lbracket },
|
||||
{"]" ,tok_rbracket },
|
||||
{"{" ,tok_lbrace },
|
||||
{"}" ,tok_rbrace },
|
||||
{";" ,tok_semi },
|
||||
{"and" ,tok_and },
|
||||
{"or" ,tok_or },
|
||||
{"," ,tok_comma },
|
||||
{"." ,tok_dot },
|
||||
{"..." ,tok_ellipsis },
|
||||
{"?" ,tok_quesmark },
|
||||
{":" ,tok_colon },
|
||||
{"+" ,tok_add },
|
||||
{"-" ,tok_sub },
|
||||
{"*" ,tok_mult },
|
||||
{"/" ,tok_div },
|
||||
{"~" ,tok_link },
|
||||
{"!" ,tok_not },
|
||||
{"=" ,tok_eq },
|
||||
{"+=" ,tok_addeq },
|
||||
{"-=" ,tok_subeq },
|
||||
{"*=" ,tok_multeq },
|
||||
{"/=" ,tok_diveq },
|
||||
{"~=" ,tok_lnkeq },
|
||||
{"==" ,tok_cmpeq },
|
||||
{"!=" ,tok_neq },
|
||||
{"<" ,tok_less },
|
||||
{"<=" ,tok_leq },
|
||||
{">" ,tok_grt },
|
||||
{">=" ,tok_geq },
|
||||
{nullptr ,0 }
|
||||
};
|
||||
|
||||
struct token
|
||||
{
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
uint32_t type;
|
||||
std::string str;
|
||||
token(uint32_t l=0,uint32_t c=0,uint32_t t=tok_null,std::string s="")
|
||||
{
|
||||
line=l;
|
||||
column=c;
|
||||
type=t;
|
||||
str=s;
|
||||
}
|
||||
};
|
||||
|
||||
class nasal_lexer
|
||||
{
|
||||
private:
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
uint32_t ptr;
|
||||
nasal_err& nerr;
|
||||
std::string res;
|
||||
std::vector<token> tokens;
|
||||
|
||||
uint32_t get_type(const std::string&);
|
||||
void die(std::string info){nerr.err("lexer",line,column,info);};
|
||||
void open(const std::string&);
|
||||
std::string id_gen();
|
||||
std::string num_gen();
|
||||
std::string str_gen();
|
||||
public:
|
||||
nasal_lexer(nasal_err& e):nerr(e){}
|
||||
void scan(const std::string&);
|
||||
void print();
|
||||
const std::vector<token>& get_tokens() const {return tokens;}
|
||||
};
|
||||
|
||||
void nasal_lexer::open(const std::string& file)
|
||||
{
|
||||
std::ifstream fin(file,std::ios::binary);
|
||||
if(fin.fail())
|
||||
nerr.err("lexer","cannot open file <"+file+">.");
|
||||
else
|
||||
nerr.load(file);
|
||||
std::stringstream ss;
|
||||
ss<<fin.rdbuf();
|
||||
res=ss.str();
|
||||
}
|
||||
|
||||
uint32_t nasal_lexer::get_type(const std::string& tk_str)
|
||||
{
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(tk_str==token_table[i].str)
|
||||
return token_table[i].tok_type;
|
||||
return tok_null;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::id_gen()
|
||||
{
|
||||
std::string str="";
|
||||
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::num_gen()
|
||||
{
|
||||
// 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() && HEX(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
if(str.length()<3)// "0x"
|
||||
die("invalid number:"+str);
|
||||
return str;
|
||||
}
|
||||
// generate oct number
|
||||
else if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o')
|
||||
{
|
||||
std::string str="0o";
|
||||
ptr+=2;
|
||||
while(ptr<res.size() && OCT(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
if(str.length()<3)// "0o"
|
||||
die("invalid number:"+str);
|
||||
return str;
|
||||
}
|
||||
// generate dec number
|
||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||
std::string str="";
|
||||
while(ptr<res.size() && DIGIT(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
if(ptr<res.size() && res[ptr]=='.')
|
||||
{
|
||||
str+=res[ptr++];
|
||||
while(ptr<res.size() && DIGIT(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
// "xxxx." is not a correct number
|
||||
if(str.back()=='.')
|
||||
{
|
||||
column+=str.length();
|
||||
die("invalid number:"+str);
|
||||
return "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() && DIGIT(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
// "xxxe(-|+)" is not a correct number
|
||||
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
|
||||
{
|
||||
column+=str.length();
|
||||
die("invalid number:"+str);
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
column+=str.length();
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::str_gen()
|
||||
{
|
||||
std::string str="";
|
||||
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 '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;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
str+=res[ptr];
|
||||
}
|
||||
// check if this string ends with a " or '
|
||||
if(ptr++>=res.size())
|
||||
{
|
||||
die("get EOF when generating string.");
|
||||
return str;
|
||||
}
|
||||
++column;
|
||||
if(begin=='`' && str.length()!=1)
|
||||
die("\'`\' is used for string that includes one character.");
|
||||
return str;
|
||||
}
|
||||
|
||||
void nasal_lexer::scan(const std::string& file)
|
||||
{
|
||||
line=1;
|
||||
column=0;
|
||||
ptr=0;
|
||||
open(file);
|
||||
|
||||
std::string 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
|
||||
++column;
|
||||
if(res[ptr++]=='\n')
|
||||
{
|
||||
++line;
|
||||
column=0;
|
||||
}
|
||||
}
|
||||
if(ptr>=res.size()) break;
|
||||
if(ID(res[ptr]))
|
||||
{
|
||||
str=id_gen();
|
||||
uint32_t type=get_type(str);
|
||||
tokens.push_back({line,column,type?type:tok_id,str});
|
||||
}
|
||||
else if(DIGIT(res[ptr]))
|
||||
{
|
||||
str=num_gen(); // make sure column is correct
|
||||
tokens.push_back({line,column,tok_num,str});
|
||||
}
|
||||
else if(STR(res[ptr]))
|
||||
{
|
||||
str=str_gen(); // make sure column is correct
|
||||
tokens.push_back({line,column,tok_str,str});
|
||||
}
|
||||
else if(SINGLE_OPERATOR(res[ptr]))
|
||||
{
|
||||
str=res[ptr];
|
||||
++column;
|
||||
uint32_t type=get_type(str);
|
||||
if(!type)
|
||||
die("invalid operator:"+str);
|
||||
tokens.push_back({line,column,type,str});
|
||||
++ptr;
|
||||
}
|
||||
else if(res[ptr]=='.')
|
||||
{
|
||||
str=".";
|
||||
if(ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.')
|
||||
str+="..";
|
||||
ptr+=str.length();
|
||||
column+=str.length();
|
||||
tokens.push_back({line,column,get_type(str),str});
|
||||
}
|
||||
else if(CALC_OPERATOR(res[ptr]))
|
||||
{
|
||||
// get calculation operator
|
||||
str=res[ptr++];
|
||||
if(ptr<res.size() && res[ptr]=='=')
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
tokens.push_back({line,column,get_type(str),str});
|
||||
}
|
||||
else if(NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
|
||||
while(++ptr<res.size() && res[ptr]!='\n');
|
||||
else
|
||||
{
|
||||
++column;
|
||||
++ptr;
|
||||
die("unknown character.");
|
||||
}
|
||||
}
|
||||
tokens.push_back({line,column,tok_eof,"eof"});
|
||||
res="";
|
||||
nerr.chkerr();
|
||||
}
|
||||
|
||||
void nasal_lexer::print()
|
||||
{
|
||||
for(auto& tok:tokens)
|
||||
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str)<<")\n";
|
||||
}
|
||||
|
||||
#endif
|
||||
62
nasal_opt.h
@@ -1,62 +0,0 @@
|
||||
#ifndef __NASAL_OPT_H__
|
||||
#define __NASAL_OPT_H__
|
||||
|
||||
void const_str(nasal_ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
root.set_str(vec[0].str()+vec[1].str());
|
||||
root.child().clear();
|
||||
root.set_type(ast_str);
|
||||
}
|
||||
|
||||
void const_num(nasal_ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
double res;
|
||||
switch(root.type())
|
||||
{
|
||||
case ast_add: res=vec[0].num()+vec[1].num(); break;
|
||||
case ast_sub: res=vec[0].num()-vec[1].num(); break;
|
||||
case ast_mult:res=vec[0].num()*vec[1].num(); break;
|
||||
case ast_div: res=vec[0].num()/vec[1].num(); break;
|
||||
case ast_less:res=vec[0].num()<vec[1].num(); break;
|
||||
case ast_leq: res=vec[0].num()<=vec[1].num();break;
|
||||
case ast_grt: res=vec[0].num()>vec[1].num(); break;
|
||||
case ast_geq: res=vec[0].num()>=vec[1].num();break;
|
||||
}
|
||||
// inf and nan will cause number hashmap error in codegen
|
||||
if(std::isinf(res) || std::isnan(res))
|
||||
return;
|
||||
root.set_num(res);
|
||||
root.child().clear();
|
||||
root.set_type(ast_num);
|
||||
}
|
||||
|
||||
void calc_const(nasal_ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
for(auto& i:vec)
|
||||
calc_const(i);
|
||||
if(vec.size()!=2)
|
||||
return;
|
||||
if(root.type()!=ast_add &&
|
||||
root.type()!=ast_sub &&
|
||||
root.type()!=ast_mult &&
|
||||
root.type()!=ast_div &&
|
||||
root.type()!=ast_link &&
|
||||
root.type()!=ast_less &&
|
||||
root.type()!=ast_leq &&
|
||||
root.type()!=ast_grt &&
|
||||
root.type()!=ast_geq)
|
||||
return;
|
||||
if(root.type()==ast_link && vec[0].type()==ast_str && vec[1].type()==ast_str)
|
||||
const_str(root);
|
||||
else if(root.type()!=ast_link && vec[0].type()==ast_num && vec[1].type()==ast_num)
|
||||
const_num(root);
|
||||
}
|
||||
void optimize(nasal_ast& root)
|
||||
{
|
||||
for(auto& i:root.child())
|
||||
calc_const(i);
|
||||
}
|
||||
#endif
|
||||
1012
nasal_parse.h
985
nasal_vm.h
@@ -1,985 +0,0 @@
|
||||
#ifndef __NASAL_VM_H__
|
||||
#define __NASAL_VM_H__
|
||||
|
||||
class nasal_vm
|
||||
{
|
||||
protected:
|
||||
/* values of nasal_vm */
|
||||
uint32_t pc; // program counter
|
||||
const double* num_table;// const numbers, ref from nasal_codegen
|
||||
const std::string* str_table;// const symbols, ref from nasal_codegen
|
||||
std::stack<nasal_func*> fstk; // stack to store function, used to get upvalues
|
||||
std::stack<uint32_t> local_stk;
|
||||
std::vector<uint32_t> imm; // immediate number
|
||||
nasal_ref* mem_addr; // used for mem_call
|
||||
/* garbage collector */
|
||||
nasal_gc gc;
|
||||
/* values used for debug */
|
||||
size_t files_size;
|
||||
const std::string* files; // ref from nasal_import
|
||||
const opcode* bytecode; // ref from nasal_codegen
|
||||
/* canary to avoid stackoverflow */
|
||||
nasal_ref* canary;
|
||||
|
||||
void init(
|
||||
const std::vector<std::string>&,
|
||||
const std::vector<double>&,
|
||||
const std::vector<std::string>&);
|
||||
/* debug functions */
|
||||
bool detail_info;
|
||||
void valinfo(nasal_ref&);
|
||||
void bytecodeinfo(const char*,const uint32_t);
|
||||
void traceback();
|
||||
void stackinfo(const uint32_t);
|
||||
void global_state();
|
||||
void local_state();
|
||||
void upval_state();
|
||||
void detail();
|
||||
void opcallsort(const uint64_t*);
|
||||
void die(std::string);
|
||||
/* vm calculation functions*/
|
||||
bool condition(nasal_ref);
|
||||
void opr_nop();
|
||||
void opr_intg();
|
||||
void opr_intl();
|
||||
void opr_loadg();
|
||||
void opr_loadl();
|
||||
void opr_loadu();
|
||||
void opr_pnum();
|
||||
void opr_pnil();
|
||||
void opr_pstr();
|
||||
void opr_newv();
|
||||
void opr_newh();
|
||||
void opr_newf();
|
||||
void opr_happ();
|
||||
void opr_para();
|
||||
void opr_defpara();
|
||||
void opr_dynpara();
|
||||
void opr_unot();
|
||||
void opr_usub();
|
||||
void opr_add();
|
||||
void opr_sub();
|
||||
void opr_mul();
|
||||
void opr_div();
|
||||
void opr_lnk();
|
||||
void opr_addc();
|
||||
void opr_subc();
|
||||
void opr_mulc();
|
||||
void opr_divc();
|
||||
void opr_lnkc();
|
||||
void opr_addeq();
|
||||
void opr_subeq();
|
||||
void opr_muleq();
|
||||
void opr_diveq();
|
||||
void opr_lnkeq();
|
||||
void opr_addeqc();
|
||||
void opr_subeqc();
|
||||
void opr_muleqc();
|
||||
void opr_diveqc();
|
||||
void opr_lnkeqc();
|
||||
void opr_meq();
|
||||
void opr_eq();
|
||||
void opr_neq();
|
||||
void opr_less();
|
||||
void opr_leq();
|
||||
void opr_grt();
|
||||
void opr_geq();
|
||||
void opr_lessc();
|
||||
void opr_leqc();
|
||||
void opr_grtc();
|
||||
void opr_geqc();
|
||||
void opr_pop();
|
||||
void opr_jmp();
|
||||
void opr_jt();
|
||||
void opr_jf();
|
||||
void opr_counter();
|
||||
void opr_findex();
|
||||
void opr_feach();
|
||||
void opr_callg();
|
||||
void opr_calll();
|
||||
void opr_upval();
|
||||
void opr_callv();
|
||||
void opr_callvi();
|
||||
void opr_callh();
|
||||
void opr_callfv();
|
||||
void opr_callfh();
|
||||
void opr_callb();
|
||||
void opr_slcbegin();
|
||||
void opr_slcend();
|
||||
void opr_slc();
|
||||
void opr_slc2();
|
||||
void opr_mcallg();
|
||||
void opr_mcalll();
|
||||
void opr_mupval();
|
||||
void opr_mcallv();
|
||||
void opr_mcallh();
|
||||
void opr_ret();
|
||||
public:
|
||||
void run(
|
||||
const nasal_codegen&,
|
||||
const nasal_import&,
|
||||
const bool,
|
||||
const bool);
|
||||
};
|
||||
|
||||
void nasal_vm::init(
|
||||
const std::vector<std::string>& strs,
|
||||
const std::vector<double>& nums,
|
||||
const std::vector<std::string>& filenames)
|
||||
{
|
||||
gc.init(strs);
|
||||
num_table=nums.data();
|
||||
str_table=strs.data();
|
||||
files=filenames.data();
|
||||
files_size=filenames.size();
|
||||
}
|
||||
void nasal_vm::valinfo(nasal_ref& val)
|
||||
{
|
||||
const nasal_val* p=val.value.gcobj;
|
||||
printf("\t");
|
||||
switch(val.type)
|
||||
{
|
||||
case vm_none: printf("| null |\n");break;
|
||||
case vm_ret: printf("| addr | pc:0x%x\n",val.ret());break;
|
||||
case vm_cnt: printf("| cnt | %ld\n",val.cnt());break;
|
||||
case vm_nil: printf("| nil |\n");break;
|
||||
case vm_num: printf("| num | ");std::cout<<val.num()<<'\n';break;
|
||||
case vm_str: printf("| str | <0x%lx> %s\n",(uint64_t)p,rawstr(*val.str()).c_str());break;
|
||||
case vm_func: printf("| func | <0x%lx> entry:0x%x\n",(uint64_t)p,val.func()->entry);break;
|
||||
case vm_vec: printf("| vec | <0x%lx> [%lu val]\n",(uint64_t)p,val.vec()->elems.size());break;
|
||||
case vm_hash: printf("| hash | <0x%lx> {%lu val}\n",(uint64_t)p,val.hash()->elems.size());break;
|
||||
case vm_obj: printf("| obj | <0x%lx> obj:0x%lx\n",(uint64_t)p,(uint64_t)val.obj()->ptr);break;
|
||||
default: printf("| err | <0x%lx> unknown object\n",(uint64_t)p);break;
|
||||
}
|
||||
}
|
||||
void nasal_vm::bytecodeinfo(const char* header,const uint32_t p)
|
||||
{
|
||||
const opcode& c=bytecode[p];
|
||||
printf("%s0x%.8x: %s 0x%x",header,p,code_table[c.op].name,c.num);
|
||||
switch(c.op)
|
||||
{
|
||||
case op_addc: case op_subc: case op_mulc: case op_divc:
|
||||
case op_addeqc:case op_subeqc: case op_muleqc:case op_diveqc:
|
||||
case op_lessc: case op_leqc: case op_grtc: case op_geqc:
|
||||
case op_pnum:
|
||||
std::cout<<" ("<<num_table[c.num]<<")";break;
|
||||
case op_callb:
|
||||
printf(" <%s@0x%lx>",builtin[c.num].name,(uint64_t)builtin[c.num].func);break;
|
||||
case op_happ: case op_pstr:
|
||||
case op_lnkc: case op_lnkeqc:
|
||||
case op_callh: case op_mcallh:
|
||||
case op_para: case op_defpara:case op_dynpara:
|
||||
printf(" (\"%s\")",rawstr(str_table[c.num]).c_str());break;
|
||||
case op_upval:case op_mupval: case op_loadu:
|
||||
printf(" (0x%x[0x%x])",(c.num>>16)&0xffff,c.num&0xffff);break;
|
||||
default:break;
|
||||
}
|
||||
printf(" (%s:%d)\n",files[c.fidx].c_str(),c.line);
|
||||
}
|
||||
void nasal_vm::traceback()
|
||||
{
|
||||
uint32_t global_size=bytecode[0].num; // bytecode[0] is op_intg
|
||||
nasal_ref* top=gc.top;
|
||||
nasal_ref* bottom=gc.stack+global_size;
|
||||
std::stack<uint32_t> ret;
|
||||
for(nasal_ref* i=bottom;i<=top;++i)
|
||||
if(i->type==vm_ret)
|
||||
ret.push(i->ret());
|
||||
// push pc to ret stack to store the position program crashed
|
||||
ret.push(pc);
|
||||
printf("trace back:\n");
|
||||
uint32_t same=0,last=0xffffffff;
|
||||
for(uint32_t point=0;!ret.empty();last=point,ret.pop())
|
||||
{
|
||||
if((point=ret.top())==last)
|
||||
{
|
||||
++same;
|
||||
continue;
|
||||
}
|
||||
if(same)
|
||||
printf("\t0x%.8x: %d same call(s)\n",last,same);
|
||||
same=0;
|
||||
bytecodeinfo("\t",point);
|
||||
}
|
||||
if(same)
|
||||
printf("\t0x%.8x: %d same call(s)\n",last,same);
|
||||
}
|
||||
void nasal_vm::stackinfo(const uint32_t limit=10)
|
||||
{
|
||||
uint32_t global_size=bytecode[0].num; // bytecode[0] is op_intg
|
||||
nasal_ref* top=gc.top;
|
||||
nasal_ref* bottom=gc.stack+global_size;
|
||||
if(top<bottom)
|
||||
{
|
||||
printf("vm stack(limit %d, total 0)\n",limit);
|
||||
return;
|
||||
}
|
||||
printf("vm stack(limit %d, total %ld):\n",limit,top-bottom+1);
|
||||
for(uint32_t i=0;i<limit && top>=bottom;++i,--top)
|
||||
valinfo(top[0]);
|
||||
}
|
||||
void nasal_vm::global_state()
|
||||
{
|
||||
if(!bytecode[0].num || gc.stack[0].type==vm_none) // bytecode[0].op is op_intg
|
||||
{
|
||||
printf("no global value exists\n");
|
||||
return;
|
||||
}
|
||||
printf("global:\n");
|
||||
for(uint32_t i=0;i<bytecode[0].num;++i)
|
||||
{
|
||||
printf("[0x%.8x]",i);
|
||||
valinfo(gc.stack[i]);
|
||||
}
|
||||
}
|
||||
void nasal_vm::local_state()
|
||||
{
|
||||
if(gc.local.empty() || !gc.local.back().vec()->elems.size())
|
||||
{
|
||||
printf("no local value exists\n");
|
||||
return;
|
||||
}
|
||||
printf("local:\n");
|
||||
auto& vec=gc.local.back().vec()->elems;
|
||||
for(uint32_t i=0;i<vec.size();++i)
|
||||
{
|
||||
printf("[0x%.8x]",i);
|
||||
valinfo(vec[i]);
|
||||
}
|
||||
}
|
||||
void nasal_vm::upval_state()
|
||||
{
|
||||
if(fstk.empty() || fstk.top()->upvalue.empty())
|
||||
{
|
||||
printf("no upvalue exists\n");
|
||||
return;
|
||||
}
|
||||
printf("upvalue:\n");
|
||||
auto& upval=fstk.top()->upvalue;
|
||||
for(uint32_t i=0;i<upval.size();++i)
|
||||
{
|
||||
auto& vec=upval[i].vec()->elems;
|
||||
for(uint32_t j=0;j<vec.size();++j)
|
||||
{
|
||||
printf("[%.4x][%.4x]",i,j);
|
||||
valinfo(vec[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
void nasal_vm::detail()
|
||||
{
|
||||
printf("mcall address: 0x%lx\n",(uint64_t)mem_addr);
|
||||
global_state();
|
||||
local_state();
|
||||
upval_state();
|
||||
}
|
||||
void nasal_vm::opcallsort(const uint64_t* arr)
|
||||
{
|
||||
typedef std::pair<uint32_t,uint64_t> op;
|
||||
std::vector<op> opcall;
|
||||
for(uint32_t i=0;i<=op_exit;++i)
|
||||
opcall.push_back({i,arr[i]});
|
||||
std::sort(
|
||||
opcall.begin(),
|
||||
opcall.end(),
|
||||
[](op& a,op& b){return a.second>b.second;}
|
||||
);
|
||||
uint64_t total=0;
|
||||
for(auto& i:opcall)
|
||||
{
|
||||
if(!i.second)
|
||||
break;
|
||||
total+=i.second;
|
||||
std::cout<<'\n'<<code_table[i.first].name<<": "<<i.second;
|
||||
}
|
||||
std::cout<<"\ntotal : "<<total<<'\n';
|
||||
}
|
||||
void nasal_vm::die(std::string str)
|
||||
{
|
||||
printf("[vm] %s\n",str.c_str());
|
||||
traceback();
|
||||
stackinfo();
|
||||
if(detail_info)
|
||||
detail();
|
||||
std::exit(1);
|
||||
}
|
||||
inline bool nasal_vm::condition(nasal_ref val)
|
||||
{
|
||||
if(val.type==vm_num)
|
||||
return val.value.num;
|
||||
else if(val.type==vm_str)
|
||||
{
|
||||
double num=str2num(val.str()->c_str());
|
||||
if(std::isnan(num))
|
||||
return !val.str()->empty();
|
||||
return num;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
inline void nasal_vm::opr_nop(){}
|
||||
inline void nasal_vm::opr_intg()
|
||||
{
|
||||
// global values store on stack
|
||||
for(uint32_t i=0;i<imm[pc];++i)
|
||||
(gc.top++)[0].type=vm_nil;
|
||||
--gc.top;// point to the top
|
||||
}
|
||||
inline void nasal_vm::opr_intl()
|
||||
{
|
||||
gc.top[0].func()->local.resize(imm[pc],nil);
|
||||
gc.top[0].func()->lsize=imm[pc];
|
||||
}
|
||||
inline void nasal_vm::opr_loadg()
|
||||
{
|
||||
gc.stack[imm[pc]]=(gc.top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_loadl()
|
||||
{
|
||||
gc.local.back().vec()->elems[imm[pc]]=(gc.top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_loadu()
|
||||
{
|
||||
fstk.top()->upvalue[(imm[pc]>>16)&0xffff].vec()->elems[imm[pc]&0xffff]=(gc.top--)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_pnum()
|
||||
{
|
||||
(++gc.top)[0]={vm_num,num_table[imm[pc]]};
|
||||
}
|
||||
inline void nasal_vm::opr_pnil()
|
||||
{
|
||||
(++gc.top)[0]={vm_nil,(double)0};
|
||||
}
|
||||
inline void nasal_vm::opr_pstr()
|
||||
{
|
||||
(++gc.top)[0]=gc.strs[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_newv()
|
||||
{
|
||||
nasal_ref newv=gc.alloc(vm_vec);
|
||||
auto& vec=newv.vec()->elems;
|
||||
vec.resize(imm[pc]);
|
||||
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
|
||||
gc.top=gc.top-imm[pc]+1;
|
||||
for(uint32_t i=0;i<imm[pc];++i)
|
||||
vec[i]=gc.top[i];
|
||||
gc.top[0]=newv;
|
||||
}
|
||||
inline void nasal_vm::opr_newh()
|
||||
{
|
||||
(++gc.top)[0]=gc.alloc(vm_hash);
|
||||
}
|
||||
inline void nasal_vm::opr_newf()
|
||||
{
|
||||
(++gc.top)[0]=gc.alloc(vm_func);
|
||||
nasal_func* func=gc.top[0].func();
|
||||
func->entry=imm[pc];
|
||||
func->psize=1;
|
||||
if(!gc.local.empty())
|
||||
{
|
||||
func->upvalue=fstk.top()->upvalue;
|
||||
func->upvalue.push_back(gc.local.back());
|
||||
}
|
||||
}
|
||||
inline void nasal_vm::opr_happ()
|
||||
{
|
||||
gc.top[-1].hash()->elems[str_table[imm[pc]]]=gc.top[0];
|
||||
--gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_para()
|
||||
{
|
||||
nasal_func* func=gc.top[0].func();
|
||||
func->keys[str_table[imm[pc]]]=func->psize;// func->size has 1 place reserved for "me"
|
||||
func->local[func->psize++]={vm_none};
|
||||
}
|
||||
inline void nasal_vm::opr_defpara()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_func* func=(--gc.top)[0].func();
|
||||
func->keys[str_table[imm[pc]]]=func->psize;// func->size has 1 place reserved for "me"
|
||||
func->local[func->psize++]=val;
|
||||
}
|
||||
inline void nasal_vm::opr_dynpara()
|
||||
{
|
||||
gc.top[0].func()->dynpara=imm[pc];
|
||||
}
|
||||
inline void nasal_vm::opr_unot()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
switch(val.type)
|
||||
{
|
||||
case vm_nil:gc.top[0]=one;break;
|
||||
case vm_num:gc.top[0]=val.num()?zero:one;break;
|
||||
case vm_str:
|
||||
{
|
||||
double num=str2num(val.str()->c_str());
|
||||
if(std::isnan(num))
|
||||
gc.top[0]={vm_num,(double)val.str()->empty()};
|
||||
else
|
||||
gc.top[0]=num?zero:one;
|
||||
}
|
||||
break;
|
||||
default:die("unot: incorrect value type");break;
|
||||
}
|
||||
}
|
||||
inline void nasal_vm::opr_usub()
|
||||
{
|
||||
gc.top[0]={vm_num,-gc.top[0].to_number()};
|
||||
}
|
||||
|
||||
#define op_calc(type)\
|
||||
nasal_ref val(vm_num,gc.top[-1].to_number() type gc.top[0].to_number());\
|
||||
(--gc.top)[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_add(){op_calc(+);}
|
||||
inline void nasal_vm::opr_sub(){op_calc(-);}
|
||||
inline void nasal_vm::opr_mul(){op_calc(*);}
|
||||
inline void nasal_vm::opr_div(){op_calc(/);}
|
||||
inline void nasal_vm::opr_lnk()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
*val.str()=gc.top[-1].to_string()+gc.top[0].to_string();
|
||||
(--gc.top)[0]=val;
|
||||
}
|
||||
|
||||
#define op_calc_const(type)\
|
||||
nasal_ref val(vm_num,gc.top[0].to_number() type num_table[imm[pc]]);\
|
||||
gc.top[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_addc(){op_calc_const(+);}
|
||||
inline void nasal_vm::opr_subc(){op_calc_const(-);}
|
||||
inline void nasal_vm::opr_mulc(){op_calc_const(*);}
|
||||
inline void nasal_vm::opr_divc(){op_calc_const(/);}
|
||||
inline void nasal_vm::opr_lnkc()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
*val.str()=gc.top[0].to_string()+str_table[imm[pc]];
|
||||
gc.top[0]=val;
|
||||
}
|
||||
|
||||
#define op_calc_eq(type)\
|
||||
nasal_ref val(vm_num,mem_addr[0].to_number() type gc.top[-1].to_number());\
|
||||
(--gc.top)[0]=mem_addr[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_addeq(){op_calc_eq(+);}
|
||||
inline void nasal_vm::opr_subeq(){op_calc_eq(-);}
|
||||
inline void nasal_vm::opr_muleq(){op_calc_eq(*);}
|
||||
inline void nasal_vm::opr_diveq(){op_calc_eq(/);}
|
||||
inline void nasal_vm::opr_lnkeq()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
*val.str()=mem_addr[0].to_string()+gc.top[-1].to_string();
|
||||
(--gc.top)[0]=mem_addr[0]=val;
|
||||
}
|
||||
|
||||
#define op_calc_eq_const(type)\
|
||||
nasal_ref val(vm_num,mem_addr[0].to_number() type num_table[imm[pc]]);\
|
||||
gc.top[0]=mem_addr[0]=val;
|
||||
|
||||
inline void nasal_vm::opr_addeqc(){op_calc_eq_const(+);}
|
||||
inline void nasal_vm::opr_subeqc(){op_calc_eq_const(-);}
|
||||
inline void nasal_vm::opr_muleqc(){op_calc_eq_const(*);}
|
||||
inline void nasal_vm::opr_diveqc(){op_calc_eq_const(/);}
|
||||
inline void nasal_vm::opr_lnkeqc()
|
||||
{
|
||||
nasal_ref val=gc.alloc(vm_str);
|
||||
*val.str()=mem_addr[0].to_string()+str_table[imm[pc]];
|
||||
gc.top[0]=mem_addr[0]=val;
|
||||
}
|
||||
|
||||
inline void nasal_vm::opr_meq()
|
||||
{
|
||||
mem_addr[0]=(--gc.top)[0];
|
||||
}
|
||||
inline void nasal_vm::opr_eq()
|
||||
{
|
||||
nasal_ref val2=gc.top[0];
|
||||
nasal_ref val1=(--gc.top)[0];
|
||||
if(val1.type==vm_nil && val2.type==vm_nil)
|
||||
gc.top[0]=one;
|
||||
else if(val1.type==vm_str && val2.type==vm_str)
|
||||
gc.top[0]=(*val1.str()==*val2.str())?one:zero;
|
||||
else if(val1.type==vm_num || val2.type==vm_num)
|
||||
gc.top[0]=(val1.to_number()==val2.to_number())?one:zero;
|
||||
else
|
||||
gc.top[0]=(val1==val2)?one:zero;
|
||||
}
|
||||
inline void nasal_vm::opr_neq()
|
||||
{
|
||||
nasal_ref val2=gc.top[0];
|
||||
nasal_ref val1=(--gc.top)[0];
|
||||
if(val1.type==vm_nil && val2.type==vm_nil)
|
||||
gc.top[0]=zero;
|
||||
else if(val1.type==vm_str && val2.type==vm_str)
|
||||
gc.top[0]=(*val1.str()!=*val2.str())?one:zero;
|
||||
else if(val1.type==vm_num || val2.type==vm_num)
|
||||
gc.top[0]=(val1.to_number()!=val2.to_number())?one:zero;
|
||||
else
|
||||
gc.top[0]=(val1!=val2)?one:zero;
|
||||
}
|
||||
|
||||
#define op_cmp(type)\
|
||||
--gc.top;\
|
||||
gc.top[0]=(gc.top[0].to_number() type gc.top[1].to_number())?one:zero;
|
||||
|
||||
inline void nasal_vm::opr_less(){op_cmp(<);}
|
||||
inline void nasal_vm::opr_leq(){op_cmp(<=);}
|
||||
inline void nasal_vm::opr_grt(){op_cmp(>);}
|
||||
inline void nasal_vm::opr_geq(){op_cmp(>=);}
|
||||
|
||||
#define op_cmp_const(type)\
|
||||
gc.top[0]=(gc.top[0].to_number() type num_table[imm[pc]])?one:zero;
|
||||
|
||||
inline void nasal_vm::opr_lessc(){op_cmp_const(<);}
|
||||
inline void nasal_vm::opr_leqc(){op_cmp_const(<=);}
|
||||
inline void nasal_vm::opr_grtc(){op_cmp_const(>);}
|
||||
inline void nasal_vm::opr_geqc(){op_cmp_const(>=);}
|
||||
|
||||
inline void nasal_vm::opr_pop()
|
||||
{
|
||||
--gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_jmp()
|
||||
{
|
||||
pc=imm[pc]-1;
|
||||
}
|
||||
inline void nasal_vm::opr_jt()
|
||||
{
|
||||
if(condition(gc.top[0]))
|
||||
pc=imm[pc]-1;
|
||||
}
|
||||
inline void nasal_vm::opr_jf()
|
||||
{
|
||||
if(!condition(gc.top[0]))
|
||||
pc=imm[pc]-1;
|
||||
--gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_counter()
|
||||
{
|
||||
(++gc.top)[0]={vm_cnt,(int64_t)-1};
|
||||
if(gc.top[-1].type!=vm_vec)
|
||||
die("cnt: must use vector in forindex/foreach");
|
||||
}
|
||||
inline void nasal_vm::opr_findex()
|
||||
{
|
||||
if(++gc.top[0].cnt()>=gc.top[-1].vec()->elems.size())
|
||||
{
|
||||
pc=imm[pc]-1;
|
||||
return;
|
||||
}
|
||||
gc.top[1]={vm_num,(double)gc.top[0].cnt()};
|
||||
++gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_feach()
|
||||
{
|
||||
std::vector<nasal_ref>& ref=gc.top[-1].vec()->elems;
|
||||
if(++gc.top[0].cnt()>=ref.size())
|
||||
{
|
||||
pc=imm[pc]-1;
|
||||
return;
|
||||
}
|
||||
gc.top[1]=ref[gc.top[0].cnt()];
|
||||
++gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_callg()
|
||||
{
|
||||
(++gc.top)[0]=gc.stack[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_calll()
|
||||
{
|
||||
(++gc.top)[0]=gc.local.back().vec()->elems[imm[pc]];
|
||||
}
|
||||
inline void nasal_vm::opr_upval()
|
||||
{
|
||||
(++gc.top)[0]=fstk.top()->upvalue[(imm[pc]>>16)&0xffff].vec()->elems[imm[pc]&0xffff];
|
||||
}
|
||||
inline void nasal_vm::opr_callv()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref vec=(--gc.top)[0];
|
||||
if(vec.type==vm_vec)
|
||||
{
|
||||
gc.top[0]=vec.vec()->get_val(val.to_number());
|
||||
if(gc.top[0].type==vm_none)
|
||||
die("callv: index out of range:"+std::to_string(val.to_number()));
|
||||
}
|
||||
else if(vec.type==vm_hash)
|
||||
{
|
||||
if(val.type!=vm_str)
|
||||
die("callv: must use string as the key");
|
||||
gc.top[0]=vec.hash()->get_val(*val.str());
|
||||
if(gc.top[0].type==vm_none)
|
||||
die("callv: cannot find member \""+*val.str()+"\" of this hash");
|
||||
if(gc.top[0].type==vm_func)
|
||||
gc.top[0].func()->local[0]=val;// 'me'
|
||||
}
|
||||
else if(vec.type==vm_str)
|
||||
{
|
||||
std::string& str=*vec.str();
|
||||
int num=val.to_number();
|
||||
int str_size=str.length();
|
||||
if(num<-str_size || num>=str_size)
|
||||
die("callv: index out of range:"+std::to_string(val.to_number()));
|
||||
gc.top[0]={vm_num,double((uint8_t)str[num>=0? num:num+str_size])};
|
||||
}
|
||||
else
|
||||
die("callv: must call a vector/hash/string");
|
||||
}
|
||||
inline void nasal_vm::opr_callvi()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
if(val.type!=vm_vec)
|
||||
die("callvi: must use a vector");
|
||||
|
||||
// cannot use operator[],because this may cause overflow
|
||||
(++gc.top)[0]=val.vec()->get_val(imm[pc]);
|
||||
if(gc.top[0].type==vm_none)
|
||||
die("callvi: index out of range:"+std::to_string(imm[pc]));
|
||||
}
|
||||
inline void nasal_vm::opr_callh()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
if(val.type!=vm_hash)
|
||||
die("callh: must call a hash");
|
||||
|
||||
gc.top[0]=val.hash()->get_val(str_table[imm[pc]]);
|
||||
if(gc.top[0].type==vm_none)
|
||||
die("callh: member \""+str_table[imm[pc]]+"\" does not exist");
|
||||
|
||||
if(gc.top[0].type==vm_func)
|
||||
gc.top[0].func()->local[0]=val;// 'me'
|
||||
}
|
||||
inline void nasal_vm::opr_callfv()
|
||||
{
|
||||
uint32_t argc=imm[pc]; // arguments counter
|
||||
nasal_ref* args=gc.top-argc+1;// arguments begin place
|
||||
if(args[-1].type!=vm_func)
|
||||
die("callfv: must call a function");
|
||||
|
||||
nasal_func* func=args[-1].func();
|
||||
if(gc.top+func->lsize>=canary)
|
||||
die("stackoverflow");
|
||||
fstk.push(func);// push called function into stack to provide upvalue
|
||||
|
||||
gc.local.push_back(gc.alloc(vm_vec)); // get new vector as local scope
|
||||
gc.local.back().vec()->elems=func->local;// copy data from func.local to local scope
|
||||
auto& local=gc.local.back().vec()->elems;
|
||||
|
||||
uint32_t psize=func->psize-1;// parameter size is func->psize-1, 1 is reserved for "me"
|
||||
// load arguments
|
||||
// if the first default value is not vm_none,then values after it are not nullptr
|
||||
// args_size+1 is because the first space is reserved for 'me'
|
||||
if(argc<psize && func->local[argc+1].type==vm_none)
|
||||
die("callfv: lack argument(s)");
|
||||
// if args_size>para_size,for 0 to args_size will cause corruption
|
||||
uint32_t min_size=std::min(psize,argc);
|
||||
for(uint32_t i=0;i<min_size;++i)
|
||||
local[i+1]=args[i];
|
||||
// load dynamic argument if args_size>=para_size
|
||||
if(func->dynpara>=0)
|
||||
{
|
||||
nasal_ref vec=gc.alloc(vm_vec);
|
||||
for(uint32_t i=psize;i<argc;++i)
|
||||
vec.vec()->elems.push_back(args[i]);
|
||||
local[min_size+1]=vec;
|
||||
}
|
||||
|
||||
gc.top-=argc; // pop arguments
|
||||
(++gc.top)[0]={vm_ret,pc};
|
||||
pc=func->entry-1;
|
||||
}
|
||||
inline void nasal_vm::opr_callfh()
|
||||
{
|
||||
auto& hash=gc.top[0].hash()->elems;
|
||||
if(gc.top[-1].type!=vm_func)
|
||||
die("callfh: must call a function");
|
||||
// push function and new local scope
|
||||
nasal_func* func=gc.top[-1].func();
|
||||
if(gc.top+func->lsize>=canary)
|
||||
die("stackoverflow");
|
||||
if(func->dynpara>=0)
|
||||
die("callfh: special call cannot use dynamic argument");
|
||||
fstk.push(func);
|
||||
|
||||
gc.local.push_back(gc.alloc(vm_vec));
|
||||
gc.local.back().vec()->elems=func->local;
|
||||
auto& local=gc.local.back().vec()->elems;
|
||||
|
||||
for(auto& i:func->keys)
|
||||
{
|
||||
if(hash.count(i.first))
|
||||
local[i.second]=hash[i.first];
|
||||
else if(local[i.second].type==vm_none)
|
||||
die("callfh: lack argument(s): \""+i.first+"\"");
|
||||
}
|
||||
|
||||
gc.top[0]={vm_ret,(uint32_t)pc}; // rewrite top with vm_ret
|
||||
pc=func->entry-1;
|
||||
}
|
||||
inline void nasal_vm::opr_callb()
|
||||
{
|
||||
(++gc.top)[0]=(*builtin[imm[pc]].func)(gc.local.back().vec()->elems,gc);
|
||||
if(gc.top[0].type==vm_none)
|
||||
die("native function error.");
|
||||
}
|
||||
inline void nasal_vm::opr_slcbegin()
|
||||
{
|
||||
// | slice_vector | <-- gc.top[0]
|
||||
// +--------------+
|
||||
// | resource_vec | <-- gc.top[-1]
|
||||
// +--------------+
|
||||
(++gc.top)[0]=gc.alloc(vm_vec);
|
||||
if(gc.top[-1].type!=vm_vec)
|
||||
die("slcbegin: must slice a vector");
|
||||
}
|
||||
inline void nasal_vm::opr_slcend()
|
||||
{
|
||||
gc.top[-1]=gc.top[0];
|
||||
--gc.top;
|
||||
}
|
||||
inline void nasal_vm::opr_slc()
|
||||
{
|
||||
nasal_ref val=(gc.top--)[0];
|
||||
nasal_ref res=gc.top[-1].vec()->get_val(val.to_number());
|
||||
if(res.type==vm_none)
|
||||
die("slc: index out of range:"+std::to_string(val.to_number()));
|
||||
gc.top[0].vec()->elems.push_back(res);
|
||||
}
|
||||
inline void nasal_vm::opr_slc2()
|
||||
{
|
||||
nasal_ref val2=(gc.top--)[0];
|
||||
nasal_ref val1=(gc.top--)[0];
|
||||
std::vector<nasal_ref>& ref=gc.top[-1].vec()->elems;
|
||||
std::vector<nasal_ref>& aim=gc.top[0].vec()->elems;
|
||||
|
||||
uint8_t type1=val1.type,type2=val2.type;
|
||||
int num1=val1.to_number();
|
||||
int num2=val2.to_number();
|
||||
int 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>=num2)
|
||||
die("slc2: begin index must be less than end index");
|
||||
else if(num1<-size || num1>=size)
|
||||
die("slc2: begin index out of range: "+std::to_string(num1));
|
||||
else if(num2<-size || num2>=size)
|
||||
die("slc2: end index out of range: "+std::to_string(num2));
|
||||
else
|
||||
for(int i=num1;i<=num2;++i)
|
||||
aim.push_back(i>=0?ref[i]:ref[i+size]);
|
||||
}
|
||||
inline void nasal_vm::opr_mcallg()
|
||||
{
|
||||
mem_addr=gc.stack+imm[pc];
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
}
|
||||
inline void nasal_vm::opr_mcalll()
|
||||
{
|
||||
mem_addr=&(gc.local.back().vec()->elems[imm[pc]]);
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
}
|
||||
inline void nasal_vm::opr_mupval()
|
||||
{
|
||||
mem_addr=&fstk.top()->upvalue[(imm[pc]>>16)&0xffff].vec()->elems[imm[pc]&0xffff];
|
||||
(++gc.top)[0]=mem_addr[0];
|
||||
}
|
||||
inline void nasal_vm::opr_mcallv()
|
||||
{
|
||||
nasal_ref val=gc.top[0];
|
||||
nasal_ref vec=(--gc.top)[0];
|
||||
if(vec.type==vm_vec)
|
||||
{
|
||||
mem_addr=vec.vec()->get_mem(val.to_number());
|
||||
if(!mem_addr)
|
||||
die("mcallv: index out of range:"+std::to_string(val.to_number()));
|
||||
}
|
||||
else if(vec.type==vm_hash)
|
||||
{
|
||||
if(val.type!=vm_str)
|
||||
die("mcallv: must use string as the key");
|
||||
nasal_hash& ref=*vec.hash();
|
||||
std::string& str=*val.str();
|
||||
mem_addr=ref.get_mem(str);
|
||||
if(!mem_addr)
|
||||
{
|
||||
ref.elems[str]={vm_nil};
|
||||
mem_addr=ref.get_mem(str);
|
||||
}
|
||||
}
|
||||
else
|
||||
die("mcallv: cannot get memory space in other types");
|
||||
}
|
||||
inline void nasal_vm::opr_mcallh()
|
||||
{
|
||||
nasal_ref hash=gc.top[0];
|
||||
if(hash.type!=vm_hash)
|
||||
die("mcallh: must call a hash");
|
||||
nasal_hash& ref=*hash.hash();
|
||||
const std::string& str=str_table[imm[pc]];
|
||||
mem_addr=ref.get_mem(str);
|
||||
if(!mem_addr) // create a new key
|
||||
{
|
||||
ref.elems[str]={vm_nil};
|
||||
mem_addr=ref.get_mem(str);
|
||||
}
|
||||
}
|
||||
inline void nasal_vm::opr_ret()
|
||||
{
|
||||
// | return value | <- gc.top[0]
|
||||
// +-----------------+
|
||||
// | return address | <- gc.top[-1]
|
||||
// +-----------------+
|
||||
// | called function | <- gc.top[-2] funct is set on stack because gc may mark it
|
||||
// +-----------------+
|
||||
pc=gc.top[-1].ret();
|
||||
gc.top[-2].func()->local[0]={vm_nil,nullptr}; // get func and set 'me' to nil
|
||||
gc.top[-2]=gc.top[0]; // rewrite func with returned value
|
||||
gc.top-=2;
|
||||
|
||||
fstk.pop();
|
||||
gc.local.pop_back();
|
||||
}
|
||||
void nasal_vm::run(
|
||||
const nasal_codegen& gen,
|
||||
const nasal_import& linker,
|
||||
const bool opcnt,
|
||||
const bool detail)
|
||||
{
|
||||
detail_info=detail;
|
||||
init(gen.get_strs(),gen.get_nums(),linker.get_file());
|
||||
uint64_t count[op_exit+1]={0};
|
||||
const void* opr_table[]=
|
||||
{
|
||||
&&nop, &&intg, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&defpara,&&dynpara,
|
||||
&&unot, &&usub, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
|
||||
&&diveqc, &&lnkeqc, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&counter, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbegin, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret, &&vmexit
|
||||
};
|
||||
bytecode=gen.get_code().data();
|
||||
std::vector<const void*> code;
|
||||
for(auto& i:gen.get_code())
|
||||
{
|
||||
code.push_back(opr_table[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
|
||||
// set canary and program counter
|
||||
canary=gc.stack+STACK_MAX_DEPTH-1;
|
||||
pc=0;
|
||||
// goto the first operand
|
||||
goto *code[pc];
|
||||
|
||||
vmexit:
|
||||
if(gc.top>=canary)
|
||||
die("stack overflow");
|
||||
if(opcnt)
|
||||
opcallsort(count);
|
||||
if(detail_info)
|
||||
gc.info();
|
||||
gc.clear();
|
||||
imm.clear();
|
||||
return;
|
||||
// may cause stackoverflow
|
||||
#define exec_operand(op,num) {op();++count[num];if(gc.top<canary)goto *code[++pc];goto vmexit;}
|
||||
// do not cause stackoverflow
|
||||
#define exec_opnodie(op,num) {op();++count[num];goto *code[++pc];}
|
||||
|
||||
nop: exec_opnodie(opr_nop ,op_nop ); // 0
|
||||
intg: exec_opnodie(opr_intg ,op_intg ); // +imm[pc] (detected at codegen)
|
||||
intl: exec_opnodie(opr_intl ,op_intl ); // -0
|
||||
loadg: exec_opnodie(opr_loadg ,op_loadg ); // -1
|
||||
loadl: exec_opnodie(opr_loadl ,op_loadl ); // -1
|
||||
loadu: exec_opnodie(opr_loadu ,op_loadu ); // -1
|
||||
pnum: exec_operand(opr_pnum ,op_pnum ); // +1
|
||||
pnil: exec_operand(opr_pnil ,op_pnil ); // +1
|
||||
pstr: exec_operand(opr_pstr ,op_pstr ); // +1
|
||||
newv: exec_operand(opr_newv ,op_newv ); // +1-imm[pc]
|
||||
newh: exec_operand(opr_newh ,op_newh ); // +1
|
||||
newf: exec_operand(opr_newf ,op_newf ); // +1
|
||||
happ: exec_opnodie(opr_happ ,op_happ ); // -1
|
||||
para: exec_opnodie(opr_para ,op_para ); // -0
|
||||
defpara: exec_opnodie(opr_defpara ,op_defpara ); // -1
|
||||
dynpara: exec_opnodie(opr_dynpara ,op_dynpara ); // -0
|
||||
unot: exec_opnodie(opr_unot ,op_unot ); // -0
|
||||
usub: exec_opnodie(opr_usub ,op_usub ); // -0
|
||||
add: exec_opnodie(opr_add ,op_add ); // -1
|
||||
sub: exec_opnodie(opr_sub ,op_sub ); // -1
|
||||
mul: exec_opnodie(opr_mul ,op_mul ); // -1
|
||||
div: exec_opnodie(opr_div ,op_div ); // -1
|
||||
lnk: exec_opnodie(opr_lnk ,op_lnk ); // -1
|
||||
addc: exec_opnodie(opr_addc ,op_addc ); // -0
|
||||
subc: exec_opnodie(opr_subc ,op_subc ); // -0
|
||||
mulc: exec_opnodie(opr_mulc ,op_mulc ); // -0
|
||||
divc: exec_opnodie(opr_divc ,op_divc ); // -0
|
||||
lnkc: exec_opnodie(opr_lnkc ,op_lnkc ); // -0
|
||||
addeq: exec_opnodie(opr_addeq ,op_addeq ); // -1
|
||||
subeq: exec_opnodie(opr_subeq ,op_subeq ); // -1
|
||||
muleq: exec_opnodie(opr_muleq ,op_muleq ); // -1
|
||||
diveq: exec_opnodie(opr_diveq ,op_diveq ); // -1
|
||||
lnkeq: exec_opnodie(opr_lnkeq ,op_lnkeq ); // -1
|
||||
addeqc: exec_opnodie(opr_addeqc ,op_addeqc ); // -0
|
||||
subeqc: exec_opnodie(opr_subeqc ,op_subeqc ); // -0
|
||||
muleqc: exec_opnodie(opr_muleqc ,op_muleqc ); // -0
|
||||
diveqc: exec_opnodie(opr_diveqc ,op_diveqc ); // -0
|
||||
lnkeqc: exec_opnodie(opr_lnkeqc ,op_lnkeqc ); // -0
|
||||
meq: exec_opnodie(opr_meq ,op_meq ); // -1
|
||||
eq: exec_opnodie(opr_eq ,op_eq ); // -1
|
||||
neq: exec_opnodie(opr_neq ,op_neq ); // -1
|
||||
less: exec_opnodie(opr_less ,op_less ); // -1
|
||||
leq: exec_opnodie(opr_leq ,op_leq ); // -1
|
||||
grt: exec_opnodie(opr_grt ,op_grt ); // -1
|
||||
geq: exec_opnodie(opr_geq ,op_geq ); // -1
|
||||
lessc: exec_opnodie(opr_lessc ,op_lessc ); // -0
|
||||
leqc: exec_opnodie(opr_leqc ,op_leqc ); // -0
|
||||
grtc: exec_opnodie(opr_grtc ,op_grtc ); // -0
|
||||
geqc: exec_opnodie(opr_geqc ,op_geqc ); // -0
|
||||
pop: exec_opnodie(opr_pop ,op_pop ); // -1
|
||||
jmp: exec_opnodie(opr_jmp ,op_jmp ); // -0
|
||||
jt: exec_opnodie(opr_jt ,op_jt ); // -0
|
||||
jf: exec_opnodie(opr_jf ,op_jf ); // -1
|
||||
counter: exec_opnodie(opr_counter ,op_cnt ); // -0
|
||||
findex: exec_operand(opr_findex ,op_findex ); // +1
|
||||
feach: exec_operand(opr_feach ,op_feach ); // +1
|
||||
callg: exec_operand(opr_callg ,op_callg ); // +1
|
||||
calll: exec_operand(opr_calll ,op_calll ); // +1
|
||||
upval: exec_operand(opr_upval ,op_upval ); // +1
|
||||
callv: exec_opnodie(opr_callv ,op_callv ); // -0
|
||||
callvi: exec_opnodie(opr_callvi ,op_callvi ); // -0
|
||||
callh: exec_opnodie(opr_callh ,op_callh ); // -0
|
||||
callfv: exec_operand(opr_callfv ,op_callfv ); // +1-imm[pc] call this will push >=0 arguments
|
||||
callfh: exec_opnodie(opr_callfh ,op_callfh ); // -0 call this will push one hash
|
||||
callb: exec_opnodie(opr_callb ,op_callb ); // -0
|
||||
slcbegin:exec_operand(opr_slcbegin,op_slcbegin); // +1
|
||||
slcend: exec_opnodie(opr_slcend ,op_slcend ); // -1
|
||||
slc: exec_opnodie(opr_slc ,op_slc ); // -1
|
||||
slc2: exec_opnodie(opr_slc2 ,op_slc2 ); // -2
|
||||
mcallg: exec_operand(opr_mcallg ,op_mcallg ); // +1
|
||||
mcalll: exec_operand(opr_mcalll ,op_mcalll ); // +1
|
||||
mupval: exec_operand(opr_mupval ,op_mupval ); // +1
|
||||
mcallv: exec_opnodie(opr_mcallv ,op_mcallv ); // -0
|
||||
mcallh: exec_opnodie(opr_mcallh ,op_mcallh ); // -0
|
||||
ret: exec_opnodie(opr_ret ,op_ret ); // -1
|
||||
}
|
||||
#endif
|
||||
157
props.nas
@@ -1,157 +0,0 @@
|
||||
import("lib.nas");
|
||||
|
||||
var props=
|
||||
{
|
||||
globals:nil,
|
||||
Node:nil,
|
||||
getNode:func(path,index)
|
||||
{
|
||||
path=split('/',path);
|
||||
var tmp=me.globals;
|
||||
var path_size=size(path);
|
||||
for(var i=0;i<path_size-1;i+=1)
|
||||
tmp=tmp.val[path[i]];
|
||||
if(path_size>0)
|
||||
{
|
||||
if(contains(tmp.val,path[i]~'['~index~']'))
|
||||
return tmp.val[path[i]~'['~index~']'];
|
||||
else
|
||||
return tmp.val[path[i]];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
props.Node=
|
||||
{
|
||||
new:func(values=nil)
|
||||
{
|
||||
var result={
|
||||
parents:[props.Node],
|
||||
val:{},
|
||||
type:'GHOST',
|
||||
parent:nil
|
||||
};
|
||||
if(typeof(values)=="hash")
|
||||
result.val=values;
|
||||
return result;
|
||||
},
|
||||
addChild:func(name)
|
||||
{
|
||||
if(!contains(me.val,name))
|
||||
{
|
||||
me.val[name]=props.Node.new();
|
||||
me.val[name].parent=me;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
addChildren:func(name,cnt=0)
|
||||
{
|
||||
for(var i=0;i<cnt;i+=1)
|
||||
{
|
||||
var label=name~'['~i~']';
|
||||
me.val[label]=props.Node.new();
|
||||
me.val[label].parent=me;
|
||||
}
|
||||
return;
|
||||
},
|
||||
setValue:func(path,val)
|
||||
{
|
||||
path=split('/',path);
|
||||
var tmp=me;
|
||||
foreach(var label;path)
|
||||
tmp=tmp.val[label];
|
||||
tmp.val=val;
|
||||
if(typeof(val)=='str')
|
||||
{
|
||||
if(val=='true' or val=='false')
|
||||
tmp.type='BOOL';
|
||||
else
|
||||
tmp.type='STRING';
|
||||
}
|
||||
elsif(typeof(val)=='num')
|
||||
tmp.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
setIntValue:func(num)
|
||||
{
|
||||
me.val=num;
|
||||
me.type='INT';
|
||||
return;
|
||||
},
|
||||
setBoolValue:func(state)
|
||||
{
|
||||
me.val=state;
|
||||
me.type='BOOL';
|
||||
return;
|
||||
},
|
||||
setDoubleValue:func(num)
|
||||
{
|
||||
me.val=num;
|
||||
me.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
getValue:func(){return me.val;},
|
||||
getName:func()
|
||||
{
|
||||
var val=me.parent.val;
|
||||
var key=keys(val);
|
||||
foreach(var k;key)
|
||||
if(val[k]==me)
|
||||
return k;
|
||||
return '';
|
||||
},
|
||||
getParent:func()
|
||||
{
|
||||
return me.parent;
|
||||
},
|
||||
getPath:func()
|
||||
{
|
||||
if(me.parent==nil) return '';
|
||||
return me.parent.getPath()~'/'~me.getName();
|
||||
},
|
||||
equals:func(node){return me==node;},
|
||||
debug:func(s='')
|
||||
{
|
||||
if(typeof(me.val)=='hash')
|
||||
{
|
||||
var key=keys(me.val);
|
||||
if(!size(key))
|
||||
{
|
||||
println("{}");
|
||||
return;
|
||||
}
|
||||
println('{');
|
||||
foreach(var k;key)
|
||||
{
|
||||
print(s~' ',k,':');
|
||||
me.val[k].debug(s~' ');
|
||||
}
|
||||
println(s,'}');
|
||||
}
|
||||
else
|
||||
println(me.val,' (',me.type,')');
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
props.globals=props.Node.new();
|
||||
var c=['aircraft','ai','models','position','orientation','controls','sim'];
|
||||
foreach(var i;c)
|
||||
props.getNode('/',1).addChild(i);
|
||||
props.getNode('/ai',1).addChildren('ai',4);
|
||||
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode('/ai/ai['~i~']',1).setBoolValue('true');
|
||||
props.getNode('/models',1).addChildren('building',4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode('/models/building['~i~']',1).setIntValue(i);
|
||||
props.getNode('/',1).addChild('test');
|
||||
props.getNode('/test',1).addChildren('in',4);
|
||||
props.getNode('/test/in',0).setValue('/','true');
|
||||
props.getNode('/test/in',1).setValue('/','false');
|
||||
props.getNode('/test/in',2).setValue('/','welcome aboard,need help? use help->tutorial');
|
||||
props.getNode('/test/in',3).setValue('/',2147483648);
|
||||
props.globals.debug();
|
||||
println(props.getNode('/test/in',3).getPath());
|
||||
503
src/ast_dumper.cpp
Normal file
@@ -0,0 +1,503 @@
|
||||
#include "ast_dumper.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_dumper::visit_use_stmt(use_stmt* node) {
|
||||
dump_indent();
|
||||
std::cout << "use" << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_path()) {
|
||||
if (i==node->get_path().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_null_expr(null_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "null" << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_nil_expr(nil_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "nil" << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_number_literal(number_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "number " << node->get_number();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_string_literal(string_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "string \"" << rawstr(node->get_content()) << "\"";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_identifier(identifier* node) {
|
||||
dump_indent();
|
||||
std::cout << "identifier " << node->get_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_bool_literal(bool_literal* node) {
|
||||
dump_indent();
|
||||
std::cout << "bool " << node->get_flag();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_vector_expr(vector_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "vector";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_hash_expr(hash_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "hash";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_members()) {
|
||||
if (i==node->get_members().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_hash_pair(hash_pair* node) {
|
||||
dump_indent();
|
||||
std::cout << "pair " << node->get_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_function(function* node) {
|
||||
dump_indent();
|
||||
std::cout << "function";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_code_block(code_block* node) {
|
||||
dump_indent();
|
||||
std::cout << "block";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_expressions()) {
|
||||
if (i==node->get_expressions().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_parameter(parameter* node) {
|
||||
dump_indent();
|
||||
std::cout << "parameter " << node->get_parameter_name();
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_default_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_default_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
|
||||
dump_indent();
|
||||
std::cout << "ternary_operator";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_condition()->accept(this);
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_binary_operator(binary_operator* node) {
|
||||
if (node->get_optimized_number()) {
|
||||
node->get_optimized_number()->accept(this);
|
||||
return true;
|
||||
}
|
||||
if (node->get_optimized_string()) {
|
||||
node->get_optimized_string()->accept(this);
|
||||
return true;
|
||||
}
|
||||
dump_indent();
|
||||
std::cout << "binary_operator ";
|
||||
switch(node->get_operator_type()) {
|
||||
case binary_operator::binary_type::add: std::cout << "+"; break;
|
||||
case binary_operator::binary_type::sub: std::cout << "-"; break;
|
||||
case binary_operator::binary_type::mult: std::cout << "*"; break;
|
||||
case binary_operator::binary_type::div: std::cout << "/"; break;
|
||||
case binary_operator::binary_type::concat: std::cout << "~"; break;
|
||||
case binary_operator::binary_type::bitwise_and: std::cout << "&"; break;
|
||||
case binary_operator::binary_type::bitwise_or: std::cout << "|"; break;
|
||||
case binary_operator::binary_type::bitwise_xor: std::cout << "^"; break;
|
||||
case binary_operator::binary_type::cmpeq: std::cout << "=="; break;
|
||||
case binary_operator::binary_type::cmpneq: std::cout << "!="; break;
|
||||
case binary_operator::binary_type::grt: std::cout << ">"; break;
|
||||
case binary_operator::binary_type::geq: std::cout << ">="; break;
|
||||
case binary_operator::binary_type::less: std::cout << "<"; break;
|
||||
case binary_operator::binary_type::leq: std::cout << "<="; break;
|
||||
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
|
||||
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_unary_operator(unary_operator* node) {
|
||||
if (node->get_optimized_number()) {
|
||||
node->get_optimized_number()->accept(this);
|
||||
return true;
|
||||
}
|
||||
dump_indent();
|
||||
std::cout << "unary_operator ";
|
||||
switch(node->get_operator_type()) {
|
||||
case unary_operator::unary_type::negative: std::cout << "-"; break;
|
||||
case unary_operator::unary_type::logical_not: std::cout << "!"; break;
|
||||
case unary_operator::unary_type::bitwise_not: std::cout << "~"; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_expr(call_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_expr";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_calls().size()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
if (i==node->get_calls().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_hash(call_hash* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_hash " << node->get_field();
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_vector(call_vector* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_vector";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_slices()) {
|
||||
if (i==node->get_slices().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_function(call_function* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_function";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_argument()) {
|
||||
if (i==node->get_argument().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_slice_vector(slice_vector* node) {
|
||||
dump_indent();
|
||||
std::cout << "slice";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_end()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_begin()->accept(this);
|
||||
if (node->get_end()) {
|
||||
set_last();
|
||||
node->get_end()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_definition_expr(definition_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "definition";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (node->get_variable_name()) {
|
||||
node->get_variable_name()->accept(this);
|
||||
} else {
|
||||
node->get_variables()->accept(this);
|
||||
}
|
||||
set_last();
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "assignment ";
|
||||
switch(node->get_assignment_type()) {
|
||||
case assignment_expr::assign_type::add_equal: std::cout << "+="; break;
|
||||
case assignment_expr::assign_type::sub_equal: std::cout << "-="; break;
|
||||
case assignment_expr::assign_type::mult_equal: std::cout << "*="; break;
|
||||
case assignment_expr::assign_type::div_equal: std::cout << "/="; break;
|
||||
case assignment_expr::assign_type::concat_equal: std::cout << "~="; break;
|
||||
case assignment_expr::assign_type::equal: std::cout << "="; break;
|
||||
case assignment_expr::assign_type::bitwise_and_equal: std::cout << "&="; break;
|
||||
case assignment_expr::assign_type::bitwise_or_equal: std::cout << "|="; break;
|
||||
case assignment_expr::assign_type::bitwise_xor_equal: std::cout << "^="; break;
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_left()->accept(this);
|
||||
set_last();
|
||||
node->get_right()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
|
||||
dump_indent();
|
||||
std::cout << "multiple_identifier";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_variables()) {
|
||||
if (i==node->get_variables().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "tuple";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_multi_assign(multi_assign* node) {
|
||||
dump_indent();
|
||||
std::cout << "multiple_assignment";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_tuple()->accept(this);
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_while_expr(while_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "while";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_condition()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_for_expr(for_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "for";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_initial()->accept(this);
|
||||
node->get_condition()->accept(this);
|
||||
node->get_step()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_iter_expr(iter_expr* node) {
|
||||
dump_indent();
|
||||
if (node->is_definition()) {
|
||||
std::cout << "iterator_definition";
|
||||
} else {
|
||||
std::cout << "iterator";
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
set_last();
|
||||
if (node->get_name()) {
|
||||
node->get_name()->accept(this);
|
||||
} else {
|
||||
node->get_call()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_forei_expr(forei_expr* node) {
|
||||
dump_indent();
|
||||
if (node->get_loop_type()==forei_expr::forei_loop_type::foreach) {
|
||||
std::cout << "foreach";
|
||||
} else {
|
||||
std::cout << "forindex";
|
||||
}
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
node->get_iterator()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_condition_expr(condition_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "condition";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (!node->get_elsif_stataments().size() &&
|
||||
!node->get_else_statement()) {
|
||||
set_last();
|
||||
}
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
if (i==node->get_elsif_stataments().back() &&
|
||||
!node->get_else_statement()) {
|
||||
set_last();
|
||||
}
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
set_last();
|
||||
node->get_else_statement()->accept(this);
|
||||
}
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_if_expr(if_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "if";
|
||||
std::cout << format_location(node->get_location());
|
||||
push_indent();
|
||||
if (node->get_condition()) {
|
||||
node->get_condition()->accept(this);
|
||||
}
|
||||
set_last();
|
||||
node->get_code_block()->accept(this);
|
||||
pop_indent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_continue_expr(continue_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "continue";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_break_expr(break_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "break";
|
||||
std::cout << format_location(node->get_location());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_return_expr(return_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "return";
|
||||
std::cout << format_location(node->get_location());
|
||||
if (node->get_value()) {
|
||||
push_indent();
|
||||
set_last();
|
||||
node->get_value()->accept(this);
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
87
src/ast_dumper.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_visitor.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_dumper:public ast_visitor {
|
||||
private:
|
||||
std::vector<std::string> indent;
|
||||
void push_indent() {
|
||||
if (indent.size()) {
|
||||
if (indent.back()=="|--") {
|
||||
indent.back() = "| ";
|
||||
} else if (indent.back()=="+--") {
|
||||
indent.back() = " ";
|
||||
}
|
||||
}
|
||||
indent.push_back("|--");
|
||||
}
|
||||
void pop_indent() {indent.pop_back();}
|
||||
void set_last() {indent.back() = "+--";}
|
||||
void dump_indent() {
|
||||
if (indent.size() && indent.back()=="| ") {
|
||||
indent.back() = "|--";
|
||||
}
|
||||
for(const auto& i : indent) {
|
||||
std::cout << i;
|
||||
}
|
||||
}
|
||||
std::string format_location(const span& location) {
|
||||
std::stringstream ss;
|
||||
ss << " -> ";
|
||||
ss << location.file << ":";
|
||||
ss << location.begin_line << ":" << location.begin_column + 1;
|
||||
ss << "\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
public:
|
||||
bool visit_use_stmt(use_stmt*) override;
|
||||
bool visit_null_expr(null_expr*) override;
|
||||
bool visit_nil_expr(nil_expr*) override;
|
||||
bool visit_number_literal(number_literal*) override;
|
||||
bool visit_string_literal(string_literal*) override;
|
||||
bool visit_identifier(identifier*) override;
|
||||
bool visit_bool_literal(bool_literal*) override;
|
||||
bool visit_vector_expr(vector_expr*) override;
|
||||
bool visit_hash_expr(hash_expr*) override;
|
||||
bool visit_hash_pair(hash_pair*) override;
|
||||
bool visit_function(function*) override;
|
||||
bool visit_code_block(code_block*) override;
|
||||
bool visit_parameter(parameter*) override;
|
||||
bool visit_ternary_operator(ternary_operator*) override;
|
||||
bool visit_binary_operator(binary_operator*) override;
|
||||
bool visit_unary_operator(unary_operator*) override;
|
||||
bool visit_call_expr(call_expr*) override;
|
||||
bool visit_call_hash(call_hash*) override;
|
||||
bool visit_call_vector(call_vector*) override;
|
||||
bool visit_call_function(call_function*) override;
|
||||
bool visit_slice_vector(slice_vector*) override;
|
||||
bool visit_definition_expr(definition_expr*) override;
|
||||
bool visit_assignment_expr(assignment_expr*) override;
|
||||
bool visit_multi_identifier(multi_identifier*) override;
|
||||
bool visit_tuple_expr(tuple_expr*) override;
|
||||
bool visit_multi_assign(multi_assign*) override;
|
||||
bool visit_while_expr(while_expr*) override;
|
||||
bool visit_for_expr(for_expr*) override;
|
||||
bool visit_iter_expr(iter_expr*) override;
|
||||
bool visit_forei_expr(forei_expr*) override;
|
||||
bool visit_condition_expr(condition_expr*) override;
|
||||
bool visit_if_expr(if_expr*) override;
|
||||
bool visit_continue_expr(continue_expr*) override;
|
||||
bool visit_break_expr(break_expr*) override;
|
||||
bool visit_return_expr(return_expr*) override;
|
||||
|
||||
public:
|
||||
void dump(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
245
src/ast_visitor.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_visitor::visit_expr(expr* node) {
|
||||
node->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_use_stmt(use_stmt* node) {
|
||||
for(auto i : node->get_path()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call(call* node) {
|
||||
node->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_null_expr(null_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_nil_expr(nil_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_number_literal(number_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_string_literal(string_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_identifier(identifier* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_bool_literal(bool_literal* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_vector_expr(vector_expr* node) {
|
||||
for(auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_hash_expr(hash_expr* node) {
|
||||
for(auto i : node->get_members()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_hash_pair(hash_pair* node) {
|
||||
if (node->get_value()) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_function(function* node) {
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_code_block(code_block* node) {
|
||||
for(auto i : node->get_expressions()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_parameter(parameter* node) {
|
||||
if (node->get_default_value()) {
|
||||
node->get_default_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_ternary_operator(ternary_operator* node) {
|
||||
node->get_condition()->accept(this);
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_binary_operator(binary_operator* node) {
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_unary_operator(unary_operator* node) {
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_expr(call_expr* node) {
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_hash(call_hash* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_vector(call_vector* node) {
|
||||
for(auto i : node->get_slices()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_function(call_function* node) {
|
||||
for(auto i : node->get_argument()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_slice_vector(slice_vector* node) {
|
||||
node->get_begin()->accept(this);
|
||||
if (node->get_end()) {
|
||||
node->get_end()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_definition_expr(definition_expr* node) {
|
||||
if (node->get_variable_name()) {
|
||||
node->get_variable_name()->accept(this);
|
||||
} else {
|
||||
node->get_variables()->accept(this);
|
||||
}
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_assignment_expr(assignment_expr* node) {
|
||||
node->get_left()->accept(this);
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_multi_identifier(multi_identifier* node) {
|
||||
for(auto i : node->get_variables()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_tuple_expr(tuple_expr* node) {
|
||||
for(auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_multi_assign(multi_assign* node) {
|
||||
node->get_tuple()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_while_expr(while_expr* node) {
|
||||
node->get_condition()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_for_expr(for_expr* node) {
|
||||
node->get_initial()->accept(this);
|
||||
node->get_condition()->accept(this);
|
||||
node->get_step()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_iter_expr(iter_expr* node) {
|
||||
if (node->get_name()) {
|
||||
node->get_name()->accept(this);
|
||||
} else {
|
||||
node->get_call()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_forei_expr(forei_expr* node) {
|
||||
node->get_iterator()->accept(this);
|
||||
node->get_value()->accept(this);
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_condition_expr(condition_expr* node) {
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
node->get_else_statement()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_if_expr(if_expr* node) {
|
||||
if (node->get_condition()) {
|
||||
node->get_condition()->accept(this);
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_continue_expr(continue_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_break_expr(break_expr* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_return_expr(return_expr* node) {
|
||||
if (node->get_value()) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
48
src/ast_visitor.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_ast.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_visitor {
|
||||
public:
|
||||
virtual bool visit_expr(expr*);
|
||||
virtual bool visit_use_stmt(use_stmt*);
|
||||
virtual bool visit_call(call*);
|
||||
virtual bool visit_null_expr(null_expr*);
|
||||
virtual bool visit_nil_expr(nil_expr*);
|
||||
virtual bool visit_number_literal(number_literal*);
|
||||
virtual bool visit_string_literal(string_literal*);
|
||||
virtual bool visit_identifier(identifier*);
|
||||
virtual bool visit_bool_literal(bool_literal*);
|
||||
virtual bool visit_vector_expr(vector_expr*);
|
||||
virtual bool visit_hash_expr(hash_expr*);
|
||||
virtual bool visit_hash_pair(hash_pair*);
|
||||
virtual bool visit_function(function*);
|
||||
virtual bool visit_code_block(code_block*);
|
||||
virtual bool visit_parameter(parameter*);
|
||||
virtual bool visit_ternary_operator(ternary_operator*);
|
||||
virtual bool visit_binary_operator(binary_operator*);
|
||||
virtual bool visit_unary_operator(unary_operator*);
|
||||
virtual bool visit_call_expr(call_expr*);
|
||||
virtual bool visit_call_hash(call_hash*);
|
||||
virtual bool visit_call_vector(call_vector*);
|
||||
virtual bool visit_call_function(call_function*);
|
||||
virtual bool visit_slice_vector(slice_vector*);
|
||||
virtual bool visit_definition_expr(definition_expr*);
|
||||
virtual bool visit_assignment_expr(assignment_expr*);
|
||||
virtual bool visit_multi_identifier(multi_identifier*);
|
||||
virtual bool visit_tuple_expr(tuple_expr*);
|
||||
virtual bool visit_multi_assign(multi_assign*);
|
||||
virtual bool visit_while_expr(while_expr*);
|
||||
virtual bool visit_for_expr(for_expr*);
|
||||
virtual bool visit_iter_expr(iter_expr*);
|
||||
virtual bool visit_forei_expr(forei_expr*);
|
||||
virtual bool visit_condition_expr(condition_expr*);
|
||||
virtual bool visit_if_expr(if_expr*);
|
||||
virtual bool visit_continue_expr(continue_expr*);
|
||||
virtual bool visit_break_expr(break_expr*);
|
||||
virtual bool visit_return_expr(return_expr*);
|
||||
};
|
||||
|
||||
}
|
||||
164
src/bits_lib.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "bits_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_u32xor(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) ^
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32and(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) &
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32or(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
return var::num(static_cast<f64>(
|
||||
static_cast<u32>(local[1].num()) |
|
||||
static_cast<u32>(local[2].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_u32nand(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
return var::num(static_cast<f64>(~(
|
||||
static_cast<u32>(local[1].num()) &
|
||||
static_cast<u32>(local[2].num())
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_u32not(context* ctx, gc* ngc) {
|
||||
return var::num(static_cast<f64>(
|
||||
~static_cast<u32>(ctx->localr[1].num())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_fld(context* ctx, gc* ngc) {
|
||||
// bits.fld(s,0,3);
|
||||
// if s stores 10100010(162)
|
||||
// will get 101(5)
|
||||
auto local = ctx->localr;
|
||||
auto str = local[1];
|
||||
auto startbit = local[2];
|
||||
auto length = local[3];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||
return nas_err("bits::fld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||
return nas_err("bits::fld", "\"startbit\",\"len\" must be number");
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("bits::fld", "bitfield out of bounds");
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
}
|
||||
return var::num(static_cast<f64>(res));
|
||||
}
|
||||
|
||||
var builtin_sfld(context* ctx, gc* ngc) {
|
||||
// bits.sfld(s,0,3);
|
||||
// if s stores 10100010(162)
|
||||
// will get 101(5) then this will be signed extended to
|
||||
// 11111101(-3)
|
||||
auto local = ctx->localr;
|
||||
auto str = local[1];
|
||||
auto startbit = local[2];
|
||||
auto length = local[3];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||
return nas_err("bits::sfld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num) {
|
||||
return nas_err("bits::sfld", "\"startbit\",\"len\" must be number");
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("bits::sfld", "bitfield out of bounds");
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
}
|
||||
if (res&(1<<(len-1))) {
|
||||
res |= ~((1<<len)-1);
|
||||
}
|
||||
return var::num(static_cast<f64>(static_cast<i32>(res)));
|
||||
}
|
||||
|
||||
var builtin_setfld(context* ctx, gc* ngc) {
|
||||
// bits.setfld(s,0,8,69);
|
||||
// set 01000101(69) to string will get this:
|
||||
// 10100010(162)
|
||||
// so s[0]=162
|
||||
auto local = ctx->localr;
|
||||
auto str = local[1];
|
||||
auto startbit = local[2];
|
||||
auto length = local[3];
|
||||
auto value = local[4];
|
||||
if (str.type!=vm_str || str.val.gcobj->unmutable) {
|
||||
return nas_err("bits::setfld", "\"str\" must be mutable string");
|
||||
}
|
||||
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
|
||||
return nas_err("bits::setfld",
|
||||
"\"startbit\", \"len\", \"val\" must be number"
|
||||
);
|
||||
}
|
||||
u32 bit = static_cast<u32>(startbit.num());
|
||||
u32 len = static_cast<u32>(length.num());
|
||||
u64 val = static_cast<u64>(value.num());
|
||||
if (bit+len>8*str.str().length()) {
|
||||
return nas_err("bits::setfld", "bitfield out of bounds");
|
||||
}
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
if (val&(1<<(i-bit))) {
|
||||
s[i>>3] |= (1<<(7-(i&7)));
|
||||
} else {
|
||||
s[i>>3] &= ~(1<<(7-(i&7)));
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_buf(context* ctx, gc* ngc) {
|
||||
var length = ctx->localr[1];
|
||||
if (length.type!=vm_num || length.num()<=0) {
|
||||
return nas_err("bits::buf", "\"len\" must be number greater than 0");
|
||||
}
|
||||
var str = ngc->alloc(vm_str);
|
||||
auto& s = str.str();
|
||||
s.resize(length.num(), '\0');
|
||||
return str;
|
||||
}
|
||||
|
||||
nasal_builtin_table bits_native[] = {
|
||||
{"__u32xor", builtin_u32xor},
|
||||
{"__u32and", builtin_u32and},
|
||||
{"__u32or", builtin_u32or},
|
||||
{"__u32nand", builtin_u32nand},
|
||||
{"__u32not", builtin_u32not},
|
||||
{"__fld", builtin_fld},
|
||||
{"__sfld", builtin_sfld},
|
||||
{"__setfld", builtin_setfld},
|
||||
{"__buf", builtin_buf},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
21
src/bits_lib.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_u32xor(context*, gc*);
|
||||
var builtin_u32and(context*, gc*);
|
||||
var builtin_u32or(context*, gc*);
|
||||
var builtin_u32nand(context*, gc*);
|
||||
var builtin_u32not(context*, gc*);
|
||||
var builtin_fld(context*, gc*);
|
||||
var builtin_sfld(context*, gc*);
|
||||
var builtin_setfld(context*, gc*);
|
||||
var builtin_buf(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table bits_native[];
|
||||
|
||||
}
|
||||
141
src/coroutine.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "coroutine.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_cocreate(context* ctx, gc* ngc) {
|
||||
// ```
|
||||
// +-------------+
|
||||
// | old pc | <- top[0]
|
||||
// +-------------+
|
||||
// | old localr | <- top[-1]
|
||||
// +-------------+
|
||||
// | old upvalr | <- top[-2]
|
||||
// +-------------+
|
||||
// | local scope |
|
||||
// | ... |
|
||||
// +-------------+ <- local pointer stored in localr
|
||||
// | old funcr | <- old function stored in funcr
|
||||
// +-------------+
|
||||
// ```
|
||||
auto coroutine_function = ctx->localr[1];
|
||||
if (coroutine_function.type!=vm_func) {
|
||||
return nas_err(
|
||||
"coroutine::create",
|
||||
"must use a function to create coroutine"
|
||||
);
|
||||
}
|
||||
if (ngc->cort) {
|
||||
return nas_err(
|
||||
"coroutine::create",
|
||||
"cannot create another coroutine in a coroutine"
|
||||
);
|
||||
}
|
||||
auto coroutine_object = ngc->alloc(vm_co);
|
||||
auto& coroutine = coroutine_object.co();
|
||||
coroutine.ctx.pc = coroutine_function.func().entry-1;
|
||||
|
||||
coroutine.ctx.top[0] = nil;
|
||||
coroutine.ctx.localr = coroutine.ctx.top+1;
|
||||
coroutine.ctx.top = coroutine.ctx.localr +
|
||||
coroutine_function.func().local_size;
|
||||
coroutine.ctx.localr[0] = coroutine_function.func().local[0];
|
||||
|
||||
// store old upvalr on stack
|
||||
coroutine.ctx.top[0] = nil;
|
||||
coroutine.ctx.top++;
|
||||
|
||||
// store old localr on stack
|
||||
coroutine.ctx.top[0] = var::addr(nullptr);
|
||||
coroutine.ctx.top++;
|
||||
|
||||
// store old pc on stack
|
||||
// set to zero to make op_ret recognizing this as coroutine function
|
||||
coroutine.ctx.top[0] = var::ret(0);
|
||||
|
||||
// make sure the coroutine function can use correct upvalues
|
||||
coroutine.ctx.funcr = coroutine_function;
|
||||
coroutine.status = nas_co::status::suspended;
|
||||
|
||||
return coroutine_object;
|
||||
}
|
||||
|
||||
var builtin_coresume(context* ctx, gc* ngc) {
|
||||
if (ngc->cort) {
|
||||
return nas_err(
|
||||
"coroutine::resume",
|
||||
"cannot start another coroutine when one is running"
|
||||
);
|
||||
}
|
||||
auto main_local_frame = ctx->localr;
|
||||
auto coroutine_object = main_local_frame[1];
|
||||
// return nil if is not a coroutine object or coroutine exited
|
||||
if (coroutine_object.type!=vm_co ||
|
||||
coroutine_object.co().status==nas_co::status::dead) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// change to coroutine context
|
||||
ngc->context_change(&coroutine_object.co());
|
||||
|
||||
// fetch coroutine's stack top and return
|
||||
// then coroutine's stack top will catch this return value
|
||||
// so the coroutine's stack top in fact is not changed
|
||||
if (ngc->running_context->top[0].type==vm_ret) {
|
||||
// when first calling this coroutine, the stack top must be vm_ret
|
||||
return ngc->running_context->top[0];
|
||||
}
|
||||
|
||||
// after first calling the coroutine, each time coroutine.yield triggered
|
||||
// a new space will be reserved on stack with value nil
|
||||
// so we could fill this place with args
|
||||
|
||||
// the coroutine seems like coroutine.yield returns the value
|
||||
// but in fact coroutine.yield stop the coroutine
|
||||
// until main context calls the coroutine.resume
|
||||
return main_local_frame[2];
|
||||
}
|
||||
|
||||
var builtin_coyield(context* ctx, gc* ngc) {
|
||||
if (!ngc->cort) {
|
||||
return nas_err("coroutine::yield", "no coroutine is running");
|
||||
}
|
||||
// get coroutine local frame
|
||||
auto coroutine_local_frame = ctx->localr;
|
||||
|
||||
// vm context will set to main context
|
||||
ngc->context_reserve();
|
||||
|
||||
// then this will return value to main's stack top[0]
|
||||
// the procedure seems like coroutine.resume returns the value
|
||||
// but in fact coroutine.resume stop the main context
|
||||
// until coroutine calls the coroutine.yield
|
||||
return coroutine_local_frame[1];
|
||||
}
|
||||
|
||||
var builtin_costatus(context* ctx, gc* ngc) {
|
||||
auto coroutine_object = ctx->localr[1];
|
||||
if (coroutine_object.type!=vm_co) {
|
||||
return ngc->newstr("error");
|
||||
}
|
||||
switch(coroutine_object.co().status) {
|
||||
case nas_co::status::suspended: return ngc->newstr("suspended");
|
||||
case nas_co::status::running: return ngc->newstr("running");
|
||||
case nas_co::status::dead: return ngc->newstr("dead");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_corun(context* ctx, gc* ngc) {
|
||||
return ngc->cort? one:zero;
|
||||
}
|
||||
|
||||
nasal_builtin_table coroutine_native[] = {
|
||||
{"__cocreate", builtin_cocreate},
|
||||
{"__coresume", builtin_coresume},
|
||||
{"__coyield", builtin_coyield},
|
||||
{"__costatus", builtin_costatus},
|
||||
{"__corun", builtin_corun},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
17
src/coroutine.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_cocreate(context*, gc*);
|
||||
var builtin_coresume(context*, gc*);
|
||||
var builtin_coyield(context*, gc*);
|
||||
var builtin_costatus(context*, gc*);
|
||||
var builtin_corun(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table coroutine_native[];
|
||||
|
||||
}
|
||||
140
src/dylib_lib.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "dylib_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto dynamic_library_type_name = "dylib";
|
||||
const auto function_address_type_name = "faddr";
|
||||
|
||||
void dynamic_library_destructor(void* pointer) {
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(static_cast<HMODULE>(pointer));
|
||||
#else
|
||||
dlclose(pointer);
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_dlopen(context* ctx, gc* ngc) {
|
||||
auto dlname = ctx->localr[1];
|
||||
if (dlname.type!=vm_str) {
|
||||
return nas_err("dylib::dlopen", "\"libname\" must be string");
|
||||
}
|
||||
|
||||
// get library pointer
|
||||
#ifdef _WIN32
|
||||
wchar_t* wide_string = new wchar_t[dlname.str().size()+1];
|
||||
if (!wide_string) {
|
||||
return nas_err("dylib::dlopen", "malloc failed");
|
||||
}
|
||||
memset(wide_string, 0, sizeof(wchar_t) * dlname.str().size() + 1);
|
||||
mbstowcs(wide_string, dlname.str().c_str(), dlname.str().size() + 1);
|
||||
// load library by using wide string name
|
||||
void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str());
|
||||
delete []wide_string;
|
||||
#else
|
||||
void* dynamic_library_pointer = dlopen(
|
||||
dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY
|
||||
);
|
||||
#endif
|
||||
|
||||
// check library pointer and insert into returned hashmap
|
||||
if (!dynamic_library_pointer) {
|
||||
return nas_err("dylib::dlopen",
|
||||
"cannot open dynamic lib <" + dlname.str() + ">"
|
||||
);
|
||||
}
|
||||
auto return_hash = ngc->temp = ngc->alloc(vm_hash);
|
||||
auto library_object = ngc->alloc(vm_obj);
|
||||
library_object.ghost().set(
|
||||
dynamic_library_type_name,
|
||||
dynamic_library_destructor,
|
||||
dynamic_library_pointer
|
||||
);
|
||||
return_hash.hash().elems["lib"] = library_object;
|
||||
|
||||
// get "get" function, to get the register table
|
||||
#ifdef _WIN32
|
||||
void* register_table_get_function = reinterpret_cast<void*>(GetProcAddress(
|
||||
static_cast<HMODULE>(library_object.ghost().pointer), "get"
|
||||
));
|
||||
#else
|
||||
void* register_table_get_function = dlsym(
|
||||
library_object.ghost().pointer, "get"
|
||||
);
|
||||
#endif
|
||||
if (!register_table_get_function) {
|
||||
return nas_err("dylib::dlopen", "cannot find <get> function");
|
||||
}
|
||||
|
||||
// get function pointer by name
|
||||
auto table = reinterpret_cast<get_func_ptr>(register_table_get_function)();
|
||||
if (!table) {
|
||||
return nas_err("dylib::dlopen", "failed to get module functions");
|
||||
}
|
||||
for(u32 i = 0; table[i].name; ++i) {
|
||||
auto function_pointer = reinterpret_cast<void*>(table[i].fd);
|
||||
auto function_object = ngc->alloc(vm_obj);
|
||||
function_object.ghost().set(
|
||||
function_address_type_name,
|
||||
nullptr,
|
||||
function_pointer
|
||||
);
|
||||
return_hash.hash().elems[table[i].name] = function_object;
|
||||
}
|
||||
|
||||
ngc->temp = nil;
|
||||
return return_hash;
|
||||
}
|
||||
|
||||
var builtin_dlclose(context* ctx, gc* ngc) {
|
||||
auto library_pointer = ctx->localr[1];
|
||||
if (!library_pointer.object_check(dynamic_library_type_name)) {
|
||||
return nas_err("dylib::dlclose", "\"lib\" is not a valid dynamic lib");
|
||||
}
|
||||
library_pointer.ghost().clear();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_dlcallv(context* ctx, gc* ngc) {
|
||||
auto function_object = ctx->localr[1];
|
||||
auto arguments = ctx->localr[2];
|
||||
if (!function_object.object_check(function_address_type_name)) {
|
||||
return nas_err("dylib::dlcall",
|
||||
"\"ptr\" is not a valid function pointer"
|
||||
);
|
||||
}
|
||||
auto& vec = arguments.vec().elems;
|
||||
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
|
||||
vec.data(),
|
||||
vec.size(),
|
||||
ngc
|
||||
);
|
||||
}
|
||||
|
||||
var builtin_dlcall(context* ctx, gc* ngc) {
|
||||
auto function_object = ctx->localr[1];
|
||||
if (!function_object.object_check(function_address_type_name)) {
|
||||
return nas_err("dylib::dlcall",
|
||||
"\"ptr\" is not a valid function pointer"
|
||||
);
|
||||
}
|
||||
|
||||
// function pointer is at ctx->localr[1]
|
||||
// so arguments starts from ctx->localr[2]
|
||||
var* local_frame_start = ctx->localr + 2;
|
||||
usize local_frame_size = ngc->running_context->top - local_frame_start;
|
||||
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
|
||||
local_frame_start,
|
||||
local_frame_size,
|
||||
ngc
|
||||
);
|
||||
}
|
||||
|
||||
nasal_builtin_table dylib_lib_native[] = {
|
||||
{"__dlopen", builtin_dlopen},
|
||||
{"__dlclose", builtin_dlclose},
|
||||
{"__dlcallv", builtin_dlcallv},
|
||||
{"__dlcall", builtin_dlcall},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
25
src/dylib_lib.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void dynamic_library_destructor(void*);
|
||||
|
||||
var builtin_dlopen(context*, gc*);
|
||||
var builtin_dlclose(context*, gc*);
|
||||
var builtin_dlcallv(context*, gc*);
|
||||
var builtin_dlcall(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table dylib_lib_native[];
|
||||
|
||||
}
|
||||
41
src/fg_props.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "fg_props.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_logprint(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto level = local[1];
|
||||
auto elems = local[2];
|
||||
if (elems.type!=vm_vec) {
|
||||
return nas_err("fg_env::logprint", "received argument is not vector.");
|
||||
}
|
||||
std::ofstream out("fgfs.log", std::ios::app);
|
||||
switch (static_cast<u32>(level.num())) {
|
||||
case SG_LOG_BULK: out << "[LOG_BULK]"; break;
|
||||
case SG_LOG_DEBUG: out << "[LOG_DEBUG]"; break;
|
||||
case SG_LOG_INFO: out << "[LOG_INFO]"; break;
|
||||
case SG_LOG_WARN: out << "[LOG_WARN]"; break;
|
||||
case SG_LOG_ALERT: out << "[LOG_ALERT]"; break;
|
||||
case SG_DEV_WARN: out << "[DEV_WARN]"; break;
|
||||
case SG_DEV_ALERT: out << "[DEV_ALERT]"; break;
|
||||
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
|
||||
default:
|
||||
return nas_err("fg_env::logprint",
|
||||
"incorrect log level " + std::to_string(level.num())
|
||||
);
|
||||
}
|
||||
for(auto& value : elems.vec().elems) {
|
||||
out << value << " ";
|
||||
}
|
||||
out << "\n";
|
||||
return nil;
|
||||
}
|
||||
|
||||
nasal_builtin_table flight_gear_native[] = {
|
||||
{"_logprint", builtin_logprint},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
22
src/fg_props.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
#define SG_LOG_BULK 1
|
||||
#define SG_LOG_DEBUG 2
|
||||
#define SG_LOG_INFO 3
|
||||
#define SG_LOG_WARN 4
|
||||
#define SG_LOG_ALERT 5
|
||||
#define SG_DEV_WARN 7
|
||||
#define SG_DEV_ALERT 8
|
||||
#define SG_MANDATORY_INFO 9
|
||||
|
||||
var builtin_logprint(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table flight_gear_native[];
|
||||
|
||||
}
|
||||
245
src/io_lib.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
#include "io_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto file_type_name = "file";
|
||||
|
||||
void filehandle_destructor(void* ptr) {
|
||||
fclose(static_cast<FILE*>(ptr));
|
||||
}
|
||||
|
||||
var builtin_readfile(context* ctx, gc* ngc) {
|
||||
auto filename = ctx->localr[1];
|
||||
if (filename.type!=vm_str) {
|
||||
return nas_err("io::readfile", "\"filename\" must be string");
|
||||
}
|
||||
std::ifstream in(filename.str(), std::ios::binary);
|
||||
std::stringstream rd;
|
||||
if (!in.fail()) {
|
||||
rd << in.rdbuf();
|
||||
}
|
||||
return ngc->newstr(rd.str());
|
||||
}
|
||||
|
||||
var builtin_fout(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto filename = local[1];
|
||||
auto source = local[2];
|
||||
if (filename.type!=vm_str) {
|
||||
return nas_err("io::fout", "\"filename\" must be string");
|
||||
}
|
||||
std::ofstream out(filename.str());
|
||||
if (out.fail()) {
|
||||
return nas_err("io::fout", "cannot open <" + filename.str() + ">");
|
||||
}
|
||||
out << source;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_exists(context* ctx, gc* ngc) {
|
||||
auto filename = ctx->localr[1];
|
||||
if (filename.type!=vm_str) {
|
||||
return zero;
|
||||
}
|
||||
return access(filename.str().c_str(), F_OK)!=-1? one:zero;
|
||||
}
|
||||
|
||||
var builtin_open(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto name = local[1];
|
||||
auto mode = local[2];
|
||||
if (name.type!=vm_str) {
|
||||
return nas_err("io::open", "\"filename\" must be string");
|
||||
}
|
||||
if (mode.type!=vm_str) {
|
||||
return nas_err("io::open", "\"mode\" must be string");
|
||||
}
|
||||
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
|
||||
if (!file_descriptor) {
|
||||
return nas_err("io::open", "failed to open file <" + name.str() + ">");
|
||||
}
|
||||
var return_object = ngc->alloc(vm_obj);
|
||||
return_object.ghost().set(
|
||||
file_type_name, filehandle_destructor, file_descriptor
|
||||
);
|
||||
return return_object;
|
||||
}
|
||||
|
||||
var builtin_close(context* ctx, gc* ngc) {
|
||||
var file_descriptor = ctx->localr[1];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::close", "not a valid filehandle");
|
||||
}
|
||||
file_descriptor.ghost().clear();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_read(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto file_descriptor = local[1];
|
||||
auto buffer = local[2];
|
||||
auto length = local[3];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::read", "not a valid filehandle");
|
||||
}
|
||||
if (buffer.type!=vm_str || buffer.val.gcobj->unmutable) {
|
||||
return nas_err("io::read", "\"buf\" must be mutable string");
|
||||
}
|
||||
if (length.type!=vm_num) {
|
||||
return nas_err("io::read", "\"len\" must be number");
|
||||
}
|
||||
if (length.num()<=0 || length.num()>=(1<<30)) {
|
||||
return nas_err("io::read", "\"len\" less than 1 or too large");
|
||||
}
|
||||
auto temp_buffer = new char[static_cast<usize>(length.num())+1];
|
||||
if (!temp_buffer) {
|
||||
return nas_err("io::read", "malloc failed");
|
||||
}
|
||||
auto read_size = fread(
|
||||
temp_buffer, 1, length.num(),
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||
);
|
||||
buffer.str() = temp_buffer;
|
||||
buffer.val.gcobj->unmutable = true;
|
||||
delete []temp_buffer;
|
||||
return var::num(read_size);
|
||||
}
|
||||
|
||||
var builtin_write(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto file_descriptor = local[1];
|
||||
auto source = local[2];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::write", "not a valid filehandle");
|
||||
}
|
||||
if (source.type!=vm_str) {
|
||||
return nas_err("io::write", "\"str\" must be string");
|
||||
}
|
||||
return var::num(static_cast<f64>(fwrite(
|
||||
source.str().c_str(), 1, source.str().length(),
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_seek(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
auto file_descriptor = local[1];
|
||||
auto position = local[2];
|
||||
auto whence = local[3];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::seek", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(fseek(
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer),
|
||||
position.num(),
|
||||
whence.num()
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_tell(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ctx->localr[1];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::tell", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(
|
||||
ftell(static_cast<FILE*>(file_descriptor.ghost().pointer))
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_readln(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ctx->localr[1];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::readln", "not a valid filehandle");
|
||||
}
|
||||
auto result = ngc->alloc(vm_str);
|
||||
char c;
|
||||
while((c = fgetc(static_cast<FILE*>(file_descriptor.ghost().pointer)))!=EOF) {
|
||||
if (c=='\r') {
|
||||
continue;
|
||||
}
|
||||
if (c=='\n') {
|
||||
return result;
|
||||
}
|
||||
result.str().push_back(c);
|
||||
}
|
||||
if (result.str().length()) {
|
||||
return result;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_stat(context* ctx, gc* ngc) {
|
||||
auto name = ctx->localr[1];
|
||||
if (name.type!=vm_str) {
|
||||
return nas_err("io::stat", "\"filename\" must be string");
|
||||
}
|
||||
struct stat buffer;
|
||||
if (stat(name.str().c_str(), &buffer)<0) {
|
||||
return nas_err("io::stat", "failed to open file <" + name.str() + ">");
|
||||
}
|
||||
auto result = ngc->alloc(vm_vec);
|
||||
result.vec().elems = {
|
||||
var::num(static_cast<f64>(buffer.st_dev)),
|
||||
var::num(static_cast<f64>(buffer.st_ino)),
|
||||
var::num(static_cast<f64>(buffer.st_mode)),
|
||||
var::num(static_cast<f64>(buffer.st_nlink)),
|
||||
var::num(static_cast<f64>(buffer.st_uid)),
|
||||
var::num(static_cast<f64>(buffer.st_gid)),
|
||||
var::num(static_cast<f64>(buffer.st_rdev)),
|
||||
var::num(static_cast<f64>(buffer.st_size)),
|
||||
var::num(static_cast<f64>(buffer.st_atime)),
|
||||
var::num(static_cast<f64>(buffer.st_mtime)),
|
||||
var::num(static_cast<f64>(buffer.st_ctime))
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
var builtin_eof(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ctx->localr[1];
|
||||
if (!file_descriptor.object_check(file_type_name)) {
|
||||
return nas_err("io::readln", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(
|
||||
feof(static_cast<FILE*>(file_descriptor.ghost().pointer))
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_stdin(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ngc->alloc(vm_obj);
|
||||
file_descriptor.ghost().set(file_type_name, nullptr, stdin);
|
||||
return file_descriptor;
|
||||
}
|
||||
|
||||
var builtin_stdout(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ngc->alloc(vm_obj);
|
||||
file_descriptor.ghost().set(file_type_name, nullptr, stdout);
|
||||
return file_descriptor;
|
||||
}
|
||||
|
||||
var builtin_stderr(context* ctx, gc* ngc) {
|
||||
auto file_descriptor = ngc->alloc(vm_obj);
|
||||
file_descriptor.ghost().set(file_type_name, nullptr, stderr);
|
||||
return file_descriptor;
|
||||
}
|
||||
|
||||
|
||||
nasal_builtin_table io_lib_native[] = {
|
||||
{"__readfile", builtin_readfile},
|
||||
{"__fout", builtin_fout},
|
||||
{"__exists", builtin_exists},
|
||||
{"__open", builtin_open},
|
||||
{"__close", builtin_close},
|
||||
{"__read", builtin_read},
|
||||
{"__write", builtin_write},
|
||||
{"__seek", builtin_seek},
|
||||
{"__tell", builtin_tell},
|
||||
{"__readln", builtin_readln},
|
||||
{"__stat", builtin_stat},
|
||||
{"__eof", builtin_eof},
|
||||
{"__stdin", builtin_stdin},
|
||||
{"__stdout", builtin_stdout},
|
||||
{"__stderr", builtin_stderr},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
41
src/io_lib.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define F_OK 0 // fuck msc
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void filehandle_destructor(void*);
|
||||
|
||||
var builtin_readfile(context*, gc*);
|
||||
var builtin_fout(context*, gc*);
|
||||
var builtin_exists(context*, gc*);
|
||||
var builtin_open(context*, gc*);
|
||||
var builtin_close(context*, gc*);
|
||||
var builtin_read(context*, gc*);
|
||||
var builtin_write(context*, gc*);
|
||||
var builtin_seek(context*, gc*);
|
||||
var builtin_tell(context*, gc*);
|
||||
var builtin_readln(context*, gc*);
|
||||
var builtin_stat(context*, gc*);
|
||||
var builtin_eof(context*, gc*);
|
||||
var builtin_stdin(context*, gc*);
|
||||
var builtin_stdout(context*, gc*);
|
||||
var builtin_stderr(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table io_lib_native[];
|
||||
|
||||
}
|
||||
233
src/main.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "nasal.h"
|
||||
#include "nasal_type.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "ast_visitor.h"
|
||||
#include "ast_dumper.h"
|
||||
#include "symbol_finder.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
#include "repl.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
|
||||
const u32 VM_RAW_AST = 1;
|
||||
const u32 VM_AST = 1<<1;
|
||||
const u32 VM_CODE = 1<<2;
|
||||
const u32 VM_TIME = 1<<3;
|
||||
const u32 VM_EXEC = 1<<4;
|
||||
const u32 VM_DETAIL = 1<<5;
|
||||
const u32 VM_DEBUG = 1<<6;
|
||||
const u32 VM_SYMINFO = 1<<7;
|
||||
const u32 VM_PROFILE = 1<<8;
|
||||
const u32 VM_PROF_ALL = 1<<9;
|
||||
|
||||
std::ostream& help(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " ,--#-,\n"
|
||||
<< "<3 / \\____\\ <3\n"
|
||||
<< " |_|__A_|\n"
|
||||
#ifdef _WIN32
|
||||
<< "use command <chcp 65001> to use unicode.\n"
|
||||
#endif
|
||||
<< "\nnasal <option>\n"
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< " -r, --repl | use repl interpreter(experimental).\n"
|
||||
<< "\nnasal [option] <file> [argv]\n"
|
||||
<< "option:\n"
|
||||
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||
<< " --raw-ast | view ast without after-processing.\n"
|
||||
<< " -c, --code | view generated bytecode.\n"
|
||||
<< " -s, --symbol | show analysed symbol info.\n"
|
||||
<< " -e, --exec | execute directly.\n"
|
||||
<< " -t, --time | show execute time.\n"
|
||||
<< " -d, --detail | get detail info.\n"
|
||||
<< " -dbg, --debug | debug mode.\n"
|
||||
<< " --prof | show profiling result, available in debug mode.\n"
|
||||
<< " --prof-all | show profiling result of all files,"
|
||||
<< "available under debug mode.\n"
|
||||
<< "file:\n"
|
||||
<< " <filename> | execute file.\n"
|
||||
<< "argv:\n"
|
||||
<< " <args> | cmd arguments used in program.\n"
|
||||
<< "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& logo(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " __ _\n"
|
||||
<< " /\\ \\ \\__ _ ___ __ _| |\n"
|
||||
<< " / \\/ / _` / __|/ _` | |\n"
|
||||
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||
<< "std : c++ " << __cplusplus << "\n"
|
||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<< "\n"
|
||||
<< "input <nasal -h> to get help .\n\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& version(std::ostream& out) {
|
||||
std::srand(std::time(nullptr));
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
if (num<0.01) {
|
||||
nasal::parse::easter_egg();
|
||||
}
|
||||
out << "nasal interpreter version " << __nasver;
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void err() {
|
||||
std::cerr
|
||||
<< "invalid argument(s).\n"
|
||||
<< "use <nasal -h> to get help.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void execute(
|
||||
const std::string& file,
|
||||
const std::vector<std::string>& argv,
|
||||
const u32 cmd) {
|
||||
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
const auto den = clk::duration::period::den;
|
||||
|
||||
nasal::lexer lex;
|
||||
nasal::parse parse;
|
||||
nasal::linker ld;
|
||||
nasal::codegen gen;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lex.scan(file).chkerr();
|
||||
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lex).chkerr();
|
||||
if (cmd&VM_RAW_AST) {
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
// linker gets parser's ast and load import files to this ast
|
||||
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
||||
|
||||
// optimizer does simple optimization on ast
|
||||
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
|
||||
opt->do_optimization(parse.tree());
|
||||
if (cmd&VM_AST) {
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
// code generator gets parser's ast and import file list to generate code
|
||||
gen.compile(parse, ld, false).chkerr();
|
||||
if (cmd&VM_CODE) {
|
||||
gen.print(std::cout);
|
||||
}
|
||||
if (cmd&VM_SYMINFO) {
|
||||
gen.symbol_dump(std::cout);
|
||||
}
|
||||
|
||||
// run
|
||||
auto start = clk::now();
|
||||
if (cmd&VM_DEBUG) {
|
||||
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg);
|
||||
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
|
||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
|
||||
runtime->set_detail_report_info(cmd&VM_DETAIL);
|
||||
runtime->run(gen, ld, argv);
|
||||
}
|
||||
|
||||
// get running time
|
||||
auto end = clk::now();
|
||||
if (cmd&VM_TIME) {
|
||||
std::clog << "process exited after ";
|
||||
std::clog << (end-start).count()*1.0/den << "s.\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
i32 main(i32 argc, const char* argv[]) {
|
||||
// output version info
|
||||
if (argc<=1) {
|
||||
std::clog << logo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run directly or show help
|
||||
if (argc==2) {
|
||||
std::string s(argv[1]);
|
||||
if (s=="-h" || s=="--help") {
|
||||
std::clog << help;
|
||||
} else if (s=="-v" || s=="--version") {
|
||||
std::clog << version;
|
||||
} else if (s=="-r" || s=="--repl") {
|
||||
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl);
|
||||
repl->execute();
|
||||
} else if (s[0]!='-') {
|
||||
execute(s, {}, VM_EXEC);
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// execute with arguments
|
||||
const std::unordered_map<std::string, u32> cmdlst = {
|
||||
{"--raw-ast", VM_RAW_AST},
|
||||
{"--ast", VM_AST},
|
||||
{"-a", VM_AST},
|
||||
{"--code", VM_CODE},
|
||||
{"-c", VM_CODE},
|
||||
{"--symbol", VM_SYMINFO},
|
||||
{"-s", VM_SYMINFO},
|
||||
{"--exec", VM_EXEC},
|
||||
{"-e", VM_EXEC},
|
||||
{"--time", VM_TIME|VM_EXEC},
|
||||
{"-t", VM_TIME|VM_EXEC},
|
||||
{"--detail", VM_DETAIL|VM_EXEC},
|
||||
{"-d", VM_DETAIL|VM_EXEC},
|
||||
{"--debug", VM_DEBUG},
|
||||
{"-dbg", VM_DEBUG},
|
||||
{"--prof", VM_PROFILE},
|
||||
{"--prof-all", VM_PROF_ALL}
|
||||
};
|
||||
u32 cmd = 0;
|
||||
std::string filename = "";
|
||||
std::vector<std::string> vm_argv;
|
||||
for(i32 i = 1; i<argc; ++i) {
|
||||
if (cmdlst.count(argv[i])) {
|
||||
cmd |= cmdlst.at(argv[i]);
|
||||
} else if (!filename.length()) {
|
||||
filename = argv[i];
|
||||
} else {
|
||||
vm_argv.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
if (!filename.length()) {
|
||||
err();
|
||||
}
|
||||
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
|
||||
return 0;
|
||||
}
|
||||
77
src/math_lib.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "math_lib.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_pow(context* ctx, gc* ngc) {
|
||||
auto x = ctx->localr[1];
|
||||
auto y = ctx->localr[2];
|
||||
if (x.type!=vm_num || y.type!=vm_num) {
|
||||
return var::num(std::nan(""));
|
||||
}
|
||||
return var::num(std::pow(x.num(), y.num()));
|
||||
}
|
||||
|
||||
var builtin_sin(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_cos(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_tan(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_exp(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_lg(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_ln(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_sqrt(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
|
||||
}
|
||||
|
||||
var builtin_atan2(context* ctx, gc* ngc) {
|
||||
auto x = ctx->localr[1];
|
||||
auto y = ctx->localr[2];
|
||||
if (x.type!=vm_num || y.type!=vm_num) {
|
||||
return var::num(std::nan(""));
|
||||
}
|
||||
return var::num(atan2(y.num(), x.num()));
|
||||
}
|
||||
|
||||
var builtin_isnan(context* ctx, gc* ngc) {
|
||||
auto x = ctx->localr[1];
|
||||
return (x.type==vm_num && std::isnan(x.num()))? one:zero;
|
||||
}
|
||||
|
||||
nasal_builtin_table math_lib_native[] = {
|
||||
{"__pow", builtin_pow},
|
||||
{"__sin", builtin_sin},
|
||||
{"__cos", builtin_cos},
|
||||
{"__tan", builtin_tan},
|
||||
{"__exp", builtin_exp},
|
||||
{"__lg", builtin_lg},
|
||||
{"__ln", builtin_ln},
|
||||
{"__sqrt", builtin_sqrt},
|
||||
{"__atan2", builtin_atan2},
|
||||
{"__isnan", builtin_isnan},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
22
src/math_lib.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_pow(context*, gc*);
|
||||
var builtin_sin(context*, gc*);
|
||||
var builtin_cos(context*, gc*);
|
||||
var builtin_tan(context*, gc*);
|
||||
var builtin_exp(context*, gc*);
|
||||
var builtin_lg(context*, gc*);
|
||||
var builtin_ln(context*, gc*);
|
||||
var builtin_sqrt(context*, gc*);
|
||||
var builtin_atan2(context*, gc*);
|
||||
var builtin_isnan(context*, gc*);
|
||||
|
||||
extern nasal_builtin_table math_lib_native[];
|
||||
|
||||
}
|
||||
58
src/nasal.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __nasver
|
||||
#define __nasver "11.1"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
// abbreviation of some useful basic type
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using usize = std::size_t;
|
||||
using f64 = double;
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool is_windows();
|
||||
bool is_linux();
|
||||
bool is_macos();
|
||||
bool is_x86();
|
||||
bool is_amd64();
|
||||
bool is_x86_64();
|
||||
bool is_arm();
|
||||
bool is_aarch64();
|
||||
bool is_ia64();
|
||||
bool is_powerpc();
|
||||
bool is_superh();
|
||||
|
||||
|
||||
// virtual machine stack depth, both global depth and value stack depth
|
||||
const u32 STACK_DEPTH = 4096;
|
||||
|
||||
f64 hex2f(const char*);
|
||||
f64 oct2f(const char*);
|
||||
|
||||
// we have the same reason not using atof here
|
||||
// just as andy's interpreter does.
|
||||
// it is not platform independent, and may have strange output.
|
||||
// so we write a new function here to convert str to number manually.
|
||||
// but this also makes 0.1+0.2==0.3,
|
||||
// not another result that you may get in other languages.
|
||||
f64 dec2f(const char*);
|
||||
|
||||
f64 str2num(const char*);
|
||||
i32 utf8_hdchk(const char);
|
||||
std::string chrhex(const char);
|
||||
std::string rawstr(const std::string&, const usize maxlen = 0);
|
||||
|
||||
}
|
||||
382
src/nasal_ast.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_expr(this);
|
||||
}
|
||||
|
||||
use_stmt::~use_stmt() {
|
||||
for(auto i : path) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void use_stmt::accept(ast_visitor* visitor) {
|
||||
visitor->visit_use_stmt(this);
|
||||
}
|
||||
|
||||
void call::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call(this);
|
||||
}
|
||||
|
||||
void null_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_null_expr(this);
|
||||
}
|
||||
|
||||
void nil_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_nil_expr(this);
|
||||
}
|
||||
|
||||
void number_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_number_literal(this);
|
||||
}
|
||||
|
||||
void string_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_string_literal(this);
|
||||
}
|
||||
|
||||
void identifier::accept(ast_visitor* visitor) {
|
||||
visitor->visit_identifier(this);
|
||||
}
|
||||
|
||||
void bool_literal::accept(ast_visitor* visitor) {
|
||||
visitor->visit_bool_literal(this);
|
||||
}
|
||||
|
||||
vector_expr::~vector_expr() {
|
||||
for(auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void vector_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_vector_expr(this);
|
||||
}
|
||||
|
||||
hash_expr::~hash_expr() {
|
||||
for(auto i : members) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_hash_expr(this);
|
||||
}
|
||||
|
||||
hash_pair::~hash_pair() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void hash_pair::accept(ast_visitor* visitor) {
|
||||
visitor->visit_hash_pair(this);
|
||||
}
|
||||
|
||||
function::~function() {
|
||||
for(auto i : parameter_list) {
|
||||
delete i;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void function::accept(ast_visitor* visitor) {
|
||||
visitor->visit_function(this);
|
||||
}
|
||||
|
||||
code_block::~code_block() {
|
||||
for(auto i : expressions) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void code_block::accept(ast_visitor* visitor) {
|
||||
visitor->visit_code_block(this);
|
||||
}
|
||||
|
||||
parameter::~parameter() {
|
||||
if (default_value) {
|
||||
delete default_value;
|
||||
}
|
||||
}
|
||||
|
||||
void parameter::accept(ast_visitor* visitor) {
|
||||
visitor->visit_parameter(this);
|
||||
}
|
||||
|
||||
ternary_operator::~ternary_operator() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
}
|
||||
|
||||
void ternary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_ternary_operator(this);
|
||||
}
|
||||
|
||||
binary_operator::~binary_operator() {
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
if (optimized_const_number) {
|
||||
delete optimized_const_number;
|
||||
}
|
||||
if (optimized_const_string) {
|
||||
delete optimized_const_string;
|
||||
}
|
||||
}
|
||||
|
||||
void binary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_binary_operator(this);
|
||||
}
|
||||
|
||||
unary_operator::~unary_operator() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
if (optimized_number) {
|
||||
delete optimized_number;
|
||||
}
|
||||
}
|
||||
|
||||
void unary_operator::accept(ast_visitor* visitor) {
|
||||
visitor->visit_unary_operator(this);
|
||||
}
|
||||
|
||||
call_expr::~call_expr() {
|
||||
if(first) {
|
||||
delete first;
|
||||
}
|
||||
for(auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_expr(this);
|
||||
}
|
||||
|
||||
void call_hash::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_hash(this);
|
||||
}
|
||||
|
||||
call_vector::~call_vector() {
|
||||
for(auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_vector::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_vector(this);
|
||||
}
|
||||
|
||||
call_function::~call_function() {
|
||||
for(auto i : args) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void call_function::accept(ast_visitor* visitor) {
|
||||
visitor->visit_call_function(this);
|
||||
}
|
||||
|
||||
slice_vector::~slice_vector() {
|
||||
if (begin) {
|
||||
delete begin;
|
||||
}
|
||||
if (end) {
|
||||
delete end;
|
||||
}
|
||||
}
|
||||
|
||||
void slice_vector::accept(ast_visitor* visitor) {
|
||||
visitor->visit_slice_vector(this);
|
||||
}
|
||||
|
||||
definition_expr::~definition_expr() {
|
||||
if (variable_name) {
|
||||
delete variable_name;
|
||||
}
|
||||
if (variables) {
|
||||
delete variables;
|
||||
}
|
||||
if (tuple) {
|
||||
delete tuple;
|
||||
}
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void definition_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_definition_expr(this);
|
||||
}
|
||||
|
||||
assignment_expr::~assignment_expr() {
|
||||
if (left) {
|
||||
delete left;
|
||||
}
|
||||
if (right) {
|
||||
delete right;
|
||||
}
|
||||
}
|
||||
|
||||
void assignment_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_assignment_expr(this);
|
||||
}
|
||||
|
||||
multi_identifier::~multi_identifier() {
|
||||
for(auto i : variables) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_identifier::accept(ast_visitor* visitor) {
|
||||
visitor->visit_multi_identifier(this);
|
||||
}
|
||||
|
||||
tuple_expr::~tuple_expr() {
|
||||
for(auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
|
||||
void tuple_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_tuple_expr(this);
|
||||
}
|
||||
|
||||
multi_assign::~multi_assign() {
|
||||
if (tuple) {
|
||||
delete tuple;
|
||||
}
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void multi_assign::accept(ast_visitor* visitor) {
|
||||
visitor->visit_multi_assign(this);
|
||||
}
|
||||
|
||||
while_expr::~while_expr() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void while_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_while_expr(this);
|
||||
}
|
||||
|
||||
for_expr::~for_expr() {
|
||||
if (initializing) {
|
||||
delete initializing;
|
||||
}
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (step) {
|
||||
delete step;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void for_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_for_expr(this);
|
||||
}
|
||||
|
||||
iter_expr::~iter_expr() {
|
||||
if (name) {
|
||||
delete name;
|
||||
}
|
||||
if (call) {
|
||||
delete call;
|
||||
}
|
||||
}
|
||||
|
||||
void iter_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_iter_expr(this);
|
||||
}
|
||||
|
||||
forei_expr::~forei_expr() {
|
||||
if (iterator) {
|
||||
delete iterator;
|
||||
}
|
||||
if (vector_node) {
|
||||
delete vector_node;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void forei_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_forei_expr(this);
|
||||
}
|
||||
|
||||
condition_expr::~condition_expr() {
|
||||
if (if_stmt) {
|
||||
delete if_stmt;
|
||||
}
|
||||
for(auto i : elsif_stmt) {
|
||||
delete i;
|
||||
}
|
||||
if (else_stmt) {
|
||||
delete else_stmt;
|
||||
}
|
||||
}
|
||||
|
||||
void condition_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_condition_expr(this);
|
||||
}
|
||||
|
||||
if_expr::~if_expr() {
|
||||
if (condition) {
|
||||
delete condition;
|
||||
}
|
||||
if (block) {
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
void if_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_if_expr(this);
|
||||
}
|
||||
|
||||
void continue_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_continue_expr(this);
|
||||
}
|
||||
|
||||
void break_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_break_expr(this);
|
||||
}
|
||||
|
||||
return_expr::~return_expr() {
|
||||
if (value) {
|
||||
delete value;
|
||||
}
|
||||
}
|
||||
|
||||
void return_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_return_expr(this);
|
||||
}
|
||||
|
||||
}
|
||||
694
src/nasal_ast.h
Normal file
@@ -0,0 +1,694 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class expr_type:u32 {
|
||||
ast_null = 0, // null node
|
||||
ast_use, // use statement
|
||||
ast_block, // code block
|
||||
ast_nil, // nil keyword
|
||||
ast_num, // number, basic value type
|
||||
ast_str, // string, basic value type
|
||||
ast_id, // identifier
|
||||
ast_bool, // bools
|
||||
ast_func, // func keyword
|
||||
ast_hash, // hash, basic value type
|
||||
ast_vec, // vector, basic value type
|
||||
ast_pair, // pair of key and value in hashmap
|
||||
ast_call, // mark a sub-tree of calling an identifier
|
||||
ast_callh, // id.name
|
||||
ast_callv, // id[index]
|
||||
ast_callf, // id()
|
||||
ast_subvec, // id[index:index]
|
||||
ast_param, // function parameter
|
||||
ast_ternary, // ternary operator
|
||||
ast_binary, // binary operator
|
||||
ast_unary, // unary operator
|
||||
ast_for, // for keyword
|
||||
ast_forei, // foreach or forindex loop
|
||||
ast_while, // while
|
||||
ast_iter, // iterator, used in forindex/foreach
|
||||
ast_cond, // mark a sub-tree of conditional expression
|
||||
ast_if, // if keyword
|
||||
ast_multi_id, // multi identifiers sub-tree
|
||||
ast_tuple, // tuple, stores multiple scalars
|
||||
ast_def, // definition
|
||||
ast_assign, // assignment
|
||||
ast_multi_assign,// multiple assignment
|
||||
ast_continue, // continue keyword, only used in loop
|
||||
ast_break, // break keyword, only used in loop
|
||||
ast_ret // return keyword, only used in function block
|
||||
};
|
||||
|
||||
class ast_visitor;
|
||||
class identifier;
|
||||
class hash_pair;
|
||||
class parameter;
|
||||
class slice_vector;
|
||||
class multi_identifier;
|
||||
class code_block;
|
||||
class if_expr;
|
||||
class tuple_expr;
|
||||
|
||||
class expr {
|
||||
protected:
|
||||
span nd_loc;
|
||||
expr_type nd_type;
|
||||
|
||||
public:
|
||||
expr(const span& location, expr_type node_type):
|
||||
nd_loc(location), nd_type(node_type) {}
|
||||
virtual ~expr() = default;
|
||||
void set_begin(u32 line, u32 column) {
|
||||
nd_loc.begin_line = line;
|
||||
nd_loc.begin_column = column;
|
||||
}
|
||||
const span& get_location() const {return nd_loc;}
|
||||
const u32 get_line() const {return nd_loc.begin_line;}
|
||||
expr_type get_type() const {return nd_type;}
|
||||
void update_location(const span& location) {
|
||||
nd_loc.end_line = location.end_line;
|
||||
nd_loc.end_column = location.end_column;
|
||||
}
|
||||
virtual void accept(ast_visitor*);
|
||||
};
|
||||
|
||||
class use_stmt: public expr {
|
||||
private:
|
||||
std::vector<identifier*> path;
|
||||
|
||||
public:
|
||||
use_stmt(const span& location):
|
||||
expr(location, expr_type::ast_use) {}
|
||||
~use_stmt() override;
|
||||
void accept(ast_visitor*) override;
|
||||
void add_path(identifier* node) {path.push_back(node);}
|
||||
const auto& get_path() const {return path;}
|
||||
};
|
||||
|
||||
class call: public expr {
|
||||
public:
|
||||
call(const span& location, expr_type node_type):
|
||||
expr(location, node_type) {}
|
||||
virtual ~call() = default;
|
||||
virtual void accept(ast_visitor*);
|
||||
};
|
||||
|
||||
class null_expr: public expr {
|
||||
public:
|
||||
null_expr(const span& location):
|
||||
expr(location, expr_type::ast_null) {}
|
||||
~null_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class nil_expr: public expr {
|
||||
public:
|
||||
nil_expr(const span& location):
|
||||
expr(location, expr_type::ast_nil) {}
|
||||
~nil_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class number_literal: public expr {
|
||||
private:
|
||||
f64 number;
|
||||
|
||||
public:
|
||||
number_literal(const span& location, const f64 num):
|
||||
expr(location, expr_type::ast_num), number(num) {}
|
||||
~number_literal() override = default;
|
||||
f64 get_number() const {return number;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class string_literal: public expr {
|
||||
private:
|
||||
std::string content;
|
||||
|
||||
public:
|
||||
string_literal(const span& location, const std::string& str):
|
||||
expr(location, expr_type::ast_str), content(str) {}
|
||||
~string_literal() override = default;
|
||||
const std::string& get_content() const {return content;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class identifier: public expr {
|
||||
private:
|
||||
std::string name;
|
||||
|
||||
public:
|
||||
identifier(const span& location, const std::string& str):
|
||||
expr(location, expr_type::ast_id), name(str) {}
|
||||
~identifier() override = default;
|
||||
const std::string& get_name() const {return name;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class bool_literal: public expr {
|
||||
private:
|
||||
bool flag;
|
||||
|
||||
public:
|
||||
bool_literal(const span& location, const bool bool_flag):
|
||||
expr(location, expr_type::ast_bool), flag(bool_flag) {}
|
||||
~bool_literal() override = default;
|
||||
bool get_flag() const {return flag;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class vector_expr: public expr {
|
||||
private:
|
||||
std::vector<expr*> elements;
|
||||
|
||||
public:
|
||||
vector_expr(const span& location):
|
||||
expr(location, expr_type::ast_vec) {}
|
||||
~vector_expr() override;
|
||||
void add_element(expr* node) {elements.push_back(node);}
|
||||
std::vector<expr*>& get_elements() {return elements;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class hash_expr: public expr {
|
||||
private:
|
||||
std::vector<hash_pair*> members;
|
||||
|
||||
public:
|
||||
hash_expr(const span& location):
|
||||
expr(location, expr_type::ast_hash) {}
|
||||
~hash_expr() override;
|
||||
void add_member(hash_pair* node) {members.push_back(node);}
|
||||
std::vector<hash_pair*>& get_members() {return members;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class hash_pair: public expr {
|
||||
private:
|
||||
std::string name;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
hash_pair(const span& location):
|
||||
expr(location, expr_type::ast_pair),
|
||||
value(nullptr) {}
|
||||
~hash_pair() override;
|
||||
void set_name(const std::string& field_name) {name = field_name;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
const std::string& get_name() const {return name;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class function: public expr {
|
||||
private:
|
||||
std::vector<parameter*> parameter_list;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
function(const span& location):
|
||||
expr(location, expr_type::ast_func),
|
||||
block(nullptr) {}
|
||||
~function() override;
|
||||
void add_parameter(parameter* node) {parameter_list.push_back(node);}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
std::vector<parameter*>& get_parameter_list() {return parameter_list;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class code_block: public expr {
|
||||
private:
|
||||
std::vector<expr*> expressions;
|
||||
|
||||
public:
|
||||
code_block(const span& location):
|
||||
expr(location, expr_type::ast_block) {}
|
||||
~code_block() override;
|
||||
void add_expression(expr* node) {expressions.push_back(node);}
|
||||
std::vector<expr*>& get_expressions() {return expressions;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class parameter: public expr {
|
||||
public:
|
||||
enum class param_type {
|
||||
normal_parameter,
|
||||
default_parameter,
|
||||
dynamic_parameter
|
||||
};
|
||||
|
||||
private:
|
||||
param_type type;
|
||||
std::string name;
|
||||
expr* default_value;
|
||||
|
||||
public:
|
||||
parameter(const span& location):
|
||||
expr(location, expr_type::ast_param),
|
||||
name(""), default_value(nullptr) {}
|
||||
~parameter() override;
|
||||
void set_parameter_type(param_type pt) {type = pt;}
|
||||
void set_parameter_name(const std::string& pname) {name = pname;}
|
||||
void set_default_value(expr* node) {default_value = node;}
|
||||
param_type get_parameter_type() {return type;}
|
||||
const std::string& get_parameter_name() const {return name;}
|
||||
expr* get_default_value() {return default_value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class ternary_operator: public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
expr* left;
|
||||
expr* right;
|
||||
|
||||
public:
|
||||
ternary_operator(const span& location):
|
||||
expr(location, expr_type::ast_ternary),
|
||||
condition(nullptr), left(nullptr), right(nullptr) {}
|
||||
~ternary_operator() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class binary_operator: public expr {
|
||||
public:
|
||||
enum class binary_type {
|
||||
add,
|
||||
sub,
|
||||
mult,
|
||||
div,
|
||||
concat,
|
||||
cmpeq,
|
||||
cmpneq,
|
||||
less,
|
||||
leq,
|
||||
grt,
|
||||
geq,
|
||||
bitwise_or,
|
||||
bitwise_xor,
|
||||
bitwise_and,
|
||||
condition_and,
|
||||
condition_or
|
||||
};
|
||||
|
||||
private:
|
||||
binary_type type;
|
||||
expr* left;
|
||||
expr* right;
|
||||
number_literal* optimized_const_number;
|
||||
string_literal* optimized_const_string;
|
||||
|
||||
public:
|
||||
binary_operator(const span& location):
|
||||
expr(location, expr_type::ast_binary),
|
||||
left(nullptr), right(nullptr),
|
||||
optimized_const_number(nullptr),
|
||||
optimized_const_string(nullptr) {}
|
||||
~binary_operator() override;
|
||||
void set_operator_type(binary_type operator_type) {type = operator_type;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
|
||||
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
|
||||
binary_type get_operator_type() const {return type;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
number_literal* get_optimized_number() {return optimized_const_number;}
|
||||
string_literal* get_optimized_string() {return optimized_const_string;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class unary_operator: public expr {
|
||||
public:
|
||||
enum class unary_type {
|
||||
negative,
|
||||
logical_not,
|
||||
bitwise_not
|
||||
};
|
||||
|
||||
private:
|
||||
unary_type type;
|
||||
expr* value;
|
||||
number_literal* optimized_number;
|
||||
|
||||
public:
|
||||
unary_operator(const span& location):
|
||||
expr(location, expr_type::ast_unary),
|
||||
value(nullptr), optimized_number(nullptr) {}
|
||||
~unary_operator() override;
|
||||
void set_operator_type(unary_type operator_type) {type = operator_type;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
void set_optimized_number(number_literal* node) {optimized_number = node;}
|
||||
unary_type get_operator_type() const {return type;}
|
||||
expr* get_value() {return value;}
|
||||
number_literal* get_optimized_number() {return optimized_number;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_expr: public expr {
|
||||
private:
|
||||
expr* first;
|
||||
std::vector<call*> calls;
|
||||
|
||||
public:
|
||||
call_expr(const span& location):
|
||||
expr(location, expr_type::ast_call),
|
||||
first(nullptr) {}
|
||||
~call_expr() override;
|
||||
void set_first(expr* node) {first = node;}
|
||||
void add_call(call* node) {calls.push_back(node);}
|
||||
expr* get_first() {return first;}
|
||||
auto& get_calls() {return calls;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_hash: public call {
|
||||
private:
|
||||
std::string field;
|
||||
|
||||
public:
|
||||
call_hash(const span& location, const std::string& name):
|
||||
call(location, expr_type::ast_callh),
|
||||
field(name) {}
|
||||
~call_hash() override = default;
|
||||
const std::string& get_field() const {return field;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_vector: public call {
|
||||
private:
|
||||
std::vector<slice_vector*> calls;
|
||||
|
||||
public:
|
||||
call_vector(const span& location):
|
||||
call(location, expr_type::ast_callv) {}
|
||||
~call_vector() override;
|
||||
void add_slice(slice_vector* node) {calls.push_back(node);}
|
||||
std::vector<slice_vector*>& get_slices() {return calls;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_function: public call {
|
||||
private:
|
||||
std::vector<expr*> args;
|
||||
|
||||
public:
|
||||
call_function(const span& location):
|
||||
call(location, expr_type::ast_callf) {}
|
||||
~call_function() override;
|
||||
void add_argument(expr* node) {args.push_back(node);}
|
||||
std::vector<expr*>& get_argument() {return args;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class slice_vector: public expr {
|
||||
private:
|
||||
expr* begin;
|
||||
expr* end;
|
||||
|
||||
public:
|
||||
slice_vector(const span& location):
|
||||
expr(location, expr_type::ast_subvec),
|
||||
begin(nullptr), end(nullptr) {}
|
||||
~slice_vector() override;
|
||||
void set_begin(expr* node) {begin = node;}
|
||||
void set_end(expr* node) {end = node;}
|
||||
expr* get_begin() {return begin;}
|
||||
expr* get_end() {return end;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class definition_expr: public expr {
|
||||
private:
|
||||
identifier* variable_name;
|
||||
multi_identifier* variables;
|
||||
tuple_expr* tuple;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
definition_expr(const span& location):
|
||||
expr(location, expr_type::ast_def),
|
||||
variable_name(nullptr), variables(nullptr),
|
||||
tuple(nullptr), value(nullptr) {}
|
||||
~definition_expr() override;
|
||||
void set_identifier(identifier* node) {variable_name = node;}
|
||||
void set_multi_define(multi_identifier* node) {variables = node;}
|
||||
void set_tuple(tuple_expr* node) {tuple = node;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
identifier* get_variable_name() {return variable_name;}
|
||||
multi_identifier* get_variables() {return variables;}
|
||||
tuple_expr* get_tuple() {return tuple;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class assignment_expr: public expr {
|
||||
public:
|
||||
enum class assign_type {
|
||||
equal,
|
||||
add_equal,
|
||||
sub_equal,
|
||||
mult_equal,
|
||||
div_equal,
|
||||
concat_equal,
|
||||
bitwise_and_equal,
|
||||
bitwise_or_equal,
|
||||
bitwise_xor_equal
|
||||
};
|
||||
|
||||
private:
|
||||
assign_type type;
|
||||
expr* left;
|
||||
expr* right;
|
||||
|
||||
public:
|
||||
assignment_expr(const span& location):
|
||||
expr(location, expr_type::ast_assign),
|
||||
left(nullptr), right(nullptr) {}
|
||||
~assignment_expr() override;
|
||||
void set_assignment_type(assign_type operator_type) {type = operator_type;}
|
||||
void set_left(expr* node) {left = node;}
|
||||
void set_right(expr* node) {right = node;}
|
||||
assign_type get_assignment_type() const {return type;}
|
||||
expr* get_left() {return left;}
|
||||
expr* get_right() {return right;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class multi_identifier: public expr {
|
||||
private:
|
||||
std::vector<identifier*> variables;
|
||||
|
||||
public:
|
||||
multi_identifier(const span& location):
|
||||
expr(location, expr_type::ast_multi_id) {}
|
||||
~multi_identifier() override;
|
||||
void add_var(identifier* node) {variables.push_back(node);}
|
||||
std::vector<identifier*>& get_variables() {return variables;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class tuple_expr: public expr {
|
||||
private:
|
||||
std::vector<expr*> elements;
|
||||
|
||||
public:
|
||||
tuple_expr(const span& location):
|
||||
expr(location, expr_type::ast_tuple) {}
|
||||
~tuple_expr() override;
|
||||
void add_element(expr* node) {elements.push_back(node);}
|
||||
std::vector<expr*>& get_elements() {return elements;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class multi_assign: public expr {
|
||||
private:
|
||||
tuple_expr* tuple;
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
multi_assign(const span& location):
|
||||
expr(location, expr_type::ast_multi_assign),
|
||||
tuple(nullptr), value(nullptr) {}
|
||||
~multi_assign() override;
|
||||
void set_tuple(tuple_expr* node) {tuple = node;}
|
||||
void set_value(expr* node) {value = node;}
|
||||
tuple_expr* get_tuple() {return tuple;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class while_expr: public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
while_expr(const span& location):
|
||||
expr(location, expr_type::ast_while),
|
||||
condition(nullptr), block(nullptr) {}
|
||||
~while_expr() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_code_block (code_block* node) {block = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class for_expr: public expr {
|
||||
private:
|
||||
expr* initializing;
|
||||
expr* condition;
|
||||
expr* step;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
for_expr(const span& location):
|
||||
expr(location, expr_type::ast_for),
|
||||
initializing(nullptr), condition(nullptr),
|
||||
step(nullptr), block(nullptr) {}
|
||||
~for_expr() override;
|
||||
void set_initial(expr* node) {initializing = node;}
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_step(expr* node) {step = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
expr* get_initial() {return initializing;}
|
||||
expr* get_condition() {return condition;}
|
||||
expr* get_step() {return step;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class iter_expr: public expr {
|
||||
private:
|
||||
bool is_iterator_definition;
|
||||
identifier* name;
|
||||
call_expr* call;
|
||||
|
||||
public:
|
||||
iter_expr(const span& location):
|
||||
expr(location, expr_type::ast_iter),
|
||||
is_iterator_definition(false),
|
||||
name(nullptr), call(nullptr) {}
|
||||
~iter_expr() override;
|
||||
void set_name(identifier* node) {name = node;}
|
||||
void set_call(call_expr* node) {call = node;}
|
||||
void set_is_definition(bool flag) {is_iterator_definition = flag;}
|
||||
identifier* get_name() {return name;}
|
||||
call_expr* get_call() {return call;}
|
||||
bool is_definition() const {return is_iterator_definition;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class forei_expr: public expr {
|
||||
public:
|
||||
enum class forei_loop_type {
|
||||
foreach,
|
||||
forindex
|
||||
};
|
||||
|
||||
private:
|
||||
forei_loop_type type;
|
||||
iter_expr* iterator;
|
||||
expr* vector_node;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
forei_expr(const span& location):
|
||||
expr(location, expr_type::ast_forei),
|
||||
type(forei_loop_type::foreach), iterator(nullptr),
|
||||
vector_node(nullptr), block(nullptr) {}
|
||||
~forei_expr() override;
|
||||
void set_loop_type(forei_loop_type ft) {type = ft;}
|
||||
void set_iterator(iter_expr* node) {iterator = node;}
|
||||
void set_value(expr* node) {vector_node = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
forei_loop_type get_loop_type() const {return type;}
|
||||
iter_expr* get_iterator() {return iterator;}
|
||||
expr* get_value() {return vector_node;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class condition_expr: public expr {
|
||||
private:
|
||||
if_expr* if_stmt;
|
||||
std::vector<if_expr*> elsif_stmt;
|
||||
if_expr* else_stmt;
|
||||
|
||||
public:
|
||||
condition_expr(const span& location):
|
||||
expr(location, expr_type::ast_cond),
|
||||
if_stmt(nullptr), else_stmt(nullptr) {}
|
||||
~condition_expr() override;
|
||||
void set_if_statement(if_expr* node) {if_stmt = node;}
|
||||
void add_elsif_statement(if_expr* node) {elsif_stmt.push_back(node);}
|
||||
void set_else_statement(if_expr* node) {else_stmt = node;}
|
||||
if_expr* get_if_statement() {return if_stmt;}
|
||||
std::vector<if_expr*>& get_elsif_stataments() {return elsif_stmt;}
|
||||
if_expr* get_else_statement() {return else_stmt;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class if_expr: public expr {
|
||||
private:
|
||||
expr* condition;
|
||||
code_block* block;
|
||||
|
||||
public:
|
||||
if_expr(const span& location):
|
||||
expr(location, expr_type::ast_if),
|
||||
condition(nullptr), block(nullptr) {}
|
||||
~if_expr() override;
|
||||
void set_condition(expr* node) {condition = node;}
|
||||
void set_code_block(code_block* node) {block = node;}
|
||||
expr* get_condition() {return condition;}
|
||||
code_block* get_code_block() {return block;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class continue_expr: public expr {
|
||||
public:
|
||||
continue_expr(const span& location):
|
||||
expr(location, expr_type::ast_continue) {}
|
||||
~continue_expr() override = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class break_expr: public expr {
|
||||
public:
|
||||
break_expr(const span& location):
|
||||
expr(location, expr_type::ast_break) {}
|
||||
~break_expr() = default;
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class return_expr: public expr {
|
||||
private:
|
||||
expr* value;
|
||||
|
||||
public:
|
||||
return_expr(const span& location):
|
||||
expr(location, expr_type::ast_ret),
|
||||
value(nullptr) {}
|
||||
~return_expr() override;
|
||||
void set_value(expr* node) {value = node;}
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
}
|
||||
693
src/nasal_builtin.cpp
Normal file
@@ -0,0 +1,693 @@
|
||||
#include "nasal_builtin.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_print(context* ctx, gc* ngc) {
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_println(context* ctx, gc* ngc) {
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_exit(context* ctx, gc* ngc) {
|
||||
std::exit(ctx->localr[1].num());
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_abort(context* ctx, gc* ngc) {
|
||||
std::abort();
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_append(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var vec = local[1];
|
||||
var elem = local[2];
|
||||
if (vec.type!=vm_vec) {
|
||||
return nas_err("append", "\"vec\" must be vector");
|
||||
}
|
||||
auto& v = vec.vec().elems;
|
||||
for(auto& i : elem.vec().elems) {
|
||||
v.push_back(i);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_setsize(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var vec = local[1];
|
||||
var size = local[2];
|
||||
if (vec.type!=vm_vec) {
|
||||
return nas_err("setsize", "\"vec\" must be vector");
|
||||
}
|
||||
if (size.type!=vm_num || size.num()<0) {
|
||||
return nil;
|
||||
}
|
||||
vec.vec().elems.resize(static_cast<i64>(size.num()), nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_system(context* ctx, gc* ngc) {
|
||||
auto str = ctx->localr[1];
|
||||
if (str.type!=vm_str) {
|
||||
return var::num(-1);
|
||||
}
|
||||
return var::num(static_cast<f64>(system(str.str().c_str())));
|
||||
}
|
||||
|
||||
var builtin_input(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var end = local[1];
|
||||
var ret = ngc->alloc(vm_str);
|
||||
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
|
||||
std::cin >> ret.str();
|
||||
} else {
|
||||
std::getline(std::cin, ret.str(), end.str()[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var builtin_split(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var delimeter = local[1];
|
||||
var str = local[2];
|
||||
if (delimeter.type!=vm_str) {
|
||||
return nas_err("split", "\"separator\" must be string");
|
||||
}
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("split", "\"str\" must be string");
|
||||
}
|
||||
const auto& deli = delimeter.str();
|
||||
const auto& s = str.str();
|
||||
|
||||
// avoid being sweeped
|
||||
auto res = ngc->temp = ngc->alloc(vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
|
||||
if (!deli.length()) {
|
||||
for(auto i : s) {
|
||||
vec.push_back(ngc->newstr(i));
|
||||
}
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
usize last = 0;
|
||||
usize pos = s.find(deli, 0);
|
||||
while(pos!=std::string::npos) {
|
||||
if (pos>last) {
|
||||
vec.push_back(ngc->newstr(s.substr(last, pos-last)));
|
||||
}
|
||||
last = pos+deli.length();
|
||||
pos = s.find(deli, last);
|
||||
}
|
||||
if (last!=s.length()) {
|
||||
vec.push_back(ngc->newstr(s.substr(last)));
|
||||
}
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_rand(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type!=vm_num && val.type!=vm_nil) {
|
||||
return nas_err("rand", "\"seed\" must be nil or number");
|
||||
}
|
||||
if (val.type==vm_num) {
|
||||
srand(static_cast<u32>(val.num()));
|
||||
return nil;
|
||||
}
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
return var::num(num);
|
||||
}
|
||||
|
||||
var builtin_id(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
std::stringstream ss;
|
||||
ss << "0";
|
||||
if (val.type>vm_num) {
|
||||
ss << "x" << std::hex;
|
||||
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
|
||||
}
|
||||
return ngc->newstr(ss.str());
|
||||
}
|
||||
|
||||
var builtin_int(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type!=vm_num && val.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
return var::num(static_cast<f64>(static_cast<i32>(val.to_num())));
|
||||
}
|
||||
|
||||
var builtin_floor(context* ctx, gc* ngc) {
|
||||
auto value = ctx->localr[1];
|
||||
return var::num(std::floor(value.num()));
|
||||
}
|
||||
|
||||
var builtin_ceil(context* ctx, gc* ngc) {
|
||||
auto value = ctx->localr[1];
|
||||
return var::num(std::ceil(value.num()));
|
||||
}
|
||||
|
||||
var builtin_num(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type==vm_num) {
|
||||
return val;
|
||||
}
|
||||
if (val.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
auto res = val.to_num();
|
||||
if (std::isnan(res)) {
|
||||
return nil;
|
||||
}
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
var builtin_pop(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type!=vm_vec) {
|
||||
return nas_err("pop", "\"vec\" must be vector");
|
||||
}
|
||||
auto& vec = val.vec().elems;
|
||||
if (vec.size()) {
|
||||
auto tmp = vec.back();
|
||||
vec.pop_back();
|
||||
return tmp;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_str(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(ctx->localr[1].to_str());
|
||||
}
|
||||
|
||||
var builtin_size(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
f64 num = 0;
|
||||
switch(val.type) {
|
||||
case vm_num: num = val.num(); break;
|
||||
case vm_str: num = val.str().length(); break;
|
||||
case vm_vec: num = val.vec().size(); break;
|
||||
case vm_hash: num = val.hash().size(); break;
|
||||
case vm_map: num = val.map().mapper.size(); break;
|
||||
}
|
||||
return var::num(num);
|
||||
}
|
||||
|
||||
var builtin_time(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type!=vm_num) {
|
||||
return nas_err("time", "\"begin\" must be number");
|
||||
}
|
||||
auto begin = static_cast<time_t>(val.num());
|
||||
return var::num(static_cast<f64>(time(&begin)));
|
||||
}
|
||||
|
||||
var builtin_contains(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var hash = local[1];
|
||||
var key = local[2];
|
||||
if (hash.type!=vm_hash || key.type!=vm_str) {
|
||||
return zero;
|
||||
}
|
||||
return hash.hash().elems.count(key.str())? one:zero;
|
||||
}
|
||||
|
||||
var builtin_delete(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var hash = local[1];
|
||||
var key = local[2];
|
||||
if (hash.type!=vm_hash) {
|
||||
return nas_err("delete", "\"hash\" must be hash");
|
||||
}
|
||||
if (key.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
if (hash.hash().elems.count(key.str())) {
|
||||
hash.hash().elems.erase(key.str());
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_keys(context* ctx, gc* ngc) {
|
||||
auto hash = ctx->localr[1];
|
||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||
return nas_err("keys", "\"hash\" must be hash");
|
||||
}
|
||||
// avoid being sweeped
|
||||
auto res = ngc->temp = ngc->alloc(vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
if (hash.type==vm_hash) {
|
||||
for(const auto& iter : hash.hash().elems) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
} else {
|
||||
for(const auto& iter : hash.map().mapper) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
}
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_die(context* ctx, gc* ngc) {
|
||||
return nas_err("error", ctx->localr[1].to_str());
|
||||
}
|
||||
|
||||
var builtin_find(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var needle = local[1];
|
||||
var haystack = local[2];
|
||||
usize pos = haystack.to_str().find(needle.to_str());
|
||||
if (pos==std::string::npos) {
|
||||
return var::num(-1.0);
|
||||
}
|
||||
return var::num(static_cast<f64>(pos));
|
||||
}
|
||||
|
||||
var builtin_type(context* ctx, gc* ngc) {
|
||||
switch(ctx->localr[1].type) {
|
||||
case vm_none: return ngc->newstr("undefined");
|
||||
case vm_nil: return ngc->newstr("nil");
|
||||
case vm_num: return ngc->newstr("num");
|
||||
case vm_str: return ngc->newstr("str");
|
||||
case vm_vec: return ngc->newstr("vec");
|
||||
case vm_hash: return ngc->newstr("hash");
|
||||
case vm_func: return ngc->newstr("func");
|
||||
case vm_obj: return ngc->newstr("obj");
|
||||
case vm_co: return ngc->newstr("coroutine");
|
||||
case vm_map: return ngc->newstr("namespace");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_substr(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var str = local[1];
|
||||
var beg = local[2];
|
||||
var len = local[3];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("substr", "\"str\" must be string");
|
||||
}
|
||||
if (beg.type!=vm_num || beg.num()<0) {
|
||||
return nas_err("substr", "\"begin\" should be number >= 0");
|
||||
}
|
||||
if (len.type!=vm_num || len.num()<0) {
|
||||
return nas_err("substr", "\"length\" should be number >= 0");
|
||||
}
|
||||
auto begin = static_cast<usize>(beg.num());
|
||||
auto length = static_cast<usize>(len.num());
|
||||
if (begin>=str.str().length()) {
|
||||
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
|
||||
}
|
||||
return ngc->newstr(str.str().substr(begin,length));
|
||||
}
|
||||
|
||||
var builtin_streq(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var a = local[1];
|
||||
var b = local[2];
|
||||
return var::num(static_cast<f64>(
|
||||
(a.type!=vm_str || b.type!=vm_str)? 0:(a.str()==b.str())
|
||||
));
|
||||
}
|
||||
|
||||
var builtin_left(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var str = local[1];
|
||||
var len = local[2];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("left", "\"string\" must be string");
|
||||
}
|
||||
if (len.type!=vm_num) {
|
||||
return nas_err("left", "\"length\" must be number");
|
||||
}
|
||||
if (len.num()<0) {
|
||||
return ngc->newstr("");
|
||||
}
|
||||
return ngc->newstr(str.str().substr(0, len.num()));
|
||||
}
|
||||
|
||||
var builtin_right(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var str = local[1];
|
||||
var len = local[2];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("right", "\"string\" must be string");
|
||||
}
|
||||
if (len.type!=vm_num) {
|
||||
return nas_err("right", "\"length\" must be number");
|
||||
}
|
||||
i32 length = static_cast<i32>(len.num());
|
||||
i32 srclen = str.str().length();
|
||||
if (length>srclen) {
|
||||
length = srclen;
|
||||
}
|
||||
if (length<0) {
|
||||
length = 0;
|
||||
}
|
||||
return ngc->newstr(str.str().substr(srclen-length, srclen));
|
||||
}
|
||||
|
||||
var builtin_cmp(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var a = local[1];
|
||||
var b = local[2];
|
||||
if (a.type!=vm_str || b.type!=vm_str) {
|
||||
return nas_err("cmp", "\"a\" and \"b\" must be string");
|
||||
}
|
||||
return var::num(static_cast<f64>(strcmp(
|
||||
a.str().c_str(),
|
||||
b.str().c_str()
|
||||
)));
|
||||
}
|
||||
|
||||
var builtin_chr(context* ctx, gc* ngc) {
|
||||
const char* extend[] = {
|
||||
"€"," ","‚","ƒ","„","…","†","‡",
|
||||
"ˆ","‰","Š","‹","Œ"," ","Ž"," ",
|
||||
" ","‘","’","“","”","•","–","—",
|
||||
"˜","™","š","›","œ"," ","ž","Ÿ",
|
||||
" ","¡","¢","£","¤","¥","¦","§",
|
||||
"¨","©","ª","«","¬"," ","®","¯",
|
||||
"°","±","²","³","´","µ","¶","·",
|
||||
"¸","¹","º","»","¼","½","¾","¿",
|
||||
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
|
||||
"È","É","Ê","Ë","Ì","Í","Î","Ï",
|
||||
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
|
||||
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
|
||||
"à","á","â","ã","ä","å","æ","ç",
|
||||
"è","é","ê","ë","ì","í","î","ï",
|
||||
"ð","ñ","ò","ó","ô","õ","ö","÷",
|
||||
"ø","ù","ú","û","ü","ý","þ","ÿ"
|
||||
};
|
||||
auto num = static_cast<i32>(ctx->localr[1].num());
|
||||
if (0<=num && num<128) {
|
||||
return ngc->newstr(static_cast<char>(num));
|
||||
} else if (128<=num && num<256) {
|
||||
return ngc->newstr(extend[num-128]);
|
||||
}
|
||||
return ngc->newstr(" ");
|
||||
}
|
||||
|
||||
var builtin_char(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(static_cast<unsigned char>(ctx->localr[1].num()));
|
||||
}
|
||||
|
||||
var builtin_values(context* ctx, gc* ngc) {
|
||||
auto hash = ctx->localr[1];
|
||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||
return nas_err("values", "\"hash\" must be hash or namespace");
|
||||
}
|
||||
auto vec = ngc->alloc(vm_vec);
|
||||
auto& v = vec.vec().elems;
|
||||
if (hash.type==vm_hash) {
|
||||
for(auto& i : hash.hash().elems) {
|
||||
v.push_back(i.second);
|
||||
}
|
||||
} else {
|
||||
for(auto& i : hash.map().mapper) {
|
||||
v.push_back(*i.second);
|
||||
}
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
var builtin_sleep(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (val.type!=vm_num) {
|
||||
return nil;
|
||||
}
|
||||
#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS)
|
||||
// mingw-w64 win32 thread model has no std::this_thread
|
||||
// also msvc will use this
|
||||
Sleep(static_cast<i64>(val.num()*1e3));
|
||||
#else
|
||||
std::this_thread::sleep_for(
|
||||
std::chrono::microseconds(static_cast<i64>(val.num()*1e6))
|
||||
);
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_platform(context* ctx, gc* ngc) {
|
||||
if (is_windows()) {
|
||||
return ngc->newstr("windows");
|
||||
} else if (is_linux()) {
|
||||
return ngc->newstr("linux");
|
||||
} else if (is_macos()) {
|
||||
return ngc->newstr("macOS");
|
||||
}
|
||||
return ngc->newstr("unknown");
|
||||
}
|
||||
|
||||
var builtin_arch(context* ctx, gc* ngc) {
|
||||
if (is_x86()) {
|
||||
return ngc->newstr("x86");
|
||||
} else if (is_x86_64()) {
|
||||
return ngc->newstr("x86-64");
|
||||
} else if (is_amd64()) {
|
||||
return ngc->newstr("amd64");
|
||||
} else if (is_arm()) {
|
||||
return ngc->newstr("arm");
|
||||
} else if (is_aarch64()) {
|
||||
return ngc->newstr("aarch64");
|
||||
} else if (is_ia64()) {
|
||||
return ngc->newstr("ia64");
|
||||
} else if (is_powerpc()) {
|
||||
return ngc->newstr("powerpc");
|
||||
} else if (is_superh()) {
|
||||
return ngc->newstr("superh");
|
||||
}
|
||||
return ngc->newstr("unknown");
|
||||
}
|
||||
|
||||
// md5 related functions
|
||||
std::string tohex(u32 num) {
|
||||
const char str16[] = "0123456789abcdef";
|
||||
std::string str = "";
|
||||
for(u32 i = 0; i<4; i++, num >>= 8) {
|
||||
std::string tmp = "";
|
||||
for(u32 j = 0, b = num&0xff; j<2; j++, b >>= 4) {
|
||||
tmp.insert(0, 1, str16[b&0xf]);
|
||||
}
|
||||
str += tmp;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string md5(const std::string& src) {
|
||||
std::vector<u32> buff;
|
||||
usize num = ((src.length()+8)>>6)+1;
|
||||
usize buffsize = num<<4;
|
||||
buff.resize(buffsize,0);
|
||||
for(usize i = 0; i<src.length(); i++) {
|
||||
buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3);
|
||||
}
|
||||
buff[src.length()>>2] |= 0x80<<(((src.length()%4))<<3);
|
||||
buff[buffsize-2] = (src.length()<<3)&0xffffffff;
|
||||
buff[buffsize-1] = ((src.length()<<3)>>32)&0xffffffff;
|
||||
|
||||
// u32(abs(sin(i+1))*(2pow32))
|
||||
const u32 k[] = {
|
||||
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
|
||||
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
|
||||
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
|
||||
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
|
||||
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
|
||||
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
|
||||
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
|
||||
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
|
||||
};
|
||||
|
||||
// left shift bits
|
||||
const u32 s[] = {
|
||||
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
|
||||
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
|
||||
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
|
||||
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
|
||||
};
|
||||
|
||||
// index
|
||||
const u32 idx[] = {
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i
|
||||
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16;
|
||||
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16;
|
||||
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16;
|
||||
};
|
||||
|
||||
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
|
||||
#define md5f(x,y,z) (((x)&(y))|((~x)&(z)))
|
||||
#define md5g(x,y,z) (((x)&(z))|((y)&(~z)))
|
||||
#define md5h(x,y,z) ((x)^(y)^(z))
|
||||
#define md5i(x,y,z) ((y)^((x)|(~z)))
|
||||
|
||||
u32 atmp = 0x67452301, btmp = 0xefcdab89;
|
||||
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
|
||||
for(u32 i = 0; i<buffsize; i += 16) {
|
||||
u32 f, a = atmp, b = btmp, c = ctmp, d = dtmp;
|
||||
for(u32 j = 0; j<64; j++) {
|
||||
if (j<16) f = md5f(b, c, d);
|
||||
else if (j<32) f = md5g(b, c, d);
|
||||
else if (j<48) f = md5h(b, c, d);
|
||||
else f = md5i(b, c, d);
|
||||
u32 tmp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]);
|
||||
a = tmp;
|
||||
}
|
||||
atmp += a;
|
||||
btmp += b;
|
||||
ctmp += c;
|
||||
dtmp += d;
|
||||
}
|
||||
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
|
||||
}
|
||||
|
||||
var builtin_md5(context* ctx, gc* ngc) {
|
||||
auto str = ctx->localr[1];
|
||||
if (str.type!=vm_str) {
|
||||
return nas_err("md5", "\"str\" must be string");
|
||||
}
|
||||
return ngc->newstr(md5(str.str()));
|
||||
}
|
||||
|
||||
var builtin_millisec(context* ctx, gc* ngc) {
|
||||
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
var builtin_gcextend(context* ctx, gc* ngc) {
|
||||
auto type = ctx->localr[1];
|
||||
if (type.type!=vm_str) {
|
||||
return nil;
|
||||
}
|
||||
const auto& s = type.str();
|
||||
if (s=="str") {
|
||||
ngc->extend(vm_str);
|
||||
} else if (s=="vec") {
|
||||
ngc->extend(vm_vec);
|
||||
} else if (s=="hash") {
|
||||
ngc->extend(vm_hash);
|
||||
} else if (s=="func") {
|
||||
ngc->extend(vm_func);
|
||||
} else if (s=="upval") {
|
||||
ngc->extend(vm_upval);
|
||||
} else if (s=="obj") {
|
||||
ngc->extend(vm_obj);
|
||||
} else if (s=="co") {
|
||||
ngc->extend(vm_co);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_gcinfo(context* ctx, gc* ngc) {
|
||||
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
var res = ngc->alloc(vm_hash);
|
||||
|
||||
double total = 0;
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
total += ngc->gcnt[i];
|
||||
}
|
||||
// using ms
|
||||
auto& map = res.hash().elems;
|
||||
map["total"] = var::num(ngc->worktime*1.0/den*1000);
|
||||
map["average"] = var::num(ngc->worktime*1.0/den*1000/total);
|
||||
map["max_gc"] = var::num(ngc->max_time*1.0/den*1000);
|
||||
map["max_mark"] = var::num(ngc->max_mark_time*1.0/den*1000);
|
||||
map["max_sweep"] = var::num(ngc->max_sweep_time*1.0/den*1000);
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_logtime(context* ctx, gc* ngc) {
|
||||
time_t t = time(nullptr);
|
||||
tm* tm_t = localtime(&t);
|
||||
char s[64];
|
||||
snprintf(
|
||||
s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d",
|
||||
tm_t->tm_year+1900,
|
||||
tm_t->tm_mon+1,
|
||||
tm_t->tm_mday,
|
||||
tm_t->tm_hour,
|
||||
tm_t->tm_min,
|
||||
tm_t->tm_sec
|
||||
);
|
||||
return ngc->newstr(s);
|
||||
}
|
||||
|
||||
var builtin_ghosttype(context* ctx, gc* ngc) {
|
||||
auto arg = ctx->localr[1];
|
||||
if (arg.type!=vm_obj) {
|
||||
return nas_err("ghosttype", "this is not a ghost object.");
|
||||
}
|
||||
const auto& name = arg.ghost().get_ghost_name();
|
||||
if (!name.length()) {
|
||||
return var::num(reinterpret_cast<u64>(arg.ghost().pointer));
|
||||
}
|
||||
return ngc->newstr(name);
|
||||
}
|
||||
|
||||
nasal_builtin_table builtin[] = {
|
||||
{"__print", builtin_print},
|
||||
{"__println", builtin_println},
|
||||
{"__exit", builtin_exit},
|
||||
{"__abort", builtin_abort},
|
||||
{"__append", builtin_append},
|
||||
{"__setsize", builtin_setsize},
|
||||
{"__system", builtin_system},
|
||||
{"__input", builtin_input},
|
||||
{"__split", builtin_split},
|
||||
{"__rand", builtin_rand},
|
||||
{"__id", builtin_id},
|
||||
{"__int", builtin_int},
|
||||
{"__floor", builtin_floor},
|
||||
{"__ceil", builtin_ceil},
|
||||
{"__num", builtin_num},
|
||||
{"__pop", builtin_pop},
|
||||
{"__str", builtin_str},
|
||||
{"__size", builtin_size},
|
||||
{"__time", builtin_time},
|
||||
{"__contains", builtin_contains},
|
||||
{"__delete", builtin_delete},
|
||||
{"__keys", builtin_keys},
|
||||
{"__die", builtin_die},
|
||||
{"__find", builtin_find},
|
||||
{"__type", builtin_type},
|
||||
{"__substr", builtin_substr},
|
||||
{"__streq", builtin_streq},
|
||||
{"__left", builtin_left},
|
||||
{"__right", builtin_right},
|
||||
{"__cmp", builtin_cmp},
|
||||
{"__chr", builtin_chr},
|
||||
{"__char", builtin_char},
|
||||
{"__values", builtin_values},
|
||||
{"__sleep", builtin_sleep},
|
||||
{"__platform", builtin_platform},
|
||||
{"__arch", builtin_arch},
|
||||
{"__md5", builtin_md5},
|
||||
{"__millisec", builtin_millisec},
|
||||
{"__gcextd", builtin_gcextend},
|
||||
{"__gcinfo", builtin_gcinfo},
|
||||
{"__logtime", builtin_logtime},
|
||||
{"__ghosttype", builtin_ghosttype},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
87
src/nasal_builtin.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_type.h"
|
||||
#include "nasal_gc.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4996)
|
||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
|
||||
// for environ
|
||||
#if defined __APPLE__
|
||||
#include <crt_externs.h>
|
||||
#define environ (*_NSGetEnviron())
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_print(context*, gc*);
|
||||
var builtin_println(context*, gc*);
|
||||
var builtin_exit(context*, gc*);
|
||||
var builtin_abort(context*, gc*);
|
||||
var builtin_append(context*, gc*);
|
||||
var builtin_setsize(context*, gc*);
|
||||
var builtin_system(context*, gc*);
|
||||
var builtin_input(context*, gc*);
|
||||
var builtin_split(context*, gc*);
|
||||
var builtin_rand(context*, gc*);
|
||||
var builtin_id(context*, gc*);
|
||||
var builtin_int(context*, gc*);
|
||||
var builtin_floor(context*, gc*);
|
||||
var builtin_ceil(context*, gc*);
|
||||
var builtin_num(context*, gc*);
|
||||
var builtin_pop(context*, gc*);
|
||||
var builtin_str(context*, gc*);
|
||||
var builtin_size(context*, gc*);
|
||||
var builtin_time(context*, gc*);
|
||||
var builtin_contains(context*, gc*);
|
||||
var builtin_delete(context*, gc*);
|
||||
var builtin_keys(context*, gc*);
|
||||
var builtin_die(context*, gc*);
|
||||
var builtin_find(context*, gc*);
|
||||
var builtin_type(context*, gc*);
|
||||
var builtin_substr(context*, gc*);
|
||||
var builtin_streq(context*, gc*);
|
||||
var builtin_left(context*, gc*);
|
||||
var builtin_right(context*, gc*);
|
||||
var builtin_cmp(context*, gc*);
|
||||
var builtin_chr(context*, gc*);
|
||||
var builtin_char(context*, gc*);
|
||||
var builtin_values(context*, gc*);
|
||||
var builtin_sleep(context*, gc*);
|
||||
var builtin_platform(context*, gc*);
|
||||
var builtin_arch(context*, gc*);
|
||||
// md5 related functions
|
||||
std::string tohex(u32);
|
||||
std::string md5(const std::string&);
|
||||
var builtin_md5(context*, gc*);
|
||||
var builtin_millisec(context*, gc*);
|
||||
var builtin_gcextend(context*, gc*);
|
||||
var builtin_gcinfo(context*, gc*);
|
||||
var builtin_logtime(context*, gc*);
|
||||
var builtin_ghosttype(context*, gc*);
|
||||
|
||||
// register builtin function's name and it's address here in this table below
|
||||
// this table must end with {nullptr,nullptr}
|
||||
struct nasal_builtin_table {
|
||||
const char* name;
|
||||
var (*func)(context*, gc*);
|
||||
};
|
||||
|
||||
extern nasal_builtin_table builtin[];
|
||||
|
||||
}
|
||||
1423
src/nasal_codegen.cpp
Normal file
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_number(const f64);
|
||||
void regist_string(const std::string&);
|
||||
void find_symbol(code_block*);
|
||||
void regist_symbol(const std::string&);
|
||||
i32 local_symbol_find(const std::string&);
|
||||
i32 global_symbol_find(const std::string&);
|
||||
i32 upvalue_symbol_find(const std::string&);
|
||||
|
||||
void emit(u8, u32, const span&);
|
||||
|
||||
void number_gen(number_literal*);
|
||||
void string_gen(string_literal*);
|
||||
void bool_gen(bool_literal*);
|
||||
void vector_gen(vector_expr*);
|
||||
void hash_gen(hash_expr*);
|
||||
void func_gen(function*);
|
||||
void call_gen(call_expr*);
|
||||
void call_identifier(identifier*);
|
||||
void call_hash_gen(call_hash*);
|
||||
void call_vector_gen(call_vector*);
|
||||
void call_func_gen(call_function*);
|
||||
void mcall(expr*);
|
||||
void mcall_identifier(identifier*);
|
||||
void mcall_vec(call_vector*);
|
||||
void mcall_hash(call_hash*);
|
||||
void multi_def(definition_expr*);
|
||||
void single_def(definition_expr*);
|
||||
void definition_gen(definition_expr*);
|
||||
void assignment_expression(assignment_expr*);
|
||||
void gen_assignment_equal_statement(assignment_expr*);
|
||||
void replace_left_assignment_with_load(const span&);
|
||||
void assignment_statement(assignment_expr*);
|
||||
void multi_assign_gen(multi_assign*);
|
||||
void cond_gen(condition_expr*);
|
||||
void loop_gen(expr*);
|
||||
void load_continue_break(i32, i32);
|
||||
void while_gen(while_expr*);
|
||||
void for_gen(for_expr*);
|
||||
void forei_gen(forei_expr*);
|
||||
void statement_generation(expr*);
|
||||
void or_gen(binary_operator*);
|
||||
void and_gen(binary_operator*);
|
||||
void unary_gen(unary_operator*);
|
||||
void binary_gen(binary_operator*);
|
||||
void trino_gen(ternary_operator*);
|
||||
void calc_gen(expr*);
|
||||
void repl_mode_info_output_gen(expr*);
|
||||
void block_gen(code_block*);
|
||||
void ret_gen(return_expr*);
|
||||
|
||||
public:
|
||||
const auto& strs() const {return const_string_table;}
|
||||
const auto& nums() const {return const_number_table;}
|
||||
const auto& natives() const {return native_function;}
|
||||
const auto& codes() const {return code;}
|
||||
const auto& globals() const {return global;}
|
||||
const auto& get_experimental_namespace() const {
|
||||
return experimental_namespace;
|
||||
}
|
||||
|
||||
public:
|
||||
codegen() = default;
|
||||
const error& compile(parse&, linker&, bool);
|
||||
void print(std::ostream&);
|
||||
void symbol_dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
}
|
||||
292
src/nasal_dbg.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void debug_prof_data::init_counter() {
|
||||
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
|
||||
operand_counter[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::load_file_line_counter(
|
||||
const std::vector<std::string>& file_list) {
|
||||
file_name_list = file_list;
|
||||
file_line_counter = {};
|
||||
file_contents = {};
|
||||
flstream fs;
|
||||
for(usize i =0; i<file_list.size(); ++i) {
|
||||
fs.load(file_list[i]);
|
||||
file_contents.push_back(fs.file_content());
|
||||
file_line_counter.push_back({});
|
||||
file_line_counter.back().resize(fs.size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::init(const std::vector<std::string>& file_list) {
|
||||
init_counter();
|
||||
load_file_line_counter(file_list);
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_counter() const {
|
||||
typedef std::pair<u32, u64> op_count;
|
||||
std::vector<op_count> opcall;
|
||||
u64 total = 0;
|
||||
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
|
||||
total += operand_counter[i];
|
||||
opcall.push_back({i, operand_counter[i]});
|
||||
}
|
||||
std::sort(opcall.begin(), opcall.end(),
|
||||
[](const op_count& a, const op_count& b) {
|
||||
return a.second>b.second;
|
||||
}
|
||||
);
|
||||
std::clog << "\noperands call info (<1% ignored)\n";
|
||||
for(const auto& i : opcall) {
|
||||
u64 rate = i.second*100/total;
|
||||
if (!rate) {
|
||||
break;
|
||||
}
|
||||
std::clog << " " << opname[i.first] << " : ";
|
||||
std::clog << i.second << " (" << rate << "%)\n";
|
||||
}
|
||||
std::clog << " total : " << total << '\n';
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_code_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& context : file_line_counter) {
|
||||
for(const auto& count : context) {
|
||||
max_call_time = count>max_call_time? count:max_call_time;
|
||||
}
|
||||
}
|
||||
auto pad_length = std::to_string(max_call_time).length();
|
||||
for(usize i = 0; i<file_name_list.size(); ++i) {
|
||||
os << "\ncode profiling data of " << file_name_list[i] << ":\n";
|
||||
const auto& context = file_contents[i];
|
||||
const auto& counter = file_line_counter[i];
|
||||
for(usize j = 0; j<context.size(); ++j) {
|
||||
os << " " << std::right << std::setw(pad_length);
|
||||
os << std::setfill(' ');
|
||||
os << (counter[j]==0? "":std::to_string(counter[j]));
|
||||
os << " " << context[j] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debug_prof_data::dump_this_file_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& count : file_line_counter[0]) {
|
||||
max_call_time = count>max_call_time? count:max_call_time;
|
||||
}
|
||||
auto pad_length = std::to_string(max_call_time).length();
|
||||
|
||||
os << "\ncode profiling data of " << file_name_list[0] << ":\n";
|
||||
const auto& context = file_contents[0];
|
||||
const auto& counter = file_line_counter[0];
|
||||
for(usize i = 0; i<context.size(); ++i) {
|
||||
os << " " << std::right << std::setw(pad_length);
|
||||
os << std::setfill(' ');
|
||||
os << (counter[i]==0? "":std::to_string(counter[i]));
|
||||
os << " " << context[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> dbg::parse(const std::string& cmd) {
|
||||
std::vector<std::string> res;
|
||||
usize last = 0, pos = cmd.find(" ", 0);
|
||||
while(pos!=std::string::npos) {
|
||||
if (pos>last) {
|
||||
res.push_back(cmd.substr(last, pos-last));
|
||||
}
|
||||
last = pos+1;
|
||||
pos = cmd.find(" ", last);
|
||||
}
|
||||
if (last<cmd.length()) {
|
||||
res.push_back(cmd.substr(last));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
u16 dbg::file_index(const std::string& filename) const {
|
||||
for(u16 i = 0; i<fsize; ++i) {
|
||||
if (filename==files[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 65535;
|
||||
}
|
||||
|
||||
void dbg::err() {
|
||||
std::cerr
|
||||
<< "incorrect command\n"
|
||||
<< "input \'h\' to get help\n";
|
||||
}
|
||||
|
||||
void dbg::help() {
|
||||
std::clog
|
||||
<< "<option>\n"
|
||||
<< " h, help | get help\n"
|
||||
<< " bt, backtrace | get function call trace\n"
|
||||
<< " c, continue | run program until break point or exit\n"
|
||||
<< " f, file | see all the compiled files\n"
|
||||
<< " g, global | see global values\n"
|
||||
<< " l, local | see local values\n"
|
||||
<< " u, upval | see upvalue\n"
|
||||
<< " r, register | show vm register detail\n"
|
||||
<< " a, all | show global,local and upvalue\n"
|
||||
<< " n, next | execute next bytecode\n"
|
||||
<< " q, exit | exit debugger\n"
|
||||
<< "<option> <filename> <line>\n"
|
||||
<< " bk, break | set break point\n";
|
||||
}
|
||||
|
||||
void dbg::list_file() const {
|
||||
for(usize i = 0; i<fsize; ++i) {
|
||||
std::clog << "[" << i << "] " << files[i] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void dbg::step_info() {
|
||||
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
|
||||
u32 begin = (line>>3)==0? 0:((line>>3)<<3);
|
||||
u32 end = (1+(line>>3))<<3;
|
||||
src.load(files[bytecode[ctx.pc].fidx]);
|
||||
std::clog << "\nsource code:\n";
|
||||
for(u32 i = begin; i<end && i<src.size(); ++i) {
|
||||
std::clog << (i==line? back_white:reset);
|
||||
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
|
||||
}
|
||||
|
||||
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
|
||||
end = (1+(ctx.pc>>3))<<3;
|
||||
codestream::set(const_number, const_string, native_function.data(), files);
|
||||
std::clog << "\nnext bytecode:\n";
|
||||
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
||||
std::clog
|
||||
<< (i==ctx.pc? back_white:reset)
|
||||
<< (i==ctx.pc? "--> ":" ")
|
||||
<< codestream(bytecode[i], i)
|
||||
<< reset << "\n";
|
||||
}
|
||||
stack_info(10);
|
||||
}
|
||||
|
||||
void dbg::interact() {
|
||||
// special operand, end execution
|
||||
if (bytecode[ctx.pc].op==op_exit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not need interact while doing profiling
|
||||
if (do_profiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is not break point and is not next stop command
|
||||
const auto& code = bytecode[ctx.pc];
|
||||
if ((code.fidx!=break_file_index || code.line!=break_line) && !next) {
|
||||
return;
|
||||
}
|
||||
|
||||
next = false;
|
||||
std::string cmd;
|
||||
step_info();
|
||||
while(true) {
|
||||
std::clog << ">> ";
|
||||
std::getline(std::cin, cmd);
|
||||
auto res = parse(cmd);
|
||||
if (res.size()==0) {
|
||||
step_info();
|
||||
} else if (res.size()==1) {
|
||||
switch(get_cmd_type(res[0])) {
|
||||
case dbg_cmd::cmd_help: help(); break;
|
||||
case dbg_cmd::cmd_backtrace:
|
||||
function_call_trace();
|
||||
trace_back();
|
||||
break;
|
||||
case dbg_cmd::cmd_continue: return;
|
||||
case dbg_cmd::cmd_list_file: list_file(); break;
|
||||
case dbg_cmd::cmd_global: global_state(); break;
|
||||
case dbg_cmd::cmd_local: local_state(); break;
|
||||
case dbg_cmd::cmd_upval: upvalue_state(); break;
|
||||
case dbg_cmd::cmd_register: register_info(); break;
|
||||
case dbg_cmd::cmd_show_all: all_state_detail(); break;
|
||||
case dbg_cmd::cmd_next: next = true; return;
|
||||
case dbg_cmd::cmd_exit: std::exit(0);
|
||||
default: err(); break;
|
||||
}
|
||||
} else if (res.size()==3 &&
|
||||
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
|
||||
break_file_index = file_index(res[1]);
|
||||
if (break_file_index==65535) {
|
||||
std::clog << "cannot find file named `" << res[1] << "`\n";
|
||||
continue;
|
||||
}
|
||||
i32 tmp = atoi(res[2].c_str());
|
||||
if (tmp<=0) {
|
||||
std::clog << "incorrect line number `" << res[2] << "`\n";
|
||||
} else {
|
||||
break_line = tmp;
|
||||
}
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbg::run(
|
||||
const codegen& gen,
|
||||
const linker& linker,
|
||||
const std::vector<std::string>& argv,
|
||||
bool profile,
|
||||
bool show_all_prof_result) {
|
||||
|
||||
set_detail_report_info(true);
|
||||
do_profiling = profile || show_all_prof_result;
|
||||
|
||||
const auto& file_list = linker.get_file_list();
|
||||
fsize = file_list.size();
|
||||
init(
|
||||
gen.strs(),
|
||||
gen.nums(),
|
||||
gen.natives(),
|
||||
gen.codes(),
|
||||
gen.globals(),
|
||||
file_list,
|
||||
argv
|
||||
);
|
||||
data.init(file_list);
|
||||
|
||||
std::vector<u32> code;
|
||||
std::vector<u16> code_file_index;
|
||||
std::vector<u32> code_line;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(i.op);
|
||||
code_file_index.push_back(i.fidx);
|
||||
code_line.push_back(i.line);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(operand_function[code[ctx.pc]]) {
|
||||
interact();
|
||||
data.add_operand_counter(code[ctx.pc]);
|
||||
data.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
|
||||
(this->*operand_function[code[ctx.pc]])();
|
||||
if (ctx.top>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
}
|
||||
++ctx.pc;
|
||||
}
|
||||
|
||||
data.dump_counter();
|
||||
if (do_profiling) {
|
||||
show_all_prof_result?
|
||||
data.dump_code_line_counter(std::clog):
|
||||
data.dump_this_file_line_counter(std::clog);
|
||||
}
|
||||
ngc.info();
|
||||
ngc.clear();
|
||||
imm.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
173
src/nasal_dbg.h
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_opcode.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class debug_prof_data {
|
||||
private:
|
||||
static const usize operand_size = op_code_type::op_ret + 1;
|
||||
u64 operand_counter[operand_size];
|
||||
std::vector<std::string> file_name_list;
|
||||
std::vector<std::vector<u64>> file_line_counter;
|
||||
std::vector<std::vector<std::string>> file_contents;
|
||||
|
||||
private:
|
||||
void init_counter();
|
||||
void load_file_line_counter(const std::vector<std::string>&);
|
||||
|
||||
public:
|
||||
void init(const std::vector<std::string>&);
|
||||
void dump_counter() const;
|
||||
void dump_code_line_counter(std::ostream&) const;
|
||||
void dump_this_file_line_counter(std::ostream&) const;
|
||||
void add_operand_counter(usize index) {
|
||||
operand_counter[index] += index<operand_size? 1:0;
|
||||
}
|
||||
void add_code_line_counter(usize fidx, usize line) {
|
||||
auto& vec = file_line_counter[fidx];
|
||||
vec[line==0? line: line-1]++;
|
||||
}
|
||||
};
|
||||
|
||||
class dbg: public vm {
|
||||
private:
|
||||
typedef void (dbg::*nasal_vm_func)();
|
||||
const nasal_vm_func operand_function[op_ret + 1] = {
|
||||
nullptr, &dbg::o_repl,
|
||||
&dbg::o_intl, &dbg::o_loadg,
|
||||
&dbg::o_loadl, &dbg::o_loadu,
|
||||
&dbg::o_pnum, &dbg::o_pnil,
|
||||
&dbg::o_pstr, &dbg::o_newv,
|
||||
&dbg::o_newh, &dbg::o_newf,
|
||||
&dbg::o_happ, &dbg::o_para,
|
||||
&dbg::o_deft, &dbg::o_dyn,
|
||||
&dbg::o_lnot, &dbg::o_usub,
|
||||
&dbg::o_bnot, &dbg::o_btor,
|
||||
&dbg::o_btxor, &dbg::o_btand,
|
||||
&dbg::o_add, &dbg::o_sub,
|
||||
&dbg::o_mul, &dbg::o_div,
|
||||
&dbg::o_lnk, &dbg::o_addc,
|
||||
&dbg::o_subc, &dbg::o_mulc,
|
||||
&dbg::o_divc, &dbg::o_lnkc,
|
||||
&dbg::o_addeq, &dbg::o_subeq,
|
||||
&dbg::o_muleq, &dbg::o_diveq,
|
||||
&dbg::o_lnkeq, &dbg::o_bandeq,
|
||||
&dbg::o_boreq, &dbg::o_bxoreq,
|
||||
&dbg::o_addeqc, &dbg::o_subeqc,
|
||||
&dbg::o_muleqc, &dbg::o_diveqc,
|
||||
&dbg::o_lnkeqc, &dbg::o_addecp,
|
||||
&dbg::o_subecp, &dbg::o_mulecp,
|
||||
&dbg::o_divecp, &dbg::o_lnkecp,
|
||||
&dbg::o_meq, &dbg::o_eq,
|
||||
&dbg::o_neq, &dbg::o_less,
|
||||
&dbg::o_leq, &dbg::o_grt,
|
||||
&dbg::o_geq, &dbg::o_lessc,
|
||||
&dbg::o_leqc, &dbg::o_grtc,
|
||||
&dbg::o_geqc, &dbg::o_pop,
|
||||
&dbg::o_jmp, &dbg::o_jt,
|
||||
&dbg::o_jf, &dbg::o_cnt,
|
||||
&dbg::o_findex, &dbg::o_feach,
|
||||
&dbg::o_callg, &dbg::o_calll,
|
||||
&dbg::o_upval, &dbg::o_callv,
|
||||
&dbg::o_callvi, &dbg::o_callh,
|
||||
&dbg::o_callfv, &dbg::o_callfh,
|
||||
&dbg::o_callb, &dbg::o_slcbeg,
|
||||
&dbg::o_slcend, &dbg::o_slc,
|
||||
&dbg::o_slc2, &dbg::o_mcallg,
|
||||
&dbg::o_mcalll, &dbg::o_mupval,
|
||||
&dbg::o_mcallv, &dbg::o_mcallh,
|
||||
&dbg::o_ret
|
||||
};
|
||||
|
||||
private:
|
||||
enum class dbg_cmd {
|
||||
cmd_error,
|
||||
cmd_help,
|
||||
cmd_backtrace,
|
||||
cmd_continue,
|
||||
cmd_list_file,
|
||||
cmd_global,
|
||||
cmd_local,
|
||||
cmd_upval,
|
||||
cmd_register,
|
||||
cmd_show_all,
|
||||
cmd_next,
|
||||
cmd_break_point,
|
||||
cmd_exit
|
||||
};
|
||||
|
||||
private:
|
||||
const std::unordered_map<std::string, dbg_cmd> command_table = {
|
||||
{"h", dbg_cmd::cmd_help},
|
||||
{"help", dbg_cmd::cmd_help},
|
||||
{"bt", dbg_cmd::cmd_backtrace},
|
||||
{"backtrace", dbg_cmd::cmd_backtrace},
|
||||
{"c", dbg_cmd::cmd_continue},
|
||||
{"continue", dbg_cmd::cmd_continue},
|
||||
{"f", dbg_cmd::cmd_list_file},
|
||||
{"file", dbg_cmd::cmd_list_file},
|
||||
{"g", dbg_cmd::cmd_global},
|
||||
{"global", dbg_cmd::cmd_global},
|
||||
{"l", dbg_cmd::cmd_local},
|
||||
{"local", dbg_cmd::cmd_local},
|
||||
{"u", dbg_cmd::cmd_upval},
|
||||
{"upval", dbg_cmd::cmd_upval},
|
||||
{"r", dbg_cmd::cmd_register},
|
||||
{"register", dbg_cmd::cmd_register},
|
||||
{"a", dbg_cmd::cmd_show_all},
|
||||
{"all", dbg_cmd::cmd_show_all},
|
||||
{"n", dbg_cmd::cmd_next},
|
||||
{"next", dbg_cmd::cmd_next},
|
||||
{"bk", dbg_cmd::cmd_break_point},
|
||||
{"break", dbg_cmd::cmd_break_point},
|
||||
{"q", dbg_cmd::cmd_exit},
|
||||
{"exit", dbg_cmd::cmd_exit}
|
||||
};
|
||||
dbg_cmd get_cmd_type(const std::string& cmd) const {
|
||||
return command_table.count(cmd)?
|
||||
command_table.at(cmd):dbg_cmd::cmd_error;
|
||||
}
|
||||
|
||||
private:
|
||||
bool next;
|
||||
usize fsize;
|
||||
u16 break_file_index;
|
||||
u32 break_line;
|
||||
error src;
|
||||
|
||||
private:
|
||||
debug_prof_data data;
|
||||
bool do_profiling;
|
||||
|
||||
private:
|
||||
std::vector<std::string> parse(const std::string&);
|
||||
u16 file_index(const std::string&) const;
|
||||
void err();
|
||||
void help();
|
||||
void list_file() const;
|
||||
void step_info();
|
||||
void interact();
|
||||
|
||||
public:
|
||||
dbg():
|
||||
next(true), fsize(0),
|
||||
break_file_index(0), break_line(0),
|
||||
do_profiling(false) {}
|
||||
void run(
|
||||
const codegen&,
|
||||
const linker&,
|
||||
const std::vector<std::string>&,
|
||||
bool,
|
||||
bool
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
198
src/nasal_err.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
#include "nasal_err.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // use SetConsoleTextAttribute
|
||||
struct for_reset {
|
||||
CONSOLE_SCREEN_BUFFER_INFO scr;
|
||||
for_reset() {
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
||||
}
|
||||
};
|
||||
static for_reset windows_system_set;
|
||||
#endif
|
||||
|
||||
std::ostream& back_white(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
||||
#else
|
||||
s << "\033[7m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& red(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
|
||||
#else
|
||||
s << "\033[91;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& cyan(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
|
||||
#else
|
||||
s << "\033[36;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& orange(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
|
||||
#else
|
||||
s << "\033[93;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& white(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
|
||||
#else
|
||||
s << "\033[0m\033[1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& reset(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(
|
||||
GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
windows_system_set.scr.wAttributes
|
||||
);
|
||||
#else
|
||||
s << "\033[0m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
void flstream::load(const std::string& f) {
|
||||
if (file==f) { // don't need to load a loaded file
|
||||
return;
|
||||
}
|
||||
|
||||
// update file name
|
||||
file = f;
|
||||
|
||||
// REPL: load from memory
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
const auto& source = repl::info::instance()->repl_file_source;
|
||||
res = {};
|
||||
size_t pos = 0, last = 0;
|
||||
while ((pos = source.find("\n", last))!=std::string::npos) {
|
||||
res.push_back(source.substr(last, pos - last));
|
||||
last = pos + 1;
|
||||
}
|
||||
if (last<source.length()) {
|
||||
res.push_back(source.substr(last));
|
||||
} else {
|
||||
res.push_back("");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
res.clear();
|
||||
std::ifstream in(f, std::ios::binary);
|
||||
if (in.fail()) {
|
||||
std::cerr << red << "src: " << reset << "cannot open <" << f << ">\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
while(!in.eof()) {
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
res.push_back(line);
|
||||
}
|
||||
}
|
||||
|
||||
void error::err(const std::string& stage, const std::string& info) {
|
||||
++cnt;
|
||||
std::cerr << red << stage << ": " << white << info << reset << "\n\n";
|
||||
}
|
||||
|
||||
void error::warn(const std::string& stage, const std::string& info) {
|
||||
std::clog << orange << stage << ": " << white << info << reset << "\n\n";
|
||||
}
|
||||
|
||||
void error::err(
|
||||
const std::string& stage, const span& loc, const std::string& info) {
|
||||
// load error occurred file into string lines
|
||||
load(loc.file);
|
||||
|
||||
++cnt;
|
||||
|
||||
std::cerr
|
||||
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
|
||||
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
|
||||
<< reset << "\n";
|
||||
|
||||
const usize maxlen = std::to_string(loc.end_line).length();
|
||||
const std::string iden = identation(maxlen);
|
||||
|
||||
for(u32 line = loc.begin_line; line<=loc.end_line; ++line) {
|
||||
// skip line 0
|
||||
if (!line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (loc.begin_line<line && line<loc.end_line) {
|
||||
if (line==loc.begin_line+1) {
|
||||
std::cerr << cyan << iden << " | " << reset << "...\n";
|
||||
std::cerr << cyan << iden << " | " << reset << "\n";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this line has nothing, skip
|
||||
if (!res[line-1].length() && line!=loc.end_line) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// line out of range
|
||||
if (line-1>=res.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& code = res[line-1];
|
||||
std::cerr << cyan << leftpad(line, maxlen) << " | " << reset << code << "\n";
|
||||
// output underline
|
||||
std::cerr << cyan << iden << " | " << reset;
|
||||
if (loc.begin_line==loc.end_line) {
|
||||
for(u32 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u32 i = loc.begin_column; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (line==loc.begin_line) {
|
||||
for(u32 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u32 i = loc.begin_column; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (loc.begin_line<line && line<loc.end_line) {
|
||||
for(u32 i = 0; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
} else {
|
||||
for(u32 i = 0; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
}
|
||||
if (line==loc.end_line) {
|
||||
std::cerr << reset;
|
||||
} else {
|
||||
std::cerr << reset << "\n";
|
||||
}
|
||||
}
|
||||
std::cerr << "\n\n";
|
||||
}
|
||||
|
||||
}
|
||||
71
src/nasal_err.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream> // MSVC need this to use std::getline
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct span {
|
||||
u32 begin_line;
|
||||
u32 begin_column;
|
||||
u32 end_line;
|
||||
u32 end_column;
|
||||
std::string file;
|
||||
};
|
||||
|
||||
std::ostream& back_white(std::ostream&);
|
||||
std::ostream& red(std::ostream&);
|
||||
std::ostream& cyan(std::ostream&);
|
||||
std::ostream& orange(std::ostream&);
|
||||
std::ostream& white(std::ostream&);
|
||||
std::ostream& reset(std::ostream&);
|
||||
|
||||
class flstream {
|
||||
protected:
|
||||
std::string file;
|
||||
std::vector<std::string> res;
|
||||
|
||||
public:
|
||||
flstream():file("") {}
|
||||
void load(const std::string&);
|
||||
const std::string& operator[](usize n) const {return res[n];}
|
||||
const auto& name() const {return file;}
|
||||
const auto& file_content() const {return res;}
|
||||
usize size() const {return res.size();}
|
||||
};
|
||||
|
||||
class error:public flstream {
|
||||
private:
|
||||
u32 cnt; // counter for errors
|
||||
|
||||
std::string identation(usize len) {
|
||||
return std::string(len,' ');
|
||||
}
|
||||
std::string leftpad(u32 num, usize len) {
|
||||
auto tmp = std::to_string(num);
|
||||
while(tmp.length()<len) {
|
||||
tmp=" "+tmp;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public:
|
||||
error():cnt(0) {}
|
||||
void err(const std::string&, const std::string&);
|
||||
void warn(const std::string&, const std::string&);
|
||||
void err(const std::string&, const span&, const std::string&);
|
||||
|
||||
void chkerr() const {
|
||||
if (cnt) {
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
u32 geterr() const {return cnt;}
|
||||
};
|
||||
|
||||
}
|
||||
385
src/nasal_gc.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
#include "nasal_gc.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void gc::do_mark_sweep() {
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
auto begin = clk::now();
|
||||
mark();
|
||||
auto mark_end = clk::now();
|
||||
sweep();
|
||||
auto sweep_end = clk::now();
|
||||
|
||||
auto total_time = (sweep_end-begin).count();
|
||||
auto mark_time = (mark_end-begin).count();
|
||||
auto sweep_time = (sweep_end-mark_end).count();
|
||||
worktime += total_time;
|
||||
max_time = max_time<total_time? total_time:max_time;
|
||||
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
|
||||
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
|
||||
}
|
||||
|
||||
void gc::mark() {
|
||||
std::vector<var> bfs;
|
||||
mark_context_root(bfs);
|
||||
if (memory.size()>8192 && bfs.size()>4) {
|
||||
usize size = bfs.size();
|
||||
std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4);
|
||||
std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2);
|
||||
std::thread t2(&gc::concurrent_mark, this, std::ref(bfs), size/2, size/4*3);
|
||||
std::thread t3(&gc::concurrent_mark, this, std::ref(bfs), size/4*3, size);
|
||||
t0.join();
|
||||
t1.join();
|
||||
t2.join();
|
||||
t3.join();
|
||||
return;
|
||||
}
|
||||
|
||||
while(!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
std::vector<var> bfs;
|
||||
for(auto i = begin; i<end; ++i) {
|
||||
var value = vec[i];
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
while(!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
continue;
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_context_root(std::vector<var>& bfs_queue) {
|
||||
// scan global
|
||||
for(usize i = 0; i<main_context_global_size; ++i) {
|
||||
auto& val = main_context_global[i];
|
||||
if (val.type>vm_num) {
|
||||
bfs_queue.push_back(val);
|
||||
}
|
||||
}
|
||||
// scan now running context, this context maybe related to coroutine or main
|
||||
for(var* i = running_context->stack; i<=running_context->top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
bfs_queue.push_back(running_context->funcr);
|
||||
bfs_queue.push_back(running_context->upvalr);
|
||||
bfs_queue.push_back(temp);
|
||||
|
||||
if (!cort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// coroutine is running, so scan main process stack from mctx
|
||||
for(var* i = main_context.stack; i<=main_context.top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
bfs_queue.push_back(main_context.funcr);
|
||||
bfs_queue.push_back(main_context.upvalr);
|
||||
}
|
||||
|
||||
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
||||
value.val.gcobj->mark = nas_val::gc_status::found;
|
||||
switch(value.type) {
|
||||
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
|
||||
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
|
||||
case vm_func: mark_func(bfs_queue, value.func()); break;
|
||||
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
|
||||
case vm_co: mark_co(bfs_queue, value.co()); break;
|
||||
case vm_map: mark_map(bfs_queue, value.map()); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
|
||||
for(auto& i : vec.elems) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
|
||||
for(auto& i : hash.elems) {
|
||||
if (i.second.type>vm_num) {
|
||||
bfs_queue.push_back(i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
|
||||
for(auto& i : function.local) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
for(auto& i : function.upval) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
||||
for(auto& i : upval.elems) {
|
||||
if (i.type>vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
||||
bfs_queue.push_back(co.ctx.funcr);
|
||||
bfs_queue.push_back(co.ctx.upvalr);
|
||||
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_map(std::vector<var>& bfs_queue, nas_map& mp) {
|
||||
for(const auto& i : mp.mapper) {
|
||||
if (i.second->type>vm_num) {
|
||||
bfs_queue.push_back(*i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::sweep() {
|
||||
for(auto i : memory) {
|
||||
if (i->mark==nas_val::gc_status::uncollected) {
|
||||
i->clear();
|
||||
unused[i->type-vm_str].push_back(i);
|
||||
i->mark = nas_val::gc_status::collected;
|
||||
} else if (i->mark==nas_val::gc_status::found) {
|
||||
i->mark = nas_val::gc_status::uncollected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::extend(u8 type) {
|
||||
const u8 index = type-vm_str;
|
||||
size[index] += incr[index];
|
||||
|
||||
for(u32 i = 0; i<incr[index]; ++i) {
|
||||
// no need to check, will be killed if memory is not enough
|
||||
nas_val* tmp = new nas_val(type);
|
||||
|
||||
// add to heap
|
||||
memory.push_back(tmp);
|
||||
unused[index].push_back(tmp);
|
||||
}
|
||||
|
||||
incr[index] = incr[index]+incr[index]/2;
|
||||
}
|
||||
|
||||
void gc::init(
|
||||
const std::vector<std::string>& constant_strings,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
// initialize counters
|
||||
worktime = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
size[i] = gcnt[i] = acnt[i] = 0;
|
||||
}
|
||||
|
||||
// coroutine pointer set to nullptr
|
||||
cort = nullptr;
|
||||
|
||||
// init constant strings
|
||||
strs.resize(constant_strings.size());
|
||||
for(u32 i = 0; i<strs.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (strs[i].type==vm_str && strs[i].str()==constant_strings[i]) {
|
||||
continue;
|
||||
}
|
||||
strs[i] = var::gcobj(new nas_val(vm_str));
|
||||
strs[i].val.gcobj->unmutable = 1;
|
||||
strs[i].str() = constant_strings[i];
|
||||
}
|
||||
|
||||
// record arguments
|
||||
env_argv.resize(argv.size());
|
||||
for(usize i = 0; i<argv.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (env_argv[i].type==vm_str && env_argv[i].str()==argv[i]) {
|
||||
continue;
|
||||
}
|
||||
env_argv[i] = var::gcobj(new nas_val(vm_str));
|
||||
env_argv[i].val.gcobj->unmutable = 1;
|
||||
env_argv[i].str() = argv[i];
|
||||
}
|
||||
}
|
||||
|
||||
void gc::clear() {
|
||||
for(auto i : memory) {
|
||||
delete i;
|
||||
}
|
||||
memory.clear();
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
unused[i].clear();
|
||||
}
|
||||
for(auto& i : strs) {
|
||||
delete i.val.gcobj;
|
||||
}
|
||||
strs.clear();
|
||||
env_argv.clear();
|
||||
}
|
||||
|
||||
void gc::info() const {
|
||||
|
||||
using std::left;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
|
||||
const char* used_table_name[] = {
|
||||
"object type", "gc count", "alloc count", "memory size",
|
||||
"detail", "time spend", "gc time", "avg time", "max gc",
|
||||
"max mark", "max sweep", nullptr
|
||||
};
|
||||
const char* name[] = {
|
||||
"string",
|
||||
"vector",
|
||||
"hashmap",
|
||||
"function",
|
||||
"upvalue",
|
||||
"object",
|
||||
"coroutine",
|
||||
"namespace",
|
||||
nullptr
|
||||
};
|
||||
|
||||
usize indent = 0, len = 0;
|
||||
for(usize i = 0; used_table_name[i]; ++i) {
|
||||
len = std::string(used_table_name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(usize i = 0; name[i]; ++i) {
|
||||
len = std::string(name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
len = std::to_string(gcnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(acnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(size[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
auto indent_string = std::string("--");
|
||||
for(usize i = 0; i<indent; ++i) {
|
||||
indent_string += "-";
|
||||
}
|
||||
auto last_line = indent_string + "+" +
|
||||
indent_string + "-" + indent_string + "-" + indent_string;
|
||||
indent_string = indent_string + "+" +
|
||||
indent_string + "+" + indent_string + "+" + indent_string;
|
||||
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
|
||||
double total = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
if (!gcnt[i] && !acnt[i] && !size[i]) {
|
||||
continue;
|
||||
}
|
||||
total += gcnt[i];
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
|
||||
std::clog << "\n";
|
||||
}
|
||||
std::clog << indent_string << "\n";
|
||||
|
||||
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << "\n" << indent_string << "\n";
|
||||
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " | " << worktime*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " | " << worktime*1.0/den*1000/total << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max gc";
|
||||
std::clog << " | " << max_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
|
||||
std::clog << last_line << "\n";
|
||||
}
|
||||
|
||||
var gc::alloc(u8 type) {
|
||||
const u8 index = type-vm_str;
|
||||
++acnt[index];
|
||||
if (unused[index].empty()) {
|
||||
++gcnt[index];
|
||||
do_mark_sweep();
|
||||
}
|
||||
if (unused[index].empty()) {
|
||||
extend(type);
|
||||
}
|
||||
var ret = var::gcobj(unused[index].back());
|
||||
ret.val.gcobj->mark = nas_val::gc_status::uncollected;
|
||||
unused[index].pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gc::context_change(nas_co* co) {
|
||||
// store running state to main context
|
||||
main_context = *running_context;
|
||||
|
||||
// restore coroutine context state
|
||||
*running_context = co->ctx;
|
||||
|
||||
// set coroutine pointer
|
||||
cort = co;
|
||||
|
||||
// set coroutine state to running
|
||||
cort->status = nas_co::status::running;
|
||||
}
|
||||
|
||||
void gc::context_reserve() {
|
||||
// pc=0 means this coroutine is finished
|
||||
cort->status = running_context->pc?
|
||||
nas_co::status::suspended:
|
||||
nas_co::status::dead;
|
||||
|
||||
// store running state to coroutine
|
||||
cort->ctx = *running_context;
|
||||
|
||||
// restore main context state
|
||||
*running_context = main_context;
|
||||
|
||||
// set coroutine pointer to nullptr
|
||||
cort = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
126
src/nasal_gc.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
// avoid MSVC warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_type.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct gc {
|
||||
/* main context temporary storage */
|
||||
context main_context;
|
||||
|
||||
/* global storage */
|
||||
var* main_context_global = nullptr;
|
||||
usize main_context_global_size = 0;
|
||||
|
||||
/* runtime context */
|
||||
context* running_context = nullptr;
|
||||
nas_co* cort = nullptr; // running coroutine
|
||||
|
||||
/* temporary space used in native/module functions */
|
||||
var temp = nil;
|
||||
|
||||
/* constants and memory pool */
|
||||
std::vector<var> strs = {}; // reserved address for const vm_str
|
||||
std::vector<var> env_argv = {}; // command line arguments
|
||||
std::vector<nas_val*> memory; // gc memory
|
||||
std::vector<nas_val*> unused[gc_type_size]; // gc free list
|
||||
|
||||
/* heap increase size */
|
||||
u32 incr[gc_type_size] = {
|
||||
128, // vm_str
|
||||
128, // vm_vec
|
||||
64, // vm_hash
|
||||
128, // vm_func
|
||||
256, // vm_upval
|
||||
16, // vm_obj
|
||||
16, // vm_co
|
||||
2, // vm_map
|
||||
};
|
||||
|
||||
/* values for analysis */
|
||||
u64 size[gc_type_size];
|
||||
u64 gcnt[gc_type_size];
|
||||
u64 acnt[gc_type_size];
|
||||
i64 worktime = 0;
|
||||
i64 max_time = 0;
|
||||
i64 max_mark_time = 0;
|
||||
i64 max_sweep_time = 0;
|
||||
|
||||
void set(context* _ctx, var* _global, usize _size) {
|
||||
running_context = _ctx;
|
||||
main_context_global = _global;
|
||||
main_context_global_size = _size;
|
||||
}
|
||||
|
||||
private:
|
||||
/* gc functions */
|
||||
void do_mark_sweep();
|
||||
void mark();
|
||||
void concurrent_mark(std::vector<var>&, usize, usize);
|
||||
void mark_context_root(std::vector<var>&);
|
||||
void mark_var(std::vector<var>&, var&);
|
||||
void mark_vec(std::vector<var>&, nas_vec&);
|
||||
void mark_hash(std::vector<var>&, nas_hash&);
|
||||
void mark_func(std::vector<var>&, nas_func&);
|
||||
void mark_upval(std::vector<var>&, nas_upval&);
|
||||
void mark_co(std::vector<var>&, nas_co&);
|
||||
void mark_map(std::vector<var>&, nas_map&);
|
||||
void sweep();
|
||||
|
||||
public:
|
||||
void extend(u8);
|
||||
void init(const std::vector<std::string>&, const std::vector<std::string>&);
|
||||
void clear();
|
||||
void info() const;
|
||||
var alloc(const u8);
|
||||
void context_change(nas_co*);
|
||||
void context_reserve();
|
||||
|
||||
public:
|
||||
var newstr(char c) {
|
||||
var s = alloc(vm_str);
|
||||
s.str() = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const char* buff) {
|
||||
var s = alloc(vm_str);
|
||||
s.str() = std::string(buff);
|
||||
return s;
|
||||
}
|
||||
|
||||
var newstr(const std::string& buff) {
|
||||
var s = alloc(vm_str);
|
||||
s.str() = buff;
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// module function type
|
||||
typedef var (*module_func)(var*, usize, gc*);
|
||||
|
||||
// module function stores in tables with this type, end with {nullptr,nullptr}
|
||||
struct module_func_info {
|
||||
const char* name;
|
||||
module_func fd;
|
||||
};
|
||||
|
||||
// module function "get" type
|
||||
typedef module_func_info* (*get_func_ptr)();
|
||||
|
||||
}
|
||||
399
src/nasal_import.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
#include "nasal_import.h"
|
||||
#include "symbol_finder.h"
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
linker::linker(): show_path_flag(false), library_loaded(false), this_file("") {
|
||||
const auto seperator= is_windows()? ';':':';
|
||||
const auto PATH = std::string(getenv("PATH"));
|
||||
usize last = 0, position = PATH.find(seperator, 0);
|
||||
while(position!=std::string::npos) {
|
||||
std::string dirpath = PATH.substr(last, position-last);
|
||||
if (dirpath.length()) {
|
||||
envpath.push_back(dirpath);
|
||||
}
|
||||
last = position+1;
|
||||
position = PATH.find(seperator, last);
|
||||
}
|
||||
if (last!=PATH.length()) {
|
||||
envpath.push_back(PATH.substr(last));
|
||||
}
|
||||
}
|
||||
|
||||
std::string linker::get_path(expr* node) {
|
||||
if (node->get_type()==expr_type::ast_use) {
|
||||
auto file_relative_path = std::string("");
|
||||
const auto& path = reinterpret_cast<use_stmt*>(node)->get_path();
|
||||
for(auto i : path) {
|
||||
file_relative_path += i->get_name();
|
||||
if (i!=path.back()) {
|
||||
file_relative_path += (is_windows()? "\\":"/");
|
||||
}
|
||||
}
|
||||
return file_relative_path + ".nas";
|
||||
}
|
||||
auto call_node = reinterpret_cast<call_expr*>(node);
|
||||
auto arguments = reinterpret_cast<call_function*>(call_node->get_calls()[0]);
|
||||
auto content = reinterpret_cast<string_literal*>(arguments->get_argument()[0]);
|
||||
return content->get_content();
|
||||
}
|
||||
|
||||
std::string linker::find_real_file_path(
|
||||
const std::string& filename, const span& location) {
|
||||
// first add file name itself into the file path
|
||||
std::vector<std::string> path_list = {filename};
|
||||
|
||||
// generate search path from environ path
|
||||
for(const auto& p : envpath) {
|
||||
path_list.push_back(p + (is_windows()? "\\":"/") + filename);
|
||||
}
|
||||
|
||||
// search file
|
||||
for(const auto& path : path_list) {
|
||||
if (access(path.c_str(), F_OK)!=-1) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// we will find lib.nas in nasal std directory
|
||||
if (filename=="lib.nas") {
|
||||
return is_windows()?
|
||||
find_real_file_path("std\\lib.nas", location):
|
||||
find_real_file_path("std/lib.nas", location);
|
||||
}
|
||||
if (!show_path_flag) {
|
||||
err.err("link",
|
||||
"in <" + location.file + ">: " +
|
||||
"cannot find file <" + filename + ">, " +
|
||||
"use <-d> to get detail search path"
|
||||
);
|
||||
return "";
|
||||
}
|
||||
auto path_list_info = std::string("");
|
||||
for(const auto& path : path_list) {
|
||||
path_list_info += " -> " + path + "\n";
|
||||
}
|
||||
err.err("link",
|
||||
"in <" + location.file + ">: " +
|
||||
"cannot find file <" + filename +
|
||||
"> in these paths:\n" + path_list_info
|
||||
);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool linker::import_check(expr* node) {
|
||||
if (node->get_type()==expr_type::ast_use) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
call
|
||||
|_id:import
|
||||
|_call_func
|
||||
|_string:'filename'
|
||||
*/
|
||||
if (node->get_type()!=expr_type::ast_call) {
|
||||
return false;
|
||||
}
|
||||
auto call_node = reinterpret_cast<call_expr*>(node);
|
||||
auto first_expr = call_node->get_first();
|
||||
if (first_expr->get_type()!=expr_type::ast_id) {
|
||||
return false;
|
||||
}
|
||||
if (reinterpret_cast<identifier*>(first_expr)->get_name()!="import") {
|
||||
return false;
|
||||
}
|
||||
if (!call_node->get_calls().size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// import("xxx");
|
||||
if (call_node->get_calls().size()!=1) {
|
||||
return false;
|
||||
}
|
||||
auto maybe_func_call = call_node->get_calls()[0];
|
||||
if (maybe_func_call->get_type()!=expr_type::ast_callf) {
|
||||
return false;
|
||||
}
|
||||
auto func_call = reinterpret_cast<call_function*>(maybe_func_call);
|
||||
if (func_call->get_argument().size()!=1) {
|
||||
return false;
|
||||
}
|
||||
if (func_call->get_argument()[0]->get_type()!=expr_type::ast_str) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool linker::check_exist_or_record_file(const std::string& file) {
|
||||
// avoid importing the same file
|
||||
for(const auto& name : imported_files) {
|
||||
if (file==name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
imported_files.push_back(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool linker::check_self_import(const std::string& file) {
|
||||
for(const auto& name : module_load_stack) {
|
||||
if (file==name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string linker::generate_self_import_path(const std::string& filename) {
|
||||
std::string res = "";
|
||||
for(const auto& i : module_load_stack) {
|
||||
res += "[" + i + "] -> ";
|
||||
}
|
||||
return res + "[" + filename + "]";
|
||||
}
|
||||
|
||||
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
|
||||
// add children of add_root to the back of root
|
||||
for(auto& i : old_tree_root->get_expressions()) {
|
||||
new_tree_root->add_expression(i);
|
||||
}
|
||||
// clean old root
|
||||
old_tree_root->get_expressions().clear();
|
||||
}
|
||||
|
||||
code_block* linker::import_regular_file(
|
||||
expr* node, std::unordered_set<std::string>& used_modules) {
|
||||
// get filename
|
||||
auto filename = get_path(node);
|
||||
|
||||
// avoid infinite loading loop
|
||||
filename = find_real_file_path(filename, node->get_location());
|
||||
// if get empty string(error) or this file is used before, do not parse
|
||||
if (!filename.length() || used_modules.count(filename)) {
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
|
||||
// check self import, avoid infinite loading loop
|
||||
if (check_self_import(filename)) {
|
||||
err.err("link",
|
||||
"self-referenced module <" + filename + ">:\n" +
|
||||
" reference path: " + generate_self_import_path(filename)
|
||||
);
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
check_exist_or_record_file(filename);
|
||||
|
||||
module_load_stack.push_back(filename);
|
||||
// start importing...
|
||||
lexer nasal_lexer;
|
||||
parse nasal_parser;
|
||||
if (nasal_lexer.scan(filename).geterr()) {
|
||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
if (nasal_parser.compile(nasal_lexer).geterr()) {
|
||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
||||
return new code_block({0, 0, 0, 0, filename});
|
||||
}
|
||||
// swap result out
|
||||
auto parse_result = nasal_parser.swap(nullptr);
|
||||
|
||||
// check if parse result has 'import'
|
||||
auto result = load(parse_result, filename);
|
||||
module_load_stack.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
code_block* linker::import_nasal_lib() {
|
||||
auto path = find_real_file_path(
|
||||
"lib.nas", {0, 0, 0, 0, this_file}
|
||||
);
|
||||
if (!path.length()) {
|
||||
return new code_block({0, 0, 0, 0, path});
|
||||
}
|
||||
|
||||
// avoid infinite loading library
|
||||
if (check_exist_or_record_file(path)) {
|
||||
return new code_block({0, 0, 0, 0, path});
|
||||
}
|
||||
|
||||
// start importing...
|
||||
lexer nasal_lexer;
|
||||
parse nasal_parser;
|
||||
if (nasal_lexer.scan(path).geterr()) {
|
||||
err.err("link",
|
||||
"error occurred when analysing library <" + path + ">"
|
||||
);
|
||||
return new code_block({0, 0, 0, 0, path});
|
||||
}
|
||||
if (nasal_parser.compile(nasal_lexer).geterr()) {
|
||||
err.err("link",
|
||||
"error occurred when analysing library <" + path + ">"
|
||||
);
|
||||
return new code_block({0, 0, 0, 0, path});
|
||||
}
|
||||
// swap result out
|
||||
auto parse_result = nasal_parser.swap(nullptr);
|
||||
// check if library has 'import' (in fact it should not)
|
||||
return load(parse_result, path);
|
||||
}
|
||||
|
||||
std::string linker::generate_module_name(const std::string& file_path) {
|
||||
auto error_name = "module@[" + file_path + "]";
|
||||
if (!file_path.length()) {
|
||||
return error_name;
|
||||
}
|
||||
|
||||
// check file suffix and get file suffix position
|
||||
auto suffix_position = file_path.find(".nas");
|
||||
if (suffix_position==std::string::npos) {
|
||||
err.warn("link",
|
||||
"get invalid module name from <" + file_path + ">, " +
|
||||
"will not be easily accessed. " +
|
||||
"\".nas\" suffix is required."
|
||||
);
|
||||
return error_name;
|
||||
}
|
||||
if (suffix_position+4!=file_path.length()) {
|
||||
err.warn("link",
|
||||
"get invalid module name from <" + file_path + ">, " +
|
||||
"will not be easily accessed. " +
|
||||
"only one \".nas\" suffix is required in the path."
|
||||
);
|
||||
return error_name;
|
||||
}
|
||||
|
||||
// only get the file name as module name, directory path is not included
|
||||
auto split_position = file_path.find_last_of("/");
|
||||
// find "\\" in windows platform
|
||||
if (split_position==std::string::npos) {
|
||||
split_position = file_path.find_last_of("\\");
|
||||
}
|
||||
|
||||
// split file path to get module name
|
||||
auto module_name = split_position==std::string::npos?
|
||||
file_path.substr(0, suffix_position):
|
||||
file_path.substr(split_position+1, suffix_position-split_position-1);
|
||||
|
||||
// check validation of module name
|
||||
if (!module_name.length()) {
|
||||
err.warn("link",
|
||||
"get empty module name from <" + file_path + ">, " +
|
||||
"will not be easily accessed."
|
||||
);
|
||||
return module_name;
|
||||
}
|
||||
if (std::isdigit(module_name[0]) ||
|
||||
module_name.find(".")!=std::string::npos ||
|
||||
module_name.find("-")!=std::string::npos) {
|
||||
err.warn("link",
|
||||
"get module <" + module_name + "> from <" + file_path + ">, " +
|
||||
"will not be easily accessed."
|
||||
);
|
||||
}
|
||||
return module_name;
|
||||
}
|
||||
|
||||
return_expr* linker::generate_module_return(code_block* block) {
|
||||
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
|
||||
auto result = new return_expr(block->get_location());
|
||||
auto value = new hash_expr(block->get_location());
|
||||
result->set_value(value);
|
||||
for(const auto& i : finder->do_find(block)) {
|
||||
auto pair = new hash_pair(block->get_location());
|
||||
// do not export symbol begins with '_'
|
||||
if (i.name.length() && i.name[0]=='_') {
|
||||
continue;
|
||||
}
|
||||
pair->set_name(i.name);
|
||||
pair->set_value(new identifier(block->get_location(), i.name));
|
||||
value->add_member(pair);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
definition_expr* linker::generate_module_definition(code_block* block) {
|
||||
auto def = new definition_expr(block->get_location());
|
||||
def->set_identifier(new identifier(
|
||||
block->get_location(),
|
||||
generate_module_name(block->get_location().file)
|
||||
));
|
||||
|
||||
auto call = new call_expr(block->get_location());
|
||||
auto func = new function(block->get_location());
|
||||
func->set_code_block(block);
|
||||
func->get_code_block()->add_expression(generate_module_return(block));
|
||||
call->set_first(func);
|
||||
call->add_call(new call_function(block->get_location()));
|
||||
|
||||
def->set_value(call);
|
||||
return def;
|
||||
}
|
||||
|
||||
code_block* linker::load(code_block* program_root, const std::string& filename) {
|
||||
auto tree = new code_block({0, 0, 0, 0, filename});
|
||||
// load library, this ast will be linked with root directly
|
||||
// so no extra namespace is generated
|
||||
if (!library_loaded) {
|
||||
auto nasal_lib_code_block = import_nasal_lib();
|
||||
// insert nasal lib code to the back of tree
|
||||
link(tree, nasal_lib_code_block);
|
||||
delete nasal_lib_code_block;
|
||||
library_loaded = true;
|
||||
}
|
||||
|
||||
// load imported modules
|
||||
std::unordered_set<std::string> used_modules = {};
|
||||
for(auto& import_node : program_root->get_expressions()) {
|
||||
if (!import_check(import_node)) {
|
||||
break;
|
||||
}
|
||||
// parse file and get ast
|
||||
auto module_code_block = import_regular_file(import_node, used_modules);
|
||||
auto replace_node = new null_expr(import_node->get_location());
|
||||
// after importing the regular file as module, delete this node
|
||||
delete import_node;
|
||||
// and replace the node with null_expr node
|
||||
import_node = replace_node;
|
||||
|
||||
// avoid repeatedly importing the same module
|
||||
const auto& module_path = module_code_block->get_location().file;
|
||||
if (used_modules.count(module_path)) {
|
||||
delete module_code_block;
|
||||
continue;
|
||||
}
|
||||
|
||||
// then we generate a function warping the code block,
|
||||
// and export the necessary global symbols in this code block
|
||||
// by generate a return statement, with a hashmap return value
|
||||
used_modules.insert(module_path);
|
||||
tree->add_expression(generate_module_definition(module_code_block));
|
||||
}
|
||||
|
||||
// insert program root to the back of tree
|
||||
link(tree, program_root);
|
||||
return tree;
|
||||
}
|
||||
|
||||
const error& linker::link(
|
||||
parse& parse, const std::string& self, bool spath = false) {
|
||||
// switch for showing path when errors occur
|
||||
show_path_flag = spath;
|
||||
|
||||
// initializing file map
|
||||
this_file = self;
|
||||
imported_files = {self};
|
||||
module_load_stack = {self};
|
||||
|
||||
// scan root and import files
|
||||
// then generate a new ast and return to import_ast
|
||||
auto new_tree_root = load(parse.tree(), self);
|
||||
auto old_tree_root = parse.swap(new_tree_root);
|
||||
delete old_tree_root;
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
59
src/nasal_import.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#define _CRT_SECURE_NO_DEPRECATE 1
|
||||
#define _CRT_NONSTDC_NO_DEPRECATE 1
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define F_OK 0
|
||||
#endif
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "symbol_finder.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class linker {
|
||||
private:
|
||||
bool show_path_flag;
|
||||
bool library_loaded;
|
||||
std::string this_file;
|
||||
error err;
|
||||
std::vector<std::string> imported_files;
|
||||
std::vector<std::string> module_load_stack;
|
||||
std::vector<std::string> envpath;
|
||||
|
||||
private:
|
||||
bool import_check(expr*);
|
||||
bool check_exist_or_record_file(const std::string&);
|
||||
bool check_self_import(const std::string&);
|
||||
std::string generate_self_import_path(const std::string&);
|
||||
void link(code_block*, code_block*);
|
||||
std::string get_path(expr*);
|
||||
std::string find_real_file_path(const std::string&, const span&);
|
||||
code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
|
||||
code_block* import_nasal_lib();
|
||||
std::string generate_module_name(const std::string&);
|
||||
return_expr* generate_module_return(code_block*);
|
||||
definition_expr* generate_module_definition(code_block*);
|
||||
code_block* load(code_block*, const std::string&);
|
||||
|
||||
public:
|
||||
linker();
|
||||
const error& link(parse&, const std::string&, bool);
|
||||
const auto& get_file_list() const {return imported_files;}
|
||||
};
|
||||
|
||||
}
|
||||
393
src/nasal_lexer.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#include "nasal_lexer.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool lexer::skip(char c) {
|
||||
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
|
||||
}
|
||||
|
||||
bool lexer::is_id(char c) {
|
||||
return (c=='_') || std::isalpha(c) || (c<0);
|
||||
}
|
||||
|
||||
bool lexer::is_hex(char c) {
|
||||
return std::isxdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_oct(char c) {
|
||||
return '0'<=c && c<='7';
|
||||
}
|
||||
|
||||
bool lexer::is_dec(char c) {
|
||||
return std::isdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_str(char c) {
|
||||
return c=='\'' || c=='\"' || c=='`';
|
||||
}
|
||||
|
||||
bool lexer::is_single_opr(char c) {
|
||||
return (
|
||||
c=='(' || c==')' || c=='[' || c==']' ||
|
||||
c=='{' || c=='}' || c==',' || c==';' ||
|
||||
c==':' || c=='?' || c=='`' || c=='@' ||
|
||||
c=='%' || c=='$' || c=='\\'
|
||||
);
|
||||
}
|
||||
|
||||
bool lexer::is_calc_opr(char c) {
|
||||
return (
|
||||
c=='=' || c=='+' || c=='-' || c=='*' ||
|
||||
c=='!' || c=='/' || c=='<' || c=='>' ||
|
||||
c=='~' || c=='|' || c=='&' || c=='^'
|
||||
);
|
||||
}
|
||||
|
||||
void lexer::skip_note() {
|
||||
// avoid note, after this process ptr will point to '\n'
|
||||
// so next loop line counter+1
|
||||
while(++ptr<res.size() && res[ptr]!='\n') {}
|
||||
}
|
||||
|
||||
void lexer::err_char() {
|
||||
++column;
|
||||
char c = res[ptr++];
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid character 0x"+chrhex(c)
|
||||
);
|
||||
++invalid_char;
|
||||
}
|
||||
|
||||
void lexer::open(const std::string& file) {
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
err.load(file);
|
||||
filename = file;
|
||||
res = repl::info::instance()->repl_file_source;
|
||||
return;
|
||||
}
|
||||
|
||||
// check file exsits and it is a regular file
|
||||
struct stat buffer;
|
||||
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
|
||||
err.err("lexer", "<"+file+"> is not a regular file");
|
||||
err.chkerr();
|
||||
}
|
||||
|
||||
// load
|
||||
filename = file;
|
||||
std::ifstream in(file, std::ios::binary);
|
||||
if (in.fail()) {
|
||||
err.err("lexer", "failed to open <" + file + ">");
|
||||
res = "";
|
||||
return;
|
||||
}
|
||||
err.load(file);
|
||||
std::stringstream ss;
|
||||
ss << in.rdbuf();
|
||||
res = ss.str();
|
||||
}
|
||||
|
||||
tok lexer::get_type(const std::string& str) {
|
||||
return typetbl.count(str)? typetbl.at(str):tok::null;
|
||||
}
|
||||
|
||||
std::string lexer::utf8_gen() {
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && res[ptr]<0) {
|
||||
std::string tmp = "";
|
||||
u32 nbytes = utf8_hdchk(res[ptr]);
|
||||
if (!nbytes) {
|
||||
++ptr;
|
||||
++column;
|
||||
continue;
|
||||
}
|
||||
|
||||
tmp += res[ptr++];
|
||||
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
|
||||
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
|
||||
tmp += res[ptr];
|
||||
}
|
||||
}
|
||||
|
||||
// utf8 character's total length is 1+nbytes
|
||||
if (tmp.length()!=1+nbytes) {
|
||||
++column;
|
||||
std::string utf_info = "0x"+chrhex(tmp[0]);
|
||||
for(u32 i = 1; i<tmp.size(); ++i) {
|
||||
utf_info += " 0x"+chrhex(tmp[i]);
|
||||
}
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid utf-8 <"+utf_info+">"
|
||||
);
|
||||
++invalid_char;
|
||||
}
|
||||
str += tmp;
|
||||
// may have some problems because not all the unicode takes 2 space
|
||||
column += 2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
token lexer::id_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
|
||||
if (res[ptr]<0) { // utf-8
|
||||
str += utf8_gen();
|
||||
} else { // ascii
|
||||
str += res[ptr++];
|
||||
++column;
|
||||
}
|
||||
}
|
||||
tok type = get_type(str);
|
||||
return {
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
(type!=tok::null)? type:tok::id, str
|
||||
};
|
||||
}
|
||||
|
||||
token lexer::num_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
// generate hex number
|
||||
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
|
||||
std::string str = "0x";
|
||||
ptr += 2;
|
||||
while(ptr<res.size() && is_hex(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
// "0x"
|
||||
if (str.length()<3) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
|
||||
std::string str = "0o";
|
||||
ptr += 2;
|
||||
while(ptr<res.size() && is_oct(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
bool erfmt = false;
|
||||
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
|
||||
erfmt = true;
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
if (str.length()==2 || erfmt) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
}
|
||||
// generate dec number
|
||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
if (ptr<res.size() && res[ptr]=='.') {
|
||||
str += res[ptr++];
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxx." is not a correct number
|
||||
if (str.back()=='.') {
|
||||
column += str.length();
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||
}
|
||||
}
|
||||
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
|
||||
str += res[ptr++];
|
||||
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxe(-|+)" is not a correct number
|
||||
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
|
||||
column += str.length();
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid number `"+str+"`"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
|
||||
}
|
||||
}
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
|
||||
}
|
||||
|
||||
token lexer::str_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = "";
|
||||
const char begin = res[ptr];
|
||||
++column;
|
||||
while(++ptr<res.size() && res[ptr]!=begin) {
|
||||
++column;
|
||||
if (res[ptr]=='\n') {
|
||||
column = 0;
|
||||
++line;
|
||||
}
|
||||
if (res[ptr]=='\\' && ptr+1<res.size()) {
|
||||
++column;
|
||||
++ptr;
|
||||
switch(res[ptr]) {
|
||||
case '0': str += '\0'; break;
|
||||
case 'a': str += '\a'; break;
|
||||
case 'b': str += '\b'; break;
|
||||
case 'e': str += '\033'; break;
|
||||
case 't': str += '\t'; break;
|
||||
case 'n': str += '\n'; break;
|
||||
case 'v': str += '\v'; break;
|
||||
case 'f': str += '\f'; break;
|
||||
case 'r': str += '\r'; break;
|
||||
case '?': str += '\?'; break;
|
||||
case '\\':str += '\\'; break;
|
||||
case '\'':str += '\''; break;
|
||||
case '\"':str += '\"'; break;
|
||||
default: str += res[ptr];break;
|
||||
}
|
||||
if (res[ptr]=='\n') {
|
||||
column = 0;
|
||||
++line;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
str += res[ptr];
|
||||
}
|
||||
// check if this string ends with a " or '
|
||||
if (ptr++>=res.size()) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"get EOF when generating string"
|
||||
);
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||
}
|
||||
++column;
|
||||
|
||||
// if is not utf8, 1+utf8_hdchk should be 1
|
||||
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"\'`\' is used for string including one character"
|
||||
);
|
||||
}
|
||||
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
|
||||
}
|
||||
|
||||
token lexer::single_opr() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str(1, res[ptr]);
|
||||
++column;
|
||||
tok type = get_type(str);
|
||||
if (type==tok::null) {
|
||||
err.err("lexer",
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
"invalid operator `"+str+"`"
|
||||
);
|
||||
}
|
||||
++ptr;
|
||||
return {{begin_line, begin_column, line, column, filename}, type, str};
|
||||
}
|
||||
|
||||
token lexer::dots() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = ".";
|
||||
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
|
||||
str += "..";
|
||||
}
|
||||
ptr += str.length();
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||
}
|
||||
|
||||
token lexer::calc_opr() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
// get calculation operator
|
||||
std::string str(1, res[ptr++]);
|
||||
if (ptr<res.size() && res[ptr]=='=') {
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
|
||||
}
|
||||
|
||||
const error& lexer::scan(const std::string& file) {
|
||||
line = 1;
|
||||
column = 0;
|
||||
ptr = 0;
|
||||
toks = {};
|
||||
open(file);
|
||||
|
||||
while(ptr<res.size()) {
|
||||
while(ptr<res.size() && skip(res[ptr])) {
|
||||
// these characters will be ignored, and '\n' will cause ++line
|
||||
++column;
|
||||
if (res[ptr++]=='\n') {
|
||||
++line;
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
if (ptr>=res.size()) {
|
||||
break;
|
||||
}
|
||||
if (is_id(res[ptr])) {
|
||||
toks.push_back(id_gen());
|
||||
} else if (is_dec(res[ptr])) {
|
||||
toks.push_back(num_gen());
|
||||
} else if (is_str(res[ptr])) {
|
||||
toks.push_back(str_gen());
|
||||
} else if (is_single_opr(res[ptr])) {
|
||||
toks.push_back(single_opr());
|
||||
} else if (res[ptr]=='.') {
|
||||
toks.push_back(dots());
|
||||
} else if (is_calc_opr(res[ptr])) {
|
||||
toks.push_back(calc_opr());
|
||||
} else if (res[ptr]=='#') {
|
||||
skip_note();
|
||||
} else {
|
||||
err_char();
|
||||
}
|
||||
if (invalid_char>10) {
|
||||
err.err("lexer", "too many invalid characters, stop");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toks.size()) {
|
||||
// eof token's location is the last token's location
|
||||
toks.push_back({toks.back().loc, tok::eof, "<eof>"});
|
||||
} else {
|
||||
// if token sequence is empty, generate a default location
|
||||
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
||||
}
|
||||
res = "";
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
190
src/nasal_lexer.h
Normal file
@@ -0,0 +1,190 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable:4244)
|
||||
#pragma warning (disable:4267)
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class tok:u32 {
|
||||
null=0, // null token (default token type)
|
||||
num, // number literal
|
||||
str, // string literal
|
||||
id, // identifier
|
||||
tktrue, // keyword true
|
||||
tkfalse, // keyword false
|
||||
use, // keyword use
|
||||
rfor, // loop keyword for
|
||||
forindex, // loop keyword forindex
|
||||
foreach, // loop keyword foreach
|
||||
rwhile, // loop keyword while
|
||||
var, // keyword for definition
|
||||
func, // keyword for definition of function
|
||||
brk, // loop keyword break
|
||||
cont, // loop keyword continue
|
||||
ret, // function keyword return
|
||||
rif, // condition expression keyword if
|
||||
elsif, // condition expression keyword elsif
|
||||
relse, // condition expression keyword else
|
||||
tknil, // nil literal
|
||||
lcurve, // (
|
||||
rcurve, // )
|
||||
lbracket, // [
|
||||
rbracket, // ]
|
||||
lbrace, // {
|
||||
rbrace, // }
|
||||
semi, // ;
|
||||
opand, // operator and
|
||||
opor, // operator or
|
||||
comma, // ,
|
||||
dot, // .
|
||||
ellipsis, // ...
|
||||
quesmark, // ?
|
||||
colon, // :
|
||||
add, // operator +
|
||||
sub, // operator -
|
||||
mult, // operator *
|
||||
div, // operator /
|
||||
floater, // operator ~ and binary operator ~
|
||||
btand, // bitwise operator &
|
||||
btor, // bitwise operator |
|
||||
btxor, // bitwise operator ^
|
||||
opnot, // operator !
|
||||
eq, // operator =
|
||||
addeq, // operator +=
|
||||
subeq, // operator -=
|
||||
multeq, // operator *=
|
||||
diveq, // operator /=
|
||||
lnkeq, // operator ~=
|
||||
btandeq, // operator &=
|
||||
btoreq, // operator |=
|
||||
btxoreq, // operator ^=
|
||||
cmpeq, // operator ==
|
||||
neq, // operator !=
|
||||
less, // operator <
|
||||
leq, // operator <=
|
||||
grt, // operator >
|
||||
geq, // operator >=
|
||||
eof // <eof> end of token list
|
||||
};
|
||||
|
||||
struct token {
|
||||
span loc; // location
|
||||
tok type; // token type
|
||||
std::string str; // content
|
||||
token() = default;
|
||||
token(const token&) = default;
|
||||
};
|
||||
|
||||
class lexer {
|
||||
private:
|
||||
u32 line;
|
||||
u32 column;
|
||||
usize ptr;
|
||||
std::string filename;
|
||||
std::string res;
|
||||
|
||||
error err;
|
||||
u64 invalid_char;
|
||||
std::vector<token> toks;
|
||||
|
||||
const std::unordered_map<std::string, tok> typetbl {
|
||||
{"use" ,tok::use },
|
||||
{"true" ,tok::tktrue },
|
||||
{"false" ,tok::tkfalse },
|
||||
{"for" ,tok::rfor },
|
||||
{"forindex",tok::forindex},
|
||||
{"foreach" ,tok::foreach },
|
||||
{"while" ,tok::rwhile },
|
||||
{"var" ,tok::var },
|
||||
{"func" ,tok::func },
|
||||
{"break" ,tok::brk },
|
||||
{"continue",tok::cont },
|
||||
{"return" ,tok::ret },
|
||||
{"if" ,tok::rif },
|
||||
{"elsif" ,tok::elsif },
|
||||
{"else" ,tok::relse },
|
||||
{"nil" ,tok::tknil },
|
||||
{"(" ,tok::lcurve },
|
||||
{")" ,tok::rcurve },
|
||||
{"[" ,tok::lbracket},
|
||||
{"]" ,tok::rbracket},
|
||||
{"{" ,tok::lbrace },
|
||||
{"}" ,tok::rbrace },
|
||||
{";" ,tok::semi },
|
||||
{"and" ,tok::opand },
|
||||
{"or" ,tok::opor },
|
||||
{"," ,tok::comma },
|
||||
{"." ,tok::dot },
|
||||
{"..." ,tok::ellipsis},
|
||||
{"?" ,tok::quesmark},
|
||||
{":" ,tok::colon },
|
||||
{"+" ,tok::add },
|
||||
{"-" ,tok::sub },
|
||||
{"*" ,tok::mult },
|
||||
{"/" ,tok::div },
|
||||
{"~" ,tok::floater },
|
||||
{"&" ,tok::btand },
|
||||
{"|" ,tok::btor },
|
||||
{"^" ,tok::btxor },
|
||||
{"!" ,tok::opnot },
|
||||
{"=" ,tok::eq },
|
||||
{"+=" ,tok::addeq },
|
||||
{"-=" ,tok::subeq },
|
||||
{"*=" ,tok::multeq },
|
||||
{"/=" ,tok::diveq },
|
||||
{"~=" ,tok::lnkeq },
|
||||
{"&=" ,tok::btandeq },
|
||||
{"|=" ,tok::btoreq },
|
||||
{"^=" ,tok::btxoreq },
|
||||
{"==" ,tok::cmpeq },
|
||||
{"!=" ,tok::neq },
|
||||
{"<" ,tok::less },
|
||||
{"<=" ,tok::leq },
|
||||
{">" ,tok::grt },
|
||||
{">=" ,tok::geq }
|
||||
};
|
||||
|
||||
tok get_type(const std::string&);
|
||||
bool skip(char);
|
||||
bool is_id(char);
|
||||
bool is_hex(char);
|
||||
bool is_oct(char);
|
||||
bool is_dec(char);
|
||||
bool is_str(char);
|
||||
bool is_single_opr(char);
|
||||
bool is_calc_opr(char);
|
||||
|
||||
void skip_note();
|
||||
void err_char();
|
||||
|
||||
void open(const std::string&);
|
||||
std::string utf8_gen();
|
||||
token id_gen();
|
||||
token num_gen();
|
||||
token str_gen();
|
||||
token single_opr();
|
||||
token dots();
|
||||
token calc_opr();
|
||||
public:
|
||||
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {}
|
||||
const error& scan(const std::string&);
|
||||
const std::vector<token>& result() const {return toks;}
|
||||
};
|
||||
|
||||
}
|
||||
237
src/nasal_misc.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool is_windows() {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_linux() {
|
||||
#if defined __linux__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_macos() {
|
||||
#if defined __APPLE__
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_x86() {
|
||||
#if defined(__i386__) || defined(_M_IX86)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_amd64() {
|
||||
#if defined(__amd64__) || defined(_M_X64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_x86_64() {
|
||||
return is_amd64();
|
||||
}
|
||||
|
||||
bool is_arm() {
|
||||
#if defined(__arm__) || defined(_M_ARM)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_aarch64() {
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_ia64() {
|
||||
#if defined(__ia64__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_powerpc() {
|
||||
#if defined(__powerpc__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_superh() {
|
||||
#if defined(__sh__)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
f64 hex2f(const char* str) {
|
||||
f64 ret = 0;
|
||||
for(; *str; ++str) {
|
||||
if ('0'<=*str && *str<='9') {
|
||||
ret = ret*16+(*str-'0');
|
||||
} else if ('a'<=*str && *str<='f') {
|
||||
ret = ret*16+(*str-'a'+10);
|
||||
} else if ('A'<=*str && *str<='F') {
|
||||
ret = ret*16+(*str-'A'+10);
|
||||
} else {
|
||||
return nan("");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
f64 oct2f(const char* str) {
|
||||
f64 ret = 0;
|
||||
while('0'<=*str && *str<'8') {
|
||||
ret = ret*8+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
return nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// we have the same reason not using atof here
|
||||
// just as andy's interpreter does.
|
||||
// it is not platform independent, and may have strange output.
|
||||
// so we write a new function here to convert str to number manually.
|
||||
// but this also makes 0.1+0.2==0.3,
|
||||
// not another result that you may get in other languages.
|
||||
f64 dec2f(const char* str) {
|
||||
f64 ret = 0, num_pow = 0;
|
||||
bool negative = false;
|
||||
while('0'<=*str && *str<='9') {
|
||||
ret = ret*10+(*str++-'0');
|
||||
}
|
||||
if (!*str) {
|
||||
return ret;
|
||||
}
|
||||
if (*str=='.') {
|
||||
if (!*++str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0.1;
|
||||
while('0'<=*str && *str<='9') {
|
||||
ret += num_pow*(*str++-'0');
|
||||
num_pow *= 0.1;
|
||||
}
|
||||
if (!*str) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (*str!='e' && *str!='E') {
|
||||
return nan("");
|
||||
}
|
||||
if (!*++str) {
|
||||
return nan("");
|
||||
}
|
||||
if (*str=='-' || *str=='+') {
|
||||
negative = (*str++=='-');
|
||||
}
|
||||
if (!*str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0;
|
||||
while('0'<=*str && *str<='9') {
|
||||
num_pow = num_pow*10+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
return nan("");
|
||||
}
|
||||
return negative?
|
||||
ret*std::pow(10, 1-num_pow)*0.1:
|
||||
ret*std::pow(10, num_pow-1)*10;
|
||||
}
|
||||
|
||||
f64 str2num(const char* str) {
|
||||
bool negative = false;
|
||||
f64 res = 0;
|
||||
if (*str=='-' || *str=='+') {
|
||||
negative = (*str++=='-');
|
||||
}
|
||||
if (!*str) {
|
||||
return nan("");
|
||||
}
|
||||
if (str[0]=='0' && str[1]=='x') {
|
||||
res = hex2f(str+2);
|
||||
} else if (str[0]=='0' && str[1]=='o') {
|
||||
res = oct2f(str+2);
|
||||
} else {
|
||||
res = dec2f(str);
|
||||
}
|
||||
return negative? -res:res;
|
||||
}
|
||||
|
||||
i32 utf8_hdchk(const char head) {
|
||||
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
|
||||
const auto c = static_cast<u8>(head);
|
||||
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
|
||||
return 1;
|
||||
}
|
||||
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
|
||||
return 2;
|
||||
}
|
||||
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string chrhex(const char c) {
|
||||
const char hextbl[] = "0123456789abcdef";
|
||||
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
|
||||
}
|
||||
|
||||
std::string rawstr(const std::string& str, const usize maxlen) {
|
||||
std::string ret("");
|
||||
for(auto i : str) {
|
||||
// windows doesn't output unicode normally, so we output the hex
|
||||
if (is_windows() && i<=0) {
|
||||
ret += "\\x"+chrhex(i);
|
||||
continue;
|
||||
}
|
||||
switch(i) {
|
||||
case '\0': ret += "\\0"; break;
|
||||
case '\a': ret += "\\a"; break;
|
||||
case '\b': ret += "\\b"; break;
|
||||
case '\t': ret += "\\t"; break;
|
||||
case '\n': ret += "\\n"; break;
|
||||
case '\v': ret += "\\v"; break;
|
||||
case '\f': ret += "\\f"; break;
|
||||
case '\r': ret += "\\r"; break;
|
||||
case '\033':ret += "\\e"; break;
|
||||
case '\"': ret += "\\\""; break;
|
||||
case '\'': ret += "\\\'"; break;
|
||||
case '\\': ret += "\\\\"; break;
|
||||
default: ret += i; break;
|
||||
}
|
||||
}
|
||||
if (maxlen && ret.length()>maxlen) {
|
||||
ret = ret.substr(0, maxlen)+"...";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
123
src/nasal_opcode.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "nasal_opcode.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const char* opname[] = {
|
||||
"exit ", "repl ", "intl ", "loadg ",
|
||||
"loadl ", "loadu ", "pnum ", "pnil ",
|
||||
"pstr ", "newv ", "newh ", "newf ",
|
||||
"happ ", "para ", "def ", "dyn ",
|
||||
"lnot ", "usub ", "bitnot", "bitor ",
|
||||
"bitxor", "bitand", "add ", "sub ",
|
||||
"mult ", "div ", "lnk ", "addc ",
|
||||
"subc ", "multc ", "divc ", "lnkc ",
|
||||
"addeq ", "subeq ", "muleq ", "diveq ",
|
||||
"lnkeq ", "bandeq", "boreq ", "bxoreq",
|
||||
"addeqc", "subeqc", "muleqc", "diveqc",
|
||||
"lnkeqc", "addecp", "subecp", "mulecp",
|
||||
"divecp", "lnkecp", "meq ", "eq ",
|
||||
"neq ", "less ", "leq ", "grt ",
|
||||
"geq ", "lessc ", "leqc ", "grtc ",
|
||||
"geqc ", "pop ", "jmp ", "jt ",
|
||||
"jf ", "cnt ", "findx ", "feach ",
|
||||
"callg ", "calll ", "upval ", "callv ",
|
||||
"callvi", "callh ", "callfv", "callfh",
|
||||
"callb ", "slcbeg", "slcend", "slice ",
|
||||
"slice2", "mcallg", "mcalll", "mupval",
|
||||
"mcallv", "mcallh", "ret "
|
||||
};
|
||||
|
||||
void codestream::set(
|
||||
const f64* number_list,
|
||||
const std::string* string_list,
|
||||
const nasal_builtin_table* native_table,
|
||||
const std::string* file_list) {
|
||||
const_number = number_list;
|
||||
const_string = string_list;
|
||||
natives = native_table;
|
||||
files = file_list;
|
||||
}
|
||||
|
||||
void codestream::dump(std::ostream& out) const {
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::hex;
|
||||
using std::dec;
|
||||
auto op = code.op;
|
||||
auto num = code.num;
|
||||
out << hex << "0x"
|
||||
<< setw(6) << setfill('0') << index << " "
|
||||
<< setw(2) << setfill('0') << static_cast<u32>(op) << " "
|
||||
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
|
||||
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " "
|
||||
<< setw(2) << setfill('0') << (num&0xff) << " "
|
||||
<<opname[op]<<" "<<dec;
|
||||
switch(op) {
|
||||
case op_addeq: case op_subeq:
|
||||
case op_muleq: case op_diveq:
|
||||
case op_lnkeq: case op_meq:
|
||||
case op_btandeq: case op_btoreq:
|
||||
case op_btxoreq:
|
||||
out << hex << "0x" << num << dec << " sp-" << num; break;
|
||||
case op_addeqc: case op_subeqc:
|
||||
case op_muleqc:case op_diveqc:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << const_number[num] << ")"; break;
|
||||
case op_lnkeqc:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(const_string[num], 16) << ")"; break;
|
||||
case op_addecp: case op_subecp:
|
||||
case op_mulecp: case op_divecp:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << const_number[num] << ") sp-1"; break;
|
||||
case op_lnkecp:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(const_string[num], 16) << ") sp-1"; break;
|
||||
case op_addc: case op_subc:
|
||||
case op_mulc: case op_divc:
|
||||
case op_lessc: case op_leqc:
|
||||
case op_grtc: case op_geqc:
|
||||
case op_pnum:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << const_number[num] << ")"; break;
|
||||
case op_callvi: case op_newv:
|
||||
case op_callfv: case op_repl:
|
||||
case op_intl: case op_findex:
|
||||
case op_feach: case op_newf:
|
||||
case op_jmp: case op_jt:
|
||||
case op_jf: case op_callg:
|
||||
case op_mcallg: case op_loadg:
|
||||
case op_calll: case op_mcalll:
|
||||
case op_loadl:
|
||||
out << hex << "0x" << num << dec; break;
|
||||
case op_callb:
|
||||
out << hex << "0x" << num << " <" << natives[num].name
|
||||
<< "@0x" << reinterpret_cast<u64>(natives[num].func)
|
||||
<< dec << ">"; break;
|
||||
case op_upval: case op_mupval:
|
||||
case op_loadu:
|
||||
out << hex << "0x" << ((num>>16)&0xffff)
|
||||
<< "[0x" << (num&0xffff) << "]" << dec; break;
|
||||
case op_happ: case op_pstr:
|
||||
case op_lnkc: case op_callh:
|
||||
case op_mcallh: case op_para:
|
||||
case op_deft: case op_dyn:
|
||||
out << hex << "0x" << num << dec
|
||||
<< " (" << rawstr(const_string[num], 16) << ")"; break;
|
||||
default:
|
||||
if (files) {
|
||||
out << hex << "0x" << num << dec;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (files) {
|
||||
out << "(" << files[code.fidx] << ":" << code.line << ")";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
|
||||
ins.dump(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
133
src/nasal_opcode.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_builtin.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum op_code_type:u8 {
|
||||
op_exit, // stop the virtual machine
|
||||
op_repl, // in repl mode: print value on stack top
|
||||
op_intl, // local scope size
|
||||
op_loadg, // load global value
|
||||
op_loadl, // load local value
|
||||
op_loadu, // load upvalue
|
||||
op_pnum, // push constant number to the stack
|
||||
op_pnil, // push constant nil to the stack
|
||||
op_pstr, // push constant std::string to the stack
|
||||
op_newv, // push new vector with initial values from stack
|
||||
op_newh, // push new hash to the stack
|
||||
op_newf, // push new function to the stack
|
||||
op_happ, // hash append
|
||||
op_para, // normal parameter
|
||||
op_deft, // default parameter
|
||||
op_dyn, // dynamic parameter
|
||||
op_lnot, // ! logical negation
|
||||
op_usub, // - negation
|
||||
op_bnot, // ~ bitwise not static_cast<i32>
|
||||
op_btor, // | bitwise or
|
||||
op_btxor, // ^ bitwise xor
|
||||
op_btand, // & bitwise and
|
||||
op_add, // +
|
||||
op_sub, // -
|
||||
op_mul, // *
|
||||
op_div, // /
|
||||
op_lnk, // ~
|
||||
op_addc, // + const
|
||||
op_subc, // - const
|
||||
op_mulc, // * const
|
||||
op_divc, // / const
|
||||
op_lnkc, // ~ const
|
||||
op_addeq, // += maybe pop stack top
|
||||
op_subeq, // -= maybe pop stack top
|
||||
op_muleq, // *= maybe pop stack top
|
||||
op_diveq, // /= maybe pop stack top
|
||||
op_lnkeq, // ~= maybe pop stack top
|
||||
op_btandeq,// &= maybe pop stack top
|
||||
op_btoreq, // |= maybe pop stack top
|
||||
op_btxoreq,// ^= maybe pop stack top
|
||||
op_addeqc, // += const don't pop stack top
|
||||
op_subeqc, // -= const don't pop stack top
|
||||
op_muleqc, // *= const don't pop stack top
|
||||
op_diveqc, // /= const don't pop stack top
|
||||
op_lnkeqc, // ~= const don't pop stack top
|
||||
op_addecp, // += const and pop stack top
|
||||
op_subecp, // -= const and pop stack top
|
||||
op_mulecp, // *= const and pop stack top
|
||||
op_divecp, // /= const and pop stack top
|
||||
op_lnkecp, // ~= concat const std::string and pop stack top
|
||||
op_meq, // = maybe pop stack top
|
||||
op_eq, // == compare operator
|
||||
op_neq, // != compare operator
|
||||
op_less, // < compare operator
|
||||
op_leq, // <= compare operator
|
||||
op_grt, // > compare operator
|
||||
op_geq, // >= compare operator
|
||||
op_lessc, // < const compare operator
|
||||
op_leqc, // <= const compare operator
|
||||
op_grtc, // > const compare operator
|
||||
op_geqc, // >= const compare operator
|
||||
op_pop, // pop a value out of stack top
|
||||
op_jmp, // jump absolute address with no condition
|
||||
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
|
||||
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
|
||||
op_cnt, // add counter for forindex/foreach
|
||||
op_findex, // index counter on the top of forindex_stack plus 1
|
||||
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
|
||||
op_callg, // get value in global scope
|
||||
op_calll, // get value in local scope
|
||||
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
|
||||
op_callv, // call vec[index]
|
||||
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
|
||||
op_callh, // call hash.label
|
||||
op_callfv, // call function(vector as parameters)
|
||||
op_callfh, // call function(hash as parameters)
|
||||
op_callb, // call native functions
|
||||
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
|
||||
op_slcend, // end of slice
|
||||
op_slc, // slice like vec[1]
|
||||
op_slc2, // slice like vec[nil:10]
|
||||
op_mcallg, // get memory space of value in global scope
|
||||
op_mcalll, // get memory space of value in local scope
|
||||
op_mupval, // get memory space of value in closure
|
||||
op_mcallv, // get memory space of vec[index]
|
||||
op_mcallh, // get memory space of hash.label
|
||||
op_ret // return
|
||||
};
|
||||
|
||||
struct opcode {
|
||||
u8 op; // opcode
|
||||
u16 fidx; // source code file index
|
||||
u32 num; // immediate num
|
||||
u32 line; // location line of source code
|
||||
opcode() = default;
|
||||
opcode(const opcode&) = default;
|
||||
opcode& operator=(const opcode&) = default;
|
||||
};
|
||||
|
||||
class codestream {
|
||||
private:
|
||||
opcode code;
|
||||
const u32 index;
|
||||
inline static const f64* const_number = nullptr;
|
||||
inline static const std::string* const_string = nullptr;
|
||||
inline static const nasal_builtin_table* natives = nullptr;
|
||||
inline static const std::string* files = nullptr;
|
||||
|
||||
public:
|
||||
codestream(const opcode& c, const u32 i): code(c), index(i) {}
|
||||
static void set(
|
||||
const f64*, const std::string*,
|
||||
const nasal_builtin_table*,
|
||||
const std::string* file_list = nullptr
|
||||
);
|
||||
void dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const codestream&);
|
||||
|
||||
extern const char* opname[];
|
||||
|
||||
}
|
||||
1086
src/nasal_parse.cpp
Normal file
160
src/nasal_parse.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class parse {
|
||||
|
||||
#define thisspan (toks[ptr].loc)
|
||||
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
|
||||
|
||||
private:
|
||||
u32 ptr;
|
||||
u32 in_func; // count function block
|
||||
u32 in_loop; // count loop block
|
||||
const token* toks;
|
||||
code_block* root;
|
||||
error err;
|
||||
|
||||
private:
|
||||
const std::unordered_map<tok, std::string> tokname {
|
||||
{tok::use ,"use" },
|
||||
{tok::rfor ,"for" },
|
||||
{tok::forindex,"forindex"},
|
||||
{tok::foreach ,"foreach" },
|
||||
{tok::rwhile ,"while" },
|
||||
{tok::var ,"var" },
|
||||
{tok::func ,"func" },
|
||||
{tok::brk ,"break" },
|
||||
{tok::cont ,"continue"},
|
||||
{tok::ret ,"return" },
|
||||
{tok::rif ,"if" },
|
||||
{tok::elsif ,"elsif" },
|
||||
{tok::relse ,"else" },
|
||||
{tok::tknil ,"nil" },
|
||||
{tok::lcurve ,"(" },
|
||||
{tok::rcurve ,")" },
|
||||
{tok::lbracket,"[" },
|
||||
{tok::rbracket,"]" },
|
||||
{tok::lbrace ,"{" },
|
||||
{tok::rbrace ,"}" },
|
||||
{tok::semi ,";" },
|
||||
{tok::opand ,"and" },
|
||||
{tok::opor ,"or" },
|
||||
{tok::comma ,"," },
|
||||
{tok::dot ,"." },
|
||||
{tok::ellipsis,"..." },
|
||||
{tok::quesmark,"?" },
|
||||
{tok::colon ,":" },
|
||||
{tok::add ,"+" },
|
||||
{tok::sub ,"-" },
|
||||
{tok::mult ,"*" },
|
||||
{tok::div ,"/" },
|
||||
{tok::floater ,"~" },
|
||||
{tok::btand ,"&" },
|
||||
{tok::btor ,"|" },
|
||||
{tok::btxor ,"^" },
|
||||
{tok::opnot ,"!" },
|
||||
{tok::eq ,"=" },
|
||||
{tok::addeq ,"+=" },
|
||||
{tok::subeq ,"-=" },
|
||||
{tok::multeq ,"*=" },
|
||||
{tok::diveq ,"/=" },
|
||||
{tok::lnkeq ,"~=" },
|
||||
{tok::btandeq ,"&=" },
|
||||
{tok::btoreq ,"|=" },
|
||||
{tok::btxoreq ,"^=" },
|
||||
{tok::cmpeq ,"==" },
|
||||
{tok::neq ,"!=" },
|
||||
{tok::less ,"<" },
|
||||
{tok::leq ,"<=" },
|
||||
{tok::grt ,">" },
|
||||
{tok::geq ,">=" }
|
||||
};
|
||||
|
||||
private:
|
||||
void die(const span&,std::string);
|
||||
void next();
|
||||
void match(tok, const char* info=nullptr);
|
||||
bool lookahead(tok);
|
||||
bool is_call(tok);
|
||||
bool check_comma(const tok*);
|
||||
bool check_tuple();
|
||||
bool check_func_end(expr*);
|
||||
bool check_in_curve_multi_definition();
|
||||
bool check_special_call();
|
||||
bool need_semi_check(expr*);
|
||||
void update_location(expr*);
|
||||
|
||||
private:
|
||||
use_stmt* use_stmt_gen();
|
||||
null_expr* null();
|
||||
nil_expr* nil();
|
||||
number_literal* num();
|
||||
string_literal* str();
|
||||
identifier* id();
|
||||
bool_literal* bools();
|
||||
vector_expr* vec();
|
||||
hash_expr* hash();
|
||||
hash_pair* pair();
|
||||
function* func();
|
||||
void params(function*);
|
||||
expr* lcurve_expr();
|
||||
expr* expression();
|
||||
code_block* expression_block();
|
||||
expr* calc();
|
||||
expr* bitwise_or();
|
||||
expr* bitwise_xor();
|
||||
expr* bitwise_and();
|
||||
expr* or_expr();
|
||||
expr* and_expr();
|
||||
expr* cmp_expr();
|
||||
expr* additive_expr();
|
||||
expr* multive_expr();
|
||||
unary_operator* unary();
|
||||
expr* scalar();
|
||||
call* call_scalar();
|
||||
call_hash* callh();
|
||||
call_vector* callv();
|
||||
call_function* callf();
|
||||
slice_vector* subvec();
|
||||
expr* definition();
|
||||
multi_identifier* incurve_def();
|
||||
multi_identifier* outcurve_def();
|
||||
multi_identifier* multi_id();
|
||||
tuple_expr* multi_scalar();
|
||||
multi_assign* multi_assignment();
|
||||
expr* loop();
|
||||
while_expr* while_loop();
|
||||
for_expr* for_loop();
|
||||
forei_expr* forei_loop();
|
||||
iter_expr* iter_gen();
|
||||
condition_expr* cond();
|
||||
continue_expr* continue_expression();
|
||||
break_expr* break_expression();
|
||||
return_expr* return_expression();
|
||||
|
||||
public:
|
||||
code_block* tree() {return root;}
|
||||
|
||||
// swap root pointer with another pointer(maybe nullptr)
|
||||
code_block* swap(code_block* another) {
|
||||
auto res = root;
|
||||
root = another;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
parse(): ptr(0), in_func(0), in_loop(0), toks(nullptr), root(nullptr) {}
|
||||
~parse() {delete root;}
|
||||
const error& compile(const lexer&);
|
||||
static void easter_egg();
|
||||
};
|
||||
|
||||
}
|
||||
355
src/nasal_type.cpp
Normal file
@@ -0,0 +1,355 @@
|
||||
#include "nasal_type.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var nas_vec::get_value(const i32 index) {
|
||||
i32 size = elems.size();
|
||||
if (index<-size || index>=size) {
|
||||
return var::none();
|
||||
}
|
||||
return elems[index>=0? index:index+size];
|
||||
}
|
||||
|
||||
var* nas_vec::get_memory(const i32 index) {
|
||||
i32 size = elems.size();
|
||||
if (index<-size || index>=size) {
|
||||
return nullptr;
|
||||
}
|
||||
return &elems[index>=0? index:index+size];
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||
if (!vec.elems.size() || vec.printed) {
|
||||
out << (vec.elems.size()? "[..]":"[]");
|
||||
return out;
|
||||
}
|
||||
vec.printed = true;
|
||||
usize iter = 0, size = vec.elems.size();
|
||||
out << "[";
|
||||
for(auto& i:vec.elems) {
|
||||
out << i << ",]"[(++iter)==size];
|
||||
}
|
||||
vec.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
var nas_hash::get_value(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
return var::none();
|
||||
}
|
||||
var ret = var::none();
|
||||
var val = elems.at("parents");
|
||||
if (val.type!=vm_vec) {
|
||||
return ret;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
if (i.type==vm_hash) {
|
||||
ret = i.hash().get_value(key);
|
||||
}
|
||||
if (ret.type!=vm_none) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
var* nas_hash::get_memory(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return &elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
return nullptr;
|
||||
}
|
||||
var* addr = nullptr;
|
||||
var val = elems.at("parents");
|
||||
if (val.type!=vm_vec) {
|
||||
return addr;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
if (i.type==vm_hash) {
|
||||
addr = i.hash().get_memory(key);
|
||||
}
|
||||
if (addr) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
||||
if (!hash.elems.size() || hash.printed) {
|
||||
out << (hash.elems.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
hash.printed = true;
|
||||
usize iter = 0, size = hash.elems.size();
|
||||
out << "{";
|
||||
for(auto& i : hash.elems) {
|
||||
out << i.first << ":" << i.second << ",}"[(++iter)==size];
|
||||
}
|
||||
hash.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
void nas_func::clear() {
|
||||
dynamic_parameter_index = -1;
|
||||
local.clear();
|
||||
upval.clear();
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
void nas_ghost::set(
|
||||
const std::string& ghost_type_name,
|
||||
destructor destructor_pointer,
|
||||
void* ghost_pointer) {
|
||||
type_name = ghost_type_name;
|
||||
destructor_function = destructor_pointer;
|
||||
pointer = ghost_pointer;
|
||||
}
|
||||
|
||||
void nas_ghost::clear() {
|
||||
// do nothing if pointer is null
|
||||
if (!pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do clear pointer if destructor function pointer is null
|
||||
if (!destructor_function) {
|
||||
type_name = "";
|
||||
pointer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// do destruction
|
||||
destructor_function(pointer);
|
||||
type_name = "";
|
||||
pointer = nullptr;
|
||||
destructor_function = nullptr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
||||
out << "<object " << ghost.get_ghost_name();
|
||||
out << " at 0x" << std::hex;
|
||||
out << reinterpret_cast<u64>(ghost.pointer) << std::dec << ">";
|
||||
return out;
|
||||
}
|
||||
|
||||
void nas_co::clear() {
|
||||
if (!ctx.stack) {
|
||||
return;
|
||||
}
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = var::nil();
|
||||
}
|
||||
|
||||
ctx.pc = 0;
|
||||
ctx.localr = nullptr;
|
||||
ctx.memr = nullptr;
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||
ctx.top = ctx.stack;
|
||||
ctx.funcr = var::nil();
|
||||
ctx.upvalr = var::nil();
|
||||
|
||||
status = status::suspended;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
|
||||
out << "<coroutine at 0x" << std::hex;
|
||||
out << reinterpret_cast<u64>(&co) << std::dec << ">";
|
||||
return out;
|
||||
}
|
||||
|
||||
var nas_map::get_value(const std::string& key) {
|
||||
if (mapper.count(key)) {
|
||||
return *mapper.at(key);
|
||||
}
|
||||
return var::none();
|
||||
}
|
||||
|
||||
var* nas_map::get_memory(const std::string& key) {
|
||||
if (mapper.count(key)) {
|
||||
return mapper.at(key);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
|
||||
if (!mp.mapper.size() || mp.printed) {
|
||||
out << (mp.mapper.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
mp.printed = true;
|
||||
usize iter = 0, size = mp.mapper.size();
|
||||
out << "{";
|
||||
for(auto& i : mp.mapper) {
|
||||
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
|
||||
}
|
||||
mp.printed = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
nas_val::nas_val(u8 val_type) {
|
||||
mark = gc_status::collected;
|
||||
type = val_type;
|
||||
unmutable = 0;
|
||||
switch(val_type) {
|
||||
case vm_str: ptr.str = new std::string; break;
|
||||
case vm_vec: ptr.vec = new nas_vec; break;
|
||||
case vm_hash: ptr.hash = new nas_hash; break;
|
||||
case vm_func: ptr.func = new nas_func; break;
|
||||
case vm_upval: ptr.upval = new nas_upval; break;
|
||||
case vm_obj: ptr.obj = new nas_ghost; break;
|
||||
case vm_co: ptr.co = new nas_co; break;
|
||||
case vm_map: ptr.map = new nas_map; break;
|
||||
}
|
||||
}
|
||||
|
||||
nas_val::~nas_val() {
|
||||
switch(type) {
|
||||
case vm_str: delete ptr.str; break;
|
||||
case vm_vec: delete ptr.vec; break;
|
||||
case vm_hash: delete ptr.hash; break;
|
||||
case vm_func: delete ptr.func; break;
|
||||
case vm_upval:delete ptr.upval; break;
|
||||
case vm_obj: delete ptr.obj; break;
|
||||
case vm_co: delete ptr.co; break;
|
||||
case vm_map: delete ptr.map; break;
|
||||
}
|
||||
type=vm_nil;
|
||||
}
|
||||
|
||||
void nas_val::clear() {
|
||||
switch(type) {
|
||||
case vm_str: ptr.str->clear(); break;
|
||||
case vm_vec: ptr.vec->elems.clear(); break;
|
||||
case vm_hash: ptr.hash->elems.clear(); break;
|
||||
case vm_func: ptr.func->clear(); break;
|
||||
case vm_upval:ptr.upval->clear(); break;
|
||||
case vm_obj: ptr.obj->clear(); break;
|
||||
case vm_co: ptr.co->clear(); break;
|
||||
case vm_map: ptr.map->clear(); break;
|
||||
}
|
||||
}
|
||||
|
||||
f64 var::to_num() {
|
||||
return type!=vm_str? val.num:str2num(str().c_str());
|
||||
}
|
||||
|
||||
std::string var::to_str() {
|
||||
if (type==vm_str) {
|
||||
return str();
|
||||
} else if (type==vm_num) {
|
||||
std::string tmp = std::to_string(num());
|
||||
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
||||
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
||||
return tmp;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, var& ref) {
|
||||
switch(ref.type) {
|
||||
case vm_none: out << "undefined"; break;
|
||||
case vm_nil: out << "nil"; break;
|
||||
case vm_num: out << ref.val.num; break;
|
||||
case vm_str: out << ref.str(); break;
|
||||
case vm_vec: out << ref.vec(); break;
|
||||
case vm_hash: out << ref.hash(); break;
|
||||
case vm_func: out << "func(..) {..}"; break;
|
||||
case vm_obj: out << ref.ghost(); break;
|
||||
case vm_co: out << ref.co(); break;
|
||||
case vm_map: out << ref.map(); break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool var::object_check(const std::string& name) {
|
||||
return type==vm_obj && ghost().type_name==name && ghost().pointer;
|
||||
}
|
||||
|
||||
var var::none() {
|
||||
return {vm_none, static_cast<u32>(0)};
|
||||
}
|
||||
|
||||
var var::nil() {
|
||||
return {vm_nil, static_cast<u32>(0)};
|
||||
}
|
||||
|
||||
var var::ret(u32 pc) {
|
||||
return {vm_ret, pc};
|
||||
}
|
||||
|
||||
var var::cnt(i64 n) {
|
||||
return {vm_cnt, n};
|
||||
}
|
||||
|
||||
var var::num(f64 n) {
|
||||
return {vm_num, n};
|
||||
}
|
||||
|
||||
var var::gcobj(nas_val* p) {
|
||||
return {p->type, p};
|
||||
}
|
||||
|
||||
var var::addr(var* p) {
|
||||
return {vm_addr, p};
|
||||
}
|
||||
|
||||
var* var::addr() {
|
||||
return val.addr;
|
||||
}
|
||||
|
||||
u32 var::ret() const {
|
||||
return val.ret;
|
||||
}
|
||||
|
||||
i64& var::cnt() {
|
||||
return val.cnt;
|
||||
}
|
||||
|
||||
f64 var::num() const {
|
||||
return val.num;
|
||||
}
|
||||
|
||||
std::string& var::str() {
|
||||
return *val.gcobj->ptr.str;
|
||||
}
|
||||
|
||||
nas_vec& var::vec() {
|
||||
return *val.gcobj->ptr.vec;
|
||||
}
|
||||
|
||||
nas_hash& var::hash() {
|
||||
return *val.gcobj->ptr.hash;
|
||||
}
|
||||
|
||||
nas_func& var::func() {
|
||||
return *val.gcobj->ptr.func;
|
||||
}
|
||||
|
||||
nas_upval& var::upval() {
|
||||
return *val.gcobj->ptr.upval;
|
||||
}
|
||||
|
||||
nas_ghost& var::ghost() {
|
||||
return *val.gcobj->ptr.obj;
|
||||
}
|
||||
|
||||
nas_co& var::co() {
|
||||
return *val.gcobj->ptr.co;
|
||||
}
|
||||
|
||||
nas_map& var::map() {
|
||||
return *val.gcobj->ptr.map;
|
||||
}
|
||||
|
||||
var nas_err(const std::string& error_function_name, const std::string& info) {
|
||||
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
|
||||
return var::none();
|
||||
}
|
||||
|
||||
}
|
||||
269
src/nasal_type.h
Normal file
@@ -0,0 +1,269 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum vm_type:u8 {
|
||||
/* none-gc object */
|
||||
vm_none = 0, // error type
|
||||
vm_cnt, // counter for forindex/foreach loop
|
||||
vm_addr, // var* address
|
||||
vm_ret, // return addres(program counter)
|
||||
vm_nil, // nil
|
||||
vm_num, // number
|
||||
/* gc object */
|
||||
vm_str, // string
|
||||
vm_vec, // vector
|
||||
vm_hash, // hashmap(dict)
|
||||
vm_func, // function(lambda)
|
||||
vm_upval, // upvalue
|
||||
vm_obj, // ghost type
|
||||
vm_co, // coroutine
|
||||
vm_map, // for globals and namespaces
|
||||
/* mark type range */
|
||||
vm_type_size_max
|
||||
};
|
||||
|
||||
// size of gc object type
|
||||
const u32 gc_type_size = vm_type_size_max-vm_str;
|
||||
|
||||
// basic types
|
||||
struct nas_vec; // vector
|
||||
struct nas_hash; // hashmap(dict)
|
||||
struct nas_func; // function(lambda)
|
||||
struct nas_upval; // upvalue
|
||||
struct nas_ghost; // objects
|
||||
struct nas_co; // coroutine
|
||||
struct nas_map; // mapper
|
||||
|
||||
// union type
|
||||
struct nas_val; // nas_val includes gc-managed types
|
||||
|
||||
struct var {
|
||||
public:
|
||||
u8 type = vm_none;
|
||||
union {
|
||||
u32 ret;
|
||||
i64 cnt;
|
||||
f64 num;
|
||||
var* addr;
|
||||
nas_val* gcobj;
|
||||
} val;
|
||||
|
||||
private:
|
||||
var(u8 t, u32 pc) {type = t; val.ret = pc;}
|
||||
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
|
||||
var(u8 t, f64 n) {type = t; val.num = n;}
|
||||
var(u8 t, var* p) {type = t; val.addr = p;}
|
||||
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
|
||||
|
||||
public:
|
||||
var() = default;
|
||||
var(const var&) = default;
|
||||
bool operator==(const var& nr) const {
|
||||
return type==nr.type && val.gcobj==nr.val.gcobj;
|
||||
}
|
||||
bool operator!=(const var& nr) const {
|
||||
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
||||
}
|
||||
|
||||
// number and string can be translated to each other
|
||||
f64 to_num();
|
||||
std::string to_str();
|
||||
bool object_check(const std::string&);
|
||||
|
||||
// create new var object
|
||||
static var none();
|
||||
static var nil();
|
||||
static var ret(u32);
|
||||
static var cnt(i64);
|
||||
static var num(f64);
|
||||
static var gcobj(nas_val*);
|
||||
static var addr(var*);
|
||||
|
||||
// get value
|
||||
var* addr();
|
||||
u32 ret() const;
|
||||
i64& cnt();
|
||||
f64 num() const;
|
||||
std::string& str();
|
||||
nas_vec& vec();
|
||||
nas_hash& hash();
|
||||
nas_func& func();
|
||||
nas_upval& upval();
|
||||
nas_ghost& ghost();
|
||||
nas_co& co();
|
||||
nas_map& map();
|
||||
};
|
||||
|
||||
struct nas_vec {
|
||||
std::vector<var> elems;
|
||||
|
||||
// mark if this is printed, avoid stack overflow
|
||||
bool printed = false;
|
||||
|
||||
usize size() const {return elems.size();}
|
||||
var get_value(const i32);
|
||||
var* get_memory(const i32);
|
||||
};
|
||||
|
||||
struct nas_hash {
|
||||
std::unordered_map<std::string, var> elems;
|
||||
|
||||
// mark if this is printed, avoid stack overflow
|
||||
bool printed = false;
|
||||
|
||||
usize size() const {return elems.size();}
|
||||
var get_value(const std::string&);
|
||||
var* get_memory(const std::string&);
|
||||
};
|
||||
|
||||
struct nas_func {
|
||||
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
|
||||
u32 entry; // pc will set to entry-1 to call this function
|
||||
u32 parameter_size; // used to load default parameters to a new function
|
||||
u32 local_size; // used to expand memory space for local values on stack
|
||||
std::vector<var> local; // local scope with default value(var)
|
||||
std::vector<var> upval; // closure
|
||||
|
||||
// parameter table, u32 begins from 1
|
||||
std::unordered_map<std::string, u32> keys;
|
||||
|
||||
nas_func():
|
||||
dynamic_parameter_index(-1), entry(0),
|
||||
parameter_size(0), local_size(0) {}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nas_upval {
|
||||
public:
|
||||
/* on stack, use these variables */
|
||||
bool on_stack;
|
||||
u32 size;
|
||||
var* stack_frame_offset;
|
||||
|
||||
/* not on stack, use this */
|
||||
std::vector<var> elems;
|
||||
|
||||
public:
|
||||
nas_upval(): on_stack(true), size(0), stack_frame_offset(nullptr) {}
|
||||
|
||||
var& operator[](usize n) {
|
||||
return on_stack? stack_frame_offset[n]:elems[n];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
on_stack = true;
|
||||
elems.clear();
|
||||
size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct nas_ghost {
|
||||
private:
|
||||
using destructor = void (*)(void*);
|
||||
|
||||
public:
|
||||
std::string type_name;
|
||||
destructor destructor_function;
|
||||
void* pointer;
|
||||
|
||||
public:
|
||||
nas_ghost():
|
||||
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
|
||||
~nas_ghost() {clear();}
|
||||
void set(const std::string&, destructor, void*);
|
||||
void clear();
|
||||
|
||||
public:
|
||||
const auto& get_ghost_name() const {return type_name;}
|
||||
};
|
||||
|
||||
struct context {
|
||||
u32 pc = 0;
|
||||
var* localr = nullptr;
|
||||
var* memr = nullptr;
|
||||
var funcr = var::nil();
|
||||
var upvalr = var::nil();
|
||||
var* canary = nullptr;
|
||||
var* stack = nullptr;
|
||||
var* top = nullptr;
|
||||
};
|
||||
|
||||
struct nas_co {
|
||||
enum class status:u32 {
|
||||
suspended,
|
||||
running,
|
||||
dead
|
||||
};
|
||||
|
||||
context ctx;
|
||||
status status;
|
||||
|
||||
nas_co() {
|
||||
ctx.stack = new var[STACK_DEPTH];
|
||||
clear();
|
||||
}
|
||||
~nas_co() {
|
||||
delete[] ctx.stack;
|
||||
}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nas_map {
|
||||
bool printed = false;
|
||||
std::unordered_map<std::string, var*> mapper;
|
||||
|
||||
void clear() {
|
||||
mapper.clear();
|
||||
}
|
||||
|
||||
var get_value(const std::string&);
|
||||
var* get_memory(const std::string&);
|
||||
};
|
||||
|
||||
struct nas_val {
|
||||
enum class gc_status:u8 {
|
||||
uncollected = 0,
|
||||
collected,
|
||||
found
|
||||
};
|
||||
|
||||
gc_status mark;
|
||||
u8 type; // value type
|
||||
u8 unmutable; // used to mark if a string is unmutable
|
||||
union {
|
||||
std::string* str;
|
||||
nas_vec* vec;
|
||||
nas_hash* hash;
|
||||
nas_func* func;
|
||||
nas_upval* upval;
|
||||
nas_ghost* obj;
|
||||
nas_co* co;
|
||||
nas_map* map;
|
||||
} ptr;
|
||||
|
||||
nas_val(u8);
|
||||
~nas_val();
|
||||
void clear();
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||
std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||
std::ostream& operator<<(std::ostream&, nas_map&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_co&);
|
||||
std::ostream& operator<<(std::ostream&, var&);
|
||||
|
||||
const var zero = var::num(0);
|
||||
const var one = var::num(1);
|
||||
const var nil = var::nil();
|
||||
|
||||
// use to print error log and return error value
|
||||
var nas_err(const std::string&, const std::string&);
|
||||
|
||||
}
|
||||
625
src/nasal_vm.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void vm::init(
|
||||
const std::vector<std::string>& strs,
|
||||
const std::vector<f64>& nums,
|
||||
const std::vector<nasal_builtin_table>& natives,
|
||||
const std::vector<opcode>& code,
|
||||
const std::unordered_map<std::string, i32>& global_symbol,
|
||||
const std::vector<std::string>& filenames,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
const_number = nums.data();
|
||||
const_string = strs.data();
|
||||
bytecode = code.data();
|
||||
files = filenames.data();
|
||||
global_size = global_symbol.size();
|
||||
|
||||
/* set native functions */
|
||||
native_function = natives;
|
||||
|
||||
/* set context and global */
|
||||
if (!is_repl_mode || first_exec_flag) {
|
||||
context_and_global_init();
|
||||
first_exec_flag = false;
|
||||
}
|
||||
|
||||
/* init gc */
|
||||
ngc.set(&ctx, global, global_size);
|
||||
ngc.init(strs, argv);
|
||||
|
||||
/* init vm globals */
|
||||
auto map_instance = ngc.alloc(vm_map);
|
||||
global[global_symbol.at("globals")] = map_instance;
|
||||
for(const auto& i : global_symbol) {
|
||||
map_instance.map().mapper[i.first] = global+i.second;
|
||||
}
|
||||
|
||||
/* init vm arg */
|
||||
auto arg_instance = ngc.alloc(vm_vec);
|
||||
global[global_symbol.at("arg")] = arg_instance;
|
||||
arg_instance.vec().elems = ngc.env_argv;
|
||||
}
|
||||
|
||||
void vm::context_and_global_init() {
|
||||
/* set canary and program counter */
|
||||
ctx.pc = 0;
|
||||
ctx.localr = nullptr;
|
||||
ctx.memr = nullptr;
|
||||
ctx.funcr = nil;
|
||||
ctx.upvalr = nil;
|
||||
|
||||
/* set canary = stack[STACK_DEPTH-1] */
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||
|
||||
/* nothing is on stack */
|
||||
ctx.top = ctx.stack - 1;
|
||||
|
||||
/* clear main stack and global */
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = nil;
|
||||
global[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void vm::value_info(var& val) {
|
||||
const auto p = reinterpret_cast<u64>(val.val.gcobj);
|
||||
switch(val.type) {
|
||||
case vm_none: std::clog << "| null |"; break;
|
||||
case vm_ret: std::clog << "| pc | 0x" << std::hex
|
||||
<< val.ret() << std::dec; break;
|
||||
case vm_addr: std::clog << "| addr | 0x" << std::hex
|
||||
<< reinterpret_cast<u64>(val.addr())
|
||||
<< std::dec; break;
|
||||
case vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
|
||||
case vm_nil: std::clog << "| nil |"; break;
|
||||
case vm_num: std::clog << "| num | " << val.num(); break;
|
||||
case vm_str: std::clog << "| str | <0x" << std::hex << p
|
||||
<< "> " << rawstr(val.str(), 16)
|
||||
<< std::dec; break;
|
||||
case vm_func: std::clog << "| func | <0x" << std::hex << p
|
||||
<< "> entry:0x" << val.func().entry
|
||||
<< std::dec; break;
|
||||
case vm_upval:std::clog << "| upval| <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.upval().size
|
||||
<< " val]"; break;
|
||||
case vm_vec: std::clog << "| vec | <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.vec().size()
|
||||
<< " val]"; break;
|
||||
case vm_hash: std::clog << "| hash | <0x" << std::hex << p
|
||||
<< std::dec << "> {" << val.hash().size()
|
||||
<< " val}"; break;
|
||||
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
|
||||
<< "> obj:0x"
|
||||
<< reinterpret_cast<u64>(val.ghost().pointer)
|
||||
<< std::dec; break;
|
||||
case vm_co: std::clog << "| co | <0x" << std::hex << p
|
||||
<< std::dec << "> coroutine"; break;
|
||||
case vm_map: std::clog << "| nmspc| <0x" << std::hex << p
|
||||
<< std::dec << "> namespace ["
|
||||
<< val.map().mapper.size() << " val]"; break;
|
||||
default: std::clog << "| err | <0x" << std::hex << p
|
||||
<< std::dec << "> unknown object"; break;
|
||||
}
|
||||
std::clog << "\n";
|
||||
}
|
||||
|
||||
void vm::function_detail_info(const nas_func& func) {
|
||||
std::clog << "func@0x";
|
||||
std::clog << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
||||
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& key : func.keys) {
|
||||
argument_list[key.second-1] = key.first;
|
||||
}
|
||||
|
||||
std::clog << "(";
|
||||
for(const auto& key : argument_list) {
|
||||
std::clog << key;
|
||||
if (key != argument_list.back()) {
|
||||
std::clog << ", ";
|
||||
}
|
||||
}
|
||||
if (func.dynamic_parameter_index>=0) {
|
||||
std::clog << (argument_list.size()? ", ":"");
|
||||
std::clog << const_string[func.dynamic_parameter_index] << "...";
|
||||
}
|
||||
std::clog << ") ";
|
||||
std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
|
||||
}
|
||||
|
||||
void vm::function_call_trace() {
|
||||
var* bottom = ctx.stack;
|
||||
var* top = ctx.top;
|
||||
|
||||
// generate trace back
|
||||
std::stack<const nas_func*> functions;
|
||||
for(var* i = bottom; i<=top; ++i) {
|
||||
if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) {
|
||||
functions.push(&i->func());
|
||||
}
|
||||
}
|
||||
if (functions.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::clog << "\ncall trace " << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||
const nas_func* last = nullptr;
|
||||
u32 same = 0;
|
||||
for(auto func = last; !functions.empty(); functions.pop()) {
|
||||
func = functions.top();
|
||||
if (last==func) {
|
||||
++same;
|
||||
continue;
|
||||
} else if (same) {
|
||||
std::clog << " --> " << same << " same call(s)\n";
|
||||
same = 0;
|
||||
}
|
||||
|
||||
last = func;
|
||||
std::clog << " call ";
|
||||
function_detail_info(*func);
|
||||
std::clog << "\n";
|
||||
}
|
||||
if (same) {
|
||||
std::clog << " --> " << same << " same call(s)\n";
|
||||
}
|
||||
}
|
||||
|
||||
void vm::trace_back() {
|
||||
// var* bottom = ctx.stack;
|
||||
// var* top = ctx.top;
|
||||
|
||||
// generate trace back
|
||||
std::stack<u32> ret;
|
||||
for(var* i = ctx.stack; i<=ctx.top; ++i) {
|
||||
if (i->type==vm_ret && i->ret()!=0) {
|
||||
ret.push(i->ret());
|
||||
}
|
||||
}
|
||||
ret.push(ctx.pc); // store the position program crashed
|
||||
|
||||
std::clog << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||
codestream::set(const_number, const_string, native_function.data(), files);
|
||||
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||
if ((p = ret.top())==prev) {
|
||||
++same;
|
||||
continue;
|
||||
}
|
||||
if (same) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(6) << std::setfill('0')
|
||||
<< prev << std::dec << " "
|
||||
<< same << " same call(s)\n";
|
||||
same = 0;
|
||||
}
|
||||
std::clog << " " << codestream(bytecode[p], p) << "\n";
|
||||
}
|
||||
// the first called place has no same calls
|
||||
}
|
||||
|
||||
void vm::stack_info(const u32 limit = 10) {
|
||||
var* top = ctx.top;
|
||||
var* bottom = ctx.stack;
|
||||
std::clog << "\nstack (0x" << std::hex << reinterpret_cast<u64>(bottom);
|
||||
std::clog << std::dec << ", limit " << limit << ", total ";
|
||||
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
||||
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(6) << std::setfill('0')
|
||||
<< static_cast<u64>(top-bottom) << std::dec
|
||||
<< " ";
|
||||
value_info(top[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::register_info() {
|
||||
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main")
|
||||
<< ")\n" << std::hex
|
||||
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
|
||||
<< " [global] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(global) << "\n"
|
||||
<< " [local ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.localr) << "\n"
|
||||
<< " [memr ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.memr) << "\n"
|
||||
<< " [canary] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.canary) << "\n"
|
||||
<< " [top ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
||||
<< std::dec;
|
||||
std::clog << " [funcr ] "; value_info(ctx.funcr);
|
||||
std::clog << " [upval ] "; value_info(ctx.upvalr);
|
||||
}
|
||||
|
||||
void vm::global_state() {
|
||||
if (!global_size || global[0].type==vm_none) {
|
||||
return;
|
||||
}
|
||||
std::clog << "\nglobal (0x" << std::hex
|
||||
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
||||
for(usize i = 0; i<global_size; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
value_info(global[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::local_state() {
|
||||
if (!ctx.localr || !ctx.funcr.func().local_size) {
|
||||
return;
|
||||
}
|
||||
const u32 lsize = ctx.funcr.func().local_size;
|
||||
std::clog << "\nlocal (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
||||
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
||||
<< ">)\n" << std::dec;
|
||||
for(u32 i = 0; i<lsize; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
value_info(ctx.localr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void vm::upvalue_state() {
|
||||
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
|
||||
return;
|
||||
}
|
||||
std::clog << "\nupvalue\n";
|
||||
auto& upval = ctx.funcr.func().upval;
|
||||
for(u32 i = 0; i<upval.size(); ++i) {
|
||||
std::clog << " -> upval[" << i << "]:\n";
|
||||
auto& uv = upval[i].upval();
|
||||
for(u32 j = 0; j<uv.size; ++j) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << j << std::dec
|
||||
<< " ";
|
||||
value_info(uv[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vm::all_state_detail() {
|
||||
register_info();
|
||||
global_state();
|
||||
local_state();
|
||||
upvalue_state();
|
||||
}
|
||||
|
||||
std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
|
||||
auto result = std::string("lack argument(s) when calling function:\n func(");
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& i : func.keys) {
|
||||
argument_list[i.second-1] = i.first;
|
||||
}
|
||||
for(u32 i = 0; i<argument_list.size(); ++i) {
|
||||
result += argument_list[i];
|
||||
if (i<argc) {
|
||||
result += "[get]";
|
||||
}
|
||||
if (i!=argument_list.size()-1) {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
if (func.dynamic_parameter_index>=0) {
|
||||
result += argument_list.size()? ", ":"";
|
||||
result += const_string[func.dynamic_parameter_index] + "[dynamic]";
|
||||
}
|
||||
result += ") ";
|
||||
std::stringstream out;
|
||||
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
|
||||
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
||||
return result + out.str();
|
||||
}
|
||||
|
||||
std::string vm::report_special_call_lack_arguments(
|
||||
var* local, const nas_func& func) const {
|
||||
auto result = std::string("lack argument(s) when calling function:\n func(");
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& i : func.keys) {
|
||||
argument_list[i.second-1] = i.first;
|
||||
}
|
||||
for(const auto& key : argument_list) {
|
||||
if (local[func.keys.at(key)].type==vm_none) {
|
||||
result += key + ", ";
|
||||
} else {
|
||||
result += key + "[get], ";
|
||||
}
|
||||
}
|
||||
result = result.substr(0, result.length()-2);
|
||||
result += ") ";
|
||||
std::stringstream out;
|
||||
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
|
||||
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
|
||||
return result + out.str();
|
||||
}
|
||||
|
||||
std::string vm::report_key_not_found(
|
||||
const std::string& not_found, const nas_hash& hash) const {
|
||||
auto result = "member \"" + not_found + "\" doesn't exist in hash {";
|
||||
for(const auto& i : hash.elems) {
|
||||
result += i.first + ", ";
|
||||
}
|
||||
if (hash.elems.size()) {
|
||||
result = result.substr(0, result.length()-2);
|
||||
}
|
||||
result += "}";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vm::report_out_of_range(f64 index, usize real_size) const {
|
||||
auto result = "index out of range: " + std::to_string(index);
|
||||
result += " but max size is " + std::to_string(real_size);
|
||||
if (!real_size) {
|
||||
return result;
|
||||
}
|
||||
result += ", index range is -" + std::to_string(real_size);
|
||||
result += "~" + std::to_string(real_size-1);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string vm::type_name_string(const var& value) const {
|
||||
switch(value.type) {
|
||||
case vm_none: return "none";
|
||||
case vm_cnt: return "counter";
|
||||
case vm_addr: return "address";
|
||||
case vm_ret: return "program counter";
|
||||
case vm_nil: return "nil";
|
||||
case vm_num: return "number";
|
||||
case vm_str: return "string";
|
||||
case vm_vec: return "vector";
|
||||
case vm_hash: return "hash";
|
||||
case vm_func: return "function";
|
||||
case vm_upval: return "upvalue";
|
||||
case vm_obj: return "ghost type";
|
||||
case vm_co: return "coroutine";
|
||||
case vm_map: return "namespace";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void vm::die(const std::string& str) {
|
||||
std::cerr << "[vm] error: " << str << "\n";
|
||||
function_call_trace();
|
||||
trace_back();
|
||||
stack_info();
|
||||
|
||||
// show verbose crash info
|
||||
if (verbose) {
|
||||
all_state_detail();
|
||||
}
|
||||
|
||||
if (!ngc.cort) {
|
||||
// in main context, exit directly
|
||||
std::exit(1);
|
||||
} else {
|
||||
// in coroutine, shut down the coroutine and return to main context
|
||||
ctx.pc = 0; // mark coroutine 'dead'
|
||||
ngc.context_reserve(); // switch context to main
|
||||
ctx.top[0] = nil; // generate return value 'nil'
|
||||
}
|
||||
}
|
||||
|
||||
void vm::run(
|
||||
const codegen& gen,
|
||||
const linker& linker,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
init(gen.strs(), gen.nums(), gen.natives(),
|
||||
gen.codes(), gen.globals(), linker.get_file_list(), argv);
|
||||
#ifndef _MSC_VER
|
||||
// using labels as values/computed goto
|
||||
const void* oprs[] = {
|
||||
&&vmexit, &&repl, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&deft, &&dyn,
|
||||
&&lnot, &&usub, &&bnot, &&btor,
|
||||
&&btxor, &&btand, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
|
||||
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
|
||||
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
|
||||
&&divecp, &&lnkecp, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&cnt, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbeg, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret
|
||||
};
|
||||
std::vector<const void*> code;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
// goto the first operand
|
||||
goto *code[ctx.pc];
|
||||
#else
|
||||
typedef void (vm::*nafunc)();
|
||||
const nafunc oprs[] = {
|
||||
nullptr, &vm::o_repl,
|
||||
&vm::o_intl, &vm::o_loadg,
|
||||
&vm::o_loadl, &vm::o_loadu,
|
||||
&vm::o_pnum, &vm::o_pnil,
|
||||
&vm::o_pstr, &vm::o_newv,
|
||||
&vm::o_newh, &vm::o_newf,
|
||||
&vm::o_happ, &vm::o_para,
|
||||
&vm::o_deft, &vm::o_dyn,
|
||||
&vm::o_lnot, &vm::o_usub,
|
||||
&vm::o_bnot, &vm::o_btor,
|
||||
&vm::o_btxor, &vm::o_btand,
|
||||
&vm::o_add, &vm::o_sub,
|
||||
&vm::o_mul, &vm::o_div,
|
||||
&vm::o_lnk, &vm::o_addc,
|
||||
&vm::o_subc, &vm::o_mulc,
|
||||
&vm::o_divc, &vm::o_lnkc,
|
||||
&vm::o_addeq, &vm::o_subeq,
|
||||
&vm::o_muleq, &vm::o_diveq,
|
||||
&vm::o_lnkeq, &vm::o_bandeq,
|
||||
&vm::o_boreq, &vm::o_bxoreq,
|
||||
&vm::o_addeqc, &vm::o_subeqc,
|
||||
&vm::o_muleqc, &vm::o_diveqc,
|
||||
&vm::o_lnkeqc, &vm::o_addecp,
|
||||
&vm::o_subecp, &vm::o_mulecp,
|
||||
&vm::o_divecp, &vm::o_lnkecp,
|
||||
&vm::o_meq, &vm::o_eq,
|
||||
&vm::o_neq, &vm::o_less,
|
||||
&vm::o_leq, &vm::o_grt,
|
||||
&vm::o_geq, &vm::o_lessc,
|
||||
&vm::o_leqc, &vm::o_grtc,
|
||||
&vm::o_geqc, &vm::o_pop,
|
||||
&vm::o_jmp, &vm::o_jt,
|
||||
&vm::o_jf, &vm::o_cnt,
|
||||
&vm::o_findex, &vm::o_feach,
|
||||
&vm::o_callg, &vm::o_calll,
|
||||
&vm::o_upval, &vm::o_callv,
|
||||
&vm::o_callvi, &vm::o_callh,
|
||||
&vm::o_callfv, &vm::o_callfh,
|
||||
&vm::o_callb, &vm::o_slcbeg,
|
||||
&vm::o_slcend, &vm::o_slc,
|
||||
&vm::o_slc2, &vm::o_mcallg,
|
||||
&vm::o_mcalll, &vm::o_mupval,
|
||||
&vm::o_mcallv, &vm::o_mcallh,
|
||||
&vm::o_ret
|
||||
};
|
||||
std::vector<nafunc> code;
|
||||
for(auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(code[ctx.pc]) {
|
||||
(this->*code[ctx.pc])();
|
||||
if (ctx.top>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
}
|
||||
++ctx.pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
vmexit:
|
||||
if (verbose) {
|
||||
ngc.info();
|
||||
}
|
||||
imm.clear();
|
||||
if (!is_repl_mode) {
|
||||
ngc.clear();
|
||||
}
|
||||
return;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// may cause stackoverflow
|
||||
#define exec_check(op) {\
|
||||
op();\
|
||||
if (ctx.top<ctx.canary)\
|
||||
goto *code[++ctx.pc];\
|
||||
die("stack overflow");\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
// do not cause stackoverflow
|
||||
#define exec_nodie(op) {\
|
||||
op();\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
|
||||
repl: exec_nodie(o_repl ); // 0
|
||||
intl: exec_nodie(o_intl ); // -0
|
||||
loadg: exec_nodie(o_loadg ); // -1
|
||||
loadl: exec_nodie(o_loadl ); // -1
|
||||
loadu: exec_nodie(o_loadu ); // -1
|
||||
pnum: exec_check(o_pnum ); // +1
|
||||
pnil: exec_check(o_pnil ); // +1
|
||||
pstr: exec_check(o_pstr ); // +1
|
||||
newv: exec_check(o_newv ); // +1-imm[pc]
|
||||
newh: exec_check(o_newh ); // +1
|
||||
newf: exec_check(o_newf ); // +1
|
||||
happ: exec_nodie(o_happ ); // -1
|
||||
para: exec_nodie(o_para ); // -0
|
||||
deft: exec_nodie(o_deft ); // -1
|
||||
dyn: exec_nodie(o_dyn ); // -0
|
||||
lnot: exec_nodie(o_lnot ); // -0
|
||||
usub: exec_nodie(o_usub ); // -0
|
||||
bnot: exec_nodie(o_bnot ); // -0
|
||||
btor: exec_nodie(o_btor ); // -1
|
||||
btxor: exec_nodie(o_btxor ); // -1
|
||||
btand: exec_nodie(o_btand ); // -1
|
||||
add: exec_nodie(o_add ); // -1
|
||||
sub: exec_nodie(o_sub ); // -1
|
||||
mul: exec_nodie(o_mul ); // -1
|
||||
div: exec_nodie(o_div ); // -1
|
||||
lnk: exec_nodie(o_lnk ); // -1
|
||||
addc: exec_nodie(o_addc ); // -0
|
||||
subc: exec_nodie(o_subc ); // -0
|
||||
mulc: exec_nodie(o_mulc ); // -0
|
||||
divc: exec_nodie(o_divc ); // -0
|
||||
lnkc: exec_nodie(o_lnkc ); // -0
|
||||
addeq: exec_nodie(o_addeq ); // -1
|
||||
subeq: exec_nodie(o_subeq ); // -1
|
||||
muleq: exec_nodie(o_muleq ); // -1
|
||||
diveq: exec_nodie(o_diveq ); // -1
|
||||
lnkeq: exec_nodie(o_lnkeq ); // -1
|
||||
bandeq: exec_nodie(o_bandeq); // -1
|
||||
boreq: exec_nodie(o_boreq ); // -1
|
||||
bxoreq: exec_nodie(o_bxoreq); // -1
|
||||
addeqc: exec_nodie(o_addeqc); // -0
|
||||
subeqc: exec_nodie(o_subeqc); // -0
|
||||
muleqc: exec_nodie(o_muleqc); // -0
|
||||
diveqc: exec_nodie(o_diveqc); // -0
|
||||
lnkeqc: exec_nodie(o_lnkeqc); // -0
|
||||
addecp: exec_nodie(o_addecp); // -1
|
||||
subecp: exec_nodie(o_subecp); // -1
|
||||
mulecp: exec_nodie(o_mulecp); // -1
|
||||
divecp: exec_nodie(o_divecp); // -1
|
||||
lnkecp: exec_nodie(o_lnkecp); // -1
|
||||
meq: exec_nodie(o_meq ); // -1
|
||||
eq: exec_nodie(o_eq ); // -1
|
||||
neq: exec_nodie(o_neq ); // -1
|
||||
less: exec_nodie(o_less ); // -1
|
||||
leq: exec_nodie(o_leq ); // -1
|
||||
grt: exec_nodie(o_grt ); // -1
|
||||
geq: exec_nodie(o_geq ); // -1
|
||||
lessc: exec_nodie(o_lessc ); // -0
|
||||
leqc: exec_nodie(o_leqc ); // -0
|
||||
grtc: exec_nodie(o_grtc ); // -0
|
||||
geqc: exec_nodie(o_geqc ); // -0
|
||||
pop: exec_nodie(o_pop ); // -1
|
||||
jmp: exec_nodie(o_jmp ); // -0
|
||||
jt: exec_nodie(o_jt ); // -0
|
||||
jf: exec_nodie(o_jf ); // -1
|
||||
cnt: exec_nodie(o_cnt ); // -0
|
||||
findex: exec_check(o_findex); // +1
|
||||
feach: exec_check(o_feach ); // +1
|
||||
callg: exec_check(o_callg ); // +1
|
||||
calll: exec_check(o_calll ); // +1
|
||||
upval: exec_check(o_upval ); // +1
|
||||
callv: exec_nodie(o_callv ); // -0
|
||||
callvi: exec_nodie(o_callvi); // -0
|
||||
callh: exec_nodie(o_callh ); // -0
|
||||
callfv: exec_nodie(o_callfv); // check in the function
|
||||
callfh: exec_nodie(o_callfh); // check in the function
|
||||
callb: exec_nodie(o_callb ); // -0
|
||||
slcbeg: exec_check(o_slcbeg); // +1
|
||||
slcend: exec_nodie(o_slcend); // -1
|
||||
slc: exec_nodie(o_slc ); // -1
|
||||
slc2: exec_nodie(o_slc2 ); // -2
|
||||
mcallg: exec_check(o_mcallg); // +1
|
||||
mcalll: exec_check(o_mcalll); // +1
|
||||
mupval: exec_check(o_mupval); // +1
|
||||
mcallv: exec_nodie(o_mcallv); // -0
|
||||
mcallh: exec_nodie(o_mcallh); // -0
|
||||
ret: exec_nodie(o_ret ); // -2
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
1031
src/nasal_vm.h
Normal file
155
src/optimizer.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "optimizer.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void optimizer::const_string(
|
||||
binary_operator* node,
|
||||
string_literal* left_node,
|
||||
string_literal* right_node) {
|
||||
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
|
||||
return;
|
||||
}
|
||||
const auto& left = left_node->get_content();
|
||||
const auto& right = right_node->get_content();
|
||||
node->set_optimized_string(
|
||||
new string_literal(node->get_location(), left+right)
|
||||
);
|
||||
}
|
||||
|
||||
void optimizer::const_number(
|
||||
binary_operator* node,
|
||||
number_literal* left_node,
|
||||
number_literal* right_node) {
|
||||
const auto left = left_node->get_number();
|
||||
const auto right = right_node->get_number();
|
||||
f64 res;
|
||||
switch(node->get_operator_type()) {
|
||||
case binary_operator::binary_type::add: res = left+right; break;
|
||||
case binary_operator::binary_type::sub: res = left-right; break;
|
||||
case binary_operator::binary_type::mult: res = left*right; break;
|
||||
case binary_operator::binary_type::div: res = left/right; break;
|
||||
case binary_operator::binary_type::less: res = left<right; break;
|
||||
case binary_operator::binary_type::leq: res = left<=right; break;
|
||||
case binary_operator::binary_type::grt: res = left>right; break;
|
||||
case binary_operator::binary_type::geq: res = left>=right; break;
|
||||
case binary_operator::binary_type::bitwise_or:
|
||||
res = static_cast<i32>(left)|static_cast<i32>(right); break;
|
||||
case binary_operator::binary_type::bitwise_xor:
|
||||
res = static_cast<i32>(left)^static_cast<i32>(right); break;
|
||||
case binary_operator::binary_type::bitwise_and:
|
||||
res = static_cast<i32>(left)&static_cast<i32>(right); break;
|
||||
default: return;
|
||||
}
|
||||
if (std::isinf(res) || std::isnan(res)) {
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res)
|
||||
);
|
||||
}
|
||||
|
||||
void optimizer::const_number(
|
||||
unary_operator* node,
|
||||
number_literal* value_node) {
|
||||
auto res = value_node->get_number();
|
||||
switch(node->get_operator_type()) {
|
||||
case unary_operator::unary_type::negative:
|
||||
res = -res; break;
|
||||
case unary_operator::unary_type::bitwise_not:
|
||||
res = ~static_cast<i32>(res); break;
|
||||
case unary_operator::unary_type::logical_not:
|
||||
res = !res; break;
|
||||
}
|
||||
if (std::isinf(res) || std::isnan(res)) {
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res)
|
||||
);
|
||||
}
|
||||
|
||||
bool optimizer::visit_binary_operator(binary_operator* node) {
|
||||
auto left_node = node->get_left();
|
||||
auto right_node = node->get_right();
|
||||
left_node->accept(this);
|
||||
right_node->accept(this);
|
||||
|
||||
number_literal* left_num_node = nullptr;
|
||||
number_literal* right_num_node = nullptr;
|
||||
string_literal* left_str_node = nullptr;
|
||||
string_literal* right_str_node = nullptr;
|
||||
if (left_node->get_type()==expr_type::ast_num) {
|
||||
left_num_node = reinterpret_cast<number_literal*>(left_node);
|
||||
} else if (left_node->get_type()==expr_type::ast_binary &&
|
||||
reinterpret_cast<binary_operator*>(left_node)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<binary_operator*>(left_node);
|
||||
left_num_node = optimized->get_optimized_number();
|
||||
} else if (left_node->get_type()==expr_type::ast_unary &&
|
||||
reinterpret_cast<unary_operator*>(left_node)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<unary_operator*>(left_node);
|
||||
left_num_node = optimized->get_optimized_number();
|
||||
}
|
||||
if (right_node->get_type()==expr_type::ast_num) {
|
||||
right_num_node = reinterpret_cast<number_literal*>(right_node);
|
||||
} else if (right_node->get_type()==expr_type::ast_binary &&
|
||||
reinterpret_cast<binary_operator*>(right_node)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<binary_operator*>(right_node);
|
||||
right_num_node = optimized->get_optimized_number();
|
||||
} else if (right_node->get_type()==expr_type::ast_unary &&
|
||||
reinterpret_cast<unary_operator*>(right_node)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<unary_operator*>(right_node);
|
||||
right_num_node = optimized->get_optimized_number();
|
||||
}
|
||||
|
||||
if (left_node->get_type()==expr_type::ast_str) {
|
||||
left_str_node = reinterpret_cast<string_literal*>(left_node);
|
||||
} else if (left_node->get_type()==expr_type::ast_binary &&
|
||||
reinterpret_cast<binary_operator*>(left_node)->get_optimized_string()) {
|
||||
auto optimized = reinterpret_cast<binary_operator*>(left_node);
|
||||
left_str_node = optimized->get_optimized_string();
|
||||
}
|
||||
if (right_node->get_type()==expr_type::ast_str) {
|
||||
right_str_node = reinterpret_cast<string_literal*>(right_node);
|
||||
} else if (right_node->get_type()==expr_type::ast_binary &&
|
||||
reinterpret_cast<binary_operator*>(right_node)->get_optimized_string()) {
|
||||
auto optimized = reinterpret_cast<binary_operator*>(right_node);
|
||||
right_str_node = optimized->get_optimized_string();
|
||||
}
|
||||
if (left_num_node && right_num_node) {
|
||||
const_number(node, left_num_node, right_num_node);
|
||||
return true;
|
||||
}
|
||||
if (left_str_node && right_str_node) {
|
||||
const_string(node, left_str_node, right_str_node);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optimizer::visit_unary_operator(unary_operator* node) {
|
||||
auto value = node->get_value();
|
||||
value->accept(this);
|
||||
|
||||
number_literal* num_node = nullptr;
|
||||
if (value->get_type()==expr_type::ast_num) {
|
||||
num_node = reinterpret_cast<number_literal*>(value);
|
||||
} else if (value->get_type()==expr_type::ast_binary &&
|
||||
reinterpret_cast<binary_operator*>(value)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<binary_operator*>(value);
|
||||
num_node = optimized->get_optimized_number();
|
||||
} else if (value->get_type()==expr_type::ast_unary &&
|
||||
reinterpret_cast<unary_operator*>(value)->get_optimized_number()) {
|
||||
auto optimized = reinterpret_cast<unary_operator*>(value);
|
||||
num_node = optimized->get_optimized_number();
|
||||
}
|
||||
if (num_node) {
|
||||
const_number(node, num_node);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void optimizer::do_optimization(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
|
||||
}
|
||||
24
src/optimizer.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class optimizer: public ast_visitor {
|
||||
private:
|
||||
void const_string(binary_operator*, string_literal*, string_literal*);
|
||||
void const_number(binary_operator*, number_literal*, number_literal*);
|
||||
void const_number(unary_operator*, number_literal*);
|
||||
|
||||
public:
|
||||
bool visit_binary_operator(binary_operator*) override;
|
||||
bool visit_unary_operator(unary_operator*) override;
|
||||
|
||||
public:
|
||||
void do_optimization(code_block*);
|
||||
};
|
||||
|
||||
}
|
||||
170
src/repl.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include "repl.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
namespace repl {
|
||||
|
||||
void repl::add_command_history(const std::string& history) {
|
||||
if (command_history.size() && command_history.back()==history) {
|
||||
return;
|
||||
}
|
||||
command_history.push_back(history);
|
||||
if (command_history.size()>1000) {
|
||||
command_history.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
std::string repl::readline(std::string prompt = ">>> ") {
|
||||
auto line = std::string("");
|
||||
std::cout << prompt;
|
||||
std::getline(std::cin, line,'\n');
|
||||
return line;
|
||||
}
|
||||
|
||||
void repl::update_temp_file() {
|
||||
auto content = std::string("");
|
||||
for(const auto& i : source) {
|
||||
content += i + "\n";
|
||||
}
|
||||
info::instance()->repl_file_source = content + " ";
|
||||
}
|
||||
|
||||
bool repl::check_need_more_input() {
|
||||
while(true) {
|
||||
update_temp_file();
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 in_curve = 0;
|
||||
i64 in_bracket = 0;
|
||||
i64 in_brace = 0;
|
||||
for(const auto& t : nasal_lexer->result()) {
|
||||
switch(t.type) {
|
||||
case tok::lcurve: ++in_curve; break;
|
||||
case tok::rcurve: --in_curve; break;
|
||||
case tok::lbracket: ++in_bracket; break;
|
||||
case tok::rbracket: --in_bracket; break;
|
||||
case tok::lbrace: ++in_brace; break;
|
||||
case tok::rbrace: --in_brace; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (in_curve<=0 && in_bracket<=0 && in_brace<=0) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto line = readline("... ");
|
||||
add_command_history(line);
|
||||
source.back() += "\n" + line;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::help() {
|
||||
std::cout << ".h, .help | show help\n";
|
||||
std::cout << ".e, .exit | quit the REPL\n";
|
||||
std::cout << ".q, .quit | quit the REPL\n";
|
||||
std::cout << ".c, .clear | clear the screen\n";
|
||||
std::cout << ".s, .source | show source code\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
bool repl::run() {
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
auto nasal_parser = std::unique_ptr<parse>(new parse);
|
||||
auto nasal_linker = std::unique_ptr<linker>(new linker);
|
||||
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer);
|
||||
auto nasal_codegen = std::unique_ptr<codegen>(new codegen);
|
||||
|
||||
update_temp_file();
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_parser->compile(*nasal_lexer).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nasal_opt->do_optimization(nasal_parser->tree());
|
||||
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
runtime.run(*nasal_codegen, *nasal_linker, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::execute() {
|
||||
source = {};
|
||||
// mark we are in repl mode
|
||||
info::instance()->in_repl_mode = true;
|
||||
std::cout << "[nasal-repl] Initializating enviroment...\n";
|
||||
// run on pass for initializing basic modules, without output
|
||||
if (!run()) {
|
||||
std::cout << "[nasal-repl] Initialization failed.\n\n";
|
||||
std::exit(-1);
|
||||
}
|
||||
// allow output now
|
||||
runtime.set_allow_repl_output_flag(true);
|
||||
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
||||
|
||||
// finish initialization, output version info
|
||||
std::cout << "Nasal REPL interpreter version " << __nasver;
|
||||
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
help();
|
||||
|
||||
while(true) {
|
||||
auto line = readline();
|
||||
if (!line.length()) {
|
||||
continue;
|
||||
}
|
||||
add_command_history(line);
|
||||
|
||||
if (line == ".e" || line == ".exit") {
|
||||
break;
|
||||
} else if (line == ".q" || line == ".quit") {
|
||||
break;
|
||||
} else if (line == ".h" || line == ".help") {
|
||||
help();
|
||||
continue;
|
||||
} else if (line == ".c" || line == ".clear") {
|
||||
std::cout << "\033c";
|
||||
continue;
|
||||
} else if (line == ".s" || line == ".source") {
|
||||
update_temp_file();
|
||||
std::cout << info::instance()->repl_file_source << "\n";
|
||||
continue;
|
||||
} else if (line[0] == "."[0]) {
|
||||
std::cout << "no such command \"" << line;
|
||||
std::cout << "\", input \".help\" for help\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
source.push_back(line);
|
||||
if (!check_need_more_input()) {
|
||||
source.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// run program
|
||||
if (!run()) {
|
||||
source.pop_back();
|
||||
}
|
||||
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
55
src/repl.h
Normal file
@@ -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->is_definition() && node->get_name()) {
|
||||
symbols.push_back({
|
||||
node->get_name()->get_name(),
|
||||
node->get_name()->get_location()
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<symbol_finder::symbol_info>& symbol_finder::do_find(code_block* root) {
|
||||
symbols.clear();
|
||||
root->accept(this);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/symbol_finder.h
Normal file
@@ -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*);
|
||||
};
|
||||
|
||||
}
|
||||