2023-07-04 00:33:57 +08:00
# __Nasal - Modern Interpreter__
2022-11-26 23:36:12 +08:00
2024-04-11 23:45:48 +08:00
< img src = "./doc/pic/header.png" style = "width:600px" > < / img >
2019-07-25 02:14:33 +08:00
2022-07-24 17:05:36 +08:00
![GitHub release(latest by date) ](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github )
2023-07-02 21:37:14 +08:00
[![license ](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github )](./LICENSE)
2023-12-09 00:07:44 +08:00
![downloads ](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github )
2024-03-01 23:05:04 +08:00
[![C/C++ CI ](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml/badge.svg )](https://github.com/ValKmjolnir/Nasal-Interpreter/actions/workflows/c-cpp.yml)
2022-06-18 00:30:43 +08:00
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
2022-06-11 12:49:26 +08:00
2022-01-25 15:02:57 +08:00
## __Contents__
* [__Introduction__ ](#introduction )
2024-05-12 12:11:31 +08:00
* [__Download__ ](#download )
* [__Compile__ ](#compile )
2022-01-25 15:02:57 +08:00
* [__Usage__ ](#how-to-use )
2023-11-17 00:13:17 +08:00
* [__Tutorial__ ](./doc/tutorial.md )
2022-07-24 13:01:42 +08:00
* [__Release Notes__ ](./doc/dev.md#release-notes )
2022-07-23 23:04:31 +08:00
* [__Development History__ ](./doc/dev.md )
* [__Benchmark__ ](./doc/benchmark.md )
2022-01-25 15:02:57 +08:00
* [__Difference__ ](#difference-between-andys-and-this-interpreter )
* [__Trace Back Info__ ](#trace-back-info )
* [__Debugger__ ](#debugger )
2023-10-04 11:52:09 +08:00
* [__REPL__ ](#repl )
2022-01-25 15:02:57 +08:00
__Contact us if having great ideas to share!__
2022-01-22 22:05:42 +08:00
2023-11-15 00:36:25 +08:00
* __E-mail__:
* __lhk101lhk101@qq.com__ (ValKmjolnir)
2023-12-01 14:35:26 +08:00
* __sidi.liang@gmail.com__ (Sidi)
2021-06-07 23:53:43 +08:00
2022-01-25 15:02:57 +08:00
## __Introduction__
2023-12-09 00:07:44 +08:00
![star ](https://img.shields.io/github/stars/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github )
![fork ](https://img.shields.io/github/forks/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github )
![issue ](https://img.shields.io/github/issues/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github )
![pr ](https://img.shields.io/github/issues-pr/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github )
2022-09-13 23:00:48 +08:00
[Nasal ](http://wiki.flightgear.org/Nasal_scripting_language )
2023-03-08 01:12:01 +08:00
is an ECMAscript-like language used in [FlightGear ](https://www.flightgear.org/ ).
2022-07-24 17:05:36 +08:00
The designer is [Andy Ross ](https://github.com/andyross ).
2023-12-09 00:07:44 +08:00
This interpreter is rewritten by [ValKmjolnir ](https://github.com/ValKmjolnir ) using `C++` (`-std=c++17`).
We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter ](https://github.com/andyross/nasal ).
2021-05-04 17:39:24 +08:00
2024-05-12 12:11:31 +08:00
Old version of this project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5). Now it uses __GPL v2 license__ (since 2023/6).
2021-05-04 17:39:24 +08:00
2023-12-09 00:07:44 +08:00
### __Why writing this Nasal interpreter?__
2023-03-08 01:12:01 +08:00
2019 summer,
2022-07-23 23:04:31 +08:00
members in [FGPRC ](https://www.fgprc.org/ ) told me that it is hard to debug with nasal-console in Flightgear,
2021-10-08 23:18:26 +08:00
especially when checking syntax errors.
2023-03-08 01:12:01 +08:00
So i wrote a new interpreter to help checking syntax error and runtime error.
2020-09-14 13:52:25 +08:00
2022-07-24 00:10:26 +08:00
I wrote the lexer, parser and
2022-09-13 22:14:17 +08:00
bytecode virtual machine to help checking errors.
2023-03-08 01:12:01 +08:00
We found it much easier to debug.
2021-05-04 01:13:53 +08:00
2022-09-13 22:14:17 +08:00
You could also use this language to write some
2021-10-08 23:18:26 +08:00
interesting programs and run them without the lib of Flightgear.
2022-01-22 22:05:42 +08:00
You could add your own modules to make
2022-09-13 22:14:17 +08:00
the interpreter a useful tool in your own projects.
2020-11-20 00:18:17 +08:00
2024-05-12 12:11:31 +08:00
## __Download__
Nightly build could be found here.
Windows nightly build is not supported yet,
please wait or just compile it by yourself, a Cmake file is given for Visual Studio to compile this project easily:
* [macOS-nightly-build ](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS )
* [linux-nightly-build ](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64 )
* windows-nightly-build: [WIP]
## __Compile__
2020-11-20 00:18:17 +08:00
2022-07-23 23:04:31 +08:00
![g++ ](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU )
![clang++ ](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM )
![vs ](https://img.shields.io/badge/Visual_Studio-MSVC-5C2D91?style=flat-square&logo=visualstudio )
2023-01-31 19:01:36 +08:00
Better download the latest update source of the interpreter and build it! It's quite easy to build this interpreter, what you need are only two things: C++ compiler and the `make` . There is no third-party library used in this project.
2023-11-08 22:23:09 +08:00
### __Windows (MinGW-w64)__
2021-04-19 19:12:41 +08:00
2023-11-08 22:23:09 +08:00
![windows ](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows )
2022-03-14 20:29:49 +08:00
2023-11-17 00:13:17 +08:00
Make sure thread model is `posix thread model` , otherwise no thread library exists.
2023-04-23 00:02:39 +08:00
2023-07-09 00:59:17 +08:00
> mingw32-make nasal.exe -j4
2022-03-14 20:29:49 +08:00
2023-11-08 22:23:09 +08:00
### __Windows (Visual Studio)__
![windows ](https://img.shields.io/badge/Microsoft-Windows-green?style=flat-square&logo=windows )
2023-07-02 20:39:28 +08:00
2023-11-17 00:13:17 +08:00
There is a [__CMakelists.txt__ ](./CMakeLists.txt ) to create project.
2022-09-13 22:14:17 +08:00
2023-11-08 22:23:09 +08:00
### __Linux / macOS / Unix__
![linux ](https://img.shields.io/badge/GNU-Linux-green?style=flat-square&logo=GNU ) ![macOS ](https://img.shields.io/badge/Apple%20Inc.-MacOS-green?style=flat-square&logo=apple )
2022-03-14 20:29:49 +08:00
2023-11-08 22:23:09 +08:00
> make -j
2022-03-14 20:29:49 +08:00
2022-07-20 22:22:50 +08:00
You could choose which compiler you want to use:
2022-07-16 16:53:11 +08:00
2023-11-17 00:13:17 +08:00
> make nasal CXX=... -j
2022-07-16 16:53:11 +08:00
2022-01-25 15:02:57 +08:00
## __How to Use__
2021-06-11 15:16:06 +08:00
2022-09-13 23:00:48 +08:00
![usage ](./doc/gif/help.gif )
2022-02-14 17:29:26 +08:00
2022-09-28 23:45:15 +08:00
If your system is `Windows` and you want to output unicode, you could write this in nasal code:
2022-04-03 18:10:00 +08:00
```javascript
2023-11-04 00:09:59 +08:00
if (os.platform()=="windows") {
2022-04-03 18:10:00 +08:00
system("chcp 65001");
2023-11-04 00:09:59 +08:00
}
2022-04-03 18:10:00 +08:00
```
2022-01-25 15:02:57 +08:00
## __Difference Between Andy's and This Interpreter__
2022-09-13 23:00:48 +08:00
![error ](./doc/gif/error.gif )
2022-09-13 22:14:17 +08:00
< details >< summary > Must use `var` to define variables</ summary >
2021-08-01 01:54:14 +08:00
This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
2023-07-09 16:21:09 +08:00
And flightgear's nasal interpreter also has the same rule.
2023-07-10 20:34:21 +08:00
So do not use variable without using `var` to declare it.
2021-08-01 01:54:14 +08:00
In Andy's interpreter:
```javascript
2023-11-04 00:09:59 +08:00
foreach(i; [0, 1, 2, 3])
2021-08-01 01:54:14 +08:00
print(i)
```
2022-07-25 20:09:41 +08:00
This program can run normally.
But take a look at the iterator `i` ,
it is defined in foreach without using keyword `var` .
I think this design will make programmers feeling confused that they maybe hard to find the `i` is defined here.
Without `var` , they may think this `i` is defined anywhere else.
2021-08-01 01:54:14 +08:00
2022-07-25 20:09:41 +08:00
So in this interpreter i use a more strict syntax to force users to use `var` to define iterator of forindex and foreach.
If you forget to add the keyword `var` , you will get this:
2021-08-01 01:54:14 +08:00
```javascript
2022-09-13 22:14:17 +08:00
code: undefined symbol "i"
--> test.nas:1:9
|
2023-11-04 00:09:59 +08:00
1 | foreach(i; [0, 1, 2, 3])
2022-09-13 22:14:17 +08:00
| ^ undefined symbol "i"
code: undefined symbol "i"
--> test.nas:2:11
|
2 | print(i)
| ^ undefined symbol "i"
2021-08-01 01:54:14 +08:00
```
2022-09-13 22:14:17 +08:00
< / details >
2022-01-25 15:02:57 +08:00
## __Trace Back Info__
2021-08-10 17:55:49 +08:00
2022-09-13 23:00:48 +08:00
![stackoverflow ](./doc/gif/stackoverflow.gif )
2022-07-25 20:09:41 +08:00
When interpreter crashes,
2021-10-08 23:18:26 +08:00
it will print trace back information:
2021-08-10 17:55:49 +08:00
2022-09-13 22:14:17 +08:00
< details >< summary > Native function `die` </ summary >
2022-01-25 15:02:57 +08:00
Function `die` is used to throw error and crash immediately.
2021-08-10 17:55:49 +08:00
```javascript
2023-11-04 00:09:59 +08:00
func() {
2021-08-10 17:55:49 +08:00
println("hello");
die("error occurred this line");
return;
}();
```
```javascript
hello
[vm] error: error occurred this line
2023-11-04 00:09:59 +08:00
[vm] error: error occurred in native function
call trace (main)
call func@0x557513935710() {entry: 0x850}
trace back (main)
0x000547 4c 00 00 16 callb 0x16 < __die @ 0x557512441780 > (std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x5575138e8c40, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x5575138e8c50
0x00000a | nil |
0x000009 | nil |
0x000008 | str | < 0x5575138d9190 > error occurred t...
0x000007 | nil |
0x000006 | func | < 0x5575139356f0 > entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
2021-08-10 17:55:49 +08:00
```
2022-07-25 20:09:41 +08:00
< / details >
2022-09-13 23:00:48 +08:00
< details > < summary > Stack overflow< / summary >
2022-01-25 15:02:57 +08:00
2021-08-10 17:55:49 +08:00
Here is an example of stack overflow:
2021-10-31 23:11:04 +08:00
2021-08-10 17:55:49 +08:00
```javascript
2023-11-04 00:09:59 +08:00
func(f) {
2021-08-10 17:55:49 +08:00
return f(f);
}(
2023-11-04 00:09:59 +08:00
func(f) {
2021-08-10 17:55:49 +08:00
f(f);
}
)();
```
```javascript
2023-11-04 00:09:59 +08:00
[vm] error: stack overflow
call trace (main)
call func@0x564106058620(f) {entry: 0x859}
--> 583 same call(s)
call func@0x5641060586c0(f) {entry: 0x851}
trace back (main)
0x000859 45 00 00 01 calll 0x1(a.nas:5)
0x00085b 4a 00 00 01 callfv 0x1(a.nas:5)
0x00085b 582 same call(s)
0x000853 4a 00 00 01 callfv 0x1(a.nas:2)
0x00085f 4a 00 00 01 callfv 0x1(a.nas:3)
stack (0x56410600be00, limit 10, total 4096)
0x000fff | func | < 0x564106058600 > entry:0x859
0x000ffe | pc | 0x85b
0x000ffd | addr | 0x56410601bd20
0x000ffc | nil |
0x000ffb | nil |
0x000ffa | func | < 0x564106058600 > entry:0x859
0x000ff9 | nil |
0x000ff8 | func | < 0x564106058600 > entry:0x859
0x000ff7 | pc | 0x85b
0x000ff6 | addr | 0x56410601bcb0
2021-10-08 23:18:26 +08:00
```
2021-10-14 13:42:07 +08:00
2022-07-25 20:09:41 +08:00
< / details >
2022-09-13 22:14:17 +08:00
< details > < summary > Normal vm error crash info< / summary >
2022-01-25 15:02:57 +08:00
2021-10-14 13:42:07 +08:00
Error will be thrown if there's a fatal error when executing:
2021-10-31 23:11:04 +08:00
2021-10-14 13:42:07 +08:00
```javascript
2023-11-04 00:09:59 +08:00
func() {
2021-10-14 13:42:07 +08:00
return 0;
}()[1];
```
```javascript
2023-11-04 00:09:59 +08:00
[vm] error: must call a vector/hash/string but get number
trace back (main)
0x000854 47 00 00 00 callv 0x0(a.nas:3)
stack (0x564993f462b0, limit 10, total 1)
0x000000 | num | 0
2021-10-29 19:52:49 +08:00
```
2022-07-25 20:09:41 +08:00
< / details >
2022-01-25 15:02:57 +08:00
2022-09-13 22:14:17 +08:00
< details > < summary > Detailed crash info< / summary >
2021-10-31 23:11:04 +08:00
2022-07-25 20:09:41 +08:00
Use command __ `-d` __ or __ `--detail` __ the trace back info will show more details:
2022-07-24 17:05:36 +08:00
2021-10-29 19:52:49 +08:00
```javascript
2022-04-27 12:49:29 +08:00
hello
[vm] error: error occurred this line
2023-11-04 00:09:59 +08:00
[vm] error: error occurred in native function
call trace (main)
call func@0x55dcb5b8fbf0() {entry: 0x850}
2022-09-13 22:14:17 +08:00
trace back (main)
2023-11-04 00:09:59 +08:00
0x000547 4c 00 00 16 callb 0x16 < __die @ 0x55dcb3c41780 > (std/lib.nas:150)
0x000856 4a 00 00 01 callfv 0x1(a.nas:3)
0x00085a 4a 00 00 00 callfv 0x0(a.nas:5)
stack (0x55dcb5b43120, limit 10, total 14)
0x00000d | null |
0x00000c | pc | 0x856
0x00000b | addr | 0x55dcb5b43130
0x00000a | nil |
0x000009 | nil |
0x000008 | str | < 0x55dcb5b33670 > error occurred t...
0x000007 | nil |
0x000006 | func | < 0x55dcb5b8fbd0 > entry:0x850
0x000005 | pc | 0x85a
0x000004 | addr | 0x0
2022-09-13 22:14:17 +08:00
registers (main)
2023-11-04 00:09:59 +08:00
[pc ] | pc | 0x547
[global] | addr | 0x55dcb5b53130
[local ] | addr | 0x55dcb5b43190
[memr ] | addr | 0x0
[canary] | addr | 0x55dcb5b53110
[top ] | addr | 0x55dcb5b431f0
[funcr ] | func | < 0x55dcb5b65620 > entry:0x547
[upval ] | nil |
global (0x55dcb5b53130)
0x000000 | nmspc| < 0x55dcb5b33780 > namespace [95 val]
0x000001 | vec | < 0x55dcb5b64c20 > [0 val]
2022-07-20 00:06:00 +08:00
...
2023-11-04 00:09:59 +08:00
0x00005e | func | < 0x55dcb5b8fc70 > entry:0x846
local (0x55dcb5b43190 < +7>)
0x000000 | nil |
0x000001 | str | < 0x55dcb5b33670 > error occurred t...
0x000002 | nil |
2021-10-31 23:11:04 +08:00
```
2021-12-23 21:46:53 +08:00
2022-07-24 17:05:36 +08:00
< / details >
2022-01-25 15:02:57 +08:00
## __Debugger__
2021-12-23 21:46:53 +08:00
2022-09-13 23:00:48 +08:00
![dbg ](./doc/gif/dbg.gif )
2022-07-24 00:10:26 +08:00
We added a debugger in `v8.0` .
2021-12-23 21:46:53 +08:00
Use command `./nasal -dbg xxx.nas` to use the debugger,
and the debugger will print this:
2022-09-13 22:14:17 +08:00
< details > < summary > Click to unfold< / summary >
2021-12-23 21:46:53 +08:00
```javascript
source code:
2022-09-13 22:14:17 +08:00
--> var fib=func(x)
{
2022-07-24 00:10:26 +08:00
if(x< 2 ) return x ;
return fib(x-1)+fib(x-2);
2022-09-13 22:14:17 +08:00
}
for(var i=0;i< 31 ; i + = 1 )
2022-07-24 00:10:26 +08:00
print(fib(i),'\n');
2023-11-04 00:09:59 +08:00
2021-12-23 21:46:53 +08:00
next bytecode:
2023-11-04 00:09:59 +08:00
0x000848 4a 00 00 01 callfv 0x1(std/lib.nas:427)
0x000849 3d 00 00 00 pop 0x0(std/lib.nas:427)
0x00084a 07 00 00 00 pnil 0x0(std/lib.nas:423)
0x00084b 56 00 00 00 ret 0x0(std/lib.nas:423)
0x00084c 03 00 00 5e loadg 0x5e(std/lib.nas:423)
--> 0x00084d 0b 00 08 51 newf 0x851(test/fib.nas:1)
0x00084e 02 00 00 03 intl 0x3(test/fib.nas:1)
0x00084f 0d 00 00 08 para 0x8 (x)(test/fib.nas:1)
stack (0x55ccd0a1b9d0, limit 10, total 0)
2021-12-23 21:46:53 +08:00
>>
```
2022-09-13 22:14:17 +08:00
< / details >
2021-12-23 21:46:53 +08:00
If want help, input `h` to get help.
2022-01-25 15:02:57 +08:00
2022-02-13 16:10:02 +08:00
When running the debugger, you could see what is on stack.
This will help you debugging or learning how the vm works:
2022-09-13 22:14:17 +08:00
< details > < summary > Click to unfold< / summary >
2022-04-27 12:49:29 +08:00
```javascript
2022-02-13 16:10:02 +08:00
source code:
2022-09-13 22:14:17 +08:00
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');
2022-07-24 00:10:26 +08:00
2023-11-04 00:09:59 +08:00
2022-02-13 16:10:02 +08:00
next bytecode:
2023-11-04 00:09:59 +08:00
0x000850 3e 00 08 60 jmp 0x860(test/fib.nas:1)
--> 0x000851 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000852 39 00 00 07 lessc 0x7 (2)(test/fib.nas:3)
0x000853 40 00 08 56 jf 0x856(test/fib.nas:3)
0x000854 45 00 00 01 calll 0x1(test/fib.nas:3)
0x000855 56 00 00 00 ret 0x0(test/fib.nas:3)
0x000856 44 00 00 5f callg 0x5f(test/fib.nas:4)
0x000857 45 00 00 01 calll 0x1(test/fib.nas:4)
stack (0x55ccd0a1b9d0, limit 10, total 8)
0x000007 | pc | 0x869
0x000006 | addr | 0x0
0x000005 | nil |
0x000004 | nil |
0x000003 | num | 0
0x000002 | nil |
0x000001 | nil |
0x000000 | func | < 0x55ccd0a58fa0 > entry:0x487
2022-09-13 22:14:17 +08:00
>>
2022-02-13 16:10:02 +08:00
```
2022-09-13 22:14:17 +08:00
< / details >
2023-10-04 11:52:09 +08:00
## REPL
We added experimental repl interpreter in v11.0.
Use this command to use the repl interpreter:
> nasal -r
2023-10-07 22:51:30 +08:00
Then enjoy!
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.0 (Oct 7 2023 17:28:31)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>>
```
2023-11-04 00:09:59 +08:00
Try import `std/json.nas` ~
```bash
[nasal-repl] Initializating enviroment...
[nasal-repl] Initialization complete.
Nasal REPL interpreter version 11.1 (Nov 1 2023 23:37:30)
.h, .help | show help
.e, .exit | quit the REPL
.q, .quit | quit the REPL
.c, .clear | clear the screen
.s, .source | show source code
>>> use std.json;
{stringify:func(..) {..},parse:func(..) {..}}
>>>
```