forked from xxq250/Nasal-Interpreter
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ffa62e5cc | ||
|
|
0f80dd7588 | ||
|
|
b09e2a7875 | ||
|
|
5aa99f396b | ||
|
|
f62d747b23 | ||
|
|
ce9f28274e | ||
|
|
f83b693f65 | ||
|
|
d535c8f7c4 | ||
|
|
958f669348 | ||
|
|
1436693cd6 | ||
|
|
4adf9541b9 | ||
|
|
43c229fc72 | ||
|
|
32c0b93e05 | ||
|
|
5e2268e4c5 | ||
|
|
5a165d3255 | ||
|
|
0cf8e3bd23 | ||
|
|
0f61f8e18e | ||
|
|
18285c1c5a | ||
|
|
b020ebf5b5 | ||
|
|
627cb9d773 | ||
|
|
886703f3bd | ||
|
|
f6d4d79c51 | ||
|
|
4ac4675491 | ||
|
|
05605c3570 | ||
|
|
c840d70a9c | ||
|
|
764a0c6b4b | ||
|
|
a7dfd34120 | ||
|
|
2c6a0fd84d | ||
|
|
40a53a4224 | ||
|
|
3b71c5fee4 | ||
|
|
b02168fc55 | ||
|
|
f9f2cf6d47 | ||
|
|
6a155f56e5 | ||
|
|
971583b1c7 | ||
|
|
b6a7b7f46d | ||
|
|
7590a286c3 | ||
|
|
77a14699f4 | ||
|
|
2c851613ce | ||
|
|
b32d4f8f82 | ||
|
|
ad87298737 | ||
|
|
4f0afb31db | ||
|
|
ba6f48c991 | ||
|
|
250667268a | ||
|
|
2bb7ebcb58 | ||
|
|
7ab40e57e3 | ||
|
|
939257d684 | ||
|
|
4550478caf | ||
|
|
bdedd0301d | ||
|
|
5a02a5bd99 | ||
|
|
1f43eae4fa | ||
|
|
290ed122ba | ||
|
|
00a655a9fc | ||
|
|
8759d12eda | ||
|
|
9b168b5d52 | ||
|
|
ef09946fd6 | ||
|
|
230848a6e1 | ||
|
|
a11e0726bb | ||
|
|
96731d180f | ||
|
|
8e38764df0 | ||
|
|
eed59cfe07 | ||
|
|
ac1960ea27 | ||
|
|
a34f90cbd1 | ||
|
|
1df1ae4a43 | ||
|
|
1c011f0aad | ||
|
|
cc6ad76eaa | ||
|
|
dbbcb134f2 | ||
|
|
539e4c4964 | ||
|
|
b7bc36bfde | ||
|
|
974d413537 | ||
|
|
7cd249f049 | ||
|
|
cc04720f12 | ||
|
|
4da8bbbd40 | ||
|
|
22b9bce298 | ||
|
|
2e321fc4d6 | ||
|
|
b3e6b5784a | ||
|
|
38c6fe2c5c | ||
|
|
4cceb63053 | ||
|
|
1831fc4245 | ||
|
|
d70864fb2e | ||
|
|
7ce8d3af25 | ||
|
|
d3840edd73 | ||
|
|
a63bb6c35a | ||
|
|
6e819391aa | ||
|
|
c59743b2ed | ||
|
|
49ebdc8e19 | ||
|
|
734aec1bc2 | ||
|
|
43ff63dc83 | ||
|
|
ca7666c220 | ||
|
|
8e0d1e18e1 | ||
|
|
a2c738d4c7 | ||
|
|
2a36db1cf5 | ||
|
|
65dbe51c05 | ||
|
|
3e226b6f73 | ||
|
|
fea52d9c38 | ||
|
|
eb753c56b8 | ||
|
|
f70ebc86b2 | ||
|
|
8666580f14 | ||
|
|
d56434ae28 | ||
|
|
f747d91efe | ||
|
|
3b1659e7ee | ||
|
|
2c03d59b6f | ||
|
|
d42e4a5897 | ||
|
|
476fbdb859 | ||
|
|
14ec9d2a34 | ||
|
|
1148ef1a08 | ||
|
|
aca8d7a011 | ||
|
|
bd3ae8c440 | ||
|
|
135665a5df | ||
|
|
f2ae974e1f | ||
|
|
4f44559a9d | ||
|
|
d55ba26c83 | ||
|
|
8c3c8d3d62 | ||
|
|
2bb9655422 | ||
|
|
cacf0ae86a | ||
|
|
01ceaf7e66 | ||
|
|
be84388f5b | ||
|
|
c453cca0a6 | ||
|
|
56e93e703e | ||
|
|
5a0d8dec20 | ||
|
|
7e5c935356 | ||
|
|
8582c0f221 | ||
|
|
28a42346b7 | ||
|
|
a03739ebb2 | ||
|
|
7d35e5bbed | ||
|
|
0b66227667 | ||
|
|
fee646965e | ||
|
|
d461cbb05c | ||
|
|
7b05a0a244 | ||
|
|
433743f790 | ||
|
|
2ea9e03522 |
94
.github/workflows/c-cpp.yml
vendored
94
.github/workflows/c-cpp.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
870
README.md
@@ -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>
|
||||
|
||||

|
||||

|
||||

|
||||
[](./LICENSE)
|
||||

|
||||
[](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__
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
[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__
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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)`__
|
||||

|
||||
|
||||
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`.
|
||||

|
||||
|
||||
### __`Linux/macOS/Unix`__
|
||||
There is a [__CMakelists.txt__](./CMakeLists.txt) to create project.
|
||||
|
||||
> mkdir build
|
||||
>
|
||||
> make -j4
|
||||
### __Linux / macOS / Unix__
|
||||
|
||||
 
|
||||
|
||||
> 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__
|
||||
|
||||

|
||||
@@ -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...) {..}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
858
doc/README_zh.md
858
doc/README_zh.md
@@ -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>
|
||||
|
||||

|
||||

|
||||

|
||||
[](../LICENSE)
|
||||

|
||||
[](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)
|
||||
|
||||
## __简介__
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
[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: [施工中...]
|
||||
|
||||
## __编译__
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
我们推荐你下载最新代码包编译,这个项目非常小巧,没有使用任何第三方库,因此编译起来非常轻松,
|
||||
你只需要这两样东西: C++ 编译器以及make程序。
|
||||
推荐下载最新代码包编译,这个项目非常小巧, 没有使用任何第三方库,因此编译起来非常轻松,
|
||||
只需要这两样东西: C++ 编译器以及make程序。
|
||||
|
||||
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器,在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
|
||||
### __Windows 平台 (MinGW-w64)__
|
||||
|
||||
### __`Windows` 平台(`MinGW-w64`)__
|
||||

|
||||
|
||||
一定要确保您的 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`中用这种方式来创建项目。
|
||||

|
||||
|
||||
### __`Linux/macOS/Unix` 平台__
|
||||
项目提供了 [__CMakeLists.txt__](../CMakeLists.txt) 用于在`Visual Studio`中创建项目。
|
||||
|
||||
> mkdir build
|
||||
>
|
||||
> make -j4
|
||||
### __Linux / macOS / Unix 平台__
|
||||
|
||||

|
||||

|
||||
|
||||
> make -j
|
||||
|
||||
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
|
||||
|
||||
> make nasal CXX=...
|
||||
> make nasal CXX=... -j
|
||||
|
||||
## __使用方法__
|
||||
|
||||

|
||||
|
||||
如果你是 `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解释器的不同之处__
|
||||
|
||||

|
||||
@@ -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...) {..}
|
||||
>>
|
||||
```
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
## 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)|
|
||||
||||
|
||||
|__Burning Ship__|__Burning Ship__|__Feigenbaum__|
|
||||
|[burningship.nas](../test/burningship.nas)|[burningship.nas](../test/burningship.nas)|[feigenbaum.nas](../test/feigenbaum.nas)|
|
||||
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# __开发历史记录__
|
||||
# __开发日志__
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -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`:
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
BIN
doc/pic/burningship_reverse.png
Normal file
BIN
doc/pic/burningship_reverse.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
BIN
doc/pic/juliaset.png
Normal file
BIN
doc/pic/juliaset.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 198 KiB |
BIN
doc/pic/mandelbrotset.png
Normal file
BIN
doc/pic/mandelbrotset.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
BIN
doc/pic/mandelbrotset_reverse.png
Normal file
BIN
doc/pic/mandelbrotset_reverse.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
766
doc/tutorial.md
Normal file
766
doc/tutorial.md
Normal file
@@ -0,0 +1,766 @@
|
||||
# __Tutorial__
|
||||
|
||||

|
||||
|
||||
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
745
doc/tutorial_zh.md
Normal file
@@ -0,0 +1,745 @@
|
||||
# __教程__
|
||||
|
||||

|
||||
|
||||
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
135
makefile
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);}
|
||||
];
|
||||
}();
|
||||
|
||||
@@ -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
120
module/libnasock.nas
Normal 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);
|
||||
}
|
||||
};
|
||||
}();
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}();
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
188
src/ast_dumper.h
188
src/ast_dumper.h
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
62
src/cli/cli.cpp
Normal 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
73
src/cli/cli.h
Normal 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&);
|
||||
|
||||
}
|
||||
200
src/main.cpp
200
src/main.cpp
@@ -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;
|
||||
}
|
||||
45
src/nasal.h
45
src/nasal.h
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
144
src/nasal_dbg.h
144
src/nasal_dbg.h
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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'? "^^^^":"^");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
216
src/nasal_gc.cpp
216
src/nasal_gc.cpp
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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 << ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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&);
|
||||
|
||||
377
src/nasal_vm.cpp
377
src/nasal_vm.cpp
@@ -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, &¶, &&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,
|
||||
&¶,
|
||||
&&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
|
||||
|
||||
367
src/nasal_vm.h
367
src/nasal_vm.h
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "natives/builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
@@ -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}
|
||||
};
|
||||
|
||||
@@ -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*);
|
||||
@@ -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) {
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "natives/builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
@@ -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;
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "natives/builtin.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -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);
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "nasal.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "natives/builtin.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
@@ -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}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
406
src/natives/json_lib.cpp
Normal 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
16
src/natives/json_lib.h
Normal 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[];
|
||||
|
||||
}
|
||||
@@ -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[] = {
|
||||
@@ -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
99
src/natives/regex_lib.cpp
Normal 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
18
src/natives/regex_lib.h
Normal 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[];
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
@@ -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>
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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*);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
@@ -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
43
src/util/fs.cpp
Normal 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
27
src/util/fs.h
Normal 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&);
|
||||
|
||||
}
|
||||
@@ -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
73
src/util/util.h
Normal 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
172
std/argparse.nas
Normal 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;
|
||||
}
|
||||
10
std/csv.nas
10
std/csv.nas
@@ -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:]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};}
|
||||
}
|
||||
441
std/fg_env.nas
441
std/fg_env.nas
@@ -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
|
||||
65
std/file.nas
65
std/file.nas
@@ -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;
|
||||
}
|
||||
18
std/io.nas
18
std/io.nas
@@ -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]
|
||||
};
|
||||
}
|
||||
|
||||
311
std/json.nas
311
std/json.nas
@@ -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);
|
||||
}
|
||||
64
std/lib.nas
64
std/lib.nas
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
24
std/log.nas
24
std/log.nas
@@ -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");
|
||||
}
|
||||
|
||||
47
std/mat.nas
47
std/mat.nas
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
147
std/phi.nas
Normal 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~" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()~"|");
|
||||
}
|
||||
|
||||
122
std/props.nas
122
std/props.nas
@@ -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
Reference in New Issue
Block a user