Compare commits
241 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d121dcd630 | ||
|
|
c705b75513 | ||
|
|
3ef8effe9a | ||
|
|
3fd1b25f79 | ||
|
|
025ff49ffc | ||
|
|
7a93527948 | ||
|
|
405175061a | ||
|
|
ae85791f01 | ||
|
|
3cb5d0f2d9 | ||
|
|
a3f5dc01d3 | ||
|
|
35d7772dd3 | ||
|
|
e25eb76e94 | ||
|
|
946aa020a5 | ||
|
|
6ef22d3228 | ||
|
|
c4cac512a6 | ||
|
|
dc3770094a | ||
|
|
791de656c3 | ||
|
|
cff9f91bee | ||
|
|
06f02ec0cb | ||
|
|
24ae1c246f | ||
|
|
0576459fbe | ||
|
|
518ce7fcb9 | ||
|
|
0e682b7c07 | ||
|
|
26f4e1359f | ||
|
|
aa5b1d3d66 | ||
|
|
6a6eab8db5 | ||
|
|
91b3074ce9 | ||
|
|
10e579dabc | ||
|
|
add5e0c2cd | ||
|
|
1e0f0f8e7b | ||
|
|
972ad49a4f | ||
|
|
a13e419518 | ||
|
|
6c04487319 | ||
|
|
5715c1df5f | ||
|
|
aa0023a23b | ||
|
|
f86ea2445f | ||
|
|
52a38709bb | ||
|
|
b022b25cea | ||
|
|
27ceeb517d | ||
|
|
8293f85c5b | ||
|
|
0e578b3e21 | ||
|
|
24ba300f3c | ||
|
|
5be6351b60 | ||
|
|
a91826607c | ||
|
|
987d3ce9e2 | ||
|
|
da8aa4744e | ||
|
|
978957aca7 | ||
|
|
caf048aae4 | ||
|
|
692f8ccefe | ||
|
|
007b83bed5 | ||
|
|
73278ea2dd | ||
|
|
732a00a1cd | ||
|
|
bb0a2e3669 | ||
|
|
2f43c47e77 | ||
|
|
d65868f1e9 | ||
|
|
170f34a5e2 | ||
|
|
0493e18d0e | ||
|
|
7ddb8593ad | ||
|
|
2937c10c77 | ||
|
|
feea8f8d66 | ||
|
|
e51878266a | ||
|
|
37841fc91e | ||
|
|
44bfd74ca9 | ||
|
|
04a45064c8 | ||
|
|
068743aa4c | ||
|
|
7ad1d69c64 | ||
|
|
52fcc9118c | ||
|
|
c0862704f0 | ||
|
|
854850d9b1 | ||
|
|
006ed644e6 | ||
|
|
33e584ab5c | ||
|
|
db896e932f | ||
|
|
3e8b9e4846 | ||
|
|
ea33ce020c | ||
|
|
ac8652f864 | ||
|
|
4c95d622f6 | ||
|
|
61dcfb9395 | ||
|
|
899b327608 | ||
|
|
c702f02a8a | ||
|
|
afbee18e26 | ||
|
|
c6129267aa | ||
|
|
6187395ce8 | ||
|
|
5dcf2ede66 | ||
|
|
99a131c552 | ||
|
|
46516485b5 | ||
|
|
e9fc70bba8 | ||
|
|
8ca0bc52c3 | ||
|
|
af761641d6 | ||
|
|
f68a512845 | ||
|
|
f7cd3a027b | ||
|
|
cc4ff38f28 | ||
|
|
cfbec9a3f1 | ||
|
|
d904123695 | ||
|
|
d8d457ce74 | ||
|
|
b3d65f0183 | ||
|
|
fc7e0a569e | ||
|
|
51f780e637 | ||
|
|
577ecd14df | ||
|
|
f1f48b4881 | ||
|
|
29084143d6 | ||
|
|
d1a7d32e7b | ||
|
|
8cc69c709f | ||
|
|
bcdc55a652 | ||
|
|
2ed20f6362 | ||
|
|
9890b46f02 | ||
|
|
11e9567b55 | ||
|
|
5d4cff0aa8 | ||
|
|
c1d13ecd85 | ||
|
|
824a31ef55 | ||
|
|
fb25a4973c | ||
|
|
75c46fa727 | ||
|
|
bd5044add2 | ||
|
|
b638708722 | ||
|
|
a04ed2a4aa | ||
|
|
82b33ffe4a | ||
|
|
2bc15697df | ||
|
|
92d68b357c | ||
|
|
452bb4a5d8 | ||
|
|
e8bd4664b3 | ||
|
|
5bc1925082 | ||
|
|
8b8fb79013 | ||
|
|
c993c77b78 | ||
|
|
a4a7f5075a | ||
|
|
4c0ef0fb99 | ||
|
|
9114ecd820 | ||
|
|
abbdb22478 | ||
|
|
4f62d2c7ea | ||
|
|
bb84c6d143 | ||
|
|
e27f773749 | ||
|
|
478d1aadf9 | ||
|
|
393b37d109 | ||
|
|
c2a23ecba6 | ||
|
|
ef3afa14b9 | ||
|
|
6b20e0f167 | ||
|
|
dea9c4ad3b | ||
|
|
f59c3c7787 | ||
|
|
0515e388da | ||
|
|
21b345af27 | ||
|
|
04806fc2c5 | ||
|
|
12d7dde42d | ||
|
|
53b85cd459 | ||
|
|
a2e2d5d8f6 | ||
|
|
cb0fee04a9 | ||
|
|
4e6cd82ccb | ||
|
|
c3a8e118f5 | ||
|
|
7ec8418740 | ||
|
|
04ab09586b | ||
|
|
ab37495960 | ||
|
|
882ecac100 | ||
|
|
36a7868b0b | ||
|
|
190014a3e5 | ||
|
|
ebfacd9197 | ||
|
|
99dca532f6 | ||
|
|
dcad554eba | ||
|
|
07eeaadf96 | ||
|
|
120ceb429a | ||
|
|
d567f5abf8 | ||
|
|
f8692f1e4e | ||
|
|
712a047a43 | ||
|
|
396d55a207 | ||
|
|
c5171c735a | ||
|
|
07857d980c | ||
|
|
51a1279110 | ||
|
|
de262980cc | ||
|
|
23a5c1b1ad | ||
|
|
fd8a148d0c | ||
|
|
9c7f5f1a6e | ||
|
|
f049e1f9fb | ||
|
|
c4b7712e53 | ||
|
|
7417d5e635 | ||
|
|
785572634b | ||
|
|
2dc8459cbf | ||
|
|
6e035d1951 | ||
|
|
4f5fd3de33 | ||
|
|
fc25dd69e1 | ||
|
|
ca073499ae | ||
|
|
022460755f | ||
|
|
b6f174e869 | ||
|
|
7b160c4589 | ||
|
|
4503239731 | ||
|
|
c12e812651 | ||
|
|
87cff700e8 | ||
|
|
bf5737ecfd | ||
|
|
651ae4ef77 | ||
|
|
e846e51175 | ||
|
|
399b2f0ce9 | ||
|
|
aed5e27409 | ||
|
|
a2b51fe212 | ||
|
|
92b684624d | ||
|
|
83a8632e8e | ||
|
|
41b5304712 | ||
|
|
c5a12ade5c | ||
|
|
7a939b417d | ||
|
|
dd7740f1fd | ||
|
|
617ad03d33 | ||
|
|
b66ebbef4b | ||
|
|
7f22b25909 | ||
|
|
86f6296268 | ||
|
|
cf722fd98c | ||
|
|
1dd3fd445c | ||
|
|
27e25f84ec | ||
|
|
e6457651d3 | ||
|
|
c4d52a88cd | ||
|
|
9bcad59e45 | ||
|
|
9a099f66cb | ||
|
|
b79d60fab5 | ||
|
|
82e9e97a26 | ||
|
|
6a1338bb23 | ||
|
|
3b8a092f36 | ||
|
|
3d86a32b12 | ||
|
|
f26719e1d3 | ||
|
|
e54ef9620f | ||
|
|
d8156e839b | ||
|
|
61666d275d | ||
|
|
99f595e16f | ||
|
|
debe32b187 | ||
|
|
ca9b8581b4 | ||
|
|
a0b341deb5 | ||
|
|
d3df356299 | ||
|
|
cd808a5e6d | ||
|
|
40f61a9dd4 | ||
|
|
f312250d27 | ||
|
|
3fac8aa665 | ||
|
|
243aafd417 | ||
|
|
6bc03601d9 | ||
|
|
f05acaecc7 | ||
|
|
9456a903d7 | ||
|
|
984deed883 | ||
|
|
557cb2ebcf | ||
|
|
9c055a9a23 | ||
|
|
13a09343e6 | ||
|
|
8c67e04cc4 | ||
|
|
e77bb73a82 | ||
|
|
05fc5db337 | ||
|
|
a4738e8c7d | ||
|
|
5f6051e333 | ||
|
|
51afe3dacd | ||
|
|
0291908675 | ||
|
|
5fba784d05 | ||
|
|
9139e34c0b | ||
|
|
e7f503fae1 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.nas linguist-language=Nasal
|
||||
9
.github/workflows/c-cpp.yml
vendored
@@ -17,7 +17,12 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make
|
||||
run: |
|
||||
make
|
||||
cd module
|
||||
make all
|
||||
cd ..
|
||||
tar -czf nasal-mac-nightly.tgz .
|
||||
- name: Release file
|
||||
# You may pin to the exact commit or the version.
|
||||
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
|
||||
@@ -30,5 +35,5 @@ jobs:
|
||||
# Name of the tag for the release (will be associated with current branch)
|
||||
automatic_release_tag: next
|
||||
# File to release
|
||||
files: nasal
|
||||
files: nasal-mac-nightly.tgz
|
||||
|
||||
|
||||
20
.gitignore
vendored
@@ -31,6 +31,24 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# VS C++ sln
|
||||
*.sln
|
||||
*.vcxproj
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
.vs
|
||||
x64
|
||||
|
||||
# nasal executable
|
||||
nasal
|
||||
nasal.exe
|
||||
|
||||
# misc
|
||||
.vscode
|
||||
dump
|
||||
dump
|
||||
|
||||
# macOS special cache directory
|
||||
.DS_Store
|
||||
|
||||
# ppm picture generated by ppmgen.nas
|
||||
*.ppm
|
||||
950
doc/README_zh.md
Normal file
@@ -0,0 +1,950 @@
|
||||
# __Nasal 脚本语言__
|
||||
|
||||

|
||||

|
||||

|
||||
[](../LICENSE)
|
||||
|
||||
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
||||
|
||||
## __目录__
|
||||
|
||||
* [__简介__](#简介)
|
||||
* [__编译__](#编译)
|
||||
* [__使用方法__](#使用方法)
|
||||
* [__教程__](#教程)
|
||||
* [__发行日志__](../doc/dev_zh.md#发行日志)
|
||||
* [__开发历史__](../doc/dev_zh.md)
|
||||
* [__测试数据__](../doc/benchmark.md)
|
||||
* [__特殊之处__](#与andy解释器的不同之处)
|
||||
* [__堆栈追踪信息__](#trace-back-info)
|
||||
* [__调试器__](#调试器)
|
||||
|
||||
__如果有好的意见或建议,欢迎联系我们!__
|
||||
|
||||
* __E-mail__: __lhk101lhk101@qq.com__
|
||||
|
||||
## __简介__
|
||||
|
||||
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
|
||||
是一个与ECMAscript标准语法设计相似的编程语言,并且作为运行脚本被著名的开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所依赖。
|
||||
该语言的设计者为 [Andy Ross](https://github.com/andyross)。
|
||||
|
||||
该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且容易上手的编程语言。
|
||||
|
||||
该项目已使用 __MIT__ 协议开源 (2021/5/4)。
|
||||
|
||||
__我们为什么想要重新写一个nasal解释器?__
|
||||
2019年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在Flightgear中提供的nasal控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误,甚至是检查运行时的错误。
|
||||
|
||||
我编写了nasal的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误提高了我们的工作效率。
|
||||
|
||||
你也可以使用这个语言来写一些与Flightgear运行环境无关的有趣的程序,并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块,使它成为项目中一个非常有用的工具。
|
||||
|
||||
## __编译__
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
我们推荐你下载最新更新的代码包来直接编译,这个项目非常小巧因此你可以非常快速地将它编译出来。
|
||||
__注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构建这个解释器,在下载之前请阅读[__发行日志__](../doc/dev_zh.md#发行日志)以保证这个发行版的文件中不包含非常严重的bug。
|
||||
|
||||
`Windows` 用户通过 g++(`MinGW-w64`) 或者使用 MSVC(`Visual Studio`) 来进行编译。
|
||||
|
||||
`Linux/macOS/Unix` 用户可以使用 g++ 或者 clang++ 来进行编译 (建议您使用 `clang`)。
|
||||
|
||||
`Windows` 平台(`MinGW-w64`):
|
||||
|
||||
> mingw32-make nasal.exe
|
||||
|
||||
你也可以在`Visual Studio`中用这种方式来创建项目:[__点击跳转__](../doc/vs.md)。
|
||||
|
||||
`Linux/macOS/Unix` 平台:
|
||||
|
||||
> make nasal
|
||||
|
||||
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
|
||||
|
||||
> make nasal CXX=...
|
||||
|
||||
如果你觉得`-O3`编译的版本不是那么安全和稳定,你也可以选择生成稳定的版本:
|
||||
|
||||
> make stable-release
|
||||
|
||||
## __使用方法__
|
||||
|
||||

|
||||
|
||||
如果你是 `Windows` 用户且想正常输出unicode,在nasal代码里写这个来开启unicode代码页:
|
||||
|
||||
```javascript
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
```
|
||||
|
||||
## __教程__
|
||||
|
||||
Nasal是非常容易上手的,你甚至可以在15分钟之内看完这里的基本教程并且直接开始编写你想要的程序。
|
||||
__如果你先前已经是C/C++, javascript选手,那么几乎可以不用看这个教程……__ 在看完该教程之后,基本上你就完全掌握了这个语言:
|
||||
|
||||
<details><summary>基本类型</summary>
|
||||
|
||||
__`none`__ 是特殊的错误类型。这个类型用于终止虚拟机的执行,该类型只能由虚拟机在抛出错误时产生。
|
||||
|
||||
__`nil`__ 是空类型。类似于null。
|
||||
|
||||
```javascript
|
||||
var spc=nil;
|
||||
```
|
||||
|
||||
__`num`__ 有三种形式:十进制,十六进制以及八进制。并且该类型使用IEEE754标准的浮点数`double`格式来存储。
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
__`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`__ 中使用,以确保闭包功能正常。
|
||||
|
||||
__`obj`__ 是用来存储`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;
|
||||
```
|
||||
|
||||
赋值运算符`=` `+=` `-=` `*=` `/=` `~=`正如其名,用于进行赋值。
|
||||
|
||||
```javascript
|
||||
a=b=c=d=1;
|
||||
a+=1;
|
||||
a-=1;
|
||||
a*=1;
|
||||
a/=1;
|
||||
a~="string";
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>定义变量</summary>
|
||||
|
||||
如下所示。
|
||||
|
||||
```javascript
|
||||
var a=1; # 定义单个变量
|
||||
var (a,b,c)=[0,1,2]; # 从数组中初始化多个变量
|
||||
var (a,b,c)=(0,1,2); # 从元组中初始化多个变量
|
||||
```
|
||||
|
||||
</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++
|
||||
// 你可以使用这个宏来直接定义一个新的内置函数
|
||||
nas_native(builtin_print);
|
||||
```
|
||||
|
||||
然后用C++完成这个函数的函数体:
|
||||
|
||||
```C++
|
||||
var builtin_print(var* local,gc& ngc)
|
||||
{
|
||||
// 局部变量的下标其实是从1开始的
|
||||
// 因为local[0]是保留给'me'的空间
|
||||
var vec=local[1];
|
||||
// 主要部分
|
||||
// 一些必要的类型检查和输入合法性检测也要在这里写出
|
||||
// 如果检测到问题,用builtin_err函数来返回vm_null
|
||||
// 并且狠狠地骂那些不好好写代码的混蛋(玩笑)
|
||||
for(auto& i:vec.vec().elems)
|
||||
switch(i.type)
|
||||
{
|
||||
case vm_none: std::cout<<"undefined"; break;
|
||||
case vm_nil: std::cout<<"nil"; break;
|
||||
case vm_num: std::cout<<i.num(); break;
|
||||
case vm_str: std::cout<<i.str(); break;
|
||||
case vm_vec: std::cout<<i.vec(); break;
|
||||
case vm_hash: std::cout<<i.hash(); break;
|
||||
case vm_func: std::cout<<"func(..){..}";break;
|
||||
case vm_obj: std::cout<<"<object>"; break;
|
||||
}
|
||||
std::cout<<std::flush;
|
||||
// 最后一定要记得生成返回值,返回值必须是一个内置的类型,
|
||||
// 可以使用ngc::alloc(type)来申请一个需要内存管理的复杂数据结构
|
||||
// 或者用我们已经定义好的nil/one/zero,这些可以直接使用
|
||||
return nil;
|
||||
}
|
||||
```
|
||||
|
||||
当运行内置函数的时候,内存分配器如果运行超过一次,那么会有更大可能性多次触发垃圾收集器的mark-sweep。这个操作会在`gc::alloc`中触发。
|
||||
如果先前获取的数值没有被正确存到可以被垃圾收集器索引到的地方,那么它会被错误地回收,这会导致严重的错误。
|
||||
|
||||
可以使用`gc::temp`来暂时存储一个会被返回的需要gc管理的变量,这样可以防止内部所有的申请错误触发垃圾回收。如下所示:
|
||||
|
||||
```C++
|
||||
var builtin_keys(var* local,gc& ngc)
|
||||
{
|
||||
var hash=local[1];
|
||||
if(hash.type!=vm_hash)
|
||||
return nas_err("keys","\"hash\" must be hash");
|
||||
// 使用gc.temp来存储gc管理的变量,防止错误的回收
|
||||
var res=ngc.temp=ngc.alloc(vm_vec);
|
||||
auto& vec=res.vec().elems;
|
||||
for(auto& iter:hash.hash().elems)
|
||||
vec.push_back(ngc.newstr(iter.first));
|
||||
ngc.temp=nil;
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
这些工作都完成之后,在内置函数注册表中填写它在nasal中的别名,并且在表中填对这个函数的函数指针:
|
||||
|
||||
```C++
|
||||
struct func
|
||||
{
|
||||
const char* name;
|
||||
var (*func)(var*,gc&);
|
||||
} 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
|
||||
import.dirname.dirname.filename;
|
||||
import("./dirname/dirname/filename.nas");
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>模块(开发者教程)</summary>
|
||||
|
||||
如果只有上文中那种方式来添加你自定义的函数到nasal中,这肯定是非常麻烦的。
|
||||
因此,我们实现了一组实用的内置函数来帮助你添加你自己创建的模块。
|
||||
|
||||
在2021/12/3更新后,我们给`lib.nas`添加了下面的这一批函数:
|
||||
|
||||
```javascript
|
||||
var dylib=
|
||||
{
|
||||
dlopen: func(libname){return __dlopen;},
|
||||
dlsym: func(lib,sym){return __dlsym; },
|
||||
dlclose: func(lib){return __dlclose; },
|
||||
dlcall: func(funcptr,args...){return __dlcall;}
|
||||
};
|
||||
```
|
||||
|
||||
看名字就大概能猜出来,这些函数就是用来加载动态库的,这样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){
|
||||
// 传参会给予一个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 {vm_num,fibonaci(num.tonum())};
|
||||
}
|
||||
|
||||
// 必须实现这个函数, 这样nasal可以通过字符串名字获得函数指针
|
||||
// 之所以用这种方式来获取函数指针, 是因为`var`是有构造函数的
|
||||
// 有构造函数的类型作为返回值, 和C是不兼容的, 这导致
|
||||
// 类似 "extern "C" var fib" 的写法会得到编译错误
|
||||
extern "C" mod get(const char* n){
|
||||
string name=n;
|
||||
if(name=="fib")
|
||||
return fib;
|
||||
return nullptr;
|
||||
}
|
||||
```
|
||||
|
||||
接着我们把`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
|
||||
var dlhandle=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var fib=dylib.dlsym(dlhandle,"fib");
|
||||
for(var i=1;i<30;i+=1)
|
||||
println(dylib.dlcall(fib,i));
|
||||
dylib.dlclose(dlhandle);
|
||||
```
|
||||
|
||||
`dylib.dlopen`用于加载动态库。
|
||||
|
||||
`dylib.dlsym`通过符号从动态库中获得函数地址。
|
||||
|
||||
`dylib.dlcall`用于调用函数,第一个参数是动态库函数的地址,这是个特殊类型,一定要保证这个参数是vm_obj类型并且type=obj_extern。
|
||||
|
||||
`dylib.dlclose`用于卸载动态库,当然,在这个函数调用之后,所有从该库中获取的函数都作废。
|
||||
|
||||
如果接下来你看到了这个运行结果,恭喜你!
|
||||
|
||||
```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>
|
||||
|
||||
## __与andy解释器的不同之处__
|
||||
|
||||

|
||||
|
||||
<details><summary>必须用 var 定义变量</summary>
|
||||
|
||||
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格,否则debug会非常痛苦。
|
||||
|
||||
在Andy的解释器中:
|
||||
|
||||
```javascript
|
||||
foreach(i;[0,1,2,3])
|
||||
print(i)
|
||||
```
|
||||
|
||||
这个程序可以正常运行。然而这个`i`标识符实际上在这里是被第一次定义,而且没有使用`var`。我认为这样的设计很容易让使用者迷惑。他们可能都没有发现这里实际上是第一次定义`i`的地方。没有使用`var`的定义会让程序员认为这个`i`也许是在别的地方定义的。
|
||||
|
||||
所以在这个解释器中,我直接使用严格的语法检查方法来强行要求用户必须要使用`var`来定义新的变量或者迭代器。如果你忘了加这个关键字,那么你就会得到这个:
|
||||
|
||||
```javascript
|
||||
code: undefined symbol "i"
|
||||
--> test.nas:1:9
|
||||
|
|
||||
1 | foreach(i;[0,1,2,3])
|
||||
| ^ undefined symbol "i"
|
||||
|
||||
code: undefined symbol "i"
|
||||
--> test.nas:2:11
|
||||
|
|
||||
2 | print(i)
|
||||
| ^ undefined symbol "i"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details><summary>默认不定长参数</summary>
|
||||
|
||||
这个解释器在运行时,函数不会将超出参数表的那部分不定长参数放到默认的`arg`中。所以你如果不定义`arg`就使用它,那你只会得到`undefined symbol`。
|
||||
|
||||
```javascript
|
||||
var f=func(){
|
||||
println(arg)
|
||||
}
|
||||
f(1,2,3);
|
||||
```
|
||||
|
||||
编译结果:
|
||||
|
||||
```javascript
|
||||
code: undefined symbol "arg"
|
||||
--> test.nas:2:15
|
||||
|
|
||||
2 | println(arg)
|
||||
| ^ undefined symbol "arg"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## __堆栈追踪信息__
|
||||
|
||||

|
||||
|
||||
当解释器崩溃时,它会反馈错误产生过程的堆栈追踪信息:
|
||||
|
||||
<details><summary>内置函数 die</summary>
|
||||
|
||||
`die`函数用于直接抛出错误并终止执行。
|
||||
|
||||
```javascript
|
||||
func()
|
||||
{
|
||||
println("hello");
|
||||
die("error occurred this line");
|
||||
return;
|
||||
}();
|
||||
```
|
||||
|
||||
```javascript
|
||||
hello
|
||||
[vm] error: error occurred this line
|
||||
[vm] native function error.
|
||||
trace back:
|
||||
0x000000ac 40 00 00 00 25 callb 0x25 <__die@0x41afc0> (lib.nas:131)
|
||||
0x000004f6 3e 00 00 00 01 callfv 0x1 (a.nas:4)
|
||||
0x000004fa 3e 00 00 00 00 callfv 0x0 (a.nas:6)
|
||||
vm stack (0x7fffcd21bc68 <sp+80>, limit 10, total 12):
|
||||
0x0000005b | null |
|
||||
...
|
||||
0x00000057 | str | <0x138ff60> error occurred t...
|
||||
...
|
||||
0x00000052 | nil |
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>栈溢出</summary>
|
||||
|
||||
这是一个会导致栈溢出的例子:
|
||||
|
||||
```javascript
|
||||
func(f){
|
||||
return f(f);
|
||||
}(
|
||||
func(f){
|
||||
f(f);
|
||||
}
|
||||
)();
|
||||
```
|
||||
|
||||
```javascript
|
||||
[vm] stack overflow
|
||||
trace back:
|
||||
0x000004fb 3e 00 00 00 01 callfv 0x1 (a.nas:5)
|
||||
0x000004fb 1349 same call(s)
|
||||
0x000004f3 3e 00 00 00 01 callfv 0x1 (a.nas:2)
|
||||
0x000004ff 3e 00 00 00 01 callfv 0x1 (a.nas:3)
|
||||
vm stack (0x7fffd3781d58 <sp+80>, limit 10, total 8108):
|
||||
0x00001ffb | func | <0x15f8d90> entry:0x4f9
|
||||
0x00001ffa | func | <0x15f8d90> entry:0x4f9
|
||||
0x00001ff9 | pc | 0x4fb
|
||||
...
|
||||
0x00001ff2 | addr | 0x7fffd37a16e8
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>运行时错误</summary>
|
||||
|
||||
如果在执行的时候出现错误,程序会直接终止执行:
|
||||
|
||||
```javascript
|
||||
func(){
|
||||
return 0;
|
||||
}()[1];
|
||||
```
|
||||
|
||||
```javascript
|
||||
[vm] callv: must call a vector/hash/string
|
||||
trace back:
|
||||
0x000004f4 3b 00 00 00 00 callv 0x0 (a.nas:3)
|
||||
vm stack (0x7fffff539c28 <sp+80>, limit 10, total 1):
|
||||
0x00000050 | num | 0
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>详细的崩溃信息</summary>
|
||||
|
||||
使用命令 __`-d`__ 或 __`--detail`__ 后,trace back信息会包含更多的细节内容:
|
||||
|
||||
```javascript
|
||||
hello
|
||||
[vm] error: error occurred this line
|
||||
[vm] error: native function error
|
||||
trace back (main)
|
||||
0x000000b0 40 00 00 00 2b callb 0x2b <__die@0x41c380> (lib.nas:131)
|
||||
0x00000553 3e 00 00 00 01 callfv 0x1 (test.nas:4)
|
||||
0x00000557 3e 00 00 00 00 callfv 0x0 (test.nas:6)
|
||||
vm stack (0x7fffe0ffed90 <sp+63>, limit 10, total 12)
|
||||
0x0000004a | null |
|
||||
0x00000049 | pc | 0x553
|
||||
0x00000048 | addr | 0x7fffe0ffeda0
|
||||
...
|
||||
0x00000041 | nil |
|
||||
registers (main)
|
||||
[ pc ] | pc | 0xb0
|
||||
[ global ] | addr | 0x7fffe0ffe9a0
|
||||
[ localr ] | addr | 0x7fffe0ffedf0
|
||||
[ memr ] | addr | 0x0
|
||||
[ canary ] | addr | 0x7fffe1002990
|
||||
[ top ] | addr | 0x7fffe0ffee40
|
||||
[ funcr ] | func | <0x677cd0> entry:0xb0
|
||||
[ upvalr ] | nil |
|
||||
global (0x7fffe0ffe9a0 <sp+0>)
|
||||
0x00000000 | func | <0x65fb00> entry:0x5
|
||||
0x00000001 | func | <0x65fb20> entry:0xd
|
||||
...
|
||||
0x0000003d | func | <0x66bf00> entry:0x51f
|
||||
0x0000003e | hash | <0x65ffa0> {5 val}
|
||||
local (0x7fffe0ffedf0 <sp+45>)
|
||||
0x00000000 | nil |
|
||||
0x00000001 | str | <0x6cb630> error occurred t...
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## __调试器__
|
||||
|
||||

|
||||
|
||||
在`v8.0`版本中我们添加了调试器。
|
||||
使用这个命令`./nasal -dbg xxx.nas`来启用调试器,接下来调试器会打开文件并输出以下内容:
|
||||
|
||||
<details><summary>展开</summary>
|
||||
|
||||
```javascript
|
||||
[debug] nasal debug mode
|
||||
input 'h' to get help
|
||||
|
||||
source code:
|
||||
--> var fib=func(x)
|
||||
{
|
||||
if(x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
next bytecode:
|
||||
--> 0x00000000 01 00 00 00 41 intg 0x41 (test/fib.nas:0)
|
||||
0x00000001 0b 00 00 00 05 newf 0x5 (lib.nas:6)
|
||||
0x00000002 02 00 00 00 02 intl 0x2 (lib.nas:6)
|
||||
0x00000003 0f 00 00 00 00 dyn 0x0 ("elems") (lib.nas:6)
|
||||
0x00000004 32 00 00 00 07 jmp 0x7 (lib.nas:6)
|
||||
0x00000005 40 00 00 00 00 callb 0x0 <__print@0x419c80> (lib.nas:7)
|
||||
0x00000006 4a 00 00 00 00 ret 0x0 (lib.nas:7)
|
||||
0x00000007 03 00 00 00 00 loadg 0x0 (lib.nas:6)
|
||||
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 0)
|
||||
>>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
如果需要查看命令的使用方法,可以输入`h`获取帮助信息。
|
||||
|
||||
当运行调试器的时候,你可以看到现在的操作数栈上到底有些什么数据。
|
||||
这些信息可以帮助你调试,同时也可以帮助你理解这个虚拟机是如何工作的:
|
||||
|
||||
<details><summary>展开</summary>
|
||||
|
||||
```javascript
|
||||
source code:
|
||||
var fib=func(x)
|
||||
{
|
||||
--> if(x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
|
||||
next bytecode:
|
||||
0x00000548 0c 00 00 00 aa happ 0xaa ("running") (lib.nas:503)
|
||||
0x00000549 03 00 00 00 3e loadg 0x3e (lib.nas:498)
|
||||
0x0000054a 0b 00 00 05 4e newf 0x54e (test/fib.nas:1)
|
||||
0x0000054b 02 00 00 00 02 intl 0x2 (test/fib.nas:1)
|
||||
0x0000054c 0d 00 00 00 1b para 0x1b ("x") (test/fib.nas:1)
|
||||
0x0000054d 32 00 00 05 5d jmp 0x55d (test/fib.nas:1)
|
||||
--> 0x0000054e 39 00 00 00 01 calll 0x1 (test/fib.nas:3)
|
||||
0x0000054f 2d 00 00 00 03 lessc 0x3 (2) (test/fib.nas:3)
|
||||
vm stack (0x7fffd0259138 <sp+65>, limit 10, total 7)
|
||||
0x00000047 | pc | 0x566
|
||||
0x00000046 | addr | 0x0
|
||||
0x00000045 | nil |
|
||||
0x00000044 | num | 0
|
||||
0x00000043 | nil |
|
||||
0x00000042 | nil |
|
||||
0x00000041 | func | <0x88d2f0> entry:0x5
|
||||
>>
|
||||
```
|
||||
|
||||
</details>
|
||||
112
doc/benchmark.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# __Benchmark__
|
||||
|
||||

|
||||
|
||||
## version 6.5 (i5-8250U windows10 2021/6/19)
|
||||
|
||||
running time and gc time:
|
||||
|
||||
|file|call gc|total time|gc time|
|
||||
|:----|:----|:----|:----|
|
||||
|pi.nas|12000049|0.593s|0.222s|
|
||||
|fib.nas|10573747|2.838s|0.187s|
|
||||
|bp.nas|4419829|1.99s|0.18s|
|
||||
|bigloop.nas|4000000|0.419s|0.039s|
|
||||
|mandelbrot.nas|1044630|0.433s|0.041s|
|
||||
|life.nas|817112|8.557s|0.199s|
|
||||
|ascii-art.nas|45612|0.48s|0.027s|
|
||||
|calc.nas|8089|0.068s|0.006s|
|
||||
|quick_sort.nas|2768|0.107s|0s|
|
||||
|bfs.nas|2471|1.763s|0.003s|
|
||||
|
||||
operands calling frequency:
|
||||
|
||||
|file|1st|2nd|3rd|4th|5th|
|
||||
|:----|:----|:----|:----|:----|:----|
|
||||
|pi.nas|callg|pop|mcallg|pnum|pone|
|
||||
|fib.nas|calll|pnum|callg|less|jf|
|
||||
|bp.nas|calll|callg|pop|callv|addeq|
|
||||
|bigloop.nas|pnum|less|jf|callg|pone|
|
||||
|mandelbrot.nas|callg|mult|loadg|pnum|pop|
|
||||
|life.nas|calll|callv|pnum|jf|callg|
|
||||
|ascii-art.nas|calll|pop|mcalll|callg|callb|
|
||||
|calc.nas|calll|pop|pstr|mcalll|jmp|
|
||||
|quick_sort.nas|calll|pop|jt|jf|less|
|
||||
|bfs.nas|calll|pop|callv|mcalll|jf|
|
||||
|
||||
operands calling total times:
|
||||
|
||||
|file|1st|2nd|3rd|4th|5th|
|
||||
|:----|:----|:----|:----|:----|:----|
|
||||
|pi.nas|6000004|6000003|6000000|4000005|4000002|
|
||||
|fib.nas|17622792|10573704|7049218|7049155|7049155|
|
||||
|bp.nas|7081480|4227268|2764676|2617112|2065441|
|
||||
|bigloop.nas|4000001|4000001|4000001|4000001|4000000|
|
||||
|mandelbrot.nas|1519632|563856|290641|286795|284844|
|
||||
|life.nas|2114371|974244|536413|534794|489743|
|
||||
|ascii-art.nas|37906|22736|22402|18315|18292|
|
||||
|calc.nas|191|124|109|99|87|
|
||||
|quick_sort.nas|16226|5561|4144|3524|2833|
|
||||
|bfs.nas|24707|16297|14606|14269|8672|
|
||||
|
||||
## version 7.0 (i5-8250U ubuntu-WSL on windows10 2021/6/29)
|
||||
|
||||
running time:
|
||||
|
||||
|file|total time|info|
|
||||
|:----|:----|:----|
|
||||
|pi.nas|0.15625s|great improvement|
|
||||
|fib.nas|0.75s|great improvement|
|
||||
|bp.nas|0.4218s(7162 epoch)|good improvement|
|
||||
|bigloop.nas|0.09375s|great improvement|
|
||||
|mandelbrot.nas|0.0312s|great improvement|
|
||||
|life.nas|8.80s(windows) 1.25(ubuntu WSL)|little improvement|
|
||||
|ascii-art.nas|0.015s|little improvement|
|
||||
|calc.nas|0.0468s|little improvement|
|
||||
|quick_sort.nas|0s|great improvement|
|
||||
|bfs.nas|0.0156s|great improvement|
|
||||
|
||||
## version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)
|
||||
|
||||
running time:
|
||||
|
||||
|file|total time|info|
|
||||
|:----|:----|:----|
|
||||
|bf.nas|1100.19s||
|
||||
|mandel.nas|28.98s||
|
||||
|life.nas|0.56s|0.857s(windows)|
|
||||
|ycombinator.nas|0.64s||
|
||||
|fib.nas|0.28s||
|
||||
|bfs.nas|0.156s|random result|
|
||||
|pi.nas|0.0625s||
|
||||
|bigloop.nas|0.047s||
|
||||
|calc.nas|0.03125s|changed test file|
|
||||
|mandelbrot.nas|0.0156s||
|
||||
|ascii-art.nas|0s||
|
||||
|quick_sort.nas|0s||
|
||||
|
||||
## version 9.0 (R9-5900HX ubuntu-WSL 2022/2/13)
|
||||
|
||||
running time:
|
||||
|
||||
|file|total time|info|
|
||||
|:----|:----|:----|
|
||||
|bf.nas|276.55s|great improvement|
|
||||
|mandel.nas|28.16s||
|
||||
|ycombinator.nas|0.59s||
|
||||
|life.nas|0.2s|0.649s(windows)|
|
||||
|fib.nas|0.234s|little improvement|
|
||||
|bfs.nas|0.14s|random result|
|
||||
|pi.nas|0.0625s||
|
||||
|bigloop.nas|0.047s||
|
||||
|calc.nas|0.0469s|changed test file|
|
||||
|quick_sort.nas|0.016s|changed test file:100->1e4|
|
||||
|mandelbrot.nas|0.0156s||
|
||||
|ascii-art.nas|0s||
|
||||
|
||||
`bf.nas` is a very interesting test file that there is a brainfuck interpreter written in nasal.
|
||||
And we use this bf interpreter to draw a mandelbrot set.
|
||||
|
||||
In 2022/2/17 update we added `\e` into the lexer. And the `bfcolored.nas` uses this special ASCII code. Here is the result:
|
||||
|
||||

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

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

|
||||
|
||||
### version 4.0 vm (last update 2020/12/17)
|
||||
|
||||
我在这个版本实现了第一版字节码虚拟机。不过这个虚拟机仍然在测试中,在这次测试结束之后,我会发布v4.0发行版。
|
||||
|
||||
现在我在找一些隐藏很深的bug。如果有人想帮忙的话,非常欢迎!:)
|
||||
|
||||
下面是生成的字节码的样例:
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 0
|
||||
.number 4e+006
|
||||
.number 1
|
||||
.symbol i
|
||||
0x00000000: pzero 0x00000000
|
||||
0x00000001: loadg 0x00000000 (i)
|
||||
0x00000002: callg 0x00000000 (i)
|
||||
0x00000003: pnum 0x00000001 (4e+006)
|
||||
0x00000004: less 0x00000000
|
||||
0x00000005: jf 0x0000000b
|
||||
0x00000006: pone 0x00000000
|
||||
0x00000007: mcallg 0x00000000 (i)
|
||||
0x00000008: addeq 0x00000000
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000002
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 5.0 vm (last update 2021/3/7)
|
||||
|
||||
从这个版本起,我决定持续优化字节码虚拟机。
|
||||
|
||||
毕竟现在这玩意从`0`数到`4000000-1`要花费1.5秒。这效率完全不能忍。
|
||||
|
||||
2021/1/23 update: 现在它确实可以在1.5秒内从`0`数到`4000000-1`了。
|
||||
|
||||
### version 6.0 vm (last update 2021/6/1)
|
||||
|
||||
使用`loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll`指令来减少分支语句的调用。
|
||||
|
||||
删除了`vm_scop`类型。
|
||||
|
||||
添加作为常量的`vm_num`来减少内存分配的开销。
|
||||
|
||||
将垃圾收集器从引用计数改为了标记清理。
|
||||
|
||||
`vapp`和`newf`开始使用先前未被使用的.num段来压缩字节码生成数量,减少生成的`exec_code`的大小。
|
||||
|
||||
2021/4/3 update: 从`0`数到`4e6-1`只需要不到0.8秒了。
|
||||
|
||||
2021/4/19 update: 从`0`数到`4e6-1`只需要不到0.4秒了。
|
||||
|
||||
在这次的更新中,我把全局变量和局部变量的存储结构从`unordered_map`变为了`vector`,从而提升执行效率。所以现在生成的字节码大变样了。
|
||||
|
||||
```javascript
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4e+006)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000c
|
||||
0x00000007: pone 0x00000000
|
||||
0x00000008: mcallg 0x00000000
|
||||
0x00000009: addeq 0x00000000
|
||||
0x0000000a: pop 0x00000000
|
||||
0x0000000b: jmp 0x00000003
|
||||
0x0000000c: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 6.5 vm (last update 2021/6/24)
|
||||
|
||||
2021/5/31 update:
|
||||
|
||||
现在垃圾收集器不会错误地重复收集未使用变量了。
|
||||
|
||||
添加了`builtin_alloc`以防止在运行内置函数的时候错误触发标记清除。
|
||||
|
||||
建议在获取大空间数组的时候尽量使用setsize,因为`append`在被频繁调用时可能会频繁触发垃圾收集器。
|
||||
|
||||
2021/6/3 update:
|
||||
|
||||
修复了垃圾收集器还是他妈的会重复收集的bug,这次我设计了三个标记状态来保证垃圾是被正确收集了。
|
||||
|
||||
将`callf`指令拆分为`callfv`和`callfh`。并且`callfv`将直接从`val_stack`获取传参,而不是先通过一个`vm_vec`把参数收集起来再传入,后者是非常低效的做法。
|
||||
|
||||
建议更多使用`callfv`而不是`callfh`,因为`callfh`只能从栈上获取参数并整合为`vm_hash`之后才能传给该指令进行处理,拖慢执行速度。
|
||||
|
||||
```javascript
|
||||
var f=func(x,y){return x+y;}
|
||||
f(1024,2048);
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 1024
|
||||
.number 2048
|
||||
.symbol x
|
||||
.symbol y
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: newf 0x00000007
|
||||
0x00000002: intl 0x00000003
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: para 0x00000000 (x)
|
||||
0x00000005: para 0x00000001 (y)
|
||||
0x00000006: jmp 0x0000000b
|
||||
0x00000007: calll 0x00000001
|
||||
0x00000008: calll 0x00000002
|
||||
0x00000009: add 0x00000000
|
||||
0x0000000a: ret 0x00000000
|
||||
0x0000000b: loadg 0x00000000
|
||||
0x0000000c: callg 0x00000000
|
||||
0x0000000d: pnum 0x00000000 (1024)
|
||||
0x0000000e: pnum 0x00000001 (2048)
|
||||
0x0000000f: callfv 0x00000002
|
||||
0x00000010: pop 0x00000000
|
||||
0x00000011: nop 0x00000000
|
||||
```
|
||||
|
||||
2021/6/21 update:
|
||||
|
||||
现在垃圾收集器不会收集空指针了。并且调用链中含有函数调用的赋值语句现在也可以执行了,下面这些赋值方式是合法的:
|
||||
|
||||
```javascript
|
||||
var f=func()
|
||||
{
|
||||
var _=[{_:0},{_:1}];
|
||||
return func(x)
|
||||
{
|
||||
return _[x];
|
||||
}
|
||||
}
|
||||
var m=f();
|
||||
m(0)._=m(1)._=10;
|
||||
|
||||
[0,1,2][1:2][0]=0;
|
||||
```
|
||||
|
||||
在老版本中,语法分析器会检查左值,并且在检测到有特别调用的情况下直接告知用户这种左值是不被接受的(bad lvalue)。但是现在它可以正常运作了。为了保证这种赋值语句能正常执行,codegen模块会优先使用`codegen::call_gen()`生成前面调用链的字节码而不是全部使用 `codegen::mcall_gen()`,在最后一个调用处才会使用`codegen::mcall_gen()`。
|
||||
|
||||
所以现在生成的相关字节码也完全不同了:
|
||||
|
||||
```x86asm
|
||||
.number 10
|
||||
.number 2
|
||||
.symbol _
|
||||
.symbol x
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: newf 0x00000005
|
||||
0x00000002: intl 0x00000002
|
||||
0x00000003: offset 0x00000001
|
||||
0x00000004: jmp 0x00000017
|
||||
0x00000005: newh 0x00000000
|
||||
0x00000006: pzero 0x00000000
|
||||
0x00000007: happ 0x00000000 (_)
|
||||
0x00000008: newh 0x00000000
|
||||
0x00000009: pone 0x00000000
|
||||
0x0000000a: happ 0x00000000 (_)
|
||||
0x0000000b: newv 0x00000002
|
||||
0x0000000c: loadl 0x00000001
|
||||
0x0000000d: newf 0x00000012
|
||||
0x0000000e: intl 0x00000003
|
||||
0x0000000f: offset 0x00000002
|
||||
0x00000010: para 0x00000001 (x)
|
||||
0x00000011: jmp 0x00000016
|
||||
0x00000012: calll 0x00000001
|
||||
0x00000013: calll 0x00000002
|
||||
0x00000014: callv 0x00000000
|
||||
0x00000015: ret 0x00000000
|
||||
0x00000016: ret 0x00000000
|
||||
0x00000017: loadg 0x00000000
|
||||
0x00000018: callg 0x00000000
|
||||
0x00000019: callfv 0x00000000
|
||||
0x0000001a: loadg 0x00000001
|
||||
0x0000001b: pnum 0x00000000 (10.000000)
|
||||
0x0000001c: callg 0x00000001
|
||||
0x0000001d: pone 0x00000000
|
||||
0x0000001e: callfv 0x00000001
|
||||
0x0000001f: mcallh 0x00000000 (_)
|
||||
0x00000020: meq 0x00000000
|
||||
0x00000021: callg 0x00000001
|
||||
0x00000022: pzero 0x00000000
|
||||
0x00000023: callfv 0x00000001
|
||||
0x00000024: mcallh 0x00000000 (_)
|
||||
0x00000025: meq 0x00000000
|
||||
0x00000026: pop 0x00000000
|
||||
0x00000027: pzero 0x00000000
|
||||
0x00000028: pzero 0x00000000
|
||||
0x00000029: pone 0x00000000
|
||||
0x0000002a: pnum 0x00000001 (2.000000)
|
||||
0x0000002b: newv 0x00000003
|
||||
0x0000002c: slcbeg 0x00000000
|
||||
0x0000002d: pone 0x00000000
|
||||
0x0000002e: pnum 0x00000001 (2.000000)
|
||||
0x0000002f: slc2 0x00000000
|
||||
0x00000030: slcend 0x00000000
|
||||
0x00000031: pzero 0x00000000
|
||||
0x00000032: mcallv 0x00000000
|
||||
0x00000033: meq 0x00000000
|
||||
0x00000034: pop 0x00000000
|
||||
0x00000035: nop 0x00000000
|
||||
```
|
||||
|
||||
从上面这些字节码可以看出,`mcall`/`mcallv`/`mcallh`指令的使用频率比以前减小了一些,而`call`/`callv`/`callh`/`callfv`/`callfh`则相反。
|
||||
|
||||
并且因为新的数据结构,`mcall`指令以及`addr_stack`,一个曾用来存储指针的栈,从`vm`中被移除。现在`vm`使用`nas_val** mem_addr`来暂存获取的内存地址。这不会导致严重的问题,因为内存空间是 __获取即使用__ 的。
|
||||
|
||||
### version 7.0 vm (last update 2021/10/8)
|
||||
|
||||
2021/6/26 update:
|
||||
|
||||
指令分派方式从call-threading改为了computed-goto。在更改了指令分派方式之后,`vm`的执行效率有了非常巨大的提升。现在虚拟机可以在0.2秒内执行完`test/bigloop`和`test/pi`!并且在linux平台虚拟机可以在0.8秒内执行完`test/fib`。你可以在下面的测试数据部分看到测试的结果。
|
||||
|
||||
这个分派方式使用了g++扩展"labels as values",clang++目前也支持这种指令分派的实现方式。(不过MSVC支不支持就不得而知了,哈哈)
|
||||
|
||||
`gc`中也有部分改动:
|
||||
全局变量不再用`std::vector`存储,而是全部存在操作数栈上(从`val_stack+0`到`val_stack+intg-1`)。
|
||||
|
||||
2021/6/29 update:
|
||||
|
||||
添加了一些直接用常量进行运算的指令:
|
||||
`op_addc`,`op_subc`,`op_mulc`,`op_divc`,`op_lnkc`,`op_addeqc`,`op_subeqc`,`op_muleqc`,`op_diveqc`,`op_lnkeqc`。
|
||||
|
||||
现在test/bigloop.nas的字节码是这样的:
|
||||
|
||||
```x86asm
|
||||
.number 4e+006
|
||||
.number 1
|
||||
0x00000000: intg 0x00000001
|
||||
0x00000001: pzero 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: callg 0x00000000
|
||||
0x00000004: pnum 0x00000000 (4000000)
|
||||
0x00000005: less 0x00000000
|
||||
0x00000006: jf 0x0000000b
|
||||
0x00000007: mcallg 0x00000000
|
||||
0x00000008: addeqc 0x00000001 (1)
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000003
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
在这次更新之后,这个测试文件可以在0.1秒内运行结束。大多数的运算操作速度都有提升。
|
||||
|
||||
并且赋值相关的字节码也有一些改动。现在赋值语句只包含一个标识符时,会优先调用`op_load`来赋值,而不是使用`op_meq`和`op_pop`。
|
||||
|
||||
```javascript
|
||||
var (a,b)=(1,2);
|
||||
a=b=0;
|
||||
```
|
||||
|
||||
```x86asm
|
||||
.number 2
|
||||
0x00000000: intg 0x00000002
|
||||
0x00000001: pone 0x00000000
|
||||
0x00000002: loadg 0x00000000
|
||||
0x00000003: pnum 0x00000000 (2)
|
||||
0x00000004: loadg 0x00000001
|
||||
0x00000005: pzero 0x00000000
|
||||
0x00000006: mcallg 0x00000001
|
||||
0x00000007: meq 0x00000000 (b=2 use meq,pop->a)
|
||||
0x00000008: loadg 0x00000000 (a=b use loadg)
|
||||
0x00000009: nop 0x00000000
|
||||
```
|
||||
|
||||
### version 8.0 vm (last update 2022/2/12)
|
||||
|
||||
2021/10/8 update:
|
||||
|
||||
从这个版本开始`vm_nil`和`vm_num`不再由`gc`管理,这会大幅度降低`gc::alloc`的调用并且会大幅度提升执行效率。
|
||||
|
||||
添加了新的数据类型: `vm_obj`。这个类型是留给用户定义他们想要的数据类型的。相关的API会在未来加入。
|
||||
|
||||
功能完备的闭包:添加了读写闭包数据的指令。删除了老的指令`op_offset`。
|
||||
|
||||
2021/10/13 update:
|
||||
|
||||
字节码信息输出格式修改为如下形式:
|
||||
|
||||
```x86asm
|
||||
0x000002f2: newf 0x2f6
|
||||
0x000002f3: intl 0x2
|
||||
0x000002f4: para 0x3e ("x")
|
||||
0x000002f5: jmp 0x309
|
||||
0x000002f6: calll 0x1
|
||||
0x000002f7: lessc 0x0 (2)
|
||||
0x000002f8: jf 0x2fb
|
||||
0x000002f9: calll 0x1
|
||||
0x000002fa: ret
|
||||
0x000002fb: upval 0x0[0x1]
|
||||
0x000002fc: upval 0x0[0x1]
|
||||
0x000002fd: callfv 0x1
|
||||
0x000002fe: calll 0x1
|
||||
0x000002ff: subc 0x1d (1)
|
||||
0x00000300: callfv 0x1
|
||||
0x00000301: upval 0x0[0x1]
|
||||
0x00000302: upval 0x0[0x1]
|
||||
0x00000303: callfv 0x1
|
||||
0x00000304: calll 0x1
|
||||
0x00000305: subc 0x0 (2)
|
||||
0x00000306: callfv 0x1
|
||||
0x00000307: add
|
||||
0x00000308: ret
|
||||
0x00000309: ret
|
||||
0x0000030a: callfv 0x1
|
||||
0x0000030b: loadg 0x32
|
||||
```
|
||||
|
||||
2022/1/22 update:
|
||||
|
||||
删除`op_pone`和`op_pzero`。这两个指令在目前已经没有实际意义,并且已经被`op_pnum`替代。
|
||||
|
||||
### version 9.0 vm (last update 2022/5/18)
|
||||
|
||||
2022/2/12 update:
|
||||
|
||||
局部变量现在也被 __存储在栈上__。
|
||||
所以函数调用比以前也会快速很多。
|
||||
在v8.0如果你想调用一个函数,
|
||||
新的`vm_vec`将被分配出来用于模拟局部作用域,这个操作会导致标记清除过程会被频繁触发并且浪费太多的执行时间。
|
||||
在测试文件`test/bf.nas`中,这种调用方式使得大部分时间都被浪费了,因为这个测试文件包含大量且频繁的函数调用(详细数据请看测试数据一节中`version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)`)。
|
||||
|
||||
现在闭包会在第一次在局部作用域创建新函数的时候产生,使用`vm_vec`。
|
||||
在那之后如果再创建新的函数,则他们会共享同一个闭包,这些闭包会在每次于局部作用域创建新函数时同步。
|
||||
|
||||
2022/3/27 update:
|
||||
|
||||
在这个月的更新中我们把闭包的数据结构从`vm_vec`换成了一个新的对象`vm_upval`,这种类型有着和另外一款编程语言 __`Lua`__ 中闭包相类似的结构。
|
||||
|
||||
同时我们也修改了字节码的输出格式。新的格式看起来像是 `objdump`:
|
||||
|
||||
```x86asm
|
||||
0x0000029b: 0a 00 00 00 00 newh
|
||||
|
||||
func <0x29c>:
|
||||
0x0000029c: 0b 00 00 02 a0 newf 0x2a0
|
||||
0x0000029d: 02 00 00 00 02 intl 0x2
|
||||
0x0000029e: 0d 00 00 00 66 para 0x66 ("libname")
|
||||
0x0000029f: 32 00 00 02 a2 jmp 0x2a2
|
||||
0x000002a0: 40 00 00 00 42 callb 0x42 <__dlopen@0x41dc40>
|
||||
0x000002a1: 4a 00 00 00 00 ret
|
||||
<0x29c>;
|
||||
|
||||
0x000002a2: 0c 00 00 00 67 happ 0x67 ("dlopen")
|
||||
|
||||
func <0x2a3>:
|
||||
0x000002a3: 0b 00 00 02 a8 newf 0x2a8
|
||||
0x000002a4: 02 00 00 00 03 intl 0x3
|
||||
0x000002a5: 0d 00 00 00 68 para 0x68 ("lib")
|
||||
0x000002a6: 0d 00 00 00 69 para 0x69 ("sym")
|
||||
0x000002a7: 32 00 00 02 aa jmp 0x2aa
|
||||
0x000002a8: 40 00 00 00 43 callb 0x43 <__dlsym@0x41df00>
|
||||
0x000002a9: 4a 00 00 00 00 ret
|
||||
<0x2a3>;
|
||||
|
||||
0x000002aa: 0c 00 00 00 6a happ 0x6a ("dlsym")
|
||||
```
|
||||
|
||||
### version 10.0 vm (latest)
|
||||
|
||||
2022/5/19 update:
|
||||
|
||||
在这个版本中我们给nasal加入了协程:
|
||||
|
||||
```javascript
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
```
|
||||
|
||||
`coroutine.create`用于创建新的协程对象。不过创建之后协程并不会直接运行。
|
||||
|
||||
`coroutine.resume`用于继续运行一个协程。
|
||||
|
||||
`coroutine.yield`用于中断一个协程的运行过程并且抛出一些数据。这些数据会被`coroutine.resume`接收并返回。而在协程函数中`coroutine.yield`本身只返回`vm_nil`。
|
||||
|
||||
`coroutine.status`用于查看协程的状态。协程有三种不同的状态:`suspended`挂起,`running`运行中,`dead`结束运行。
|
||||
|
||||
`coroutine.running`用于判断当前是否有协程正在运行。
|
||||
|
||||
__注意:__ 协程不能在其他正在运行的协程中创建。
|
||||
|
||||
__接下来我们解释这个协程的运行原理:__
|
||||
|
||||
当`op_callb`被执行时,栈帧如下所示:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| old pc(vm_ret) | <- top[0]
|
||||
+----------------------+
|
||||
| old localr(vm_addr) | <- top[-1]
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) | <- top[-2]
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
在`op_callb`执行过程中,下一步的栈帧如下:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
接着我们调用`resume`,这个函数会替换操作数栈。我们会看到,协程的操作数栈上已经保存了一些数据,但是我们首次进入协程执行时,这个操作数栈的栈顶将会是`vm_ret`,并且返回的`pc`值是`0`。
|
||||
|
||||
为了保证栈顶的数据不会被破坏,`resume`会返回`gc.top[0]`。`op_callb`将会执行`top[0]=resume()`,所以栈顶的数据虽然被覆盖了一次,但是实际上还是原来的数据。
|
||||
|
||||
```C++
|
||||
+----------------------+(协程操作数栈)
|
||||
| pc:0(vm_ret) | <- now gc.top[0]
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
当我们调用`yield`的时候,该函数会执行出这个情况,我们发现`op_callb` 已经把`nil`放在的栈顶。但是应该返回的`local[1]`到底发送到哪里去了?
|
||||
|
||||
```C++
|
||||
+----------------------+(协程操作数栈)
|
||||
| nil(vm_nil) | <- push nil
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
当`builtin_coyield`执行完毕之后,栈又切换到了主操作数栈上,这时可以看到返回的`local[1]`实际上被`op_callb`放在了这里的栈顶:
|
||||
|
||||
```C++
|
||||
+----------------------+(主操作数栈)
|
||||
| return_value(var) |
|
||||
+----------------------+
|
||||
| old pc(vm_ret) |
|
||||
+----------------------+
|
||||
| old localr(vm_addr) |
|
||||
+----------------------+
|
||||
| old upvalr(vm_upval) |
|
||||
+----------------------+
|
||||
| local scope(var) |
|
||||
| ... |
|
||||
+----------------------+ <- local pointer stored in localr
|
||||
| old funcr(vm_func) | <- old function stored in funcr
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
所以主程序会认为顶部这个返回值好像是`resume`返回的。而实际上`resume`的返回值在协程的操作数栈顶。综上所述:
|
||||
|
||||
```C++
|
||||
resume (main->coroutine) return coroutine.top[0]. coroutine.top[0] = coroutine.top[0];
|
||||
yield (coroutine->main) return a vector. main.top[0] = vector;
|
||||
```
|
||||
|
||||
## __发行日志__
|
||||
|
||||
### __version 8.0 release__
|
||||
|
||||
这个版本的发行版有个 __严重的问题__:
|
||||
|
||||
in __`nasal_dbg.h:215`__: `auto canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
这个会导致不正确的`stackoverflow`报错。因为它覆盖了原有的变量。
|
||||
请修改为:
|
||||
|
||||
`canary=gc.stack+STACK_MAX_DEPTH-1;`
|
||||
|
||||
如果不修改这一行,调试器运行肯定是不正常的。在`v9.0`第一个commit中我们修复了这个问题。
|
||||
|
||||
另外一个bug在 `nasal_err.h:class nasal_err`这边,要给这个类添加一个构造函数来进行初始化,否则会出问题:
|
||||
|
||||
```C++
|
||||
nasal_err():error(0){}
|
||||
```
|
||||
|
||||
同样这个也在`v9.0`中修复了。所以我们建议不要使用`v8.0`。
|
||||
BIN
doc/gif/dbg.gif
Normal file
|
After Width: | Height: | Size: 867 KiB |
BIN
doc/gif/error.gif
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
doc/gif/help.gif
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
doc/gif/opcode.gif
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
doc/gif/stackoverflow.gif
Normal file
|
After Width: | Height: | Size: 64 KiB |
164
doc/nasal-http-test-web.html
Normal file
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title> nasal-http-test-web </title>
|
||||
<meta charset="utf-8">
|
||||
<meta author="ValKmjolnir">
|
||||
<style>
|
||||
body{
|
||||
background: white;
|
||||
width: 60%;
|
||||
}
|
||||
pre{
|
||||
background: #303030;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
h1,h2,h3{
|
||||
padding: 5px;
|
||||
background-color: #555588;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: white;
|
||||
}
|
||||
p{
|
||||
margin-left: 15px;
|
||||
}
|
||||
div.badges{
|
||||
text-align: center;
|
||||
}
|
||||
tr{
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Nasal | Not another scripting language!</h1>
|
||||
<div class="badges">
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/dev-v10.0-blue?style=flat-square&logo=github"></img></a>
|
||||
<a href="https://github.com/ValKmjolnir/Nasal-Interpreter"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square&logo=github"><br/></img></a>
|
||||
</div>
|
||||
<h2> Introduction | 介绍</h2>
|
||||
<text>
|
||||
<p>
|
||||
Hello, this is a simple HTML document just for test. This simple http server is written in nasal.
|
||||
Nasal is an ECMAscript-like programming language that used in Flightgear designed by Andy Ross.<br/>
|
||||
</p>
|
||||
<p>
|
||||
这是个用于测试的简易HTML文档。该http服务器是用nasal编写的。Nasal是一款用于飞行模拟器Flightgear的、类似ECMAscript的编程语言。该语言由Andy Ross设计完成。
|
||||
</p>
|
||||
<p>
|
||||
The interpreter is totally rewritten by <a href="https://github.com/ValKmjolnir">ValKmjolnir</a> using C++(-std=c++11) without reusing the code in Andy Ross's nasal interpreter.
|
||||
But we really appreciate that Andy created this amazing programming language and his interpreter project.
|
||||
Now this project uses <a href="/license">MIT license</a> (2021/5/4). Edit it if you want, use this project to learn or create more interesting things(But don't forget me XD).<br/>
|
||||
</p>
|
||||
<p>
|
||||
这个解释器是由<a href="https://github.com/ValKmjolnir">ValKmjolnir</a>用C++11编写的,完全没有复用Andy Ross版解释器的代码。但是我们仍然非常感谢Andy为我们带来了这么一款有趣的编程语言。
|
||||
现在这个项目使用<a href="/license">MIT协议</a>开源 (2021/5/4)。
|
||||
如果你有需求,可以自行修改,用这个解释器项目来学习或者创造更有意思的东西吧!(不过别忘了在项目协议中留下原作者名字哦)
|
||||
</p>
|
||||
</text>
|
||||
<h2> Benchmark | 执行效率</h2>
|
||||
<img src="/doc/pic/benchmark.png" width="450" height="350"></img>
|
||||
<img src="/doc/pic/mandelbrot.png" width="450" height="350"><br /></img>
|
||||
<text>
|
||||
<p>
|
||||
Benchmark of different versions of nasal interpreter(left).
|
||||
Beautiful picture generated by brainfuck interpreter written in nasal(right).
|
||||
</p>
|
||||
<p>
|
||||
不同版本的nasal解释器执行效率图(左)。
|
||||
nasal运行brainfuck绘制的曼德勃罗集合(右)。
|
||||
</p>
|
||||
<p>
|
||||
Nasal can run this test file(test/bfcolored.nas) to draw this picture in about 220 seconds.
|
||||
In fact this test file cost over 2200 seconds before ver 8.0 .
|
||||
</p>
|
||||
<p>
|
||||
Nasal现在可以在220秒内运行该文件(test/bfcolored.nas)并绘制出这张图。
|
||||
在8.0版本中这个解释器需要跑超过2200秒来绘制这张图。
|
||||
</p>
|
||||
</text>
|
||||
<h2> Example | 样例代码</h2>
|
||||
<form method="get">
|
||||
<text style="margin-left: 15px;"> </text>
|
||||
<input type="text" name="filename" value="ascii-art.nas">
|
||||
<input type="submit" value="search">
|
||||
</form>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/ascii-art.nas">ascii-art.nas</a></li>
|
||||
<li><a href="/auto_crash.nas">auto_crash.nas</a></li>
|
||||
<li><a href="/bf.nas">bf.nas</a></li>
|
||||
<li><a href="/bfconvertor.nas">bfconvertor.nas</a></li>
|
||||
<li><a href="/bfs.nas">bfs.nas</a></li>
|
||||
<li><a href="/bigloop.nas">bigloop.nas</a></li>
|
||||
<li><a href="/bp.nas">bp.nas</a></li>
|
||||
<li><a href="/calc.nas">calc.nas</a></li>
|
||||
<li><a href="/choice.nas">choice.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/class.nas">class.nas</a></li>
|
||||
<li><a href="/coroutine.nas">coroutine.nas</a></li>
|
||||
<li><a href="/diff.nas">diff.nas</a></li>
|
||||
<li><a href="/exception.nas">exception.nas</a></li>
|
||||
<li><a href="/fib.nas">fib.nas</a></li>
|
||||
<li><a href="/filesystem.nas">filesystem.nas</a></li>
|
||||
<li><a href="/hexdump.nas">hexdump.nas</a></li>
|
||||
<li><a href="/httptest.nas">httptest.nas</a></li>
|
||||
<li><a href="/json.nas">json.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/leetcode1319.nas">leetcode1319.nas</a></li>
|
||||
<li><a href="/lexer.nas">lexer.nas</a></li>
|
||||
<li><a href="/life.nas">life.nas</a></li>
|
||||
<li><a href="/loop.nas">loop.nas</a></li>
|
||||
<li><a href="/mandel.nas">mandel.nas</a></li>
|
||||
<li><a href="/mandelbrot.nas">mandelbrot.nas</a></li>
|
||||
<li><a href="/mcpu.nas">mcpu.nas</a></li>
|
||||
<li><a href="/md5.nas">md5.nas</a></li>
|
||||
<li><a href="/md5compare.nas">md5compare.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/module_test.nas">module_test.nas</a></li>
|
||||
<li><a href="/nasal_test.nas">nasal_test.nas</a></li>
|
||||
<li><a href="/occupation.nas">occupation.nas</a></li>
|
||||
<li><a href="/pi.nas">pi.nas</a></li>
|
||||
<li><a href="/ppmgen.nas">ppmgen.nas</a></li>
|
||||
<li><a href="/prime.nas">prime.nas</a></li>
|
||||
<li><a href="/qrcode.nas">qrcode.nas</a></li>
|
||||
<li><a href="/quick_sort.nas">quick_sort.nas</a></li>
|
||||
<li><a href="/scalar.nas">scalar.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li><a href="/snake.nas">snake.nas</a></li>
|
||||
<li><a href="/tetris.nas">tetris.nas</a></li>
|
||||
<li><a href="/trait.nas">trait.nas</a></li>
|
||||
<li><a href="/turingmachine.nas">turingmachine.nas</a></li>
|
||||
<li><a href="/utf8chk.nas">utf8chk.nas</a></li>
|
||||
<li><a href="/watchdog.nas">watchdog.nas</a></li>
|
||||
<li><a href="/wavecollapse.nas">wavecollapse.nas</a></li>
|
||||
<li><a href="/word_collector.nas">word_collector.nas</a></li>
|
||||
<li><a href="/ycombinator.nas">ycombinator.nas</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<h2> Shutdown | 关闭服务器</h2>
|
||||
<text>
|
||||
<p>Click <a href="/shutdown">here</a> to shutdown http server.</p>
|
||||
<p>Don't click <a href="/teapot">me</a> besauce i am just a teapot.</p>
|
||||
</text>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
doc/pic/favicon.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
doc/pic/mandelbrot.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
31
doc/vs.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Create VS project | 创建 VS 工程
|
||||
|
||||
## First | 首先
|
||||
|
||||
Make sure you are using VS 2022.
|
||||
|
||||
确保你使用的是 VS 2022。
|
||||
|
||||
## How to Create VS project
|
||||
|
||||
1. Get code from this repo using `git`.
|
||||
|
||||
2. In Visual Studio, click `File`->`New`->`Project From Existing Code...`.
|
||||
|
||||
3. Select `Visual C++`->`Next`->choose `project file location`->write the `project name` at ease->`Finish`.
|
||||
|
||||
4. Remove cpp files in `module` to avoid compilation problems.(they should be compiled to dynamic libraries)
|
||||
|
||||
5. Click `Source Files` in `Search Solution Explorer` at left, right click `main.cpp`, compile.
|
||||
|
||||
## 如何创建VS工程
|
||||
|
||||
1. 用`git`从这个仓库获取代码。
|
||||
|
||||
2. 在VS的界面,点击文件(F)->新建(N)->从现有代码创建项目(E)。
|
||||
|
||||
3. 选择创建`Visual C++`项目->下一步->项目文件位置选择你下载的代码存放的文件夹->填项目名称,随便写->完成。
|
||||
|
||||
4. 从项目中去掉 `module` 里的cpp文件,以防止编译错误。(那些本应该编译到动态库)
|
||||
|
||||
5. 点开左侧解决方案资源管理器中的`Source Files`,右键点击`main.cpp`,编译。
|
||||
395
lib.nas
@@ -1,165 +1,286 @@
|
||||
# lib.nas
|
||||
|
||||
# import is used to link another file, this lib function will do nothing.
|
||||
# because nasal_import will recognize this and link files before generating bytecode.
|
||||
var import=func(filename){
|
||||
return __builtin_import(filename);
|
||||
}
|
||||
# 2019 ValKmjolnir
|
||||
|
||||
# print is used to print all things in nasal, try and see how it works.
|
||||
# this function uses std::cout/printf to output logs.
|
||||
# this function uses std::cout to output logs.
|
||||
var print=func(elems...){
|
||||
return __builtin_print(elems);
|
||||
return __print(elems);
|
||||
}
|
||||
|
||||
# append is used to add values into a vector.
|
||||
var append=func(vec,elems...){
|
||||
return __builtin_append(vec,elems);
|
||||
return __append(vec,elems);
|
||||
}
|
||||
|
||||
# setsize is used to change the size of vector.
|
||||
# if the size is larger than before,
|
||||
# this function will fill vm_nil into uninitialized space.
|
||||
var setsize=func(vec,size){
|
||||
return __builtin_setsize(vec,size);
|
||||
return __setsize(vec,size);
|
||||
}
|
||||
|
||||
# system has the same use in C.
|
||||
var system=func(str){
|
||||
return __builtin_system(str);
|
||||
return __system(str);
|
||||
}
|
||||
|
||||
# input uses std::cin and returns what we input.
|
||||
var input=func(){
|
||||
return __builtin_input();
|
||||
var input=func(end=nil){
|
||||
return __input(end);
|
||||
}
|
||||
|
||||
# split a string by delimiter for example:
|
||||
# split a string by separator for example:
|
||||
# split("ll","hello world") -> ["he","o world"]
|
||||
# this function will return a vector.
|
||||
var split=func(deli,str){
|
||||
return __builtin_split(deli,str);
|
||||
var split=func(separator,str){
|
||||
return __split(separator,str);
|
||||
}
|
||||
|
||||
# rand has the same function as the rand in C
|
||||
# if seed is nil, it will return the random number.
|
||||
# if seed is not nil, it will be initialized by this seed.
|
||||
var rand=func(seed=nil){
|
||||
return __builtin_rand(seed);
|
||||
return __rand(seed);
|
||||
}
|
||||
|
||||
# id will return the pointer of an gc-object.
|
||||
# if this object is not managed by gc, it will return 0.
|
||||
var id=func(object){
|
||||
return __builtin_id(object);
|
||||
return __id(object);
|
||||
}
|
||||
|
||||
# int will get the integer of input number.
|
||||
# int will get the integer of input number/string.
|
||||
# but carefully use it, because int has range between -2147483648~2147483647
|
||||
var int=func(val){
|
||||
return __builtin_int(val);
|
||||
return __int(val);
|
||||
}
|
||||
|
||||
# floor will get the integral number of input argument
|
||||
# which is less than or equal to this argument
|
||||
var floor=func(val){
|
||||
return __floor(val);
|
||||
}
|
||||
|
||||
# exit using std::exit
|
||||
var exit=func(val=-1){
|
||||
return __exit(val);
|
||||
}
|
||||
|
||||
# abort using std::abort
|
||||
var abort=func(){
|
||||
__abort();
|
||||
}
|
||||
|
||||
# abs gets absolute number.
|
||||
var abs=func(n){
|
||||
return n>0?n:-n;
|
||||
}
|
||||
|
||||
# num will change all the other types into number.
|
||||
# mostly used to change a numerable string.
|
||||
var num=func(val){
|
||||
return __builtin_num(val);
|
||||
return __num(val);
|
||||
}
|
||||
|
||||
# pop used to pop the last element in a vector.
|
||||
# this function will return the value that poped if vector has element(s).
|
||||
# if the vector is empty, it will return nil.
|
||||
var pop=func(vec){
|
||||
return __builtin_pop(vec);
|
||||
return __pop(vec);
|
||||
}
|
||||
|
||||
# str is used to change number into string.
|
||||
var str=func(num){
|
||||
return __builtin_str(num);
|
||||
return __str(num);
|
||||
}
|
||||
|
||||
# size can get the size of a string/vector/hashmap.
|
||||
# in fact it can also get the size of number, and the result is the number itself.
|
||||
# so don't do useless things, though it really works.
|
||||
var size=func(object){
|
||||
return __builtin_size(object);
|
||||
return __size(object);
|
||||
}
|
||||
|
||||
# contains is used to check if a key exists in a hashmap/dict.
|
||||
var contains=func(hash,key){
|
||||
return __builtin_contains(hash,key);
|
||||
return __contains(hash,key);
|
||||
}
|
||||
|
||||
# delete is used to delete a pair in a hashmap/dict by key.
|
||||
var delete=func(hash,key){
|
||||
return __builtin_delete(hash,key);
|
||||
return __delete(hash,key);
|
||||
}
|
||||
|
||||
# keys is used to get all keys in a hashmap/dict.
|
||||
# this function will return a vector.
|
||||
var keys=func(hash){
|
||||
return __builtin_keys(hash);
|
||||
return __keys(hash);
|
||||
}
|
||||
|
||||
# time has the same function in C.
|
||||
var time=func(begin){
|
||||
return __builtin_time(begin);
|
||||
return __time(begin);
|
||||
}
|
||||
var systime=func(){
|
||||
return time(0);
|
||||
}
|
||||
|
||||
# die is a special native function.
|
||||
# use it at where you want the program to crash immediately.
|
||||
var die=func(str){
|
||||
return __builtin_die(str);
|
||||
return __die(str);
|
||||
}
|
||||
|
||||
# find will give the first position of the needle in haystack
|
||||
var find=func(needle,haystack){
|
||||
return __find(needle,haystack);
|
||||
}
|
||||
|
||||
# typeof is used to get the type of an object.
|
||||
# this function returns a string.
|
||||
var typeof=func(object){
|
||||
return __builtin_type(object);
|
||||
return __type(object);
|
||||
}
|
||||
|
||||
# subvec is used to get part of a vector
|
||||
var subvec=func(vec,begin,length=nil){
|
||||
return vec[begin:(length==nil?nil:begin+length-1)];
|
||||
}
|
||||
|
||||
# substr will get the sub-string.
|
||||
# it gets the string, the begin index and sub-string's length as arguments.
|
||||
var substr=func(str,begin,len){
|
||||
return __builtin_substr(str,begin,len);
|
||||
return __substr(str,begin,len);
|
||||
}
|
||||
|
||||
# streq is used to compare if two strings are the same.
|
||||
var streq=func(a,b){
|
||||
return __builtin_streq(a,b);
|
||||
return __streq(a,b);
|
||||
}
|
||||
|
||||
# left is used to get the sub-string like substr.
|
||||
# but the begin index is 0.
|
||||
var left=func(str,len){
|
||||
return __builtin_left(str,len);
|
||||
return __left(str,len);
|
||||
}
|
||||
|
||||
# right i used to get the sub-string like substr.
|
||||
# but the begin index is strlen-len.
|
||||
var right=func(str,len){
|
||||
return __builtin_right(str,len);
|
||||
return __right(str,len);
|
||||
}
|
||||
|
||||
# cmp is used to compare two strings.
|
||||
# normal string will not be correctly compared by operators < > <= >=
|
||||
# because these operators will turn strings into numbers then compare.
|
||||
var cmp=func(a,b){
|
||||
return __builtin_cmp(a,b);
|
||||
return __cmp(a,b);
|
||||
}
|
||||
|
||||
# chr is used to get the character by ascii-number.
|
||||
# for example chr(65) -> 'A'
|
||||
var chr=func(code){
|
||||
return __builtin_chr(code);
|
||||
return __chr(code);
|
||||
}
|
||||
|
||||
# mut is used to change unmutable strings to mutable.
|
||||
var mut=func(str){
|
||||
return str~"";
|
||||
}
|
||||
|
||||
# srand wraps up rand, using time(0) as the seed.
|
||||
var srand=func(){
|
||||
rand(time(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
# values() gets all values in a hash.
|
||||
var values=func(hash){
|
||||
return __values(hash);
|
||||
}
|
||||
|
||||
# println has the same function as print.
|
||||
# but it will output a '\n' after using print.
|
||||
var println=func(elems...){
|
||||
__builtin_print(elems);
|
||||
elems=['\n'];
|
||||
return __builtin_print(elems);
|
||||
return __println(elems);
|
||||
}
|
||||
|
||||
var isfunc=func(f){
|
||||
return typeof(f)=="func";
|
||||
}
|
||||
|
||||
var isghost=func(g){
|
||||
die("this runtime has no ghost object");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var ishash=func(h){
|
||||
return typeof(h)=="hash";
|
||||
}
|
||||
|
||||
var isint=func(x){
|
||||
return x==floor(x);
|
||||
}
|
||||
|
||||
var isnum=func(x){
|
||||
return typeof(x)=="num" or !math.isnan(num(x));
|
||||
}
|
||||
|
||||
var isscalar=func(s){
|
||||
var t=typeof(s);
|
||||
return (t=="num" or t=="str")?1:0;
|
||||
}
|
||||
|
||||
var isstr=func(s){
|
||||
return typeof(s)=="str";
|
||||
}
|
||||
|
||||
var isvec=func(v){
|
||||
return typeof(v)=="vec";
|
||||
}
|
||||
|
||||
|
||||
# get the index of val in the vec
|
||||
var vecindex=func(vec,val){
|
||||
forindex(var i;vec)
|
||||
if(val==vec[i])
|
||||
return i;
|
||||
return nil;
|
||||
}
|
||||
|
||||
# check if the object is an instance of the class
|
||||
var isa=func(object,class){
|
||||
if(!contains(object,"parents") or typeof(object.parents)!="vec")
|
||||
return 0;
|
||||
foreach(var elem;object.parents)
|
||||
if(elem==class)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
# assert aborts when condition is not true
|
||||
var assert=func(condition,message="assertion failed!"){
|
||||
if(condition)
|
||||
return 1;
|
||||
die(message);
|
||||
}
|
||||
|
||||
# get time stamp, this will return a timestamp object
|
||||
var maketimestamp=func(){
|
||||
var t=0;
|
||||
var millisec=func(){
|
||||
return __millisec;
|
||||
}
|
||||
return {
|
||||
stamp:func(){t=millisec();},
|
||||
elapsedMSec:func(){return millisec()-t;},
|
||||
elapsedUSec:func(){return (millisec()-t)*1000;}
|
||||
};
|
||||
}
|
||||
|
||||
# md5
|
||||
var md5=func(str){
|
||||
return __md5(str);
|
||||
}
|
||||
|
||||
var io=
|
||||
@@ -168,28 +289,31 @@ var io=
|
||||
SEEK_CUR:1,
|
||||
SEEK_END:2,
|
||||
# get content of a file by filename. returns a string.
|
||||
fin: func(filename){return __builtin_fin(filename);},
|
||||
fin: func(filename){return __fin(filename);},
|
||||
# input a string as the content of a file.
|
||||
fout: func(filename,str){return __builtin_fout(filename,str);},
|
||||
fout: func(filename,str){return __fout(filename,str);},
|
||||
# use C access
|
||||
exists:func(filename){return __exists(filename);},
|
||||
# same as C fopen. open file and get the FILE*.
|
||||
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
|
||||
open: func(filename,mode="r"){return __open(filename,mode);},
|
||||
# same as C fclose. close file by FILE*.
|
||||
close: func(filehandle){return __builtin_close(filehandle);},
|
||||
close: func(filehandle){return __close(filehandle);},
|
||||
# same as C fread. read file by FILE*.
|
||||
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
|
||||
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
|
||||
read: func(filehandle,buf,len){return __read(filehandle,buf,len);},
|
||||
# same as C fwrite. write file by FILE*.
|
||||
write: func(filehandle,str){return __builtin_write(filehandle,str);},
|
||||
write: func(filehandle,str){return __write(filehandle,str);},
|
||||
# same as C fseek. seek place by FILE*.
|
||||
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
|
||||
seek: func(filehandle,pos,whence){return __seek(filehandle,pos,whence);},
|
||||
# same as C ftell.
|
||||
tell: func(filehandle){return __builtin_tell(filehandle);},
|
||||
tell: func(filehandle){return __tell(filehandle);},
|
||||
# read file by lines. use FILE*.
|
||||
# get nil if EOF
|
||||
readln:func(filehandle){return __builtin_readln(filehandle);},
|
||||
readln:func(filehandle){return __readln(filehandle);},
|
||||
# same as C stat.
|
||||
stat: func(filename){return __builtin_stat(filename);},
|
||||
stat: func(filename){return __stat(filename);},
|
||||
# same as C feof. check if FILE* gets the end of file(EOF).
|
||||
eof: func(filehandle){return __builtin_eof(filehandle);}
|
||||
eof: func(filehandle){return __eof(filehandle);}
|
||||
};
|
||||
|
||||
# get file status. using data from io.stat
|
||||
@@ -214,35 +338,46 @@ var fstat=func(filename){
|
||||
# carefully use it, all the calculations are based on integer.
|
||||
var bits=
|
||||
{
|
||||
# xor
|
||||
bitxor: func(a,b){return __builtin_xor(a,b); },
|
||||
# and
|
||||
bitand: func(a,b){return __builtin_and(a,b); },
|
||||
# or
|
||||
bitor: func(a,b){return __builtin_or(a,b); },
|
||||
# nand
|
||||
bitnand: func(a,b){return __builtin_nand(a,b);},
|
||||
# not
|
||||
bitnot: func(a) {return __builtin_not(a); },
|
||||
# i32 xor
|
||||
i32_xor: func(a,b){return __i32xor(a,b); },
|
||||
# i32 and
|
||||
i32_and: func(a,b){return __i32and(a,b); },
|
||||
# i32 or
|
||||
i32_or: func(a,b){return __i32or(a,b); },
|
||||
# i32 nand
|
||||
i32_nand:func(a,b){return __i32nand(a,b);},
|
||||
# i32 not
|
||||
i32_not: func(a) {return __i32not(a); },
|
||||
# u32 xor
|
||||
u32_xor: func(a,b){return __u32xor(a,b); },
|
||||
# u32 and
|
||||
u32_and: func(a,b){return __u32and(a,b); },
|
||||
# u32 or
|
||||
u32_or: func(a,b){return __u32or(a,b); },
|
||||
# u32 nand
|
||||
u32_nand:func(a,b){return __u32nand(a,b);},
|
||||
# u32 not
|
||||
u32_not: func(a) {return __u32not(a); },
|
||||
|
||||
# get bit data from a special string. for example:
|
||||
# bits.fld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5).
|
||||
fld: func(str,startbit,len){return __builtin_fld;},
|
||||
fld: func(str,startbit,len){return __fld;},
|
||||
# get sign-extended data from a special string. for example:
|
||||
# bits.sfld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5) then this will be signed extended to
|
||||
# 11111101(-3).
|
||||
sfld: func(str,startbit,len){return __builtin_sfld;},
|
||||
sfld: func(str,startbit,len){return __sfld;},
|
||||
# set value into a special string to store it. little-endian, for example:
|
||||
# bits.setfld(s,0,8,69);
|
||||
# set 01000101(69) to string will get this:
|
||||
# 10100010(162)
|
||||
# so s[0]=162.
|
||||
setfld: func(str,startbit,len,val){return __builtin_setfld;},
|
||||
setfld: func(str,startbit,len,val){return __setfld;},
|
||||
# get a special string filled by '\0' to use in setfld.
|
||||
buf: func(len){return __builtin_buf;}
|
||||
buf: func(len){return __buf;}
|
||||
};
|
||||
|
||||
# mostly used math functions and special constants, you know.
|
||||
@@ -250,50 +385,77 @@ var math=
|
||||
{
|
||||
e: 2.7182818284590452354,
|
||||
pi: 3.14159265358979323846264338327950288,
|
||||
D2R: 2.7182818284590452354/180,
|
||||
R2D: 180/2.7182818284590452354,
|
||||
inf: 1/0,
|
||||
nan: 0/0,
|
||||
sin: func(x) {return __builtin_sin(x); },
|
||||
cos: func(x) {return __builtin_cos(x); },
|
||||
tan: func(x) {return __builtin_tan(x); },
|
||||
exp: func(x) {return __builtin_exp(x); },
|
||||
lg: func(x) {return __builtin_lg(x); },
|
||||
ln: func(x) {return __builtin_ln(x); },
|
||||
sqrt: func(x) {return __builtin_sqrt(x); },
|
||||
atan2: func(x,y){return __builtin_atan2(x,y);},
|
||||
isnan: func(x) {return __builtin_isnan(x); }
|
||||
abs: func(x) {return x>0?x:-x; },
|
||||
floor: func(x) {return __floor(x); },
|
||||
pow: func(x,y){return __pow(x,y); },
|
||||
sin: func(x) {return __sin(x); },
|
||||
cos: func(x) {return __cos(x); },
|
||||
tan: func(x) {return __tan(x); },
|
||||
exp: func(x) {return __exp(x); },
|
||||
lg: func(x) {return __lg(x); },
|
||||
ln: func(x) {return __ln(x); },
|
||||
sqrt: func(x) {return __sqrt(x); },
|
||||
atan2: func(x,y){return __atan2(x,y);},
|
||||
isnan: func(x) {return __isnan(x); },
|
||||
max: func(x,y){return x>y?x:y; },
|
||||
min: func(x,y){return x<y?x:y; }
|
||||
};
|
||||
|
||||
var unix=
|
||||
{
|
||||
pipe: func(){die("not supported yet");},
|
||||
fork: func(){die("not supported yet");},
|
||||
pipe: func(){return __pipe;},
|
||||
fork: func(){return __fork;},
|
||||
dup2: func(fd0,fd1){die("not supported yet");},
|
||||
exec: func(filename,argv,envp){die("not supported yet");},
|
||||
waitpid: func(pid,nohang=0){die("not supported yet");},
|
||||
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
|
||||
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
|
||||
opendir: func(path){return __builtin_opendir;},
|
||||
readdir: func(handle){return __builtin_readdir;},
|
||||
closedir: func(handle){return __builtin_closedir;},
|
||||
waitpid: func(pid,nohang=0){return __waitpid;},
|
||||
isdir: func(path){return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
|
||||
isfile: func(path){return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
|
||||
opendir: func(path){return __opendir;},
|
||||
readdir: func(handle){return __readdir;},
|
||||
closedir: func(handle){return __closedir;},
|
||||
time: func(){return time(0);},
|
||||
sleep: func(secs){return __builtin_sleep(secs);},
|
||||
chdir: func(path){return __builtin_chdir(path);},
|
||||
environ: func(){die("not supported yet");},
|
||||
getcwd: func(){return __builtin_getcwd();},
|
||||
getenv: func(envvar){return __builtin_getenv(envvar);}
|
||||
sleep: func(secs){return __sleep(secs);},
|
||||
chdir: func(path){return __chdir(path);},
|
||||
environ: func(){return __environ();},
|
||||
getcwd: func(){return __getcwd();},
|
||||
getenv: func(envvar){return __getenv(envvar);},
|
||||
getpath: func(){return split(os.platform()=="windows"?";":":",unix.getenv("PATH"));}
|
||||
};
|
||||
|
||||
# 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
|
||||
var dylib=
|
||||
{
|
||||
# open dynamic lib.
|
||||
dlopen: func(libname){return __builtin_dlopen;},
|
||||
dlopen: func(libname){
|
||||
# find dynamic lib from local dir first
|
||||
libname=(os.platform()=="windows"?".\\":"./")~libname;
|
||||
if(io.exists(libname))
|
||||
return __dlopen(libname);
|
||||
# find dynamic lib through PATH
|
||||
var envpath=split(os.platform()=="windows"?";":":",unix.getenv("PATH"));
|
||||
# first find ./module
|
||||
append(envpath,".");
|
||||
var path=os.platform()=="windows"?"\\module\\":"/module/";
|
||||
foreach(var p;envpath){
|
||||
p~=path~libname;
|
||||
if(io.exists(p)){
|
||||
libname=p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return __dlopen(libname);
|
||||
},
|
||||
# load symbol from an open dynamic lib.
|
||||
dlsym: func(lib,sym){return __builtin_dlsym; },
|
||||
dlsym: func(lib,sym){return __dlsym; },
|
||||
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
|
||||
dlclose: func(lib){return __builtin_dlclose; },
|
||||
dlclose: func(lib){return __dlclose; },
|
||||
# call the loaded symbol.
|
||||
dlcall: func(funcptr,args...){return __builtin_dlcall}
|
||||
dlcall: func(funcptr,args...){return __dlcall}
|
||||
};
|
||||
|
||||
# os is used to use or get some os-related info/functions.
|
||||
@@ -301,31 +463,42 @@ var dylib=
|
||||
var os=
|
||||
{
|
||||
# get a string that tell which os it runs on.
|
||||
platform: func(){return __builtin_platform;}
|
||||
platform: func(){return __platform;},
|
||||
time: func(){return __logtime; }
|
||||
};
|
||||
|
||||
# runtime gives us some functions that we could manage it manually.
|
||||
var runtime=
|
||||
{
|
||||
# do garbage collection manually.
|
||||
# carefully use it because using it frequently may make program running slower.
|
||||
gc: func(){return __builtin_gc;}
|
||||
# command line arguments
|
||||
argv: func(){return __sysargv;}
|
||||
};
|
||||
|
||||
# important global constants
|
||||
var D2R=math.pi/180;
|
||||
var FPS2KT=0.5925;
|
||||
var FT2M=0.3048;
|
||||
var GAL2L=3.7854;
|
||||
var IN2M=0.0254;
|
||||
var KG2LB=2.2046;
|
||||
var KT2FPS=1.6878;
|
||||
var KT2MPS=0.5144;
|
||||
var L2GAL=0.2642;
|
||||
var LB2KG=0.4536;
|
||||
var M2FT=3.2808;
|
||||
var M2IN=39.3701;
|
||||
var M2NM=0.00054;
|
||||
var MPS2KT=1.9438;
|
||||
var NM2M=1852;
|
||||
var R2D=180/math.pi;
|
||||
# functions that not supported in this runtime:
|
||||
var bind=func(function,locals,outer_scope=nil){
|
||||
die("this runtime does not support bind");
|
||||
}
|
||||
|
||||
var call=func(function,args=nil,_me=nil,locals=nil,error=nil){
|
||||
die("this runtime does not support call");
|
||||
}
|
||||
|
||||
var caller=func(level=1){
|
||||
die("this runtime does not support caller");
|
||||
}
|
||||
|
||||
var closure=func(function,level=1){
|
||||
die("this runtime uses \"vm_upval\" instead of \"vm_hash\" as the closure");
|
||||
}
|
||||
|
||||
var compile=func(code,filename="<compile>"){
|
||||
die("this runtime uses static code generator");
|
||||
}
|
||||
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
|
||||
198
main.cpp
@@ -1,54 +1,71 @@
|
||||
#include "nasal.h"
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_opt.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
const uint32_t VM_LEXINFO =1;
|
||||
const uint32_t VM_ASTINFO =2;
|
||||
const uint32_t VM_CODEINFO =4;
|
||||
const uint32_t VM_EXECTIME =8;
|
||||
const uint32_t VM_OPCALLNUM=16;
|
||||
const uint32_t VM_EXEC =32;
|
||||
const uint32_t VM_DBGINFO =64;
|
||||
const uint32_t VM_DEBUG =128;
|
||||
const uint32_t VM_OPTIMIZE =256;
|
||||
#include <unordered_map>
|
||||
|
||||
using ch_clk=std::chrono::high_resolution_clock;
|
||||
|
||||
const u32 VM_TOKEN =0x01;
|
||||
const u32 VM_AST =0x02;
|
||||
const u32 VM_CODE =0x04;
|
||||
const u32 VM_TIME =0x08;
|
||||
const u32 VM_EXEC =0x10;
|
||||
const u32 VM_DETAIL=0x20;
|
||||
const u32 VM_DEBUG =0x40;
|
||||
const u32 VM_OPT =0x80;
|
||||
|
||||
void help()
|
||||
{
|
||||
std::cout
|
||||
std::clog
|
||||
<<" ,--#-,\n"
|
||||
<<"<3 / \\____\\ <3\n"
|
||||
<<" |_|__A_|\n"
|
||||
#ifdef _WIN32
|
||||
<<"use command \'chcp 65001\' if want to use unicode.\n"
|
||||
<<"use command <chcp 65001> if want to use unicode.\n"
|
||||
#endif
|
||||
<<"nasal <option>\n"
|
||||
<<"option:\n"
|
||||
<<" -h, --help | get help.\n"
|
||||
<<" -v, --version | get version of nasal interpreter.\n\n"
|
||||
<<"nasal <file>\n"
|
||||
<<"file:\n"
|
||||
<<" input file name to execute script file.\n\n"
|
||||
<<"nasal [options...] <file>\n"
|
||||
<<" -h, --help | get this help and exit.\n"
|
||||
<<" -v, --version | get version of nasal interpreter.\n\n"
|
||||
<<"nasal [option...] <file> [argv...]\n"
|
||||
<<"option:\n"
|
||||
<<" -l, --lex | view token info.\n"
|
||||
<<" -a, --ast | view abstract syntax tree.\n"
|
||||
<<" -c, --code | view bytecode.\n"
|
||||
<<" -e, --exec | execute.\n"
|
||||
<<" -t, --time | execute and get the running time.\n"
|
||||
<<" -o, --opcnt | execute and count used operands.\n"
|
||||
<<" -d, --detail | execute and get detail crash info.\n"
|
||||
<<" -l, --lex | view token info.\n"
|
||||
<<" -a, --ast | view abstract syntax tree.\n"
|
||||
<<" -c, --code | view bytecode.\n"
|
||||
<<" -e, --exec | execute.\n"
|
||||
<<" -t, --time | get the running time.\n"
|
||||
<<" -d, --detail | get detail runtime crash info.\n"
|
||||
<<" | get detail linker path-not-found info.\n"
|
||||
<<" | get garbage collector info if didn't crash.\n"
|
||||
<<" -op, --optimize| use optimizer(beta).\n"
|
||||
<<" | if want to use -op and run, please use -op -e/-t/-o/-d.\n"
|
||||
<<" -dbg, --debug | debug mode (this will ignore -t -o -d).\n"
|
||||
<<" -o, --optimize | use optimizer(beta).\n"
|
||||
<<" | if want to use -op and run, please use -op -e/-t/-d.\n"
|
||||
<<" -dbg, --debug | debug mode (this will ignore -t -d).\n"
|
||||
<<"file:\n"
|
||||
<<" input file name to execute script file.\n";
|
||||
<<" input file name to execute.\n"
|
||||
<<"argv:\n"
|
||||
<<" command line arguments used in program.\n";
|
||||
}
|
||||
|
||||
void logo()
|
||||
{
|
||||
std::cout
|
||||
<<" __ _ \n"
|
||||
<<" /\\ \\ \\__ _ ___ __ _| | \n"
|
||||
<<" / \\/ / _` / __|/ _` | | \n"
|
||||
<<" / /\\ / (_| \\__ \\ (_| | | \n"
|
||||
std::clog
|
||||
<<" __ _\n"
|
||||
<<" /\\ \\ \\__ _ ___ __ _| |\n"
|
||||
<<" / \\/ / _` / __|/ _` | |\n"
|
||||
<<" / /\\ / (_| \\__ \\ (_| | |\n"
|
||||
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<<"nasal interpreter ver 8.0\n"
|
||||
<<"version : "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n"
|
||||
<<"c++ std : "<<__cplusplus<<"\n"
|
||||
<<"thanks to : https://github.com/andyross/nasal\n"
|
||||
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
|
||||
<<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
@@ -56,63 +73,59 @@ void logo()
|
||||
<<"input <nasal -h> to get help .\n";
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void err()
|
||||
{
|
||||
std::cout
|
||||
std::cerr
|
||||
<<"invalid argument(s).\n"
|
||||
<<"use <nasal -h> to get help.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void execute(const std::string& file,const uint32_t cmd)
|
||||
void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
|
||||
{
|
||||
// front end use the same error module
|
||||
nasal_err nerr;
|
||||
nasal_lexer lexer(nerr);
|
||||
nasal_parse parse(nerr);
|
||||
nasal_import linker(nerr);
|
||||
nasal_codegen gen(nerr);
|
||||
// back end
|
||||
nasal_vm vm;
|
||||
error err;
|
||||
lexer lex(err);
|
||||
parse parse(err);
|
||||
linker ld(err);
|
||||
codegen gen(err);
|
||||
vm ctx;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lexer.scan(file);
|
||||
if(cmd&VM_LEXINFO)
|
||||
lexer.print();
|
||||
lex.scan(file);
|
||||
if(cmd&VM_TOKEN)
|
||||
lex.print();
|
||||
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lexer);
|
||||
parse.compile(lex);
|
||||
// linker gets parser's ast and load import files to this ast
|
||||
linker.link(parse,file);
|
||||
if(cmd&VM_ASTINFO)
|
||||
ld.link(parse,file,cmd&VM_DETAIL);
|
||||
// optimizer does simple optimization on ast
|
||||
if(cmd&VM_OPT)
|
||||
optimize(parse.tree());
|
||||
if(cmd&VM_AST)
|
||||
parse.print();
|
||||
|
||||
// optimizer does simple optimization on ast
|
||||
if(cmd&VM_OPTIMIZE)
|
||||
optimize(parse.ast());
|
||||
|
||||
// code generator gets parser's ast and linker's import file list to generate code
|
||||
gen.compile(parse,linker);
|
||||
if(cmd&VM_CODEINFO)
|
||||
gen.compile(parse,ld);
|
||||
if(cmd&VM_CODE)
|
||||
gen.print();
|
||||
|
||||
// run bytecode
|
||||
// run
|
||||
if(cmd&VM_DEBUG)
|
||||
debugger(err).run(gen,ld,argv);
|
||||
else if(cmd&VM_TIME)
|
||||
{
|
||||
nasal_dbg debugger;
|
||||
debugger.run(gen,linker);
|
||||
}
|
||||
else if(cmd&VM_EXECTIME)
|
||||
{
|
||||
clock_t t=clock();
|
||||
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
|
||||
std::cout<<"process exited after "<<((double)(clock()-t))/CLOCKS_PER_SEC<<"s.\n";
|
||||
auto start=ch_clk::now();
|
||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
auto end=ch_clk::now();
|
||||
std::clog<<"process exited after "<<(end-start).count()*1.0/ch_clk::duration::period::den<<"s.\n";
|
||||
}
|
||||
else if(cmd&VM_EXEC)
|
||||
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
|
||||
ctx.run(gen,ld,argv,cmd&VM_DETAIL);
|
||||
}
|
||||
|
||||
int main(int argc,const char* argv[])
|
||||
i32 main(i32 argc,const char* argv[])
|
||||
{
|
||||
if(argc<=1)
|
||||
{
|
||||
@@ -121,42 +134,41 @@ int main(int argc,const char* argv[])
|
||||
}
|
||||
if(argc==2)
|
||||
{
|
||||
std::string s(argv[1]);
|
||||
string s(argv[1]);
|
||||
if(s=="-v" || s=="--version")
|
||||
logo();
|
||||
std::clog<<"nasal "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n";
|
||||
else if(s=="-h" || s=="--help")
|
||||
help();
|
||||
else if(s[0]!='-')
|
||||
execute(s,VM_EXEC);
|
||||
execute(s,{},VM_EXEC);
|
||||
else
|
||||
err();
|
||||
return 0;
|
||||
}
|
||||
uint32_t cmd=0;
|
||||
for(int i=1;i<argc-1;++i)
|
||||
std::unordered_map<string,u32> cmdlst={
|
||||
{"--lex",VM_TOKEN},{"-l",VM_TOKEN},
|
||||
{"--ast",VM_AST},{"-a",VM_AST},
|
||||
{"--code",VM_CODE},{"-c",VM_CODE},
|
||||
{"--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},
|
||||
{"--optimize",VM_OPT},{"-o",VM_OPT},
|
||||
{"--debug",VM_DEBUG},{"-dbg",VM_DEBUG}
|
||||
};
|
||||
u32 cmd=0;
|
||||
string filename;
|
||||
std::vector<string> vm_argv;
|
||||
for(i32 i=1;i<argc;++i)
|
||||
{
|
||||
std::string s(argv[i]);
|
||||
if(s=="--lex" || s=="-l")
|
||||
cmd|=VM_LEXINFO;
|
||||
else if(s=="--ast" || s=="-a")
|
||||
cmd|=VM_ASTINFO;
|
||||
else if(s=="--code" || s=="-c")
|
||||
cmd|=VM_CODEINFO;
|
||||
else if(s=="--exec" || s=="-e")
|
||||
cmd|=VM_EXEC;
|
||||
else if(s=="--opcnt" || s=="-o")
|
||||
cmd|=VM_OPCALLNUM|VM_EXEC;
|
||||
else if(s=="--time" || s=="-t")
|
||||
cmd|=VM_EXECTIME;
|
||||
else if(s=="--detail" || s=="-d")
|
||||
cmd|=VM_DBGINFO|VM_EXEC;
|
||||
else if(s=="--optimize" || s=="-op")
|
||||
cmd|=VM_OPTIMIZE;
|
||||
else if(s=="--debug" || s=="-dbg")
|
||||
cmd|=VM_DEBUG;
|
||||
if(cmdlst.count(argv[i]))
|
||||
cmd|=cmdlst[argv[i]];
|
||||
else if(!filename.length())
|
||||
filename=argv[i];
|
||||
else
|
||||
err();
|
||||
vm_argv.push_back(argv[i]);
|
||||
}
|
||||
execute(argv[argc-1],cmd);
|
||||
if(!filename.length())
|
||||
err();
|
||||
execute(filename,vm_argv,cmd?cmd:VM_EXEC);
|
||||
return 0;
|
||||
}
|
||||
103
makefile
@@ -1,34 +1,71 @@
|
||||
.PHONY=test
|
||||
nasal:main.cpp nasal_ast.h nasal_err.h nasal_builtin.h nasal_opt.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal_dbg.h nasal.h
|
||||
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
||||
.PHONY:test
|
||||
|
||||
SRC=\
|
||||
main.cpp\
|
||||
nasal_ast.h\
|
||||
nasal_err.h\
|
||||
nasal_builtin.h\
|
||||
nasal_opt.h\
|
||||
nasal_codegen.h\
|
||||
nasal_gc.h\
|
||||
nasal_import.h\
|
||||
nasal_lexer.h\
|
||||
nasal_parse.h\
|
||||
nasal_vm.h\
|
||||
nasal_dbg.h\
|
||||
nasal.h
|
||||
|
||||
STD=14
|
||||
|
||||
nasal:$(SRC)
|
||||
$(CXX) -std=c++$(STD) -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
||||
nasal.exe:$(SRC)
|
||||
$(CXX) -std=c++$(STD) -O3 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
|
||||
|
||||
stable-release:$(SRC)
|
||||
$(CXX) -std=c++$(STD) -O2 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
|
||||
stable-release-mingw:$(SRC)
|
||||
$(CXX) -std=c++$(STD) -O2 main.cpp -o nasal.exe -fno-exceptions -Wshadow -Wall -static
|
||||
|
||||
test:nasal
|
||||
./nasal -op -e test/ascii-art.nas
|
||||
./nasal -op -c test/bf.nas
|
||||
./nasal -op -c test/bfconvertor.nas
|
||||
./nasal -op -e -d test/bfs.nas
|
||||
./nasal -op -t test/bigloop.nas
|
||||
./nasal -op -e test/bp.nas
|
||||
./nasal -op -e -d test/calc.nas
|
||||
./nasal -op -e test/choice.nas
|
||||
./nasal -op -e test/class.nas
|
||||
./nasal -op -c test/exception.nas
|
||||
./nasal -op -t -d test/fib.nas
|
||||
./nasal -op -e test/filesystem.nas
|
||||
./nasal -op -e -d test/hexdump.nas
|
||||
./nasal -op -e test/json.nas
|
||||
./nasal -op -e test/leetcode1319.nas
|
||||
./nasal -op -e -d test/lexer.nas
|
||||
./nasal -op -e -d test/life.nas
|
||||
./nasal -op -t test/loop.nas
|
||||
./nasal -op -t -d test/mandel.nas
|
||||
./nasal -op -t -d test/mandelbrot.nas
|
||||
./nasal -op -c test/module_test.nas
|
||||
./nasal -op -e test/nasal_test.nas
|
||||
./nasal -op -t -d test/pi.nas
|
||||
./nasal -op -t -d test/prime.nas
|
||||
./nasal -op -t -d test/quick_sort.nas
|
||||
./nasal -op -e test/scalar.nas
|
||||
./nasal -op -e test/trait.nas
|
||||
./nasal -op -t -d test/turingmachine.nas
|
||||
./nasal -op -t -d test/ycombinator.nas
|
||||
|
||||
@ ./nasal -o -e test/ascii-art.nas
|
||||
@ ./nasal -o -c test/auto_crash.nas
|
||||
@ ./nasal -o -a -c test/bf.nas
|
||||
@ ./nasal -o -a -c test/bfconvertor.nas
|
||||
@ ./nasal -o -e -d test/bfs.nas
|
||||
@ ./nasal -o -t test/bigloop.nas
|
||||
@ ./nasal -o -e test/bp.nas
|
||||
@ ./nasal -o -e -d test/calc.nas
|
||||
@ ./nasal -o -e test/choice.nas
|
||||
@ ./nasal -o -e test/class.nas
|
||||
@ ./nasal -o -d test/coroutine.nas
|
||||
@ ./nasal -o -e test/diff.nas
|
||||
-@ ./nasal -o -d test/exception.nas
|
||||
@ ./nasal -o -t -d test/fib.nas
|
||||
@ ./nasal -o -e test/filesystem.nas
|
||||
@ ./nasal -o -e -d test/hexdump.nas
|
||||
@ ./nasal -o -c test/httptest.nas
|
||||
@ ./nasal -o -e test/json.nas
|
||||
@ ./nasal -o -e test/leetcode1319.nas
|
||||
@ ./nasal -o -e -d test/lexer.nas
|
||||
@ ./nasal -o -e -d test/life.nas
|
||||
@ ./nasal -o -t test/loop.nas
|
||||
@ ./nasal -o -t -d test/mandel.nas
|
||||
@ ./nasal -o -t -d test/mandelbrot.nas
|
||||
@ ./nasal -o -t -d test/md5.nas
|
||||
@ ./nasal -o -t -d test/md5compare.nas
|
||||
-@ ./nasal -o -d test/module_test.nas
|
||||
@ ./nasal -o -e test/nasal_test.nas
|
||||
@ ./nasal -o -c test/occupation.nas
|
||||
@ ./nasal -o -t -d test/pi.nas
|
||||
@ ./nasal -o -c test/ppmgen.nas
|
||||
@ ./nasal -o -t -d test/prime.nas
|
||||
@ ./nasal -o -e test/qrcode.nas
|
||||
@ ./nasal -o -t -d test/quick_sort.nas
|
||||
@ ./nasal -o -e test/scalar.nas hello world
|
||||
-@ ./nasal -o -c -t test/snake.nas
|
||||
@ ./nasal -o -c -e test/trait.nas
|
||||
-@ ./nasal -o -c -t test/tetris.nas
|
||||
@ ./nasal -o -c -t -d test/turingmachine.nas
|
||||
@ ./nasal -o -c -t -d test/ycombinator.nas
|
||||
@ ./nasal -o -d test/wavecollapse.nas
|
||||
@@ -6,18 +6,24 @@ double fibonaci(double x){
|
||||
return x;
|
||||
return fibonaci(x-1)+fibonaci(x-2);
|
||||
}
|
||||
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
|
||||
|
||||
var fib(var* args,usize size,gc* ngc){
|
||||
std::cout<<"[mod] this is the first test module of nasal\n";
|
||||
nasal_ref num=args[0];
|
||||
if(!size)
|
||||
return nas_err("fib","lack arguments");
|
||||
var num=args[0];
|
||||
if(num.type!=vm_num)
|
||||
return builtin_err("extern_fib","\"num\" must be number");
|
||||
return {vm_num,fibonaci(num.to_number())};
|
||||
return nas_err("extern_fib","\"num\" must be number");
|
||||
return {vm_num,fibonaci(num.tonum())};
|
||||
}
|
||||
extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
|
||||
|
||||
var quick_fib(var* args,usize size,gc* ngc){
|
||||
std::cout<<"[mod] this is the first test module of nasal\n";
|
||||
nasal_ref num=args[0];
|
||||
if(!size)
|
||||
return nas_err("fib","lack arguments");
|
||||
var num=args[0];
|
||||
if(num.type!=vm_num)
|
||||
return builtin_err("extern_quick_fib","\"num\" must be number");
|
||||
return nas_err("extern_quick_fib","\"num\" must be number");
|
||||
if(num.num()<2)
|
||||
return num;
|
||||
double a=1,b=1,res=0;
|
||||
@@ -27,4 +33,13 @@ extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
|
||||
b=res;
|
||||
}
|
||||
return {vm_num,res};
|
||||
}
|
||||
|
||||
extern "C" mod get(const char* n){
|
||||
string name=n;
|
||||
if(name=="fib")
|
||||
return fib;
|
||||
else if(name=="quick_fib")
|
||||
return quick_fib;
|
||||
return nullptr;
|
||||
}
|
||||
95
module/keyboard.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "../nasal.h"
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#ifdef _WIN32
|
||||
#include <conio.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
class noecho_input{
|
||||
private:
|
||||
#ifndef _WIN32
|
||||
struct termios init_termios;
|
||||
struct termios new_termios;
|
||||
int peek_char=-1;
|
||||
#endif
|
||||
public:
|
||||
noecho_input(){
|
||||
#ifndef _WIN32
|
||||
tcflush(0,TCIOFLUSH);
|
||||
tcgetattr(0,&init_termios);
|
||||
new_termios=init_termios;
|
||||
new_termios.c_lflag&=~(ICANON|ECHO|ECHONL|ECHOE);
|
||||
// vmin=0 is nonblock input, but in wsl there is a bug that will block input
|
||||
// so we use fcntl to write the nonblock input
|
||||
new_termios.c_cc[VMIN]=1;
|
||||
new_termios.c_cc[VTIME]=0;
|
||||
tcsetattr(0,TCSANOW,&new_termios);
|
||||
#endif
|
||||
}
|
||||
~noecho_input(){
|
||||
#ifndef _WIN32
|
||||
tcflush(0,TCIOFLUSH);
|
||||
tcsetattr(0,TCSANOW,&init_termios);
|
||||
#endif
|
||||
}
|
||||
int noecho_kbhit(){
|
||||
#ifndef _WIN32
|
||||
unsigned char ch=0;
|
||||
int nread=0;
|
||||
if(peek_char!=-1)
|
||||
return 1;
|
||||
int flag=fcntl(0,F_GETFL);
|
||||
fcntl(0,F_SETFL,flag|O_NONBLOCK);
|
||||
nread=read(0,&ch,1);
|
||||
fcntl(0,F_SETFL,flag);
|
||||
if(nread==1){
|
||||
peek_char=ch;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return kbhit();
|
||||
#endif
|
||||
}
|
||||
int noecho_getch(){
|
||||
#ifndef _WIN32
|
||||
int ch=0;
|
||||
if(peek_char!=-1){
|
||||
ch=peek_char;
|
||||
peek_char=-1;
|
||||
return ch;
|
||||
}
|
||||
ssize_t tmp=read(0,&ch,1);
|
||||
return ch;
|
||||
#else
|
||||
return getch();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
noecho_input this_window;
|
||||
var nas_getch(var* args,usize size,gc* ngc){
|
||||
return {vm_num,(double)this_window.noecho_getch()};
|
||||
}
|
||||
var nas_kbhit(var* args,usize size,gc* ngc){
|
||||
return {vm_num,(double)this_window.noecho_kbhit()};
|
||||
}
|
||||
var nas_noblock(var* args,usize size,gc* ngc){
|
||||
if(this_window.noecho_kbhit())
|
||||
return {vm_num,(double)this_window.noecho_getch()};
|
||||
return nil;
|
||||
}
|
||||
|
||||
extern "C" mod get(const char* n){
|
||||
string name=n;
|
||||
if(name=="nas_getch")
|
||||
return nas_getch;
|
||||
else if(name=="nas_kbhit")
|
||||
return nas_kbhit;
|
||||
else if(name=="nas_noblock")
|
||||
return nas_noblock;
|
||||
return nullptr;
|
||||
}
|
||||
10
module/libfib.nas
Normal file
@@ -0,0 +1,10 @@
|
||||
var libfib=func(){
|
||||
var dl=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var fib=dylib.dlsym(dl,"fib");
|
||||
var qfib=dylib.dlsym(dl,"quick_fib");
|
||||
var call=dylib.dlcall;
|
||||
return {
|
||||
fib: func(x){return call(fib,x)},
|
||||
qfib:func(x){return call(qfib,x)}
|
||||
};
|
||||
}();
|
||||
12
module/libkey.nas
Normal file
@@ -0,0 +1,12 @@
|
||||
var libkey=func(){
|
||||
var lib=dylib.dlopen("libkey"~(os.platform()=="windows"?".dll":".so"));
|
||||
var kb=dylib.dlsym(lib,"nas_kbhit");
|
||||
var gt=dylib.dlsym(lib,"nas_getch");
|
||||
var nb=dylib.dlsym(lib,"nas_noblock");
|
||||
var call=dylib.dlcall;
|
||||
return {
|
||||
kbhit:func(){return call(kb);},
|
||||
getch:func(){return call(gt);},
|
||||
nonblock:func(){return call(nb);}
|
||||
}
|
||||
}();
|
||||
85
module/libsock.nas
Normal file
@@ -0,0 +1,85 @@
|
||||
var socket=func(){
|
||||
var lib=dylib.dlopen("libnasock"~(os.platform()=="windows"?".dll":".so"));
|
||||
var sock=dylib.dlsym(lib,"nas_socket");
|
||||
var closesocket=dylib.dlsym(lib,"nas_closesocket");
|
||||
var shutdown=dylib.dlsym(lib,"nas_shutdown");
|
||||
var bind=dylib.dlsym(lib,"nas_bind");
|
||||
var listen=dylib.dlsym(lib,"nas_listen");
|
||||
var connect=dylib.dlsym(lib,"nas_connect");
|
||||
var accept=dylib.dlsym(lib,"nas_accept");
|
||||
var send=dylib.dlsym(lib,"nas_send");
|
||||
var sendto=dylib.dlsym(lib,"nas_sendto");
|
||||
var recv=dylib.dlsym(lib,"nas_recv");
|
||||
var recvfrom=dylib.dlsym(lib,"nas_recvfrom");
|
||||
var errno=dylib.dlsym(lib,"nas_errno");
|
||||
var call=dylib.dlcall;
|
||||
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 call(sock,af,type,proto);
|
||||
},
|
||||
closesocket:func(sd){
|
||||
return call(closesocket,sd);
|
||||
},
|
||||
shutdown: func(sd,how){
|
||||
return call(shutdown,sd,how);
|
||||
},
|
||||
bind: func(sd,ip,port){
|
||||
return call(bind,sd,ip,port);
|
||||
},
|
||||
listen: func(sd,backlog){
|
||||
return call(listen,sd,backlog);
|
||||
},
|
||||
connect: func(sd,hostname,port){
|
||||
return call(connect,sd,hostname,port);
|
||||
},
|
||||
accept: func(sd){
|
||||
return call(accept,sd);
|
||||
},
|
||||
send: func(sd,buff,flags=0){
|
||||
return call(send,sd,buff,flags);
|
||||
},
|
||||
sendto: func(sd,hostname,port,buff,flags=0){
|
||||
return call(sendto,sd,hostname,port,buff,flags);
|
||||
},
|
||||
recv: func(sd,len,flags=0){
|
||||
return call(recv,sd,len,flags);
|
||||
},
|
||||
recvfrom: func(sd,len,flags=0){
|
||||
return call(recvfrom,sd,len,flags);
|
||||
},
|
||||
errno: func(){
|
||||
return call(errno);
|
||||
}
|
||||
};
|
||||
}();
|
||||
@@ -1,9 +1,43 @@
|
||||
.PHONY=clean
|
||||
.PHONY=clean all mingw-all
|
||||
|
||||
STD=14
|
||||
|
||||
libfib.so: fib.cpp
|
||||
clang++ -c -O3 fib.cpp -fPIC -o fib.o
|
||||
clang++ -shared -o libfib.so fib.o
|
||||
@ echo "[build] libfib.so"
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||
@ $(CXX) -shared -o libfib.so fib.o
|
||||
@ rm fib.o
|
||||
libfib.dll: fib.cpp
|
||||
g++ -c -O3 fib.cpp -fPIC -o fib.o
|
||||
g++ -shared -o libfib.dll fib.o
|
||||
@ echo [build] libfib.dll
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 fib.cpp -fPIC -o fib.o
|
||||
@ $(CXX) -shared -o libfib.dll fib.o
|
||||
@ del fib.o
|
||||
|
||||
libkey.so: keyboard.cpp
|
||||
@ echo "[build] libkey.so"
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
|
||||
@ $(CXX) -shared -o libkey.so keyboard.o
|
||||
@ rm keyboard.o
|
||||
libkey.dll: keyboard.cpp
|
||||
@ echo [build] libkey.dll
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
|
||||
@ $(CXX) -shared -o libkey.dll keyboard.o -static
|
||||
@ del keyboard.o
|
||||
|
||||
libnasock.so: nasocket.cpp
|
||||
@ echo "[build] libnasock.so"
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
|
||||
@ $(CXX) -shared -o libnasock.so nasocket.o
|
||||
@ rm nasocket.o
|
||||
libnasock.dll: nasocket.cpp
|
||||
@ echo [build] libnasock.dll
|
||||
@ $(CXX) -std=c++$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
|
||||
@ $(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static
|
||||
@ del nasocket.o
|
||||
|
||||
clean:
|
||||
rm *.o *.so *.dll *.dylib
|
||||
-@ rm *.so *.dll *.dylib
|
||||
all: libfib.so libkey.so libnasock.so
|
||||
@ echo "[build] done"
|
||||
mingw-all: libfib.dll libkey.dll libnasock.dll
|
||||
@ echo [build] done
|
||||
|
||||
217
module/nasocket.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "../nasal.h"
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h>
|
||||
#pragma comment(lib,"ws2_32")
|
||||
|
||||
class WSAmanager{
|
||||
private:
|
||||
WSAData data;
|
||||
public:
|
||||
WSAmanager(){
|
||||
WSAStartup(0x1010,&data);
|
||||
}
|
||||
~WSAmanager(){
|
||||
WSACleanup();
|
||||
}
|
||||
};
|
||||
|
||||
static WSAmanager win;
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
var nas_socket(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num || args[1].type!=vm_num || args[2].type!=vm_num)
|
||||
return nas_err("socket","\"af\", \"type\", \"protocol\" should be number");
|
||||
int sd=socket(args[0].num(),args[1].num(),args[2].num());
|
||||
return {vm_num,(double)sd};
|
||||
}
|
||||
|
||||
var nas_closesocket(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("closesocket","\"\" should be number");
|
||||
#ifdef _WIN32
|
||||
return {vm_num,(double)closesocket(args[0].num())};
|
||||
#else
|
||||
return {vm_num,(double)close(args[0].num())};
|
||||
#endif
|
||||
}
|
||||
|
||||
var nas_shutdown(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("shutdown","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_num)
|
||||
return nas_err("shutdown","\"how\" must be a number");
|
||||
return {vm_num,(double)shutdown(args[0].num(),args[1].num())};
|
||||
}
|
||||
|
||||
var nas_bind(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("bind","\"sd\" muse be a number");
|
||||
if(args[1].type!=vm_str)
|
||||
return nas_err("bind","\"ip\" should be a string including an ip with correct format");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("bind","\"port\" must be a number");
|
||||
sockaddr_in server;
|
||||
memset(&server,0,sizeof(sockaddr_in));
|
||||
server.sin_family=AF_INET;
|
||||
server.sin_addr.s_addr=inet_addr(args[1].str().c_str());
|
||||
server.sin_port=htons(args[2].num());
|
||||
return {vm_num,(double)bind(args[0].num(),(sockaddr*)&server,sizeof(server))};
|
||||
}
|
||||
|
||||
var nas_listen(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("listen","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_num)
|
||||
return nas_err("listen","\"backlog\" must be a number");
|
||||
return{vm_num,(double)listen(args[0].num(),args[1].num())};
|
||||
}
|
||||
|
||||
var nas_connect(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("connect","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_str)
|
||||
return nas_err("connect","\"hostname\" must be a string");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("connect","\"port\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr,0,sizeof(sockaddr_in));
|
||||
addr.sin_family=AF_INET;
|
||||
addr.sin_port=htons(args[2].num());
|
||||
hostent* entry=gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr,entry->h_addr,entry->h_length);
|
||||
return {vm_num,(double)connect(args[0].num(),(sockaddr*)&addr,sizeof(sockaddr_in))};
|
||||
}
|
||||
|
||||
var nas_accept(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("accept","\"sd\" must be a number");
|
||||
sockaddr_in client;
|
||||
int socklen=sizeof(sockaddr_in);
|
||||
#ifdef _WIN32
|
||||
int client_sd=accept(args[0].num(),(sockaddr*)&client,&socklen);
|
||||
#else
|
||||
int client_sd=accept(args[0].num(),(sockaddr*)&client,(socklen_t*)&socklen);
|
||||
#endif
|
||||
var res=ngc->temp=ngc->alloc(vm_hash);
|
||||
auto& hash=res.hash().elems;
|
||||
hash["sd"]={vm_num,(double)client_sd};
|
||||
hash["ip"]=ngc->newstr(inet_ntoa(client.sin_addr));
|
||||
ngc->temp=nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_send(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("send","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_str)
|
||||
return nas_err("send","\"buff\" must be a string");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("send","\"flags\" muse be a number");
|
||||
return {vm_num,(double)send(args[0].num(),args[1].str().c_str(),args[1].str().length(),args[2].num())};
|
||||
}
|
||||
|
||||
var nas_sendto(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("sendto","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_str)
|
||||
return nas_err("sendto","\"hostname\" must be a string");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("sendto","\"port\" must be a number");
|
||||
if(args[3].type!=vm_str)
|
||||
return nas_err("sendto","\"buff\" must be a string");
|
||||
if(args[4].type!=vm_num)
|
||||
return nas_err("sendto","\"flags\" must be a number");
|
||||
sockaddr_in addr;
|
||||
memset(&addr,0,sizeof(sockaddr_in));
|
||||
addr.sin_family=AF_INET;
|
||||
addr.sin_port=htons(args[2].num());
|
||||
hostent* entry=gethostbyname(args[1].str().c_str());
|
||||
memcpy(&addr.sin_addr,entry->h_addr,entry->h_length);
|
||||
return {vm_num,(double)sendto(args[0].num(),args[3].str().c_str(),args[3].str().length(),args[4].num(),(sockaddr*)&addr,sizeof(sockaddr_in))};
|
||||
}
|
||||
|
||||
var nas_recv(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("recv","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_num)
|
||||
return nas_err("recv","\"len\" must be a number");
|
||||
if(args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recv","\"len\" out of range");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("recv","\"flags\" muse be a number");
|
||||
var res=ngc->temp=ngc->alloc(vm_hash);
|
||||
auto& hash=res.hash().elems;
|
||||
char* buf=new char[(int)args[1].num()];
|
||||
hash["size"]={vm_num,(double)recv(args[0].num(),buf,args[1].num(),args[2].num())};
|
||||
hash["str"]=ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
ngc->temp=nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_recvfrom(var* args,usize size,gc* ngc){
|
||||
if(args[0].type!=vm_num)
|
||||
return nas_err("recvfrom","\"sd\" must be a number");
|
||||
if(args[1].type!=vm_num)
|
||||
return nas_err("recvfrom","\"len\" must be a number");
|
||||
if(args[1].num()<=0 || args[1].num()>16*1024*1024)
|
||||
return nas_err("recvfrom","\"len\" out of range");
|
||||
if(args[2].type!=vm_num)
|
||||
return nas_err("recvfrom","\"flags\" muse be a number");
|
||||
sockaddr_in addr;
|
||||
int socklen=sizeof(sockaddr_in);
|
||||
var res=ngc->temp=ngc->alloc(vm_hash);
|
||||
auto& hash=res.hash().elems;
|
||||
char* buf=new char[(int)args[1].num()+1];
|
||||
#ifdef _WIN32
|
||||
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,&socklen)};
|
||||
#else
|
||||
hash["size"]={vm_num,(double)recvfrom(args[0].num(),buf,args[1].num(),args[2].num(),(sockaddr*)&addr,(socklen_t*)&socklen)};
|
||||
#endif
|
||||
buf[(int)hash["size"].num()]=0;
|
||||
hash["str"]=ngc->newstr(buf);
|
||||
delete[] buf;
|
||||
hash["fromip"]=ngc->newstr(inet_ntoa(addr.sin_addr));
|
||||
ngc->temp=nil;
|
||||
return res;
|
||||
}
|
||||
|
||||
var nas_errno(var* args,usize size,gc* ngc){
|
||||
return ngc->newstr(strerror(errno));
|
||||
}
|
||||
|
||||
extern "C" mod get(const char* n){
|
||||
string name=n;
|
||||
if(name=="nas_socket")
|
||||
return nas_socket;
|
||||
else if(name=="nas_closesocket")
|
||||
return nas_closesocket;
|
||||
else if(name=="nas_shutdown")
|
||||
return nas_shutdown;
|
||||
else if(name=="nas_bind")
|
||||
return nas_bind;
|
||||
else if(name=="nas_listen")
|
||||
return nas_listen;
|
||||
else if(name=="nas_connect")
|
||||
return nas_connect;
|
||||
else if(name=="nas_accept")
|
||||
return nas_accept;
|
||||
else if(name=="nas_send")
|
||||
return nas_send;
|
||||
else if(name=="nas_sendto")
|
||||
return nas_sendto;
|
||||
else if(name=="nas_recv")
|
||||
return nas_recv;
|
||||
else if(name=="nas_recvfrom")
|
||||
return nas_recvfrom;
|
||||
else if(name=="nas_errno")
|
||||
return nas_errno;
|
||||
return nullptr;
|
||||
}
|
||||
181
nasal.h
@@ -1,67 +1,59 @@
|
||||
#ifndef __NASAL_H__
|
||||
#define __NASAL_H__
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <cmath>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#ifndef __nasver
|
||||
#define __nasver "10.1"
|
||||
#endif
|
||||
|
||||
inline double hex_to_double(const char* str)
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
using i32=std::int32_t;
|
||||
using i64=std::int64_t;
|
||||
using u8=std::uint8_t;
|
||||
using u16=std::uint16_t;
|
||||
using u32=std::uint32_t;
|
||||
using u64=std::uint64_t;
|
||||
using usize=std::size_t;
|
||||
using f64=double;
|
||||
using std::string;
|
||||
|
||||
const u32 STACK_DEPTH=1024;
|
||||
|
||||
inline f64 hex2f(const char* str)
|
||||
{
|
||||
double ret=0;
|
||||
f64 ret=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
ret*=16;
|
||||
if('0'<=*str && *str<='9')
|
||||
ret+=(*str-'0');
|
||||
ret=ret*16+(*str-'0');
|
||||
else if('a'<=*str && *str<='f')
|
||||
ret+=(*str-'a'+10);
|
||||
ret=ret*16+(*str-'a'+10);
|
||||
else if('A'<=*str && *str<='F')
|
||||
ret+=(*str-'A'+10);
|
||||
ret=ret*16+(*str-'A'+10);
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
inline double oct_to_double(const char* str)
|
||||
inline f64 oct2f(const char* str)
|
||||
{
|
||||
double ret=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
ret*=8;
|
||||
if('0'<=*str && *str<'8')
|
||||
ret+=(*str-'0');
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
f64 ret=0;
|
||||
while('0'<=*str && *str<'8')
|
||||
ret=ret*8+(*str++-'0');
|
||||
if(*str) return nan("");
|
||||
return ret;
|
||||
}
|
||||
inline double dec_to_double(const char* str)
|
||||
// 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.
|
||||
inline f64 dec2f(const char* str)
|
||||
{
|
||||
double ret=0,negative=1,num_pow=0;
|
||||
f64 ret=0,negative=1,num_pow=0;
|
||||
while('0'<=*str && *str<='9')
|
||||
ret=ret*10+(*str++-'0');
|
||||
if(!*str) return ret;
|
||||
@@ -83,60 +75,81 @@ inline double dec_to_double(const char* str)
|
||||
negative=(*str++=='-'? -1:1);
|
||||
if(!*str) return nan("");
|
||||
num_pow=0;
|
||||
for(;*str;++str)
|
||||
{
|
||||
if('0'<=*str && *str<='9')
|
||||
num_pow=num_pow*10+(*str-'0');
|
||||
else
|
||||
return nan("");
|
||||
}
|
||||
while('0'<=*str && *str<='9')
|
||||
num_pow=num_pow*10+(*str++-'0');
|
||||
if(*str) return nan("");
|
||||
return ret*std::pow(10,negative*num_pow);
|
||||
}
|
||||
double str2num(const char* str)
|
||||
f64 str2num(const char* str)
|
||||
{
|
||||
bool is_negative=false;
|
||||
double ret_num=0;
|
||||
bool negative=false;
|
||||
f64 res=0;
|
||||
if(*str=='-' || *str=='+')
|
||||
is_negative=(*str++=='-');
|
||||
negative=(*str++=='-');
|
||||
if(!*str)
|
||||
return nan("");
|
||||
if(str[0]=='0' && str[1]=='x')
|
||||
ret_num=hex_to_double(str+2);
|
||||
res=hex2f(str+2);
|
||||
else if(str[0]=='0' && str[1]=='o')
|
||||
ret_num=oct_to_double(str+2);
|
||||
res=oct2f(str+2);
|
||||
else
|
||||
ret_num=dec_to_double(str);
|
||||
return is_negative?-ret_num:ret_num;
|
||||
res=dec2f(str);
|
||||
return negative?-res:res;
|
||||
}
|
||||
|
||||
std::string rawstr(const std::string& str)
|
||||
i32 utf8_hdchk(const char head)
|
||||
{
|
||||
std::string ret("");
|
||||
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
|
||||
const u8 c=(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;
|
||||
}
|
||||
|
||||
string chrhex(const char c)
|
||||
{
|
||||
const char hextbl[]="0123456789abcdef";
|
||||
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
|
||||
}
|
||||
|
||||
string rawstr(const string& str,const usize maxlen=0)
|
||||
{
|
||||
string ret("");
|
||||
for(auto i:str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// windows ps or cmd doesn't output unicode normally
|
||||
// if 'chcp65001' is not enabled, we output the hex
|
||||
if(i<=0)
|
||||
{
|
||||
ret+="\\x"+chrhex(i);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
switch(i)
|
||||
{
|
||||
case '\a': ret+="\\a";break;
|
||||
case '\b': ret+="\\b";break;
|
||||
case '\f': ret+="\\f";break;
|
||||
case '\n': ret+="\\n";break;
|
||||
case '\r': ret+="\\r";break;
|
||||
case '\t': ret+="\\t";break;
|
||||
case '\v': ret+="\\v";break;
|
||||
case '\0': ret+="\\0";break;
|
||||
default: ret+=i; break;
|
||||
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;
|
||||
}
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "nasal_opt.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "nasal_builtin.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
#endif
|
||||
#include "nasal_gc.h" // declarations of var and nasal_gc
|
||||
|
||||
198
nasal_ast.h
@@ -1,7 +1,9 @@
|
||||
#ifndef __NASAL_AST_H__
|
||||
#define __NASAL_AST_H__
|
||||
#pragma once
|
||||
|
||||
enum ast_node
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
enum ast_node:u32
|
||||
{
|
||||
ast_null=0, // null node
|
||||
ast_root, // mark the root node of ast
|
||||
@@ -50,12 +52,12 @@ enum ast_node
|
||||
ast_foreach, // foreach keyword
|
||||
ast_while, // while
|
||||
ast_iter, // iterator, used in forindex/foreach
|
||||
ast_conditional, // mark a sub-tree of conditional expression
|
||||
ast_cond, // mark a sub-tree of conditional expression
|
||||
ast_if, // if keyword
|
||||
ast_elsif, // elsif keyword
|
||||
ast_else, // else keyword
|
||||
ast_multi_id, // multi identifiers sub-tree
|
||||
ast_multi_scalar,// multi value sub-tree
|
||||
ast_tuple, // tuple, only used in multiple assignment
|
||||
ast_def, // definition
|
||||
ast_multi_assign,// multi assignment sub-tree
|
||||
ast_continue, // continue keyword, only used in loop
|
||||
@@ -76,7 +78,7 @@ const char* ast_name[]=
|
||||
"func",
|
||||
"hash",
|
||||
"vec",
|
||||
"hashmember",
|
||||
"pair",
|
||||
"call",
|
||||
"callh",
|
||||
"callv",
|
||||
@@ -104,145 +106,151 @@ const char* ast_name[]=
|
||||
"*",
|
||||
"/",
|
||||
"~",
|
||||
"unary-",
|
||||
"unary!",
|
||||
"neg",
|
||||
"!",
|
||||
"trino",
|
||||
"for",
|
||||
"forindex",
|
||||
"foreach",
|
||||
"while",
|
||||
"iter",
|
||||
"conditional",
|
||||
"cond",
|
||||
"if",
|
||||
"elsif",
|
||||
"else",
|
||||
"multi_id",
|
||||
"multi_scalar",
|
||||
"multi-id",
|
||||
"tuple",
|
||||
"def",
|
||||
"multi_assign",
|
||||
"multi-assign",
|
||||
"continue",
|
||||
"break",
|
||||
"return",
|
||||
nullptr
|
||||
"return"
|
||||
};
|
||||
|
||||
class nasal_ast
|
||||
class ast
|
||||
{
|
||||
private:
|
||||
uint32_t _line;
|
||||
uint32_t _type;
|
||||
double _num;
|
||||
std::string _str;
|
||||
std::vector<nasal_ast> _child;
|
||||
u32 nd_line;
|
||||
u32 nd_col;
|
||||
u32 nd_type;
|
||||
f64 nd_num;
|
||||
string nd_str;
|
||||
std::vector<ast> nd_child;
|
||||
public:
|
||||
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):_line(l),_type(t){}
|
||||
nasal_ast(const nasal_ast&);
|
||||
nasal_ast(nasal_ast&&);
|
||||
void print(int,bool);
|
||||
ast(const u32 l,const u32 c,const u32 t):
|
||||
nd_line(l),nd_col(c),nd_type(t),nd_num(0){}
|
||||
ast(const ast&);
|
||||
ast(ast&&);
|
||||
void print_tree();
|
||||
void print(u32,bool,std::vector<string>&);
|
||||
void clear();
|
||||
|
||||
nasal_ast& operator=(const nasal_ast&);
|
||||
nasal_ast& operator=(nasal_ast&&);
|
||||
nasal_ast& operator[](const int index){return _child[index];}
|
||||
const nasal_ast& operator[](const int index) const {return _child[index];}
|
||||
size_t size() const {return _child.size();}
|
||||
ast& operator=(const ast&);
|
||||
ast& operator=(ast&&);
|
||||
ast& operator[](usize n){return nd_child[n];}
|
||||
const ast& operator[](usize n) const {return nd_child[n];}
|
||||
usize size() const {return nd_child.size();}
|
||||
|
||||
void add(nasal_ast&& ast){_child.push_back(std::move(ast));}
|
||||
void add(const nasal_ast& ast){_child.push_back(ast);}
|
||||
void set_line(const uint32_t l){_line=l;}
|
||||
void set_type(const uint32_t t){_type=t;}
|
||||
void set_str(const std::string& s){_str=s;}
|
||||
void set_num(const double n){_num=n;}
|
||||
void add(ast&& node){nd_child.push_back(std::move(node));}
|
||||
void add(const ast& node){nd_child.push_back(node);}
|
||||
void set_line(const u32 l){nd_line=l;}
|
||||
void set_type(const u32 t){nd_type=t;}
|
||||
void set_str(const string& s){nd_str=s;}
|
||||
void set_num(const f64 n){nd_num=n;}
|
||||
|
||||
inline uint32_t line() const {return _line;}
|
||||
inline uint32_t type() const {return _type;}
|
||||
inline double num() const {return _num;}
|
||||
inline const std::string& str() const {return _str;}
|
||||
inline const std::vector<nasal_ast>& child() const {return _child;}
|
||||
inline std::vector<nasal_ast>& child(){return _child;}
|
||||
inline u32 line() const {return nd_line;}
|
||||
inline u32 col() const {return nd_col;}
|
||||
inline u32 type() const {return nd_type;}
|
||||
inline f64 num() const {return nd_num;}
|
||||
inline const string& str() const {return nd_str;}
|
||||
inline const std::vector<ast>& child() const {return nd_child;}
|
||||
inline std::vector<ast>& child(){return nd_child;}
|
||||
};
|
||||
|
||||
nasal_ast::nasal_ast(const nasal_ast& tmp)
|
||||
ast::ast(const ast& tmp):
|
||||
nd_str(tmp.nd_str),nd_child(tmp.nd_child)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num =tmp._num;
|
||||
_str =tmp._str;
|
||||
_child=tmp._child;
|
||||
nd_line=tmp.nd_line;
|
||||
nd_col=tmp.nd_col;
|
||||
nd_type=tmp.nd_type;
|
||||
nd_num =tmp.nd_num;
|
||||
}
|
||||
|
||||
nasal_ast::nasal_ast(nasal_ast&& tmp)
|
||||
ast::ast(ast&& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num =tmp._num;
|
||||
_str.swap(tmp._str);
|
||||
_child.swap(tmp._child);
|
||||
nd_line=tmp.nd_line;
|
||||
nd_col=tmp.nd_col;
|
||||
nd_type=tmp.nd_type;
|
||||
nd_num =tmp.nd_num;
|
||||
nd_str.swap(tmp.nd_str);
|
||||
nd_child.swap(tmp.nd_child);
|
||||
}
|
||||
|
||||
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
|
||||
ast& ast::operator=(const ast& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num=tmp._num;
|
||||
_str=tmp._str;
|
||||
_child=tmp._child;
|
||||
nd_line=tmp.nd_line;
|
||||
nd_col=tmp.nd_col;
|
||||
nd_type=tmp.nd_type;
|
||||
nd_num=tmp.nd_num;
|
||||
nd_str=tmp.nd_str;
|
||||
nd_child=tmp.nd_child;
|
||||
return *this;
|
||||
}
|
||||
|
||||
nasal_ast& nasal_ast::operator=(nasal_ast&& tmp)
|
||||
ast& ast::operator=(ast&& tmp)
|
||||
{
|
||||
_line=tmp._line;
|
||||
_type=tmp._type;
|
||||
_num=tmp._num;
|
||||
_str.swap(tmp._str);
|
||||
_child.swap(tmp._child);
|
||||
nd_line=tmp.nd_line;
|
||||
nd_col=tmp.nd_col;
|
||||
nd_type=tmp.nd_type;
|
||||
nd_num=tmp.nd_num;
|
||||
nd_str.swap(tmp.nd_str);
|
||||
nd_child.swap(tmp.nd_child);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void nasal_ast::clear()
|
||||
void ast::clear()
|
||||
{
|
||||
_line=0;
|
||||
_num=0;
|
||||
_str="";
|
||||
_type=ast_null;
|
||||
_child.clear();
|
||||
nd_line=nd_col=0;
|
||||
nd_num=0;
|
||||
nd_str.clear();
|
||||
nd_type=ast_null;
|
||||
nd_child.clear();
|
||||
}
|
||||
|
||||
void nasal_ast::print(int depth,bool last=false)
|
||||
void ast::print_tree()
|
||||
{
|
||||
static std::vector<std::string> intentation={};
|
||||
for(auto& i:intentation)
|
||||
std::vector<string> tmp;
|
||||
print(0,false,tmp);
|
||||
}
|
||||
|
||||
void ast::print(u32 depth,bool last,std::vector<string>& indent)
|
||||
{
|
||||
for(auto& i:indent)
|
||||
std::cout<<i;
|
||||
std::cout<<ast_name[_type];
|
||||
if(
|
||||
_type==ast_str ||
|
||||
_type==ast_id ||
|
||||
_type==ast_default ||
|
||||
_type==ast_dynamic ||
|
||||
_type==ast_callh)
|
||||
std::cout<<":"<<rawstr(_str);
|
||||
else if(_type==ast_num || _type==ast_file)
|
||||
std::cout<<":"<<_num;
|
||||
std::cout<<'\n';
|
||||
std::cout<<ast_name[nd_type];
|
||||
if(nd_type==ast_str || nd_type==ast_id ||
|
||||
nd_type==ast_default || nd_type==ast_dynamic ||
|
||||
nd_type==ast_callh)
|
||||
std::cout<<":"<<rawstr(nd_str);
|
||||
else if(nd_type==ast_num || nd_type==ast_file)
|
||||
std::cout<<":"<<nd_num;
|
||||
std::cout<<"\n";
|
||||
if(last && depth)
|
||||
intentation.back()=" ";
|
||||
indent.back()=" ";
|
||||
else if(!last && depth)
|
||||
#ifdef _WIN32
|
||||
intentation.back()="| ";
|
||||
indent.back()="| ";
|
||||
#else
|
||||
intentation.back()="│ ";
|
||||
indent.back()="│ ";
|
||||
#endif
|
||||
for(uint32_t i=0;i<_child.size();++i)
|
||||
for(u32 i=0;i<nd_child.size();++i)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
intentation.push_back(i==_child.size()-1?"`-":"|-");
|
||||
indent.push_back(i==nd_child.size()-1?"+-":"|-");
|
||||
#else
|
||||
intentation.push_back(i==_child.size()-1?"└─":"├─");
|
||||
indent.push_back(i==nd_child.size()-1?"└─":"├─");
|
||||
#endif
|
||||
_child[i].print(depth+1,i==_child.size()-1);
|
||||
intentation.pop_back();
|
||||
nd_child[i].print(depth+1,i==nd_child.size()-1,indent);
|
||||
indent.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1716
nasal_builtin.h
1361
nasal_codegen.h
486
nasal_dbg.h
@@ -1,124 +1,159 @@
|
||||
#ifndef __NASAL_DBG_H__
|
||||
#define __NASAL_DBG_H__
|
||||
#pragma once
|
||||
|
||||
#include "nasal_err.h"
|
||||
#include "nasal_vm.h"
|
||||
#include <algorithm>
|
||||
|
||||
class nasal_dbg:public nasal_vm
|
||||
class debugger:public vm
|
||||
{
|
||||
private:
|
||||
bool next_step;
|
||||
uint16_t bk_fidx;
|
||||
uint32_t bk_line;
|
||||
file_line src;
|
||||
bool next;
|
||||
usize fsize;
|
||||
u16 bk_fidx;
|
||||
u32 bk_line;
|
||||
error& src;
|
||||
|
||||
std::vector<std::string> parse(const std::string&);
|
||||
uint16_t get_fileindex(const std::string&);
|
||||
std::vector<string> parse(const string&);
|
||||
u16 fileindex(const string&);
|
||||
void err();
|
||||
void help();
|
||||
void callsort(const u64*);
|
||||
void stepinfo();
|
||||
void interact();
|
||||
public:
|
||||
nasal_dbg():
|
||||
next_step(false),
|
||||
bk_fidx(0),bk_line(0){}
|
||||
debugger(error& err):
|
||||
next(false),fsize(0),
|
||||
bk_fidx(0),bk_line(0),
|
||||
src(err){}
|
||||
void run(
|
||||
const nasal_codegen&,
|
||||
const nasal_import&
|
||||
const codegen&,
|
||||
const linker&,
|
||||
const std::vector<string>&
|
||||
);
|
||||
};
|
||||
|
||||
std::vector<std::string> nasal_dbg::parse(const std::string& cmd)
|
||||
std::vector<string> debugger::parse(const string& cmd)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
std::string tmp="";
|
||||
for(uint32_t i=0;i<cmd.length();++i)
|
||||
std::vector<string> res;
|
||||
usize last=0,pos=cmd.find(" ",0);
|
||||
while(pos!=string::npos)
|
||||
{
|
||||
if(cmd[i]==' ' && tmp.length())
|
||||
{
|
||||
res.push_back(tmp);
|
||||
tmp="";
|
||||
continue;
|
||||
}
|
||||
tmp+=cmd[i];
|
||||
if(pos>last)
|
||||
res.push_back(cmd.substr(last,pos-last));
|
||||
last=pos+1;
|
||||
pos=cmd.find(" ",last);
|
||||
}
|
||||
if(tmp.length())
|
||||
res.push_back(tmp);
|
||||
if(last<cmd.length())
|
||||
res.push_back(cmd.substr(last));
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t nasal_dbg::get_fileindex(const std::string& filename)
|
||||
u16 debugger::fileindex(const string& filename)
|
||||
{
|
||||
for(uint16_t i=0;i<files_size;++i)
|
||||
for(u16 i=0;i<fsize;++i)
|
||||
if(filename==files[i])
|
||||
return i;
|
||||
return 65535;
|
||||
}
|
||||
|
||||
void nasal_dbg::err()
|
||||
void debugger::err()
|
||||
{
|
||||
std::cerr
|
||||
<<"incorrect command\n"
|
||||
<<"input \'h\' to get help\n";
|
||||
}
|
||||
|
||||
void nasal_dbg::help()
|
||||
void debugger::help()
|
||||
{
|
||||
std::cout
|
||||
<<"<option>\n"
|
||||
<<"\th, help | get help\n"
|
||||
<<"\tbt, backtrace | get function call trace\n"
|
||||
<<"\tc, continue | run program until break point or exit\n"
|
||||
<<"\tg, global | see global values\n"
|
||||
<<"\tl, local | see local values\n"
|
||||
<<"\tu, upval | see upvalue\n"
|
||||
<<"\ta, all | show global,local and upvalue\n"
|
||||
<<"\tn, next | execute next bytecode\n"
|
||||
<<"\tq, exit | exit debugger\n"
|
||||
<<"\th, help | get help\n"
|
||||
<<"\tbt, backtrace | get function call trace\n"
|
||||
<<"\tc, continue | run program until break point or exit\n"
|
||||
<<"\tf, file | see all the compiled files\n"
|
||||
<<"\tg, global | see global values\n"
|
||||
<<"\tl, local | see local values\n"
|
||||
<<"\tu, upval | see upvalue\n"
|
||||
<<"\tr, register | show vm register detail\n"
|
||||
<<"\ta, all | show global,local and upvalue\n"
|
||||
<<"\tn, next | execute next bytecode\n"
|
||||
<<"\tq, exit | exit debugger\n"
|
||||
<<"<option> <filename> <line>\n"
|
||||
<<"\tbk, break | set break point\n";
|
||||
<<"\tbk, break | set break point\n";
|
||||
}
|
||||
|
||||
void nasal_dbg::stepinfo()
|
||||
void debugger::callsort(const u64* arr)
|
||||
{
|
||||
uint32_t begin,end;
|
||||
uint32_t line=bytecode[pc].line==0?0:bytecode[pc].line-1;
|
||||
typedef std::pair<u32,u64> op;
|
||||
std::vector<op> opcall;
|
||||
u64 total=0;
|
||||
for(u32 i=0;i<op_ret+1;++i)
|
||||
{
|
||||
total+=arr[i];
|
||||
opcall.push_back({i,arr[i]});
|
||||
}
|
||||
std::sort(opcall.begin(),opcall.end(),
|
||||
[](const op& a,const op& b){return a.second>b.second;}
|
||||
);
|
||||
std::clog<<"\noperands call info";
|
||||
for(auto& i:opcall)
|
||||
{
|
||||
u64 rate=i.second*100/total;
|
||||
if(!rate)
|
||||
{
|
||||
std::clog<<"\n ...";
|
||||
break;
|
||||
}
|
||||
std::clog<<"\n "<<code_table[i.first]
|
||||
<<" : "<<i.second<<" ("<<rate<<"%)";
|
||||
}
|
||||
std::clog<<"\n total : "<<total<<'\n';
|
||||
}
|
||||
|
||||
void debugger::stepinfo()
|
||||
{
|
||||
u32 line=bytecode[pc].line==0?0:bytecode[pc].line-1;
|
||||
u32 begin=(line>>3)==0?0:((line>>3)<<3);
|
||||
u32 end=(1+(line>>3))<<3;
|
||||
src.load(files[bytecode[pc].fidx]);
|
||||
printf("\nsource code:\n");
|
||||
begin=(line>>3)==0?0:((line>>3)<<3);
|
||||
end=(1+(line>>3))<<3;
|
||||
for(uint32_t i=begin;i<end && i<src.size();++i)
|
||||
printf("%s\t%s\n",i==line?"-->":" ",src[i].c_str());
|
||||
printf("next bytecode:\n");
|
||||
std::cout<<"\nsource code:\n";
|
||||
for(u32 i=begin;i<end && i<src.size();++i)
|
||||
std::cout<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
|
||||
std::cout<<"next bytecode:\n";
|
||||
begin=(pc>>3)==0?0:((pc>>3)<<3);
|
||||
end=(1+(pc>>3))<<3;
|
||||
for(uint32_t i=begin;i<end && bytecode[i].op!=op_exit;++i)
|
||||
bytecodeinfo(i==pc?"-->\t":" \t",i);
|
||||
stackinfo(5);
|
||||
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i)
|
||||
std::cout
|
||||
<<(i==pc?back_white:reset)<<(i==pc?"--> ":" ")
|
||||
<<codestream(bytecode[i],i,cnum,cstr,files)
|
||||
<<reset<<"\n";
|
||||
stackinfo(10);
|
||||
}
|
||||
|
||||
void nasal_dbg::interact()
|
||||
void debugger::interact()
|
||||
{
|
||||
// special operand
|
||||
if(bytecode[pc].op==op_intg)
|
||||
{
|
||||
std::cout
|
||||
<<"[debug] nasal debug mode\n"
|
||||
<<bold_cyan<<"[debug] "<<reset
|
||||
<<"nasal debug mode\n"
|
||||
<<"input \'h\' to get help\n";
|
||||
}
|
||||
else if(bytecode[pc].op==op_nop || bytecode[pc].op==op_exit)
|
||||
else if(bytecode[pc].op==op_exit)
|
||||
return;
|
||||
|
||||
if(
|
||||
(bytecode[pc].fidx!=bk_fidx || bytecode[pc].line!=bk_line) && // break point
|
||||
!next_step // next step
|
||||
!next // next step
|
||||
)return;
|
||||
|
||||
next_step=false;
|
||||
std::string cmd;
|
||||
next=false;
|
||||
string cmd;
|
||||
stepinfo();
|
||||
while(1)
|
||||
{
|
||||
printf(">> ");
|
||||
std::cout<<">> ";
|
||||
std::getline(std::cin,cmd);
|
||||
auto res=parse(cmd);
|
||||
if(res.size()==1)
|
||||
@@ -129,21 +164,22 @@ void nasal_dbg::interact()
|
||||
traceback();
|
||||
else if(res[0]=="c" || res[0]=="continue")
|
||||
return;
|
||||
else if(res[0]=="f" || res[0]=="file")
|
||||
for(usize i=0;i<fsize;++i)
|
||||
std::cout<<"["<<i<<"] "<<files[i]<<"\n";
|
||||
else if(res[0]=="g" || res[0]=="global")
|
||||
global_state();
|
||||
gstate();
|
||||
else if(res[0]=="l" || res[0]=="local")
|
||||
local_state();
|
||||
lstate();
|
||||
else if(res[0]=="u" || res[0]=="upval")
|
||||
upval_state();
|
||||
ustate();
|
||||
else if(res[0]=="r" || res[0]=="register")
|
||||
reginfo();
|
||||
else if(res[0]=="a" || res[0]=="all")
|
||||
{
|
||||
global_state();
|
||||
local_state();
|
||||
upval_state();
|
||||
}
|
||||
detail();
|
||||
else if(res[0]=="n" || res[0]=="next")
|
||||
{
|
||||
next_step=true;
|
||||
next=true;
|
||||
return;
|
||||
}
|
||||
else if(res[0]=="q" || res[0]=="exit")
|
||||
@@ -151,156 +187,214 @@ void nasal_dbg::interact()
|
||||
else
|
||||
err();
|
||||
}
|
||||
else if(res.size()==3)
|
||||
else if(res.size()==3 && (res[0]=="bk" || res[0]=="break"))
|
||||
{
|
||||
if(res[0]=="bk" || res[0]=="break")
|
||||
bk_fidx=fileindex(res[1]);
|
||||
if(bk_fidx==65535)
|
||||
{
|
||||
bk_fidx=get_fileindex(res[1]);
|
||||
if(bk_fidx==65535)
|
||||
{
|
||||
printf("cannot find file named \"%s\"\n",res[1].c_str());
|
||||
bk_fidx=0;
|
||||
}
|
||||
int tmp=atoi(res[2].c_str());
|
||||
if(tmp<=0)
|
||||
printf("incorrect line number \"%s\"\n",res[2].c_str());
|
||||
else
|
||||
bk_line=tmp;
|
||||
std::cout<<"cannot find file named `"<<res[1]<<"`\n";
|
||||
bk_fidx=0;
|
||||
}
|
||||
i32 tmp=atoi(res[2].c_str());
|
||||
if(tmp<=0)
|
||||
std::cout<<"incorrect line number `"<<res[2]<<"`\n";
|
||||
else
|
||||
err();
|
||||
bk_line=tmp;
|
||||
}
|
||||
else
|
||||
err();
|
||||
}
|
||||
}
|
||||
|
||||
void nasal_dbg::run(
|
||||
const nasal_codegen& gen,
|
||||
const nasal_import& linker)
|
||||
void debugger::run(
|
||||
const codegen& gen,
|
||||
const linker& linker,
|
||||
const std::vector<string>& argv)
|
||||
{
|
||||
detail_info=true;
|
||||
init(gen.get_strs(),gen.get_nums(),linker.get_file());
|
||||
const void* opr_table[]=
|
||||
fsize=linker.filelist().size();
|
||||
init(gen.strs(),gen.nums(),gen.codes(),linker.filelist(),argv);
|
||||
u64 count[op_ret+1]={0};
|
||||
#ifndef _MSC_VER
|
||||
const void* oprs[]=
|
||||
{
|
||||
&&nop, &&intg, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&defpara,&&dynpara,
|
||||
&&unot, &&usub, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
|
||||
&&diveqc, &&lnkeqc, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&counter, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbegin, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret, &&vmexit
|
||||
&&vmexit, &&intg, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&deft, &&dyn,
|
||||
&&unot, &&usub, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
|
||||
&&diveqc, &&lnkeqc, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&cnt, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbeg, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret
|
||||
};
|
||||
bytecode=gen.get_code().data();
|
||||
std::vector<const void*> code;
|
||||
for(auto& i:gen.get_code())
|
||||
for(auto& i:gen.codes())
|
||||
{
|
||||
code.push_back(opr_table[i.op]);
|
||||
code.push_back(oprs[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
|
||||
// set canary and program counter
|
||||
auto canary=gc.stack+STACK_MAX_DEPTH-1;
|
||||
pc=0;
|
||||
// goto the first operand
|
||||
goto *code[pc];
|
||||
#else
|
||||
typedef void (debugger::*nafunc)();
|
||||
const nafunc oprs[]=
|
||||
{
|
||||
nullptr, &debugger::o_intg,
|
||||
&debugger::o_intl, &debugger::o_loadg,
|
||||
&debugger::o_loadl, &debugger::o_loadu,
|
||||
&debugger::o_pnum, &debugger::o_pnil,
|
||||
&debugger::o_pstr, &debugger::o_newv,
|
||||
&debugger::o_newh, &debugger::o_newf,
|
||||
&debugger::o_happ, &debugger::o_para,
|
||||
&debugger::o_deft, &debugger::o_dyn,
|
||||
&debugger::o_unot, &debugger::o_usub,
|
||||
&debugger::o_add, &debugger::o_sub,
|
||||
&debugger::o_mul, &debugger::o_div,
|
||||
&debugger::o_lnk, &debugger::o_addc,
|
||||
&debugger::o_subc, &debugger::o_mulc,
|
||||
&debugger::o_divc, &debugger::o_lnkc,
|
||||
&debugger::o_addeq, &debugger::o_subeq,
|
||||
&debugger::o_muleq, &debugger::o_diveq,
|
||||
&debugger::o_lnkeq, &debugger::o_addeqc,
|
||||
&debugger::o_subeqc, &debugger::o_muleqc,
|
||||
&debugger::o_diveqc, &debugger::o_lnkeqc,
|
||||
&debugger::o_meq, &debugger::o_eq,
|
||||
&debugger::o_neq, &debugger::o_less,
|
||||
&debugger::o_leq, &debugger::o_grt,
|
||||
&debugger::o_geq, &debugger::o_lessc,
|
||||
&debugger::o_leqc, &debugger::o_grtc,
|
||||
&debugger::o_geqc, &debugger::o_pop,
|
||||
&debugger::o_jmp, &debugger::o_jt,
|
||||
&debugger::o_jf, &debugger::o_cnt,
|
||||
&debugger::o_findex, &debugger::o_feach,
|
||||
&debugger::o_callg, &debugger::o_calll,
|
||||
&debugger::o_upval, &debugger::o_callv,
|
||||
&debugger::o_callvi, &debugger::o_callh,
|
||||
&debugger::o_callfv, &debugger::o_callfh,
|
||||
&debugger::o_callb, &debugger::o_slcbeg,
|
||||
&debugger::o_slcend, &debugger::o_slc,
|
||||
&debugger::o_slc2, &debugger::o_mcallg,
|
||||
&debugger::o_mcalll, &debugger::o_mupval,
|
||||
&debugger::o_mcallv, &debugger::o_mcallh,
|
||||
&debugger::o_ret
|
||||
};
|
||||
std::vector<u32> code;
|
||||
for(auto& i:gen.codes())
|
||||
{
|
||||
code.push_back(i.op);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(oprs[code[pc]]){
|
||||
interact();
|
||||
++count[code[pc]];
|
||||
(this->*oprs[code[pc]])();
|
||||
if(top>=canary)
|
||||
die("stack overflow");
|
||||
++pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
vmexit:
|
||||
if(gc.top>=canary)
|
||||
die("stack overflow");
|
||||
gc.clear();
|
||||
callsort(count);
|
||||
ngc.clear();
|
||||
imm.clear();
|
||||
printf("[debug] debugger exited\n");
|
||||
std::cout<<bold_cyan<<"[debug] "<<reset<<"debugger exited\n";
|
||||
return;
|
||||
#define dbg(op) {interact();op();if(gc.top<canary)goto *code[++pc];goto vmexit;}
|
||||
#ifndef _MSC_VER
|
||||
#define dbg(op,num) {\
|
||||
interact();\
|
||||
op();\
|
||||
++count[num];\
|
||||
if(top<canary)\
|
||||
goto *code[++pc];\
|
||||
die("stack overflow");\
|
||||
goto *code[++pc];\
|
||||
}
|
||||
|
||||
nop: dbg(opr_nop );
|
||||
intg: dbg(opr_intg );
|
||||
intl: dbg(opr_intl );
|
||||
loadg: dbg(opr_loadg );
|
||||
loadl: dbg(opr_loadl );
|
||||
loadu: dbg(opr_loadu );
|
||||
pnum: dbg(opr_pnum );
|
||||
pnil: dbg(opr_pnil );
|
||||
pstr: dbg(opr_pstr );
|
||||
newv: dbg(opr_newv );
|
||||
newh: dbg(opr_newh );
|
||||
newf: dbg(opr_newf );
|
||||
happ: dbg(opr_happ );
|
||||
para: dbg(opr_para );
|
||||
defpara: dbg(opr_defpara );
|
||||
dynpara: dbg(opr_dynpara );
|
||||
unot: dbg(opr_unot );
|
||||
usub: dbg(opr_usub );
|
||||
add: dbg(opr_add );
|
||||
sub: dbg(opr_sub );
|
||||
mul: dbg(opr_mul );
|
||||
div: dbg(opr_div );
|
||||
lnk: dbg(opr_lnk );
|
||||
addc: dbg(opr_addc );
|
||||
subc: dbg(opr_subc );
|
||||
mulc: dbg(opr_mulc );
|
||||
divc: dbg(opr_divc );
|
||||
lnkc: dbg(opr_lnkc );
|
||||
addeq: dbg(opr_addeq );
|
||||
subeq: dbg(opr_subeq );
|
||||
muleq: dbg(opr_muleq );
|
||||
diveq: dbg(opr_diveq );
|
||||
lnkeq: dbg(opr_lnkeq );
|
||||
addeqc: dbg(opr_addeqc );
|
||||
subeqc: dbg(opr_subeqc );
|
||||
muleqc: dbg(opr_muleqc );
|
||||
diveqc: dbg(opr_diveqc );
|
||||
lnkeqc: dbg(opr_lnkeqc );
|
||||
meq: dbg(opr_meq );
|
||||
eq: dbg(opr_eq );
|
||||
neq: dbg(opr_neq );
|
||||
less: dbg(opr_less );
|
||||
leq: dbg(opr_leq );
|
||||
grt: dbg(opr_grt );
|
||||
geq: dbg(opr_geq );
|
||||
lessc: dbg(opr_lessc );
|
||||
leqc: dbg(opr_leqc );
|
||||
grtc: dbg(opr_grtc );
|
||||
geqc: dbg(opr_geqc );
|
||||
pop: dbg(opr_pop );
|
||||
jmp: dbg(opr_jmp );
|
||||
jt: dbg(opr_jt );
|
||||
jf: dbg(opr_jf );
|
||||
counter: dbg(opr_counter );
|
||||
findex: dbg(opr_findex );
|
||||
feach: dbg(opr_feach );
|
||||
callg: dbg(opr_callg );
|
||||
calll: dbg(opr_calll );
|
||||
upval: dbg(opr_upval );
|
||||
callv: dbg(opr_callv );
|
||||
callvi: dbg(opr_callvi );
|
||||
callh: dbg(opr_callh );
|
||||
callfv: dbg(opr_callfv );
|
||||
callfh: dbg(opr_callfh );
|
||||
callb: dbg(opr_callb );
|
||||
slcbegin:dbg(opr_slcbegin);
|
||||
slcend: dbg(opr_slcend );
|
||||
slc: dbg(opr_slc );
|
||||
slc2: dbg(opr_slc2 );
|
||||
mcallg: dbg(opr_mcallg );
|
||||
mcalll: dbg(opr_mcalll );
|
||||
mupval: dbg(opr_mupval );
|
||||
mcallv: dbg(opr_mcallv );
|
||||
mcallh: dbg(opr_mcallh );
|
||||
ret: dbg(opr_ret );
|
||||
intg: dbg(o_intg ,op_intg );
|
||||
intl: dbg(o_intl ,op_intl );
|
||||
loadg: dbg(o_loadg ,op_loadg );
|
||||
loadl: dbg(o_loadl ,op_loadl );
|
||||
loadu: dbg(o_loadu ,op_loadu );
|
||||
pnum: dbg(o_pnum ,op_pnum );
|
||||
pnil: dbg(o_pnil ,op_pnil );
|
||||
pstr: dbg(o_pstr ,op_pstr );
|
||||
newv: dbg(o_newv ,op_newv );
|
||||
newh: dbg(o_newh ,op_newh );
|
||||
newf: dbg(o_newf ,op_newf );
|
||||
happ: dbg(o_happ ,op_happ );
|
||||
para: dbg(o_para ,op_para );
|
||||
deft: dbg(o_deft ,op_deft );
|
||||
dyn: dbg(o_dyn ,op_dyn );
|
||||
unot: dbg(o_unot ,op_unot );
|
||||
usub: dbg(o_usub ,op_usub );
|
||||
add: dbg(o_add ,op_add );
|
||||
sub: dbg(o_sub ,op_sub );
|
||||
mul: dbg(o_mul ,op_mul );
|
||||
div: dbg(o_div ,op_div );
|
||||
lnk: dbg(o_lnk ,op_lnk );
|
||||
addc: dbg(o_addc ,op_addc );
|
||||
subc: dbg(o_subc ,op_subc );
|
||||
mulc: dbg(o_mulc ,op_mulc );
|
||||
divc: dbg(o_divc ,op_divc );
|
||||
lnkc: dbg(o_lnkc ,op_lnkc );
|
||||
addeq: dbg(o_addeq ,op_addeq );
|
||||
subeq: dbg(o_subeq ,op_subeq );
|
||||
muleq: dbg(o_muleq ,op_muleq );
|
||||
diveq: dbg(o_diveq ,op_diveq );
|
||||
lnkeq: dbg(o_lnkeq ,op_lnkeq );
|
||||
addeqc: dbg(o_addeqc,op_addeqc);
|
||||
subeqc: dbg(o_subeqc,op_subeqc);
|
||||
muleqc: dbg(o_muleqc,op_muleqc);
|
||||
diveqc: dbg(o_diveqc,op_diveqc);
|
||||
lnkeqc: dbg(o_lnkeqc,op_lnkeqc);
|
||||
meq: dbg(o_meq ,op_meq );
|
||||
eq: dbg(o_eq ,op_eq );
|
||||
neq: dbg(o_neq ,op_neq );
|
||||
less: dbg(o_less ,op_less );
|
||||
leq: dbg(o_leq ,op_leq );
|
||||
grt: dbg(o_grt ,op_grt );
|
||||
geq: dbg(o_geq ,op_geq );
|
||||
lessc: dbg(o_lessc ,op_lessc );
|
||||
leqc: dbg(o_leqc ,op_leqc );
|
||||
grtc: dbg(o_grtc ,op_grtc );
|
||||
geqc: dbg(o_geqc ,op_geqc );
|
||||
pop: dbg(o_pop ,op_pop );
|
||||
jmp: dbg(o_jmp ,op_jmp );
|
||||
jt: dbg(o_jt ,op_jt );
|
||||
jf: dbg(o_jf ,op_jf );
|
||||
cnt: dbg(o_cnt ,op_cnt );
|
||||
findex: dbg(o_findex,op_findex);
|
||||
feach: dbg(o_feach ,op_feach );
|
||||
callg: dbg(o_callg ,op_callg );
|
||||
calll: dbg(o_calll ,op_calll );
|
||||
upval: dbg(o_upval ,op_upval );
|
||||
callv: dbg(o_callv ,op_callv );
|
||||
callvi: dbg(o_callvi,op_callvi);
|
||||
callh: dbg(o_callh ,op_callh );
|
||||
callfv: dbg(o_callfv,op_callfv);
|
||||
callfh: dbg(o_callfh,op_callfh);
|
||||
callb: dbg(o_callb ,op_callb );
|
||||
slcbeg: dbg(o_slcbeg,op_slcbeg);
|
||||
slcend: dbg(o_slcend,op_slcend);
|
||||
slc: dbg(o_slc ,op_slc );
|
||||
slc2: dbg(o_slc2 ,op_slc2 );
|
||||
mcallg: dbg(o_mcallg,op_mcallg);
|
||||
mcalll: dbg(o_mcalll,op_mcalll);
|
||||
mupval: dbg(o_mupval,op_mupval);
|
||||
mcallv: dbg(o_mcallv,op_mcallv);
|
||||
mcallh: dbg(o_mcallh,op_mcallh);
|
||||
ret: dbg(o_ret ,op_ret );
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
169
nasal_err.h
@@ -1,77 +1,160 @@
|
||||
#ifndef __NASAL_ERR_H__
|
||||
#define __NASAL_ERR_H__
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream> // MSVC need this to use std::getline
|
||||
#include <cstring>
|
||||
|
||||
class file_line
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // use SetConsoleTextAttribute
|
||||
struct for_reset
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO scr;
|
||||
for_reset(){
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&scr);
|
||||
}
|
||||
}reset_ter_color;
|
||||
#endif
|
||||
|
||||
std::ostream& back_white(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0xf0);
|
||||
#else
|
||||
s<<"\033[7m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
std::ostream& bold_red(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c);
|
||||
#else
|
||||
s<<"\033[91;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
std::ostream& bold_cyan(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03);
|
||||
#else
|
||||
s<<"\033[36;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
std::ostream& bold_orange(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0e);
|
||||
#else
|
||||
s<<"\033[93;1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
std::ostream& bold_white(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0f);
|
||||
#else
|
||||
s<<"\033[0m\033[1m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
std::ostream& reset(std::ostream& s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),reset_ter_color.scr.wAttributes);
|
||||
#else
|
||||
s<<"\033[0m";
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
class fstreamline
|
||||
{
|
||||
protected:
|
||||
std::string file;
|
||||
std::vector<std::string> res;
|
||||
string file;
|
||||
std::vector<string> res;
|
||||
public:
|
||||
void load(const std::string& f)
|
||||
void load(const string& f)
|
||||
{
|
||||
if(file==f) // don't need to load a loaded file
|
||||
return;
|
||||
if(file==f) return; // don't need to load a loaded file
|
||||
file=f;
|
||||
res.clear();
|
||||
std::ifstream fin(f,std::ios::binary);
|
||||
if(fin.fail())
|
||||
std::ifstream in(f,std::ios::binary);
|
||||
if(in.fail())
|
||||
{
|
||||
std::cerr<<"[src] cannot open file <"<<f<<">\n";
|
||||
std::cerr<<bold_red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
|
||||
std::exit(1);
|
||||
}
|
||||
std::string line;
|
||||
while(!fin.eof())
|
||||
string line;
|
||||
while(!in.eof())
|
||||
{
|
||||
std::getline(fin,line);
|
||||
std::getline(in,line);
|
||||
res.push_back(line);
|
||||
}
|
||||
}
|
||||
void clear()
|
||||
{
|
||||
std::vector<std::string> tmp;
|
||||
res.swap(tmp);
|
||||
}
|
||||
const std::string& operator[](const uint32_t line){return res[line];}
|
||||
const std::string& name(){return file;}
|
||||
size_t size(){return res.size();}
|
||||
const string& operator[](usize n){return res[n];}
|
||||
const string& name(){return file;}
|
||||
usize size(){return res.size();}
|
||||
};
|
||||
|
||||
class nasal_err:public file_line
|
||||
class error:public fstreamline
|
||||
{
|
||||
private:
|
||||
uint32_t error;
|
||||
public:
|
||||
void err(const char* stage,const std::string& info)
|
||||
u32 cnt;
|
||||
string identation(usize len)
|
||||
{
|
||||
++error;
|
||||
std::cerr<<"["<<stage<<"] "<<info<<'\n';
|
||||
string tmp="";
|
||||
tmp.resize(len,' ');
|
||||
return tmp;
|
||||
}
|
||||
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
|
||||
public:
|
||||
error():cnt(0){}
|
||||
void err(const char* stage,const string& info)
|
||||
{
|
||||
++error;
|
||||
++cnt;
|
||||
std::cerr<<bold_red<<stage<<": "
|
||||
<<bold_white<<info<<reset<<"\n\n";
|
||||
}
|
||||
void err(const char* stage,u32 line,u32 column,const string& info)
|
||||
{
|
||||
++cnt;
|
||||
const string& code=res[line-1];
|
||||
const string ident=identation(std::to_string(line).length());
|
||||
std::cerr<<bold_red<<stage<<": "
|
||||
<<bold_white<<info<<reset<<"\n"
|
||||
<<bold_cyan<<" --> "<<reset
|
||||
<<bold_orange<<file<<":"<<line<<":"<<column<<"\n";
|
||||
if(!line)
|
||||
{
|
||||
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
|
||||
std::cerr<<"\n";
|
||||
return;
|
||||
}
|
||||
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<res[line-1]<<'\n';
|
||||
for(int i=0;i<(int)column-1;++i)
|
||||
std::cerr<<char(" \t"[res[line-1][i]=='\t']);
|
||||
std::cerr<<"^\n";
|
||||
std::cerr<<bold_cyan<<ident<<" | "<<reset<<"\n"
|
||||
<<bold_cyan<<line<<" | "<<reset<<code<<"\n"
|
||||
<<bold_cyan<<ident<<" | "<<reset;
|
||||
for(i32 i=0;i<(i32)column-1;++i)
|
||||
std::cerr<<char(" \t"[code[i]=='\t']);
|
||||
std::cerr<<bold_red<<"^ "<<info<<reset<<"\n\n";
|
||||
}
|
||||
void err(const char* stage,uint32_t line,const std::string& info)
|
||||
void err(const char* stage,u32 line,const string& info)
|
||||
{
|
||||
++error;
|
||||
++cnt;
|
||||
const string ident=identation(std::to_string(line).length());
|
||||
std::cerr<<bold_red<<stage<<": "
|
||||
<<bold_white<<info<<reset<<"\n"
|
||||
<<bold_cyan<<" --> "<<reset
|
||||
<<bold_orange<<file<<":"<<line<<"\n";
|
||||
if(!line)
|
||||
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
|
||||
else
|
||||
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
|
||||
{
|
||||
std::cerr<<"\n";
|
||||
return;
|
||||
}
|
||||
std::cerr<<bold_cyan<<ident<<" | "<<reset<<"\n"
|
||||
<<bold_cyan<<line<<" | "<<reset<<res[line-1]<<"\n"
|
||||
<<bold_cyan<<ident<<" | "<<reset<<"\n\n";
|
||||
}
|
||||
void chkerr(){if(error)std::exit(1);}
|
||||
void chkerr(){if(cnt)std::exit(1);}
|
||||
};
|
||||
|
||||
#endif
|
||||
822
nasal_gc.h
196
nasal_import.h
@@ -1,26 +1,122 @@
|
||||
#ifndef __NASAL_IMPORT_H__
|
||||
#define __NASAL_IMPORT_H__
|
||||
#pragma once
|
||||
|
||||
class nasal_import
|
||||
#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
|
||||
|
||||
class linker
|
||||
{
|
||||
private:
|
||||
nasal_err& nerr;
|
||||
std::vector<std::string> files;
|
||||
bool check_import(const nasal_ast&);
|
||||
bool check_exist(const std::string&);
|
||||
void linker(nasal_ast&,nasal_ast&&);
|
||||
nasal_ast file_import(nasal_ast&);
|
||||
nasal_ast load(nasal_ast&,uint16_t);
|
||||
bool show_path;
|
||||
bool lib_loaded;
|
||||
error& err;
|
||||
std::vector<string> files;
|
||||
std::vector<string> envpath;
|
||||
bool imptchk(const ast&);
|
||||
bool exist(const string&);
|
||||
void link(ast&,ast&&);
|
||||
string path(const ast&);
|
||||
string findf(const string&);
|
||||
ast fimpt(ast&);
|
||||
ast libimpt();
|
||||
ast load(ast&,u16);
|
||||
public:
|
||||
nasal_import(nasal_err& e):nerr(e){}
|
||||
void link(nasal_parse&,const std::string&);
|
||||
const std::vector<std::string>& get_file() const {return files;}
|
||||
linker(error&);
|
||||
void link(parse&,const string&,bool);
|
||||
const std::vector<string>& filelist() const {return files;}
|
||||
};
|
||||
|
||||
bool nasal_import::check_import(const nasal_ast& node)
|
||||
linker::linker(error& e):show_path(false),lib_loaded(false),err(e){
|
||||
#ifdef _WIN32
|
||||
char sep=';';
|
||||
#else
|
||||
char sep=':';
|
||||
#endif
|
||||
string PATH=getenv("PATH");
|
||||
usize last=0,pos=PATH.find(sep,0);
|
||||
while(pos!=string::npos)
|
||||
{
|
||||
string dirpath=PATH.substr(last,pos-last);
|
||||
if(dirpath.length())
|
||||
envpath.push_back(dirpath);
|
||||
last=pos+1;
|
||||
pos=PATH.find(sep,last);
|
||||
}
|
||||
if(last!=PATH.length())
|
||||
envpath.push_back(PATH.substr(last));
|
||||
}
|
||||
|
||||
string linker::path(const ast& node)
|
||||
{
|
||||
if(node[1].type()==ast_callf)
|
||||
return node[1][0].str();
|
||||
string fpath=".";
|
||||
for(usize i=1;i<node.size();++i)
|
||||
#ifndef _WIN32
|
||||
fpath+="/"+node[i].str();
|
||||
#else
|
||||
fpath+="\\"+node[i].str();
|
||||
#endif
|
||||
return fpath+".nas";
|
||||
}
|
||||
|
||||
string linker::findf(const string& fname)
|
||||
{
|
||||
std::vector<string> filepath={fname};
|
||||
for(auto&p:envpath)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
filepath.push_back(p+"\\"+fname);
|
||||
#else
|
||||
filepath.push_back(p+"/"+fname);
|
||||
#endif
|
||||
}
|
||||
for(auto& i:filepath)
|
||||
if(access(i.c_str(),F_OK)!=-1)
|
||||
return i;
|
||||
if(fname=="lib.nas")
|
||||
#ifdef _WIN32
|
||||
return findf("stl\\lib.nas");
|
||||
#else
|
||||
return findf("stl/lib.nas");
|
||||
#endif
|
||||
if(!show_path)
|
||||
{
|
||||
err.err("link","cannot find file <"+fname+">");
|
||||
return "";
|
||||
}
|
||||
string paths="";
|
||||
for(auto& i:filepath)
|
||||
paths+=" "+i+"\n";
|
||||
err.err("link","cannot find file <"+fname+"> in these paths:\n"+paths);
|
||||
return "";
|
||||
}
|
||||
|
||||
bool linker::imptchk(const ast& node)
|
||||
{
|
||||
// only these two kinds of node can be recognized as 'import':
|
||||
/*
|
||||
call
|
||||
|_id:import
|
||||
|_callh:stl
|
||||
|_callh:file
|
||||
*/
|
||||
if(node.type()==ast_call && node[0].str()=="import" && node.size()>=2 && node[1].type()==ast_callh)
|
||||
{
|
||||
for(usize i=1;i<node.size();++i)
|
||||
if(node[i].type()!=ast_callh)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
only this kind of node can be recognized as 'import':
|
||||
call
|
||||
|_id:import
|
||||
|_call_func
|
||||
@@ -28,15 +124,15 @@ only this kind of node can be recognized as 'import':
|
||||
*/
|
||||
return (
|
||||
node.type()==ast_call &&
|
||||
node.size()==2 &&
|
||||
node[0].str()=="import" &&
|
||||
node.size()==2 &&
|
||||
node[1].type()==ast_callf &&
|
||||
node[1].size()==1 &&
|
||||
node[1][0].type()==ast_str
|
||||
);
|
||||
}
|
||||
|
||||
bool nasal_import::check_exist(const std::string& file)
|
||||
bool linker::exist(const string& file)
|
||||
{
|
||||
// avoid importing the same file
|
||||
for(auto& fname:files)
|
||||
@@ -46,54 +142,84 @@ bool nasal_import::check_exist(const std::string& file)
|
||||
return false;
|
||||
}
|
||||
|
||||
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
|
||||
void linker::link(ast& root,ast&& add_root)
|
||||
{
|
||||
// add children of add_root to the back of root
|
||||
for(auto& i:add_root.child())
|
||||
root.add(std::move(i));
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::file_import(nasal_ast& node)
|
||||
ast linker::fimpt(ast& node)
|
||||
{
|
||||
nasal_lexer lex(nerr);
|
||||
nasal_parse par(nerr);
|
||||
lexer lex(err);
|
||||
parse par(err);
|
||||
// get filename and set node to ast_null
|
||||
std::string filename=node[1][0].str();
|
||||
string filename=path(node);
|
||||
node.clear();
|
||||
|
||||
// avoid infinite loading loop
|
||||
if(check_exist(filename))
|
||||
return {0,ast_root};
|
||||
filename=findf(filename);
|
||||
if(!filename.length() || exist(filename))
|
||||
return {0,0,ast_root};
|
||||
|
||||
// start importing...
|
||||
lex.scan(filename);
|
||||
par.compile(lex);
|
||||
nasal_ast tmp=std::move(par.ast());
|
||||
ast tmp=std::move(par.tree());
|
||||
// check if tmp has 'import'
|
||||
return load(tmp,files.size()-1);
|
||||
}
|
||||
|
||||
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
|
||||
ast linker::libimpt()
|
||||
{
|
||||
nasal_ast new_root(0,ast_root);
|
||||
lexer lex(err);
|
||||
parse par(err);
|
||||
string filename=findf("lib.nas");
|
||||
if(!filename.length())
|
||||
return {0,0,ast_root};
|
||||
|
||||
// avoid infinite loading loop
|
||||
if(exist(filename))
|
||||
return {0,0,ast_root};
|
||||
|
||||
// start importing...
|
||||
lex.scan(filename);
|
||||
par.compile(lex);
|
||||
ast tmp=std::move(par.tree());
|
||||
// check if tmp has 'import'
|
||||
return load(tmp,files.size()-1);
|
||||
}
|
||||
|
||||
ast linker::load(ast& root,u16 fileindex)
|
||||
{
|
||||
ast new_root(0,0,ast_root);
|
||||
if(!lib_loaded)
|
||||
{
|
||||
link(new_root,libimpt());
|
||||
lib_loaded=true;
|
||||
}
|
||||
for(auto& i:root.child())
|
||||
if(check_import(i))
|
||||
linker(new_root,file_import(i));
|
||||
{
|
||||
if(imptchk(i))
|
||||
link(new_root,fimpt(i));
|
||||
else
|
||||
break;
|
||||
}
|
||||
// add root to the back of new_root
|
||||
nasal_ast file_head(0,ast_file);
|
||||
ast file_head(0,0,ast_file);
|
||||
file_head.set_num(fileindex);
|
||||
new_root.add(std::move(file_head));
|
||||
linker(new_root,std::move(root));
|
||||
link(new_root,std::move(root));
|
||||
return new_root;
|
||||
}
|
||||
|
||||
void nasal_import::link(nasal_parse& parse,const std::string& self)
|
||||
void linker::link(parse& parse,const string& self,bool spath=false)
|
||||
{
|
||||
show_path=spath;
|
||||
// initializing
|
||||
files={self};
|
||||
// scan root and import files,then generate a new ast and return to import_ast
|
||||
// the main file's index is 0
|
||||
parse.ast()=load(parse.ast(),0);
|
||||
parse.tree()=load(parse.tree(),0);
|
||||
err.chkerr();
|
||||
}
|
||||
|
||||
#endif
|
||||
338
nasal_lexer.h
@@ -1,44 +1,69 @@
|
||||
#ifndef __NASAL_LEXER_H__
|
||||
#define __NASAL_LEXER_H__
|
||||
#pragma once
|
||||
|
||||
#define ID(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
|
||||
#define HEX(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
|
||||
#define OCT(c) ('0'<=c&&c<='7')
|
||||
#define DIGIT(c) ('0'<=c&&c<='9')
|
||||
#define STR(c) (c=='\''||c=='\"'||c=='`')
|
||||
// single operators have only one character
|
||||
#define SINGLE_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
|
||||
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
|
||||
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
|
||||
#define CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
|
||||
#define NOTE(c) (c=='#')
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
enum token_type
|
||||
{
|
||||
tok_null=0,// null token default token type
|
||||
tok_num, // number basic token type
|
||||
tok_str, // string basic token type
|
||||
tok_id, // identifier basic token type
|
||||
tok_for,tok_forindex,tok_foreach,tok_while,
|
||||
tok_var,tok_func,tok_break,tok_continue,
|
||||
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
|
||||
tok_lcurve,tok_rcurve,
|
||||
tok_lbracket,tok_rbracket,
|
||||
tok_lbrace,tok_rbrace,
|
||||
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
|
||||
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
|
||||
tok_eq,
|
||||
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
|
||||
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
|
||||
tok_eof // end of token list
|
||||
#ifdef _MSC_VER
|
||||
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
||||
#endif
|
||||
|
||||
enum tok:u32{
|
||||
tok_null=0, // null token (default token type)
|
||||
tok_num, // number literal
|
||||
tok_str, // string literal
|
||||
tok_id, // identifier
|
||||
tok_for, // loop keyword for
|
||||
tok_forindex,// loop keyword forindex
|
||||
tok_foreach, // loop keyword foreach
|
||||
tok_while, // loop keyword while
|
||||
tok_var, // keyword for definition
|
||||
tok_func, // keyword for definition of function
|
||||
tok_break, // loop keyword break
|
||||
tok_continue,// loop keyword continue
|
||||
tok_ret, // function keyword return
|
||||
tok_if, // condition expression keyword if
|
||||
tok_elsif, // condition expression keyword elsif
|
||||
tok_else, // condition expression keyword else
|
||||
tok_nil, // nil literal
|
||||
tok_lcurve, // (
|
||||
tok_rcurve, // )
|
||||
tok_lbracket,// [
|
||||
tok_rbracket,// ]
|
||||
tok_lbrace, // {
|
||||
tok_rbrace, // }
|
||||
tok_semi, // ;
|
||||
tok_and, // operator and
|
||||
tok_or, // operator or
|
||||
tok_comma, // ,
|
||||
tok_dot, // .
|
||||
tok_ellipsis,// ...
|
||||
tok_quesmark,// ?
|
||||
tok_colon, // :
|
||||
tok_add, // operator +
|
||||
tok_sub, // operator -
|
||||
tok_mult, // operator *
|
||||
tok_div, // operator /
|
||||
tok_link, // operator ~
|
||||
tok_not, // operator !
|
||||
tok_eq, // operator =
|
||||
tok_addeq, // operator +=
|
||||
tok_subeq, // operator -=
|
||||
tok_multeq, // operator *=
|
||||
tok_diveq, // operator /=
|
||||
tok_lnkeq, // operator ~=
|
||||
tok_cmpeq, // operator ==
|
||||
tok_neq, // operator !=
|
||||
tok_less, // operator <
|
||||
tok_leq, // operator <=
|
||||
tok_grt, // operator >
|
||||
tok_geq, // operator >=
|
||||
tok_eof // <eof> end of token list
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
struct{
|
||||
const char* str;
|
||||
const uint32_t tok_type;
|
||||
}token_table[]=
|
||||
{
|
||||
const u32 type;
|
||||
}tok_table[]={
|
||||
{"for" ,tok_for },
|
||||
{"forindex",tok_forindex },
|
||||
{"foreach" ,tok_foreach },
|
||||
@@ -89,112 +114,210 @@ struct
|
||||
|
||||
struct token
|
||||
{
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
uint32_t type;
|
||||
std::string str;
|
||||
token(uint32_t l=0,uint32_t c=0,uint32_t t=tok_null,std::string s="")
|
||||
u32 line;
|
||||
u32 col;
|
||||
u32 type;
|
||||
string str;
|
||||
token(u32 l=0,u32 c=0,u32 t=tok_null,const string& s=""):str(s)
|
||||
{
|
||||
line=l;
|
||||
column=c;
|
||||
col=c;
|
||||
type=t;
|
||||
str=s;
|
||||
}
|
||||
};
|
||||
|
||||
class nasal_lexer
|
||||
class lexer
|
||||
{
|
||||
private:
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
uint32_t ptr;
|
||||
nasal_err& nerr;
|
||||
std::string res;
|
||||
u32 line;
|
||||
u32 column;
|
||||
usize ptr;
|
||||
string res;
|
||||
error& err;
|
||||
std::vector<token> tokens;
|
||||
|
||||
uint32_t get_type(const std::string&);
|
||||
void die(std::string info){nerr.err("lexer",line,column,info);};
|
||||
void open(const std::string&);
|
||||
std::string id_gen();
|
||||
std::string num_gen();
|
||||
std::string str_gen();
|
||||
u32 get_type(const string&);
|
||||
bool is_id(char);
|
||||
bool is_hex(char);
|
||||
bool is_oct(char);
|
||||
bool is_dec(char);
|
||||
bool is_str(char);
|
||||
bool is_single_opr(char);
|
||||
bool is_calc_opr(char);
|
||||
void die(const string& info){err.err("lexer",line,column,info);}
|
||||
void open(const string&);
|
||||
string utf8_gen();
|
||||
string id_gen();
|
||||
string num_gen();
|
||||
string str_gen();
|
||||
public:
|
||||
nasal_lexer(nasal_err& e):nerr(e){}
|
||||
void scan(const std::string&);
|
||||
lexer(error& e):
|
||||
line(1),column(0),
|
||||
ptr(0),res(""),
|
||||
err(e){}
|
||||
void scan(const string&);
|
||||
void print();
|
||||
const std::vector<token>& get_tokens() const {return tokens;}
|
||||
const std::vector<token>& result() const {return tokens;}
|
||||
};
|
||||
|
||||
void nasal_lexer::open(const std::string& file)
|
||||
bool lexer::is_id(char c)
|
||||
{
|
||||
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
|
||||
}
|
||||
|
||||
bool lexer::is_hex(char c)
|
||||
{
|
||||
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
|
||||
}
|
||||
|
||||
bool lexer::is_oct(char c)
|
||||
{
|
||||
return '0'<=c&&c<='7';
|
||||
}
|
||||
|
||||
bool lexer::is_dec(char c)
|
||||
{
|
||||
return '0'<=c&&c<='9';
|
||||
}
|
||||
|
||||
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=='%'||c=='$'||
|
||||
c=='^'||c=='\\'
|
||||
);
|
||||
}
|
||||
|
||||
bool lexer::is_calc_opr(char c)
|
||||
{
|
||||
return c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~';
|
||||
}
|
||||
|
||||
void lexer::open(const string& 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();
|
||||
}
|
||||
std::ifstream fin(file,std::ios::binary);
|
||||
if(fin.fail())
|
||||
nerr.err("lexer","cannot open file <"+file+">.");
|
||||
err.err("lexer","failed to open <"+file+">");
|
||||
else
|
||||
nerr.load(file);
|
||||
err.load(file);
|
||||
std::stringstream ss;
|
||||
ss<<fin.rdbuf();
|
||||
res=ss.str();
|
||||
}
|
||||
|
||||
uint32_t nasal_lexer::get_type(const std::string& tk_str)
|
||||
u32 lexer::get_type(const string& str)
|
||||
{
|
||||
for(int i=0;token_table[i].str;++i)
|
||||
if(tk_str==token_table[i].str)
|
||||
return token_table[i].tok_type;
|
||||
for(u32 i=0;tok_table[i].str;++i)
|
||||
if(str==tok_table[i].str)
|
||||
return tok_table[i].type;
|
||||
return tok_null;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::id_gen()
|
||||
string lexer::utf8_gen()
|
||||
{
|
||||
std::string str="";
|
||||
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
string str="";
|
||||
while(ptr<res.size() && res[ptr]<0)
|
||||
{
|
||||
string tmp="";
|
||||
u32 nbytes=utf8_hdchk(res[ptr]);
|
||||
if(nbytes)
|
||||
{
|
||||
tmp+=res[ptr++];
|
||||
for(u32 i=0;i<nbytes;++i,++ptr)
|
||||
if(ptr<res.size() && (res[ptr]&0xc0)==0x80)
|
||||
tmp+=res[ptr];
|
||||
if(tmp.length()!=1+nbytes)
|
||||
{
|
||||
++column;
|
||||
string utf_info="0x"+chrhex(tmp[0]);
|
||||
for(u32 i=1;i<tmp.size();++i)
|
||||
utf_info+=" 0x"+chrhex(tmp[i]);
|
||||
die("invalid utf-8 character `"+utf_info+"`, make sure it is utf8-text file");
|
||||
std::exit(1);
|
||||
}
|
||||
str+=tmp;
|
||||
column+=2; // may have some problems because not all the unicode takes 2 space
|
||||
}
|
||||
else
|
||||
{
|
||||
++ptr;
|
||||
++column;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::num_gen()
|
||||
string lexer::id_gen()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
string lexer::num_gen()
|
||||
{
|
||||
// generate hex number
|
||||
if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x')
|
||||
{
|
||||
std::string str="0x";
|
||||
string str="0x";
|
||||
ptr+=2;
|
||||
while(ptr<res.size() && HEX(res[ptr]))
|
||||
while(ptr<res.size() && is_hex(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
if(str.length()<3)// "0x"
|
||||
die("invalid number:"+str);
|
||||
die("invalid number `"+str+"`");
|
||||
return str;
|
||||
}
|
||||
// generate oct number
|
||||
else if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o')
|
||||
{
|
||||
std::string str="0o";
|
||||
string str="0o";
|
||||
ptr+=2;
|
||||
while(ptr<res.size() && OCT(res[ptr]))
|
||||
while(ptr<res.size() && is_oct(res[ptr]))
|
||||
str+=res[ptr++];
|
||||
column+=str.length();
|
||||
if(str.length()<3)// "0o"
|
||||
die("invalid number:"+str);
|
||||
die("invalid number `"+str+"`");
|
||||
return str;
|
||||
}
|
||||
// generate dec number
|
||||
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
|
||||
std::string str="";
|
||||
while(ptr<res.size() && DIGIT(res[ptr]))
|
||||
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() && DIGIT(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();
|
||||
die("invalid number:"+str);
|
||||
die("invalid number `"+str+"`");
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
@@ -203,13 +326,13 @@ std::string nasal_lexer::num_gen()
|
||||
str+=res[ptr++];
|
||||
if(ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+'))
|
||||
str+=res[ptr++];
|
||||
while(ptr<res.size() && DIGIT(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();
|
||||
die("invalid number:"+str);
|
||||
die("invalid number `"+str+"`");
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
@@ -217,10 +340,10 @@ std::string nasal_lexer::num_gen()
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string nasal_lexer::str_gen()
|
||||
string lexer::str_gen()
|
||||
{
|
||||
std::string str="";
|
||||
char begin=res[ptr];
|
||||
string str="";
|
||||
const char begin=res[ptr];
|
||||
++column;
|
||||
while(++ptr<res.size() && res[ptr]!=begin)
|
||||
{
|
||||
@@ -239,6 +362,7 @@ std::string nasal_lexer::str_gen()
|
||||
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;
|
||||
@@ -257,26 +381,26 @@ std::string nasal_lexer::str_gen()
|
||||
// check if this string ends with a " or '
|
||||
if(ptr++>=res.size())
|
||||
{
|
||||
die("get EOF when generating string.");
|
||||
die("get EOF when generating string");
|
||||
return str;
|
||||
}
|
||||
++column;
|
||||
if(begin=='`' && str.length()!=1)
|
||||
die("\'`\' is used for string that includes one character.");
|
||||
die("\'`\' is used for string that includes one character");
|
||||
return str;
|
||||
}
|
||||
|
||||
void nasal_lexer::scan(const std::string& file)
|
||||
void lexer::scan(const string& file)
|
||||
{
|
||||
line=1;
|
||||
column=0;
|
||||
ptr=0;
|
||||
open(file);
|
||||
|
||||
std::string str;
|
||||
string str;
|
||||
while(ptr<res.size())
|
||||
{
|
||||
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
|
||||
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]==0))
|
||||
{
|
||||
// these characters will be ignored, and '\n' will cause ++line
|
||||
++column;
|
||||
@@ -287,29 +411,29 @@ void nasal_lexer::scan(const std::string& file)
|
||||
}
|
||||
}
|
||||
if(ptr>=res.size()) break;
|
||||
if(ID(res[ptr]))
|
||||
if(is_id(res[ptr]))
|
||||
{
|
||||
str=id_gen();
|
||||
uint32_t type=get_type(str);
|
||||
u32 type=get_type(str);
|
||||
tokens.push_back({line,column,type?type:tok_id,str});
|
||||
}
|
||||
else if(DIGIT(res[ptr]))
|
||||
else if(is_dec(res[ptr]))
|
||||
{
|
||||
str=num_gen(); // make sure column is correct
|
||||
tokens.push_back({line,column,tok_num,str});
|
||||
}
|
||||
else if(STR(res[ptr]))
|
||||
else if(is_str(res[ptr]))
|
||||
{
|
||||
str=str_gen(); // make sure column is correct
|
||||
tokens.push_back({line,column,tok_str,str});
|
||||
}
|
||||
else if(SINGLE_OPERATOR(res[ptr]))
|
||||
else if(is_single_opr(res[ptr]))
|
||||
{
|
||||
str=res[ptr];
|
||||
++column;
|
||||
uint32_t type=get_type(str);
|
||||
u32 type=get_type(str);
|
||||
if(!type)
|
||||
die("invalid operator:"+str);
|
||||
die("invalid operator `"+str+"`");
|
||||
tokens.push_back({line,column,type,str});
|
||||
++ptr;
|
||||
}
|
||||
@@ -322,7 +446,7 @@ void nasal_lexer::scan(const std::string& file)
|
||||
column+=str.length();
|
||||
tokens.push_back({line,column,get_type(str),str});
|
||||
}
|
||||
else if(CALC_OPERATOR(res[ptr]))
|
||||
else if(is_calc_opr(res[ptr]))
|
||||
{
|
||||
// get calculation operator
|
||||
str=res[ptr++];
|
||||
@@ -331,24 +455,22 @@ void nasal_lexer::scan(const std::string& file)
|
||||
column+=str.length();
|
||||
tokens.push_back({line,column,get_type(str),str});
|
||||
}
|
||||
else if(NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
|
||||
else if(res[ptr]=='#')// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
|
||||
while(++ptr<res.size() && res[ptr]!='\n');
|
||||
else
|
||||
{
|
||||
++column;
|
||||
++ptr;
|
||||
die("unknown character.");
|
||||
char c=res[ptr++];
|
||||
die("invalid character 0x"+chrhex(c));
|
||||
}
|
||||
}
|
||||
tokens.push_back({line,column,tok_eof,"eof"});
|
||||
tokens.push_back({line,column,tok_eof,"<eof>"});
|
||||
res="";
|
||||
nerr.chkerr();
|
||||
err.chkerr();
|
||||
}
|
||||
|
||||
void nasal_lexer::print()
|
||||
void lexer::print()
|
||||
{
|
||||
for(auto& tok:tokens)
|
||||
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str)<<")\n";
|
||||
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str,128)<<")\n";
|
||||
}
|
||||
|
||||
#endif
|
||||
44
nasal_opt.h
@@ -1,7 +1,8 @@
|
||||
#ifndef __NASAL_OPT_H__
|
||||
#define __NASAL_OPT_H__
|
||||
#pragma once
|
||||
|
||||
void const_str(nasal_ast& root)
|
||||
#include <cmath>
|
||||
|
||||
void const_str(ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
root.set_str(vec[0].str()+vec[1].str());
|
||||
@@ -9,10 +10,10 @@ void const_str(nasal_ast& root)
|
||||
root.set_type(ast_str);
|
||||
}
|
||||
|
||||
void const_num(nasal_ast& root)
|
||||
void const_num(ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
double res;
|
||||
f64 res;
|
||||
switch(root.type())
|
||||
{
|
||||
case ast_add: res=vec[0].num()+vec[1].num(); break;
|
||||
@@ -32,31 +33,36 @@ void const_num(nasal_ast& root)
|
||||
root.set_type(ast_num);
|
||||
}
|
||||
|
||||
void calc_const(nasal_ast& root)
|
||||
void calc_const(ast& root)
|
||||
{
|
||||
auto& vec=root.child();
|
||||
for(auto& i:vec)
|
||||
calc_const(i);
|
||||
if(vec.size()==1 && root.type()==ast_neg && vec[0].type()==ast_num)
|
||||
{
|
||||
f64 res=-vec[0].num();
|
||||
root.set_num(res);
|
||||
root.child().clear();
|
||||
root.set_type(ast_num);
|
||||
return;
|
||||
}
|
||||
if(vec.size()!=2)
|
||||
return;
|
||||
if(root.type()!=ast_add &&
|
||||
root.type()!=ast_sub &&
|
||||
root.type()!=ast_mult &&
|
||||
root.type()!=ast_div &&
|
||||
root.type()!=ast_link &&
|
||||
root.type()!=ast_less &&
|
||||
root.type()!=ast_leq &&
|
||||
root.type()!=ast_grt &&
|
||||
root.type()!=ast_geq)
|
||||
if(root.type()!=ast_add && root.type()!=ast_sub &&
|
||||
root.type()!=ast_mult && root.type()!=ast_div &&
|
||||
root.type()!=ast_link && root.type()!=ast_less &&
|
||||
root.type()!=ast_leq && root.type()!=ast_grt &&
|
||||
root.type()!=ast_geq)
|
||||
return;
|
||||
if(root.type()==ast_link && vec[0].type()==ast_str && vec[1].type()==ast_str)
|
||||
if(root.type()==ast_link &&
|
||||
vec[0].type()==ast_str && vec[1].type()==ast_str)
|
||||
const_str(root);
|
||||
else if(root.type()!=ast_link && vec[0].type()==ast_num && vec[1].type()==ast_num)
|
||||
else if(root.type()!=ast_link &&
|
||||
vec[0].type()==ast_num && vec[1].type()==ast_num)
|
||||
const_num(root);
|
||||
}
|
||||
void optimize(nasal_ast& root)
|
||||
void optimize(ast& root)
|
||||
{
|
||||
for(auto& i:root.child())
|
||||
calc_const(i);
|
||||
}
|
||||
#endif
|
||||
655
nasal_parse.h
1551
nasal_vm.h
157
props.nas
@@ -1,157 +0,0 @@
|
||||
import("lib.nas");
|
||||
|
||||
var props=
|
||||
{
|
||||
globals:nil,
|
||||
Node:nil,
|
||||
getNode:func(path,index)
|
||||
{
|
||||
path=split('/',path);
|
||||
var tmp=me.globals;
|
||||
var path_size=size(path);
|
||||
for(var i=0;i<path_size-1;i+=1)
|
||||
tmp=tmp.val[path[i]];
|
||||
if(path_size>0)
|
||||
{
|
||||
if(contains(tmp.val,path[i]~'['~index~']'))
|
||||
return tmp.val[path[i]~'['~index~']'];
|
||||
else
|
||||
return tmp.val[path[i]];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
props.Node=
|
||||
{
|
||||
new:func(values=nil)
|
||||
{
|
||||
var result={
|
||||
parents:[props.Node],
|
||||
val:{},
|
||||
type:'GHOST',
|
||||
parent:nil
|
||||
};
|
||||
if(typeof(values)=="hash")
|
||||
result.val=values;
|
||||
return result;
|
||||
},
|
||||
addChild:func(name)
|
||||
{
|
||||
if(!contains(me.val,name))
|
||||
{
|
||||
me.val[name]=props.Node.new();
|
||||
me.val[name].parent=me;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
addChildren:func(name,cnt=0)
|
||||
{
|
||||
for(var i=0;i<cnt;i+=1)
|
||||
{
|
||||
var label=name~'['~i~']';
|
||||
me.val[label]=props.Node.new();
|
||||
me.val[label].parent=me;
|
||||
}
|
||||
return;
|
||||
},
|
||||
setValue:func(path,val)
|
||||
{
|
||||
path=split('/',path);
|
||||
var tmp=me;
|
||||
foreach(var label;path)
|
||||
tmp=tmp.val[label];
|
||||
tmp.val=val;
|
||||
if(typeof(val)=='str')
|
||||
{
|
||||
if(val=='true' or val=='false')
|
||||
tmp.type='BOOL';
|
||||
else
|
||||
tmp.type='STRING';
|
||||
}
|
||||
elsif(typeof(val)=='num')
|
||||
tmp.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
setIntValue:func(num)
|
||||
{
|
||||
me.val=num;
|
||||
me.type='INT';
|
||||
return;
|
||||
},
|
||||
setBoolValue:func(state)
|
||||
{
|
||||
me.val=state;
|
||||
me.type='BOOL';
|
||||
return;
|
||||
},
|
||||
setDoubleValue:func(num)
|
||||
{
|
||||
me.val=num;
|
||||
me.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
getValue:func(){return me.val;},
|
||||
getName:func()
|
||||
{
|
||||
var val=me.parent.val;
|
||||
var key=keys(val);
|
||||
foreach(var k;key)
|
||||
if(val[k]==me)
|
||||
return k;
|
||||
return '';
|
||||
},
|
||||
getParent:func()
|
||||
{
|
||||
return me.parent;
|
||||
},
|
||||
getPath:func()
|
||||
{
|
||||
if(me.parent==nil) return '';
|
||||
return me.parent.getPath()~'/'~me.getName();
|
||||
},
|
||||
equals:func(node){return me==node;},
|
||||
debug:func(s='')
|
||||
{
|
||||
if(typeof(me.val)=='hash')
|
||||
{
|
||||
var key=keys(me.val);
|
||||
if(!size(key))
|
||||
{
|
||||
println("{}");
|
||||
return;
|
||||
}
|
||||
println('{');
|
||||
foreach(var k;key)
|
||||
{
|
||||
print(s~' ',k,':');
|
||||
me.val[k].debug(s~' ');
|
||||
}
|
||||
println(s,'}');
|
||||
}
|
||||
else
|
||||
println(me.val,' (',me.type,')');
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
props.globals=props.Node.new();
|
||||
var c=['aircraft','ai','models','position','orientation','controls','sim'];
|
||||
foreach(var i;c)
|
||||
props.getNode('/',1).addChild(i);
|
||||
props.getNode('/ai',1).addChildren('ai',4);
|
||||
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode('/ai/ai['~i~']',1).setBoolValue('true');
|
||||
props.getNode('/models',1).addChildren('building',4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode('/models/building['~i~']',1).setIntValue(i);
|
||||
props.getNode('/',1).addChild('test');
|
||||
props.getNode('/test',1).addChildren('in',4);
|
||||
props.getNode('/test/in',0).setValue('/','true');
|
||||
props.getNode('/test/in',1).setValue('/','false');
|
||||
props.getNode('/test/in',2).setValue('/','welcome aboard,need help? use help->tutorial');
|
||||
props.getNode('/test/in',3).setValue('/',2147483648);
|
||||
props.globals.debug();
|
||||
println(props.getNode('/test/in',3).getPath());
|
||||
16
stl/csv.nas
Normal file
@@ -0,0 +1,16 @@
|
||||
# lib csv.nas
|
||||
# ValKmjolnir 2022/10/15
|
||||
var read_csv=func(path,delimeter=",",endline="\n"){
|
||||
var context=io.fin(path);
|
||||
context=split(endline,context);
|
||||
forindex(var i;context){
|
||||
context[i]=split(delimeter,context[i]);
|
||||
}
|
||||
if(size(context)<=1){
|
||||
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
|
||||
}
|
||||
return {
|
||||
property:context[0],
|
||||
data:context[1:]
|
||||
};
|
||||
}
|
||||
844
stl/fg_env.nas
Normal file
@@ -0,0 +1,844 @@
|
||||
# flightgear developer environments simulator (beta)
|
||||
# ValKmjolnir 2022
|
||||
|
||||
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("-------------------------------------------------------------");
|
||||
println(" See help using command line argument: --fg-env-help");
|
||||
println("-------------------------------------------------------------");
|
||||
|
||||
# important global constants
|
||||
var D2R=math.pi/180;
|
||||
var FPS2KT=0.5925;
|
||||
var FT2M=0.3048;
|
||||
var GAL2L=3.7854;
|
||||
var IN2M=0.0254;
|
||||
var KG2LB=2.2046;
|
||||
var KT2FPS=1.6878;
|
||||
var KT2MPS=0.5144;
|
||||
var L2GAL=0.2642;
|
||||
var LB2KG=0.4536;
|
||||
var M2FT=3.2808;
|
||||
var M2IN=39.3701;
|
||||
var M2NM=0.00054;
|
||||
var MPS2KT=1.9438;
|
||||
var NM2M=1852;
|
||||
var R2D=180/math.pi;
|
||||
|
||||
var fg_env_cli={
|
||||
"--fg-env-help":{
|
||||
info:"get help",
|
||||
trigger:0,
|
||||
f:func{
|
||||
if(me.trigger)
|
||||
return;
|
||||
println("-------------------------------------------------------------");
|
||||
println(" Help:");
|
||||
foreach(var i;keys(fg_env_cli))
|
||||
println(" ",i,": ",fg_env_cli[i].info);
|
||||
println("-------------------------------------------------------------");
|
||||
me.trigger=1;
|
||||
}
|
||||
},
|
||||
"--fg-env-debug":{
|
||||
info:"get property tree structure",
|
||||
trigger:0,
|
||||
f:func{
|
||||
if(me.trigger)
|
||||
return;
|
||||
props.globals.debug();
|
||||
me.trigger=1;
|
||||
}
|
||||
},
|
||||
"--fg-env-mktmtest":{
|
||||
info:"test maketimer",
|
||||
trigger:0,
|
||||
f:func{
|
||||
if(me.trigger)
|
||||
return;
|
||||
maketimer_multi_coroutine_test(32);
|
||||
me.trigger=1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
println("[\e[32m fg_env \e[0m] [",os.time(),"] init begin");
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] init tasks");
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] init events");
|
||||
var fg_globals={
|
||||
task:{},
|
||||
event:{}
|
||||
};
|
||||
|
||||
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 timestamp=maketimestamp();
|
||||
timestamp.stamp();
|
||||
while(timestamp.elapsedMSec()<interval*1000)
|
||||
coroutine.yield();
|
||||
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[33mevent\e[0m interval:\e[34m",interval,"\e[0m");
|
||||
function();
|
||||
});
|
||||
}
|
||||
|
||||
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 counter=0;
|
||||
var timestamp=maketimestamp();
|
||||
while(1){
|
||||
counter+=1;
|
||||
timestamp.stamp();
|
||||
while(timestamp.elapsedMSec()<interval*1000)
|
||||
coroutine.yield();
|
||||
println("[\e[32m",name,"\e[0m] [",os.time(),"] type:\e[34mtask\e[0m interval:\e[34m",interval,"\e[0m invoke-time:\e[96m",counter,"\e[0m");
|
||||
function();
|
||||
coroutine.yield();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func remove_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))
|
||||
delete(fg_globals.event,name);
|
||||
}
|
||||
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] new func maketimer(interval,function)");
|
||||
var maketimer=func(interval,function){
|
||||
var name="nasal-timer-";
|
||||
var res={
|
||||
start:func{
|
||||
if(me.isRunning)
|
||||
return;
|
||||
me.isRunning=1;
|
||||
if(me.singleShot){
|
||||
add_event(name,interval,function);
|
||||
}else{
|
||||
add_task(name,interval,function);
|
||||
}
|
||||
},
|
||||
stop:func{
|
||||
if(me.isRunning){
|
||||
remove_task(name);
|
||||
me.isRunning=0;
|
||||
}
|
||||
},
|
||||
restart:func(itv){
|
||||
interval=itv;
|
||||
me.stop();
|
||||
me.start();
|
||||
},
|
||||
singleShot:0,
|
||||
isRunning:0,
|
||||
simulatedTime:0
|
||||
};
|
||||
name~=id(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
println("[\e[32m settimer \e[0m] [",os.time(),"] new func settimer(function,interval,rt)");
|
||||
var settimer=func(){
|
||||
var index=0;
|
||||
return func(function,interval,realtime=1){
|
||||
var name="nasal-settimer-"~index;
|
||||
index+=1;
|
||||
add_task(name,interval,function);
|
||||
}
|
||||
}();
|
||||
|
||||
println("[\e[32m maketimer \e[0m] [",os.time(),"] test func simulation()");
|
||||
var simulation=func(){
|
||||
var running=1;
|
||||
while(running){
|
||||
running=0;
|
||||
foreach(var i;keys(fg_globals.task)){
|
||||
if(!contains(fg_globals.task,i))
|
||||
continue;
|
||||
if(coroutine.resume(fg_globals.task[i])!=nil){
|
||||
running=1;
|
||||
}else{
|
||||
remove_task(i);
|
||||
}
|
||||
}
|
||||
foreach(var i;keys(fg_globals.event)){
|
||||
if(!contains(fg_globals.event,i))
|
||||
continue;
|
||||
if(coroutine.resume(fg_globals.event[i])!=nil){
|
||||
running=1;
|
||||
}else{
|
||||
remove_event(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
var task_vec=[];
|
||||
setsize(task_vec,coroutine_size);
|
||||
forindex(var i;task_vec)
|
||||
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();
|
||||
}
|
||||
var event_vec=[];
|
||||
setsize(event_vec,coroutine_size);
|
||||
forindex(var i;event_vec)
|
||||
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{
|
||||
println("\e[101m",coroutine_size," settimer invoked.\e[0m");
|
||||
}
|
||||
|
||||
forindex(var i;task_vec){
|
||||
task_vec[i]=maketimer((i+1)/10,task_vec[i]);
|
||||
task_vec[i].start();
|
||||
}
|
||||
forindex(var i;event_vec){
|
||||
event_vec[i]=maketimer((i+1)/10,event_vec[i]);
|
||||
event_vec[i].singleShot=1;
|
||||
event_vec[i].start();
|
||||
}
|
||||
#forindex(var i;set_vec)
|
||||
# settimer(set_vec[i],(i+1)/10);
|
||||
simulation();
|
||||
}
|
||||
|
||||
println("[\e[32m geodinfo \e[0m] [",os.time(),"] init geodinfo(lat,lon)");
|
||||
var geodinfo=func(lat,lon){
|
||||
return [nil,{
|
||||
names:["Road","Freeway"]
|
||||
}];
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init props");
|
||||
var props={
|
||||
globals:nil,
|
||||
Node:nil,
|
||||
getNode:func(path,index){
|
||||
path=split('/',path);
|
||||
var tmp=me.globals;
|
||||
var path_size=size(path);
|
||||
for(var i=0;i<path_size-1;i+=1)
|
||||
tmp=tmp.val[path[i]];
|
||||
if(path_size>0){
|
||||
if(contains(tmp.val,path[i]~'['~index~']'))
|
||||
return tmp.val[path[i]~'['~index~']'];
|
||||
else
|
||||
return tmp.val[path[i]];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init props.Node");
|
||||
|
||||
props.Node={
|
||||
new:func(values=nil){
|
||||
var res={
|
||||
parents:fg_env_props_node_traits,
|
||||
val:{},
|
||||
type:'GHOST',
|
||||
parent:nil
|
||||
};
|
||||
if(typeof(values)=="hash")
|
||||
res.val=values;
|
||||
return res;
|
||||
},
|
||||
addChild:func(name){
|
||||
if(!contains(me.val,name)){
|
||||
me.val[name]=props.Node.new();
|
||||
me.val[name].parent=me;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
addChildren:func(name,cnt=0){
|
||||
for(var i=0;i<cnt;i+=1){
|
||||
var label=name~'['~i~']';
|
||||
me.val[label]=props.Node.new();
|
||||
me.val[label].parent=me;
|
||||
}
|
||||
return;
|
||||
},
|
||||
setValue:func(path,val){
|
||||
path=split('/',path);
|
||||
var tmp=me;
|
||||
foreach(var label;path)
|
||||
tmp=tmp.val[label];
|
||||
tmp.val=val;
|
||||
if(typeof(val)=='str'){
|
||||
if(val=='true' or val=='false')
|
||||
tmp.type='BOOL';
|
||||
else
|
||||
tmp.type='STRING';
|
||||
}
|
||||
elsif(typeof(val)=='num')
|
||||
tmp.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
setIntValue:func(num){
|
||||
me.val=num;
|
||||
me.type='INT';
|
||||
return;
|
||||
},
|
||||
setBoolValue:func(state){
|
||||
me.val=state;
|
||||
me.type='BOOL';
|
||||
return;
|
||||
},
|
||||
setDoubleValue:func(num){
|
||||
me.val=num;
|
||||
me.type='DOUBLE';
|
||||
return;
|
||||
},
|
||||
getValue:func(){return me.val;},
|
||||
getName:func(){
|
||||
var val=me.parent.val;
|
||||
foreach(var k;keys(val))
|
||||
if(val[k]==me)
|
||||
return k;
|
||||
return '';
|
||||
},
|
||||
getParent:func(){
|
||||
return me.parent;
|
||||
},
|
||||
getPath:func(){
|
||||
if(me.parent==nil) return '';
|
||||
return me.parent.getPath()~'/'~me.getName();
|
||||
},
|
||||
equals:func(node){return me==node;},
|
||||
debug:func(s=''){
|
||||
if(typeof(me.val)=='hash'){
|
||||
var key=keys(me.val);
|
||||
if(!size(key)){
|
||||
println("\e[91m{}\e[0m");
|
||||
return;
|
||||
}
|
||||
println('\e[91m{\e[0m');
|
||||
foreach(var k;key){
|
||||
print(s~" ","\e[34m",k,"\e[0m\e[95m:\e[0m");
|
||||
me.val[k].debug(s~" ");
|
||||
}
|
||||
println(s,'\e[91m}\e[0m');
|
||||
}
|
||||
else
|
||||
println("\e[35m ",me.val,"\e[0m\e[33m(\e[0m\e[96m",me.type,'\e[0m\e[33m)\e[0m');
|
||||
return;
|
||||
}
|
||||
};
|
||||
var fg_env_props_node_traits=[props.Node];
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init props.globals");
|
||||
props.globals=props.Node.new();
|
||||
var c=['aircraft','ai','models','position','orientation','controls','sim','consumables','engines','velocities','accelerations','gear','instrumentation','rotors'];
|
||||
foreach(var i;c)
|
||||
props.getNode('/',1).addChild(i);
|
||||
|
||||
props.getNode('/ai',1).addChildren('ai',4);
|
||||
props.getNode('/aircraft',1).setValue('/','IDG MD-11');
|
||||
props.getNode('/models',1).addChildren('building',4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode('/models/building['~i~']',1).setIntValue(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /consumables");
|
||||
props.getNode("/consumables",1).addChild("fuel");
|
||||
props.getNode("/consumables/fuel",1).addChild("total-fuel-lbs");
|
||||
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){
|
||||
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");
|
||||
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("capacity-gal_us");
|
||||
props.getNode("/consumables/fuel/tank["~i~"]",1).addChild("density-ppg");
|
||||
|
||||
props.getNode("/consumables/fuel/tank["~i~"]/level-lb",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel/tank["~i~"]/level-lbs",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel/tank["~i~"]/level-gal_us",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel/tank["~i~"]/capacity-gal_us",1).setValue('/',0);
|
||||
props.getNode("/consumables/fuel/tank["~i~"]/density-ppg",1).setValue('/',0);
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls");
|
||||
foreach(var i;['anti-ice','APU','armament','autoflight','electric','engines','flight','fuel','gear','hydraulic','lighting','pneumatic','pressurization','seat'])
|
||||
props.getNode("/controls",1).addChild(i);
|
||||
|
||||
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){
|
||||
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("carb-heat");
|
||||
props.getNode("/controls/anti-ice/engine["~i~"]",1).addChild("inlet-heat");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/APU");
|
||||
props.getNode("/controls/APU",1).addChild("off-start-run");
|
||||
props.getNode("/controls/APU",1).addChild("fire-switch");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/armament");
|
||||
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){
|
||||
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");
|
||||
props.getNode("/controls/armament/station["~i~"]",1).addChild("jettison-all");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/autoflight");
|
||||
foreach(var i;['autothrottle-arm','autothrottle-engage','heading-select','altitude-select','bank-angle-select','vertical-speed-select','speed-select','mach-select','vertical-mode','lateral-mode'])
|
||||
props.getNode("/controls/autoflight",1).addChild(i);
|
||||
props.getNode("/controls/autoflight",1).addChildren("autopilot",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
props.getNode("/controls/autoflight/autopilot["~i~"]",1).addChild("engage");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/electric");
|
||||
foreach(var i;['battery-switch','external-power','APU-generator'])
|
||||
props.getNode("/controls/electric",1).addChild(i);
|
||||
props.getNode("/controls/electric",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1){
|
||||
props.getNode("/controls/electric/engine["~i~"]",1).addChild("generator");
|
||||
props.getNode("/controls/electric/engine["~i~"]",1).addChild("bus-tie");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/engines");
|
||||
props.getNode("/controls/engines",1).addChild("throttle-idle");
|
||||
props.getNode("/controls/engines",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
foreach(var j;['throttle','starter','fuel-pump','fire-switch','fire-bottle-discharge','cutoff','mixture','propeller-pitch','magnetos','boost','WEP','cowl-flaps-norm','feather','ignition','augmentation','afterburner','reverser','water-injection','condition'])
|
||||
props.getNode("/controls/engines/engine["~i~"]",1).addChild(j);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/flight");
|
||||
foreach(var i;['aileron','aileron-trim','elevator','elevator-trim','rudder','rudder-trim','flaps','slats','BLC','spoilers','speedbrake','wing-sweep','wing-fold','drag-chute'])
|
||||
props.getNode("/controls/flight",1).addChild(i);
|
||||
|
||||
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){
|
||||
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);
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/gear");
|
||||
foreach(var i;['brake-left','brake-right','brake-parking','steering','gear-down','antiskid','tailhook','tailwheel-lock'])
|
||||
props.getNode("/controls/gear",1).addChild(i);
|
||||
props.getNode("/controls/gear",1).addChildren("wheel",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode("/controls/gear/wheel["~i~"]",1).addChild("alternate-extension");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/hydraulic");
|
||||
props.getNode("/controls/hydraulic",1).addChildren("system",2);
|
||||
for(var i=0;i<2;i+=1){
|
||||
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("engine-pump");
|
||||
props.getNode("/controls/hydraulic/system["~i~"]",1).addChild("electric-pump");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/lighting");
|
||||
foreach(var i;['landing-lights','turn-off-lights','formation-lights','taxi-light','logo-lights','nav-lights','beacon','strobe','panel-norm','instruments-norm','dome-norm'])
|
||||
props.getNode("/controls/lighting",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pneumatic");
|
||||
props.getNode("/controls/pneumatic",1).addChild("APU-bleed");
|
||||
props.getNode("/controls/pneumatic",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
props.getNode("/controls/pneumatic/engine["~i~"]",1).addChild("bleed");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/pressurization");
|
||||
foreach(var i;['mode','dump','outflow-valve'])
|
||||
props.getNode("/controls/pressurization",1).addChild(i);
|
||||
props.getNode("/controls/pressurization",1).addChildren("pack",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
props.getNode("/controls/pressurization/pack["~i~"]",1).addChild("pack-on");
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /controls/seat");
|
||||
foreach(var i;['vertical-adjust','fore-aft-adjust','cmd_selector_valve'])
|
||||
props.getNode("/controls/seat",1).addChild(i);
|
||||
props.getNode("/controls/seat",1).addChildren("eject",3);
|
||||
for(var i=0;i<3;i+=1){
|
||||
props.getNode("/controls/seat/eject["~i~"]",1).addChild("initiate");
|
||||
props.getNode("/controls/seat/eject["~i~"]",1).addChild("status");
|
||||
}
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /engines");
|
||||
props.getNode("/engines",1).addChildren("engine",2);
|
||||
for(var i=0;i<2;i+=1)
|
||||
foreach(var j;
|
||||
['fuel-flow-gph','fuel-flow-pph','thrust_lb','running','starter','cranking',
|
||||
'n1','n2','epr','augmentation','water-injection','ignition','nozzle-pos-norm',
|
||||
'inlet-pos-norm','reversed','cutoff','mp-osi','egt-degf','oil-temperature-degf',
|
||||
'oil-pressure-psi','cht-degf','rpm','pitch','torque'])
|
||||
props.getNode("/engines/engine["~i~"]",1).addChild(j);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /position");
|
||||
foreach(var i;['','altitude-agl-ft','altitude-ft','ground-elev-ft','ground-elev-m','latitude-deg','latitude-string','longitude-deg','longitude-string','sea-level-radius-ft'])
|
||||
props.getNode("/position",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /orientation");
|
||||
foreach(var i;['roll-deg','pitch-deg','heading-deg','roll-rate-degps','pitch-rate-degps','yaw-rate-degps','side-slip-rad','side-slip-deg','alpha-deg'])
|
||||
props.getNode("/orientation",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /velocities");
|
||||
foreach(var i;['airspeed-kt','mach','speed-north-fps','speed-east-fps','speed-down-fps','uBody-fps','vBody-fps','wBody-fps','vertical-speed-fps','glideslope'])
|
||||
props.getNode("/velocities",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /accelerations");
|
||||
foreach(var i;['nlf','ned','pilot'])
|
||||
props.getNode("/accelerations",1).addChild(i);
|
||||
foreach(var i;['north-accel-fps_sec','east-accel-fps_sec','down-accel-fps_sec'])
|
||||
props.getNode("/accelerations/ned",1).addChild(i);
|
||||
foreach(var i;['x-accel-fps_sec','y-accel-fps_sec','z-accel-fps_sec'])
|
||||
props.getNode("/accelerations/pilot",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /gear");
|
||||
props.getNode("/gear",1).addChild("serviceable");
|
||||
props.getNode("/gear",1).addChildren("gear",4);
|
||||
for(var i=0;i<4;i+=1)
|
||||
foreach(var j;['cast-angle-deg','compression-m','compression-norm','ground-friction-factor','ground-is-solid','has-brake','rollspeed-ms','wow','xoffset-in','yoffset-in','zoffset-in'])
|
||||
props.getNode("/gear/gear["~i~"]",1).addChild(j);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /instrumentation");
|
||||
foreach(var i;['adf','airspeed-indicator','altimeter','annunciator','clock','comm','comm[1]','dme','efis','encoder','flightdirector','gps','gps-annunciator','heading-indicator','heading-indicator-fg','magnetic-compass','marker-beacon','nav','nav[1]','radar','slip-skid-ball','tacan','transponder','turn-indicator','vertical-speed-indicator','wxradar'])
|
||||
props.getNode("/instrumentation",1).addChild(i);
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init /rotors");
|
||||
foreach(var i;['gear','{name}'])
|
||||
props.getNode("/rotors",1).addChild(i);
|
||||
foreach(var i;['torque-sound-filtered','total-torque'])
|
||||
props.getNode("/rotors/gear",1).addChild(i);
|
||||
foreach(var i;['balance','bladesvisible','cone-deg','cone2-deg','roll-deg','rpm','stall','stall-filtered','tilt','torque','yaw-deg'])
|
||||
props.getNode("/rotors/{name}",1).addChild(i);
|
||||
props.getNode("/rotors/{name}",1).addChildren("blade",8);
|
||||
for(var i=0;i<8;i+=1)
|
||||
foreach(var j;['flap-deg','incidence-deg','position-deg'])
|
||||
props.getNode("/rotors/{name}/blade["~i~"]",1).addChild(j);
|
||||
|
||||
props.getNode("/sim",1).addChild("messages");
|
||||
props.getNode("/sim",1).addChild("fg-home");
|
||||
props.getNode("/sim/messages",1).addChild("copilot");
|
||||
|
||||
props.getNode("/sim/messages/copilot",1).setValue('/',"nothing");
|
||||
props.getNode("/position/latitude-deg",1).setValue('/',90);
|
||||
props.getNode("/position/longitude-deg",1).setValue('/',90);
|
||||
props.getNode("/position/altitude-ft",1).setValue('/',28.244);
|
||||
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(){
|
||||
srand();
|
||||
var tmp=nil;
|
||||
var vec=[props.globals];
|
||||
while(size(vec)){
|
||||
tmp=[];
|
||||
foreach(var i;vec){
|
||||
if(typeof(i.val)=="hash"){
|
||||
if(size(i.val)==0){
|
||||
i.setDoubleValue(rand()*10);
|
||||
}else{
|
||||
foreach(var j;keys(i.val))
|
||||
append(tmp,i.val[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
vec=tmp;
|
||||
}
|
||||
}();
|
||||
|
||||
println("[\e[32m props \e[0m] [",os.time(),"] init done");
|
||||
println("[\e[32m fg_env \e[0m] [",os.time(),"] init done");
|
||||
println("-------------------------------------------------------------");
|
||||
|
||||
foreach(var a;runtime.argv())
|
||||
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
|
||||
60
stl/file.nas
Normal file
@@ -0,0 +1,60 @@
|
||||
# lib file.nas
|
||||
# ValKmjolnir 2022/3/6
|
||||
var file={
|
||||
SEEK_SET:io.SEEK_SET,
|
||||
SEEK_CUR:io.SEEK_CUR,
|
||||
SEEK_END:io.SEEK_END,
|
||||
new: func(filename,mode="r"){
|
||||
if(!io.exists(filename))
|
||||
return nil;
|
||||
var fd=io.open(filename,mode);
|
||||
return {
|
||||
close: func(){io.close(fd);},
|
||||
read: func(len){
|
||||
var buf=mut("");
|
||||
io.read(fd,buf,len);
|
||||
return buf;
|
||||
},
|
||||
write: func(str){return io.write(fd,str);},
|
||||
seek: func(pos,whence){return io.seek(fd,pos,whence);},
|
||||
tell: func(){return io.tell(fd);},
|
||||
readln: func(){return io.readln(fd);},
|
||||
stat: func(){return io.stat(filename);},
|
||||
eof: func(){return io.eof(fd);}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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))
|
||||
append(res,n);
|
||||
unix.closedir(dd);
|
||||
return res;
|
||||
}
|
||||
|
||||
var recursive_find_files=func(path){
|
||||
if(!io.exists(path))
|
||||
return nil;
|
||||
var dd=unix.opendir(path);
|
||||
var res={
|
||||
dir:path,
|
||||
files:[]
|
||||
};
|
||||
while(var n=unix.readdir(dd)){
|
||||
if(unix.isfile(path~"/"~n)){
|
||||
append(res.files,n);
|
||||
}elsif(unix.isdir(path~"/"~n) and n!="." and n!=".."){
|
||||
var tmp=recursive_find_files(path~"/"~n);
|
||||
if(tmp!=nil)
|
||||
append(res.files,tmp);
|
||||
}
|
||||
|
||||
}
|
||||
unix.closedir(dd);
|
||||
return res;
|
||||
}
|
||||
268
stl/json.nas
Normal file
@@ -0,0 +1,268 @@
|
||||
# lib json.nas
|
||||
# 2021 ValKmjolnir
|
||||
var JSON=func(){
|
||||
|
||||
var (
|
||||
j_eof,
|
||||
j_lbrace,
|
||||
j_rbrace,
|
||||
j_lbracket,
|
||||
j_rbracket,
|
||||
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 text='';
|
||||
var line=1;
|
||||
var text_size=0;
|
||||
var ptr=0;
|
||||
var token={content:'',type:''};
|
||||
var content={};
|
||||
var init=func(){
|
||||
text='';
|
||||
line=1;
|
||||
text_size=0;
|
||||
ptr=0;
|
||||
content={};
|
||||
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=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))
|
||||
die("empty string");
|
||||
text=split('',str);
|
||||
text_size=size(text);
|
||||
return;
|
||||
}
|
||||
var next=func(){
|
||||
while(ptr<text_size and !check()){
|
||||
if(text[ptr]=='\n')
|
||||
line+=1;
|
||||
ptr+=1;
|
||||
}
|
||||
if(ptr>=text_size){
|
||||
token.content="eof";
|
||||
token.type=j_eof;
|
||||
return;
|
||||
}
|
||||
|
||||
var c=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_lbracket;
|
||||
}elsif(c==']'){
|
||||
token.content=']';
|
||||
token.type=j_rbracket;
|
||||
}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 text[ptr]!=strbegin){
|
||||
s~=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(text[ptr]) or text[ptr]=='.'))){
|
||||
s~=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(text[ptr]) or isnum(text[ptr]))){
|
||||
s~=text[ptr];
|
||||
ptr+=1;
|
||||
}
|
||||
ptr-=1;
|
||||
token.content=s;
|
||||
token.type=j_id;
|
||||
}
|
||||
ptr+=1;
|
||||
return;
|
||||
}
|
||||
|
||||
var match=func(type){
|
||||
if(token.type!=type)
|
||||
print("line ",line,": expect ",j_content[type]," but get `",token.content,"`.\n");
|
||||
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_lbracket);
|
||||
if(token.type==j_lbrace){
|
||||
append(vec,hash_gen());
|
||||
}elsif(token.type==j_lbracket){
|
||||
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,me.hash_gen());
|
||||
}elsif(token.type==j_lbracket){
|
||||
append(vec,vec_gen());
|
||||
}elsif(token.type==j_str or token.type==j_num){
|
||||
append(vec,token.content);
|
||||
next();
|
||||
}
|
||||
}
|
||||
match(j_rbracket);
|
||||
return vec;
|
||||
}
|
||||
|
||||
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_lbracket){
|
||||
hash[name]=vec_gen();
|
||||
}elsif(token.type==j_str or token.type==j_num){
|
||||
hash[name]=token.content;
|
||||
next();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return {
|
||||
parse:func(str){
|
||||
if(typeof(str)!="str")
|
||||
die("JSON.parse: must use string");
|
||||
get(str);
|
||||
next();
|
||||
|
||||
match(j_lbrace);
|
||||
member(content);
|
||||
while(token.type==j_comma){
|
||||
match(j_comma);
|
||||
member(content);
|
||||
}
|
||||
match(j_rbrace);
|
||||
|
||||
var res=content;
|
||||
init();
|
||||
return res;
|
||||
},
|
||||
stringify:func(hash){
|
||||
if(typeof(hash)!="hash")
|
||||
die("JSON.stringify: must use hashmap");
|
||||
var s="";
|
||||
var gen=func(elem){
|
||||
var t=typeof(elem);
|
||||
if(t=="num"){
|
||||
s~=elem;
|
||||
}elsif(t=="str"){
|
||||
s~='"'~elem~'"';
|
||||
}elsif(t=="vec"){
|
||||
vgen(elem);
|
||||
}elsif(t=="hash"){
|
||||
hgen(elem);
|
||||
}else{
|
||||
s~='"undefined"';
|
||||
}
|
||||
}
|
||||
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~="}";
|
||||
}
|
||||
hgen(hash);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
}();
|
||||
395
stl/lib.nas
@@ -1,165 +1,286 @@
|
||||
# lib.nas
|
||||
|
||||
# import is used to link another file, this lib function will do nothing.
|
||||
# because nasal_import will recognize this and link files before generating bytecode.
|
||||
var import=func(filename){
|
||||
return __builtin_import(filename);
|
||||
}
|
||||
# 2019 ValKmjolnir
|
||||
|
||||
# print is used to print all things in nasal, try and see how it works.
|
||||
# this function uses std::cout/printf to output logs.
|
||||
# this function uses std::cout to output logs.
|
||||
var print=func(elems...){
|
||||
return __builtin_print(elems);
|
||||
return __print(elems);
|
||||
}
|
||||
|
||||
# append is used to add values into a vector.
|
||||
var append=func(vec,elems...){
|
||||
return __builtin_append(vec,elems);
|
||||
return __append(vec,elems);
|
||||
}
|
||||
|
||||
# setsize is used to change the size of vector.
|
||||
# if the size is larger than before,
|
||||
# this function will fill vm_nil into uninitialized space.
|
||||
var setsize=func(vec,size){
|
||||
return __builtin_setsize(vec,size);
|
||||
return __setsize(vec,size);
|
||||
}
|
||||
|
||||
# system has the same use in C.
|
||||
var system=func(str){
|
||||
return __builtin_system(str);
|
||||
return __system(str);
|
||||
}
|
||||
|
||||
# input uses std::cin and returns what we input.
|
||||
var input=func(){
|
||||
return __builtin_input();
|
||||
var input=func(end=nil){
|
||||
return __input(end);
|
||||
}
|
||||
|
||||
# split a string by delimiter for example:
|
||||
# split a string by separator for example:
|
||||
# split("ll","hello world") -> ["he","o world"]
|
||||
# this function will return a vector.
|
||||
var split=func(deli,str){
|
||||
return __builtin_split(deli,str);
|
||||
var split=func(separator,str){
|
||||
return __split(separator,str);
|
||||
}
|
||||
|
||||
# rand has the same function as the rand in C
|
||||
# if seed is nil, it will return the random number.
|
||||
# if seed is not nil, it will be initialized by this seed.
|
||||
var rand=func(seed=nil){
|
||||
return __builtin_rand(seed);
|
||||
return __rand(seed);
|
||||
}
|
||||
|
||||
# id will return the pointer of an gc-object.
|
||||
# if this object is not managed by gc, it will return 0.
|
||||
var id=func(object){
|
||||
return __builtin_id(object);
|
||||
return __id(object);
|
||||
}
|
||||
|
||||
# int will get the integer of input number.
|
||||
# int will get the integer of input number/string.
|
||||
# but carefully use it, because int has range between -2147483648~2147483647
|
||||
var int=func(val){
|
||||
return __builtin_int(val);
|
||||
return __int(val);
|
||||
}
|
||||
|
||||
# floor will get the integral number of input argument
|
||||
# which is less than or equal to this argument
|
||||
var floor=func(val){
|
||||
return __floor(val);
|
||||
}
|
||||
|
||||
# exit using std::exit
|
||||
var exit=func(val=-1){
|
||||
return __exit(val);
|
||||
}
|
||||
|
||||
# abort using std::abort
|
||||
var abort=func(){
|
||||
__abort();
|
||||
}
|
||||
|
||||
# abs gets absolute number.
|
||||
var abs=func(n){
|
||||
return n>0?n:-n;
|
||||
}
|
||||
|
||||
# num will change all the other types into number.
|
||||
# mostly used to change a numerable string.
|
||||
var num=func(val){
|
||||
return __builtin_num(val);
|
||||
return __num(val);
|
||||
}
|
||||
|
||||
# pop used to pop the last element in a vector.
|
||||
# this function will return the value that poped if vector has element(s).
|
||||
# if the vector is empty, it will return nil.
|
||||
var pop=func(vec){
|
||||
return __builtin_pop(vec);
|
||||
return __pop(vec);
|
||||
}
|
||||
|
||||
# str is used to change number into string.
|
||||
var str=func(num){
|
||||
return __builtin_str(num);
|
||||
return __str(num);
|
||||
}
|
||||
|
||||
# size can get the size of a string/vector/hashmap.
|
||||
# in fact it can also get the size of number, and the result is the number itself.
|
||||
# so don't do useless things, though it really works.
|
||||
var size=func(object){
|
||||
return __builtin_size(object);
|
||||
return __size(object);
|
||||
}
|
||||
|
||||
# contains is used to check if a key exists in a hashmap/dict.
|
||||
var contains=func(hash,key){
|
||||
return __builtin_contains(hash,key);
|
||||
return __contains(hash,key);
|
||||
}
|
||||
|
||||
# delete is used to delete a pair in a hashmap/dict by key.
|
||||
var delete=func(hash,key){
|
||||
return __builtin_delete(hash,key);
|
||||
return __delete(hash,key);
|
||||
}
|
||||
|
||||
# keys is used to get all keys in a hashmap/dict.
|
||||
# this function will return a vector.
|
||||
var keys=func(hash){
|
||||
return __builtin_keys(hash);
|
||||
return __keys(hash);
|
||||
}
|
||||
|
||||
# time has the same function in C.
|
||||
var time=func(begin){
|
||||
return __builtin_time(begin);
|
||||
return __time(begin);
|
||||
}
|
||||
var systime=func(){
|
||||
return time(0);
|
||||
}
|
||||
|
||||
# die is a special native function.
|
||||
# use it at where you want the program to crash immediately.
|
||||
var die=func(str){
|
||||
return __builtin_die(str);
|
||||
return __die(str);
|
||||
}
|
||||
|
||||
# find will give the first position of the needle in haystack
|
||||
var find=func(needle,haystack){
|
||||
return __find(needle,haystack);
|
||||
}
|
||||
|
||||
# typeof is used to get the type of an object.
|
||||
# this function returns a string.
|
||||
var typeof=func(object){
|
||||
return __builtin_type(object);
|
||||
return __type(object);
|
||||
}
|
||||
|
||||
# subvec is used to get part of a vector
|
||||
var subvec=func(vec,begin,length=nil){
|
||||
return vec[begin:(length==nil?nil:begin+length-1)];
|
||||
}
|
||||
|
||||
# substr will get the sub-string.
|
||||
# it gets the string, the begin index and sub-string's length as arguments.
|
||||
var substr=func(str,begin,len){
|
||||
return __builtin_substr(str,begin,len);
|
||||
return __substr(str,begin,len);
|
||||
}
|
||||
|
||||
# streq is used to compare if two strings are the same.
|
||||
var streq=func(a,b){
|
||||
return __builtin_streq(a,b);
|
||||
return __streq(a,b);
|
||||
}
|
||||
|
||||
# left is used to get the sub-string like substr.
|
||||
# but the begin index is 0.
|
||||
var left=func(str,len){
|
||||
return __builtin_left(str,len);
|
||||
return __left(str,len);
|
||||
}
|
||||
|
||||
# right i used to get the sub-string like substr.
|
||||
# but the begin index is strlen-len.
|
||||
var right=func(str,len){
|
||||
return __builtin_right(str,len);
|
||||
return __right(str,len);
|
||||
}
|
||||
|
||||
# cmp is used to compare two strings.
|
||||
# normal string will not be correctly compared by operators < > <= >=
|
||||
# because these operators will turn strings into numbers then compare.
|
||||
var cmp=func(a,b){
|
||||
return __builtin_cmp(a,b);
|
||||
return __cmp(a,b);
|
||||
}
|
||||
|
||||
# chr is used to get the character by ascii-number.
|
||||
# for example chr(65) -> 'A'
|
||||
var chr=func(code){
|
||||
return __builtin_chr(code);
|
||||
return __chr(code);
|
||||
}
|
||||
|
||||
# mut is used to change unmutable strings to mutable.
|
||||
var mut=func(str){
|
||||
return str~"";
|
||||
}
|
||||
|
||||
# srand wraps up rand, using time(0) as the seed.
|
||||
var srand=func(){
|
||||
rand(time(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
# values() gets all values in a hash.
|
||||
var values=func(hash){
|
||||
return __values(hash);
|
||||
}
|
||||
|
||||
# println has the same function as print.
|
||||
# but it will output a '\n' after using print.
|
||||
var println=func(elems...){
|
||||
__builtin_print(elems);
|
||||
elems=['\n'];
|
||||
return __builtin_print(elems);
|
||||
return __println(elems);
|
||||
}
|
||||
|
||||
var isfunc=func(f){
|
||||
return typeof(f)=="func";
|
||||
}
|
||||
|
||||
var isghost=func(g){
|
||||
die("this runtime has no ghost object");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var ishash=func(h){
|
||||
return typeof(h)=="hash";
|
||||
}
|
||||
|
||||
var isint=func(x){
|
||||
return x==floor(x);
|
||||
}
|
||||
|
||||
var isnum=func(x){
|
||||
return typeof(x)=="num" or !math.isnan(num(x));
|
||||
}
|
||||
|
||||
var isscalar=func(s){
|
||||
var t=typeof(s);
|
||||
return (t=="num" or t=="str")?1:0;
|
||||
}
|
||||
|
||||
var isstr=func(s){
|
||||
return typeof(s)=="str";
|
||||
}
|
||||
|
||||
var isvec=func(v){
|
||||
return typeof(v)=="vec";
|
||||
}
|
||||
|
||||
|
||||
# get the index of val in the vec
|
||||
var vecindex=func(vec,val){
|
||||
forindex(var i;vec)
|
||||
if(val==vec[i])
|
||||
return i;
|
||||
return nil;
|
||||
}
|
||||
|
||||
# check if the object is an instance of the class
|
||||
var isa=func(object,class){
|
||||
if(!contains(object,"parents") or typeof(object.parents)!="vec")
|
||||
return 0;
|
||||
foreach(var elem;object.parents)
|
||||
if(elem==class)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
# assert aborts when condition is not true
|
||||
var assert=func(condition,message="assertion failed!"){
|
||||
if(condition)
|
||||
return 1;
|
||||
die(message);
|
||||
}
|
||||
|
||||
# get time stamp, this will return a timestamp object
|
||||
var maketimestamp=func(){
|
||||
var t=0;
|
||||
var millisec=func(){
|
||||
return __millisec;
|
||||
}
|
||||
return {
|
||||
stamp:func(){t=millisec();},
|
||||
elapsedMSec:func(){return millisec()-t;},
|
||||
elapsedUSec:func(){return (millisec()-t)*1000;}
|
||||
};
|
||||
}
|
||||
|
||||
# md5
|
||||
var md5=func(str){
|
||||
return __md5(str);
|
||||
}
|
||||
|
||||
var io=
|
||||
@@ -168,28 +289,31 @@ var io=
|
||||
SEEK_CUR:1,
|
||||
SEEK_END:2,
|
||||
# get content of a file by filename. returns a string.
|
||||
fin: func(filename){return __builtin_fin(filename);},
|
||||
fin: func(filename){return __fin(filename);},
|
||||
# input a string as the content of a file.
|
||||
fout: func(filename,str){return __builtin_fout(filename,str);},
|
||||
fout: func(filename,str){return __fout(filename,str);},
|
||||
# use C access
|
||||
exists:func(filename){return __exists(filename);},
|
||||
# same as C fopen. open file and get the FILE*.
|
||||
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
|
||||
open: func(filename,mode="r"){return __open(filename,mode);},
|
||||
# same as C fclose. close file by FILE*.
|
||||
close: func(filehandle){return __builtin_close(filehandle);},
|
||||
close: func(filehandle){return __close(filehandle);},
|
||||
# same as C fread. read file by FILE*.
|
||||
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
|
||||
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
|
||||
read: func(filehandle,buf,len){return __read(filehandle,buf,len);},
|
||||
# same as C fwrite. write file by FILE*.
|
||||
write: func(filehandle,str){return __builtin_write(filehandle,str);},
|
||||
write: func(filehandle,str){return __write(filehandle,str);},
|
||||
# same as C fseek. seek place by FILE*.
|
||||
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
|
||||
seek: func(filehandle,pos,whence){return __seek(filehandle,pos,whence);},
|
||||
# same as C ftell.
|
||||
tell: func(filehandle){return __builtin_tell(filehandle);},
|
||||
tell: func(filehandle){return __tell(filehandle);},
|
||||
# read file by lines. use FILE*.
|
||||
# get nil if EOF
|
||||
readln:func(filehandle){return __builtin_readln(filehandle);},
|
||||
readln:func(filehandle){return __readln(filehandle);},
|
||||
# same as C stat.
|
||||
stat: func(filename){return __builtin_stat(filename);},
|
||||
stat: func(filename){return __stat(filename);},
|
||||
# same as C feof. check if FILE* gets the end of file(EOF).
|
||||
eof: func(filehandle){return __builtin_eof(filehandle);}
|
||||
eof: func(filehandle){return __eof(filehandle);}
|
||||
};
|
||||
|
||||
# get file status. using data from io.stat
|
||||
@@ -214,35 +338,46 @@ var fstat=func(filename){
|
||||
# carefully use it, all the calculations are based on integer.
|
||||
var bits=
|
||||
{
|
||||
# xor
|
||||
bitxor: func(a,b){return __builtin_xor(a,b); },
|
||||
# and
|
||||
bitand: func(a,b){return __builtin_and(a,b); },
|
||||
# or
|
||||
bitor: func(a,b){return __builtin_or(a,b); },
|
||||
# nand
|
||||
bitnand: func(a,b){return __builtin_nand(a,b);},
|
||||
# not
|
||||
bitnot: func(a) {return __builtin_not(a); },
|
||||
# i32 xor
|
||||
i32_xor: func(a,b){return __i32xor(a,b); },
|
||||
# i32 and
|
||||
i32_and: func(a,b){return __i32and(a,b); },
|
||||
# i32 or
|
||||
i32_or: func(a,b){return __i32or(a,b); },
|
||||
# i32 nand
|
||||
i32_nand:func(a,b){return __i32nand(a,b);},
|
||||
# i32 not
|
||||
i32_not: func(a) {return __i32not(a); },
|
||||
# u32 xor
|
||||
u32_xor: func(a,b){return __u32xor(a,b); },
|
||||
# u32 and
|
||||
u32_and: func(a,b){return __u32and(a,b); },
|
||||
# u32 or
|
||||
u32_or: func(a,b){return __u32or(a,b); },
|
||||
# u32 nand
|
||||
u32_nand:func(a,b){return __u32nand(a,b);},
|
||||
# u32 not
|
||||
u32_not: func(a) {return __u32not(a); },
|
||||
|
||||
# get bit data from a special string. for example:
|
||||
# bits.fld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5).
|
||||
fld: func(str,startbit,len){return __builtin_fld;},
|
||||
fld: func(str,startbit,len){return __fld;},
|
||||
# get sign-extended data from a special string. for example:
|
||||
# bits.sfld(s,0,3);
|
||||
# if s stores 10100010(162)
|
||||
# will get 101(5) then this will be signed extended to
|
||||
# 11111101(-3).
|
||||
sfld: func(str,startbit,len){return __builtin_sfld;},
|
||||
sfld: func(str,startbit,len){return __sfld;},
|
||||
# set value into a special string to store it. little-endian, for example:
|
||||
# bits.setfld(s,0,8,69);
|
||||
# set 01000101(69) to string will get this:
|
||||
# 10100010(162)
|
||||
# so s[0]=162.
|
||||
setfld: func(str,startbit,len,val){return __builtin_setfld;},
|
||||
setfld: func(str,startbit,len,val){return __setfld;},
|
||||
# get a special string filled by '\0' to use in setfld.
|
||||
buf: func(len){return __builtin_buf;}
|
||||
buf: func(len){return __buf;}
|
||||
};
|
||||
|
||||
# mostly used math functions and special constants, you know.
|
||||
@@ -250,50 +385,77 @@ var math=
|
||||
{
|
||||
e: 2.7182818284590452354,
|
||||
pi: 3.14159265358979323846264338327950288,
|
||||
D2R: 2.7182818284590452354/180,
|
||||
R2D: 180/2.7182818284590452354,
|
||||
inf: 1/0,
|
||||
nan: 0/0,
|
||||
sin: func(x) {return __builtin_sin(x); },
|
||||
cos: func(x) {return __builtin_cos(x); },
|
||||
tan: func(x) {return __builtin_tan(x); },
|
||||
exp: func(x) {return __builtin_exp(x); },
|
||||
lg: func(x) {return __builtin_lg(x); },
|
||||
ln: func(x) {return __builtin_ln(x); },
|
||||
sqrt: func(x) {return __builtin_sqrt(x); },
|
||||
atan2: func(x,y){return __builtin_atan2(x,y);},
|
||||
isnan: func(x) {return __builtin_isnan(x); }
|
||||
abs: func(x) {return x>0?x:-x; },
|
||||
floor: func(x) {return __floor(x); },
|
||||
pow: func(x,y){return __pow(x,y); },
|
||||
sin: func(x) {return __sin(x); },
|
||||
cos: func(x) {return __cos(x); },
|
||||
tan: func(x) {return __tan(x); },
|
||||
exp: func(x) {return __exp(x); },
|
||||
lg: func(x) {return __lg(x); },
|
||||
ln: func(x) {return __ln(x); },
|
||||
sqrt: func(x) {return __sqrt(x); },
|
||||
atan2: func(x,y){return __atan2(x,y);},
|
||||
isnan: func(x) {return __isnan(x); },
|
||||
max: func(x,y){return x>y?x:y; },
|
||||
min: func(x,y){return x<y?x:y; }
|
||||
};
|
||||
|
||||
var unix=
|
||||
{
|
||||
pipe: func(){die("not supported yet");},
|
||||
fork: func(){die("not supported yet");},
|
||||
pipe: func(){return __pipe;},
|
||||
fork: func(){return __fork;},
|
||||
dup2: func(fd0,fd1){die("not supported yet");},
|
||||
exec: func(filename,argv,envp){die("not supported yet");},
|
||||
waitpid: func(pid,nohang=0){die("not supported yet");},
|
||||
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
|
||||
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
|
||||
opendir: func(path){return __builtin_opendir;},
|
||||
readdir: func(handle){return __builtin_readdir;},
|
||||
closedir: func(handle){return __builtin_closedir;},
|
||||
waitpid: func(pid,nohang=0){return __waitpid;},
|
||||
isdir: func(path){return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
|
||||
isfile: func(path){return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
|
||||
opendir: func(path){return __opendir;},
|
||||
readdir: func(handle){return __readdir;},
|
||||
closedir: func(handle){return __closedir;},
|
||||
time: func(){return time(0);},
|
||||
sleep: func(secs){return __builtin_sleep(secs);},
|
||||
chdir: func(path){return __builtin_chdir(path);},
|
||||
environ: func(){die("not supported yet");},
|
||||
getcwd: func(){return __builtin_getcwd();},
|
||||
getenv: func(envvar){return __builtin_getenv(envvar);}
|
||||
sleep: func(secs){return __sleep(secs);},
|
||||
chdir: func(path){return __chdir(path);},
|
||||
environ: func(){return __environ();},
|
||||
getcwd: func(){return __getcwd();},
|
||||
getenv: func(envvar){return __getenv(envvar);},
|
||||
getpath: func(){return split(os.platform()=="windows"?";":":",unix.getenv("PATH"));}
|
||||
};
|
||||
|
||||
# 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
|
||||
var dylib=
|
||||
{
|
||||
# open dynamic lib.
|
||||
dlopen: func(libname){return __builtin_dlopen;},
|
||||
dlopen: func(libname){
|
||||
# find dynamic lib from local dir first
|
||||
libname=(os.platform()=="windows"?".\\":"./")~libname;
|
||||
if(io.exists(libname))
|
||||
return __dlopen(libname);
|
||||
# find dynamic lib through PATH
|
||||
var envpath=split(os.platform()=="windows"?";":":",unix.getenv("PATH"));
|
||||
# first find ./module
|
||||
append(envpath,".");
|
||||
var path=os.platform()=="windows"?"\\module\\":"/module/";
|
||||
foreach(var p;envpath){
|
||||
p~=path~libname;
|
||||
if(io.exists(p)){
|
||||
libname=p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return __dlopen(libname);
|
||||
},
|
||||
# load symbol from an open dynamic lib.
|
||||
dlsym: func(lib,sym){return __builtin_dlsym; },
|
||||
dlsym: func(lib,sym){return __dlsym; },
|
||||
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
|
||||
dlclose: func(lib){return __builtin_dlclose; },
|
||||
dlclose: func(lib){return __dlclose; },
|
||||
# call the loaded symbol.
|
||||
dlcall: func(funcptr,args...){return __builtin_dlcall}
|
||||
dlcall: func(funcptr,args...){return __dlcall}
|
||||
};
|
||||
|
||||
# os is used to use or get some os-related info/functions.
|
||||
@@ -301,31 +463,42 @@ var dylib=
|
||||
var os=
|
||||
{
|
||||
# get a string that tell which os it runs on.
|
||||
platform: func(){return __builtin_platform;}
|
||||
platform: func(){return __platform;},
|
||||
time: func(){return __logtime; }
|
||||
};
|
||||
|
||||
# runtime gives us some functions that we could manage it manually.
|
||||
var runtime=
|
||||
{
|
||||
# do garbage collection manually.
|
||||
# carefully use it because using it frequently may make program running slower.
|
||||
gc: func(){return __builtin_gc;}
|
||||
# command line arguments
|
||||
argv: func(){return __sysargv;}
|
||||
};
|
||||
|
||||
# important global constants
|
||||
var D2R=math.pi/180;
|
||||
var FPS2KT=0.5925;
|
||||
var FT2M=0.3048;
|
||||
var GAL2L=3.7854;
|
||||
var IN2M=0.0254;
|
||||
var KG2LB=2.2046;
|
||||
var KT2FPS=1.6878;
|
||||
var KT2MPS=0.5144;
|
||||
var L2GAL=0.2642;
|
||||
var LB2KG=0.4536;
|
||||
var M2FT=3.2808;
|
||||
var M2IN=39.3701;
|
||||
var M2NM=0.00054;
|
||||
var MPS2KT=1.9438;
|
||||
var NM2M=1852;
|
||||
var R2D=180/math.pi;
|
||||
# functions that not supported in this runtime:
|
||||
var bind=func(function,locals,outer_scope=nil){
|
||||
die("this runtime does not support bind");
|
||||
}
|
||||
|
||||
var call=func(function,args=nil,_me=nil,locals=nil,error=nil){
|
||||
die("this runtime does not support call");
|
||||
}
|
||||
|
||||
var caller=func(level=1){
|
||||
die("this runtime does not support caller");
|
||||
}
|
||||
|
||||
var closure=func(function,level=1){
|
||||
die("this runtime uses \"vm_upval\" instead of \"vm_hash\" as the closure");
|
||||
}
|
||||
|
||||
var compile=func(code,filename="<compile>"){
|
||||
die("this runtime uses static code generator");
|
||||
}
|
||||
|
||||
var coroutine={
|
||||
create: func(function){return __cocreate;},
|
||||
resume: func(co) {return __coresume;},
|
||||
yield: func(args...) {return __coyield; },
|
||||
status: func(co) {return __costatus;},
|
||||
running:func() {return __corun; }
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# lib list.nas
|
||||
# list.nas
|
||||
# valkmjolnir 2021/3/31
|
||||
var list=func()
|
||||
{
|
||||
var list=func(){
|
||||
var (begin,end)=(nil,nil);
|
||||
return{
|
||||
push_back:func(elem){
|
||||
|
||||
40
stl/log.nas
Normal file
@@ -0,0 +1,40 @@
|
||||
# log.nas
|
||||
# ValKmjolnir 2022/6/14
|
||||
var log=func(){
|
||||
var (log_date,log_time,prefix)=(1,1,"");
|
||||
var os_time="";
|
||||
var prt_core=func(elem){
|
||||
os_time=os.time();
|
||||
print(prefix," ");
|
||||
if(log_date and log_time)
|
||||
print(os_time," ");
|
||||
elsif(log_date or log_time){
|
||||
var s=split(" ",os_time);
|
||||
if(log_date)
|
||||
print(s[0]," ");
|
||||
if(log_time)
|
||||
print(s[1]," ");
|
||||
}
|
||||
foreach(var i;elem)
|
||||
print(i);
|
||||
print("\n");
|
||||
}
|
||||
return {
|
||||
setflags:func(date,time){
|
||||
log_date=!!date;
|
||||
log_time=!!time;
|
||||
},
|
||||
setprefix:func(s){
|
||||
if(typeof(s)!="str")
|
||||
println("[log.nas] must use string as the prefix.");
|
||||
prefix=s;
|
||||
},
|
||||
println:func(elem...){
|
||||
prt_core(elem);
|
||||
},
|
||||
fatalln:func(elem...){
|
||||
prt_core(elem);
|
||||
die("log:fatal error");
|
||||
}
|
||||
};
|
||||
}();
|
||||
43
stl/module.nas
Normal file
@@ -0,0 +1,43 @@
|
||||
# module.nas
|
||||
# ValKmjolnir 2022/3/5
|
||||
|
||||
# this provides safe usage of dylib
|
||||
# when dylib is closed,
|
||||
# all the invalid functions cannot be called
|
||||
|
||||
var module_call_func=func(fptr,args){
|
||||
return __dlcall;
|
||||
}
|
||||
var extern={
|
||||
new: func(fptr){
|
||||
var isopen=1;
|
||||
return {
|
||||
close:func(){isopen=0;},
|
||||
call:func(args...){
|
||||
return (!isopen)?
|
||||
nil:
|
||||
module_call_func(fptr,args);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
var module={
|
||||
new: func(name){
|
||||
var lib=dylib.dlopen(name);
|
||||
var f={};
|
||||
return {
|
||||
get:func(symbol){
|
||||
if(contains(f,symbol))
|
||||
return f[symbol];
|
||||
var fp=extern.new(dylib.dlsym(lib,symbol));
|
||||
f[symbol]=fp;
|
||||
return fp;
|
||||
},
|
||||
close: func(){
|
||||
foreach(var i;keys(f))
|
||||
f[i].close();
|
||||
dylib.dlclose(lib);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
20
stl/padding.nas
Normal file
@@ -0,0 +1,20 @@
|
||||
# padding.nas
|
||||
# ValKmjolnir 2022/9/4
|
||||
|
||||
var leftpad=func(s,len,char=" "){
|
||||
if(typeof(s)=="num")
|
||||
s=str(s);
|
||||
var strlen=size(s);
|
||||
for(var i=strlen;i<len;i+=1)
|
||||
s=char~s;
|
||||
return s;
|
||||
}
|
||||
|
||||
var rightpad=func(s,len,char=" "){
|
||||
if(typeof(s)=="num")
|
||||
s=str(s);
|
||||
var strlen=size(s);
|
||||
for(var i=strlen;i<len;i+=1)
|
||||
s~=char;
|
||||
return s;
|
||||
}
|
||||
352
stl/process_bar.nas
Normal file
@@ -0,0 +1,352 @@
|
||||
# process_bar.nas
|
||||
# ValKmjolnir 2022/6/14
|
||||
# this file is inspired by a Python lib: alive_progress
|
||||
|
||||
var process_bar={
|
||||
bar:nil,
|
||||
high_resolution_bar:nil,
|
||||
spinner:nil
|
||||
};
|
||||
|
||||
process_bar.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:["(",")"]
|
||||
};
|
||||
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))
|
||||
back="space";
|
||||
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)
|
||||
number=1;
|
||||
if(number<0)
|
||||
number=0;
|
||||
var finish_length=int(number*length);
|
||||
var other=length-finish_length;
|
||||
var s="";
|
||||
for(var i=0;i<finish_length;i+=1)
|
||||
s~=front;
|
||||
for(var i=0;i<other;i+=1)
|
||||
s~=back;
|
||||
return sep[0]~s~sep[1];
|
||||
}
|
||||
};
|
||||
};
|
||||
}();
|
||||
|
||||
# return a high resolution progress bar
|
||||
# example:
|
||||
# var bar=process_bar.high_resolution_bar(40);
|
||||
# for(var i=0;i<=1;i+=0.001){
|
||||
# print(bar.bar(i,40),'\r');
|
||||
# unix.sleep(0.001);
|
||||
# }
|
||||
# println();
|
||||
process_bar.high_resolution_bar=func(){
|
||||
var block=["▏","▎","▍","▌","▋","▊","▉","█"];
|
||||
return func(length){
|
||||
return {
|
||||
bar: func(number){
|
||||
if(number>1)
|
||||
number=1;
|
||||
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){
|
||||
s~="█";
|
||||
}
|
||||
if(decimal!=0){
|
||||
s~=block[int(decimal*10)/10*size(block)];
|
||||
}
|
||||
for(var i=0;i<length-progress;i+=1){
|
||||
s~=" ";
|
||||
}
|
||||
s~="|";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
};
|
||||
}();
|
||||
|
||||
process_bar.spinner=func(){
|
||||
var generate_scrolling_spinner=func(s){
|
||||
if(typeof(s)!="str")
|
||||
s="****";
|
||||
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){
|
||||
tmp=pop(vec)~tmp;
|
||||
append(res,tmp);
|
||||
while(size(res[-1])!=16)
|
||||
res[-1]~=" ";
|
||||
}
|
||||
tmp=res[-1];
|
||||
while(tmp!=" "){
|
||||
tmp=" "~substr(tmp,0,15);
|
||||
append(res,tmp);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
var spinner={
|
||||
rise:["▁","▂","▃","▄","▅","▆","▇","█","█","▇","▆","▅","▄","▃","▂","▁"],
|
||||
vertical:["▏","▎","▍","▌","▋","▊","▉","▇","▇","▉","▊","▋","▌","▍","▎","▏"],
|
||||
dot:["⠁","⠈","⠐","⠠","⢀","⡀","⠄","⠂"],
|
||||
dots:["⣾","⣷","⣯","⣟","⡿","⢿","⣻","⣽"],
|
||||
arrow:["↑","↗","→","↘","↓","↙","←","↖"],
|
||||
classic:["/","-","\\","-"],
|
||||
balls:["●...",".●..","..●.","...●",],
|
||||
dots_wave:[
|
||||
"⠈⠁⠂⠄⡀⢀⠠⠐",
|
||||
"⠐⠈⠁⠂⠄⡀⢀⠠",
|
||||
"⠠⠐⠈⠁⠂⠄⡀⢀",
|
||||
"⢀⠠⠐⠈⠁⠂⠄⡀",
|
||||
"⡀⢀⠠⠐⠈⠁⠂⠄",
|
||||
"⠄⡀⢀⠠⠐⠈⠁⠂",
|
||||
"⠂⠄⡀⢀⠠⠐⠈⠁",
|
||||
"⠁⠂⠄⡀⢀⠠⠐⠈"
|
||||
],
|
||||
pulse:[
|
||||
"●---------------",
|
||||
"-●--------------",
|
||||
"--●-------------",
|
||||
"---●------------",
|
||||
"----●-----------",
|
||||
"-----●----------",
|
||||
"------●---------",
|
||||
"-------√--------",
|
||||
"-------√\\-------",
|
||||
"-------√\\/------",
|
||||
"-------√\\/●-----",
|
||||
"--------\\/-●----",
|
||||
"---------/--●---",
|
||||
"-------------●--",
|
||||
"--------------●-",
|
||||
"---------------●"
|
||||
],
|
||||
wave:[
|
||||
"▁▂▃▄▅▆▇█",
|
||||
"▂▃▄▅▆▇█▇",
|
||||
"▃▄▅▆▇█▇▆",
|
||||
"▄▅▆▇█▇▆▅",
|
||||
"▅▆▇█▇▆▅▄",
|
||||
"▆▇█▇▆▅▄▃",
|
||||
"▇█▇▆▅▄▃▂",
|
||||
"█▇▆▅▄▃▂▁",
|
||||
"▇▆▅▄▃▂▁▂",
|
||||
"▆▅▄▃▂▁▂▃",
|
||||
"▅▄▃▂▁▂▃▄",
|
||||
"▄▃▂▁▂▃▄▅",
|
||||
"▃▂▁▂▃▄▅▆",
|
||||
"▂▁▂▃▄▅▆▇"
|
||||
],
|
||||
short_wave:[
|
||||
"▅▄█▇",
|
||||
"▄▃▇▆",
|
||||
"▃▂▆▅",
|
||||
"▂▁▅▄",
|
||||
"▁▂▄▃",
|
||||
"▂▃▃▂",
|
||||
"▃▄▂▁",
|
||||
"▄▅▁▂",
|
||||
"▅▆▂▃",
|
||||
"▆▇▃▄",
|
||||
"▇█▄▅",
|
||||
"█▇▅▆",
|
||||
"▇▆▆▇",
|
||||
"▆▅▇█"
|
||||
],
|
||||
happy:[
|
||||
" ",
|
||||
"ᕗ ",
|
||||
" ᕗ ",
|
||||
") ᕗ ",
|
||||
"ᐛ ) ᕗ ",
|
||||
" ᐛ ) ᕗ ",
|
||||
"( ᐛ ) ᕗ ",
|
||||
" ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ ",
|
||||
" ᕕ ( ᐛ ) ᕗ",
|
||||
" ᕕ ( ᐛ ) ",
|
||||
" ᕕ ( ᐛ )",
|
||||
" ᕕ ( ᐛ ",
|
||||
" ᕕ ( ᐛ",
|
||||
" ᕕ ( ",
|
||||
" ᕕ (",
|
||||
" ᕕ ",
|
||||
" ᕕ",
|
||||
],
|
||||
fish:generate_scrolling_spinner("><))))`>"),
|
||||
wait:generate_scrolling_spinner("please wait"),
|
||||
stars:generate_scrolling_spinner("********")
|
||||
};
|
||||
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(){
|
||||
var s="";
|
||||
for(var i=0;i<repeat;i+=1)
|
||||
s~=type[counter];
|
||||
counter+=1;
|
||||
if(counter>=size(type))
|
||||
counter=0;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
};
|
||||
}();
|
||||
process_bar.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);
|
||||
else
|
||||
return process_bar.bar("sharp","point","bracket",length);
|
||||
}
|
||||
process_bar.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);
|
||||
else
|
||||
return process_bar.spinner("classic",repeat);
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
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)
|
||||
};
|
||||
var bar_key=keys(bars);
|
||||
var spin_key=keys(spinners);
|
||||
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){
|
||||
var k=spin_key[j];
|
||||
print("\e["~(j+1+size(bars))~";1H["~k~"] |"~spinners[k].next()~"|");
|
||||
}
|
||||
unix.sleep(1/20);
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
# lib queue.nas
|
||||
# queue.nas
|
||||
# valkmjolnir 2021/3/31
|
||||
var queue=func()
|
||||
{
|
||||
var queue=func(){
|
||||
var (begin,end)=(nil,nil);
|
||||
return{
|
||||
push:func(elem){
|
||||
|
||||
@@ -1,28 +1,23 @@
|
||||
|
||||
import("lib.nas");
|
||||
var ResultTrait={
|
||||
Ok:func(val){
|
||||
me.ok=val;
|
||||
me.flag=0;
|
||||
return me;
|
||||
},
|
||||
Err:func(info){
|
||||
me.err=info;
|
||||
me.flag=1;
|
||||
return me;
|
||||
},
|
||||
unwrap:func(){
|
||||
if(me.flag)
|
||||
die(me.err);
|
||||
return me.ok;
|
||||
}
|
||||
};
|
||||
# result.nas
|
||||
# ValKmjolnir 2021
|
||||
|
||||
var Result=func(){
|
||||
var (ok,err,flag)=(nil,"",1);
|
||||
return{
|
||||
ok:nil,
|
||||
err:"",
|
||||
flag:1,
|
||||
parents:[ResultTrait]
|
||||
Ok:func(val){
|
||||
ok=val;
|
||||
flag=0;
|
||||
return me;
|
||||
},
|
||||
Err:func(info){
|
||||
err=info;
|
||||
flag=1;
|
||||
return me;
|
||||
},
|
||||
unwrap:func(){
|
||||
if(flag)
|
||||
die(err);
|
||||
return ok;
|
||||
}
|
||||
};
|
||||
};
|
||||
46
stl/sort.nas
@@ -1,20 +1,30 @@
|
||||
# lib sort.nas
|
||||
# sort.nas
|
||||
# valkmjolnir 2021/4/2
|
||||
var sort=func(vec,left,right,cmp=func(a,b){return a<=b;})
|
||||
{
|
||||
if(left>=right) return nil;
|
||||
var (L,R,tmp)=(left,right,vec[left]);
|
||||
while(left<right)
|
||||
{
|
||||
while(left<right and cmp(tmp,vec[right]))
|
||||
right-=1;
|
||||
while(left<right and cmp(vec[left],tmp))
|
||||
left+=1;
|
||||
if(left!=right)
|
||||
(vec[left],vec[right])=(vec[right],vec[left]);
|
||||
|
||||
# please make sure the compare function has the function like <= or >=
|
||||
# only using < or > may cause infinite loop or the program may crash
|
||||
var sort=func(){
|
||||
srand(); # be aware! this causes global changes
|
||||
var quick_sort_core=func(vec,left,right,cmp){
|
||||
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]);
|
||||
while(i!=j){
|
||||
while(i<j and cmp(tmp,vec[j]))
|
||||
j-=1;
|
||||
vec[i]=vec[j];
|
||||
while(i<j and cmp(vec[i],tmp))
|
||||
i+=1;
|
||||
vec[j]=vec[i];
|
||||
}
|
||||
vec[i]=tmp;
|
||||
quick_sort_core(vec,left,i-1,cmp);
|
||||
quick_sort_core(vec,i+1,right,cmp);
|
||||
return nil;
|
||||
}
|
||||
(vec[L],vec[left])=(vec[left],tmp);
|
||||
sort(vec,L,left-1,cmp);
|
||||
sort(vec,left+1,R,cmp);
|
||||
return nil;
|
||||
}
|
||||
return func(vec,cmp=func(a,b){return a<=b;}){
|
||||
quick_sort_core(vec,0,size(vec)-1,cmp);
|
||||
return nil;
|
||||
}
|
||||
}();
|
||||
@@ -1,25 +1,23 @@
|
||||
# lib stack.nas
|
||||
# stack.nas
|
||||
# valkmjolnir 2021/3/31
|
||||
var stack=func()
|
||||
{
|
||||
var next=nil;
|
||||
var stack=func(){
|
||||
var vec=[];
|
||||
return{
|
||||
push:func(elem){
|
||||
next={elem:elem,next:next};
|
||||
append(vec,elem);
|
||||
},
|
||||
pop:func(){
|
||||
if(next!=nil)
|
||||
next=next.next;
|
||||
return pop(vec);
|
||||
},
|
||||
top:func(){
|
||||
if(next!=nil)
|
||||
return next.elem;
|
||||
if(size(vec)!=0)
|
||||
return vec[-1];
|
||||
},
|
||||
clear:func(){
|
||||
next=nil;
|
||||
vec=[];
|
||||
},
|
||||
empty:func(){
|
||||
return next==nil;
|
||||
return size(vec)==0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
9
stl/string.nas
Normal file
@@ -0,0 +1,9 @@
|
||||
# string.nas
|
||||
# ValKmjolnir 2022/10/5
|
||||
|
||||
var join=func(vec){
|
||||
var res="";
|
||||
foreach(var i;vec)
|
||||
res~=i;
|
||||
return res;
|
||||
}
|
||||
@@ -1,38 +1,37 @@
|
||||
import("lib.nas");
|
||||
import.stl.padding;
|
||||
|
||||
var char_ttf=[
|
||||
[" "," "," "," "," "," "],
|
||||
[" ████╗"," ██╔██║"," ██╔╝██║"," ███████║","██╔═══██║","╚═╝ ╚═╝"],
|
||||
[" █████╗ ","██╔══██╗","███████║","██╔══██║","██║ ██║","╚═╝ ╚═╝"],
|
||||
["██████╗ ","██╔══██╗","██████╔╝","██╔══██╗","██████╔╝","╚═════╝ "],
|
||||
[" ██████╗","██╔════╝","██║ ","██║ ","╚██████╗"," ╚═════╝"],
|
||||
["██████╗ ","██╔══██╗","██║ ██║","██║ ██║","██████╔╝","╚═════╝ "],
|
||||
["███████╗","██╔════╝","█████╗ ","██╔══╝ ","███████╗","╚══════╝"],
|
||||
["███████╗","██╔════╝","█████╗ ","██╔══╝ ","██║ ","╚═╝ "],
|
||||
[" █████╗ ","██╔═══╝ ","██║ ██╗ ","██║ ╚██╗","╚█████╔╝"," ╚════╝ "],
|
||||
[" ██████╗ ","██╔════╝ ","██║ ███╗","██║ ██║","╚██████╔╝"," ╚═════╝ "],
|
||||
["██╗ ██╗","██║ ██║","███████║","██╔══██║","██║ ██║","╚═╝ ╚═╝"],
|
||||
[" ██████╗"," ██╔═╝"," ██║ "," ██║ "," ██████╗"," ╚═════╝"],
|
||||
["██╗","██║","██║","██║","██║","╚═╝"],
|
||||
[" ██╗"," ██║"," ██║","██ ██║","╚█████╔╝"," ╚════╝ "],
|
||||
["██╗ ██╗","██║ ██╔╝","█████╔╝ ","██╔═██╗ ","██║ ██╗","╚═╝ ╚═╝"],
|
||||
["██╗ ","██║ ","██║ ","██║ ","███████╗","╚══════╝"],
|
||||
["██╗ ██╗","███╗ ███║","████████║","██╔██╔██║","██║╚═╝██║","╚═╝ ╚═╝"],
|
||||
["██╗ ██╗","███╗ ██║","█████╗██║","██╔█████║","██║ ╚███║","╚═╝ ╚══╝"],
|
||||
["███╗ ███╗","████╗ ████║","██╔████╔██║","██║╚██╔╝██║","██║ ╚═╝ ██║","╚═╝ ╚═╝"],
|
||||
["███╗ ██╗","████╗ ██║","██╔██╗ ██║","██║╚██╗██║","██║ ╚████║","╚═╝ ╚═══╝"],
|
||||
[" ██████╗ ","██╔═══██╗","██║ ██║","██║ ██║","╚██████╔╝"," ╚═════╝ "],
|
||||
["██████╗ ","██╔══██╗","██████╔╝","██╔═══╝ ","██║ ","╚═╝ "],
|
||||
[" ██████╗ ","██╔═══██╗","██║ ██║","██║ ██╔╝","╚████╔██╗"," ╚═══╝╚═╝"],
|
||||
[" ██████╗ ","██╔═══██╗","██║ ██║","██║▄▄ ██║","╚██████╔╝"," ╚══▀▀═╝ "],
|
||||
["██████╗ ","██╔══██╗","██████╔╝","██╔══██╗","██║ ██║","╚═╝ ╚═╝"],
|
||||
["███████╗","██╔════╝","███████╗","╚════██║","███████║","╚══════╝"],
|
||||
["████████╗","╚══██╔══╝"," ██║ "," ██║ "," ██║ "," ╚═╝ "],
|
||||
["██╗ ██╗","██║ ██║","██║ ██║","██║ ██║","╚██████╔╝"," ╚═════╝ "],
|
||||
["██╗ ██╗","██║ ██║","██║ ██║","╚██╗ ██╔╝"," ╚████╔╝ "," ╚═══╝ "],
|
||||
["██╗ ██╗","██║██╗██║","████████║","███╔═███║","██╔╝ ╚██║","╚═╝ ╚═╝"],
|
||||
["██╗ ██╗","██║ ██║","██║ █╗ ██║","██║███╗██║","╚███╔███╔╝"," ╚══╝╚══╝ "],
|
||||
["██╗ ██╗","╚██╗██╔╝"," ╚███╔╝ "," ██╔██╗ ","██╔╝╚██╗","╚═╝ ╚═╝"],
|
||||
["██╗ ██╗","╚██╗ ██╔╝"," ╚████╔╝ "," ╚██╔╝ "," ██║ "," ╚═╝ "],
|
||||
["████████╗","╚════██╔╝"," ██╔═╝ "," ██╔═╝ ","████████╗","╚═══════╝"],
|
||||
["███████╗","╚══███╔╝"," ███╔╝ "," ███╔╝ ","███████╗","╚══════╝"],
|
||||
];
|
||||
var trans_ttf=func(string)
|
||||
{
|
||||
var trans_ttf=func(string){
|
||||
var str=["","","","","",""];
|
||||
for(var i=0;i<size(string);i+=1)
|
||||
{
|
||||
for(var i=0;i<size(string);i+=1){
|
||||
var number=string[i];
|
||||
if(97<=number and number<=122)
|
||||
for(var j=0;j<6;j+=1)
|
||||
@@ -48,60 +47,38 @@ var trans_ttf=func(string)
|
||||
println(i);
|
||||
return;
|
||||
}
|
||||
var curve1=func()
|
||||
{
|
||||
var shadow=["░","▒","▓","█","▀","▄","▐","▌"];
|
||||
rand(100);
|
||||
var s="";
|
||||
for(var i=0;i<10;i+=1)
|
||||
{
|
||||
for(var j=0;j<40;j+=1)
|
||||
s~=shadow[int(8*rand())];
|
||||
s~='\n';
|
||||
}
|
||||
print(s);
|
||||
}
|
||||
var curve2=func()
|
||||
{
|
||||
var curve1=func(line=4){
|
||||
var table=["╚","═","╝","╔","║","╗"];
|
||||
rand(100);
|
||||
var s="";
|
||||
for(var i=0;i<10;i+=1)
|
||||
{
|
||||
for(var j=0;j<40;j+=1)
|
||||
for(var i=0;i<line;i+=1){
|
||||
for(var j=0;j<45;j+=1)
|
||||
s~=table[int(6*rand())];
|
||||
s~='\n';
|
||||
}
|
||||
print(s);
|
||||
}
|
||||
var curve3=func()
|
||||
{
|
||||
var s=["","","","","",""];
|
||||
var cnt=0;
|
||||
foreach(var char;char_ttf)
|
||||
{
|
||||
cnt+=1;
|
||||
forindex(var i;char)
|
||||
s[i]~=char[i];
|
||||
if(cnt==9)
|
||||
{
|
||||
forindex(var i;s)
|
||||
{
|
||||
println(s[i]);
|
||||
s[i]='';
|
||||
}
|
||||
cnt=0;
|
||||
}
|
||||
var curve2=func(line=2){
|
||||
var shadow=["░","▒","▓","█","▀","▄","▐","▌"];
|
||||
rand(100);
|
||||
var s="";
|
||||
for(var i=0;i<line;i+=1){
|
||||
for(var j=0;j<45;j+=1)
|
||||
s~=shadow[int(8*rand())];
|
||||
s~='\n';
|
||||
}
|
||||
return;
|
||||
print(s);
|
||||
}
|
||||
var curve4=func()
|
||||
{
|
||||
var arr=[0,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8,0,1,2,3,4,5,6,7,8];
|
||||
for(var loop=0;loop<10;loop+=1)
|
||||
{
|
||||
for(var i=26;i>=0;i-=1)
|
||||
{
|
||||
var curve3=func(line=2){
|
||||
var arr=[
|
||||
0,1,2,3,4,5,6,7,8,
|
||||
0,1,2,3,4,5,6,7,8,
|
||||
0,1,2,3,4,5,6,7,8,
|
||||
0,1,2,3,4,5,6,7,8,
|
||||
0,1,2,3,4,5,6,7,8
|
||||
];
|
||||
for(var loop=0;loop<line;loop+=1){
|
||||
for(var i=size(arr)-1;i>=0;i-=1){
|
||||
var rand_index=int(i*rand());
|
||||
(arr[i],arr[rand_index])=(arr[rand_index],arr[i]);
|
||||
}
|
||||
@@ -114,9 +91,58 @@ var curve4=func()
|
||||
}
|
||||
return;
|
||||
}
|
||||
trans_ttf("just for test");
|
||||
var curve4=func(line=4){
|
||||
var shadow=["m░\e[0m","m▒\e[0m","m▓\e[0m","m█\e[0m","m▀\e[0m","m▄\e[0m","m▐\e[0m","m▌\e[0m"];
|
||||
var front=[
|
||||
"30","31","32","33","34","35","36","37",
|
||||
"90","91","92","93","94","95","96","97"
|
||||
];
|
||||
var back=[
|
||||
"40","41","42","43","44","45","46","47",
|
||||
"100","101","102","103","104","105","106","107"
|
||||
];
|
||||
rand(time(0));
|
||||
for(var i=0;i<line;i+=1){
|
||||
for(var j=0;j<45;j+=1)
|
||||
print("\e["~front[16*rand()]~";"~back[16*rand()]~shadow[8*rand()]);
|
||||
print('\n');
|
||||
}
|
||||
}
|
||||
var curve5=func(line=4){
|
||||
var vec=["▀▄─","▄▀─","▀─▄","▄─▀"];
|
||||
for(var (y,p)=(0,0);y!=line;y+=1){
|
||||
for(var x=0;x!=15;x+=1)
|
||||
print(vec[p]);
|
||||
print("\n");
|
||||
p+=1;
|
||||
p=p>=4?0:p;
|
||||
}
|
||||
}
|
||||
var ansi_escape_sequence=func(){
|
||||
for(var i=0;i<=9;i+=1)
|
||||
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
|
||||
print("\n");
|
||||
for(var i=30;i<=37;i+=1)
|
||||
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
|
||||
print("\n");
|
||||
for(var i=40;i<=47;i+=1)
|
||||
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
|
||||
print("\n");
|
||||
for(var i=90;i<=97;i+=1)
|
||||
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
|
||||
print("\n");
|
||||
for(var i=100;i<=107;i+=1)
|
||||
print(rightpad(i,3),":\e["~i~"mhi\e[0m ");
|
||||
print("\n");
|
||||
}
|
||||
|
||||
# enable unicode
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
trans_ttf(" ValKmjolnir ");
|
||||
curve1();
|
||||
curve2();
|
||||
curve3();
|
||||
curve4();
|
||||
curve4();
|
||||
curve5();
|
||||
ansi_escape_sequence();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Road check and auto pilot by ValKmjolnir
|
||||
import("lib.nas");
|
||||
import("props.nas");
|
||||
import.stl.fg_env;
|
||||
|
||||
var dt=0.01;
|
||||
var intergral=0;
|
||||
var derivative=0;
|
||||
@@ -21,16 +21,14 @@ var road_check_func = func(){
|
||||
var position_info = geodinfo(lat,lon);
|
||||
var position_names = position_info[1].names;
|
||||
|
||||
if((position_names[0]=="Freeway") or (position_names[0]=="Road"))
|
||||
{
|
||||
if((position_names[0]=="Freeway") or (position_names[0]=="Road")){
|
||||
var car_heading = 0;
|
||||
var lat_change = 0;
|
||||
var lon_change = 0;
|
||||
var left_range = 0;
|
||||
var right_range = 0;
|
||||
|
||||
for(var i=0;i>-0.00005;i-=0.000001)
|
||||
{
|
||||
for(var i=0;i>-0.00005;i-=0.000001){
|
||||
car_heading = props.getNode("/orientation/heading-deg",1).getValue();
|
||||
lat_change = math.sin(D2R*car_heading);
|
||||
lon_change = -math.cos(D2R*car_heading);
|
||||
@@ -43,8 +41,7 @@ var road_check_func = func(){
|
||||
else
|
||||
break;
|
||||
}
|
||||
for(var i=0;i<0.00005;i+=0.000001)
|
||||
{
|
||||
for(var i=0;i<0.00005;i+=0.000001){
|
||||
car_heading = props.getNode("/orientation/heading-deg",1).getValue();
|
||||
lat_change = math.sin(D2R*car_heading);
|
||||
lon_change = -math.cos(D2R*car_heading);
|
||||
@@ -68,21 +65,30 @@ var road_check_func = func(){
|
||||
props.getNode("/", 1).setValue("/controls/flight/rudder",Kp*error*error+Ki*intergral+Kd*derivative);
|
||||
else
|
||||
props.getNode("/", 1).setValue("/controls/flight/rudder",0);
|
||||
|
||||
# for simulation test, in fg these three lines are deleted
|
||||
println(" rudder :",props.getNode("/controls/flight/rudder",1).getValue());
|
||||
println(" dt :",dt,'\tintergral :',intergral,'\tderivative :',derivative);
|
||||
println(" prev-err :",previous_error,'\terror :',error);
|
||||
|
||||
previous_error=error;
|
||||
}
|
||||
};
|
||||
|
||||
var road_check_timer = maketimer(0.01,road_check_func);
|
||||
var toggle_auto_pilot = func(){
|
||||
if(!road_check_timer.isRunning)
|
||||
{
|
||||
if(!road_check_timer.isRunning){
|
||||
intergral=0;
|
||||
road_check_timer.start();
|
||||
props.getNode("/sim/messages/copilot",1).setValue("ze dong sheng teaan see tong yee tse yung. Auto Sheng Teaan System Activated!");
|
||||
}
|
||||
else
|
||||
{
|
||||
props.getNode("/sim/messages/copilot",1).setValue('/',"ze dong sheng teaan see tong yee tse yung. Auto Sheng Teaan System Activated!");
|
||||
}else{
|
||||
road_check_timer.stop();
|
||||
props.getNode("/sim/messages/copilot",1).setValue("ze dong sheng teaan see tong yee guan bee. Auto Sheng Teaan System is off.");
|
||||
props.getNode("/sim/messages/copilot",1).setValue('/',"ze dong sheng teaan see tong yee guan bee. Auto Sheng Teaan System is off.");
|
||||
}
|
||||
}
|
||||
|
||||
# this is used to simulate the running process in fg
|
||||
# when using in fg, delete these lines below
|
||||
toggle_auto_pilot();
|
||||
road_check_timer.restart(0.1);
|
||||
simulation();
|
||||
43
test/bf.nas
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var mandelbrot=
|
||||
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
|
||||
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
|
||||
@@ -149,8 +147,32 @@ var mandelbrot=
|
||||
|
||||
var paper=[];
|
||||
var (ptr,pc)=(0,0);
|
||||
var (code,inum,stack)=([],[],[]);
|
||||
var (code,inum,stack,char)=([],[],[],[]);
|
||||
var (add,mov,jt,jf,in,out)=(0,1,2,3,4,5);
|
||||
setsize(char,256);
|
||||
|
||||
var color=[
|
||||
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",
|
||||
"\e[90m","\e[91m","\e[92m","\e[93m","\e[94m","\e[95m","\e[96m"
|
||||
];
|
||||
func(){
|
||||
var cnt=0;
|
||||
forindex(var i;char){
|
||||
char[i]=color[cnt]~chr(i)~"\e[0m";
|
||||
cnt+=1;
|
||||
if(cnt>12)
|
||||
cnt=0;
|
||||
}
|
||||
}();
|
||||
|
||||
var funcs=[
|
||||
func{paper[ptr]+=inum[pc];},
|
||||
func{ptr+=inum[pc];},
|
||||
func{if(paper[ptr])pc=inum[pc];},
|
||||
func{if(!paper[ptr])pc=inum[pc];},
|
||||
func{paper[ptr]=input()[0];},
|
||||
func{print(char[paper[ptr]]);}
|
||||
];
|
||||
|
||||
var bf=func(program){
|
||||
setsize(paper,131072);
|
||||
@@ -211,16 +233,13 @@ var bf=func(program){
|
||||
die("lack ]");
|
||||
return;
|
||||
}
|
||||
|
||||
# enable ANSI escape sequence
|
||||
if(os.platform()=="windows")
|
||||
system("color");
|
||||
len=size(code);
|
||||
for(pc=0;pc<len;pc+=1){
|
||||
var c=code[pc];
|
||||
if(c==add) paper[ptr]+=inum[pc];
|
||||
elsif(c==mov)ptr+=inum[pc];
|
||||
elsif(c==jt){if(paper[ptr])pc=inum[pc];}
|
||||
elsif(c==jf){if(!paper[ptr])pc=inum[pc];}
|
||||
elsif(c==in) paper[ptr]=input()[0];
|
||||
else print(chr(paper[ptr]));
|
||||
}
|
||||
for(pc=0;pc<len;pc+=1)
|
||||
funcs[code[pc]]();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var mandelbrot=
|
||||
"[A mandelbrot set fractal viewer in brainf*** written by Erik Bosman]
|
||||
+++++++++++++[->++>>>+++++>++>+<<<<<<]>>>>>++++++>--->>>>>>>>>>+++++++++++++++[[
|
||||
@@ -151,21 +149,17 @@ var bf=func(program)
|
||||
{
|
||||
var stack=[];
|
||||
var len=size(program);
|
||||
var f="import('lib.nas');\nvar ptr=0;\nvar paper=[];\nsetsize(paper,131072);\n";
|
||||
for(var i=0;i<len;i+=1)
|
||||
{
|
||||
var f="#automatically generated by bfconvertor.nas\nimport('lib.nas');\nvar ptr=0;\nvar paper=[];\nsetsize(paper,131072);\n";
|
||||
for(var i=0;i<len;i+=1){
|
||||
var c=chr(program[i]);
|
||||
if(c=='+' or c=='-')
|
||||
{
|
||||
if(c=='+' or c=='-'){
|
||||
var cnt=0;
|
||||
for(;i<len;i+=1)
|
||||
{
|
||||
for(;i<len;i+=1){
|
||||
if(chr(program[i])=='+')
|
||||
cnt+=1;
|
||||
elsif(chr(program[i])=='-')
|
||||
cnt-=1;
|
||||
elsif(chr(program[i])!='\n')
|
||||
{
|
||||
elsif(chr(program[i])!='\n'){
|
||||
i-=1;
|
||||
break;
|
||||
}
|
||||
@@ -177,18 +171,14 @@ var bf=func(program)
|
||||
f~="paper[ptr]+="~cnt~";\n";
|
||||
elsif(cnt<0)
|
||||
f~="paper[ptr]-="~(-cnt)~";\n";
|
||||
}
|
||||
elsif(c=='<' or c=='>')
|
||||
{
|
||||
}elsif(c=='<' or c=='>'){
|
||||
var cnt=0;
|
||||
for(;i<len;i+=1)
|
||||
{
|
||||
for(;i<len;i+=1){
|
||||
if(chr(program[i])=='>')
|
||||
cnt+=1;
|
||||
elsif(chr(program[i])=='<')
|
||||
cnt-=1;
|
||||
elsif(chr(program[i])!='\n')
|
||||
{
|
||||
elsif(chr(program[i])!='\n'){
|
||||
i-=1;
|
||||
break;
|
||||
}
|
||||
@@ -200,21 +190,15 @@ var bf=func(program)
|
||||
f~="ptr+="~cnt~";\n";
|
||||
elsif(cnt<0)
|
||||
f~="ptr-="~(-cnt)~";\n";
|
||||
}
|
||||
elsif(c==',')
|
||||
{
|
||||
}elsif(c==','){
|
||||
for(var j=0;j<size(stack);j+=1)
|
||||
f~='\t';
|
||||
f~="paper[ptr]=input();\n";
|
||||
}
|
||||
elsif(c=='.')
|
||||
{
|
||||
f~="paper[ptr]=input()[0];\n";
|
||||
}elsif(c=='.'){
|
||||
for(var j=0;j<size(stack);j+=1)
|
||||
f~='\t';
|
||||
f~="print(chr(paper[ptr]));\n";
|
||||
}
|
||||
elsif(c=='[')
|
||||
{
|
||||
}elsif(c=='['){
|
||||
for(var j=0;j<size(stack);j+=1)
|
||||
f~='\t';
|
||||
f~="while(paper[ptr])\n";
|
||||
@@ -222,11 +206,8 @@ var bf=func(program)
|
||||
f~='\t';
|
||||
f~="{\n";
|
||||
append(stack,0);
|
||||
}
|
||||
elsif(c==']')
|
||||
{
|
||||
if(!size(stack))
|
||||
{
|
||||
}elsif(c==']'){
|
||||
if(!size(stack)){
|
||||
println("lack [");
|
||||
return;
|
||||
}
|
||||
@@ -236,8 +217,7 @@ var bf=func(program)
|
||||
f~="}\n";
|
||||
}
|
||||
}
|
||||
if(size(stack))
|
||||
{
|
||||
if(size(stack)){
|
||||
println("lack ]");
|
||||
return;
|
||||
}
|
||||
|
||||
42
test/bfs.nas
@@ -1,26 +1,17 @@
|
||||
import("stl/lib.nas");
|
||||
import("stl/queue.nas");
|
||||
import.stl.queue;
|
||||
|
||||
rand(time(0));
|
||||
|
||||
var pixel=[' ','#','.','*'];
|
||||
var map=[];
|
||||
for(var i=0;i<10;i+=1)
|
||||
{
|
||||
for(var i=0;i<10;i+=1){
|
||||
append(map,[]);
|
||||
for(var j=0;j<20;j+=1)
|
||||
append(map[i],(rand()>0.7));
|
||||
}
|
||||
|
||||
var prt=func()
|
||||
{
|
||||
if(os.platform()=="windows")
|
||||
system("cls");
|
||||
else
|
||||
system("clear");
|
||||
var s="+--------------------+\n";
|
||||
for(var i=0;i<10;i+=1)
|
||||
{
|
||||
var prt=func(){
|
||||
var s="\e[0;0H+--------------------+\n";
|
||||
for(var i=0;i<10;i+=1){
|
||||
s~="|";
|
||||
for(var j=0;j<20;j+=1)
|
||||
s~=pixel[map[i][j]];
|
||||
@@ -28,31 +19,30 @@ var prt=func()
|
||||
}
|
||||
s~='+--------------------+\n';
|
||||
print(s);
|
||||
unix.sleep(1/144);
|
||||
}
|
||||
|
||||
var bfs=func(begin,end)
|
||||
{
|
||||
var bfs=func(begin,end){
|
||||
var move=[[1,0],[0,1],[-1,0],[0,-1]];
|
||||
var que=queue();
|
||||
que.push(begin);
|
||||
map[begin[0]][begin[1]]=2;
|
||||
map[end[0]][end[1]]=0;
|
||||
while(!que.empty())
|
||||
{
|
||||
if(map[1][0]==1 and map[0][1]==1)
|
||||
map[1][0]=0;
|
||||
while(!que.empty()){
|
||||
var vertex=que.front();
|
||||
que.pop();
|
||||
foreach(var i;move)
|
||||
{
|
||||
foreach(var i;move){
|
||||
var x=vertex[0]+i[0];
|
||||
var y=vertex[1]+i[1];
|
||||
if(x==end[0] and y==end[1])
|
||||
{
|
||||
if(x==end[0] and y==end[1]){
|
||||
map[x][y]=3;
|
||||
prt();
|
||||
print("reached.\n");
|
||||
return;
|
||||
}
|
||||
if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0)
|
||||
{
|
||||
if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0){
|
||||
que.push([x,y]);
|
||||
map[x][y]=2;
|
||||
}
|
||||
@@ -63,4 +53,8 @@ var bfs=func(begin,end)
|
||||
return;
|
||||
}
|
||||
|
||||
# enable ANSI escape sequence
|
||||
if(os.platform()=="windows")
|
||||
system("color");
|
||||
print("\ec");
|
||||
bfs([0,0],[9,19]);
|
||||
@@ -1 +1 @@
|
||||
for(var i=0;i<4e6;i+=1);
|
||||
for(var i=0;i<4e6;i+=1);
|
||||
|
||||
91
test/bp.nas
@@ -1,9 +1,6 @@
|
||||
import("lib.nas");
|
||||
|
||||
rand(time(0));
|
||||
|
||||
var new_neuron=func()
|
||||
{
|
||||
var new_neuron=func(){
|
||||
return {
|
||||
in:0,
|
||||
out:0,
|
||||
@@ -13,50 +10,51 @@ var new_neuron=func()
|
||||
};
|
||||
}
|
||||
|
||||
var sigmoid=func(x)
|
||||
{
|
||||
var tanh=func(x){
|
||||
var (a,b)=(math.exp(x),math.exp(-x));
|
||||
return (a-b)/(a+b);
|
||||
}
|
||||
var difftanh=func(x){
|
||||
x=tanh(x);
|
||||
return 1-x*x;
|
||||
}
|
||||
var sigmoid=func(x){
|
||||
return 1/(1+math.exp(-x));
|
||||
}
|
||||
var diffsigmoid=func(x)
|
||||
{
|
||||
var diffsigmoid=func(x){
|
||||
x=sigmoid(x);
|
||||
return x*(1-x);
|
||||
}
|
||||
|
||||
var (inum,hnum,onum,lr)=(2,4,1,0.1);
|
||||
var (inum,hnum,onum)=(2,5,1);
|
||||
var training_set=[[0,0],[0,1],[1,0],[1,1]];
|
||||
var expect=[0,1,1,0];
|
||||
|
||||
var hidden=[];
|
||||
for(var i=0;i<hnum;i+=1)
|
||||
{
|
||||
for(var i=0;i<hnum;i+=1){
|
||||
append(hidden,new_neuron());
|
||||
for(var j=0;j<inum;j+=1)
|
||||
append(hidden[i].w,2*rand());
|
||||
hidden[i].bia=5*rand();
|
||||
append(hidden[i].w,rand()>0.5?-2*rand():2*rand());
|
||||
hidden[i].bia=rand()>0.5?-5*rand():5*rand();
|
||||
}
|
||||
|
||||
var output=[];
|
||||
for(var i=0;i<onum;i+=1)
|
||||
{
|
||||
for(var i=0;i<onum;i+=1){
|
||||
append(output,new_neuron());
|
||||
for(var j=0;j<hnum;j+=1)
|
||||
append(output[i].w,2*rand());
|
||||
output[i].bia=5*rand();
|
||||
append(output[i].w,rand()>0.5?-2*rand():2*rand());
|
||||
output[i].bia=rand()>0.5?-5*rand():5*rand();
|
||||
}
|
||||
|
||||
var forward=func(x)
|
||||
{
|
||||
var forward=func(x){
|
||||
var input=training_set[x];
|
||||
for(var i=0;i<hnum;i+=1)
|
||||
{
|
||||
for(var i=0;i<hnum;i+=1){
|
||||
hidden[i].in=hidden[i].bia;
|
||||
for(var j=0;j<inum;j+=1)
|
||||
hidden[i].in+=hidden[i].w[j]*input[j];
|
||||
hidden[i].out=sigmoid(hidden[i].in);
|
||||
hidden[i].out=tanh(hidden[i].in);
|
||||
}
|
||||
for(var i=0;i<onum;i+=1)
|
||||
{
|
||||
for(var i=0;i<onum;i+=1){
|
||||
output[i].in=output[i].bia;
|
||||
for(var j=0;j<hnum;j+=1)
|
||||
output[i].in+=output[i].w[j]*hidden[j].out;
|
||||
@@ -64,18 +62,15 @@ var forward=func(x)
|
||||
}
|
||||
return;
|
||||
}
|
||||
var run=func(vec)
|
||||
{
|
||||
var run=func(vec){
|
||||
var input=vec;
|
||||
for(var i=0;i<hnum;i+=1)
|
||||
{
|
||||
for(var i=0;i<hnum;i+=1){
|
||||
hidden[i].in=hidden[i].bia;
|
||||
for(var j=0;j<inum;j+=1)
|
||||
hidden[i].in+=hidden[i].w[j]*input[j];
|
||||
hidden[i].out=sigmoid(hidden[i].in);
|
||||
hidden[i].out=tanh(hidden[i].in);
|
||||
}
|
||||
for(var i=0;i<onum;i+=1)
|
||||
{
|
||||
for(var i=0;i<onum;i+=1){
|
||||
output[i].in=output[i].bia;
|
||||
for(var j=0;j<hnum;j+=1)
|
||||
output[i].in+=output[i].w[j]*hidden[j].out;
|
||||
@@ -83,29 +78,24 @@ var run=func(vec)
|
||||
}
|
||||
return;
|
||||
}
|
||||
var get_error=func(x)
|
||||
{
|
||||
var get_error=func(x){
|
||||
return 0.5*(expect[x]-output[0].out)*(expect[x]-output[0].out);
|
||||
}
|
||||
var backward=func(x)
|
||||
{
|
||||
var backward=func(x){
|
||||
var input=training_set[x];
|
||||
output[0].diff=(expect[x]-output[0].out)*diffsigmoid(output[0].in);
|
||||
for(var i=0;i<hnum;i+=1)
|
||||
{
|
||||
for(var i=0;i<hnum;i+=1){
|
||||
hidden[i].diff=0;
|
||||
for(var j=0;j<onum;j+=1)
|
||||
hidden[i].diff+=output[j].w[i]*output[j].diff;
|
||||
hidden[i].diff*=diffsigmoid(hidden[i].in);
|
||||
hidden[i].diff*=difftanh(hidden[i].in);
|
||||
}
|
||||
for(var i=0;i<hnum;i+=1)
|
||||
{
|
||||
for(var i=0;i<hnum;i+=1){
|
||||
hidden[i].bia+=hidden[i].diff;
|
||||
for(var j=0;j<inum;j+=1)
|
||||
hidden[i].w[j]+=hidden[i].diff*input[j];
|
||||
}
|
||||
for(var i=0;i<onum;i+=1)
|
||||
{
|
||||
for(var i=0;i<onum;i+=1){
|
||||
output[i].bia+=output[i].diff;
|
||||
for(var j=0;j<hnum;j+=1)
|
||||
output[i].w[j]+=output[i].diff*hidden[j].out;
|
||||
@@ -114,20 +104,23 @@ var backward=func(x)
|
||||
}
|
||||
|
||||
var (cnt,error)=(0,100);
|
||||
while(error>0.0005)
|
||||
{
|
||||
while(error>0.0005){
|
||||
error=0;
|
||||
for(var i=0;i<4;i+=1)
|
||||
{
|
||||
for(var i=0;i<4;i+=1){
|
||||
forward(i);
|
||||
error+=get_error(i);
|
||||
backward(i);
|
||||
}
|
||||
cnt+=1;
|
||||
if(cnt>=3e5)
|
||||
break;
|
||||
}
|
||||
print('finished after ',cnt,' epoch.\n');
|
||||
foreach(var v;training_set)
|
||||
{
|
||||
if(cnt>=3e5){
|
||||
print("failed to train, ",cnt," epoch.\n");
|
||||
}else{
|
||||
print('finished after ',cnt,' epoch.\n');
|
||||
}
|
||||
foreach(var v;training_set){
|
||||
run(v);
|
||||
print(v,': ',output[0].out,'\n');
|
||||
}
|
||||
184
test/calc.nas
@@ -1,96 +1,142 @@
|
||||
import("lib.nas");
|
||||
import.stl.padding;
|
||||
|
||||
var source=[
|
||||
"main.cpp ",
|
||||
"nasal_err.h ",
|
||||
"nasal_ast.h ",
|
||||
"nasal_builtin.h ",
|
||||
"nasal_codegen.h ",
|
||||
"nasal_opt.h ",
|
||||
"nasal_gc.h ",
|
||||
"nasal_import.h ",
|
||||
"nasal_lexer.h ",
|
||||
"nasal_parse.h ",
|
||||
"nasal_vm.h ",
|
||||
"nasal_dbg.h ",
|
||||
"nasal.h "
|
||||
"main.cpp",
|
||||
"nasal_ast.h",
|
||||
"nasal_builtin.h",
|
||||
"nasal_codegen.h",
|
||||
"nasal_dbg.h",
|
||||
"nasal_err.h",
|
||||
"nasal_gc.h",
|
||||
"nasal_import.h",
|
||||
"nasal_lexer.h",
|
||||
"nasal_opt.h",
|
||||
"nasal_parse.h",
|
||||
"nasal_vm.h",
|
||||
"nasal.h"
|
||||
];
|
||||
|
||||
var lib=[
|
||||
"stl/lib.nas ",
|
||||
"stl/list.nas ",
|
||||
"stl/queue.nas ",
|
||||
"stl/result.nas ",
|
||||
"stl/sort.nas ",
|
||||
"stl/stack.nas "
|
||||
"fg_env.nas",
|
||||
"file.nas",
|
||||
"json.nas",
|
||||
"lib.nas",
|
||||
"list.nas",
|
||||
"log.nas",
|
||||
"module.nas",
|
||||
"padding.nas",
|
||||
"process_bar.nas",
|
||||
"queue.nas",
|
||||
"result.nas",
|
||||
"sort.nas",
|
||||
"stack.nas",
|
||||
"string.nas"
|
||||
];
|
||||
|
||||
var testfile=[
|
||||
"test/ascii-art.nas ",
|
||||
"test/auto_crash.nas ",
|
||||
"test/bf.nas ",
|
||||
"test/bfconvertor.nas ",
|
||||
"test/bfs.nas ",
|
||||
"test/bigloop.nas ",
|
||||
"test/bp.nas ",
|
||||
"test/calc.nas ",
|
||||
"test/choice.nas ",
|
||||
"test/class.nas ",
|
||||
"test/exception.nas ",
|
||||
"test/fib.nas ",
|
||||
"test/filesystem.nas ",
|
||||
"test/hexdump.nas ",
|
||||
"test/json.nas ",
|
||||
"test/leetcode1319.nas ",
|
||||
"test/lexer.nas ",
|
||||
"test/life.nas ",
|
||||
"test/loop.nas ",
|
||||
"test/mandel.nas ",
|
||||
"test/mandelbrot.nas ",
|
||||
"test/module_test.nas ",
|
||||
"test/nasal_test.nas ",
|
||||
"test/pi.nas ",
|
||||
"test/prime.nas ",
|
||||
"test/props.nas ",
|
||||
"test/quick_sort.nas ",
|
||||
"test/scalar.nas ",
|
||||
"test/trait.nas ",
|
||||
"test/turingmachine.nas",
|
||||
"test/ycombinator.nas "
|
||||
"ascii-art.nas",
|
||||
"auto_crash.nas",
|
||||
"bf.nas",
|
||||
"bfconvertor.nas",
|
||||
"bfs.nas",
|
||||
"bigloop.nas",
|
||||
"bp.nas",
|
||||
"calc.nas",
|
||||
"choice.nas",
|
||||
"class.nas",
|
||||
"coroutine.nas",
|
||||
"diff.nas",
|
||||
"exception.nas",
|
||||
"fib.nas",
|
||||
"filesystem.nas",
|
||||
"hexdump.nas",
|
||||
"httptest.nas",
|
||||
"json.nas",
|
||||
"leetcode1319.nas",
|
||||
"lexer.nas",
|
||||
"life.nas",
|
||||
"loop.nas",
|
||||
"mandel.nas",
|
||||
"mandelbrot.nas",
|
||||
"mcpu.nas",
|
||||
"md5.nas",
|
||||
"md5compare.nas",
|
||||
"module_test.nas",
|
||||
"nasal_test.nas",
|
||||
"occupation.nas",
|
||||
"pi.nas",
|
||||
"ppmgen.nas",
|
||||
"prime.nas",
|
||||
"qrcode.nas",
|
||||
"quick_sort.nas",
|
||||
"scalar.nas",
|
||||
"snake.nas",
|
||||
"tetris.nas",
|
||||
"trait.nas",
|
||||
"turingmachine.nas",
|
||||
"utf8chk.nas",
|
||||
"watchdog.nas",
|
||||
"wavecollapse.nas",
|
||||
"word_collector.nas",
|
||||
"ycombinator.nas"
|
||||
];
|
||||
|
||||
var module=[
|
||||
"module/fib.cpp "
|
||||
"fib.cpp",
|
||||
"keyboard.cpp",
|
||||
"nasocket.cpp",
|
||||
"libfib.nas",
|
||||
"libkey.nas",
|
||||
"libsock.nas"
|
||||
];
|
||||
|
||||
var getname=func(s){
|
||||
var (len,ch)=(size(s),' '[0]);
|
||||
for(var i=0;i<len and s[i]!=ch;i+=1);
|
||||
return substr(s,0,i);
|
||||
var longest=func(vec...){
|
||||
var len=0;
|
||||
foreach(var v;vec)
|
||||
foreach(var f;v)
|
||||
len=size(f)>len?size(f):len;
|
||||
return len;
|
||||
}
|
||||
var padding_length=longest(source,lib,testfile,module);
|
||||
|
||||
var count=func(s,c){
|
||||
var (cnt,len,ch)=(0,size(s),c[0]);
|
||||
for(var i=0;i<len;i+=1)
|
||||
cnt+=(s[i]==ch);
|
||||
var cnt=0;
|
||||
foreach(var i;split(c,s))
|
||||
cnt+=(size(i)!=0 and i!="\r")
|
||||
return cnt;
|
||||
}
|
||||
|
||||
var calc=func(codetype,files){
|
||||
var column=func(number){
|
||||
number=number>=1000?substr(str(number/1000),0,3)~'k':str(number);
|
||||
return rightpad(number,6);
|
||||
}
|
||||
|
||||
var calc=func(codetype,files,path=""){
|
||||
println(codetype);
|
||||
var (bytes,line,semi,line_cnt,semi_cnt)=(0,0,0,0,0);
|
||||
var (bytes,ctx,line,semi,line_cnt,semi_cnt)=(0,"",0,0,0,0);
|
||||
forindex(var i;files){
|
||||
var s=io.fin(getname(files[i]));
|
||||
var s=io.exists(path~files[i])?io.fin(path~files[i]):"";
|
||||
(line_cnt,semi_cnt)=(count(s,'\n'),count(s,';'));
|
||||
println(files[i],'| ',line_cnt,' \tline | ',semi_cnt,' \tsemi');
|
||||
println(rightpad(files[i],padding_length),'| ',
|
||||
column(line_cnt),'line | ',
|
||||
column(semi_cnt),'semi | ',
|
||||
rightpad(str(int(size(s)/1024)),6),'kb | ',
|
||||
md5(s),' |');
|
||||
bytes+=size(s);
|
||||
ctx~=s;
|
||||
line+=line_cnt;
|
||||
semi+=semi_cnt;
|
||||
}
|
||||
println('total: | ',line,' \tline | ',semi,' \tsemi');
|
||||
println('bytes: | ',bytes,'\tbytes| ',int(bytes/1024),' \tkb');
|
||||
println(rightpad("total:",padding_length),'| ',
|
||||
column(line),'line | ',
|
||||
column(semi),'semi | ',
|
||||
rightpad(str(int(bytes/1024)),6),'kb | ',
|
||||
md5(ctx),' |\n');
|
||||
return int(bytes/1024);
|
||||
}
|
||||
|
||||
calc("source code:",source);
|
||||
calc("lib:",lib);
|
||||
calc("test file:",testfile);
|
||||
calc("module:",module);
|
||||
var all=calc("source code:",source)
|
||||
+calc("lib:",lib,"stl/")
|
||||
+calc("test file:",testfile,"test/")
|
||||
+calc("module:",module,"module/");
|
||||
println(rightpad("total:",padding_length),'| ',rightpad(str(all),6),'kb');
|
||||
@@ -1,4 +1,3 @@
|
||||
import("lib.nas");
|
||||
var condition_true=1;
|
||||
var condition_false=0;
|
||||
if(condition_true)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var student=func(n,a){
|
||||
return {
|
||||
print_info:func println(n,' ',a),
|
||||
|
||||
96
test/coroutine.nas
Normal file
@@ -0,0 +1,96 @@
|
||||
# coroutine.nas by ValKmjolnir
|
||||
# 2022/5/19
|
||||
import.stl.process_bar;
|
||||
|
||||
var fib=func(){
|
||||
var (a,b)=(1,1);
|
||||
coroutine.yield(a);
|
||||
coroutine.yield(b);
|
||||
while(1){
|
||||
(a,b)=(b,a+b);
|
||||
coroutine.yield(b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var co=[coroutine.create(fib),coroutine.create(fib)];
|
||||
for(var i=0;i<45;i+=1){
|
||||
var res=[coroutine.resume(co[0]),coroutine.resume(co[1])];
|
||||
if(res[0]==nil or res[1]==nil or res[0][0]!=res[1][0])
|
||||
die("different coroutines don't share the same local scope");
|
||||
}
|
||||
|
||||
# test if coroutine can get upvalues
|
||||
func(){
|
||||
var x=1;
|
||||
var co=coroutine.create(func(){
|
||||
for(var j=0;j<128;j+=1){
|
||||
coroutine.yield(x,i,j);
|
||||
x+=1;
|
||||
}
|
||||
});
|
||||
for(var i=0;i<16;i+=1){
|
||||
var res=coroutine.resume(co);
|
||||
if(res==nil or res[0]!=x or res[1]!=i)
|
||||
die("coroutine should have the ability to get upvalues");
|
||||
}
|
||||
}();
|
||||
|
||||
# test crash in coroutines
|
||||
var co=coroutine.create(func{
|
||||
var b=func(){b()}
|
||||
coroutine.yield(b);
|
||||
b();
|
||||
coroutine.yield(0);
|
||||
});
|
||||
|
||||
println("coroutine yield: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[32m ",coroutine.status(co),"\e[0m");
|
||||
println("coroutine error: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
|
||||
println("coroutine yield: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
|
||||
|
||||
var co=coroutine.create(func{
|
||||
var a=1;
|
||||
var b=func(){
|
||||
b();
|
||||
}
|
||||
coroutine.yield(b);
|
||||
coroutine.yield(b());
|
||||
});
|
||||
|
||||
println("coroutine yield: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[32m ",coroutine.status(co),"\e[0m");
|
||||
println("coroutine error: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
|
||||
println("coroutine yield: ",coroutine.resume(co));
|
||||
println("coroutine state:\e[91m ",coroutine.status(co),"\e[0m");
|
||||
println("ok");
|
||||
|
||||
# pressure test
|
||||
var productor=func(){
|
||||
for(var i=0;;i+=1)
|
||||
coroutine.yield(i);
|
||||
}
|
||||
var total=1000; # ms
|
||||
var co=coroutine.create(productor);
|
||||
var tm=maketimestamp();
|
||||
|
||||
if(os.platform()=="windows"){
|
||||
system("chcp 65001");
|
||||
system("color");
|
||||
}
|
||||
var counter=0;
|
||||
var bar=process_bar.high_resolution_bar(40);
|
||||
var consumer=func(){
|
||||
counter+=1;
|
||||
for(var i=0;i<5;i+=1)
|
||||
coroutine.resume(co);
|
||||
var rate=(tm.elapsedMSec()+1)/total;
|
||||
print(bar.bar(rate)," ",rate*100,"% \r");
|
||||
}
|
||||
|
||||
tm.stamp();
|
||||
while(tm.elapsedMSec()<total)
|
||||
consumer();
|
||||
println("\nexecute ",counter," tasks during ",total," ms, avg ",counter/total," tasks/ms.");
|
||||
124
test/diff.nas
Normal file
@@ -0,0 +1,124 @@
|
||||
var myers=func(src,dst,show_table=0){
|
||||
(src,dst)=(split("\n",src),split("\n",dst));
|
||||
append(src,"");
|
||||
append(dst,"");
|
||||
var (src_len,dst_len)=(size(src),size(dst));
|
||||
|
||||
var mat=[];
|
||||
setsize(mat,dst_len*src_len);
|
||||
forindex(var i;mat){
|
||||
mat[i]=0;
|
||||
}
|
||||
var visited=[];
|
||||
setsize(visited,dst_len*src_len);
|
||||
forindex(var i;visited){
|
||||
visited[i]=0;
|
||||
}
|
||||
|
||||
forindex(var y;dst)
|
||||
forindex(var x;src)
|
||||
mat[y*src_len+x]=(src[x]==dst[y]);
|
||||
|
||||
if(show_table){
|
||||
var curve=[
|
||||
["+---","| "],
|
||||
["+---","| \\ "]
|
||||
];
|
||||
var s="";
|
||||
forindex(var y;dst){
|
||||
forindex(var t;curve[0]){
|
||||
forindex(var x;src){
|
||||
s~=curve[mat[y*src_len+x]][t];
|
||||
}
|
||||
s~=["+","|"][t]~"\n";
|
||||
}
|
||||
}
|
||||
forindex(var i;src)
|
||||
s~="+---";
|
||||
print(s~"+\n");
|
||||
}
|
||||
|
||||
var (total,path,vec)=([],[],[[0,0,-1]]);
|
||||
visited[0]=1;
|
||||
while(size(vec)){
|
||||
append(total,vec);
|
||||
var tmp=[];
|
||||
forindex(var i;vec){
|
||||
var elem=vec[i];
|
||||
var (x,y)=(elem[1],elem[0]);
|
||||
|
||||
# find solution
|
||||
if(x==src_len-1 and y==dst_len-1){
|
||||
append(path,vec[i]);
|
||||
for(var (prev,iter)=(elem[2],size(total)-1);iter>0;iter-=1){
|
||||
var t=total[iter-1][prev];
|
||||
append(path,t);
|
||||
prev=t[2];
|
||||
}
|
||||
|
||||
if(show_table){
|
||||
for(var t=size(path)-1;t>=0;t-=1)
|
||||
print("("~path[t][1]~","~path[t][0]~")",t==0?"":"->");
|
||||
println();
|
||||
}
|
||||
|
||||
# reverse path
|
||||
for(var t=0;t<size(path)/2;t+=1)
|
||||
(path[t],path[-1-t])=(path[-1-t],path[t]);
|
||||
# print diff
|
||||
for(var t=1;t<size(path);t+=1){
|
||||
var (prev_x,prev_y)=(path[t-1][1],path[t-1][0]);
|
||||
var (x,y)=(path[t][1],path[t][0]);
|
||||
var (sub_x,sub_y)=(x-prev_x,y-prev_y);
|
||||
if(sub_x==1 and sub_y==1){
|
||||
if(show_table)
|
||||
println(" ",src[prev_x]);
|
||||
}elsif(sub_x==1 and sub_y==0){
|
||||
println("\e[31m - ",src[prev_x],"\e[0m");
|
||||
}elsif(sub_x==0 and sub_y==1){
|
||||
println("\e[32m + ",dst[prev_y],"\e[0m");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# do bfs
|
||||
if(mat[y*src_len+x]==1){
|
||||
if(x+1<src_len and y+1<dst_len and visited[(y+1)*src_len+x+1]==0){
|
||||
append(tmp,[y+1,x+1,i]);
|
||||
visited[(y+1)*src_len+x+1]=1;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(x+1<src_len and visited[y*src_len+x+1]==0){
|
||||
append(tmp,[y,x+1,i]);
|
||||
visited[y*src_len+x+1]=1;
|
||||
}
|
||||
if(y+1<dst_len and visited[(y+1)*src_len+x]==0){
|
||||
append(tmp,[y+1,x,i]);
|
||||
visited[(y+1)*src_len+x]=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
vec=tmp;
|
||||
}
|
||||
}
|
||||
|
||||
func(diff){
|
||||
diff(
|
||||
"var a=0;\nvar b=1;\nprint(\"hello \",a);\nvar c=2;\nc=[];\nvar d=3;\nvar l=list();\nvar q=queue();\n",
|
||||
"var a=0;\nvar b=1;\nb=[];\nprintln(\"hello \",a);\nvar c=2;\nvar d=3;\nprintln(\"hello world!\");\nvar l=list();\nvar q=queue();\n",
|
||||
1
|
||||
);
|
||||
print("\n");
|
||||
diff(
|
||||
"A\nB\nC\nA\nB\nB\nA\n",
|
||||
"C\nB\nA\nB\nA\nC\n",
|
||||
1
|
||||
);
|
||||
print("\n");
|
||||
diff(
|
||||
io.fin("test/bf.nas"),
|
||||
io.fin("test/bfconvertor.nas")
|
||||
);
|
||||
}(myers);
|
||||
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var ResultTrait={
|
||||
Ok:func(val){
|
||||
me.ok=val;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import("lib.nas");
|
||||
var fib=func(x)
|
||||
{
|
||||
if(x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
if(x<2) return x;
|
||||
return fib(x-1)+fib(x-2);
|
||||
}
|
||||
for(var i=0;i<31;i+=1)
|
||||
print(fib(i),'\n');
|
||||
print(fib(i),'\n');
|
||||
|
||||
@@ -1,18 +1,7 @@
|
||||
import("lib.nas");
|
||||
|
||||
var fd=io.open("test/filesystem.nas");
|
||||
while((var line=io.readln(fd))!=nil)
|
||||
println(line);
|
||||
io.close(fd);
|
||||
println(io.stat("test/filesystem.nas"));
|
||||
|
||||
var dd=unix.opendir("test");
|
||||
while(var name=unix.readdir(dd))
|
||||
println(name);
|
||||
unix.closedir(dd);
|
||||
|
||||
var files=func(dir){
|
||||
var dd=unix.opendir(dir);
|
||||
var files=func(path){
|
||||
if(!io.exists(path))
|
||||
return [];
|
||||
var dd=unix.opendir(path);
|
||||
var res=[];
|
||||
while(var n=unix.readdir(dd))
|
||||
append(res,n);
|
||||
@@ -20,17 +9,29 @@ var files=func(dir){
|
||||
return res;
|
||||
}
|
||||
var prt=func(s,path){
|
||||
foreach(var i;files(path)){
|
||||
print(s,i);
|
||||
if(unix.isdir(path~'/'~i)){
|
||||
print(' <dir>\n');
|
||||
if(i!='.' and i!='..')
|
||||
prt(s~' |',path~'/'~i);
|
||||
var vec=files(path);
|
||||
var last=size(vec)-1;
|
||||
forindex(var i;vec){
|
||||
var f=vec[i];
|
||||
if(f=="." or f=="..")
|
||||
continue;
|
||||
foreach(var j;s)
|
||||
print("\e[34m",j,"\e[0m");
|
||||
if(unix.isdir(path~"/"~f)){
|
||||
println("\e[34m",i==last?" └─":" ├─","\e[0m\e[33m[",f,"]\e[36m>\e[0m");
|
||||
append(s,i==last?" ":" │ ");
|
||||
prt(s,path~"/"~f);
|
||||
pop(s);
|
||||
}elsif(unix.isfile(path~"/"~f)){
|
||||
println("\e[34m",i==last?" └─":" ├─","\e[0m\e[32m",f,"\e[0m");
|
||||
}else{
|
||||
println("\e[34m",i==last?" └─":" ├─","\e[0m\e[91m",f,"\e[0m");
|
||||
}
|
||||
elsif(unix.isfile(path~'/'~i))
|
||||
print(" <file>\n");
|
||||
else
|
||||
print(' <unknown>\n');
|
||||
}
|
||||
}
|
||||
prt('',".");
|
||||
|
||||
# enable unicode
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
println("\e[33m[",unix.getcwd(),"]\e[36m>\e[0m");
|
||||
prt([""],".");
|
||||
@@ -1,8 +1,6 @@
|
||||
# hexdump.nas by ValKmjolnir
|
||||
# 2021/8/13
|
||||
|
||||
import("lib.nas");
|
||||
|
||||
# init
|
||||
var hex_num=[
|
||||
'0','1','2','3',
|
||||
@@ -18,16 +16,23 @@ foreach(var i;hex_num)
|
||||
# read file
|
||||
var s=func(){
|
||||
var filename=[
|
||||
"nasal.h",
|
||||
"LICENSE",
|
||||
"main.cpp",
|
||||
"makefile",
|
||||
"nasal_ast.h",
|
||||
"nasal_builtin.h",
|
||||
"nasal_codegen.h",
|
||||
"nasal_dbg.h",
|
||||
"nasal_err.h",
|
||||
"nasal_gc.h",
|
||||
"nasal_import.h",
|
||||
"nasal_lexer.h",
|
||||
"nasal_opt.h",
|
||||
"nasal_parse.h",
|
||||
"nasal_vm.h",
|
||||
"nasal.ebnf",
|
||||
"nasal.h",
|
||||
"README.md"
|
||||
];
|
||||
var ret="";
|
||||
foreach(var elem;filename)
|
||||
@@ -44,8 +49,12 @@ var hex_index=[0,0,0,0];
|
||||
# print binary in text format
|
||||
var textprint=func(index){
|
||||
var info='';
|
||||
for(var i=index-cnt;i<index;i+=1)
|
||||
info~=(0<=s[i] and s[i]<32)?'.':chr(s[i]);
|
||||
if(os.platform()=="windows")
|
||||
for(var i=index-cnt;i<index;i+=1)
|
||||
info~=(s[i]<32 or s[i]>=128)?'.':chr(s[i]);
|
||||
else
|
||||
for(var i=index-cnt;i<index;i+=1)
|
||||
info~=(0<=s[i] and s[i]<32)?'.':chr(s[i]);
|
||||
for(var i=cnt;i<16;i+=1)
|
||||
info~='.';
|
||||
return ' |'~info~'|\n';
|
||||
|
||||
335
test/httptest.nas
Normal file
@@ -0,0 +1,335 @@
|
||||
import.module.libsock;
|
||||
|
||||
var http=func(){
|
||||
var sd=nil;
|
||||
return {
|
||||
establish:func(ip,port){
|
||||
sd=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP);
|
||||
if(socket.bind(sd,ip,port)<0){
|
||||
println("failed to bind socket "~sd~" at IP: "~ip~" port: "~port~".");
|
||||
return;
|
||||
}
|
||||
socket.listen(sd,1);
|
||||
println("[",os.time(),"] start server at [",ip,":",port,"]");
|
||||
},
|
||||
shutdown:func(){
|
||||
println("[",os.time(),"] shutdown server");
|
||||
socket.closesocket(sd);
|
||||
},
|
||||
accept:func(){
|
||||
return socket.accept(sd);
|
||||
},
|
||||
disconnect:func(client,log=0){
|
||||
if(log)
|
||||
println("[",os.time(),"] [",client.ip,"] disconnected");
|
||||
return socket.closesocket(client.sd);
|
||||
},
|
||||
recv:func(client){
|
||||
var data=socket.recv(client.sd,2048);
|
||||
if(!data.size){
|
||||
println("[",os.time(),"] [",client.ip,"] closed connection");
|
||||
return nil;
|
||||
}
|
||||
var first=split("\n",data.str)[0];
|
||||
var (type,path)=split(" ",first)[0,1];
|
||||
println("[",os.time(),"] [",client.ip,"] request ",type," [",path,"]");
|
||||
return {type:type,path:path};
|
||||
},
|
||||
send:func(client,content){
|
||||
println("[",os.time(),"] [",client.ip,"] get size ",size(content)," byte(s)");
|
||||
return socket.send(client.sd,content);
|
||||
}
|
||||
};
|
||||
}();
|
||||
http.establish("127.0.0.1",8080);
|
||||
|
||||
var highlight_style="
|
||||
<style>
|
||||
body{
|
||||
background: #303030;
|
||||
}
|
||||
pre{
|
||||
background: #303030;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
code{
|
||||
color: white;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
text-align: left;
|
||||
}
|
||||
code.key{color: #f895e7;}
|
||||
code.id{color: #8abef0;}
|
||||
code.opr{color: #f895e7;}
|
||||
code.brace{color: #eafd70;}
|
||||
code.str{color: #a5ffd0;}
|
||||
code.num{color: #ff9a41;}
|
||||
code.note{color: #808080;}
|
||||
</style>";
|
||||
|
||||
var html_read_file=func(filename){
|
||||
var timer=maketimestamp();
|
||||
timer.stamp();
|
||||
var keyword=["var","func","for","while","foreach","forindex","break","continue","return","if","else","elsif","nil"];
|
||||
var file_text=split("",io.fin(filename));
|
||||
var (s,index,len)=("",-1,size(file_text));
|
||||
var content="";
|
||||
|
||||
var next=func(){
|
||||
if(index+1>=len)
|
||||
return index+1;
|
||||
index+=1;
|
||||
s=file_text[index];
|
||||
return index;
|
||||
}
|
||||
var prev=func(){
|
||||
index-=1;
|
||||
s=file_text[index];
|
||||
}
|
||||
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(s==">")
|
||||
content~="<code class=\"opr\">></code>";
|
||||
elsif(s=="<")
|
||||
content~="<code class=\"opr\"><</code>";
|
||||
elsif(s=="[" or s=="]" or s=="(" or s==")" or s=="{" or s=="}")
|
||||
content~="<code class=\"brace\">"~s~"</code>";
|
||||
elsif(s=="=" or s=="," or s==";" or s==":" or s=="|" or s=="&" or s=="!" or s=="?" or s=="+" or s=="-" or s=="*" or s=="/" or s=="~" or s==".")
|
||||
content~="<code class=\"opr\">"~s~"</code>";
|
||||
elsif(s=="_" or ("a"[0]<=s[0] and s[0]<="z"[0]) or ("A"[0]<=s[0] and s[0]<="Z"[0]) or s[0]<0 or s[0]>=128){
|
||||
var tmp=""~s; # generate a new string
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(s=="_" or ("a"[0]<=s[0] and s[0]<="z"[0]) or ("A"[0]<=s[0] and s[0]<="Z"[0]) or ("0"[0]<=s[0] and s[0]<="9"[0]) or s[0]<0 or s[0]>=128)
|
||||
tmp~=s;
|
||||
else{
|
||||
prev();
|
||||
break;
|
||||
}
|
||||
}
|
||||
var is_key=0;
|
||||
foreach(var i;keyword)
|
||||
if(tmp==i){
|
||||
is_key=1;
|
||||
content~="<code class=\"key\">"~tmp~"</code>";
|
||||
break;
|
||||
}
|
||||
if(!is_key)
|
||||
content~="<code class=\"id\">"~tmp~"</code>";
|
||||
}elsif("0"[0]<=s[0] and s[0]<="9"[0]){
|
||||
content~="<code class=\"num\">"~s;
|
||||
if(next()>=len){
|
||||
content~="</code>";
|
||||
break;
|
||||
}
|
||||
if(s=="o"){
|
||||
content~="o";
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if("0"[0]<=s[0] and s[0]<="7"[0])
|
||||
content~=s;
|
||||
else
|
||||
break;
|
||||
}
|
||||
content~="</code>";
|
||||
prev();
|
||||
}elsif(s=="x"){
|
||||
content~="x";
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(("0"[0]<=s[0] and s[0]<="9"[0]) or ("a"[0]<=s[0] and s[0]<='f') or ("A"[0]<=s[0] or s[0]<="F"))
|
||||
content~=s;
|
||||
else
|
||||
break;
|
||||
}
|
||||
content~="</code>";
|
||||
prev();
|
||||
}elsif(("0"[0]<=s[0] and s[0]<="9"[0]) or s=="." or s=="e"){
|
||||
while("0"[0]<=s[0] and s[0]<="9"[0]){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
}
|
||||
if(s=="."){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
}
|
||||
while("0"[0]<=s[0] and s[0]<="9"[0]){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
}
|
||||
if(s=="e"){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(s=="-" or s=="+"){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while("0"[0]<=s[0] and s[0]<="9"[0]){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
}
|
||||
prev();
|
||||
content~="</code>";
|
||||
}else{
|
||||
prev();
|
||||
content~="</code>";
|
||||
}
|
||||
}elsif(s=="\"" or s=="'" or s=="`"){
|
||||
var quot=s~""; # generate a new string
|
||||
content~="<code class=\"str\">"~s;
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(s==quot){
|
||||
content~=s~"</code>";
|
||||
break;
|
||||
}elsif(s=="\\"){
|
||||
content~=s;
|
||||
if(next()>=len)
|
||||
break;
|
||||
content~=s;
|
||||
}elsif(s==">"){
|
||||
content~=">";
|
||||
}elsif(s=="<"){
|
||||
content~="<";
|
||||
}else{
|
||||
content~=s;
|
||||
}
|
||||
}
|
||||
}elsif(s=="#"){
|
||||
content~="<code class=\"note\">"~s;
|
||||
while(1){
|
||||
if(next()>=len)
|
||||
break;
|
||||
if(s=="\n" or s=="\r"){
|
||||
content~=s;
|
||||
break;
|
||||
}elsif(s==">"){
|
||||
content~=">";
|
||||
}elsif(s=="<"){
|
||||
content~="<";
|
||||
}else{
|
||||
content~=s;
|
||||
}
|
||||
}
|
||||
content~="</code>";
|
||||
}
|
||||
else
|
||||
content~=s;
|
||||
}
|
||||
println("[",os.time(),"] analyzed [",filename,"] for ",timer.elapsedMSec(),"ms");
|
||||
return content;
|
||||
}
|
||||
|
||||
var respond={
|
||||
ok:func(html){
|
||||
println("[",os.time(),"] respond 200 OK");
|
||||
return "Http/1.1 200 OK\n\n"~html~"\n";
|
||||
},
|
||||
not_found:func(){
|
||||
println("[",os.time(),"] respond 404 NOT FOUND");
|
||||
return "Http/1.1 404 NOT FOUND\n\n<!DOCTYPE html>
|
||||
<head>
|
||||
<title> 404 not found </title>
|
||||
<meta charset=\"utf-8\">
|
||||
</head>
|
||||
<body>
|
||||
<text>
|
||||
404 NOT FOUND!
|
||||
</text>
|
||||
</body>
|
||||
</html>\n";
|
||||
},
|
||||
teapot:func(){
|
||||
println("[",os.time(),"] respond 418 I'm a teapot");
|
||||
return "Http/1.1 418 I'm a teapot\n\n<!DOCTYPE html>
|
||||
<head>
|
||||
<title> I'm a teapot </title>
|
||||
<meta charset=\"utf-8\">
|
||||
</head>
|
||||
<body>
|
||||
<text>
|
||||
This server cannot brew coffee because it is a teapot.
|
||||
</text>
|
||||
</body>
|
||||
</html>\n";
|
||||
}
|
||||
};
|
||||
|
||||
var files=func(){
|
||||
var res={};
|
||||
var dd=unix.opendir("./test");
|
||||
while((var name=unix.readdir(dd))!=nil)
|
||||
res[name]=1;
|
||||
return res;
|
||||
}();
|
||||
|
||||
while(1){
|
||||
var client=http.accept();
|
||||
var data=http.recv(client);
|
||||
if(data==nil){
|
||||
http.disconnect(client);
|
||||
continue;
|
||||
}
|
||||
if(data.type=="GET"){
|
||||
var path=data.path;
|
||||
var args=split("?",path);
|
||||
var tmp={};
|
||||
if(size(args)==2){
|
||||
path=args[0];
|
||||
args=split("=",args[1]);
|
||||
for(var i=0;i<size(args);i+=2)
|
||||
tmp[args[i]]=args[i+1];
|
||||
}
|
||||
if(path=="/" or path=="/index"){
|
||||
if(contains(tmp,"filename") and contains(files,tmp.filename)){
|
||||
var filename=tmp.filename;
|
||||
var page="<!DOCTYPE html><head><title> "~filename~" </title><meta charset=\"utf-8\">"~highlight_style~"</head>\n<body><pre>\n";
|
||||
var page_back="</pre>\n</body>\n</html>\n";
|
||||
http.send(client,respond.ok(page~html_read_file("./test/"~filename)~page_back));
|
||||
}else{
|
||||
http.send(client,respond.ok(io.fin("./doc/nasal-http-test-web.html")));
|
||||
}
|
||||
}
|
||||
elsif(path=="/shutdown"){
|
||||
http.send(client,respond.ok("http server shutdown."));
|
||||
break;
|
||||
}
|
||||
elsif(path=="/favicon.ico")
|
||||
http.send(client,respond.ok(io.fin("./doc/pic/favicon.ico")));
|
||||
elsif(path=="/license")
|
||||
http.send(client,respond.ok(io.fin("./LICENSE")));
|
||||
elsif(path=="/doc/pic/nasal.png" or path=="/doc/pic/benchmark.png" or path=="/doc/pic/mandelbrot.png")
|
||||
http.send(client,respond.ok(io.fin("."~path)));
|
||||
else{
|
||||
var filename=substr(path,1,size(path)-1);
|
||||
if(contains(files,filename)){
|
||||
var page="<!DOCTYPE html><head><title> "~filename~" </title><meta charset=\"utf-8\">"~highlight_style~"</head>\n<body><pre>\n";
|
||||
var page_back="</pre>\n</body>\n</html>\n";
|
||||
http.send(client,respond.ok(page~html_read_file("./test/"~filename)~page_back));
|
||||
}
|
||||
elsif(filename=="teapot")
|
||||
http.send(client,respond.teapot());
|
||||
else
|
||||
http.send(client,respond.not_found());
|
||||
}
|
||||
}elsif(data.type=="POST"){
|
||||
http.send(client,respond.not_found);
|
||||
}
|
||||
http.disconnect(client);
|
||||
}
|
||||
http.shutdown();
|
||||
212
test/json.nas
@@ -1,196 +1,18 @@
|
||||
#lib json.nas
|
||||
import("lib.nas");
|
||||
var json={
|
||||
text:'',
|
||||
line:1,
|
||||
size:0,
|
||||
ptr:0,
|
||||
get:nil,
|
||||
check:nil,
|
||||
next:nil,
|
||||
match:nil,
|
||||
hash_gen:nil,
|
||||
vec_gen:nil,
|
||||
member:nil,
|
||||
parse:nil,
|
||||
token:{content:'',type:''},
|
||||
content:[],
|
||||
};
|
||||
import.stl.json;
|
||||
|
||||
json.get=func(filename)
|
||||
{
|
||||
me.line=1;
|
||||
me.ptr=0;
|
||||
me.content=[];
|
||||
me.token={content:'',type:''};
|
||||
me.text=io.fin(filename);
|
||||
if(!size(me.text))
|
||||
die("cannot open "~filename);
|
||||
me.text=split('',me.text);
|
||||
me.size=size(me.text);
|
||||
return;
|
||||
}
|
||||
|
||||
json.check=func(ptr)
|
||||
{
|
||||
var str=me.text[ptr];
|
||||
return (str=='{' or str=='}' or str=='[' or str==']' or str==',' or str==':' or str=='\"' or ('0'<=str and str<='9'));
|
||||
}
|
||||
|
||||
json.next=func()
|
||||
{
|
||||
while(me.ptr<me.size and !json.check(me.ptr))
|
||||
{
|
||||
if(me.text[me.ptr]=='\n')
|
||||
me.line+=1;
|
||||
me.ptr+=1;
|
||||
}
|
||||
if(me.ptr>=me.size)
|
||||
{
|
||||
me.token.content="";
|
||||
me.token.type="eof";
|
||||
return;
|
||||
}
|
||||
|
||||
if(me.text[me.ptr]=='{')
|
||||
{
|
||||
me.token.content='{';
|
||||
me.token.type="left brace";
|
||||
}
|
||||
elsif(me.text[me.ptr]=='}')
|
||||
{
|
||||
me.token.content='}';
|
||||
me.token.type="right brace";
|
||||
}
|
||||
elsif(me.text[me.ptr]=='[')
|
||||
{
|
||||
me.token.content='[';
|
||||
me.token.type="left bracket";
|
||||
}
|
||||
elsif(me.text[me.ptr]==']')
|
||||
{
|
||||
me.token.content=']';
|
||||
me.token.type="right bracket";
|
||||
}
|
||||
elsif(me.text[me.ptr]==',')
|
||||
{
|
||||
me.token.content=',';
|
||||
me.token.type="comma";
|
||||
}
|
||||
elsif(me.text[me.ptr]==':')
|
||||
{
|
||||
me.token.content=':';
|
||||
me.token.type="colon";
|
||||
}
|
||||
elsif(me.text[me.ptr]=='\"')
|
||||
{
|
||||
var s="";
|
||||
me.ptr+=1;
|
||||
while(me.ptr<me.size and me.text[me.ptr]!='\"')
|
||||
{
|
||||
s~=me.text[me.ptr];
|
||||
me.ptr+=1;
|
||||
}
|
||||
me.token.content=s;
|
||||
me.token.type="string";
|
||||
}
|
||||
elsif('0'<=me.text[me.ptr] and me.text[me.ptr]<='9')
|
||||
{
|
||||
var s=me.text[me.ptr];
|
||||
me.ptr+=1;
|
||||
while(me.ptr<me.size and (('0'<=me.text[me.ptr] and me.text[me.ptr]<='9') or me.text[me.ptr]=='.'))
|
||||
{
|
||||
s~=me.text[me.ptr];
|
||||
me.ptr+=1;
|
||||
}
|
||||
me.ptr-=1;
|
||||
me.token.content=num(s);
|
||||
me.token.type="number";
|
||||
}
|
||||
me.ptr+=1;
|
||||
return;
|
||||
}
|
||||
|
||||
json.match=func(type)
|
||||
{
|
||||
if(me.token.type!=type)
|
||||
print("line ",me.line,": expect ",type," but get ",me.token.content,".");
|
||||
me.next();
|
||||
return;
|
||||
}
|
||||
|
||||
json.hash_gen=func()
|
||||
{
|
||||
var hash={};
|
||||
me.match("left brace");
|
||||
me.member(hash);
|
||||
while(me.token.type=="comma")
|
||||
{
|
||||
me.match("comma");
|
||||
me.member(hash);
|
||||
}
|
||||
me.match("right brace");
|
||||
return hash;
|
||||
}
|
||||
|
||||
json.vec_gen=func()
|
||||
{
|
||||
var vec=[];
|
||||
me.match("left bracket");
|
||||
if(me.token.type=="left brace")
|
||||
append(vec,me.hash_gen());
|
||||
elsif(me.token.type=="left bracket")
|
||||
append(vec,me.vec_gen());
|
||||
elsif(me.token.type=="string" or me.token.type=="number")
|
||||
{
|
||||
append(vec,me.token.content);
|
||||
me.next();
|
||||
}
|
||||
while(me.token.type=="comma")
|
||||
{
|
||||
me.match("comma");
|
||||
if(me.token.type=="left brace")
|
||||
append(vec,me.hash_gen());
|
||||
elsif(me.token.type=="left bracket")
|
||||
append(vec,me.vec_gen());
|
||||
elsif(me.token.type=="string" or me.token.type=="number")
|
||||
{
|
||||
append(vec,me.token.content);
|
||||
me.next();
|
||||
}
|
||||
}
|
||||
me.match("right bracket");
|
||||
return vec;
|
||||
}
|
||||
|
||||
json.member=func(hash)
|
||||
{
|
||||
var name=me.token.content;
|
||||
me.match("string");
|
||||
me.match("colon");
|
||||
if(me.token.type=="left brace")
|
||||
hash[name]=me.hash_gen();
|
||||
elsif(me.token.type=="left bracket")
|
||||
hash[name]=me.vec_gen();
|
||||
elsif(me.token.type=="string" or me.token.type=="number")
|
||||
{
|
||||
hash[name]=me.token.content;
|
||||
me.next();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
json.parse=func()
|
||||
{
|
||||
me.content={};
|
||||
me.next();
|
||||
me.match("left brace");
|
||||
me.member(me.content);
|
||||
while(me.token.type=="comma")
|
||||
{
|
||||
me.match("comma");
|
||||
me.member(me.content);
|
||||
}
|
||||
me.match("right brace");
|
||||
return;
|
||||
}
|
||||
var ss=JSON.stringify({
|
||||
vec:[0,1,2],
|
||||
hash:{
|
||||
m1:0,
|
||||
m2:"str",
|
||||
m3:[114514],
|
||||
m4:{year:1919,month:8,date:10}
|
||||
},
|
||||
emptyhash:{},
|
||||
emptyvec:[],
|
||||
empty:[{}],
|
||||
empty_an:[[[[[[{}]]]]]],
|
||||
function:func(){}
|
||||
});
|
||||
println(ss);
|
||||
println(JSON.parse(ss));
|
||||
@@ -1,4 +1,3 @@
|
||||
import("lib.nas");
|
||||
# union set
|
||||
var n=4;
|
||||
var input=[[0,1],[0,2],[1,2]];
|
||||
|
||||
138
test/lexer.nas
@@ -1,23 +1,26 @@
|
||||
import("lib.nas");
|
||||
|
||||
var lexer=func(file)
|
||||
{
|
||||
var (ptr,token)=(0,[]);
|
||||
var s=io.fin(file);
|
||||
var len=size(s);
|
||||
return
|
||||
{
|
||||
jmp_note:func()
|
||||
{
|
||||
while(ptr<len and chr(s[ptr])!='\n')
|
||||
var line=0;
|
||||
var gen=func(tok){
|
||||
append(token,{
|
||||
line:line,
|
||||
token:tok
|
||||
});
|
||||
}
|
||||
return {
|
||||
jmp_note:func(){
|
||||
while(ptr<len and s[ptr]!='\n'[0])
|
||||
ptr+=1;
|
||||
if(ptr<len and s[ptr]=='\n'[0])
|
||||
line+=1;
|
||||
ptr+=1;
|
||||
},
|
||||
id_gen:func()
|
||||
{
|
||||
id_gen:func(){
|
||||
var tmp="";
|
||||
while(ptr<len)
|
||||
{
|
||||
while(ptr<len){
|
||||
var c=s[ptr];
|
||||
if(('a'[0]<=c and c<='z'[0])
|
||||
or ('A'[0]<=c and c<='Z'[0])
|
||||
@@ -28,21 +31,19 @@ var lexer=func(file)
|
||||
break;
|
||||
ptr+=1;
|
||||
}
|
||||
append(token,tmp);
|
||||
gen(tmp);
|
||||
},
|
||||
str_gen:func()
|
||||
{
|
||||
str_gen:func(){
|
||||
var str="";
|
||||
var mark=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
while(ptr<len and chr(s[ptr])!=mark)
|
||||
{
|
||||
if(chr(s[ptr])=='\\')
|
||||
{
|
||||
while(ptr<len and chr(s[ptr])!=mark){
|
||||
if(chr(s[ptr])=='\\'){
|
||||
ptr+=1;
|
||||
var c=chr(s[ptr]);
|
||||
if (c=='a' ) str~='\a';
|
||||
elsif(c=='b' ) str~='\b';
|
||||
elsif(c=='e' ) str~='\e';
|
||||
elsif(c=='f' ) str~='\f';
|
||||
elsif(c=='n' ) str~='\n';
|
||||
elsif(c=='r' ) str~='\r';
|
||||
@@ -54,70 +55,60 @@ var lexer=func(file)
|
||||
elsif(c=='\'') str~='\'';
|
||||
elsif(c=='\"') str~='\"';
|
||||
else str~=c;
|
||||
}
|
||||
else
|
||||
}else{
|
||||
if(s[ptr]=='\n'[0])
|
||||
line+=1;
|
||||
str~=chr(s[ptr]);
|
||||
}
|
||||
ptr+=1;
|
||||
}
|
||||
if(ptr>=len)
|
||||
print("read eof when generating string.\n");
|
||||
ptr+=1;
|
||||
append(token,str);
|
||||
gen(str);
|
||||
},
|
||||
num_gen:func()
|
||||
{
|
||||
num_gen:func(){
|
||||
var number=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
if(ptr<len and chr(s[ptr])=='x')
|
||||
{
|
||||
if(ptr<len and chr(s[ptr])=='x'){
|
||||
ptr+=1;
|
||||
while(ptr<len and
|
||||
('a'[0]<=s[ptr] and s[ptr]<='f'[0]
|
||||
or '0'[0]<=s[ptr] and s[ptr]<='9'[0]))
|
||||
{
|
||||
or '0'[0]<=s[ptr] and s[ptr]<='9'[0])){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
append(token,num(number));
|
||||
gen(num(number));
|
||||
return;
|
||||
}elsif(ptr<len and chr(s[ptr])=='o'){
|
||||
ptr+=1;
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='7'[0])){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
gen(num(number));
|
||||
return;
|
||||
}
|
||||
elsif(ptr<len and chr(s[ptr])=='o')
|
||||
{
|
||||
ptr+=1;
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='7'[0]))
|
||||
{
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
append(token,num(number));
|
||||
return;
|
||||
}
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
|
||||
{
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
if(ptr<len and chr(s[ptr])=='.')
|
||||
{
|
||||
if(ptr<len and chr(s[ptr])=='.'){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
|
||||
{
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
}
|
||||
if(chr(s[ptr])=='e' or chr(s[ptr])=='E')
|
||||
{
|
||||
if(chr(s[ptr])=='e' or chr(s[ptr])=='E'){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
if(chr(s[ptr])=='-' or chr(s[ptr])=='+')
|
||||
{
|
||||
if(chr(s[ptr])=='-' or chr(s[ptr])=='+'){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0]))
|
||||
{
|
||||
while(ptr<len and ('0'[0]<=s[ptr] and s[ptr]<='9'[0])){
|
||||
number~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
@@ -125,49 +116,45 @@ var lexer=func(file)
|
||||
var last_c=chr(number[-1]);
|
||||
if(last_c=='.' or last_c=='e' or last_c=='E' or last_c=='-' or last_c=='+')
|
||||
println("error number: ",number);
|
||||
append(token,num(number));
|
||||
gen(num(number));
|
||||
},
|
||||
opr_gen:func()
|
||||
{
|
||||
opr_gen:func(){
|
||||
var c=chr(s[ptr]);
|
||||
if(c=='+' or c=='-' or c=='~' or c=='/' or c=='*' or c=='>' or c=='<' or c=='!' or c=='=')
|
||||
{
|
||||
if(c=='+' or c=='-' or c=='~' or c=='/' or c=='*' or c=='>' or c=='<' or c=='!' or c=='='){
|
||||
var tmp=c;
|
||||
ptr+=1;
|
||||
if(ptr<len and chr(s[ptr])=='=')
|
||||
{
|
||||
if(ptr<len and chr(s[ptr])=='='){
|
||||
tmp~=chr(s[ptr]);
|
||||
ptr+=1;
|
||||
}
|
||||
append(token,tmp);
|
||||
gen(tmp);
|
||||
return;
|
||||
}
|
||||
elsif(c=='.')
|
||||
{
|
||||
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.')
|
||||
{
|
||||
append(token,"...");
|
||||
}elsif(c=='.'){
|
||||
if(ptr+2<len and chr(s[ptr+1])=='.' and chr(s[ptr+2])=='.'){
|
||||
gen("...");
|
||||
ptr+=3;
|
||||
}
|
||||
else
|
||||
{
|
||||
append(token,".");
|
||||
else{
|
||||
gen(".");
|
||||
ptr+=1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
elsif(c!=' ' and c!='\t' and c!='\n' and c!='\r' and s[ptr]>0)
|
||||
append(token,c);
|
||||
gen(c);
|
||||
ptr+=1;
|
||||
return;
|
||||
},
|
||||
compile:func()
|
||||
{
|
||||
while(ptr<len)
|
||||
{
|
||||
compile:func(){
|
||||
line=1;
|
||||
while(ptr<len){
|
||||
var c=s[ptr];
|
||||
if(c=='#'[0])
|
||||
me.jmp_note();
|
||||
elsif(c=='\n'[0]){
|
||||
line+=1;
|
||||
ptr+=1;
|
||||
}
|
||||
elsif('a'[0]<=c and c<='z'[0]
|
||||
or 'A'[0]<=c and c<='Z'[0]
|
||||
or c=='_'[0])
|
||||
@@ -185,8 +172,7 @@ var lexer=func(file)
|
||||
};
|
||||
}
|
||||
|
||||
var lex=lexer("test/props.nas");
|
||||
var lex=lexer("stl/fg_env.nas");
|
||||
lex.compile();
|
||||
foreach(var tok;lex.get_token())
|
||||
print(tok,' ');
|
||||
print('\n');
|
||||
print('(',tok.line,' | ',tok.token,')\n');
|
||||
@@ -1,20 +1,15 @@
|
||||
import("lib.nas");
|
||||
|
||||
var map=nil;
|
||||
|
||||
var check=func(x,y)
|
||||
{
|
||||
var check=func(x,y){
|
||||
if(x>14) x=0;
|
||||
if(y>19) y=0;
|
||||
return map[x][y];
|
||||
}
|
||||
|
||||
var new_map=func()
|
||||
{
|
||||
var new_map=func(){
|
||||
var tmp=[];
|
||||
setsize(tmp,15);
|
||||
forindex(var i;tmp)
|
||||
{
|
||||
forindex(var i;tmp){
|
||||
tmp[i]=[];
|
||||
setsize(tmp[i],20);
|
||||
}
|
||||
@@ -23,35 +18,32 @@ var new_map=func()
|
||||
|
||||
var prt=func()
|
||||
{
|
||||
var s='';
|
||||
foreach(var line;map)
|
||||
{
|
||||
var s='\e[0;0H';
|
||||
foreach(var line;map){
|
||||
foreach(var elem;line)
|
||||
s~=elem~' ';
|
||||
s~='\n';
|
||||
}
|
||||
if(os.platform()=="windows")
|
||||
system("cls");
|
||||
else
|
||||
system("clear");
|
||||
print(s);
|
||||
unix.sleep(1/144);
|
||||
}
|
||||
|
||||
func()
|
||||
{
|
||||
func(){
|
||||
# enable ANSI escape sequence
|
||||
if(os.platform()=="windows")
|
||||
system("color");
|
||||
print("\ec");
|
||||
rand(time(0));
|
||||
map=new_map();
|
||||
forindex(var i;map)
|
||||
forindex(var j;map[i])
|
||||
map[i][j]=rand()>0.7?'O':' ';
|
||||
var calc=[[0,1],[1,0],[0,-1],[-1,0],[1,1],[1,-1],[-1,-1],[-1,1]];
|
||||
for(var r=0;r<100;r+=1)
|
||||
{
|
||||
for(var r=0;r<100;r+=1){
|
||||
prt(map);
|
||||
var tmp=new_map();
|
||||
for(var i=0;i<15;i+=1)
|
||||
for(var j=0;j<20;j+=1)
|
||||
{
|
||||
for(var j=0;j<20;j+=1){
|
||||
var cnt=0;
|
||||
foreach(var k;calc)
|
||||
cnt+=(check(i+k[0],j+k[1])=='O');
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import("lib.nas");
|
||||
for(;;)break;
|
||||
for(;;)
|
||||
{
|
||||
for(;;){
|
||||
var a=1;
|
||||
break;
|
||||
}
|
||||
@@ -11,21 +9,18 @@ for(var i=1;i<10;i+=1)print(i,'\n');
|
||||
|
||||
while(1)break;
|
||||
var j=0;
|
||||
while(j<10)
|
||||
{
|
||||
while(j<10){
|
||||
print(j,'\n');
|
||||
j+=1;
|
||||
}
|
||||
|
||||
forindex(var j;[0,1,2,3])print(j,'\n');
|
||||
forindex(var j;[0,1,2,3])
|
||||
{
|
||||
forindex(var j;[0,1,2,3]){
|
||||
var a=j;
|
||||
print(a*a,'\n');
|
||||
}
|
||||
foreach(var j;[0,1,2,3])print([0,1,2,3][j],'\n');
|
||||
foreach(var j;[0,1,2,3])
|
||||
{
|
||||
foreach(var j;[0,1,2,3]){
|
||||
var a=[0,1,2,3][j];
|
||||
print(a*a-1,'\n');
|
||||
}
|
||||
|
||||
5566
test/mandel.nas
@@ -1,21 +1,16 @@
|
||||
import("lib.nas");
|
||||
var (yMin,yMax,xMin,xMax,line)=(-0.2,0.2,-1.5,-1.0,"");
|
||||
var (yDel,xDel)=(yMax-yMin,xMax-xMin);
|
||||
for(var yPixel=0;yPixel<24;yPixel+=1)
|
||||
{
|
||||
for(var yPixel=0;yPixel<24;yPixel+=1){
|
||||
var y=(yPixel/24)*yDel+yMin;
|
||||
for(var xPixel=0;xPixel<80;xPixel+=1)
|
||||
{
|
||||
for(var xPixel=0;xPixel<80;xPixel+=1){
|
||||
var x=(xPixel/80)*xDel+xMin;
|
||||
var pixel=" ";
|
||||
var (x0,y0)=(x,y);
|
||||
for(var iter=0;iter<80;iter+=1)
|
||||
{
|
||||
for(var iter=0;iter<80;iter+=1){
|
||||
var x1=(x0*x0)-(y0*y0)+x;
|
||||
var y1=2*x0*y0+y;
|
||||
(x0,y0)=(x1,y1);
|
||||
if((x0*x0)+(y0*y0)>4)
|
||||
{
|
||||
if((x0*x0)+(y0*y0)>4){
|
||||
pixel=chr(" .:;+=xX$&"[iter/8]);
|
||||
break;
|
||||
}
|
||||
|
||||
172
test/mcpu.nas
Normal file
@@ -0,0 +1,172 @@
|
||||
var inst={
|
||||
inst_stop:0,
|
||||
inst_mov_reg_reg:1,
|
||||
inst_mov_reg_imm_low:2,
|
||||
inst_mov_reg_imm_high:3,
|
||||
inst_add:4,
|
||||
inst_sub:5,
|
||||
inst_mult:6,
|
||||
inst_div:7,
|
||||
inst_and:8,
|
||||
inst_or:9,
|
||||
inst_xor:10,
|
||||
inst_not:11,
|
||||
inst_nand:12,
|
||||
inst_shl:13,
|
||||
inst_shr:14,
|
||||
inst_jmp:15,
|
||||
inst_jt:16,
|
||||
inst_jf:17,
|
||||
inst_les:18,
|
||||
inst_grt:19,
|
||||
inst_leq:20,
|
||||
inst_geq:21,
|
||||
inst_eq:22,
|
||||
inst_in:23,
|
||||
inst_out:24
|
||||
};
|
||||
|
||||
var hex=func(){
|
||||
var vec=[];
|
||||
foreach(var i;['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'])
|
||||
foreach(var j;['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'])
|
||||
append(vec,i~j);
|
||||
return func(n){
|
||||
return vec[n];
|
||||
}
|
||||
}();
|
||||
|
||||
var hex32=func(n){
|
||||
return hex(bits.u32_and(n/math.pow(2,24),0xff))~
|
||||
hex(bits.u32_and(n/math.pow(2,16),0xff))~
|
||||
hex(bits.u32_and(n/math.pow(2,8),0xff))~
|
||||
hex(bits.u32_and(n,0xff));
|
||||
}
|
||||
|
||||
var machine=func(disk_file){
|
||||
|
||||
var reg=[];
|
||||
var reg_size=32;
|
||||
var pc=0;
|
||||
var ir=[0,0,0,0]; # 32 bit instruction word
|
||||
var mem=[];
|
||||
var mem_size=1024*1024*1; # memory size, byte
|
||||
var init=func(){
|
||||
println("[",os.time(),"] init ",reg_size," registers.");
|
||||
setsize(reg,reg_size); # 8 bit address wire
|
||||
for(var i=0;i<reg_size;i+=1)
|
||||
reg[i]=0;
|
||||
println("[",os.time(),"] init memory, memory size: ",mem_size/1024/1024,"MB.");
|
||||
setsize(mem,mem_size);
|
||||
for(var i=0;i<mem_size;i+=1)
|
||||
mem[i]=0;
|
||||
println("[",os.time(),"] init completed.");
|
||||
}
|
||||
init();
|
||||
var load=func(){
|
||||
var vec=split(" ",disk_file);
|
||||
println("[",os.time(),"] loading boot from disk: ",size(vec)," byte.");
|
||||
forindex(var i;vec)
|
||||
mem[i]=int("0x"~vec[i]);
|
||||
println("[",os.time(),"] loading complete.");
|
||||
}
|
||||
load();
|
||||
var ctx_info=func(){
|
||||
var cnt=0;
|
||||
println("pc : 0x",hex32(pc));
|
||||
println("instr : 0x",hex(ir[0]),hex(ir[1]),hex(ir[2]),hex(ir[3]));
|
||||
for(var i=0;i<reg_size;i+=1){
|
||||
print("reg[",hex(i),"]: 0x",hex32(reg[i])," ");
|
||||
if(cnt==3){
|
||||
print("\n");
|
||||
cnt=0;
|
||||
}else{
|
||||
cnt+=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
var exec=func(info=1){
|
||||
while(1){
|
||||
ir=[mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]];
|
||||
if(info)ctx_info();
|
||||
var op=ir[0];
|
||||
if(op==inst.inst_stop){
|
||||
break;
|
||||
}elsif(op==inst.inst_mov_reg_reg){
|
||||
reg[ir[1]]=reg[ir[2]];
|
||||
}elsif(op==inst.inst_mov_reg_imm_low){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[1]],0xffff0000);
|
||||
reg[ir[1]]=bits.u32_or(reg[ir[1]],ir[2]*math.pow(2,8)+ir[3]);
|
||||
}elsif(op==inst.inst_mov_reg_imm_high){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[1]],0x0000ffff);
|
||||
reg[ir[1]]=bits.u32_or(reg[ir[1]],ir[2]*math.pow(2,24)+ir[3]*math.pow(2,16));
|
||||
}elsif(op==inst.inst_add){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]+reg[ir[3]],0xffffffff);
|
||||
}elsif(op==inst.inst_sub){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]-reg[ir[3]],0xffffffff);
|
||||
}elsif(op==inst.inst_mult){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]*reg[ir[3]],0xffffffff);
|
||||
}elsif(op==inst.inst_div){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]/reg[ir[3]],0xffffffff);
|
||||
}elsif(op==inst.inst_and){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]],reg[ir[3]]);
|
||||
}elsif(op==inst.inst_or){
|
||||
reg[ir[1]]=bits.u32_or(reg[ir[2]],reg[ir[3]]);
|
||||
}elsif(op==inst.inst_xor){
|
||||
reg[ir[1]]=bits.u32_xor(reg[ir[2]],reg[ir[3]]);
|
||||
}elsif(op==inst.inst_not){
|
||||
reg[ir[1]]=bits.u32_not(reg[ir[2]]);
|
||||
}elsif(op==inst.inst_nand){
|
||||
reg[ir[1]]=bits.u32_nand(reg[ir[2]],reg[ir[3]]);
|
||||
}elsif(op==inst.inst_shl){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]*math.pow(2,reg[ir[3]]),0xffffffff);
|
||||
}elsif(op==inst.inst_shr){
|
||||
reg[ir[1]]=bits.u32_and(reg[ir[2]]/math.pow(2,reg[ir[3]]),0xffffffff);
|
||||
}elsif(op==inst.inst_jmp){
|
||||
pc=reg[ir[1]];
|
||||
}elsif(op==inst.inst_jt){
|
||||
pc=reg[ir[1]]?reg[ir[2]]:pc;
|
||||
}elsif(op==inst.inst_jf){
|
||||
pc=reg[ir[1]]?pc:reg[ir[2]];
|
||||
}elsif(op==inst.inst_les){
|
||||
reg[ir[1]]=reg[ir[2]]<reg[ir[3]];
|
||||
}elsif(op==inst.inst_grt){
|
||||
reg[ir[1]]=reg[ir[2]]>reg[ir[3]];
|
||||
}elsif(op==inst.inst_leq){
|
||||
reg[ir[1]]=reg[ir[2]]<=reg[ir[3]];
|
||||
}elsif(op==inst.inst_geq){
|
||||
reg[ir[1]]=reg[ir[2]]>=reg[ir[3]];
|
||||
}elsif(op==inst.inst_eq){
|
||||
reg[ir[1]]=reg[ir[2]]==reg[ir[3]];
|
||||
}elsif(op==inst.inst_in){
|
||||
reg[0]=0; # unfinished
|
||||
}elsif(op==inst.inst_out){
|
||||
println("reg[",ir[1],"]: 0x",hex32(reg[ir[1]]));
|
||||
}
|
||||
pc+=4;
|
||||
}
|
||||
};
|
||||
return {exec:exec};
|
||||
}(
|
||||
hex(inst.inst_mov_reg_imm_high)~" 01 ca fe "~ # reg[1]=0xcafe0000
|
||||
hex(inst.inst_mov_reg_imm_low)~" 01 ba be "~ # reg[1]=0xcafebabe
|
||||
hex(inst.inst_out)~" 01 00 00 "~ # output reg[1]
|
||||
hex(inst.inst_mov_reg_imm_low)~" 02 00 20 "~ # reg[2]=0x10
|
||||
hex(inst.inst_jmp)~" 02 00 00 "~ # jmp *reg[2]
|
||||
hex(inst.inst_out)~" 01 00 00 "~
|
||||
hex(inst.inst_out)~" 01 00 00 "~
|
||||
hex(inst.inst_out)~" 01 00 00 "~
|
||||
hex(inst.inst_out)~" 01 00 00 "~
|
||||
hex(inst.inst_out)~" 01 00 00 "~ # should jump here
|
||||
hex(inst.inst_jf)~" 01 02 00 "~ # should not jump
|
||||
hex(inst.inst_mov_reg_imm_low)~" 03 00 04 "~ # reg[3]=4
|
||||
hex(inst.inst_mov_reg_imm_low)~" 04 00 00 "~ # reg[4]=0
|
||||
hex(inst.inst_out)~" 04 00 00 "~ # output reg[4]
|
||||
hex(inst.inst_grt)~" 00 04 03 "~ # reg[0]=reg[4]>reg[3]
|
||||
hex(inst.inst_mov_reg_imm_low)~" 05 00 30 "~ # reg[5]=0x2c
|
||||
hex(inst.inst_mov_reg_imm_low)~" 06 00 01 "~ # reg[6]=1
|
||||
hex(inst.inst_add)~" 04 04 06 "~ # reg[4]+=reg[6]
|
||||
hex(inst.inst_jf)~" 00 05 00 " # jmp *reg[5] if reg[0]!=true
|
||||
);
|
||||
|
||||
machine.exec(0);
|
||||
216
test/md5.nas
Normal file
@@ -0,0 +1,216 @@
|
||||
var check=func(x){
|
||||
if(x<0x100000000)
|
||||
return x;
|
||||
return x-floor(x/0x100000000)*0x100000000;
|
||||
}
|
||||
var u32_bits_and=func(x,y){
|
||||
return bits.u32_and(check(x),check(y));
|
||||
}
|
||||
var u32_bits_or=func(x,y){
|
||||
return bits.u32_or(check(x),check(y));
|
||||
}
|
||||
var u32_bits_xor=func(x,y){
|
||||
return bits.u32_xor(check(x),check(y));
|
||||
}
|
||||
var u32_bits_not=func(x){
|
||||
return bits.u32_not(check(x));
|
||||
}
|
||||
|
||||
var hex32str=func(){
|
||||
var ch=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];
|
||||
var tbl=[];
|
||||
setsize(tbl,256);
|
||||
for(var i=0;i<16;i+=1){
|
||||
for(var j=0;j<16;j+=1)
|
||||
tbl[i*16+j]=ch[i]~ch[j];
|
||||
}
|
||||
return func(num){
|
||||
var res="";
|
||||
for(var i=0;i<4;i+=1){
|
||||
res~=tbl[u32_bits_and(num,0xff)];
|
||||
num=floor(num/256);
|
||||
}
|
||||
return res;
|
||||
};
|
||||
}();
|
||||
|
||||
var _md5=func(){
|
||||
var 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
|
||||
];
|
||||
|
||||
var 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
|
||||
];
|
||||
var idx=[
|
||||
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
|
||||
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
|
||||
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
|
||||
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9
|
||||
];
|
||||
|
||||
var l=func(num,cx){
|
||||
for(var i=0;i<cx;i+=1){
|
||||
num=check(num*2);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
var r=func(num,cx){
|
||||
num=check(num);
|
||||
for(var i=0;i<cx;i+=1){
|
||||
num=num/2;
|
||||
}
|
||||
return floor(num);
|
||||
}
|
||||
var rol=func(num,cx){
|
||||
return u32_bits_or(l(num,cx),r(num,32-cx));
|
||||
}
|
||||
# round 1
|
||||
var F=func(x,y,z){
|
||||
return u32_bits_or(
|
||||
u32_bits_and(x,y),
|
||||
u32_bits_and(u32_bits_not(x),z)
|
||||
);
|
||||
}
|
||||
# round 2
|
||||
var G=func(x,y,z){
|
||||
return u32_bits_or(
|
||||
u32_bits_and(x,z),
|
||||
u32_bits_and(y,u32_bits_not(z))
|
||||
);
|
||||
}
|
||||
# round 3
|
||||
var H=func(x,y,z){
|
||||
return u32_bits_xor(u32_bits_xor(x,y),z);
|
||||
}
|
||||
# round 4
|
||||
var I=func(x,y,z){
|
||||
return u32_bits_xor(
|
||||
y,
|
||||
u32_bits_or(x,u32_bits_not(z))
|
||||
);
|
||||
}
|
||||
var functions=[
|
||||
F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
|
||||
G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,G,
|
||||
H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,
|
||||
I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I
|
||||
];
|
||||
|
||||
return func(s){
|
||||
var (s_size,len,res)=(size(s),size(s)*8,[]);
|
||||
setsize(res,s_size);
|
||||
for(var i=0;i<s_size;i+=1){
|
||||
res[i]=s[i];
|
||||
}
|
||||
# +------len------+--1~512--+--64--+
|
||||
# | text | fill | size |
|
||||
# +---------------+---------+------+ N*512 bit
|
||||
var (mod,res_size)=(s_size-floor(s_size/64)*64,0);
|
||||
if(mod==56){
|
||||
res_size=s_size+64;
|
||||
}elsif(mod<56){
|
||||
res_size=floor(s_size/64)*64+56;
|
||||
}elsif(mod>56){
|
||||
res_size=floor(s_size/64)*64+120; # 512+448=960 960/8=120
|
||||
}
|
||||
setsize(res,res_size);
|
||||
res[s_size]=0x80;
|
||||
for(var i=s_size+1;i<res_size;i+=1)
|
||||
res[i]=0;
|
||||
|
||||
# little endian
|
||||
setsize(res,size(res)+8);
|
||||
var (s_size,lower32,higher32)=(size(res),check(len),check(len/math.pow(2,32)));
|
||||
for(var i=4;i>0;i-=1){
|
||||
res[s_size-4-i]=floor(lower32-floor(lower32/256)*256);
|
||||
lower32=floor(lower32/256);
|
||||
}
|
||||
for(var i=4;i>0;i-=1){
|
||||
res[s_size-i]=floor(higher32-floor(higher32/256)*256);
|
||||
higher32=floor(higher32/256);
|
||||
}
|
||||
|
||||
# 1 block=>16 uint32=>64 byte=>512 bit
|
||||
# because using double to discribe number
|
||||
# this may only work when string's length is under 1<<51
|
||||
var tmp=[];
|
||||
setsize(tmp,size(res)/4);
|
||||
for(var i=0;i<size(res);i+=4){
|
||||
tmp[i/4]=res[i+3]*math.pow(2,24)+
|
||||
res[i+2]*math.pow(2,16)+
|
||||
res[i+1]*math.pow(2,8)+
|
||||
res[i];
|
||||
}
|
||||
res=tmp;
|
||||
|
||||
var A=0x67452301;
|
||||
var B=0xefcdab89;
|
||||
var C=0x98badcfe;
|
||||
var D=0x10325476;
|
||||
|
||||
res_size=size(res);
|
||||
for(var i=0;i<res_size;i+=16){
|
||||
var (f,a,b,c,d)=(0,A,B,C,D);
|
||||
for(var j=0;j<64;j+=1){
|
||||
f=functions[j](b,c,d);
|
||||
(a,b,c,d)=(d,check(b+rol(a+f+K[j]+res[i+idx[j]],S[j])),b,c);
|
||||
}
|
||||
(A,B,C,D)=(check(a+A),check(b+B),check(c+C),check(d+D));
|
||||
}
|
||||
return hex32str(A)~hex32str(B)~hex32str(C)~hex32str(D);
|
||||
};
|
||||
}();
|
||||
|
||||
# check if md5 runs correctly
|
||||
var md5check=func(){
|
||||
var test_set=[
|
||||
"md5",
|
||||
"github.com",
|
||||
"helloworld",
|
||||
"abc",
|
||||
"https://www.github.com/ValKmjolnir/Nasal-Interpreter",
|
||||
"https://github.com/andyross/nasal",
|
||||
"var (lower32,higher32)=(check(len),check(len/math.pow(2,32)));",
|
||||
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
|
||||
"let the bass kick",
|
||||
"f499377c9ae8454c6c8a21ddba7f00de5817fccdc611333ed004d826abb17f4efdacad297f72956e0619002cecffc8e3d18d9b03b082f3cb114bc29173954043",
|
||||
"you are our last hope"
|
||||
];
|
||||
var result=[
|
||||
"1bc29b36f623ba82aaf6724fd3b16718",
|
||||
"99cd2175108d157588c04758296d1cfc",
|
||||
"fc5e038d38a57032085441e7fe7010b0",
|
||||
"900150983cd24fb0d6963f7d28e17f72",
|
||||
"6b3a7bbc2240046c4fb1b0b3a4ed8181",
|
||||
"14a6afca5f3a7b239c56b5a9678c428e",
|
||||
"f499377c9ae8454c6c8a21ddba7f00de",
|
||||
"fdacad297f72956e0619002cecffc8e3",
|
||||
"16eadccb9799dfb4c1ca512f40638bbb",
|
||||
"a7916c5ce54e73b7ddf6a286b36d976d",
|
||||
"ec6d5b197ba019db23c719112f3f70b7"
|
||||
];
|
||||
forindex(var i;test_set){
|
||||
var res=_md5(test_set[i]);
|
||||
if(cmp(res,result[i]))
|
||||
println(
|
||||
"md5 cannot work:\n",
|
||||
" test \""~test_set[i]~"\"\n",
|
||||
" result \""~result[i]~"\"\n",
|
||||
" but get \""~res~"\"\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# check when loading md5.nas
|
||||
md5check();
|
||||
101
test/md5compare.nas
Normal file
@@ -0,0 +1,101 @@
|
||||
import.test.md5;
|
||||
import.stl.process_bar;
|
||||
srand();
|
||||
|
||||
var compare=func(){
|
||||
var ch=[
|
||||
"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","+",
|
||||
"_","*","/","\'","\"",".",",",";",":","<",">","!","@","#","$","%",
|
||||
"^","&","*","(",")","-","=","\\","|","[","]","{","}","`"," ","\t","?"
|
||||
];
|
||||
return func(begin,end){
|
||||
var byte=0;
|
||||
var total=end-begin;
|
||||
var timestamp=maketimestamp();
|
||||
timestamp.stamp();
|
||||
var bar=process_bar.high_resolution_bar(40);
|
||||
for(var i=begin;i<end;i+=1){
|
||||
var s="";
|
||||
for(var j=0;j<i;j+=1){
|
||||
s~=ch[rand()*size(ch)];
|
||||
}
|
||||
byte+=size(s);
|
||||
var res=md5(s);
|
||||
if(cmp(res,_md5(s))){
|
||||
die("error: "~str(i));
|
||||
}
|
||||
print(" ",bar.bar((i-begin+1)/total)," (",i-begin+1,"/",total,")\t",res," byte: ",byte," time: ",timestamp.elapsedMSec()," \r");
|
||||
}
|
||||
print("\n");
|
||||
};
|
||||
}();
|
||||
|
||||
var filechecksum=func(){
|
||||
var files=[
|
||||
"./stl/fg_env.nas", "./stl/file.nas",
|
||||
"./stl/json.nas",
|
||||
"./stl/lib.nas", "./stl/list.nas",
|
||||
"./stl/log.nas", "./stl/module.nas",
|
||||
"./stl/padding.nas", "./stl/process_bar.nas",
|
||||
"./stl/queue.nas", "./stl/result.nas",
|
||||
"./stl/sort.nas", "./stl/stack.nas",
|
||||
"./stl/string.nas",
|
||||
"./test/ascii-art.nas", "./test/auto_crash.nas",
|
||||
"./test/bf.nas", "./test/bfconvertor.nas",
|
||||
"./test/bfs.nas", "./test/bigloop.nas",
|
||||
"./test/bp.nas", "./test/calc.nas",
|
||||
"./test/choice.nas", "./test/class.nas",
|
||||
"./test/coroutine.nas", "./test/diff.nas",
|
||||
"./test/exception.nas", "./test/fib.nas",
|
||||
"./test/filesystem.nas", "./test/hexdump.nas",
|
||||
"./test/httptest.nas", "./test/json.nas",
|
||||
"./test/leetcode1319.nas", "./test/lexer.nas",
|
||||
"./test/life.nas", "./test/loop.nas",
|
||||
"./test/mandel.nas", "./test/mandelbrot.nas",
|
||||
"./test/mcpu.nas",
|
||||
"./test/md5.nas", "./test/md5compare.nas",
|
||||
"./test/module_test.nas", "./test/nasal_test.nas",
|
||||
"./test/occupation.nas", "./test/pi.nas",
|
||||
"./test/ppmgen.nas", "./test/prime.nas",
|
||||
"./test/qrcode.nas", "./test/quick_sort.nas",
|
||||
"./test/scalar.nas", "./test/snake.nas",
|
||||
"./test/tetris.nas", "./test/trait.nas",
|
||||
"./test/turingmachine.nas", "./test/utf8chk.nas",
|
||||
"./test/watchdog.nas", "./test/wavecollapse.nas",
|
||||
"./test/word_collector.nas",
|
||||
"./test/ycombinator.nas", "LICENSE",
|
||||
"main.cpp", "makefile",
|
||||
"nasal_ast.h", "nasal_builtin.h",
|
||||
"nasal_codegen.h", "nasal_dbg.h",
|
||||
"nasal_err.h", "nasal_gc.h",
|
||||
"nasal_import.h", "nasal_lexer.h",
|
||||
"nasal_opt.h", "nasal_parse.h",
|
||||
"nasal_vm.h", "nasal.ebnf",
|
||||
"nasal.h", "README.md"
|
||||
];
|
||||
var byte=0;
|
||||
var total=size(files);
|
||||
var timestamp=maketimestamp();
|
||||
timestamp.stamp();
|
||||
var bar=process_bar.high_resolution_bar(40);
|
||||
forindex(var i;files){
|
||||
var f=io.fin(files[i]);
|
||||
var res=md5(f);
|
||||
byte+=size(f);
|
||||
if(cmp(res,_md5(f))){
|
||||
die("error: "~files[i]);
|
||||
}
|
||||
print(" ",bar.bar((i+1)/total)," (",i+1,"/",total,")\t",res," byte: ",byte," time: ",timestamp.elapsedMSec()," \r");
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
var randomchecksum=func(){
|
||||
for(var i=0;i<4096;i+=512)
|
||||
compare(i,i+512);
|
||||
}
|
||||
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
filechecksum();
|
||||
randomchecksum();
|
||||
@@ -1,17 +1,44 @@
|
||||
import("lib.nas");
|
||||
|
||||
var libfib=func(){
|
||||
var dl=dylib.dlopen("./module/libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
var fib=dylib.dlsym(dl,"fib");
|
||||
var qfib=dylib.dlsym(dl,"quick_fib");
|
||||
var call=dylib.dlcall;
|
||||
return
|
||||
{
|
||||
fib: func(x){return call(fib,x)},
|
||||
qfib:func(x){return call(qfib,x)}
|
||||
};
|
||||
var (dd,fib,qfib)=(nil,nil,nil);
|
||||
return {
|
||||
open:func(){
|
||||
if(dd==nil){
|
||||
dd=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
|
||||
fib=dylib.dlsym(dd,"fib");
|
||||
qfib=dylib.dlsym(dd,"quick_fib");
|
||||
}else{
|
||||
println("[info ] already loaded.");
|
||||
}
|
||||
},
|
||||
close:func(){
|
||||
if(dd==nil){
|
||||
println("[error ] already closed.");
|
||||
return;
|
||||
}
|
||||
dylib.dlclose(dd);
|
||||
(dd,fib,qfib)=(nil,nil,nil);
|
||||
},
|
||||
fib:func(x){
|
||||
if(fib!=nil)
|
||||
return dylib.dlcall(fib,x);
|
||||
println("[error ] cannot call fib.");
|
||||
return nil;
|
||||
},
|
||||
qfib:func(x){
|
||||
if(qfib!=nil)
|
||||
return dylib.dlcall(qfib,x);
|
||||
println("[error ] cannot call qfib.");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
println(libfib);
|
||||
println(libfib.fib(29));
|
||||
println(libfib.qfib(29));
|
||||
println("[keys ] ",keys(libfib));
|
||||
libfib.open();
|
||||
libfib.open();
|
||||
println("[result] ",libfib.fib(40));
|
||||
println("[result] ",libfib.qfib(40));
|
||||
libfib.close();
|
||||
println("[result] ",libfib.fib(40));
|
||||
println("[result] ",libfib.qfib(40));
|
||||
libfib.close();
|
||||
@@ -1,6 +1,5 @@
|
||||
# This is written for Nasal Intepreter
|
||||
# Sidi Liang
|
||||
import("lib.nas");
|
||||
var w = 1;
|
||||
var x = "hello";
|
||||
var f = func(){
|
||||
@@ -45,10 +44,10 @@ println(z.numb);#//1
|
||||
println(z.listt[2][1]);#//hello
|
||||
println(z1.hashh.listt[2][1]);#//hello
|
||||
println(y2[3].hashh.listt[2][1]);#//hello
|
||||
println(f);#//func(...){...}
|
||||
println(f);#//func(..){..}
|
||||
f();#//f is called
|
||||
println(z.funcc);#//func(...){...}
|
||||
println(z.funcc);#//func(..){..}
|
||||
z.funcc();#//f is called
|
||||
println(z.funcccall);#//func(...){...}
|
||||
println(z.funcccall);#//func(..){..}
|
||||
z2.listt2[3].hashh.funcc();#//f is called
|
||||
println(y1[f2()][w]);#//hello
|
||||
69
test/occupation.nas
Normal file
@@ -0,0 +1,69 @@
|
||||
import.stl.process_bar;
|
||||
import.module.libkey;
|
||||
|
||||
var cpu_stat=func(){
|
||||
var cpu=split("\n",io.fin("/proc/stat"))[0];
|
||||
cpu=split(" ",cpu);
|
||||
cpu={
|
||||
name:cpu[0],
|
||||
user:cpu[1],
|
||||
nice:cpu[2],
|
||||
system:cpu[3],
|
||||
idle:cpu[4],
|
||||
iowait:cpu[5],
|
||||
irq:cpu[6],
|
||||
softirq:cpu[7],
|
||||
};
|
||||
return cpu;
|
||||
}
|
||||
|
||||
var cpu_occupation=func(){
|
||||
while(1){
|
||||
var cpu0=cpu_stat();
|
||||
for(var i=0;i<5;i+=1){
|
||||
unix.sleep(0.1);
|
||||
coroutine.yield(nil);
|
||||
}
|
||||
var cpu1=cpu_stat();
|
||||
var t0=cpu0.user+cpu0.nice+cpu0.system+cpu0.idle+cpu0.iowait+cpu0.irq+cpu0.softirq;
|
||||
var t1=cpu1.user+cpu1.nice+cpu1.system+cpu1.idle+cpu1.iowait+cpu1.irq+cpu1.softirq;
|
||||
var interval=cpu1.idle-cpu0.idle;
|
||||
coroutine.yield(t0==t1?0:(1-interval/(t1-t0))*100);
|
||||
}
|
||||
}
|
||||
|
||||
var mem_occupation=func(){
|
||||
var meminfo=split("\n",io.fin("/proc/meminfo"));
|
||||
var mem_res={};
|
||||
forindex(var i;meminfo){
|
||||
var tmp=split(" ",meminfo[i])[0:1];
|
||||
tmp[0]=substr(tmp[0],0,size(tmp[0])-1);
|
||||
mem_res[tmp[0]]=num(tmp[1]);
|
||||
}
|
||||
return mem_res;
|
||||
}
|
||||
|
||||
func(){
|
||||
if(os.platform()=="windows"){
|
||||
println("haven't supported yet.");
|
||||
return;
|
||||
}
|
||||
var co=coroutine.create(cpu_occupation);
|
||||
var bar=process_bar.high_resolution_bar(30);
|
||||
print("\ec");
|
||||
while(1){
|
||||
var mem=mem_occupation();
|
||||
var mem_occ=(mem.MemTotal-mem.MemFree)/mem.MemTotal*100;
|
||||
var cpu_occ=nil;
|
||||
while((cpu_occ=coroutine.resume(co)[0])==nil){
|
||||
var key=libkey.nonblock();
|
||||
if(key!=nil and chr(key)=="q")
|
||||
return;
|
||||
}
|
||||
println("\e[1;1H\e[1m Memory total(GB) : \e[0m\e[36m",mem.MemTotal/1024/1024,"\e[0m");
|
||||
println("\e[2;1H\e[1m Memory free(GB) : \e[0m\e[36m",mem.MemFree/1024/1024,"\e[0m");
|
||||
println("\e[3;1H\e[1m Memory occupation(%): \e[0m",mem_occ>60?"\e[91m":"\e[32m",bar.bar(mem_occ/100)~" ",mem_occ,"\e[0m ");
|
||||
println("\e[4;1H\e[1m CPU occupation(%) : \e[0m",cpu_occ>90?"\e[91m":"\e[32m",bar.bar(cpu_occ/100)~" ",cpu_occ,"\e[0m ");
|
||||
println("\e[5;1H Press 'q' to quit.");
|
||||
}
|
||||
}();
|
||||
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var (t,res)=(1,0);
|
||||
for(var m=1;m<4e6;m+=2)
|
||||
{
|
||||
|
||||
39
test/ppmgen.nas
Normal file
@@ -0,0 +1,39 @@
|
||||
import.stl.process_bar;
|
||||
|
||||
var ppm=func(filename,width,height,RGB){
|
||||
# P3 use ASCII number
|
||||
# P6 use binary character
|
||||
var fd=io.open(filename,"wb");
|
||||
io.write(fd,"P3\n"~width~" "~height~"\n255\n");
|
||||
for(var i=0;i<height;i+=1){
|
||||
for(var j=0;j<width;j+=1)
|
||||
io.write(fd,RGB(i,j));
|
||||
io.write(fd,"\n");
|
||||
}
|
||||
io.close(fd);
|
||||
}
|
||||
|
||||
var width=1280;
|
||||
var height=720;
|
||||
var bar=(os.platform()=="windows")?
|
||||
process_bar.bar(front:"sharp",back:"point",sep:"line",length:50):
|
||||
process_bar.high_resolution_bar(50);
|
||||
var f=func(i,j){
|
||||
var (yMin,yMax,xMin,xMax)=(-1.35,1.35,-3.3,1.5);
|
||||
var (yDel,xDel)=(yMax-yMin,xMax-xMin);
|
||||
var (y,x)=((i/height)*yDel+yMin,(j/width)*xDel+xMin);
|
||||
var (x0,y0)=(x,y);
|
||||
for(var iter=0;iter<25;iter+=1){
|
||||
var (x1,y1)=((x0*x0)-(y0*y0)+x,2*x0*y0+y);
|
||||
(x0,y0)=(x1,y1);
|
||||
if((x0*x0)+(y0*y0)>4){
|
||||
break;
|
||||
}
|
||||
}
|
||||
var progress=(i*width+j+1)/(width*height);
|
||||
print(bar.bar(progress)," ",progress*100,"% \r");
|
||||
iter=iter==25?255:int(iter/25*255);
|
||||
return iter~" "~iter~" "~iter~" ";
|
||||
}
|
||||
ppm("a.ppm",width,height,f);
|
||||
println("\nfinished.");
|
||||
@@ -1,5 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var is_prime=func(x){
|
||||
for(var i=2;i<x;i+=1)
|
||||
if(x/i==int(x/i))
|
||||
|
||||
577
test/props.nas
@@ -1,577 +0,0 @@
|
||||
##
|
||||
# Node class definition. The class methods simply wrap the
|
||||
# low level extension functions which work on a "ghost" handle to a
|
||||
# SGPropertyNode object stored in the _g field.
|
||||
#
|
||||
# Not all of the features of SGPropertyNode are supported. There is
|
||||
# no support for ties, obviously, as that wouldn't make much sense
|
||||
# from a Nasal context. The various get/set methods work only on the
|
||||
# local node, there is no equivalent of the "relative path" variants
|
||||
# available in C++; just use node.getNode(path).whatever() instead.
|
||||
#
|
||||
|
||||
##
|
||||
# Utility. Turns any ghosts it finds (either solo, or in an
|
||||
# array) into Node objects.
|
||||
#
|
||||
var wrap = func(node) {
|
||||
var argtype = typeof(node);
|
||||
if(argtype == "ghost") {
|
||||
return wrapNode(node);
|
||||
} elsif(argtype == "vector") {
|
||||
var v = node;
|
||||
var n = size(v);
|
||||
for(var i=0; i<n; i+=1) { v[i] = wrapNode(v[i]); }
|
||||
return v;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
var Node = {
|
||||
getNode : func wrap(_getNode(me._g, arg)),
|
||||
getParent : func wrap(_getParent(me._g, arg)),
|
||||
getChild : func wrap(_getChild(me._g, arg)),
|
||||
getChildren : func wrap(_getChildren(me._g, arg)),
|
||||
setChildren : func wrap(_setChildren(me._g, arg)),
|
||||
addChild : func wrap(_addChild(me._g, arg)),
|
||||
addChildren : func wrap(_addChildren(me._g, arg)),
|
||||
removeChild : func wrap(_removeChild(me._g, arg)),
|
||||
removeChildren : func wrap(_removeChildren(me._g, arg)),
|
||||
removeAllChildren: func wrap(_removeAllChildren(me._g, arg)),
|
||||
getAliasTarget : func wrap(_getAliasTarget(me._g, arg)),
|
||||
|
||||
getName : func _getName(me._g, arg),
|
||||
getIndex : func _getIndex(me._g, arg),
|
||||
getType : func _getType(me._g, arg),
|
||||
getAttribute : func _getAttribute(me._g, arg),
|
||||
setAttribute : func _setAttribute(me._g, arg),
|
||||
getValue : func _getValue(me._g, arg),
|
||||
setValue : func _setValue(me._g, arg),
|
||||
setValues : func _setValues(me._g, arg),
|
||||
setIntValue : func _setIntValue(me._g, arg),
|
||||
setBoolValue : func _setBoolValue(me._g, arg),
|
||||
setDoubleValue : func _setDoubleValue(me._g, arg),
|
||||
unalias : func _unalias(me._g, arg),
|
||||
alias : func(n) _alias(me._g, [isa(n, Node) ? n._g : n]),
|
||||
equals : func(n) _equals(me._g, [isa(n, Node) ? n._g : n]),
|
||||
clearValue : func _alias(me._g, [_globals()]) and me.unalias(),
|
||||
|
||||
getPath : func {
|
||||
var (name, index, parent) = (me.getName(), me.getIndex(), me.getParent());
|
||||
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;
|
||||
return !!val;
|
||||
},
|
||||
|
||||
remove : func {
|
||||
if((var p = me.getParent()) == nil) return nil;
|
||||
p.removeChild(me.getName(), me.getIndex());
|
||||
},
|
||||
};
|
||||
|
||||
##
|
||||
# Static constructor for a Node object. Accepts a Nasal hash
|
||||
# expression to initialize the object a-la setValues().
|
||||
#
|
||||
Node.new = func(values = nil) {
|
||||
var result = wrapNode(_new());
|
||||
if(typeof(values) == "hash")
|
||||
result.setValues(values);
|
||||
return result;
|
||||
}
|
||||
|
||||
##
|
||||
# Counter piece of setValues(). Returns a hash with all values
|
||||
# in the subtree. Nodes with same name are returned as vector,
|
||||
# where the original node indices are lost. The function should
|
||||
# only be used if all or almost all values are needed, and never
|
||||
# in performance-critical code paths. If it's called on a node
|
||||
# without children, then the result is equivalent to getValue().
|
||||
#
|
||||
Node.getValues = func {
|
||||
var children = me.getChildren();
|
||||
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]; }
|
||||
else {
|
||||
var nc = size(me.getChildren(name));
|
||||
numchld[name] = nc;
|
||||
if(nc > 1 and !contains(val, name)) val[name] = [];
|
||||
}
|
||||
if(nc > 1) append(val[name], c.getValues());
|
||||
else val[name] = c.getValues();
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
##
|
||||
# Initializes property if it's still undefined. First argument
|
||||
# is a property name/path. It can also be nil or an empty string,
|
||||
# in which case the node itself gets initialized, rather than one
|
||||
# of its children. Second argument is the default value. The third,
|
||||
# optional argument is a property type (one of "STRING", "DOUBLE",
|
||||
# "INT", or "BOOL"). If it is omitted, then "DOUBLE" is used for
|
||||
# numbers, and STRING for everything else. Returns the property
|
||||
# as props.Node. The fourth optional argument enforces a type if
|
||||
# non-zero.
|
||||
#
|
||||
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);
|
||||
else die("initNode(): unsupported type '" ~ type ~ "'");
|
||||
return prop;
|
||||
}
|
||||
|
||||
##
|
||||
# Useful debugging utility. Recursively dumps the full state of a
|
||||
# Node object to the console. Try binding "props.dump(props.globals)"
|
||||
# to a key for a fun hack.
|
||||
#
|
||||
var dump = func {
|
||||
if(size(arg) == 1) { prefix = ""; node = arg[0]; }
|
||||
else { prefix = arg[0]; node = arg[1]; }
|
||||
|
||||
index = node.getIndex();
|
||||
type = node.getType();
|
||||
name = node.getName();
|
||||
val = node.getValue();
|
||||
|
||||
if(val == nil) { val = "nil"; }
|
||||
name = prefix ~ name;
|
||||
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") {
|
||||
children = node.getChildren();
|
||||
foreach(c; children) { dump(name ~ "/", c); }
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
# Recursively copy property branch from source Node to
|
||||
# destination Node. Doesn't copy aliases. Copies attributes
|
||||
# if optional third argument is set and non-zero.
|
||||
#
|
||||
var copy = func(src, dest, attr = 0) {
|
||||
foreach(var c; src.getChildren()) {
|
||||
var name = c.getName() ~ "[" ~ c.getIndex() ~ "]";
|
||||
copy(src.getNode(name), dest.getNode(name, 1), attr);
|
||||
}
|
||||
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);
|
||||
else dest.setValue(val);
|
||||
if(attr) dest.setAttribute(src.getAttribute());
|
||||
}
|
||||
|
||||
##
|
||||
# Utility. Returns a new object with its superclass/parent set to the
|
||||
# Node object and its _g (ghost) field set to the specified object.
|
||||
# Nasal's literal syntax can be pleasingly terse. I like that. :)
|
||||
#
|
||||
var wrapNode = func(node) { { parents : [Node], _g : node } }
|
||||
|
||||
##
|
||||
# Global property tree. Set once at initialization. Is that OK?
|
||||
# Does anything ever call globals.set_props() from C++? May need to
|
||||
# turn this into a function if so.
|
||||
#
|
||||
var globals = wrapNode(_globals());
|
||||
|
||||
##
|
||||
# Shortcut for props.globals.getNode().
|
||||
#
|
||||
var getNode = func return call(props.globals.getNode, arg, props.globals);
|
||||
|
||||
##
|
||||
# Sets all indexed property children to a single value. arg[0]
|
||||
# specifies a property name (e.g. /controls/engines/engine), arg[1] a
|
||||
# path under each node of that name to set (e.g. "throttle"), arg[2]
|
||||
# is the value.
|
||||
#
|
||||
var setAll = func(base, child, value) {
|
||||
var node = props.globals.getNode(base);
|
||||
if(node == nil) return;
|
||||
var name = node.getName();
|
||||
node = node.getParent();
|
||||
if(node == nil) return;
|
||||
var children = node.getChildren();
|
||||
foreach(var c; children)
|
||||
if(c.getName() == name)
|
||||
c.getNode(child, 1).setValue(value);
|
||||
}
|
||||
|
||||
##
|
||||
# Turns about anything into a list of props.Nodes, including ghosts,
|
||||
# path strings, vectors or hashes containing, as well as functions
|
||||
# returning any of the former and in arbitrary nesting. This is meant
|
||||
# to be used in functions whose main purpose is to handle collections
|
||||
# of properties.
|
||||
#
|
||||
var nodeList = func {
|
||||
var list = [];
|
||||
foreach(var a; arg) {
|
||||
var t = typeof(a);
|
||||
if(isa(a, Node))
|
||||
append(list, a);
|
||||
elsif(t == "scalar")
|
||||
append(list, props.globals.getNode(a, 1));
|
||||
elsif(t == "vector")
|
||||
foreach(var i; a)
|
||||
list ~= nodeList(i);
|
||||
elsif(t == "hash")
|
||||
foreach(var i; keys(a))
|
||||
list ~= nodeList(a[i]);
|
||||
elsif(t == "func")
|
||||
list ~= nodeList(a());
|
||||
elsif(t == "ghost" and ghosttype(a) == "prop")
|
||||
append(list, wrapNode(a));
|
||||
else
|
||||
die("nodeList: invalid nil property");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
##
|
||||
# Compiles a <condition> property branch according to the rules
|
||||
# set out in $FG_ROOT/Docs/README.conditions into a Condition object.
|
||||
# The 'test' method of the returend object can be used to evaluate
|
||||
# the condition.
|
||||
# The function returns nil on error.
|
||||
#
|
||||
var compileCondition = func(p) {
|
||||
if(p == nil) return nil;
|
||||
if(!isa(p, Node)) p = props.globals.getNode(p);
|
||||
return _createCondition(p._g);
|
||||
}
|
||||
|
||||
##
|
||||
# Evaluates a <condition> property branch according to the rules
|
||||
# set out in $FG_ROOT/Docs/README.conditions. Undefined conditions
|
||||
# and a nil argument are "true". The function dumps the condition
|
||||
# branch and returns nil on error.
|
||||
#
|
||||
var condition = func(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;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var _cond_or = func(p) {
|
||||
foreach(var c; p.getChildren())
|
||||
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());
|
||||
printlog("alert", "condition: invalid operator ", n);
|
||||
dump(p);
|
||||
return nil;
|
||||
}
|
||||
|
||||
var _cond_cmp = func(p, op) {
|
||||
var left = p.getChild("property", 0, 0);
|
||||
if(left != nil) { left = getprop(left.getValue()); }
|
||||
else {
|
||||
printlog("alert", "condition: no left value");
|
||||
dump(p);
|
||||
return nil;
|
||||
}
|
||||
var right = p.getChild("property", 1, 0);
|
||||
if(right != nil) { right = getprop(right.getValue()); }
|
||||
else {
|
||||
right = p.getChild("value", 0, 0);
|
||||
if(right != nil) { right = right.getValue(); }
|
||||
else {
|
||||
printlog("alert", "condition: no right value");
|
||||
dump(p);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
if(left == nil or right == nil) {
|
||||
printlog("alert", "condition: comparing with nil");
|
||||
dump(p);
|
||||
return nil;
|
||||
}
|
||||
if(op < 0) return left < right;
|
||||
if(op > 0) return left > right;
|
||||
return left == right;
|
||||
}
|
||||
|
||||
##
|
||||
# Runs <binding> as described in $FG_ROOT/Docs/README.commands using
|
||||
# a given module by default, and returns 1 if fgcommand() succeeded,
|
||||
# or 0 otherwise. The module name won't override a <module> defined
|
||||
# in the binding.
|
||||
#
|
||||
var runBinding = func(node, 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;
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Property / object update manager
|
||||
#
|
||||
# - Manage updates when a value has changed more than a predetermined amount.
|
||||
# This class is designed to make updating displays (e.g. canvas), or
|
||||
# performing actions based on a property (or value in a hash) changing
|
||||
# by more than the preset amount.
|
||||
# This can make a significant improvement to performance compared to simply
|
||||
# redrawing a canvas in an update loop.
|
||||
# - Author : Richard Harrison (rjh@zaretto.com)
|
||||
#---------------------------------------------------------------------------*/
|
||||
|
||||
#example usage:
|
||||
# this is using the hashlist (which works well with an Emesary notification)
|
||||
# basically when the method is called it will call each section (in the lambda)
|
||||
# when the value changes by more than the amount specified as the second parameter.
|
||||
# It is possible to reference multiple elements from the hashlist in each FromHashList; if either
|
||||
# one changes then it will result in the lambda being called.
|
||||
#
|
||||
# obj.update_items = [
|
||||
# UpdateManager.FromHashList(["VV_x","VV_y"], 0.01, func(val)
|
||||
# {
|
||||
# obj.VV.setTranslation (val.VV_x, val.VV_y + pitch_offset);
|
||||
# }),
|
||||
# UpdateManager.FromHashList(["pitch","roll"], 0.025, func(hdp)
|
||||
# {
|
||||
# obj.ladder.setTranslation (0.0, hdp.pitch * pitch_factor+pitch_offset);
|
||||
# obj.ladder.setCenter (118,830 - hdp.pitch * pitch_factor-pitch_offset);
|
||||
# obj.ladder.setRotation (-hdp.roll_rad);
|
||||
# obj.roll_pointer.setRotation (hdp.roll_rad);
|
||||
# }),
|
||||
# props.UpdateManager.FromProperty("velocities/airspeed-kt", 0.01, func(val)
|
||||
# {
|
||||
# obj.ias_range.setTranslation(0, val * ias_range_factor);
|
||||
# }),
|
||||
# props.UpdateManager.FromPropertyHashList(["orientation/alpha-indicated-deg", "orientation/side-slip-deg"], 0.1, func(val)
|
||||
# {
|
||||
# obj.VV_x = val.property["orientation/side-slip-deg"].getValue()*10; # adjust for view
|
||||
# obj.VV_y = val.property["orientation/alpha-indicated-deg"].getValue()*10; # adjust for view
|
||||
# obj.VV.setTranslation (obj.VV_x, obj.VV_y);
|
||||
# }),
|
||||
# ]
|
||||
#
|
||||
#==== the update loop then becomes ======
|
||||
#
|
||||
# foreach(var update_item; me.update_items)
|
||||
# {
|
||||
# # hdp is a data provider that can be used as the hashlist for the property
|
||||
# # update from hash methods.
|
||||
# update_item.update(hdp);
|
||||
# }
|
||||
#
|
||||
var UpdateManager =
|
||||
{
|
||||
_updateProperty : func(_property)
|
||||
{
|
||||
},
|
||||
FromProperty : func(_propname, _delta, _changed_method)
|
||||
{
|
||||
var obj = {parents : [UpdateManager] };
|
||||
obj.propname = _propname;
|
||||
obj.property = props.globals.getNode(_propname);
|
||||
obj.delta = _delta;
|
||||
obj.curval = obj.property.getValue();
|
||||
obj.lastval = obj.curval;
|
||||
obj.changed = _changed_method;
|
||||
obj.update = func(obj)
|
||||
{
|
||||
me.curval = me.property.getValue();
|
||||
if (me.curval != nil)
|
||||
{
|
||||
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)
|
||||
{
|
||||
me.lastval = me.curval;
|
||||
me.changed(me.curval);
|
||||
}
|
||||
}
|
||||
else if(me.lastval == nil or me.lastval != me.curval)
|
||||
{
|
||||
me.lastval = me.curval;
|
||||
me.changed(me.curval);
|
||||
}
|
||||
}
|
||||
};
|
||||
obj.update(obj);
|
||||
return obj;
|
||||
},
|
||||
|
||||
IsNumeric : func(hashkey)
|
||||
{
|
||||
me.localType = me.property[hashkey].getType();
|
||||
if (me.localType == "UNSPECIFIED") {
|
||||
print("UpdateManager: warning ",hashkey," is ",ty, " excluding from update");
|
||||
me.property[hashkey] = nil;
|
||||
}
|
||||
if (me.localType == "INT" or me.localType == "LONG" or me.localType == "FLOAT" or me.localType == "DOUBLE")
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
},
|
||||
|
||||
FromPropertyHashList : func(_keylist, _delta, _changed_method)
|
||||
{
|
||||
var obj = {parents : [UpdateManager] };
|
||||
obj.hashkeylist = _keylist;
|
||||
obj.delta = _delta;
|
||||
obj.lastval = {};
|
||||
obj.hashkey = nil;
|
||||
obj.changed = _changed_method;
|
||||
obj.needs_update = 0;
|
||||
obj.property = {};
|
||||
obj.is_numeric = {};
|
||||
foreach (hashkey; obj.hashkeylist) {
|
||||
obj.property[hashkey] = props.globals.getNode(hashkey);
|
||||
obj.lastval[hashkey] = nil;
|
||||
# var ty = obj.property[hashkey].getType();
|
||||
# if (ty == "INT" or ty == "LONG" or ty == "FLOAT" or ty == "DOUBLE") {
|
||||
# obj.is_numeric[hashkey] = 1;
|
||||
# } else
|
||||
# obj.is_numeric[hashkey] = 0;
|
||||
#print("create: ", hashkey," ", ty, " isnum=",obj.is_numeric[hashkey]);
|
||||
# if (ty == "UNSPECIFIED")
|
||||
# print("UpdateManager: warning ",hashkey," is ",ty);
|
||||
}
|
||||
obj.update = func(obj)
|
||||
{
|
||||
if (me.lastval == nil)
|
||||
me.needs_update = 1;
|
||||
else {
|
||||
me.needs_update = 0;
|
||||
|
||||
foreach (hashkey; me.hashkeylist) {
|
||||
if (me.property[hashkey] != nil) {
|
||||
me.valIsNumeric = me.IsNumeric(hashkey);
|
||||
|
||||
if (me.lastval[hashkey] == nil
|
||||
or (me.valIsNumeric and (math.abs(me.lastval[hashkey] - me.property[hashkey].getValue()) >= me.delta))
|
||||
or (!me.valIsNumeric and (me.lastval[hashkey] != me.property[hashkey].getValue()))) {
|
||||
me.needs_update = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (me.needs_update) {
|
||||
me.changed(me);
|
||||
foreach (hashkey; me.hashkeylist) {
|
||||
me.lastval[hashkey] = me.property[hashkey].getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
return obj;
|
||||
},
|
||||
FromHashValue : func(_key, _delta, _changed_method)
|
||||
{
|
||||
var obj = {parents : [UpdateManager] };
|
||||
obj.hashkey = _key;
|
||||
obj.delta = _delta;
|
||||
obj.isnum = _delta != nil;
|
||||
obj.curval = nil;
|
||||
obj.lastval = nil;
|
||||
obj.changed = _changed_method;
|
||||
obj.update = func(obj)
|
||||
{
|
||||
me.curval = obj[me.hashkey];
|
||||
if (me.curval != nil) {
|
||||
if (me.isnum) {
|
||||
me.curval = num(me.curval);
|
||||
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) {
|
||||
me.lastval = me.curval;
|
||||
me.changed(me.curval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
return obj;
|
||||
},
|
||||
FromHashList : func(_keylist, _delta, _changed_method)
|
||||
{
|
||||
var obj = {parents : [UpdateManager] };
|
||||
obj.hashkeylist = _keylist;
|
||||
obj.delta = _delta;
|
||||
obj.lastval = {};
|
||||
obj.hashkey = nil;
|
||||
obj.changed = _changed_method;
|
||||
obj.needs_update = 0;
|
||||
obj.isnum = _delta != nil;
|
||||
obj.update = func(obj)
|
||||
{
|
||||
if (me.lastval == nil)
|
||||
me.needs_update = 1;
|
||||
else
|
||||
me.needs_update = 0;
|
||||
|
||||
if (obj != nil or me.lastval == nil) {
|
||||
foreach (hashkey; me.hashkeylist) {
|
||||
if (me.isnum) {
|
||||
if (me.lastval[hashkey] == nil or math.abs(me.lastval[hashkey] - obj[hashkey]) >= me.delta) {
|
||||
me.needs_update = 1;
|
||||
break;
|
||||
}
|
||||
} elsif (me.lastval[hashkey] == nil or me.lastval[hashkey] != obj[hashkey]) {
|
||||
me.needs_update = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (me.needs_update) {
|
||||
me.changed(obj);
|
||||
foreach (hashkey; me.hashkeylist) {
|
||||
me.lastval[hashkey] = obj[hashkey];
|
||||
}
|
||||
}
|
||||
};
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
64
test/qrcode.nas
Normal file
@@ -0,0 +1,64 @@
|
||||
var code=[
|
||||
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1],
|
||||
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1],
|
||||
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1],
|
||||
[1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
];
|
||||
|
||||
# enable unicode
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
var texture=[" ","██"];
|
||||
for(var i=0;i<size(code);i+=1){
|
||||
for(var j=0;j<size(code[i]);j+=1)
|
||||
print(texture[code[i][j]]);
|
||||
print('\n');
|
||||
}
|
||||
|
||||
var transfer=func(s){
|
||||
var mode=[0,1,0,0];
|
||||
println(mode);
|
||||
|
||||
var len=size(s);
|
||||
var vec=[0,0,0,0,0,0,0,0,0];
|
||||
for(var i=8;i>=0;i-=1){
|
||||
vec[i]=bits.u32_and(1,len);
|
||||
len=int(len/2);
|
||||
}
|
||||
println(vec);
|
||||
|
||||
var bitstr=[];
|
||||
for(var i=0;i<size(s);i+=1){
|
||||
var tmp=[0,0,0,0,0,0,0,0];
|
||||
var c=s[i];
|
||||
for(var j=7;j>=0;j-=1){
|
||||
tmp[j]=bits.u32_and(1,c);
|
||||
c=int(c/2);
|
||||
}
|
||||
foreach(var j;tmp)
|
||||
append(bitstr,j);
|
||||
}
|
||||
println(bitstr);
|
||||
|
||||
var end=[0,0,0,0];
|
||||
println(end);
|
||||
}
|
||||
|
||||
transfer("github.com/ValKmjolnir");
|
||||
@@ -1,25 +1,7 @@
|
||||
import("lib.nas");
|
||||
var sort=func(vec,left,right)
|
||||
{
|
||||
if(left>=right) return;
|
||||
var (L,R,tmp)=(left,right,vec[left]);
|
||||
while(left<right)
|
||||
{
|
||||
while(left<right and tmp<=vec[right])
|
||||
right-=1;
|
||||
while(left<right and tmp>=vec[left])
|
||||
left+=1;
|
||||
if(left!=right)
|
||||
(vec[left],vec[right])=(vec[right],vec[left]);
|
||||
}
|
||||
(vec[L],vec[left])=(vec[left],tmp);
|
||||
sort(vec,L,left-1);
|
||||
sort(vec,left+1,R);
|
||||
return;
|
||||
}
|
||||
var vec=[];
|
||||
rand(time(0));
|
||||
for(var i=0;i<1e4;i+=1)
|
||||
append(vec,int(rand()*1e5));
|
||||
sort(vec,0,size(vec)-1);
|
||||
import.stl.sort;
|
||||
var vec=[];
|
||||
rand(time(0));
|
||||
for(var i=0;i<1e4;i+=1)
|
||||
append(vec,int(rand()*1e5));
|
||||
sort(vec);
|
||||
println(vec);
|
||||
@@ -1,5 +1,4 @@
|
||||
# basic type
|
||||
import("lib.nas");
|
||||
nil;
|
||||
2147483647;
|
||||
0x7fffffff;
|
||||
@@ -149,4 +148,60 @@ f1() or f2();
|
||||
# print '1'
|
||||
# this means that when using 'or' or 'and',
|
||||
# if the result is clear when calculating,
|
||||
# objects behind will not be calculated
|
||||
# objects behind will not be calculated
|
||||
|
||||
print(
|
||||
subvec([0,1,2,3],2),'\n',
|
||||
subvec([0,1,2,3],2,1),'\n',
|
||||
abs(1),'\n',
|
||||
abs(-1),'\n',
|
||||
systime(),'\n',
|
||||
isfunc(func{}),' ',isfunc([]),'\n',
|
||||
ishash({}),' ',ishash([]),'\n',
|
||||
isint(114.514),' ',isint(114514),'\n',
|
||||
isnum("0xaa55"),' ',isnum("?"),'\n',
|
||||
isscalar(0.618),' ',isscalar("hello"),' ',isscalar([]),'\n',
|
||||
isstr("hello"),' ',isstr(func{}),'\n',
|
||||
isvec([]),' ',isvec("[]"),'\n',
|
||||
vecindex([0,1,2,3,4],1),'\n',
|
||||
vecindex(["apple","banana"],"apple")!=nil,'\n'
|
||||
);
|
||||
|
||||
println(values({
|
||||
a:1,
|
||||
b:2,
|
||||
c:3
|
||||
}));
|
||||
println(find("cd", "abcdef")); # prints 2
|
||||
println(find("x", "abcdef")); # prints -1
|
||||
println(find("cd", "abcdef")); # prints 2
|
||||
|
||||
var a={
|
||||
new: func(x=0){
|
||||
return {
|
||||
x:x,
|
||||
parents:[a]
|
||||
};
|
||||
},
|
||||
new2: func(x=0){
|
||||
return {
|
||||
x:x,
|
||||
parents:a
|
||||
};
|
||||
}
|
||||
};
|
||||
println(isa(a.new(),a)); # 1
|
||||
println(isa(a.new2(),a));# 0
|
||||
|
||||
var a=[10,-10,0,1,2,3,nil,"string","hello",[],[0,1,2,3],{},{a:0,b:1,c:2},func{}];
|
||||
println("type\tsize\tnum\tsrc");
|
||||
foreach(var i;a){
|
||||
println(typeof(i),'\t',size(i),'\t',num(i),'\t',i);
|
||||
}
|
||||
foreach(i;a){
|
||||
;
|
||||
}
|
||||
println(runtime.argv());
|
||||
func(a,b,c,d="只有红茶可以吗"){
|
||||
println(a,' ',b,' ',c,' ',d);
|
||||
}(c:1919810,b:514,a:114);
|
||||
225
test/snake.nas
Normal file
@@ -0,0 +1,225 @@
|
||||
import.module.libkey;
|
||||
var list=func(){
|
||||
var (begin,end,len)=(nil,nil,0);
|
||||
return{
|
||||
push_back:func(elem){
|
||||
var tmp={
|
||||
elem:elem,
|
||||
prev:nil,
|
||||
next:nil
|
||||
};
|
||||
if(end!=nil){
|
||||
end.next=tmp;
|
||||
tmp.prev=end;
|
||||
end=tmp;
|
||||
}else{
|
||||
begin=end=tmp;
|
||||
}
|
||||
len+=1;
|
||||
},
|
||||
push_front:func(elem){
|
||||
var tmp={
|
||||
elem:elem,
|
||||
prev:nil,
|
||||
next:nil
|
||||
};
|
||||
if(begin!=nil){
|
||||
begin.prev=tmp;
|
||||
tmp.next=begin;
|
||||
begin=tmp;
|
||||
}else{
|
||||
begin=end=tmp;
|
||||
}
|
||||
len+=1;
|
||||
},
|
||||
pop_back:func(){
|
||||
if(end!=nil)
|
||||
end=end.prev;
|
||||
if(end==nil)
|
||||
begin=nil;
|
||||
else
|
||||
end.next=nil;
|
||||
if(len)
|
||||
len-=1;
|
||||
},
|
||||
pop_front:func(){
|
||||
if(begin!=nil)
|
||||
begin=begin.next;
|
||||
if(begin==nil)
|
||||
end=nil;
|
||||
else
|
||||
begin.prev=nil;
|
||||
if(len)
|
||||
len-=1;
|
||||
},
|
||||
front:func(){
|
||||
if(begin!=nil)
|
||||
return begin.elem;
|
||||
},
|
||||
back:func(){
|
||||
if(end!=nil)
|
||||
return end.elem;
|
||||
},
|
||||
length:func(){
|
||||
return len;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var game=func(x,y){
|
||||
rand(time(0));
|
||||
var texture=[" ","██","\e[91m██\e[0m"];
|
||||
var edge0="╔";
|
||||
var edge1="╚";
|
||||
for(var i=0;i<x;i+=1){
|
||||
edge0~="══";
|
||||
edge1~="══";
|
||||
}
|
||||
edge0~="╗\n";
|
||||
edge1~="╝\n";
|
||||
|
||||
var vec=[];
|
||||
setsize(vec,x);
|
||||
for(var i=0;i<x;i+=1){
|
||||
vec[i]=[];
|
||||
setsize(vec[i],y);
|
||||
for(var j=0;j<y;j+=1)
|
||||
vec[i][j]=0;
|
||||
}
|
||||
|
||||
var snake=list();
|
||||
snake.push_back([int(x/2),int(y/3)]);
|
||||
snake.push_back([int(x/2),int(y/3)+1]);
|
||||
vec[int(x/2)][int(y/3)]=1;
|
||||
vec[int(x/2)][int(y/3)+1]=1;
|
||||
|
||||
var move='w';
|
||||
var gameover=0;
|
||||
var setapple=func(){
|
||||
var (cord_x,cord_y)=(int(rand()*x),int(rand()*y));
|
||||
while(vec[cord_x][cord_y]!=0)
|
||||
(cord_x,cord_y)=(int(rand()*x),int(rand()*y));
|
||||
vec[cord_x][cord_y]=2;
|
||||
}
|
||||
setapple();
|
||||
|
||||
return {
|
||||
print:func(){
|
||||
var s="";
|
||||
var (fx,fy)=snake.front();
|
||||
for(var i=0;i<y;i+=1){
|
||||
s~="║";
|
||||
for(var j=0;j<x;j+=1){
|
||||
if(fx==j and fy==i)
|
||||
s~="\e[93m"~texture[vec[j][i]]~"\e[0m";
|
||||
else
|
||||
s~=texture[vec[j][i]];
|
||||
}
|
||||
s~='║\n';
|
||||
}
|
||||
print('\e[1;1H'~edge0~s~edge1);
|
||||
},
|
||||
next:func(){
|
||||
var (fx,fy)=snake.front();
|
||||
var eat=0;
|
||||
if(move=="w" and fy-1>=0){
|
||||
snake.push_front([fx,fy-1]);
|
||||
if(vec[fx][fy-1]==1)
|
||||
gameover=1;
|
||||
elsif(vec[fx][fy-1]==2)
|
||||
eat=1;
|
||||
vec[fx][fy-1]=1;
|
||||
}elsif(move=='a' and fx-1>=0){
|
||||
snake.push_front([fx-1,fy]);
|
||||
if(vec[fx-1][fy]==1)
|
||||
gameover=1;
|
||||
elsif(vec[fx-1][fy]==2)
|
||||
eat=1;
|
||||
vec[fx-1][fy]=1;
|
||||
}elsif(move=='s' and fy+1<y){
|
||||
snake.push_front([fx,fy+1]);
|
||||
if(vec[fx][fy+1]==1)
|
||||
gameover=1;
|
||||
elsif(vec[fx][fy+1]==2)
|
||||
eat=1;
|
||||
vec[fx][fy+1]=1;
|
||||
}elsif(move=='d' and fx+1<x){
|
||||
snake.push_front([fx+1,fy]);
|
||||
if(vec[fx+1][fy]==1)
|
||||
gameover=1;
|
||||
elsif(vec[fx+1][fy]==2)
|
||||
eat=1;
|
||||
vec[fx+1][fy]=1;
|
||||
}else{
|
||||
gameover=1;
|
||||
}
|
||||
if(!gameover and !eat){
|
||||
var (bx,by)=snake.back();
|
||||
vec[bx][by]=0;
|
||||
snake.pop_back();
|
||||
}
|
||||
|
||||
if(eat and snake.length()!=x*y)
|
||||
setapple();
|
||||
elsif(snake.length()==x*y)
|
||||
gameover=2;
|
||||
},
|
||||
move:func(c){
|
||||
move=c;
|
||||
},
|
||||
gameover:func(){
|
||||
return gameover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var co=coroutine.create(func(){
|
||||
var t=maketimestamp();
|
||||
while(1){
|
||||
t.stamp();
|
||||
while(t.elapsedMSec()<20);
|
||||
coroutine.yield();
|
||||
}
|
||||
});
|
||||
|
||||
var main=func(){
|
||||
# enable unicode
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
print("\ec");
|
||||
|
||||
var g=game(15,10);
|
||||
g.print();
|
||||
print("\rpress any key to start...");
|
||||
libkey.getch();
|
||||
print("\r \r");
|
||||
var counter=20;
|
||||
while(1){
|
||||
var ch=libkey.nonblock();
|
||||
if(ch!=nil){
|
||||
if(ch=='q'[0])
|
||||
break;
|
||||
elsif(ch=='p'[0]){
|
||||
print("\rpress any key to continue...");
|
||||
libkey.getch();
|
||||
print("\r \r");
|
||||
}
|
||||
g.move(chr(ch));
|
||||
}
|
||||
counter-=1;
|
||||
if(!counter){
|
||||
counter=20;
|
||||
g.next();
|
||||
if(g.gameover())
|
||||
break;
|
||||
g.print();
|
||||
}
|
||||
coroutine.resume(co);
|
||||
}
|
||||
|
||||
println(g.gameover()<=1?"game over.":"you win!");
|
||||
println("press 'q' to quit.");
|
||||
while(libkey.getch()!='q'[0]);
|
||||
}
|
||||
|
||||
main();
|
||||
347
test/tetris.nas
Normal file
@@ -0,0 +1,347 @@
|
||||
import.module.libkey;
|
||||
var color=[
|
||||
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",
|
||||
"\e[91m","\e[92m","\e[93m","\e[94m","\e[95m","\e[96m",
|
||||
];
|
||||
var blocktype=[
|
||||
[0,1,2,3 ],
|
||||
[4,5,6,7 ],
|
||||
[8,9,10,11],
|
||||
[12,13 ],
|
||||
[14 ],
|
||||
[15,16 ],
|
||||
[17,18 ]
|
||||
];
|
||||
var blockshape=[
|
||||
# [][] [] [][][]
|
||||
# [] [] [] []
|
||||
# [] [][][] [][]
|
||||
[[0,0],[1,0],[0,1],[0,2]],
|
||||
[[0,0],[0,1],[1,1],[2,1]],
|
||||
[[0,0],[0,1],[0,2],[-1,2]],
|
||||
[[0,0],[1,0],[2,0],[2,1]],
|
||||
# [][] [][][] []
|
||||
# [] [] [] []
|
||||
# [] [][] [][][]
|
||||
[[0,0],[1,0],[1,1],[1,2]],
|
||||
[[0,0],[1,0],[2,0],[0,1]],
|
||||
[[0,0],[0,1],[0,2],[1,2]],
|
||||
[[0,0],[0,1],[-1,1],[-2,1]],
|
||||
# [] [] [][][] []
|
||||
# [][][] [][] [] [][]
|
||||
# [] []
|
||||
[[0,0],[0,1],[-1,1],[1,1]],
|
||||
[[0,0],[0,1],[-1,1],[0,2]],
|
||||
[[0,0],[-1,0],[1,0],[0,1]],
|
||||
[[0,0],[0,1],[0,2],[1,1]],
|
||||
# [] [][][][]
|
||||
# []
|
||||
# []
|
||||
# []
|
||||
[[0,0],[0,1],[0,2],[0,3]],
|
||||
[[0,0],[1,0],[2,0],[3,0]],
|
||||
# [][]
|
||||
# [][]
|
||||
[[0,0],[1,0],[0,1],[1,1]],
|
||||
# [] [][]
|
||||
# [][] [][]
|
||||
# []
|
||||
[[0,0],[0,1],[-1,1],[-1,2]],
|
||||
[[0,0],[1,0],[1,1],[2,1]],
|
||||
# [] [][]
|
||||
# [][] [][]
|
||||
# []
|
||||
[[0,0],[0,1],[1,1],[1,2]],
|
||||
[[0,0],[1,0],[0,1],[-1,1]]
|
||||
];
|
||||
|
||||
var color_count=0;
|
||||
var counter=0;
|
||||
var package=[0,1,2,3,4,5,6];
|
||||
var exchange=func(){
|
||||
for(var i=6;i>=0;i-=1){
|
||||
var index=int(i*rand());
|
||||
(package[i],package[index])=(package[index],package[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var block={
|
||||
x:0,
|
||||
y:0,
|
||||
rotate:0,
|
||||
type:nil,
|
||||
shape:nil,
|
||||
color:nil,
|
||||
new:func(x=0,y=0){
|
||||
(me.x,me.y)=(x,y);
|
||||
me.rotate=0;
|
||||
me.type=blocktype[package[counter]];
|
||||
counter+=1;
|
||||
if(counter==7){
|
||||
exchange();
|
||||
counter=0;
|
||||
}
|
||||
|
||||
me.shape=blockshape[me.type[me.rotate]];
|
||||
|
||||
me.color=color_count;
|
||||
color_count+=1;
|
||||
if(color_count>=size(color))
|
||||
color_count=0;
|
||||
|
||||
return {parents:[block]};
|
||||
}
|
||||
};
|
||||
|
||||
var mapgen=func(mapx,mapy){
|
||||
var (score,gameover)=(0,0);
|
||||
var (empty,unset,full)=(0,1,2);
|
||||
|
||||
if(mapx<1 or mapy<1)
|
||||
die("map_x or map_y must be greater than 1");
|
||||
|
||||
# use in print
|
||||
var line="";
|
||||
for(var i=0;i<mapx;i+=1)
|
||||
line~="══";
|
||||
var head="\e[32m╔"~line~"╗\e[0m\n";
|
||||
var tail="\e[32m╚"~line~"╝\e[0m\n";
|
||||
|
||||
# generate new map
|
||||
var map=[];
|
||||
for(var y=0;y<mapy;y+=1){
|
||||
var tmp=[];
|
||||
for(var x=0;x<mapx;x+=1)
|
||||
append(tmp,empty);
|
||||
append(map,tmp);
|
||||
}
|
||||
|
||||
var blk=nil;
|
||||
var new_block=func(){
|
||||
blk=block.new(int(mapx/2),0);
|
||||
|
||||
# check if has enough place to place a new block
|
||||
foreach(var i;blk.shape)
|
||||
if(map[blk.y+i[1]][blk.x+i[0]]>=full){
|
||||
gameover=1;
|
||||
return;
|
||||
}
|
||||
# update map
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=unset;
|
||||
}
|
||||
new_block(); # initialize the first block
|
||||
|
||||
# color print
|
||||
var map_print=func(){
|
||||
var s="\e[1;1H"~head;
|
||||
for(var y=0;y<mapy;y+=1){
|
||||
s~="\e[32m║\e[0m";
|
||||
for(var x=0;x<mapx;x+=1){
|
||||
var c=map[y][x];
|
||||
if(c==empty)
|
||||
s~=" ";
|
||||
elsif(c==unset)
|
||||
s~=color[blk.color]~"██\e[0m";
|
||||
elsif(c>=full)
|
||||
s~=color[c-full]~"██\e[0m";
|
||||
}
|
||||
s~="\e[32m║\e[0m\n";
|
||||
}
|
||||
s~=tail;
|
||||
print(s,"\e[31ms\e[32mc\e[33mo\e[34mr\e[35me\e[36m: \e[0m",score,'\n');
|
||||
}
|
||||
|
||||
var moveleft=func(){
|
||||
var (x,y)=(blk.x-1,blk.y);
|
||||
foreach(var i;blk.shape){
|
||||
if(x+i[0]<0)
|
||||
return;
|
||||
if(map[y+i[1]][x+i[0]]>=full)
|
||||
return;
|
||||
}
|
||||
# update block state and map
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=empty;
|
||||
blk.x=x;
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=unset;
|
||||
map_print();
|
||||
}
|
||||
|
||||
var moveright=func(){
|
||||
var (x,y)=(blk.x+1,blk.y);
|
||||
foreach(var i;blk.shape){
|
||||
if(x+i[0]>=mapx)
|
||||
return;
|
||||
if(map[y+i[1]][x+i[0]]>=full)
|
||||
return;
|
||||
}
|
||||
# update block state and map
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=empty;
|
||||
blk.x=x;
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=unset;
|
||||
map_print();
|
||||
}
|
||||
|
||||
var rotate=func(){
|
||||
var (r,x,y)=(blk.rotate,blk.x,blk.y);
|
||||
r=(r+1>=size(blk.type))?0:r+1;
|
||||
var shape=blockshape[blk.type[r]];
|
||||
foreach(var i;shape){
|
||||
if(x+i[0]>=mapx or x+i[0]<0 or y+i[1]>=mapy or y+i[1]<0)
|
||||
return;
|
||||
if(map[y+i[1]][x+i[0]]>=full)
|
||||
return;
|
||||
}
|
||||
|
||||
# update block state and map
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=empty;
|
||||
blk.rotate=r;
|
||||
blk.shape=shape;
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=unset;
|
||||
map_print();
|
||||
}
|
||||
|
||||
var fall=func(){
|
||||
var (x,y)=(blk.x,blk.y+1);
|
||||
# check if falls to the edge of other blocks or map
|
||||
var sethere=0;
|
||||
foreach(var i;blk.shape)
|
||||
if(y+i[1]>=mapy or map[y+i[1]][x+i[0]]>=full){
|
||||
sethere=1;
|
||||
break;
|
||||
}
|
||||
# set block here and generate a new block
|
||||
if(sethere){
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=blk.color+full;
|
||||
checkmap();
|
||||
new_block();
|
||||
map_print();
|
||||
return;
|
||||
}
|
||||
# update block state and map
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=empty;
|
||||
blk.y=y;
|
||||
foreach(var i;blk.shape)
|
||||
map[blk.y+i[1]][blk.x+i[0]]=unset;
|
||||
map_print();
|
||||
}
|
||||
|
||||
var checkmap=func(){
|
||||
var lines=1;
|
||||
for(var y=mapy-1;y>=0;y-=1){
|
||||
# check if this line is full of blocks
|
||||
var tmp=0;
|
||||
for(var x=0;x<mapx;x+=1){
|
||||
if(map[y][x]<full)
|
||||
break;
|
||||
tmp+=map[y][x];
|
||||
}
|
||||
# if is full, clear this line and
|
||||
# all the lines above fall one block
|
||||
if(x==mapx){
|
||||
score+=lines*tmp;
|
||||
lines*=2;
|
||||
for(var t=y;t>=1;t-=1)
|
||||
for(var x=0;x<mapx;x+=1)
|
||||
map[t][x]=map[t-1][x];
|
||||
for(var x=0;x<mapx;x+=1)
|
||||
map[0][x]=empty;
|
||||
y+=1;
|
||||
}
|
||||
}
|
||||
map_print();
|
||||
}
|
||||
return {
|
||||
print:map_print,
|
||||
moveleft:moveleft,
|
||||
moveright:moveright,
|
||||
rotate:rotate,
|
||||
fall:fall,
|
||||
checkmap:checkmap,
|
||||
gameover:func(){return gameover;}
|
||||
};
|
||||
}
|
||||
|
||||
var main=func(){
|
||||
# windows use chcp 65001 to output unicode
|
||||
if(os.platform()=="windows")
|
||||
system("chcp 65001");
|
||||
|
||||
print(
|
||||
"\ec\e[1:1H",
|
||||
"╔═════════════════════════╗\n",
|
||||
"║ TETRIS ║\n",
|
||||
"╠═════════════════════════╣\n",
|
||||
"║ w:rotate, a:move left ║\n",
|
||||
"║ s:fall, d:move right ║\n",
|
||||
"║ p:pause, q:quit ║\n",
|
||||
"╠═════════════════════════╣\n",
|
||||
"║ press any key to start ║\n",
|
||||
"╚═════════════════════════╝\n"
|
||||
);
|
||||
|
||||
rand(time(0));
|
||||
exchange();
|
||||
var map=mapgen(mapx:12,mapy:18);
|
||||
|
||||
libkey.getch();
|
||||
print("\ec");
|
||||
|
||||
var counter=30;
|
||||
while(1){
|
||||
# nonblock input one character
|
||||
var ch=libkey.nonblock();
|
||||
if(ch){
|
||||
if(ch=='a'[0]) # move left
|
||||
map.moveleft();
|
||||
elsif(ch=='d'[0]) # move right
|
||||
map.moveright();
|
||||
elsif(ch=='w'[0]) # rotate
|
||||
map.rotate();
|
||||
elsif(ch=='s'[0]) # move down
|
||||
map.fall();
|
||||
elsif(ch=='q'[0]) # quit the game
|
||||
break;
|
||||
if(ch=='p'[0]){ # pause the game
|
||||
print("\rpress any key to continue...");
|
||||
libkey.getch();
|
||||
print("\r ");
|
||||
}
|
||||
map.checkmap();
|
||||
if(map.gameover())
|
||||
break;
|
||||
}
|
||||
if(!counter){
|
||||
# automatically fall one block and check
|
||||
map.fall();
|
||||
map.checkmap();
|
||||
if(map.gameover())
|
||||
break;
|
||||
counter=30;
|
||||
}
|
||||
unix.sleep(0.02);
|
||||
counter-=1;
|
||||
}
|
||||
|
||||
print(
|
||||
map.gameover()?
|
||||
"\e[31mg\e[32ma\e[33mm\e[34me \e[35mo\e[36mv\e[94me\e[31mr \e[32m~\e[0m\n":
|
||||
"\e[31ms\e[32me\e[33me \e[34my\e[35mo\e[36mu \e[94m~\e[0m\n"
|
||||
);
|
||||
print(
|
||||
"\e[31me\e[32mn\e[33mt\e[34me\e[35mr ",
|
||||
"\e[36m'\e[94mq\e[95m' ",
|
||||
"\e[35mt\e[36mo \e[94mq\e[95mu\e[91mi\e[92mt\e[0m\n"
|
||||
);
|
||||
while(libkey.getch()!='q'[0]);
|
||||
};
|
||||
|
||||
main();
|
||||
@@ -1,4 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var trait={
|
||||
get:func{return me.val;},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import("lib.nas");
|
||||
|
||||
var table=[
|
||||
['q0','0','1','R','q1'],
|
||||
@@ -7,8 +6,44 @@ var table=[
|
||||
['q2','0','1','R','q3'],
|
||||
['q3',nil,nil,'S','q3']
|
||||
];
|
||||
|
||||
var operand={
|
||||
new:func(symbol,changed_symbol,move,next_state){
|
||||
if(move!='L' and move!='R' and move!='S')
|
||||
die("invalid move type:"+move);
|
||||
return {
|
||||
symbol:symbol,
|
||||
changed_symbol:changed_symbol,
|
||||
move:move,
|
||||
next_state:next_state
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var machine={
|
||||
states:{},
|
||||
add:func(state,operand){
|
||||
if(!contains(me.states,state))
|
||||
me.states[state]=[operand];
|
||||
else{
|
||||
foreach(var i;me.states[state])
|
||||
if(i.symbol==operand.symbol or i.symbol==nil){
|
||||
println(i);
|
||||
die("conflict operand");
|
||||
}
|
||||
append(me.states[state],operand);
|
||||
}
|
||||
},
|
||||
load:func(data){
|
||||
foreach(var opr;data){
|
||||
var (nstat,sym,csym,move,nextstat)=opr;
|
||||
me.add(nstat,operand.new(sym,csym,move,nextstat));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var prt=func(state,pointer,paper,act=nil){
|
||||
print(state,':',pointer,':',act!=nil?act:'','\n\t');
|
||||
print(act!=nil?act:'','\n\t');
|
||||
var s='';
|
||||
foreach(var i;paper)
|
||||
s~=i;
|
||||
@@ -16,36 +51,50 @@ var prt=func(state,pointer,paper,act=nil){
|
||||
for(var i=0;i<pointer;i+=1)
|
||||
for(var j=0;j<size(paper[i]);j+=1)
|
||||
s~=' ';
|
||||
print(s,'^\n');
|
||||
print(s,'^\n',state," ");
|
||||
}
|
||||
var run=func(table,node,start,stop){
|
||||
var run=func(table,start,stop){
|
||||
var paper=['0','1','1','1','0','1','0','a'];
|
||||
var pointer=0;
|
||||
foreach(var action;table){
|
||||
if(!contains(node,action[0]))
|
||||
node[action[0]]=nil;
|
||||
if(!contains(node,action[4]))
|
||||
node[action[4]]=nil;
|
||||
}
|
||||
print("nodes: ",keys(node),'\n');
|
||||
if(!contains(node,start))
|
||||
|
||||
machine.load(table);
|
||||
|
||||
print("states: ",keys(machine.states),'\n');
|
||||
if(!contains(machine.states,start))
|
||||
die(start~" is not a valid node");
|
||||
if(!contains(node,stop))
|
||||
if(!contains(machine.states,stop))
|
||||
die(stop~" is not a valid node");
|
||||
var state=start;
|
||||
|
||||
var (state,pointer)=(start,0);
|
||||
|
||||
prt(state,pointer,paper);
|
||||
while(state!=stop){
|
||||
foreach(var action;table)
|
||||
if(action[0]==state and (action[1]==paper[pointer] or action[1]==' ')){
|
||||
paper[pointer]=action[2]==nil?paper[pointer]:action[2];
|
||||
if(action[3]=='L') pointer-=1;
|
||||
elsif(action[3]=='R') pointer+=1;
|
||||
elsif(action[3]!='S') die("invalid action <"~action[3]~'>');
|
||||
state=action[4];
|
||||
if(!contains(machine.states,state))
|
||||
die("no matching function for state:"~state);
|
||||
var found=0;
|
||||
foreach(var action;machine.states[state]){
|
||||
var (sym,csym,move,next)=(
|
||||
action.symbol,
|
||||
action.changed_symbol,
|
||||
action.move,
|
||||
action.next_state
|
||||
);
|
||||
if(sym==paper[pointer] or sym==nil){
|
||||
if(sym!=nil)
|
||||
paper[pointer]=csym;
|
||||
if(move=='L') pointer-=1;
|
||||
elsif(move=='R') pointer+=1;
|
||||
(state,found)=(next,1);
|
||||
break;
|
||||
}
|
||||
prt(state,pointer,paper,action);
|
||||
}
|
||||
|
||||
if(!found)
|
||||
die("no matching function for state:"~state);
|
||||
|
||||
prt(state,pointer,paper,[sym,csym,move,next]);
|
||||
}
|
||||
}
|
||||
|
||||
run(table,{},'q0','q3');
|
||||
run(table,'q0','q3');
|
||||
print('\n');
|
||||
49
test/utf8chk.nas
Normal file
@@ -0,0 +1,49 @@
|
||||
var unicode测试=func(){
|
||||
var 输出=print;
|
||||
var 测试成功=[
|
||||
"unicode: utf-8支持测试成功",
|
||||
"目前仅支持utf-8以及ascii格式文件",
|
||||
"注意: windows系统请开启chcp 65001代码页"
|
||||
];
|
||||
foreach(var 内容;测试成功)
|
||||
输出(内容~"\n");
|
||||
}
|
||||
var emoji测试=func(){
|
||||
var 💻=print;
|
||||
var 🎤="\n";
|
||||
var 🤣="🤣笑嘻了";
|
||||
var 😅="😅差不多得了";
|
||||
var 🤤="🤤收收味";
|
||||
var 🥵="🥵太烧啦";
|
||||
var 🥶="🥶捏麻麻滴冷死了";
|
||||
var 🤢="🤢老八秘制小汉堡🍔";
|
||||
var 🤓="🤓我是傻逼";
|
||||
var 😭="😭你带我走吧😭😭😭";
|
||||
var 👿="👿密麻麻石蜡";
|
||||
var 🤡="🤡居然就是你";
|
||||
var 💩="💩奥利给干了兄弟们";
|
||||
var 🍾="🍾好似,开🍾咯";
|
||||
var 🐘="🐘太🚬🐘了兄弟们";
|
||||
var 📁=[🤣,😅,🤤,🥵,🥶,🤢,🤓,😭,👿,🤡,💩,🍾,🐘];
|
||||
var 🗄️={
|
||||
🤣:🤣,
|
||||
😅:😅,
|
||||
🤤:🤤,
|
||||
🥵:🥵,
|
||||
🥶:🥶,
|
||||
🤢:🤢,
|
||||
🤓:🤓,
|
||||
😭:😭,
|
||||
👿:👿,
|
||||
🤡:🤡,
|
||||
💩:💩,
|
||||
🍾:🍾,
|
||||
🐘:🐘
|
||||
};
|
||||
foreach(var 📄;📁)
|
||||
💻(📄,🎤);
|
||||
foreach(var 📄;keys(🗄️))
|
||||
💻(📄,🗄️[📄],🎤);
|
||||
}
|
||||
unicode测试();
|
||||
emoji测试();
|
||||