130 Commits
v11.1 ... v11.2

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

View File

@@ -1,65 +1,69 @@
name: C/C++ CI
name: Build/Test/Package CI
on:
schedule:
- cron: "0 16 * * *"
push:
branches: [ master,develop ]
branches: [ master, develop ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
mac-build:
mac-aarch64-build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: make
- uses: actions/checkout@v4
- name: Update Tag
run: |
make -j4
cd module
make all -j4
cd ..
make test
tar -czf nasal-mac-nightly.tgz .
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
git fetch --tags origin
git tag -f next_macOS
git push -f origin next_macOS
- name: Build
run: |
make -j4
cd module
make all -j4
cd ..
- name: Test
run: make test
- name: Package
run: python3 tools/pack.py
- name: Release
uses: softprops/action-gh-release@v2.0.5
with:
# GitHub auth token
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Name of Release to add file to
title: macOS Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_macOS
# File to release
files: nasal-mac-nightly.tgz
name: macOS nightly build
tag_name: next_macOS
prerelease: true
draft: false
files: |
nasal-Darwin.tar
linux-x86_64-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: make
- uses: actions/checkout@v4
- name: Update Tag
run: |
make -j4
cd module
make all -j4
cd ..
make test
touch nasal-linux-x86_64-nightly.tgz
tar -czf nasal-linux-x86_64-nightly.tgz --exclude=nasal-linux-x86_64-nightly.tgz .
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
git fetch --tags origin
git tag -f next_linux_x86_64
git push -f origin next_linux_x86_64
- name: Build
run: |
make -j4
cd module
make all -j4
cd ..
- name: Test
run: make test
- name: Package
run: python3 tools/pack.py
- name: Release
uses: softprops/action-gh-release@v2.0.5
with:
# GitHub auth token
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Name of Release to add file to
title: Linux Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_linux_x86_64
# File to release
files: nasal-linux-x86_64-nightly.tgz
name: linux nightly build
tag_name: next_linux_x86_64
prerelease: true
draft: false
files: |
nasal-Linux.tar

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.10)
project(nasal VERSION 10.1)
project(nasal VERSION 11.2)
message("CMAKE_HOST_SYSTEM_NAME: ${CMAKE_HOST_SYSTEM_NAME}")
@@ -10,48 +10,58 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
add_compile_options(-fPIC)
# MSVC needs this command option to really enable utf-8 output
if(MSVC)
add_compile_options(/utf-8)
endif()
# generate release executables
set(CMAKE_BUILD_TYPE "Release")
# build nasal used object
set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/cli/cli.cpp
${CMAKE_SOURCE_DIR}/src/natives/builtin.cpp
${CMAKE_SOURCE_DIR}/src/natives/coroutine.cpp
${CMAKE_SOURCE_DIR}/src/natives/fg_props.cpp
${CMAKE_SOURCE_DIR}/src/natives/bits_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/io_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/json_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/math_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/dylib_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/regex_lib.cpp
${CMAKE_SOURCE_DIR}/src/natives/unix_lib.cpp
${CMAKE_SOURCE_DIR}/src/repl/repl.cpp
${CMAKE_SOURCE_DIR}/src/util/fs.cpp
${CMAKE_SOURCE_DIR}/src/util/util.cpp
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
${CMAKE_SOURCE_DIR}/src/nasal_ast.cpp
${CMAKE_SOURCE_DIR}/src/nasal_builtin.cpp
${CMAKE_SOURCE_DIR}/src/coroutine.cpp
${CMAKE_SOURCE_DIR}/src/fg_props.cpp
${CMAKE_SOURCE_DIR}/src/bits_lib.cpp
${CMAKE_SOURCE_DIR}/src/io_lib.cpp
${CMAKE_SOURCE_DIR}/src/math_lib.cpp
${CMAKE_SOURCE_DIR}/src/dylib_lib.cpp
${CMAKE_SOURCE_DIR}/src/unix_lib.cpp
${CMAKE_SOURCE_DIR}/src/nasal_codegen.cpp
${CMAKE_SOURCE_DIR}/src/nasal_dbg.cpp
${CMAKE_SOURCE_DIR}/src/nasal_err.cpp
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_import.cpp
${CMAKE_SOURCE_DIR}/src/nasal_lexer.cpp
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
${CMAKE_SOURCE_DIR}/src/repl.cpp)
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp)
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
# build nasal
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/main.cpp)
target_link_libraries(nasal nasal-object)
# link ldl and lpthread
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(nasal dl)
target_link_libraries(nasal pthread)
endif()
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
# copy nasal from build dir to the outside dir
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
add_custom_command(
TARGET nasal POST_BUILD
@@ -65,10 +75,12 @@ endif()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
set(MODULE_USED_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/util/util.cpp
${CMAKE_SOURCE_DIR}/src/util/fs.cpp
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
target_include_directories(module-used-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
@@ -84,4 +96,4 @@ target_link_libraries(mat module-used-object)
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(nasock module-used-object)
target_link_libraries(nasock module-used-object)

870
README.md
View File

@@ -1,20 +1,21 @@
# __Nasal - Modern Interpreter__
<img src="./doc/pic/header.png" style="width:800px"></img>
<img src="./doc/pic/header.png" style="width:600px"></img>
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
[![C/C++ CI](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
## __Contents__
* [__Introduction__](#introduction)
* [__Compile__](#how-to-compile)
* [__Download__](#download)
* [__Compile__](#compile)
* [__Usage__](#how-to-use)
* [__Tutorial__](#tutorial)
* [__Tutorial__](./doc/tutorial.md)
* [__Release Notes__](./doc/dev.md#release-notes)
* [__Development History__](./doc/dev.md)
* [__Benchmark__](./doc/benchmark.md)
@@ -25,21 +26,26 @@
__Contact us if having great ideas to share!__
* __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762)
* __lhk101lhk101@qq.com__ (ValKmjolnir)
* __sidi.liang@gmail.com__ (Sidi)
## __Introduction__
![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
The designer is [Andy Ross](https://github.com/andyross).
This interpreter is rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`).
We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`)
without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
But we really appreciate that Andy created this amazing programming language.
Old version of this project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5). Now it uses __GPL v2 license__ (since 2023/6).
This project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5), __GPL v2 license__ (since 2023/6).
### __Why writing this nasal interpreter?__
### __Why writing this Nasal interpreter?__
2019 summer,
members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear,
@@ -55,40 +61,47 @@ interesting programs and run them without the lib of Flightgear.
You could add your own modules to make
the interpreter a useful tool in your own projects.
## __How to Compile__
## __Download__
Nightly build could be found here.
Windows nightly build is not supported yet,
please wait or just compile it by yourself, a Cmake file is given for Visual Studio to compile this project easily:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [WIP]
## __Compile__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
Better download the latest update source of the interpreter and build it! It's quite easy to build this interpreter, what you need are only two things: C++ compiler and the `make`. There is no third-party library used in this project.
__CAUTION__: If want to use the release zip/tar.gz file to build the interpreter, please read the [__Release Notes__](./doc/dev.md#release-notes) to make sure this release file has no fatal bugs.
### __Windows (MinGW-w64)__
### __`Windows (MinGW-w64)`__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
Make sure your MinGW thread model is `posix thread model`, otherwise it may not have the thread library.
Make sure thread model is `posix thread model`, otherwise no thread library exists.
> mkdir build
>
> mingw32-make nasal.exe -j4
### __`Windows (Visual Studio)`__
### __Windows (Visual Studio)__
This project gives a [__CMakelists.txt__](./CMakeLists.txt) for you to create project in `Visual Studio`.
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
### __`Linux/macOS/Unix`__
There is a [__CMakelists.txt__](./CMakeLists.txt) to create project.
> mkdir build
>
> make -j4
### __Linux / macOS / Unix__
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU) ![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
> make -j
You could choose which compiler you want to use:
> make nasal CXX=...
> make nasal CXX=... -j
## __How to Use__
@@ -102,757 +115,14 @@ if (os.platform()=="windows") {
}
```
## __Tutorial__
Nasal is really __easy__ to learn.
Reading this tutorial will not takes you over 15 minutes.
__If you have learnt C/C++/Javascript before, this will take less time.__
You could totally use it after reading this simple tutorial:
<details><summary> Basic type </summary>
__`none`__ is error type used to interrupt the execution.
This type is not created by user program.
__`nil`__ is a null type. Just like `null`.
Or use `std.runtime.windows.set_utf8_output()`:
```javascript
var spc = nil;
use std.runtime;
runtime.windows.set_utf8_output();
```
__`num`__ has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 `double` to store.
```javascript
# this language use '#' to write notes
var n = 2.71828; # dec
var n = 2.147e16; # dec
var n = 1e-10; # dec
var n = 0xAA55; # hex
var n = 0o170001; # oct
# caution: true and false also useful in nasal now
var n = true; # in fact n is now 1.0
var n = false; # in face n is now 0.0
```
__`str`__ has 3 formats. The third one is used to declare a character.
```javascript
var s = 'str';
var s = "another string";
var s = `c`;
# some special characters is allowed in this language:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vec`__ has unlimited length and can store all types of values.
```javascript
var vec = [];
var vec = [0, nil, {}, [], func(){return 0}];
append(vec, 0, 1, 2);
```
__`hash`__ is a hashmap (or like a `dict` in `python`) that stores values with strings/identifiers as the key.
```javascript
var hash = {
member1: nil,
member2: "str",
"member3": "member\'s name can also be a string constant",
funct: func() {
return me.member2~me.member3;
}
};
```
__`func`__ is a function type (in fact it is `lambda`).
```javascript
var f = func(x, y, z) {
return nil;
}
# function could be declared without parameters and `(`, `)`
var f = func {
return 114514;
}
var f = func(x, y, z, deft = 1) {
return x+y+z+deft;
}
var f = func(args...) {
var sum = 0;
foreach(var i; args) {
sum += i;
}
return sum;
}
```
__`upval`__ is used to store upvalues, used in __`vm`__ to make sure closure runs correctly.
__`ghost`__ is used to store other complex `C/C++` data types.
This type is created by native-function of nasal. If want to define a new data type, see how to add native-functions by editing code.
</details>
<details><summary> Operators </summary>
Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that joints strings.
```javascript
1+2-(1+3)*(2+4)/(16-9);
"str1"~"str2";
```
For conditional expressions, operators `==` `!=` `<` `>` `<=` `>=` are used to compare two values.
`and` `or` have the same function as C/C++ `&&` `||`.
```javascript
1+1 and (1<0 or 1>0);
1<=0 and 1>=0;
1==0 or 1!=0;
```
Unary operators `-` `!` have the same function as C/C++.
```javascript
-1;
!0;
```
Bitwise operators `~` `|` `&` `^` have the same function as C/C++.
```javascript
# these operators will:
# 1. convert f64 to i32 (static_cast<int32_t>)
# 2. do the bitwise function
~0x80000000; # not 2147483647
0x8|0x1; # or
0x1&0x2; # and
0x8^0x1; # xor
```
Operators `=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=` are used in assignment expressions.
```javascript
a = b = c = d = 1;
a += 1;
a -= 1;
a *= 1;
a /= 1;
a ~= "string";
a ^= 0xff;
a &= 0xca;
a |= 0xba;
```
</details>
<details><summary> Definition </summary>
As follows.
```javascript
var a = 1; # define single variable
var (a, b, c) = [0, 1, 2]; # define multiple variables from a vector
var (a, b, c) = (0, 1, 2); # define multiple variables from a tuple
```
Nasal has many special global symbols:
```javascript
globals; # hashmap including all global symbols and their values
arg; # in global scope, arg is the command line arguments
# in local scope, arg is the dynamic arguments of this function call
```
For example:
```javascript
var a = 1;
println(globals); # will print {a:1}
```
```javascript
# nasal a b c
println(arg); # will print ["a", "b", "c"]
func() {
println(arg);
}(1, 2, 3); # will print [1, 2, 3]
```
</details>
<details><summary> Multi-assignment </summary>
The last one is often used to swap two variables.
```javascript
(a, b[0], c.d) = [0, 1, 2];
(a, b[1], c.e) = (0, 1, 2);
(a, b) = (b, a);
```
</details>
<details><summary> Conditional expression </summary>
In nasal there's a new key word `elsif`.
It has the same functions as `else if`.
```javascript
if (1) {
;
} elsif (2) {
;
} else if (3) {
;
} else {
;
}
```
</details>
<details><summary> Loop </summary>
While loop and for loop is simalar to C/C++.
```javascript
while(condition) {
continue;
}
for(var i = 0; i<10; i += 1) {
break;
}
```
Nasal has another two kinds of loops that iterates through a vector:
`forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`.
```javascript
forindex(var i; elem) {
print(elem[i]);
}
```
`foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`.
```javascript
foreach(var i; elem) {
print(i);
}
```
</details>
<details><summary> Subvec </summary>
Nasal provides this special syntax to help user generate a new vector by getting values by one index or getting values by indexes in a range from an old vector.
If there's only one index in the bracket, then we will get the value directly.
Use index to search one element in the string will get the __ascii number__ of this character.
If you want to get the character, use built-in function `chr()`.
```javascript
a[0];
a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0];
```
</details>
<details><summary> Special function call </summary>
This is not very efficient,
because hashmap use string as the key to compare.
But if it really useful, the efficientcy may not be so important...
```javascript
f(x:0, y:nil, z:[]);
```
</details>
<details><summary> Lambda </summary>
Also functions have this kind of use:
```javascript
func(x, y) {
return x+y
}(0, 1);
func(x) {
return 1/(1+math.exp(-x));
}(0.5);
```
There's an interesting test file `y-combinator.nas`,
try it for fun:
```javascript
var fib = func(f) {
return f(f);
}(
func(f) {
return func(x) {
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
}
}
);
```
</details>
<details><summary> Closure </summary>
Closure means you could get the variable that is not in the local scope of a function that you called.
Here is an example, result is `1`:
```javascript
var f = func() {
var a = 1;
return func() {return a;};
}
print(f()());
```
Using closure makes it easier to OOP.
```javascript
var student = func(n, a) {
var (name, age) = (n, a);
return {
print_info: func() {println(name, ' ', age);},
set_age: func(a) {age = a;},
get_age: func() {return age;},
set_name: func(n) {name = n;},
get_name: func() {return name;}
};
}
```
</details>
<details><summary> Trait </summary>
Also there's another way to OOP, that is `trait`.
When a hash has a member named `parents` and the value type is vector,
then when you are trying to find a member that is not in this hash,
virtual machine will search the member in `parents`.
If there is a hash that has the member, you will get the member's value.
Using this mechanism, we could OOP like this, the result is `114514`:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
a.set(114514);
println(a.get());
```
First virtual machine cannot find member `set` in hash `a`, but in `a.parents` there's a hash `trait` has the member `set`, so we get the `set`.
variable `me` points to hash `a`, so we change the `a.val`.
And `get` has the same process.
And we must remind you that if you do this:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
var b = class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c = a.get;
var d = b.get;
println(c());
println(c());
println(d());
println(d());
```
You will get this result now:
```bash
114
514
514
514
514
514
```
Because `a.get` will set `me=a` in the `trait.get`. Then `b.get` do the `me=b`. So in fact c is `b.get` too after running `var d=b.get`.
If you want to use this trick to make the program running more efficiently, you must know this special mechanism.
</details>
<details><summary> Native functions and module import </summary>
This part shows how we add native functions in this interpreter.
If you are interested in this part, this may help you.
And...
__CAUTION:__ If you want to add your own functions __without__ changing the source code, see the __`module`__ after this part.
If you really want to change source code, check built-in functions in `lib.nas` and see the example below.
Definition:
```C++
// you could also use a macro to define one.
var builtin_print(context*, gc*);
```
Then complete this function using C++:
```C++
var builtin_print(context* ctx, gc* ngc) {
// find value with index begin from 1
// because local[0] is reserved for value 'me'
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
// generate return value,
// use ngc::alloc(type) to make a new value
// or use reserved reference nil/one/zero
return nil;
}
```
When running a builtin function, alloc will run more than one time, this may cause mark-sweep in `gc::alloc`.
The value got before will be collected, but stil in use in this builtin function, this will cause a fatal error.
So use `gc::temp` in builtin functions to temprorarily store the gc-managed value that you want to return later. Like this:
```C++
var builtin_keys(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// use gc.temp to store the gc-managed-value, to avoid being sweeped
auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc->newstr(iter.first));
}
}
ngc->temp = nil;
return res;
}
```
After that, register the built-in function's name(in nasal) and the function's pointer in this table:
```C++
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{nullptr, nullptr}
};
```
At last,warp the `__print` in a nasal file:
```javascript
var print = func(elems...) {
return __print(elems);
};
```
In fact the arguments that `__print` uses are not necessary.
So writting it like this is also right:
```javascript
var print = func(elems...) {
return __print;
};
```
If you don't warp built-in function in a normal nasal function,
this native function may cause __segmentation fault__ when searching arguments.
Use `import("filename.nas")` to get the nasal file including your built-in functions, then you could use it.
Also there's another way of importing nasal files, the two way of importing have the same function:
```javascript
use dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
</details>
<details><summary> Modules (for lib developers) </summary>
If there is only one way to add your own functions into nasal,
that is really inconvenient.
Luckily, we have developed some useful native-functions to help you add modules that created by you.
Functions used to load dynamic libraries are added to `std/dylib.nas`:
```javascript
var dlopen = func(libname) {
...
}
var dlclose = func(lib) {
...
}
var dlcall = func(ptr, args...) {
...
}
var limitcall = func(arg_size = 0) {
...
}
```
As you could see, these functions are used to load dynamic libraries into the nasal runtime and execute.
Let's see how they work.
First, write a cpp file that you want to generate the dynamic lib, take the `fib.cpp` as the example(example codes are in `./module`):
```C++
// add header file nasal.h to get api
#include "nasal.h"
double fibonaci(double x) {
if (x<=2) {
return x;
}
return fibonaci(x-1)+fibonaci(x-2);
}
// module functions' parameter list example
var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib", "lack arguments");
}
// the arguments are generated into a vm_vec: args
// get values from the vector that must be used here
var num = args[0];
// if you want your function safer, try this
// nas_err will print the error info on screen
// and return vm_null for runtime to interrupt
if(num.type!=vm_num) {
return nas_err("extern_fib", "\"num\" must be number");
}
// ok, you must know that vm_num now is not managed by gc
// if want to return a gc object, use ngc->alloc(type)
// usage of gc is the same as adding a native function
return var::num(fibonaci(num.tonum()));
}
// then put function name and address into this table
// make sure the end of the table is {nullptr,nullptr}
module_func_info func_tbl[] = {
{"fib", fib},
{nullptr, nullptr}
};
// must write this function, this will help nasal to
// get the function pointer by name
// the reason why using this way to get function pointer
// is because `var` has constructors, which is not compatiable in C
// so "extern "C" var fib" may get compilation warnings
extern "C" module_func_info* get() {
return func_tbl;
}
```
Next, compile this `fib.cpp` into dynamic lib.
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac(`.so` & `.dylib`): same as Linux.
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
Then we write a test nasal file to run this fib function, using `os.platform()` we could write a cross-platform program:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib);
```
`dylib.dlopen` is used to load dynamic library and get the function address.
`dylib.dlcall` is used to call the function, the first argument is the function address, make sure this argument is `vm_obj` and `type=obj_extern`.
`dylib.dlclose` is used to unload the library, at the moment that you call the function, all the function addresses that got from it are invalid.
`dylib.limitcall` is used to get `dlcall` function that has limited parameter size, this function will prove the performance of your code because it does not use `vm_vec` to store the arguments, instead it uses local scope to store them, so this could avoid frequently garbage collecting. And the code above could also be written like this:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i += 1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib);
```
If get this, Congratulations!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
</details>
<details><summary> Ghost Type (for lib developers) </summary>
It's quite easy to create a new ghost by yourself now.
Look at the example below:
```c++
const auto ghost_for_test = "ghost_for_test";
// declare destructor for ghost type
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<u32*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj);
// create ghost type
res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
// check ghost type by the type name
if (!res.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.ghost().pointer) << "\n";
return nil;
}
```
We use this function to create a new ghost type:
`void nas_ghost::set(const std::string&, nasal::nas_ghost::destructor, void*);`
`const std::string&` is the name of the ghost type.
`nasal::nas_ghost::destructor` is the pointer of the destructor of the ghost type.
`void*` is the pointer of the ghost type instance.
And we use this function to check if value is the correct ghost type:
`bool var::object_check(const std::string&);`
The parameter is the name of the ghost type.
</details>
## __Difference Between Andy's and This Interpreter__
![error](./doc/gif/error.gif)
@@ -1073,9 +343,8 @@ and the debugger will print this:
```javascript
source code:
--> var fib=func(x)
{
if(x<2) return x;
--> var fib = func(x) {
if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
@@ -1083,16 +352,16 @@ source code:
next bytecode:
0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427)
0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427)
0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423)
0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423)
0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423)
--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1)
0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1)
0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1)
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
--> 0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
stack (0x55ccd0a1b9d0, limit 10, total 0)
vm stack (0x7fca7e9f1010, limit 16, total 0)
>>
```
@@ -1107,9 +376,8 @@ This will help you debugging or learning how the vm works:
```javascript
source code:
var fib=func(x)
{
--> if(x<2) return x;
var fib = func(x) {
--> if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
@@ -1117,24 +385,24 @@ source code:
next bytecode:
0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1)
--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3)
0x000853 40 00 08 56 jf 0x856(test/fib.nas:3)
0x000854 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000855 56 00 00 00 ret 0x0(test/fib.nas:3)
0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4)
0x000857 45 00 00 01 calll 0x1(test/fib.nas:4)
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
--> 0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
stack (0x55ccd0a1b9d0, limit 10, total 8)
0x000007 | pc | 0x869
vm stack (0x7fca7e9f1010, limit 16, total 8)
0x000007 | pc | 0x3c7
0x000006 | addr | 0x0
0x000005 | nil |
0x000004 | nil |
0x000003 | num | 0
0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x55ccd0a58fa0> entry:0x487
0x000000 | func | <0x5573f66ef5f0> func(elems...) {..}
>>
```

View File

@@ -1,20 +1,21 @@
# __Nasal - Modern Interpreter__
<img src="../doc/pic/header.png" style="width:800px"></img>
<img src="../doc/pic/header.png" style="width:600px"></img>
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.2-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
[![C/C++ CI](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
## __目录__
* [__简介__](#简介)
* [__下载__](#下载)
* [__编译__](#编译)
* [__使用方法__](#使用方法)
* [__教程__](#教程)
* [__教程__](../doc/tutorial_zh.md)
* [__发行日志__](../doc/dev_zh.md#发行日志)
* [__开发历史__](../doc/dev_zh.md)
* [__测试数据__](../doc/benchmark.md)
@@ -25,67 +26,82 @@
__如果有好的意见或建议欢迎联系我们!__
* __E-mail__: __lhk101lhk101@qq.com__(ValKmjolnir) __1467329765@qq.com__(Sidi762)
* __lhk101lhk101@qq.com__ (ValKmjolnir)
* __sidi.liang@gmail.com__ (Sidi)
## __简介__
![star](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![fork](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![issue](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![pr](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
是一款语法与ECMAscript相似的编程语言并作为运行脚本被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。
是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。
该语言的设计者为 [Andy Ross](https://github.com/andyross)。
该解释器由 [ValKmjolnir](https://github.com/ValKmjolnir) 使用 `C++`(`-std=c++17`)重新实现。非常感谢 Andy 为我们设计了这个神奇且简洁的编程语言: [Andy Ross 的 nasal 解释器](https://github.com/andyross/nasal)。
解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++17`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言
项目旧版本使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始新版本使用 __GPL v2__ 协议
该项目使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始使用 __GPL v2__ 协议。
### __为什么重新写 Nasal 解释器?__
### __我们为什么想要重新写一个nasal解释器?__
2019 年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在 Flightgear 中提供的 nasal 控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。
2019年暑假[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我在Flightgear中提供的nasal控制台窗口中进行调试很不方便仅仅是想检查语法错误也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误
我编写了 nasal 的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行 nasal 程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率
我编写了nasal的词法分析器和语法分析器以及一个全新的字节码虚拟机并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率
你也可以使用这个语言来写一些与 Flightgear 运行环境无关的有趣的程序,并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块,使它成为项目中一个非常有用的工具
你也可以使用这个语言来写一些与Flightgear运行环境无关的有趣的程序并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块使它成为项目中一个非常有用的工具。
## __下载__
现在支持下载预览版(Nightly Build)。
Windows 平台的预览版解释器现在还没配置相关流水线,
请耐心等候或者直接在本地编译。
我们提供了一份 Cmake 文件,可以很方便地在 Visual Studio 中编译:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [施工中...]
## __编译__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)
![vs](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio)
我们推荐下载最新代码包编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
只需要这两样东西: C++ 编译器以及make程序。
推荐下载最新代码包编译,这个项目非常小巧, 没有使用任何第三方库,因此编译起来非常轻松,
只需要这两样东西: C++ 编译器以及make程序。
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
### __Windows 平台 (MinGW-w64)__
### __`Windows` 平台(`MinGW-w64`)__
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
一定要确保您的 MinGW thread model 是 `posix thread model`, 否则可能存在没有 thread 库的问题
确保 thread model 是 `posix thread model`, 否则没有 thread 库。
> mkdir build
>
> mingw32-make nasal.exe -j4
### __`Windows` 平台(`Vistual Studio`)__
### __Windows 平台 (Vistual Studio)__
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中用这种方式来创建项目。
![windows](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows)
### __`Linux/macOS/Unix` 平台__
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中创建项目。
> mkdir build
>
> make -j4
### __Linux / macOS / Unix 平台__
![linux](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU)
![macOS](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple)
> make -j
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
> make nasal CXX=...
> make nasal CXX=... -j
## __使用方法__
![usage](../doc/gif/help.gif)
如果你是 `Windows` 用户且想正常输出unicode在nasal代码里写这个来开启unicode代码页:
如果你是 `Windows` 用户且想正常输出 unicode可以这样开启 unicode 代码页:
```javascript
if (os.platform()=="windows") {
@@ -93,738 +109,14 @@ if (os.platform()=="windows") {
}
```
## __教程__
Nasal是非常容易上手的你甚至可以在15分钟之内看完这里的基本教程并且直接开始编写你想要的程序。
__如果你先前已经是C/C++, javascript选手那么几乎可以不用看这个教程……__ 在看完该教程之后,基本上你就完全掌握了这个语言:
<details><summary>基本类型</summary>
__`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行,该类型只能由虚拟机在抛出错误时产生。
__`nil`__ 是空类型。类似于null。
或者使用 `std.runtime.windows.set_utf8_output()`:
```javascript
var spc = nil;
use std.runtime;
runtime.windows.set_utf8_output();
```
__`num`__ 有三种形式:十进制十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。
```javascript
# 该语言用 '#' 来作为注释的开头
var n = 2.71828; # dec 十进制
var n = 2.147e16; # dec 十进制
var n = 1e-10; # dec 十进制
var n = 0xAA55; # hex 十六进制
var n = 0o170001; # oct 八进制
# 注意: true false 关键字在现在的 nasal 里也是可用的
var n = true; # n 实际上是数字 1.0
var n = false; # n 实际上是数字 0.0
```
__`str`__ 也有三种不同的格式。第三种只允许包含一个的字符。
```javascript
var s = 'str';
var s = "another string";
var s = `c`;
# 该语言也支持一些特别的转义字符:
'\a'; '\b'; '\e'; '\f';
'\n'; '\r'; '\t'; '\v';
'\0'; '\\'; '\?'; '\'';
'\"';
```
__`vec`__ 有不受限制的长度并且可以存储所有类型的数据。(当然不能超过可分配内存空间的长度)
```javascript
var vec = [];
var vec = [0, nil, {}, [], func(){return 0}];
append(vec, 0, 1, 2);
```
__`hash`__ 使用哈希表 (类似于`python`中的`dict`)通过键值对来存储数据。key可以是一个字符串也可以是一个标识符。
```javascript
var hash = {
member1: nil,
member2: "str",
"member3": "member\'s name can also be a string constant",
funct: func() {
return me.member2~me.member3;
}
};
```
__`func`__ 函数类型。(实际上在这个语言里函数是一种`lambda`表达式)
```javascript
var f = func(x, y, z) {
return nil;
}
# 函数声明可以没有参数列表以及 `(`, `)`
var f = func {
return 114514;
}
var f = func(x, y, z, deft = 1) {
return x+y+z+deft;
}
var f = func(args...) {
var sum = 0;
foreach(var i; args) {
sum += i;
}
return sum;
}
```
__`upval`__ 是存储闭包数据的特殊类型, 在 __`vm`__ 中使用,以确保闭包功能正常。
__`ghost`__ 是用来存储`C/C++`的一些复杂数据结构。这种类型的数据由内置函数生成。如果想为nasal添加新的数据结构, 可以看下文如何通过修改本项目来添加内置函数。
</details>
<details><summary>运算符</summary>
Nasal拥有基本的四种数学运算符 `+` `-` `*` `/`以及一个特别的运算符 `~`,用于拼接字符串。
```javascript
1+2-(1+3)*(2+4)/(16-9);
"str1"~"str2";
```
对于条件语句,可以使用`==` `!=` `<` `>` `<=` `>=`来比较数据。`and` `or` 与C/C++中 `&&` `||`运算符一致。
```javascript
1+1 and (1<0 or 1>0);
1<=0 and 1>=0;
1==0 or 1!=0;
```
单目运算符`-` `!`与C/C++中的运算符功能类似。
```javascript
-1;
!0;
```
位运算符`~` `|` `&` `^`与C/C++中的运算符功能类似。
```javascript
# 运行过程:
# 1. f64 强转为 i32 (static_cast<int32_t>)
# 2. 执行位运算符
~0x80000000; # 按位取反 2147483647
0x8|0x1; # 按位或
0x1&0x2; # 按位与
0x8^0x1; # 按位异或
```
赋值运算符`=` `+=` `-=` `*=` `/=` `~=` `^=` `&=` `|=`正如其名,用于进行赋值。
```javascript
a = b = c = d = 1;
a += 1;
a -= 1;
a *= 1;
a /= 1;
a ~= "string";
a ^= 0xff;
a &= 0xca;
a |= 0xba;
```
</details>
<details><summary>定义变量</summary>
如下所示。
```javascript
var a = 1; # 定义单个变量
var (a, b, c) = [0, 1, 2]; # 从数组中初始化多个变量
var (a, b, c) = (0, 1, 2); # 从元组中初始化多个变量
```
Nasal 有很多特别的全局变量:
```javascript
globals; # 包含所有全局声明变量名和对应数据的哈希表
arg; # 在全局作用域arg 是包含命令行参数的数组
# 在局部作用域arg 是函数调用时的动态参数数组
```
具体实例:
```javascript
var a = 1;
println(globals); # 输出 {a:1}
```
```javascript
# nasal a b c
println(arg); # 输出 ["a", "b", "c"]
func() {
println(arg);
}(1, 2, 3); # 输出 [1, 2, 3]
```
</details>
<details><summary>多变量赋值</summary>
最后这个语句通常用于交换两个变量的数据类似于Python中的操作。
```javascript
(a, b[0], c.d) = [0, 1, 2];
(a, b[1], c.e) = (0, 1, 2);
(a, b) = (b, a);
```
</details>
<details><summary>条件语句</summary>
nasal在提供`else if`的同时还有另外一个关键字`elsif`。该关键字与`else if`有相同的功能。
```javascript
if (1) {
;
} elsif (2) {
;
} else if (3) {
;
} else {
;
}
```
</details>
<details><summary>循环语句</summary>
while循环和for循环大体上与C/C++是一致的。
```javascript
while(condition) {
continue;
}
for(var i = 0; i<10; i += 1) {
break;
}
```
同时nasal还有另外两种直接遍历列表的循环方式:
`forindex` 会获取列表的下标,依次递增. 下标会从`0`递增到`size(elem)-1`结束。
```javascript
forindex(var i; elem) {
print(elem[i]);
}
```
`foreach`会依次直接获取列表中的数据. 这些数据会从`elem[0]`依次获取到`elem[size(elem)-1]`.
```javascript
foreach(var i; elem) {
print(i);
}
```
</details>
<details><summary>生成子列表(subvec)</summary>
nasal提供了下面第一句的类似语法来从列表中随机或者按照一个区间获取数据并且拼接生成一个新的列表。当然如果中括号内只有一个下标的话你会直接获得这个下标对应的数据而不是一个子列表。如果直接对string使用下标来获取内容的话会得到对应字符的 __ascii值__。如果你想进一步获得这个字符串,可以尝试使用内置函数`chr()`
```javascript
a[0];
a[-1, 1, 0:2, 0:, :3, :, nil:8, 3:nil, nil:nil];
"hello world"[0];
```
</details>
<details><summary>特殊函数调用语法</summary>
这种调用方式不是很高效,因为哈希表会使用字符串比对来找到数据存放的位置。
然而如果它用起来非常舒适,那效率也显得不是非常重要了……
```javascript
f(x:0, y:nil, z:[]);
```
</details>
<details><summary>lambda表达式</summary>
函数有这样一种直接编写函数体并且立即调用的方式:
```javascript
func(x, y) {
return x+y;
}(0, 1);
func(x) {
return 1/(1+math.exp(-x));
}(0.5);
```
测试文件中有一个非常有趣的文件`y-combinator.nas`,可以试一试:
```javascript
var fib = func(f) {
return f(f);
}(
func(f) {
return func(x) {
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
}
}
);
```
</details>
<details><summary>闭包</summary>
闭包是一种特别的作用域,你可以从这个作用域中获取其保存的所有变量,
而这些变量原本不是你当前运行的函数的局部作用域中的。
下面这个例子里,结果是`1`:
```javascript
var f = func() {
var a = 1;
return func() {return a;};
}
print(f()());
```
如果善用闭包,你可以使用它来进行面向对象编程。
```javascript
var student = func(n, a) {
var (name, age) = (n, a);
return {
print_info: func() {println(name, ' ', age);},
set_age: func(a) {age = a;},
get_age: func() {return age;},
set_name: func(n) {name = n;},
get_name: func() {return name;}
};
}
```
</details>
<details><summary>特性与继承</summary>
当然,也有另外一种办法来面向对象编程,那就是利用`trait`
当一个hash类型中有一个成员的key是`parents`,并且该成员是一个数组的话,
那么当你试图从这个hash中寻找一个它自己没有的成员名时虚拟机会进一步搜索`parents`数组。
如果该数组中有一个hash类型有一个成员的key与当前你搜索的成员名一致
那么你会得到这个成员对应的值。
使用这个机制,我们可以进行面向对象编程,下面样例的结果是`114514`:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
a.set(114514);
println(a.get());
```
首先虚拟机会发现在`a`中找不到成员`set`,但是在`a.parents`中有个hash类型`trait`存在该成员,所以返回了这个成员的值。
成员`me`指向的是`a`自身,类似于一些语言中的`this`,所以我们通过这个函数,实际上修改了`a.val``get`函数的调用实际上也经过了相同的过程。
不过我们必须提醒你一点如果你在这个地方使用该优化来减少hash的搜索开销:
```javascript
var trait = {
get: func {return me.val;},
set: func(x) {me.val = x;}
};
var class = {
new: func() {
return {
val: nil,
parents: [trait]
};
}
};
var a = class.new();
var b = class.new();
a.set(114);
b.set(514);
println(a.get());
println(b.get());
var c = a.get;
var d = b.get;
println(c());
println(c());
println(d());
println(d());
```
那么你会发现现在虚拟机会输出这个结果:
```bash
114
514
514
514
514
514
```
因为执行`a.get`时在`trait.get`函数的属性中进行了`me=a`的操作。而`b.get`则执行了`me=b`的操作。所以在运行`var d=b.get`后实际上c也变成`b.get`了。
如果你想要用这种小技巧来让程序运行更高效的话,最好是要知道这里存在这样一个机制。
</details>
<details><summary>原生内置函数以及模块导入(import)语法</summary>
这个部分对于纯粹的使用者来说是不需要了解的,
它将告诉你我们是如何为解释器添加新的内置函数的。
如果你对此很感兴趣,那么这个部分可能会帮到你,并且……
__警告:__ 如果你 __不想__ 通过直接修改解释器源码来添加你自定义的函数,那么你应该看下一个节 __`模块`__ 的内容。
如果你确实是想修改源码来搞一个自己私人订制的解释器 ———— “我他妈就是想自己私人订制,你们他妈的管得着吗?”,
参考源码中关于内置函数的部分,以及`lib.nas`中是如何包装这些函数的,下面是其中一个样例:
定义新的内置函数:
```C++
// 你可以使用这个宏来直接定义一个新的内置函数
var builtin_print(context*, gc*);
```
然后用C++完成这个函数的函数体:
```C++
var builtin_print(context* ctx, gc* ngc) {
// 局部变量的下标其实是从 1 开始的
// 因为 local[0] 是保留给 'me' 的空间
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
// 最后生成返回值,返回值必须是一个内置的类型,
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
// 或者用我们已经定义好的nil/one/zero这些可以直接使用
return nil;
}
```
当运行内置函数的时候内存分配器如果运行超过一次那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量这样可以防止内部所有的申请错误触发垃圾回收。如下所示
```C++
var builtin_keys(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// 使用gc.temp来存储gc管理的变量防止错误的回收
auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc->newstr(iter.first));
}
}
ngc->temp = nil;
return res;
}
```
这些工作都完成之后在内置函数注册表中填写它在nasal中的别名并且在表中填对这个函数的函数指针:
```C++
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{nullptr, nullptr}
};
```
最后将其包装到nasal文件中:
```javascript
var print = func(elems...) {
return __print(elems);
};
```
事实上`__print`后面跟着的传参列表不是必须要写的。所以这样写也对:
```javascript
var print = func(elems...) {
return __print;
};
```
如果你不把内置函数包装到一个普通的nasal函数中那么直接调用这个内置函数会在参数传入阶段出现 __segmentation fault(段错误)__。
在nasal文件中使用`import("文件名.nas")`可以导入该文件中你包装的所有内置函数,接下来你就可以使用他们了。
当然也有另外一种办法来导入这些nasal文件下面两种导入方式的效果是一样的
```javascript
use dirname.dirname.filename;
import("./dirname/dirname/filename.nas");
```
</details>
<details><summary>模块(开发者教程)</summary>
如果只有上文中那种方式来添加你自定义的函数到nasal中这肯定是非常麻烦的。
因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。
用于加载动态库的函数在`std/dylib.nas`中:
```javascript
var dlopen = func(libname) {
...
}
var dlclose = func(lib) {
...
}
var dlcall = func(ptr, args...) {
...
}
var limitcall = func(arg_size = 0) {
...
}
```
这些函数是用来加载动态库的这样nasal解释器可以根据用户需求灵活加载动态库来执行。让我们看看这些函数该如何使用。
首先用C++写个项目,并且编译成动态库。我们就拿`fib.cpp`作为例子来说明(样例代码可以在`./module`中找到):
```C++
// 这个头文件得加上因为我们需要拿到nasal的api
#include "nasal.h"
double fibonaci(double x) {
if (x<=2) {
return x;
}
return fibonaci(x-1)+fibonaci(x-2);
}
// 模块函数的参数列表一律以这个为准
var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib", "lack arguments");
}
// 传参会给予一个var指针指向一个vm_vec的data()
var num = args[0];
// 如果你想让这个函数有更强的稳定性,那么一定要进行合法性检查
// nas_err会输出错误信息并返回错误类型让虚拟机终止执行
if(num.type!=vm_num) {
return nas_err("extern_fib", "\"num\" must be number");
}
// vm_num作为普通的数字类型不是内存管理的对象所以无需申请
// 如果需要返回内存管理的对象请使用ngc->alloc(type)
return var::num(fibonaci(num.tonum()));
}
// 然后将函数名字和函数地址放到一个表里,一定要记住表尾是{nullptr,nullptr}
module_func_info func_tbl[] = {
{"fib", fib},
{nullptr, nullptr}
};
// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针
// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的
// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致
// 类似 "extern "C" var fib" 的写法会得到编译错误
extern "C" module_func_info* get() {
return func_tbl;
}
```
接着我们把`fib.cpp`编译成动态库。
Linux(`.so`):
`clang++ -c -O3 fib.cpp -fPIC -o fib.o`
`clang++ -shared -o libfib.so fib.o`
Mac(`.so` & `.dylib`): 和Linux下操作相同。
Windows(`.dll`):
`g++ -c -O3 fib.cpp -fPIC -o fib.o`
`g++ -shared -o libfib.dll fib.o`
好了那么我们可以写一个测试用的nasal代码来运行这个斐波那契函数了。
下面例子中`os.platform()`是用来检测当前运行的系统环境的,这样可以实现跨平台:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
for(var i = 1; i<30; i += 1)
println(dylib.dlcall(fib, i));
dylib.dlclose(dlhandle.lib);
```
`dylib.dlopen`用于加载动态库并从动态库中获得函数地址。
`dylib.dlcall`用于调用函数,第一个参数是动态库函数的地址,这是个特殊类型,一定要保证这个参数是`vm_obj`类型并且`type=obj_extern`。
`dylib.dlclose`用于卸载动态库,当然,在这个函数调用之后,所有从该库中获取的函数都作废。
`dylib.limitcall`用于获取使用固定长度传参的 `dlcall` 函数,这种函数可以提高你的程序运行效率,因为它不需要用 `vm_vec` 来存储传入参数,而是使用局部作用域来直接存储,从而避免了频繁调用可能导致的频繁垃圾收集。所以上面展示的代码同样可以这样写:
```javascript
use std.dylib;
var dlhandle = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib = dlhandle.fib;
var invoke = dylib.limitcall(1); # this means the called function has only one parameter
for(var i = 1; i<30; i += 1)
println(invoke(fib, i));
dylib.dlclose(dlhandle.lib);
```
如果得到如下运行结果,恭喜你!
```bash
./nasal a.nas
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025
121393
196418
317811
514229
832040
```
</details>
<details><summary> 自定义类型(开发者教程) </summary>
创建一个自定义类型很容易。下面是使用示例:
```c++
const auto ghost_for_test = "ghost_for_test";
// 声明自定义类型的析构函数
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<u32*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj);
// 创建自定义类型
res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
// 用自定义类型的名字来检查是否是正确的自定义类型
if (!res.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.ghost().pointer) << "\n";
return nil;
}
```
我们使用下面这个函数来创建一个自定义类型:
`void nas_ghost::set(const std::string&, nasal::nas_ghost::destructor, void*);`
`const std::string&` 是自定义类型的类型名。
`nasal::nas_ghost::destructor` 是自定义类型的析构函数指针。
`void*` 是指向自定义类型实例的指针。
我们使用下面的这个函数检测是否是正确的自定义类型:
`bool var::object_check(const std::string&);`
参数是自定义类型的类型名。
</details>
## __与andy解释器的不同之处__
![error](../doc/gif/error.gif)
@@ -1036,9 +328,8 @@ local (0x55dcb5b43190 <+7>)
```javascript
source code:
--> var fib=func(x)
{
if(x<2) return x;
--> var fib = func(x) {
if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
@@ -1046,16 +337,16 @@ source code:
next bytecode:
0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427)
0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427)
0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423)
0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423)
0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423)
--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1)
0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1)
0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1)
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
--> 0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
stack (0x55ccd0a1b9d0, limit 10, total 0)
vm stack (0x7fca7e9f1010, limit 16, total 0)
>>
```
@@ -1070,9 +361,8 @@ stack (0x55ccd0a1b9d0, limit 10, total 0)
```javascript
source code:
var fib=func(x)
{
--> if(x<2) return x;
var fib = func(x) {
--> if (x<2) return x;
return fib(x-1)+fib(x-2);
}
for(var i=0;i<31;i+=1)
@@ -1080,24 +370,24 @@ source code:
next bytecode:
0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1)
--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3)
0x000853 40 00 08 56 jf 0x856(test/fib.nas:3)
0x000854 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000855 56 00 00 00 ret 0x0(test/fib.nas:3)
0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4)
0x000857 45 00 00 01 calll 0x1(test/fib.nas:4)
0x0003a8 07:00 00 00 00 00 00 00 00 pnil 0x0 (std/lib.nas:413)
0x0003a9 56:00 00 00 00 00 00 00 00 ret 0x0 (std/lib.nas:413)
0x0003aa 03:00 00 00 00 00 00 00 56 loadg 0x56 (std/lib.nas:413)
0x0003ab 0b:00 00 00 00 00 00 03 af newf 0x3af (test/fib.nas:1)
0x0003ac 02:00 00 00 00 00 00 00 03 intl 0x3 (test/fib.nas:1)
0x0003ad 0d:00 00 00 00 00 00 00 22 para 0x22 (x) (test/fib.nas:1)
0x0003ae 3e:00 00 00 00 00 00 03 be jmp 0x3be (test/fib.nas:1)
--> 0x0003af 45:00 00 00 00 00 00 00 01 calll 0x1 (test/fib.nas:2)
stack (0x55ccd0a1b9d0, limit 10, total 8)
0x000007 | pc | 0x869
vm stack (0x7fca7e9f1010, limit 16, total 8)
0x000007 | pc | 0x3c7
0x000006 | addr | 0x0
0x000005 | nil |
0x000004 | nil |
0x000003 | num | 0
0x000002 | nil |
0x000001 | nil |
0x000000 | func | <0x55ccd0a58fa0> entry:0x487
0x000000 | func | <0x5573f66ef5f0> func(elems...) {..}
>>
```

View File

@@ -110,3 +110,13 @@ And we use this bf interpreter to draw a mandelbrot set.
In 2022/2/17 update we added `\e` into the lexer. And the `bfcolored.nas` uses this special ASCII code. Here is the result:
![mandelbrot](../doc/pic/mandelbrot.png)
## More Nasal Generated Pictures
|Mandelbrot Set|Mandelbrot Set|Julia Set|
|:----:|:----:|:----:|
|[mandelbrotset.nas](../test/mandelbrotset.nas)|[mandelbrotset.nas](../test/mandelbrotset.nas)|[juliaset.nas](../test/juliaset.nas)|
|![mandelbrotset](../doc/pic/mandelbrotset.png)|![mandelbrotset_reverse](../doc/pic/mandelbrotset_reverse.png)|![juliaset](../doc/pic/juliaset.png)|
|__Burning Ship__|__Burning Ship__|__Feigenbaum__|
|[burningship.nas](../test/burningship.nas)|[burningship.nas](../test/burningship.nas)|[feigenbaum.nas](../test/feigenbaum.nas)|
|![burningship](../doc/pic/burningship.png)|![burningship_reverse](../doc/pic/burningship_reverse.png)|![feigenbaum](../doc/pic/feigenbaum.png)|

View File

@@ -1,4 +1,4 @@
# __开发历史记录__
# __开发日志__
![buringship](./pic/burningship.png)

View File

@@ -44,6 +44,7 @@ In `std/example_module.nas`:
```nasal
var a = 1;
var _a = 1;
```
We analysed this file and generated the ast.
@@ -54,16 +55,20 @@ So the result is equal to:
```nasal
var example_module = func {
# source code begin
var a = 1;
var _a = 1;
# source code end
return {
a: a
# _a begins with underscore so do not export
};
}();
```
## Import a module
## Import a Module
Here is a module named `std/example_module.nas`:

View File

@@ -128,10 +128,10 @@
<li><a href="/lexer.nas">lexer.nas</a></li>
<li><a href="/life.nas">life.nas</a></li>
<li><a href="/loop.nas">loop.nas</a></li>
<li><a href="/mandel.nas">mandel.nas</a></li>
<li><a href="/mandelbrot.nas">mandelbrot.nas</a></li>
<li><a href="/mandelbrotset.nas">mandelbrotset.nas</a></li>
<li><a href="/mcpu.nas">mcpu.nas</a></li>
<li><a href="/md5.nas">md5.nas</a></li>
<li><a href="/md5_self.nas">md5_self.nas</a></li>
<li><a href="/md5compare.nas">md5compare.nas</a></li>
</ul>
</td>

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
doc/pic/juliaset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
doc/pic/mandelbrotset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

766
doc/tutorial.md Normal file
View File

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

745
doc/tutorial_zh.md Normal file
View File

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

135
makefile
View File

@@ -4,16 +4,16 @@ ifndef OS
OS = $(shell uname)
endif
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -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 -fno-exceptions -fPIC
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -I src
endif
NASAL_HEADER=\
NASAL_HEADER = \
src/ast_dumper.h\
src/ast_visitor.h\
src/nasal_ast.h\
src/nasal_builtin.h\
src/natives/builtin.h\
src/nasal_codegen.h\
src/nasal_dbg.h\
src/nasal_err.h\
@@ -27,16 +27,21 @@ NASAL_HEADER=\
src/nasal.h\
src/optimizer.h\
src/symbol_finder.h\
src/fg_props.h\
src/bits_lib.h\
src/io_lib.h\
src/math_lib.h\
src/dylib_lib.h\
src/unix_lib.h\
src/coroutine.h\
src/repl.h
src/cli/cli.h\
src/natives/fg_props.h\
src/natives/bits_lib.h\
src/natives/io_lib.h\
src/natives/math_lib.h\
src/natives/dylib_lib.h\
src/natives/json_lib.h\
src/natives/unix_lib.h\
src/natives/coroutine.h\
src/natives/regex_lib.h\
src/repl/repl.h\
src/util/fs.h\
src/util/util.h
NASAL_OBJECT=\
NASAL_OBJECT = \
build/nasal_err.o\
build/nasal_ast.o\
build/ast_visitor.o\
@@ -49,19 +54,23 @@ NASAL_OBJECT=\
build/nasal_opcode.o\
build/symbol_finder.o\
build/nasal_codegen.o\
build/nasal_misc.o\
build/nasal_gc.o\
build/nasal_builtin.o\
build/builtin.o\
build/fg_props.o\
build/io_lib.o\
build/math_lib.o\
build/unix_lib.o\
build/dylib_lib.o\
build/json_lib.o\
build/coroutine.o\
build/nasal_type.o\
build/nasal_vm.o\
build/nasal_dbg.o\
build/regex_lib.o\
build/repl.o\
build/cli.o\
build/fs.o\
build/util.o\
build/main.o
@@ -82,19 +91,33 @@ build:
build/main.o: $(NASAL_HEADER) src/main.cpp | build
$(CXX) $(CXXFLAGS) src/main.cpp -o build/main.o
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_misc.cpp -o build/nasal_misc.o
build/cli.o: src/cli/cli.h src/cli/cli.cpp | build
$(CXX) $(CXXFLAGS) src/cli/cli.cpp -o build/cli.o
build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
$(CXX) $(CXXFLAGS) src/repl.cpp -o build/repl.o
build/util.o: src/util/util.h src/util/util.cpp | build
$(CXX) $(CXXFLAGS) src/util/util.cpp -o build/util.o
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
build/fs.o: src/nasal.h src/util/util.h src/util/fs.h src/util/fs.cpp | build
$(CXX) $(CXXFLAGS) src/util/fs.cpp -o build/fs.o
build/repl.o: $(NASAL_HEADER) src/repl/repl.h src/repl/repl.cpp | build
$(CXX) $(CXXFLAGS) src/repl/repl.cpp -o build/repl.o
build/nasal_err.o: src/nasal.h src/repl/repl.h src/nasal_err.h src/nasal_err.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
build/nasal_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build
build/nasal_type.o:\
src/nasal.h\
src/util/util.h\
src/nasal_type.h src/nasal_type.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o
build/nasal_gc.o: src/nasal.h src/nasal_type.h src/nasal_gc.h src/nasal_gc.cpp | build
build/nasal_gc.o:\
src/nasal.h\
src/util/util.h\
src/nasal_type.h\
src/nasal_gc.h\
src/nasal_gc.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
build/nasal_import.o: \
@@ -102,12 +125,16 @@ build/nasal_import.o: \
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_parse.h\
src/util/util.h\
src/util/fs.h\
src/nasal_import.h src/nasal_import.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_import.cpp -o build/nasal_import.o
build/nasal_lexer.o: \
src/nasal.h\
src/repl.h\
src/repl/repl.h\
src/util/util.h\
src/util/fs.h\
src/nasal_err.h\
src/nasal_lexer.h src/nasal_lexer.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_lexer.cpp -o build/nasal_lexer.o
@@ -118,69 +145,87 @@ build/nasal_ast.o: \
src/nasal_ast.h src/nasal_ast.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_ast.cpp -o build/nasal_ast.o
build/nasal_builtin.o: \
build/builtin.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/nasal_builtin.h src/nasal_builtin.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
src/util/util.h\
src/natives/builtin.h\
src/natives/builtin.cpp | build
$(CXX) $(CXXFLAGS) src/natives/builtin.cpp -o build/builtin.o
build/coroutine.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/coroutine.h src/coroutine.cpp | build
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
src/natives/coroutine.h src/natives/coroutine.cpp | build
$(CXX) $(CXXFLAGS) src/natives/coroutine.cpp -o build/coroutine.o
build/bits_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/bits_lib.h src/bits_lib.cpp | build
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
src/natives/bits_lib.h src/natives/bits_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/bits_lib.cpp -o build/bits_lib.o
build/math_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/math_lib.h src/math_lib.cpp | build
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
src/natives/math_lib.h src/natives/math_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/math_lib.cpp -o build/math_lib.o
build/io_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/io_lib.h src/io_lib.cpp | build
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
src/util/fs.h\
src/natives/io_lib.h src/natives/io_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/io_lib.cpp -o build/io_lib.o
build/dylib_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/dylib_lib.h src/dylib_lib.cpp | build
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
src/natives/dylib_lib.h src/natives/dylib_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/dylib_lib.cpp -o build/dylib_lib.o
build/json_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/util/util.h\
src/natives/json_lib.h src/natives/json_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/json_lib.cpp -o build/json_lib.o
build/unix_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/unix_lib.h src/unix_lib.cpp | build
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
src/natives/unix_lib.h src/natives/unix_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/unix_lib.cpp -o build/unix_lib.o
build/regex_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/natives/regex_lib.h src/natives/regex_lib.cpp | build
$(CXX) $(CXXFLAGS) src/natives/regex_lib.cpp -o build/regex_lib.o
build/fg_props.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/fg_props.h src/fg_props.cpp | build
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
src/natives/fg_props.h src/natives/fg_props.cpp | build
$(CXX) $(CXXFLAGS) src/natives/fg_props.cpp -o build/fg_props.o
build/nasal_codegen.o: $(NASAL_HEADER) src/nasal_codegen.h src/nasal_codegen.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_codegen.cpp -o build/nasal_codegen.o
build/nasal_opcode.o: \
src/nasal.h\
src/nasal_builtin.h\
src/natives/builtin.h\
src/util/util.h\
src/nasal_opcode.h src/nasal_opcode.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_opcode.cpp -o build/nasal_opcode.o
@@ -189,6 +234,7 @@ build/nasal_parse.o: \
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_err.h\
src/util/util.h\
src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
$(CXX) $(CXXFLAGS) src/nasal_parse.cpp -o build/nasal_parse.o
@@ -220,6 +266,7 @@ build/ast_dumper.o: \
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/util/util.h\
src/ast_dumper.h src/ast_dumper.cpp | build
$(CXX) $(CXXFLAGS) src/ast_dumper.cpp -o build/ast_dumper.o
@@ -237,6 +284,8 @@ clean:
.PHONY: test
test:nasal
@ ./nasal -t -d test/andy_gc_test.nas
@ ./nasal test/argparse_test.nas
@ ./nasal -e test/ascii-art.nas
@ ./nasal -t -d test/bfs.nas
@ ./nasal -t test/bigloop.nas
@@ -269,9 +318,11 @@ test:nasal
@ ./nasal -t -d test/prime.nas
@ ./nasal -e test/qrcode.nas
@ ./nasal -t -d test/quick_sort.nas
@ ./nasal -t -d test/regex_test.nas
@ ./nasal -e test/scalar.nas hello world
@ ./nasal -e test/trait.nas
@ ./nasal -t -d test/turingmachine.nas
@ ./nasal -d test/wavecollapse.nas
@ ./nasal -d test/wavecity.nas
@ ./nasal test/word_collector.nas test/md5compare.nas
@ ./nasal -t -d test/ycombinator.nas

View File

@@ -25,7 +25,7 @@ var fib(var* args, usize size, gc* ngc) {
var quick_fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("quick_fib","lack arguments");
return nas_err("quick_fib", "lack arguments");
}
double num = args[0].to_num();
if (num<2) {
@@ -42,18 +42,44 @@ var quick_fib(var* args, usize size, gc* ngc) {
const auto ghost_for_test = "ghost_for_test";
struct ghost_obj {
u32 number = 0;
var test_string = nil;
};
// if the dynamic library is closed, the pointer of this function will be unsafe
// make sure gc deletes the object before closing the dynamic library
// or just do not close the dynamic library...
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<u32*>(ptr);
delete static_cast<ghost_obj*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n";
}
void ghost_for_test_gc_marker(void* ptr, std::vector<var>* bfs_queue) {
std::cout << "ghost_for_test::mark (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
bfs_queue->push_back(static_cast<ghost_obj*>(ptr)->test_string);
std::cout << " mark 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << "->test_string;\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj);
res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
var res = ngc->alloc(vm_type::vm_ghost);
res.ghost().set(
ghost_for_test,
ghost_for_test_destructor,
ghost_for_test_gc_marker,
new ghost_obj
);
return res;
}
@@ -64,8 +90,12 @@ var set_new_ghost(var* args, usize size, gc* ngc) {
return nil;
}
f64 num = args[1].num();
*(reinterpret_cast<u32*>(res.ghost().pointer)) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n";
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string = ngc->newstr("just for test");
std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n";
return nil;
}
@@ -75,8 +105,11 @@ var print_new_ghost(var* args, usize size, gc* ngc) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.ghost().pointer) << "\n";
std::cout << "print_new_ghost: " << res.ghost() << " number = "
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number
<< " test_string = "
<< reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string
<< "\n";
return nil;
}
@@ -91,7 +124,7 @@ module_func_info func_tbl[] = {
}
extern "C" module_func_info* get() {
NASAL_EXTERN module_func_info* get() {
return fib_module::func_tbl;
}

View File

@@ -7,6 +7,10 @@
#include <unistd.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4996)
#endif
#ifdef _WIN32
#include <conio.h>
#else
@@ -30,7 +34,8 @@ public:
tcgetattr(0, &init_termios);
new_termios = init_termios;
new_termios.c_lflag &= ~(ICANON|ECHO|ECHONL|ECHOE);
// vmin=0 is nonblock input, but in wsl there is a bug that will block input
// vmin = 0 is nonblock input,
// but in wsl1 there is a bug that will block input
// so we use fcntl to write the nonblock input
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
@@ -106,7 +111,7 @@ module_func_info func_tbl[] = {
{nullptr, nullptr}
};
extern "C" module_func_info* get() {
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}

View File

@@ -1,4 +1,5 @@
use std.dylib;
use std.os;
var _dl = dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
@@ -18,37 +19,40 @@ var _call = dylib.limitcall(1);
var _test_call = dylib.limitcall(2);
var fib = func(x) {
return _call(_fib, x)
}
var qfib = func(x) {
return _call(_qfib, x)
}
var create_ghost = func() {
return _zero_call(_create_ghost)
}
var set_ghost = func(object, x) {
return _test_call(_set_ghost, object, x)
}
var print_ghost = func(object) {
return _call(_print_ghost, object)
}
var test_ghost=func() {
var test_ghost = func() {
var ghost = create_ghost();
print_ghost(nil); # err
print("\n");
print_ghost(ghost); # random
print("\n");
set_ghost(nil, 114); # err
print("\n");
set_ghost(ghost, 114); # success
print("\n");
for(var i = 0; i<256; i+=1) {
var temp = []; # try to trigger gc
}
print("\n");
print_ghost(ghost); # 114
print("\n");
}

View File

@@ -1,4 +1,5 @@
use std.dylib;
use std.os;
var (
kbhit,
@@ -11,8 +12,8 @@ var (
var nb = lib.nas_noblock;
var call = dylib.limitcall(0);
return [
func(){return call(kb);},
func(){return call(gt);},
func(){return call(nb);}
func() {return call(kb);},
func() {return call(gt);},
func() {return call(nb);}
];
}();

View File

@@ -1,4 +1,5 @@
use std.dylib;
use std.os;
var _dl = dylib.dlopen("libmat."~(os.platform()=="windows"?"dll":"so"));

120
module/libnasock.nas Normal file
View File

@@ -0,0 +1,120 @@
use std.dylib;
use std.os;
var socket = func() {
var lib = dylib.dlopen("libnasock"~(os.platform()=="windows"? ".dll":".so"));
var sock = lib.nas_socket;
var closesocket = lib.nas_closesocket;
var shutdown = lib.nas_shutdown;
var bind = lib.nas_bind;
var listen = lib.nas_listen;
var connect = lib.nas_connect;
var accept = lib.nas_accept;
var send = lib.nas_send;
var sendto = lib.nas_sendto;
var recv = lib.nas_recv;
var recvfrom = lib.nas_recvfrom;
var errno = lib.nas_errno;
var (invoke, invoke_i, invoke_ii, invoke_iii, invoke_iiii, invoke_iiiii) = (
dylib.limitcall(0),
dylib.limitcall(1),
dylib.limitcall(2),
dylib.limitcall(3),
dylib.limitcall(4),
dylib.limitcall(5),
);
return {
AF_UNSPEC:0,
AF_UNIX: 1,
AF_INET: 2,
AF_IMPLINK: 3,
AF_PUP: 4,
AF_CHAOS: 5,
AF_IPX: 6,
AF_NS: 6,
AF_ISO: 7,
AF_OSI: 7,
AF_ECMA: 8,
AF_DATAKIT: 9,
AF_CCITT: 10,
AF_SNA: 11,
AF_DECnet: 12,
AF_DLI: 13,
AF_LAT: 14,
AF_HYLINK: 15,
AF_APPLETALK: 16,
AF_NETBIOS: 17,
AF_VOICEVIEW: 18,
AF_FIREFOX: 19,
AF_UNKNOWN1: 20,
AF_BAN: 21,
AF_MAX: 22,
SOCKET_ERROR: -1,
SOCK_STREAM: 1,
SOCK_DGRAM: 2,
SOCK_RAW: 3,
SOCK_RDM: 4,
SOCK_SEQPACKET: 5,
IPPROTO_IP:0,IPPROTO_ICMP:1,IPPROTO_IGMP:2,IPPROTO_GGP:3,
IPPROTO_TCP:6,IPPROTO_PUP:12,IPPROTO_UDP:17,IPPROTO_IDP:22,
IPPROTO_ND:77,IPPROTO_RAW:255,IPPROTO_MAX:256,
IPPORT_ECHO:7,IPPORT_DISCARD:9,IPPORT_SYSTAT:11,IPPORT_DAYTIME:13,
IPPORT_NETSTAT:15,IPPORT_FTP:21,IPPORT_TELNET:23,IPPORT_SMTP:25,
IPPORT_TIMESERVER:37,IPPORT_NAMESERVER:42,IPPORT_WHOIS:43,IPPORT_MTP:57,
IPPORT_TFTP:69,IPPORT_RJE:77,IPPORT_FINGER:79,IPPORT_TTYLINK:87,
IPPORT_SUPDUP:95,IPPORT_EXECSERVER:512,IPPORT_LOGINSERVER:513,IPPORT_CMDSERVER:514,
IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520,
IPPORT_RESERVED:1024,
SHUT_RD : 0x00,
SHUT_WR : 0x01,
SHUT_RDWR: 0x02,
MSG_OOB: 0x1,
MSG_PEEK: 0x2,
MSG_DONTROUTE: 0x4,
MSG_DONTWAIT: 0x40,
socket: func(af, type, proto = 0) {
return invoke_iii(sock, af, type, proto);
},
closesocket: func(sd) {
return invoke_i(closesocket, sd);
},
shutdown: func(sd, how) {
return invoke_ii(shutdown, sd, how);
},
bind: func(sd, ip, port) {
return invoke_iii(bind, sd, ip, port);
},
listen: func(sd, backlog) {
return invoke_ii(listen, sd, backlog);
},
connect: func(sd, hostname, port) {
return invoke_iii(connect, sd, hostname, port);
},
accept: func(sd) {
return invoke_i(accept, sd);
},
send: func(sd, buff, flags = 0) {
return invoke_iii(send, sd, buff, flags);
},
sendto: func(sd, hostname, port, buff, flags = 0) {
return invoke_iiiii(sendto, sd, hostname, port, buff, flags);
},
recv: func(sd, len, flags = 0) {
return invoke_iii(recv, sd, len, flags);
},
recvfrom: func(sd, len, flags = 0) {
return invoke_iii(recvfrom, sd, len, flags);
},
errno: func() {
return invoke(errno);
}
};
}();

View File

@@ -1,117 +0,0 @@
use std.dylib;
var socket=func(){
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
var sock=lib.nas_socket;
var closesocket=lib.nas_closesocket;
var shutdown=lib.nas_shutdown;
var bind=lib.nas_bind;
var listen=lib.nas_listen;
var connect=lib.nas_connect;
var accept=lib.nas_accept;
var send=lib.nas_send;
var sendto=lib.nas_sendto;
var recv=lib.nas_recv;
var recvfrom=lib.nas_recvfrom;
var errno=lib.nas_errno;
var (invoke,invoke_i,invoke_ii,invoke_iii,invoke_iiii,invoke_iiiii)=(
dylib.limitcall(0),
dylib.limitcall(1),
dylib.limitcall(2),
dylib.limitcall(3),
dylib.limitcall(4),
dylib.limitcall(5),
);
return {
AF_UNSPEC:0,
AF_UNIX:1,
AF_INET:2,
AF_IMPLINK:3,
AF_PUP:4,
AF_CHAOS:5,
AF_IPX:6,
AF_NS:6,
AF_ISO:7,
AF_OSI:7,
AF_ECMA:8,
AF_DATAKIT:9,
AF_CCITT:10,
AF_SNA:11,
AF_DECnet:12,
AF_DLI:13,
AF_LAT:14,
AF_HYLINK:15,
AF_APPLETALK:16,
AF_NETBIOS:17,
AF_VOICEVIEW:18,
AF_FIREFOX:19,
AF_UNKNOWN1:20,
AF_BAN:21,
AF_MAX:22,
SOCK_STREAM:1,
SOCK_DGRAM:2,
SOCK_RAW:3,
SOCK_RDM:4,
SOCK_SEQPACKET:5,
IPPROTO_IP:0,IPPROTO_ICMP:1,IPPROTO_IGMP:2,IPPROTO_GGP:3,
IPPROTO_TCP:6,IPPROTO_PUP:12,IPPROTO_UDP:17,IPPROTO_IDP:22,
IPPROTO_ND:77,IPPROTO_RAW:255,IPPROTO_MAX:256,
IPPORT_ECHO:7,IPPORT_DISCARD:9,IPPORT_SYSTAT:11,IPPORT_DAYTIME:13,
IPPORT_NETSTAT:15,IPPORT_FTP:21,IPPORT_TELNET:23,IPPORT_SMTP:25,
IPPORT_TIMESERVER:37,IPPORT_NAMESERVER:42,IPPORT_WHOIS:43,IPPORT_MTP:57,
IPPORT_TFTP:69,IPPORT_RJE:77,IPPORT_FINGER:79,IPPORT_TTYLINK:87,
IPPORT_SUPDUP:95,IPPORT_EXECSERVER:512,IPPORT_LOGINSERVER:513,IPPORT_CMDSERVER:514,
IPPORT_EFSSERVER:520,IPPORT_BIFFUDP:512,IPPORT_WHOSERVER:513,IPPORT_ROUTESERVER:520,
IPPORT_RESERVED:1024,
SHUT_RD :0x00,
SHUT_WR :0x01,
SHUT_RDWR:0x02,
MSG_OOB:0x1,
MSG_PEEK:0x2,
MSG_DONTROUTE:0x4,
socket:func(af,type,proto){
return invoke_iii(sock,af,type,proto);
},
closesocket:func(sd){
return invoke_i(closesocket,sd);
},
shutdown: func(sd,how){
return invoke_ii(shutdown,sd,how);
},
bind: func(sd,ip,port){
return invoke_iii(bind,sd,ip,port);
},
listen: func(sd,backlog){
return invoke_ii(listen,sd,backlog);
},
connect: func(sd,hostname,port){
return invoke_iii(connect,sd,hostname,port);
},
accept: func(sd){
return invoke_i(accept,sd);
},
send: func(sd,buff,flags=0){
return invoke_iii(send,sd,buff,flags);
},
sendto: func(sd,hostname,port,buff,flags=0){
return invoke_iiiii(sendto,sd,hostname,port,buff,flags);
},
recv: func(sd,len,flags=0){
return invoke_iii(recv,sd,len,flags);
},
recvfrom: func(sd,len,flags=0){
return invoke_iii(recvfrom,sd,len,flags);
},
errno: func(){
return invoke(errno);
}
};
}();

View File

@@ -1,10 +1,10 @@
.PHONY=clean all winall
.PHONY = clean all winall
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
used_header = ../src/nasal.h ../src/util/util.h ../src/nasal_type.h ../src/nasal_gc.h
used_object = ../build/util.o ../build/nasal_type.o ../build/nasal_gc.o
STD = c++17

View File

@@ -6,14 +6,14 @@
namespace nasal {
var nas_vec2(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(args[0]);
res.vec().elems.push_back(args[1]);
return res;
}
var nas_vec3(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(args[0]);
res.vec().elems.push_back(args[1]);
res.vec().elems.push_back(args[2]);
@@ -21,71 +21,71 @@ var nas_vec3(var* args, usize size, gc* ngc) {
}
var nas_vec2_add(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
return res;
}
var nas_vec2_sub(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
return res;
}
var nas_vec2_mult(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
return res;
}
var nas_vec2_div(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=2 || v1.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
return res;
}
var nas_vec2_neg(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
return res;
}
var nas_vec2_norm(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
@@ -93,14 +93,14 @@ var nas_vec2_norm(var* args, usize size, gc* ngc) {
auto x = v0[0].num();
auto y = v0[1].num();
auto t = std::sqrt(x*x+y*y);
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
return res;
}
var nas_vec2_len(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=2)
@@ -111,7 +111,7 @@ var nas_vec2_len(var* args, usize size, gc* ngc) {
}
var nas_vec2_dot(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
@@ -121,13 +121,13 @@ var nas_vec2_dot(var* args, usize size, gc* ngc) {
}
var nas_vec3_add(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()+v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()+v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()+v1[2].num()));
@@ -135,13 +135,13 @@ var nas_vec3_add(var* args, usize size, gc* ngc) {
}
var nas_vec3_sub(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()-v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()-v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()-v1[2].num()));
@@ -149,13 +149,13 @@ var nas_vec3_sub(var* args, usize size, gc* ngc) {
}
var nas_vec3_mult(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()*v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()*v1[2].num()));
@@ -163,13 +163,13 @@ var nas_vec3_mult(var* args, usize size, gc* ngc) {
}
var nas_vec3_div(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
if (v0.size()!=3 || v1.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()/v1[0].num()));
res.vec().elems.push_back(var::num(v0[1].num()/v1[1].num()));
res.vec().elems.push_back(var::num(v0[2].num()/v1[2].num()));
@@ -177,12 +177,12 @@ var nas_vec3_div(var* args, usize size, gc* ngc) {
}
var nas_vec3_neg(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(-v0[0].num()));
res.vec().elems.push_back(var::num(-v0[1].num()));
res.vec().elems.push_back(var::num(-v0[2].num()));
@@ -190,7 +190,7 @@ var nas_vec3_neg(var* args, usize size, gc* ngc) {
}
var nas_vec3_norm(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
@@ -199,7 +199,7 @@ var nas_vec3_norm(var* args, usize size, gc* ngc) {
auto y = v0[1].num();
auto z = v0[2].num();
auto t = std::sqrt(x*x+y*y+z*z);
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(x/t));
res.vec().elems.push_back(var::num(y/t));
res.vec().elems.push_back(var::num(z/t));
@@ -207,7 +207,7 @@ var nas_vec3_norm(var* args, usize size, gc* ngc) {
}
var nas_vec3_len(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
@@ -219,13 +219,13 @@ var nas_vec3_len(var* args, usize size, gc* ngc) {
}
var nas_rotate_x(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()));
res.vec().elems.push_back(var::num(v0[2].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
@@ -233,13 +233,13 @@ var nas_rotate_x(var* args, usize size, gc* ngc) {
}
var nas_rotate_y(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[2].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[1].num()));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[2].num()*std::cos(angle)));
@@ -247,13 +247,13 @@ var nas_rotate_y(var* args, usize size, gc* ngc) {
}
var nas_rotate_z(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec)
if (!args[0].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
if (v0.size()!=3)
return nil;
auto angle = args[1].num();
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
res.vec().elems.push_back(var::num(v0[0].num()*std::cos(angle)-v0[1].num()*std::sin(angle)));
res.vec().elems.push_back(var::num(v0[0].num()*std::sin(angle)+v0[1].num()*std::cos(angle)));
res.vec().elems.push_back(var::num(v0[2].num()));
@@ -261,7 +261,7 @@ var nas_rotate_z(var* args, usize size, gc* ngc) {
}
var nas_vec3_dot(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_vec || args[1].type!=vm_vec)
if (!args[0].is_vec() || !args[1].is_vec())
return nil;
auto& v0 = args[0].vec().elems;
auto& v1 = args[1].vec().elems;
@@ -295,7 +295,7 @@ module_func_info func_tbl[] = {
{nullptr, nullptr}
};
extern "C" module_func_info* get() {
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}

View File

@@ -6,7 +6,12 @@
#include <unistd.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4996)
#endif
#ifdef _WIN32
// load socket library on windows platform
#include <winsock.h>
#pragma comment(lib,"ws2_32")
@@ -21,8 +26,9 @@ public:
WSACleanup();
}
};
// use static object to do WSAStartup and WSACleanup
static WSAmanager win;
#else
#include <netdb.h>
#include <sys/socket.h>
@@ -33,14 +39,14 @@ static WSAmanager win;
namespace nasal {
var nas_socket(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
if (!args[0].is_num() || !args[1].is_num() || !args[2].is_num())
return nas_err("socket", "\"af\", \"type\", \"protocol\" should be number");
int sd = socket(args[0].num(), args[1].num(), args[2].num());
return var::num(static_cast<double>(sd));
}
var nas_closesocket(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("closesocket", "\"sd\" should be number");
#ifdef _WIN32
return var::num(static_cast<double>(closesocket(args[0].num())));
@@ -50,19 +56,19 @@ var nas_closesocket(var* args, usize size, gc* ngc) {
}
var nas_shutdown(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("shutdown", "\"sd\" must be a number");
if (args[1].type!=vm_num)
if (!args[1].is_num())
return nas_err("shutdown", "\"how\" must be a number");
return var::num(static_cast<double>(shutdown(args[0].num(), args[1].num())));
}
var nas_bind(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("bind", "\"sd\" muse be a number");
if (args[1].type!=vm_str)
if (!args[1].is_str())
return nas_err("bind", "\"ip\" should be a string including an ip with correct format");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("bind", "\"port\" must be a number");
sockaddr_in server;
memset(&server, 0, sizeof(sockaddr_in));
@@ -77,19 +83,19 @@ var nas_bind(var* args, usize size, gc* ngc) {
}
var nas_listen(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("listen", "\"sd\" must be a number");
if (args[1].type!=vm_num)
if (!args[1].is_num())
return nas_err("listen", "\"backlog\" must be a number");
return var::num(static_cast<double>(listen(args[0].num(), args[1].num())));
}
var nas_connect(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("connect", "\"sd\" must be a number");
if (args[1].type!=vm_str)
if (!args[1].is_str())
return nas_err("connect", "\"hostname\" must be a string");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("connect", "\"port\" must be a number");
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
@@ -105,7 +111,7 @@ var nas_connect(var* args, usize size, gc* ngc) {
}
var nas_accept(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("accept", "\"sd\" must be a number");
sockaddr_in client;
int socklen = sizeof(sockaddr_in);
@@ -122,7 +128,7 @@ var nas_accept(var* args, usize size, gc* ngc) {
reinterpret_cast<socklen_t*>(&socklen)
);
#endif
var res = ngc->temp = ngc->alloc(vm_hash);
var res = ngc->temp = ngc->alloc(vm_type::vm_hash);
auto& hash = res.hash().elems;
hash["sd"] = var::num(static_cast<double>(client_sd));
hash["ip"] = ngc->newstr(inet_ntoa(client.sin_addr));
@@ -131,11 +137,11 @@ var nas_accept(var* args, usize size, gc* ngc) {
}
var nas_send(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("send", "\"sd\" must be a number");
if (args[1].type!=vm_str)
if (!args[1].is_str())
return nas_err("send", "\"buff\" must be a string");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("send", "\"flags\" muse be a number");
return var::num(static_cast<double>(send(
args[0].num(),
@@ -146,15 +152,15 @@ var nas_send(var* args, usize size, gc* ngc) {
}
var nas_sendto(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("sendto", "\"sd\" must be a number");
if (args[1].type!=vm_str)
if (!args[1].is_str())
return nas_err("sendto", "\"hostname\" must be a string");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("sendto", "\"port\" must be a number");
if (args[3].type!=vm_str)
if (!args[3].is_str())
return nas_err("sendto", "\"buff\" must be a string");
if (args[4].type!=vm_num)
if (!args[4].is_num())
return nas_err("sendto", "\"flags\" must be a number");
sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in));
@@ -173,18 +179,18 @@ var nas_sendto(var* args, usize size, gc* ngc) {
}
var nas_recv(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("recv", "\"sd\" must be a number");
if (args[1].type!=vm_num)
if (!args[1].is_num())
return nas_err("recv", "\"len\" must be a number");
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
return nas_err("recv", "\"len\" out of range");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("recv", "\"flags\" muse be a number");
var res = ngc->temp = ngc->alloc(vm_hash);
var res = ngc->temp = ngc->alloc(vm_type::vm_hash);
auto& hash = res.hash().elems;
char* buf = new char[static_cast<int>(args[1].num())];
auto recvsize = recv(args[0].num(), buf,args[1].num(), args[2].num());
auto recvsize = recv(args[0].num(), buf, args[1].num(), args[2].num());
hash["size"] = var::num(static_cast<double>(recvsize));
buf[recvsize>=0? recvsize:0] = 0;
hash["str"] = ngc->newstr(buf);
@@ -194,17 +200,17 @@ var nas_recv(var* args, usize size, gc* ngc) {
}
var nas_recvfrom(var* args, usize size, gc* ngc) {
if (args[0].type!=vm_num)
if (!args[0].is_num())
return nas_err("recvfrom", "\"sd\" must be a number");
if (args[1].type!=vm_num)
if (!args[1].is_num())
return nas_err("recvfrom", "\"len\" must be a number");
if (args[1].num()<=0 || args[1].num()>16*1024*1024)
return nas_err("recvfrom", "\"len\" out of range");
if (args[2].type!=vm_num)
if (!args[2].is_num())
return nas_err("recvfrom", "\"flags\" muse be a number");
sockaddr_in addr;
int socklen = sizeof(sockaddr_in);
var res = ngc->temp = ngc->alloc(vm_hash);
var res = ngc->temp = ngc->alloc(vm_type::vm_hash);
auto& hash = res.hash().elems;
char* buf = new char[static_cast<int>(args[1].num()+1)];
#ifdef _WIN32
@@ -231,6 +237,7 @@ var nas_recvfrom(var* args, usize size, gc* ngc) {
hash["str"] = ngc->newstr(buf);
delete[] buf;
hash["fromip"] = ngc->newstr(inet_ntoa(addr.sin_addr));
hash["port"] = var::num(ntohs(addr.sin_port));
ngc->temp = nil;
return res;
}
@@ -255,7 +262,7 @@ module_func_info func_tbl[] = {
{nullptr, nullptr}
};
extern "C" module_func_info* get() {
NASAL_EXTERN module_func_info* get() {
return func_tbl;
}

View File

@@ -1,4 +1,5 @@
#include "ast_dumper.h"
#include "util/util.h"
#include <iostream>
@@ -6,7 +7,7 @@ namespace nasal {
bool ast_dumper::visit_use_stmt(use_stmt* node) {
dump_indent();
std::cout << "use" << format_location(node->get_location());
std::cout << "use" << format_location(node);
push_indent();
for(auto i : node->get_path()) {
if (i==node->get_path().back()) {
@@ -20,48 +21,48 @@ bool ast_dumper::visit_use_stmt(use_stmt* node) {
bool ast_dumper::visit_null_expr(null_expr* node) {
dump_indent();
std::cout << "null" << format_location(node->get_location());
std::cout << "null" << format_location(node);
return true;
}
bool ast_dumper::visit_nil_expr(nil_expr* node) {
dump_indent();
std::cout << "nil" << format_location(node->get_location());
std::cout << "nil" << format_location(node);
return true;
}
bool ast_dumper::visit_number_literal(number_literal* node) {
dump_indent();
std::cout << "number " << node->get_number();
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_string_literal(string_literal* node) {
dump_indent();
std::cout << "string \"" << rawstr(node->get_content()) << "\"";
std::cout << format_location(node->get_location());
std::cout << "string \"" << util::rawstr(node->get_content()) << "\"";
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_identifier(identifier* node) {
dump_indent();
std::cout << "identifier " << node->get_name();
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_bool_literal(bool_literal* node) {
dump_indent();
std::cout << "bool " << node->get_flag();
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_vector_expr(vector_expr* node) {
dump_indent();
std::cout << "vector";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_elements()) {
if (i==node->get_elements().back()) {
@@ -76,7 +77,7 @@ bool ast_dumper::visit_vector_expr(vector_expr* node) {
bool ast_dumper::visit_hash_expr(hash_expr* node) {
dump_indent();
std::cout << "hash";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_members()) {
if (i==node->get_members().back()) {
@@ -91,7 +92,7 @@ bool ast_dumper::visit_hash_expr(hash_expr* node) {
bool ast_dumper::visit_hash_pair(hash_pair* node) {
dump_indent();
std::cout << "pair " << node->get_name();
std::cout << format_location(node->get_location());
std::cout << format_location(node);
if (node->get_value()) {
push_indent();
set_last();
@@ -104,7 +105,7 @@ bool ast_dumper::visit_hash_pair(hash_pair* node) {
bool ast_dumper::visit_function(function* node) {
dump_indent();
std::cout << "function";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_parameter_list()) {
i->accept(this);
@@ -118,7 +119,7 @@ bool ast_dumper::visit_function(function* node) {
bool ast_dumper::visit_code_block(code_block* node) {
dump_indent();
std::cout << "block";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_expressions()) {
if (i==node->get_expressions().back()) {
@@ -132,8 +133,14 @@ bool ast_dumper::visit_code_block(code_block* node) {
bool ast_dumper::visit_parameter(parameter* node) {
dump_indent();
std::cout << "parameter " << node->get_parameter_name();
std::cout << format_location(node->get_location());
std::cout << "parameter ";
switch(node->get_parameter_type()) {
case parameter::kind::normal_parameter: std::cout << "[normal]"; break;
case parameter::kind::dynamic_parameter: std::cout << "[dynamic]"; break;
case parameter::kind::default_parameter: std::cout << "[default]"; break;
}
std::cout << " " << node->get_parameter_name();
std::cout << format_location(node);
if (node->get_default_value()) {
push_indent();
set_last();
@@ -146,7 +153,7 @@ bool ast_dumper::visit_parameter(parameter* node) {
bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
dump_indent();
std::cout << "ternary_operator";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_condition()->accept(this);
node->get_left()->accept(this);
@@ -166,26 +173,27 @@ bool ast_dumper::visit_binary_operator(binary_operator* node) {
return true;
}
dump_indent();
std::cout << "binary_operator ";
std::cout << "binary_operator \"";
switch(node->get_operator_type()) {
case binary_operator::binary_type::add: std::cout << "+"; break;
case binary_operator::binary_type::sub: std::cout << "-"; break;
case binary_operator::binary_type::mult: std::cout << "*"; break;
case binary_operator::binary_type::div: std::cout << "/"; break;
case binary_operator::binary_type::concat: std::cout << "~"; break;
case binary_operator::binary_type::bitwise_and: std::cout << "&"; break;
case binary_operator::binary_type::bitwise_or: std::cout << "|"; break;
case binary_operator::binary_type::bitwise_xor: std::cout << "^"; break;
case binary_operator::binary_type::cmpeq: std::cout << "=="; break;
case binary_operator::binary_type::cmpneq: std::cout << "!="; break;
case binary_operator::binary_type::grt: std::cout << ">"; break;
case binary_operator::binary_type::geq: std::cout << ">="; break;
case binary_operator::binary_type::less: std::cout << "<"; break;
case binary_operator::binary_type::leq: std::cout << "<="; break;
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
case binary_operator::kind::add: std::cout << "+"; break;
case binary_operator::kind::sub: std::cout << "-"; break;
case binary_operator::kind::mult: std::cout << "*"; break;
case binary_operator::kind::div: std::cout << "/"; break;
case binary_operator::kind::concat: std::cout << "~"; break;
case binary_operator::kind::bitwise_and: std::cout << "&"; break;
case binary_operator::kind::bitwise_or: std::cout << "|"; break;
case binary_operator::kind::bitwise_xor: std::cout << "^"; break;
case binary_operator::kind::cmpeq: std::cout << "=="; break;
case binary_operator::kind::cmpneq: std::cout << "!="; break;
case binary_operator::kind::grt: std::cout << ">"; break;
case binary_operator::kind::geq: std::cout << ">="; break;
case binary_operator::kind::less: std::cout << "<"; break;
case binary_operator::kind::leq: std::cout << "<="; break;
case binary_operator::kind::condition_and: std::cout << "and"; break;
case binary_operator::kind::condition_or: std::cout << "or"; break;
case binary_operator::kind::null_chain: std::cout << "??"; break;
}
std::cout << format_location(node->get_location());
std::cout << "\"" << format_location(node);
push_indent();
node->get_left()->accept(this);
set_last();
@@ -200,13 +208,13 @@ bool ast_dumper::visit_unary_operator(unary_operator* node) {
return true;
}
dump_indent();
std::cout << "unary_operator ";
std::cout << "unary_operator \"";
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative: std::cout << "-"; break;
case unary_operator::unary_type::logical_not: std::cout << "!"; break;
case unary_operator::unary_type::bitwise_not: std::cout << "~"; break;
case unary_operator::kind::negative: std::cout << "-"; break;
case unary_operator::kind::logical_not: std::cout << "!"; break;
case unary_operator::kind::bitwise_not: std::cout << "~"; break;
}
std::cout << format_location(node->get_location());
std::cout << "\"" << format_location(node);
push_indent();
set_last();
node->get_value()->accept(this);
@@ -217,7 +225,7 @@ bool ast_dumper::visit_unary_operator(unary_operator* node) {
bool ast_dumper::visit_call_expr(call_expr* node) {
dump_indent();
std::cout << "call_expr";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
if (!node->get_calls().size()) {
set_last();
@@ -236,14 +244,21 @@ bool ast_dumper::visit_call_expr(call_expr* node) {
bool ast_dumper::visit_call_hash(call_hash* node) {
dump_indent();
std::cout << "call_hash " << node->get_field();
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_null_access(null_access* node) {
dump_indent();
std::cout << "null_access " << node->get_field();
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_call_vector(call_vector* node) {
dump_indent();
std::cout << "call_vector";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_slices()) {
if (i==node->get_slices().back()) {
@@ -258,7 +273,7 @@ bool ast_dumper::visit_call_vector(call_vector* node) {
bool ast_dumper::visit_call_function(call_function* node) {
dump_indent();
std::cout << "call_function";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_argument()) {
if (i==node->get_argument().back()) {
@@ -273,7 +288,7 @@ bool ast_dumper::visit_call_function(call_function* node) {
bool ast_dumper::visit_slice_vector(slice_vector* node) {
dump_indent();
std::cout << "slice";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
if (!node->get_end()) {
set_last();
@@ -290,7 +305,7 @@ bool ast_dumper::visit_slice_vector(slice_vector* node) {
bool ast_dumper::visit_definition_expr(definition_expr* node) {
dump_indent();
std::cout << "definition";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
if (node->get_variable_name()) {
node->get_variable_name()->accept(this);
@@ -311,17 +326,17 @@ bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
dump_indent();
std::cout << "assignment ";
switch(node->get_assignment_type()) {
case assignment_expr::assign_type::add_equal: std::cout << "+="; break;
case assignment_expr::assign_type::sub_equal: std::cout << "-="; break;
case assignment_expr::assign_type::mult_equal: std::cout << "*="; break;
case assignment_expr::assign_type::div_equal: std::cout << "/="; break;
case assignment_expr::assign_type::concat_equal: std::cout << "~="; break;
case assignment_expr::assign_type::equal: std::cout << "="; break;
case assignment_expr::assign_type::bitwise_and_equal: std::cout << "&="; break;
case assignment_expr::assign_type::bitwise_or_equal: std::cout << "|="; break;
case assignment_expr::assign_type::bitwise_xor_equal: std::cout << "^="; break;
case assignment_expr::kind::add_equal: std::cout << "+="; break;
case assignment_expr::kind::sub_equal: std::cout << "-="; break;
case assignment_expr::kind::mult_equal: std::cout << "*="; break;
case assignment_expr::kind::div_equal: std::cout << "/="; break;
case assignment_expr::kind::concat_equal: std::cout << "~="; break;
case assignment_expr::kind::equal: std::cout << "="; break;
case assignment_expr::kind::bitwise_and_equal: std::cout << "&="; break;
case assignment_expr::kind::bitwise_or_equal: std::cout << "|="; break;
case assignment_expr::kind::bitwise_xor_equal: std::cout << "^="; break;
}
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_left()->accept(this);
set_last();
@@ -333,7 +348,7 @@ bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
dump_indent();
std::cout << "multiple_identifier";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_variables()) {
if (i==node->get_variables().back()) {
@@ -348,7 +363,7 @@ bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
dump_indent();
std::cout << "tuple";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
for(auto i : node->get_elements()) {
if (i==node->get_elements().back()) {
@@ -363,7 +378,7 @@ bool ast_dumper::visit_tuple_expr(tuple_expr* node) {
bool ast_dumper::visit_multi_assign(multi_assign* node) {
dump_indent();
std::cout << "multiple_assignment";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_tuple()->accept(this);
set_last();
@@ -375,7 +390,7 @@ bool ast_dumper::visit_multi_assign(multi_assign* node) {
bool ast_dumper::visit_while_expr(while_expr* node) {
dump_indent();
std::cout << "while";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_condition()->accept(this);
set_last();
@@ -387,7 +402,7 @@ bool ast_dumper::visit_while_expr(while_expr* node) {
bool ast_dumper::visit_for_expr(for_expr* node) {
dump_indent();
std::cout << "for";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_initial()->accept(this);
node->get_condition()->accept(this);
@@ -405,7 +420,7 @@ bool ast_dumper::visit_iter_expr(iter_expr* node) {
} else {
std::cout << "iterator";
}
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
set_last();
if (node->get_name()) {
@@ -419,12 +434,12 @@ bool ast_dumper::visit_iter_expr(iter_expr* node) {
bool ast_dumper::visit_forei_expr(forei_expr* node) {
dump_indent();
if (node->get_loop_type()==forei_expr::forei_loop_type::foreach) {
if (node->get_loop_type()==forei_expr::kind::foreach) {
std::cout << "foreach";
} else {
std::cout << "forindex";
}
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
node->get_iterator()->accept(this);
node->get_value()->accept(this);
@@ -437,7 +452,7 @@ bool ast_dumper::visit_forei_expr(forei_expr* node) {
bool ast_dumper::visit_condition_expr(condition_expr* node) {
dump_indent();
std::cout << "condition";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
if (!node->get_elsif_stataments().size() &&
!node->get_else_statement()) {
@@ -462,7 +477,7 @@ bool ast_dumper::visit_condition_expr(condition_expr* node) {
bool ast_dumper::visit_if_expr(if_expr* node) {
dump_indent();
std::cout << "if";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
push_indent();
if (node->get_condition()) {
node->get_condition()->accept(this);
@@ -476,21 +491,21 @@ bool ast_dumper::visit_if_expr(if_expr* node) {
bool ast_dumper::visit_continue_expr(continue_expr* node) {
dump_indent();
std::cout << "continue";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_break_expr(break_expr* node) {
dump_indent();
std::cout << "break";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
return true;
}
bool ast_dumper::visit_return_expr(return_expr* node) {
dump_indent();
std::cout << "return";
std::cout << format_location(node->get_location());
std::cout << format_location(node);
if (node->get_value()) {
push_indent();
set_last();

View File

@@ -1,87 +1,101 @@
#pragma once
#include "ast_visitor.h"
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
namespace nasal {
class ast_dumper:public ast_visitor {
private:
std::vector<std::string> indent;
void push_indent() {
if (indent.size()) {
if (indent.back()=="|--") {
indent.back() = "| ";
} else if (indent.back()=="+--") {
indent.back() = " ";
}
}
indent.push_back("|--");
}
void pop_indent() {indent.pop_back();}
void set_last() {indent.back() = "+--";}
void dump_indent() {
if (indent.size() && indent.back()=="| ") {
indent.back() = "|--";
}
for(const auto& i : indent) {
std::cout << i;
}
}
std::string format_location(const span& location) {
std::stringstream ss;
ss << " -> ";
ss << location.file << ":";
ss << location.begin_line << ":" << location.begin_column + 1;
ss << "\n";
return ss.str();
}
public:
bool visit_use_stmt(use_stmt*) override;
bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override;
bool visit_string_literal(string_literal*) override;
bool visit_identifier(identifier*) override;
bool visit_bool_literal(bool_literal*) override;
bool visit_vector_expr(vector_expr*) override;
bool visit_hash_expr(hash_expr*) override;
bool visit_hash_pair(hash_pair*) override;
bool visit_function(function*) override;
bool visit_code_block(code_block*) override;
bool visit_parameter(parameter*) override;
bool visit_ternary_operator(ternary_operator*) override;
bool visit_binary_operator(binary_operator*) override;
bool visit_unary_operator(unary_operator*) override;
bool visit_call_expr(call_expr*) override;
bool visit_call_hash(call_hash*) override;
bool visit_call_vector(call_vector*) override;
bool visit_call_function(call_function*) override;
bool visit_slice_vector(slice_vector*) override;
bool visit_definition_expr(definition_expr*) override;
bool visit_assignment_expr(assignment_expr*) override;
bool visit_multi_identifier(multi_identifier*) override;
bool visit_tuple_expr(tuple_expr*) override;
bool visit_multi_assign(multi_assign*) override;
bool visit_while_expr(while_expr*) override;
bool visit_for_expr(for_expr*) override;
bool visit_iter_expr(iter_expr*) override;
bool visit_forei_expr(forei_expr*) override;
bool visit_condition_expr(condition_expr*) override;
bool visit_if_expr(if_expr*) override;
bool visit_continue_expr(continue_expr*) override;
bool visit_break_expr(break_expr*) override;
bool visit_return_expr(return_expr*) override;
public:
void dump(code_block* root) {
root->accept(this);
}
};
}
#pragma once
#include "ast_visitor.h"
#include "util/util.h"
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
namespace nasal {
class ast_dumper: public ast_visitor {
private:
std::vector<std::string> indent;
private:
void push_indent() {
if (indent.size()) {
if (indent.back()=="├──") {
indent.back() = "";
} else if (indent.back()=="╰──") {
indent.back() = " ";
}
}
indent.push_back("├──");
}
void pop_indent() {
indent.pop_back();
}
void set_last() {
indent.back() = "╰──";
}
void dump_indent() {
if (indent.size() && indent.back()=="") {
indent.back() = "├──";
}
for(const auto& i : indent) {
std::cout << i;
}
}
std::string format_location(expr* node) {
std::stringstream ss;
ss << " ⇒ [";
node->get_location().dump_begin(ss);
ss << "]\n";
return ss.str();
}
public:
bool visit_use_stmt(use_stmt*) override;
bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override;
bool visit_string_literal(string_literal*) override;
bool visit_identifier(identifier*) override;
bool visit_bool_literal(bool_literal*) override;
bool visit_vector_expr(vector_expr*) override;
bool visit_hash_expr(hash_expr*) override;
bool visit_hash_pair(hash_pair*) override;
bool visit_function(function*) override;
bool visit_code_block(code_block*) override;
bool visit_parameter(parameter*) override;
bool visit_ternary_operator(ternary_operator*) override;
bool visit_binary_operator(binary_operator*) override;
bool visit_unary_operator(unary_operator*) override;
bool visit_call_expr(call_expr*) override;
bool visit_call_hash(call_hash*) override;
bool visit_null_access(null_access*) override;
bool visit_call_vector(call_vector*) override;
bool visit_call_function(call_function*) override;
bool visit_slice_vector(slice_vector*) override;
bool visit_definition_expr(definition_expr*) override;
bool visit_assignment_expr(assignment_expr*) override;
bool visit_multi_identifier(multi_identifier*) override;
bool visit_tuple_expr(tuple_expr*) override;
bool visit_multi_assign(multi_assign*) override;
bool visit_while_expr(while_expr*) override;
bool visit_for_expr(for_expr*) override;
bool visit_iter_expr(iter_expr*) override;
bool visit_forei_expr(forei_expr*) override;
bool visit_condition_expr(condition_expr*) override;
bool visit_if_expr(if_expr*) override;
bool visit_continue_expr(continue_expr*) override;
bool visit_break_expr(break_expr*) override;
bool visit_return_expr(return_expr*) override;
public:
void dump(code_block* root) {
util::windows_code_page_manager wm;
wm.set_utf8_output();
root->accept(this);
wm.restore_code_page();
}
};
}

View File

@@ -116,6 +116,10 @@ bool ast_visitor::visit_call_hash(call_hash* node) {
return true;
}
bool ast_visitor::visit_null_access(null_access* node) {
return true;
}
bool ast_visitor::visit_call_vector(call_vector* node) {
for(auto i : node->get_slices()) {
i->accept(this);

View File

@@ -26,6 +26,7 @@ public:
virtual bool visit_unary_operator(unary_operator*);
virtual bool visit_call_expr(call_expr*);
virtual bool visit_call_hash(call_hash*);
virtual bool visit_null_access(null_access*);
virtual bool visit_call_vector(call_vector*);
virtual bool visit_call_function(call_function*);
virtual bool visit_slice_vector(slice_vector*);

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

@@ -0,0 +1,62 @@
#include "cli/cli.h"
#include <iostream>
namespace nasal::cli {
cli_config parse(const std::vector<std::string>& args) {
cli_config result;
for(const auto& arg : args) {
if (cli_options.count(arg)) {
result.options.insert(cli_options.at(arg));
} else if (!result.input_file_path.length()) {
result.input_file_path = arg;
} else {
result.nasal_vm_args.push_back(arg);
}
}
if (result.input_file_path.length() && result.options.empty()) {
result.options.insert(option::cli_execute);
}
return result;
}
std::ostream& help(std::ostream& out) {
out
<< "\n"
<< " ,--#-,\n"
<< "<3 / \\____\\ <3\n"
<< " |_|__A_|\n"
<< "\nnasal <option>\n"
<< "option:\n"
<< " -h, --help | get help.\n"
<< " -v, --version | get version.\n"
<< " -r, --repl | use repl interpreter.\n"
<< "\nnasal [option] <file> [argv]\n"
<< "option:\n"
<< " -a, --ast | view ast after link/optimize process.\n"
<< " --raw-ast | view ast without after-processing.\n"
<< " -c, --code | view generated bytecode.\n"
<< " -s, --symbol | show analysed symbol info.\n"
<< " -e, --exec | execute directly.\n"
<< " -t, --time | show execute time.\n"
<< " -d, --detail | get detail info.\n"
<< " -f, --ref-file | get referenced files.\n"
<< " -dbg, --debug | debug mode.\n"
<< " --prof | show profiling result, "
<< "available under debug mode.\n"
<< " --prof-all | show profiling result of all files, "
<< "available under debug mode.\n"
<< " --limit | use limited execution mode "
<< "(readonly api enabled).\n"
<< "file:\n"
<< " <filename> | execute file.\n"
<< "argv:\n"
<< " <args> | cmd arguments used in program.\n"
<< "\n";
return out;
}
}

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

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

View File

@@ -13,58 +13,14 @@
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"
#include "repl.h"
#include <unordered_map>
#include "util/util.h"
#include "repl/repl.h"
#include "cli/cli.h"
#include <thread>
#include <cstdlib>
const u32 VM_RAW_AST = 1;
const u32 VM_AST = 1<<1;
const u32 VM_CODE = 1<<2;
const u32 VM_TIME = 1<<3;
const u32 VM_EXEC = 1<<4;
const u32 VM_DETAIL = 1<<5;
const u32 VM_DEBUG = 1<<6;
const u32 VM_SYMINFO = 1<<7;
const u32 VM_PROFILE = 1<<8;
const u32 VM_PROF_ALL = 1<<9;
std::ostream& help(std::ostream& out) {
out
<< "\n"
<< " ,--#-,\n"
<< "<3 / \\____\\ <3\n"
<< " |_|__A_|\n"
#ifdef _WIN32
<< "use command <chcp 65001> to use unicode.\n"
#endif
<< "\nnasal <option>\n"
<< "option:\n"
<< " -h, --help | get help.\n"
<< " -v, --version | get version.\n"
<< " -r, --repl | use repl interpreter(experimental).\n"
<< "\nnasal [option] <file> [argv]\n"
<< "option:\n"
<< " -a, --ast | view ast after link/optimize process.\n"
<< " --raw-ast | view ast without after-processing.\n"
<< " -c, --code | view generated bytecode.\n"
<< " -s, --symbol | show analysed symbol info.\n"
<< " -e, --exec | execute directly.\n"
<< " -t, --time | show execute time.\n"
<< " -d, --detail | get detail info.\n"
<< " -dbg, --debug | debug mode.\n"
<< " --prof | show profiling result, available in debug mode.\n"
<< " --prof-all | show profiling result of all files,"
<< "available under debug mode.\n"
<< "file:\n"
<< " <filename> | execute file.\n"
<< "argv:\n"
<< " <args> | cmd arguments used in program.\n"
<< "\n";
return out;
}
std::ostream& logo(std::ostream& out) {
out
<< "\n"
@@ -73,44 +29,52 @@ std::ostream& logo(std::ostream& out) {
<< " / \\/ / _` / __|/ _` | |\n"
<< " / /\\ / (_| \\__ \\ (_| | |\n"
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<< "ver : " << __nasver << " (" << __DATE__ << " " << __TIME__ << ")\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(std::time(nullptr));
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));
}
if (num<0.01) {
// give you 5% to see this easter egg
if (num<0.05) {
nasal::parse::easter_egg();
}
out << "nasal interpreter version " << __nasver;
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).\n"
<< "use <nasal -h> to get help.\n";
std::cerr << "invalid argument(s), use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(
const std::string& file,
const std::vector<std::string>& argv,
const u32 cmd) {
void execute(const nasal::cli::cli_config& config) {
using option = nasal::cli::option;
using clk = std::chrono::high_resolution_clock;
const auto den = clk::duration::period::den;
@@ -120,51 +84,67 @@ void execute(
nasal::codegen gen;
// lexer scans file to get tokens
lex.scan(file).chkerr();
lex.scan(config.input_file_path).chkerr();
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
if (cmd&VM_RAW_AST) {
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
dumper->dump(parse.tree());
if (config.has(option::cli_view_raw_ast)) {
nasal::ast_dumper().dump(parse.tree());
}
// linker gets parser's ast and load import files to this ast
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
ld.link(parse, config.has(option::cli_detail_info)).chkerr();
if (config.has(option::cli_show_referenced_file)) {
if (ld.get_file_list().size()) {
std::cout << "referenced file(s):\n";
}
for(const auto& file: ld.get_file_list()) {
std::cout << " " << file << "\n";
}
}
// optimizer does simple optimization on ast
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
auto opt = std::make_unique<nasal::optimizer>();
opt->do_optimization(parse.tree());
if (cmd&VM_AST) {
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
dumper->dump(parse.tree());
if (config.has(option::cli_view_ast)) {
nasal::ast_dumper().dump(parse.tree());
}
// code generator gets parser's ast and import file list to generate code
gen.compile(parse, ld, false).chkerr();
if (cmd&VM_CODE) {
gen.compile(parse, ld, false, config.has(option::cli_limit_mode)).chkerr();
if (config.has(option::cli_view_code)) {
gen.print(std::cout);
}
if (cmd&VM_SYMINFO) {
if (config.has(option::cli_show_symbol)) {
gen.symbol_dump(std::cout);
}
// run
auto start = clk::now();
if (cmd&VM_DEBUG) {
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg);
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
runtime->set_detail_report_info(cmd&VM_DETAIL);
runtime->run(gen, ld, argv);
const auto start = clk::now();
if (config.has(option::cli_debug_mode)) {
auto debugger = std::make_unique<nasal::dbg>();
debugger->run(
gen,
ld,
config.nasal_vm_args,
config.has(option::cli_profile),
config.has(option::cli_profile_all)
);
} else if (config.has(option::cli_show_execute_time) ||
config.has(option::cli_detail_info) ||
config.has(option::cli_limit_mode) ||
config.has(option::cli_execute)) {
auto runtime = std::make_unique<nasal::vm>();
runtime->set_detail_report_info(config.has(option::cli_detail_info));
runtime->set_limit_mode_flag(config.has(option::cli_limit_mode));
runtime->run(gen, ld, config.nasal_vm_args);
}
// get running time
auto end = clk::now();
if (cmd&VM_TIME) {
const auto end = clk::now();
if (config.has(option::cli_show_execute_time)) {
std::clog << "process exited after ";
std::clog << (end-start).count()*1.0/den << "s.\n\n";
std::clog << static_cast<f64>((end-start).count())/den << "s.\n\n";
}
}
@@ -175,18 +155,20 @@ i32 main(i32 argc, const char* argv[]) {
return 0;
}
// the first argument is the executable itself, ignore it
const auto config = nasal::cli::parse({argv+1, argv+argc});
// run directly or show help
if (argc==2) {
std::string s(argv[1]);
if (s=="-h" || s=="--help") {
std::clog << help;
} else if (s=="-v" || s=="--version") {
if (config.has(nasal::cli::option::cli_help)) {
std::clog << nasal::cli::help;
} else if (config.has(nasal::cli::option::cli_version)) {
std::clog << version;
} else if (s=="-r" || s=="--repl") {
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl);
} else if (config.has(nasal::cli::option::cli_repl_mode)) {
auto repl = std::make_unique<nasal::repl::repl>();
repl->execute();
} else if (s[0]!='-') {
execute(s, {}, VM_EXEC);
} else if (config.input_file_path.size()) {
execute(config);
} else {
err();
}
@@ -194,40 +176,6 @@ i32 main(i32 argc, const char* argv[]) {
}
// execute with arguments
const std::unordered_map<std::string, u32> cmdlst = {
{"--raw-ast", VM_RAW_AST},
{"--ast", VM_AST},
{"-a", VM_AST},
{"--code", VM_CODE},
{"-c", VM_CODE},
{"--symbol", VM_SYMINFO},
{"-s", VM_SYMINFO},
{"--exec", VM_EXEC},
{"-e", VM_EXEC},
{"--time", VM_TIME|VM_EXEC},
{"-t", VM_TIME|VM_EXEC},
{"--detail", VM_DETAIL|VM_EXEC},
{"-d", VM_DETAIL|VM_EXEC},
{"--debug", VM_DEBUG},
{"-dbg", VM_DEBUG},
{"--prof", VM_PROFILE},
{"--prof-all", VM_PROF_ALL}
};
u32 cmd = 0;
std::string filename = "";
std::vector<std::string> vm_argv;
for(i32 i = 1; i<argc; ++i) {
if (cmdlst.count(argv[i])) {
cmd |= cmdlst.at(argv[i]);
} else if (!filename.length()) {
filename = argv[i];
} else {
vm_argv.push_back(argv[i]);
}
}
if (!filename.length()) {
err();
}
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
execute(config);
return 0;
}

View File

@@ -1,15 +1,11 @@
#pragma once
#ifndef __nasver
#define __nasver "11.1"
#ifndef __nasver__
#define __nasver__ "11.2"
#endif
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <sstream>
#include <cmath>
// abbreviation of some useful basic type
using i32 = std::int32_t;
@@ -21,38 +17,5 @@ using u64 = std::uint64_t;
using usize = std::size_t;
using f64 = double;
namespace nasal {
bool is_windows();
bool is_linux();
bool is_macos();
bool is_x86();
bool is_amd64();
bool is_x86_64();
bool is_arm();
bool is_aarch64();
bool is_ia64();
bool is_powerpc();
bool is_superh();
// virtual machine stack depth, both global depth and value stack depth
const u32 STACK_DEPTH = 4096;
f64 hex2f(const char*);
f64 oct2f(const char*);
// we have the same reason not using atof here
// just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char*);
f64 str2num(const char*);
i32 utf8_hdchk(const char);
std::string chrhex(const char);
std::string rawstr(const std::string&, const usize maxlen = 0);
}
const u32 VM_STACK_DEPTH = UINT16_MAX;

View File

@@ -173,6 +173,10 @@ void call_hash::accept(ast_visitor* visitor) {
visitor->visit_call_hash(this);
}
void null_access::accept(ast_visitor* visitor) {
visitor->visit_null_access(this);
}
call_vector::~call_vector() {
for(auto i : calls) {
delete i;

View File

@@ -8,7 +8,7 @@
namespace nasal {
enum class expr_type:u32 {
enum class expr_type {
ast_null = 0, // null node
ast_use, // use statement
ast_block, // code block
@@ -23,6 +23,7 @@ enum class expr_type:u32 {
ast_pair, // pair of key and value in hashmap
ast_call, // mark a sub-tree of calling an identifier
ast_callh, // id.name
ast_null_access, // id?.name
ast_callv, // id[index]
ast_callf, // id()
ast_subvec, // id[index:index]
@@ -65,13 +66,13 @@ public:
expr(const span& location, expr_type node_type):
nd_loc(location), nd_type(node_type) {}
virtual ~expr() = default;
void set_begin(u32 line, u32 column) {
void set_begin(u64 line, u64 column) {
nd_loc.begin_line = line;
nd_loc.begin_column = column;
}
const span& get_location() const {return nd_loc;}
const u32 get_line() const {return nd_loc.begin_line;}
expr_type get_type() const {return nd_type;}
const auto& get_location() const { return nd_loc; }
const auto get_line() const { return nd_loc.begin_line; }
auto get_type() const { return nd_type; }
void update_location(const span& location) {
nd_loc.end_line = location.end_line;
nd_loc.end_column = location.end_column;
@@ -239,14 +240,14 @@ public:
class parameter: public expr {
public:
enum class param_type {
enum class kind {
normal_parameter,
default_parameter,
dynamic_parameter
};
private:
param_type type;
kind type;
std::string name;
expr* default_value;
@@ -255,12 +256,12 @@ public:
expr(location, expr_type::ast_param),
name(""), default_value(nullptr) {}
~parameter() override;
void set_parameter_type(param_type pt) {type = pt;}
void set_parameter_type(kind pt) {type = pt;}
void set_parameter_name(const std::string& pname) {name = pname;}
void set_default_value(expr* node) {default_value = node;}
param_type get_parameter_type() {return type;}
const std::string& get_parameter_name() const {return name;}
expr* get_default_value() {return default_value;}
auto get_parameter_type() {return type;}
const auto& get_parameter_name() const {return name;}
auto get_default_value() {return default_value;}
void accept(ast_visitor*) override;
};
@@ -286,7 +287,7 @@ public:
class binary_operator: public expr {
public:
enum class binary_type {
enum class kind {
add,
sub,
mult,
@@ -302,11 +303,12 @@ public:
bitwise_xor,
bitwise_and,
condition_and,
condition_or
condition_or,
null_chain
};
private:
binary_type type;
kind type;
expr* left;
expr* right;
number_literal* optimized_const_number;
@@ -319,29 +321,29 @@ public:
optimized_const_number(nullptr),
optimized_const_string(nullptr) {}
~binary_operator() override;
void set_operator_type(binary_type operator_type) {type = operator_type;}
void set_operator_type(kind operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
binary_type get_operator_type() const {return type;}
expr* get_left() {return left;}
expr* get_right() {return right;}
number_literal* get_optimized_number() {return optimized_const_number;}
string_literal* get_optimized_string() {return optimized_const_string;}
auto get_operator_type() const {return type;}
auto get_left() {return left;}
auto get_right() {return right;}
auto get_optimized_number() {return optimized_const_number;}
auto get_optimized_string() {return optimized_const_string;}
void accept(ast_visitor*) override;
};
class unary_operator: public expr {
public:
enum class unary_type {
enum class kind {
negative,
logical_not,
bitwise_not
};
private:
unary_type type;
kind type;
expr* value;
number_literal* optimized_number;
@@ -350,12 +352,12 @@ public:
expr(location, expr_type::ast_unary),
value(nullptr), optimized_number(nullptr) {}
~unary_operator() override;
void set_operator_type(unary_type operator_type) {type = operator_type;}
void set_operator_type(kind operator_type) {type = operator_type;}
void set_value(expr* node) {value = node;}
void set_optimized_number(number_literal* node) {optimized_number = node;}
unary_type get_operator_type() const {return type;}
expr* get_value() {return value;}
number_literal* get_optimized_number() {return optimized_number;}
auto get_operator_type() const {return type;}
auto get_value() {return value;}
auto get_optimized_number() {return optimized_number;}
void accept(ast_visitor*) override;
};
@@ -389,6 +391,19 @@ public:
void accept(ast_visitor*) override;
};
class null_access: public call {
private:
std::string field;
public:
null_access(const span& location, const std::string& name):
call(location, expr_type::ast_null_access),
field(name) {}
~null_access() override = default;
const std::string& get_field() const {return field;}
void accept(ast_visitor*) override;
};
class call_vector: public call {
private:
std::vector<slice_vector*> calls;
@@ -458,7 +473,7 @@ public:
class assignment_expr: public expr {
public:
enum class assign_type {
enum class kind {
equal,
add_equal,
sub_equal,
@@ -471,7 +486,7 @@ public:
};
private:
assign_type type;
kind type;
expr* left;
expr* right;
@@ -480,12 +495,12 @@ public:
expr(location, expr_type::ast_assign),
left(nullptr), right(nullptr) {}
~assignment_expr() override;
void set_assignment_type(assign_type operator_type) {type = operator_type;}
void set_assignment_type(kind operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
assign_type get_assignment_type() const {return type;}
expr* get_left() {return left;}
expr* get_right() {return right;}
auto get_assignment_type() const {return type;}
auto get_left() {return left;}
auto get_right() {return right;}
void accept(ast_visitor*) override;
};
@@ -596,13 +611,13 @@ public:
class forei_expr: public expr {
public:
enum class forei_loop_type {
enum class kind {
foreach,
forindex
};
private:
forei_loop_type type;
kind type;
iter_expr* iterator;
expr* vector_node;
code_block* block;
@@ -610,17 +625,17 @@ private:
public:
forei_expr(const span& location):
expr(location, expr_type::ast_forei),
type(forei_loop_type::foreach), iterator(nullptr),
type(kind::foreach), iterator(nullptr),
vector_node(nullptr), block(nullptr) {}
~forei_expr() override;
void set_loop_type(forei_loop_type ft) {type = ft;}
void set_loop_type(kind ft) {type = ft;}
void set_iterator(iter_expr* node) {iterator = node;}
void set_value(expr* node) {vector_node = node;}
void set_code_block(code_block* node) {block = node;}
forei_loop_type get_loop_type() const {return type;}
iter_expr* get_iterator() {return iterator;}
expr* get_value() {return vector_node;}
code_block* get_code_block() {return block;}
auto get_loop_type() const {return type;}
auto get_iterator() {return iterator;}
auto get_value() {return vector_node;}
auto get_code_block() {return block;}
void accept(ast_visitor*) override;
};

View File

@@ -1,4 +1,5 @@
#include "nasal_codegen.h"
#include "util/util.h"
namespace nasal {
@@ -11,12 +12,18 @@ void codegen::init_file_map(const std::vector<std::string>& file_list) {
void codegen::load_native_function_table(nasal_builtin_table* table) {
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;
}
native_function.push_back(table[i]);
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
native_function_mapper[table[i].name] = index;
}
}
@@ -30,6 +37,8 @@ void codegen::init_native_function() {
load_native_function_table(flight_gear_native);
load_native_function_table(dylib_lib_native);
load_native_function_table(unix_lib_native);
load_native_function_table(json_lib_native);
load_native_function_table(regex_lib_native);
}
void codegen::check_id_exist(identifier* node) {
@@ -62,7 +71,8 @@ void codegen::regist_number(const f64 num) {
if (const_number_map.count(num)) {
return;
}
u32 size = const_number_map.size();
auto size = const_number_map.size();
const_number_map[num] = size;
const_number_table.push_back(num);
}
@@ -71,13 +81,14 @@ void codegen::regist_string(const std::string& str) {
if (const_string_map.count(str)) {
return;
}
u32 size = const_string_map.size();
auto size = const_string_map.size();
const_string_map[str] = size;
const_string_table.push_back(str);
}
void codegen::find_symbol(code_block* node) {
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
auto finder = std::make_unique<symbol_finder>();
for(const auto& i : finder->do_find(node)) {
// check if symbol conflicts with native function name
if (native_function_mapper.count(i.name)) {
@@ -85,11 +96,11 @@ void codegen::find_symbol(code_block* node) {
continue;
}
// create new namespace with checking existence of location file
if (!experimental_namespace.count(i.location.file)) {
experimental_namespace[i.location.file] = {};
if (!nasal_namespace.count(i.location.file)) {
nasal_namespace[i.location.file] = {};
}
// if in global scope, load global symbol into this namespace
auto& scope = experimental_namespace.at(i.location.file);
auto& scope = nasal_namespace.at(i.location.file);
if (local.empty() && !scope.count(i.name)) {
scope.insert(i.name);
}
@@ -99,41 +110,47 @@ void codegen::find_symbol(code_block* node) {
}
void codegen::regist_symbol(const std::string& name) {
// regist global if local scope list is empty
if (local.empty()) {
if (global.count(name)) {
return;
}
i32 index = global.size();
auto index = global.size();
global[name] = index;
return;
}
if (local.back().count(name)) {
return;
}
i32 index = local.back().size();
auto index = local.back().size();
local.back()[name] = index;
}
i32 codegen::local_symbol_find(const std::string& name) {
i64 codegen::local_symbol_find(const std::string& name) {
if (local.empty()) {
return -1;
}
return local.back().count(name)? local.back().at(name):-1;
}
i32 codegen::global_symbol_find(const std::string& name) {
i64 codegen::global_symbol_find(const std::string& name) {
return global.count(name)? global.at(name):-1;
}
i32 codegen::upvalue_symbol_find(const std::string& name) {
i64 codegen::upvalue_symbol_find(const std::string& name) {
// 32768 level 65536 upvalues
i32 index = -1;
// may cause some errors if local scope depth is too deep or
// local scope's symbol list size is greater than 65536,
// but we check the local size in codegen::func_gen
i64 index = -1;
usize size = local.size();
if (size<=1) {
return -1;
}
auto iter = local.begin();
for(u32 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));
}
@@ -141,7 +158,7 @@ i32 codegen::upvalue_symbol_find(const std::string& name) {
return index;
}
void codegen::emit(u8 operation_code, u32 immediate_num, const span& location) {
void codegen::emit(u8 operation_code, u64 immediate_num, const span& location) {
code.push_back({
operation_code,
static_cast<u16>(file_map.at(location.file)),
@@ -192,16 +209,16 @@ void codegen::func_gen(function* node) {
std::unordered_map<std::string, bool> argname;
for(auto tmp : node->get_parameter_list()) {
if (tmp->get_parameter_type()==
parameter::param_type::default_parameter) {
parameter::kind::default_parameter) {
checked_default = true;
} else if (tmp->get_parameter_type()==
parameter::param_type::dynamic_parameter) {
parameter::kind::dynamic_parameter) {
checked_dynamic = true;
}
// check default parameter and dynamic parameter
if (checked_default &&
tmp->get_parameter_type()!=
parameter::param_type::default_parameter) {
parameter::kind::default_parameter) {
die("must use default parameter here",
tmp->get_location()
);
@@ -223,9 +240,9 @@ void codegen::func_gen(function* node) {
}
}
usize newf=code.size();
const auto newf = code.size();
emit(op_newf, 0, node->get_location());
usize lsize=code.size();
const auto lsize = code.size();
emit(op_intl, 0, node->get_location());
// add special keyword 'me' into symbol table
@@ -245,14 +262,14 @@ void codegen::func_gen(function* node) {
}
regist_string(name);
switch(tmp->get_parameter_type()) {
case parameter::param_type::normal_parameter:
case parameter::kind::normal_parameter:
emit(op_para, const_string_map.at(name), tmp->get_location());
break;
case parameter::param_type::default_parameter:
case parameter::kind::default_parameter:
calc_gen(tmp->get_default_value());
emit(op_deft, const_string_map.at(name), tmp->get_location());
break;
case parameter::param_type::dynamic_parameter:
case parameter::kind::dynamic_parameter:
emit(op_dyn, const_string_map.at(name), tmp->get_location());
break;
}
@@ -289,10 +306,14 @@ void codegen::func_gen(function* node) {
block_gen(block);
in_foreach_loop_level.pop_back();
// we must check the local scope symbol list size
// the local scope should not cause stack overflow
// and should not greater than upvalue's max size(65536)
code[lsize].num = local.back().size();
if (local.back().size()>=STACK_DEPTH) {
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()
std::to_string(local.back().size()),
block->get_location()
);
}
local.pop_back();
@@ -318,6 +339,8 @@ void codegen::call_gen(call_expr* node) {
call_vector_gen(reinterpret_cast<call_vector*>(i)); break;
case expr_type::ast_callf:
call_func_gen(reinterpret_cast<call_function*>(i)); break;
case expr_type::ast_null_access:
null_access_gen(reinterpret_cast<null_access*>(i)); break;
default: break;
}
}
@@ -338,7 +361,7 @@ void codegen::call_identifier(identifier* node) {
return;
}
i32 index;
i64 index;
if ((index = local_symbol_find(name))>=0) {
emit(op_calll, index, node->get_location());
return;
@@ -361,6 +384,24 @@ void codegen::call_hash_gen(call_hash* node) {
emit(op_callh, const_string_map.at(node->get_field()), node->get_location());
}
void codegen::null_access_gen(null_access* node) {
regist_string(node->get_field());
emit(op_dup, 0, node->get_location());
emit(op_pnil, 0, node->get_location());
emit(op_eq, 0, node->get_location());
const auto jmp_false_point = code.size();
emit(op_jf, 0, node->get_location());
const auto jmp_direct_point = code.size();
emit(op_jmp, 0, node->get_location());
code[jmp_false_point].num = code.size();
emit(op_callh, const_string_map.at(node->get_field()), node->get_location());
code[jmp_direct_point].num = code.size();
}
void codegen::call_vector_gen(call_vector* node) {
// maybe this place can use callv-const if ast's first child is ast_num
if (node->get_slices().size()==1 &&
@@ -434,6 +475,8 @@ void codegen::mcall(expr* node) {
call_vector_gen(reinterpret_cast<call_vector*>(tmp)); break;
case expr_type::ast_callf:
call_func_gen(reinterpret_cast<call_function*>(tmp)); break;
case expr_type::ast_null_access:
null_access_gen(reinterpret_cast<null_access*>(tmp)); break;
default: break;
}
}
@@ -446,6 +489,8 @@ void codegen::mcall(expr* node) {
mcall_vec(reinterpret_cast<call_vector*>(tmp)); break;
case expr_type::ast_callf:
die("bad left-value: function call", tmp->get_location()); break;
case expr_type::ast_null_access:
die("bad left-value: null access test", tmp->get_location()); break;
default:
die("bad left-value: unknown call", tmp->get_location()); break;
}
@@ -458,7 +503,7 @@ void codegen::mcall_identifier(identifier* node) {
return;
}
i32 index;
i64 index;
if ((index = local_symbol_find(name))>=0) {
emit(op_mcalll, index, node->get_location());
return;
@@ -497,7 +542,7 @@ void codegen::single_def(definition_expr* node) {
const auto& str = node->get_variable_name()->get_name();
calc_gen(node->get_value());
// only generate in repl mode and in global scope
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
emit(op_repl, 0, node->get_location());
}
if (local.empty()) {
@@ -510,7 +555,7 @@ void codegen::single_def(definition_expr* node) {
void codegen::multi_def(definition_expr* node) {
auto& identifiers = node->get_variables()->get_variables();
usize size = identifiers.size();
// (var a,b,c) = (c,b,a);
// (var a, b, c) = (c, b, a);
if (node->get_tuple()) {
auto& vals = node->get_tuple()->get_elements();
if (identifiers.size()>vals.size()) {
@@ -537,7 +582,7 @@ void codegen::multi_def(definition_expr* node) {
}
return;
}
// (var a,b,c) = [0,1,2];
// (var a, b, c) = [0, 1, 2];
calc_gen(node->get_value());
for(usize i = 0; i<size; ++i) {
emit(op_callvi, i, node->get_value()->get_location());
@@ -558,12 +603,12 @@ void codegen::definition_gen(definition_expr* node) {
void codegen::assignment_expression(assignment_expr* node) {
switch(node->get_assignment_type()) {
case assignment_expr::assign_type::equal:
case assignment_expr::kind::equal:
calc_gen(node->get_right());
mcall(node->get_left());
emit(op_meq, 0, node->get_location());
break;
case assignment_expr::assign_type::add_equal:
case assignment_expr::kind::add_equal:
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
}
@@ -577,7 +622,7 @@ void codegen::assignment_expression(assignment_expr* node) {
emit(op_addeqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::sub_equal:
case assignment_expr::kind::sub_equal:
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
}
@@ -591,7 +636,7 @@ void codegen::assignment_expression(assignment_expr* node) {
emit(op_subeqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::mult_equal:
case assignment_expr::kind::mult_equal:
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
}
@@ -605,7 +650,7 @@ void codegen::assignment_expression(assignment_expr* node) {
emit(op_muleqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::div_equal:
case assignment_expr::kind::div_equal:
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
}
@@ -619,7 +664,7 @@ void codegen::assignment_expression(assignment_expr* node) {
emit(op_diveqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::concat_equal:
case assignment_expr::kind::concat_equal:
if (node->get_right()->get_type()!=expr_type::ast_str) {
calc_gen(node->get_right());
}
@@ -633,17 +678,17 @@ void codegen::assignment_expression(assignment_expr* node) {
emit(op_lnkeqc, const_string_map[str], node->get_location());
}
break;
case assignment_expr::assign_type::bitwise_and_equal:
case assignment_expr::kind::bitwise_and_equal:
calc_gen(node->get_right());
mcall(node->get_left());
emit(op_btandeq, 0, node->get_location());
break;
case assignment_expr::assign_type::bitwise_or_equal:
case assignment_expr::kind::bitwise_or_equal:
calc_gen(node->get_right());
mcall(node->get_left());
emit(op_btoreq, 0, node->get_location());
break;
case assignment_expr::assign_type::bitwise_xor_equal:
case assignment_expr::kind::bitwise_xor_equal:
calc_gen(node->get_right());
mcall(node->get_left());
emit(op_btxoreq, 0, node->get_location());
@@ -695,17 +740,17 @@ void codegen::replace_left_assignment_with_load(const span& location) {
void codegen::assignment_statement(assignment_expr* node) {
switch(node->get_assignment_type()) {
case assignment_expr::assign_type::equal:
case assignment_expr::kind::equal:
gen_assignment_equal_statement(node);
break;
case assignment_expr::assign_type::add_equal:
case assignment_expr::assign_type::sub_equal:
case assignment_expr::assign_type::mult_equal:
case assignment_expr::assign_type::div_equal:
case assignment_expr::assign_type::concat_equal:
case assignment_expr::assign_type::bitwise_and_equal:
case assignment_expr::assign_type::bitwise_or_equal:
case assignment_expr::assign_type::bitwise_xor_equal:
case assignment_expr::kind::add_equal:
case assignment_expr::kind::sub_equal:
case assignment_expr::kind::mult_equal:
case assignment_expr::kind::div_equal:
case assignment_expr::kind::concat_equal:
case assignment_expr::kind::bitwise_and_equal:
case assignment_expr::kind::bitwise_or_equal:
case assignment_expr::kind::bitwise_xor_equal:
calc_gen(node);
if (op_addeq<=code.back().op && code.back().op<=op_btxoreq) {
code.back().num = 1;
@@ -744,16 +789,16 @@ void codegen::multi_assign_gen(multi_assign* node) {
}
}
i32 size = tuple_node->get_elements().size();
i64 size = static_cast<i64>(tuple_node->get_elements().size());
// generate multiple assignment: (a, b, c) = (1, 2, 3);
if (value_node->get_type()==expr_type::ast_tuple) {
const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node)
->get_elements();
for(i32 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(i32 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
@@ -765,13 +810,14 @@ 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(i32 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
// and this operation changes local and global value directly
replace_left_assignment_with_load(tuple[i]->get_location());
}
// pop source vector
emit(op_pop, 0, node->get_location());
}
@@ -826,7 +872,7 @@ void codegen::loop_gen(expr* node) {
}
}
void codegen::load_continue_break(i32 continue_place, i32 break_place) {
void codegen::load_continue_break(u64 continue_place, u64 break_place) {
for(auto i : continue_ptr.front()) {
code[i].num = continue_place;
}
@@ -874,7 +920,7 @@ void codegen::forei_gen(forei_expr* node) {
calc_gen(node->get_value());
emit(op_cnt, 0, node->get_value()->get_location());
usize loop_begin = code.size();
if (node->get_loop_type()==forei_expr::forei_loop_type::forindex) {
if (node->get_loop_type()==forei_expr::kind::forindex) {
emit(op_findex, 0, node->get_location());
} else {
emit(op_feach, 0, node->get_location());
@@ -935,7 +981,7 @@ void codegen::statement_generation(expr* node) {
case expr_type::ast_ternary:
calc_gen(node);
// only generate in repl mode and in global scope
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
emit(op_repl, 0, node->get_location());
}
emit(op_pop, 0, node->get_location());
@@ -986,11 +1032,11 @@ void codegen::unary_gen(unary_operator* node) {
calc_gen(node->get_value());
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative:
case unary_operator::kind::negative:
emit(op_usub, 0, node->get_location()); break;
case unary_operator::unary_type::logical_not:
case unary_operator::kind::logical_not:
emit(op_lnot, 0, node->get_location()); break;
case unary_operator::unary_type::bitwise_not:
case unary_operator::kind::bitwise_not:
emit(op_bnot, 0, node->get_location()); break;
}
}
@@ -1007,40 +1053,43 @@ void codegen::binary_gen(binary_operator* node) {
}
switch(node->get_operator_type()) {
case binary_operator::binary_type::condition_or: or_gen(node); return;
case binary_operator::binary_type::condition_and: and_gen(node); return;
case binary_operator::kind::condition_or: or_gen(node); return;
case binary_operator::kind::condition_and: and_gen(node); return;
default: break;
}
switch(node->get_operator_type()) {
case binary_operator::binary_type::cmpeq:
case binary_operator::kind::cmpeq:
calc_gen(node->get_left());
calc_gen(node->get_right());
emit(op_eq, 0, node->get_location());
return;
case binary_operator::binary_type::cmpneq:
case binary_operator::kind::cmpneq:
calc_gen(node->get_left());
calc_gen(node->get_right());
emit(op_neq, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_or:
case binary_operator::kind::bitwise_or:
calc_gen(node->get_left());
calc_gen(node->get_right());
emit(op_btor, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_xor:
case binary_operator::kind::bitwise_xor:
calc_gen(node->get_left());
calc_gen(node->get_right());
emit(op_btxor, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_and:
case binary_operator::kind::bitwise_and:
calc_gen(node->get_left());
calc_gen(node->get_right());
emit(op_btand, 0, node->get_location());
return;
case binary_operator::kind::null_chain:
null_chain_gen(node);
return;
default: break;
}
switch(node->get_operator_type()) {
case binary_operator::binary_type::add:
case binary_operator::kind::add:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1052,7 +1101,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_addc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::sub:
case binary_operator::kind::sub:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1064,7 +1113,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_subc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::mult:
case binary_operator::kind::mult:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1076,7 +1125,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_mulc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::div:
case binary_operator::kind::div:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1088,7 +1137,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_divc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::concat:
case binary_operator::kind::concat:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_str) {
calc_gen(node->get_right());
@@ -1100,7 +1149,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_lnkc, const_string_map.at(str), node->get_location());
}
break;
case binary_operator::binary_type::less:
case binary_operator::kind::less:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1112,7 +1161,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_lessc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::leq:
case binary_operator::kind::leq:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1124,7 +1173,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_leqc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::grt:
case binary_operator::kind::grt:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1136,7 +1185,7 @@ void codegen::binary_gen(binary_operator* node) {
emit(op_grtc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::geq:
case binary_operator::kind::geq:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
@@ -1152,6 +1201,23 @@ void codegen::binary_gen(binary_operator* node) {
}
}
void codegen::null_chain_gen(binary_operator* node) {
calc_gen(node->get_left());
emit(op_pnil, 0, node->get_location());
emit(op_eq, 0, node->get_location());
const auto jmp_false_point = code.size();
emit(op_jf, 0, node->get_location());
calc_gen(node->get_right());
const auto jmp_direct_point = code.size();
emit(op_jmp, 0, node->get_location());
code[jmp_false_point].num = code.size();
emit(op_pop, 0, node->get_location());
code[jmp_direct_point].num = code.size();
}
void codegen::trino_gen(ternary_operator* node) {
calc_gen(node->get_condition());
usize label_jump_false = code.size();
@@ -1246,7 +1312,7 @@ void codegen::block_gen(code_block* node) {
break;
case expr_type::ast_null: break;
case expr_type::ast_id:
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp);
} else {
check_id_exist(reinterpret_cast<identifier*>(tmp));
@@ -1256,7 +1322,7 @@ void codegen::block_gen(code_block* node) {
case expr_type::ast_num:
case expr_type::ast_str:
case expr_type::ast_bool:
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp);
}
break;
@@ -1301,10 +1367,14 @@ void codegen::ret_gen(return_expr* node) {
emit(op_ret, 0, node->get_location());
}
const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
const error& codegen::compile(parse& parse,
linker& import,
bool repl_flag,
bool limit_mode) {
flag_need_repl_output = repl_flag;
flag_limited_mode = limit_mode;
init_native_function();
init_file_map(import.get_file_list());
need_repl_output = repl_flag;
in_foreach_loop_level.push_back(0);
@@ -1322,13 +1392,13 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
emit(op_exit, 0, parse.tree()->get_location());
// size out of bound check
if (const_number_table.size()>0xffffff) {
if (const_number_table.size()>INT64_MAX) {
err.err("code",
"too many constant numbers: " +
std::to_string(const_number_table.size())
);
}
if (const_string_table.size()>0xffffff) {
if (const_string_table.size()>INT64_MAX) {
err.err("code",
"too many constant strings: " +
std::to_string(const_string_table.size())
@@ -1336,7 +1406,7 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
}
// check global variables size
if (global.size()>=STACK_DEPTH/2) {
if (global.size()>=VM_STACK_DEPTH) {
err.err("code",
"too many global variables: " +
std::to_string(global.size())
@@ -1344,7 +1414,7 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
}
// check generated code size
if (code.size()>0xffffff) {
if (code.size()>INT64_MAX) {
err.err("code",
"bytecode size overflow: " +
std::to_string(code.size())
@@ -1355,8 +1425,8 @@ const error& codegen::compile(parse& parse, linker& import, bool repl_flag) {
void codegen::print(std::ostream& out) {
// func end stack, reserved for code print
std::stack<u32> func_begin_stack;
std::stack<u32> func_end_stack;
std::stack<u64> func_begin_stack;
std::stack<u64> func_end_stack;
// print const numbers
for(auto num : const_number_table) {
@@ -1365,7 +1435,7 @@ void codegen::print(std::ostream& out) {
// print const strings
for(const auto& str : const_string_table) {
out << " .symbol \"" << rawstr(str) << "\"\n";
out << " .symbol \"" << util::rawstr(str) << "\"\n";
}
// print blank line
@@ -1379,7 +1449,8 @@ void codegen::print(std::ostream& out) {
const_string_table.data(),
native_function.data()
);
for(u32 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()) {
@@ -1395,7 +1466,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(u32 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);
@@ -1410,7 +1481,7 @@ void codegen::print(std::ostream& out) {
}
void codegen::symbol_dump(std::ostream& out) const {
for(const auto& domain : experimental_namespace) {
for(const auto& domain : nasal_namespace) {
out << "<" << domain.first << ">\n";
for(const auto& i : domain.second) {
out << " 0x" << std::setw(4) << std::setfill('0');

View File

@@ -8,14 +8,16 @@
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_builtin.h"
#include "coroutine.h"
#include "bits_lib.h"
#include "math_lib.h"
#include "fg_props.h"
#include "io_lib.h"
#include "dylib_lib.h"
#include "unix_lib.h"
#include "natives/builtin.h"
#include "natives/coroutine.h"
#include "natives/bits_lib.h"
#include "natives/math_lib.h"
#include "natives/fg_props.h"
#include "natives/io_lib.h"
#include "natives/json_lib.h"
#include "natives/dylib_lib.h"
#include "natives/regex_lib.h"
#include "natives/unix_lib.h"
#include <iomanip>
#include <list>
@@ -35,7 +37,28 @@ private:
error err;
// repl output flag, will generate op_repl to output stack top value if true
bool need_repl_output;
bool flag_need_repl_output = false;
// limit mode flag
bool flag_limited_mode = false;
// under limited mode, unsafe system api will be banned
const std::unordered_set<std::string> unsafe_system_api = {
// builtin
"__system", "__input",
// io
"__fout", "__open", "__write", "__stat"
// bits
"__fld", "__sfld", "__setfld",
"__buf",
// fg
"__logprint",
// dylib
"__dlopen", "__dlclose", "__dlcallv", "__dlcall",
// unix
"__pipe", "__fork", "__waitpid", "__chdir",
"__environ", "__getcwd", "__getenv"
};
// file mapper for file -> index
std::unordered_map<std::string, usize> file_map;
@@ -45,8 +68,8 @@ private:
std::vector<u32> in_foreach_loop_level;
// constant numbers and strings
std::unordered_map<f64, u32> const_number_map;
std::unordered_map<std::string, u32> const_string_map;
std::unordered_map<f64, u64> const_number_map;
std::unordered_map<std::string, u64> const_string_map;
std::vector<f64> const_number_table;
std::vector<std::string> const_string_table;
@@ -60,17 +83,20 @@ private:
std::vector<opcode> code;
// used to store jmp operands index, to fill the jump address back
std::list<std::vector<i32>> continue_ptr;
std::list<std::vector<i32>> break_ptr;
std::list<std::vector<u64>> continue_ptr;
std::list<std::vector<u64>> break_ptr;
// symbol table
// global : max STACK_DEPTH-1 values
std::unordered_map<std::string, i32> global;
std::unordered_map<std::string, std::unordered_set<std::string>> experimental_namespace;
// global : max VM_STACK_DEPTH-1 values
std::unordered_map<std::string, u64> global;
// nasal namespace
// stores all global symbols of each file
std::unordered_map<std::string, std::unordered_set<std::string>> nasal_namespace;
// local : max 32768 upvalues 65536 values
// but in fact local scope also has less than STACK_DEPTH value
std::list<std::unordered_map<std::string, i32>> local;
// but in fact local scope also has less than VM_STACK_DEPTH value
std::list<std::unordered_map<std::string, u64>> local;
void check_id_exist(identifier*);
@@ -82,11 +108,11 @@ private:
void regist_string(const std::string&);
void find_symbol(code_block*);
void regist_symbol(const std::string&);
i32 local_symbol_find(const std::string&);
i32 global_symbol_find(const std::string&);
i32 upvalue_symbol_find(const std::string&);
i64 local_symbol_find(const std::string&);
i64 global_symbol_find(const std::string&);
i64 upvalue_symbol_find(const std::string&);
void emit(u8, u32, const span&);
void emit(u8, u64, const span&);
void number_gen(number_literal*);
void string_gen(string_literal*);
@@ -97,6 +123,7 @@ private:
void call_gen(call_expr*);
void call_identifier(identifier*);
void call_hash_gen(call_hash*);
void null_access_gen(null_access*);
void call_vector_gen(call_vector*);
void call_func_gen(call_function*);
void mcall(expr*);
@@ -113,7 +140,7 @@ private:
void multi_assign_gen(multi_assign*);
void cond_gen(condition_expr*);
void loop_gen(expr*);
void load_continue_break(i32, i32);
void load_continue_break(u64, u64);
void while_gen(while_expr*);
void for_gen(for_expr*);
void forei_gen(forei_expr*);
@@ -122,6 +149,7 @@ private:
void and_gen(binary_operator*);
void unary_gen(unary_operator*);
void binary_gen(binary_operator*);
void null_chain_gen(binary_operator*);
void trino_gen(ternary_operator*);
void calc_gen(expr*);
void repl_mode_info_output_gen(expr*);
@@ -134,13 +162,10 @@ public:
const auto& natives() const {return native_function;}
const auto& codes() const {return code;}
const auto& globals() const {return global;}
const auto& get_experimental_namespace() const {
return experimental_namespace;
}
public:
codegen() = default;
const error& compile(parse&, linker&, bool);
const error& compile(parse&, linker&, bool, bool);
void print(std::ostream&);
void symbol_dump(std::ostream&) const;
};

View File

@@ -2,18 +2,18 @@
namespace nasal {
void debug_prof_data::init_counter() {
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
void operand_line_counter::init_counter() {
for(usize i = 0; i<operand_line_counter::operand_size; ++i) {
operand_counter[i] = 0;
}
}
void debug_prof_data::load_file_line_counter(
void operand_line_counter::load_file_line_counter(
const std::vector<std::string>& file_list) {
file_name_list = file_list;
file_line_counter = {};
file_contents = {};
flstream fs;
filestream fs;
for(usize i =0; i<file_list.size(); ++i) {
fs.load(file_list[i]);
file_contents.push_back(fs.file_content());
@@ -22,16 +22,16 @@ void debug_prof_data::load_file_line_counter(
}
}
void debug_prof_data::init(const std::vector<std::string>& file_list) {
void operand_line_counter::init(const std::vector<std::string>& file_list) {
init_counter();
load_file_line_counter(file_list);
}
void debug_prof_data::dump_counter() const {
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<debug_prof_data::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]});
}
@@ -46,13 +46,14 @@ void debug_prof_data::dump_counter() const {
if (!rate) {
break;
}
std::clog << " " << opname[i.first] << " : ";
std::clog << i.second << " (" << rate << "%)\n";
std::clog << " ";
std::clog << operand_name_table.at(static_cast<op_code_type>(i.first));
std::clog << " : " << i.second << " (" << rate << "%)\n";
}
std::clog << " total : " << total << '\n';
}
void debug_prof_data::dump_code_line_counter(std::ostream& os) 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) {
@@ -73,7 +74,7 @@ void debug_prof_data::dump_code_line_counter(std::ostream& os) const {
}
}
void debug_prof_data::dump_this_file_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]) {
max_call_time = count>max_call_time? count:max_call_time;
@@ -108,21 +109,21 @@ std::vector<std::string> dbg::parse(const std::string& cmd) {
}
u16 dbg::file_index(const std::string& filename) const {
for(u16 i = 0; i<fsize; ++i) {
for(u16 i = 0; i<file_list_size; ++i) {
if (filename==files[i]) {
return i;
}
}
return 65535;
return UINT16_MAX;
}
void dbg::err() {
void dbg::err() const {
std::cerr
<< "incorrect command\n"
<< "input \'h\' to get help\n";
}
void dbg::help() {
void dbg::help() const {
std::clog
<< "<option>\n"
<< " h, help | get help\n"
@@ -133,7 +134,7 @@ void dbg::help() {
<< " l, local | see local values\n"
<< " u, upval | see upvalue\n"
<< " r, register | show vm register detail\n"
<< " a, all | show global,local and upvalue\n"
<< " a, all | show global, local and upvalue\n"
<< " n, next | execute next bytecode\n"
<< " q, exit | exit debugger\n"
<< "<option> <filename> <line>\n"
@@ -141,18 +142,20 @@ void dbg::help() {
}
void dbg::list_file() const {
for(usize i = 0; i<fsize; ++i) {
for(usize i = 0; i<file_list_size; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n";
}
}
void dbg::step_info() {
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u32 begin = (line>>3)==0? 0:((line>>3)<<3);
u32 end = (1+(line>>3))<<3;
u64 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u64 begin = (line>>3)==0? 0:((line>>3)<<3);
u64 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog << "\nsource code:\n";
for(u32 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";
}
@@ -160,15 +163,16 @@ void dbg::step_info() {
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3;
codestream::set(const_number, const_string, native_function.data(), files);
std::clog << "\nnext bytecode:\n";
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
for(u64 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog
<< (i==ctx.pc? back_white:reset)
<< (i==ctx.pc? "--> ":" ")
<< codestream(bytecode[i], i)
<< reset << "\n";
}
stack_info(10);
stack_info(16);
}
void dbg::interact() {
@@ -178,7 +182,7 @@ void dbg::interact() {
}
// do not need interact while doing profiling
if (do_profiling) {
if (do_operand_count) {
return;
}
@@ -199,26 +203,26 @@ void dbg::interact() {
step_info();
} else if (res.size()==1) {
switch(get_cmd_type(res[0])) {
case dbg_cmd::cmd_help: help(); break;
case dbg_cmd::cmd_backtrace:
case cmd_kind::cmd_help: help(); break;
case cmd_kind::cmd_backtrace:
function_call_trace();
trace_back();
break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: global_state(); break;
case dbg_cmd::cmd_local: local_state(); break;
case dbg_cmd::cmd_upval: upvalue_state(); break;
case dbg_cmd::cmd_register: register_info(); break;
case dbg_cmd::cmd_show_all: all_state_detail(); break;
case dbg_cmd::cmd_next: next = true; return;
case dbg_cmd::cmd_exit: std::exit(0);
case cmd_kind::cmd_continue: return;
case cmd_kind::cmd_list_file: list_file(); break;
case cmd_kind::cmd_global: global_state(); break;
case cmd_kind::cmd_local: local_state(); break;
case cmd_kind::cmd_upval: upvalue_state(); break;
case cmd_kind::cmd_register: register_info(); break;
case cmd_kind::cmd_show_all: all_state_detail(); break;
case cmd_kind::cmd_next: next = true; return;
case cmd_kind::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
get_cmd_type(res[0])==cmd_kind::cmd_break_point) {
break_file_index = file_index(res[1]);
if (break_file_index==65535) {
if (break_file_index==UINT16_MAX) {
std::clog << "cannot find file named `" << res[1] << "`\n";
continue;
}
@@ -234,19 +238,19 @@ void dbg::interact() {
}
}
void dbg::run(
const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv,
bool profile,
bool show_all_prof_result) {
void dbg::run(const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv,
bool profile,
bool show_all_prof_result) {
set_detail_report_info(true);
do_profiling = profile || show_all_prof_result;
do_operand_count = profile || show_all_prof_result;
const auto& file_list = linker.get_file_list();
fsize = file_list.size();
init(
file_list_size = file_list.size();
vm_init_enrty(
gen.strs(),
gen.nums(),
gen.natives(),
@@ -255,12 +259,12 @@ void dbg::run(
file_list,
argv
);
data.init(file_list);
counter.init(file_list);
std::vector<u32> code;
std::vector<u8> code;
std::vector<u16> code_file_index;
std::vector<u32> code_line;
for(auto& i : gen.codes()) {
std::vector<u64> code_line;
for(const auto& i : gen.codes()) {
code.push_back(i.op);
code_file_index.push_back(i.fidx);
code_line.push_back(i.line);
@@ -268,8 +272,8 @@ void dbg::run(
}
while(operand_function[code[ctx.pc]]) {
interact();
data.add_operand_counter(code[ctx.pc]);
data.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
counter.add_operand_counter(code[ctx.pc]);
counter.add_code_line_counter(code_file_index[ctx.pc], code_line[ctx.pc]);
(this->*operand_function[code[ctx.pc]])();
if (ctx.top>=ctx.canary) {
die("stack overflow");
@@ -277,11 +281,11 @@ void dbg::run(
++ctx.pc;
}
data.dump_counter();
if (do_profiling) {
counter.dump_operand_count();
if (do_operand_count) {
show_all_prof_result?
data.dump_code_line_counter(std::clog):
data.dump_this_file_line_counter(std::clog);
counter.dump_all_code_line_counter(std::clog):
counter.dump_this_file_line_counter(std::clog);
}
ngc.info();
ngc.clear();

View File

@@ -11,7 +11,9 @@
namespace nasal {
class debug_prof_data {
// count detail operand calling
// and show them before each line of the source file
class operand_line_counter {
private:
static const usize operand_size = op_code_type::op_ret + 1;
u64 operand_counter[operand_size];
@@ -25,8 +27,8 @@ private:
public:
void init(const std::vector<std::string>&);
void dump_counter() const;
void dump_code_line_counter(std::ostream&) const;
void dump_operand_count() const;
void dump_all_code_line_counter(std::ostream&) const;
void dump_this_file_line_counter(std::ostream&) const;
void add_operand_counter(usize index) {
operand_counter[index] += index<operand_size? 1:0;
@@ -39,56 +41,7 @@ public:
class dbg: public vm {
private:
typedef void (dbg::*nasal_vm_func)();
const nasal_vm_func operand_function[op_ret + 1] = {
nullptr, &dbg::o_repl,
&dbg::o_intl, &dbg::o_loadg,
&dbg::o_loadl, &dbg::o_loadu,
&dbg::o_pnum, &dbg::o_pnil,
&dbg::o_pstr, &dbg::o_newv,
&dbg::o_newh, &dbg::o_newf,
&dbg::o_happ, &dbg::o_para,
&dbg::o_deft, &dbg::o_dyn,
&dbg::o_lnot, &dbg::o_usub,
&dbg::o_bnot, &dbg::o_btor,
&dbg::o_btxor, &dbg::o_btand,
&dbg::o_add, &dbg::o_sub,
&dbg::o_mul, &dbg::o_div,
&dbg::o_lnk, &dbg::o_addc,
&dbg::o_subc, &dbg::o_mulc,
&dbg::o_divc, &dbg::o_lnkc,
&dbg::o_addeq, &dbg::o_subeq,
&dbg::o_muleq, &dbg::o_diveq,
&dbg::o_lnkeq, &dbg::o_bandeq,
&dbg::o_boreq, &dbg::o_bxoreq,
&dbg::o_addeqc, &dbg::o_subeqc,
&dbg::o_muleqc, &dbg::o_diveqc,
&dbg::o_lnkeqc, &dbg::o_addecp,
&dbg::o_subecp, &dbg::o_mulecp,
&dbg::o_divecp, &dbg::o_lnkecp,
&dbg::o_meq, &dbg::o_eq,
&dbg::o_neq, &dbg::o_less,
&dbg::o_leq, &dbg::o_grt,
&dbg::o_geq, &dbg::o_lessc,
&dbg::o_leqc, &dbg::o_grtc,
&dbg::o_geqc, &dbg::o_pop,
&dbg::o_jmp, &dbg::o_jt,
&dbg::o_jf, &dbg::o_cnt,
&dbg::o_findex, &dbg::o_feach,
&dbg::o_callg, &dbg::o_calll,
&dbg::o_upval, &dbg::o_callv,
&dbg::o_callvi, &dbg::o_callh,
&dbg::o_callfv, &dbg::o_callfh,
&dbg::o_callb, &dbg::o_slcbeg,
&dbg::o_slcend, &dbg::o_slc,
&dbg::o_slc2, &dbg::o_mcallg,
&dbg::o_mcalll, &dbg::o_mupval,
&dbg::o_mcallv, &dbg::o_mcallh,
&dbg::o_ret
};
private:
enum class dbg_cmd {
enum class cmd_kind {
cmd_error,
cmd_help,
cmd_backtrace,
@@ -105,69 +58,66 @@ private:
};
private:
const std::unordered_map<std::string, dbg_cmd> command_table = {
{"h", dbg_cmd::cmd_help},
{"help", dbg_cmd::cmd_help},
{"bt", dbg_cmd::cmd_backtrace},
{"backtrace", dbg_cmd::cmd_backtrace},
{"c", dbg_cmd::cmd_continue},
{"continue", dbg_cmd::cmd_continue},
{"f", dbg_cmd::cmd_list_file},
{"file", dbg_cmd::cmd_list_file},
{"g", dbg_cmd::cmd_global},
{"global", dbg_cmd::cmd_global},
{"l", dbg_cmd::cmd_local},
{"local", dbg_cmd::cmd_local},
{"u", dbg_cmd::cmd_upval},
{"upval", dbg_cmd::cmd_upval},
{"r", dbg_cmd::cmd_register},
{"register", dbg_cmd::cmd_register},
{"a", dbg_cmd::cmd_show_all},
{"all", dbg_cmd::cmd_show_all},
{"n", dbg_cmd::cmd_next},
{"next", dbg_cmd::cmd_next},
{"bk", dbg_cmd::cmd_break_point},
{"break", dbg_cmd::cmd_break_point},
{"q", dbg_cmd::cmd_exit},
{"exit", dbg_cmd::cmd_exit}
const std::unordered_map<std::string, cmd_kind> command_table = {
{"h", cmd_kind::cmd_help},
{"help", cmd_kind::cmd_help},
{"bt", cmd_kind::cmd_backtrace},
{"backtrace", cmd_kind::cmd_backtrace},
{"c", cmd_kind::cmd_continue},
{"continue", cmd_kind::cmd_continue},
{"f", cmd_kind::cmd_list_file},
{"file", cmd_kind::cmd_list_file},
{"g", cmd_kind::cmd_global},
{"global", cmd_kind::cmd_global},
{"l", cmd_kind::cmd_local},
{"local", cmd_kind::cmd_local},
{"u", cmd_kind::cmd_upval},
{"upval", cmd_kind::cmd_upval},
{"r", cmd_kind::cmd_register},
{"register", cmd_kind::cmd_register},
{"a", cmd_kind::cmd_show_all},
{"all", cmd_kind::cmd_show_all},
{"n", cmd_kind::cmd_next},
{"next", cmd_kind::cmd_next},
{"bk", cmd_kind::cmd_break_point},
{"break", cmd_kind::cmd_break_point},
{"q", cmd_kind::cmd_exit},
{"exit", cmd_kind::cmd_exit}
};
dbg_cmd get_cmd_type(const std::string& cmd) const {
cmd_kind get_cmd_type(const std::string& cmd) const {
return command_table.count(cmd)?
command_table.at(cmd):dbg_cmd::cmd_error;
command_table.at(cmd):cmd_kind::cmd_error;
}
private:
bool next;
usize fsize;
usize file_list_size;
u16 break_file_index;
u32 break_line;
u64 break_line;
error src;
private:
debug_prof_data data;
bool do_profiling;
operand_line_counter counter;
bool do_operand_count;
private:
std::vector<std::string> parse(const std::string&);
u16 file_index(const std::string&) const;
void err();
void help();
void err() const;
void help() const;
void list_file() const;
void step_info();
void interact();
public:
dbg():
next(true), fsize(0),
break_file_index(0), break_line(0),
do_profiling(false) {}
void run(
const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool
);
dbg(): next(true), file_list_size(0),
break_file_index(0), break_line(0),
do_operand_count(false) {}
void run(const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool);
};
}

View File

@@ -1,5 +1,5 @@
#include "nasal_err.h"
#include "repl.h"
#include "repl/repl.h"
namespace nasal {
@@ -71,7 +71,7 @@ std::ostream& reset(std::ostream& s) {
return s;
}
void flstream::load(const std::string& f) {
void filestream::load(const std::string& f) {
if (file==f) { // don't need to load a loaded file
return;
}
@@ -120,22 +120,23 @@ void error::warn(const std::string& stage, const std::string& info) {
std::clog << orange << stage << ": " << white << info << reset << "\n\n";
}
void error::err(
const std::string& stage, const span& loc, const std::string& info) {
void error::err(const std::string& stage,
const span& loc,
const std::string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
<< reset << "\n";
std::cerr << red << stage << ": " << white << info << reset << "\n";
std::cerr << cyan << " --> " << red;
loc.dump_begin(std::cerr);
std::cerr << reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen);
for(u32 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;
@@ -164,25 +165,25 @@ void error::err(
// output underline
std::cerr << cyan << iden << " | " << reset;
if (loc.begin_line==loc.end_line) {
for(u32 i = 0; i<loc.begin_column; ++i) {
for(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 i = loc.begin_column; i<loc.end_column; ++i) {
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(u32 i = 0; i<loc.begin_column; ++i) {
for(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 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(u32 i = 0; i<code.size(); ++i) {
for(u64 i = 0; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
} else {
for(u32 i = 0; i<loc.end_column; ++i) {
for(u64 i = 0; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
}

View File

@@ -11,11 +11,15 @@
namespace nasal {
struct span {
u32 begin_line;
u32 begin_column;
u32 end_line;
u32 end_column;
u64 begin_line;
u64 begin_column;
u64 end_line;
u64 end_column;
std::string file;
void dump_begin(std::ostream& out) const {
out << file << ":" << begin_line << ":" << begin_column + 1;
}
};
std::ostream& back_white(std::ostream&);
@@ -25,13 +29,13 @@ std::ostream& orange(std::ostream&);
std::ostream& white(std::ostream&);
std::ostream& reset(std::ostream&);
class flstream {
class filestream {
protected:
std::string file;
std::vector<std::string> res;
public:
flstream():file("") {}
filestream(): file("") {}
void load(const std::string&);
const std::string& operator[](usize n) const {return res[n];}
const auto& name() const {return file;}
@@ -39,23 +43,23 @@ public:
usize size() const {return res.size();}
};
class error:public flstream {
class error: public filestream {
private:
u32 cnt; // counter for errors
std::string identation(usize len) {
return std::string(len,' ');
return std::string(len, ' ');
}
std::string leftpad(u32 num, usize len) {
std::string leftpad(u64 num, usize len) {
auto tmp = std::to_string(num);
while(tmp.length()<len) {
tmp=" "+tmp;
tmp = " "+tmp;
}
return tmp;
}
public:
error():cnt(0) {}
error(): cnt(0) {}
void err(const std::string&, const std::string&);
void warn(const std::string&, const std::string&);
void err(const std::string&, const span&, const std::string&);
@@ -65,7 +69,7 @@ public:
std::exit(1);
}
}
u32 geterr() const {return cnt;}
auto geterr() const { return cnt; }
};
}

View File

@@ -1,4 +1,5 @@
#include "nasal_gc.h"
#include "nasal_gc.h"
#include "util/util.h"
namespace nasal {
@@ -22,7 +23,10 @@ void gc::do_mark_sweep() {
void gc::mark() {
std::vector<var> bfs;
mark_context_root(bfs);
if (memory.size()>8192 && bfs.size()>4) {
// concurrent mark, experimental
if (memory.size()>UINT16_MAX && bfs.size()>32) {
flag_concurrent_mark_triggered = true;
usize size = bfs.size();
std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4);
std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2);
@@ -34,11 +38,12 @@ void gc::mark() {
t3.join();
return;
}
// normal mark
while(!bfs.empty()) {
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
if (value.type<=vm_type::vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
@@ -50,7 +55,7 @@ void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
std::vector<var> bfs;
for(auto i = begin; i<end; ++i) {
var value = vec[i];
if (value.type<=vm_num ||
if (value.type<=vm_type::vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
@@ -59,7 +64,7 @@ void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
while(!bfs.empty()) {
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
if (value.type<=vm_type::vm_num ||
value.val.gcobj->mark!=nas_val::gc_status::uncollected) {
continue;
}
@@ -71,13 +76,13 @@ void gc::mark_context_root(std::vector<var>& bfs_queue) {
// scan global
for(usize i = 0; i<main_context_global_size; ++i) {
auto& val = main_context_global[i];
if (val.type>vm_num) {
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_num) {
if (i->type>vm_type::vm_num) {
bfs_queue.push_back(*i);
}
}
@@ -91,7 +96,7 @@ 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_num) {
if (i->type>vm_type::vm_num) {
bfs_queue.push_back(*i);
}
}
@@ -102,19 +107,20 @@ void gc::mark_context_root(std::vector<var>& bfs_queue) {
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
value.val.gcobj->mark = nas_val::gc_status::found;
switch(value.type) {
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
case vm_func: mark_func(bfs_queue, value.func()); break;
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
case vm_co: mark_co(bfs_queue, value.co()); break;
case vm_map: mark_map(bfs_queue, value.map()); break;
case vm_type::vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_type::vm_hash: mark_hash(bfs_queue, value.hash()); break;
case vm_type::vm_func: mark_func(bfs_queue, value.func()); break;
case vm_type::vm_upval: mark_upval(bfs_queue, value.upval()); break;
case vm_type::vm_ghost: mark_ghost(bfs_queue, value.ghost()); break;
case vm_type::vm_co: mark_co(bfs_queue, value.co()); break;
case vm_type::vm_map: mark_map(bfs_queue, value.map()); break;
default: break;
}
}
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i : vec.elems) {
if (i.type>vm_num) {
if (i.type>vm_type::vm_num) {
bfs_queue.push_back(i);
}
}
@@ -122,7 +128,7 @@ void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i : hash.elems) {
if (i.second.type>vm_num) {
if (i.second.type>vm_type::vm_num) {
bfs_queue.push_back(i.second);
}
}
@@ -130,7 +136,7 @@ void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
for(auto& i : function.local) {
if (i.type>vm_num) {
if (i.type>vm_type::vm_num) {
bfs_queue.push_back(i);
}
}
@@ -141,17 +147,24 @@ void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
for(auto& i : upval.elems) {
if (i.type>vm_num) {
if (i.type>vm_type::vm_num) {
bfs_queue.push_back(i);
}
}
}
void gc::mark_ghost(std::vector<var>& bfs_queue, nas_ghost& ghost) {
if (!ghost.gc_mark_function) {
return;
}
ghost.gc_mark_function(ghost.pointer, &bfs_queue);
}
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
bfs_queue.push_back(co.ctx.funcr);
bfs_queue.push_back(co.ctx.upvalr);
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
if (i->type>vm_num) {
if (i->type>vm_type::vm_num) {
bfs_queue.push_back(*i);
}
}
@@ -159,7 +172,7 @@ void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
void gc::mark_map(std::vector<var>& bfs_queue, nas_map& mp) {
for(const auto& i : mp.mapper) {
if (i.second->type>vm_num) {
if (i.second->type>vm_type::vm_num) {
bfs_queue.push_back(*i.second);
}
}
@@ -169,7 +182,7 @@ void gc::sweep() {
for(auto i : memory) {
if (i->mark==nas_val::gc_status::uncollected) {
i->clear();
unused[i->type-vm_str].push_back(i);
unused[static_cast<u8>(i->type)-static_cast<u8>(vm_type::vm_str)].push_back(i);
i->mark = nas_val::gc_status::collected;
} else if (i->mark==nas_val::gc_status::found) {
i->mark = nas_val::gc_status::uncollected;
@@ -177,11 +190,11 @@ void gc::sweep() {
}
}
void gc::extend(u8 type) {
const u8 index = type-vm_str;
void gc::extend(const vm_type type) {
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str);
size[index] += incr[index];
for(u32 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);
@@ -190,13 +203,12 @@ void gc::extend(u8 type) {
unused[index].push_back(tmp);
}
// if incr[index] = 1, this will always be 1
incr[index] = incr[index]+incr[index]/2;
}
void gc::init(
const std::vector<std::string>& constant_strings,
const std::vector<std::string>& argv
) {
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) {
@@ -208,25 +220,25 @@ void gc::init(
// init constant strings
strs.resize(constant_strings.size());
for(u32 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].type==vm_str && strs[i].str()==constant_strings[i]) {
if (strs[i].is_str() && strs[i].str()==constant_strings[i]) {
continue;
}
strs[i] = var::gcobj(new nas_val(vm_str));
strs[i].val.gcobj->unmutable = 1;
strs[i] = var::gcobj(new nas_val(vm_type::vm_str));
strs[i].val.gcobj->immutable = 1;
strs[i].str() = constant_strings[i];
}
// record arguments
env_argv.resize(argv.size());
for(usize 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].type==vm_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_str));
env_argv[i].val.gcobj->unmutable = 1;
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];
}
}
@@ -247,15 +259,27 @@ void gc::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
"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",
@@ -263,7 +287,7 @@ void gc::info() const {
"hashmap",
"function",
"upvalue",
"object",
"ghost",
"coroutine",
"namespace",
nullptr
@@ -286,58 +310,92 @@ void gc::info() const {
len = std::to_string(size[i]).length();
indent = indent<len? len:indent;
}
auto indent_string = std::string("--");
auto indent_string = std::string("──");
for(usize i = 0; i<indent; ++i) {
indent_string += "-";
indent_string += "";
}
auto last_line = indent_string + "+" +
indent_string + "-" + indent_string + "-" + indent_string;
indent_string = indent_string + "+" +
indent_string + "+" + indent_string + "+" + 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" << indent_string << "\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "object type";
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
std::clog << "\n" << indent_string << "\n";
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 += 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";
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 << indent_string << "\n";
std::clog << mid_line << "\n";
auto den = std::chrono::high_resolution_clock::duration::period::den;
std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << "\n" << indent_string << "\n";
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 << " " << left << setw(indent) << setfill(' ') << "gc time";
std::clog << " | " << worktime*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "avg time";
std::clog << " | " << worktime*1.0/den*1000/total << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max gc";
std::clog << " | " << max_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max mark";
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
std::clog << last_line << "\n";
wm.restore_code_page();
}
var gc::alloc(u8 type) {
const u8 index = type-vm_str;
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];
@@ -367,7 +425,7 @@ void gc::context_change(nas_co* co) {
}
void gc::context_reserve() {
// pc=0 means this coroutine is finished
// pc = 0 means this coroutine is finished
cort->status = running_context->pc?
nas_co::status::suspended:
nas_co::status::dead;

View File

@@ -13,6 +13,7 @@
#include <thread>
#include <cstring>
#include <sstream>
#include <iostream>
#include "nasal.h"
#include "nasal_type.h"
@@ -41,11 +42,11 @@ struct gc {
std::vector<nas_val*> unused[gc_type_size]; // gc free list
/* heap increase size */
u32 incr[gc_type_size] = {
u64 incr[gc_type_size] = {
128, // vm_str
128, // vm_vec
64, // vm_hash
128, // vm_func
256, // vm_func
256, // vm_upval
16, // vm_obj
16, // vm_co
@@ -60,6 +61,7 @@ struct gc {
i64 max_time = 0;
i64 max_mark_time = 0;
i64 max_sweep_time = 0;
bool flag_concurrent_mark_triggered = false;
void set(context* _ctx, var* _global, usize _size) {
running_context = _ctx;
@@ -78,34 +80,35 @@ private:
void mark_hash(std::vector<var>&, nas_hash&);
void mark_func(std::vector<var>&, nas_func&);
void mark_upval(std::vector<var>&, nas_upval&);
void mark_ghost(std::vector<var>&, nas_ghost&);
void mark_co(std::vector<var>&, nas_co&);
void mark_map(std::vector<var>&, nas_map&);
void sweep();
public:
void extend(u8);
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 u8);
var alloc(const vm_type);
void context_change(nas_co*);
void context_reserve();
public:
var newstr(char c) {
var s = alloc(vm_str);
var s = alloc(vm_type::vm_str);
s.str() = c;
return s;
}
var newstr(const char* buff) {
var s = alloc(vm_str);
var s = alloc(vm_type::vm_str);
s.str() = std::string(buff);
return s;
}
var newstr(const std::string& buff) {
var s = alloc(vm_str);
var s = alloc(vm_type::vm_str);
s.str() = buff;
return s;
}
@@ -114,7 +117,7 @@ public:
// module function type
typedef var (*module_func)(var*, usize, gc*);
// module function stores in tables with this type, end with {nullptr,nullptr}
// module function stores in tables with this type, end with {nullptr, nullptr}
struct module_func_info {
const char* name;
module_func fd;
@@ -123,4 +126,13 @@ 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
}

View File

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

View File

@@ -1,59 +1,56 @@
#pragma once
#ifndef _MSC_VER
#include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#endif
#ifdef _MSC_VER
#define F_OK 0
#endif
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
#include "symbol_finder.h"
#include <cstring>
#include <sstream>
#include <vector>
#include <unordered_set>
namespace nasal {
class linker {
private:
bool show_path_flag;
bool library_loaded;
std::string this_file;
error err;
std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack;
std::vector<std::string> envpath;
private:
bool import_check(expr*);
bool check_exist_or_record_file(const std::string&);
bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&);
void link(code_block*, code_block*);
std::string get_path(expr*);
std::string find_real_file_path(const std::string&, const span&);
code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
code_block* import_nasal_lib();
std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*);
definition_expr* generate_module_definition(code_block*);
code_block* load(code_block*, const std::string&);
public:
linker();
const error& link(parse&, const std::string&, bool);
const auto& get_file_list() const {return imported_files;}
};
}
#pragma once
#ifndef _MSC_VER
#include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#endif
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
#include "symbol_finder.h"
#include "util/fs.h"
#include <cstring>
#include <sstream>
#include <vector>
#include <unordered_set>
namespace nasal {
class linker {
private:
const u32 MAX_RECURSION_DEPTH = 256;
bool show_path_flag;
std::string this_file;
error err;
std::vector<std::string> imported_files;
std::vector<std::string> module_load_stack;
std::vector<fs::path> envpath;
private:
bool import_check(expr*);
bool check_exist_or_record_file(const std::string&);
bool check_self_import(const std::string&);
std::string generate_self_import_path(const std::string&);
void merge_tree(code_block*, code_block*);
std::string get_path(expr*);
std::string find_real_file_path(const std::string&, const span&);
code_block* import_regular_file(expr*, std::unordered_set<std::string>&);
code_block* import_nasal_lib();
std::string generate_module_name(const std::string&);
return_expr* generate_module_return(code_block*);
definition_expr* generate_module_definition(code_block*);
void load(code_block*, const std::string&);
public:
linker();
const error& link(parse&, bool);
const auto& get_file_list() const {return imported_files;}
};
}

View File

@@ -1,393 +1,458 @@
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include "nasal_lexer.h"
#include "repl.h"
namespace nasal {
bool lexer::skip(char c) {
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
}
bool lexer::is_id(char c) {
return (c=='_') || std::isalpha(c) || (c<0);
}
bool lexer::is_hex(char c) {
return std::isxdigit(c);
}
bool lexer::is_oct(char c) {
return '0'<=c && c<='7';
}
bool lexer::is_dec(char c) {
return std::isdigit(c);
}
bool lexer::is_str(char c) {
return c=='\'' || c=='\"' || c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='(' || c==')' || c=='[' || c==']' ||
c=='{' || c=='}' || c==',' || c==';' ||
c==':' || c=='?' || c=='`' || c=='@' ||
c=='%' || c=='$' || c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='=' || c=='+' || c=='-' || c=='*' ||
c=='!' || c=='/' || c=='<' || c=='>' ||
c=='~' || c=='|' || c=='&' || c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to '\n'
// so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c = res[ptr++];
err.err("lexer",
{line, column-1, line, column, filename},
"invalid character 0x"+chrhex(c)
);
++invalid_char;
}
void lexer::open(const std::string& file) {
if (repl::info::instance()->in_repl_mode &&
repl::info::instance()->repl_file_name==file) {
err.load(file);
filename = file;
res = repl::info::instance()->repl_file_source;
return;
}
// check file exsits and it is a regular file
struct stat buffer;
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename = file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <" + file + ">");
res = "";
return;
}
err.load(file);
std::stringstream ss;
ss << in.rdbuf();
res = ss.str();
}
tok lexer::get_type(const std::string& str) {
return typetbl.count(str)? typetbl.at(str):tok::null;
}
std::string lexer::utf8_gen() {
std::string str = "";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp = "";
u32 nbytes = utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp += res[ptr++];
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp += res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
std::string utf_info = "0x"+chrhex(tmp[0]);
for(u32 i = 1; i<tmp.size(); ++i) {
utf_info += " 0x"+chrhex(tmp[i]);
}
err.err("lexer",
{line, column-1, line, column, filename},
"invalid utf-8 <"+utf_info+">"
);
++invalid_char;
}
str += tmp;
// may have some problems because not all the unicode takes 2 space
column += 2;
}
return str;
}
token lexer::id_gen() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str += utf8_gen();
} else { // ascii
str += res[ptr++];
++column;
}
}
tok type = get_type(str);
return {
{begin_line, begin_column, line, column, filename},
(type!=tok::null)? type:tok::id, str
};
}
token lexer::num_gen() {
u32 begin_line = line;
u32 begin_column = column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str = "0x";
ptr += 2;
while(ptr<res.size() && is_hex(res[ptr])) {
str += res[ptr++];
}
column += str.length();
// "0x"
if (str.length()<3) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
std::string str = "0o";
ptr += 2;
while(ptr<res.size() && is_oct(res[ptr])) {
str += res[ptr++];
}
bool erfmt = false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
erfmt = true;
str += res[ptr++];
}
column += str.length();
if (str.length()==2 || erfmt) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string str = "";
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
if (ptr<res.size() && res[ptr]=='.') {
str += res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
// "xxxx." is not a correct number
if (str.back()=='.') {
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
str += res[ptr++];
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
str += res[ptr++];
}
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
// "xxxe(-|+)" is not a correct number
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
column += str.length();
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
token lexer::str_gen() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
const char begin = res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin) {
++column;
if (res[ptr]=='\n') {
column = 0;
++line;
}
if (res[ptr]=='\\' && ptr+1<res.size()) {
++column;
++ptr;
switch(res[ptr]) {
case '0': str += '\0'; break;
case 'a': str += '\a'; break;
case 'b': str += '\b'; break;
case 'e': str += '\033'; break;
case 't': str += '\t'; break;
case 'n': str += '\n'; break;
case 'v': str += '\v'; break;
case 'f': str += '\f'; break;
case 'r': str += '\r'; break;
case '?': str += '\?'; break;
case '\\':str += '\\'; break;
case '\'':str += '\''; break;
case '\"':str += '\"'; break;
default: str += res[ptr];break;
}
if (res[ptr]=='\n') {
column = 0;
++line;
}
continue;
}
str += res[ptr];
}
// check if this string ends with a " or '
if (ptr++>=res.size()) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"get EOF when generating string"
);
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
++column;
// if is not utf8, 1+utf8_hdchk should be 1
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"\'`\' is used for string including one character"
);
}
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
token lexer::single_opr() {
u32 begin_line = line;
u32 begin_column = column;
std::string str(1, res[ptr]);
++column;
tok type = get_type(str);
if (type==tok::null) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid operator `"+str+"`"
);
}
++ptr;
return {{begin_line, begin_column, line, column, filename}, type, str};
}
token lexer::dots() {
u32 begin_line = line;
u32 begin_column = column;
std::string str = ".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str += "..";
}
ptr += str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
token lexer::calc_opr() {
u32 begin_line = line;
u32 begin_column = column;
// get calculation operator
std::string str(1, res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') {
str += res[ptr++];
}
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
const error& lexer::scan(const std::string& file) {
line = 1;
column = 0;
ptr = 0;
toks = {};
open(file);
while(ptr<res.size()) {
while(ptr<res.size() && skip(res[ptr])) {
// these characters will be ignored, and '\n' will cause ++line
++column;
if (res[ptr++]=='\n') {
++line;
column = 0;
}
}
if (ptr>=res.size()) {
break;
}
if (is_id(res[ptr])) {
toks.push_back(id_gen());
} else if (is_dec(res[ptr])) {
toks.push_back(num_gen());
} else if (is_str(res[ptr])) {
toks.push_back(str_gen());
} else if (is_single_opr(res[ptr])) {
toks.push_back(single_opr());
} else if (res[ptr]=='.') {
toks.push_back(dots());
} else if (is_calc_opr(res[ptr])) {
toks.push_back(calc_opr());
} else if (res[ptr]=='#') {
skip_note();
} else {
err_char();
}
if (invalid_char>10) {
err.err("lexer", "too many invalid characters, stop");
break;
}
}
if (toks.size()) {
// eof token's location is the last token's location
toks.push_back({toks.back().loc, tok::eof, "<eof>"});
} else {
// if token sequence is empty, generate a default location
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
}
res = "";
return err;
}
}
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include "nasal_lexer.h"
#include "repl/repl.h"
#include "util/util.h"
#include "util/fs.h"
namespace nasal {
bool lexer::skip(char c) {
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
}
bool lexer::is_id(char c) {
return (c=='_') || std::isalpha(c) || (c<0);
}
bool lexer::is_hex(char c) {
return std::isxdigit(c);
}
bool lexer::is_oct(char c) {
return '0'<=c && c<='7';
}
bool lexer::is_dec(char c) {
return std::isdigit(c);
}
bool lexer::is_str(char c) {
return c=='\'' || c=='\"' || c=='`';
}
bool lexer::is_quesmark(char c) {
return c=='?';
}
bool lexer::is_single_opr(char c) {
return (
c=='(' || c==')' || c=='[' || c==']' ||
c=='{' || c=='}' || c==',' || c==';' ||
c==':' || c=='`' || c=='@' || c=='%' ||
c=='$' || c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='=' || c=='+' || c=='-' || c=='*' ||
c=='!' || c=='/' || c=='<' || c=='>' ||
c=='~' || c=='|' || c=='&' || c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to '\n'
// so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c = res[ptr++];
err.err("lexer",
{line, column-1, line, column, filename},
"invalid character 0x" + util::char_to_hex(c)
);
++invalid_char;
}
void lexer::open(const std::string& file) {
if (repl::info::instance()->in_repl_mode &&
repl::info::instance()->repl_file_name==file) {
err.load(file);
filename = file;
res = repl::info::instance()->repl_file_source;
return;
}
if (file.empty()) {
err.err("lexer", "empty input file");
err.chkerr();
}
// check file exsits and it is a regular file
if (!fs::is_regular(file)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename = file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <" + file + ">");
res = "";
return;
}
err.load(file);
std::stringstream ss;
ss << in.rdbuf();
res = ss.str();
}
tok lexer::get_type(const std::string& str) {
// search token type from mapper
// if cannot find, just return null
return token_mapper.count(str)? token_mapper.at(str):tok::tk_null;
}
std::string lexer::utf8_gen() {
std::string str = "";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp = "";
u32 nbytes = util::utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp += res[ptr++];
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp += res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
std::string utf_info = "0x" + util::char_to_hex(tmp[0]);
for(u32 i = 1; i<tmp.size(); ++i) {
utf_info += " 0x" + util::char_to_hex(tmp[i]);
}
err.err("lexer",
{line, column-1, line, column, filename},
"invalid utf-8 <"+utf_info+">"
);
++invalid_char;
}
str += tmp;
// may have some problems because not all the unicode takes 2 space
column += 2;
}
return str;
}
token lexer::id_gen() {
u64 begin_line = line;
u64 begin_column = column;
std::string str = "";
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str += utf8_gen();
} else { // ascii
str += res[ptr++];
++column;
}
}
tok type = get_type(str);
return {
{begin_line, begin_column, line, column, filename},
(type!=tok::tk_null)? type:tok::tk_id,
str
};
}
token lexer::num_gen() {
u64 begin_line = line;
u64 begin_column = column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str = "0x";
ptr += 2;
while(ptr<res.size() && is_hex(res[ptr])) {
str += res[ptr++];
}
column += str.length();
// "0x"
if (str.length()<3) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
str
};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
std::string str = "0o";
ptr += 2;
while(ptr<res.size() && is_oct(res[ptr])) {
str += res[ptr++];
}
bool erfmt = false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
erfmt = true;
str += res[ptr++];
}
column += str.length();
if (str.length()==2 || erfmt) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
}
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
str
};
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string str = "";
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
if (ptr<res.size() && res[ptr]=='.') {
str += res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
// "xxxx." is not a correct number
if (str.back()=='.') {
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
"0"
};
}
}
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
str += res[ptr++];
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
str += res[ptr++];
}
while(ptr<res.size() && is_dec(res[ptr])) {
str += res[ptr++];
}
// "xxxe(-|+)" is not a correct number
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`"
);
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
"0"
};
}
}
column += str.length();
return {
{begin_line, begin_column, line, column, filename},
tok::tk_num,
str
};
}
token lexer::str_gen() {
u64 begin_line = line;
u64 begin_column = column;
std::string str = "";
const char begin = res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin) {
++column;
if (res[ptr]=='\n') {
column = 0;
++line;
}
if (res[ptr]=='\\' && ptr+1<res.size()) {
++column;
++ptr;
switch(res[ptr]) {
case '0': str += '\0'; break;
case 'a': str += '\a'; break;
case 'b': str += '\b'; break;
case 'e': str += '\033'; break;
case 't': str += '\t'; break;
case 'n': str += '\n'; break;
case 'v': str += '\v'; break;
case 'f': str += '\f'; break;
case 'r': str += '\r'; break;
case '?': str += '\?'; break;
case '\\':str += '\\'; break;
case '\'':str += '\''; break;
case '\"':str += '\"'; break;
default: str += res[ptr];break;
}
if (res[ptr]=='\n') {
column = 0;
++line;
}
continue;
}
str += res[ptr];
}
// check if this string ends with a " or '
if (ptr++>=res.size()) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"get EOF when generating string"
);
return {
{begin_line, begin_column, line, column, filename},
tok::tk_str,
str
};
}
++column;
// if is not utf8, 1+utf8_hdchk should be 1
if (begin=='`' && str.length()!=1+util::utf8_hdchk(str[0])) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"\'`\' is used for string including one character"
);
}
return {
{begin_line, begin_column, line, column, filename},
tok::tk_str,
str
};
}
token lexer::quesmark_gen() {
u64 begin_line = line;
u64 begin_column = column;
std::string str(1, res[ptr]);
++column;
++ptr;
if (ptr < res.size() && (res[ptr]=='?' || res[ptr]=='.')) {
str += res[ptr];
++column;
++ptr;
}
return {
{begin_line, begin_column, line, column, filename},
get_type(str),
str
};
}
token lexer::single_opr() {
u64 begin_line = line;
u64 begin_column = column;
std::string str(1, res[ptr]);
++column;
tok type = get_type(str);
if (type==tok::tk_null) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid operator `"+str+"`"
);
}
++ptr;
return {{begin_line, begin_column, line, column, filename}, type, str};
}
token lexer::dots() {
u64 begin_line = line;
u64 begin_column = column;
std::string str = ".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str += "..";
}
ptr += str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
token lexer::calc_opr() {
u64 begin_line = line;
u64 begin_column = column;
// get calculation operator
std::string str(1, res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') {
str += res[ptr++];
}
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
const error& lexer::scan(const std::string& file) {
line = 1;
column = 0;
ptr = 0;
toks = {};
open(file);
while(ptr<res.size()) {
while(ptr<res.size() && skip(res[ptr])) {
// these characters will be ignored, and '\n' will cause ++line
++column;
if (res[ptr++]=='\n') {
++line;
column = 0;
}
}
if (ptr>=res.size()) {
break;
}
if (is_id(res[ptr])) {
toks.push_back(id_gen());
} else if (is_dec(res[ptr])) {
toks.push_back(num_gen());
} else if (is_str(res[ptr])) {
toks.push_back(str_gen());
} else if (is_quesmark(res[ptr])) {
toks.push_back(quesmark_gen());
} else if (is_single_opr(res[ptr])) {
toks.push_back(single_opr());
} else if (res[ptr]=='.') {
toks.push_back(dots());
} else if (is_calc_opr(res[ptr])) {
toks.push_back(calc_opr());
} else if (res[ptr]=='#') {
skip_note();
} else {
err_char();
}
if (invalid_char>10) {
err.err("lexer", "too many invalid characters, stop");
break;
}
}
if (toks.size()) {
// eof token's location is the last token's location
toks.push_back({toks.back().loc, tok::tk_eof, "<eof>"});
} else {
// if token sequence is empty, generate a default location
toks.push_back({
{line, column, line, column, filename},
tok::tk_eof,
"<eof>"
});
}
res = "";
return err;
}
}

View File

@@ -10,156 +10,159 @@
#include <sstream>
#include <vector>
#include <unordered_map>
#include <sys/stat.h>
#include "nasal.h"
#include "nasal_err.h"
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
namespace nasal {
enum class tok:u32 {
null=0, // null token (default token type)
num, // number literal
str, // string literal
id, // identifier
tktrue, // keyword true
tkfalse, // keyword false
use, // keyword use
rfor, // loop keyword for
forindex, // loop keyword forindex
foreach, // loop keyword foreach
rwhile, // loop keyword while
var, // keyword for definition
func, // keyword for definition of function
brk, // loop keyword break
cont, // loop keyword continue
ret, // function keyword return
rif, // condition expression keyword if
elsif, // condition expression keyword elsif
relse, // condition expression keyword else
tknil, // nil literal
lcurve, // (
rcurve, // )
lbracket, // [
rbracket, // ]
lbrace, // {
rbrace, // }
semi, // ;
opand, // operator and
opor, // operator or
comma, // ,
dot, // .
ellipsis, // ...
quesmark, // ?
colon, // :
add, // operator +
sub, // operator -
mult, // operator *
div, // operator /
floater, // operator ~ and binary operator ~
btand, // bitwise operator &
btor, // bitwise operator |
btxor, // bitwise operator ^
opnot, // operator !
eq, // operator =
addeq, // operator +=
subeq, // operator -=
multeq, // operator *=
diveq, // operator /=
lnkeq, // operator ~=
btandeq, // operator &=
btoreq, // operator |=
btxoreq, // operator ^=
cmpeq, // operator ==
neq, // operator !=
less, // operator <
leq, // operator <=
grt, // operator >
geq, // operator >=
eof // <eof> end of token list
enum class tok {
tk_null = 0, // null token (default token type)
tk_num, // number literal
tk_str, // string literal
tk_id, // identifier
tk_true, // keyword true
tk_false, // keyword false
tk_use, // keyword use
tk_for, // loop keyword for
tk_forindex, // loop keyword forindex
tk_foreach, // loop keyword foreach
tk_while, // loop keyword while
tk_var, // keyword for definition
tk_func, // keyword for definition of function
tk_brk, // loop keyword break
tk_cont, // loop keyword continue
tk_ret, // function keyword return
tk_if, // condition expression keyword if
tk_elsif, // condition expression keyword elsif
tk_else, // condition expression keyword else
tk_nil, // nil literal
tk_lcurve, // (
tk_rcurve, // )
tk_lbracket, // [
tk_rbracket, // ]
tk_lbrace, // {
tk_rbrace, // }
tk_semi, // ;
tk_and, // operator and
tk_or, // operator or
tk_comma, // ,
tk_dot, // .
tk_ellipsis, // ...
tk_quesmark, // ?
tk_quesques, // ??
tk_quesdot, // ?.
tk_colon, // :
tk_add, // operator +
tk_sub, // operator -
tk_mult, // operator *
tk_div, // operator /
tk_floater, // operator ~ and binary operator ~
tk_btand, // bitwise operator &
tk_btor, // bitwise operator |
tk_btxor, // bitwise operator ^
tk_not, // operator !
tk_eq, // operator =
tk_addeq, // operator +=
tk_subeq, // operator -=
tk_multeq, // operator *=
tk_diveq, // operator /=
tk_lnkeq, // operator ~=
tk_btandeq, // operator &=
tk_btoreq, // operator |=
tk_btxoreq, // operator ^=
tk_cmpeq, // operator ==
tk_neq, // operator !=
tk_less, // operator <
tk_leq, // operator <=
tk_grt, // operator >
tk_geq, // operator >=
tk_eof // <eof> end of token list
};
struct token {
span loc; // location
tok type; // token type
span loc; // location
tok type; // token type
std::string str; // content
token() = default;
token(const token&) = default;
};
class lexer {
private:
u32 line;
u32 column;
u64 line;
u64 column;
usize ptr;
std::string filename;
std::string res;
private:
error err;
u64 invalid_char;
std::vector<token> toks;
const std::unordered_map<std::string, tok> typetbl {
{"use" ,tok::use },
{"true" ,tok::tktrue },
{"false" ,tok::tkfalse },
{"for" ,tok::rfor },
{"forindex",tok::forindex},
{"foreach" ,tok::foreach },
{"while" ,tok::rwhile },
{"var" ,tok::var },
{"func" ,tok::func },
{"break" ,tok::brk },
{"continue",tok::cont },
{"return" ,tok::ret },
{"if" ,tok::rif },
{"elsif" ,tok::elsif },
{"else" ,tok::relse },
{"nil" ,tok::tknil },
{"(" ,tok::lcurve },
{")" ,tok::rcurve },
{"[" ,tok::lbracket},
{"]" ,tok::rbracket},
{"{" ,tok::lbrace },
{"}" ,tok::rbrace },
{";" ,tok::semi },
{"and" ,tok::opand },
{"or" ,tok::opor },
{"," ,tok::comma },
{"." ,tok::dot },
{"..." ,tok::ellipsis},
{"?" ,tok::quesmark},
{":" ,tok::colon },
{"+" ,tok::add },
{"-" ,tok::sub },
{"*" ,tok::mult },
{"/" ,tok::div },
{"~" ,tok::floater },
{"&" ,tok::btand },
{"|" ,tok::btor },
{"^" ,tok::btxor },
{"!" ,tok::opnot },
{"=" ,tok::eq },
{"+=" ,tok::addeq },
{"-=" ,tok::subeq },
{"*=" ,tok::multeq },
{"/=" ,tok::diveq },
{"~=" ,tok::lnkeq },
{"&=" ,tok::btandeq },
{"|=" ,tok::btoreq },
{"^=" ,tok::btxoreq },
{"==" ,tok::cmpeq },
{"!=" ,tok::neq },
{"<" ,tok::less },
{"<=" ,tok::leq },
{">" ,tok::grt },
{">=" ,tok::geq }
private:
const std::unordered_map<std::string, tok> token_mapper = {
{"use" , tok::tk_use },
{"true" , tok::tk_true },
{"false" , tok::tk_false },
{"for" , tok::tk_for },
{"forindex", tok::tk_forindex},
{"foreach" , tok::tk_foreach },
{"while" , tok::tk_while },
{"var" , tok::tk_var },
{"func" , tok::tk_func },
{"break" , tok::tk_brk },
{"continue", tok::tk_cont },
{"return" , tok::tk_ret },
{"if" , tok::tk_if },
{"elsif" , tok::tk_elsif },
{"else" , tok::tk_else },
{"nil" , tok::tk_nil },
{"(" , tok::tk_lcurve },
{")" , tok::tk_rcurve },
{"[" , tok::tk_lbracket},
{"]" , tok::tk_rbracket},
{"{" , tok::tk_lbrace },
{"}" , tok::tk_rbrace },
{";" , tok::tk_semi },
{"and" , tok::tk_and },
{"or" , tok::tk_or },
{"," , tok::tk_comma },
{"." , tok::tk_dot },
{"..." , tok::tk_ellipsis},
{"?" , tok::tk_quesmark},
{"??" , tok::tk_quesques},
{"?." , tok::tk_quesdot },
{":" , tok::tk_colon },
{"+" , tok::tk_add },
{"-" , tok::tk_sub },
{"*" , tok::tk_mult },
{"/" , tok::tk_div },
{"~" , tok::tk_floater },
{"&" , tok::tk_btand },
{"|" , tok::tk_btor },
{"^" , tok::tk_btxor },
{"!" , tok::tk_not },
{"=" , tok::tk_eq },
{"+=" , tok::tk_addeq },
{"-=" , tok::tk_subeq },
{"*=" , tok::tk_multeq },
{"/=" , tok::tk_diveq },
{"~=" , tok::tk_lnkeq },
{"&=" , tok::tk_btandeq },
{"|=" , tok::tk_btoreq },
{"^=" , tok::tk_btxoreq },
{"==" , tok::tk_cmpeq },
{"!=" , tok::tk_neq },
{"<" , tok::tk_less },
{"<=" , tok::tk_leq },
{">" , tok::tk_grt },
{">=" , tok::tk_geq }
};
private:
tok get_type(const std::string&);
bool skip(char);
bool is_id(char);
@@ -167,6 +170,7 @@ private:
bool is_oct(char);
bool is_dec(char);
bool is_str(char);
bool is_quesmark(char);
bool is_single_opr(char);
bool is_calc_opr(char);
@@ -178,13 +182,17 @@ private:
token id_gen();
token num_gen();
token str_gen();
token quesmark_gen();
token single_opr();
token dots();
token calc_opr();
public:
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {}
lexer(): line(1), column(0), ptr(0),
filename(""), res(""),
invalid_char(0) {}
const error& scan(const std::string&);
const std::vector<token>& result() const {return toks;}
const auto& result() const {return toks;}
};
}

View File

@@ -1,37 +1,12 @@
#include "nasal_opcode.h"
#include "util/util.h"
namespace nasal {
const char* opname[] = {
"exit ", "repl ", "intl ", "loadg ",
"loadl ", "loadu ", "pnum ", "pnil ",
"pstr ", "newv ", "newh ", "newf ",
"happ ", "para ", "def ", "dyn ",
"lnot ", "usub ", "bitnot", "bitor ",
"bitxor", "bitand", "add ", "sub ",
"mult ", "div ", "lnk ", "addc ",
"subc ", "multc ", "divc ", "lnkc ",
"addeq ", "subeq ", "muleq ", "diveq ",
"lnkeq ", "bandeq", "boreq ", "bxoreq",
"addeqc", "subeqc", "muleqc", "diveqc",
"lnkeqc", "addecp", "subecp", "mulecp",
"divecp", "lnkecp", "meq ", "eq ",
"neq ", "less ", "leq ", "grt ",
"geq ", "lessc ", "leqc ", "grtc ",
"geqc ", "pop ", "jmp ", "jt ",
"jf ", "cnt ", "findx ", "feach ",
"callg ", "calll ", "upval ", "callv ",
"callvi", "callh ", "callfv", "callfh",
"callb ", "slcbeg", "slcend", "slice ",
"slice2", "mcallg", "mcalll", "mupval",
"mcallv", "mcallh", "ret "
};
void codestream::set(
const f64* number_list,
const std::string* string_list,
const nasal_builtin_table* native_table,
const std::string* file_list) {
void codestream::set(const f64* number_list,
const std::string* string_list,
const nasal_builtin_table* native_table,
const std::string* file_list) {
const_number = number_list;
const_string = string_list;
natives = native_table;
@@ -43,75 +18,117 @@ void codestream::dump(std::ostream& out) const {
using std::setfill;
using std::hex;
using std::dec;
auto op = code.op;
auto num = code.num;
const auto op = code.op;
const auto num = code.num;
// dump operand index and bytecode(hex format)
out << hex << "0x"
<< setw(6) << setfill('0') << index << " "
<< setw(2) << setfill('0') << static_cast<u32>(op) << " "
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " "
<< setw(2) << setfill('0') << (num&0xff) << " "
<<opname[op]<<" "<<dec;
<< setw(8) << setfill('0') << index << " "
<< setw(2) << setfill('0') << static_cast<u32>(op) << ":" << dec;
// dump immediate number(hex format)
for(i32 i = 64-8; i>=0; i -= 8) {
out << hex << setw(2) << setfill('0') << ((num>>i)&0xff) << dec << " ";
}
// dump operand name
out << " " << operand_name_table.at(static_cast<op_code_type>(op)) << " ";
switch(op) {
case op_addeq: case op_subeq:
case op_muleq: case op_diveq:
case op_lnkeq: case op_meq:
case op_btandeq: case op_btoreq:
case op_addeq:
case op_subeq:
case op_muleq:
case op_diveq:
case op_lnkeq:
case op_meq:
case op_btandeq:
case op_btoreq:
case op_btxoreq:
out << hex << "0x" << num << dec << " sp-" << num; break;
case op_addeqc: case op_subeqc:
case op_muleqc:case op_diveqc:
out << hex << "0x" << num << dec
<< " (" << const_number[num] << ")"; break;
out << hex << "0x" << num << dec << " sp-" << num;
break;
case op_addeqc:
case op_subeqc:
case op_muleqc:
case op_diveqc:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ")";
break;
case op_lnkeqc:
out << hex << "0x" << num << dec
<< " (" << rawstr(const_string[num], 16) << ")"; break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out << hex << "0x" << num << dec
<< " (" << const_number[num] << ") sp-1"; break;
out << hex << "0x" << num << dec;
out << " (" << util::rawstr(const_string[num], 16) << ")";
break;
case op_addecp:
case op_subecp:
case op_mulecp:
case op_divecp:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ") sp-1";
break;
case op_lnkecp:
out << hex << "0x" << num << dec
<< " (" << rawstr(const_string[num], 16) << ") sp-1"; break;
case op_addc: case op_subc:
case op_mulc: case op_divc:
case op_lessc: case op_leqc:
case op_grtc: case op_geqc:
out << hex << "0x" << num << dec;
out << " (" << util::rawstr(const_string[num], 16) << ") sp-1";
break;
case op_addc:
case op_subc:
case op_mulc:
case op_divc:
case op_lessc:
case op_leqc:
case op_grtc:
case op_geqc:
case op_pnum:
out << hex << "0x" << num << dec
<< " (" << const_number[num] << ")"; break;
case op_callvi: case op_newv:
case op_callfv: case op_repl:
case op_intl: case op_findex:
case op_feach: case op_newf:
case op_jmp: case op_jt:
case op_jf: case op_callg:
case op_mcallg: case op_loadg:
case op_calll: case op_mcalll:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ")";
break;
case op_callvi:
case op_newv:
case op_callfv:
case op_repl:
case op_intl:
case op_findex:
case op_feach:
case op_newf:
case op_jmp:
case op_jt:
case op_jf:
case op_callg:
case op_mcallg:
case op_loadg:
case op_calll:
case op_mcalll:
case op_loadl:
out << hex << "0x" << num << dec; break;
case op_callb:
out << hex << "0x" << num << " <" << natives[num].name
<< "@0x" << reinterpret_cast<u64>(natives[num].func)
<< dec << ">"; break;
case op_upval: case op_mupval:
case op_upval:
case op_mupval:
case op_loadu:
out << hex << "0x" << ((num>>16)&0xffff)
<< "[0x" << (num&0xffff) << "]" << dec; break;
case op_happ: case op_pstr:
case op_lnkc: case op_callh:
case op_mcallh: case op_para:
case op_deft: case op_dyn:
out << hex << "0x" << num << dec
<< " (" << rawstr(const_string[num], 16) << ")"; break;
case op_happ:
case op_pstr:
case op_lnkc:
case op_callh:
case op_mcallh:
case op_para:
case op_deft:
case op_dyn:
out << hex << "0x" << num << dec;
out << " (" << util::rawstr(const_string[num], 16) << ")";
break;
default:
if (files) {
out << hex << "0x" << num << dec;
}
break;
}
// if file list is loaded, dump file location info
if (files) {
out << "(" << files[code.fidx] << ":" << code.line << ")";
out << " (" << files[code.fidx] << ":" << code.line << ")";
}
}

View File

@@ -1,107 +1,199 @@
#pragma once
#include "nasal.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
#include <iostream>
namespace nasal {
enum op_code_type:u8 {
op_exit, // stop the virtual machine
op_repl, // in repl mode: print value on stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant std::string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq,// &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq,// ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const std::string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // == compare operator
op_neq, // != compare operator
op_less, // < compare operator
op_leq, // <= compare operator
op_grt, // > compare operator
op_geq, // >= compare operator
op_lessc, // < const compare operator
op_leqc, // <= const compare operator
op_grtc, // > const compare operator
op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call native functions
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
enum op_code_type: u8 {
op_exit, // stop the virtual machine
op_repl, // in repl mode: print value on stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_dup, // copy value on stack top
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant std::string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq, // &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq, // ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const std::string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // == compare operator
op_neq, // != compare operator
op_less, // < compare operator
op_leq, // <= compare operator
op_grt, // > compare operator
op_geq, // >= compare operator
op_lessc, // < const compare operator
op_leqc, // <= const compare operator
op_grtc, // > const compare operator
op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or, jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop, jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call native functions
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
};
static std::unordered_map<op_code_type, std::string> operand_name_table = {
{op_code_type::op_exit, "exit "},
{op_code_type::op_repl, "repl "},
{op_code_type::op_intl, "intl "},
{op_code_type::op_loadg, "loadg "},
{op_code_type::op_loadl, "loadl "},
{op_code_type::op_loadu, "loadu "},
{op_code_type::op_dup, "dup "},
{op_code_type::op_pnum, "pnum "},
{op_code_type::op_pnil, "pnil "},
{op_code_type::op_pstr, "pstr "},
{op_code_type::op_newv, "newv "},
{op_code_type::op_newh, "newh "},
{op_code_type::op_newf, "newf "},
{op_code_type::op_happ, "happ "},
{op_code_type::op_para, "para "},
{op_code_type::op_deft, "def "},
{op_code_type::op_dyn, "dyn "},
{op_code_type::op_lnot, "lnot "},
{op_code_type::op_usub, "usub "},
{op_code_type::op_bnot, "bitnot"},
{op_code_type::op_btor, "bitor "},
{op_code_type::op_btxor, "bitxor"},
{op_code_type::op_btand, "bitand"},
{op_code_type::op_add, "add "},
{op_code_type::op_sub, "sub "},
{op_code_type::op_mul, "mult "},
{op_code_type::op_div, "div "},
{op_code_type::op_lnk, "lnk "},
{op_code_type::op_addc, "addc "},
{op_code_type::op_subc, "subc "},
{op_code_type::op_mulc, "multc "},
{op_code_type::op_divc, "divc "},
{op_code_type::op_lnkc, "lnkc "},
{op_code_type::op_addeq, "addeq "},
{op_code_type::op_subeq, "subeq "},
{op_code_type::op_muleq, "muleq "},
{op_code_type::op_diveq, "diveq "},
{op_code_type::op_lnkeq, "lnkeq "},
{op_code_type::op_btandeq, "bandeq"},
{op_code_type::op_btoreq, "boreq "},
{op_code_type::op_btxoreq, "bxoreq"},
{op_code_type::op_addeqc, "addeqc"},
{op_code_type::op_subeqc, "subeqc"},
{op_code_type::op_muleqc, "muleqc"},
{op_code_type::op_diveqc, "diveqc"},
{op_code_type::op_lnkeqc, "lnkeqc"},
{op_code_type::op_addecp, "addecp"},
{op_code_type::op_subecp, "subecp"},
{op_code_type::op_mulecp, "mulecp"},
{op_code_type::op_divecp, "divecp"},
{op_code_type::op_lnkecp, "lnkecp"},
{op_code_type::op_meq, "meq "},
{op_code_type::op_eq, "eq "},
{op_code_type::op_neq, "neq "},
{op_code_type::op_less, "less "},
{op_code_type::op_leq, "leq "},
{op_code_type::op_grt, "grt "},
{op_code_type::op_geq, "geq "},
{op_code_type::op_lessc, "lessc "},
{op_code_type::op_leqc, "leqc "},
{op_code_type::op_grtc, "grtc "},
{op_code_type::op_geqc, "geqc "},
{op_code_type::op_pop, "pop "},
{op_code_type::op_jmp, "jmp "},
{op_code_type::op_jt, "jt "},
{op_code_type::op_jf, "jf "},
{op_code_type::op_cnt, "cnt "},
{op_code_type::op_findex, "findx "},
{op_code_type::op_feach, "feach "},
{op_code_type::op_callg, "callg "},
{op_code_type::op_calll, "calll "},
{op_code_type::op_upval, "upval "},
{op_code_type::op_callv, "callv "},
{op_code_type::op_callvi, "callvi"},
{op_code_type::op_callh, "callh "},
{op_code_type::op_callfv, "callfv"},
{op_code_type::op_callfh, "callfh"},
{op_code_type::op_callb, "callb "},
{op_code_type::op_slcbeg, "slcbeg"},
{op_code_type::op_slcend, "slcend"},
{op_code_type::op_slc, "slice "},
{op_code_type::op_slc2, "slice2"},
{op_code_type::op_mcallg, "mcallg"},
{op_code_type::op_mcalll, "mcalll"},
{op_code_type::op_mupval, "mupval"},
{op_code_type::op_mcallv, "mcallv"},
{op_code_type::op_mcallh, "mcallh"},
{op_code_type::op_ret, "ret "}
};
struct opcode {
u8 op; // opcode
u16 fidx; // source code file index
u32 num; // immediate num
u32 line; // location line of source code
u64 num; // immediate num
u64 line; // location line of source code
opcode() = default;
opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default;
@@ -110,24 +202,21 @@ struct opcode {
class codestream {
private:
opcode code;
const u32 index;
const u64 index;
inline static const f64* const_number = nullptr;
inline static const std::string* const_string = nullptr;
inline static const nasal_builtin_table* natives = nullptr;
inline static const std::string* files = nullptr;
public:
codestream(const opcode& c, const u32 i): code(c), index(i) {}
static void set(
const f64*, const std::string*,
const nasal_builtin_table*,
const std::string* file_list = nullptr
);
codestream(const opcode& c, const u64 i): code(c), index(i) {}
static void set(const f64*,
const std::string*,
const nasal_builtin_table*,
const std::string* file_list = nullptr);
void dump(std::ostream&) const;
};
std::ostream& operator<<(std::ostream&, const codestream&);
extern const char* opname[];
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,73 +15,77 @@ class parse {
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
private:
u32 ptr;
u32 in_func; // count function block
u32 in_loop; // count loop block
u64 ptr;
u64 in_func_depth; // count function block
u64 in_loop_depth; // count loop block
const token* toks;
code_block* root;
error err;
private:
const std::unordered_map<tok, std::string> tokname {
{tok::use ,"use" },
{tok::rfor ,"for" },
{tok::forindex,"forindex"},
{tok::foreach ,"foreach" },
{tok::rwhile ,"while" },
{tok::var ,"var" },
{tok::func ,"func" },
{tok::brk ,"break" },
{tok::cont ,"continue"},
{tok::ret ,"return" },
{tok::rif ,"if" },
{tok::elsif ,"elsif" },
{tok::relse ,"else" },
{tok::tknil ,"nil" },
{tok::lcurve ,"(" },
{tok::rcurve ,")" },
{tok::lbracket,"[" },
{tok::rbracket,"]" },
{tok::lbrace ,"{" },
{tok::rbrace ,"}" },
{tok::semi ,";" },
{tok::opand ,"and" },
{tok::opor ,"or" },
{tok::comma ,"," },
{tok::dot ,"." },
{tok::ellipsis,"..." },
{tok::quesmark,"?" },
{tok::colon ,":" },
{tok::add ,"+" },
{tok::sub ,"-" },
{tok::mult ,"*" },
{tok::div ,"/" },
{tok::floater ,"~" },
{tok::btand ,"&" },
{tok::btor ,"|" },
{tok::btxor ,"^" },
{tok::opnot ,"!" },
{tok::eq ,"=" },
{tok::addeq ,"+=" },
{tok::subeq ,"-=" },
{tok::multeq ,"*=" },
{tok::diveq ,"/=" },
{tok::lnkeq ,"~=" },
{tok::btandeq ,"&=" },
{tok::btoreq ,"|=" },
{tok::btxoreq ,"^=" },
{tok::cmpeq ,"==" },
{tok::neq ,"!=" },
{tok::less ,"<" },
{tok::leq ,"<=" },
{tok::grt ,">" },
{tok::geq ,">=" }
const std::unordered_map<tok, std::string> token_name_mapper = {
{tok::tk_true , "true" },
{tok::tk_false , "false" },
{tok::tk_use , "use" },
{tok::tk_for , "for" },
{tok::tk_forindex, "forindex"},
{tok::tk_foreach , "foreach" },
{tok::tk_while , "while" },
{tok::tk_var , "var" },
{tok::tk_func , "func" },
{tok::tk_brk , "break" },
{tok::tk_cont , "continue"},
{tok::tk_ret , "return" },
{tok::tk_if , "if" },
{tok::tk_elsif , "elsif" },
{tok::tk_else , "else" },
{tok::tk_nil , "nil" },
{tok::tk_lcurve , "(" },
{tok::tk_rcurve , ")" },
{tok::tk_lbracket, "[" },
{tok::tk_rbracket, "]" },
{tok::tk_lbrace , "{" },
{tok::tk_rbrace , "}" },
{tok::tk_semi , ";" },
{tok::tk_and , "and" },
{tok::tk_or , "or" },
{tok::tk_comma , "," },
{tok::tk_dot , "." },
{tok::tk_ellipsis, "..." },
{tok::tk_quesmark, "?" },
{tok::tk_quesques, "??" },
{tok::tk_quesdot , "?." },
{tok::tk_colon , ":" },
{tok::tk_add , "+" },
{tok::tk_sub , "-" },
{tok::tk_mult , "*" },
{tok::tk_div , "/" },
{tok::tk_floater , "~" },
{tok::tk_btand , "&" },
{tok::tk_btor , "|" },
{tok::tk_btxor , "^" },
{tok::tk_not , "!" },
{tok::tk_eq , "=" },
{tok::tk_addeq , "+=" },
{tok::tk_subeq , "-=" },
{tok::tk_multeq , "*=" },
{tok::tk_diveq , "/=" },
{tok::tk_lnkeq , "~=" },
{tok::tk_btandeq , "&=" },
{tok::tk_btoreq , "|=" },
{tok::tk_btxoreq , "^=" },
{tok::tk_cmpeq , "==" },
{tok::tk_neq , "!=" },
{tok::tk_less , "<" },
{tok::tk_leq , "<=" },
{tok::tk_grt , ">" },
{tok::tk_geq , ">=" }
};
private:
void die(const span&,std::string);
void die(const span&, const std::string&);
void next();
void match(tok, const char* info=nullptr);
void match(tok, const char* info = nullptr);
bool lookahead(tok);
bool is_call(tok);
bool check_comma(const tok*);
@@ -115,12 +119,14 @@ private:
expr* or_expr();
expr* and_expr();
expr* cmp_expr();
expr* null_chain_expr();
expr* additive_expr();
expr* multive_expr();
unary_operator* unary();
expr* scalar();
call* call_scalar();
call_hash* callh();
null_access* null_access_call();
call_vector* callv();
call_function* callf();
slice_vector* subvec();
@@ -151,7 +157,9 @@ public:
}
public:
parse(): ptr(0), in_func(0), in_loop(0), toks(nullptr), root(nullptr) {}
parse(): ptr(0), in_func_depth(0),
in_loop_depth(0), toks(nullptr),
root(nullptr) {}
~parse() {delete root;}
const error& compile(const lexer&);
static void easter_egg();

View File

@@ -1,4 +1,5 @@
#include "nasal_type.h"
#include "util/util.h"
#include <cstring>
#include <sstream>
@@ -42,16 +43,17 @@ var nas_hash::get_value(const std::string& key) {
} else if (!elems.count("parents")) {
return var::none();
}
var ret = var::none();
var val = elems.at("parents");
if (val.type!=vm_vec) {
auto ret = var::none();
auto& val = elems.at("parents");
if (!val.is_vec()) {
return ret;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
if (i.is_hash()) {
ret = i.hash().get_value(key);
}
if (ret.type!=vm_none) {
if (!ret.is_none()) {
return ret;
}
}
@@ -64,13 +66,15 @@ var* nas_hash::get_memory(const std::string& key) {
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr = nullptr;
var val = elems.at("parents");
if (val.type!=vm_vec) {
var& val = elems.at("parents");
if (!val.is_vec()) {
return addr;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
// recursively search key in `parents`
if (i.is_hash()) {
addr = i.hash().get_memory(key);
}
if (addr) {
@@ -95,6 +99,30 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
return out;
}
std::ostream& operator<<(std::ostream& out, nas_func& func) {
out << "func(";
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& key : func.keys) {
argument_list[key.second-1] = key.first;
}
for(const auto& key : argument_list) {
out << key;
if (key != argument_list.back()) {
out << ", ";
}
}
if (func.dynamic_parameter_index>=0) {
out << (argument_list.size()? ", ":"");
out << func.dynamic_parameter_name << "...";
}
out << ") {..}";
return out;
}
void nas_func::clear() {
dynamic_parameter_index = -1;
local.clear();
@@ -102,12 +130,13 @@ void nas_func::clear() {
keys.clear();
}
void nas_ghost::set(
const std::string& ghost_type_name,
destructor destructor_pointer,
void* ghost_pointer) {
void nas_ghost::set(const std::string& ghost_type_name,
destructor destructor_pointer,
marker gc_marker_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
destructor_function = destructor_pointer;
gc_mark_function = gc_marker_pointer;
pointer = ghost_pointer;
}
@@ -129,6 +158,7 @@ void nas_ghost::clear() {
type_name = "";
pointer = nullptr;
destructor_function = nullptr;
gc_mark_function = nullptr;
}
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
@@ -142,14 +172,14 @@ void nas_co::clear() {
if (!ctx.stack) {
return;
}
for(u32 i = 0; i<STACK_DEPTH; ++i) {
for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = var::nil();
}
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.canary = ctx.stack+STACK_DEPTH-1;
ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
ctx.top = ctx.stack;
ctx.funcr = var::nil();
ctx.upvalr = var::nil();
@@ -192,103 +222,110 @@ std::ostream& operator<<(std::ostream& out, nas_map& mp) {
return out;
}
nas_val::nas_val(u8 val_type) {
nas_val::nas_val(vm_type val_type) {
mark = gc_status::collected;
type = val_type;
unmutable = 0;
immutable = 0;
switch(val_type) {
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
case vm_map: ptr.map = new nas_map; break;
case vm_type::vm_str: ptr.str = new std::string; break;
case vm_type::vm_vec: ptr.vec = new nas_vec; break;
case vm_type::vm_hash: ptr.hash = new nas_hash; break;
case vm_type::vm_func: ptr.func = new nas_func; break;
case vm_type::vm_upval: ptr.upval = new nas_upval; break;
case vm_type::vm_ghost: ptr.obj = new nas_ghost; break;
case vm_type::vm_co: ptr.co = new nas_co; break;
case vm_type::vm_map: ptr.map = new nas_map; break;
default: break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_upval:delete ptr.upval; break;
case vm_obj: delete ptr.obj; break;
case vm_co: delete ptr.co; break;
case vm_map: delete ptr.map; break;
case vm_type::vm_str: delete ptr.str; break;
case vm_type::vm_vec: delete ptr.vec; break;
case vm_type::vm_hash: delete ptr.hash; break;
case vm_type::vm_func: delete ptr.func; break;
case vm_type::vm_upval: delete ptr.upval; break;
case vm_type::vm_ghost: delete ptr.obj; break;
case vm_type::vm_co: delete ptr.co; break;
case vm_type::vm_map: delete ptr.map; break;
default: break;
}
type=vm_nil;
type = vm_type::vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_str: ptr.str->clear(); break;
case vm_vec: ptr.vec->elems.clear(); break;
case vm_hash: ptr.hash->elems.clear(); break;
case vm_func: ptr.func->clear(); break;
case vm_upval:ptr.upval->clear(); break;
case vm_obj: ptr.obj->clear(); break;
case vm_co: ptr.co->clear(); break;
case vm_map: ptr.map->clear(); break;
case vm_type::vm_str: ptr.str->clear(); break;
case vm_type::vm_vec: ptr.vec->elems.clear(); break;
case vm_type::vm_hash: ptr.hash->elems.clear(); break;
case vm_type::vm_func: ptr.func->clear(); break;
case vm_type::vm_upval: ptr.upval->clear(); break;
case vm_type::vm_ghost: ptr.obj->clear(); break;
case vm_type::vm_co: ptr.co->clear(); break;
case vm_type::vm_map: ptr.map->clear(); break;
default: break;
}
}
f64 var::to_num() {
return type!=vm_str? val.num:str2num(str().c_str());
return type!=vm_type::vm_str? val.num:util::str_to_num(str().c_str());
}
std::string var::to_str() {
if (type==vm_str) {
if (type==vm_type::vm_str) {
return str();
} else if (type==vm_num) {
std::string tmp = std::to_string(num());
} else if (type==vm_type::vm_num) {
auto tmp = std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp;
}
return "";
std::stringstream ss;
ss << *this;
return ss.str();
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out << "undefined"; break;
case vm_nil: out << "nil"; break;
case vm_num: out << ref.val.num; break;
case vm_str: out << ref.str(); break;
case vm_vec: out << ref.vec(); break;
case vm_hash: out << ref.hash(); break;
case vm_func: out << "func(..) {..}"; break;
case vm_obj: out << ref.ghost(); break;
case vm_co: out << ref.co(); break;
case vm_map: out << ref.map(); break;
case vm_type::vm_none: out << "undefined"; break;
case vm_type::vm_nil: out << "nil"; break;
case vm_type::vm_num: out << ref.val.num; break;
case vm_type::vm_str: out << ref.str(); break;
case vm_type::vm_vec: out << ref.vec(); break;
case vm_type::vm_hash: out << ref.hash(); break;
case vm_type::vm_func: out << ref.func(); break;
case vm_type::vm_ghost: out << ref.ghost(); break;
case vm_type::vm_co: out << ref.co(); break;
case vm_type::vm_map: out << ref.map(); break;
default: break;
}
return out;
}
bool var::object_check(const std::string& name) {
return type==vm_obj && ghost().type_name==name && ghost().pointer;
return is_ghost() && ghost().type_name==name && ghost().pointer;
}
var var::none() {
return {vm_none, static_cast<u32>(0)};
return {vm_type::vm_none, static_cast<u64>(0)};
}
var var::nil() {
return {vm_nil, static_cast<u32>(0)};
return {vm_type::vm_nil, static_cast<u64>(0)};
}
var var::ret(u32 pc) {
return {vm_ret, pc};
var var::ret(u64 pc) {
return {vm_type::vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_cnt, n};
return {vm_type::vm_cnt, n};
}
var var::num(f64 n) {
return {vm_num, n};
return {vm_type::vm_num, n};
}
var var::gcobj(nas_val* p) {
@@ -296,14 +333,14 @@ var var::gcobj(nas_val* p) {
}
var var::addr(var* p) {
return {vm_addr, p};
return {vm_type::vm_addr, p};
}
var* var::addr() {
return val.addr;
}
u32 var::ret() const {
u64 var::ret() const {
return val.ret;
}

View File

@@ -2,12 +2,15 @@
#include "nasal.h"
#include <cstring>
#include <sstream>
#include <iostream>
#include <vector>
#include <unordered_map>
namespace nasal {
enum vm_type:u8 {
enum class vm_type: u8 {
/* none-gc object */
vm_none = 0, // error type
vm_cnt, // counter for forindex/foreach loop
@@ -15,21 +18,25 @@ enum vm_type:u8 {
vm_ret, // return addres(program counter)
vm_nil, // nil
vm_num, // number
/* gc object */
vm_str, // string
vm_vec, // vector
vm_hash, // hashmap(dict)
vm_func, // function(lambda)
vm_upval, // upvalue
vm_obj, // ghost type
vm_ghost, // ghost type
vm_co, // coroutine
vm_map, // for globals and namespaces
/* mark type range */
vm_type_size_max
};
// size of gc object type
const u32 gc_type_size = vm_type_size_max-vm_str;
const u32 gc_type_size =
static_cast<u32>(vm_type::vm_type_size_max) -
static_cast<u32>(vm_type::vm_str);
// basic types
struct nas_vec; // vector
@@ -45,9 +52,9 @@ struct nas_val; // nas_val includes gc-managed types
struct var {
public:
u8 type = vm_none;
vm_type type = vm_type::vm_none;
union {
u32 ret;
u64 ret;
i64 cnt;
f64 num;
var* addr;
@@ -55,11 +62,11 @@ public:
} val;
private:
var(u8 t, u32 pc) {type = t; val.ret = pc;}
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
var(u8 t, f64 n) {type = t; val.num = n;}
var(u8 t, var* p) {type = t; val.addr = p;}
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
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;
@@ -76,18 +83,20 @@ public:
std::string to_str();
bool object_check(const std::string&);
public:
// create new var object
static var none();
static var nil();
static var ret(u32);
static var ret(u64);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
public:
// get value
var* addr();
u32 ret() const;
u64 ret() const;
i64& cnt();
f64 num() const;
std::string& str();
@@ -98,6 +107,22 @@ public:
nas_ghost& ghost();
nas_co& co();
nas_map& map();
public:
bool is_none() const { return type==vm_type::vm_none; }
bool is_cnt() const { return type==vm_type::vm_cnt; }
bool is_addr() const { return type==vm_type::vm_addr; }
bool is_ret() const { return type==vm_type::vm_ret; }
bool is_nil() const { return type==vm_type::vm_nil; }
bool is_num() const { return type==vm_type::vm_num; }
bool is_str() const { return type==vm_type::vm_str; }
bool is_vec() const { return type==vm_type::vm_vec; }
bool is_hash() const { return type==vm_type::vm_hash; }
bool is_func() const { return type==vm_type::vm_func; }
bool is_upval() const { return type==vm_type::vm_upval; }
bool is_ghost() const { return type==vm_type::vm_ghost; }
bool is_coroutine() const { return type==vm_type::vm_co; }
bool is_map() const { return type==vm_type::vm_map; }
};
struct nas_vec {
@@ -123,19 +148,23 @@ struct nas_hash {
};
struct nas_func {
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
i64 dynamic_parameter_index; // dynamic parameter name index in hash.
u64 entry; // pc will set to entry-1 to call this function
u32 parameter_size; // used to load default parameters to a new function
u32 local_size; // used to expand memory space for local values on stack
u64 local_size; // used to expand memory space for local values on stack
std::vector<var> local; // local scope with default value(var)
std::vector<var> upval; // closure
// parameter table, u32 begins from 1
std::unordered_map<std::string, u32> keys;
// dynamic parameter name
std::string dynamic_parameter_name;
nas_func():
dynamic_parameter_index(-1), entry(0),
parameter_size(0), local_size(0) {}
parameter_size(0), local_size(0),
dynamic_parameter_name("") {}
void clear();
};
@@ -143,7 +172,7 @@ struct nas_upval {
public:
/* on stack, use these variables */
bool on_stack;
u32 size;
u64 size;
var* stack_frame_offset;
/* not on stack, use this */
@@ -166,25 +195,28 @@ public:
struct nas_ghost {
private:
using destructor = void (*)(void*);
using marker = void (*)(void*, std::vector<var>*);
public:
std::string type_name;
destructor destructor_function;
marker gc_mark_function;
void* pointer;
public:
nas_ghost():
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
~nas_ghost() {clear();}
void set(const std::string&, destructor, void*);
type_name(""), destructor_function(nullptr),
gc_mark_function(nullptr), pointer(nullptr) {}
~nas_ghost() { clear(); }
void set(const std::string&, destructor, marker, void*);
void clear();
public:
const auto& get_ghost_name() const {return type_name;}
const auto& get_ghost_name() const { return type_name; }
};
struct context {
u32 pc = 0;
u64 pc = 0;
var* localr = nullptr;
var* memr = nullptr;
var funcr = var::nil();
@@ -205,7 +237,7 @@ struct nas_co {
status status;
nas_co() {
ctx.stack = new var[STACK_DEPTH];
ctx.stack = new var[VM_STACK_DEPTH];
clear();
}
~nas_co() {
@@ -227,15 +259,15 @@ struct nas_map {
};
struct nas_val {
enum class gc_status:u8 {
enum class gc_status: u8 {
uncollected = 0,
collected,
found
};
gc_status mark;
u8 type; // value type
u8 unmutable; // used to mark if a string is unmutable
vm_type type; // value type
u8 immutable; // used to mark if a string is immutable
union {
std::string* str;
nas_vec* vec;
@@ -247,13 +279,14 @@ struct nas_val {
nas_map* map;
} ptr;
nas_val(u8);
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&);

View File

@@ -1,16 +1,15 @@
#include "nasal_vm.h"
#include "util/util.h"
namespace nasal {
void vm::init(
const std::vector<std::string>& strs,
const std::vector<f64>& nums,
const std::vector<nasal_builtin_table>& natives,
const std::vector<opcode>& code,
const std::unordered_map<std::string, i32>& global_symbol,
const std::vector<std::string>& filenames,
const std::vector<std::string>& argv
) {
void vm::vm_init_enrty(const std::vector<std::string>& strs,
const std::vector<f64>& nums,
const std::vector<nasal_builtin_table>& natives,
const std::vector<opcode>& code,
const std::unordered_map<std::string, u64>& global_symbol,
const std::vector<std::string>& filenames,
const std::vector<std::string>& argv) {
const_number = nums.data();
const_string = strs.data();
bytecode = code.data();
@@ -31,14 +30,14 @@ void vm::init(
ngc.init(strs, argv);
/* init vm globals */
auto map_instance = ngc.alloc(vm_map);
auto map_instance = ngc.alloc(vm_type::vm_map);
global[global_symbol.at("globals")] = map_instance;
for(const auto& i : global_symbol) {
map_instance.map().mapper[i.first] = global+i.second;
}
/* init vm arg */
auto arg_instance = ngc.alloc(vm_vec);
auto arg_instance = ngc.alloc(vm_type::vm_vec);
global[global_symbol.at("arg")] = arg_instance;
arg_instance.vec().elems = ngc.env_argv;
}
@@ -51,14 +50,14 @@ void vm::context_and_global_init() {
ctx.funcr = nil;
ctx.upvalr = nil;
/* set canary = stack[STACK_DEPTH-1] */
ctx.canary = ctx.stack+STACK_DEPTH-1;
/* set canary = stack[VM_STACK_DEPTH-1] */
ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
/* nothing is on stack */
ctx.top = ctx.stack - 1;
/* clear main stack and global */
for(u32 i = 0; i<STACK_DEPTH; ++i) {
for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = nil;
global[i] = nil;
}
@@ -67,41 +66,42 @@ void vm::context_and_global_init() {
void vm::value_info(var& val) {
const auto p = reinterpret_cast<u64>(val.val.gcobj);
switch(val.type) {
case vm_none: std::clog << "| null |"; break;
case vm_ret: std::clog << "| pc | 0x" << std::hex
<< val.ret() << std::dec; break;
case vm_addr: std::clog << "| addr | 0x" << std::hex
<< reinterpret_cast<u64>(val.addr())
<< std::dec; break;
case vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
case vm_nil: std::clog << "| nil |"; break;
case vm_num: std::clog << "| num | " << val.num(); break;
case vm_str: std::clog << "| str | <0x" << std::hex << p
<< "> " << rawstr(val.str(), 16)
<< std::dec; break;
case vm_func: std::clog << "| func | <0x" << std::hex << p
<< "> entry:0x" << val.func().entry
<< std::dec; break;
case vm_upval:std::clog << "| upval| <0x" << std::hex << p
<< std::dec << "> [" << val.upval().size
<< " val]"; break;
case vm_vec: std::clog << "| vec | <0x" << std::hex << p
<< std::dec << "> [" << val.vec().size()
<< " val]"; break;
case vm_hash: std::clog << "| hash | <0x" << std::hex << p
<< std::dec << "> {" << val.hash().size()
<< " val}"; break;
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
<< "> obj:0x"
<< reinterpret_cast<u64>(val.ghost().pointer)
<< std::dec; break;
case vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
case vm_map: std::clog << "| nmspc| <0x" << std::hex << p
<< std::dec << "> namespace ["
<< val.map().mapper.size() << " val]"; break;
default: std::clog << "| err | <0x" << std::hex << p
<< std::dec << "> unknown object"; break;
case vm_type::vm_none: std::clog << "| null |"; break;
case vm_type::vm_ret: std::clog << "| pc | 0x" << std::hex
<< val.ret() << std::dec; break;
case vm_type::vm_addr: std::clog << "| addr | 0x" << std::hex
<< reinterpret_cast<u64>(val.addr())
<< std::dec; break;
case vm_type::vm_cnt: std::clog << "| cnt | " << val.cnt(); break;
case vm_type::vm_nil: std::clog << "| nil |"; break;
case vm_type::vm_num: std::clog << "| num | " << val.num(); break;
case vm_type::vm_str: std::clog << "| str | <0x" << std::hex << p
<< "> \"" << util::rawstr(val.str(), 16)
<< "\"" << std::dec; break;
case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p
<< std::dec << "> " << val.func();
break;
case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p
<< std::dec << "> [" << val.upval().size
<< " val]"; break;
case vm_type::vm_vec: std::clog << "| vec | <0x" << std::hex << p
<< std::dec << "> [" << val.vec().size()
<< " val]"; break;
case vm_type::vm_hash: std::clog << "| hash | <0x" << std::hex << p
<< std::dec << "> {" << val.hash().size()
<< " val}"; break;
case vm_type::vm_ghost:std::clog << "| obj | <0x" << std::hex << p
<< "> obj:0x"
<< reinterpret_cast<u64>(val.ghost().pointer)
<< std::dec; break;
case vm_type::vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
case vm_type::vm_map: std::clog << "| nmspc| <0x" << std::hex << p
<< std::dec << "> namespace ["
<< val.map().mapper.size()
<< " val]"; break;
default: std::clog << "| err | <0x" << std::hex << p
<< std::dec << "> unknown object"; break;
}
std::clog << "\n";
}
@@ -125,10 +125,11 @@ void vm::function_detail_info(const nas_func& func) {
}
if (func.dynamic_parameter_index>=0) {
std::clog << (argument_list.size()? ", ":"");
std::clog << const_string[func.dynamic_parameter_index] << "...";
std::clog << func.dynamic_parameter_name << "...";
}
std::clog << ") ";
std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
const auto& code = bytecode[func.entry];
std::clog << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
}
void vm::function_call_trace() {
@@ -138,7 +139,7 @@ void vm::function_call_trace() {
// generate trace back
std::stack<const nas_func*> functions;
for(var* i = bottom; i<=top; ++i) {
if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) {
if (i->is_func() && i-1>=bottom && (i-1)->is_ret()) {
functions.push(&i->func());
}
}
@@ -176,7 +177,7 @@ void vm::trace_back() {
// generate trace back
std::stack<u32> ret;
for(var* i = ctx.stack; i<=ctx.top; ++i) {
if (i->type==vm_ret && i->ret()!=0) {
if (i->is_ret() && i->ret()!=0) {
ret.push(i->ret());
}
}
@@ -191,8 +192,8 @@ void vm::trace_back() {
}
if (same) {
std::clog << " 0x" << std::hex
<< std::setw(6) << std::setfill('0')
<< prev << std::dec << " "
<< std::setw(8) << std::setfill('0')
<< prev << std::dec << " "
<< same << " same call(s)\n";
same = 0;
}
@@ -201,15 +202,18 @@ void vm::trace_back() {
// the first called place has no same calls
}
void vm::stack_info(const u32 limit = 10) {
void vm::stack_info(const u64 limit = 16) {
var* top = ctx.top;
var* bottom = ctx.stack;
std::clog << "\nstack (0x" << std::hex << reinterpret_cast<u64>(bottom);
std::clog << std::dec << ", limit " << limit << ", total ";
const auto stack_address = reinterpret_cast<u64>(bottom);
std::clog << "\nvm stack (0x" << std::hex << stack_address << std::dec;
std::clog << ", limit " << limit << ", total ";
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
std::clog << " 0x" << std::hex
<< std::setw(6) << std::setfill('0')
<< std::setw(8) << std::setfill('0')
<< static_cast<u64>(top-bottom) << std::dec
<< " ";
value_info(top[0]);
@@ -217,32 +221,32 @@ void vm::stack_info(const u32 limit = 10) {
}
void vm::register_info() {
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main")
<< ")\n" << std::hex
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
<< " [global] | addr | 0x"
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main") << ")\n";
std::clog << std::hex
<< " [ pc ] | pc | 0x" << ctx.pc << "\n"
<< " [ global ] | addr | 0x"
<< reinterpret_cast<u64>(global) << "\n"
<< " [local ] | addr | 0x"
<< " [ 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);
std::clog << " [upval ] "; value_info(ctx.upvalr);
std::clog << " [ funcr ] "; value_info(ctx.funcr);
std::clog << " [ upval ] "; value_info(ctx.upvalr);
}
void vm::global_state() {
if (!global_size || global[0].type==vm_none) {
if (!global_size || global[0].is_none()) {
return;
}
std::clog << "\nglobal (0x" << std::hex
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
for(usize i = 0; i<global_size; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
std::clog << " 0x" << std::hex << std::setw(8)
<< std::setfill('0') << i << std::dec
<< " ";
value_info(global[i]);
@@ -258,7 +262,7 @@ void vm::local_state() {
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
<< ">)\n" << std::dec;
for(u32 i = 0; i<lsize; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
std::clog << " 0x" << std::hex << std::setw(8)
<< std::setfill('0') << i << std::dec
<< " ";
value_info(ctx.localr[i]);
@@ -266,7 +270,7 @@ void vm::local_state() {
}
void vm::upvalue_state() {
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
if (ctx.funcr.is_nil() || ctx.funcr.func().upval.empty()) {
return;
}
std::clog << "\nupvalue\n";
@@ -275,7 +279,7 @@ void vm::upvalue_state() {
std::clog << " -> upval[" << i << "]:\n";
auto& uv = upval[i].upval();
for(u32 j = 0; j<uv.size; ++j) {
std::clog << " 0x" << std::hex << std::setw(6)
std::clog << " 0x" << std::hex << std::setw(8)
<< std::setfill('0') << j << std::dec
<< " ";
value_info(uv[j]);
@@ -312,7 +316,8 @@ std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
}
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
const auto& code = bytecode[func.entry];
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
@@ -326,7 +331,7 @@ std::string vm::report_special_call_lack_arguments(
argument_list[i.second-1] = i.first;
}
for(const auto& key : argument_list) {
if (local[func.keys.at(key)].type==vm_none) {
if (local[func.keys.at(key)].is_none()) {
result += key + ", ";
} else {
result += key + "[get], ";
@@ -335,7 +340,8 @@ std::string vm::report_special_call_lack_arguments(
result = result.substr(0, result.length()-2);
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
const auto& code = bytecode[func.entry];
out << "{ entry: " << files[code.fidx] << ":" << code.line << " }";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
@@ -366,20 +372,21 @@ std::string vm::report_out_of_range(f64 index, usize real_size) const {
std::string vm::type_name_string(const var& value) const {
switch(value.type) {
case vm_none: return "none";
case vm_cnt: return "counter";
case vm_addr: return "address";
case vm_ret: return "program counter";
case vm_nil: return "nil";
case vm_num: return "number";
case vm_str: return "string";
case vm_vec: return "vector";
case vm_hash: return "hash";
case vm_func: return "function";
case vm_upval: return "upvalue";
case vm_obj: return "ghost type";
case vm_co: return "coroutine";
case vm_map: return "namespace";
case vm_type::vm_none: return "none";
case vm_type::vm_cnt: return "counter";
case vm_type::vm_addr: return "address";
case vm_type::vm_ret: return "program counter";
case vm_type::vm_nil: return "nil";
case vm_type::vm_num: return "number";
case vm_type::vm_str: return "string";
case vm_type::vm_vec: return "vector";
case vm_type::vm_hash: return "hash";
case vm_type::vm_func: return "function";
case vm_type::vm_upval: return "upvalue";
case vm_type::vm_ghost: return "ghost type";
case vm_type::vm_co: return "coroutine";
case vm_type::vm_map: return "namespace";
default: break;
}
return "unknown";
}
@@ -406,97 +413,122 @@ void vm::die(const std::string& str) {
}
}
void vm::run(
const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv
) {
init(gen.strs(), gen.nums(), gen.natives(),
gen.codes(), gen.globals(), linker.get_file_list(), argv);
void vm::run(const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv) {
vm_init_enrty(
gen.strs(),
gen.nums(),
gen.natives(),
gen.codes(),
gen.globals(),
linker.get_file_list(),
argv
);
#ifndef _MSC_VER
// using labels as values/computed goto
const void* oprs[] = {
&&vmexit, &&repl, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil,
&&pstr, &&newv, &&newh, &&newf,
&&happ, &&para, &&deft, &&dyn,
&&lnot, &&usub, &&bnot, &&btor,
&&btxor, &&btand, &&add, &&sub,
&&mul, &&div, &&lnk, &&addc,
&&subc, &&mulc, &&divc, &&lnkc,
&&addeq, &&subeq, &&muleq, &&diveq,
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
&&divecp, &&lnkecp, &&meq, &&eq,
&&neq, &&less, &&leq, &&grt,
&&geq, &&lessc, &&leqc, &&grtc,
&&geqc, &&pop, &&jmp, &&jt,
&&jf, &&cnt, &&findex, &&feach,
&&callg, &&calll, &&upval, &&callv,
&&callvi, &&callh, &&callfv, &&callfh,
&&callb, &&slcbeg, &&slcend, &&slc,
&&slc2, &&mcallg, &&mcalll, &&mupval,
&&mcallv, &&mcallh, &&ret
&&vmexit,
&&repl,
&&intl,
&&loadg,
&&loadl,
&&loadu,
&&dup,
&&pnum,
&&pnil,
&&pstr,
&&newv,
&&newh,
&&newf,
&&happ,
&&para,
&&deft,
&&dyn,
&&lnot,
&&usub,
&&bnot,
&&btor,
&&btxor,
&&btand,
&&add,
&&sub,
&&mul,
&&div,
&&lnk,
&&addc,
&&subc,
&&mulc,
&&divc,
&&lnkc,
&&addeq,
&&subeq,
&&muleq,
&&diveq,
&&lnkeq,
&&bandeq,
&&boreq,
&&bxoreq,
&&addeqc,
&&subeqc,
&&muleqc,
&&diveqc,
&&lnkeqc,
&&addecp,
&&subecp,
&&mulecp,
&&divecp,
&&lnkecp,
&&meq,
&&eq,
&&neq,
&&less,
&&leq,
&&grt,
&&geq,
&&lessc,
&&leqc,
&&grtc,
&&geqc,
&&pop,
&&jmp,
&&jt,
&&jf,
&&cnt,
&&findex,
&&feach,
&&callg,
&&calll,
&&upval,
&&callv,
&&callvi,
&&callh,
&&callfv,
&&callfh,
&&callb,
&&slcbeg,
&&slcend,
&&slc,
&&slc2,
&&mcallg,
&&mcalll,
&&mupval,
&&mcallv,
&&mcallh,
&&ret
};
std::vector<const void*> code;
for(auto& i : gen.codes()) {
for(const auto& i : gen.codes()) {
code.push_back(oprs[i.op]);
imm.push_back(i.num);
}
// goto the first operand
goto *code[ctx.pc];
#else
typedef void (vm::*nafunc)();
const nafunc oprs[] = {
nullptr, &vm::o_repl,
&vm::o_intl, &vm::o_loadg,
&vm::o_loadl, &vm::o_loadu,
&vm::o_pnum, &vm::o_pnil,
&vm::o_pstr, &vm::o_newv,
&vm::o_newh, &vm::o_newf,
&vm::o_happ, &vm::o_para,
&vm::o_deft, &vm::o_dyn,
&vm::o_lnot, &vm::o_usub,
&vm::o_bnot, &vm::o_btor,
&vm::o_btxor, &vm::o_btand,
&vm::o_add, &vm::o_sub,
&vm::o_mul, &vm::o_div,
&vm::o_lnk, &vm::o_addc,
&vm::o_subc, &vm::o_mulc,
&vm::o_divc, &vm::o_lnkc,
&vm::o_addeq, &vm::o_subeq,
&vm::o_muleq, &vm::o_diveq,
&vm::o_lnkeq, &vm::o_bandeq,
&vm::o_boreq, &vm::o_bxoreq,
&vm::o_addeqc, &vm::o_subeqc,
&vm::o_muleqc, &vm::o_diveqc,
&vm::o_lnkeqc, &vm::o_addecp,
&vm::o_subecp, &vm::o_mulecp,
&vm::o_divecp, &vm::o_lnkecp,
&vm::o_meq, &vm::o_eq,
&vm::o_neq, &vm::o_less,
&vm::o_leq, &vm::o_grt,
&vm::o_geq, &vm::o_lessc,
&vm::o_leqc, &vm::o_grtc,
&vm::o_geqc, &vm::o_pop,
&vm::o_jmp, &vm::o_jt,
&vm::o_jf, &vm::o_cnt,
&vm::o_findex, &vm::o_feach,
&vm::o_callg, &vm::o_calll,
&vm::o_upval, &vm::o_callv,
&vm::o_callvi, &vm::o_callh,
&vm::o_callfv, &vm::o_callfh,
&vm::o_callb, &vm::o_slcbeg,
&vm::o_slcend, &vm::o_slc,
&vm::o_slc2, &vm::o_mcallg,
&vm::o_mcalll, &vm::o_mupval,
&vm::o_mcallv, &vm::o_mcallh,
&vm::o_ret
};
std::vector<nafunc> code;
for(auto& i : gen.codes()) {
code.push_back(oprs[i.op]);
std::vector<nasal_vm_func> code;
for(const auto& i : gen.codes()) {
code.push_back(operand_function[i.op]);
imm.push_back(i.num);
}
while(code[ctx.pc]) {
@@ -507,7 +539,7 @@ void vm::run(
++ctx.pc;
}
#endif
// all nasal programs should end here
vmexit:
if (verbose) {
ngc.info();
@@ -538,6 +570,7 @@ intl: exec_nodie(o_intl ); // -0
loadg: exec_nodie(o_loadg ); // -1
loadl: exec_nodie(o_loadl ); // -1
loadu: exec_nodie(o_loadu ); // -1
dup: exec_check(o_dup ); // +1
pnum: exec_check(o_pnum ); // +1
pnil: exec_check(o_pnil ); // +1
pstr: exec_check(o_pstr ); // +1

View File

@@ -8,6 +8,7 @@
#include "nasal_import.h"
#include "nasal_gc.h"
#include "nasal_codegen.h"
#include "util/util.h"
#ifdef _MSC_VER
#pragma warning (disable:4244)
@@ -20,21 +21,23 @@ namespace nasal {
class vm {
protected:
/* registers of vm */
/* vm context */
context ctx;
/* constants */
const f64* const_number = nullptr; // constant numbers
const std::string* const_string = nullptr; // constant symbols and strings
std::vector<u32> imm; // immediate number table
const f64* const_number = nullptr;
const std::string* const_string = nullptr;
std::vector<u64> imm; // immediate number table
/* nasal native functions */
std::vector<nasal_builtin_table> native_function;
/* garbage collector */
gc ngc;
/* main stack */
var* global = nullptr;
usize global_size = 0;
var* global = nullptr; // used to store global variables
usize global_size = 0; // mark size of global variables
/* values used for debugger */
const std::string* files = nullptr; // file name list
@@ -45,25 +48,28 @@ protected:
bool first_exec_flag = true;
bool allow_repl_output = false;
/* limited mode, will not load unsafe system api if switched on */
bool flag_limited_mode = false;
protected:
/* vm initializing function */
void init(
const std::vector<std::string>&,
const std::vector<f64>&,
const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&,
const std::unordered_map<std::string, i32>&,
const std::vector<std::string>&,
const std::vector<std::string>&
);
void vm_init_enrty(const std::vector<std::string>&,
const std::vector<f64>&,
const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&,
const std::unordered_map<std::string, u64>&,
const std::vector<std::string>&,
const std::vector<std::string>&);
void context_and_global_init();
protected:
/* debug functions */
bool verbose = false;
void value_info(var&);
void function_detail_info(const nas_func&);
void function_call_trace();
void trace_back();
void stack_info(const u32);
void stack_info(const u64);
void register_info();
void global_state();
void local_state();
@@ -76,15 +82,18 @@ protected:
std::string type_name_string(const var&) const;
void die(const std::string&);
protected:
/* vm calculation functions*/
inline bool cond(var&);
protected:
/* vm operands */
inline void o_repl();
inline void o_intl();
inline void o_loadg();
inline void o_loadl();
inline void o_loadu();
inline void o_dup();
inline void o_pnum();
inline void o_pnil();
inline void o_pstr();
@@ -167,12 +176,106 @@ protected:
inline void o_mcallh();
inline void o_ret();
protected:
// for debugger and MSVC(does not support labels as values)
typedef void (vm::*nasal_vm_func)();
const nasal_vm_func operand_function[op_ret + 1] = {
nullptr,
&vm::o_repl,
&vm::o_intl,
&vm::o_loadg,
&vm::o_loadl,
&vm::o_loadu,
&vm::o_dup,
&vm::o_pnum,
&vm::o_pnil,
&vm::o_pstr,
&vm::o_newv,
&vm::o_newh,
&vm::o_newf,
&vm::o_happ,
&vm::o_para,
&vm::o_deft,
&vm::o_dyn,
&vm::o_lnot,
&vm::o_usub,
&vm::o_bnot,
&vm::o_btor,
&vm::o_btxor,
&vm::o_btand,
&vm::o_add,
&vm::o_sub,
&vm::o_mul,
&vm::o_div,
&vm::o_lnk,
&vm::o_addc,
&vm::o_subc,
&vm::o_mulc,
&vm::o_divc,
&vm::o_lnkc,
&vm::o_addeq,
&vm::o_subeq,
&vm::o_muleq,
&vm::o_diveq,
&vm::o_lnkeq,
&vm::o_bandeq,
&vm::o_boreq,
&vm::o_bxoreq,
&vm::o_addeqc,
&vm::o_subeqc,
&vm::o_muleqc,
&vm::o_diveqc,
&vm::o_lnkeqc,
&vm::o_addecp,
&vm::o_subecp,
&vm::o_mulecp,
&vm::o_divecp,
&vm::o_lnkecp,
&vm::o_meq,
&vm::o_eq,
&vm::o_neq,
&vm::o_less,
&vm::o_leq,
&vm::o_grt,
&vm::o_geq,
&vm::o_lessc,
&vm::o_leqc,
&vm::o_grtc,
&vm::o_geqc,
&vm::o_pop,
&vm::o_jmp,
&vm::o_jt,
&vm::o_jf,
&vm::o_cnt,
&vm::o_findex,
&vm::o_feach,
&vm::o_callg,
&vm::o_calll,
&vm::o_upval,
&vm::o_callv,
&vm::o_callvi,
&vm::o_callh,
&vm::o_callfv,
&vm::o_callfh,
&vm::o_callb,
&vm::o_slcbeg,
&vm::o_slcend,
&vm::o_slc,
&vm::o_slc2,
&vm::o_mcallg,
&vm::o_mcalll,
&vm::o_mupval,
&vm::o_mcallv,
&vm::o_mcallh,
&vm::o_ret
};
public:
/* constructor of vm instance */
vm() {
ctx.stack = new var[STACK_DEPTH];
global = new var[STACK_DEPTH];
ctx.stack = new var[VM_STACK_DEPTH];
global = new var[VM_STACK_DEPTH];
}
~vm() {
delete[] ctx.stack;
@@ -180,25 +283,36 @@ public:
}
/* execution entry */
void run(
const codegen&,
const linker&,
const std::vector<std::string>&
);
void run(const codegen&, // get generated code
const linker&, // get list of used files
const std::vector<std::string>&); // get command line arguments
/* set detail report info flag */
void set_detail_report_info(bool flag) {verbose = flag;}
void set_detail_report_info(bool flag) {
verbose = flag;
}
/* set repl mode flag */
void set_repl_mode_flag(bool flag) {is_repl_mode = flag;}
void set_repl_mode_flag(bool flag) {
is_repl_mode = flag;
}
/* set repl output flag */
void set_allow_repl_output_flag(bool flag) {allow_repl_output = flag;}
void set_allow_repl_output_flag(bool flag) {
allow_repl_output = flag;
}
/* set limit mode flag */
void set_limit_mode_flag(bool flag) {
flag_limited_mode = flag;
}
};
inline bool vm::cond(var& val) {
if (val.type==vm_num) {
if (val.is_num()) {
return val.num();
} else if (val.type==vm_str) {
const f64 num = str2num(val.str().c_str());
} 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 false;
@@ -229,6 +343,11 @@ inline void vm::o_loadu() {
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
}
inline void vm::o_dup() {
ctx.top[1] = ctx.top[0];
++ctx.top;
}
inline void vm::o_pnum() {
(++ctx.top)[0] = var::num(const_number[imm[ctx.pc]]);
}
@@ -242,37 +361,45 @@ inline void vm::o_pstr() {
}
inline void vm::o_newv() {
var newv = ngc.alloc(vm_vec);
var newv = ngc.alloc(vm_type::vm_vec);
auto& vec = newv.vec().elems;
vec.resize(imm[ctx.pc]);
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
ctx.top = ctx.top-imm[ctx.pc]+1;
for(u32 i = 0; i<imm[ctx.pc]; ++i) {
ctx.top = ctx.top - imm[ctx.pc] + 1;
for(u64 i = 0; i<imm[ctx.pc]; ++i) {
vec[i] = ctx.top[i];
}
ctx.top[0] = newv;
}
inline void vm::o_newh() {
(++ctx.top)[0] = ngc.alloc(vm_hash);
(++ctx.top)[0] = ngc.alloc(vm_type::vm_hash);
}
inline void vm::o_newf() {
(++ctx.top)[0] = ngc.alloc(vm_func);
(++ctx.top)[0] = ngc.alloc(vm_type::vm_func);
auto& func = ctx.top[0].func();
func.entry = imm[ctx.pc];
func.parameter_size = 1;
/* this means you create a new function in local scope */
if (ctx.localr) {
// copy upval scope list from upper level function
func.upval = ctx.funcr.func().upval;
// function created in the same local scope shares one closure
// so this size & stk setting has no problem
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
upval.upval().size = ctx.funcr.func().local_size;
upval.upval().stack_frame_offset = ctx.localr;
// function created in the same local scope shares same closure
var upval = (ctx.upvalr.is_nil())?
ngc.alloc(vm_type::vm_upval):
ctx.upvalr;
// if no upval scope exists, now it's time to create one
if (ctx.upvalr.is_nil()) {
upval.upval().size = ctx.funcr.func().local_size;
upval.upval().stack_frame_offset = ctx.localr;
ctx.upvalr = upval;
}
func.upval.push_back(upval);
ctx.upvalr = upval;
}
}
@@ -298,15 +425,16 @@ inline void vm::o_deft() {
inline void vm::o_dyn() {
ctx.top[0].func().dynamic_parameter_index = imm[ctx.pc];
ctx.top[0].func().dynamic_parameter_name = const_string[imm[ctx.pc]];
}
inline void vm::o_lnot() {
var val = ctx.top[0];
switch(val.type) {
case vm_nil: ctx.top[0] = one; break;
case vm_num: ctx.top[0] = val.num()? zero:one; break;
case vm_str: {
const f64 num = str2num(val.str().c_str());
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_str: {
const f64 num = util::str_to_num(val.str().c_str());
if (std::isnan(num)) {
ctx.top[0] = var::num(static_cast<f64>(val.str().empty()));
} else {
@@ -361,8 +489,8 @@ 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].type==vm_vec && ctx.top[0].type==vm_vec) {
ngc.temp = ngc.alloc(vm_vec);
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) {
ngc.temp.vec().elems.push_back(i);
}
@@ -496,12 +624,12 @@ inline void vm::o_meq() {
inline void vm::o_eq() {
var val2 = ctx.top[0];
var val1 = (--ctx.top)[0];
if (val1.type==vm_nil && val2.type==vm_nil) {
if (val1.is_nil() && val2.is_nil()) {
ctx.top[0] = one;
} else if (val1.type==vm_str && val2.type==vm_str) {
} else if (val1.is_str() && val2.is_str()) {
ctx.top[0] = (val1.str()==val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
} 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;
} else {
ctx.top[0] = (val1==val2)? one:zero;
@@ -511,12 +639,12 @@ inline void vm::o_eq() {
inline void vm::o_neq() {
var val2 = ctx.top[0];
var val1 = (--ctx.top)[0];
if (val1.type==vm_nil && val2.type==vm_nil) {
if (val1.is_nil() && val2.is_nil()) {
ctx.top[0] = zero;
} else if (val1.type==vm_str && val2.type==vm_str) {
} else if (val1.is_str() && val2.is_str()) {
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
} 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;
} else {
ctx.top[0] = (val1!=val2)? one:zero;
@@ -565,7 +693,7 @@ inline void vm::o_jf() {
}
inline void vm::o_cnt() {
if (ctx.top[0].type!=vm_vec) {
if (!ctx.top[0].is_vec()) {
die("must use vector in forindex/foreach but get "+
type_name_string(ctx.top[0])
);
@@ -611,25 +739,25 @@ inline void vm::o_upval() {
inline void vm::o_callv() {
var val = ctx.top[0];
var vec = (--ctx.top)[0];
if (vec.type==vm_vec) {
if (vec.is_vec()) {
ctx.top[0] = vec.vec().get_value(val.to_num());
if (ctx.top[0].type==vm_none) {
if (ctx.top[0].is_none()) {
die(report_out_of_range(val.to_num(), vec.vec().size()));
return;
}
} else if (vec.type==vm_hash) {
if (val.type!=vm_str) {
} else if (vec.is_hash()) {
if (!val.is_str()) {
die("must use string as the key but get "+type_name_string(val));
return;
}
ctx.top[0] = vec.hash().get_value(val.str());
if (ctx.top[0].type==vm_none) {
if (ctx.top[0].is_none()) {
die(report_key_not_found(val.str(), vec.hash()));
return;
} else if (ctx.top[0].type==vm_func) {
} else if (ctx.top[0].is_func()) {
ctx.top[0].func().local[0] = val; // 'me'
}
} else if (vec.type==vm_str) {
} else if (vec.is_str()) {
const auto& str = vec.str();
i32 num = val.to_num();
i32 len = str.length();
@@ -640,13 +768,13 @@ inline void vm::o_callv() {
ctx.top[0] = var::num(
static_cast<f64>(static_cast<u8>(str[num>=0? num:num+len]))
);
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
} else if (vec.is_map()) {
if (!val.is_str()) {
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].type==vm_none) {
if (ctx.top[0].is_none()) {
die("cannot find symbol \""+val.str()+"\"");
return;
}
@@ -658,13 +786,13 @@ inline void vm::o_callv() {
inline void vm::o_callvi() {
var val = ctx.top[0];
if (val.type!=vm_vec) {
if (!val.is_vec()) {
die("must use a vector but get "+type_name_string(val));
return;
}
// cannot use operator[],because this may cause overflow
// cannot use operator[], because this may cause overflow
(++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]);
if (ctx.top[0].type==vm_none) {
if (ctx.top[0].is_none()) {
die(report_out_of_range(imm[ctx.pc], val.vec().size()));
return;
}
@@ -672,30 +800,31 @@ inline void vm::o_callvi() {
inline void vm::o_callh() {
var val = ctx.top[0];
if (val.type!=vm_hash && val.type!=vm_map) {
if (!val.is_hash() && !val.is_map()) {
die("must call a hash but get "+type_name_string(val));
return;
}
const auto& str = const_string[imm[ctx.pc]];
if (val.type==vm_hash) {
if (val.is_hash()) {
ctx.top[0] = val.hash().get_value(str);
} else {
ctx.top[0] = val.map().get_value(str);
}
if (ctx.top[0].type==vm_none) {
val.type==vm_hash?
if (ctx.top[0].is_none()) {
val.is_hash()?
die(report_key_not_found(str, val.hash())):
die("cannot find symbol \"" + str + "\"");
return;
} else if (ctx.top[0].type==vm_func) {
} else if (ctx.top[0].is_func()) {
ctx.top[0].func().local[0] = val; // 'me'
}
}
inline void vm::o_callfv() {
const u32 argc = imm[ctx.pc]; // arguments counter
const auto argc = imm[ctx.pc]; // arguments counter
var* local = ctx.top-argc+1; // arguments begin address
if (local[-1].type!=vm_func) {
if (!local[-1].is_func()) {
die("must call a function but get "+type_name_string(local[-1]));
return;
}
@@ -712,8 +841,8 @@ inline void vm::o_callfv() {
return;
}
// parameter size is func->psize-1, 1 is reserved for "me"
const u32 parameter_size = func.parameter_size-1;
if (argc<parameter_size && func.local[argc+1].type==vm_none) {
const u64 parameter_size = func.parameter_size-1;
if (argc<parameter_size && func.local[argc+1].is_none()) {
die(report_lack_arguments(argc, func));
return;
}
@@ -722,17 +851,18 @@ inline void vm::o_callfv() {
var dynamic = nil;
if (func.dynamic_parameter_index>=0) {
// load dynamic argument
dynamic = ngc.alloc(vm_vec);
for(u32 i = parameter_size; i<argc; ++i) {
dynamic = ngc.alloc(vm_type::vm_vec);
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_vec);
for(u32 i = parameter_size; i<argc; ++i) {
dynamic = ngc.alloc(vm_type::vm_vec);
for(u64 i = parameter_size; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
}
// should reset stack top after allocating vector
// because this may cause gc
// then all the available values the vector needs
@@ -740,14 +870,18 @@ inline void vm::o_callfv() {
// collected incorrectly
ctx.top = local+func.local_size;
const u32 min_size = (std::min)(parameter_size, argc); // avoid error in MSVC
for(u32 i = min_size; i>=1; --i) { // load arguments
// 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) {
local[i] = local[i-1];
}
local[0] = func.local[0];// load "me"
local[0] = func.local[0]; // load "me"
// load local scope & default arguments
for(u32 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
@@ -764,7 +898,7 @@ inline void vm::o_callfv() {
inline void vm::o_callfh() {
const auto& hash = ctx.top[0].hash().elems;
if (ctx.top[-1].type!=vm_func) {
if (!ctx.top[-1].is_func()) {
die("must call a function but get "+type_name_string(ctx.top[-1]));
return;
}
@@ -797,7 +931,7 @@ inline void vm::o_callfh() {
const auto& key = i.first;
if (hash.count(key)) {
local[i.second] = hash.at(key);
} else if (local[i.second].type==vm_none) {
} else if (local[i.second].is_none()) {
lack_arguments_flag = true;
}
}
@@ -829,7 +963,7 @@ inline void vm::o_callb() {
ctx.top[0] = result;
// if get none, this means errors occurred when calling this native function
if (ctx.top[0].type==vm_none) {
if (ctx.top[0].is_none()) {
die("error occurred in native function");
return;
}
@@ -841,8 +975,8 @@ inline void vm::o_slcbeg() {
// +--------------+
// | resource_vec | <-- top[-1]
// +--------------+
(++ctx.top)[0] = ngc.alloc(vm_vec);
if (ctx.top[-1].type!=vm_vec) {
(++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]));
return;
}
@@ -856,7 +990,7 @@ inline void vm::o_slcend() {
inline void vm::o_slc() {
var val = (ctx.top--)[0];
var res = ctx.top[-1].vec().get_value(val.to_num());
if (res.type==vm_none) {
if (res.is_none()) {
die(report_out_of_range(val.to_num(), ctx.top[-1].vec().size()));
return;
}
@@ -869,16 +1003,17 @@ inline void vm::o_slc2() {
const auto& ref = ctx.top[-1].vec().elems;
auto& aim = ctx.top[0].vec().elems;
u8 type1 = val1.type,type2=val2.type;
vm_type type1 = val1.type;
vm_type type2 = val2.type;
i32 num1 = val1.to_num();
i32 num2 = val2.to_num();
i32 size = ref.size();
if (type1==vm_nil && type2==vm_nil) {
if (val1.is_nil() && val2.is_nil()) {
num1 = 0;
num2 = size-1;
} else if (type1==vm_nil && type2!=vm_nil) {
} else if (val1.is_nil() && type2!=vm_type::vm_nil) {
num1 = num2<0? -size:0;
} else if (type1!=vm_nil && type2==vm_nil) {
} else if (type1!=vm_type::vm_nil && val2.is_nil()) {
num2 = num1<0? -1:size-1;
}
@@ -923,14 +1058,14 @@ inline void vm::o_mupval() {
inline void vm::o_mcallv() {
var val = ctx.top[0]; // index
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
if (vec.type==vm_vec) {
if (vec.is_vec()) {
ctx.memr = vec.vec().get_memory(val.to_num());
if (!ctx.memr) {
die(report_out_of_range(val.to_num(), vec.vec().size()));
return;
}
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
if (val.type!=vm_str) {
} 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));
return;
}
@@ -941,8 +1076,8 @@ inline void vm::o_mcallv() {
ref.elems[str] = nil;
ctx.memr = ref.get_memory(str);
}
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
} else if (vec.is_map()) {
if (!val.is_str()) {
die("must use string as the key but get "+type_name_string(val));
return;
}
@@ -959,25 +1094,31 @@ inline void vm::o_mcallv() {
}
inline void vm::o_mcallh() {
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
if (hash.type!=vm_hash && hash.type!=vm_map) {
die("must call a hash/namespace but get "+type_name_string(hash));
// mcall hash, reserved on stack to avoid gc, so do not do ctx.top--
var hash = ctx.top[0];
if (!hash.is_hash() && !hash.is_map()) {
die("must call a hash/namespace but get " + type_name_string(hash));
return;
}
const auto& str = const_string[imm[ctx.pc]];
if (hash.type==vm_map) {
ctx.memr = hash.map().get_memory(str);
const auto& key = const_string[imm[ctx.pc]];
// map is for nasal namespace type, for example `globals`
if (hash.is_map()) {
ctx.memr = hash.map().get_memory(key);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
die("cannot find symbol \"" + key + "\"");
}
return;
}
// call hash member
auto& ref = hash.hash();
ctx.memr = ref.get_memory(str);
// create a new key
ctx.memr = ref.get_memory(key);
// create a new key if not exists
if (!ctx.memr) {
ref.elems[str] = nil;
ctx.memr = ref.get_memory(str);
ref.elems[key] = nil;
ctx.memr = ref.get_memory(key);
}
}
@@ -1010,12 +1151,12 @@ inline void vm::o_ret() {
ctx.top[0] = ret; // rewrite func with returned value
// synchronize upvalue
if (up.type==vm_upval) {
if (up.is_upval()) {
auto& upval = up.upval();
auto size = func.func().local_size;
upval.on_stack = false;
upval.elems.resize(size);
for(u32 i = 0; i<size; ++i) {
for(u64 i = 0; i<size; ++i) {
upval.elems[i] = local[i];
}
}

View File

@@ -1,4 +1,4 @@
#include "bits_lib.h"
#include "natives/bits_lib.h"
namespace nasal {
@@ -41,18 +41,18 @@ var builtin_u32not(context* ctx, gc* ngc) {
}
var builtin_fld(context* ctx, gc* ngc) {
// bits.fld(s,0,3);
// bits.fld(s, 0, 3);
// if s stores 10100010(162)
// will get 101(5)
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::fld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("bits::fld", "\"startbit\",\"len\" must be number");
if (!startbit.is_num() || !length.is_num()) {
return nas_err("bits::fld", "\"startbit\", \"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
@@ -70,7 +70,7 @@ var builtin_fld(context* ctx, gc* ngc) {
}
var builtin_sfld(context* ctx, gc* ngc) {
// bits.sfld(s,0,3);
// bits.sfld(s, 0, 3);
// if s stores 10100010(162)
// will get 101(5) then this will be signed extended to
// 11111101(-3)
@@ -78,10 +78,10 @@ var builtin_sfld(context* ctx, gc* ngc) {
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::sfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
if (!startbit.is_num() || !length.is_num()) {
return nas_err("bits::sfld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
@@ -103,7 +103,7 @@ var builtin_sfld(context* ctx, gc* ngc) {
}
var builtin_setfld(context* ctx, gc* ngc) {
// bits.setfld(s,0,8,69);
// bits.setfld(s, 0, 8, 69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
@@ -112,10 +112,10 @@ var builtin_setfld(context* ctx, gc* ngc) {
auto startbit = local[2];
auto length = local[3];
auto value = local[4];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::setfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
if (!startbit.is_num() || !length.is_num() || !value.is_num()) {
return nas_err("bits::setfld",
"\"startbit\", \"len\", \"val\" must be number"
);
@@ -139,10 +139,10 @@ var builtin_setfld(context* ctx, gc* ngc) {
var builtin_buf(context* ctx, gc* ngc) {
var length = ctx->localr[1];
if (length.type!=vm_num || length.num()<=0) {
if (!length.is_num() || length.num()<=0) {
return nas_err("bits::buf", "\"len\" must be number greater than 0");
}
var str = ngc->alloc(vm_str);
var str = ngc->alloc(vm_type::vm_str);
auto& s = str.str();
s.resize(length.num(), '\0');
return str;

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
namespace nasal {

View File

@@ -1,8 +1,21 @@
#include "nasal_builtin.h"
#include "natives/builtin.h"
#include "util/util.h"
#include <chrono>
#ifdef _WIN32
#include <windows.h>
#endif
namespace nasal {
var builtin_unsafe(context* ctx, gc* ngc) {
return nas_err(
"unsafe_redirect",
"you are using unsafe system api under limited mode!"
);
}
var builtin_print(context* ctx, gc* ngc) {
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
@@ -33,8 +46,8 @@ var builtin_append(context* ctx, gc* ngc) {
auto local = ctx->localr;
var vec = local[1];
var elem = local[2];
if (vec.type!=vm_vec) {
return nas_err("append", "\"vec\" must be vector");
if (!vec.is_vec()) {
return nas_err("native::append", "\"vec\" must be vector");
}
auto& v = vec.vec().elems;
for(auto& i : elem.vec().elems) {
@@ -47,10 +60,10 @@ var builtin_setsize(context* ctx, gc* ngc) {
auto local = ctx->localr;
var vec = local[1];
var size = local[2];
if (vec.type!=vm_vec) {
return nas_err("setsize", "\"vec\" must be vector");
if (!vec.is_vec()) {
return nas_err("native::setsize", "\"vec\" must be vector");
}
if (size.type!=vm_num || size.num()<0) {
if (!size.is_num() || size.num()<0) {
return nil;
}
vec.vec().elems.resize(static_cast<i64>(size.num()), nil);
@@ -59,7 +72,7 @@ var builtin_setsize(context* ctx, gc* ngc) {
var builtin_system(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
if (!str.is_str()) {
return var::num(-1);
}
return var::num(static_cast<f64>(system(str.str().c_str())));
@@ -68,8 +81,8 @@ var builtin_system(context* ctx, gc* ngc) {
var builtin_input(context* ctx, gc* ngc) {
auto local = ctx->localr;
var end = local[1];
var ret = ngc->alloc(vm_str);
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
var ret = ngc->alloc(vm_type::vm_str);
if (!end.is_str() || end.str().length()>1 || !end.str().length()) {
std::cin >> ret.str();
} else {
std::getline(std::cin, ret.str(), end.str()[0]);
@@ -81,17 +94,17 @@ var builtin_split(context* ctx, gc* ngc) {
auto local = ctx->localr;
var delimeter = local[1];
var str = local[2];
if (delimeter.type!=vm_str) {
return nas_err("split", "\"separator\" must be string");
if (!delimeter.is_str()) {
return nas_err("native::split", "\"separator\" must be string");
}
if (str.type!=vm_str) {
return nas_err("split", "\"str\" must be string");
if (!str.is_str()) {
return nas_err("native::split", "\"str\" must be string");
}
const auto& deli = delimeter.str();
const auto& s = str.str();
// avoid being sweeped
auto res = ngc->temp = ngc->alloc(vm_vec);
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
auto& vec = res.vec().elems;
if (!deli.length()) {
@@ -119,10 +132,10 @@ var builtin_split(context* ctx, gc* ngc) {
var builtin_rand(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num && val.type!=vm_nil) {
return nas_err("rand", "\"seed\" must be nil or number");
if (!val.is_num() && !val.is_nil()) {
return nas_err("native::rand", "\"seed\" must be nil or number");
}
if (val.type==vm_num) {
if (val.is_num()) {
srand(static_cast<u32>(val.num()));
return nil;
}
@@ -137,7 +150,7 @@ var builtin_id(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
std::stringstream ss;
ss << "0";
if (val.type>vm_num) {
if (val.type>vm_type::vm_num) {
ss << "x" << std::hex;
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
}
@@ -146,7 +159,7 @@ var builtin_id(context* ctx, gc* ngc) {
var builtin_int(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num && val.type!=vm_str) {
if (!val.is_num() && !val.is_str()) {
return nil;
}
return var::num(static_cast<f64>(static_cast<i32>(val.to_num())));
@@ -164,10 +177,10 @@ var builtin_ceil(context* ctx, gc* ngc) {
var builtin_num(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type==vm_num) {
if (val.is_num()) {
return val;
}
if (val.type!=vm_str) {
if (!val.is_str()) {
return nil;
}
auto res = val.to_num();
@@ -179,8 +192,8 @@ var builtin_num(context* ctx, gc* ngc) {
var builtin_pop(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_vec) {
return nas_err("pop", "\"vec\" must be vector");
if (!val.is_vec()) {
return nas_err("native::pop", "\"vec\" must be vector");
}
auto& vec = val.vec().elems;
if (vec.size()) {
@@ -197,21 +210,23 @@ var builtin_str(context* ctx, gc* ngc) {
var builtin_size(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
f64 num = 0;
usize num = 0;
switch(val.type) {
case vm_num: num = val.num(); break;
case vm_str: num = val.str().length(); break;
case vm_vec: num = val.vec().size(); break;
case vm_hash: num = val.hash().size(); break;
case vm_map: num = val.map().mapper.size(); break;
case vm_type::vm_num: return val;
case vm_type::vm_str: num = val.str().length(); break;
case vm_type::vm_vec: num = val.vec().size(); break;
case vm_type::vm_hash: num = val.hash().size(); break;
case vm_type::vm_map: num = val.map().mapper.size(); break;
default: break;
}
return var::num(num);
return var::num(static_cast<f64>(num));
}
var builtin_time(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num) {
return nas_err("time", "\"begin\" must be number");
if (!val.is_num()) {
return nas_err("native::time", "\"begin\" must be number");
}
auto begin = static_cast<time_t>(val.num());
return var::num(static_cast<f64>(time(&begin)));
@@ -221,7 +236,7 @@ var builtin_contains(context* ctx, gc* ngc) {
auto local = ctx->localr;
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash || key.type!=vm_str) {
if (!hash.is_hash() || !key.is_str()) {
return zero;
}
return hash.hash().elems.count(key.str())? one:zero;
@@ -231,10 +246,10 @@ var builtin_delete(context* ctx, gc* ngc) {
auto local = ctx->localr;
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash) {
return nas_err("delete", "\"hash\" must be hash");
if (!hash.is_hash()) {
return nas_err("native::delete", "\"hash\" must be hash");
}
if (key.type!=vm_str) {
if (!key.is_str()) {
return nil;
}
if (hash.hash().elems.count(key.str())) {
@@ -245,13 +260,13 @@ var builtin_delete(context* ctx, gc* ngc) {
var builtin_keys(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
if (!hash.is_hash() && !hash.is_map()) {
return nas_err("native::keys", "\"hash\" must be hash");
}
// avoid being sweeped
auto res = ngc->temp = ngc->alloc(vm_vec);
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
if (hash.is_hash()) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc->newstr(iter.first));
}
@@ -265,7 +280,7 @@ var builtin_keys(context* ctx, gc* ngc) {
}
var builtin_die(context* ctx, gc* ngc) {
return nas_err("error", ctx->localr[1].to_str());
return nas_err("native::error", ctx->localr[1].to_str());
}
var builtin_find(context* ctx, gc* ngc) {
@@ -281,16 +296,17 @@ var builtin_find(context* ctx, gc* ngc) {
var builtin_type(context* ctx, gc* ngc) {
switch(ctx->localr[1].type) {
case vm_none: return ngc->newstr("undefined");
case vm_nil: return ngc->newstr("nil");
case vm_num: return ngc->newstr("num");
case vm_str: return ngc->newstr("str");
case vm_vec: return ngc->newstr("vec");
case vm_hash: return ngc->newstr("hash");
case vm_func: return ngc->newstr("func");
case vm_obj: return ngc->newstr("obj");
case vm_co: return ngc->newstr("coroutine");
case vm_map: return ngc->newstr("namespace");
case vm_type::vm_none: return ngc->newstr("undefined");
case vm_type::vm_nil: return ngc->newstr("nil");
case vm_type::vm_num: return ngc->newstr("num");
case vm_type::vm_str: return ngc->newstr("str");
case vm_type::vm_vec: return ngc->newstr("vec");
case vm_type::vm_hash: return ngc->newstr("hash");
case vm_type::vm_func: return ngc->newstr("func");
case vm_type::vm_ghost: return ngc->newstr("ghost");
case vm_type::vm_co: return ngc->newstr("coroutine");
case vm_type::vm_map: return ngc->newstr("namespace");
default: break;
}
return nil;
}
@@ -300,21 +316,21 @@ var builtin_substr(context* ctx, gc* ngc) {
var str = local[1];
var beg = local[2];
var len = local[3];
if (str.type!=vm_str) {
return nas_err("substr", "\"str\" must be string");
if (!str.is_str()) {
return nas_err("native::substr", "\"str\" must be string");
}
if (beg.type!=vm_num || beg.num()<0) {
return nas_err("substr", "\"begin\" should be number >= 0");
if (!beg.is_num() || beg.num()<0) {
return nas_err("native::substr", "\"begin\" should be number >= 0");
}
if (len.type!=vm_num || len.num()<0) {
return nas_err("substr", "\"length\" should be number >= 0");
if (!len.is_num() || len.num()<0) {
return nas_err("native::substr", "\"length\" should be number >= 0");
}
auto begin = static_cast<usize>(beg.num());
auto length = static_cast<usize>(len.num());
if (begin>=str.str().length()) {
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
return nas_err("native::susbtr", "begin index out of range: "+std::to_string(begin));
}
return ngc->newstr(str.str().substr(begin,length));
return ngc->newstr(str.str().substr(begin, length));
}
var builtin_streq(context* ctx, gc* ngc) {
@@ -322,7 +338,7 @@ var builtin_streq(context* ctx, gc* ngc) {
var a = local[1];
var b = local[2];
return var::num(static_cast<f64>(
(a.type!=vm_str || b.type!=vm_str)? 0:(a.str()==b.str())
(!a.is_str() || !b.is_str())? 0:(a.str()==b.str())
));
}
@@ -330,11 +346,12 @@ var builtin_left(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
return nas_err("left", "\"string\" must be string");
if (!str.is_str()) {
return nas_err("native::left", "\"string\" must be string");
}
if (len.type!=vm_num) {
return nas_err("left", "\"length\" must be number");
if (!len.is_num()) {
return nas_err("native::left", "\"length\" must be number");
}
if (len.num()<0) {
return ngc->newstr("");
@@ -346,20 +363,23 @@ var builtin_right(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
return nas_err("right", "\"string\" must be string");
if (!str.is_str()) {
return nas_err("native::right", "\"string\" must be string");
}
if (len.type!=vm_num) {
return nas_err("right", "\"length\" must be number");
if (!len.is_num()) {
return nas_err("native::right", "\"length\" must be number");
}
i32 length = static_cast<i32>(len.num());
i32 srclen = str.str().length();
i32 srclen = static_cast<i32>(str.str().length());
if (length>srclen) {
length = srclen;
}
if (length<0) {
length = 0;
}
return ngc->newstr(str.str().substr(srclen-length, srclen));
}
@@ -367,8 +387,8 @@ var builtin_cmp(context* ctx, gc* ngc) {
auto local = ctx->localr;
var a = local[1];
var b = local[2];
if (a.type!=vm_str || b.type!=vm_str) {
return nas_err("cmp", "\"a\" and \"b\" must be string");
if (!a.is_str() || !b.is_str()) {
return nas_err("native::cmp", "\"a\" and \"b\" must be string");
}
return var::num(static_cast<f64>(strcmp(
a.str().c_str(),
@@ -378,22 +398,22 @@ var builtin_cmp(context* ctx, gc* ngc) {
var builtin_chr(context* ctx, gc* ngc) {
const char* extend[] = {
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
"", " ", "", "ƒ", "", "", "", "",
"ˆ", "", "Š", "", "Œ", " ", "Ž", " ",
" ", "", "", "", "", "", "", "",
"˜", "", "š", "", "œ", " ", "ž", "Ÿ",
" ", "¡", "¢", "£", "¤", "¥", "¦", "§",
"¨", "©", "ª", "«", "¬", " ", "®", "¯",
"°", "±", "²", "³", "´", "µ", "", "·",
"¸", "¹", "º", "»", "¼", "½", "¾", "¿",
"À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç",
"È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï",
"Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×",
"Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß",
"à", "á", "â", "ã", "ä", "å", "æ", "ç",
"è", "é", "ê", "ë", "ì", "í", "î", "ï",
"ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷",
"ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ"
};
auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) {
@@ -410,12 +430,12 @@ var builtin_char(context* ctx, gc* ngc) {
var builtin_values(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("values", "\"hash\" must be hash or namespace");
if (!hash.is_hash() && !hash.is_map()) {
return nas_err("native::values", "\"hash\" must be hash or namespace");
}
auto vec = ngc->alloc(vm_vec);
auto vec = ngc->alloc(vm_type::vm_vec);
auto& v = vec.vec().elems;
if (hash.type==vm_hash) {
if (hash.is_hash()) {
for(auto& i : hash.hash().elems) {
v.push_back(i.second);
}
@@ -429,7 +449,7 @@ var builtin_values(context* ctx, gc* ngc) {
var builtin_sleep(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num) {
if (!val.is_num()) {
return nil;
}
#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS)
@@ -445,35 +465,11 @@ var builtin_sleep(context* ctx, gc* ngc) {
}
var builtin_platform(context* ctx, gc* ngc) {
if (is_windows()) {
return ngc->newstr("windows");
} else if (is_linux()) {
return ngc->newstr("linux");
} else if (is_macos()) {
return ngc->newstr("macOS");
}
return ngc->newstr("unknown");
return ngc->newstr(util::get_platform());
}
var builtin_arch(context* ctx, gc* ngc) {
if (is_x86()) {
return ngc->newstr("x86");
} else if (is_x86_64()) {
return ngc->newstr("x86-64");
} else if (is_amd64()) {
return ngc->newstr("amd64");
} else if (is_arm()) {
return ngc->newstr("arm");
} else if (is_aarch64()) {
return ngc->newstr("aarch64");
} else if (is_ia64()) {
return ngc->newstr("ia64");
} else if (is_powerpc()) {
return ngc->newstr("powerpc");
} else if (is_superh()) {
return ngc->newstr("superh");
}
return ngc->newstr("unknown");
return ngc->newstr(util::get_arch());
}
// md5 related functions
@@ -494,7 +490,7 @@ std::string md5(const std::string& src) {
std::vector<u32> buff;
usize num = ((src.length()+8)>>6)+1;
usize buffsize = num<<4;
buff.resize(buffsize,0);
buff.resize(buffsize, 0);
for(usize i = 0; i<src.length(); i++) {
buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3);
}
@@ -504,37 +500,45 @@ std::string md5(const std::string& src) {
// u32(abs(sin(i+1))*(2pow32))
const u32 k[] = {
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
// left shift bits
const u32 s[] = {
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};
// index
const u32 idx[] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16;
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16;
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16;
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // g=i
1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, // g=(5*i+1)%16;
5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, // g=(3*i+5)%16;
0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 // g=(7*i)%16;
};
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x,y,z) (((x)&(y))|((~x)&(z)))
#define md5g(x,y,z) (((x)&(z))|((y)&(~z)))
#define md5h(x,y,z) ((x)^(y)^(z))
#define md5i(x,y,z) ((y)^((x)|(~z)))
#define shift(x, n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x, y, z) (((x)&(y))|((~x)&(z)))
#define md5g(x, y, z) (((x)&(z))|((y)&(~z)))
#define md5h(x, y, z) ((x)^(y)^(z))
#define md5i(x, y, z) ((y)^((x)|(~z)))
u32 atmp = 0x67452301, btmp = 0xefcdab89;
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
@@ -548,7 +552,7 @@ std::string md5(const std::string& src) {
u32 tmp = d;
d = c;
c = b;
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]);
b = b+shift((a+f+k[j]+buff[i+idx[j]]), s[j]);
a = tmp;
}
atmp += a;
@@ -561,58 +565,125 @@ std::string md5(const std::string& src) {
var builtin_md5(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
return nas_err("md5", "\"str\" must be string");
if (!str.is_str()) {
return nas_err("native::md5", "\"str\" must be string");
}
return ngc->newstr(md5(str.str()));
}
var builtin_millisec(context* ctx, gc* ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return var::num(res);
class time_stamp {
private:
std::chrono::high_resolution_clock::time_point stamp;
public:
time_stamp() {
stamp = std::chrono::high_resolution_clock::now();
}
void make_stamp() {
stamp = std::chrono::high_resolution_clock::now();
}
auto elapsed_milliseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
}
auto elapsed_microseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
}
};
void time_stamp_destructor(void* ptr) {
delete static_cast<time_stamp*>(ptr);
}
var builtin_maketimestamp(context* ctx, gc* ngc) {
auto res = ngc->alloc(vm_type::vm_ghost);
res.ghost().set(
"nasal-time-stamp",
time_stamp_destructor,
nullptr,
new time_stamp
);
return res;
}
var builtin_time_stamp(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return nil;
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
stamp->make_stamp();
return nil;
}
var builtin_elapsed_millisecond(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return var::num(-1);
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(static_cast<f64>(stamp->elapsed_milliseconds()));
}
var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
auto object = ctx->localr[1];
if (!object.object_check("nasal-time-stamp")) {
return var::num(-1);
}
auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(static_cast<f64>(stamp->elapsed_microseconds()));
}
var builtin_gcextend(context* ctx, gc* ngc) {
auto type = ctx->localr[1];
if (type.type!=vm_str) {
if (!type.is_str()) {
return nil;
}
const auto& s = type.str();
if (s=="str") {
ngc->extend(vm_str);
ngc->extend(vm_type::vm_str);
} else if (s=="vec") {
ngc->extend(vm_vec);
ngc->extend(vm_type::vm_vec);
} else if (s=="hash") {
ngc->extend(vm_hash);
ngc->extend(vm_type::vm_hash);
} else if (s=="func") {
ngc->extend(vm_func);
ngc->extend(vm_type::vm_func);
} else if (s=="upval") {
ngc->extend(vm_upval);
} else if (s=="obj") {
ngc->extend(vm_obj);
ngc->extend(vm_type::vm_upval);
} else if (s=="ghost") {
ngc->extend(vm_type::vm_ghost);
} else if (s=="co") {
ngc->extend(vm_co);
ngc->extend(vm_type::vm_co);
}
return nil;
}
var builtin_gcinfo(context* ctx, gc* ngc) {
auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc->alloc(vm_hash);
const auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc->alloc(vm_type::vm_hash);
double total = 0;
f64 total = 0;
for(u32 i = 0; i<gc_type_size; ++i) {
total += ngc->gcnt[i];
total += static_cast<f64>(ngc->gcnt[i]);
}
// using ms
auto& map = res.hash().elems;
map["total"] = var::num(ngc->worktime*1.0/den*1000);
map["average"] = var::num(ngc->worktime*1.0/den*1000/total);
map["max_gc"] = var::num(ngc->max_time*1.0/den*1000);
map["max_mark"] = var::num(ngc->max_mark_time*1.0/den*1000);
map["max_sweep"] = var::num(ngc->max_sweep_time*1.0/den*1000);
const auto worktime = static_cast<f64>(ngc->worktime);
const auto max_time = static_cast<f64>(ngc->max_time);
const auto max_mark_time = static_cast<f64>(ngc->max_mark_time);
const auto max_sweep_time = static_cast<f64>(ngc->max_sweep_time);
// using ms
map["total"] = var::num(worktime/den*1000);
map["average"] = var::num(worktime/den*1000/total);
map["max_gc"] = var::num(max_time/den*1000);
map["max_mark"] = var::num(max_mark_time/den*1000);
map["max_sweep"] = var::num(max_sweep_time/den*1000);
return res;
}
@@ -621,7 +692,7 @@ var builtin_logtime(context* ctx, gc* ngc) {
tm* tm_t = localtime(&t);
char s[64];
snprintf(
s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d",
s, 64, "%d-%.2d-%.2d %.2d:%.2d:%.2d",
tm_t->tm_year+1900,
tm_t->tm_mon+1,
tm_t->tm_mday,
@@ -634,16 +705,33 @@ var builtin_logtime(context* ctx, gc* ngc) {
var builtin_ghosttype(context* ctx, gc* ngc) {
auto arg = ctx->localr[1];
if (arg.type!=vm_obj) {
return nas_err("ghosttype", "this is not a ghost object.");
if (!arg.is_ghost()) {
return nas_err("native::ghosttype", "this is not a ghost object.");
}
const auto& name = arg.ghost().get_ghost_name();
// https://wiki.flightgear.org/Nasal_library#ghosttype()
// tolds us if no name has been set,
// return a unique id (the pointer to the instance)
if (!name.length()) {
return var::num(reinterpret_cast<u64>(arg.ghost().pointer));
std::stringstream ss;
ss << "0x" << std::hex;
ss << reinterpret_cast<u64>(arg.ghost().pointer) << std::dec;
return ngc->newstr(ss.str());
}
return ngc->newstr(name);
}
var builtin_set_utf8_output(context* ctx, gc* ngc) {
#ifdef _WIN32
// allow 65001 code page
SetConsoleOutputCP(CP_UTF8);
#endif
// do nothing on other platforms
return nil;
}
nasal_builtin_table builtin[] = {
{"__print", builtin_print},
{"__println", builtin_println},
@@ -682,11 +770,15 @@ nasal_builtin_table builtin[] = {
{"__platform", builtin_platform},
{"__arch", builtin_arch},
{"__md5", builtin_md5},
{"__millisec", builtin_millisec},
{"__maketimestamp", builtin_maketimestamp},
{"__time_stamp", builtin_time_stamp},
{"__elapsed_millisecond", builtin_elapsed_millisecond},
{"__elapsed_microsecond", builtin_elapsed_microsecond},
{"__gcextd", builtin_gcextend},
{"__gcinfo", builtin_gcinfo},
{"__logtime", builtin_logtime},
{"__ghosttype", builtin_ghosttype},
{"__set_utf8_output", builtin_set_utf8_output},
{nullptr, nullptr}
};

View File

@@ -29,6 +29,7 @@
namespace nasal {
var builtin_unsafe(context*, gc*);
var builtin_print(context*, gc*);
var builtin_println(context*, gc*);
var builtin_exit(context*, gc*);
@@ -65,18 +66,26 @@ var builtin_values(context*, gc*);
var builtin_sleep(context*, gc*);
var builtin_platform(context*, gc*);
var builtin_arch(context*, gc*);
// md5 related functions
std::string tohex(u32);
std::string md5(const std::string&);
var builtin_md5(context*, gc*);
var builtin_millisec(context*, gc*);
var builtin_maketimestamp(context*, gc*);
var builtin_time_stamp(context*, gc*);
var builtin_elapsed_millisecond(context*, gc*);
var builtin_elapsed_microsecond(context*, gc*);
var builtin_gcextend(context*, gc*);
var builtin_gcinfo(context*, gc*);
var builtin_logtime(context*, gc*);
var builtin_ghosttype(context*, gc*);
// only useful on windows platform
var builtin_set_utf8_output(context*, gc*);
// register builtin function's name and it's address here in this table below
// this table must end with {nullptr,nullptr}
// this table must end with {nullptr, nullptr}
struct nasal_builtin_table {
const char* name;
var (*func)(context*, gc*);

View File

@@ -1,4 +1,4 @@
#include "coroutine.h"
#include "natives/coroutine.h"
namespace nasal {
@@ -18,7 +18,7 @@ var builtin_cocreate(context* ctx, gc* ngc) {
// +-------------+
// ```
auto coroutine_function = ctx->localr[1];
if (coroutine_function.type!=vm_func) {
if (!coroutine_function.is_func()) {
return nas_err(
"coroutine::create",
"must use a function to create coroutine"
@@ -30,7 +30,7 @@ var builtin_cocreate(context* ctx, gc* ngc) {
"cannot create another coroutine in a coroutine"
);
}
auto coroutine_object = ngc->alloc(vm_co);
auto coroutine_object = ngc->alloc(vm_type::vm_co);
auto& coroutine = coroutine_object.co();
coroutine.ctx.pc = coroutine_function.func().entry-1;
@@ -69,7 +69,7 @@ var builtin_coresume(context* ctx, gc* ngc) {
auto main_local_frame = ctx->localr;
auto coroutine_object = main_local_frame[1];
// return nil if is not a coroutine object or coroutine exited
if (coroutine_object.type!=vm_co ||
if (!coroutine_object.is_coroutine() ||
coroutine_object.co().status==nas_co::status::dead) {
return nil;
}
@@ -80,7 +80,7 @@ var builtin_coresume(context* ctx, gc* ngc) {
// fetch coroutine's stack top and return
// then coroutine's stack top will catch this return value
// so the coroutine's stack top in fact is not changed
if (ngc->running_context->top[0].type==vm_ret) {
if (ngc->running_context->top[0].is_ret()) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc->running_context->top[0];
}
@@ -114,7 +114,7 @@ var builtin_coyield(context* ctx, gc* ngc) {
var builtin_costatus(context* ctx, gc* ngc) {
auto coroutine_object = ctx->localr[1];
if (coroutine_object.type!=vm_co) {
if (!coroutine_object.is_coroutine()) {
return ngc->newstr("error");
}
switch(coroutine_object.co().status) {

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
namespace nasal {

View File

@@ -1,4 +1,4 @@
#include "dylib_lib.h"
#include "natives/dylib_lib.h"
namespace nasal {
@@ -15,7 +15,7 @@ void dynamic_library_destructor(void* pointer) {
var builtin_dlopen(context* ctx, gc* ngc) {
auto dlname = ctx->localr[1];
if (dlname.type!=vm_str) {
if (!dlname.is_str()) {
return nas_err("dylib::dlopen", "\"libname\" must be string");
}
@@ -42,11 +42,12 @@ var builtin_dlopen(context* ctx, gc* ngc) {
"cannot open dynamic lib <" + dlname.str() + ">"
);
}
auto return_hash = ngc->temp = ngc->alloc(vm_hash);
auto library_object = ngc->alloc(vm_obj);
auto return_hash = ngc->temp = ngc->alloc(vm_type::vm_hash);
auto library_object = ngc->alloc(vm_type::vm_ghost);
library_object.ghost().set(
dynamic_library_type_name,
dynamic_library_destructor,
nullptr,
dynamic_library_pointer
);
return_hash.hash().elems["lib"] = library_object;
@@ -72,10 +73,11 @@ var builtin_dlopen(context* ctx, gc* ngc) {
}
for(u32 i = 0; table[i].name; ++i) {
auto function_pointer = reinterpret_cast<void*>(table[i].fd);
auto function_object = ngc->alloc(vm_obj);
auto function_object = ngc->alloc(vm_type::vm_ghost);
function_object.ghost().set(
function_address_type_name,
nullptr,
nullptr,
function_pointer
);
return_hash.hash().elems[table[i].name] = function_object;

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
#ifdef _WIN32
#include <windows.h>

View File

@@ -1,4 +1,4 @@
#include "fg_props.h"
#include "natives/fg_props.h"
#include <fstream>
@@ -8,7 +8,7 @@ var builtin_logprint(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto level = local[1];
auto elems = local[2];
if (elems.type!=vm_vec) {
if (!elems.is_vec()) {
return nas_err("fg_env::logprint", "received argument is not vector.");
}
std::ofstream out("fgfs.log", std::ios::app);

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
namespace nasal {

View File

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

View File

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

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

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

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

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

View File

@@ -1,11 +1,11 @@
#include "math_lib.h"
#include "natives/math_lib.h"
namespace nasal {
var builtin_pow(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (x.type!=vm_num || y.type!=vm_num) {
if (!x.is_num() || !y.is_num()) {
return var::num(std::nan(""));
}
return var::num(std::pow(x.num(), y.num()));
@@ -13,43 +13,43 @@ var builtin_pow(context* ctx, gc* ngc) {
var builtin_sin(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
return var::num(val.is_num()? sin(val.num()):std::nan(""));
}
var builtin_cos(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
return var::num(val.is_num()? cos(val.num()):std::nan(""));
}
var builtin_tan(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
return var::num(val.is_num()? tan(val.num()):std::nan(""));
}
var builtin_exp(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
return var::num(val.is_num()? exp(val.num()):std::nan(""));
}
var builtin_lg(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
return var::num(val.is_num()? log(val.num())/log(10.0):std::nan(""));
}
var builtin_ln(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
return var::num(val.is_num()? log(val.num()):std::nan(""));
}
var builtin_sqrt(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
return var::num(val.is_num()? sqrt(val.num()):std::nan(""));
}
var builtin_atan2(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (x.type!=vm_num || y.type!=vm_num) {
if (!x.is_num() || !y.is_num()) {
return var::num(std::nan(""));
}
return var::num(atan2(y.num(), x.num()));
@@ -57,7 +57,7 @@ var builtin_atan2(context* ctx, gc* ngc) {
var builtin_isnan(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
return (x.type==vm_num && std::isnan(x.num()))? one:zero;
return (x.is_num() && std::isnan(x.num()))? one:zero;
}
nasal_builtin_table math_lib_native[] = {

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
namespace nasal {

99
src/natives/regex_lib.cpp Normal file
View File

@@ -0,0 +1,99 @@
#include "natives/regex_lib.h"
namespace nasal {
var builtin_regex_match(context* ctx, gc* ngc) {
auto source = ctx->localr[1];
auto reg_str = ctx->localr[2];
if (!source.is_str()) {
return nas_err("regex::match", "\"src\" must be a string");
}
if (!reg_str.is_str()) {
return nas_err("regex::match", "\"reg\" must be a format string");
}
try {
auto res = std::regex_match(source.str(), std::regex(reg_str.str()));
return res? one:zero;
} catch(const std::regex_error& e) {
return nas_err("regex::match", e.what());
}
return zero;
}
var builtin_regex_search(context* ctx, gc* ngc) {
auto source = ctx->localr[1];
auto reg_str = ctx->localr[2];
if (!source.is_str()) {
return nas_err("regex::search", "\"src\" must be a string");
}
if (!reg_str.is_str()) {
return nas_err("regex::search", "\"reg\" must be a format string");
}
try {
auto res = std::regex_search(source.str(), std::regex(reg_str.str()));
return res? one:zero;
} catch(const std::regex_error& e) {
return nas_err("regex::search", e.what());
}
return nil;
}
var builtin_regex_replace(context* ctx, gc* ngc) {
auto source = ctx->localr[1];
auto reg_str = ctx->localr[2];
auto fmt = ctx->localr[3];
if (!source.is_str()) {
return nas_err("regex::replace", "\"src\" must be a string");
}
if (!reg_str.is_str()) {
return nas_err("regex::replace", "\"reg\" must be a format string");
}
if (!fmt.is_str()) {
return nas_err("regex::replace", "\"fmt\" must be a format string");
}
try {
auto res = std::regex_replace(
source.str(),
std::regex(reg_str.str()),
fmt.str()
);
return ngc->newstr(res);
} catch(const std::regex_error& e) {
return nas_err("regex::replace", e.what());
}
return ngc->newstr(source.str());
}
var builtin_regex_match_all(context* ctx, gc* ngc) {
auto source = ctx->localr[1];
auto reg_str = ctx->localr[2];
if (!source.is_str()) {
return nas_err("regex::match_all", "\"src\" must be a string");
}
if (!reg_str.is_str()) {
return nas_err("regex::match_all", "\"reg\" must be a format string");
}
auto res = ngc->temp = ngc->alloc(vm_type::vm_vec);
try {
const auto& src = source.str();
auto words_regex = std::regex(reg_str.str());
auto begin = std::sregex_iterator(src.begin(), src.end(), words_regex);
auto end = std::sregex_iterator();
for (auto i = begin; i!=end; ++i) {
res.vec().elems.push_back(ngc->newstr((*i).str()));
}
} catch(const std::regex_error& e) {
return nas_err("regex::match_all", e.what());
}
return res;
}
nasal_builtin_table regex_lib_native[] = {
{"__regex_match", builtin_regex_match},
{"__regex_search", builtin_regex_search},
{"__regex_replace", builtin_regex_replace},
{"__regex_match_all", builtin_regex_match_all},
{nullptr, nullptr}
};
}

18
src/natives/regex_lib.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
#include <regex>
#include "nasal.h"
#include "nasal_gc.h"
#include "natives/builtin.h"
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*);
extern nasal_builtin_table regex_lib_native[];
}

View File

@@ -1,4 +1,4 @@
#include "unix_lib.h"
#include "natives/unix_lib.h"
namespace nasal {
@@ -15,7 +15,7 @@ void dir_entry_destructor(void* ptr) {
var builtin_pipe(context* ctx, gc* ngc) {
#ifndef _WIN32
i32 fd[2];
var res = ngc->alloc(vm_vec);
var res = ngc->alloc(vm_type::vm_vec);
if (pipe(fd)==-1) {
return nas_err("unix::pipe", "failed to create pipe");
}
@@ -28,7 +28,7 @@ var builtin_pipe(context* ctx, gc* ngc) {
var builtin_fork(context* ctx, gc* ngc) {
#ifndef _WIN32
f64 res=fork();
f64 res = fork();
if (res<0) {
return nas_err("unix::fork", "failed to fork a process");
}
@@ -40,13 +40,13 @@ var builtin_fork(context* ctx, gc* ngc) {
var builtin_waitpid(context* ctx, gc* ngc) {
auto pid = ctx->localr[1];
auto nohang = ctx->localr[2];
if (pid.type!=vm_num || nohang.type!=vm_num) {
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_vec);
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;
@@ -56,7 +56,7 @@ var builtin_waitpid(context* ctx, gc* ngc) {
var builtin_opendir(context* ctx, gc* ngc) {
auto path = ctx->localr[1];
if (path.type!=vm_str) {
if (!path.is_str()) {
return nas_err("unix::opendir", "\"path\" must be string");
}
#ifdef _MSC_VER
@@ -72,8 +72,8 @@ var builtin_opendir(context* ctx, gc* ngc) {
return nas_err("unix::opendir", "cannot open dir <"+path.str()+">");
}
#endif
var ret = ngc->alloc(vm_obj);
ret.ghost().set(dir_type_name, dir_entry_destructor, p);
var ret = ngc->alloc(vm_type::vm_ghost);
ret.ghost().set(dir_type_name, dir_entry_destructor, nullptr, p);
return ret;
}
@@ -105,14 +105,14 @@ var builtin_closedir(context* ctx, gc* ngc) {
var builtin_chdir(context* ctx, gc* ngc) {
auto path = ctx->localr[1];
if (path.type!=vm_str) {
if (!path.is_str()) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(chdir(path.str().c_str())));
}
var builtin_environ(context* ctx, gc* ngc) {
var res = ngc->temp = ngc->alloc(vm_vec);
var res = ngc->temp = ngc->alloc(vm_type::vm_vec);
auto& vec = res.vec().elems;
for(char** env = environ; *env; ++env) {
vec.push_back(ngc->newstr(*env));
@@ -131,7 +131,7 @@ var builtin_getcwd(context* ctx, gc* ngc) {
var builtin_getenv(context* ctx, gc* ngc) {
auto envvar = ctx->localr[1];
if (envvar.type!=vm_str) {
if (!envvar.is_str()) {
return nas_err("unix::getenv", "\"envvar\" must be string");
}
char* res = getenv(envvar.str().c_str());

View File

@@ -2,7 +2,7 @@
#include "nasal.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "natives/builtin.h"
#ifndef _MSC_VER
#include <unistd.h>

View File

@@ -6,7 +6,7 @@ void optimizer::const_string(
binary_operator* node,
string_literal* left_node,
string_literal* right_node) {
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
if (node->get_operator_type()!=binary_operator::kind::concat) {
return;
}
const auto& left = left_node->get_content();
@@ -24,19 +24,19 @@ void optimizer::const_number(
const auto right = right_node->get_number();
f64 res;
switch(node->get_operator_type()) {
case binary_operator::binary_type::add: res = left+right; break;
case binary_operator::binary_type::sub: res = left-right; break;
case binary_operator::binary_type::mult: res = left*right; break;
case binary_operator::binary_type::div: res = left/right; break;
case binary_operator::binary_type::less: res = left<right; break;
case binary_operator::binary_type::leq: res = left<=right; break;
case binary_operator::binary_type::grt: res = left>right; break;
case binary_operator::binary_type::geq: res = left>=right; break;
case binary_operator::binary_type::bitwise_or:
case binary_operator::kind::add: res = left+right; break;
case binary_operator::kind::sub: res = left-right; break;
case binary_operator::kind::mult: res = left*right; break;
case binary_operator::kind::div: res = left/right; break;
case binary_operator::kind::less: res = left<right; break;
case binary_operator::kind::leq: res = left<=right; break;
case binary_operator::kind::grt: res = left>right; break;
case binary_operator::kind::geq: res = left>=right; break;
case binary_operator::kind::bitwise_or:
res = static_cast<i32>(left)|static_cast<i32>(right); break;
case binary_operator::binary_type::bitwise_xor:
case binary_operator::kind::bitwise_xor:
res = static_cast<i32>(left)^static_cast<i32>(right); break;
case binary_operator::binary_type::bitwise_and:
case binary_operator::kind::bitwise_and:
res = static_cast<i32>(left)&static_cast<i32>(right); break;
default: return;
}
@@ -53,11 +53,11 @@ void optimizer::const_number(
number_literal* value_node) {
auto res = value_node->get_number();
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative:
case unary_operator::kind::negative:
res = -res; break;
case unary_operator::unary_type::bitwise_not:
case unary_operator::kind::bitwise_not:
res = ~static_cast<i32>(res); break;
case unary_operator::unary_type::logical_not:
case unary_operator::kind::logical_not:
res = !res; break;
}
if (std::isinf(res) || std::isnan(res)) {

View File

@@ -7,6 +7,7 @@
namespace nasal {
// experimental optimizer for constant calculation
class optimizer: public ast_visitor {
private:
void const_string(binary_operator*, string_literal*, string_literal*);

View File

@@ -19,10 +19,10 @@ void repl::add_command_history(const std::string& history) {
}
}
std::string repl::readline(std::string prompt = ">>> ") {
std::string repl::readline(const std::string& prompt = ">>> ") {
auto line = std::string("");
std::cout << prompt;
std::getline(std::cin, line,'\n');
std::getline(std::cin, line, '\n');
return line;
}
@@ -37,7 +37,7 @@ void repl::update_temp_file() {
bool repl::check_need_more_input() {
while(true) {
update_temp_file();
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
auto nasal_lexer = std::make_unique<lexer>();
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
return false;
}
@@ -47,12 +47,12 @@ bool repl::check_need_more_input() {
i64 in_brace = 0;
for(const auto& t : nasal_lexer->result()) {
switch(t.type) {
case tok::lcurve: ++in_curve; break;
case tok::rcurve: --in_curve; break;
case tok::lbracket: ++in_bracket; break;
case tok::rbracket: --in_bracket; break;
case tok::lbrace: ++in_brace; break;
case tok::rbrace: --in_brace; break;
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;
}
}
@@ -77,11 +77,11 @@ void repl::help() {
}
bool repl::run() {
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
auto nasal_parser = std::unique_ptr<parse>(new parse);
auto nasal_linker = std::unique_ptr<linker>(new linker);
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer);
auto nasal_codegen = std::unique_ptr<codegen>(new codegen);
auto nasal_lexer = std::make_unique<lexer>();
auto nasal_parser = std::make_unique<parse>();
auto nasal_linker = std::make_unique<linker>();
auto nasal_opt = std::make_unique<optimizer>();
auto nasal_codegen = std::make_unique<codegen>();
update_temp_file();
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
@@ -92,12 +92,12 @@ bool repl::run() {
return false;
}
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
if (nasal_linker->link(*nasal_parser, true).geterr()) {
return false;
}
nasal_opt->do_optimization(nasal_parser->tree());
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true).geterr()) {
if (nasal_codegen->compile(*nasal_parser, *nasal_linker, true, false).geterr()) {
return false;
}
@@ -120,7 +120,7 @@ void repl::execute() {
std::cout << "[nasal-repl] Initialization complete.\n\n";
// finish initialization, output version info
std::cout << "Nasal REPL interpreter version " << __nasver;
std::cout << "Nasal REPL interpreter version " << __nasver__;
std::cout << " (" << __DATE__ << " " << __TIME__ << ")\n";
help();

View File

@@ -15,7 +15,7 @@ namespace repl {
struct info {
bool in_repl_mode = false;
std::string repl_file_name = "<nasal-repl>";
const std::string repl_file_name = "<nasal-repl>";
std::string repl_file_source = "";
// singleton
@@ -33,7 +33,7 @@ private:
private:
void add_command_history(const std::string&);
std::string readline(std::string);
std::string readline(const std::string&);
bool check_need_more_input();
void update_temp_file();
void help();

View File

@@ -4,11 +4,15 @@ namespace nasal {
bool symbol_finder::visit_definition_expr(definition_expr* node) {
if (node->get_variable_name()) {
// single variable definition
// example: var a = 1;
symbols.push_back({
node->get_variable_name()->get_name(),
node->get_variable_name()->get_location()
});
} 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(),
@@ -25,6 +29,7 @@ bool symbol_finder::visit_definition_expr(definition_expr* node) {
}
bool symbol_finder::visit_function(function* node) {
// do not scan variables defined in function inside this code block
return true;
}

43
src/util/fs.cpp Normal file
View File

@@ -0,0 +1,43 @@
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <io.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4996)
#endif
#include <sys/stat.h>
#include "util/util.h"
#include "util/fs.h"
#include "nasal.h"
namespace nasal::fs {
path& path::operator/(const path& another) {
this->file_system_path += util::is_windows()? "\\":"/";
this->file_system_path += another.file_system_path;
return *this;
}
bool exists(const path& file_path) {
#ifdef _MSC_VER
#define F_OK 0 // fuck msc
#endif
return access(file_path.c_str(), F_OK)==0;
}
bool is_regular(const path& file_path) {
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
struct stat buffer;
if (stat(file_path.c_str(), &buffer)!=0) {
return false;
}
return S_ISREG(buffer.st_mode);
}
}

27
src/util/fs.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <cstring>
#include <sstream>
namespace nasal::fs {
class path {
private:
std::string file_system_path;
public:
path(const path&) = default;
path(const std::string& file_path): file_system_path(file_path) {}
path& operator/(const path&);
const char* c_str() const {
return file_system_path.c_str();
}
const std::string& str() const {
return file_system_path;
}
};
bool exists(const path&);
bool is_regular(const path&);
}

View File

@@ -1,6 +1,8 @@
#include "nasal.h"
#include "util/util.h"
namespace nasal {
#include <cmath>
namespace nasal::util {
bool is_windows() {
#if defined(_WIN32) || defined(_WIN64)
@@ -86,7 +88,89 @@ bool is_superh() {
#endif
}
f64 hex2f(const char* str) {
const char* get_platform() {
if (is_windows()) {
return "windows";
} else if (is_linux()) {
return "linux";
} else if (is_macos()) {
return "macOS";
}
return "unknown";
}
const char* get_arch() {
if (is_x86()) {
return "x86";
} else if (is_x86_64()) {
return "x86-64";
} else if (is_amd64()) {
return "amd64";
} else if (is_arm()) {
return "arm";
} else if (is_aarch64()) {
return "aarch64";
} else if (is_ia64()) {
return "ia64";
} else if (is_powerpc()) {
return "powerpc";
} else if (is_superh()) {
return "superh";
}
return "unknown";
}
u32 utf8_hdchk(const char head) {
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const auto c = static_cast<u8>(head);
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
std::string char_to_hex(const char c) {
const char hextbl[] = "0123456789abcdef";
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
}
std::string rawstr(const std::string& str, const usize maxlen) {
std::string ret("");
for(auto i : str) {
// windows doesn't output unicode normally, so we output the hex
if (util::is_windows() && i<=0) {
ret += "\\x" + char_to_hex(i);
continue;
}
switch(i) {
case '\0': ret += "\\0"; break;
case '\a': ret += "\\a"; break;
case '\b': ret += "\\b"; break;
case '\t': ret += "\\t"; break;
case '\n': ret += "\\n"; break;
case '\v': ret += "\\v"; break;
case '\f': ret += "\\f"; break;
case '\r': ret += "\\r"; break;
case '\033':ret += "\\e"; break;
case '\"': ret += "\\\""; break;
case '\'': ret += "\\\'"; break;
case '\\': ret += "\\\\"; break;
default: ret += i; break;
}
}
if (maxlen && ret.length()>maxlen) {
ret = ret.substr(0, maxlen)+"...";
}
return ret;
}
f64 hex_to_f64(const char* str) {
f64 ret = 0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
@@ -102,7 +186,7 @@ f64 hex2f(const char* str) {
return ret;
}
f64 oct2f(const char* str) {
f64 oct_to_f64(const char* str) {
f64 ret = 0;
while('0'<=*str && *str<'8') {
ret = ret*8+(*str++-'0');
@@ -119,7 +203,7 @@ f64 oct2f(const char* str) {
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char* str) {
f64 dec_to_f64(const char* str) {
f64 ret = 0, num_pow = 0;
bool negative = false;
while('0'<=*str && *str<='9') {
@@ -165,7 +249,7 @@ f64 dec2f(const char* str) {
ret*std::pow(10, num_pow-1)*10;
}
f64 str2num(const char* str) {
f64 str_to_num(const char* str) {
bool negative = false;
f64 res = 0;
if (*str=='-' || *str=='+') {
@@ -175,63 +259,13 @@ f64 str2num(const char* str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res = hex2f(str+2);
res = hex_to_f64(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res = oct2f(str+2);
res = oct_to_f64(str+2);
} else {
res = dec2f(str);
res = dec_to_f64(str);
}
return negative? -res:res;
}
i32 utf8_hdchk(const char head) {
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const auto c = static_cast<u8>(head);
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
std::string chrhex(const char c) {
const char hextbl[] = "0123456789abcdef";
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
}
std::string rawstr(const std::string& str, const usize maxlen) {
std::string ret("");
for(auto i : str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
ret += "\\x"+chrhex(i);
continue;
}
switch(i) {
case '\0': ret += "\\0"; break;
case '\a': ret += "\\a"; break;
case '\b': ret += "\\b"; break;
case '\t': ret += "\\t"; break;
case '\n': ret += "\\n"; break;
case '\v': ret += "\\v"; break;
case '\f': ret += "\\f"; break;
case '\r': ret += "\\r"; break;
case '\033':ret += "\\e"; break;
case '\"': ret += "\\\""; break;
case '\'': ret += "\\\'"; break;
case '\\': ret += "\\\\"; break;
default: ret += i; break;
}
}
if (maxlen && ret.length()>maxlen) {
ret = ret.substr(0, maxlen)+"...";
}
return ret;
}
}
}

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

@@ -0,0 +1,73 @@
#pragma once
#include "nasal.h"
#ifdef _WIN32
#include <windows.h>
#endif
#include <cstring>
#include <sstream>
namespace nasal::util {
bool is_windows();
bool is_linux();
bool is_macos();
bool is_x86();
bool is_amd64();
bool is_x86_64();
bool is_arm();
bool is_aarch64();
bool is_ia64();
bool is_powerpc();
bool is_superh();
const char* get_platform();
const char* get_arch();
u32 utf8_hdchk(const char);
std::string char_to_hex(const char);
std::string rawstr(const std::string&, const usize maxlen = 0);
f64 hex_to_f64(const char*);
f64 oct_to_f64(const char*);
// we have the same reason not using atof here
// just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec_to_f64(const char*);
f64 str_to_num(const char*);
class windows_code_page_manager {
private:
u32 code_page;
public:
windows_code_page_manager() {
#ifdef _WIN32
code_page = GetConsoleOutputCP();
#endif
}
void set_utf8_output() {
#ifdef _WIN32
// store previous code page
code_page = GetConsoleOutputCP();
// allow 65001 code page
SetConsoleOutputCP(CP_UTF8);
#endif
}
void restore_code_page() {
#ifdef _WIN32
// restore previous code page
SetConsoleOutputCP(code_page);
#endif
}
};
}

172
std/argparse.nas Normal file
View File

@@ -0,0 +1,172 @@
# argparse.nas
# 2023/12/7 by ValKmjolnir
use std.padding;
var new = func(description) {
var _arg = globals.arg;
var parser = {
description: description,
subparser: [],
command_list: [],
add_command: func(long, short, help, need_arg = false, need_nargs = false) {
return _add_command(parser, long, short, help, need_arg, need_nargs);
},
add_subparser: func(name, help) {
return _add_subparser(parser, name, help);
},
parse_args: func() {
var result_hash = {};
_parse(parser, _arg, result_hash);
return result_hash;
}
};
parser.add_command("--help", "-h", "Get help info and exit");
return parser;
}
var _new_sub_parser = func(description) {
var parser = {
description: description,
subparser: [],
command_list: [],
add_command: func(long, short, help, need_arg = false, need_nargs = false) {
return _add_command(parser, long, short, help, need_arg, need_nargs);
},
add_subparser: func(name, help) {
return _add_subparser(parser, name, help);
}
};
parser.add_command("--help", "-h", "Get help info and exit");
return parser;
}
var _help = func(parser) {
println(parser.description, "\n");
if (size(parser.subparser)>0) {
println("Subcommand:");
var max_pad_length = 0;
var info_pairs = [];
foreach(var cmd; parser.subparser) {
var info = " "~cmd.name;
append(info_pairs, {info: info, help: cmd.parser.description});
info_length = size(info);
max_pad_length = max_pad_length>info_length? max_pad_length:info_length;
}
foreach(var pair; info_pairs) {
println(padding.rightpad(pair.info, max_pad_length), " ", pair.help);
}
println();
}
if (size(parser.command_list)>0) {
println("Options:");
var max_pad_length = 0;
var info_pairs = [];
foreach(var cmd; parser.command_list) {
if (cmd.need_nargs) {
var info = " "~cmd.full_name~" [args...] "~cmd.short_name~" [args...]";
append(info_pairs, {info: info, help: cmd.help});
} elsif (cmd.need_arg) {
var info = " "~cmd.full_name~" arg "~cmd.short_name~" arg";
append(info_pairs, {info: info, help: cmd.help});
} else {
var info = " "~cmd.full_name~" "~cmd.short_name;
append(info_pairs, {info: info, help: cmd.help});
}
var info_length = size(info);
max_pad_length = max_pad_length>info_length? max_pad_length:info_length;
}
foreach(var pair; info_pairs) {
println(padding.rightpad(pair.info, max_pad_length), " ", pair.help);
}
}
}
var _in_list = func(arginfo, command_list) {
foreach(var cmd; command_list) {
if (arginfo==cmd.full_name or arginfo==cmd.short_name) {
return true;
}
}
return false;
}
var _parse = func(parser, args, result_hash) {
if (size(args)==0) {
println("Require more command, use \"--help\" to get help.\n");
_help(parser);
exit(0);
}
foreach(var subparser; parser.subparser) {
if (subparser.name==args[0]) {
result_hash[subparser.name] = true;
_parse(subparser.parser, size(args)>1? args[1:]:[], result_hash);
return;
}
}
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) {
if (this_arg=="--help" or this_arg=="-h") {
_help(parser);
exit(0);
}
if (this_arg==cmd.full_name or this_arg==cmd.short_name) {
find_command_flag = true;
if (cmd.need_nargs) {
i += 1;
var args_collect = [];
while (i<size(args) and !_in_list(args[i], parser.command_list)) {
append(args_collect, args[i]);
i += 1;
}
i -= 1;
if (!size(args_collect)) {
println("Require argument(s) after command `", this_arg, "`.\n");
_help(parser);
exit(0);
}
result_hash[cmd.full_name] = args_collect;
} elsif (cmd.need_arg) {
i += 1;
if (i<size(args) and !_in_list(args[i], parser.command_list)) {
result_hash[cmd.full_name] = args[i];
} else {
println("Require argument after command `", this_arg, "`.\n");
_help(parser);
exit(0);
}
} else {
result_hash[cmd.full_name] = true;
}
}
}
if (!find_command_flag) {
println("Invalid command `", this_arg, "`.\n");
_help(parser);
exit(0);
}
}
return;
}
var _add_command = func(parser, long, short, help, need_arg , need_nargs) {
var new_command = {
full_name: long,
short_name: short,
help: help,
need_arg: need_arg,
need_nargs: need_nargs
};
append(parser.command_list, new_command);
}
var _add_subparser = func(parser, name, help) {
var new_subparser = {
name: name,
parser: _new_sub_parser(help)
};
append(parser.subparser, new_subparser);
return new_subparser.parser;
}

View File

@@ -1,16 +1,18 @@
# lib csv.nas
# ValKmjolnir 2022/10/15
var read = func(path, delimeter=",", endline="\n"){
use std.io;
var read = func(path, delimeter=",", endline="\n") {
var context = io.readfile(path);
context = split(endline, context);
forindex(var i;context){
forindex(var i;context) {
context[i] = split(delimeter,context[i]);
}
if(size(context)<=1){
if (size(context)<=1) {
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
}
return {
property: context[0],
data: context[1:]
};
}
}

View File

@@ -2,12 +2,15 @@
# 2023 by ValKmjolnir
# dylib is the core hashmap for developers to load their own library.
# for safe using dynamic library, you could use 'module' in stl/module.nas
use std.os;
use std.io;
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))
if (io.exists(libname))
return __dlopen(libname);
# find dynamic lib through PATH
var envpath = split(os.platform()=="windows"? ";":":",unix.getenv("PATH"));
@@ -16,7 +19,7 @@ var dlopen = func(libname) {
var path = os.platform()=="windows"? "\\module\\":"/module/";
foreach(var p;envpath) {
p ~= path~libname;
if(io.exists(p)) {
if (io.exists(p)) {
libname = p;
break;
}
@@ -37,14 +40,14 @@ var dlcall = func(ptr, args...) {
# get dlcall function with limited parameter list
var limitcall = func(arg_size = 0) {
if(arg_size==0) {return func(ptr) {return __dlcall};}
elsif(arg_size==1) {return func(ptr, _0) {return __dlcall};}
elsif(arg_size==2) {return func(ptr, _0, _1) {return __dlcall};}
elsif(arg_size==3) {return func(ptr, _0, _1, _2) {return __dlcall};}
elsif(arg_size==4) {return func(ptr, _0, _1, _2, _3) {return __dlcall};}
elsif(arg_size==5) {return func(ptr, _0, _1, _2, _3, _4) {return __dlcall};}
elsif(arg_size==6) {return func(ptr, _0, _1, _2, _3, _4, _5) {return __dlcall};}
elsif(arg_size==7) {return func(ptr, _0, _1, _2, _3, _4, _5, _6) {return __dlcall};}
elsif(arg_size==8) {return func(ptr, _0, _1, _2, _3, _4, _5, _6, _7) {return __dlcall};}
if (arg_size==0) {return func(ptr) {return __dlcall};}
elsif (arg_size==1) {return func(ptr, _0) {return __dlcall};}
elsif (arg_size==2) {return func(ptr, _0, _1) {return __dlcall};}
elsif (arg_size==3) {return func(ptr, _0, _1, _2) {return __dlcall};}
elsif (arg_size==4) {return func(ptr, _0, _1, _2, _3) {return __dlcall};}
elsif (arg_size==5) {return func(ptr, _0, _1, _2, _3, _4) {return __dlcall};}
elsif (arg_size==6) {return func(ptr, _0, _1, _2, _3, _4, _5) {return __dlcall};}
elsif (arg_size==7) {return func(ptr, _0, _1, _2, _3, _4, _5, _6) {return __dlcall};}
elsif (arg_size==8) {return func(ptr, _0, _1, _2, _3, _4, _5, _6, _7) {return __dlcall};}
else {return func(ptr, args...) {return __dlcallv};}
}

View File

@@ -1,12 +1,14 @@
# flightgear developer environments simulator (beta)
# ValKmjolnir 2022
use std.runtime;
use std.coroutine;
use std.os;
println("-------------------------------------------------------------");
println(" FlightGear simulated-env for developers project, since 2019");
println(" Developed by:");
println(" Sidi Liang (FGPRC-0762)");
println(" Haokun Lee (FGPRC-0818 aka ValKmjolnir)");
println(" Sidi762 (FGPRC-0762)");
println(" ValKmjolnir (FGPRC-0818)");
println("-------------------------------------------------------------");
println(" See help using command line argument: --fg-env-help");
println("-------------------------------------------------------------");
@@ -15,8 +17,8 @@ var fg_env_cli={
"--fg-env-help":{
info:"get help",
trigger:0,
f:func{
if(me.trigger)
f:func {
if (me.trigger)
return;
println("-------------------------------------------------------------");
println(" Help:");
@@ -29,8 +31,8 @@ var fg_env_cli={
"--fg-env-debug":{
info:"get property tree structure",
trigger:0,
f:func{
if(me.trigger)
f:func {
if (me.trigger)
return;
props.globals.debug();
me.trigger=1;
@@ -39,8 +41,8 @@ var fg_env_cli={
"--fg-env-mktmtest":{
info:"test maketimer",
trigger:0,
f:func{
if(me.trigger)
f:func {
if (me.trigger)
return;
maketimer_multi_coroutine_test(32);
me.trigger=1;
@@ -57,8 +59,8 @@ var fg_globals={
};
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func add_event(name,interval,function)");
var add_event=func(name,interval,function){
fg_globals.event[name]=coroutine.create(func{
var add_event = func(name,interval,function) {
fg_globals.event[name]=coroutine.create(func {
var timestamp=maketimestamp();
timestamp.stamp();
while(timestamp.elapsedMSec()<interval*1000)
@@ -69,11 +71,11 @@ var add_event=func(name,interval,function){
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func add_task(name,interval,function)");
var add_task=func(name,interval,function){
fg_globals.task[name]=coroutine.create(func{
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)
@@ -86,38 +88,38 @@ var add_task=func(name,interval,function){
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func remove_task(name)");
var remove_task=func(name){
if(contains(fg_globals.task,name))
var remove_task = func(name) {
if (contains(fg_globals.task,name))
delete(fg_globals.task,name);
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func remove_event(name)");
var remove_event=func(name){
if(contains(fg_globals.event,name))
var remove_event = func(name) {
if (contains(fg_globals.event,name))
delete(fg_globals.event,name);
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func maketimer(interval,function)");
var maketimer=func(interval,function){
var maketimer = func(interval,function) {
var name="nasal-timer-";
var res={
start:func{
if(me.isRunning)
start:func {
if (me.isRunning)
return;
me.isRunning=1;
if(me.singleShot){
if (me.singleShot) {
add_event(name,interval,function);
}else{
} else {
add_task(name,interval,function);
}
},
stop:func{
if(me.isRunning){
stop:func {
if (me.isRunning) {
remove_task(name);
me.isRunning=0;
}
},
restart:func(itv){
restart:func(itv) {
interval=itv;
me.stop();
me.start();
@@ -131,9 +133,9 @@ var maketimer=func(interval,function){
}
println("[\e[32m settimer \e[0m] [",os.time(),"] new func settimer(function,interval,rt)");
var settimer=func(){
var settimer = func() {
var index=0;
return func(function,interval,realtime=1){
return func(function,interval,realtime=1) {
var name="nasal-settimer-"~index;
index+=1;
add_task(name,interval,function);
@@ -141,25 +143,25 @@ var settimer=func(){
}();
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func simulation()");
var simulation=func(){
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))
foreach(var i;keys(fg_globals.task)) {
if (!contains(fg_globals.task,i))
continue;
if(coroutine.resume(fg_globals.task[i])!=nil){
if (coroutine.resume(fg_globals.task[i])!=nil) {
running=1;
}else{
} else {
remove_task(i);
}
}
foreach(var i;keys(fg_globals.event)){
if(!contains(fg_globals.event,i))
foreach(var i;keys(fg_globals.event)) {
if (!contains(fg_globals.event,i))
continue;
if(coroutine.resume(fg_globals.event[i])!=nil){
if (coroutine.resume(fg_globals.event[i])!=nil) {
running=1;
}else{
} else {
remove_event(i);
}
}
@@ -167,14 +169,14 @@ var simulation=func(){
}
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func maketimer_multi_coroutine_test(size)");
var maketimer_multi_coroutine_test=func(coroutine_size){
if(coroutine_size<1)
var maketimer_multi_coroutine_test = func(coroutine_size) {
if (coroutine_size<1)
return;
var task_vec=[];
setsize(task_vec,coroutine_size);
forindex(var i;task_vec)
task_vec[i]=func{};
task_vec[coroutine_size-1]=func{
task_vec[i] = func {};
task_vec[coroutine_size-1] = func {
println("\e[101m",coroutine_size," tasks invoked.\e[0m");
forindex(var i;task_vec)
task_vec[i].stop();
@@ -182,23 +184,23 @@ var maketimer_multi_coroutine_test=func(coroutine_size){
var event_vec=[];
setsize(event_vec,coroutine_size);
forindex(var i;event_vec)
event_vec[i]=func{};
event_vec[coroutine_size-1]=func{
event_vec[i] = func {};
event_vec[coroutine_size-1] = func {
println("\e[101m",coroutine_size," events invoked.\e[0m");
}
var set_vec=[];
setsize(set_vec,coroutine_size);
forindex(var i;set_vec)
set_vec[i]=func{};
set_vec[coroutine_size-1]=func{
set_vec[i] = func {};
set_vec[coroutine_size-1] = func {
println("\e[101m",coroutine_size," settimer invoked.\e[0m");
}
forindex(var i;task_vec){
forindex(var i;task_vec) {
task_vec[i]=maketimer((i+1)/10,task_vec[i]);
task_vec[i].start();
}
forindex(var i;event_vec){
forindex(var i;event_vec) {
event_vec[i]=maketimer((i+1)/10,event_vec[i]);
event_vec[i].singleShot=1;
event_vec[i].start();
@@ -209,7 +211,7 @@ var maketimer_multi_coroutine_test=func(coroutine_size){
}
println("[\e[32m geodinfo \e[0m] [",os.time(),"] init geodinfo(lat,lon)");
var geodinfo=func(lat,lon){
var geodinfo = func(lat,lon) {
return [nil,{
names:["Road","Freeway"]
}];
@@ -219,14 +221,14 @@ println("[\e[32m props \e[0m] [",os.time(),"] init props");
var props={
globals:nil,
Node:nil,
getNode:func(path,index){
getNode:func(path,index) {
path=split('/',path);
var tmp=me.globals;
var path_size=size(path);
for(var i=0;i<path_size-1;i+=1)
tmp=tmp.val[path[i]];
if(path_size>0){
if(contains(tmp.val,path[i]~'['~index~']'))
if (path_size>0) {
if (contains(tmp.val,path[i]~'['~index~']'))
return tmp.val[path[i]~'['~index~']'];
else
return tmp.val[path[i]];
@@ -238,89 +240,89 @@ var props={
println("[\e[32m props \e[0m] [",os.time(),"] init props.Node");
props.Node={
new:func(values=nil){
new:func(values=nil) {
var res={
parents:fg_env_props_node_traits,
val:{},
type:'GHOST',
parent:nil
};
if(typeof(values)=="hash")
if (typeof(values)=="hash")
res.val=values;
return res;
},
addChild:func(name){
if(!contains(me.val,name)){
addChild:func(name) {
if (!contains(me.val,name)) {
me.val[name]=props.Node.new();
me.val[name].parent=me;
return 1;
}
return 0;
},
addChildren:func(name,cnt=0){
for(var i=0;i<cnt;i+=1){
addChildren:func(name,cnt=0) {
for(var i=0;i<cnt;i+=1) {
var label=name~'['~i~']';
me.val[label]=props.Node.new();
me.val[label].parent=me;
}
return;
},
setValue:func(path,val){
setValue:func(path,val) {
path=split('/',path);
var tmp=me;
foreach(var label;path)
tmp=tmp.val[label];
tmp.val=val;
if(typeof(val)=='str'){
if(val=='true' or val=='false')
if (typeof(val)=='str') {
if (val=='true' or val=='false')
tmp.type='BOOL';
else
tmp.type='STRING';
}
elsif(typeof(val)=='num')
elsif (typeof(val)=='num')
tmp.type='DOUBLE';
return;
},
setIntValue:func(num){
setIntValue:func(num) {
me.val=num;
me.type='INT';
return;
},
setBoolValue:func(state){
setBoolValue:func(state) {
me.val=state;
me.type='BOOL';
return;
},
setDoubleValue:func(num){
setDoubleValue:func(num) {
me.val=num;
me.type='DOUBLE';
return;
},
getValue:func(){return me.val;},
getName:func(){
getValue:func() {return me.val;},
getName:func() {
var val=me.parent.val;
foreach(var k;keys(val))
if(val[k]==me)
if (val[k]==me)
return k;
return '';
},
getParent:func(){
getParent:func() {
return me.parent;
},
getPath:func(){
if(me.parent==nil) return '';
getPath:func() {
if (me.parent==nil) return '';
return me.parent.getPath()~'/'~me.getName();
},
equals:func(node){return me==node;},
debug:func(s=''){
if(typeof(me.val)=='hash'){
equals:func(node) {return me==node;},
debug:func(s='') {
if (typeof(me.val)=='hash') {
var key=keys(me.val);
if(!size(key)){
if (!size(key)) {
println("\e[91m{}\e[0m");
return;
}
println('\e[91m{\e[0m');
foreach(var k;key){
foreach(var k;key) {
print(s~" ","\e[34m",k,"\e[0m\e[95m:\e[0m");
me.val[k].debug(s~" ");
}
@@ -352,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");
@@ -374,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");
}
@@ -388,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");
@@ -406,7 +408,7 @@ 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");
}
@@ -425,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);
@@ -440,7 +442,7 @@ for(var i=0;i<4;i+=1)
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");
}
@@ -466,7 +468,7 @@ 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");
}
@@ -536,17 +538,17 @@ props.getNode("/position/altitude-agl-ft",1).setValue('/',22.4704);
props.getNode("/orientation/heading-deg",1).setValue('/',90);
props.getNode("/controls/flight/rudder",1).setValue('/',0.114);
func(){
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"){
if(size(i.val)==0){
foreach(var i;vec) {
if (typeof(i.val)=="hash") {
if (size(i.val)==0) {
i.setDoubleValue(rand()*10);
}else{
} else {
foreach(var j;keys(i.val))
append(tmp,i.val[j]);
}
@@ -561,267 +563,6 @@ println("[\e[32m fg_env \e[0m] [",os.time(),"] init done");
println("-------------------------------------------------------------");
foreach(var a;runtime.argv())
if(contains(fg_env_cli,a)){
if (contains(fg_env_cli,a)) {
fg_env_cli[a].f();
}
# related doc: https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Docs/README.properties
# ================================================================================
# CONTROLS
# ================================================================================
# Flight Controls
# ---------------
# /controls/flight/aileron
# /controls/flight/aileron-trim
# /controls/flight/elevator
# /controls/flight/elevator-trim
# /controls/flight/rudder
# /controls/flight/rudder-trim
# /controls/flight/flaps
# /controls/flight/slats
# /controls/flight/BLC // Boundary Layer Control
# /controls/flight/spoilers
# /controls/flight/speedbrake
# /controls/flight/wing-sweep
# /controls/flight/wing-fold
# /controls/flight/drag-chute
# Engines
# -------
# /controls/engines/throttle_idle
# /controls/engines/engine[%d]/throttle
# /controls/engines/engine[%d]/starter
# /controls/engines/engine[%d]/fuel-pump
# /controls/engines/engine[%d]/fire-switch
# /controls/engines/engine[%d]/fire-bottle-discharge
# /controls/engines/engine[%d]/cutoff
# /controls/engines/engine[%d]/mixture
# /controls/engines/engine[%d]/propeller-pitch
# /controls/engines/engine[%d]/magnetos
# /controls/engines/engine[%d]/boost
# /controls/engines/engine[%d]/WEP
# /controls/engines/engine[%d]/cowl-flaps-norm
# /controls/engines/engine[%d]/feather
# /controls/engines/engine[%d]/ignition
# /controls/engines/engine[%d]/augmentation
# /controls/engines/engine[%d]/afterburner
# /controls/engines/engine[%d]/reverser
# /controls/engines/engine[%d]/water-injection
# /controls/engines/engine[%d]/condition
# Fuel
# ----
# /controls/fuel/dump-valve
# /controls/fuel/tank[%d]/fuel_selector
# /controls/fuel/tank[%d]/to_engine
# /controls/fuel/tank[%d]/to_tank
# /controls/fuel/tank[%d]/boost-pump[%d]
# /consumables/fuel/tank[%d]/level-lbs
# /consumables/fuel/tank[%d]/level-gal_us
# /consumables/fuel/tank[%d]/capacity-gal_us
# /consumables/fuel/tank[%d]/density-ppg
# /consumables/fuel/total-fuel-lbs
# /consumables/fuel/total-gal_us
# Gear
# ----
# /controls/gear/brake-left
# /controls/gear/brake-right
# /controls/gear/brake-parking
# /controls/gear/steering
# /controls/gear/gear-down
# /controls/gear/antiskid
# /controls/gear/tailhook
# /controls/gear/tailwheel-lock
# /controls/gear/wheel[%d]/alternate-extension
# Anti-Ice
# --------
# /controls/anti-ice/wing-heat
# /controls/anti-ice/pitot-heat
# /controls/anti-ice/wiper
# /controls/anti-ice/window-heat
# /controls/anti-ice/engine[%d]/carb-heat
# /controls/anti-ice/engine[%d]/inlet-heat
# Hydraulics
# ----------
# /controls/hydraulic/system[%d]/engine-pump
# /controls/hydraulic/system[%d]/electric-pump
# Electric
# --------
# /controls/electric/battery-switch
# /controls/electric/external-power
# /controls/electric/APU-generator
# /controls/electric/engine[%d]/generator
# /controls/electric/engine[%d]/bus-tie
# Pneumatic
# ---------
# /controls/pneumatic/APU-bleed
# /controls/pneumatic/engine[%d]/bleed
# Pressurization
# --------------
# /controls/pressurization/mode
# /controls/pressurization/dump
# /controls/pressurization/outflow-valve
# /controls/pressurization/pack[%d]/pack-on
# Lights
# ------
# /controls/lighting/landing-lights
# /controls/lighting/turn-off-lights
# /controls/lighting/formation-lights
# /controls/lighting/taxi-light
# /controls/lighting/logo-lights
# /controls/lighting/nav-lights
# /controls/lighting/beacon
# /controls/lighting/strobe
# /controls/lighting/panel-norm
# /controls/lighting/instruments-norm
# /controls/lighting/dome-norm
# Armament
# --------
# /controls/armament/master-arm
# /controls/armament/station-select
# /controls/armament/release-all
# /controls/armament/station[%d]/stick-size
# /controls/armament/station[%d]/release-stick
# /controls/armament/station[%d]/release-all
# /controls/armament/station[%d]/jettison-all
# Seat
# ----
# /controls/seat/vertical-adjust
# /controls/seat/fore-aft-adjust
# /controls/seat/cmd_selector_valve
# /controls/seat/eject[%d]/initiate
# /controls/seat/eject[%d]/status
# APU
# ---
# /controls/APU/off-start-run
# /controls/APU/fire-switch
# Autoflight
# ----------
# /controls/autoflight/autopilot[%d]/engage
# /controls/autoflight/autothrottle-arm
# /controls/autoflight/autothrottle-engage
# /controls/autoflight/heading-select
# /controls/autoflight/altitude-select
# /controls/autoflight/bank-angle-select
# /controls/autoflight/vertical-speed-select
# /controls/autoflight/speed-select
# /controls/autoflight/mach-select
# /controls/autoflight/vertical-mode
# /controls/autoflight/lateral-mode
# ================================================================================
# FDM (Aircraft settings)
# ================================================================================
# Position
# ---------------
# /position/latitude-deg
# /position/longitude-deg
# /position/altitude-ft
# Orientation
# -----------
# /orientation/roll-deg
# /orientation/pitch-deg
# /orientation/heading-deg
# /orientation/roll-rate-degps
# /orientation/pitch-rate-degps
# /orientation/yaw-rate-degps
# /orientation/side-slip-rad
# /orientation/side-slip-deg
# /orientation/alpha-deg
# Velocities
# ----------
# /velocities/airspeed-kt
# /velocities/mach
# /velocities/speed-north-fps
# /velocities/speed-east-fps
# /velocities/speed-down-fps
# /velocities/uBody-fps
# /velocities/vBody-fps
# /velocities/wBody-fps
# /velocities/vertical-speed-fps
# /velocities/glideslope
# Acceleration
# ------------
# /accelerations/nlf
# /accelerations/ned/north-accel-fps_sec
# /accelerations/ned/east-accel-fps_sec
# /accelerations/ned/down-accel-fps_sec
# /accelerations/pilot/x-accel-fps_sec
# /accelerations/pilot/y-accel-fps_sec
# /accelerations/pilot/z-accel-fps_sec
# Engines
# -------
# common:
# /engines/engine[%d]/fuel-flow-gph
# /engines/engine[%d]/fuel-flow_pph
# /engines/engine[%d]/thrust_lb
# /engines/engine[%d]/running
# /engines/engine[%d]/starter
# /engines/engine[%d]/cranking
# piston:
# /engines/engine[%d]/mp-osi
# /engines/engine[%d]/egt-degf
# /engines/engine[%d]/oil-temperature-degf
# /engines/engine[%d]/oil-pressure-psi
# /engines/engine[%d]/cht-degf
# /engines/engine[%d]/rpm
# turbine:
# /engines/engine[%d]/n1
# /engines/engine[%d]/n2
# /engines/engine[%d]/epr
# /engines/engine[%d]/augmentation
# /engines/engine[%d]/water-injection
# /engines/engine[%d]/ignition
# /engines/engine[%d]/nozzle-pos-norm
# /engines/engine[%d]/inlet-pos-norm
# /engines/engine[%d]/reversed
# /engines/engine[%d]/cutoff
# propeller:
# /engines/engine[%d]/rpm
# /engines/engine[%d]/pitch
# /engines/engine[%d]/torque
# ================================================================================
# LIGHT
# ================================================================================
# /sim/time/sun-angle-rad
# /rendering/scene/ambient/red
# /rendering/scene/ambient/ggreen
# /rendering/scene/ambient/blue
# /rendering/scene/diffuse/red
# /rendering/scene/diffuse/green
# /rendering/scene/diffuse/blue
# /rendering/scene/specular/red
# /rendering/scene/specular/green
# /rendering/scene/specular/blue

View File

@@ -1,5 +1,7 @@
# lib file.nas
# ValKmjolnir 2022/3/6
use std.io;
use std.unix;
var SEEK_SET = io.SEEK_SET;
@@ -7,8 +9,7 @@ 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;
}
@@ -29,17 +30,17 @@ var new = func(filename, mode="r"){
};
}
var find_all_files_with_extension = func(path, extensions...){
var find_all_files_with_extension = func(path, extensions...) {
var in_vec = func(ext) {
foreach(var i;extensions) {
if (ext==i){
foreach(var i; extensions) {
if (ext==i) {
return 1;
}
}
return 0;
}
var res = [];
foreach(var f;find_all_files(path)){
foreach(var f; find_all_files(path)) {
var tmp = split('.', f);
if (size(tmp)>1 and in_vec(tmp[-1])) {
append(res, f);
@@ -48,16 +49,17 @@ var find_all_files_with_extension = func(path, extensions...){
return res;
}
var find_all_files = func(path){
var find_all_files = func(path) {
if (!io.exists(path)) {
return [];
}
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);
}
}
unix.closedir(dd);
return res;
}
@@ -73,14 +75,55 @@ var recursive_find_files = func(path) {
};
while(var n = unix.readdir(dd)) {
if (unix.isfile(path~"/"~n)) {
append(res.files,n);
append(res.files, n);
} elsif (unix.isdir(path~"/"~n) and n!="." and n!="..") {
var tmp = recursive_find_files(path~"/"~n);
if (tmp!=nil) {
append(res.files,tmp);
append(res.files, tmp);
}
}
}
unix.closedir(dd);
return res;
}
var recursive_find_files_flat = func(path) {
var tree_files = recursive_find_files(path);
if (tree_files==nil) {
return [];
}
var flat = [];
var bfs = [tree_files];
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);
}
}
return flat;
}
var recursive_find_files_with_extension = func(path, extensions...) {
var in_vec = func(ext) {
foreach(var i; extensions) {
if (ext==i) {
return 1;
}
}
return 0;
}
var files = recursive_find_files_flat(path);
var res = [];
foreach(var filename; files) {
var tmp = split('.', filename);
if (size(tmp)>1 and in_vec(tmp[-1])) {
append(res, filename);
}
}
return res;
}

View File

@@ -72,3 +72,21 @@ var stdin = func() { return __stdin; }();
var stdout = func() { return __stdout;}();
var stderr = func() { return __stderr; }();
# get file status. using data from stat
var fstat = func(filename) {
var s = stat(filename);
return {
st_dev: s[0],
st_ino: s[1],
st_mode: s[2],
st_nlink: s[3],
st_uid: s[4],
st_gid: s[5],
st_rdev: s[6],
st_size: s[7],
st_atime: s[8],
st_mtime: s[9],
st_ctime: s[10]
};
}

View File

@@ -1,299 +1,30 @@
# lib json.nas
# 2021 ValKmjolnir
var (
_j_eof,
_j_lbrace,
_j_rbrace,
_j_lbrkt,
_j_rbrkt,
_j_comma,
_j_colon,
_j_str,
_j_num,
_j_id
) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
var _j_content = [
"eof",
"`{`",
"`}`",
"`[`",
"`]`",
"`,`",
"`:`",
"string",
"number",
"identifier"
];
var parse = func() {
var text = "";
var line = 1;
var text_size = 0;
var ptr = 0;
var token = {
content: "",
type: ""
};
var init = func() {
text = "";
line = 1;
text_size = 0;
ptr = 0;
token = {
content: "",
type: ""
};
}
var isnum = func(c) {
return '0'<=c and c<='9';
}
var isid = func(c) {
var tmp = c[0];
return ('a'[0]<=tmp and tmp<='z'[0]) or
('A'[0]<=tmp and tmp<='Z'[0]) or
c=='_';
}
var check = func() {
var c = char(text[ptr]);
return (
c=='{' or c=='}' or
c=='[' or c==']' or
c==',' or c==':' or
c=='\"' or c=='\'' or
isnum(c) or isid(c)
);
}
var get = func(str) {
init();
if (!size(str)) {
println("json::parse: empty string");
str = "[]";
}
text = str;
text_size = size(text);
return;
}
var next = func() {
while(ptr<text_size and !check()) {
if (char(text[ptr])=='\n') {
line += 1;
}
ptr += 1;
}
if(ptr>=text_size) {
token.content = "eof";
token.type = _j_eof;
return;
}
var c = char(text[ptr]);
if (c=='{') {
token.content = '{';
token.type = _j_lbrace;
} elsif (c=='}') {
token.content = '}';
token.type = _j_rbrace;
} elsif (c=='[') {
token.content = '[';
token.type = _j_lbrkt;
} elsif (c==']') {
token.content = ']';
token.type = _j_rbrkt;
} elsif (c==',') {
token.content = ',';
token.type = _j_comma;
} elsif (c==':') {
token.content = ':';
token.type = _j_colon;
} elsif (c=='\"' or c=='\'') {
var strbegin = c;
var s = "";
ptr += 1;
while(ptr<text_size and char(text[ptr])!=strbegin) {
s ~= char(text[ptr]);
ptr += 1;
if (char(text[ptr-1])=="\\" and ptr<text_size) {
s ~= char(text[ptr]);
ptr += 1;
}
}
token.content=s;
token.type=_j_str;
} elsif (isnum(c)) {
var s = c;
ptr += 1;
while(ptr<text_size and ((isnum(char(text[ptr])) or char(text[ptr])=='.'))) {
s ~= char(text[ptr]);
ptr += 1;
}
ptr -= 1;
token.content = num(s);
token.type = _j_num;
} elsif (isid(c)) {
var s = c;
ptr += 1;
while(ptr<text_size and (isid(char(text[ptr])) or isnum(char(text[ptr])))) {
s ~= char(text[ptr]);
ptr += 1;
}
ptr -= 1;
token.content = s;
token.type = _j_id;
}
ptr += 1;
return;
}
var match = func(type) {
if(token.type!=type) {
println("json::parse: line ",line,": expect ",_j_content[type]," but get `",token.content,"`.");
}
next();
return;
}
var member = func(hash) {
var name = token.content;
if (token.type==_j_rbrace) {
return;
}
if (token.type==_j_str) {
match(_j_str);
} else {
match(_j_id);
}
match(_j_colon);
if (token.type==_j_lbrace) {
hash[name] = hash_gen();
} elsif (token.type==_j_lbrkt) {
hash[name] = vec_gen();
} elsif (token.type==_j_str or token.type==_j_num) {
hash[name] = token.content;
next();
}
return;
}
var hash_gen = func() {
var hash = {};
match(_j_lbrace);
member(hash);
while(token.type==_j_comma) {
match(_j_comma);
member(hash);
}
match(_j_rbrace);
return hash;
}
var vec_gen = func() {
var vec = [];
match(_j_lbrkt);
if (token.type==_j_lbrace) {
append(vec, hash_gen());
} elsif (token.type==_j_lbrkt) {
append(vec, vec_gen());
} elsif (token.type==_j_str or token.type==_j_num) {
append(vec, token.content);
next();
}
while(token.type==_j_comma) {
match(_j_comma);
if (token.type==_j_lbrace) {
append(vec, hash_gen());
} elsif (token.type==_j_lbrkt) {
append(vec, vec_gen());
} elsif (token.type==_j_str or token.type==_j_num) {
append(vec, token.content);
next();
}
}
match(_j_rbrkt);
return vec;
}
return func(source) {
if(typeof(source)!="str") {
println("json::parse: must use string but get", typeof(str));
return [];
}
get(source);
next();
if (token.type==_j_lbrkt) {
var res = vec_gen();
} else {
var res = hash_gen();
}
init();
return res;
}
var _json = func() {
return _json_new();
}();
var _stringify = func(json_object, object) {
return _json_stringify(json_object, object);
}
var _parse = func(json_object, input_string) {
return _json_parse(json_object, input_string);
}
var _get_error = func(json_object) {
return _json_get_error(json_object);
}
var stringify = func(object) {
var object_type = typeof(object);
if(object_type!="hash" and object_type!="vec" and object_type!="namespace") {
println("json::stringify: must use hashmap or vector, but get ", typeof(object));
return "[]";
}
return _stringify(_json, object);
}
var s = "";
var gen = func(elem) {
var t = typeof(elem);
if (t=="num") {
s ~= str(elem);
} elsif (t=="str") {
s ~= '"'~elem~'"';
} elsif (t=="vec") {
vgen(elem);
} elsif (t=="hash") {
hgen(elem);
} else {
s ~= '"undefined"';
}
}
var parse = func(input_string) {
return _parse(_json, input_string);
}
var vgen = func(v) {
s ~= "[";
var vsize = size(v);
for(var i = 0; i<vsize; i += 1) {
gen(v[i]);
if (i!=vsize-1) {
s~=",";
}
}
s ~= "]";
}
var hgen = func(h) {
s ~= "{";
var k = keys(h);
var vsize = size(k);
for(var i = 0; i<vsize; i += 1) {
s ~= k[i]~":";
gen(h[k[i]]);
if (i!=vsize-1) {
s ~= ",";
}
}
s ~= "}";
}
if (typeof(object)=="vec") {
vgen(object);
} else {
hgen(object);
}
return s;
var get_error = func() {
return _get_error(_json);
}

View File

@@ -1,14 +1,6 @@
# lib.nas
# 2019 ValKmjolnir
use std.coroutine;
use std.math;
use std.string;
use std.io;
use std.os;
use std.bits;
use std.unix;
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout to output logs.
var print = func(elems...) {
@@ -235,7 +227,7 @@ var println = func(elems...) {
var sort = func() {
srand(); # be aware! this causes global changes
var quick_sort_core = func(vec, left, right, cmp) {
if(left>=right) return nil;
if (left>=right) return nil;
var base = left+int(rand()*(right-left));
(vec[left], vec[base]) = (vec[base], vec[left]);
var (i, j, tmp) = (left, right, vec[left]);
@@ -278,7 +270,14 @@ var isint = func(x) {
}
var isnum = func(x) {
return typeof(x)=="num" or !math.isnan(num(x));
if (typeof(x)=="num") {
return true;
}
x = num(x);
if (!__isnan(x) and x!=nil) {
return true;
}
return false;
}
var isscalar = func(s) {
@@ -301,7 +300,7 @@ var ghosttype = func(ghost_object) {
# get the index of val in the vec
var vecindex = func(vec, val) {
forindex(var i; vec) {
if(val==vec[i]) {
if (val==vec[i]) {
return i;
}
}
@@ -313,11 +312,11 @@ var isa = func(object, class) {
if (!ishash(object)) {
return false;
}
if(!contains(object, "parents") or !isvec(object.parents)) {
if (!contains(object, "parents") or !isvec(object.parents)) {
return false;
}
foreach(var elem; object.parents) {
if(elem==class or isa(elem, class)) {
if (elem==class or isa(elem, class)) {
return true;
}
}
@@ -331,11 +330,20 @@ var assert = func(condition, message = "assertion failed!") {
# get time stamp, this will return a timestamp object
var maketimestamp = func() {
var t = 0;
var stamp = __maketimestamp();
var time_stamp = func(stamp) {
return __time_stamp(stamp);
}
var elapsed_millisecond = func(stamp) {
return __elapsed_millisecond(stamp);
}
var elapsed_microsecond = func(stamp) {
return __elapsed_microsecond(stamp);
}
return {
stamp: func() {t = __millisec();},
elapsedMSec: func() {return __millisec()-t;},
elapsedUSec: func() {return (__millisec()-t)*1000;}
stamp: func() {return time_stamp(stamp);},
elapsedMSec: func() {return elapsed_millisecond(stamp);},
elapsedUSec: func() {return elapsed_microsecond(stamp);}
};
}
@@ -344,27 +352,9 @@ var md5 = func(str) {
return __md5(str);
}
# get file status. using data from io.stat
var fstat = func(filename) {
var s = io.stat(filename);
return {
st_dev: s[0],
st_ino: s[1],
st_mode: s[2],
st_nlink: s[3],
st_uid: s[4],
st_gid: s[5],
st_rdev: s[6],
st_size: s[7],
st_atime: s[8],
st_mtime: s[9],
st_ctime: s[10]
};
}
# important global constants
var D2R = math.pi / 180; # degree to radian
var R2D = 180 / math.pi; # radian to degree
var D2R = 3.14159265358979323846264338327950288 / 180; # degree to radian
var R2D = 180 / 3.14159265358979323846264338327950288; # radian to degree
var FT2M = 0.3048; # feet to meter
var M2FT = 1 / FT2M;

View File

@@ -10,7 +10,7 @@ var new = func() {
prev: nil,
next: nil
};
if(end!=nil) {
if (end!=nil) {
end.next = tmp;
tmp.prev = end;
end = tmp;

View File

@@ -1,18 +1,20 @@
# log.nas
# ValKmjolnir 2022/6/14
var log=func(){
use std.os;
var log = func() {
var (log_date,log_time,prefix)=(1,1,"");
var os_time="";
var prt_core=func(elem){
var prt_core = func(elem) {
os_time=os.time();
print(prefix," ");
if(log_date and log_time)
if (log_date and log_time)
print(os_time," ");
elsif(log_date or log_time){
elsif (log_date or log_time) {
var s=split(" ",os_time);
if(log_date)
if (log_date)
print(s[0]," ");
if(log_time)
if (log_time)
print(s[1]," ");
}
foreach(var i;elem)
@@ -20,19 +22,19 @@ var log=func(){
print("\n");
}
return {
setflags:func(date,time){
setflags:func(date,time) {
log_date=!!date;
log_time=!!time;
},
setprefix:func(s){
if(typeof(s)!="str")
setprefix:func(s) {
if (typeof(s)!="str")
println("[log.nas] must use string as the prefix.");
prefix=s;
},
println:func(elem...){
println:func(elem...) {
prt_core(elem);
},
fatalln:func(elem...){
fatalln:func(elem...) {
prt_core(elem);
die("log:fatal error");
}

View File

@@ -1,5 +1,6 @@
use std.math;
var mat=func(width,height) {
var mat = func(width,height) {
var res=[];
setsize(res,width*height);
forindex(var i;res) {
@@ -12,14 +13,14 @@ var mat=func(width,height) {
};
}
var rand_init=func(a) {
var rand_init = func(a) {
var ref=a.mat;
forindex(var i;ref) {
ref[i]=rand()*2-1;
}
}
var mat_print=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) {
@@ -29,8 +30,8 @@ var mat_print=func(a) {
}
}
var add=func(a,b) {
if(a.width!=b.width or a.height!=b.height) {
var add = func(a,b) {
if (a.width!=b.width or a.height!=b.height) {
println("matrix a: ",a);
println("matrix b: ",b);
die("width and height must be the same");
@@ -49,8 +50,8 @@ var add=func(a,b) {
return res;
}
var sub=func(a,b) {
if(a.width!=b.width or a.height!=b.height) {
var sub = func(a,b) {
if (a.width!=b.width or a.height!=b.height) {
println("matrix a: ",a);
println("matrix b: ",b);
die("width and height must be the same");
@@ -69,8 +70,8 @@ var sub=func(a,b) {
return res;
}
var hardamard=func(a,b) {
if(a.width!=b.width or a.height!=b.height) {
var hardamard = func(a,b) {
if (a.width!=b.width or a.height!=b.height) {
println("matrix a: ",a);
println("matrix b: ",b);
die("width and height must be the same");
@@ -89,7 +90,7 @@ var hardamard=func(a,b) {
return res;
}
var neg=func(a) {
var neg = func(a) {
var res=mat(a.width,a.height);
var (aref,ref)=(a.mat,res.mat);
forindex(var i;aref) {
@@ -98,7 +99,7 @@ var neg=func(a) {
return res;
}
var sum=func(a) {
var sum = func(a) {
var res=0;
var aref=a.mat;
forindex(var i;aref) {
@@ -107,7 +108,7 @@ var sum=func(a) {
return res;
}
var mult_num=func(a,c) {
var mult_num = func(a,c) {
var res=mat(a.width,a.height);
var ref=res.mat;
var aref=a.mat;
@@ -117,7 +118,7 @@ var mult_num=func(a,c) {
return res;
}
var trans=func(a) {
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);
@@ -129,7 +130,7 @@ var trans=func(a) {
return res;
}
var activate=func(a,f) {
var activate = func(a,f) {
var res=mat(a.width,a.height);
var (aref,ref)=(a.mat,res.mat);
forindex(var i;aref) {
@@ -138,8 +139,8 @@ var activate=func(a,f) {
return res;
}
var mult=func(a,b) {
if(a.width!=b.height) {
var mult = func(a,b) {
if (a.width!=b.height) {
println("matrix a: ",a);
println("matrix b: ",b);
die("a.width must equal to b.height, but get a.width:"~str(a.width)~" and b.height"~str(b.height));
@@ -161,28 +162,28 @@ var mult=func(a,b) {
return res;
}
var sigmoid=func(x) {
var sigmoid = func(x) {
var t=math.exp(-x);
return 1/(1+t);
}
var diffsigmoid=func(x) {
var diffsigmoid = func(x) {
x=sigmoid(x);
return x*(1-x);
}
var tanh=func(x) {
var tanh = func(x) {
var t1=math.exp(x);
var t2=math.exp(-x);
return (t1-t2)/(t1+t2);
}
var difftanh=func(x) {
var difftanh = func(x) {
x=tanh(x);
return 1-x*x;
}
var bp_example=func() {
var bp_example = func() {
srand();
var lr=0.01;
var input=[
@@ -221,7 +222,7 @@ var bp_example=func() {
var total=1e6;
while(total>0.001) {
epoch+=1;
if(epoch>1e4) {
if (epoch>1e4) {
println("Training failed after ",epoch," epoch.");
break;
}
@@ -248,7 +249,7 @@ var bp_example=func() {
total+=sum(mult_num(mult(error,trans(error)),0.5));
}
}
if(epoch<=1e4) {
if (epoch<=1e4) {
println("Training succeeded after ",epoch," epoch.");
}

View File

@@ -4,35 +4,36 @@
# this provides safe usage of dylib
# when dylib is closed,
# all the invalid functions cannot be called
use std.dylib;
var module_call_func=func(fptr,args){
var module_call_func = func(fptr, args) {
return __dlcallv;
}
var extern={
new: func(fptr){
var isopen=1;
var extern = {
new: func(fptr) {
var isopen = 1;
return {
close:func(){isopen=0;},
call:func(args...){
close: func() {isopen = 0;},
call: func(args...) {
return isopen?module_call_func(fptr,args):nil;
}
};
}
};
var module={
new: func(name){
var lib=dylib.dlopen(name);
var f={};
var module = {
new: func(name) {
var lib = dylib.dlopen(name);
var f = {};
return {
get:func(symbol){
if(contains(f,symbol))
get: func(symbol) {
if (contains(f,symbol))
return f[symbol];
var fp=extern.new(dylib.dlsym(lib,symbol));
f[symbol]=fp;
var fp = extern.new(dylib.dlsym(lib,symbol));
f[symbol] = fp;
return fp;
},
close: func(){
foreach(var i;keys(f))
close: func() {
foreach(var i; keys(f))
f[i].close();
dylib.dlclose(lib);
}

View File

@@ -1,24 +1,24 @@
# padding.nas
# ValKmjolnir 2022/9/4
var leftpad = func(s, len, char=" ") {
if (typeof(s)=="num") {
s = str(s);
var leftpad = func(input_string, length, char=" ") {
if (typeof(input_string)=="num") {
input_string = str(input_string);
}
var strlen = size(s);
for(var i = strlen; i<len; i += 1) {
s = char~s;
var strlen = size(input_string);
for(var i = strlen; i<length; i += 1) {
input_string = char~input_string;
}
return s;
return input_string;
}
var rightpad = func(s, len, char=" ") {
if (typeof(s)=="num") {
s = str(s);
var rightpad = func(input_string, length, char=" ") {
if (typeof(input_string)=="num") {
input_string = str(input_string);
}
var strlen = size(s);
for(var i = strlen; i<len; i += 1) {
s ~= char;
var strlen = size(input_string);
for(var i = strlen; i<length; i += 1) {
input_string ~= char;
}
return s;
return input_string;
}

147
std/phi.nas Normal file
View File

@@ -0,0 +1,147 @@
# used to get property tree from fgfs by httpd
# use --httpd=5500 to start fgfs
# and use this script to get property tree
# 2023/11/06 ValKmjolnir
use module.libnasock;
use std.json;
use std.os;
use std.unix;
var _raw_str = func(s) {
var v = split("", s);
var res = "";
foreach(var i; v) {
if (i=="\r") {
res ~= "\\r";
continue;
}
if (i=="\n") {
res ~= "\\n";
continue;
}
res ~= i;
}
return res;
}
var _get_time = func() {
return "["~os.time()~"]";
}
var _connect = func(hostname, port) {
var socket = libnasock.socket;
var sd = socket.socket(
socket.AF_INET,
socket.SOCK_STREAM,
socket.IPPROTO_TCP
);
var ip_info = hostname~":"~port;
while((var err = socket.connect(sd, hostname, port))==socket.SOCKET_ERROR) {
println(_get_time(), " failed to connect ", ip_info, ": ", socket.errno());
unix.sleep(1);
}
println(_get_time(), " connect to ", ip_info, " succeeded");
return sd;
}
var new = func(hostname, port) {
var socket = libnasock.socket;
var sd = _connect(hostname, port);
var getprop = func(path) {
if (size(path)==0 or path[0]!='/'[0]) {
println("node \"", path, "\" not found, invalid path");
return {path: path};
}
# GET header
var header = "GET /json"~path~" HTTP/1.1\r\n\r\n";
var res = socket.send(sd, header);
# get message head 1024
var message = socket.recv(sd, 1024);
if (find("404 Not Found", message.str)>=0) {
println("node \"", path, "\" not found, get 404 response");
return {path: path};
}
# 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) {
message = socket.recv(sd, 1024);
total_source ~= message.str;
}
# get json in this message
var begin_position = find("{", total_source);
var end_position = find("0\r\n\r\n", total_source);
var length = end_position-begin_position;
if (begin_position<0) {
println("node \"", path, "\" not found, invalid begin token");
return {path: path};
}
if (length<0) {
println("node \"", path, "\" not found, invalid end token");
return {path: path};
}
var data = substr(total_source, begin_position, length);
# parse this json and return
var props = json.parse(data);
if (size(json.get_error())>0) {
println("encounter error when parsing \"", path, "\":\n", json.get_error());
logprint(LOG_DEBUG, _raw_str(data));
return {path: path};
}
# empty prop node is not allowed...
if (size(props)==0) {
println("node \"", path, "\" not found, empty tree node");
}
return props;
}
var setprop = func(path, data) {
if (size(path)==0 or path[0]!='/'[0]) {
println("node \"", path, "\" not found, invalid path");
return {path: path};
}
# POST header
var header = "POST /json"~path~" HTTP/1.1\r\n\r\n";
# generate value
header ~= "{\"value\":\""~data~"\"}\r\n\r\n";
var res = socket.send(sd, header);
var message = socket.recv(sd, 1024);
}
return {
getprop: getprop,
setprop: setprop
};
}
var dump = func(tree, indent = "") {
if (size(tree)==0) {
return;
}
println(indent, "-------------------");
var tree_keys = keys(tree);
sort(tree_keys, func(a,b) {return cmp(a, b)<0;});
foreach(var key; tree_keys) {
if (key == "children") {
continue;
}
println(indent, key, " : \"", tree[key], "\"");
}
println(indent, "-------------------");
if (contains(tree, "children")) {
foreach(var i; tree.children) {
dump(i, indent~" ");
}
}
}

View File

@@ -1,50 +1,52 @@
# process_bar.nas
# ValKmjolnir 2022/6/14
# this file is inspired by a Python lib: alive_progress
use std.os;
use std.unix;
var bar=func(){
var bar={
solid_triangle_right:"▶",
hollow_triangle_right:"▷",
solid_triangle_left:"◀",
hollow_triangle_left:"◁",
solid_circle:"●",
hollow_circle:"○",
tick:"✔",
cross:"✘",
light_shadow:"░",
medium_shadow:"▒",
deep_shadow:"▓",
block:"█",
sharp:"#",
square:"√",
equal:"=",
space:" ",
point:".",
line:"━"
var bar = func() {
var bar = {
solid_triangle_right: "▶",
hollow_triangle_right: "▷",
solid_triangle_left: "◀",
hollow_triangle_left: "◁",
solid_circle: "●",
hollow_circle: "○",
tick: "✔",
cross: "✘",
light_shadow: "░",
medium_shadow: "▒",
deep_shadow: "▓",
block: "█",
sharp: "#",
square: "√",
equal: "=",
space: " ",
point: ".",
line: "━"
};
var separator={
angle_bracket:["<",">"],
line:["|","|"],
bracket:["[","]"],
space:[" "," "],
curve:["(",")"]
var separator = {
angle_bracket: ["<",">"],
line: ["|","|"],
bracket: ["[","]"],
space: [" "," "],
curve: ["(",")"]
};
return func(front="sharp",back="space",sep="line",length=20){
if(typeof(front)!="str" or !contains(bar,front))
return func(front = "sharp", back = "space", sep = "line", length = 20) {
if (typeof(front)!="str" or !contains(bar,front))
front="sharp";
if(typeof(back)!="str" or !contains(bar,back))
if (typeof(back)!="str" or !contains(bar,back))
back="space";
if(typeof(sep)!="str" or !contains(separator,sep))
if (typeof(sep)!="str" or !contains(separator,sep))
sep="line";
front=bar[front];
back=bar[back];
sep=separator[sep];
return {
bar:func(number){
if(number>1)
bar: func(number) {
if (number>1)
number=1;
if(number<0)
if (number<0)
number=0;
var finish_length=int(number*length);
var other=length-finish_length;
@@ -62,32 +64,32 @@ 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);
# }
# println();
var high_resolution_bar=func(){
var high_resolution_bar = func() {
var block=["▏","▎","▍","▌","▋","▊","▉","█"];
return func(length){
return func(length) {
return {
bar: func(number){
if(number>1)
bar: func(number) {
if (number>1)
number=1;
if(number<0)
if (number<0)
number=0;
var block_len=number*length;
var complete_block=int(block_len);
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){
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~="|";
@@ -97,30 +99,30 @@ var high_resolution_bar=func(){
};
}();
var spinner=func(){
var generate_scrolling_spinner=func(s){
if(typeof(s)!="str")
var spinner = func() {
var generate_scrolling_spinner = func(s) {
if (typeof(s)!="str")
s="****";
if(size(s)>16)
if (size(s)>16)
s="****";
var vec=split("",s);
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)
res[-1]~=" ";
}
tmp=res[-1];
while(tmp!=" "){
while(tmp!=" ") {
tmp=" "~substr(tmp,0,15);
append(res,tmp);
}
return res;
}
var spinner={
var spinner = {
rise:["▁","▂","▃","▄","▅","▆","▇","█","█","▇","▆","▅","▄","▃","▂","▁"],
vertical:["▏","▎","▍","▌","▋","▊","▉","▇","▇","▉","▊","▋","▌","▍","▎","▏"],
dot:["⠁","⠈","⠐","⠠","⢀","⡀","⠄","⠂"],
@@ -217,18 +219,18 @@ var spinner=func(){
wait:generate_scrolling_spinner("please wait"),
stars:generate_scrolling_spinner("********")
};
return func(type="classic",repeat=1){
if(typeof(type)!="str" or !contains(spinner,type))
return func(type = "classic", repeat = 1) {
if (typeof(type)!="str" or !contains(spinner,type))
type="classic";
type=spinner[type];
var counter=0;
return {
next:func(){
next: func() {
var s="";
for(var i=0;i<repeat;i+=1)
s~=type[counter];
counter+=1;
if(counter>=size(type))
if (counter>=size(type))
counter=0;
return s;
}
@@ -236,109 +238,109 @@ var spinner=func(){
};
}();
var default_bar=func(name="classic",length=20){
if(typeof(name)!="str")
var default_bar = func(name = "classic", length = 20) {
if (typeof(name)!="str")
name="classic";
if(name=="classic")
return process_bar.bar("sharp","point","bracket",length);
elsif(name=="classic2")
return process_bar.bar("equal","point","bracket",length);
elsif(name=="classic3")
return process_bar.bar("sharp","point","line",length);
elsif(name=="classic4")
return process_bar.bar("equal","point","line",length);
elsif(name=="triangle")
return process_bar.bar("solid_triangle_right","hollow_triangle_right","angle_bracket",length);
elsif(name=="dots")
return process_bar.bar("solid_circle","hollow_circle","curve",length);
elsif(name=="ticks")
return process_bar.bar("tick","space","line",length);
elsif(name=="deep_shadow")
return process_bar.bar("deep_shadow","light_shadow","line",length);
elsif(name=="block")
return process_bar.bar("block","light_shadow","line",length);
elsif(name=="oneline")
return process_bar.bar("line","space","space",length);
if (name=="classic")
return bar("sharp","point","bracket",length);
elsif (name=="classic2")
return bar("equal","point","bracket",length);
elsif (name=="classic3")
return bar("sharp","point","line",length);
elsif (name=="classic4")
return bar("equal","point","line",length);
elsif (name=="triangle")
return bar("solid_triangle_right","hollow_triangle_right","angle_bracket",length);
elsif (name=="dots")
return bar("solid_circle","hollow_circle","curve",length);
elsif (name=="ticks")
return bar("tick","space","line",length);
elsif (name=="deep_shadow")
return bar("deep_shadow","light_shadow","line",length);
elsif (name=="block")
return bar("block","light_shadow","line",length);
elsif (name=="oneline")
return bar("line","space","space",length);
else
return process_bar.bar("sharp","point","bracket",length);
return bar("sharp","point","bracket",length);
}
var default_spinner=func(name="classic",repeat=1){
if(typeof(name)!="str")
var default_spinner = func(name = "classic", repeat = 1) {
if (typeof(name)!="str")
name="classic";
if(name=="rise")
return process_bar.spinner("rise",repeat);
elsif(name=="vertical")
return process_bar.spinner("vertical",repeat);
elsif(name=="dot")
return process_bar.spinner("dot",repeat);
elsif(name=="dots")
return process_bar.spinner("dots",repeat);
elsif(name=="arrow")
return process_bar.spinner("arrow",repeat);
elsif(name=="classic")
return process_bar.spinner("classic",repeat);
elsif(name=="balls")
return process_bar.spinner("balls",repeat);
elsif(name=="dots_wave")
return process_bar.spinner("dots_wave",repeat);
elsif(name=="pulse")
return process_bar.spinner("pulse",repeat);
elsif(name=="wave")
return process_bar.spinner("wave",repeat);
elsif(name=="short_wave")
return process_bar.spinner("short_wave",repeat);
elsif(name=="fish")
return process_bar.spinner("fish",repeat);
elsif(name=="happy")
return process_bar.spinner("happy",repeat);
elsif(name=="wait")
return process_bar.spinner("wait",repeat);
elsif(name=="stars")
return process_bar.spinner("stars",repeat);
if (name=="rise")
return spinner("rise",repeat);
elsif (name=="vertical")
return spinner("vertical",repeat);
elsif (name=="dot")
return spinner("dot",repeat);
elsif (name=="dots")
return spinner("dots",repeat);
elsif (name=="arrow")
return spinner("arrow",repeat);
elsif (name=="classic")
return spinner("classic",repeat);
elsif (name=="balls")
return spinner("balls",repeat);
elsif (name=="dots_wave")
return spinner("dots_wave",repeat);
elsif (name=="pulse")
return spinner("pulse",repeat);
elsif (name=="wave")
return spinner("wave",repeat);
elsif (name=="short_wave")
return spinner("short_wave",repeat);
elsif (name=="fish")
return spinner("fish",repeat);
elsif (name=="happy")
return spinner("happy",repeat);
elsif (name=="wait")
return spinner("wait",repeat);
elsif (name=="stars")
return spinner("stars",repeat);
else
return process_bar.spinner("classic",repeat);
return spinner("classic",repeat);
}
var show=func(){
var show = func() {
print("\ec");
var bars={
"classic ":process_bar.default_bar("classic",40),
"classic2 ":process_bar.default_bar("classic2",40),
"classic3 ":process_bar.default_bar("classic3",40),
"classic4 ":process_bar.default_bar("classic4",40),
"triangle ":process_bar.default_bar("triangle",40),
"dots ":process_bar.default_bar("dots",40),
"ticks ":process_bar.default_bar("ticks",40),
"deep_shadow":process_bar.default_bar("deep_shadow",40),
"block ":process_bar.default_bar("block",40),
"oneline ":process_bar.default_bar("oneline",40)
"classic ":default_bar("classic",40),
"classic2 ":default_bar("classic2",40),
"classic3 ":default_bar("classic3",40),
"classic4 ":default_bar("classic4",40),
"triangle ":default_bar("triangle",40),
"dots ":default_bar("dots",40),
"ticks ":default_bar("ticks",40),
"deep_shadow":default_bar("deep_shadow",40),
"block ":default_bar("block",40),
"oneline ":default_bar("oneline",40)
};
var spinners={
"rise ":process_bar.default_spinner("rise",16),
"vertical ":process_bar.default_spinner("vertical",16),
"dot ":process_bar.default_spinner("dot",16),
"dots ":process_bar.default_spinner("dots",16),
"arrow ":process_bar.default_spinner("arrow",16),
"classic ":process_bar.default_spinner("classic",16),
"balls ":process_bar.default_spinner("balls",4),
"dots_wave ":process_bar.default_spinner("dots_wave",2),
"pulse ":process_bar.default_spinner("pulse",1),
"wave ":process_bar.default_spinner("wave",2),
"short_wave ":process_bar.default_spinner("short_wave",4),
"fish ":process_bar.default_spinner("fish",1),
"happy ":process_bar.default_spinner("happy",1),
"wait ":process_bar.default_spinner("wait",1),
"stars ":process_bar.default_spinner("stars",1)
"rise ":default_spinner("rise",16),
"vertical ":default_spinner("vertical",16),
"dot ":default_spinner("dot",16),
"dots ":default_spinner("dots",16),
"arrow ":default_spinner("arrow",16),
"classic ":default_spinner("classic",16),
"balls ":default_spinner("balls",4),
"dots_wave ":default_spinner("dots_wave",2),
"pulse ":default_spinner("pulse",1),
"wave ":default_spinner("wave",2),
"short_wave ":default_spinner("short_wave",4),
"fish ":default_spinner("fish",1),
"happy ":default_spinner("happy",1),
"wait ":default_spinner("wait",1),
"stars ":default_spinner("stars",1)
};
var bar_key=keys(bars);
var spin_key=keys(spinners);
for(var i=0;i<40;i+=1){
forindex(var j;bar_key){
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));
}
forindex(var j;spin_key){
forindex(var j; spin_key) {
var k=spin_key[j];
print("\e["~(j+1+size(bars))~";1H["~k~"] |"~spinners[k].next()~"|");
}

View File

@@ -9,6 +9,8 @@
# local node, there is no equivalent of the "relative path" variants
# available in C++; just use node.getNode(path).whatever() instead.
#
use std.math;
use std.string;
var _new = func {
return {
@@ -195,15 +197,15 @@ var Node = {
getPath : func {
var (name, index, parent) = (me.getName(), me.getIndex(), me.getParent());
if(index != 0) { name ~= "[" ~ index ~ "]"; }
if(parent != nil) { name = parent.getPath() ~ "/" ~ name; }
if (index != 0) { name ~= "[" ~ index ~ "]"; }
if (parent != nil) { name = parent.getPath() ~ "/" ~ name; }
return name;
},
getBoolValue : func {
var val = me.getValue();
var mytype = me.getType();
if((mytype == "STRING" or mytype == "UNSPECIFIED") and val == "false") return 0;
if ((mytype == "STRING" or mytype == "UNSPECIFIED") and val == "false") return 0;
return !!val;
},
@@ -216,7 +218,7 @@ var Node = {
},
remove : func {
if((var p = me.getParent()) == nil) return nil;
if ((var p = me.getParent()) == nil) return nil;
p.removeChild(me.getName(), me.getIndex());
},
@@ -300,7 +302,7 @@ var Node = {
#
Node.new = func(values = nil) {
var result = wrapNode(_new());
if(ishash(values)) {
if (ishash(values)) {
result.setValues(values);
}
return result;
@@ -316,18 +318,18 @@ Node.new = func(values = nil) {
#
Node.getValues = func {
var children = me.getChildren();
if(!size(children)) return me.getValue();
if (!size(children)) return me.getValue();
var val = {};
var numchld = {};
foreach(var c; children) {
var name = c.getName();
if(contains(numchld, name)) { var nc = numchld[name]; }
if (contains(numchld, name)) { var nc = numchld[name]; }
else {
var nc = size(me.getChildren(name));
numchld[name] = nc;
if(nc > 1 and !contains(val, name)) val[name] = [];
if (nc > 1 and !contains(val, name)) val[name] = [];
}
if(nc > 1) append(val[name], c.getValues());
if (nc > 1) append(val[name], c.getValues());
else val[name] = c.getValues();
}
return val;
@@ -346,13 +348,13 @@ Node.getValues = func {
#
Node.initNode = func(path = nil, value = 0, type = nil, force = 0) {
var prop = me.getNode(path or "", 1);
if(prop.getType() != "NONE") value = prop.getValue();
if(force) prop.clearValue();
if(type == nil) prop.setValue(value);
elsif(type == "DOUBLE") prop.setDoubleValue(value);
elsif(type == "INT") prop.setIntValue(value);
elsif(type == "BOOL") prop.setBoolValue(value);
elsif(type == "STRING") prop.setValue("" ~ value);
if (prop.getType() != "NONE") value = prop.getValue();
if (force) prop.clearValue();
if (type == nil) prop.setValue(value);
elsif (type == "DOUBLE") prop.setDoubleValue(value);
elsif (type == "INT") prop.setIntValue(value);
elsif (type == "BOOL") prop.setBoolValue(value);
elsif (type == "STRING") prop.setValue("" ~ value);
else die("initNode(): unsupported type '" ~ type ~ "'");
return prop;
}
@@ -363,7 +365,7 @@ Node.initNode = func(path = nil, value = 0, type = nil, force = 0) {
# to a key for a fun hack.
#
var dump = func {
if(size(arg) == 1) { var prefix = ""; var node = arg[0]; }
if (size(arg) == 1) { var prefix = ""; var node = arg[0]; }
else { var prefix = arg[0]; var node = arg[1]; }
var index = node.getIndex();
@@ -371,13 +373,13 @@ var dump = func {
var name = node.getName();
var val = node.getValue();
if(val == nil) { val = "nil"; }
if (val == nil) { val = "nil"; }
name = prefix ~ name;
if(index > 0) { name = name ~ "[" ~ index ~ "]"; }
if (index > 0) { name = name ~ "[" ~ index ~ "]"; }
print(name, " {", type, "} = ", val);
# Don't recurse into aliases, lest we get stuck in a loop
if(type != "ALIAS") {
if (type != "ALIAS") {
var children = node.getChildren();
foreach(var c; children) { dump(name ~ "/", c); }
}
@@ -413,12 +415,12 @@ var copy = func(src, dest, attr = 0) {
}
var type = src.getType();
var val = src.getValue();
if(type == "ALIAS" or type == "NONE") return;
elsif(type == "BOOL") dest.setBoolValue(val);
elsif(type == "INT" or type == "LONG") dest.setIntValue(val);
elsif(type == "FLOAT" or type == "DOUBLE") dest.setDoubleValue(val);
if (type == "ALIAS" or type == "NONE") return;
elsif (type == "BOOL") dest.setBoolValue(val);
elsif (type == "INT" or type == "LONG") dest.setIntValue(val);
elsif (type == "FLOAT" or type == "DOUBLE") dest.setDoubleValue(val);
else dest.setValue(val);
if(attr) dest.setAttribute(src.getAttribute());
if (attr) dest.setAttribute(src.getAttribute());
}
##
@@ -426,9 +428,9 @@ var copy = func(src, dest, attr = 0) {
# array) into Node objects.
#
var wrap = func(node) {
if(isghost(node)) {
if (isghost(node)) {
return wrapNode(node);
} elsif(isvec(node)) {
} elsif (isvec(node)) {
var v = node;
var n = size(v);
for(var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
@@ -468,13 +470,13 @@ var getNode = func return call(props.globals.getNode, arg, props.globals);
#
var setAll = func(base, child, value) {
var node = props.globals.getNode(base);
if(node == nil) return;
if (node == nil) return;
var name = node.getName();
node = node.getParent();
if(node == nil) return;
if (node == nil) return;
var children = node.getChildren();
foreach(var c; children)
if(c.getName() == name)
if (c.getName() == name)
c.getNode(child, 1).setValue(value);
}
@@ -513,19 +515,19 @@ var createNodeObjectsFromHash = func (property_list, namespace = nil) {
var nodeList = func {
var list = [];
foreach(var a; arg) {
if(isa(a, Node))
if (isa(a, Node))
append(list, a);
elsif(isscalar(a))
elsif (isscalar(a))
append(list, props.globals.getNode(a, 1));
elsif(isvec(a))
elsif (isvec(a))
foreach(var i; a)
list ~= nodeList(i);
elsif(ishash(a))
elsif (ishash(a))
foreach(var i; keys(a))
list ~= nodeList(a[i]);
elsif(isfunc(a))
elsif (isfunc(a))
list ~= nodeList(a());
elsif(isghost(a) and ghosttype(a) == "prop")
elsif (isghost(a) and ghosttype(a) == "prop")
append(list, wrapNode(a));
else
die("nodeList: invalid nil property");
@@ -541,8 +543,8 @@ var nodeList = func {
# The function returns nil on error.
#
var compileCondition = func(p) {
if(p == nil) return nil;
if(!isa(p, Node)) p = props.globals.getNode(p);
if (p == nil) return nil;
if (!isa(p, Node)) p = props.globals.getNode(p);
return _createCondition(p._g);
}
@@ -553,35 +555,35 @@ var compileCondition = func(p) {
# branch and returns nil on error.
#
var condition = func(p) {
if(p == nil) return 1;
if(!isa(p, Node)) p = props.globals.getNode(p);
if (p == nil) return 1;
if (!isa(p, Node)) p = props.globals.getNode(p);
return _cond_and(p)
}
var _cond_and = func(p) {
foreach(var c; p.getChildren())
if(!_cond(c)) return 0;
if (!_cond(c)) return 0;
return 1;
}
var _cond_or = func(p) {
foreach(var c; p.getChildren())
if(_cond(c)) return 1;
if (_cond(c)) return 1;
return 0;
}
var _cond = func(p) {
var n = p.getName();
if(n == "or") return _cond_or(p);
if(n == "and") return _cond_and(p);
if(n == "not") return !_cond_and(p);
if(n == "equals") return _cond_cmp(p, 0);
if(n == "not-equals") return !_cond_cmp(p, 0);
if(n == "less-than") return _cond_cmp(p, -1);
if(n == "greater-than") return _cond_cmp(p, 1);
if(n == "less-than-equals") return !_cond_cmp(p, 1);
if(n == "greater-than-equals") return !_cond_cmp(p, -1);
if(n == "property") return !!getprop(p.getValue());
if (n == "or") return _cond_or(p);
if (n == "and") return _cond_and(p);
if (n == "not") return !_cond_and(p);
if (n == "equals") return _cond_cmp(p, 0);
if (n == "not-equals") return !_cond_cmp(p, 0);
if (n == "less-than") return _cond_cmp(p, -1);
if (n == "greater-than") return _cond_cmp(p, 1);
if (n == "less-than-equals") return !_cond_cmp(p, 1);
if (n == "greater-than-equals") return !_cond_cmp(p, -1);
if (n == "property") return !!getprop(p.getValue());
logprint(LOG_ALERT, "condition: invalid operator ", n);
dump(p);
return nil;
@@ -589,17 +591,17 @@ var _cond = func(p) {
var _cond_cmp = func(p, op) {
var left = p.getChild("property", 0, 0);
if(left != nil) { left = getprop(left.getValue()); }
if (left != nil) { left = getprop(left.getValue()); }
else {
logprint(LOG_ALERT, "condition: no left value");
dump(p);
return nil;
}
var right = p.getChild("property", 1, 0);
if(right != nil) { right = getprop(right.getValue()); }
if (right != nil) { right = getprop(right.getValue()); }
else {
right = p.getChild("value", 0, 0);
if(right != nil) { right = right.getValue(); }
if (right != nil) { right = right.getValue(); }
else {
logprint(LOG_ALERT, "condition: no right value");
dump(p);
@@ -610,8 +612,8 @@ var _cond_cmp = func(p, op) {
if (left == nil) left = 0.0;
if (right == nil) right = 0.0;
if(op < 0) return left < right;
if(op > 0) return left > right;
if (op < 0) return left < right;
if (op > 0) return left > right;
return left == right;
}
@@ -622,7 +624,7 @@ var _cond_cmp = func(p, op) {
# in the binding.
#
var runBinding = func(node, module = nil) {
if(module != nil and node.getNode("module") == nil)
if (module != nil and node.getNode("module") == nil)
node.getNode("module", 1).setValue(module);
var cmd = node.getNode("command", 1).getValue() or "null";
condition(node.getNode("condition")) ? fgcommand(cmd, node) : 0;
@@ -702,13 +704,13 @@ var UpdateManager =
me.localType = me.property.getType();
if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE")
{
if(me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta)
if (me.lastval == nil or math.abs(me.lastval - me.curval) >= me.delta)
{
me.lastval = me.curval;
me.changed(me.curval);
}
}
else if(me.lastval == nil or me.lastval != me.curval)
else if (me.lastval == nil or me.lastval != me.curval)
{
me.lastval = me.curval;
me.changed(me.curval);

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