forked from xxq250/Nasal-Interpreter
Compare commits
143 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d19f53bdb0 | ||
|
|
c2d82d4806 | ||
|
|
87b3ec06a2 | ||
|
|
cfb6f92e3f | ||
|
|
0310faba3c | ||
|
|
3032070d1a | ||
|
|
12cdd1e9ea | ||
|
|
3cc47b744c | ||
|
|
0f1e5fca91 | ||
|
|
8ccb5c36b8 | ||
|
|
6dc102a5dd | ||
|
|
4a03179457 | ||
|
|
9f1d50bfe6 | ||
|
|
6b975b4a9c | ||
|
|
6034234c87 | ||
|
|
c08befd0c0 | ||
|
|
e35410c6de | ||
|
|
9f4e33f23d | ||
|
|
af3d93ab64 | ||
|
|
2cc5bb8625 | ||
|
|
240095ab37 | ||
|
|
12afc5422d | ||
|
|
906f337f1a | ||
|
|
e724aa6ef2 | ||
|
|
dd9d5baf22 | ||
|
|
b5f02de883 | ||
|
|
10d2965197 | ||
|
|
ac8e5c6361 | ||
|
|
a83978c553 | ||
|
|
a85a4e37c1 | ||
|
|
bde152b6e9 | ||
|
|
705df8dc1d | ||
|
|
eeb126ab65 | ||
|
|
a7a2a0a369 | ||
|
|
065f6ae162 | ||
|
|
9ea97c5608 | ||
|
|
b177a58662 | ||
|
|
a25f309063 | ||
|
|
8b8b8aef52 | ||
|
|
998fd08353 | ||
|
|
3fd21a62ef | ||
|
|
04e30b9c7f | ||
|
|
09cd3027e9 | ||
|
|
e405c3e6d4 | ||
|
|
0077d21330 | ||
|
|
b09e5bb8ad | ||
|
|
3ff885abe3 | ||
|
|
0c54f216fc | ||
|
|
b449dcf655 | ||
|
|
699c5f7af4 | ||
|
|
749f1ada67 | ||
|
|
3752bd8c07 | ||
|
|
8435ac5b63 | ||
|
|
644fbd8647 | ||
|
|
fc2bb809aa | ||
|
|
89826da791 | ||
|
|
c6840da201 | ||
|
|
247172a9f2 | ||
|
|
277ddb9c0f | ||
|
|
c228dcc149 | ||
|
|
dadc0afa35 | ||
|
|
77ec4b0d7c | ||
|
|
030a3ad920 | ||
|
|
04097341ac | ||
|
|
fd93fbafdf | ||
|
|
688fbe8c5d | ||
|
|
4dc4c1d2b7 | ||
|
|
4035c6c98a | ||
|
|
f6fa78d33e | ||
|
|
4062c40bb3 | ||
|
|
260f0cb5a3 | ||
|
|
985dee8001 | ||
|
|
94d0ce9c2d | ||
|
|
9cbefa1003 | ||
|
|
dea19fe3c3 | ||
|
|
1b745ad176 | ||
|
|
96ad435e23 | ||
|
|
c784b7b490 | ||
|
|
fc6f7c60c7 | ||
|
|
c9c6b905ea | ||
|
|
0d8a60e4e3 | ||
|
|
0e85bfccab | ||
|
|
ebca28d0aa | ||
|
|
c8869080c6 | ||
|
|
8e7074fd25 | ||
|
|
afdaac9670 | ||
|
|
eae25eeb9a | ||
|
|
d3d204a704 | ||
|
|
44e7077f46 | ||
|
|
e6b88c9dec | ||
|
|
068da2fb41 | ||
|
|
1ecd0a6912 | ||
|
|
8dc06c085c | ||
|
|
40976bf0c1 | ||
|
|
6260cc1665 | ||
|
|
8ecf309791 | ||
|
|
95031f508b | ||
|
|
037ac9e79f | ||
|
|
83ffcc3087 | ||
|
|
1c48e08889 | ||
|
|
b7176b39ed | ||
|
|
255f10dfae | ||
|
|
20504efcf3 | ||
|
|
0c64090547 | ||
|
|
d4eafae5f2 | ||
|
|
fdbc6cf6da | ||
|
|
617598ea40 | ||
|
|
55f8485561 | ||
|
|
52dfd52f39 | ||
|
|
99298b86ab | ||
|
|
cf2323623b | ||
|
|
efd403c758 | ||
|
|
21911f21f0 | ||
|
|
8d3f752429 | ||
|
|
d37dfec225 | ||
|
|
94114416fe | ||
|
|
4da38f686f | ||
|
|
1c537f0398 | ||
|
|
858ffdcb61 | ||
|
|
bbd4d1907b | ||
|
|
456ed5c782 | ||
|
|
35c8afe56b | ||
|
|
9dcb45d786 | ||
|
|
4d838dc694 | ||
|
|
1de0874c8d | ||
|
|
abb587c62b | ||
|
|
f1fb58ead3 | ||
|
|
78dd535a72 | ||
|
|
5da80d1b22 | ||
|
|
7cc9a9f5c9 | ||
|
|
7c175a2883 | ||
|
|
671b1ef212 | ||
|
|
ce204352c6 | ||
|
|
0725352349 | ||
|
|
9752b8823e | ||
|
|
40ca6c19bf | ||
|
|
ef00209018 | ||
|
|
bf9f0d6338 | ||
|
|
0c216e95e8 | ||
|
|
2337994b08 | ||
|
|
d48c9f44e8 | ||
|
|
7e3c6d06b2 | ||
|
|
1205c6d788 |
89
.github/workflows/c-cpp.yml
vendored
89
.github/workflows/c-cpp.yml
vendored
@@ -1,12 +1,8 @@
|
||||
name: Build/Test/Package CI
|
||||
name: Nightly Build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 16 * * *"
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -21,10 +17,8 @@ jobs:
|
||||
git push -f origin next_macOS
|
||||
- name: Build
|
||||
run: |
|
||||
make -j4
|
||||
cd module
|
||||
make all -j4
|
||||
cd ..
|
||||
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
make -j6
|
||||
- name: Test
|
||||
run: make test
|
||||
- name: Package
|
||||
@@ -37,7 +31,7 @@ jobs:
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: |
|
||||
nasal-Darwin.tar
|
||||
nasal-macOS-aarch64.tar
|
||||
|
||||
linux-x86_64-build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -50,10 +44,8 @@ jobs:
|
||||
git push -f origin next_linux_x86_64
|
||||
- name: Build
|
||||
run: |
|
||||
make -j4
|
||||
cd module
|
||||
make all -j4
|
||||
cd ..
|
||||
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
make -j6
|
||||
- name: Test
|
||||
run: make test
|
||||
- name: Package
|
||||
@@ -66,4 +58,71 @@ jobs:
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: |
|
||||
nasal-Linux.tar
|
||||
nasal-linux-x86_64.tar
|
||||
|
||||
windows-x86_64-build-msvc:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch Deps
|
||||
run: |
|
||||
choco install -y cmake
|
||||
- name: Update Tag
|
||||
run: |
|
||||
git fetch --tags origin
|
||||
git tag -f next_windows_x86_64_msvc
|
||||
git push -f origin next_windows_x86_64_msvc
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022"
|
||||
MSBuild.exe nasal.sln /p:Configuration=Release /p:Platform=x64
|
||||
- name: Package
|
||||
run: |
|
||||
python3 tools/msvc_move_file.py
|
||||
python3 tools/pack.py msvc
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
with:
|
||||
name: windows MSVC nightly build
|
||||
tag_name: next_windows_x86_64_msvc
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: |
|
||||
nasal-windows-x86_64-msvc.tar
|
||||
|
||||
windows-x86_64-build-mingw:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch Deps
|
||||
run: |
|
||||
choco install -y cmake
|
||||
choco install -y mingw
|
||||
- name: Update Tag
|
||||
run: |
|
||||
git fetch --tags origin
|
||||
git tag -f next_windows_x86_64_mingw
|
||||
git push -f origin next_windows_x86_64_mingw
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
|
||||
mingw32-make.exe -j4
|
||||
- name: Package
|
||||
run: |
|
||||
python3 tools/mingw_move_file.py
|
||||
python3 tools/pack.py mingw
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2.0.5
|
||||
with:
|
||||
name: windows MINGW nightly build
|
||||
tag_name: next_windows_x86_64_mingw
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: |
|
||||
nasal-windows-x86_64-mingw.tar
|
||||
31
.github/workflows/test.yml
vendored
Normal file
31
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Nasal Interpreter Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
mac-aarch64:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
make -j6
|
||||
- name: Test
|
||||
run: make test
|
||||
|
||||
linux-x86_64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
make -j6
|
||||
- name: Test
|
||||
run: make test
|
||||
79
.gitignore
vendored
79
.gitignore
vendored
@@ -1,3 +1,22 @@
|
||||
# Build directories
|
||||
build/
|
||||
out/
|
||||
dist/
|
||||
cmake-build-*
|
||||
cmake-windows-*
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
.vs/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# C++ specific
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
@@ -30,32 +49,60 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
nasal
|
||||
nasal-format
|
||||
nasal.exe
|
||||
nasal-format.exe
|
||||
|
||||
# VS C++ sln
|
||||
# Visual Studio specific
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
.vs
|
||||
x64
|
||||
x64/
|
||||
CMakePresents.json
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# nasal executable
|
||||
nasal
|
||||
nasal.exe
|
||||
# CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
CTestTestfile.cmake
|
||||
_deps/
|
||||
|
||||
# misc
|
||||
.vscode
|
||||
dump
|
||||
# Node.js specific (for the web app)
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-debug.log
|
||||
yarn-error.log
|
||||
package-lock.json
|
||||
|
||||
# Project specific
|
||||
dump/
|
||||
fgfs.log
|
||||
.temp.*
|
||||
*.ppm
|
||||
|
||||
# build dir
|
||||
build
|
||||
out
|
||||
# Logs and databases
|
||||
*.log
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
*.db
|
||||
|
||||
# macOS special cache directory
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
|
||||
# ppm picture generated by ppmgen.nas
|
||||
*.ppm
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(nasal VERSION 11.2)
|
||||
project(nasal VERSION 11.3)
|
||||
|
||||
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
|
||||
|
||||
@@ -9,14 +9,19 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
||||
|
||||
add_compile_options(-fPIC)
|
||||
# MSVC does not need -fPIC
|
||||
if (NOT MSVC)
|
||||
add_compile_options(-fPIC)
|
||||
endif()
|
||||
|
||||
# MSVC needs this command option to really enable utf-8 output
|
||||
if(MSVC)
|
||||
if (MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
# generate release executables
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
if (APPLE)
|
||||
add_compile_options(-mmacosx-version-min=10.15)
|
||||
endif()
|
||||
|
||||
# build nasal used object
|
||||
set(NASAL_OBJECT_SOURCE_FILE
|
||||
@@ -30,11 +35,14 @@ set(NASAL_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/natives/math_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/natives/dylib_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/natives/regex_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/natives/subprocess.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/natives/unix_lib.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/repl/repl.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/util/fs.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/util/gc_stat.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/util/util.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ast_format.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
|
||||
@@ -55,12 +63,20 @@ 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)
|
||||
|
||||
# build nasal-format
|
||||
add_executable(nasal-format ${CMAKE_SOURCE_DIR}/src/format.cpp)
|
||||
target_link_libraries(nasal-format nasal-object)
|
||||
|
||||
# link ldl and lpthread
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
target_link_libraries(nasal dl)
|
||||
target_link_libraries(nasal pthread)
|
||||
target_link_libraries(nasal-format pthread)
|
||||
endif()
|
||||
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_include_directories(nasal-format PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
# copy nasal from build dir to the outside dir
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
add_custom_command(
|
||||
@@ -69,6 +85,12 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
||||
${CMAKE_SOURCE_DIR}/build/nasal
|
||||
${CMAKE_SOURCE_DIR}/nasal
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET nasal-format POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/build/nasal-format
|
||||
${CMAKE_SOURCE_DIR}/nasal-format
|
||||
)
|
||||
endif()
|
||||
|
||||
# build module
|
||||
@@ -76,6 +98,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
|
||||
|
||||
set(MODULE_USED_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/util/util.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/util/gc_stat.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/util/fs.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
||||
@@ -96,4 +119,21 @@ 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)
|
||||
if (WIN32)
|
||||
target_link_libraries(nasock ws2_32)
|
||||
endif()
|
||||
target_link_libraries(nasock module-used-object)
|
||||
|
||||
# Add web library, not for MSVC now
|
||||
if (NOT MSVC)
|
||||
add_library(nasal-web SHARED
|
||||
src/nasal_web.cpp
|
||||
)
|
||||
target_include_directories(nasal-web PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
target_link_libraries(nasal-web nasal-object)
|
||||
set_target_properties(nasal-web PROPERTIES
|
||||
C_VISIBILITY_PRESET hidden
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
)
|
||||
endif()
|
||||
90
README.md
90
README.md
@@ -1,4 +1,4 @@
|
||||
# __Nasal - Modern Interpreter__
|
||||
# <img src="./doc/svg/nasal_transparent.svg" height="40px"/> __Nasal - Modern Interpreter__
|
||||
|
||||
<img src="./doc/pic/header.png" style="width:600px"></img>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
[](./LICENSE)
|
||||

|
||||
[](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
|
||||
[](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/test.yml)
|
||||

|
||||
|
||||
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
|
||||
|
||||
@@ -23,6 +25,7 @@
|
||||
* [__Trace Back Info__](#trace-back-info)
|
||||
* [__Debugger__](#debugger)
|
||||
* [__REPL__](#repl)
|
||||
* [__Web Interface__](#web-interface)
|
||||
|
||||
__Contact us if having great ideas to share!__
|
||||
|
||||
@@ -34,6 +37,7 @@ __Contact us if having great ideas to share!__
|
||||
|
||||

|
||||

|
||||
[](https://deepwiki.com/ValKmjolnir/Nasal-Interpreter)
|
||||

|
||||

|
||||
|
||||
@@ -50,7 +54,7 @@ Old version of this project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5). N
|
||||
2019 summer,
|
||||
members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear,
|
||||
especially when checking syntax errors.
|
||||
So i wrote a new interpreter to help checking syntax error and runtime error.
|
||||
So i wrote a new interpreter to help checking syntax error and runtime error.
|
||||
|
||||
I wrote the lexer, parser and
|
||||
bytecode virtual machine to help checking errors.
|
||||
@@ -63,35 +67,29 @@ the interpreter a useful tool in your own projects.
|
||||
|
||||
## __Download__
|
||||
|
||||
Nightly build could be found here.
|
||||
Windows nightly build is not supported yet,
|
||||
please wait or just compile it by yourself, a Cmake file is given for Visual Studio to compile this project easily:
|
||||
Nightly build could be found here:
|
||||
|
||||
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
|
||||
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
|
||||
* windows-nightly-build: [WIP]
|
||||
* [MacOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
|
||||
* [Linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
|
||||
* [Windows-MSVC-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_windows_x86_64_msvc)
|
||||
* [Windows-MINGW-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_windows_x86_64_mingw)
|
||||
|
||||
## __Compile__
|
||||
|
||||

|
||||

|
||||

|
||||
  
|
||||
|
||||
Better download the latest update source of the interpreter and build it! It's quite easy to build this interpreter, what you need are only two things: C++ compiler and the `make`. There is no third-party library used in this project.
|
||||
Download the latest source of the interpreter and build it! It's quite easy to build, what you need are only two things: C++ compiler and the `make`. There is no third-party library used in this project.
|
||||
|
||||
### __Windows (MinGW-w64)__
|
||||
|
||||

|
||||
|
||||
Make sure thread model is `posix thread model`, otherwise no thread library exists.
|
||||
|
||||
> mkdir build;
|
||||
> mingw32-make nasal.exe -j4
|
||||
|
||||
### __Windows (Visual Studio)__
|
||||
|
||||

|
||||
|
||||
There is a [__CMakelists.txt__](./CMakeLists.txt) to create project.
|
||||
There is a [__CMakelists.txt__](./CMakeLists.txt) to create project. Recently we find how to build it with command line tools: [__Build Nasal-Interpreter on Windows__](./doc/windows-build.md).
|
||||
|
||||
### __Linux / macOS / Unix__
|
||||
|
||||
@@ -127,7 +125,7 @@ runtime.windows.set_utf8_output();
|
||||
|
||||

|
||||
|
||||
<details><summary>Must use `var` to define variables</summary>
|
||||
<details><summary>Must use `var` to define variables</summary>
|
||||
|
||||
This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
|
||||
And flightgear's nasal interpreter also has the same rule.
|
||||
@@ -152,13 +150,13 @@ If you forget to add the keyword `var`, you will get this:
|
||||
```javascript
|
||||
code: undefined symbol "i"
|
||||
--> test.nas:1:9
|
||||
|
|
||||
|
|
||||
1 | foreach(i; [0, 1, 2, 3])
|
||||
| ^ undefined symbol "i"
|
||||
|
||||
code: undefined symbol "i"
|
||||
--> test.nas:2:11
|
||||
|
|
||||
|
|
||||
2 | print(i)
|
||||
| ^ undefined symbol "i"
|
||||
```
|
||||
@@ -347,7 +345,7 @@ source code:
|
||||
if (x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
for (var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
|
||||
@@ -380,7 +378,7 @@ source code:
|
||||
--> if (x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
for (var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
|
||||
@@ -447,5 +445,51 @@ Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
|
||||
>>> use std.json;
|
||||
{stringify:func(..) {..},parse:func(..) {..}}
|
||||
|
||||
>>>
|
||||
>>>
|
||||
```
|
||||
|
||||
## __Web Interface__
|
||||
|
||||
A web-based interface is now available for trying out Nasal code directly in your browser. It includes both a code editor and an interactive REPL (WIP).
|
||||
|
||||
### Web Code Editor
|
||||
- Syntax highlighting using CodeMirror
|
||||
- Error highlighting and formatting
|
||||
- Example programs
|
||||
- Execution time display option
|
||||
- Configurable execution time limits
|
||||
- Notice: The security of the online interpreter is not well tested, please use it with sandbox mechanism or other security measures.
|
||||
|
||||
### Web REPL
|
||||
- ** IMPORTANT: The time limit in REPL is not correctly implemented yet. Thus this REPL web binding is not considered finished. Do not use it in production before it's fixed. **
|
||||
- Interactive command-line style interface in browser
|
||||
- Multi-line input support with proper prompts (`>>>` and `...`)
|
||||
- Command history navigation
|
||||
- Error handling with formatted error messages
|
||||
- Example snippets for quick testing
|
||||
|
||||
### Running the Web Interface
|
||||
|
||||
1. Build the Nasal shared library:
|
||||
```bash
|
||||
cmake -DBUILD_SHARED_LIBS=ON .
|
||||
make nasal-web
|
||||
```
|
||||
|
||||
2. Set up and run the web application:
|
||||
|
||||
For the code editor:
|
||||
```bash
|
||||
cd nasal-web-app
|
||||
npm install
|
||||
node server.js
|
||||
```
|
||||
Visit `http://127.0.0.1:3000/`
|
||||
|
||||
For the REPL:
|
||||
```bash
|
||||
cd nasal-web-app
|
||||
npm install
|
||||
node server_repl.js
|
||||
```
|
||||
Visit `http://127.0.0.1:3001/repl.html`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# __Nasal - Modern Interpreter__
|
||||
# <img src="./svg/nasal_transparent.svg" height="40px"/> __Nasal - Modern Interpreter__
|
||||
|
||||
<img src="../doc/pic/header.png" style="width:600px"></img>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
[](../LICENSE)
|
||||

|
||||
[](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
|
||||
[](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/test.yml)
|
||||
|
||||
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
||||
|
||||
@@ -26,9 +27,8 @@
|
||||
|
||||
__如果有好的意见或建议,欢迎联系我们!__
|
||||
|
||||
* __lhk101lhk101@qq.com__ (ValKmjolnir)
|
||||
|
||||
* __sidi.liang@gmail.com__ (Sidi)
|
||||
- __lhk101lhk101@qq.com__ (ValKmjolnir)
|
||||
- __sidi.liang@gmail.com__ (Sidi)
|
||||
|
||||
## __简介__
|
||||
|
||||
@@ -46,7 +46,7 @@ __如果有好的意见或建议,欢迎联系我们!__
|
||||
|
||||
### __为什么重新写 Nasal 解释器?__
|
||||
|
||||
2019 年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在 Flightgear 中提供的 nasal 控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。
|
||||
2019 年夏天,[FGPRC](https://www.fgprc.org.cn/) 的成员吐槽,在 Flightgear 中提供的 nasal 控制台中进行调试很不方便,仅仅是想检查语法错误,也要花时间打开软件等待加载进去后调试。所以我就写了一个新的解释器来帮助检查语法错误和运行时错误。
|
||||
|
||||
我编写了 nasal 的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行 nasal 程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率。
|
||||
|
||||
@@ -59,9 +59,9 @@ Windows 平台的预览版解释器现在还没配置相关流水线,
|
||||
请耐心等候或者直接在本地编译。
|
||||
我们提供了一份 Cmake 文件,可以很方便地在 Visual Studio 中编译:
|
||||
|
||||
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
|
||||
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
|
||||
* windows-nightly-build: [施工中...]
|
||||
* [MacOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
|
||||
* [Linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
|
||||
* [Windows-nightly-build](#下载) [施工中]
|
||||
|
||||
## __编译__
|
||||
|
||||
@@ -69,22 +69,19 @@ Windows 平台的预览版解释器现在还没配置相关流水线,
|
||||

|
||||

|
||||
|
||||
推荐下载最新代码包编译,这个项目非常小巧, 没有使用任何第三方库,因此编译起来非常轻松,
|
||||
只需要这两样东西: C++ 编译器以及make程序。
|
||||
下载最新代码包编译,项目非常小巧, 没有使用任何第三方库,只需要这两样即可编译: C++ 编译器以及 make 程序。
|
||||
|
||||
### __Windows 平台 (MinGW-w64)__
|
||||
|
||||

|
||||
|
||||
确保 thread model 是 `posix thread model`, 否则没有 thread 库。
|
||||
|
||||
> mkdir build;
|
||||
> mingw32-make nasal.exe -j4
|
||||
|
||||
### __Windows 平台 (Vistual Studio)__
|
||||
|
||||

|
||||
|
||||
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中创建项目。
|
||||
最近我们总结了命令行编译的方法 [__如何在 Windows 平台编译 Nasal-Interpreter__](./windows-build.md)。
|
||||
|
||||
### __Linux / macOS / Unix 平台__
|
||||
|
||||
@@ -332,7 +329,7 @@ source code:
|
||||
if (x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
for (var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
|
||||
@@ -365,7 +362,7 @@ source code:
|
||||
--> if (x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
for (var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
|
||||
@@ -433,3 +430,57 @@ Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
|
||||
|
||||
>>>
|
||||
```
|
||||
## __Web 界面__
|
||||
|
||||
现已提供基于 Web 的库以及示例界面,您可以直接在浏览器中编写和运行 Nasal 代码。该界面包括代码编辑器和交互式 REPL(未完成)。
|
||||
|
||||
### __Web 代码编辑器__
|
||||
|
||||
- **语法高亮:** 使用 CodeMirror 提供增强的编码体验。
|
||||
- **错误高亮和格式化:** 清晰显示语法和运行时错误。
|
||||
- **示例程序:** 预加载的示例,帮助您快速上手。
|
||||
- **执行时间显示选项:** 可选择查看代码执行所需时间。
|
||||
- **可配置的执行时间限制:** 设置时间限制以防止代码长时间运行。
|
||||
- **提示:** 在线解释器的安全性尚未得到广泛测试,建议配合沙盒机制等安全措施使用。
|
||||
|
||||
### __Web REPL__
|
||||
|
||||
- **重要提示:** REPL 中的代码执行时间限制尚未正确实现。此 REPL 库目前不稳定,请勿在生产环境中使用。
|
||||
- **交互式命令行界面:** 在浏览器中体验熟悉的 REPL 环境。
|
||||
- **多行输入支持:** 使用 `>>>` 和 `...` 提示符无缝输入多行代码。
|
||||
- **命令历史导航:** 使用箭头键轻松浏览命令历史。
|
||||
- **格式化的错误处理:** 接收清晰且格式化的错误消息,助力调试。
|
||||
- **快速测试的示例代码片段:** 访问并运行示例代码片段,快速测试功能。
|
||||
|
||||
### __运行 Web 界面__
|
||||
|
||||
1. **构建 Nasal 共享库:**
|
||||
|
||||
```bash
|
||||
cmake -DBUILD_SHARED_LIBS=ON .
|
||||
make nasal-web
|
||||
```
|
||||
|
||||
2. **设置并运行 Web 应用:**
|
||||
|
||||
**代码编辑器:**
|
||||
|
||||
```bash
|
||||
cd nasal-web-app
|
||||
npm install
|
||||
node server.js
|
||||
```
|
||||
|
||||
在浏览器中访问 `http://127.0.0.1:3000/` 以使用代码编辑器。
|
||||
|
||||
**REPL:**
|
||||
|
||||
```bash
|
||||
cd nasal-web-app
|
||||
npm install
|
||||
node server_repl.js
|
||||
```
|
||||
|
||||
在浏览器中访问 `http://127.0.0.1:3001/repl.html` 以使用 REPL 界面。
|
||||
|
||||
---
|
||||
|
||||
34
doc/dev.md
34
doc/dev.md
@@ -40,15 +40,15 @@ These two expressions have the same first set,so `LL(1)` is useless for this lan
|
||||
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);
|
||||
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);
|
||||
var f = func(x, y, z) { return x + y + z }(a, b, c)
|
||||
= (0, 1, 2);
|
||||
```
|
||||
|
||||
and causes fatal syntax error.
|
||||
@@ -58,9 +58,9 @@ 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);
|
||||
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)
|
||||
@@ -124,7 +124,7 @@ Hope you could help me! :)
|
||||
There's an example of byte code below:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
for (var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -175,7 +175,7 @@ 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);
|
||||
for (var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -222,8 +222,8 @@ Better use `callfv` instead of `callfh`,
|
||||
making this process slow.
|
||||
|
||||
```javascript
|
||||
var f=func(x,y){return x+y;}
|
||||
f(1024,2048);
|
||||
var f = func(x, y) { return x + y; }
|
||||
f(1024, 2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -530,12 +530,12 @@ func <0x2a3>:
|
||||
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; }
|
||||
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; }
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
@@ -40,23 +40,23 @@
|
||||
上面这个问题已经解决很久了,不过我最近发现了一个新的语法问题:
|
||||
|
||||
```javascript
|
||||
var f=func(x,y,z){return x+y+z}
|
||||
(a,b,c)=(0,1,2);
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
@@ -112,7 +112,7 @@ __该项目于2019/7/25正式开始__。
|
||||
下面是生成的字节码的样例:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
for (var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -161,7 +161,7 @@ for(var i=0;i<4000000;i+=1);
|
||||
在这次的更新中,我把全局变量和局部变量的存储结构从`unordered_map`变为了`vector`,从而提升执行效率。所以现在生成的字节码大变样了。
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
for (var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -193,15 +193,15 @@ for(var i=0;i<4000000;i+=1);
|
||||
|
||||
2021/6/3 update:
|
||||
|
||||
修复了垃圾收集器还是他妈的会重复收集的bug,这次我设计了三个标记状态来保证垃圾是被正确收集了。
|
||||
修复了垃圾收集器还是会重复收集的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);
|
||||
var f = func(x, y) { return x + y; }
|
||||
f(1024, 2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
@@ -475,12 +475,12 @@ func <0x2a3>:
|
||||
在这个版本中我们给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; }
|
||||
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; }
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
19
doc/svg/nasal.svg
Normal file
19
doc/svg/nasal.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100" height="100" fill="#9eb0d8"/>
|
||||
<g transform="translate(6,0)">
|
||||
<g fill="#1d2c4e">
|
||||
<!-- left side -->
|
||||
<path d="M 10 10 L 10 80 L 20 90 L 25 90 L 25 20 L 15 10 Z"/>
|
||||
<path d="M 10 10 L 84 84 L 84 90 L 70 90 L 10 30 Z"/>
|
||||
<!-- right-up corner -->
|
||||
<path d="M 30 10 L 70 50 L 70 55 L 30 15 Z" />
|
||||
<path d="M 30 10 L 43 10 L 70 37 L 70 55 Z" />
|
||||
<path d="M 68 10 L 68 55 L 83 55 L 83 18 L 75 10 Z" />
|
||||
<path d="M 70 50 L 70 55 L 65 55 L 55 45 L 55 35 Z"/>
|
||||
</g>
|
||||
<g fill="#97363a">
|
||||
<!-- right-down corner -->
|
||||
<path d="M 83 60 L 83 75 L 68 60 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 755 B |
19
doc/svg/nasal_transparent.svg
Normal file
19
doc/svg/nasal_transparent.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- <rect width="100" height="100" fill="#9eb0d8"/> -->
|
||||
<g transform="translate(6,0)">
|
||||
<g fill="#6e93e9">
|
||||
<!-- left side -->
|
||||
<path d="M 10 10 L 10 80 L 20 90 L 25 90 L 25 20 L 15 10 Z"/>
|
||||
<path d="M 10 10 L 84 84 L 84 90 L 70 90 L 10 30 Z"/>
|
||||
<!-- right-up corner -->
|
||||
<path d="M 30 10 L 70 50 L 70 55 L 30 15 Z" />
|
||||
<path d="M 30 10 L 43 10 L 70 37 L 70 55 Z" />
|
||||
<path d="M 68 10 L 68 55 L 83 55 L 83 18 L 75 10 Z" />
|
||||
<path d="M 70 50 L 70 55 L 65 55 L 55 45 L 55 35 Z"/>
|
||||
</g>
|
||||
<g fill="#97363a">
|
||||
<!-- right-down corner -->
|
||||
<path d="M 83 60 L 83 75 L 68 60 Z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 764 B |
@@ -1,4 +1,4 @@
|
||||
# __Tutorial__
|
||||
# <img src="./svg/nasal_transparent.svg" height="50px"/> __Tutorial__
|
||||
|
||||

|
||||
|
||||
@@ -67,7 +67,7 @@ __`vec`__ has unlimited length and can store all types of values.
|
||||
|
||||
```javascript
|
||||
var vec = [];
|
||||
var vec = [0, nil, {}, [], func(){return 0}];
|
||||
var vec = [0, nil, {}, [], func() { return 0 }];
|
||||
append(vec, 0, 1, 2);
|
||||
```
|
||||
|
||||
@@ -113,11 +113,12 @@ This type is created by native-function of nasal. If want to define a new data t
|
||||
|
||||
## Operators
|
||||
|
||||
Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that joints strings.
|
||||
Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that joints strings or vectors.
|
||||
|
||||
```javascript
|
||||
1+2-(1+3)*(2+4)/(16-9);
|
||||
"str1"~"str2";
|
||||
[0]~[1]; # should be [0, 1]
|
||||
```
|
||||
|
||||
For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values.
|
||||
@@ -162,6 +163,9 @@ a ~= "string";
|
||||
a ^= 0xff;
|
||||
a &= 0xca;
|
||||
a |= 0xba;
|
||||
|
||||
a = [0];
|
||||
a ~= [1]; # should be [0, 1]
|
||||
```
|
||||
|
||||
Operator `??` is used to check left hand side value is `nil` or not, if not,
|
||||
@@ -251,10 +255,10 @@ if (1) {
|
||||
While loop and for loop is simalar to C/C++.
|
||||
|
||||
```javascript
|
||||
while(condition) {
|
||||
while (condition) {
|
||||
continue;
|
||||
}
|
||||
for(var i = 0; i<10; i += 1) {
|
||||
for (var i = 0; i<10; i += 1) {
|
||||
break;
|
||||
}
|
||||
```
|
||||
@@ -323,7 +327,7 @@ var fib = func(f) {
|
||||
}(
|
||||
func(f) {
|
||||
return func(x) {
|
||||
if(x<2) return x;
|
||||
if (x<2) return x;
|
||||
return f(f)(x-1)+f(f)(x-2);
|
||||
}
|
||||
}
|
||||
@@ -465,7 +469,7 @@ Then complete this function using C++:
|
||||
var builtin_print(context* ctx, gc* ngc) {
|
||||
// find value with index begin from 1
|
||||
// because local[0] is reserved for value 'me'
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
for (auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
@@ -491,11 +495,11 @@ var builtin_keys(context* ctx, gc* ngc) {
|
||||
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) {
|
||||
for (const auto& iter : hash.hash().elems) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
} else {
|
||||
for(const auto& iter : hash.map().mapper) {
|
||||
for (const auto& iter : hash.map().mapper) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
}
|
||||
@@ -513,7 +517,7 @@ nasal_builtin_table builtin[] = {
|
||||
};
|
||||
```
|
||||
|
||||
At last,warp the `__print` in a nasal file:
|
||||
At last, wrap the `__print` up in a nasal file:
|
||||
|
||||
```javascript
|
||||
var print = func(elems...) {
|
||||
@@ -530,7 +534,7 @@ var print = func(elems...) {
|
||||
};
|
||||
```
|
||||
|
||||
If you don't warp built-in function in a normal nasal function,
|
||||
If you don't wrap built-in function up in a normal nasal function,
|
||||
this native function may cause __segmentation fault__ when searching arguments.
|
||||
|
||||
Use `import("filename.nas")` to get the nasal file including your built-in functions, then you could use it.
|
||||
@@ -593,7 +597,7 @@ var fib(var* args, usize size, gc* ngc) {
|
||||
// if you want your function safer, try this
|
||||
// nas_err will print the error info on screen
|
||||
// and return vm_null for runtime to interrupt
|
||||
if(num.type!=vm_num) {
|
||||
if (num.type!=vm_num) {
|
||||
return nas_err("extern_fib", "\"num\" must be number");
|
||||
}
|
||||
// ok, you must know that vm_num now is not managed by gc
|
||||
@@ -635,13 +639,13 @@ Windows(`.dll`):
|
||||
|
||||
`g++ -shared -o libfib.dll fib.o`
|
||||
|
||||
Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program:
|
||||
Then we write a test nasal file to run this fib function:
|
||||
|
||||
```javascript
|
||||
use std.dylib;
|
||||
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var dlhandle = dylib.dlopen("libfib");
|
||||
var fib = dlhandle.fib;
|
||||
for(var i = 1; i<30; i += 1)
|
||||
for (var i = 1; i<30; i += 1)
|
||||
println(dylib.dlcall(fib, i));
|
||||
dylib.dlclose(dlhandle.lib);
|
||||
```
|
||||
@@ -656,10 +660,10 @@ dylib.dlclose(dlhandle.lib);
|
||||
|
||||
```javascript
|
||||
use std.dylib;
|
||||
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var dlhandle = dylib.dlopen("libfib");
|
||||
var fib = dlhandle.fib;
|
||||
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
|
||||
for(var i = 1; i<30; i += 1)
|
||||
for (var i = 1; i<30; i += 1)
|
||||
println(invoke(fib, i));
|
||||
dylib.dlclose(dlhandle.lib);
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# __教程__
|
||||
# <img src="./svg/nasal_transparent.svg" height="50px"/> __教程__
|
||||
|
||||

|
||||
|
||||
@@ -64,7 +64,7 @@ __`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当
|
||||
|
||||
```javascript
|
||||
var vec = [];
|
||||
var vec = [0, nil, {}, [], func(){return 0}];
|
||||
var vec = [0, nil, {}, [], func() { return 0 }];
|
||||
append(vec, 0, 1, 2);
|
||||
```
|
||||
|
||||
@@ -109,11 +109,12 @@ __`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的
|
||||
|
||||
## 运算符
|
||||
|
||||
Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的运算符 `~`,用于拼接字符串。
|
||||
Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的运算符 `~`,用于拼接字符串或者数组。
|
||||
|
||||
```javascript
|
||||
1+2-(1+3)*(2+4)/(16-9);
|
||||
"str1"~"str2";
|
||||
[0]~[1]; # should be [0, 1]
|
||||
```
|
||||
|
||||
对于条件语句,可以使用`==` `!=` `<` `>` `<=` `>=`来比较数据。`and` `or` 与C/C++中 `&&` `||`运算符一致。
|
||||
@@ -157,6 +158,9 @@ a ~= "string";
|
||||
a ^= 0xff;
|
||||
a &= 0xca;
|
||||
a |= 0xba;
|
||||
|
||||
a = [0];
|
||||
a ~= [1]; # should be [0, 1]
|
||||
```
|
||||
|
||||
`??` 运算符用于检查左侧值是否为 `nil`,如果不是则返回右侧的值:
|
||||
@@ -243,10 +247,10 @@ if (1) {
|
||||
while循环和for循环大体上与C/C++是一致的。
|
||||
|
||||
```javascript
|
||||
while(condition) {
|
||||
while (condition) {
|
||||
continue;
|
||||
}
|
||||
for(var i = 0; i<10; i += 1) {
|
||||
for (var i = 0; i<10; i += 1) {
|
||||
break;
|
||||
}
|
||||
```
|
||||
@@ -310,7 +314,7 @@ var fib = func(f) {
|
||||
}(
|
||||
func(f) {
|
||||
return func(x) {
|
||||
if(x<2) return x;
|
||||
if (x<2) return x;
|
||||
return f(f)(x-1)+f(f)(x-2);
|
||||
}
|
||||
}
|
||||
@@ -453,7 +457,7 @@ var builtin_print(context*, gc*);
|
||||
var builtin_print(context* ctx, gc* ngc) {
|
||||
// 局部变量的下标其实是从 1 开始的
|
||||
// 因为 local[0] 是保留给 'me' 的空间
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
for (auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
@@ -479,11 +483,11 @@ var builtin_keys(context* ctx, gc* ngc) {
|
||||
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) {
|
||||
for (const auto& iter : hash.hash().elems) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
} else {
|
||||
for(const auto& iter : hash.map().mapper) {
|
||||
for (const auto& iter : hash.map().mapper) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
}
|
||||
@@ -575,7 +579,7 @@ var fib(var* args, usize size, gc* ngc) {
|
||||
var num = args[0];
|
||||
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
|
||||
// nas_err会输出错误信息并返回错误类型让虚拟机终止执行
|
||||
if(num.type!=vm_num) {
|
||||
if (num.type!=vm_num) {
|
||||
return nas_err("extern_fib", "\"num\" must be number");
|
||||
}
|
||||
// vm_num作为普通的数字类型,不是内存管理的对象,所以无需申请
|
||||
@@ -614,14 +618,13 @@ Windows(`.dll`):
|
||||
|
||||
`g++ -shared -o libfib.dll fib.o`
|
||||
|
||||
好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。
|
||||
下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台:
|
||||
好了,那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了:
|
||||
|
||||
```javascript
|
||||
use std.dylib;
|
||||
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var dlhandle = dylib.dlopen("libfib");
|
||||
var fib = dlhandle.fib;
|
||||
for(var i = 1; i<30; i += 1)
|
||||
for (var i = 1; i<30; i += 1)
|
||||
println(dylib.dlcall(fib, i));
|
||||
dylib.dlclose(dlhandle.lib);
|
||||
```
|
||||
@@ -636,10 +639,10 @@ dylib.dlclose(dlhandle.lib);
|
||||
|
||||
```javascript
|
||||
use std.dylib;
|
||||
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var dlhandle = dylib.dlopen("libfib");
|
||||
var fib = dlhandle.fib;
|
||||
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
|
||||
for(var i = 1; i<30; i += 1)
|
||||
for (var i = 1; i<30; i += 1)
|
||||
println(invoke(fib, i));
|
||||
dylib.dlclose(dlhandle.lib);
|
||||
```
|
||||
|
||||
27
doc/windows-build.md
Normal file
27
doc/windows-build.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Build Nasal-Interpreter on Windows
|
||||
|
||||
## MSVC / Visual Studio
|
||||
|
||||
Need CMake and Visual Studio 2022. Remember to add MSBuild.exe to Path.
|
||||
|
||||
Valid on powershell:
|
||||
|
||||
```sh
|
||||
mkdir cmake-windows-msvc
|
||||
cd cmake-windows-msvc
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022"
|
||||
MSBuild.exe nasal.sln /p:Configuration=Release /p:Platform=x64
|
||||
```
|
||||
|
||||
## MingW-W64
|
||||
|
||||
Need CMake and MingW-W64. Remember to add MingW-W64 bin to Path.
|
||||
|
||||
Valid on powershell:
|
||||
|
||||
```sh
|
||||
mkdir cmake-windows-mingw
|
||||
cd cmake-windows-mingw
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -G "MinGW Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
|
||||
mingw32-make.exe -j6
|
||||
```
|
||||
24
makefile
24
makefile
@@ -37,6 +37,7 @@ NASAL_HEADER = \
|
||||
src/natives/unix_lib.h\
|
||||
src/natives/coroutine.h\
|
||||
src/natives/regex_lib.h\
|
||||
src/natives/subprocess.h\
|
||||
src/repl/repl.h\
|
||||
src/util/fs.h\
|
||||
src/util/util.h
|
||||
@@ -61,6 +62,7 @@ NASAL_OBJECT = \
|
||||
build/math_lib.o\
|
||||
build/unix_lib.o\
|
||||
build/dylib_lib.o\
|
||||
build/subprocess.o\
|
||||
build/json_lib.o\
|
||||
build/coroutine.o\
|
||||
build/nasal_type.o\
|
||||
@@ -187,6 +189,8 @@ build/dylib_lib.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/util/util.h\
|
||||
src/util/fs.h\
|
||||
src/natives/dylib_lib.h src/natives/dylib_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/natives/dylib_lib.cpp -o build/dylib_lib.o
|
||||
|
||||
@@ -212,6 +216,14 @@ build/regex_lib.o: \
|
||||
src/natives/regex_lib.h src/natives/regex_lib.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/natives/regex_lib.cpp -o build/regex_lib.o
|
||||
|
||||
build/subprocess.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/natives/subprocess.h src/natives/subprocess.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/natives/subprocess.cpp -o build/subprocess.o
|
||||
|
||||
|
||||
build/fg_props.o: \
|
||||
src/nasal.h\
|
||||
src/nasal_type.h\
|
||||
@@ -283,7 +295,7 @@ clean:
|
||||
@ rm $(NASAL_OBJECT)
|
||||
|
||||
.PHONY: test
|
||||
test:nasal
|
||||
test:
|
||||
@ ./nasal -t -d test/andy_gc_test.nas
|
||||
@ ./nasal test/argparse_test.nas
|
||||
@ ./nasal -e test/ascii-art.nas
|
||||
@@ -319,10 +331,12 @@ test:nasal
|
||||
@ ./nasal -e test/qrcode.nas
|
||||
@ ./nasal -t -d test/quick_sort.nas
|
||||
@ ./nasal -t -d test/regex_test.nas
|
||||
@ ./nasal -t -d test/replace_test.nas
|
||||
@ ./nasal -e test/scalar.nas hello world
|
||||
@ ./nasal -e test/trait.nas
|
||||
@ ./nasal -t test/subprocess_test.nas
|
||||
@ ./nasal -t test/trait.nas
|
||||
@ ./nasal -t -d test/turingmachine.nas
|
||||
@ ./nasal -d test/wavecollapse.nas
|
||||
@ ./nasal -d test/wavecity.nas
|
||||
@ ./nasal test/word_collector.nas test/md5compare.nas
|
||||
@ ./nasal -t -d test/wavecollapse.nas
|
||||
@ ./nasal -t -d test/wavecity.nas
|
||||
@ ./nasal -t test/word_collector.nas test/md5compare.nas
|
||||
@ ./nasal -t -d test/ycombinator.nas
|
||||
|
||||
@@ -32,7 +32,7 @@ var quick_fib(var* args, usize size, gc* ngc) {
|
||||
return var::num(num);
|
||||
}
|
||||
double a = 1, b = 1, res = 0;
|
||||
for(double i = 1; i<num; ++i) {
|
||||
for (double i = 1; i<num; ++i) {
|
||||
res = a+b;
|
||||
a = b;
|
||||
b = res;
|
||||
@@ -91,10 +91,10 @@ var set_new_ghost(var* args, usize size, gc* ngc) {
|
||||
}
|
||||
f64 num = args[1].num();
|
||||
|
||||
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num);
|
||||
res.ghost().get<ghost_obj>()->number = static_cast<u32>(num);
|
||||
std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n";
|
||||
|
||||
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string = ngc->newstr("just for test");
|
||||
res.ghost().get<ghost_obj>()->test_string = ngc->newstr("just for test");
|
||||
std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n";
|
||||
return nil;
|
||||
}
|
||||
@@ -106,9 +106,9 @@ var print_new_ghost(var* args, usize size, gc* ngc) {
|
||||
return nil;
|
||||
}
|
||||
std::cout << "print_new_ghost: " << res.ghost() << " number = "
|
||||
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number
|
||||
<< res.ghost().get<ghost_obj>()->number
|
||||
<< " test_string = "
|
||||
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string
|
||||
<< res.ghost().get<ghost_obj>()->test_string
|
||||
<< "\n";
|
||||
return nil;
|
||||
}
|
||||
@@ -124,7 +124,7 @@ module_func_info func_tbl[] = {
|
||||
|
||||
}
|
||||
|
||||
NASAL_EXTERN module_func_info* get() {
|
||||
NASAL_EXPORT module_func_info* get() {
|
||||
return fib_module::func_tbl;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ module_func_info func_tbl[] = {
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
NASAL_EXTERN module_func_info* get() {
|
||||
NASAL_EXPORT module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std.dylib;
|
||||
use std.os;
|
||||
|
||||
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var _dl = dylib.dlopen("libfib");
|
||||
|
||||
var _fib = _dl.fib;
|
||||
|
||||
@@ -49,7 +49,7 @@ var test_ghost = func() {
|
||||
print("\n");
|
||||
set_ghost(ghost, 114); # success
|
||||
print("\n");
|
||||
for(var i = 0; i<256; i+=1) {
|
||||
for (var i = 0; i<256; i+=1) {
|
||||
var temp = []; # try to trigger gc
|
||||
}
|
||||
print("\n");
|
||||
|
||||
@@ -6,7 +6,7 @@ var (
|
||||
getch,
|
||||
nonblock
|
||||
) = func {
|
||||
var lib = dylib.dlopen("libkey"~(os.platform()=="windows"? ".dll":".so"));
|
||||
var lib = dylib.dlopen("libkey");
|
||||
var kb = lib.nas_kbhit;
|
||||
var gt = lib.nas_getch;
|
||||
var nb = lib.nas_noblock;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std.dylib;
|
||||
use std.os;
|
||||
|
||||
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));
|
||||
var _dl = dylib.dlopen("libmat");
|
||||
|
||||
var _vec2 = _dl.nas_vec2;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std.dylib;
|
||||
use std.os;
|
||||
|
||||
var socket = func() {
|
||||
var lib = dylib.dlopen("libnasock"~(os.platform()=="windows"? ".dll":".so"));
|
||||
var lib = dylib.dlopen("libnasock");
|
||||
|
||||
var sock = lib.nas_socket;
|
||||
var closesocket = lib.nas_closesocket;
|
||||
|
||||
@@ -12,9 +12,9 @@ ifndef OS
|
||||
OS = $(shell uname)
|
||||
endif
|
||||
ifeq ($(OS), Darwin)
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15 -I ../src
|
||||
else
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC
|
||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -I ../src
|
||||
endif
|
||||
|
||||
all: $(dynamic_libs_so)
|
||||
@@ -29,7 +29,7 @@ libfib.so: fib.cpp $(used_header) $(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) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o -I ../src
|
||||
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
|
||||
@ del fib.o
|
||||
|
||||
@@ -40,7 +40,7 @@ libkey.so: keyboard.cpp $(used_header) $(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) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static -I ../src
|
||||
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
|
||||
@ del keyboard.o
|
||||
|
||||
@@ -51,7 +51,7 @@ libnasock.so: nasocket.cpp $(used_header) $(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) -std=$(STD) -c -O3 nasocket.cpp -fPIC -I ../src -o nasocket.o -lwsock32 -static
|
||||
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
|
||||
@ del nasocket.o
|
||||
|
||||
@@ -62,7 +62,7 @@ libmat.so: matrix.cpp $(used_header) $(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) -std=$(STD) -c -O3 matrix.cpp -fPIC -I ../src -o matrix.o -static
|
||||
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
|
||||
@ del matrix.o
|
||||
|
||||
|
||||
@@ -295,7 +295,7 @@ module_func_info func_tbl[] = {
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
NASAL_EXTERN module_func_info* get() {
|
||||
NASAL_EXPORT module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ module_func_info func_tbl[] = {
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
NASAL_EXTERN module_func_info* get() {
|
||||
NASAL_EXPORT module_func_info* get() {
|
||||
return func_tbl;
|
||||
}
|
||||
|
||||
|
||||
17
nasal-web-app/package.json
Normal file
17
nasal-web-app/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "nasal-web-app",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.21.1",
|
||||
"ffi-napi": "^4.0.3",
|
||||
"yargs": "^17.7.2"
|
||||
}
|
||||
}
|
||||
638
nasal-web-app/public/index.html
Normal file
638
nasal-web-app/public/index.html
Normal file
@@ -0,0 +1,638 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nasal Interpreter Web Demo</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 30px;
|
||||
background: linear-gradient(145deg, #2c3e50, #3498db);
|
||||
border-radius: 12px;
|
||||
color: white;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.ascii-art {
|
||||
font-family: 'Monaco', monospace;
|
||||
color: #ecf0f1;
|
||||
text-shadow: 0 0 10px rgba(255,255,255,0.3);
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
margin: 0 0 10px 0;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(to right, #fff, #ecf0f1);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2em;
|
||||
color: #ecf0f1;
|
||||
margin: 0 0 25px 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.credits {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.credit-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.credit-label {
|
||||
color: #bdc3c7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.credit-link {
|
||||
text-decoration: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 20px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.credit-link:hover {
|
||||
background: rgba(255,255,255,0.2);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.author {
|
||||
color: #bdc3c7;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.header {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.credits {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.code-section, .output-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: 400px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.output-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #2ecc71;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: #e74c3c;
|
||||
}
|
||||
|
||||
#output {
|
||||
height: 400px;
|
||||
background: #282c34;
|
||||
color: #abb2bf;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin: 4px 0;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: #2c313a;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
border-left: 3px solid #e06c75;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
border-left: 3px solid #98c379;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
color: #5c6370;
|
||||
font-size: 0.9em;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.terminal-output {
|
||||
margin: 8px 0 0 0;
|
||||
padding: 8px;
|
||||
background: #21252b;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.terminal-output:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message + .message {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #98c379;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: #e06c75;
|
||||
}
|
||||
|
||||
.output-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.output-header h2 {
|
||||
margin: 0;
|
||||
color: #abb2bf;
|
||||
}
|
||||
|
||||
.controls {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #bdc3c7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.examples {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.example-btn {
|
||||
background-color: #27ae60;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.example-btn:hover {
|
||||
background-color: #219a52;
|
||||
}
|
||||
|
||||
.terminal-output {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
background: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
font-size: 0.9em;
|
||||
color: #7f8c8d;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.terminal-red-bold {
|
||||
color: #ff5555;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.terminal-cyan-bold {
|
||||
color: #8be9fd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.terminal-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.terminal-caret {
|
||||
color: #ff5555;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #2c3e50;
|
||||
border-left: 4px solid #e74c3c;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: #2c3e50;
|
||||
border-left: 4px solid #2ecc71;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
#output {
|
||||
background: #34495e;
|
||||
color: #ecf0f1;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
box-shadow: inset 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.error-message pre, .success-message pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Error message styling */
|
||||
.error-type {
|
||||
color: #e06c75;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
color: #abb2bf;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-arrow {
|
||||
color: #56b6c2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-file {
|
||||
color: #e06c75;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-loc {
|
||||
color: #abb2bf;
|
||||
}
|
||||
|
||||
.error-line-number {
|
||||
color: #56b6c2;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
color: #abb2bf;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.error-pointer-space {
|
||||
color: #abb2bf;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.error-pointer {
|
||||
color: #e06c75;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.terminal-output {
|
||||
margin: 8px 0 0 0;
|
||||
padding: 8px;
|
||||
background: #21252b;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
overflow-x: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.timing-checkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
color: #abb2bf;
|
||||
}
|
||||
|
||||
.timing-checkbox input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<pre class="ascii-art">
|
||||
__ _
|
||||
/\ \ \__ _ ___ __ _| |
|
||||
/ \/ / _` / __|/ _` | |
|
||||
/ /\ / (_| \__ \ (_| | |
|
||||
\_\ \/ \__,_|___/\__,_|_|
|
||||
</pre>
|
||||
</div>
|
||||
<h1>Nasal Interpreter Web Demo</h1>
|
||||
<p class="subtitle">Write and execute Nasal code directly in your browser</p>
|
||||
<div class="credits">
|
||||
<div class="credit-item">
|
||||
<span class="credit-label">Powered by</span>
|
||||
<a href="https://www.fgprc.org.cn/nasal_interpreter.html" class="credit-link">
|
||||
<span class="highlight">Nasal Interpreter</span>
|
||||
<span class="author">by ValKmjolnir</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="credit-item">
|
||||
<span class="credit-label">Web App by</span>
|
||||
<a href="https://sidi762.github.io" class="credit-link">
|
||||
<span class="highlight">LIANG Sidi</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-container">
|
||||
<div class="code-section">
|
||||
<h2>Code Editor</h2>
|
||||
<textarea id="code">var x = 1 + 2;
|
||||
println(x);</textarea>
|
||||
</div>
|
||||
<div class="output-section">
|
||||
<div class="output-header">
|
||||
<h2>Output</h2>
|
||||
<div id="status"></div>
|
||||
</div>
|
||||
<div id="output"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="runBtn" onclick="runCode()">Run</button>
|
||||
<button onclick="clearOutput()">Clear Output</button>
|
||||
<label class="timing-checkbox">
|
||||
<input type="checkbox" id="showTime"> Show execution time
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="examples">
|
||||
<h3>Example Programs:</h3>
|
||||
<button class="example-btn" onclick="loadExample('basic')">Basic Math</button>
|
||||
<button class="example-btn" onclick="loadExample('loops')">Loops</button>
|
||||
<button class="example-btn" onclick="loadExample('functions')">Functions</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/javascript/javascript.min.js"></script>
|
||||
<script>
|
||||
// Initialize CodeMirror
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
mode: "javascript", // Using JavaScript mode as it's close enough to Nasal
|
||||
theme: "monokai",
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets: true,
|
||||
indentUnit: 4,
|
||||
tabSize: 4,
|
||||
lineWrapping: true
|
||||
});
|
||||
|
||||
// Example programs
|
||||
const examples = {
|
||||
basic: `# Basic math operations
|
||||
var a = 10;
|
||||
var b = 5;
|
||||
|
||||
println("Addition: ", a + b);
|
||||
println("Subtraction: ", a - b);
|
||||
println("Multiplication: ", a * b);
|
||||
println("Division: ", a / b);`,
|
||||
|
||||
loops: `# Loop example
|
||||
var sum = 0;
|
||||
for (var i = 1; i <= 5; i += 1) {
|
||||
sum += i;
|
||||
println("Current sum: ", sum);
|
||||
}
|
||||
println("Final sum: ", sum);`,
|
||||
|
||||
functions: `# Function example
|
||||
var factorial = func(n) {
|
||||
if (n <= 1) return 1;
|
||||
return n * factorial(n - 1);
|
||||
}
|
||||
|
||||
for (var i = 0; i <= 5; i += 1) {
|
||||
println("Factorial of ", i, " is ", factorial(i));
|
||||
}`
|
||||
};
|
||||
|
||||
function loadExample(type) {
|
||||
editor.setValue(examples[type]);
|
||||
}
|
||||
|
||||
function formatTerminalOutput(text) {
|
||||
// Remove the output/error message header if present
|
||||
text = text.replace(/^\[(.*?)\] (Output|Error):\s*\n/, '');
|
||||
|
||||
// Convert ANSI color codes to CSS classes
|
||||
const colorMap = {
|
||||
'\u001b[91;1m': '<span class="terminal-red-bold">', // bright red bold
|
||||
'\u001b[36;1m': '<span class="terminal-cyan-bold">', // bright cyan bold
|
||||
'\u001b[0m': '</span>', // reset
|
||||
'\u001b[1m': '<span class="terminal-bold">' // bold
|
||||
};
|
||||
|
||||
// Replace ANSI codes with HTML spans
|
||||
Object.entries(colorMap).forEach(([code, html]) => {
|
||||
text = text.replace(new RegExp(escapeRegExp(code), 'g'), html);
|
||||
});
|
||||
|
||||
// Preserve whitespace and arrow formatting
|
||||
text = text.replace(/\^/g, '<span class="terminal-caret">^</span>');
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
// Utility function to escape special characters in strings for RegExp
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function formatErrorMessage(text) {
|
||||
// Replace ANSI escape codes with CSS classes
|
||||
return text
|
||||
// Remove any existing formatting first
|
||||
.replace(/\u001b\[\d+(?:;\d+)*m/g, '')
|
||||
// Format the error line
|
||||
.replace(/^parse: (.+)$/m, '<span class="error-type">parse:</span> <span class="error-desc">$1</span>')
|
||||
// Format the file location
|
||||
.replace(/^\s*--> (.+?)(\d+):(\d+)$/m, '<span class="error-arrow">--></span> <span class="error-file">$1</span><span class="error-loc">$2:$3</span>')
|
||||
// Format the code line
|
||||
.replace(/^(\d+ \|)(.*)$/m, '<span class="error-line-number">$1</span><span class="error-code">$2</span>')
|
||||
// Format the error pointer
|
||||
.replace(/^(\s*\|)(\s*)(\^+)$/m, '<span class="error-line-number">$1</span><span class="error-pointer-space">$2</span><span class="error-pointer">$3</span>');
|
||||
}
|
||||
|
||||
async function runCode() {
|
||||
const runBtn = document.getElementById('runBtn');
|
||||
const output = document.getElementById('output');
|
||||
const status = document.getElementById('status');
|
||||
const showTime = document.getElementById('showTime').checked;
|
||||
|
||||
try {
|
||||
runBtn.disabled = true;
|
||||
const code = editor.getValue();
|
||||
|
||||
const response = await fetch('/eval', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
code,
|
||||
showTime
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
|
||||
if (data.error) {
|
||||
status.innerHTML = '<span class="status-indicator status-error"></span>';
|
||||
output.innerHTML += `<div class="message error-message">
|
||||
<span class="timestamp">[${timestamp}]</span>
|
||||
<pre class="terminal-output">${formatErrorMessage(data.error)}</pre>
|
||||
</div>`;
|
||||
} else {
|
||||
status.innerHTML = '<span class="status-indicator status-success"></span>';
|
||||
output.innerHTML += `<div class="message success-message">
|
||||
<span class="timestamp">[${timestamp}]</span>
|
||||
<pre class="terminal-output">${data.result.trim()}</pre>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
output.scrollTop = output.scrollHeight;
|
||||
} catch (err) {
|
||||
status.innerHTML = '<span class="status-indicator status-error"></span>';
|
||||
output.innerHTML += `<div class="message error-message">
|
||||
<span class="timestamp">[${new Date().toLocaleTimeString()}]</span>
|
||||
<pre class="terminal-output">Server Error: ${err.message}</pre>
|
||||
</div>`;
|
||||
} finally {
|
||||
runBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearOutput() {
|
||||
document.getElementById('output').innerHTML = '';
|
||||
document.getElementById('status').innerHTML = '';
|
||||
}
|
||||
|
||||
// Utility function to escape HTML to prevent XSS
|
||||
function escapeHtml(text) {
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
240
nasal-web-app/public/repl.html
Normal file
240
nasal-web-app/public/repl.html
Normal file
@@ -0,0 +1,240 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nasal Interpreter Web REPL</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.repl-container {
|
||||
background: #34495e;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
height: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.repl-output {
|
||||
flex-grow: 1;
|
||||
background: #21252b;
|
||||
color: #abb2bf;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.repl-input-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.repl-prompt {
|
||||
color: #3498db;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
padding: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.repl-input {
|
||||
flex-grow: 1;
|
||||
background: #21252b;
|
||||
border: none;
|
||||
color: #abb2bf;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
outline: none;
|
||||
resize: none;
|
||||
min-height: 24px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.controls {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background-color: #bdc3c7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.examples {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.example-btn {
|
||||
background-color: #27ae60;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.example-btn:hover {
|
||||
background-color: #219a52;
|
||||
}
|
||||
|
||||
.output-line {
|
||||
margin: 2px 0;
|
||||
min-height: 1.2em;
|
||||
}
|
||||
|
||||
.input-line {
|
||||
color: #3498db;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.error-line {
|
||||
color: #e74c3c;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.result-line {
|
||||
color: #2ecc71;
|
||||
white-space: pre;
|
||||
margin-left: 4px; /* Slight indent for results */
|
||||
}
|
||||
|
||||
.system-message {
|
||||
color: #7f8c8d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: #95a5a6;
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.error-type {
|
||||
color: #ff5f5f;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
color: #abb2bf;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-arrow, .error-line-number {
|
||||
color: #56b6c2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-file {
|
||||
color: #ff5f5f;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-code {
|
||||
color: #abb2bf;
|
||||
}
|
||||
|
||||
.error-pointer-space {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.error-pointer {
|
||||
color: #ff5f5f;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-red-bold {
|
||||
color: #e06c75;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-cyan-bold {
|
||||
color: #56b6c2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.repl-container {
|
||||
height: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Nasal Interpreter Web REPL</h1>
|
||||
<p>Interactive Read-Eval-Print Loop for Nasal</p>
|
||||
<p>Powered by ValKmjolnir's <a href="https://www.fgprc.org.cn/nasal_interpreter.html">Nasal Interpreter</a>, Web App by <a href="https://sidi762.github.io">LIANG Sidi</a></p>
|
||||
</div>
|
||||
|
||||
<div class="repl-container">
|
||||
<div id="repl-output" class="repl-output">
|
||||
<div class="output-line">Welcome to Nasal Web REPL Demo!</div>
|
||||
</div>
|
||||
<div class="repl-input-container">
|
||||
<span class="repl-prompt">>>></span>
|
||||
<textarea id="repl-input" class="repl-input" rows="1"
|
||||
placeholder="Enter Nasal code here..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button onclick="clearREPL()">Clear REPL</button>
|
||||
</div>
|
||||
|
||||
<div class="examples">
|
||||
<h3>Example Commands:</h3>
|
||||
<button class="example-btn" onclick="insertExample('basic')">Basic Math</button>
|
||||
<button class="example-btn" onclick="insertExample('loops')">Loops</button>
|
||||
<button class="example-btn" onclick="insertExample('functions')">Functions</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="repl.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
249
nasal-web-app/public/repl.js
Normal file
249
nasal-web-app/public/repl.js
Normal file
@@ -0,0 +1,249 @@
|
||||
let replSessionId = null;
|
||||
let multilineInput = [];
|
||||
let historyIndex = -1;
|
||||
let commandHistory = [];
|
||||
let multilineBuffer = [];
|
||||
let isMultilineMode = false;
|
||||
|
||||
// Initialize REPL
|
||||
async function initRepl() {
|
||||
try {
|
||||
const response = await fetch('/repl/init', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.sessionId) {
|
||||
replSessionId = data.sessionId;
|
||||
console.log('REPL session initialized:', replSessionId);
|
||||
|
||||
// Display version info
|
||||
appendOutput('Nasal REPL interpreter version ' + data.version);
|
||||
appendOutput('Type your code below and press Enter to execute.');
|
||||
appendOutput('Use Shift+Enter for multiline input.\n');
|
||||
showPrompt();
|
||||
} else {
|
||||
throw new Error('Failed to initialize REPL session');
|
||||
}
|
||||
} catch (err) {
|
||||
appendOutput(`Error: ${err.message}`, 'error-line');
|
||||
}
|
||||
}
|
||||
|
||||
// Format error messages to match command-line REPL
|
||||
function formatError(error) {
|
||||
// Split the error message into lines
|
||||
const lines = error.split('\n');
|
||||
return lines.map(line => {
|
||||
// Add appropriate indentation for the error pointer
|
||||
if (line.includes('-->')) {
|
||||
return ' ' + line;
|
||||
} else if (line.includes('^')) {
|
||||
return ' ' + line;
|
||||
}
|
||||
return line;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
// Handle input
|
||||
const input = document.getElementById('repl-input');
|
||||
input.addEventListener('keydown', async (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (e.shiftKey) {
|
||||
// Shift+Enter: add newline
|
||||
const pos = input.selectionStart;
|
||||
const value = input.value;
|
||||
input.value = value.substring(0, pos) + '\n' + value.substring(pos);
|
||||
input.selectionStart = input.selectionEnd = pos + 1;
|
||||
input.style.height = 'auto';
|
||||
input.style.height = input.scrollHeight + 'px';
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
const code = input.value.trim();
|
||||
|
||||
// Skip empty lines but still show prompt
|
||||
if (!code) {
|
||||
showPrompt(isMultilineMode ? '... ' : '>>> ');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// First check if input is complete
|
||||
const checkResponse = await fetch('/repl/eval', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sessionId: replSessionId,
|
||||
line: code,
|
||||
check: true,
|
||||
buffer: multilineBuffer // Send existing buffer
|
||||
})
|
||||
});
|
||||
|
||||
const checkData = await checkResponse.json();
|
||||
|
||||
if (checkData.needsMore) {
|
||||
// Add to multiline buffer and show continuation prompt
|
||||
multilineBuffer.push(code);
|
||||
isMultilineMode = true;
|
||||
|
||||
// Display the input with continuation prompt
|
||||
appendOutput(code, 'input-line', multilineBuffer.length === 1 ? '>>> ' : '... ');
|
||||
|
||||
input.value = '';
|
||||
showPrompt('... ');
|
||||
return;
|
||||
}
|
||||
|
||||
// If we were in multiline mode, add the final line
|
||||
if (isMultilineMode) {
|
||||
multilineBuffer.push(code);
|
||||
}
|
||||
|
||||
// Get the complete code to evaluate
|
||||
const fullCode = isMultilineMode ?
|
||||
multilineBuffer.join('\n') : code;
|
||||
|
||||
// Display the input
|
||||
appendOutput(code, 'input-line', isMultilineMode ? '... ' : '>>> ');
|
||||
|
||||
// Evaluate the code
|
||||
const response = await fetch('/repl/eval', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
sessionId: replSessionId,
|
||||
line: fullCode
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
appendOutput(formatError(data.error.trim()), 'error-line');
|
||||
} else if (data.result) {
|
||||
handleResult(data.result);
|
||||
}
|
||||
|
||||
// Reset multiline state
|
||||
multilineBuffer = [];
|
||||
isMultilineMode = false;
|
||||
input.value = '';
|
||||
showPrompt('>>> ');
|
||||
|
||||
} catch (err) {
|
||||
appendOutput(`Error: ${err.message}`, 'error-line');
|
||||
multilineBuffer = [];
|
||||
isMultilineMode = false;
|
||||
input.value = '';
|
||||
showPrompt('>>> ');
|
||||
}
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (historyIndex > 0) {
|
||||
historyIndex--;
|
||||
input.value = commandHistory[historyIndex];
|
||||
}
|
||||
} else if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (historyIndex < commandHistory.length - 1) {
|
||||
historyIndex++;
|
||||
input.value = commandHistory[historyIndex];
|
||||
} else {
|
||||
historyIndex = commandHistory.length;
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-resize input
|
||||
input.addEventListener('input', () => {
|
||||
input.style.height = 'auto';
|
||||
input.style.height = input.scrollHeight + 'px';
|
||||
});
|
||||
|
||||
// Show prompt and scroll to bottom
|
||||
function showPrompt(prompt = '>>> ') {
|
||||
const promptSpan = document.querySelector('.repl-prompt');
|
||||
if (promptSpan) {
|
||||
promptSpan.textContent = prompt;
|
||||
}
|
||||
}
|
||||
|
||||
// Append output to REPL
|
||||
function appendOutput(text, className = '', prefix = '') {
|
||||
const output = document.getElementById('repl-output');
|
||||
const line = document.createElement('div');
|
||||
line.className = `output-line ${className}`;
|
||||
|
||||
line.innerHTML = prefix + formatErrorMessage(text);
|
||||
|
||||
|
||||
output.appendChild(line);
|
||||
output.scrollTop = output.scrollHeight;
|
||||
}
|
||||
|
||||
// Clear REPL
|
||||
function clearREPL() {
|
||||
const output = document.getElementById('repl-output');
|
||||
output.innerHTML = '';
|
||||
appendOutput('Screen cleared', 'system-message');
|
||||
showPrompt();
|
||||
}
|
||||
|
||||
// Example snippets
|
||||
const examples = {
|
||||
basic: `var x = 1011 + 1013;
|
||||
println("x = ", x);`,
|
||||
|
||||
loops: `var sum = 0;
|
||||
for (var i = 1; i <= 5; i += 1) {
|
||||
sum += i;
|
||||
}
|
||||
println("Sum:", sum);`,
|
||||
|
||||
functions: `var factorial = func(n) {
|
||||
if (n <= 1) return 1;
|
||||
return n * factorial(n - 1);
|
||||
};
|
||||
factorial(5);`
|
||||
};
|
||||
|
||||
// Insert example into input
|
||||
function insertExample(type) {
|
||||
input.value = examples[type];
|
||||
input.style.height = 'auto';
|
||||
input.style.height = input.scrollHeight + 'px';
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// Initialize REPL on page load
|
||||
window.addEventListener('load', initRepl);
|
||||
|
||||
// Add these utility functions
|
||||
function formatErrorMessage(text) {
|
||||
// Replace ANSI escape codes with CSS classes
|
||||
return text
|
||||
// Remove any existing formatting first
|
||||
.replace(/\u001b\[\d+(?:;\d+)*m/g, '')
|
||||
// Format the error line
|
||||
.replace(/^parse: (.+)$/m, '<span class="error-type">parse:</span> <span class="error-desc">$1</span>')
|
||||
// Format the file location
|
||||
.replace(/^\s*--> (.+?)(\d+):(\d+)$/m, '<span class="error-arrow">--></span> <span class="error-file">$1</span><span class="error-loc">$2:$3</span>')
|
||||
// Format the code line
|
||||
.replace(/^(\d+ \|)(.*)$/m, '<span class="error-line-number">$1</span><span class="error-code">$2</span>')
|
||||
// Format the error pointer
|
||||
.replace(/^(\s*\|)(\s*)(\^+)$/m, '<span class="error-line-number">$1</span><span class="error-pointer-space">$2</span><span class="error-pointer">$3</span>');
|
||||
}
|
||||
|
||||
function handleResult(result) {
|
||||
const lines = result.split('\n');
|
||||
lines.forEach(line => {
|
||||
if (line.trim()) {
|
||||
appendOutput(line.trim(), 'result-line');
|
||||
}
|
||||
});
|
||||
}
|
||||
96
nasal-web-app/server.js
Normal file
96
nasal-web-app/server.js
Normal file
@@ -0,0 +1,96 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const koffi = require('koffi');
|
||||
require('expose-gc');
|
||||
|
||||
// Parse command line arguments
|
||||
const argv = yargs(hideBin(process.argv))
|
||||
.usage('Usage: $0 [options]')
|
||||
.option('verbose', {
|
||||
alias: 'v',
|
||||
type: 'boolean',
|
||||
description: 'Run with verbose logging'
|
||||
})
|
||||
.option('port', {
|
||||
alias: 'p',
|
||||
type: 'number',
|
||||
description: 'Port to run the server on',
|
||||
default: 3000
|
||||
})
|
||||
.option('host', {
|
||||
type: 'string',
|
||||
description: 'Host to run the server on',
|
||||
default: 'localhost'
|
||||
})
|
||||
.help()
|
||||
.alias('help', 'h')
|
||||
.version()
|
||||
.argv;
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.static('public'));
|
||||
|
||||
let nasalLib;
|
||||
try {
|
||||
// First load the library
|
||||
const lib = koffi.load(path.join(__dirname, '../module/libnasal-web.dylib'));
|
||||
|
||||
// Then declare the functions explicitly
|
||||
nasalLib = {
|
||||
nasal_init: lib.func('nasal_init', 'void*', []),
|
||||
nasal_cleanup: lib.func('nasal_cleanup', 'void', ['void*']),
|
||||
nasal_eval: lib.func('nasal_eval', 'const char*', ['void*', 'const char*', 'int']),
|
||||
nasal_get_error: lib.func('nasal_get_error', 'const char*', ['void*'])
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to load nasal library:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
app.post('/eval', (req, res) => {
|
||||
const { code, showTime = false } = req.body;
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: 'No code provided' });
|
||||
}
|
||||
|
||||
if (argv.verbose) {
|
||||
console.log('Received code evaluation request:', code);
|
||||
console.log('Show time:', showTime);
|
||||
}
|
||||
|
||||
const ctx = nasalLib.nasal_init();
|
||||
try {
|
||||
const result = nasalLib.nasal_eval(ctx, code, showTime ? 1 : 0);
|
||||
const error = nasalLib.nasal_get_error(ctx);
|
||||
|
||||
if (error && error !== 'null') {
|
||||
if (argv.verbose) console.log('Nasal error:', error);
|
||||
res.json({ error: error });
|
||||
} else if (result && result.trim() !== '') {
|
||||
if (argv.verbose) console.log('Nasal output:', result);
|
||||
res.json({ result: result });
|
||||
} else {
|
||||
if (argv.verbose) console.log('No output or error returned');
|
||||
res.json({ error: 'No output or error returned' });
|
||||
}
|
||||
} catch (err) {
|
||||
if (argv.verbose) console.error('Server error:', err);
|
||||
res.status(500).json({ error: err.message });
|
||||
} finally {
|
||||
if (argv.verbose) console.log('Cleaning up Nasal context');
|
||||
nasalLib.nasal_cleanup(ctx);
|
||||
global.gc()
|
||||
}
|
||||
});
|
||||
|
||||
const PORT = argv.port || 3000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
console.log(`Visit http://localhost:${PORT} to use the Nasal interpreter`);
|
||||
if (argv.verbose) console.log('Verbose logging enabled');
|
||||
});
|
||||
188
nasal-web-app/server_repl.js
Normal file
188
nasal-web-app/server_repl.js
Normal file
@@ -0,0 +1,188 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs/yargs');
|
||||
const { hideBin } = require('yargs/helpers');
|
||||
const koffi = require('koffi');
|
||||
|
||||
// Parse command line arguments
|
||||
const argv = yargs(hideBin(process.argv))
|
||||
.usage('Usage: $0 [options]')
|
||||
.option('verbose', {
|
||||
alias: 'v',
|
||||
type: 'boolean',
|
||||
description: 'Run with verbose logging'
|
||||
})
|
||||
.option('port', {
|
||||
alias: 'p',
|
||||
type: 'number',
|
||||
description: 'Port to run the server on',
|
||||
default: 3001
|
||||
})
|
||||
.option('host', {
|
||||
type: 'string',
|
||||
description: 'Host to run the server on',
|
||||
default: 'localhost'
|
||||
})
|
||||
.help()
|
||||
.alias('help', 'h')
|
||||
.version()
|
||||
.argv;
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.use(express.static('public'));
|
||||
|
||||
|
||||
// Load Nasal REPL library functions
|
||||
let nasalLib;
|
||||
try {
|
||||
const lib = koffi.load(path.join(__dirname, '../module/libnasal-web.dylib'));
|
||||
|
||||
nasalLib = {
|
||||
nasal_repl_init: lib.func('nasal_repl_init', 'void*', []),
|
||||
nasal_repl_cleanup: lib.func('nasal_repl_cleanup', 'void', ['void*']),
|
||||
nasal_repl_eval: lib.func('nasal_repl_eval', 'const char*', ['void*', 'const char*']),
|
||||
nasal_repl_is_complete: lib.func('nasal_repl_is_complete', 'int', ['void*', 'const char*']),
|
||||
nasal_repl_get_version: lib.func('nasal_repl_get_version', 'const char*', [])
|
||||
};
|
||||
|
||||
if (argv.verbose) {
|
||||
console.log('REPL Library loaded successfully');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load REPL library:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Store active REPL sessions
|
||||
const replSessions = new Map();
|
||||
|
||||
// Clean up inactive sessions periodically (30 minutes timeout)
|
||||
const SESSION_TIMEOUT = 30 * 60 * 1000;
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [sessionId, session] of replSessions.entries()) {
|
||||
if (now - session.lastAccess > SESSION_TIMEOUT) {
|
||||
if (argv.verbose) {
|
||||
console.log(`Cleaning up inactive session: ${sessionId}`);
|
||||
}
|
||||
nasalLib.nasal_repl_cleanup(session.context);
|
||||
replSessions.delete(sessionId);
|
||||
}
|
||||
}
|
||||
}, 60000); // Check every minute
|
||||
|
||||
app.post('/repl/init', (req, res) => {
|
||||
try {
|
||||
const ctx = nasalLib.nasal_repl_init();
|
||||
const sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
const version = nasalLib.nasal_repl_get_version();
|
||||
|
||||
replSessions.set(sessionId, {
|
||||
context: ctx,
|
||||
lastAccess: Date.now()
|
||||
});
|
||||
|
||||
if (argv.verbose) {
|
||||
console.log(`New REPL session initialized: ${sessionId}`);
|
||||
}
|
||||
|
||||
res.json({
|
||||
sessionId,
|
||||
version
|
||||
});
|
||||
} catch (err) {
|
||||
if (argv.verbose) {
|
||||
console.error('Failed to initialize REPL session:', err);
|
||||
}
|
||||
res.status(500).json({ error: 'Failed to initialize REPL session' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/repl/eval', (req, res) => {
|
||||
const { sessionId, line, check, buffer } = req.body;
|
||||
|
||||
if (!sessionId || !replSessions.has(sessionId)) {
|
||||
return res.status(400).json({ error: 'Invalid or expired session' });
|
||||
}
|
||||
|
||||
if (!line) {
|
||||
return res.status(400).json({ error: 'No code provided' });
|
||||
}
|
||||
|
||||
try {
|
||||
const session = replSessions.get(sessionId);
|
||||
session.lastAccess = Date.now();
|
||||
|
||||
if (check) {
|
||||
const codeToCheck = buffer ? [...buffer, line].join('\n') : line;
|
||||
const isComplete = nasalLib.nasal_repl_is_complete(session.context, codeToCheck);
|
||||
|
||||
if (isComplete === 1) {
|
||||
return res.json({ needsMore: true });
|
||||
} else if (isComplete === -1) {
|
||||
return res.json({ error: 'Invalid input' });
|
||||
}
|
||||
}
|
||||
|
||||
const result = nasalLib.nasal_repl_eval(session.context, line);
|
||||
|
||||
if (argv.verbose) {
|
||||
console.log(`REPL evaluation for session ${sessionId}:`, { line, result });
|
||||
}
|
||||
|
||||
res.json({ result });
|
||||
} catch (err) {
|
||||
if (argv.verbose) {
|
||||
console.error(`REPL evaluation error for session ${sessionId}:`, err);
|
||||
}
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/repl/cleanup', (req, res) => {
|
||||
const { sessionId } = req.body;
|
||||
|
||||
if (!sessionId || !replSessions.has(sessionId)) {
|
||||
return res.status(400).json({ error: 'Invalid session' });
|
||||
}
|
||||
|
||||
try {
|
||||
const session = replSessions.get(sessionId);
|
||||
nasalLib.nasal_repl_cleanup(session.context);
|
||||
replSessions.delete(sessionId);
|
||||
|
||||
if (argv.verbose) {
|
||||
console.log(`REPL session cleaned up: ${sessionId}`);
|
||||
}
|
||||
|
||||
res.json({ message: 'Session cleaned up successfully' });
|
||||
} catch (err) {
|
||||
if (argv.verbose) {
|
||||
console.error(`Failed to cleanup session ${sessionId}:`, err);
|
||||
}
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Handle cleanup on server shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\nCleaning up REPL sessions before exit...');
|
||||
for (const [sessionId, session] of replSessions.entries()) {
|
||||
try {
|
||||
nasalLib.nasal_repl_cleanup(session.context);
|
||||
if (argv.verbose) {
|
||||
console.log(`Cleaned up session: ${sessionId}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error cleaning up session ${sessionId}:`, err);
|
||||
}
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
const PORT = argv.port || 3001;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`REPL Server running on http://localhost:${PORT}`);
|
||||
if (argv.verbose) console.log('Verbose logging enabled');
|
||||
});
|
||||
1
nasal-web-app/std
Symbolic link
1
nasal-web-app/std
Symbolic link
@@ -0,0 +1 @@
|
||||
../std
|
||||
@@ -9,7 +9,7 @@ bool ast_dumper::visit_use_stmt(use_stmt* node) {
|
||||
dump_indent();
|
||||
std::cout << "use" << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_path()) {
|
||||
for (auto i : node->get_path()) {
|
||||
if (i==node->get_path().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -64,7 +64,7 @@ bool ast_dumper::visit_vector_expr(vector_expr* node) {
|
||||
std::cout << "vector";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
for (auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -79,7 +79,7 @@ bool ast_dumper::visit_hash_expr(hash_expr* node) {
|
||||
std::cout << "hash";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_members()) {
|
||||
for (auto i : node->get_members()) {
|
||||
if (i==node->get_members().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -107,7 +107,7 @@ bool ast_dumper::visit_function(function* node) {
|
||||
std::cout << "function";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
for (auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
set_last();
|
||||
@@ -121,7 +121,7 @@ bool ast_dumper::visit_code_block(code_block* node) {
|
||||
std::cout << "block";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_expressions()) {
|
||||
for (auto i : node->get_expressions()) {
|
||||
if (i==node->get_expressions().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -231,7 +231,7 @@ bool ast_dumper::visit_call_expr(call_expr* node) {
|
||||
set_last();
|
||||
}
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
for (auto i : node->get_calls()) {
|
||||
if (i==node->get_calls().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -260,7 +260,7 @@ bool ast_dumper::visit_call_vector(call_vector* node) {
|
||||
std::cout << "call_vector";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_slices()) {
|
||||
for (auto i : node->get_slices()) {
|
||||
if (i==node->get_slices().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -275,7 +275,7 @@ bool ast_dumper::visit_call_function(call_function* node) {
|
||||
std::cout << "call_function";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_argument()) {
|
||||
for (auto i : node->get_argument()) {
|
||||
if (i==node->get_argument().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -350,7 +350,7 @@ bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
|
||||
std::cout << "multiple_identifier";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_variables()) {
|
||||
for (auto i : node->get_variables()) {
|
||||
if (i==node->get_variables().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -365,7 +365,7 @@ bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
|
||||
std::cout << "tuple";
|
||||
std::cout << format_location(node);
|
||||
push_indent();
|
||||
for(auto i : node->get_elements()) {
|
||||
for (auto i : node->get_elements()) {
|
||||
if (i==node->get_elements().back()) {
|
||||
set_last();
|
||||
}
|
||||
@@ -459,7 +459,7 @@ bool ast_dumper::visit_condition_expr(condition_expr* node) {
|
||||
set_last();
|
||||
}
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
for (auto i : node->get_elsif_stataments()) {
|
||||
if (i==node->get_elsif_stataments().back() &&
|
||||
!node->get_else_statement()) {
|
||||
set_last();
|
||||
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
if (indent.size() && indent.back()=="│ ") {
|
||||
indent.back() = "├──";
|
||||
}
|
||||
for(const auto& i : indent) {
|
||||
for (const auto& i : indent) {
|
||||
std::cout << i;
|
||||
}
|
||||
}
|
||||
|
||||
427
src/ast_format.cpp
Normal file
427
src/ast_format.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
#include "ast_format.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_format::visit_use_stmt(use_stmt* node) {
|
||||
dump_formating_node_info(node, "use statement");
|
||||
out << "use ";
|
||||
for (auto i : node->get_path()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_path().back()) {
|
||||
out << ".";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_null_expr(null_expr* node) {
|
||||
dump_formating_node_info(node, "null expression");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_nil_expr(nil_expr* node) {
|
||||
dump_formating_node_info(node, "nil expression");
|
||||
out << "nil";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_number_literal(number_literal* node) {
|
||||
dump_formating_node_info(node, "number expression");
|
||||
out << node->get_raw_text();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_string_literal(string_literal* node) {
|
||||
dump_formating_node_info(node, "string expression");
|
||||
out << "\"" << util::rawstr(node->get_content()) << "\"";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_identifier(identifier* node) {
|
||||
dump_formating_node_info(node, "identifier");
|
||||
out << node->get_name();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_bool_literal(bool_literal* node) {
|
||||
dump_formating_node_info(node, "bool expression");
|
||||
out << (node->get_flag()? "true" : "false");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_vector_expr(vector_expr* node) {
|
||||
dump_formating_node_info(node, "vector expression");
|
||||
out << "[";
|
||||
for (auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_elements().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_hash_expr(hash_expr* node) {
|
||||
dump_formating_node_info(node, "hash expression");
|
||||
out << "{";
|
||||
for (auto i : node->get_members()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_members().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_hash_pair(hash_pair* node) {
|
||||
dump_formating_node_info(node, "hash pair");
|
||||
bool contain_not_identifier = false;
|
||||
for (auto c : node->get_name()) {
|
||||
if (!(std::isdigit(c) || std::isalpha(c) || c == '_')) {
|
||||
contain_not_identifier = true;
|
||||
}
|
||||
}
|
||||
if (contain_not_identifier) {
|
||||
out << "\"" << node->get_name() << "\"";
|
||||
} else {
|
||||
out << node->get_name();
|
||||
}
|
||||
if (node->get_value()) {
|
||||
out << " : ";
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_function(function* node) {
|
||||
dump_formating_node_info(node, "function");
|
||||
out << "func(";
|
||||
for (auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_parameter_list().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << ") ";
|
||||
if (node->get_code_block()->get_expressions().empty()) {
|
||||
out << "{}";
|
||||
return true;
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_code_block(code_block* node) {
|
||||
dump_formating_node_info(node, "code block");
|
||||
out << "{\n";
|
||||
push_indent();
|
||||
for (auto i : node->get_expressions()) {
|
||||
dump_indent();
|
||||
i->accept(this);
|
||||
if (need_dump_semi(i)) {
|
||||
out << ";\n";
|
||||
} else {
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
pop_indent();
|
||||
dump_indent();
|
||||
out << "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_parameter(parameter* node) {
|
||||
dump_formating_node_info(node, "parameter");
|
||||
out << node->get_parameter_name();
|
||||
switch (node->get_parameter_type()) {
|
||||
case parameter::kind::normal_parameter: break;
|
||||
case parameter::kind::dynamic_parameter: out << "..."; break;
|
||||
case parameter::kind::default_parameter: out << " = "; break;
|
||||
}
|
||||
if (node->get_default_value()) {
|
||||
node->get_default_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_ternary_operator(ternary_operator* node) {
|
||||
dump_formating_node_info(node, "ternary operator");
|
||||
out << "(";
|
||||
node->get_condition()->accept(this);
|
||||
out << " ? ";
|
||||
node->get_left()->accept(this);
|
||||
out << " : ";
|
||||
node->get_right()->accept(this);
|
||||
out << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_binary_operator(binary_operator* node) {
|
||||
dump_formating_node_info(node, "binary operator");
|
||||
out << "(";
|
||||
node->get_left()->accept(this);
|
||||
switch(node->get_operator_type()) {
|
||||
case binary_operator::kind::add: out << " + "; break;
|
||||
case binary_operator::kind::sub: out << " - "; break;
|
||||
case binary_operator::kind::mult: out << " * "; break;
|
||||
case binary_operator::kind::div: out << " / "; break;
|
||||
case binary_operator::kind::concat: out << " ~ "; break;
|
||||
case binary_operator::kind::bitwise_and: out << " & "; break;
|
||||
case binary_operator::kind::bitwise_or: out << " | "; break;
|
||||
case binary_operator::kind::bitwise_xor: out << " ^ "; break;
|
||||
case binary_operator::kind::cmpeq: out << " == "; break;
|
||||
case binary_operator::kind::cmpneq: out << " != "; break;
|
||||
case binary_operator::kind::grt: out << " > "; break;
|
||||
case binary_operator::kind::geq: out << " >= "; break;
|
||||
case binary_operator::kind::less: out << " < "; break;
|
||||
case binary_operator::kind::leq: out << " <= "; break;
|
||||
case binary_operator::kind::condition_and: out << " and "; break;
|
||||
case binary_operator::kind::condition_or: out << " or "; break;
|
||||
case binary_operator::kind::null_chain: out << " ?? "; break;
|
||||
}
|
||||
node->get_right()->accept(this);
|
||||
out << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_unary_operator(unary_operator* node) {
|
||||
dump_formating_node_info(node, "unary operator");
|
||||
switch(node->get_operator_type()) {
|
||||
case unary_operator::kind::negative: out << "-"; break;
|
||||
case unary_operator::kind::logical_not: out << "!"; break;
|
||||
case unary_operator::kind::bitwise_not: out << "~"; break;
|
||||
}
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_call_expr(call_expr* node) {
|
||||
dump_formating_node_info(node, "call expression");
|
||||
node->get_first()->accept(this);
|
||||
for (auto i : node->get_calls()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_call_hash(call_hash* node) {
|
||||
dump_formating_node_info(node, "call hash");
|
||||
out << "." << node->get_field();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_null_access(null_access* node) {
|
||||
dump_formating_node_info(node, "null access operator(?.)");
|
||||
out << "?." << node->get_field();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_call_vector(call_vector* node) {
|
||||
dump_formating_node_info(node, "call vector");
|
||||
out << "[";
|
||||
for (auto i : node->get_slices()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_slices().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_call_function(call_function* node) {
|
||||
dump_formating_node_info(node, "call function");
|
||||
out << "(";
|
||||
for (auto i : node->get_argument()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_argument().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_slice_vector(slice_vector* node) {
|
||||
dump_formating_node_info(node, "slice vector");
|
||||
node->get_begin()->accept(this);
|
||||
if (node->get_end()) {
|
||||
out << " : ";
|
||||
node->get_end()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_definition_expr(definition_expr* node) {
|
||||
dump_formating_node_info(node, "definition");
|
||||
out << "var ";
|
||||
if (node->get_variable_name()) {
|
||||
node->get_variable_name()->accept(this);
|
||||
} else {
|
||||
node->get_variables()->accept(this);
|
||||
}
|
||||
out << " = ";
|
||||
if (node->get_tuple()) {
|
||||
node->get_tuple()->accept(this);
|
||||
} else {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_assignment_expr(assignment_expr* node) {
|
||||
dump_formating_node_info(node, "assignment");
|
||||
node->get_left()->accept(this);
|
||||
switch(node->get_assignment_type()) {
|
||||
case assignment_expr::kind::add_equal: out << " += "; break;
|
||||
case assignment_expr::kind::sub_equal: out << " -= "; break;
|
||||
case assignment_expr::kind::mult_equal: out << " *= "; break;
|
||||
case assignment_expr::kind::div_equal: out << " /= "; break;
|
||||
case assignment_expr::kind::concat_equal: out << " ~= "; break;
|
||||
case assignment_expr::kind::equal: out << " = "; break;
|
||||
case assignment_expr::kind::bitwise_and_equal: out << " &= "; break;
|
||||
case assignment_expr::kind::bitwise_or_equal: out << " |= "; break;
|
||||
case assignment_expr::kind::bitwise_xor_equal: out << " ^= "; break;
|
||||
}
|
||||
node->get_right()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_multi_identifier(multi_identifier* node) {
|
||||
dump_formating_node_info(node, "multi identifier");
|
||||
out << "(";
|
||||
for (auto i : node->get_variables()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_variables().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_tuple_expr(tuple_expr* node) {
|
||||
dump_formating_node_info(node, "tuple expression");
|
||||
out << "(";
|
||||
for (auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
if (i != node->get_elements().back()) {
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
out << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_multi_assign(multi_assign* node) {
|
||||
dump_formating_node_info(node, "multi assign");
|
||||
node->get_tuple()->accept(this);
|
||||
out << " = ";
|
||||
node->get_value()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_while_expr(while_expr* node) {
|
||||
dump_formating_node_info(node, "while statement");
|
||||
out << "while (";
|
||||
node->get_condition()->accept(this);
|
||||
out << ") ";
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_for_expr(for_expr* node) {
|
||||
dump_formating_node_info(node, "for statement");
|
||||
out << "for (";
|
||||
node->get_initial()->accept(this);
|
||||
out << "; ";
|
||||
node->get_condition()->accept(this);
|
||||
out << "; ";
|
||||
node->get_step()->accept(this);
|
||||
out << ") ";
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_iter_expr(iter_expr* node) {
|
||||
dump_formating_node_info(node, "iteration expression");
|
||||
if (node->is_definition()) {
|
||||
out << "var ";
|
||||
}
|
||||
if (node->get_name()) {
|
||||
node->get_name()->accept(this);
|
||||
} else {
|
||||
node->get_call()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_forei_expr(forei_expr* node) {
|
||||
dump_formating_node_info(node, "forindex/foreach statement");
|
||||
if (node->get_loop_type()==forei_expr::kind::foreach) {
|
||||
out << "foreach ";
|
||||
} else {
|
||||
out << "forindex ";
|
||||
}
|
||||
out << "(";
|
||||
node->get_iterator()->accept(this);
|
||||
out << "; ";
|
||||
node->get_value()->accept(this);
|
||||
out << ") ";
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_condition_expr(condition_expr* node) {
|
||||
dump_formating_node_info(node, "condition statement");
|
||||
out << "if ";
|
||||
node->get_if_statement()->accept(this);
|
||||
for (auto i : node->get_elsif_stataments()) {
|
||||
out << " elsif ";
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
out << " else ";
|
||||
node->get_else_statement()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_if_expr(if_expr* node) {
|
||||
dump_formating_node_info(node, "if statement");
|
||||
if (node->get_condition()) {
|
||||
out << "(";
|
||||
node->get_condition()->accept(this);
|
||||
out << ") ";
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_continue_expr(continue_expr* node) {
|
||||
dump_formating_node_info(node, "continue statement");
|
||||
out << "continue";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_break_expr(break_expr* node) {
|
||||
dump_formating_node_info(node, "break statement");
|
||||
out << "break";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_format::visit_return_expr(return_expr* node) {
|
||||
dump_formating_node_info(node, "return statement");
|
||||
out << "return ";
|
||||
if (node->get_value()) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
148
src/ast_format.h
Normal file
148
src/ast_format.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include "ast_visitor.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_format: public ast_visitor {
|
||||
private:
|
||||
std::ofstream out;
|
||||
std::vector<std::string> indent;
|
||||
|
||||
private:
|
||||
void push_indent() {
|
||||
indent.push_back(" ");
|
||||
}
|
||||
void pop_indent() {
|
||||
if (indent.size()) {
|
||||
indent.pop_back();
|
||||
}
|
||||
}
|
||||
void dump_indent() {
|
||||
for (const auto& i : indent) {
|
||||
out << i;
|
||||
}
|
||||
}
|
||||
void dump_formating_node_info(expr* n, const char* name) {
|
||||
std::cout << " formating " << name << " @ 0x";
|
||||
std::cout << std::hex << n << std::dec << "\n";
|
||||
}
|
||||
bool need_dump_semi(expr* n) {
|
||||
switch (n->get_type()) {
|
||||
case expr_type::ast_use:
|
||||
case expr_type::ast_null:
|
||||
case expr_type::ast_nil:
|
||||
case expr_type::ast_num:
|
||||
case expr_type::ast_str:
|
||||
case expr_type::ast_bool:
|
||||
case expr_type::ast_vec:
|
||||
case expr_type::ast_hash:
|
||||
case expr_type::ast_call:
|
||||
case expr_type::ast_multi_assign:
|
||||
case expr_type::ast_unary:
|
||||
case expr_type::ast_binary:
|
||||
case expr_type::ast_ternary: return true;
|
||||
case expr_type::ast_def: {
|
||||
auto dn = reinterpret_cast<definition_expr*>(n);
|
||||
if (dn->get_value() &&
|
||||
dn->get_value()->get_type() == expr_type::ast_func) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case expr_type::ast_assign: {
|
||||
auto dn = reinterpret_cast<assignment_expr*>(n);
|
||||
if (dn->get_right() &&
|
||||
dn->get_right()->get_type() == expr_type::ast_func) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case expr_type::ast_ret: {
|
||||
auto dn = reinterpret_cast<return_expr*>(n);
|
||||
if (dn->get_value() &&
|
||||
dn->get_value()->get_type() == expr_type::ast_func) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
bool visit_use_stmt(use_stmt*) override;
|
||||
bool visit_null_expr(null_expr*) override;
|
||||
bool visit_nil_expr(nil_expr*) override;
|
||||
bool visit_number_literal(number_literal*) override;
|
||||
bool visit_string_literal(string_literal*) override;
|
||||
bool visit_identifier(identifier*) override;
|
||||
bool visit_bool_literal(bool_literal*) override;
|
||||
bool visit_vector_expr(vector_expr*) override;
|
||||
bool visit_hash_expr(hash_expr*) override;
|
||||
bool visit_hash_pair(hash_pair*) override;
|
||||
bool visit_function(function*) override;
|
||||
bool visit_code_block(code_block*) override;
|
||||
bool visit_parameter(parameter*) override;
|
||||
bool visit_ternary_operator(ternary_operator*) override;
|
||||
bool visit_binary_operator(binary_operator*) override;
|
||||
bool visit_unary_operator(unary_operator*) override;
|
||||
bool visit_call_expr(call_expr*) override;
|
||||
bool visit_call_hash(call_hash*) override;
|
||||
bool visit_null_access(null_access*) override;
|
||||
bool visit_call_vector(call_vector*) override;
|
||||
bool visit_call_function(call_function*) override;
|
||||
bool visit_slice_vector(slice_vector*) override;
|
||||
bool visit_definition_expr(definition_expr*) override;
|
||||
bool visit_assignment_expr(assignment_expr*) override;
|
||||
bool visit_multi_identifier(multi_identifier*) override;
|
||||
bool visit_tuple_expr(tuple_expr*) override;
|
||||
bool visit_multi_assign(multi_assign*) override;
|
||||
bool visit_while_expr(while_expr*) override;
|
||||
bool visit_for_expr(for_expr*) override;
|
||||
bool visit_iter_expr(iter_expr*) override;
|
||||
bool visit_forei_expr(forei_expr*) override;
|
||||
bool visit_condition_expr(condition_expr*) override;
|
||||
bool visit_if_expr(if_expr*) override;
|
||||
bool visit_continue_expr(continue_expr*) override;
|
||||
bool visit_break_expr(break_expr*) override;
|
||||
bool visit_return_expr(return_expr*) override;
|
||||
|
||||
public:
|
||||
ast_format(const std::string output_file): out(output_file) {
|
||||
if (out.fail()) {
|
||||
throw std::runtime_error("can't open file: " + output_file);
|
||||
}
|
||||
}
|
||||
|
||||
void do_format(code_block* root) {
|
||||
std::cout << "nasal-format is not stable right now, ";
|
||||
std::cout << "take care of source code!\n";
|
||||
dump_formating_node_info(root, "program root");
|
||||
bool is_use_statement = true;
|
||||
for (auto i : root->get_expressions()) {
|
||||
if (is_use_statement && i->get_type() != expr_type::ast_use) {
|
||||
is_use_statement = false;
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
i->accept(this);
|
||||
if (need_dump_semi(i)) {
|
||||
out << ";\n";
|
||||
} else {
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ bool ast_visitor::visit_expr(expr* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_use_stmt(use_stmt* node) {
|
||||
for(auto i : node->get_path()) {
|
||||
for (auto i : node->get_path()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -44,14 +44,14 @@ bool ast_visitor::visit_bool_literal(bool_literal* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_vector_expr(vector_expr* node) {
|
||||
for(auto i : node->get_elements()) {
|
||||
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()) {
|
||||
for (auto i : node->get_members()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -65,7 +65,7 @@ bool ast_visitor::visit_hash_pair(hash_pair* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_function(function* node) {
|
||||
for(auto i : node->get_parameter_list()) {
|
||||
for (auto i : node->get_parameter_list()) {
|
||||
i->accept(this);
|
||||
}
|
||||
node->get_code_block()->accept(this);
|
||||
@@ -73,7 +73,7 @@ bool ast_visitor::visit_function(function* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_code_block(code_block* node) {
|
||||
for(auto i : node->get_expressions()) {
|
||||
for (auto i : node->get_expressions()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -106,7 +106,7 @@ bool ast_visitor::visit_unary_operator(unary_operator* node) {
|
||||
|
||||
bool ast_visitor::visit_call_expr(call_expr* node) {
|
||||
node->get_first()->accept(this);
|
||||
for(auto i : node->get_calls()) {
|
||||
for (auto i : node->get_calls()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -121,14 +121,14 @@ bool ast_visitor::visit_null_access(null_access* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_vector(call_vector* node) {
|
||||
for(auto i : node->get_slices()) {
|
||||
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()) {
|
||||
for (auto i : node->get_argument()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -163,14 +163,14 @@ bool ast_visitor::visit_assignment_expr(assignment_expr* node) {
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_multi_identifier(multi_identifier* node) {
|
||||
for(auto i : node->get_variables()) {
|
||||
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()) {
|
||||
for (auto i : node->get_elements()) {
|
||||
i->accept(this);
|
||||
}
|
||||
return true;
|
||||
@@ -214,7 +214,7 @@ bool ast_visitor::visit_forei_expr(forei_expr* node) {
|
||||
|
||||
bool ast_visitor::visit_condition_expr(condition_expr* node) {
|
||||
node->get_if_statement()->accept(this);
|
||||
for(auto i : node->get_elsif_stataments()) {
|
||||
for (auto i : node->get_elsif_stataments()) {
|
||||
i->accept(this);
|
||||
}
|
||||
if (node->get_else_statement()) {
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
#include "nasal.h"
|
||||
#include "cli/cli.h"
|
||||
#include "util/util.h"
|
||||
#include "nasal_parse.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
namespace nasal::cli {
|
||||
|
||||
cli_config parse(const std::vector<std::string>& args) {
|
||||
cli_config result;
|
||||
|
||||
for(const auto& arg : args) {
|
||||
for (const auto& arg : args) {
|
||||
if (cli_options.count(arg)) {
|
||||
result.options.insert(cli_options.at(arg));
|
||||
} else if (!result.input_file_path.length()) {
|
||||
@@ -59,4 +65,75 @@ std::ostream& help(std::ostream& out) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& nasal_format_help(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " ,--#-,\n"
|
||||
<< "<3 / \\____\\ <3\n"
|
||||
<< " |_|__A_|\n"
|
||||
<< "\nnasal-format <option>\n"
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< "\nnasal-format <file>\n"
|
||||
<< "file:\n"
|
||||
<< " <filename> | file to be formatted.\n"
|
||||
<< "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& logo(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " __ _\n"
|
||||
<< " /\\ \\ \\__ _ ___ __ _| |\n"
|
||||
<< " / \\/ / _` / __|/ _` | |\n"
|
||||
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<< "\n"
|
||||
<< "ver : " << __nasver__
|
||||
<< " " << nasal::util::get_platform()
|
||||
<< " " << nasal::util::get_arch()
|
||||
<< " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||
<< "std : c++ " << __cplusplus << "\n"
|
||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<< "\n"
|
||||
<< "presented by fgprc members:\n"
|
||||
<< " - http://fgprc.org\n"
|
||||
<< " - http://fgprc.org.cn\n"
|
||||
<< "\n"
|
||||
<< "input <nasal -h> to get help.\n\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& version(std::ostream& out) {
|
||||
std::srand(static_cast<u32>(std::time(nullptr)));
|
||||
|
||||
f64 num = 0;
|
||||
for (u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
// give you 5% to see this easter egg
|
||||
if (num<0.05) {
|
||||
nasal::parse::easter_egg();
|
||||
}
|
||||
|
||||
out << "nasal version " << __nasver__;
|
||||
out << " " << nasal::util::get_platform();
|
||||
out << " " << nasal::util::get_arch();
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& nasal_format_version(std::ostream& out) {
|
||||
out << "nasal-format version " << __nasver__ << "-beta";
|
||||
out << " " << nasal::util::get_platform();
|
||||
out << " " << nasal::util::get_arch();
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,5 +69,10 @@ const std::unordered_map<std::string, option> cli_options = {
|
||||
cli_config parse(const std::vector<std::string>&);
|
||||
|
||||
std::ostream& help(std::ostream&);
|
||||
std::ostream& nasal_format_help(std::ostream&);
|
||||
|
||||
std::ostream& logo(std::ostream&);
|
||||
std::ostream& version(std::ostream&);
|
||||
std::ostream& nasal_format_version(std::ostream&);
|
||||
|
||||
}
|
||||
53
src/format.cpp
Normal file
53
src/format.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "nasal.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "util/util.h"
|
||||
#include "cli/cli.h"
|
||||
#include "ast_format.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
[[noreturn]]
|
||||
void err() {
|
||||
std::cerr << "invalid argument(s), use <nasal-format -h> to get help.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void execute(const nasal::cli::cli_config& config) {
|
||||
nasal::lexer lex;
|
||||
nasal::parse parse;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lex.scan(config.input_file_path).chkerr();
|
||||
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lex).chkerr();
|
||||
|
||||
nasal::ast_format("nasal-format-out.nas").do_format(parse.tree());
|
||||
}
|
||||
|
||||
int main(i32 argc, const char* argv[]) {
|
||||
// output version info
|
||||
if (argc <= 1) {
|
||||
err();
|
||||
} else if (argc > 2) {
|
||||
err();
|
||||
}
|
||||
|
||||
// the first argument is the executable itself, ignore it
|
||||
const auto config = nasal::cli::parse({argv+1, argv+argc});
|
||||
|
||||
// run directly or show help
|
||||
if (config.has(nasal::cli::option::cli_help)) {
|
||||
std::clog << nasal::cli::nasal_format_help;
|
||||
} else if (config.has(nasal::cli::option::cli_version)) {
|
||||
std::clog << nasal::cli::nasal_format_version;
|
||||
} else if (config.input_file_path.size()) {
|
||||
execute(config);
|
||||
} else {
|
||||
err();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
70
src/main.cpp
70
src/main.cpp
@@ -18,55 +18,8 @@
|
||||
#include "repl/repl.h"
|
||||
#include "cli/cli.h"
|
||||
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
|
||||
std::ostream& logo(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " __ _\n"
|
||||
<< " /\\ \\ \\__ _ ___ __ _| |\n"
|
||||
<< " / \\/ / _` / __|/ _` | |\n"
|
||||
<< " / /\\ / (_| \\__ \\ (_| | |\n"
|
||||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<< "\n"
|
||||
<< "ver : " << __nasver__
|
||||
<< " " << nasal::util::get_platform()
|
||||
<< " " << nasal::util::get_arch()
|
||||
<< " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||
<< "std : c++ " << __cplusplus << "\n"
|
||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||
<< "repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<< "\n"
|
||||
<< "presented by fgprc members:\n"
|
||||
<< " - http://fgprc.org\n"
|
||||
<< " - http://fgprc.org.cn\n"
|
||||
<< "\n"
|
||||
<< "input <nasal -h> to get help .\n\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& version(std::ostream& out) {
|
||||
std::srand(static_cast<u32>(std::time(nullptr)));
|
||||
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
// give you 5% to see this easter egg
|
||||
if (num<0.05) {
|
||||
nasal::parse::easter_egg();
|
||||
}
|
||||
|
||||
out << "nasal version " << __nasver__;
|
||||
out << " " << nasal::util::get_platform();
|
||||
out << " " << nasal::util::get_arch();
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void err() {
|
||||
std::cerr << "invalid argument(s), use <nasal -h> to get help.\n";
|
||||
@@ -98,7 +51,7 @@ void execute(const nasal::cli::cli_config& config) {
|
||||
if (ld.get_file_list().size()) {
|
||||
std::cout << "referenced file(s):\n";
|
||||
}
|
||||
for(const auto& file: ld.get_file_list()) {
|
||||
for (const auto& file: ld.get_file_list()) {
|
||||
std::cout << " " << file << "\n";
|
||||
}
|
||||
}
|
||||
@@ -121,6 +74,8 @@ void execute(const nasal::cli::cli_config& config) {
|
||||
|
||||
// run
|
||||
const auto start = clk::now();
|
||||
double gc_time_ms = 0.0;
|
||||
double gc_total_memory = 0.0;
|
||||
if (config.has(option::cli_debug_mode)) {
|
||||
auto debugger = std::make_unique<nasal::dbg>();
|
||||
debugger->run(
|
||||
@@ -130,6 +85,8 @@ void execute(const nasal::cli::cli_config& config) {
|
||||
config.has(option::cli_profile),
|
||||
config.has(option::cli_profile_all)
|
||||
);
|
||||
gc_time_ms = debugger->get_gc_time_ms();
|
||||
gc_total_memory = debugger->get_total_memory();
|
||||
} else if (config.has(option::cli_show_execute_time) ||
|
||||
config.has(option::cli_detail_info) ||
|
||||
config.has(option::cli_limit_mode) ||
|
||||
@@ -138,20 +95,27 @@ void execute(const nasal::cli::cli_config& config) {
|
||||
runtime->set_detail_report_info(config.has(option::cli_detail_info));
|
||||
runtime->set_limit_mode_flag(config.has(option::cli_limit_mode));
|
||||
runtime->run(gen, ld, config.nasal_vm_args);
|
||||
gc_time_ms = runtime->get_gc_time_ms();
|
||||
gc_total_memory = runtime->get_total_memory();
|
||||
}
|
||||
|
||||
// get running time
|
||||
const auto end = clk::now();
|
||||
if (config.has(option::cli_show_execute_time)) {
|
||||
double execute_time_sec = static_cast<f64>((end - start).count())/den;
|
||||
double gc_time_sec = gc_time_ms / 1000.0;
|
||||
std::clog << "process exited after ";
|
||||
std::clog << static_cast<f64>((end-start).count())/den << "s.\n\n";
|
||||
std::clog << execute_time_sec << "s, gc time: ";
|
||||
std::clog << gc_time_sec << "s (";
|
||||
std::clog << gc_time_sec / execute_time_sec * 100.0 << "%), ";
|
||||
std::clog << "memory usage: " << gc_total_memory << "MB\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
i32 main(i32 argc, const char* argv[]) {
|
||||
// output version info
|
||||
if (argc<=1) {
|
||||
std::clog << logo;
|
||||
if (argc <= 1) {
|
||||
std::clog << nasal::cli::logo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -159,11 +123,11 @@ i32 main(i32 argc, const char* argv[]) {
|
||||
const auto config = nasal::cli::parse({argv+1, argv+argc});
|
||||
|
||||
// run directly or show help
|
||||
if (argc==2) {
|
||||
if (argc == 2) {
|
||||
if (config.has(nasal::cli::option::cli_help)) {
|
||||
std::clog << nasal::cli::help;
|
||||
} else if (config.has(nasal::cli::option::cli_version)) {
|
||||
std::clog << version;
|
||||
std::clog << nasal::cli::version;
|
||||
} else if (config.has(nasal::cli::option::cli_repl_mode)) {
|
||||
auto repl = std::make_unique<nasal::repl::repl>();
|
||||
repl->execute();
|
||||
|
||||
12
src/nasal.h
12
src/nasal.h
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __nasver__
|
||||
#define __nasver__ "11.2"
|
||||
#define __nasver__ "11.3.3"
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
@@ -18,4 +18,12 @@ using usize = std::size_t;
|
||||
using f64 = double;
|
||||
|
||||
// virtual machine stack depth, both global depth and value stack depth
|
||||
const u32 VM_STACK_DEPTH = UINT16_MAX;
|
||||
const u32 VM_STACK_DEPTH = UINT16_MAX + 1;
|
||||
|
||||
// avoid error loading function bug in MSVC version nasal.exe
|
||||
#ifdef _MSC_VER
|
||||
// and fuck MSVC again
|
||||
#define NASAL_EXPORT extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define NASAL_EXPORT extern "C" __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
@@ -8,7 +8,7 @@ void expr::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
use_stmt::~use_stmt() {
|
||||
for(auto i : path) {
|
||||
for (auto i : path) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ void bool_literal::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
vector_expr::~vector_expr() {
|
||||
for(auto i : elements) {
|
||||
for (auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ void vector_expr::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
hash_expr::~hash_expr() {
|
||||
for(auto i : members) {
|
||||
for (auto i : members) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ void hash_pair::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
function::~function() {
|
||||
for(auto i : parameter_list) {
|
||||
for (auto i : parameter_list) {
|
||||
delete i;
|
||||
}
|
||||
if (block) {
|
||||
@@ -89,7 +89,7 @@ void function::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
code_block::~code_block() {
|
||||
for(auto i : expressions) {
|
||||
for (auto i : expressions) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -157,10 +157,10 @@ void unary_operator::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
call_expr::~call_expr() {
|
||||
if(first) {
|
||||
if (first) {
|
||||
delete first;
|
||||
}
|
||||
for(auto i : calls) {
|
||||
for (auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ void null_access::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
call_vector::~call_vector() {
|
||||
for(auto i : calls) {
|
||||
for (auto i : calls) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -188,7 +188,7 @@ void call_vector::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
call_function::~call_function() {
|
||||
for(auto i : args) {
|
||||
for (auto i : args) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void assignment_expr::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
multi_identifier::~multi_identifier() {
|
||||
for(auto i : variables) {
|
||||
for (auto i : variables) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -253,7 +253,7 @@ void multi_identifier::accept(ast_visitor* visitor) {
|
||||
}
|
||||
|
||||
tuple_expr::~tuple_expr() {
|
||||
for(auto i : elements) {
|
||||
for (auto i : elements) {
|
||||
delete i;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +340,7 @@ condition_expr::~condition_expr() {
|
||||
if (if_stmt) {
|
||||
delete if_stmt;
|
||||
}
|
||||
for(auto i : elsif_stmt) {
|
||||
for (auto i : elsif_stmt) {
|
||||
delete i;
|
||||
}
|
||||
if (else_stmt) {
|
||||
|
||||
@@ -120,12 +120,14 @@ public:
|
||||
class number_literal: public expr {
|
||||
private:
|
||||
f64 number;
|
||||
std::string raw_text;
|
||||
|
||||
public:
|
||||
number_literal(const span& location, const f64 num):
|
||||
expr(location, expr_type::ast_num), number(num) {}
|
||||
number_literal(const span& location, const f64 num, const std::string& raw):
|
||||
expr(location, expr_type::ast_num), number(num), raw_text(raw) {}
|
||||
~number_literal() override = default;
|
||||
f64 get_number() const {return number;}
|
||||
f64 get_number() const { return number; }
|
||||
const std::string& get_raw_text() const { return raw_text; }
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,25 +5,28 @@ namespace nasal {
|
||||
|
||||
void codegen::init_file_map(const std::vector<std::string>& file_list) {
|
||||
file_map = {};
|
||||
for(usize i = 0; i<file_list.size(); ++i) {
|
||||
for (usize i = 0; i<file_list.size(); ++i) {
|
||||
file_map.insert({file_list[i], i});
|
||||
}
|
||||
}
|
||||
|
||||
void codegen::load_native_function_table(nasal_builtin_table* table) {
|
||||
for(usize i = 0; table[i].func; ++i) {
|
||||
for (usize i = 0; table[i].func; ++i) {
|
||||
// check confliction
|
||||
if (native_function_mapper.count(table[i].name)) {
|
||||
err.err("code", "\"" + std::string(table[i].name) + "\" conflicts.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// replace unsafe native functions with redirect function in limit mode
|
||||
if (flag_limited_mode && unsafe_system_api.count(table[i].name)) {
|
||||
native_function.push_back({"__unsafe_redirect", builtin_unsafe});
|
||||
} else {
|
||||
native_function.push_back(table[i]);
|
||||
}
|
||||
auto index = native_function_mapper.size();
|
||||
|
||||
// insert into mapper
|
||||
auto index = native_function_mapper.size();
|
||||
native_function_mapper[table[i].name] = index;
|
||||
}
|
||||
}
|
||||
@@ -39,15 +42,14 @@ void codegen::init_native_function() {
|
||||
load_native_function_table(unix_lib_native);
|
||||
load_native_function_table(json_lib_native);
|
||||
load_native_function_table(regex_lib_native);
|
||||
load_native_function_table(subprocess_native);
|
||||
}
|
||||
|
||||
void codegen::check_id_exist(identifier* node) {
|
||||
const auto& name = node->get_name();
|
||||
if (native_function_mapper.count(name)) {
|
||||
if (local.empty()) {
|
||||
die("native function should not be used in global scope",
|
||||
node->get_location()
|
||||
);
|
||||
die("native function should not be used in global scope", node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -61,9 +63,9 @@ void codegen::check_id_exist(identifier* node) {
|
||||
if (global_symbol_find(name)>=0) {
|
||||
return;
|
||||
}
|
||||
die("undefined symbol \"" + name +
|
||||
"\", and this symbol is useless here",
|
||||
node->get_location()
|
||||
die("undefined symbol \""
|
||||
+ name + "\", and this symbol is useless here",
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,18 +91,19 @@ void codegen::regist_string(const std::string& str) {
|
||||
|
||||
void codegen::find_symbol(code_block* node) {
|
||||
auto finder = std::make_unique<symbol_finder>();
|
||||
for(const auto& i : finder->do_find(node)) {
|
||||
for (const auto& i : finder->do_find(node)) {
|
||||
const auto& file = i.pos_node->get_location().file;
|
||||
// check if symbol conflicts with native function name
|
||||
if (native_function_mapper.count(i.name)) {
|
||||
die("symbol conflicts with native function", i.location);
|
||||
die("symbol conflicts with native function", i.pos_node);
|
||||
continue;
|
||||
}
|
||||
// create new namespace with checking existence of location file
|
||||
if (!nasal_namespace.count(i.location.file)) {
|
||||
nasal_namespace[i.location.file] = {};
|
||||
if (!nasal_namespace.count(file)) {
|
||||
nasal_namespace[file] = {};
|
||||
}
|
||||
// if in global scope, load global symbol into this namespace
|
||||
auto& scope = nasal_namespace.at(i.location.file);
|
||||
auto& scope = nasal_namespace.at(file);
|
||||
if (local.empty() && !scope.count(i.name)) {
|
||||
scope.insert(i.name);
|
||||
}
|
||||
@@ -150,7 +153,7 @@ i64 codegen::upvalue_symbol_find(const std::string& name) {
|
||||
}
|
||||
|
||||
auto iter = local.begin();
|
||||
for(u64 i = 0; i<size-1; ++i, ++iter) {
|
||||
for (u64 i = 0; i<size-1; ++i, ++iter) {
|
||||
if (iter->count(name)) {
|
||||
index = ((i<<16)|(*iter).at(name));
|
||||
}
|
||||
@@ -186,7 +189,7 @@ void codegen::bool_gen(bool_literal* node) {
|
||||
}
|
||||
|
||||
void codegen::vector_gen(vector_expr* node) {
|
||||
for(auto child : node->get_elements()) {
|
||||
for (auto child : node->get_elements()) {
|
||||
calc_gen(child);
|
||||
}
|
||||
emit(op_newv, node->get_elements().size(), node->get_location());
|
||||
@@ -194,7 +197,7 @@ void codegen::vector_gen(vector_expr* node) {
|
||||
|
||||
void codegen::hash_gen(hash_expr* node) {
|
||||
emit(op_newh, 0, node->get_location());
|
||||
for(auto child : node->get_members()) {
|
||||
for (auto child : node->get_members()) {
|
||||
calc_gen(child->get_value());
|
||||
const auto& field_name = child->get_name();
|
||||
regist_string(field_name);
|
||||
@@ -207,7 +210,7 @@ void codegen::func_gen(function* node) {
|
||||
bool checked_default = false;
|
||||
bool checked_dynamic = false;
|
||||
std::unordered_map<std::string, bool> argname;
|
||||
for(auto tmp : node->get_parameter_list()) {
|
||||
for (auto tmp : node->get_parameter_list()) {
|
||||
if (tmp->get_parameter_type()==
|
||||
parameter::kind::default_parameter) {
|
||||
checked_default = true;
|
||||
@@ -219,22 +222,16 @@ void codegen::func_gen(function* node) {
|
||||
if (checked_default &&
|
||||
tmp->get_parameter_type()!=
|
||||
parameter::kind::default_parameter) {
|
||||
die("must use default parameter here",
|
||||
tmp->get_location()
|
||||
);
|
||||
die("must use default parameter here", tmp);
|
||||
}
|
||||
if (checked_dynamic &&
|
||||
tmp!=node->get_parameter_list().back()) {
|
||||
die("dynamic parameter must be the last one",
|
||||
tmp->get_location()
|
||||
);
|
||||
die("dynamic parameter must be the last one", tmp);
|
||||
}
|
||||
// check redefinition
|
||||
const auto& name = tmp->get_parameter_name();
|
||||
if (argname.count(name)) {
|
||||
die("redefinition of parameter: " + name,
|
||||
tmp->get_location()
|
||||
);
|
||||
die("redefinition of parameter: " + name, tmp);
|
||||
} else {
|
||||
argname[name] = true;
|
||||
}
|
||||
@@ -253,12 +250,10 @@ void codegen::func_gen(function* node) {
|
||||
local.push_back({{"me", 0}});
|
||||
|
||||
// generate parameter list
|
||||
for(auto tmp : node->get_parameter_list()) {
|
||||
for (auto tmp : node->get_parameter_list()) {
|
||||
const auto& name = tmp->get_parameter_name();
|
||||
if (name=="me") {
|
||||
die("\"me\" should not be a parameter",
|
||||
tmp->get_location()
|
||||
);
|
||||
die("\"me\" should not be parameter", tmp);
|
||||
}
|
||||
regist_string(name);
|
||||
switch(tmp->get_parameter_type()) {
|
||||
@@ -296,7 +291,7 @@ void codegen::func_gen(function* node) {
|
||||
// var f = func(a, arg...) {return(arg)}
|
||||
auto arg = std::string("arg");
|
||||
// this is used to avoid confliction with defined parameter
|
||||
while(local_symbol_find(arg)>=0) {
|
||||
while (local_symbol_find(arg)>=0) {
|
||||
arg = "0" + arg;
|
||||
}
|
||||
regist_symbol(arg);
|
||||
@@ -313,7 +308,7 @@ void codegen::func_gen(function* node) {
|
||||
if (local.back().size()>=VM_STACK_DEPTH || local.back().size()>=UINT16_MAX) {
|
||||
die("too many local variants: " +
|
||||
std::to_string(local.back().size()),
|
||||
block->get_location()
|
||||
block
|
||||
);
|
||||
}
|
||||
local.pop_back();
|
||||
@@ -331,7 +326,7 @@ void codegen::call_gen(call_expr* node) {
|
||||
if (code.size() && code.back().op==op_callb) {
|
||||
return;
|
||||
}
|
||||
for(auto i : node->get_calls()) {
|
||||
for (auto i : node->get_calls()) {
|
||||
switch(i->get_type()) {
|
||||
case expr_type::ast_callh:
|
||||
call_hash_gen(reinterpret_cast<call_hash*>(i)); break;
|
||||
@@ -354,9 +349,7 @@ void codegen::call_identifier(identifier* node) {
|
||||
node->get_location()
|
||||
);
|
||||
if (local.empty()) {
|
||||
die("should warp native function in local scope",
|
||||
node->get_location()
|
||||
);
|
||||
die("should wrap up native function in local scope", node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -374,7 +367,7 @@ void codegen::call_identifier(identifier* node) {
|
||||
emit(op_callg, index, node->get_location());
|
||||
return;
|
||||
}
|
||||
die("undefined symbol \"" + name + "\"", node->get_location());
|
||||
die("undefined symbol \"" + name + "\"", node);
|
||||
// generation failed, put a push nil operand here to fill the space
|
||||
emit(op_pnil, index, node->get_location());
|
||||
}
|
||||
@@ -411,7 +404,7 @@ void codegen::call_vector_gen(call_vector* node) {
|
||||
return;
|
||||
}
|
||||
emit(op_slcbeg, 0, node->get_location());
|
||||
for(auto tmp : node->get_slices()) {
|
||||
for (auto tmp : node->get_slices()) {
|
||||
if (!tmp->get_end()) {
|
||||
calc_gen(tmp->get_begin());
|
||||
emit(op_slc, 0, tmp->get_location());
|
||||
@@ -428,7 +421,7 @@ void codegen::call_func_gen(call_function* node) {
|
||||
if (node->get_argument().size() &&
|
||||
node->get_argument()[0]->get_type()==expr_type::ast_pair) {
|
||||
emit(op_newh, 0, node->get_location());
|
||||
for(auto child : node->get_argument()) {
|
||||
for (auto child : node->get_argument()) {
|
||||
auto pair_node = reinterpret_cast<hash_pair*>(child);
|
||||
calc_gen(pair_node->get_value());
|
||||
const auto& field_name = pair_node->get_name();
|
||||
@@ -437,7 +430,7 @@ void codegen::call_func_gen(call_function* node) {
|
||||
}
|
||||
emit(op_callfh, 0, node->get_location());
|
||||
} else {
|
||||
for(auto child : node->get_argument()) {
|
||||
for (auto child : node->get_argument()) {
|
||||
calc_gen(child);
|
||||
}
|
||||
emit(op_callfv, node->get_argument().size(), node->get_location());
|
||||
@@ -455,7 +448,7 @@ void codegen::call_func_gen(call_function* node) {
|
||||
void codegen::mcall(expr* node) {
|
||||
if (node->get_type()!=expr_type::ast_id &&
|
||||
node->get_type()!=expr_type::ast_call) {
|
||||
die("bad left-value: cannot get memory space", node->get_location());
|
||||
die("bad left-value: cannot get memory space", node);
|
||||
return;
|
||||
}
|
||||
// generate symbol call if node is just ast_id and return
|
||||
@@ -466,7 +459,7 @@ void codegen::mcall(expr* node) {
|
||||
// generate call expression until the last sub-node
|
||||
auto call_node = static_cast<call_expr*>(node);
|
||||
calc_gen(call_node->get_first());
|
||||
for(usize i = 0; i<call_node->get_calls().size()-1; ++i) {
|
||||
for (usize i = 0; i<call_node->get_calls().size()-1; ++i) {
|
||||
auto tmp = call_node->get_calls()[i];
|
||||
switch(tmp->get_type()) {
|
||||
case expr_type::ast_callh:
|
||||
@@ -488,18 +481,18 @@ void codegen::mcall(expr* node) {
|
||||
case expr_type::ast_callv:
|
||||
mcall_vec(reinterpret_cast<call_vector*>(tmp)); break;
|
||||
case expr_type::ast_callf:
|
||||
die("bad left-value: function call", tmp->get_location()); break;
|
||||
die("bad left-value: function call", tmp); break;
|
||||
case expr_type::ast_null_access:
|
||||
die("bad left-value: null access test", tmp->get_location()); break;
|
||||
die("bad left-value: null access test", tmp); break;
|
||||
default:
|
||||
die("bad left-value: unknown call", tmp->get_location()); break;
|
||||
die("bad left-value: unknown call", tmp); break;
|
||||
}
|
||||
}
|
||||
|
||||
void codegen::mcall_identifier(identifier* node) {
|
||||
const auto& name = node->get_name();
|
||||
if (native_function_mapper.count(name)) {
|
||||
die("cannot modify native function", node->get_location());
|
||||
die("cannot modify native function", node);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -516,17 +509,17 @@ void codegen::mcall_identifier(identifier* node) {
|
||||
emit(op_mcallg, index, node->get_location());
|
||||
return;
|
||||
}
|
||||
die("undefined symbol \"" + name + "\"", node->get_location());
|
||||
die("undefined symbol \"" + name + "\"", node);
|
||||
}
|
||||
|
||||
void codegen::mcall_vec(call_vector* node) {
|
||||
if (node->get_slices().size()>1) {
|
||||
die("bad left-value: subvec call", node->get_location());
|
||||
die("bad left-value: subvec call", node);
|
||||
return;
|
||||
}
|
||||
auto call = node->get_slices()[0];
|
||||
if (call->get_end()) {
|
||||
die("bad left-value: subvec call", node->get_location());
|
||||
die("bad left-value: subvec call", node);
|
||||
return;
|
||||
}
|
||||
calc_gen(call->get_begin());
|
||||
@@ -562,18 +555,18 @@ void codegen::multi_def(definition_expr* node) {
|
||||
die("lack values in multi-definition, expect " +
|
||||
std::to_string(identifiers.size()) + " but get " +
|
||||
std::to_string(vals.size()),
|
||||
node->get_tuple()->get_location()
|
||||
node->get_tuple()
|
||||
);
|
||||
return;
|
||||
} else if (identifiers.size()<vals.size()) {
|
||||
die("too many values in multi-definition, expect " +
|
||||
std::to_string(identifiers.size()) + " but get " +
|
||||
std::to_string(vals.size()),
|
||||
node->get_tuple()->get_location()
|
||||
node->get_tuple()
|
||||
);
|
||||
return;
|
||||
}
|
||||
for(usize i = 0; i<size; ++i) {
|
||||
for (usize i = 0; i<size; ++i) {
|
||||
calc_gen(vals[i]);
|
||||
const auto& name = identifiers[i]->get_name();
|
||||
local.empty()?
|
||||
@@ -584,7 +577,7 @@ void codegen::multi_def(definition_expr* node) {
|
||||
}
|
||||
// (var a, b, c) = [0, 1, 2];
|
||||
calc_gen(node->get_value());
|
||||
for(usize i = 0; i<size; ++i) {
|
||||
for (usize i = 0; i<size; ++i) {
|
||||
emit(op_callvi, i, node->get_value()->get_location());
|
||||
const auto& name = identifiers[i]->get_name();
|
||||
local.empty()?
|
||||
@@ -596,7 +589,7 @@ void codegen::multi_def(definition_expr* node) {
|
||||
|
||||
void codegen::definition_gen(definition_expr* node) {
|
||||
if (node->get_variable_name() && node->get_tuple()) {
|
||||
die("cannot accept too many values", node->get_value()->get_location());
|
||||
die("cannot accept too many values", node->get_value());
|
||||
}
|
||||
node->get_variable_name()? single_def(node):multi_def(node);
|
||||
}
|
||||
@@ -720,7 +713,7 @@ void codegen::gen_assignment_equal_statement(assignment_expr* node) {
|
||||
case op_mcallg: code.back().op = op_loadg; break;
|
||||
case op_mcalll: code.back().op = op_loadl; break;
|
||||
case op_mupval: code.back().op = op_loadu; break;
|
||||
default: die("unexpected operand to replace", node->get_location());
|
||||
default: die("unexpected operand to replace", node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,7 +768,7 @@ void codegen::multi_assign_gen(multi_assign* node) {
|
||||
"lack value(s) in multi-assignment, expect " +
|
||||
std::to_string(tuple_size) + " but get " +
|
||||
std::to_string(value_size),
|
||||
value_node->get_location()
|
||||
value_node
|
||||
);
|
||||
return;
|
||||
} else if (tuple_size<value_size) {
|
||||
@@ -783,7 +776,7 @@ void codegen::multi_assign_gen(multi_assign* node) {
|
||||
"too many values in multi-assignment, expect " +
|
||||
std::to_string(tuple_size) + " but get " +
|
||||
std::to_string(value_size),
|
||||
value_node->get_location()
|
||||
value_node
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -794,11 +787,11 @@ void codegen::multi_assign_gen(multi_assign* node) {
|
||||
if (value_node->get_type()==expr_type::ast_tuple) {
|
||||
const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node)
|
||||
->get_elements();
|
||||
for(i64 i = size-1; i>=0; --i) {
|
||||
for (i64 i = size-1; i>=0; --i) {
|
||||
calc_gen(value_tuple[i]);
|
||||
}
|
||||
auto& tuple = tuple_node->get_elements();
|
||||
for(i64 i = 0; i<size; ++i) {
|
||||
for (i64 i = 0; i<size; ++i) {
|
||||
mcall(tuple[i]);
|
||||
// use load operands to avoid meq's pop operand
|
||||
// and this operation changes local and global value directly
|
||||
@@ -810,7 +803,7 @@ void codegen::multi_assign_gen(multi_assign* node) {
|
||||
// generate multiple assignment: (a, b, c) = [1, 2, 3];
|
||||
calc_gen(value_node);
|
||||
auto& tuple = tuple_node->get_elements();
|
||||
for(i64 i = 0; i<size; ++i) {
|
||||
for (i64 i = 0; i<size; ++i) {
|
||||
emit(op_callvi, i, value_node->get_location());
|
||||
mcall(tuple[i]);
|
||||
// use load operands to avoid meq's pop operand
|
||||
@@ -835,7 +828,7 @@ void codegen::cond_gen(condition_expr* node) {
|
||||
}
|
||||
code[ptr].num = code.size();
|
||||
|
||||
for(auto tmp : node->get_elsif_stataments()) {
|
||||
for (auto tmp : node->get_elsif_stataments()) {
|
||||
calc_gen(tmp->get_condition());
|
||||
ptr = code.size();
|
||||
emit(op_jf, 0, tmp->get_location());
|
||||
@@ -852,7 +845,7 @@ void codegen::cond_gen(condition_expr* node) {
|
||||
if (node->get_else_statement()) {
|
||||
block_gen(node->get_else_statement()->get_code_block());
|
||||
}
|
||||
for(auto i:jmp_label) {
|
||||
for (auto i:jmp_label) {
|
||||
code[i].num = code.size();
|
||||
}
|
||||
}
|
||||
@@ -873,10 +866,10 @@ void codegen::loop_gen(expr* node) {
|
||||
}
|
||||
|
||||
void codegen::load_continue_break(u64 continue_place, u64 break_place) {
|
||||
for(auto i : continue_ptr.front()) {
|
||||
for (auto i : continue_ptr.front()) {
|
||||
code[i].num = continue_place;
|
||||
}
|
||||
for(auto i : break_ptr.front()) {
|
||||
for (auto i : break_ptr.front()) {
|
||||
code[i].num = break_place;
|
||||
}
|
||||
continue_ptr.pop_front();
|
||||
@@ -1294,20 +1287,16 @@ void codegen::repl_mode_info_output_gen(expr* node) {
|
||||
|
||||
void codegen::block_gen(code_block* node) {
|
||||
bool is_use_statement = true;
|
||||
for(auto tmp : node->get_expressions()) {
|
||||
for (auto tmp : node->get_expressions()) {
|
||||
if (tmp->get_type()!=expr_type::ast_use) {
|
||||
is_use_statement = false;
|
||||
}
|
||||
switch(tmp->get_type()) {
|
||||
case expr_type::ast_use:
|
||||
if (!local.empty()) {
|
||||
die("module import is not allowed here.",
|
||||
tmp->get_location()
|
||||
);
|
||||
die("module import is not allowed here.", tmp);
|
||||
} else if (!is_use_statement) {
|
||||
die("module import should be used at the top of the file.",
|
||||
tmp->get_location()
|
||||
);
|
||||
die("module import should be used at top of file.", tmp);
|
||||
}
|
||||
break;
|
||||
case expr_type::ast_null: break;
|
||||
@@ -1359,7 +1348,7 @@ void codegen::block_gen(code_block* node) {
|
||||
}
|
||||
|
||||
void codegen::ret_gen(return_expr* node) {
|
||||
for(u32 i = 0; i<in_foreach_loop_level.back(); ++i) {
|
||||
for (u32 i = 0; i<in_foreach_loop_level.back(); ++i) {
|
||||
emit(op_pop, 0, node->get_location());
|
||||
emit(op_pop, 0, node->get_location());
|
||||
}
|
||||
@@ -1429,12 +1418,12 @@ void codegen::print(std::ostream& out) {
|
||||
std::stack<u64> func_end_stack;
|
||||
|
||||
// print const numbers
|
||||
for(auto num : const_number_table) {
|
||||
for (auto num : const_number_table) {
|
||||
out << " .number " << num << "\n";
|
||||
}
|
||||
|
||||
// print const strings
|
||||
for(const auto& str : const_string_table) {
|
||||
for (const auto& str : const_string_table) {
|
||||
out << " .symbol \"" << util::rawstr(str) << "\"\n";
|
||||
}
|
||||
|
||||
@@ -1447,10 +1436,11 @@ void codegen::print(std::ostream& out) {
|
||||
codestream::set(
|
||||
const_number_table.data(),
|
||||
const_string_table.data(),
|
||||
global,
|
||||
native_function.data()
|
||||
);
|
||||
|
||||
for(u64 i = 0; i<code.size(); ++i) {
|
||||
for (u64 i = 0; i<code.size(); ++i) {
|
||||
// print opcode index, opcode name, opcode immediate number
|
||||
const auto& c = code[i];
|
||||
if (!func_end_stack.empty() && i==func_end_stack.top()) {
|
||||
@@ -1466,7 +1456,7 @@ void codegen::print(std::ostream& out) {
|
||||
// get function begin index and end index
|
||||
if (c.op==op_newf) {
|
||||
out << std::hex << "\nfunc <0x" << i << std::dec << ">:\n";
|
||||
for(u64 j = i; j<code.size(); ++j) {
|
||||
for (u64 j = i; j<code.size(); ++j) {
|
||||
if (code[j].op==op_jmp) {
|
||||
func_begin_stack.push(i);
|
||||
func_end_stack.push(code[j].num);
|
||||
@@ -1481,9 +1471,9 @@ void codegen::print(std::ostream& out) {
|
||||
}
|
||||
|
||||
void codegen::symbol_dump(std::ostream& out) const {
|
||||
for(const auto& domain : nasal_namespace) {
|
||||
for (const auto& domain : nasal_namespace) {
|
||||
out << "<" << domain.first << ">\n";
|
||||
for(const auto& i : domain.second) {
|
||||
for (const auto& i : domain.second) {
|
||||
out << " 0x" << std::setw(4) << std::setfill('0');
|
||||
out << std::hex << global.at(i) << std::dec << " ";
|
||||
out << i << std::endl;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "natives/dylib_lib.h"
|
||||
#include "natives/regex_lib.h"
|
||||
#include "natives/unix_lib.h"
|
||||
#include "natives/subprocess.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
@@ -45,7 +46,7 @@ private:
|
||||
// under limited mode, unsafe system api will be banned
|
||||
const std::unordered_set<std::string> unsafe_system_api = {
|
||||
// builtin
|
||||
"__system", "__input",
|
||||
"__system", "__input", "__terminal_size",
|
||||
// io
|
||||
"__fout", "__open", "__write", "__stat"
|
||||
// bits
|
||||
@@ -56,8 +57,11 @@ private:
|
||||
// dylib
|
||||
"__dlopen", "__dlclose", "__dlcallv", "__dlcall",
|
||||
// unix
|
||||
"__pipe", "__fork", "__waitpid", "__chdir",
|
||||
"__environ", "__getcwd", "__getenv"
|
||||
"__chdir", "__environ", "__getcwd", "__getenv",
|
||||
// subprocess
|
||||
"__subprocess_create",
|
||||
"__subprocess_active",
|
||||
"__subprocess_terminate"
|
||||
};
|
||||
|
||||
// file mapper for file -> index
|
||||
@@ -100,8 +104,8 @@ private:
|
||||
|
||||
void check_id_exist(identifier*);
|
||||
|
||||
void die(const std::string& info, const span& loc) {
|
||||
err.err("code", loc, info);
|
||||
void die(const std::string& info, expr* node) {
|
||||
err.err("code", node->get_location(), info);
|
||||
}
|
||||
|
||||
void regist_number(const f64);
|
||||
@@ -157,11 +161,11 @@ private:
|
||||
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& strs() const { return const_string_table; }
|
||||
const auto& nums() const { return const_number_table; }
|
||||
const auto& natives() const { return native_function; }
|
||||
const auto& codes() const { return code; }
|
||||
const auto& globals() const { return global; }
|
||||
|
||||
public:
|
||||
codegen() = default;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace nasal {
|
||||
|
||||
void operand_line_counter::init_counter() {
|
||||
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
|
||||
for (usize i = 0; i<operand_line_counter::operand_size; ++i) {
|
||||
operand_counter[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ void operand_line_counter::load_file_line_counter(
|
||||
file_line_counter = {};
|
||||
file_contents = {};
|
||||
filestream fs;
|
||||
for(usize i =0; i<file_list.size(); ++i) {
|
||||
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({});
|
||||
@@ -31,7 +31,7 @@ void operand_line_counter::dump_operand_count() const {
|
||||
typedef std::pair<u32, u64> op_count;
|
||||
std::vector<op_count> opcall;
|
||||
u64 total = 0;
|
||||
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
|
||||
for (usize i = 0; i<operand_line_counter::operand_size; ++i) {
|
||||
total += operand_counter[i];
|
||||
opcall.push_back({i, operand_counter[i]});
|
||||
}
|
||||
@@ -41,13 +41,13 @@ void operand_line_counter::dump_operand_count() const {
|
||||
}
|
||||
);
|
||||
std::clog << "\noperands call info (<1% ignored)\n";
|
||||
for(const auto& i : opcall) {
|
||||
for (const auto& i : opcall) {
|
||||
u64 rate = i.second*100/total;
|
||||
if (!rate) {
|
||||
break;
|
||||
}
|
||||
std::clog << " ";
|
||||
std::clog << operand_name_table.at(static_cast<op_code_type>(i.first));
|
||||
std::clog << operand_name_table.at(static_cast<opcode_type>(i.first));
|
||||
std::clog << " : " << i.second << " (" << rate << "%)\n";
|
||||
}
|
||||
std::clog << " total : " << total << '\n';
|
||||
@@ -55,17 +55,17 @@ void operand_line_counter::dump_operand_count() const {
|
||||
|
||||
void operand_line_counter::dump_all_code_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& context : file_line_counter) {
|
||||
for(const auto& count : context) {
|
||||
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) {
|
||||
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) {
|
||||
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]));
|
||||
@@ -76,7 +76,7 @@ void operand_line_counter::dump_all_code_line_counter(std::ostream& os) const {
|
||||
|
||||
void operand_line_counter::dump_this_file_line_counter(std::ostream& os) const {
|
||||
u64 max_call_time = 0;
|
||||
for(const auto& count : file_line_counter[0]) {
|
||||
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();
|
||||
@@ -84,7 +84,7 @@ void operand_line_counter::dump_this_file_line_counter(std::ostream& os) const {
|
||||
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) {
|
||||
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]));
|
||||
@@ -95,7 +95,7 @@ void operand_line_counter::dump_this_file_line_counter(std::ostream& os) const {
|
||||
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) {
|
||||
while (pos!=std::string::npos) {
|
||||
if (pos>last) {
|
||||
res.push_back(cmd.substr(last, pos-last));
|
||||
}
|
||||
@@ -109,7 +109,7 @@ std::vector<std::string> dbg::parse(const std::string& cmd) {
|
||||
}
|
||||
|
||||
u16 dbg::file_index(const std::string& filename) const {
|
||||
for(u16 i = 0; i<file_list_size; ++i) {
|
||||
for (u16 i = 0; i<file_list_size; ++i) {
|
||||
if (filename==files[i]) {
|
||||
return i;
|
||||
}
|
||||
@@ -142,7 +142,7 @@ void dbg::help() const {
|
||||
}
|
||||
|
||||
void dbg::list_file() const {
|
||||
for(usize i = 0; i<file_list_size; ++i) {
|
||||
for (usize i = 0; i<file_list_size; ++i) {
|
||||
std::clog << "[" << i << "] " << files[i] << "\n";
|
||||
}
|
||||
}
|
||||
@@ -154,18 +154,25 @@ void dbg::step_info() {
|
||||
|
||||
src.load(files[bytecode[ctx.pc].fidx]);
|
||||
|
||||
std::clog << clear_screen << set_cursor;
|
||||
std::clog << "\nsource code:\n";
|
||||
for(u64 i = begin; i<end && i<src.size(); ++i) {
|
||||
for (u64 i = begin; i<end && i<src.size(); ++i) {
|
||||
std::clog << (i==line? back_white:reset);
|
||||
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
|
||||
}
|
||||
|
||||
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
|
||||
end = (1+(ctx.pc>>3))<<3;
|
||||
codestream::set(const_number, const_string, native_function.data(), files);
|
||||
codestream::set(
|
||||
const_number,
|
||||
const_string,
|
||||
global_symbol_name,
|
||||
native_function.data(),
|
||||
files
|
||||
);
|
||||
|
||||
std::clog << "\nnext bytecode:\n";
|
||||
for(u64 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
||||
for (u64 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
||||
std::clog
|
||||
<< (i==ctx.pc? back_white:reset)
|
||||
<< (i==ctx.pc? "--> ":" ")
|
||||
@@ -195,12 +202,14 @@ void dbg::interact() {
|
||||
next = false;
|
||||
std::string cmd;
|
||||
step_info();
|
||||
while(true) {
|
||||
while (true) {
|
||||
std::clog << ">> ";
|
||||
std::getline(std::cin, cmd);
|
||||
auto res = parse(cmd);
|
||||
if (res.size()==0) {
|
||||
step_info();
|
||||
// enter key without input using cmd_next by default
|
||||
next = true;
|
||||
return;
|
||||
} else if (res.size()==1) {
|
||||
switch(get_cmd_type(res[0])) {
|
||||
case cmd_kind::cmd_help: help(); break;
|
||||
@@ -264,13 +273,13 @@ void dbg::run(const codegen& gen,
|
||||
std::vector<u8> code;
|
||||
std::vector<u16> code_file_index;
|
||||
std::vector<u64> code_line;
|
||||
for(const auto& i : gen.codes()) {
|
||||
for (const auto& i : gen.codes()) {
|
||||
code.push_back(i.op);
|
||||
code_file_index.push_back(i.fidx);
|
||||
code_line.push_back(i.line);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(operand_function[code[ctx.pc]]) {
|
||||
while (operand_function[code[ctx.pc]]) {
|
||||
interact();
|
||||
counter.add_operand_counter(code[ctx.pc]);
|
||||
counter.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
|
||||
@@ -287,7 +296,7 @@ void dbg::run(const codegen& gen,
|
||||
counter.dump_all_code_line_counter(std::clog):
|
||||
counter.dump_this_file_line_counter(std::clog);
|
||||
}
|
||||
ngc.info();
|
||||
ngc.status.dump_info();
|
||||
ngc.clear();
|
||||
imm.clear();
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace nasal {
|
||||
// and show them before each line of the source file
|
||||
class operand_line_counter {
|
||||
private:
|
||||
static const usize operand_size = op_code_type::op_ret + 1;
|
||||
static const usize operand_size = opcode_type::op_ret + 1;
|
||||
u64 operand_counter[operand_size];
|
||||
std::vector<std::string> file_name_list;
|
||||
std::vector<std::vector<u64>> file_line_counter;
|
||||
@@ -85,8 +85,9 @@ private:
|
||||
{"exit", cmd_kind::cmd_exit}
|
||||
};
|
||||
cmd_kind get_cmd_type(const std::string& cmd) const {
|
||||
return command_table.count(cmd)?
|
||||
command_table.at(cmd):cmd_kind::cmd_error;
|
||||
return command_table.count(cmd)
|
||||
? command_table.at(cmd)
|
||||
: cmd_kind::cmd_error;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -14,6 +14,52 @@ struct for_reset {
|
||||
static for_reset windows_system_set;
|
||||
#endif
|
||||
|
||||
std::ostream& clear_screen(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole == INVALID_HANDLE_VALUE) {
|
||||
return s;
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
auto rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
auto cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
|
||||
COORD coord = { 0, 0 };
|
||||
DWORD dwCharsWritten;
|
||||
|
||||
FillConsoleOutputCharacter(hConsole, ' ', dwConSize, coord, &dwCharsWritten);
|
||||
|
||||
// set raw attribute
|
||||
FillConsoleOutputAttribute(
|
||||
hConsole,
|
||||
csbi.wAttributes,
|
||||
dwConSize,
|
||||
coord,
|
||||
&dwCharsWritten
|
||||
);
|
||||
|
||||
// set cursor position
|
||||
SetConsoleCursorPosition(hConsole, coord);
|
||||
#else
|
||||
s << "\033c";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& set_cursor(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {0, 0});
|
||||
#else
|
||||
s << "\033[0;0H";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
std::ostream& back_white(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
|
||||
@@ -104,7 +150,7 @@ void filestream::load(const std::string& f) {
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
while(!in.eof()) {
|
||||
while (!in.eof()) {
|
||||
std::string line;
|
||||
std::getline(in, line);
|
||||
res.push_back(line);
|
||||
@@ -136,7 +182,7 @@ void error::err(const std::string& stage,
|
||||
const usize maxlen = std::to_string(loc.end_line).length();
|
||||
const std::string iden = identation(maxlen);
|
||||
|
||||
for(u64 line = loc.begin_line; line<=loc.end_line; ++line) {
|
||||
for (u64 line = loc.begin_line; line<=loc.end_line; ++line) {
|
||||
// skip line 0
|
||||
if (!line) {
|
||||
continue;
|
||||
@@ -165,25 +211,25 @@ void error::err(const std::string& stage,
|
||||
// output underline
|
||||
std::cerr << cyan << iden << " | " << reset;
|
||||
if (loc.begin_line==loc.end_line) {
|
||||
for(u64 i = 0; i<loc.begin_column; ++i) {
|
||||
for (u64 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u64 i = loc.begin_column; i<loc.end_column; ++i) {
|
||||
for (u64 i = loc.begin_column; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (line==loc.begin_line) {
|
||||
for(u64 i = 0; i<loc.begin_column; ++i) {
|
||||
for (u64 i = 0; i<loc.begin_column; ++i) {
|
||||
std::cerr << char(" \t"[code[i]=='\t']);
|
||||
}
|
||||
for(u64 i = loc.begin_column; i<code.size(); ++i) {
|
||||
for (u64 i = loc.begin_column; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
|
||||
}
|
||||
} else if (loc.begin_line<line && line<loc.end_line) {
|
||||
for(u64 i = 0; i<code.size(); ++i) {
|
||||
for (u64 i = 0; i<code.size(); ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
} else {
|
||||
for(u64 i = 0; i<loc.end_column; ++i) {
|
||||
for (u64 i = 0; i<loc.end_column; ++i) {
|
||||
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ struct span {
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& clear_screen(std::ostream&);
|
||||
std::ostream& set_cursor(std::ostream&);
|
||||
std::ostream& back_white(std::ostream&);
|
||||
std::ostream& red(std::ostream&);
|
||||
std::ostream& cyan(std::ostream&);
|
||||
@@ -52,7 +54,7 @@ private:
|
||||
}
|
||||
std::string leftpad(u64 num, usize len) {
|
||||
auto tmp = std::to_string(num);
|
||||
while(tmp.length()<len) {
|
||||
while (tmp.length()<len) {
|
||||
tmp = " "+tmp;
|
||||
}
|
||||
return tmp;
|
||||
|
||||
320
src/nasal_gc.cpp
320
src/nasal_gc.cpp
@@ -1,33 +1,38 @@
|
||||
#include "nasal_gc.h"
|
||||
#include "util/util.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void gc::do_mark_sweep() {
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
auto begin = clk::now();
|
||||
count_mark_time();
|
||||
count_sweep_time();
|
||||
}
|
||||
|
||||
void gc::count_mark_time() {
|
||||
if (in_incremental_sweep_stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
status.stamp();
|
||||
mark();
|
||||
auto mark_end = clk::now();
|
||||
status.elapsed_mark_time();
|
||||
|
||||
in_incremental_sweep_stage = true;
|
||||
current_sweep_index = memory.size() - 1;
|
||||
}
|
||||
|
||||
void gc::count_sweep_time() {
|
||||
status.stamp();
|
||||
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;
|
||||
status.elapsed_sweep_time();
|
||||
}
|
||||
|
||||
void gc::mark() {
|
||||
std::vector<var> bfs;
|
||||
mark_context_root(bfs);
|
||||
|
||||
// concurrent mark, experimental
|
||||
if (memory.size()>UINT16_MAX && bfs.size()>32) {
|
||||
flag_concurrent_mark_triggered = true;
|
||||
usize size = bfs.size();
|
||||
// concurrent mark
|
||||
if (memory.size() > UINT16_MAX * 16 && bfs.size() > 16) {
|
||||
auto 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);
|
||||
@@ -40,7 +45,7 @@ void gc::mark() {
|
||||
}
|
||||
|
||||
// normal mark
|
||||
while(!bfs.empty()) {
|
||||
while (!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_type::vm_num ||
|
||||
@@ -53,7 +58,7 @@ void gc::mark() {
|
||||
|
||||
void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
std::vector<var> bfs;
|
||||
for(auto i = begin; i<end; ++i) {
|
||||
for (auto i = begin; i<end; ++i) {
|
||||
var value = vec[i];
|
||||
if (value.type<=vm_type::vm_num ||
|
||||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
|
||||
@@ -61,7 +66,7 @@ void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
}
|
||||
mark_var(bfs, value);
|
||||
}
|
||||
while(!bfs.empty()) {
|
||||
while (!bfs.empty()) {
|
||||
var value = bfs.back();
|
||||
bfs.pop_back();
|
||||
if (value.type<=vm_type::vm_num ||
|
||||
@@ -74,15 +79,15 @@ void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
|
||||
void gc::mark_context_root(std::vector<var>& bfs_queue) {
|
||||
// scan global
|
||||
for(usize i = 0; i<main_context_global_size; ++i) {
|
||||
for (usize i = 0; i < main_context_global_size; ++i) {
|
||||
auto& val = main_context_global[i];
|
||||
if (val.type>vm_type::vm_num) {
|
||||
if (val.type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(val);
|
||||
}
|
||||
}
|
||||
// scan now running context, this context maybe related to coroutine or main
|
||||
for(var* i = running_context->stack; i<=running_context->top; ++i) {
|
||||
if (i->type>vm_type::vm_num) {
|
||||
for (var* i = running_context->stack; i <= running_context->top; ++i) {
|
||||
if (i->type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
@@ -95,8 +100,8 @@ void gc::mark_context_root(std::vector<var>& bfs_queue) {
|
||||
}
|
||||
|
||||
// coroutine is running, so scan main process stack from mctx
|
||||
for(var* i = main_context.stack; i<=main_context.top; ++i) {
|
||||
if (i->type>vm_type::vm_num) {
|
||||
for (var* i = main_context.stack; i <= main_context.top; ++i) {
|
||||
if (i->type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
@@ -119,35 +124,35 @@ void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
||||
}
|
||||
|
||||
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
|
||||
for(auto& i : vec.elems) {
|
||||
if (i.type>vm_type::vm_num) {
|
||||
for (auto& i : vec.elems) {
|
||||
if (i.type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
|
||||
for(auto& i : hash.elems) {
|
||||
if (i.second.type>vm_type::vm_num) {
|
||||
for (auto& i : hash.elems) {
|
||||
if (i.second.type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
|
||||
for(auto& i : function.local) {
|
||||
if (i.type>vm_type::vm_num) {
|
||||
for (auto& i : function.local) {
|
||||
if (i.type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
for(auto& i : function.upval) {
|
||||
for (auto& i : function.upval) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
||||
for(auto& i : upval.elems) {
|
||||
if (i.type>vm_type::vm_num) {
|
||||
for (auto& i : upval.elems) {
|
||||
if (i.type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(i);
|
||||
}
|
||||
}
|
||||
@@ -163,38 +168,50 @@ void gc::mark_ghost(std::vector<var>& bfs_queue, nas_ghost& ghost) {
|
||||
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
||||
bfs_queue.push_back(co.ctx.funcr);
|
||||
bfs_queue.push_back(co.ctx.upvalr);
|
||||
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
|
||||
if (i->type>vm_type::vm_num) {
|
||||
for (var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
|
||||
if (i->type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::mark_map(std::vector<var>& bfs_queue, nas_map& mp) {
|
||||
for(const auto& i : mp.mapper) {
|
||||
if (i.second->type>vm_type::vm_num) {
|
||||
for (const auto& i : mp.mapper) {
|
||||
if (i.second->type > vm_type::vm_num) {
|
||||
bfs_queue.push_back(*i.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc::sweep() {
|
||||
for(auto i : memory) {
|
||||
// if threshold is too small, too many allocated objects will be marked as "found"
|
||||
// objects with "found" will be marked to "uncollected" in the next gc cycle
|
||||
// this will cause memory wasting.
|
||||
const i64 threshold = 4096;
|
||||
for (i64 it = 0; it < threshold; ++it) {
|
||||
if (current_sweep_index - it < 0) {
|
||||
break;
|
||||
}
|
||||
auto i = memory[current_sweep_index - it];
|
||||
if (i->mark==nas_val::gc_status::uncollected) {
|
||||
i->clear();
|
||||
unused[static_cast<u8>(i->type)-static_cast<u8>(vm_type::vm_str)].push_back(i);
|
||||
unused[static_cast<u32>(i->type)-static_cast<u32>(vm_type::vm_str)].push_back(i);
|
||||
i->mark = nas_val::gc_status::collected;
|
||||
} else if (i->mark==nas_val::gc_status::found) {
|
||||
i->mark = nas_val::gc_status::uncollected;
|
||||
}
|
||||
}
|
||||
current_sweep_index -= threshold;
|
||||
if (current_sweep_index < 0) {
|
||||
in_incremental_sweep_stage = false;
|
||||
current_sweep_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gc::extend(const vm_type type) {
|
||||
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str);
|
||||
size[index] += incr[index];
|
||||
const u32 index = static_cast<u32>(type)-static_cast<u32>(vm_type::vm_str);
|
||||
status.object_size[index] += incr[index];
|
||||
|
||||
for(u64 i = 0; i<incr[index]; ++i) {
|
||||
for (u64 i = 0; i<incr[index]; ++i) {
|
||||
// no need to check, will be killed if memory is not enough
|
||||
nas_val* tmp = new nas_val(type);
|
||||
|
||||
@@ -202,25 +219,41 @@ void gc::extend(const vm_type type) {
|
||||
memory.push_back(tmp);
|
||||
unused[index].push_back(tmp);
|
||||
}
|
||||
switch(type) {
|
||||
case vm_type::vm_str:
|
||||
total_object_count += incr[index] * sizeof(std::string); break;
|
||||
case vm_type::vm_vec:
|
||||
total_object_count += incr[index] * sizeof(nas_vec); break;
|
||||
case vm_type::vm_hash:
|
||||
total_object_count += incr[index] * sizeof(nas_hash); break;
|
||||
case vm_type::vm_func:
|
||||
total_object_count += incr[index] * sizeof(nas_func); break;
|
||||
case vm_type::vm_upval:
|
||||
total_object_count += incr[index] * sizeof(nas_upval); break;
|
||||
case vm_type::vm_ghost:
|
||||
total_object_count += incr[index] * sizeof(nas_ghost); break;
|
||||
case vm_type::vm_co:
|
||||
total_object_count += incr[index] * sizeof(nas_co); break;
|
||||
case vm_type::vm_map:
|
||||
total_object_count += incr[index] * sizeof(nas_map); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// if incr[index] = 1, this will always be 1
|
||||
incr[index] = incr[index]+incr[index]/2;
|
||||
incr[index] = incr[index] + incr[index];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// initialize gc status recorder
|
||||
status.init();
|
||||
|
||||
// coroutine pointer set to nullptr
|
||||
cort = nullptr;
|
||||
|
||||
// init constant strings
|
||||
strs.resize(constant_strings.size());
|
||||
for(u64 i = 0; i<strs.size(); ++i) {
|
||||
for (u64 i = 0; i < strs.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (strs[i].is_str() && strs[i].str()==constant_strings[i]) {
|
||||
continue;
|
||||
@@ -228,184 +261,69 @@ void gc::init(const std::vector<std::string>& constant_strings,
|
||||
strs[i] = var::gcobj(new nas_val(vm_type::vm_str));
|
||||
strs[i].val.gcobj->immutable = 1;
|
||||
strs[i].str() = constant_strings[i];
|
||||
total_object_count += strs[i].str().size();
|
||||
total_object_count += sizeof(std::string);
|
||||
}
|
||||
|
||||
// record arguments
|
||||
env_argv.resize(argv.size());
|
||||
for(u64 i = 0; i<argv.size(); ++i) {
|
||||
for (u64 i = 0; i < argv.size(); ++i) {
|
||||
// incremental initialization, avoid memory leak in repl mode
|
||||
if (env_argv[i].is_str() && env_argv[i].str()==argv[i]) {
|
||||
if (env_argv[i].is_str() && env_argv[i].str() == argv[i]) {
|
||||
continue;
|
||||
}
|
||||
env_argv[i] = var::gcobj(new nas_val(vm_type::vm_str));
|
||||
env_argv[i].val.gcobj->immutable = 1;
|
||||
env_argv[i].str() = argv[i];
|
||||
total_object_count += env_argv[i].str().size();
|
||||
total_object_count += sizeof(std::string);
|
||||
}
|
||||
}
|
||||
|
||||
void gc::clear() {
|
||||
for(auto i : memory) {
|
||||
for (auto i : memory) {
|
||||
delete i;
|
||||
}
|
||||
memory.clear();
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
for (u32 i = 0; i<GC_TYPE_SIZE; ++i) {
|
||||
unused[i].clear();
|
||||
}
|
||||
for(auto& i : strs) {
|
||||
for (auto& i : strs) {
|
||||
delete i.val.gcobj;
|
||||
}
|
||||
strs.clear();
|
||||
env_argv.clear();
|
||||
}
|
||||
|
||||
void gc::info() const {
|
||||
util::windows_code_page_manager wm;
|
||||
wm.set_utf8_output();
|
||||
|
||||
using std::left;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::setprecision;
|
||||
|
||||
const char* used_table_name[] = {
|
||||
"object type",
|
||||
"gc count",
|
||||
"alloc count",
|
||||
"memory size",
|
||||
"detail",
|
||||
"time spend",
|
||||
"gc time",
|
||||
"avg time",
|
||||
"max gc",
|
||||
"max mark",
|
||||
"max sweep",
|
||||
nullptr
|
||||
};
|
||||
const char* name[] = {
|
||||
"string",
|
||||
"vector",
|
||||
"hashmap",
|
||||
"function",
|
||||
"upvalue",
|
||||
"ghost",
|
||||
"coroutine",
|
||||
"namespace",
|
||||
nullptr
|
||||
};
|
||||
|
||||
usize indent = 0, len = 0;
|
||||
for(usize i = 0; used_table_name[i]; ++i) {
|
||||
len = std::string(used_table_name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(usize i = 0; name[i]; ++i) {
|
||||
len = std::string(name[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
len = std::to_string(gcnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(acnt[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(size[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
auto indent_string = std::string("──");
|
||||
for(usize i = 0; i<indent; ++i) {
|
||||
indent_string += "─";
|
||||
}
|
||||
const auto first_line = "╭" + indent_string + "┬" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "╮";
|
||||
const auto mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┤";
|
||||
const auto another_mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┤";
|
||||
const auto last_line = "╰" + indent_string + "┴" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "╯";
|
||||
|
||||
std::clog << "\n" << first_line << "\n";
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "gc count";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "memory size";
|
||||
std::clog << " │\n" << mid_line << "\n";
|
||||
|
||||
double total = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
if (!gcnt[i] && !acnt[i] && !size[i]) {
|
||||
continue;
|
||||
}
|
||||
total += static_cast<f64>(gcnt[i]);
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << size[i];
|
||||
std::clog << " │\n";
|
||||
}
|
||||
std::clog << mid_line << "\n";
|
||||
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " │ " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " │ " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " │\n" << another_mid_line << "\n";
|
||||
|
||||
const auto gc_time = worktime*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << gc_time << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
const auto avg_time = worktime*1.0/den*1000/total;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << avg_time << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
const auto max_gc = max_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max gc";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_gc << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
const auto max_mark = max_mark_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_mark << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
const auto max_sweep = max_sweep_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_sweep << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "concurrent";
|
||||
std::clog << " │ " << setw(indent)
|
||||
<< (flag_concurrent_mark_triggered? "true":"false");
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << last_line << "\n";
|
||||
|
||||
wm.restore_code_page();
|
||||
}
|
||||
|
||||
var gc::alloc(const vm_type type) {
|
||||
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str);
|
||||
++acnt[index];
|
||||
if (unused[index].empty()) {
|
||||
++gcnt[index];
|
||||
const u32 index = static_cast<u32>(type)-static_cast<u32>(vm_type::vm_str);
|
||||
++status.alloc_count[index];
|
||||
// if still in incremental sweep stage? do it
|
||||
// if not in incremental sweep stage, run a new gc cycle
|
||||
if (in_incremental_sweep_stage) {
|
||||
do_mark_sweep();
|
||||
} else if (unused[index].empty()) {
|
||||
++status.gc_cycle_trigger_count[index];
|
||||
do_mark_sweep();
|
||||
}
|
||||
// if in incremental sweep stage, but the unused list is empty,
|
||||
// do it until the unused list has something
|
||||
while (unused[index].empty() && in_incremental_sweep_stage) {
|
||||
do_mark_sweep();
|
||||
}
|
||||
// after all gc stages, still get empty list, extend
|
||||
if (unused[index].empty()) {
|
||||
extend(type);
|
||||
}
|
||||
|
||||
var ret = var::gcobj(unused[index].back());
|
||||
ret.val.gcobj->mark = nas_val::gc_status::uncollected;
|
||||
ret.val.gcobj->clear();
|
||||
|
||||
// if incremental sweep stage, mark it as found
|
||||
// but be aware that it may be collected in next gc cycle
|
||||
ret.val.gcobj->mark = in_incremental_sweep_stage
|
||||
? nas_val::gc_status::found
|
||||
: nas_val::gc_status::uncollected;
|
||||
unused[index].pop_back();
|
||||
return ret;
|
||||
}
|
||||
@@ -426,9 +344,9 @@ void gc::context_change(nas_co* co) {
|
||||
|
||||
void gc::context_reserve() {
|
||||
// pc = 0 means this coroutine is finished
|
||||
cort->status = running_context->pc?
|
||||
nas_co::status::suspended:
|
||||
nas_co::status::dead;
|
||||
cort->status = running_context->pc
|
||||
? nas_co::status::suspended
|
||||
: nas_co::status::dead;
|
||||
|
||||
// store running state to coroutine
|
||||
cort->ctx = *running_context;
|
||||
|
||||
@@ -17,9 +17,18 @@
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_type.h"
|
||||
#include "util/gc_stat.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct free_list {
|
||||
std::vector<nas_val*> elem[GC_TYPE_SIZE];
|
||||
|
||||
auto& operator[](i64 index) {
|
||||
return elem[index];
|
||||
}
|
||||
};
|
||||
|
||||
struct gc {
|
||||
/* main context temporary storage */
|
||||
context main_context;
|
||||
@@ -30,38 +39,37 @@ struct gc {
|
||||
|
||||
/* runtime context */
|
||||
context* running_context = nullptr;
|
||||
nas_co* cort = nullptr; // running coroutine
|
||||
nas_co* cort = nullptr; // running coroutine
|
||||
|
||||
/* temporary space used in native/module functions */
|
||||
/* 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
|
||||
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
|
||||
free_list unused; // gc free list
|
||||
|
||||
/* heap increase size */
|
||||
u64 incr[gc_type_size] = {
|
||||
128, // vm_str
|
||||
128, // vm_vec
|
||||
64, // vm_hash
|
||||
u64 incr[GC_TYPE_SIZE] = {
|
||||
256, // vm_str
|
||||
256, // vm_vec
|
||||
256, // vm_hash
|
||||
256, // vm_func
|
||||
256, // vm_upval
|
||||
16, // vm_obj
|
||||
16, // vm_co
|
||||
2, // vm_map
|
||||
4, // vm_obj
|
||||
4, // vm_co
|
||||
1, // vm_map
|
||||
};
|
||||
|
||||
// total object count
|
||||
u64 total_object_count = 0;
|
||||
|
||||
/* values for analysis */
|
||||
u64 size[gc_type_size];
|
||||
u64 gcnt[gc_type_size];
|
||||
u64 acnt[gc_type_size];
|
||||
i64 worktime = 0;
|
||||
i64 max_time = 0;
|
||||
i64 max_mark_time = 0;
|
||||
i64 max_sweep_time = 0;
|
||||
bool flag_concurrent_mark_triggered = false;
|
||||
gc_stat status;
|
||||
|
||||
bool in_incremental_sweep_stage = false;
|
||||
i64 current_sweep_index = 0;
|
||||
|
||||
void set(context* _ctx, var* _global, usize _size) {
|
||||
running_context = _ctx;
|
||||
@@ -72,6 +80,8 @@ struct gc {
|
||||
private:
|
||||
/* gc functions */
|
||||
void do_mark_sweep();
|
||||
void count_mark_time();
|
||||
void count_sweep_time();
|
||||
void mark();
|
||||
void concurrent_mark(std::vector<var>&, usize, usize);
|
||||
void mark_context_root(std::vector<var>&);
|
||||
@@ -89,11 +99,20 @@ public:
|
||||
void extend(const vm_type);
|
||||
void init(const std::vector<std::string>&, const std::vector<std::string>&);
|
||||
void clear();
|
||||
void info() const;
|
||||
var alloc(const vm_type);
|
||||
void context_change(nas_co*);
|
||||
void context_reserve();
|
||||
|
||||
public:
|
||||
f64 get_gc_time_ms() const {
|
||||
return status.gc_time_ms();
|
||||
}
|
||||
|
||||
// not very accurate
|
||||
f64 get_total_memory() const {
|
||||
return total_object_count * 3.5 / 1024.0 / 1024.0;
|
||||
}
|
||||
|
||||
public:
|
||||
var newstr(char c) {
|
||||
var s = alloc(vm_type::vm_str);
|
||||
@@ -126,13 +145,4 @@ struct module_func_info {
|
||||
// module function "get" type
|
||||
typedef module_func_info* (*get_func_ptr)();
|
||||
|
||||
|
||||
// avoid error loading function bug in MSVC version nasal.exe
|
||||
#ifdef _MSC_VER
|
||||
// and fuck MSVC again
|
||||
#define NASAL_EXTERN extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define NASAL_EXTERN extern "C"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,17 @@
|
||||
namespace nasal {
|
||||
|
||||
linker::linker(): show_path_flag(false), this_file("") {
|
||||
const auto env_get_path = getenv("PATH");
|
||||
if (!env_get_path) {
|
||||
err.warn("link", "cannot get env \"PATH\".");
|
||||
envpath = {};
|
||||
return;
|
||||
}
|
||||
|
||||
const auto seperator = util::is_windows()? ';':':';
|
||||
const auto PATH = std::string(getenv("PATH"));
|
||||
const auto PATH = std::string(env_get_path);
|
||||
usize last = 0, position = PATH.find(seperator, 0);
|
||||
while(position!=std::string::npos) {
|
||||
while (position!=std::string::npos) {
|
||||
std::string dirpath = PATH.substr(last, position-last);
|
||||
if (dirpath.length()) {
|
||||
envpath.push_back(dirpath);
|
||||
@@ -29,7 +36,7 @@ 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) {
|
||||
for (auto i : path) {
|
||||
file_relative_path += i->get_name();
|
||||
if (i!=path.back()) {
|
||||
file_relative_path += (util::is_windows()? "\\":"/");
|
||||
@@ -49,12 +56,12 @@ std::string linker::find_real_file_path(const std::string& filename,
|
||||
std::vector<fs::path> path_list = {filename};
|
||||
|
||||
// generate search path from environ path
|
||||
for(const auto& p : envpath) {
|
||||
for (const auto& p : envpath) {
|
||||
path_list.push_back(fs::path(p)/filename);
|
||||
}
|
||||
|
||||
// search file
|
||||
for(const auto& path : path_list) {
|
||||
for (const auto& path : path_list) {
|
||||
if (fs::exists(path)) {
|
||||
return path.str();
|
||||
}
|
||||
@@ -75,7 +82,7 @@ std::string linker::find_real_file_path(const std::string& filename,
|
||||
return "";
|
||||
}
|
||||
auto path_list_info = std::string("");
|
||||
for(const auto& path : path_list) {
|
||||
for (const auto& path : path_list) {
|
||||
path_list_info += " -> " + path.str() + "\n";
|
||||
}
|
||||
err.err("link",
|
||||
@@ -131,7 +138,7 @@ bool linker::import_check(expr* node) {
|
||||
|
||||
bool linker::check_exist_or_record_file(const std::string& file) {
|
||||
// avoid importing the same file
|
||||
for(const auto& name : imported_files) {
|
||||
for (const auto& name : imported_files) {
|
||||
if (file==name) {
|
||||
return true;
|
||||
}
|
||||
@@ -141,7 +148,7 @@ bool linker::check_exist_or_record_file(const std::string& file) {
|
||||
}
|
||||
|
||||
bool linker::check_self_import(const std::string& file) {
|
||||
for(const auto& name : module_load_stack) {
|
||||
for (const auto& name : module_load_stack) {
|
||||
if (file==name) {
|
||||
return true;
|
||||
}
|
||||
@@ -151,7 +158,7 @@ bool linker::check_self_import(const std::string& file) {
|
||||
|
||||
std::string linker::generate_self_import_path(const std::string& filename) {
|
||||
std::string res = "";
|
||||
for(const auto& i : module_load_stack) {
|
||||
for (const auto& i : module_load_stack) {
|
||||
res += "[" + i + "] -> ";
|
||||
}
|
||||
return res + "[" + filename + "]";
|
||||
@@ -159,7 +166,7 @@ std::string linker::generate_self_import_path(const std::string& filename) {
|
||||
|
||||
void linker::merge_tree(code_block* new_tree_root, code_block* old_tree_root) {
|
||||
// add children of add_root to the back of root
|
||||
for(auto& i : old_tree_root->get_expressions()) {
|
||||
for (auto& i : old_tree_root->get_expressions()) {
|
||||
new_tree_root->add_expression(i);
|
||||
}
|
||||
// clean old root
|
||||
@@ -322,12 +329,12 @@ return_expr* linker::generate_module_return(code_block* block) {
|
||||
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());
|
||||
for (const auto& i : finder->do_find(block)) {
|
||||
// do not export symbol begins with '_'
|
||||
if (i.name.length() && i.name[0]=='_') {
|
||||
continue;
|
||||
}
|
||||
auto pair = new hash_pair(block->get_location());
|
||||
pair->set_name(i.name);
|
||||
pair->set_value(new identifier(block->get_location(), i.name));
|
||||
value->add_member(pair);
|
||||
@@ -362,7 +369,7 @@ definition_expr* linker::generate_module_definition(code_block* block) {
|
||||
void linker::load(code_block* program_root, const std::string& filename) {
|
||||
// load imported modules
|
||||
std::unordered_set<std::string> used_modules = {};
|
||||
for(auto& import_node : program_root->get_expressions()) {
|
||||
for (auto& import_node : program_root->get_expressions()) {
|
||||
if (!import_check(import_node)) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ bool lexer::is_calc_opr(char 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') {}
|
||||
while (++ptr<res.size() && res[ptr]!='\n') {}
|
||||
}
|
||||
|
||||
void lexer::err_char() {
|
||||
@@ -114,7 +114,7 @@ tok lexer::get_type(const std::string& str) {
|
||||
|
||||
std::string lexer::utf8_gen() {
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && res[ptr]<0) {
|
||||
while (ptr<res.size() && res[ptr]<0) {
|
||||
std::string tmp = "";
|
||||
u32 nbytes = util::utf8_hdchk(res[ptr]);
|
||||
if (!nbytes) {
|
||||
@@ -124,7 +124,7 @@ std::string lexer::utf8_gen() {
|
||||
}
|
||||
|
||||
tmp += res[ptr++];
|
||||
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
|
||||
for (u32 i = 0; i<nbytes; ++i, ++ptr) {
|
||||
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
|
||||
tmp += res[ptr];
|
||||
}
|
||||
@@ -134,7 +134,7 @@ std::string lexer::utf8_gen() {
|
||||
if (tmp.length()!=1+nbytes) {
|
||||
++column;
|
||||
std::string utf_info = "0x" + util::char_to_hex(tmp[0]);
|
||||
for(u32 i = 1; i<tmp.size(); ++i) {
|
||||
for (u32 i = 1; i<tmp.size(); ++i) {
|
||||
utf_info += " 0x" + util::char_to_hex(tmp[i]);
|
||||
}
|
||||
err.err("lexer",
|
||||
@@ -154,7 +154,7 @@ token lexer::id_gen() {
|
||||
u64 begin_line = line;
|
||||
u64 begin_column = column;
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
|
||||
while (ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
|
||||
if (res[ptr]<0) { // utf-8
|
||||
str += utf8_gen();
|
||||
} else { // ascii
|
||||
@@ -177,7 +177,7 @@ token lexer::num_gen() {
|
||||
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])) {
|
||||
while (ptr<res.size() && is_hex(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
column += str.length();
|
||||
@@ -196,11 +196,11 @@ token lexer::num_gen() {
|
||||
} 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])) {
|
||||
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]))) {
|
||||
while (ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
|
||||
erfmt = true;
|
||||
str += res[ptr++];
|
||||
}
|
||||
@@ -220,12 +220,12 @@ token lexer::num_gen() {
|
||||
// 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])) {
|
||||
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])) {
|
||||
while (ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxx." is not a correct number
|
||||
@@ -247,7 +247,7 @@ token lexer::num_gen() {
|
||||
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
while(ptr<res.size() && is_dec(res[ptr])) {
|
||||
while (ptr<res.size() && is_dec(res[ptr])) {
|
||||
str += res[ptr++];
|
||||
}
|
||||
// "xxxe(-|+)" is not a correct number
|
||||
@@ -278,7 +278,7 @@ token lexer::str_gen() {
|
||||
std::string str = "";
|
||||
const char begin = res[ptr];
|
||||
++column;
|
||||
while(++ptr<res.size() && res[ptr]!=begin) {
|
||||
while (++ptr<res.size() && res[ptr]!=begin) {
|
||||
++column;
|
||||
if (res[ptr]=='\n') {
|
||||
column = 0;
|
||||
@@ -404,8 +404,8 @@ const error& lexer::scan(const std::string& file) {
|
||||
toks = {};
|
||||
open(file);
|
||||
|
||||
while(ptr<res.size()) {
|
||||
while(ptr<res.size() && skip(res[ptr])) {
|
||||
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') {
|
||||
|
||||
@@ -5,12 +5,31 @@ namespace nasal {
|
||||
|
||||
void codestream::set(const f64* number_list,
|
||||
const std::string* string_list,
|
||||
const std::unordered_map<std::string, u64>& globals,
|
||||
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;
|
||||
|
||||
global_variable.resize(globals.size());
|
||||
for (auto& [name, index]: globals) {
|
||||
global_variable[index] = name;
|
||||
}
|
||||
}
|
||||
|
||||
void codestream::set(const f64* number_list,
|
||||
const std::string* string_list,
|
||||
const std::vector<std::string>& globals,
|
||||
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;
|
||||
|
||||
global_variable = globals;
|
||||
}
|
||||
|
||||
void codestream::dump(std::ostream& out) const {
|
||||
@@ -28,12 +47,13 @@ void codestream::dump(std::ostream& out) const {
|
||||
<< setw(2) << setfill('0') << static_cast<u32>(op) << ":" << dec;
|
||||
|
||||
// dump immediate number(hex format)
|
||||
for(i32 i = 64-8; i>=0; i -= 8) {
|
||||
out << hex << setw(2) << setfill('0') << ((num>>i)&0xff) << dec << " ";
|
||||
for (i32 i = 64-8; i>=0; i -= 8) {
|
||||
auto this_byte = ((num>>i)&0xff);
|
||||
out << hex << setw(2) << setfill('0') << this_byte << dec << " ";
|
||||
}
|
||||
|
||||
// dump operand name
|
||||
out << " " << operand_name_table.at(static_cast<op_code_type>(op)) << " ";
|
||||
out << " " << operand_name_table.at(static_cast<opcode_type>(op)) << " ";
|
||||
|
||||
switch(op) {
|
||||
case op_addeq:
|
||||
@@ -56,7 +76,7 @@ void codestream::dump(std::ostream& out) const {
|
||||
break;
|
||||
case op_lnkeqc:
|
||||
out << hex << "0x" << num << dec;
|
||||
out << " (" << util::rawstr(const_string[num], 16) << ")";
|
||||
out << " (\"" << util::rawstr(const_string[num], 32) << "\")";
|
||||
break;
|
||||
case op_addecp:
|
||||
case op_subecp:
|
||||
@@ -67,7 +87,7 @@ void codestream::dump(std::ostream& out) const {
|
||||
break;
|
||||
case op_lnkecp:
|
||||
out << hex << "0x" << num << dec;
|
||||
out << " (" << util::rawstr(const_string[num], 16) << ") sp-1";
|
||||
out << " (\"" << util::rawstr(const_string[num], 32) << "\") sp-1";
|
||||
break;
|
||||
case op_addc:
|
||||
case op_subc:
|
||||
@@ -92,17 +112,22 @@ void codestream::dump(std::ostream& out) const {
|
||||
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_loadg:
|
||||
case op_mcallg:
|
||||
case op_callg:
|
||||
out << hex << "0x" << num << dec;
|
||||
out << " (" << util::rawstr(global_variable[num], 32) << ")";
|
||||
break;
|
||||
case op_callb:
|
||||
out << hex << "0x" << num << " <" << natives[num].name
|
||||
<< "@0x" << reinterpret_cast<u64>(natives[num].func)
|
||||
<< dec << ">"; break;
|
||||
out << hex << "0x" << num << dec;
|
||||
out << " <" << natives[num].name << "@0x";
|
||||
out << hex << reinterpret_cast<u64>(natives[num].func) << dec;
|
||||
out << ">";
|
||||
break;
|
||||
case op_upval:
|
||||
case op_mupval:
|
||||
case op_loadu:
|
||||
@@ -117,7 +142,7 @@ void codestream::dump(std::ostream& out) const {
|
||||
case op_deft:
|
||||
case op_dyn:
|
||||
out << hex << "0x" << num << dec;
|
||||
out << " (" << util::rawstr(const_string[num], 16) << ")";
|
||||
out << " (\"" << util::rawstr(const_string[num], 32) << "\")";
|
||||
break;
|
||||
default:
|
||||
if (files) {
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
#include "natives/builtin.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum op_code_type: u8 {
|
||||
enum opcode_type: u8 {
|
||||
op_exit, // stop the virtual machine
|
||||
op_repl, // in repl mode: print value on stack top
|
||||
op_intl, // local scope size
|
||||
@@ -98,95 +102,95 @@ enum op_code_type: u8 {
|
||||
op_ret // return
|
||||
};
|
||||
|
||||
static std::unordered_map<op_code_type, std::string> operand_name_table = {
|
||||
{op_code_type::op_exit, "exit "},
|
||||
{op_code_type::op_repl, "repl "},
|
||||
{op_code_type::op_intl, "intl "},
|
||||
{op_code_type::op_loadg, "loadg "},
|
||||
{op_code_type::op_loadl, "loadl "},
|
||||
{op_code_type::op_loadu, "loadu "},
|
||||
{op_code_type::op_dup, "dup "},
|
||||
{op_code_type::op_pnum, "pnum "},
|
||||
{op_code_type::op_pnil, "pnil "},
|
||||
{op_code_type::op_pstr, "pstr "},
|
||||
{op_code_type::op_newv, "newv "},
|
||||
{op_code_type::op_newh, "newh "},
|
||||
{op_code_type::op_newf, "newf "},
|
||||
{op_code_type::op_happ, "happ "},
|
||||
{op_code_type::op_para, "para "},
|
||||
{op_code_type::op_deft, "def "},
|
||||
{op_code_type::op_dyn, "dyn "},
|
||||
{op_code_type::op_lnot, "lnot "},
|
||||
{op_code_type::op_usub, "usub "},
|
||||
{op_code_type::op_bnot, "bitnot"},
|
||||
{op_code_type::op_btor, "bitor "},
|
||||
{op_code_type::op_btxor, "bitxor"},
|
||||
{op_code_type::op_btand, "bitand"},
|
||||
{op_code_type::op_add, "add "},
|
||||
{op_code_type::op_sub, "sub "},
|
||||
{op_code_type::op_mul, "mult "},
|
||||
{op_code_type::op_div, "div "},
|
||||
{op_code_type::op_lnk, "lnk "},
|
||||
{op_code_type::op_addc, "addc "},
|
||||
{op_code_type::op_subc, "subc "},
|
||||
{op_code_type::op_mulc, "multc "},
|
||||
{op_code_type::op_divc, "divc "},
|
||||
{op_code_type::op_lnkc, "lnkc "},
|
||||
{op_code_type::op_addeq, "addeq "},
|
||||
{op_code_type::op_subeq, "subeq "},
|
||||
{op_code_type::op_muleq, "muleq "},
|
||||
{op_code_type::op_diveq, "diveq "},
|
||||
{op_code_type::op_lnkeq, "lnkeq "},
|
||||
{op_code_type::op_btandeq, "bandeq"},
|
||||
{op_code_type::op_btoreq, "boreq "},
|
||||
{op_code_type::op_btxoreq, "bxoreq"},
|
||||
{op_code_type::op_addeqc, "addeqc"},
|
||||
{op_code_type::op_subeqc, "subeqc"},
|
||||
{op_code_type::op_muleqc, "muleqc"},
|
||||
{op_code_type::op_diveqc, "diveqc"},
|
||||
{op_code_type::op_lnkeqc, "lnkeqc"},
|
||||
{op_code_type::op_addecp, "addecp"},
|
||||
{op_code_type::op_subecp, "subecp"},
|
||||
{op_code_type::op_mulecp, "mulecp"},
|
||||
{op_code_type::op_divecp, "divecp"},
|
||||
{op_code_type::op_lnkecp, "lnkecp"},
|
||||
{op_code_type::op_meq, "meq "},
|
||||
{op_code_type::op_eq, "eq "},
|
||||
{op_code_type::op_neq, "neq "},
|
||||
{op_code_type::op_less, "less "},
|
||||
{op_code_type::op_leq, "leq "},
|
||||
{op_code_type::op_grt, "grt "},
|
||||
{op_code_type::op_geq, "geq "},
|
||||
{op_code_type::op_lessc, "lessc "},
|
||||
{op_code_type::op_leqc, "leqc "},
|
||||
{op_code_type::op_grtc, "grtc "},
|
||||
{op_code_type::op_geqc, "geqc "},
|
||||
{op_code_type::op_pop, "pop "},
|
||||
{op_code_type::op_jmp, "jmp "},
|
||||
{op_code_type::op_jt, "jt "},
|
||||
{op_code_type::op_jf, "jf "},
|
||||
{op_code_type::op_cnt, "cnt "},
|
||||
{op_code_type::op_findex, "findx "},
|
||||
{op_code_type::op_feach, "feach "},
|
||||
{op_code_type::op_callg, "callg "},
|
||||
{op_code_type::op_calll, "calll "},
|
||||
{op_code_type::op_upval, "upval "},
|
||||
{op_code_type::op_callv, "callv "},
|
||||
{op_code_type::op_callvi, "callvi"},
|
||||
{op_code_type::op_callh, "callh "},
|
||||
{op_code_type::op_callfv, "callfv"},
|
||||
{op_code_type::op_callfh, "callfh"},
|
||||
{op_code_type::op_callb, "callb "},
|
||||
{op_code_type::op_slcbeg, "slcbeg"},
|
||||
{op_code_type::op_slcend, "slcend"},
|
||||
{op_code_type::op_slc, "slice "},
|
||||
{op_code_type::op_slc2, "slice2"},
|
||||
{op_code_type::op_mcallg, "mcallg"},
|
||||
{op_code_type::op_mcalll, "mcalll"},
|
||||
{op_code_type::op_mupval, "mupval"},
|
||||
{op_code_type::op_mcallv, "mcallv"},
|
||||
{op_code_type::op_mcallh, "mcallh"},
|
||||
{op_code_type::op_ret, "ret "}
|
||||
static std::unordered_map<opcode_type, std::string> operand_name_table = {
|
||||
{opcode_type::op_exit, "exit "},
|
||||
{opcode_type::op_repl, "repl "},
|
||||
{opcode_type::op_intl, "intl "},
|
||||
{opcode_type::op_loadg, "loadg "},
|
||||
{opcode_type::op_loadl, "loadl "},
|
||||
{opcode_type::op_loadu, "loadu "},
|
||||
{opcode_type::op_dup, "dup "},
|
||||
{opcode_type::op_pnum, "pnum "},
|
||||
{opcode_type::op_pnil, "pnil "},
|
||||
{opcode_type::op_pstr, "pstr "},
|
||||
{opcode_type::op_newv, "newv "},
|
||||
{opcode_type::op_newh, "newh "},
|
||||
{opcode_type::op_newf, "newf "},
|
||||
{opcode_type::op_happ, "happ "},
|
||||
{opcode_type::op_para, "para "},
|
||||
{opcode_type::op_deft, "def "},
|
||||
{opcode_type::op_dyn, "dyn "},
|
||||
{opcode_type::op_lnot, "lnot "},
|
||||
{opcode_type::op_usub, "usub "},
|
||||
{opcode_type::op_bnot, "bitnot"},
|
||||
{opcode_type::op_btor, "bitor "},
|
||||
{opcode_type::op_btxor, "bitxor"},
|
||||
{opcode_type::op_btand, "bitand"},
|
||||
{opcode_type::op_add, "add "},
|
||||
{opcode_type::op_sub, "sub "},
|
||||
{opcode_type::op_mul, "mult "},
|
||||
{opcode_type::op_div, "div "},
|
||||
{opcode_type::op_lnk, "lnk "},
|
||||
{opcode_type::op_addc, "addc "},
|
||||
{opcode_type::op_subc, "subc "},
|
||||
{opcode_type::op_mulc, "multc "},
|
||||
{opcode_type::op_divc, "divc "},
|
||||
{opcode_type::op_lnkc, "lnkc "},
|
||||
{opcode_type::op_addeq, "addeq "},
|
||||
{opcode_type::op_subeq, "subeq "},
|
||||
{opcode_type::op_muleq, "muleq "},
|
||||
{opcode_type::op_diveq, "diveq "},
|
||||
{opcode_type::op_lnkeq, "lnkeq "},
|
||||
{opcode_type::op_btandeq, "bandeq"},
|
||||
{opcode_type::op_btoreq, "boreq "},
|
||||
{opcode_type::op_btxoreq, "bxoreq"},
|
||||
{opcode_type::op_addeqc, "addeqc"},
|
||||
{opcode_type::op_subeqc, "subeqc"},
|
||||
{opcode_type::op_muleqc, "muleqc"},
|
||||
{opcode_type::op_diveqc, "diveqc"},
|
||||
{opcode_type::op_lnkeqc, "lnkeqc"},
|
||||
{opcode_type::op_addecp, "addecp"},
|
||||
{opcode_type::op_subecp, "subecp"},
|
||||
{opcode_type::op_mulecp, "mulecp"},
|
||||
{opcode_type::op_divecp, "divecp"},
|
||||
{opcode_type::op_lnkecp, "lnkecp"},
|
||||
{opcode_type::op_meq, "meq "},
|
||||
{opcode_type::op_eq, "eq "},
|
||||
{opcode_type::op_neq, "neq "},
|
||||
{opcode_type::op_less, "less "},
|
||||
{opcode_type::op_leq, "leq "},
|
||||
{opcode_type::op_grt, "grt "},
|
||||
{opcode_type::op_geq, "geq "},
|
||||
{opcode_type::op_lessc, "lessc "},
|
||||
{opcode_type::op_leqc, "leqc "},
|
||||
{opcode_type::op_grtc, "grtc "},
|
||||
{opcode_type::op_geqc, "geqc "},
|
||||
{opcode_type::op_pop, "pop "},
|
||||
{opcode_type::op_jmp, "jmp "},
|
||||
{opcode_type::op_jt, "jt "},
|
||||
{opcode_type::op_jf, "jf "},
|
||||
{opcode_type::op_cnt, "cnt "},
|
||||
{opcode_type::op_findex, "findx "},
|
||||
{opcode_type::op_feach, "feach "},
|
||||
{opcode_type::op_callg, "callg "},
|
||||
{opcode_type::op_calll, "calll "},
|
||||
{opcode_type::op_upval, "upval "},
|
||||
{opcode_type::op_callv, "callv "},
|
||||
{opcode_type::op_callvi, "callvi"},
|
||||
{opcode_type::op_callh, "callh "},
|
||||
{opcode_type::op_callfv, "callfv"},
|
||||
{opcode_type::op_callfh, "callfh"},
|
||||
{opcode_type::op_callb, "callb "},
|
||||
{opcode_type::op_slcbeg, "slcbeg"},
|
||||
{opcode_type::op_slcend, "slcend"},
|
||||
{opcode_type::op_slc, "slice "},
|
||||
{opcode_type::op_slc2, "slice2"},
|
||||
{opcode_type::op_mcallg, "mcallg"},
|
||||
{opcode_type::op_mcalll, "mcalll"},
|
||||
{opcode_type::op_mupval, "mupval"},
|
||||
{opcode_type::op_mcallv, "mcallv"},
|
||||
{opcode_type::op_mcallh, "mcallh"},
|
||||
{opcode_type::op_ret, "ret "}
|
||||
};
|
||||
|
||||
struct opcode {
|
||||
@@ -207,16 +211,22 @@ private:
|
||||
inline static const std::string* const_string = nullptr;
|
||||
inline static const nasal_builtin_table* natives = nullptr;
|
||||
inline static const std::string* files = nullptr;
|
||||
inline static std::vector<std::string> global_variable;
|
||||
|
||||
public:
|
||||
codestream(const opcode& c, const u64 i): code(c), index(i) {}
|
||||
static void set(const f64*,
|
||||
const std::string*,
|
||||
const std::unordered_map<std::string, u64>&,
|
||||
const nasal_builtin_table*,
|
||||
const std::string* file_list = nullptr);
|
||||
static void set(const f64*,
|
||||
const std::string*,
|
||||
const std::vector<std::string>&,
|
||||
const nasal_builtin_table*,
|
||||
const std::string* file_list = nullptr);
|
||||
void dump(std::ostream&) const;
|
||||
friend std::ostream& operator<<(std::ostream&, const codestream&);
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const codestream&);
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ const error& parse::compile(const lexer& lexer) {
|
||||
|
||||
root = new code_block(toks[0].loc);
|
||||
|
||||
while(!lookahead(tok::tk_eof)) {
|
||||
while (!lookahead(tok::tk_eof)) {
|
||||
root->add_expression(expression());
|
||||
if (lookahead(tok::tk_semi)) {
|
||||
match(tok::tk_semi);
|
||||
@@ -94,7 +94,36 @@ void parse::match(tok type, const char* info) {
|
||||
}
|
||||
|
||||
bool parse::lookahead(tok type) {
|
||||
return toks[ptr].type==type;
|
||||
return toks[ptr].type == type;
|
||||
}
|
||||
|
||||
bool parse::lookahead_expression() {
|
||||
switch (toks[ptr].type) {
|
||||
case tok::tk_nil:
|
||||
case tok::tk_num:
|
||||
case tok::tk_str:
|
||||
case tok::tk_id:
|
||||
case tok::tk_true:
|
||||
case tok::tk_false:
|
||||
case tok::tk_func:
|
||||
case tok::tk_lbracket:
|
||||
case tok::tk_lbrace:
|
||||
case tok::tk_sub:
|
||||
case tok::tk_floater:
|
||||
case tok::tk_not:
|
||||
case tok::tk_var:
|
||||
case tok::tk_lcurve:
|
||||
case tok::tk_for:
|
||||
case tok::tk_forindex:
|
||||
case tok::tk_foreach:
|
||||
case tok::tk_while:
|
||||
case tok::tk_if:
|
||||
case tok::tk_cont:
|
||||
case tok::tk_brk:
|
||||
case tok::tk_ret: return true;
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse::is_call(tok type) {
|
||||
@@ -103,7 +132,7 @@ bool parse::is_call(tok type) {
|
||||
}
|
||||
|
||||
bool parse::check_comma(const tok* panic_set) {
|
||||
for(u32 i = 0; panic_set[i]!=tok::tk_null; ++i) {
|
||||
for (u32 i = 0; panic_set[i]!=tok::tk_null; ++i) {
|
||||
if (lookahead(panic_set[i])) {
|
||||
die(prevspan, "expected \",\" between scalars");
|
||||
return true;
|
||||
@@ -114,7 +143,7 @@ bool parse::check_comma(const tok* panic_set) {
|
||||
|
||||
bool parse::check_tuple() {
|
||||
u64 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
|
||||
while(toks[++check_ptr].type!=tok::tk_eof && curve) {
|
||||
while (toks[++check_ptr].type!=tok::tk_eof && curve) {
|
||||
switch(toks[check_ptr].type) {
|
||||
case tok::tk_lcurve: ++curve; break;
|
||||
case tok::tk_lbracket: ++bracket; break;
|
||||
@@ -167,7 +196,7 @@ bool parse::check_in_curve_multi_definition() {
|
||||
bool parse::check_special_call() {
|
||||
// special call means like this: function_name(a:1, b:2, c:3);
|
||||
u64 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
|
||||
while(toks[++check_ptr].type!=tok::tk_eof && curve) {
|
||||
while (toks[++check_ptr].type!=tok::tk_eof && curve) {
|
||||
switch(toks[check_ptr].type) {
|
||||
case tok::tk_lcurve: ++curve; break;
|
||||
case tok::tk_lbracket: ++bracket;break;
|
||||
@@ -216,7 +245,7 @@ use_stmt* parse::use_stmt_gen() {
|
||||
auto node = new use_stmt(toks[ptr].loc);
|
||||
match(tok::tk_use);
|
||||
node->add_path(id());
|
||||
while(lookahead(tok::tk_dot)) {
|
||||
while (lookahead(tok::tk_dot)) {
|
||||
match(tok::tk_dot);
|
||||
node->add_path(id());
|
||||
}
|
||||
@@ -235,7 +264,8 @@ nil_expr* parse::nil() {
|
||||
number_literal* parse::num() {
|
||||
auto node = new number_literal(
|
||||
toks[ptr].loc,
|
||||
util::str_to_num(toks[ptr].str.c_str())
|
||||
util::str_to_num(toks[ptr].str.c_str()),
|
||||
toks[ptr].str
|
||||
);
|
||||
match(tok::tk_num);
|
||||
return node;
|
||||
@@ -275,7 +305,7 @@ vector_expr* parse::vec() {
|
||||
};
|
||||
auto node = new vector_expr(toks[ptr].loc);
|
||||
match(tok::tk_lbracket);
|
||||
while(!lookahead(tok::tk_rbracket)) {
|
||||
while (!lookahead(tok::tk_rbracket)) {
|
||||
node->add_element(calc());
|
||||
if (lookahead(tok::tk_comma)) {
|
||||
match(tok::tk_comma);
|
||||
@@ -293,7 +323,7 @@ vector_expr* parse::vec() {
|
||||
hash_expr* parse::hash() {
|
||||
auto node = new hash_expr(toks[ptr].loc);
|
||||
match(tok::tk_lbrace);
|
||||
while(!lookahead(tok::tk_rbrace)) {
|
||||
while (!lookahead(tok::tk_rbrace)) {
|
||||
node->add_member(pair());
|
||||
if (lookahead(tok::tk_comma)) {
|
||||
match(tok::tk_comma);
|
||||
@@ -341,7 +371,7 @@ function* parse::func() {
|
||||
|
||||
void parse::params(function* func_node) {
|
||||
match(tok::tk_lcurve);
|
||||
while(!lookahead(tok::tk_rcurve)) {
|
||||
while (!lookahead(tok::tk_rcurve)) {
|
||||
auto param = new parameter(toks[ptr].loc);
|
||||
param->set_parameter_name(toks[ptr].str);
|
||||
match(tok::tk_id);
|
||||
@@ -377,14 +407,11 @@ expr* parse::lcurve_expr() {
|
||||
}
|
||||
|
||||
expr* parse::expression() {
|
||||
tok type=toks[ptr].type;
|
||||
if ((type==tok::tk_brk || type==tok::tk_cont) && !in_loop_depth) {
|
||||
tok type = toks[ptr].type;
|
||||
if ((type == tok::tk_brk || type == tok::tk_cont) && !in_loop_depth) {
|
||||
die(thisspan, "must use break/continue in loops");
|
||||
}
|
||||
if (type==tok::tk_ret && !in_func_depth) {
|
||||
die(thisspan, "must use return in functions");
|
||||
}
|
||||
switch(type) {
|
||||
switch (type) {
|
||||
case tok::tk_use: return use_stmt_gen();
|
||||
case tok::tk_nil:
|
||||
case tok::tk_num:
|
||||
@@ -427,7 +454,7 @@ code_block* parse::expression_block() {
|
||||
auto node = new code_block(toks[ptr].loc);
|
||||
if (lookahead(tok::tk_lbrace)) {
|
||||
match(tok::tk_lbrace);
|
||||
while(!lookahead(tok::tk_rbrace) && !lookahead(tok::tk_eof)) {
|
||||
while (!lookahead(tok::tk_rbrace) && !lookahead(tok::tk_eof)) {
|
||||
node->add_expression(expression());
|
||||
if (lookahead(tok::tk_semi)) {
|
||||
match(tok::tk_semi);
|
||||
@@ -438,7 +465,7 @@ code_block* parse::expression_block() {
|
||||
}
|
||||
}
|
||||
match(tok::tk_rbrace, "expected \"}\" when generating expressions");
|
||||
} else {
|
||||
} else if (lookahead_expression()) {
|
||||
node->add_expression(expression());
|
||||
if (lookahead(tok::tk_semi)) {
|
||||
match(tok::tk_semi);
|
||||
@@ -495,7 +522,7 @@ expr* parse::calc() {
|
||||
|
||||
expr* parse::bitwise_or() {
|
||||
auto node = bitwise_xor();
|
||||
while(lookahead(tok::tk_btor)) {
|
||||
while (lookahead(tok::tk_btor)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::bitwise_or);
|
||||
tmp->set_left(node);
|
||||
@@ -510,7 +537,7 @@ expr* parse::bitwise_or() {
|
||||
|
||||
expr* parse::bitwise_xor() {
|
||||
auto node = bitwise_and();
|
||||
while(lookahead(tok::tk_btxor)) {
|
||||
while (lookahead(tok::tk_btxor)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::bitwise_xor);
|
||||
tmp->set_left(node);
|
||||
@@ -525,7 +552,7 @@ expr* parse::bitwise_xor() {
|
||||
|
||||
expr* parse::bitwise_and() {
|
||||
auto node = or_expr();
|
||||
while(lookahead(tok::tk_btand)) {
|
||||
while (lookahead(tok::tk_btand)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::bitwise_and);
|
||||
tmp->set_left(node);
|
||||
@@ -540,7 +567,7 @@ expr* parse::bitwise_and() {
|
||||
|
||||
expr* parse::or_expr() {
|
||||
auto node = and_expr();
|
||||
while(lookahead(tok::tk_or)) {
|
||||
while (lookahead(tok::tk_or)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::condition_or);
|
||||
tmp->set_left(node);
|
||||
@@ -555,7 +582,7 @@ expr* parse::or_expr() {
|
||||
|
||||
expr* parse::and_expr() {
|
||||
auto node = cmp_expr();
|
||||
while(lookahead(tok::tk_and)) {
|
||||
while (lookahead(tok::tk_and)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::condition_and);
|
||||
tmp->set_left(node);
|
||||
@@ -570,7 +597,7 @@ expr* parse::and_expr() {
|
||||
|
||||
expr* parse::cmp_expr() {
|
||||
auto node = null_chain_expr();
|
||||
while(tok::tk_cmpeq<=toks[ptr].type && toks[ptr].type<=tok::tk_geq) {
|
||||
while (tok::tk_cmpeq<=toks[ptr].type && toks[ptr].type<=tok::tk_geq) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
switch(toks[ptr].type) {
|
||||
case tok::tk_cmpeq: tmp->set_operator_type(binary_operator::kind::cmpeq); break;
|
||||
@@ -593,7 +620,7 @@ expr* parse::cmp_expr() {
|
||||
|
||||
expr* parse::null_chain_expr() {
|
||||
auto node = additive_expr();
|
||||
while(lookahead(tok::tk_quesques)) {
|
||||
while (lookahead(tok::tk_quesques)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::kind::null_chain);
|
||||
tmp->set_left(node);
|
||||
@@ -608,7 +635,7 @@ expr* parse::null_chain_expr() {
|
||||
|
||||
expr* parse::additive_expr() {
|
||||
auto node = multive_expr();
|
||||
while(lookahead(tok::tk_add) ||
|
||||
while (lookahead(tok::tk_add) ||
|
||||
lookahead(tok::tk_sub) ||
|
||||
lookahead(tok::tk_floater)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
@@ -632,7 +659,7 @@ expr* parse::multive_expr() {
|
||||
expr* node=(lookahead(tok::tk_sub) ||
|
||||
lookahead(tok::tk_not) ||
|
||||
lookahead(tok::tk_floater))? unary():scalar();
|
||||
while(lookahead(tok::tk_mult) || lookahead(tok::tk_div)) {
|
||||
while (lookahead(tok::tk_mult) || lookahead(tok::tk_div)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
if (lookahead(tok::tk_mult)) {
|
||||
tmp->set_operator_type(binary_operator::kind::mult);
|
||||
@@ -717,14 +744,14 @@ expr* parse::scalar() {
|
||||
return null();
|
||||
}
|
||||
// check call and avoid ambiguous syntax:
|
||||
// var f = func(){}
|
||||
// var f = func() {}
|
||||
// (var a, b, c) = (1, 2, 3);
|
||||
// will be incorrectly recognized like:
|
||||
// var f = func(){}(var a, b, c)
|
||||
// var f = func() {}(var a, b, c)
|
||||
if (is_call(toks[ptr].type) && !check_in_curve_multi_definition()) {
|
||||
auto call_node = new call_expr(toks[ptr].loc);
|
||||
call_node->set_first(node);
|
||||
while(is_call(toks[ptr].type)) {
|
||||
while (is_call(toks[ptr].type)) {
|
||||
call_node->add_call(call_scalar());
|
||||
}
|
||||
node = call_node;
|
||||
@@ -775,7 +802,7 @@ call_vector* parse::callv() {
|
||||
};
|
||||
auto node = new call_vector(toks[ptr].loc);
|
||||
match(tok::tk_lbracket);
|
||||
while(!lookahead(tok::tk_rbracket)) {
|
||||
while (!lookahead(tok::tk_rbracket)) {
|
||||
node->add_slice(subvec());
|
||||
if (lookahead(tok::tk_comma)) {
|
||||
match(tok::tk_comma);
|
||||
@@ -806,7 +833,7 @@ call_function* parse::callf() {
|
||||
auto node = new call_function(toks[ptr].loc);
|
||||
bool special_call=check_special_call();
|
||||
match(tok::tk_lcurve);
|
||||
while(!lookahead(tok::tk_rcurve)) {
|
||||
while (!lookahead(tok::tk_rcurve)) {
|
||||
node->add_argument(special_call?pair():calc());
|
||||
if (lookahead(tok::tk_comma))
|
||||
match(tok::tk_comma);
|
||||
@@ -882,7 +909,7 @@ multi_identifier* parse::outcurve_def() {
|
||||
|
||||
multi_identifier* parse::multi_id() {
|
||||
auto node = new multi_identifier(toks[ptr].loc);
|
||||
while(!lookahead(tok::tk_eof)) {
|
||||
while (!lookahead(tok::tk_eof)) {
|
||||
// only identifier is allowed here
|
||||
node->add_var(id());
|
||||
if (lookahead(tok::tk_comma)) {
|
||||
@@ -908,7 +935,7 @@ tuple_expr* parse::multi_scalar() {
|
||||
};
|
||||
auto node = new tuple_expr(toks[ptr].loc);
|
||||
match(tok::tk_lcurve);
|
||||
while(!lookahead(tok::tk_rcurve)) {
|
||||
while (!lookahead(tok::tk_rcurve)) {
|
||||
node->add_element(calc());
|
||||
if (lookahead(tok::tk_comma)) {
|
||||
match(tok::tk_comma);
|
||||
@@ -986,7 +1013,7 @@ for_expr* parse::for_loop() {
|
||||
} else {
|
||||
node->set_initial(calc());
|
||||
}
|
||||
match(tok::tk_semi, "expected \";\" in for(;;)");
|
||||
match(tok::tk_semi, "expected \";\" in for (;;)");
|
||||
|
||||
// conditional expression
|
||||
if (lookahead(tok::tk_eof)) {
|
||||
@@ -997,7 +1024,7 @@ for_expr* parse::for_loop() {
|
||||
} else {
|
||||
node->set_condition(calc());
|
||||
}
|
||||
match(tok::tk_semi, "expected \";\" in for(;;)");
|
||||
match(tok::tk_semi, "expected \";\" in for (;;)");
|
||||
|
||||
//after loop expression
|
||||
if (lookahead(tok::tk_eof)) {
|
||||
@@ -1067,7 +1094,7 @@ iter_expr* parse::iter_gen() {
|
||||
// call expression
|
||||
auto tmp = new call_expr(id_node->get_location());
|
||||
tmp->set_first(id_node);
|
||||
while(is_call(toks[ptr].type)) {
|
||||
while (is_call(toks[ptr].type)) {
|
||||
tmp->add_call(call_scalar());
|
||||
}
|
||||
node->set_call(tmp);
|
||||
@@ -1089,7 +1116,7 @@ condition_expr* parse::cond() {
|
||||
node->set_if_statement(ifnode);
|
||||
|
||||
// generate elsif
|
||||
while(lookahead(tok::tk_elsif)) {
|
||||
while (lookahead(tok::tk_elsif)) {
|
||||
auto elsifnode = new if_expr(toks[ptr].loc);
|
||||
match(tok::tk_elsif);
|
||||
match(tok::tk_lcurve);
|
||||
|
||||
@@ -87,6 +87,7 @@ private:
|
||||
void next();
|
||||
void match(tok, const char* info = nullptr);
|
||||
bool lookahead(tok);
|
||||
bool lookahead_expression();
|
||||
bool is_call(tok);
|
||||
bool check_comma(const tok*);
|
||||
bool check_tuple();
|
||||
|
||||
@@ -6,22 +6,6 @@
|
||||
|
||||
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()? "[..]":"[]");
|
||||
@@ -30,7 +14,7 @@ std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||
vec.printed = true;
|
||||
usize iter = 0, size = vec.elems.size();
|
||||
out << "[";
|
||||
for(auto& i:vec.elems) {
|
||||
for (auto& i:vec.elems) {
|
||||
out << i << ",]"[(++iter)==size];
|
||||
}
|
||||
vec.printed = false;
|
||||
@@ -40,7 +24,8 @@ std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
|
||||
var nas_hash::get_value(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
}
|
||||
if (!elems.count("parents")) {
|
||||
return var::none();
|
||||
}
|
||||
|
||||
@@ -49,7 +34,7 @@ var nas_hash::get_value(const std::string& key) {
|
||||
if (!val.is_vec()) {
|
||||
return ret;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
for (auto& i : val.vec().elems) {
|
||||
if (i.is_hash()) {
|
||||
ret = i.hash().get_value(key);
|
||||
}
|
||||
@@ -63,7 +48,8 @@ var nas_hash::get_value(const std::string& key) {
|
||||
var* nas_hash::get_memory(const std::string& key) {
|
||||
if (elems.count(key)) {
|
||||
return &elems.at(key);
|
||||
} else if (!elems.count("parents")) {
|
||||
}
|
||||
if (!elems.count("parents")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -72,7 +58,7 @@ var* nas_hash::get_memory(const std::string& key) {
|
||||
if (!val.is_vec()) {
|
||||
return addr;
|
||||
}
|
||||
for(auto& i : val.vec().elems) {
|
||||
for (auto& i : val.vec().elems) {
|
||||
// recursively search key in `parents`
|
||||
if (i.is_hash()) {
|
||||
addr = i.hash().get_memory(key);
|
||||
@@ -89,12 +75,18 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
||||
out << (hash.elems.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
|
||||
// mark print, to avoid infinite recursion
|
||||
hash.printed = true;
|
||||
|
||||
static const char* sep[] = {", ", "}"};
|
||||
usize iter = 0, size = hash.elems.size();
|
||||
out << "{";
|
||||
for(auto& i : hash.elems) {
|
||||
out << i.first << ":" << i.second << ",}"[(++iter)==size];
|
||||
for (auto& i : hash.elems) {
|
||||
out << i.first << ": " << i.second << sep[(++iter)==size];
|
||||
}
|
||||
|
||||
// restore flag
|
||||
hash.printed = false;
|
||||
return out;
|
||||
}
|
||||
@@ -104,11 +96,11 @@ std::ostream& operator<<(std::ostream& out, nas_func& func) {
|
||||
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& key : func.keys) {
|
||||
for (const auto& key : func.keys) {
|
||||
argument_list[key.second-1] = key.first;
|
||||
}
|
||||
|
||||
for(const auto& key : argument_list) {
|
||||
for (const auto& key : argument_list) {
|
||||
out << key;
|
||||
if (key != argument_list.back()) {
|
||||
out << ", ";
|
||||
@@ -162,9 +154,8 @@ void nas_ghost::clear() {
|
||||
}
|
||||
|
||||
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 << ">";
|
||||
out << "<" << ghost.get_ghost_name();
|
||||
out << "@0x" << std::hex << ghost.convert<u64>() << std::dec << ">";
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -172,7 +163,7 @@ void nas_co::clear() {
|
||||
if (!ctx.stack) {
|
||||
return;
|
||||
}
|
||||
for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
|
||||
for (u32 i = 0; i<VM_STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = var::nil();
|
||||
}
|
||||
|
||||
@@ -212,12 +203,18 @@ std::ostream& operator<<(std::ostream& out, nas_map& mp) {
|
||||
out << (mp.mapper.size()? "{..}":"{}");
|
||||
return out;
|
||||
}
|
||||
|
||||
// mark print, to avoid infinite recursion
|
||||
mp.printed = true;
|
||||
|
||||
static const char* sep[] = {", ", "}"};
|
||||
usize iter = 0, size = mp.mapper.size();
|
||||
out << "{";
|
||||
for(auto& i : mp.mapper) {
|
||||
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
|
||||
for (auto& i : mp.mapper) {
|
||||
out << i.first << ": " << *i.second << sep[(++iter)==size];
|
||||
}
|
||||
|
||||
// restore flag
|
||||
mp.printed = false;
|
||||
return out;
|
||||
}
|
||||
@@ -268,17 +265,13 @@ void nas_val::clear() {
|
||||
}
|
||||
}
|
||||
|
||||
f64 var::to_num() {
|
||||
return type!=vm_type::vm_str? val.num:util::str_to_num(str().c_str());
|
||||
}
|
||||
|
||||
std::string var::to_str() {
|
||||
if (type==vm_type::vm_str) {
|
||||
return str();
|
||||
} else if (type==vm_type::vm_num) {
|
||||
auto tmp = std::to_string(num());
|
||||
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
|
||||
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -304,89 +297,4 @@ std::ostream& operator<<(std::ostream& out, var& ref) {
|
||||
return out;
|
||||
}
|
||||
|
||||
bool var::object_check(const std::string& name) {
|
||||
return is_ghost() && ghost().type_name==name && ghost().pointer;
|
||||
}
|
||||
|
||||
var var::none() {
|
||||
return {vm_type::vm_none, static_cast<u64>(0)};
|
||||
}
|
||||
|
||||
var var::nil() {
|
||||
return {vm_type::vm_nil, static_cast<u64>(0)};
|
||||
}
|
||||
|
||||
var var::ret(u64 pc) {
|
||||
return {vm_type::vm_ret, pc};
|
||||
}
|
||||
|
||||
var var::cnt(i64 n) {
|
||||
return {vm_type::vm_cnt, n};
|
||||
}
|
||||
|
||||
var var::num(f64 n) {
|
||||
return {vm_type::vm_num, n};
|
||||
}
|
||||
|
||||
var var::gcobj(nas_val* p) {
|
||||
return {p->type, p};
|
||||
}
|
||||
|
||||
var var::addr(var* p) {
|
||||
return {vm_type::vm_addr, p};
|
||||
}
|
||||
|
||||
var* var::addr() {
|
||||
return val.addr;
|
||||
}
|
||||
|
||||
u64 var::ret() const {
|
||||
return val.ret;
|
||||
}
|
||||
|
||||
i64& var::cnt() {
|
||||
return val.cnt;
|
||||
}
|
||||
|
||||
f64 var::num() const {
|
||||
return val.num;
|
||||
}
|
||||
|
||||
std::string& var::str() {
|
||||
return *val.gcobj->ptr.str;
|
||||
}
|
||||
|
||||
nas_vec& var::vec() {
|
||||
return *val.gcobj->ptr.vec;
|
||||
}
|
||||
|
||||
nas_hash& var::hash() {
|
||||
return *val.gcobj->ptr.hash;
|
||||
}
|
||||
|
||||
nas_func& var::func() {
|
||||
return *val.gcobj->ptr.func;
|
||||
}
|
||||
|
||||
nas_upval& var::upval() {
|
||||
return *val.gcobj->ptr.upval;
|
||||
}
|
||||
|
||||
nas_ghost& var::ghost() {
|
||||
return *val.gcobj->ptr.obj;
|
||||
}
|
||||
|
||||
nas_co& var::co() {
|
||||
return *val.gcobj->ptr.co;
|
||||
}
|
||||
|
||||
nas_map& var::map() {
|
||||
return *val.gcobj->ptr.map;
|
||||
}
|
||||
|
||||
var nas_err(const std::string& error_function_name, const std::string& info) {
|
||||
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
|
||||
return var::none();
|
||||
}
|
||||
|
||||
}
|
||||
236
src/nasal_type.h
236
src/nasal_type.h
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
@@ -34,7 +35,7 @@ enum class vm_type: u8 {
|
||||
};
|
||||
|
||||
// size of gc object type
|
||||
const u32 gc_type_size =
|
||||
const u32 GC_TYPE_SIZE =
|
||||
static_cast<u32>(vm_type::vm_type_size_max) -
|
||||
static_cast<u32>(vm_type::vm_str);
|
||||
|
||||
@@ -47,8 +48,32 @@ struct nas_ghost; // objects
|
||||
struct nas_co; // coroutine
|
||||
struct nas_map; // mapper
|
||||
|
||||
// union type
|
||||
struct nas_val; // nas_val includes gc-managed types
|
||||
// nas_val includes gc-managed types
|
||||
struct nas_val {
|
||||
enum class gc_status: u8 {
|
||||
uncollected = 0,
|
||||
collected,
|
||||
found
|
||||
};
|
||||
|
||||
gc_status mark;
|
||||
vm_type type; // value type
|
||||
u8 immutable; // used to mark if a string is immutable
|
||||
union elem {
|
||||
std::string* str;
|
||||
nas_vec* vec;
|
||||
nas_hash* hash;
|
||||
nas_func* func;
|
||||
nas_upval* upval;
|
||||
nas_ghost* obj;
|
||||
nas_co* co;
|
||||
nas_map* map;
|
||||
} ptr;
|
||||
|
||||
nas_val(vm_type);
|
||||
~nas_val();
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct var {
|
||||
public:
|
||||
@@ -62,67 +87,103 @@ public:
|
||||
} val;
|
||||
|
||||
private:
|
||||
var(vm_type t, u64 pc) {type = t; val.ret = pc;}
|
||||
var(vm_type t, i64 ct) {type = t; val.cnt = ct;}
|
||||
var(vm_type t, f64 n) {type = t; val.num = n;}
|
||||
var(vm_type t, var* p) {type = t; val.addr = p;}
|
||||
var(vm_type t, nas_val* p) {type = t; val.gcobj = p;}
|
||||
var(vm_type t, u64 pc) { type = t; val.ret = pc; }
|
||||
var(vm_type t, i64 ct) { type = t; val.cnt = ct; }
|
||||
var(vm_type t, f64 n) { type = t; val.num = n; }
|
||||
var(vm_type t, var* p) { type = t; val.addr = p; }
|
||||
var(vm_type t, nas_val* p) { type = t; val.gcobj = p; }
|
||||
|
||||
public:
|
||||
var() = default;
|
||||
var(const var&) = default;
|
||||
bool operator==(const var& nr) const {
|
||||
return type==nr.type && val.gcobj==nr.val.gcobj;
|
||||
return type == nr.type && val.gcobj == nr.val.gcobj;
|
||||
}
|
||||
bool operator!=(const var& nr) const {
|
||||
return type!=nr.type || val.gcobj!=nr.val.gcobj;
|
||||
return type != nr.type || val.gcobj != nr.val.gcobj;
|
||||
}
|
||||
|
||||
// number and string can be translated to each other
|
||||
f64 to_num();
|
||||
std::string to_str();
|
||||
bool object_check(const std::string&);
|
||||
|
||||
public:
|
||||
// create new var object
|
||||
static var none();
|
||||
static var nil();
|
||||
static var ret(u64);
|
||||
static var cnt(i64);
|
||||
static var num(f64);
|
||||
static var gcobj(nas_val*);
|
||||
static var addr(var*);
|
||||
static var none() {
|
||||
return var(vm_type::vm_none, static_cast<u64>(0));
|
||||
}
|
||||
static var nil() {
|
||||
return var(vm_type::vm_nil, static_cast<u64>(0));
|
||||
}
|
||||
static var ret(u64 pc) {
|
||||
return var(vm_type::vm_ret, pc);
|
||||
}
|
||||
static var cnt(i64 n) {
|
||||
return var(vm_type::vm_cnt, n);
|
||||
}
|
||||
static var num(f64 n) {
|
||||
return var(vm_type::vm_num, n);
|
||||
}
|
||||
static var gcobj(nas_val* p) {
|
||||
return var(p->type, p);
|
||||
}
|
||||
static var addr(var* p) {
|
||||
return var(vm_type::vm_addr, p);
|
||||
}
|
||||
|
||||
public:
|
||||
// get value
|
||||
var* addr();
|
||||
u64 ret() const;
|
||||
i64& cnt();
|
||||
f64 num() const;
|
||||
std::string& str();
|
||||
nas_vec& vec();
|
||||
nas_hash& hash();
|
||||
nas_func& func();
|
||||
nas_upval& upval();
|
||||
nas_ghost& ghost();
|
||||
nas_co& co();
|
||||
nas_map& map();
|
||||
var* addr() const { return val.addr; }
|
||||
u64 ret() const { return val.ret; }
|
||||
i64& cnt() { return val.cnt; }
|
||||
f64 num() const { return val.num; }
|
||||
|
||||
public:
|
||||
bool is_none() const { return type==vm_type::vm_none; }
|
||||
bool is_cnt() const { return type==vm_type::vm_cnt; }
|
||||
bool is_addr() const { return type==vm_type::vm_addr; }
|
||||
bool is_ret() const { return type==vm_type::vm_ret; }
|
||||
bool is_nil() const { return type==vm_type::vm_nil; }
|
||||
bool is_num() const { return type==vm_type::vm_num; }
|
||||
bool is_str() const { return type==vm_type::vm_str; }
|
||||
bool is_vec() const { return type==vm_type::vm_vec; }
|
||||
bool is_hash() const { return type==vm_type::vm_hash; }
|
||||
bool is_func() const { return type==vm_type::vm_func; }
|
||||
bool is_upval() const { return type==vm_type::vm_upval; }
|
||||
bool is_ghost() const { return type==vm_type::vm_ghost; }
|
||||
bool is_coroutine() const { return type==vm_type::vm_co; }
|
||||
bool is_map() const { return type==vm_type::vm_map; }
|
||||
// get gc object
|
||||
std::string& str() { return *val.gcobj->ptr.str; }
|
||||
nas_vec& vec() { return *val.gcobj->ptr.vec; }
|
||||
nas_hash& hash() { return *val.gcobj->ptr.hash; }
|
||||
nas_func& func() { return *val.gcobj->ptr.func; }
|
||||
nas_upval& upval() { return *val.gcobj->ptr.upval; }
|
||||
nas_ghost& ghost() { return *val.gcobj->ptr.obj; }
|
||||
nas_co& co() { return *val.gcobj->ptr.co; }
|
||||
nas_map& map() { return *val.gcobj->ptr.map; }
|
||||
|
||||
|
||||
public:
|
||||
// get const gc object
|
||||
const std::string& str() const { return *val.gcobj->ptr.str; }
|
||||
const nas_vec& vec() const { return *val.gcobj->ptr.vec; }
|
||||
const nas_hash& hash() const { return *val.gcobj->ptr.hash; }
|
||||
const nas_func& func() const { return *val.gcobj->ptr.func; }
|
||||
const nas_upval& upval() const { return *val.gcobj->ptr.upval; }
|
||||
const nas_ghost& ghost() const { return *val.gcobj->ptr.obj; }
|
||||
const nas_co& co() const { return *val.gcobj->ptr.co; }
|
||||
const nas_map& map() const { return *val.gcobj->ptr.map; }
|
||||
|
||||
public:
|
||||
bool is_none() const { return type == vm_type::vm_none; }
|
||||
bool is_cnt() const { return type == vm_type::vm_cnt; }
|
||||
bool is_addr() const { return type == vm_type::vm_addr; }
|
||||
bool is_ret() const { return type == vm_type::vm_ret; }
|
||||
bool is_nil() const { return type == vm_type::vm_nil; }
|
||||
bool is_num() const { return type == vm_type::vm_num; }
|
||||
bool is_str() const { return type == vm_type::vm_str; }
|
||||
bool is_vec() const { return type == vm_type::vm_vec; }
|
||||
bool is_hash() const { return type == vm_type::vm_hash; }
|
||||
bool is_func() const { return type == vm_type::vm_func; }
|
||||
bool is_upval() const { return type == vm_type::vm_upval; }
|
||||
bool is_ghost() const { return type == vm_type::vm_ghost; }
|
||||
bool is_coroutine() const { return type == vm_type::vm_co; }
|
||||
bool is_map() const { return type == vm_type::vm_map; }
|
||||
|
||||
public:
|
||||
// convert to number
|
||||
f64 to_num() const {
|
||||
return type != vm_type::vm_str
|
||||
? val.num
|
||||
: util::str_to_num(str().c_str());
|
||||
}
|
||||
// convert to string
|
||||
std::string to_str();
|
||||
inline bool object_check(const std::string&) const;
|
||||
friend std::ostream& operator<<(std::ostream&, var&);
|
||||
};
|
||||
|
||||
struct nas_vec {
|
||||
@@ -131,9 +192,22 @@ struct nas_vec {
|
||||
// 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);
|
||||
auto size() const { return elems.size(); }
|
||||
var 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* get_memory(const i32 index) {
|
||||
i32 size = elems.size();
|
||||
if (index < -size || index >= size) {
|
||||
return nullptr;
|
||||
}
|
||||
return &elems[index >= 0 ? index : index + size];
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||
};
|
||||
|
||||
struct nas_hash {
|
||||
@@ -142,9 +216,10 @@ struct nas_hash {
|
||||
// mark if this is printed, avoid stack overflow
|
||||
bool printed = false;
|
||||
|
||||
usize size() const {return elems.size();}
|
||||
auto size() const { return elems.size(); }
|
||||
var get_value(const std::string&);
|
||||
var* get_memory(const std::string&);
|
||||
friend std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||
};
|
||||
|
||||
struct nas_func {
|
||||
@@ -166,6 +241,7 @@ struct nas_func {
|
||||
parameter_size(0), local_size(0),
|
||||
dynamic_parameter_name("") {}
|
||||
void clear();
|
||||
friend std::ostream& operator<<(std::ostream&, nas_func&);
|
||||
};
|
||||
|
||||
struct nas_upval {
|
||||
@@ -182,7 +258,7 @@ 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];
|
||||
return on_stack? stack_frame_offset[n] : elems[n];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@@ -210,9 +286,16 @@ public:
|
||||
~nas_ghost() { clear(); }
|
||||
void set(const std::string&, destructor, marker, void*);
|
||||
void clear();
|
||||
friend std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
||||
|
||||
public:
|
||||
const auto& get_ghost_name() const { return type_name; }
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
T* get() { return static_cast<T*>(pointer); }
|
||||
template<typename T>
|
||||
T convert() const { return reinterpret_cast<T>(pointer); }
|
||||
};
|
||||
|
||||
struct context {
|
||||
@@ -244,59 +327,36 @@ struct nas_co {
|
||||
delete[] ctx.stack;
|
||||
}
|
||||
void clear();
|
||||
friend std::ostream& operator<<(std::ostream&, const nas_co&);
|
||||
};
|
||||
|
||||
struct nas_map {
|
||||
bool printed = false;
|
||||
std::unordered_map<std::string, var*> mapper;
|
||||
|
||||
public:
|
||||
void clear() {
|
||||
mapper.clear();
|
||||
}
|
||||
auto size() const { return mapper.size(); }
|
||||
|
||||
var get_value(const std::string&);
|
||||
var* get_memory(const std::string&);
|
||||
friend std::ostream& operator<<(std::ostream&, nas_map&);
|
||||
};
|
||||
|
||||
struct nas_val {
|
||||
enum class gc_status: u8 {
|
||||
uncollected = 0,
|
||||
collected,
|
||||
found
|
||||
};
|
||||
|
||||
gc_status mark;
|
||||
vm_type type; // value type
|
||||
u8 immutable; // used to mark if a string is immutable
|
||||
union {
|
||||
std::string* str;
|
||||
nas_vec* vec;
|
||||
nas_hash* hash;
|
||||
nas_func* func;
|
||||
nas_upval* upval;
|
||||
nas_ghost* obj;
|
||||
nas_co* co;
|
||||
nas_map* map;
|
||||
} ptr;
|
||||
|
||||
nas_val(vm_type);
|
||||
~nas_val();
|
||||
void clear();
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream&, nas_vec&);
|
||||
std::ostream& operator<<(std::ostream&, nas_hash&);
|
||||
std::ostream& operator<<(std::ostream&, nas_func&);
|
||||
std::ostream& operator<<(std::ostream&, nas_map&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_ghost&);
|
||||
std::ostream& operator<<(std::ostream&, const nas_co&);
|
||||
std::ostream& operator<<(std::ostream&, var&);
|
||||
|
||||
const var zero = var::num(0);
|
||||
const var one = var::num(1);
|
||||
const var nil = var::nil();
|
||||
|
||||
inline bool var::object_check(const std::string& name) const {
|
||||
return is_ghost() && ghost().type_name == name && ghost().pointer;
|
||||
}
|
||||
|
||||
// use to print error log and return error value
|
||||
var nas_err(const std::string&, const std::string&);
|
||||
static var nas_err(const std::string& func, const std::string& info) {
|
||||
std::cerr << "[vm] " << func << ": " << info << "\n";
|
||||
return var::none();
|
||||
}
|
||||
|
||||
}
|
||||
405
src/nasal_vm.cpp
405
src/nasal_vm.cpp
@@ -31,9 +31,11 @@ void vm::vm_init_enrty(const std::vector<std::string>& strs,
|
||||
|
||||
/* init vm globals */
|
||||
auto map_instance = ngc.alloc(vm_type::vm_map);
|
||||
global_symbol_name.resize(global_symbol.size());
|
||||
global[global_symbol.at("globals")] = map_instance;
|
||||
for(const auto& i : global_symbol) {
|
||||
map_instance.map().mapper[i.first] = global+i.second;
|
||||
for (const auto& i : global_symbol) {
|
||||
map_instance.map().mapper[i.first] = global + i.second;
|
||||
global_symbol_name[i.second] = i.first;
|
||||
}
|
||||
|
||||
/* init vm arg */
|
||||
@@ -57,67 +59,146 @@ void vm::context_and_global_init() {
|
||||
ctx.top = ctx.stack - 1;
|
||||
|
||||
/* clear main stack and global */
|
||||
for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
|
||||
for (u32 i = 0; i<VM_STACK_DEPTH; ++i) {
|
||||
ctx.stack[i] = nil;
|
||||
global[i] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void vm::value_info(var& val) {
|
||||
const auto p = reinterpret_cast<u64>(val.val.gcobj);
|
||||
void vm::return_address_info(const var& val) {
|
||||
std::clog << "0x";
|
||||
std::clog << std::hex << val.ret() << std::dec;
|
||||
}
|
||||
|
||||
void vm::memory_address_info(const var& val) {
|
||||
std::clog << "0x";
|
||||
std::clog << std::hex << reinterpret_cast<u64>(val.addr()) << std::dec;
|
||||
}
|
||||
|
||||
void vm::raw_string_info(var& val) {
|
||||
std::clog << "\"" << util::rawstr(val.str(), 24) << "\"";
|
||||
}
|
||||
|
||||
void vm::upvalue_info(var& val) {
|
||||
std::clog << "[" << val.upval().size << " val] ";
|
||||
if (val.upval().on_stack) {
|
||||
std::clog << "offset:0x" << std::hex;
|
||||
std::clog << reinterpret_cast<u64>(val.upval().stack_frame_offset);
|
||||
std::clog << std::dec;
|
||||
}
|
||||
}
|
||||
|
||||
void vm::vector_value_info(var& val) {
|
||||
std::clog << "[" << val.vec().size() << " val]";
|
||||
}
|
||||
|
||||
void vm::hash_value_info(var& val, const usize max_show_elems) {
|
||||
std::clog << "{";
|
||||
usize count = 0;
|
||||
for (const auto& i : val.hash().elems) {
|
||||
++count;
|
||||
if (count>max_show_elems) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::clog << i.first;
|
||||
if (count!=val.hash().size()) {
|
||||
std::clog << ", ";
|
||||
}
|
||||
}
|
||||
if (val.hash().size()>max_show_elems) {
|
||||
std::clog << "...";
|
||||
}
|
||||
std::clog << "}";
|
||||
}
|
||||
|
||||
void vm::coroutine_value_info(var& val) {
|
||||
std::clog << "[ ";
|
||||
switch(val.co().status) {
|
||||
case nas_co::status::dead: std::clog << "dead"; break;
|
||||
case nas_co::status::running: std::clog << "running"; break;
|
||||
case nas_co::status::suspended: std::clog << "suspended"; break;
|
||||
}
|
||||
std::clog << " ] @0x";
|
||||
std::clog << std::hex << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
|
||||
}
|
||||
|
||||
void vm::namespace_value_info(var& val, const usize max_show_elems) {
|
||||
std::clog << "{";
|
||||
usize count = 0;
|
||||
for (const auto& i : val.map().mapper) {
|
||||
++count;
|
||||
if (count>max_show_elems) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::clog << i.first;
|
||||
if (count!=val.map().size()) {
|
||||
std::clog << ", ";
|
||||
}
|
||||
}
|
||||
if (val.map().size()>max_show_elems) {
|
||||
std::clog << "...";
|
||||
}
|
||||
std::clog << "}";
|
||||
}
|
||||
|
||||
void vm::value_name_form(const var& val) {
|
||||
std::clog << "| ";
|
||||
switch(val.type) {
|
||||
case vm_type::vm_none: std::clog << "| null |"; break;
|
||||
case vm_type::vm_ret: std::clog << "| pc | 0x" << std::hex
|
||||
<< val.ret() << std::dec; break;
|
||||
case vm_type::vm_addr: std::clog << "| addr | 0x" << std::hex
|
||||
<< reinterpret_cast<u64>(val.addr())
|
||||
<< std::dec; break;
|
||||
case vm_type::vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
|
||||
case vm_type::vm_nil: std::clog << "| nil |"; break;
|
||||
case vm_type::vm_num: std::clog << "| num | " << val.num(); break;
|
||||
case vm_type::vm_str: std::clog << "| str | <0x" << std::hex << p
|
||||
<< "> \"" << util::rawstr(val.str(), 16)
|
||||
<< "\"" << std::dec; break;
|
||||
case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p
|
||||
<< std::dec << "> " << val.func();
|
||||
break;
|
||||
case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.upval().size
|
||||
<< " val]"; break;
|
||||
case vm_type::vm_vec: std::clog << "| vec | <0x" << std::hex << p
|
||||
<< std::dec << "> [" << val.vec().size()
|
||||
<< " val]"; break;
|
||||
case vm_type::vm_hash: std::clog << "| hash | <0x" << std::hex << p
|
||||
<< std::dec << "> {" << val.hash().size()
|
||||
<< " val}"; break;
|
||||
case vm_type::vm_ghost:std::clog << "| obj | <0x" << std::hex << p
|
||||
<< "> obj:0x"
|
||||
<< reinterpret_cast<u64>(val.ghost().pointer)
|
||||
<< std::dec; break;
|
||||
case vm_type::vm_co: std::clog << "| co | <0x" << std::hex << p
|
||||
<< std::dec << "> coroutine"; break;
|
||||
case vm_type::vm_map: std::clog << "| nmspc| <0x" << std::hex << p
|
||||
<< std::dec << "> namespace ["
|
||||
<< val.map().mapper.size()
|
||||
<< " val]"; break;
|
||||
default: std::clog << "| err | <0x" << std::hex << p
|
||||
<< std::dec << "> unknown object"; break;
|
||||
case vm_type::vm_none: std::clog << "null "; break;
|
||||
case vm_type::vm_ret: std::clog << "ret "; break;
|
||||
case vm_type::vm_addr: std::clog << "addr "; break;
|
||||
case vm_type::vm_cnt: std::clog << "cnt "; break;
|
||||
case vm_type::vm_nil: std::clog << "nil "; break;
|
||||
case vm_type::vm_num: std::clog << "num "; break;
|
||||
case vm_type::vm_str: std::clog << "str "; break;
|
||||
case vm_type::vm_func: std::clog << "func "; break;
|
||||
case vm_type::vm_upval: std::clog << "upval"; break;
|
||||
case vm_type::vm_vec: std::clog << "vec "; break;
|
||||
case vm_type::vm_hash: std::clog << "hash "; break;
|
||||
case vm_type::vm_ghost: std::clog << "ghost"; break;
|
||||
case vm_type::vm_co: std::clog << "co "; break;
|
||||
case vm_type::vm_map: std::clog << "map "; break;
|
||||
default: std::clog << "err "; break;
|
||||
}
|
||||
std::clog << " | ";
|
||||
}
|
||||
|
||||
void vm::value_info(var& val) {
|
||||
value_name_form(val);
|
||||
|
||||
switch(val.type) {
|
||||
case vm_type::vm_none: break;
|
||||
case vm_type::vm_ret: return_address_info(val); break;
|
||||
case vm_type::vm_addr: memory_address_info(val); break;
|
||||
case vm_type::vm_cnt: std::clog << val.cnt(); break;
|
||||
case vm_type::vm_nil: break;
|
||||
case vm_type::vm_num: std::clog << val.num(); break;
|
||||
case vm_type::vm_str: raw_string_info(val); break;
|
||||
case vm_type::vm_func: std::clog << val.func(); break;
|
||||
case vm_type::vm_upval: upvalue_info(val); break;
|
||||
case vm_type::vm_vec: vector_value_info(val); break;
|
||||
case vm_type::vm_hash: hash_value_info(val, 4); break;
|
||||
case vm_type::vm_ghost: std::clog << val.ghost(); break;
|
||||
case vm_type::vm_co: coroutine_value_info(val); break;
|
||||
case vm_type::vm_map: namespace_value_info(val, 4); break;
|
||||
default: std::clog << "unknown"; 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::clog << "func ";
|
||||
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& key : func.keys) {
|
||||
for (const auto& key : func.keys) {
|
||||
argument_list[key.second-1] = key.first;
|
||||
}
|
||||
|
||||
std::clog << "(";
|
||||
for(const auto& key : argument_list) {
|
||||
for (const auto& key : argument_list) {
|
||||
std::clog << key;
|
||||
if (key != argument_list.back()) {
|
||||
std::clog << ", ";
|
||||
@@ -133,64 +214,121 @@ void vm::function_detail_info(const nas_func& func) {
|
||||
}
|
||||
|
||||
void vm::function_call_trace() {
|
||||
// no function is called when error ocurred
|
||||
if (!ctx.funcr.is_func()) {
|
||||
return;
|
||||
}
|
||||
|
||||
util::windows_code_page_manager cp;
|
||||
cp.set_utf8_output();
|
||||
|
||||
var* bottom = ctx.stack;
|
||||
var* top = ctx.top;
|
||||
|
||||
// generate trace back
|
||||
std::stack<const nas_func*> functions;
|
||||
for(var* i = bottom; i<=top; ++i) {
|
||||
if (i->is_func() && i-1>=bottom && (i-1)->is_ret()) {
|
||||
functions.push(&i->func());
|
||||
std::vector<const nas_func*> functions;
|
||||
std::vector<u64> callsite;
|
||||
|
||||
var* prev_func = &ctx.funcr;
|
||||
functions.push_back(&prev_func->func());
|
||||
for (var* i = top; i >= bottom; i--) {
|
||||
// +-------+------------------+
|
||||
// | ret | 0x3bf | <-- i + 1 (should not be 0, except coroutine)
|
||||
// +-------+------------------+
|
||||
// | addr | 0x7ff5f61ae020 | <-- i
|
||||
// +-------+------------------+
|
||||
// | upval | ... | <-- i - 1
|
||||
// +-------+------------------+
|
||||
// | locals| ... |
|
||||
// +-------+------------------+
|
||||
// | func | function | <-- i - 1 - prev_func->local_size - 1
|
||||
// +-------+------------------+
|
||||
if (i + 1 <= top && i[0].is_addr() && i[1].is_ret()) {
|
||||
auto r_addr = i[1].ret();
|
||||
callsite.push_back(r_addr);
|
||||
i--;
|
||||
i -= prev_func->func().local_size;
|
||||
i--;
|
||||
if (i >= bottom && i[0].is_func()) {
|
||||
prev_func = i;
|
||||
functions.push_back(&prev_func->func());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (functions.empty()) {
|
||||
|
||||
std::clog << "\ncall trace ";
|
||||
std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||
std::clog << " crash occurred at\n ";
|
||||
function_detail_info(ctx.funcr.func());
|
||||
std::clog << " at " << files[bytecode[ctx.pc].fidx] << ":";
|
||||
std::clog << bytecode[ctx.pc].line << "\n";
|
||||
|
||||
if (callsite.empty()) {
|
||||
cp.restore_code_page();
|
||||
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;
|
||||
const nas_func* prev = nullptr;
|
||||
u64 prev_addr = 0;
|
||||
u64 same_call_count = 0;
|
||||
for (int i = 0; i < functions.size(); ++i) {
|
||||
if (functions[i] == prev && callsite[i] == prev_addr) {
|
||||
same_call_count++;
|
||||
continue;
|
||||
} else if (same) {
|
||||
std::clog << " --> " << same << " same call(s)\n";
|
||||
same = 0;
|
||||
} else if (same_call_count) {
|
||||
std::clog << " `--> " << same_call_count << " same call(s)\n";
|
||||
same_call_count = 0;
|
||||
}
|
||||
|
||||
// in coroutine
|
||||
if (callsite[i] == 0 && ngc.cort) {
|
||||
std::clog << " call by coroutine\n";
|
||||
break;
|
||||
}
|
||||
|
||||
last = func;
|
||||
std::clog << " call ";
|
||||
function_detail_info(*func);
|
||||
std::clog << "\n";
|
||||
function_detail_info(*functions[i]);
|
||||
auto r_addr = callsite[i];
|
||||
std::clog << " from " << files[bytecode[r_addr].fidx] << ":";
|
||||
std::clog << bytecode[r_addr].line << "\n";
|
||||
|
||||
prev = functions[i];
|
||||
prev_addr = r_addr;
|
||||
}
|
||||
if (same) {
|
||||
std::clog << " --> " << same << " same call(s)\n";
|
||||
if (same_call_count) {
|
||||
std::clog << " `--> " << same_call_count << " same call(s)\n";
|
||||
}
|
||||
|
||||
cp.restore_code_page();
|
||||
}
|
||||
|
||||
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) {
|
||||
std::stack<u64> ret;
|
||||
for (var* i = ctx.stack; i<=ctx.top; ++i) {
|
||||
if (i->is_ret() && i->ret()!=0) {
|
||||
ret.push(i->ret());
|
||||
}
|
||||
}
|
||||
ret.push(ctx.pc); // store the position program crashed
|
||||
|
||||
std::clog << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||
codestream::set(const_number, const_string, native_function.data(), files);
|
||||
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||
// store the position program crashed
|
||||
ret.push(ctx.pc);
|
||||
|
||||
std::clog << "\nback trace ";
|
||||
std::clog << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||
codestream::set(
|
||||
const_number,
|
||||
const_string,
|
||||
global_symbol_name,
|
||||
native_function.data(),
|
||||
files
|
||||
);
|
||||
|
||||
for (u64 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||
if ((p = ret.top())==prev) {
|
||||
++same;
|
||||
continue;
|
||||
}
|
||||
if (same) {
|
||||
} else if (same) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(8) << std::setfill('0')
|
||||
<< prev << std::dec << " "
|
||||
@@ -202,16 +340,16 @@ void vm::trace_back() {
|
||||
// the first called place has no same calls
|
||||
}
|
||||
|
||||
void vm::stack_info(const u64 limit = 16) {
|
||||
void vm::stack_info(const u64 limit) {
|
||||
var* top = ctx.top;
|
||||
var* bottom = ctx.stack;
|
||||
const auto stack_address = reinterpret_cast<u64>(bottom);
|
||||
|
||||
std::clog << "\nvm stack (0x" << std::hex << stack_address << std::dec;
|
||||
std::clog << "\nstack (0x" << std::hex << stack_address << std::dec;
|
||||
std::clog << ", limit " << limit << ", total ";
|
||||
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
||||
|
||||
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||
for (u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(8) << std::setfill('0')
|
||||
<< static_cast<u64>(top-bottom) << std::dec
|
||||
@@ -221,18 +359,18 @@ void vm::stack_info(const u64 limit = 16) {
|
||||
}
|
||||
|
||||
void vm::register_info() {
|
||||
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main") << ")\n";
|
||||
std::clog << "\nregister (" << (ngc.cort? "coroutine":"main") << ")\n";
|
||||
std::clog << std::hex
|
||||
<< " [ pc ] | pc | 0x" << ctx.pc << "\n"
|
||||
<< " [ global ] | addr | 0x"
|
||||
<< " [ pc ] | pc | 0x" << ctx.pc << "\n"
|
||||
<< " [ global ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(global) << "\n"
|
||||
<< " [ local ] | addr | 0x"
|
||||
<< " [ local ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.localr) << "\n"
|
||||
<< " [ memr ] | addr | 0x"
|
||||
<< " [ memr ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.memr) << "\n"
|
||||
<< " [ canary ] | addr | 0x"
|
||||
<< " [ canary ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.canary) << "\n"
|
||||
<< " [ top ] | addr | 0x"
|
||||
<< " [ top ] | addr | 0x"
|
||||
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
||||
<< std::dec;
|
||||
std::clog << " [ funcr ] "; value_info(ctx.funcr);
|
||||
@@ -245,10 +383,19 @@ void vm::global_state() {
|
||||
}
|
||||
std::clog << "\nglobal (0x" << std::hex
|
||||
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
||||
for(usize i = 0; i<global_size; ++i) {
|
||||
for (usize i = 0; i<global_size; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(8)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< std::setfill('0') << static_cast<u64>(i) << std::dec
|
||||
<< " ";
|
||||
auto name = global_symbol_name[i];
|
||||
if (name.length()>=10) {
|
||||
name = name.substr(0, 7) + "...";
|
||||
} else {
|
||||
|
||||
}
|
||||
std::clog << "| " << std::left << std::setw(10)
|
||||
<< std::setfill(' ') << name << " "
|
||||
<< std::internal;
|
||||
value_info(global[i]);
|
||||
}
|
||||
}
|
||||
@@ -261,7 +408,7 @@ void vm::local_state() {
|
||||
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) {
|
||||
for (u32 i = 0; i<lsize; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(8)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
@@ -275,10 +422,10 @@ void vm::upvalue_state() {
|
||||
}
|
||||
std::clog << "\nupvalue\n";
|
||||
auto& upval = ctx.funcr.func().upval;
|
||||
for(u32 i = 0; i<upval.size(); ++i) {
|
||||
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) {
|
||||
for (u32 j = 0; j<uv.size; ++j) {
|
||||
std::clog << " 0x" << std::hex << std::setw(8)
|
||||
<< std::setfill('0') << j << std::dec
|
||||
<< " ";
|
||||
@@ -298,10 +445,10 @@ 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) {
|
||||
for (const auto& i : func.keys) {
|
||||
argument_list[i.second-1] = i.first;
|
||||
}
|
||||
for(u32 i = 0; i<argument_list.size(); ++i) {
|
||||
for (u32 i = 0; i<argument_list.size(); ++i) {
|
||||
result += argument_list[i];
|
||||
if (i<argc) {
|
||||
result += "[get]";
|
||||
@@ -322,15 +469,15 @@ std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
|
||||
return result + out.str();
|
||||
}
|
||||
|
||||
std::string vm::report_special_call_lack_arguments(
|
||||
var* local, const nas_func& func) const {
|
||||
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) {
|
||||
for (const auto& i : func.keys) {
|
||||
argument_list[i.second-1] = i.first;
|
||||
}
|
||||
for(const auto& key : argument_list) {
|
||||
for (const auto& key : argument_list) {
|
||||
if (local[func.keys.at(key)].is_none()) {
|
||||
result += key + ", ";
|
||||
} else {
|
||||
@@ -346,10 +493,10 @@ std::string vm::report_special_call_lack_arguments(
|
||||
return result + out.str();
|
||||
}
|
||||
|
||||
std::string vm::report_key_not_found(
|
||||
const std::string& not_found, const nas_hash& hash) const {
|
||||
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) {
|
||||
for (const auto& i : hash.elems) {
|
||||
result += i.first + ", ";
|
||||
}
|
||||
if (hash.elems.size()) {
|
||||
@@ -392,10 +539,21 @@ std::string vm::type_name_string(const var& value) const {
|
||||
}
|
||||
|
||||
void vm::die(const std::string& str) {
|
||||
std::cerr << "[vm] error: " << str << "\n";
|
||||
const auto& file = files[bytecode[ctx.pc].fidx];
|
||||
const auto line = bytecode[ctx.pc].line;
|
||||
std::cerr << "[vm] error occurred at " << file << ":" << line << ": ";
|
||||
std::cerr << str << "\n";
|
||||
function_call_trace();
|
||||
trace_back();
|
||||
stack_info();
|
||||
|
||||
// trace back contains bytecode info, dump in verbose mode
|
||||
if (verbose) {
|
||||
trace_back();
|
||||
}
|
||||
|
||||
// verbose will dump more values on stack
|
||||
if (verbose) {
|
||||
stack_info(64);
|
||||
}
|
||||
|
||||
// show verbose crash info
|
||||
if (verbose) {
|
||||
@@ -403,14 +561,17 @@ void vm::die(const std::string& str) {
|
||||
}
|
||||
|
||||
if (!ngc.cort) {
|
||||
if (!verbose) {
|
||||
std::cerr << "\n[vm] use <-d> for detailed crash info.\n\n";
|
||||
}
|
||||
// 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'
|
||||
}
|
||||
|
||||
// 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,
|
||||
@@ -427,6 +588,14 @@ void vm::run(const codegen& gen,
|
||||
);
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
||||
// interrupt check macro for computed goto mode.
|
||||
#define CHECK_INTERRUPT { \
|
||||
if (interrupt_ptr && interrupt_ptr->load()) { \
|
||||
throw std::runtime_error("VM execution interrupted by timeout"); \
|
||||
} \
|
||||
}
|
||||
|
||||
// using labels as values/computed goto
|
||||
const void* oprs[] = {
|
||||
&&vmexit,
|
||||
@@ -519,19 +688,23 @@ void vm::run(const codegen& gen,
|
||||
&&ret
|
||||
};
|
||||
std::vector<const void*> code;
|
||||
for(const auto& i : gen.codes()) {
|
||||
for (const auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
CHECK_INTERRUPT;
|
||||
// goto the first operand
|
||||
goto *code[ctx.pc];
|
||||
#else
|
||||
std::vector<nasal_vm_func> code;
|
||||
for(const auto& i : gen.codes()) {
|
||||
for (const auto& i : gen.codes()) {
|
||||
code.push_back(operand_function[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(code[ctx.pc]) {
|
||||
while (code[ctx.pc]) {
|
||||
if (interrupt_ptr && interrupt_ptr->load()) {
|
||||
throw std::runtime_error("VM execution interrupted by timeout");
|
||||
}
|
||||
(this->*code[ctx.pc])();
|
||||
if (ctx.top>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
@@ -542,7 +715,7 @@ void vm::run(const codegen& gen,
|
||||
// all nasal programs should end here
|
||||
vmexit:
|
||||
if (verbose) {
|
||||
ngc.info();
|
||||
ngc.status.dump_info();
|
||||
}
|
||||
imm.clear();
|
||||
if (!is_repl_mode) {
|
||||
@@ -551,17 +724,19 @@ vmexit:
|
||||
return;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// may cause stackoverflow
|
||||
// IR which may cause stackoverflow
|
||||
#define exec_check(op) {\
|
||||
op();\
|
||||
CHECK_INTERRUPT;\
|
||||
if (ctx.top<ctx.canary)\
|
||||
goto *code[++ctx.pc];\
|
||||
die("stack overflow");\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
// do not cause stackoverflow
|
||||
// IR which does not cause stackoverflow
|
||||
#define exec_nodie(op) {\
|
||||
op();\
|
||||
CHECK_INTERRUPT;\
|
||||
goto *code[++ctx.pc];\
|
||||
}
|
||||
|
||||
|
||||
287
src/nasal_vm.h
287
src/nasal_vm.h
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <stack>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_gc.h"
|
||||
@@ -31,7 +31,7 @@ protected:
|
||||
|
||||
/* nasal native functions */
|
||||
std::vector<nasal_builtin_table> native_function;
|
||||
|
||||
|
||||
/* garbage collector */
|
||||
gc ngc;
|
||||
|
||||
@@ -42,6 +42,7 @@ protected:
|
||||
/* values used for debugger */
|
||||
const std::string* files = nullptr; // file name list
|
||||
const opcode* bytecode = nullptr; // bytecode buffer address
|
||||
std::vector<std::string> global_symbol_name; // global symbol name
|
||||
|
||||
/* variables for repl mode */
|
||||
bool is_repl_mode = false;
|
||||
@@ -65,6 +66,15 @@ protected:
|
||||
protected:
|
||||
/* debug functions */
|
||||
bool verbose = false;
|
||||
void return_address_info(const var&);
|
||||
void memory_address_info(const var&);
|
||||
void raw_string_info(var&);
|
||||
void upvalue_info(var&);
|
||||
void vector_value_info(var&);
|
||||
void hash_value_info(var&, const usize);
|
||||
void coroutine_value_info(var&);
|
||||
void namespace_value_info(var&, const usize);
|
||||
void value_name_form(const var&);
|
||||
void value_info(var&);
|
||||
void function_detail_info(const nas_func&);
|
||||
void function_call_trace();
|
||||
@@ -75,6 +85,8 @@ protected:
|
||||
void local_state();
|
||||
void upvalue_state();
|
||||
void all_state_detail();
|
||||
|
||||
protected:
|
||||
std::string report_lack_arguments(u32, const nas_func&) const;
|
||||
std::string report_special_call_lack_arguments(var*, const nas_func&) const;
|
||||
std::string report_key_not_found(const std::string&, const nas_hash&) const;
|
||||
@@ -84,7 +96,8 @@ protected:
|
||||
|
||||
protected:
|
||||
/* vm calculation functions*/
|
||||
inline bool cond(var&);
|
||||
inline bool boolify(const var&);
|
||||
inline void set_frame(const nas_func&, var*);
|
||||
|
||||
protected:
|
||||
/* vm operands */
|
||||
@@ -306,18 +319,50 @@ public:
|
||||
void set_limit_mode_flag(bool flag) {
|
||||
flag_limited_mode = flag;
|
||||
}
|
||||
|
||||
auto get_gc_time_ms() const {
|
||||
return ngc.get_gc_time_ms();
|
||||
}
|
||||
|
||||
auto get_total_memory() const {
|
||||
return ngc.get_total_memory();
|
||||
}
|
||||
|
||||
void set_interrupt_ptr(std::atomic<bool>* p) {
|
||||
interrupt_ptr = p;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool>* interrupt_ptr = nullptr;
|
||||
};
|
||||
|
||||
inline bool vm::cond(var& val) {
|
||||
inline bool vm::boolify(const var& val) {
|
||||
if (val.is_num()) {
|
||||
return val.num();
|
||||
} else if (val.is_str()) {
|
||||
const f64 num = util::str_to_num(val.str().c_str());
|
||||
return std::isnan(num)? !val.str().empty():num;
|
||||
return std::isnan(num)? !val.str().empty() : num;
|
||||
} else if (val.is_vec()) {
|
||||
return val.vec().size() > 0;
|
||||
} else if (val.is_hash()) {
|
||||
return val.hash().size() > 0;
|
||||
} else if (val.is_func() || val.is_ghost() || val.is_coroutine()) {
|
||||
return true;
|
||||
} else if (val.is_map()) {
|
||||
return val.map().size() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void vm::set_frame(const nas_func& func, var* local) {
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
(++ctx.top)[0] = var::ret(ctx.pc); // rewrite top with vm_ret
|
||||
ctx.pc = func.entry - 1;
|
||||
ctx.localr = local;
|
||||
ctx.upvalr = nil;
|
||||
}
|
||||
|
||||
inline void vm::o_repl() {
|
||||
// reserved for repl mode stack top value output
|
||||
if (allow_repl_output) {
|
||||
@@ -339,8 +384,8 @@ inline void vm::o_loadl() {
|
||||
}
|
||||
|
||||
inline void vm::o_loadu() {
|
||||
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
|
||||
ctx.funcr.func().upval[(imm[ctx.pc]>>16) & 0xffff]
|
||||
.upval()[imm[ctx.pc] & 0xffff] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_dup() {
|
||||
@@ -367,7 +412,7 @@ inline void vm::o_newv() {
|
||||
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
|
||||
ctx.top = ctx.top - imm[ctx.pc] + 1;
|
||||
|
||||
for(u64 i = 0; i<imm[ctx.pc]; ++i) {
|
||||
for (u64 i = 0; i<imm[ctx.pc]; ++i) {
|
||||
vec[i] = ctx.top[i];
|
||||
}
|
||||
ctx.top[0] = newv;
|
||||
@@ -432,7 +477,7 @@ inline void vm::o_lnot() {
|
||||
var val = ctx.top[0];
|
||||
switch(val.type) {
|
||||
case vm_type::vm_nil: ctx.top[0] = one; break;
|
||||
case vm_type::vm_num: ctx.top[0] = val.num()? zero:one; break;
|
||||
case vm_type::vm_num: ctx.top[0] = val.num()? zero : one; break;
|
||||
case vm_type::vm_str: {
|
||||
const f64 num = util::str_to_num(val.str().c_str());
|
||||
if (std::isnan(num)) {
|
||||
@@ -442,7 +487,7 @@ inline void vm::o_lnot() {
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
die("cannot do not-operation on "+type_name_string(val));
|
||||
die("cannot do not-operation on " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -457,7 +502,7 @@ inline void vm::o_bnot() {
|
||||
|
||||
inline void vm::o_btor() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].to_num())|
|
||||
static_cast<i32>(ctx.top[-1].to_num()) |
|
||||
static_cast<i32>(ctx.top[0].to_num())
|
||||
);
|
||||
--ctx.top;
|
||||
@@ -465,7 +510,7 @@ inline void vm::o_btor() {
|
||||
|
||||
inline void vm::o_btxor() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].to_num())^
|
||||
static_cast<i32>(ctx.top[-1].to_num()) ^
|
||||
static_cast<i32>(ctx.top[0].to_num())
|
||||
);
|
||||
--ctx.top;
|
||||
@@ -473,7 +518,7 @@ inline void vm::o_btxor() {
|
||||
|
||||
inline void vm::o_btand() {
|
||||
ctx.top[-1] = var::num(
|
||||
static_cast<i32>(ctx.top[-1].to_num())&
|
||||
static_cast<i32>(ctx.top[-1].to_num()) &
|
||||
static_cast<i32>(ctx.top[0].to_num())
|
||||
);
|
||||
--ctx.top;
|
||||
@@ -483,18 +528,18 @@ inline void vm::o_btand() {
|
||||
ctx.top[-1] = var::num(ctx.top[-1].to_num() type ctx.top[0].to_num());\
|
||||
--ctx.top;
|
||||
|
||||
inline void vm::o_add() {op_calc(+);}
|
||||
inline void vm::o_sub() {op_calc(-);}
|
||||
inline void vm::o_mul() {op_calc(*);}
|
||||
inline void vm::o_div() {op_calc(/);}
|
||||
inline void vm::o_add() { op_calc(+); }
|
||||
inline void vm::o_sub() { op_calc(-); }
|
||||
inline void vm::o_mul() { op_calc(*); }
|
||||
inline void vm::o_div() { op_calc(/); }
|
||||
inline void vm::o_lnk() {
|
||||
// concat two vectors into one
|
||||
if (ctx.top[-1].is_vec() && ctx.top[0].is_vec()) {
|
||||
ngc.temp = ngc.alloc(vm_type::vm_vec);
|
||||
for(auto i : ctx.top[-1].vec().elems) {
|
||||
for (auto& i : ctx.top[-1].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
for(auto i : ctx.top[0].vec().elems) {
|
||||
for (auto& i : ctx.top[0].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
ctx.top[-1] = ngc.temp;
|
||||
@@ -503,19 +548,19 @@ inline void vm::o_lnk() {
|
||||
return;
|
||||
}
|
||||
// concat strings
|
||||
ctx.top[-1] = ngc.newstr(ctx.top[-1].to_str()+ctx.top[0].to_str());
|
||||
ctx.top[-1] = ngc.newstr(ctx.top[-1].to_str() + ctx.top[0].to_str());
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
#define op_calc_const(type)\
|
||||
ctx.top[0] = var::num(ctx.top[0].to_num() type const_number[imm[ctx.pc]]);
|
||||
|
||||
inline void vm::o_addc() {op_calc_const(+);}
|
||||
inline void vm::o_subc() {op_calc_const(-);}
|
||||
inline void vm::o_mulc() {op_calc_const(*);}
|
||||
inline void vm::o_divc() {op_calc_const(/);}
|
||||
inline void vm::o_addc() { op_calc_const(+); }
|
||||
inline void vm::o_subc() { op_calc_const(-); }
|
||||
inline void vm::o_mulc() { op_calc_const(*); }
|
||||
inline void vm::o_divc() { op_calc_const(/); }
|
||||
inline void vm::o_lnkc() {
|
||||
ctx.top[0] = ngc.newstr(ctx.top[0].to_str()+const_string[imm[ctx.pc]]);
|
||||
ctx.top[0] = ngc.newstr(ctx.top[0].to_str() + const_string[imm[ctx.pc]]);
|
||||
}
|
||||
|
||||
// top[0] stores the value of memr[0], to avoid being garbage-collected
|
||||
@@ -528,18 +573,34 @@ inline void vm::o_lnkc() {
|
||||
ctx.memr[0].to_num() type ctx.top[-1].to_num()\
|
||||
);\
|
||||
ctx.memr = nullptr;\
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
ctx.top -= imm[ctx.pc] + 1;
|
||||
|
||||
inline void vm::o_addeq() {op_calc_eq(+);}
|
||||
inline void vm::o_subeq() {op_calc_eq(-);}
|
||||
inline void vm::o_muleq() {op_calc_eq(*);}
|
||||
inline void vm::o_diveq() {op_calc_eq(/);}
|
||||
inline void vm::o_addeq() { op_calc_eq(+); }
|
||||
inline void vm::o_subeq() { op_calc_eq(-); }
|
||||
inline void vm::o_muleq() { op_calc_eq(*); }
|
||||
inline void vm::o_diveq() { op_calc_eq(/); }
|
||||
inline void vm::o_lnkeq() {
|
||||
// concat two vectors into one
|
||||
if (ctx.top[-1].is_vec() && ctx.memr[0].is_vec()) {
|
||||
ngc.temp = ngc.alloc(vm_type::vm_vec);
|
||||
for (auto i : ctx.memr[0].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
for (auto i : ctx.top[-1].vec().elems) {
|
||||
ngc.temp.vec().elems.push_back(i);
|
||||
}
|
||||
ctx.top[-1] = ctx.memr[0] = ngc.temp;
|
||||
ngc.temp = nil;
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.top[-1] = ctx.memr[0] = ngc.newstr(
|
||||
ctx.memr[0].to_str()+ctx.top[-1].to_str()
|
||||
ctx.memr[0].to_str() + ctx.top[-1].to_str()
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
ctx.top -= imm[ctx.pc] + 1;
|
||||
}
|
||||
|
||||
inline void vm::o_bandeq() {
|
||||
@@ -562,11 +623,11 @@ inline void vm::o_boreq() {
|
||||
|
||||
inline void vm::o_bxoreq() {
|
||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||
static_cast<i32>(ctx.memr[0].to_num())^
|
||||
static_cast<i32>(ctx.memr[0].to_num()) ^
|
||||
static_cast<i32>(ctx.top[-1].to_num())
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
ctx.top -= imm[ctx.pc]+1;
|
||||
ctx.top -= imm[ctx.pc] + 1;
|
||||
}
|
||||
|
||||
// top[0] stores the value of memr[0], to avoid being garbage-collected
|
||||
@@ -580,13 +641,13 @@ inline void vm::o_bxoreq() {
|
||||
);\
|
||||
ctx.memr = nullptr;
|
||||
|
||||
inline void vm::o_addeqc() {op_calc_eq_const(+);}
|
||||
inline void vm::o_subeqc() {op_calc_eq_const(-);}
|
||||
inline void vm::o_muleqc() {op_calc_eq_const(*);}
|
||||
inline void vm::o_diveqc() {op_calc_eq_const(/);}
|
||||
inline void vm::o_addeqc() { op_calc_eq_const(+); }
|
||||
inline void vm::o_subeqc() { op_calc_eq_const(-); }
|
||||
inline void vm::o_muleqc() { op_calc_eq_const(*); }
|
||||
inline void vm::o_diveqc() { op_calc_eq_const(/); }
|
||||
inline void vm::o_lnkeqc() {
|
||||
ctx.top[0] = ctx.memr[0] = ngc.newstr(
|
||||
ctx.memr[0].to_str()+const_string[imm[ctx.pc]]
|
||||
ctx.memr[0].to_str() + const_string[imm[ctx.pc]]
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
}
|
||||
@@ -598,13 +659,13 @@ inline void vm::o_lnkeqc() {
|
||||
ctx.memr = nullptr;\
|
||||
--ctx.top;
|
||||
|
||||
inline void vm::o_addecp() {op_calc_eq_const_and_pop(+);}
|
||||
inline void vm::o_subecp() {op_calc_eq_const_and_pop(-);}
|
||||
inline void vm::o_mulecp() {op_calc_eq_const_and_pop(*);}
|
||||
inline void vm::o_divecp() {op_calc_eq_const_and_pop(/);}
|
||||
inline void vm::o_addecp() { op_calc_eq_const_and_pop(+); }
|
||||
inline void vm::o_subecp() { op_calc_eq_const_and_pop(-); }
|
||||
inline void vm::o_mulecp() { op_calc_eq_const_and_pop(*); }
|
||||
inline void vm::o_divecp() { op_calc_eq_const_and_pop(/); }
|
||||
inline void vm::o_lnkecp() {
|
||||
ctx.top[0] = ctx.memr[0] = ngc.newstr(
|
||||
ctx.memr[0].to_str()+const_string[imm[ctx.pc]]
|
||||
ctx.memr[0].to_str() + const_string[imm[ctx.pc]]
|
||||
);
|
||||
ctx.memr = nullptr;
|
||||
--ctx.top;
|
||||
@@ -627,12 +688,12 @@ inline void vm::o_eq() {
|
||||
if (val1.is_nil() && val2.is_nil()) {
|
||||
ctx.top[0] = one;
|
||||
} else if (val1.is_str() && val2.is_str()) {
|
||||
ctx.top[0] = (val1.str()==val2.str())? one:zero;
|
||||
ctx.top[0] = (val1.str()==val2.str())? one : zero;
|
||||
} else if ((val1.is_num() || val2.is_num())
|
||||
&& !val1.is_nil() && !val2.is_nil()) {
|
||||
ctx.top[0] = (val1.to_num()==val2.to_num())? one:zero;
|
||||
ctx.top[0] = (val1.to_num()==val2.to_num())? one : zero;
|
||||
} else {
|
||||
ctx.top[0] = (val1==val2)? one:zero;
|
||||
ctx.top[0] = (val1==val2)? one : zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,59 +703,59 @@ inline void vm::o_neq() {
|
||||
if (val1.is_nil() && val2.is_nil()) {
|
||||
ctx.top[0] = zero;
|
||||
} else if (val1.is_str() && val2.is_str()) {
|
||||
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
|
||||
ctx.top[0] = (val1.str()!=val2.str())? one : zero;
|
||||
} else if ((val1.is_num() || val2.is_num())
|
||||
&& !val1.is_nil() && !val2.is_nil()) {
|
||||
ctx.top[0] = (val1.to_num()!=val2.to_num())? one:zero;
|
||||
ctx.top[0] = (val1.to_num()!=val2.to_num())? one : zero;
|
||||
} else {
|
||||
ctx.top[0] = (val1!=val2)? one:zero;
|
||||
ctx.top[0] = (val1!=val2)? one : zero;
|
||||
}
|
||||
}
|
||||
|
||||
#define op_cmp(type)\
|
||||
--ctx.top;\
|
||||
ctx.top[0] = (ctx.top[0].to_num() type ctx.top[1].to_num())? one:zero;
|
||||
ctx.top[0] = (ctx.top[0].to_num() type ctx.top[1].to_num())? one : zero;
|
||||
|
||||
inline void vm::o_less() {op_cmp(<);}
|
||||
inline void vm::o_leq() {op_cmp(<=);}
|
||||
inline void vm::o_grt() {op_cmp(>);}
|
||||
inline void vm::o_geq() {op_cmp(>=);}
|
||||
inline void vm::o_less() { op_cmp(<); }
|
||||
inline void vm::o_leq() { op_cmp(<=); }
|
||||
inline void vm::o_grt() { op_cmp(>); }
|
||||
inline void vm::o_geq() { op_cmp(>=); }
|
||||
|
||||
#define op_cmp_const(type)\
|
||||
ctx.top[0] = (ctx.top[0].to_num() type const_number[imm[ctx.pc]])? one:zero;
|
||||
ctx.top[0] = (ctx.top[0].to_num() type const_number[imm[ctx.pc]])? one : zero;
|
||||
|
||||
inline void vm::o_lessc() {op_cmp_const(<);}
|
||||
inline void vm::o_leqc() {op_cmp_const(<=);}
|
||||
inline void vm::o_grtc() {op_cmp_const(>);}
|
||||
inline void vm::o_geqc() {op_cmp_const(>=);}
|
||||
inline void vm::o_lessc() { op_cmp_const(<); }
|
||||
inline void vm::o_leqc() { op_cmp_const(<=); }
|
||||
inline void vm::o_grtc() { op_cmp_const(>); }
|
||||
inline void vm::o_geqc() { op_cmp_const(>=); }
|
||||
|
||||
inline void vm::o_pop() {
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_jmp() {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
ctx.pc = imm[ctx.pc] - 1;
|
||||
}
|
||||
|
||||
inline void vm::o_jt() {
|
||||
// jump true needs to reserve the result on stack
|
||||
// because conditional expression in nasal has return value
|
||||
if (cond(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
if (boolify(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_jf() {
|
||||
// jump false doesn't need to reserve result
|
||||
if (!cond(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc]-1;
|
||||
if (!boolify(ctx.top[0])) {
|
||||
ctx.pc = imm[ctx.pc] - 1;
|
||||
}
|
||||
--ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_cnt() {
|
||||
if (!ctx.top[0].is_vec()) {
|
||||
die("must use vector in forindex/foreach but get "+
|
||||
die("must use vector in forindex/foreach but get " +
|
||||
type_name_string(ctx.top[0])
|
||||
);
|
||||
return;
|
||||
@@ -732,8 +793,8 @@ inline void vm::o_calll() {
|
||||
|
||||
inline void vm::o_upval() {
|
||||
(++ctx.top)[0] = ctx.funcr.func()
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff];
|
||||
.upval[(imm[ctx.pc] >> 16) & 0xffff]
|
||||
.upval()[imm[ctx.pc] & 0xffff];
|
||||
}
|
||||
|
||||
inline void vm::o_callv() {
|
||||
@@ -747,7 +808,7 @@ inline void vm::o_callv() {
|
||||
}
|
||||
} else if (vec.is_hash()) {
|
||||
if (!val.is_str()) {
|
||||
die("must use string as the key but get "+type_name_string(val));
|
||||
die("must use string as the key but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = vec.hash().get_value(val.str());
|
||||
@@ -766,20 +827,20 @@ inline void vm::o_callv() {
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = var::num(
|
||||
static_cast<f64>(static_cast<u8>(str[num>=0? num:num+len]))
|
||||
static_cast<f64>(static_cast<u8>(str[num>=0? num : num + len]))
|
||||
);
|
||||
} else if (vec.is_map()) {
|
||||
if (!val.is_str()) {
|
||||
die("must use string as the key but get "+type_name_string(val));
|
||||
die("must use string as the key but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
ctx.top[0] = vec.map().get_value(val.str());
|
||||
if (ctx.top[0].is_none()) {
|
||||
die("cannot find symbol \""+val.str()+"\"");
|
||||
die("cannot find symbol \"" + val.str() + "\"");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
die("must call a vector/hash/string but get "+type_name_string(vec));
|
||||
die("must call a vector/hash/string but get " + type_name_string(vec));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -787,7 +848,7 @@ inline void vm::o_callv() {
|
||||
inline void vm::o_callvi() {
|
||||
var val = ctx.top[0];
|
||||
if (!val.is_vec()) {
|
||||
die("must use a vector but get "+type_name_string(val));
|
||||
die("must use a vector but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
// cannot use operator[], because this may cause overflow
|
||||
@@ -801,7 +862,7 @@ inline void vm::o_callvi() {
|
||||
inline void vm::o_callh() {
|
||||
var val = ctx.top[0];
|
||||
if (!val.is_hash() && !val.is_map()) {
|
||||
die("must call a hash but get "+type_name_string(val));
|
||||
die("must call a hash but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -811,21 +872,26 @@ inline void vm::o_callh() {
|
||||
} else {
|
||||
ctx.top[0] = val.map().get_value(str);
|
||||
}
|
||||
|
||||
// report key not found if get_value returns none
|
||||
if (ctx.top[0].is_none()) {
|
||||
val.is_hash()?
|
||||
die(report_key_not_found(str, val.hash())):
|
||||
die("cannot find symbol \"" + str + "\"");
|
||||
val.is_hash()
|
||||
? die(report_key_not_found(str, val.hash()))
|
||||
: die("cannot find symbol \"" + str + "\"");
|
||||
return;
|
||||
} else if (ctx.top[0].is_func()) {
|
||||
}
|
||||
|
||||
// if get function from hash, set 'me'
|
||||
if (ctx.top[0].is_func() && val.is_hash()) {
|
||||
ctx.top[0].func().local[0] = val; // 'me'
|
||||
}
|
||||
}
|
||||
|
||||
inline void vm::o_callfv() {
|
||||
const auto argc = imm[ctx.pc]; // arguments counter
|
||||
var* local = ctx.top-argc+1; // arguments begin address
|
||||
var* local = ctx.top - argc + 1; // arguments begin address
|
||||
if (!local[-1].is_func()) {
|
||||
die("must call a function but get "+type_name_string(local[-1]));
|
||||
die("must call a function but get " + type_name_string(local[-1]));
|
||||
return;
|
||||
}
|
||||
const auto& func = local[-1].func();
|
||||
@@ -852,13 +918,13 @@ inline void vm::o_callfv() {
|
||||
if (func.dynamic_parameter_index>=0) {
|
||||
// load dynamic argument
|
||||
dynamic = ngc.alloc(vm_type::vm_vec);
|
||||
for(u64 i = parameter_size; i<argc; ++i) {
|
||||
for (u64 i = parameter_size; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
} else if (parameter_size<argc) {
|
||||
// load arguments to default dynamic argument "arg", located at stack+1
|
||||
dynamic = ngc.alloc(vm_type::vm_vec);
|
||||
for(u64 i = parameter_size; i<argc; ++i) {
|
||||
for (u64 i = parameter_size; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
}
|
||||
@@ -868,38 +934,35 @@ inline void vm::o_callfv() {
|
||||
// then all the available values the vector needs
|
||||
// are all outside the stack top and may be
|
||||
// collected incorrectly
|
||||
ctx.top = local+func.local_size;
|
||||
ctx.top = local + func.local_size;
|
||||
|
||||
// use (std::min) to avoid compilation error in MSVC
|
||||
// MSVC windows.h uses macro std::min
|
||||
const u64 min_size = (std::min)(parameter_size, argc);
|
||||
|
||||
// load arguments
|
||||
for(u64 i = min_size; i>=1; --i) {
|
||||
for (u64 i = min_size; i>=1; --i) {
|
||||
local[i] = local[i-1];
|
||||
}
|
||||
local[0] = func.local[0]; // load "me"
|
||||
|
||||
// load local scope & default arguments
|
||||
for(u64 i = min_size+1; i<func.local_size; ++i) {
|
||||
for (u64 i = min_size + 1; i<func.local_size; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
// load dynamic argument
|
||||
local[func.dynamic_parameter_index>=0?
|
||||
parameter_size+1:func.local_size-1] = dynamic;
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
(++ctx.top)[0] = var::ret(ctx.pc);
|
||||
ctx.pc = func.entry-1;
|
||||
ctx.localr = local;
|
||||
ctx.upvalr = nil;
|
||||
// load dynamic argument
|
||||
local[func.dynamic_parameter_index >= 0
|
||||
? parameter_size + 1
|
||||
: func.local_size - 1] = dynamic;
|
||||
|
||||
set_frame(func, local);
|
||||
}
|
||||
|
||||
inline void vm::o_callfh() {
|
||||
const auto& hash = ctx.top[0].hash().elems;
|
||||
if (!ctx.top[-1].is_func()) {
|
||||
die("must call a function but get "+type_name_string(ctx.top[-1]));
|
||||
die("must call a function but get " + type_name_string(ctx.top[-1]));
|
||||
return;
|
||||
}
|
||||
const auto& func = ctx.top[-1].func();
|
||||
@@ -922,12 +985,12 @@ inline void vm::o_callfh() {
|
||||
|
||||
var* local = ctx.top;
|
||||
ctx.top += func.local_size;
|
||||
for(u32 i = 0; i<func.local_size; ++i) {
|
||||
for (u32 i = 0; i<func.local_size; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
|
||||
|
||||
bool lack_arguments_flag = false;
|
||||
for(const auto& i : func.keys) {
|
||||
for (const auto& i : func.keys) {
|
||||
const auto& key = i.first;
|
||||
if (hash.count(key)) {
|
||||
local[i.second] = hash.at(key);
|
||||
@@ -940,12 +1003,7 @@ inline void vm::o_callfh() {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
(++ctx.top)[0] = var::ret(ctx.pc); // rewrite top with vm_ret
|
||||
ctx.pc=func.entry-1;
|
||||
ctx.localr = local;
|
||||
ctx.upvalr = nil;
|
||||
set_frame(func, local);
|
||||
}
|
||||
|
||||
inline void vm::o_callb() {
|
||||
@@ -977,7 +1035,7 @@ inline void vm::o_slcbeg() {
|
||||
// +--------------+
|
||||
(++ctx.top)[0] = ngc.alloc(vm_type::vm_vec);
|
||||
if (!ctx.top[-1].is_vec()) {
|
||||
die("must slice a vector but get "+type_name_string(ctx.top[-1]));
|
||||
die("must slice a vector but get " + type_name_string(ctx.top[-1]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1024,7 +1082,7 @@ inline void vm::o_slc2() {
|
||||
);
|
||||
return;
|
||||
} else if (num1<=num2) {
|
||||
for(i32 i = num1; i<=num2; ++i) {
|
||||
for (i32 i = num1; i<=num2; ++i) {
|
||||
aim.push_back(i>=0? ref[i]:ref[i+size]);
|
||||
}
|
||||
}
|
||||
@@ -1047,8 +1105,8 @@ inline void vm::o_mcalll() {
|
||||
inline void vm::o_mupval() {
|
||||
ctx.memr = &(
|
||||
ctx.funcr.func()
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff]
|
||||
.upval[(imm[ctx.pc]>>16) & 0xffff]
|
||||
.upval()[imm[ctx.pc] & 0xffff]
|
||||
);
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
@@ -1066,7 +1124,7 @@ inline void vm::o_mcallv() {
|
||||
}
|
||||
} else if (vec.is_hash()) { // do mcallh but use the mcallv way
|
||||
if (!val.is_str()) {
|
||||
die("must use string as the key but get "+type_name_string(val));
|
||||
die("must use string as the key but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
auto& ref = vec.hash();
|
||||
@@ -1078,7 +1136,7 @@ inline void vm::o_mcallv() {
|
||||
}
|
||||
} else if (vec.is_map()) {
|
||||
if (!val.is_str()) {
|
||||
die("must use string as the key but get "+type_name_string(val));
|
||||
die("must use string as the key but get " + type_name_string(val));
|
||||
return;
|
||||
}
|
||||
auto& ref = vec.map();
|
||||
@@ -1123,6 +1181,11 @@ inline void vm::o_mcallh() {
|
||||
}
|
||||
|
||||
inline void vm::o_ret() {
|
||||
// return statement outside any other functions
|
||||
// directly exit the program
|
||||
if (ctx.funcr.is_nil()) {
|
||||
std::exit(0);
|
||||
}
|
||||
/* +-------------+
|
||||
* | return value| <- top[0]
|
||||
* +-------------+
|
||||
@@ -1156,7 +1219,7 @@ inline void vm::o_ret() {
|
||||
auto size = func.func().local_size;
|
||||
upval.on_stack = false;
|
||||
upval.elems.resize(size);
|
||||
for(u64 i = 0; i<size; ++i) {
|
||||
for (u64 i = 0; i < size; ++i) {
|
||||
upval.elems[i] = local[i];
|
||||
}
|
||||
}
|
||||
|
||||
399
src/nasal_web.cpp
Normal file
399
src/nasal_web.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
#include "nasal_web.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_import.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "repl/repl.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
// Helper function implementations inside anonymous namespace
|
||||
std::vector<std::string> split_string(const std::string& str, char delim) {
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(str);
|
||||
std::string item;
|
||||
while (std::getline(ss, item, delim)) {
|
||||
result.push_back(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string join_string(const std::vector<std::string>& vec, const std::string& delim) {
|
||||
if (vec.empty()) return "";
|
||||
std::stringstream ss;
|
||||
ss << vec[0];
|
||||
for (size_t i = 1; i < vec.size(); ++i) {
|
||||
ss << delim << vec[i];
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
struct NasalContext {
|
||||
std::unique_ptr<nasal::vm> vm_instance;
|
||||
std::string last_result;
|
||||
std::string last_error;
|
||||
std::chrono::seconds timeout{5}; // Default 5 second timeout
|
||||
std::atomic<bool> interrupted{false};
|
||||
|
||||
NasalContext() {
|
||||
vm_instance = std::make_unique<nasal::vm>();
|
||||
vm_instance->set_interrupt_ptr(&interrupted);
|
||||
}
|
||||
|
||||
~NasalContext() {
|
||||
vm_instance.reset(); // Reset explicitly
|
||||
}
|
||||
};
|
||||
|
||||
struct WebReplContext {
|
||||
std::unique_ptr<nasal::repl::repl> repl_instance;
|
||||
std::vector<std::string> source;
|
||||
std::string last_result;
|
||||
std::string last_error;
|
||||
bool allow_output;
|
||||
bool initialized;
|
||||
std::chrono::seconds timeout{1}; // Default 1 second timeout
|
||||
|
||||
WebReplContext() : allow_output(false), initialized(false) {
|
||||
repl_instance = std::make_unique<nasal::repl::repl>();
|
||||
}
|
||||
};
|
||||
|
||||
void* nasal_init() {
|
||||
return new NasalContext();
|
||||
}
|
||||
|
||||
void nasal_cleanup(void* context) {
|
||||
auto* ctx = static_cast<NasalContext*>(context);
|
||||
ctx->vm_instance.reset();
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
// Add new function to set timeout
|
||||
void nasal_set_timeout(void* context, int seconds) {
|
||||
auto* ctx = static_cast<NasalContext*>(context);
|
||||
ctx->timeout = std::chrono::seconds(seconds);
|
||||
}
|
||||
|
||||
const char* nasal_eval(void* context, const char* code, int show_time) {
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
const auto den = clk::duration::period::den;
|
||||
|
||||
auto* ctx = static_cast<NasalContext*>(context);
|
||||
|
||||
try {
|
||||
nasal::lexer lex;
|
||||
nasal::parse parse;
|
||||
nasal::linker ld;
|
||||
nasal::codegen gen;
|
||||
|
||||
// Create a unique temporary file
|
||||
char temp_filename[256];
|
||||
snprintf(temp_filename, sizeof(temp_filename), "/tmp/nasal_eval_%ld_XXXXXX", std::time(nullptr));
|
||||
int fd = mkstemp(temp_filename);
|
||||
if (fd == -1) {
|
||||
throw std::runtime_error("Failed to create temporary file");
|
||||
}
|
||||
|
||||
// Write the code to the temporary file
|
||||
std::ofstream temp_file(temp_filename);
|
||||
if (!temp_file.is_open()) {
|
||||
close(fd);
|
||||
throw std::runtime_error("Failed to open temporary file for writing");
|
||||
}
|
||||
temp_file << code;
|
||||
temp_file.close();
|
||||
close(fd);
|
||||
|
||||
// Capture stdout and stderr
|
||||
std::stringstream output;
|
||||
std::stringstream error_output;
|
||||
auto old_cout = std::cout.rdbuf(output.rdbuf());
|
||||
auto old_cerr = std::cerr.rdbuf(error_output.rdbuf());
|
||||
|
||||
// Process the code
|
||||
if (lex.scan(std::string(temp_filename)).geterr()) {
|
||||
ctx->last_error = error_output.str();
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
std::remove(temp_filename);
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
if (parse.compile(lex).geterr()) {
|
||||
ctx->last_error = error_output.str();
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
std::remove(temp_filename);
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
if (ld.link(parse, false).geterr()) {
|
||||
ctx->last_error = error_output.str();
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
std::remove(temp_filename);
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
auto opt = std::make_unique<nasal::optimizer>();
|
||||
opt->do_optimization(parse.tree());
|
||||
|
||||
if (gen.compile(parse, ld, false, true).geterr()) {
|
||||
ctx->last_error = error_output.str();
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
std::remove(temp_filename);
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
const auto start = show_time ? clk::now() : clk::time_point();
|
||||
|
||||
// Create a future for the VM execution
|
||||
auto future = std::async(std::launch::async, [&]() {
|
||||
// Wrap VM execution in try/catch
|
||||
try {
|
||||
ctx->vm_instance->run(gen, ld, {});
|
||||
} catch (const std::exception& e) {
|
||||
ctx->last_error = e.what();
|
||||
throw std::runtime_error(ctx->last_error);
|
||||
} catch (...) {
|
||||
ctx->last_error = "Unknown error in VM run()";
|
||||
throw std::runtime_error(ctx->last_error);
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for completion or timeout
|
||||
auto status = future.wait_for(ctx->timeout);
|
||||
if (status == std::future_status::timeout) {
|
||||
ctx->interrupted.store(true);
|
||||
std::remove(temp_filename);
|
||||
throw std::runtime_error("Execution timed out after " +
|
||||
std::to_string(ctx->timeout.count()) + " seconds");
|
||||
}
|
||||
|
||||
const auto end = show_time ? clk::now() : clk::time_point();
|
||||
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
|
||||
std::stringstream result;
|
||||
result << output.str();
|
||||
if (!error_output.str().empty()) {
|
||||
result << error_output.str();
|
||||
}
|
||||
if (result.str().empty()) {
|
||||
result << "Execution completed successfully.\n";
|
||||
}
|
||||
|
||||
if (show_time) {
|
||||
double execution_time = static_cast<double>((end-start).count())/den;
|
||||
result << "\nExecution time: " << execution_time << "s";
|
||||
}
|
||||
|
||||
ctx->last_result = result.str();
|
||||
std::remove(temp_filename);
|
||||
|
||||
return ctx->last_result.c_str();
|
||||
} catch (const std::exception& e) {
|
||||
ctx->last_error = e.what();
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
const char* nasal_get_error(void* context) {
|
||||
auto* ctx = static_cast<NasalContext*>(context);
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
void* nasal_repl_init() {
|
||||
auto* ctx = new WebReplContext();
|
||||
|
||||
try {
|
||||
// Initialize environment silently
|
||||
nasal::repl::info::instance()->in_repl_mode = true;
|
||||
ctx->repl_instance->get_runtime().set_repl_mode_flag(true);
|
||||
ctx->repl_instance->get_runtime().set_detail_report_info(false);
|
||||
|
||||
// Run initial setup
|
||||
ctx->repl_instance->set_source({});
|
||||
if (!ctx->repl_instance->run()) {
|
||||
ctx->last_error = "Failed to initialize REPL environment";
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// Enable output after initialization
|
||||
ctx->allow_output = true;
|
||||
ctx->repl_instance->get_runtime().set_allow_repl_output_flag(true);
|
||||
ctx->initialized = true;
|
||||
} catch (const std::exception& e) {
|
||||
ctx->last_error = std::string("Initialization error: ") + e.what();
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void nasal_repl_cleanup(void* context) {
|
||||
delete static_cast<WebReplContext*>(context);
|
||||
}
|
||||
|
||||
// Add new function to set REPL timeout
|
||||
void nasal_repl_set_timeout(void* context, int seconds) {
|
||||
auto* ctx = static_cast<WebReplContext*>(context);
|
||||
ctx->timeout = std::chrono::seconds(seconds);
|
||||
}
|
||||
|
||||
const char* nasal_repl_eval(void* context, const char* line) {
|
||||
auto* ctx = static_cast<WebReplContext*>(context);
|
||||
|
||||
if (!ctx->initialized) {
|
||||
ctx->last_error = "REPL not properly initialized";
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
try {
|
||||
std::string input_line(line);
|
||||
|
||||
// Handle empty input
|
||||
if (input_line.empty()) {
|
||||
ctx->last_result = "";
|
||||
return ctx->last_result.c_str();
|
||||
}
|
||||
|
||||
// Handle REPL commands
|
||||
if (input_line[0] == '.') {
|
||||
if (input_line == ".help" || input_line == ".h") {
|
||||
ctx->last_result =
|
||||
"Nasal REPL commands:\n"
|
||||
" .help .h show this help message\n"
|
||||
" .clear .c clear screen\n"
|
||||
" .exit .e exit repl\n"
|
||||
" .quit .q exit repl\n"
|
||||
" .source .s show source\n";
|
||||
return ctx->last_result.c_str();
|
||||
}
|
||||
else if (input_line == ".clear" || input_line == ".c") {
|
||||
ctx->last_result = "\033c"; // Special marker for clear screen
|
||||
return ctx->last_result.c_str();
|
||||
}
|
||||
else if (input_line == ".exit" || input_line == ".e" ||
|
||||
input_line == ".quit" || input_line == ".q") {
|
||||
ctx->last_result = "__EXIT__"; // Special marker for exit
|
||||
return ctx->last_result.c_str();
|
||||
}
|
||||
else if (input_line == ".source" || input_line == ".s") {
|
||||
// Return accumulated source
|
||||
ctx->last_result = ctx->source.empty() ?
|
||||
"(no source)" :
|
||||
join_string(ctx->source, "\n");
|
||||
return ctx->last_result.c_str();
|
||||
}
|
||||
else {
|
||||
ctx->last_error = "no such command \"" + input_line + "\", input \".help\" for help";
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
// Add the line to source
|
||||
ctx->source.push_back(input_line);
|
||||
|
||||
// Capture output
|
||||
std::stringstream output;
|
||||
auto old_cout = std::cout.rdbuf(output.rdbuf());
|
||||
auto old_cerr = std::cerr.rdbuf(output.rdbuf());
|
||||
|
||||
// Create a copy of the source for the async task
|
||||
auto source_copy = ctx->source;
|
||||
|
||||
// Create a future for the REPL execution using the existing instance
|
||||
auto future = std::async(std::launch::async, [repl = ctx->repl_instance.get(), source_copy]() {
|
||||
repl->get_runtime().set_repl_mode_flag(true);
|
||||
repl->get_runtime().set_allow_repl_output_flag(true);
|
||||
repl->set_source(source_copy);
|
||||
return repl->run();
|
||||
});
|
||||
|
||||
// Wait for completion or timeout
|
||||
auto status = future.wait_for(ctx->timeout);
|
||||
|
||||
// Restore output streams first
|
||||
std::cout.rdbuf(old_cout);
|
||||
std::cerr.rdbuf(old_cerr);
|
||||
|
||||
if (status == std::future_status::timeout) {
|
||||
ctx->source.pop_back(); // Remove the line that caused timeout
|
||||
|
||||
// Reset the REPL instance state
|
||||
ctx->repl_instance->get_runtime().set_repl_mode_flag(true);
|
||||
ctx->repl_instance->get_runtime().set_allow_repl_output_flag(true);
|
||||
ctx->repl_instance->set_source(ctx->source);
|
||||
|
||||
throw std::runtime_error("Execution timed out after " +
|
||||
std::to_string(ctx->timeout.count()) + " seconds");
|
||||
}
|
||||
|
||||
bool success = future.get();
|
||||
std::string result = output.str();
|
||||
|
||||
if (!success) {
|
||||
ctx->last_error = result;
|
||||
ctx->source.pop_back(); // Remove failed line
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
|
||||
ctx->last_result = result;
|
||||
return ctx->last_result.c_str();
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
ctx->last_error = std::string("Error: ") + e.what();
|
||||
ctx->source.pop_back(); // Remove failed line
|
||||
return ctx->last_error.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
int nasal_repl_is_complete(void* context, const char* line) {
|
||||
auto* ctx = static_cast<WebReplContext*>(context);
|
||||
|
||||
if (!ctx->initialized) {
|
||||
return -1; // Error state
|
||||
}
|
||||
|
||||
// Handle empty input
|
||||
if (!line || strlen(line) == 0) {
|
||||
return 0; // Complete
|
||||
}
|
||||
|
||||
// Handle REPL commands
|
||||
if (line[0] == '.') {
|
||||
return 0; // Commands are always complete
|
||||
}
|
||||
|
||||
// Create a temporary source vector with existing source plus new line
|
||||
std::vector<std::string> temp_source = ctx->source;
|
||||
temp_source.push_back(line);
|
||||
|
||||
// Use the REPL's check_need_more_input method
|
||||
int result = ctx->repl_instance->check_need_more_input(temp_source);
|
||||
return result; // Ensure a return value is provided
|
||||
}
|
||||
|
||||
// Add this function to expose version info
|
||||
const char* nasal_repl_get_version() {
|
||||
static std::string version_info =
|
||||
std::string("version ") + __nasver__ +
|
||||
" (" + __DATE__ + " " + __TIME__ + ")";
|
||||
return version_info.c_str();
|
||||
}
|
||||
29
src/nasal_web.h
Normal file
29
src/nasal_web.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef __NASAL_WEB_H__
|
||||
#define __NASAL_WEB_H__
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Main API functions
|
||||
NASAL_EXPORT void* nasal_init();
|
||||
NASAL_EXPORT void nasal_cleanup(void* context);
|
||||
NASAL_EXPORT void nasal_set_timeout(void* context, int seconds);
|
||||
NASAL_EXPORT const char* nasal_eval(void* context, const char* code, int show_time);
|
||||
NASAL_EXPORT const char* nasal_get_error(void* context);
|
||||
|
||||
// REPL
|
||||
NASAL_EXPORT void* nasal_repl_init();
|
||||
NASAL_EXPORT void nasal_repl_cleanup(void* repl_context);
|
||||
NASAL_EXPORT void nasal_repl_set_timeout(void* repl_context, int seconds);
|
||||
NASAL_EXPORT const char* nasal_repl_eval(void* repl_context, const char* line);
|
||||
NASAL_EXPORT int nasal_repl_is_complete(void* repl_context, const char* line);
|
||||
NASAL_EXPORT const char* nasal_repl_get_version();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -61,7 +61,7 @@ var builtin_fld(context* ctx, gc* ngc) {
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
for (u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
@@ -91,7 +91,7 @@ var builtin_sfld(context* ctx, gc* ngc) {
|
||||
}
|
||||
u32 res = 0;
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
for (u32 i = bit; i<bit+len; ++i) {
|
||||
if (s[i>>3]&(1<<(7-(i&7)))) {
|
||||
res |= 1<<(bit+len-i-1);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ var builtin_setfld(context* ctx, gc* ngc) {
|
||||
return nas_err("bits::setfld", "bitfield out of bounds");
|
||||
}
|
||||
auto& s = str.str();
|
||||
for(u32 i = bit; i<bit+len; ++i) {
|
||||
for (u32 i = bit; i<bit+len; ++i) {
|
||||
if (val&(1<<(i-bit))) {
|
||||
s[i>>3] |= (1<<(7-(i&7)));
|
||||
} else {
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
@@ -17,7 +20,7 @@ var builtin_unsafe(context* ctx, gc* ngc) {
|
||||
}
|
||||
|
||||
var builtin_print(context* ctx, gc* ngc) {
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
for (auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::flush;
|
||||
@@ -25,7 +28,7 @@ var builtin_print(context* ctx, gc* ngc) {
|
||||
}
|
||||
|
||||
var builtin_println(context* ctx, gc* ngc) {
|
||||
for(auto& i : ctx->localr[1].vec().elems) {
|
||||
for (auto& i : ctx->localr[1].vec().elems) {
|
||||
std::cout << i;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
@@ -50,7 +53,7 @@ var builtin_append(context* ctx, gc* ngc) {
|
||||
return nas_err("native::append", "\"vec\" must be vector");
|
||||
}
|
||||
auto& v = vec.vec().elems;
|
||||
for(auto& i : elem.vec().elems) {
|
||||
for (auto& i : elem.vec().elems) {
|
||||
v.push_back(i);
|
||||
}
|
||||
return nil;
|
||||
@@ -92,36 +95,38 @@ var builtin_input(context* ctx, gc* ngc) {
|
||||
|
||||
var builtin_split(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var delimeter = local[1];
|
||||
var separator = local[1];
|
||||
var str = local[2];
|
||||
if (!delimeter.is_str()) {
|
||||
if (!separator.is_str()) {
|
||||
return nas_err("native::split", "\"separator\" must be string");
|
||||
}
|
||||
if (!str.is_str()) {
|
||||
return nas_err("native::split", "\"str\" must be string");
|
||||
}
|
||||
const auto& deli = delimeter.str();
|
||||
const auto& sep = separator.str();
|
||||
const auto& s = str.str();
|
||||
|
||||
// avoid being sweeped
|
||||
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
|
||||
if (!deli.length()) {
|
||||
for(auto i : s) {
|
||||
// empty separator means split every char
|
||||
if (!sep.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) {
|
||||
usize pos = s.find(sep, 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);
|
||||
last = pos + sep.length();
|
||||
pos = s.find(sep, last);
|
||||
}
|
||||
if (last!=s.length()) {
|
||||
vec.push_back(ngc->newstr(s.substr(last)));
|
||||
@@ -130,6 +135,54 @@ var builtin_split(context* ctx, gc* ngc) {
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_split_with_empty_substr(context* ctx, gc* ngc) {
|
||||
auto local = ctx->localr;
|
||||
var separator = local[1];
|
||||
var str = local[2];
|
||||
if (!separator.is_str()) {
|
||||
return nas_err(
|
||||
"native::split_with_empty_substr",
|
||||
"\"separator\" must be string"
|
||||
);
|
||||
}
|
||||
if (!str.is_str()) {
|
||||
return nas_err(
|
||||
"native::split_with_empty_substr",
|
||||
"\"str\" must be string"
|
||||
);
|
||||
}
|
||||
const auto& sep = separator.str();
|
||||
const auto& s = str.str();
|
||||
|
||||
// avoid being sweeped
|
||||
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
|
||||
// empty separator means split every char
|
||||
if (!sep.length()) {
|
||||
for (auto i : s) {
|
||||
vec.push_back(ngc->newstr(i));
|
||||
}
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
usize last = 0;
|
||||
usize pos = s.find(sep, 0);
|
||||
while (pos!=std::string::npos) {
|
||||
if (pos>=last) {
|
||||
vec.push_back(ngc->newstr(s.substr(last, pos-last)));
|
||||
}
|
||||
last = pos + sep.length();
|
||||
pos = s.find(sep, last);
|
||||
}
|
||||
if (last<=s.length()) {
|
||||
vec.push_back(ngc->newstr(s.substr(last)));
|
||||
}
|
||||
ngc->temp = nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var builtin_rand(context* ctx, gc* ngc) {
|
||||
auto val = ctx->localr[1];
|
||||
if (!val.is_num() && !val.is_nil()) {
|
||||
@@ -140,7 +193,7 @@ var builtin_rand(context* ctx, gc* ngc) {
|
||||
return nil;
|
||||
}
|
||||
f64 num = 0;
|
||||
for(u32 i = 0; i<5; ++i) {
|
||||
for (u32 i = 0; i<5; ++i) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
return var::num(num);
|
||||
@@ -267,11 +320,11 @@ var builtin_keys(context* ctx, gc* ngc) {
|
||||
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
if (hash.is_hash()) {
|
||||
for(const auto& iter : hash.hash().elems) {
|
||||
for (const auto& iter : hash.hash().elems) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
} else {
|
||||
for(const auto& iter : hash.map().mapper) {
|
||||
for (const auto& iter : hash.map().mapper) {
|
||||
vec.push_back(ngc->newstr(iter.first));
|
||||
}
|
||||
}
|
||||
@@ -327,8 +380,11 @@ var builtin_substr(context* ctx, gc* ngc) {
|
||||
}
|
||||
auto begin = static_cast<usize>(beg.num());
|
||||
auto length = static_cast<usize>(len.num());
|
||||
if (begin>=str.str().length()) {
|
||||
return nas_err("native::susbtr", "begin index out of range: "+std::to_string(begin));
|
||||
if (begin >= str.str().length()) {
|
||||
return nas_err(
|
||||
"native::susbtr",
|
||||
"begin index out of range: " + std::to_string(begin)
|
||||
);
|
||||
}
|
||||
return ngc->newstr(str.str().substr(begin, length));
|
||||
}
|
||||
@@ -353,7 +409,7 @@ var builtin_left(context* ctx, gc* ngc) {
|
||||
if (!len.is_num()) {
|
||||
return nas_err("native::left", "\"length\" must be number");
|
||||
}
|
||||
if (len.num()<0) {
|
||||
if (len.num() < 0) {
|
||||
return ngc->newstr("");
|
||||
}
|
||||
return ngc->newstr(str.str().substr(0, len.num()));
|
||||
@@ -373,14 +429,14 @@ var builtin_right(context* ctx, gc* ngc) {
|
||||
|
||||
i32 length = static_cast<i32>(len.num());
|
||||
i32 srclen = static_cast<i32>(str.str().length());
|
||||
if (length>srclen) {
|
||||
if (length > srclen) {
|
||||
length = srclen;
|
||||
}
|
||||
if (length<0) {
|
||||
if (length < 0) {
|
||||
length = 0;
|
||||
}
|
||||
|
||||
return ngc->newstr(str.str().substr(srclen-length, srclen));
|
||||
return ngc->newstr(str.str().substr(srclen - length, srclen));
|
||||
}
|
||||
|
||||
var builtin_cmp(context* ctx, gc* ngc) {
|
||||
@@ -436,11 +492,11 @@ var builtin_values(context* ctx, gc* ngc) {
|
||||
auto vec = ngc->alloc(vm_type::vm_vec);
|
||||
auto& v = vec.vec().elems;
|
||||
if (hash.is_hash()) {
|
||||
for(auto& i : hash.hash().elems) {
|
||||
for (auto& i : hash.hash().elems) {
|
||||
v.push_back(i.second);
|
||||
}
|
||||
} else {
|
||||
for(auto& i : hash.map().mapper) {
|
||||
for (auto& i : hash.map().mapper) {
|
||||
v.push_back(*i.second);
|
||||
}
|
||||
}
|
||||
@@ -457,7 +513,7 @@ var builtin_sleep(context* ctx, gc* ngc) {
|
||||
// also msvc will use this
|
||||
Sleep(static_cast<i64>(val.num()*1e3));
|
||||
#else
|
||||
std::this_thread::sleep_for(
|
||||
std::this_thread::sleep_for (
|
||||
std::chrono::microseconds(static_cast<i64>(val.num()*1e6))
|
||||
);
|
||||
#endif
|
||||
@@ -468,6 +524,10 @@ var builtin_platform(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(util::get_platform());
|
||||
}
|
||||
|
||||
var builtin_version(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(__nasver__);
|
||||
}
|
||||
|
||||
var builtin_arch(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(util::get_arch());
|
||||
}
|
||||
@@ -476,9 +536,9 @@ var builtin_arch(context* ctx, gc* ngc) {
|
||||
std::string tohex(u32 num) {
|
||||
const char str16[] = "0123456789abcdef";
|
||||
std::string str = "";
|
||||
for(u32 i = 0; i<4; i++, num >>= 8) {
|
||||
for (u32 i = 0; i<4; i++, num >>= 8) {
|
||||
std::string tmp = "";
|
||||
for(u32 j = 0, b = num&0xff; j<2; j++, b >>= 4) {
|
||||
for (u32 j = 0, b = num&0xff; j<2; j++, b >>= 4) {
|
||||
tmp.insert(0, 1, str16[b&0xf]);
|
||||
}
|
||||
str += tmp;
|
||||
@@ -491,7 +551,7 @@ std::string md5(const std::string& src) {
|
||||
usize num = ((src.length()+8)>>6)+1;
|
||||
usize buffsize = num<<4;
|
||||
buff.resize(buffsize, 0);
|
||||
for(usize i = 0; i<src.length(); i++) {
|
||||
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);
|
||||
@@ -542,9 +602,9 @@ std::string md5(const std::string& src) {
|
||||
|
||||
u32 atmp = 0x67452301, btmp = 0xefcdab89;
|
||||
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
|
||||
for(u32 i = 0; i<buffsize; i += 16) {
|
||||
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++) {
|
||||
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);
|
||||
@@ -615,7 +675,7 @@ var builtin_time_stamp(context* ctx, gc* ngc) {
|
||||
if (!object.object_check("nasal-time-stamp")) {
|
||||
return nil;
|
||||
}
|
||||
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
|
||||
auto stamp = object.ghost().get<time_stamp>();
|
||||
stamp->make_stamp();
|
||||
return nil;
|
||||
}
|
||||
@@ -625,7 +685,7 @@ var builtin_elapsed_millisecond(context* ctx, gc* ngc) {
|
||||
if (!object.object_check("nasal-time-stamp")) {
|
||||
return var::num(-1);
|
||||
}
|
||||
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
|
||||
auto stamp = object.ghost().get<time_stamp>();
|
||||
return var::num(static_cast<f64>(stamp->elapsed_milliseconds()));
|
||||
}
|
||||
|
||||
@@ -634,7 +694,7 @@ var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
|
||||
if (!object.object_check("nasal-time-stamp")) {
|
||||
return var::num(-1);
|
||||
}
|
||||
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
|
||||
auto stamp = object.ghost().get<time_stamp>();
|
||||
return var::num(static_cast<f64>(stamp->elapsed_microseconds()));
|
||||
}
|
||||
|
||||
@@ -663,27 +723,17 @@ var builtin_gcextend(context* ctx, gc* ngc) {
|
||||
}
|
||||
|
||||
var builtin_gcinfo(context* ctx, gc* ngc) {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
var res = ngc->alloc(vm_type::vm_hash);
|
||||
|
||||
f64 total = 0;
|
||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||
total += static_cast<f64>(ngc->gcnt[i]);
|
||||
}
|
||||
|
||||
|
||||
auto& map = res.hash().elems;
|
||||
const auto worktime = static_cast<f64>(ngc->worktime);
|
||||
const auto max_time = static_cast<f64>(ngc->max_time);
|
||||
const auto max_mark_time = static_cast<f64>(ngc->max_mark_time);
|
||||
const auto max_sweep_time = static_cast<f64>(ngc->max_sweep_time);
|
||||
|
||||
// using ms
|
||||
map["total"] = var::num(worktime/den*1000);
|
||||
map["average"] = var::num(worktime/den*1000/total);
|
||||
map["max_gc"] = var::num(max_time/den*1000);
|
||||
map["max_mark"] = var::num(max_mark_time/den*1000);
|
||||
map["max_sweep"] = var::num(max_sweep_time/den*1000);
|
||||
map["total"] = var::num(ngc->status.gc_time_ms());
|
||||
map["average"] = var::num(ngc->status.avg_time_ms());
|
||||
map["mark_count"] = var::num(ngc->status.total_mark_count);
|
||||
map["sweep_count"] = var::num(ngc->status.total_sweep_count);
|
||||
map["avg_mark"] = var::num(ngc->status.avg_mark_time_ms());
|
||||
map["avg_sweep"] = var::num(ngc->status.avg_sweep_time_ms());
|
||||
map["max_mark"] = var::num(ngc->status.max_mark_time_ms());
|
||||
map["max_sweep"] = var::num(ngc->status.max_sweep_time_ms());
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -717,7 +767,7 @@ var builtin_ghosttype(context* ctx, gc* ngc) {
|
||||
if (!name.length()) {
|
||||
std::stringstream ss;
|
||||
ss << "0x" << std::hex;
|
||||
ss << reinterpret_cast<u64>(arg.ghost().pointer) << std::dec;
|
||||
ss << arg.ghost().convert<u64>() << std::dec;
|
||||
return ngc->newstr(ss.str());
|
||||
}
|
||||
return ngc->newstr(name);
|
||||
@@ -732,6 +782,25 @@ var builtin_set_utf8_output(context* ctx, gc* ngc) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_terminal_size(context* ctx, gc* ngc) {
|
||||
var res = ngc->alloc(vm_type::vm_hash);
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
|
||||
auto rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
auto cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
res.hash().elems["rows"] = var::num(rows);
|
||||
res.hash().elems["cols"] = var::num(cols);
|
||||
}
|
||||
#else
|
||||
struct winsize w;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
|
||||
res.hash().elems["rows"] = var::num(w.ws_row);
|
||||
res.hash().elems["cols"] = var::num(w.ws_col);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
nasal_builtin_table builtin[] = {
|
||||
{"__print", builtin_print},
|
||||
{"__println", builtin_println},
|
||||
@@ -742,6 +811,7 @@ nasal_builtin_table builtin[] = {
|
||||
{"__system", builtin_system},
|
||||
{"__input", builtin_input},
|
||||
{"__split", builtin_split},
|
||||
{"__split_with_empty_substr", builtin_split_with_empty_substr},
|
||||
{"__rand", builtin_rand},
|
||||
{"__id", builtin_id},
|
||||
{"__int", builtin_int},
|
||||
@@ -769,6 +839,7 @@ nasal_builtin_table builtin[] = {
|
||||
{"__sleep", builtin_sleep},
|
||||
{"__platform", builtin_platform},
|
||||
{"__arch", builtin_arch},
|
||||
{"__version", builtin_version},
|
||||
{"__md5", builtin_md5},
|
||||
{"__maketimestamp", builtin_maketimestamp},
|
||||
{"__time_stamp", builtin_time_stamp},
|
||||
@@ -779,6 +850,7 @@ nasal_builtin_table builtin[] = {
|
||||
{"__logtime", builtin_logtime},
|
||||
{"__ghosttype", builtin_ghosttype},
|
||||
{"__set_utf8_output", builtin_set_utf8_output},
|
||||
{"__terminal_size", builtin_terminal_size},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ var builtin_setsize(context*, gc*);
|
||||
var builtin_system(context*, gc*);
|
||||
var builtin_input(context*, gc*);
|
||||
var builtin_split(context*, gc*);
|
||||
var builtin_split_with_empty_substr(context*, gc*);
|
||||
var builtin_rand(context*, gc*);
|
||||
var builtin_id(context*, gc*);
|
||||
var builtin_int(context*, gc*);
|
||||
@@ -66,6 +67,7 @@ var builtin_values(context*, gc*);
|
||||
var builtin_sleep(context*, gc*);
|
||||
var builtin_platform(context*, gc*);
|
||||
var builtin_arch(context*, gc*);
|
||||
var builtin_version(context*, gc*);
|
||||
|
||||
// md5 related functions
|
||||
std::string tohex(u32);
|
||||
@@ -83,6 +85,7 @@ var builtin_ghosttype(context*, gc*);
|
||||
|
||||
// only useful on windows platform
|
||||
var builtin_set_utf8_output(context*, gc*);
|
||||
var builtin_terminal_size(context*, gc*);
|
||||
|
||||
// register builtin function's name and it's address here in this table below
|
||||
// this table must end with {nullptr, nullptr}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#include "natives/dylib_lib.h"
|
||||
#include "util/util.h"
|
||||
#include "util/fs.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto dynamic_library_type_name = "dylib";
|
||||
const auto function_address_type_name = "faddr";
|
||||
const auto dynamic_library_type_name = "nasal::dynamic_library";
|
||||
const auto function_address_type_name = "nasal::function_address";
|
||||
|
||||
void dynamic_library_destructor(void* pointer) {
|
||||
#ifdef _WIN32
|
||||
@@ -13,33 +18,103 @@ void dynamic_library_destructor(void* pointer) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::string> possible_dylib_path() {
|
||||
const auto env_path = std::string(getenv("PATH"));
|
||||
const auto sep = (util::is_windows()? ";":":");
|
||||
const auto path_front = util::is_windows()? "\\module\\":"/module/";
|
||||
|
||||
// do split string
|
||||
std::vector<std::string> env_path_vec = {"."};
|
||||
usize last = 0;
|
||||
usize pos = env_path.find(sep, 0);
|
||||
while (pos != std::string::npos) {
|
||||
if (pos > last) {
|
||||
env_path_vec.push_back(env_path.substr(last, pos - last));
|
||||
}
|
||||
last = pos + 1;
|
||||
pos = env_path.find(sep, last);
|
||||
}
|
||||
if (last != env_path.length()) {
|
||||
env_path_vec.push_back(env_path.substr(last));
|
||||
}
|
||||
|
||||
for (auto& p : env_path_vec) {
|
||||
p += path_front;
|
||||
}
|
||||
|
||||
return env_path_vec;
|
||||
}
|
||||
|
||||
std::string search_dynamic_library_path(const std::string& dlname) {
|
||||
const auto ext = (util::is_windows()? ".dll":".so");
|
||||
const auto lib_path = (util::is_windows()? ".\\":"./") + dlname + ext;
|
||||
if (fs::exists(lib_path)) {
|
||||
return lib_path;
|
||||
}
|
||||
// macos may use .dylib as extension
|
||||
if (util::is_macos()) {
|
||||
const auto dylib_path = "./" + dlname + ".dylib";
|
||||
if (fs::exists(dylib_path)) {
|
||||
return dylib_path;
|
||||
}
|
||||
}
|
||||
|
||||
// search library in PATH
|
||||
const auto possible_path = possible_dylib_path();
|
||||
for (const auto& p : possible_path) {
|
||||
const auto env_p = p + lib_path;
|
||||
if (fs::exists(env_p)) {
|
||||
return env_p;
|
||||
}
|
||||
}
|
||||
|
||||
// macos may use .dylib as extension
|
||||
if (util::is_macos()) {
|
||||
const auto dylib_path = "./" + dlname + ".dylib";
|
||||
for (const auto& p : possible_path) {
|
||||
const auto env_p = p + dylib_path;
|
||||
if (fs::exists(env_p)) {
|
||||
return env_p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
var builtin_dlopen(context* ctx, gc* ngc) {
|
||||
auto dlname = ctx->localr[1];
|
||||
if (!dlname.is_str()) {
|
||||
auto dl = ctx->localr[1];
|
||||
if (!dl.is_str()) {
|
||||
return nas_err("dylib::dlopen", "\"libname\" must be string");
|
||||
}
|
||||
|
||||
const auto dlname = search_dynamic_library_path(dl.str());
|
||||
if (dlname.empty()) {
|
||||
return nas_err("dylib::dlopen",
|
||||
"cannot find dynamic library <" + dl.str() + ">"
|
||||
);
|
||||
}
|
||||
|
||||
// get library pointer
|
||||
#ifdef _WIN32
|
||||
wchar_t* wide_string = new wchar_t[dlname.str().size()+1];
|
||||
wchar_t* wide_string = new wchar_t[dlname.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);
|
||||
memset(wide_string, 0, sizeof(wchar_t) * dlname.size() + 1);
|
||||
mbstowcs(wide_string, dlname.c_str(), dlname.size() + 1);
|
||||
// load library by using wide string name
|
||||
void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str());
|
||||
void* dynamic_library_pointer = LoadLibraryA(dlname.c_str());
|
||||
delete []wide_string;
|
||||
#else
|
||||
void* dynamic_library_pointer = dlopen(
|
||||
dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY
|
||||
dlname.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() + ">"
|
||||
"cannot open dynamic lib <" + dl.str() + ">"
|
||||
);
|
||||
}
|
||||
auto return_hash = ngc->temp = ngc->alloc(vm_type::vm_hash);
|
||||
@@ -55,7 +130,7 @@ var builtin_dlopen(context* ctx, gc* ngc) {
|
||||
// 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"
|
||||
library_object.ghost().convert<HMODULE>(), "get"
|
||||
));
|
||||
#else
|
||||
void* register_table_get_function = dlsym(
|
||||
@@ -71,7 +146,7 @@ var builtin_dlopen(context* ctx, gc* ngc) {
|
||||
if (!table) {
|
||||
return nas_err("dylib::dlopen", "failed to get module functions");
|
||||
}
|
||||
for(u32 i = 0; table[i].name; ++i) {
|
||||
for (u32 i = 0; table[i].name; ++i) {
|
||||
auto function_pointer = reinterpret_cast<void*>(table[i].fd);
|
||||
auto function_object = ngc->alloc(vm_type::vm_ghost);
|
||||
function_object.ghost().set(
|
||||
@@ -105,7 +180,7 @@ var builtin_dlcallv(context* ctx, gc* ngc) {
|
||||
);
|
||||
}
|
||||
auto& vec = arguments.vec().elems;
|
||||
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
|
||||
return function_object.ghost().convert<module_func>()(
|
||||
vec.data(),
|
||||
vec.size(),
|
||||
ngc
|
||||
@@ -124,7 +199,7 @@ var builtin_dlcall(context* ctx, gc* ngc) {
|
||||
// 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)(
|
||||
return function_object.ghost().convert<module_func>()(
|
||||
local_frame_start,
|
||||
local_frame_size,
|
||||
ngc
|
||||
|
||||
@@ -11,10 +11,17 @@
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void dynamic_library_destructor(void*);
|
||||
|
||||
std::vector<std::string> possible_dylib_path();
|
||||
std::string search_dynamic_library_path(const std::string&);
|
||||
|
||||
var builtin_dlopen(context*, gc*);
|
||||
var builtin_dlclose(context*, gc*);
|
||||
var builtin_dlcallv(context*, gc*);
|
||||
|
||||
@@ -26,7 +26,7 @@ var builtin_logprint(context* ctx, gc* ngc) {
|
||||
"incorrect log level " + std::to_string(level.num())
|
||||
);
|
||||
}
|
||||
for(auto& value : elems.vec().elems) {
|
||||
for (auto& value : elems.vec().elems) {
|
||||
out << value << " ";
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto file_type_name = "file";
|
||||
const auto file_type_name = "nasal::FILE";
|
||||
|
||||
void filehandle_destructor(void* ptr) {
|
||||
fclose(static_cast<FILE*>(ptr));
|
||||
@@ -59,8 +59,9 @@ var builtin_open(context* ctx, gc* ngc) {
|
||||
return nas_err("io::open", "\"mode\" must be string");
|
||||
}
|
||||
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
|
||||
// if failed to open, just return nil for check
|
||||
if (!file_descriptor) {
|
||||
return nas_err("io::open", "failed to open file <" + name.str() + ">");
|
||||
return nil;
|
||||
}
|
||||
var return_object = ngc->alloc(vm_type::vm_ghost);
|
||||
return_object.ghost().set(
|
||||
@@ -101,7 +102,7 @@ var builtin_read(context* ctx, gc* ngc) {
|
||||
}
|
||||
auto read_size = fread(
|
||||
temp_buffer, 1, length.num(),
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||
file_descriptor.ghost().get<FILE>()
|
||||
);
|
||||
buffer.str() = temp_buffer;
|
||||
buffer.val.gcobj->immutable = true;
|
||||
@@ -121,7 +122,7 @@ var builtin_write(context* ctx, gc* ngc) {
|
||||
}
|
||||
return var::num(static_cast<f64>(fwrite(
|
||||
source.str().c_str(), 1, source.str().length(),
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||
file_descriptor.ghost().get<FILE>()
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ var builtin_seek(context* ctx, gc* ngc) {
|
||||
return nas_err("io::seek", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(fseek(
|
||||
static_cast<FILE*>(file_descriptor.ghost().pointer),
|
||||
file_descriptor.ghost().get<FILE>(),
|
||||
position.num(),
|
||||
whence.num()
|
||||
)));
|
||||
@@ -146,7 +147,7 @@ var builtin_tell(context* ctx, gc* ngc) {
|
||||
return nas_err("io::tell", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(
|
||||
ftell(static_cast<FILE*>(file_descriptor.ghost().pointer))
|
||||
ftell(file_descriptor.ghost().get<FILE>())
|
||||
));
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ var builtin_readln(context* ctx, gc* ngc) {
|
||||
}
|
||||
auto result = ngc->alloc(vm_type::vm_str);
|
||||
char c;
|
||||
while((c = fgetc(static_cast<FILE*>(file_descriptor.ghost().pointer)))!=EOF) {
|
||||
while ((c = fgetc(file_descriptor.ghost().get<FILE>()))!=EOF) {
|
||||
if (c=='\r') {
|
||||
continue;
|
||||
}
|
||||
@@ -178,8 +179,9 @@ var builtin_stat(context* ctx, gc* ngc) {
|
||||
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() + ">");
|
||||
// if failed to stat, return nil
|
||||
if (stat(name.str().c_str(), &buffer) < 0) {
|
||||
return nil;
|
||||
}
|
||||
auto result = ngc->alloc(vm_type::vm_vec);
|
||||
result.vec().elems = {
|
||||
@@ -204,7 +206,7 @@ var builtin_eof(context* ctx, gc* ngc) {
|
||||
return nas_err("io::readln", "not a valid filehandle");
|
||||
}
|
||||
return var::num(static_cast<f64>(
|
||||
feof(static_cast<FILE*>(file_descriptor.ghost().pointer))
|
||||
feof(file_descriptor.ghost().get<FILE>())
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ std::string json::vector_generate(nas_vec& vect) {
|
||||
}
|
||||
vect.printed = true;
|
||||
std::string out = "[";
|
||||
for(auto& i : vect.elems) {
|
||||
for (auto& i : vect.elems) {
|
||||
out += var_generate(i) + ",";
|
||||
}
|
||||
if (out.back()==',') {
|
||||
@@ -142,7 +142,7 @@ std::string json::hash_generate(nas_hash& hash) {
|
||||
}
|
||||
hash.printed = true;
|
||||
std::string out = "{";
|
||||
for(auto& i : hash.elems) {
|
||||
for (auto& i : hash.elems) {
|
||||
out += "\"" + i.first + "\":";
|
||||
out += var_generate(i.second) + ",";
|
||||
}
|
||||
@@ -165,7 +165,7 @@ std::string json::stringify(var& object) {
|
||||
}
|
||||
|
||||
void json::next() {
|
||||
while(ptr<text.length() && !check(text[ptr])) {
|
||||
while (ptr<text.length() && !check(text[ptr])) {
|
||||
if (text[ptr]=='\n') {
|
||||
++line;
|
||||
} else if (text[ptr]!=' ' && text[ptr]!='\t' && text[ptr]!='\r') {
|
||||
@@ -193,7 +193,7 @@ void json::next() {
|
||||
if (is_num(c) || c=='-' || c=='+') {
|
||||
auto temp = std::string(1, c);
|
||||
++ptr;
|
||||
while(ptr<text.length() && (
|
||||
while (ptr<text.length() && (
|
||||
is_num(text[ptr]) ||
|
||||
text[ptr]=='.' ||
|
||||
text[ptr]=='e' ||
|
||||
@@ -207,7 +207,7 @@ void json::next() {
|
||||
} else if (is_id(c)) {
|
||||
auto temp = std::string(1, c);
|
||||
++ptr;
|
||||
while(ptr<text.length() && (is_id(text[ptr]) || is_num(text[ptr]))) {
|
||||
while (ptr<text.length() && (is_id(text[ptr]) || is_num(text[ptr]))) {
|
||||
temp += text[ptr];
|
||||
++ptr;
|
||||
}
|
||||
@@ -221,7 +221,7 @@ void json::next() {
|
||||
auto begin = c;
|
||||
auto temp = std::string("");
|
||||
++ptr;
|
||||
while(ptr<text.length() && text[ptr]!=begin) {
|
||||
while (ptr<text.length() && text[ptr]!=begin) {
|
||||
temp += text[ptr];
|
||||
++ptr;
|
||||
if (text[ptr-1]=='\\' && ptr<text.length()) {
|
||||
@@ -264,7 +264,7 @@ var json::vector_object_generate(gc* ngc) {
|
||||
temp_stack.vec().elems.push_back(vect_object);
|
||||
match(json_token_type::tok_lbrkt);
|
||||
vector_member(vect_object.vec(), ngc);
|
||||
while(this_token.type==json_token_type::tok_comma) {
|
||||
while (this_token.type==json_token_type::tok_comma) {
|
||||
match(json_token_type::tok_comma);
|
||||
vector_member(vect_object.vec(), ngc);
|
||||
}
|
||||
@@ -303,7 +303,7 @@ var json::hash_object_generate(gc* ngc) {
|
||||
temp_stack.vec().elems.push_back(hash_object);
|
||||
match(json_token_type::tok_lbrace);
|
||||
hash_member(hash_object.hash(), ngc);
|
||||
while(this_token.type==json_token_type::tok_comma) {
|
||||
while (this_token.type==json_token_type::tok_comma) {
|
||||
match(json_token_type::tok_comma);
|
||||
hash_member(hash_object.hash(), ngc);
|
||||
}
|
||||
@@ -369,7 +369,7 @@ var builtin_json_stringify(context* ctx, gc* ngc) {
|
||||
if (!json_object.object_check("nasal::json")) {
|
||||
return nas_err("json::stringify", "expect a json object.");
|
||||
}
|
||||
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
|
||||
auto json_ptr = json_object.ghost().get<json>();
|
||||
return ngc->newstr(json_ptr->stringify(object));
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ var builtin_json_parse(context* ctx, gc* ngc) {
|
||||
if (!input_string.is_str()) {
|
||||
return nas_err("json::parse", "require string as the input.");
|
||||
}
|
||||
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
|
||||
auto json_ptr = json_object.ghost().get<json>();
|
||||
return json_ptr->parse(input_string.str(), ngc);
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ var builtin_json_get_error(context* ctx, gc* ngc) {
|
||||
if (!json_object.object_check("nasal::json")) {
|
||||
return nas_err("json::get_error", "expect a json object.");
|
||||
}
|
||||
auto json_ptr = static_cast<json*>(json_object.ghost().pointer);
|
||||
auto json_ptr = json_object.ghost().get<json>();
|
||||
return ngc->newstr(json_ptr->get_error());
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_regex_match(context* ctx, gc* ngc) {
|
||||
var builtin_regex_match(context* ctx, gc* ngc) noexcept {
|
||||
auto source = ctx->localr[1];
|
||||
auto reg_str = ctx->localr[2];
|
||||
if (!source.is_str()) {
|
||||
@@ -20,7 +20,7 @@ var builtin_regex_match(context* ctx, gc* ngc) {
|
||||
return zero;
|
||||
}
|
||||
|
||||
var builtin_regex_search(context* ctx, gc* ngc) {
|
||||
var builtin_regex_search(context* ctx, gc* ngc) noexcept {
|
||||
auto source = ctx->localr[1];
|
||||
auto reg_str = ctx->localr[2];
|
||||
if (!source.is_str()) {
|
||||
@@ -38,7 +38,7 @@ var builtin_regex_search(context* ctx, gc* ngc) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
var builtin_regex_replace(context* ctx, gc* ngc) {
|
||||
var builtin_regex_replace(context* ctx, gc* ngc) noexcept {
|
||||
auto source = ctx->localr[1];
|
||||
auto reg_str = ctx->localr[2];
|
||||
auto fmt = ctx->localr[3];
|
||||
@@ -64,7 +64,7 @@ var builtin_regex_replace(context* ctx, gc* ngc) {
|
||||
return ngc->newstr(source.str());
|
||||
}
|
||||
|
||||
var builtin_regex_match_all(context* ctx, gc* ngc) {
|
||||
var builtin_regex_match_all(context* ctx, gc* ngc) noexcept {
|
||||
auto source = ctx->localr[1];
|
||||
auto reg_str = ctx->localr[2];
|
||||
if (!source.is_str()) {
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
namespace nasal {
|
||||
|
||||
var builtin_regex_match(context*, gc*);
|
||||
var builtin_regex_search(context*, gc*);
|
||||
var builtin_regex_replace(context*, gc*);
|
||||
var builtin_regex_match_all(context*, gc*);
|
||||
var builtin_regex_match(context*, gc*) noexcept;
|
||||
var builtin_regex_search(context*, gc*) noexcept;
|
||||
var builtin_regex_replace(context*, gc*) noexcept;
|
||||
var builtin_regex_match_all(context*, gc*) noexcept;
|
||||
|
||||
extern nasal_builtin_table regex_lib_native[];
|
||||
|
||||
|
||||
234
src/natives/subprocess.cpp
Normal file
234
src/natives/subprocess.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "natives/subprocess.h"
|
||||
|
||||
// if you ask why, i will say: only MSVC
|
||||
#ifdef _MSC_VER
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#define perror _perror
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cwchar>
|
||||
#include <clocale>
|
||||
#include <vector>
|
||||
#include <csignal>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void subprocess_desc_dtor(void* ptr) {
|
||||
#ifdef WIN32
|
||||
auto pi = &static_cast<subprocess*>(ptr)->pi;
|
||||
|
||||
WaitForSingleObject(pi->hProcess, 0);
|
||||
TerminateProcess(pi->hProcess, 0);
|
||||
CloseHandle(pi->hProcess);
|
||||
CloseHandle(pi->hThread);
|
||||
#else
|
||||
auto pid = static_cast<subprocess*>(ptr)->pid;
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, WNOHANG);
|
||||
|
||||
// child process running
|
||||
if (result==0) {
|
||||
kill(pid, SIGTERM);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_subprocess_create(context* ctx, gc* ngc) {
|
||||
auto cmd = ctx->localr[1];
|
||||
if (!cmd.is_vec()) {
|
||||
return nas_err("subprocess::create",
|
||||
"expect string vector as the command"
|
||||
);
|
||||
}
|
||||
|
||||
for (const auto& v : cmd.vec().elems) {
|
||||
if (!v.is_str()) {
|
||||
return nas_err("subprocess::create",
|
||||
"non-string argument found"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto obj = ngc->alloc(vm_type::vm_ghost);
|
||||
obj.ghost().set(
|
||||
subprocess::name(),
|
||||
subprocess_desc_dtor,
|
||||
nullptr,
|
||||
new subprocess
|
||||
);
|
||||
|
||||
#ifdef WIN32
|
||||
auto si = &obj.ghost().get<subprocess>()->si;
|
||||
auto pi = &obj.ghost().get<subprocess>()->pi;
|
||||
|
||||
// init STARTUPINFO
|
||||
ZeroMemory(si, sizeof(STARTUPINFOW));
|
||||
si->cb = sizeof(STARTUPINFOW);
|
||||
si->dwFlags = STARTF_USESHOWWINDOW;
|
||||
si->wShowWindow = SW_SHOW;
|
||||
|
||||
// init PROCESS_INFORMATION
|
||||
ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
auto command = cmd.vec().elems[0].str();
|
||||
for (usize i = 1; i<cmd.vec().elems.size(); ++i) {
|
||||
command += " " + cmd.vec().elems[i].str();
|
||||
}
|
||||
|
||||
std::setlocale(LC_ALL, ".UTF8");
|
||||
auto str_length = 1 + (command.length()<<1);
|
||||
auto buffer = new wchar_t[str_length];
|
||||
auto converted = mbstowcs(buffer, command.c_str(), str_length);
|
||||
if (converted == size_t(-1)) {
|
||||
delete[] buffer;
|
||||
return nas_err("subprocess::create", "failed to convert wchat_t*");
|
||||
}
|
||||
|
||||
buffer[converted] = L'\0';
|
||||
|
||||
if (!CreateProcessW(
|
||||
nullptr,
|
||||
buffer,
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
si,
|
||||
pi
|
||||
)) {
|
||||
return nas_err("subprocess::create",
|
||||
"failed to create subprocess: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
delete[] buffer;
|
||||
#else
|
||||
// create argv
|
||||
char** argv = new char*[cmd.vec().elems.size()+1];
|
||||
for (usize i = 0; i<cmd.vec().elems.size(); ++i) {
|
||||
argv[i] = strdup(cmd.vec().elems[i].str().c_str());
|
||||
}
|
||||
argv[cmd.vec().elems.size()] = nullptr;
|
||||
|
||||
// create child process
|
||||
auto pid = fork();
|
||||
if (pid < 0) {
|
||||
return nas_err("subprocess::create", "failed to create sub-process");
|
||||
}
|
||||
// child process
|
||||
if (pid==0) {
|
||||
execvp(argv[0], argv);
|
||||
nas_err("subprocess::create", "failed to execute command");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
// parent process
|
||||
obj.ghost().get<subprocess>()->pid = pid;
|
||||
for (usize i = 0; argv[i]; ++i) {
|
||||
delete argv[i];
|
||||
}
|
||||
delete[] argv;
|
||||
|
||||
#endif
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
var builtin_subprocess_active(context* ctx, gc* ngc) {
|
||||
auto obj = ctx->localr[1];
|
||||
if (!obj.object_check(subprocess::name())) {
|
||||
return nas_err("subprocess::active",
|
||||
"need correct subprocess object"
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
auto pi = &obj.ghost().get<subprocess>()->pi;
|
||||
|
||||
DWORD res;
|
||||
GetExitCodeProcess(pi->hProcess, &res);
|
||||
|
||||
return res==STILL_ACTIVE? one:zero;
|
||||
#else
|
||||
auto pid = obj.ghost().get<subprocess>()->pid;
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, WNOHANG);
|
||||
|
||||
// this means the child process is returned
|
||||
if (result==pid) {
|
||||
obj.ghost().get<subprocess>()->status = status;
|
||||
}
|
||||
|
||||
return result==0? one:zero;
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_subprocess_terminate(context* ctx, gc* ngc) {
|
||||
auto obj = ctx->localr[1];
|
||||
if (!obj.object_check(subprocess::name())) {
|
||||
return nas_err("subprocess::terminate",
|
||||
"need correct subprocess object"
|
||||
);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
auto pi = &obj.ghost().get<subprocess>()->pi;
|
||||
|
||||
WaitForSingleObject(pi->hProcess, 0);
|
||||
TerminateProcess(pi->hProcess, -1);
|
||||
|
||||
DWORD res;
|
||||
GetExitCodeProcess(pi->hProcess, &res);
|
||||
|
||||
CloseHandle(pi->hProcess);
|
||||
CloseHandle(pi->hThread);
|
||||
|
||||
return var::num(res);
|
||||
#else
|
||||
auto pid = obj.ghost().get<subprocess>()->pid;
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, WNOHANG);
|
||||
|
||||
if (result<0) {
|
||||
auto pstat = obj.ghost().get<subprocess>()->status;
|
||||
// if pstat is not 0, means child process already exited
|
||||
auto res = WIFEXITED(pstat)? WEXITSTATUS(pstat):-1;
|
||||
return var::num(res);
|
||||
}
|
||||
|
||||
// child process is still running
|
||||
if (result==0) {
|
||||
kill(pid, SIGTERM);
|
||||
return var::num(-1);
|
||||
}
|
||||
|
||||
// child process exited when calling the waitpid above
|
||||
auto res = WIFEXITED(status)? WEXITSTATUS(status):-1;
|
||||
return var::num(res);
|
||||
#endif
|
||||
}
|
||||
|
||||
nasal_builtin_table subprocess_native[] = {
|
||||
{"__subprocess_create", builtin_subprocess_create},
|
||||
{"__subprocess_active", builtin_subprocess_active},
|
||||
{"__subprocess_terminate", builtin_subprocess_terminate},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
}
|
||||
39
src/natives/subprocess.h
Normal file
39
src/natives/subprocess.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "natives/builtin.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct subprocess {
|
||||
#ifndef WIN32
|
||||
pid_t pid;
|
||||
int status = 0;
|
||||
#else
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static const char* name() {
|
||||
return "nasal::subprocess";
|
||||
}
|
||||
};
|
||||
|
||||
void subprocess_desc_dtor(void*);
|
||||
|
||||
var builtin_subprocess_create(context*, gc*);
|
||||
var builtin_subprocess_active(context*, gc*);
|
||||
var builtin_subprocess_terminate(context*, gc*);
|
||||
extern nasal_builtin_table subprocess_native[];
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const auto dir_type_name = "dir";
|
||||
const auto dir_type_name = "nasal::DIR";
|
||||
|
||||
void dir_entry_destructor(void* ptr) {
|
||||
#ifndef _MSC_VER
|
||||
@@ -12,48 +12,6 @@ void dir_entry_destructor(void* ptr) {
|
||||
#endif
|
||||
}
|
||||
|
||||
var builtin_pipe(context* ctx, gc* ngc) {
|
||||
#ifndef _WIN32
|
||||
i32 fd[2];
|
||||
var res = ngc->alloc(vm_type::vm_vec);
|
||||
if (pipe(fd)==-1) {
|
||||
return nas_err("unix::pipe", "failed to create pipe");
|
||||
}
|
||||
res.vec().elems.push_back(var::num(static_cast<f64>(fd[0])));
|
||||
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
|
||||
return res;
|
||||
#endif
|
||||
return nas_err("unix::pipe", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_fork(context* ctx, gc* ngc) {
|
||||
#ifndef _WIN32
|
||||
f64 res = fork();
|
||||
if (res<0) {
|
||||
return nas_err("unix::fork", "failed to fork a process");
|
||||
}
|
||||
return var::num(static_cast<f64>(res));
|
||||
#endif
|
||||
return nas_err("unix::fork", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_waitpid(context* ctx, gc* ngc) {
|
||||
auto pid = ctx->localr[1];
|
||||
auto nohang = ctx->localr[2];
|
||||
if (!pid.is_num() || !nohang.is_num()) {
|
||||
return nas_err("unix::waitpid", "pid and nohang must be number");
|
||||
}
|
||||
#ifndef _WIN32
|
||||
i32 ret_pid, status;
|
||||
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
|
||||
var vec = ngc->alloc(vm_type::vm_vec);
|
||||
vec.vec().elems.push_back(var::num(static_cast<f64>(ret_pid)));
|
||||
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
|
||||
return vec;
|
||||
#endif
|
||||
return nas_err("unix::waitpid", "not supported on windows");
|
||||
}
|
||||
|
||||
var builtin_opendir(context* ctx, gc* ngc) {
|
||||
auto path = ctx->localr[1];
|
||||
if (!path.is_str()) {
|
||||
@@ -89,7 +47,7 @@ var builtin_readdir(context* ctx, gc* ngc) {
|
||||
}
|
||||
return ngc->newstr(data.cFileName);
|
||||
#else
|
||||
dirent* p = readdir(static_cast<DIR*>(handle.ghost().pointer));
|
||||
dirent* p = readdir(handle.ghost().get<DIR>());
|
||||
return p? ngc->newstr(p->d_name):nil;
|
||||
#endif
|
||||
}
|
||||
@@ -114,7 +72,7 @@ var builtin_chdir(context* ctx, gc* ngc) {
|
||||
var builtin_environ(context* ctx, gc* ngc) {
|
||||
var res = ngc->temp = ngc->alloc(vm_type::vm_vec);
|
||||
auto& vec = res.vec().elems;
|
||||
for(char** env = environ; *env; ++env) {
|
||||
for (char** env = environ; *env; ++env) {
|
||||
vec.push_back(ngc->newstr(*env));
|
||||
}
|
||||
ngc->temp = nil;
|
||||
@@ -139,9 +97,6 @@ var builtin_getenv(context* ctx, gc* ngc) {
|
||||
}
|
||||
|
||||
nasal_builtin_table unix_lib_native[] = {
|
||||
{"__pipe", builtin_pipe},
|
||||
{"__fork", builtin_fork},
|
||||
{"__waitpid", builtin_waitpid},
|
||||
{"__opendir", builtin_opendir},
|
||||
{"__readdir", builtin_readdir},
|
||||
{"__closedir", builtin_closedir},
|
||||
|
||||
@@ -24,9 +24,6 @@ namespace nasal {
|
||||
|
||||
void dir_entry_destructor(void*);
|
||||
|
||||
var builtin_pipe(context*, gc*);
|
||||
var builtin_fork(context*, gc*);
|
||||
var builtin_waitpid(context*, gc*);
|
||||
var builtin_opendir(context*, gc*);
|
||||
var builtin_readdir(context*, gc*);
|
||||
var builtin_closedir(context*, gc*);
|
||||
|
||||
@@ -44,7 +44,7 @@ void optimizer::const_number(
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res)
|
||||
new number_literal(node->get_location(), res, "")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ void optimizer::const_number(
|
||||
return;
|
||||
}
|
||||
node->set_optimized_number(
|
||||
new number_literal(node->get_location(), res)
|
||||
new number_literal(node->get_location(), res, "")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,22 @@ std::string repl::readline(const std::string& prompt = ">>> ") {
|
||||
|
||||
void repl::update_temp_file() {
|
||||
auto content = std::string("");
|
||||
for(const auto& i : source) {
|
||||
for (const auto& i : source) {
|
||||
content += i + "\n";
|
||||
}
|
||||
info::instance()->repl_file_source = content + " ";
|
||||
}
|
||||
|
||||
void repl::update_temp_file(const std::vector<std::string>& src) {
|
||||
auto content = std::string("");
|
||||
for (const auto& i : src) {
|
||||
content += i + "\n";
|
||||
}
|
||||
info::instance()->repl_file_source = content + " ";
|
||||
}
|
||||
|
||||
bool repl::check_need_more_input() {
|
||||
while(true) {
|
||||
while (true) {
|
||||
update_temp_file();
|
||||
auto nasal_lexer = std::make_unique<lexer>();
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
@@ -45,7 +53,7 @@ bool repl::check_need_more_input() {
|
||||
i64 in_curve = 0;
|
||||
i64 in_bracket = 0;
|
||||
i64 in_brace = 0;
|
||||
for(const auto& t : nasal_lexer->result()) {
|
||||
for (const auto& t : nasal_lexer->result()) {
|
||||
switch(t.type) {
|
||||
case tok::tk_lcurve: ++in_curve; break;
|
||||
case tok::tk_rcurve: --in_curve; break;
|
||||
@@ -67,6 +75,34 @@ bool repl::check_need_more_input() {
|
||||
return true;
|
||||
}
|
||||
|
||||
int repl::check_need_more_input(std::vector<std::string>& src) {
|
||||
update_temp_file(src);
|
||||
auto nasal_lexer = std::make_unique<lexer>();
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i64 in_curve = 0;
|
||||
i64 in_bracket = 0;
|
||||
i64 in_brace = 0;
|
||||
for (const auto& t : nasal_lexer->result()) {
|
||||
switch(t.type) {
|
||||
case tok::tk_lcurve: ++in_curve; break;
|
||||
case tok::tk_rcurve: --in_curve; break;
|
||||
case tok::tk_lbracket: ++in_bracket; break;
|
||||
case tok::tk_rbracket: --in_bracket; break;
|
||||
case tok::tk_lbrace: ++in_brace; break;
|
||||
case tok::tk_rbrace: --in_brace; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (in_curve > 0 || in_bracket > 0 || in_brace > 0) {
|
||||
return 1; // More input needed
|
||||
}
|
||||
return 0; // Input is complete
|
||||
}
|
||||
|
||||
|
||||
void repl::help() {
|
||||
std::cout << ".h, .help | show help\n";
|
||||
std::cout << ".e, .exit | quit the REPL\n";
|
||||
@@ -124,7 +160,7 @@ void repl::execute() {
|
||||
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
help();
|
||||
|
||||
while(true) {
|
||||
while (true) {
|
||||
auto line = readline();
|
||||
if (!line.length()) {
|
||||
continue;
|
||||
@@ -150,7 +186,7 @@ void repl::execute() {
|
||||
std::cout << "\", input \".help\" for help\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
source.push_back(line);
|
||||
if (!check_need_more_input()) {
|
||||
source.pop_back();
|
||||
@@ -158,6 +194,9 @@ void repl::execute() {
|
||||
}
|
||||
|
||||
// run program
|
||||
if (source.back().back() != ';') {
|
||||
source.back() += ";";
|
||||
}
|
||||
if (!run()) {
|
||||
source.pop_back();
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ private:
|
||||
std::string readline(const std::string&);
|
||||
bool check_need_more_input();
|
||||
void update_temp_file();
|
||||
void update_temp_file(const std::vector<std::string>& src);
|
||||
void help();
|
||||
bool run();
|
||||
|
||||
public:
|
||||
repl() {
|
||||
@@ -48,7 +48,20 @@ public:
|
||||
// set empty history
|
||||
command_history = {""};
|
||||
}
|
||||
|
||||
// Make these methods public for web REPL
|
||||
bool run();
|
||||
void execute();
|
||||
int check_need_more_input(std::vector<std::string>& src);
|
||||
// Add method to access source
|
||||
void set_source(const std::vector<std::string>& src) {
|
||||
source = src;
|
||||
}
|
||||
|
||||
// Add method to access runtime
|
||||
vm& get_runtime() {
|
||||
return runtime;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -8,16 +8,13 @@ bool symbol_finder::visit_definition_expr(definition_expr* node) {
|
||||
// example: var a = 1;
|
||||
symbols.push_back({
|
||||
node->get_variable_name()->get_name(),
|
||||
node->get_variable_name()->get_location()
|
||||
node->get_variable_name()
|
||||
});
|
||||
} else {
|
||||
// multiple variable definition
|
||||
// example: var (a, b, c) = (0, 1, 2);
|
||||
for(auto i : node->get_variables()->get_variables()) {
|
||||
symbols.push_back({
|
||||
i->get_name(),
|
||||
i->get_location()
|
||||
});
|
||||
for (auto i : node->get_variables()->get_variables()) {
|
||||
symbols.push_back({i->get_name(), i});
|
||||
}
|
||||
}
|
||||
if (node->get_tuple()) {
|
||||
@@ -37,7 +34,7 @@ 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()
|
||||
node->get_name()
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -13,7 +13,7 @@ class symbol_finder: public ast_visitor {
|
||||
public:
|
||||
struct symbol_info {
|
||||
std::string name;
|
||||
span location;
|
||||
identifier* pos_node;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
193
src/util/gc_stat.cpp
Normal file
193
src/util/gc_stat.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "util/util.h"
|
||||
#include "util/gc_stat.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void gc_stat::init() {
|
||||
for (i64 i = 0; i < GC_TYPE_SIZE; i++) {
|
||||
object_size[i] = 0;
|
||||
alloc_count[i] = 0;
|
||||
gc_cycle_trigger_count[i] = 0;
|
||||
}
|
||||
|
||||
total_mark_count = 0;
|
||||
total_sweep_count = 0;
|
||||
|
||||
total_mark_time = 0;
|
||||
total_sweep_time = 0;
|
||||
|
||||
max_mark_time = 0;
|
||||
max_sweep_time = 0;
|
||||
}
|
||||
|
||||
f64 gc_stat::gc_time_ms() const {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
return ((total_mark_time + total_sweep_time) * 1000.0) / den;
|
||||
}
|
||||
|
||||
f64 gc_stat::avg_time_ms() const {
|
||||
u64 total_gc_cycle = 0;
|
||||
for (i64 i = 0; i < GC_TYPE_SIZE; i++) {
|
||||
total_gc_cycle += gc_cycle_trigger_count[i];
|
||||
}
|
||||
return gc_time_ms() / total_mark_count;
|
||||
}
|
||||
|
||||
f64 gc_stat::avg_mark_time_ms() const {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
return (total_mark_time * 1000.0) / den / total_mark_count;
|
||||
}
|
||||
|
||||
f64 gc_stat::avg_sweep_time_ms() const {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
return (total_sweep_time * 1000.0) / den / total_sweep_count;
|
||||
}
|
||||
|
||||
f64 gc_stat::max_mark_time_ms() const {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
return (max_mark_time * 1000.0) / den;
|
||||
}
|
||||
|
||||
f64 gc_stat::max_sweep_time_ms() const {
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
return (max_sweep_time * 1000.0) / den;
|
||||
}
|
||||
|
||||
void gc_stat::dump_info() const {
|
||||
util::windows_code_page_manager wm;
|
||||
wm.set_utf8_output();
|
||||
|
||||
using std::left;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::setprecision;
|
||||
|
||||
const char* used_table_name[] = {
|
||||
"object type",
|
||||
"gc cycle",
|
||||
"alloc count",
|
||||
"object count",
|
||||
"detail",
|
||||
"time spend",
|
||||
"gc time",
|
||||
"avg time",
|
||||
"max gc",
|
||||
"max mark",
|
||||
"max sweep"
|
||||
};
|
||||
const char* name[] = {
|
||||
"string",
|
||||
"vector",
|
||||
"hashmap",
|
||||
"function",
|
||||
"upvalue",
|
||||
"ghost",
|
||||
"coroutine",
|
||||
"namespace"
|
||||
};
|
||||
|
||||
// calculate max indent length
|
||||
usize indent = 0;
|
||||
for (auto tname : used_table_name) {
|
||||
auto len = strlen(tname);
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for (auto n : name) {
|
||||
auto len = strlen(n);
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
for (u32 i = 0; i < GC_TYPE_SIZE; ++i) {
|
||||
auto len = std::to_string(gc_cycle_trigger_count[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(alloc_count[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
len = std::to_string(object_size[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
|
||||
auto indent_string = std::string("──");
|
||||
for (usize i = 0; i < indent; ++i) {
|
||||
indent_string += "─";
|
||||
}
|
||||
|
||||
const auto first_line = "╭" + indent_string + "┬" +
|
||||
indent_string + "┬" +
|
||||
indent_string + "┬" +
|
||||
indent_string + "╮";
|
||||
const auto second_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┤";
|
||||
const auto mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┤";
|
||||
const auto another_mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "┤";
|
||||
const auto last_line = "╰" + indent_string + "┴" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "╯";
|
||||
|
||||
std::clog << "\n" << first_line << "\n";
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "gc cycle";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "object count";
|
||||
std::clog << " │\n" << second_line << "\n" << std::internal;
|
||||
|
||||
for (u32 i = 0; i < GC_TYPE_SIZE; ++i) {
|
||||
if (!gc_cycle_trigger_count[i] &&
|
||||
!alloc_count[i] &&
|
||||
!object_size[i]) {
|
||||
continue;
|
||||
}
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << gc_cycle_trigger_count[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << alloc_count[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << object_size[i];
|
||||
std::clog << " │\n" << std::internal;
|
||||
}
|
||||
std::clog << mid_line << "\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << " ";
|
||||
std::clog << " " << left << setw(indent) << setfill(' ') << " ";
|
||||
std::clog << " │\n" << another_mid_line << "\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << gc_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << avg_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "avg mark";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << avg_mark_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "avg sweep";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << avg_sweep_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_mark_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_sweep_time_ms() << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << last_line << "\n" << std::internal;
|
||||
|
||||
wm.restore_code_page();
|
||||
}
|
||||
|
||||
}
|
||||
57
src/util/gc_stat.h
Normal file
57
src/util/gc_stat.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_type.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct gc_stat {
|
||||
u64 object_size[GC_TYPE_SIZE];
|
||||
u64 gc_cycle_trigger_count[GC_TYPE_SIZE];
|
||||
u64 alloc_count[GC_TYPE_SIZE];
|
||||
|
||||
u64 total_mark_count = 0;
|
||||
u64 total_sweep_count = 0;
|
||||
|
||||
i64 total_mark_time = 0;
|
||||
i64 total_sweep_time = 0;
|
||||
|
||||
i64 max_mark_time = 0;
|
||||
i64 max_sweep_time = 0;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_time;
|
||||
|
||||
void stamp() {
|
||||
start_time = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void elapsed_mark_time() {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto mark_time = (end - start_time).count();
|
||||
++total_mark_count;
|
||||
total_mark_time += mark_time;
|
||||
max_mark_time = max_mark_time > mark_time ? max_mark_time : mark_time;
|
||||
}
|
||||
|
||||
void elapsed_sweep_time() {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto sweep_time = (end - start_time).count();
|
||||
++total_sweep_count;
|
||||
total_sweep_time += sweep_time;
|
||||
max_sweep_time = max_sweep_time > sweep_time ? max_sweep_time : sweep_time;
|
||||
}
|
||||
|
||||
void init();
|
||||
f64 gc_time_ms() const;
|
||||
f64 avg_time_ms() const;
|
||||
f64 avg_mark_time_ms() const;
|
||||
f64 avg_sweep_time_ms() const;
|
||||
f64 max_mark_time_ms() const;
|
||||
f64 max_sweep_time_ms() const;
|
||||
|
||||
void dump_info() const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -142,7 +142,7 @@ std::string char_to_hex(const char c) {
|
||||
|
||||
std::string rawstr(const std::string& str, const usize maxlen) {
|
||||
std::string ret("");
|
||||
for(auto i : str) {
|
||||
for (auto i : str) {
|
||||
// windows doesn't output unicode normally, so we output the hex
|
||||
if (util::is_windows() && i<=0) {
|
||||
ret += "\\x" + char_to_hex(i);
|
||||
@@ -172,7 +172,7 @@ std::string rawstr(const std::string& str, const usize maxlen) {
|
||||
|
||||
f64 hex_to_f64(const char* str) {
|
||||
f64 ret = 0;
|
||||
for(; *str; ++str) {
|
||||
for (; *str; ++str) {
|
||||
if ('0'<=*str && *str<='9') {
|
||||
ret = ret*16+(*str-'0');
|
||||
} else if ('a'<=*str && *str<='f') {
|
||||
@@ -188,7 +188,7 @@ f64 hex_to_f64(const char* str) {
|
||||
|
||||
f64 oct_to_f64(const char* str) {
|
||||
f64 ret = 0;
|
||||
while('0'<=*str && *str<'8') {
|
||||
while ('0'<=*str && *str<'8') {
|
||||
ret = ret*8+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
@@ -206,7 +206,7 @@ f64 oct_to_f64(const char* str) {
|
||||
f64 dec_to_f64(const char* str) {
|
||||
f64 ret = 0, num_pow = 0;
|
||||
bool negative = false;
|
||||
while('0'<=*str && *str<='9') {
|
||||
while ('0'<=*str && *str<='9') {
|
||||
ret = ret*10+(*str++-'0');
|
||||
}
|
||||
if (!*str) {
|
||||
@@ -217,7 +217,7 @@ f64 dec_to_f64(const char* str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0.1;
|
||||
while('0'<=*str && *str<='9') {
|
||||
while ('0'<=*str && *str<='9') {
|
||||
ret += num_pow*(*str++-'0');
|
||||
num_pow *= 0.1;
|
||||
}
|
||||
@@ -238,7 +238,7 @@ f64 dec_to_f64(const char* str) {
|
||||
return nan("");
|
||||
}
|
||||
num_pow = 0;
|
||||
while('0'<=*str && *str<='9') {
|
||||
while ('0'<=*str && *str<='9') {
|
||||
num_pow = num_pow*10+(*str++-'0');
|
||||
}
|
||||
if (*str) {
|
||||
|
||||
@@ -104,7 +104,7 @@ var _parse = func(parser, args, result_hash) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(var i = 0; i<size(args); i += 1) {
|
||||
for (var i = 0; i<size(args); i += 1) {
|
||||
var this_arg = args[i];
|
||||
var find_command_flag = false;
|
||||
foreach(var cmd; parser.command_list) {
|
||||
@@ -151,7 +151,7 @@ var _parse = func(parser, args, result_hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
var _add_command = func(parser, long, short, help, need_arg , need_nargs) {
|
||||
var _add_command = func(parser, long, short, help, need_arg, need_nargs) {
|
||||
var new_command = {
|
||||
full_name: long,
|
||||
short_name: short,
|
||||
|
||||
@@ -64,7 +64,7 @@ var bit = func() {
|
||||
var append = func(vec, arg...) {
|
||||
return __append;
|
||||
}
|
||||
for(var i = 1; i<32; i += 1) {
|
||||
for (var i = 1; i<32; i += 1) {
|
||||
append(res, __ += __);
|
||||
}
|
||||
return res;
|
||||
|
||||
10
std/csv.nas
10
std/csv.nas
@@ -2,14 +2,14 @@
|
||||
# ValKmjolnir 2022/10/15
|
||||
use std.io;
|
||||
|
||||
var read = func(path, delimeter=",", endline="\n") {
|
||||
var read = func(path, delimeter = ",", endline = "\n") {
|
||||
var context = io.readfile(path);
|
||||
context = split(endline, context);
|
||||
forindex(var i;context) {
|
||||
context[i] = split(delimeter,context[i]);
|
||||
forindex(var i; context) {
|
||||
context[i] = split(delimeter, context[i]);
|
||||
}
|
||||
if (size(context)<=1) {
|
||||
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
|
||||
if (size(context) <= 1) {
|
||||
die("incorrect csv file <" ~ path ~ ">: " ~ size(context) ~ " line(s).");
|
||||
}
|
||||
return {
|
||||
property: context[0],
|
||||
|
||||
@@ -8,22 +8,6 @@ use std.unix;
|
||||
|
||||
# open dynamic lib. return a hash including dl pointer and function pointers
|
||||
var dlopen = func(libname) {
|
||||
# find dynamic lib from local dir first
|
||||
libname = (os.platform()=="windows"? ".\\":"./")~libname;
|
||||
if (io.exists(libname))
|
||||
return __dlopen(libname);
|
||||
# find dynamic lib through PATH
|
||||
var envpath = split(os.platform()=="windows"? ";":":",unix.getenv("PATH"));
|
||||
# first find ./module
|
||||
append(envpath, ".");
|
||||
var path = os.platform()=="windows"? "\\module\\":"/module/";
|
||||
foreach(var p;envpath) {
|
||||
p ~= path~libname;
|
||||
if (io.exists(p)) {
|
||||
libname = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return __dlopen(libname);
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ var add_event = func(name,interval,function) {
|
||||
fg_globals.event[name]=coroutine.create(func {
|
||||
var timestamp=maketimestamp();
|
||||
timestamp.stamp();
|
||||
while(timestamp.elapsedMSec()<interval*1000)
|
||||
while (timestamp.elapsedMSec()<interval*1000)
|
||||
coroutine.yield();
|
||||
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[33mevent\e[0m interval:\e[34m",interval,"\e[0m");
|
||||
function();
|
||||
@@ -75,10 +75,10 @@ var add_task = func(name,interval,function) {
|
||||
fg_globals.task[name]=coroutine.create(func {
|
||||
var counter=0;
|
||||
var timestamp=maketimestamp();
|
||||
while(1) {
|
||||
while (1) {
|
||||
counter+=1;
|
||||
timestamp.stamp();
|
||||
while(timestamp.elapsedMSec()<interval*1000)
|
||||
while (timestamp.elapsedMSec()<interval*1000)
|
||||
coroutine.yield();
|
||||
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[34mtask\e[0m interval:\e[34m",interval,"\e[0m invoke-time:\e[96m",counter,"\e[0m");
|
||||
function();
|
||||
@@ -145,7 +145,7 @@ var settimer = func() {
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func simulation()");
|
||||
var simulation = func() {
|
||||
var running=1;
|
||||
while(running) {
|
||||
while (running) {
|
||||
running=0;
|
||||
foreach(var i;keys(fg_globals.task)) {
|
||||
if (!contains(fg_globals.task,i))
|
||||
@@ -225,7 +225,7 @@ var props={
|
||||
path=split('/',path);
|
||||
var tmp=me.globals;
|
||||
var path_size=size(path);
|
||||
for(var i=0;i<path_size-1;i+=1)
|
||||
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~']'))
|
||||
@@ -260,7 +260,7 @@ props.Node={
|
||||
return 0;
|
||||
},
|
||||
addChildren:func(name,cnt=0) {
|
||||
for(var i=0;i<cnt;i+=1) {
|
||||
for (var i=0;i<cnt;i+=1) {
|
||||
var label=name~'['~i~']';
|
||||
me.val[label]=props.Node.new();
|
||||
me.val[label].parent=me;
|
||||
@@ -344,7 +344,7 @@ foreach(var i;c)
|
||||
props.getNode('/ai',1).addChildren('ai',4);
|
||||
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
|
||||
props.getNode('/models',1).addChildren('building',4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
for (var i=0;i<4;i+=1)
|
||||
props.getNode('/models/building['~i~']',1).setIntValue(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /consumables");
|
||||
@@ -354,7 +354,7 @@ props.getNode("/consumables/fuel",1).addChild("total-gal_us");
|
||||
props.getNode("/consumables/fuel/total-fuel-lbs",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel/total-gal_us",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel",1).addChildren("tank",4);
|
||||
for(var i=0;i<4;i+=1) {
|
||||
for (var i=0;i<4;i+=1) {
|
||||
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-lb");
|
||||
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-lbs");
|
||||
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("level-gal_us");
|
||||
@@ -376,7 +376,7 @@ println("[\e[32m props \e[0m] [",os.time(),"] init /controls/anti-ice");
|
||||
foreach(var i;['wing-heat','pitot-heat','wiper','window-heat'])
|
||||
props.getNode("/controls/anti-ice",1).addChild(i);
|
||||
props.getNode("/controls/anti-ice",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1) {
|
||||
for (var i=0;i<2;i+=1) {
|
||||
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("carb-heat");
|
||||
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("inlet-heat");
|
||||
}
|
||||
@@ -390,7 +390,7 @@ props.getNode("/controls/armament",1).addChild("master-arm");
|
||||
props.getNode("/controls/armament",1).addChild("station-select");
|
||||
props.getNode("/controls/armament",1).addChild("release-all");
|
||||
props.getNode("/controls/armament",1).addChildren("station",4);
|
||||
for(var i=0;i<4;i+=1) {
|
||||
for (var i=0;i<4;i+=1) {
|
||||
props.getNode("/controls/armament/station["~i~"]",1).addChild("stick-size");
|
||||
props.getNode("/controls/armament/station["~i~"]",1).addChild("release-stick");
|
||||
props.getNode("/controls/armament/station["~i~"]",1).addChild("release-all");
|
||||
@@ -401,14 +401,14 @@ println("[\e[32m props \e[0m] [",os.time(),"] init /controls/autoflight");
|
||||
foreach(var i;['autothrottle-arm','autothrottle-engage','heading-select','altitude-select','bank-angle-select','vertical-speed-select','speed-select','mach-select','vertical-mode','lateral-mode'])
|
||||
props.getNode("/controls/autoflight",1).addChild(i);
|
||||
props.getNode("/controls/autoflight",1).addChildren("autopilot",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
for (var i=0;i<2;i+=1)
|
||||
props.getNode("/controls/autoflight/autopilot["~i~"]",1).addChild("engage");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/electric");
|
||||
foreach(var i;['battery-switch','external-power','APU-generator'])
|
||||
props.getNode("/controls/electric",1).addChild(i);
|
||||
props.getNode("/controls/electric",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1) {
|
||||
for (var i=0;i<2;i+=1) {
|
||||
props.getNode("/controls/electric/engine["~i~"]",1).addChild("generator");
|
||||
props.getNode("/controls/electric/engine["~i~"]",1).addChild("bus-tie");
|
||||
}
|
||||
@@ -416,7 +416,7 @@ for(var i=0;i<2;i+=1) {
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/engines");
|
||||
props.getNode("/controls/engines",1).addChild("throttle-idle");
|
||||
props.getNode("/controls/engines",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
for (var i=0;i<2;i+=1)
|
||||
foreach(var j;['throttle','starter','fuel-pump','fire-switch','fire-bottle-discharge','cutoff','mixture','propeller-pitch','magnetos','boost','WEP','cowl-flaps-norm','feather','ignition','augmentation','afterburner','reverser','water-injection','condition'])
|
||||
props.getNode("/controls/engines/engine["~i~"]",1).addChild(j);
|
||||
|
||||
@@ -427,7 +427,7 @@ foreach(var i;['aileron','aileron-trim','elevator','elevator-trim','rudder','rud
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/fuel");
|
||||
props.getNode("/controls/fuel",1).addChild("dump-value");
|
||||
props.getNode("/controls/fuel",1).addChildren("tank",4);
|
||||
for(var i=0;i<4;i+=1) {
|
||||
for (var i=0;i<4;i+=1) {
|
||||
foreach(var j;['fuel-selector','to_engine','to_tank'])
|
||||
props.getNode("/controls/fuel/tank["~i~"]",1).addChild(j);
|
||||
props.getNode("/controls/fuel/tank["~i~"]",1).addChildren("boost-pump",4);
|
||||
@@ -437,12 +437,12 @@ println("[\e[32m props \e[0m] [",os.time(),"] init /controls/gear");
|
||||
foreach(var i;['brake-left','brake-right','brake-parking','steering','gear-down','antiskid','tailhook','tailwheel-lock'])
|
||||
props.getNode("/controls/gear",1).addChild(i);
|
||||
props.getNode("/controls/gear",1).addChildren("wheel",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
for (var i=0;i<4;i+=1)
|
||||
props.getNode("/controls/gear/wheel["~i~"]",1).addChild("alternate-extension");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/hydraulic");
|
||||
props.getNode("/controls/hydraulic",1).addChildren("system",2);
|
||||
for(var i=0;i<2;i+=1) {
|
||||
for (var i=0;i<2;i+=1) {
|
||||
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("engine-pump");
|
||||
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("electric-pump");
|
||||
}
|
||||
@@ -454,28 +454,28 @@ foreach(var i;['landing-lights','turn-off-lights','formation-lights','taxi-light
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pneumatic");
|
||||
props.getNode("/controls/pneumatic",1).addChild("APU-bleed");
|
||||
props.getNode("/controls/pneumatic",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
for (var i=0;i<2;i+=1)
|
||||
props.getNode("/controls/pneumatic/engine["~i~"]",1).addChild("bleed");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pressurization");
|
||||
foreach(var i;['mode','dump','outflow-valve'])
|
||||
props.getNode("/controls/pressurization",1).addChild(i);
|
||||
props.getNode("/controls/pressurization",1).addChildren("pack",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
for (var i=0;i<4;i+=1)
|
||||
props.getNode("/controls/pressurization/pack["~i~"]",1).addChild("pack-on");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/seat");
|
||||
foreach(var i;['vertical-adjust','fore-aft-adjust','cmd_selector_valve'])
|
||||
props.getNode("/controls/seat",1).addChild(i);
|
||||
props.getNode("/controls/seat",1).addChildren("eject",3);
|
||||
for(var i=0;i<3;i+=1) {
|
||||
for (var i=0;i<3;i+=1) {
|
||||
props.getNode("/controls/seat/eject["~i~"]",1).addChild("initiate");
|
||||
props.getNode("/controls/seat/eject["~i~"]",1).addChild("status");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /engines");
|
||||
props.getNode("/engines",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
for (var i=0;i<2;i+=1)
|
||||
foreach(var j;
|
||||
['fuel-flow-gph','fuel-flow-pph','thrust_lb','running','starter','cranking',
|
||||
'n1','n2','epr','augmentation','water-injection','ignition','nozzle-pos-norm',
|
||||
@@ -506,7 +506,7 @@ foreach(var i;['x-accel-fps_sec','y-accel-fps_sec','z-accel-fps_sec'])
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /gear");
|
||||
props.getNode("/gear",1).addChild("serviceable");
|
||||
props.getNode("/gear",1).addChildren("gear",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
for (var i=0;i<4;i+=1)
|
||||
foreach(var j;['cast-angle-deg','compression-m','compression-norm','ground-friction-factor','ground-is-solid','has-brake','rollspeed-ms','wow','xoffset-in','yoffset-in','zoffset-in'])
|
||||
props.getNode("/gear/gear["~i~"]",1).addChild(j);
|
||||
|
||||
@@ -522,7 +522,7 @@ foreach(var i;['torque-sound-filtered','total-torque'])
|
||||
foreach(var i;['balance','bladesvisible','cone-deg','cone2-deg','roll-deg','rpm','stall','stall-filtered','tilt','torque','yaw-deg'])
|
||||
props.getNode("/rotors/{name}",1).addChild(i);
|
||||
props.getNode("/rotors/{name}",1).addChildren("blade",8);
|
||||
for(var i=0;i<8;i+=1)
|
||||
for (var i=0;i<8;i+=1)
|
||||
foreach(var j;['flap-deg','incidence-deg','position-deg'])
|
||||
props.getNode("/rotors/{name}/blade["~i~"]",1).addChild(j);
|
||||
|
||||
@@ -542,7 +542,7 @@ func() {
|
||||
srand();
|
||||
var tmp=nil;
|
||||
var vec=[props.globals];
|
||||
while(size(vec)) {
|
||||
while (size(vec)) {
|
||||
tmp=[];
|
||||
foreach(var i;vec) {
|
||||
if (typeof(i.val)=="hash") {
|
||||
|
||||
28
std/file.nas
28
std/file.nas
@@ -4,12 +4,10 @@ use std.io;
|
||||
use std.unix;
|
||||
|
||||
var SEEK_SET = io.SEEK_SET;
|
||||
|
||||
var SEEK_CUR = io.SEEK_CUR;
|
||||
|
||||
var SEEK_END = io.SEEK_END;
|
||||
|
||||
var new = func(filename, mode="r") {
|
||||
var new = func(filename, mode = "r") {
|
||||
if (!io.exists(filename)) {
|
||||
return nil;
|
||||
}
|
||||
@@ -55,8 +53,8 @@ var find_all_files = func(path) {
|
||||
}
|
||||
var dd = unix.opendir(path);
|
||||
var res = [];
|
||||
while(var n = unix.readdir(dd)) {
|
||||
if (unix.isfile(path~"/"~n)) {
|
||||
while (var n = unix.readdir(dd)) {
|
||||
if (unix.isfile(path ~ "/" ~ n)) {
|
||||
append(res, n);
|
||||
}
|
||||
}
|
||||
@@ -73,12 +71,12 @@ var recursive_find_files = func(path) {
|
||||
dir: path,
|
||||
files: []
|
||||
};
|
||||
while(var n = unix.readdir(dd)) {
|
||||
if (unix.isfile(path~"/"~n)) {
|
||||
while (var n = unix.readdir(dd)) {
|
||||
if (unix.isfile(path ~ "/" ~ n)) {
|
||||
append(res.files, n);
|
||||
} elsif (unix.isdir(path~"/"~n) and n!="." and n!="..") {
|
||||
var tmp = recursive_find_files(path~"/"~n);
|
||||
if (tmp!=nil) {
|
||||
} elsif (unix.isdir(path ~ "/" ~ n) and n != "." and n != "..") {
|
||||
var tmp = recursive_find_files(path ~ "/" ~ n);
|
||||
if (tmp != nil) {
|
||||
append(res.files, tmp);
|
||||
}
|
||||
}
|
||||
@@ -89,19 +87,19 @@ var recursive_find_files = func(path) {
|
||||
|
||||
var recursive_find_files_flat = func(path) {
|
||||
var tree_files = recursive_find_files(path);
|
||||
if (tree_files==nil) {
|
||||
if (tree_files == nil) {
|
||||
return [];
|
||||
}
|
||||
var flat = [];
|
||||
var bfs = [tree_files];
|
||||
while(size(bfs)!=0) {
|
||||
while (size(bfs) != 0) {
|
||||
var first = pop(bfs);
|
||||
foreach(var file_record; first.files) {
|
||||
if (ishash(file_record)) {
|
||||
append(bfs, file_record);
|
||||
continue;
|
||||
}
|
||||
append(flat, first.dir~"/"~file_record);
|
||||
append(flat, first.dir ~ "/" ~ file_record);
|
||||
}
|
||||
}
|
||||
return flat;
|
||||
@@ -110,7 +108,7 @@ var recursive_find_files_flat = func(path) {
|
||||
var recursive_find_files_with_extension = func(path, extensions...) {
|
||||
var in_vec = func(ext) {
|
||||
foreach(var i; extensions) {
|
||||
if (ext==i) {
|
||||
if (ext == i) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -121,7 +119,7 @@ var recursive_find_files_with_extension = func(path, extensions...) {
|
||||
var res = [];
|
||||
foreach(var filename; files) {
|
||||
var tmp = split('.', filename);
|
||||
if (size(tmp)>1 and in_vec(tmp[-1])) {
|
||||
if (size(tmp) > 1 and in_vec(tmp[-1])) {
|
||||
append(res, filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ var stderr = func() { return __stderr; }();
|
||||
# get file status. using data from stat
|
||||
var fstat = func(filename) {
|
||||
var s = stat(filename);
|
||||
if (s == nil) {
|
||||
return nil;
|
||||
}
|
||||
return {
|
||||
st_dev: s[0],
|
||||
st_ino: s[1],
|
||||
|
||||
@@ -231,12 +231,12 @@ var sort = func() {
|
||||
var base = left+int(rand()*(right-left));
|
||||
(vec[left], vec[base]) = (vec[base], vec[left]);
|
||||
var (i, j, tmp) = (left, right, vec[left]);
|
||||
while(i<j) {
|
||||
while(i<j and cmp(tmp,vec[j])) {
|
||||
while (i<j) {
|
||||
while (i<j and cmp(tmp,vec[j])) {
|
||||
j -= 1;
|
||||
}
|
||||
vec[i] = vec[j];
|
||||
while(i<j and cmp(vec[i],tmp)) {
|
||||
while (i<j and cmp(vec[i],tmp)) {
|
||||
i += 1;
|
||||
}
|
||||
vec[j] = vec[i];
|
||||
@@ -380,7 +380,7 @@ var bind = func(function, locals, outer_scope = nil) {
|
||||
die("this runtime does not support bind");
|
||||
}
|
||||
|
||||
var call = func(function ,args = nil, _me = nil, locals = nil, error = nil) {
|
||||
var call = func(function, args = nil, _me = nil, locals = nil, error = nil) {
|
||||
die("this runtime does not support call");
|
||||
}
|
||||
|
||||
|
||||
28
std/mat.nas
28
std/mat.nas
@@ -22,8 +22,8 @@ var rand_init = func(a) {
|
||||
|
||||
var mat_print = func(a) {
|
||||
var (width,height,ref)=(a.width,a.height,a.mat);
|
||||
for(var i=0;i<height;i+=1) {
|
||||
for(var j=0;j<width;j+=1) {
|
||||
for (var i=0;i<height;i+=1) {
|
||||
for (var j=0;j<width;j+=1) {
|
||||
print(ref[i*width+j]," ");
|
||||
}
|
||||
println();
|
||||
@@ -42,8 +42,8 @@ var add = func(a,b) {
|
||||
var (width,height,ref)=(res.width,res.height,res.mat);
|
||||
var (aref,bref)=(a.mat,b.mat);
|
||||
|
||||
for(var i=0;i<height;i+=1) {
|
||||
for(var j=0;j<width;j+=1) {
|
||||
for (var i=0;i<height;i+=1) {
|
||||
for (var j=0;j<width;j+=1) {
|
||||
ref[i*width+j]=aref[i*width+j]+bref[i*width+j];
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,8 @@ var sub = func(a,b) {
|
||||
var (width,height,ref)=(res.width,res.height,res.mat);
|
||||
var (aref,bref)=(a.mat,b.mat);
|
||||
|
||||
for(var i=0;i<height;i+=1) {
|
||||
for(var j=0;j<width;j+=1) {
|
||||
for (var i=0;i<height;i+=1) {
|
||||
for (var j=0;j<width;j+=1) {
|
||||
ref[i*width+j]=aref[i*width+j]-bref[i*width+j];
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,8 @@ var hardamard = func(a,b) {
|
||||
var (width,height,ref)=(res.width,res.height,res.mat);
|
||||
var (aref,bref)=(a.mat,b.mat);
|
||||
|
||||
for(var i=0;i<height;i+=1) {
|
||||
for(var j=0;j<width;j+=1) {
|
||||
for (var i=0;i<height;i+=1) {
|
||||
for (var j=0;j<width;j+=1) {
|
||||
ref[i*width+j]=aref[i*width+j]*bref[i*width+j];
|
||||
}
|
||||
}
|
||||
@@ -122,8 +122,8 @@ var trans = func(a) {
|
||||
var res=mat(a.height,a.width);
|
||||
var ref=res.mat;
|
||||
var (a_width,a_height,aref)=(a.width,a.height,a.mat);
|
||||
for(var i=0;i<a_width;i+=1) {
|
||||
for(var j=0;j<a_height;j+=1) {
|
||||
for (var i=0;i<a_width;i+=1) {
|
||||
for (var j=0;j<a_height;j+=1) {
|
||||
ref[i*a_height+j]=aref[j*a_width+i];
|
||||
}
|
||||
}
|
||||
@@ -152,9 +152,9 @@ var mult = func(a,b) {
|
||||
var (a_width,aref)=(a.width,a.mat);
|
||||
var (b_width,bref)=(b.width,b.mat);
|
||||
|
||||
for(var i=0;i<res_width;i+=1) {
|
||||
for(var j=0;j<res_height;j+=1) {
|
||||
for(var k=0;k<a_width;k+=1) {
|
||||
for (var i=0;i<res_width;i+=1) {
|
||||
for (var j=0;j<res_height;j+=1) {
|
||||
for (var k=0;k<a_width;k+=1) {
|
||||
ref[j*res_width+i]+=aref[j*a_width+k]*bref[k*b_width+i];
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ var bp_example = func() {
|
||||
|
||||
var epoch=0;
|
||||
var total=1e6;
|
||||
while(total>0.001) {
|
||||
while (total>0.001) {
|
||||
epoch+=1;
|
||||
if (epoch>1e4) {
|
||||
println("Training failed after ",epoch," epoch.");
|
||||
|
||||
@@ -6,7 +6,7 @@ var leftpad = func(input_string, length, char=" ") {
|
||||
input_string = str(input_string);
|
||||
}
|
||||
var strlen = size(input_string);
|
||||
for(var i = strlen; i<length; i += 1) {
|
||||
for (var i = strlen; i<length; i += 1) {
|
||||
input_string = char~input_string;
|
||||
}
|
||||
return input_string;
|
||||
@@ -17,7 +17,7 @@ var rightpad = func(input_string, length, char=" ") {
|
||||
input_string = str(input_string);
|
||||
}
|
||||
var strlen = size(input_string);
|
||||
for(var i = strlen; i<length; i += 1) {
|
||||
for (var i = strlen; i<length; i += 1) {
|
||||
input_string ~= char;
|
||||
}
|
||||
return input_string;
|
||||
|
||||
@@ -37,7 +37,7 @@ var _connect = func(hostname, port) {
|
||||
socket.IPPROTO_TCP
|
||||
);
|
||||
var ip_info = hostname~":"~port;
|
||||
while((var err = socket.connect(sd, hostname, port))==socket.SOCKET_ERROR) {
|
||||
while ((var err = socket.connect(sd, hostname, port))==socket.SOCKET_ERROR) {
|
||||
println(_get_time(), " failed to connect ", ip_info, ": ", socket.errno());
|
||||
unix.sleep(1);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ var new = func(hostname, port) {
|
||||
# get total message
|
||||
var total_source = message.str;
|
||||
# 0\r\n\r\n is the tail of chunked http info
|
||||
while(find("0\r\n\r\n", total_source)<0) {
|
||||
while (find("0\r\n\r\n", total_source)<0) {
|
||||
message = socket.recv(sd, 1024);
|
||||
total_source ~= message.str;
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ var bar = func() {
|
||||
var finish_length=int(number*length);
|
||||
var other=length-finish_length;
|
||||
var s="";
|
||||
for(var i=0;i<finish_length;i+=1)
|
||||
for (var i=0;i<finish_length;i+=1)
|
||||
s~=front;
|
||||
for(var i=0;i<other;i+=1)
|
||||
for (var i=0;i<other;i+=1)
|
||||
s~=back;
|
||||
return sep[0]~s~sep[1];
|
||||
}
|
||||
@@ -64,7 +64,7 @@ var bar = func() {
|
||||
# return a high resolution progress bar
|
||||
# example:
|
||||
# var bar=process_bar.high_resolution_bar(40);
|
||||
# for(var i=0;i<=1;i+=0.001) {
|
||||
# for (var i=0;i<=1;i+=0.001) {
|
||||
# print(bar.bar(i,40),'\r');
|
||||
# unix.sleep(0.001);
|
||||
# }
|
||||
@@ -83,13 +83,13 @@ var high_resolution_bar = func() {
|
||||
var decimal=block_len-complete_block;
|
||||
var progress=complete_block+(decimal!=0);
|
||||
var s="|";
|
||||
for(var i=0;i<complete_block;i+=1) {
|
||||
for (var i=0;i<complete_block;i+=1) {
|
||||
s~="█";
|
||||
}
|
||||
if (decimal!=0) {
|
||||
s~=block[int(decimal*10)/10*size(block)];
|
||||
}
|
||||
for(var i=0;i<length-progress;i+=1) {
|
||||
for (var i=0;i<length-progress;i+=1) {
|
||||
s~=" ";
|
||||
}
|
||||
s~="|";
|
||||
@@ -109,14 +109,14 @@ var spinner = func() {
|
||||
var res=[];
|
||||
var len=size(vec);
|
||||
var tmp="";
|
||||
for(var i=0;i<len;i+=1) {
|
||||
for (var i=0;i<len;i+=1) {
|
||||
tmp=pop(vec)~tmp;
|
||||
append(res,tmp);
|
||||
while(size(res[-1])!=16)
|
||||
while (size(res[-1])!=16)
|
||||
res[-1]~=" ";
|
||||
}
|
||||
tmp=res[-1];
|
||||
while(tmp!=" ") {
|
||||
while (tmp!=" ") {
|
||||
tmp=" "~substr(tmp,0,15);
|
||||
append(res,tmp);
|
||||
}
|
||||
@@ -227,7 +227,7 @@ var spinner = func() {
|
||||
return {
|
||||
next: func() {
|
||||
var s="";
|
||||
for(var i=0;i<repeat;i+=1)
|
||||
for (var i=0;i<repeat;i+=1)
|
||||
s~=type[counter];
|
||||
counter+=1;
|
||||
if (counter>=size(type))
|
||||
@@ -335,7 +335,7 @@ var show = func() {
|
||||
};
|
||||
var bar_key=keys(bars);
|
||||
var spin_key=keys(spinners);
|
||||
for(var i=0; i<40; i+=1) {
|
||||
for (var i=0; i<40; i+=1) {
|
||||
forindex(var j; bar_key) {
|
||||
var k=bar_key[j];
|
||||
print("\e["~(j+1)~";1H["~k~"] "~bars[k].bar((i+1)/40));
|
||||
|
||||
@@ -433,7 +433,7 @@ var wrap = func(node) {
|
||||
} elsif (isvec(node)) {
|
||||
var v = node;
|
||||
var n = size(v);
|
||||
for(var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
|
||||
for (var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
|
||||
return v;
|
||||
}
|
||||
return node;
|
||||
|
||||
@@ -4,32 +4,32 @@ var new = func() {
|
||||
var (begin, end) = (nil, nil);
|
||||
return{
|
||||
push: func(elem) {
|
||||
var new_node={
|
||||
var new_node = {
|
||||
elem:elem,
|
||||
next:nil
|
||||
};
|
||||
if (begin==nil)
|
||||
begin=end=new_node;
|
||||
if (begin == nil)
|
||||
begin = end = new_node;
|
||||
else {
|
||||
end.next=new_node;
|
||||
end=new_node;
|
||||
end.next = new_node;
|
||||
end = new_node;
|
||||
}
|
||||
},
|
||||
pop: func() {
|
||||
if (begin!=nil)
|
||||
begin=begin.next;
|
||||
if (begin==nil)
|
||||
end=nil;
|
||||
if (begin != nil)
|
||||
begin = begin.next;
|
||||
if (begin == nil)
|
||||
end = nil;
|
||||
},
|
||||
front: func() {
|
||||
if (begin!=nil)
|
||||
if (begin != nil)
|
||||
return begin.elem;
|
||||
},
|
||||
clear: func() {
|
||||
begin=end=nil;
|
||||
begin = end = nil;
|
||||
},
|
||||
empty: func() {
|
||||
return begin==nil;
|
||||
return begin == nil;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ var gc = {
|
||||
times = 16;
|
||||
}
|
||||
|
||||
for(var i = 0; i<times; i+=1) {
|
||||
for (var i = 0; i<times; i+=1) {
|
||||
_gc_extend(type);
|
||||
}
|
||||
return nil;
|
||||
@@ -33,3 +33,15 @@ var windows = {
|
||||
return __set_utf8_output;
|
||||
}
|
||||
};
|
||||
|
||||
var version = func() {
|
||||
return __version;
|
||||
}
|
||||
|
||||
var major_version = func() {
|
||||
return split(".", version())[0];
|
||||
}
|
||||
|
||||
var minor_version = func() {
|
||||
return split(".", version())[1];
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ var stack = func() {
|
||||
return pop(vec);
|
||||
},
|
||||
top: func() {
|
||||
if (size(vec)!=0) {
|
||||
if (size(vec) != 0) {
|
||||
return vec[-1];
|
||||
}
|
||||
},
|
||||
@@ -18,7 +18,7 @@ var stack = func() {
|
||||
vec = [];
|
||||
},
|
||||
empty: func() {
|
||||
return size(vec)==0;
|
||||
return size(vec) == 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user