update README.md

This commit is contained in:
ValKmjolnir 2022-01-25 15:02:57 +08:00
parent 5778d1e38d
commit b92eb4b089
1 changed files with 231 additions and 144 deletions

375
README.md
View File

@ -1,30 +1,38 @@
# Nasal Script Language
# __Nasal Script Language__
## Contents
```C++
__ _
/\ \ \__ _ ___ __ _| |
/ \/ / _` / __|/ _` | |
/ /\ / (_| \__ \ (_| | |
\_\ \/ \__,_|___/\__,_|_|
```
* [Introduction](#introduction)
* [Why Writing Nasal Interpreter](#why-writing-nasal-interpreter)
* [Compile](#how-to-compile)
* [Usage](#how-to-use)
* [Parser](#parser)
## __Contents__
* [__Introduction__](#introduction)
* [__Why Writing Nasal Interpreter__](#why-writing-nasal-interpreter)
* [__Compile__](#how-to-compile)
* [__Usage__](#how-to-use)
* [__Parser__](#parser)
* [v1.0](#version-10-parser-last-update-20191014)
* [Abstract Syntax Tree](#abstract-syntax-tree)
* [__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)
* [__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-latest)
* [Benchmark](#benchmark)
* [__Benchmark__](#benchmark)
* [v6.5 (i5-8250U windows 10)](#version-65-i5-8250u-windows10-2021619)
* [v6.5 (i5-8250U ubuntu-WSL)](#version-70-i5-8250u-ubuntu-wsl-on-windows10-2021629)
* [v8.0 (R9-5900HX ubuntu-WSL)](#version-80-r9-5900hx-ubuntu-wsl-2022123)
* [Tutorial](#simple-tutorial)
* [__Tutorial__](#tutorial)
* [basic value type](#basic-value-type)
* [operators](#operators)
* [definition](#definition)
@ -37,30 +45,43 @@
* [closure](#closure)
* [trait](#trait)
* [native functions](#native-functions)
* [modules](#modulesthis-is-for-library-developers)
* [Difference](#difference-between-andys-nasal-interpreter-and-this-interpreter)
* [Trace Back Info](#trace-back-info)
* [Debugger](#debugger)
* [modules](#modulesfor-library-developers)
* [__Difference__](#difference-between-andys-and-this-interpreter)
* [strict definition](#1-must-use-var-to-define-variables)
* [(outdated)use after definition](#2-now-supported-couldnt-use-variables-before-definitions)
* [default dynamic arguments](#3-default-dynamic-arguments-not-supported)
* [__Trace Back Info__](#trace-back-info)
* [native function 'die'](#1-native-function-die)
* [stack overflow](#2-stack-overflow-crash-info)
* [runtime error](#3-normal-vm-error-crash-info)
* [detailed crash info](#4-detailed-crash-info)
* [__Debugger__](#debugger)
## Introduction
__Contact us if having great ideas to share!__
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
is an ECMAscript-like programming language that used in [FlightGear](https://www.flightgear.org/).
This language is designed by [Andy Ross](https://github.com/andyross).
* __E-mail__: lhk101lhk101@qq.com
The interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using C++(`-std=c++11`)
without reusing the code in [Andy Ross's nasal interpreter](<https://github.com/andyross/nasal>).
* __QQ__: 896693328
## __Introduction__
__[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)__
is an ECMAscript-like programming language that used in __[FlightGear](https://www.flightgear.org/)__.
This language is designed by __[Andy Ross](https://github.com/andyross)__.
The interpreter is totally rewritten by __[ValKmjolnir](https://github.com/ValKmjolnir)__ using `C++`(`-std=c++11`)
without reusing the code in __[Andy Ross's nasal interpreter](<https://github.com/andyross/nasal>)__.
But we really appreciate that Andy created this amazing programming language and his interpreter project.
Now this project uses MIT license (2021/5/4).
Now this project uses __MIT license__ (2021/5/4).
Edit it if you want,
use this project to learn or create more interesting things
(But don't forget me XD).
## Why Writing Nasal Interpreter
## __Why Writing Nasal Interpreter__
In 2019 summer holiday,
members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear,
members in __[FGPRC](https://www.fgprc.org/)__ told me that it is hard to debug with nasal-console in Flightgear,
especially when checking syntax errors.
So i tried to write a new interpreter to help them checking syntax error and even, runtime error.
@ -76,25 +97,25 @@ interesting programs and run them without the lib of Flightgear.
You could add your own modules to make
this interpreter a useful tool in your own projects (such as a script in a game just as Flightgear does).
## How to Compile
## __How to Compile__
Better choose the latest update of the interpreter.
Download the source code and build it!
It's quite easy to build this interpreter.
MUST USE `-O2/-O3` if want to optimize the interpreter!
__MUST USE__ __`-O2/-O3`__ if want to optimize the interpreter!
Also remember to use g++ or clang++.(mingw-w64 in Windows)
Also remember to use g++ or clang++.(mingw-w64 in __`Windows`__)
> [cpp compiler] -std=c++11 -O3 main.cpp -o nasal.exe -fno-exceptions
Or use this in linux/macOS/Unix
Or use this in __`linux/macOS/Unix`__
> [cpp compiler] -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl
## How to Use?
## __How to Use__
Input this command to run scripts directly:
Input this command to run scripts __directly__:
> ./nasal filename
@ -102,28 +123,66 @@ Use these commands to get version of interpreter:
> ./nasal -v | --version
```bash
__ _
/\ \ \__ _ ___ __ _| |
/ \/ / _` / __|/ _` | |
/ /\ / (_| \__ \ (_| | |
\_\ \/ \__,_|___/\__,_|_|
nasal interpreter ver 8.0
thanks to : https://github.com/andyross/nasal
code repo : https://github.com/ValKmjolnir/Nasal-Interpreter
code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter
lang info : http://wiki.flightgear.org/Nasal_scripting_language
input <nasal -h> to get help .
```
Use these commands to get help(see more debug commands in help):
> ./nasal -h | --help
If your system is Windows and you want to output unicode,please use this command before running nasal interpreter:
```bash
nasal <option>
option:
-h, --help | get help.
-v, --version | get version of nasal interpreter.
nasal <file>
file:
input file name to execute script file.
nasal [options...] <file>
option:
-l, --lex | view token info.
-a, --ast | view abstract syntax tree.
-c, --code | view bytecode.
-t, --time | execute and get the running time.
-o, --opcnt | execute and count used operands.
-d, --detail | execute and get detail crash info.
-op, --optimize| use optimizer(beta).
-dbg, --debug | debug mode (this will ignore -t -o -d).
file:
input file name to execute script file.
```
If your system is __`Windows`__ and you want to output unicode,please use this command before running nasal interpreter:
> chcp 65001
The interpreter's interactive mode will do this automatically,so you don't need to run this command if you use the interactive interpreter.
## Parser
## __Parser__
LL(k) parser.
`LL(k)` parser.
```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.
These two expressions have the same first set,so `LL(1)` is useless for this language.
Maybe in the future i can refactor it to LL(1) with special checks.
Maybe in the future i can refactor it to `LL(1)` with special checks.
Problems mentioned above have been solved for a long time, but recently i found a new problem here:
@ -151,29 +210,29 @@ 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)
### version 1.0 parser (last update 2019/10/14)
First fully functional version of nasal_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).
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/8/31.
__This project began at 2019/8/31__.
## Abstract Syntax Tree
## __Abstract Syntax Tree__
### Version 1.2 ast (last update 2019/10/31)
### 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)
### 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)
### version 3.0 ast (last update 2020/10/23)
The ast is refactored and is now easier to read and maintain.
@ -183,7 +242,7 @@ 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)
### version 5.0 ast (last update 2021/3/7)
I change my mind.
AST interpreter leaves me too much things to do.
@ -191,15 +250,17 @@ 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
## __Bytecode Virtual Machine__
### Version 4.0 vm (last update 2020/12/17)
### version 4.0 vm (last update 2020/12/17)
I have just finished the first version of byte-code-interpreter.
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!
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! :)
Now i am trying to search hidden bugs in this interpreter.
Hope you could help me! :)
There's an example of byte code below:
@ -226,31 +287,31 @@ for(var i=0;i<4000000;i+=1);
0x0000000b: nop 0x00000000
```
### Version 5.0 vm (last update 2021/3/7)
### 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!
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.
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)
### version 6.0 vm (last update 2021/6/1)
Use loadg loadl callg calll mcallg mcalll to avoid branches.
Use `loadg`/`loadl`/`callg`/`calll`/`mcallg`/`mcalll` to avoid branches.
Delete type vm_scop.
Delete type `vm_scop`.
Use const vm_num to avoid frequently new & delete.
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.
`vapp` and `newf` operand use .num to reduce the size of `exec_code`.
2021/4/3 update: Now it can count from 0 to 4000000-1 in 0.8s.
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.
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.
In this update i changed global and local scope from `unordered_map` to `vector`.
So the bytecode generator changed a lot.
@ -275,30 +336,30 @@ for(var i=0;i<4000000;i+=1);
0x0000000c: nop 0x00000000
```
### Version 6.5 vm (last update 2021/6/24)
### 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,
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.
`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,
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,
Better use `callfv` instead of `callfh`,
`callfh` will fetch a `vm_hash` from stack and parse it,
making this process slow.
```javascript
@ -356,8 +417,8 @@ parser will check this left-value and tells that these kinds of left-value are n
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 nasal_codegen::call_gen() instead of nasal_codegen::mcall_gen(),
and the last child of the ast will be generated by nasal_codegen::mcall_gen().
codegen will generate byte code by `nasal_codegen::call_gen()` instead of `nasal_codegen::mcall_gen()`,
and the last child of the ast will be generated by `nasal_codegen::mcall_gen()`.
So the bytecode is totally different now:
```x86asm
@ -422,16 +483,16 @@ So the bytecode is totally different now:
```
As you could see from the bytecode above,
mcall/mcallv/mcallh operands' using frequency will reduce,
call/callv/callh/callfv/callfh at the opposite.
`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 nasal_vm,
and now nasal_vm use nasal_val** mem_addr to store the memory address.
And because of the new structure of `mcall`,
`addr_stack`, a stack used to store the memory address,
is deleted from `nasal_vm`,
and now `nasal_vm` use `nasal_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)
### version 7.0 vm (last update 2021/10/8)
2021/6/26 update:
@ -448,13 +509,13 @@ which is also supported by clang++.
(But i don't know if MSVC supports this)
There is also a change in nasal_gc:
std::vector global is deleted,
now the global values are all stored on stack(from val_stack+0 to val_stack+intg-1).
`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.
`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:
@ -479,8 +540,8 @@ 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.
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);
@ -501,20 +562,20 @@ a=b=0;
0x00000009: nop 0x00000000
```
### Version 8.0 vm (latest)
### version 8.0 vm (latest)
2021/10/8 update:
In this version vm_nil and vm_num now is not managed by nasal_gc,
this will decrease the usage of gc::alloc and increase the efficiency of execution.
In this version vm_nil and vm_num now is not managed by `nasal_gc`,
this will decrease the usage of `gc::alloc` and increase the efficiency of execution.
New value type is added: vm_obj.
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'.
Delete an old operand `op_offset`.
2021/10/13 update:
@ -568,7 +629,7 @@ Both of them are meaningless and will be replaced by `op_pnum`.
## Benchmark
### Version 6.5 (i5-8250U windows10 2021/6/19)
### version 6.5 (i5-8250U windows10 2021/6/19)
running time and gc time:
@ -615,7 +676,7 @@ operands calling total times:
|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)
### version 7.0 (i5-8250U ubuntu-WSL on windows10 2021/6/29)
running time:
@ -632,7 +693,7 @@ running time:
|quick_sort.nas|0s|great improvement|
|bfs.nas|0.0156s|great improvement|
### Version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)
### version 8.0 (R9-5900HX ubuntu-WSL 2022/1/23)
running time:
@ -650,13 +711,13 @@ running time:
|ascii-art.nas|0s||
|quick_sort.nas|0s||
## Simple Tutorial
## __Tutorial__
Nasal is really easy to learn.
Nasal is really __easy__ to learn.
Reading this tutorial will not takes you over 15 minutes.
You could totally use it after reading this simple tutorial:
### __Basic Value Type__
### __basic value type__
`vm_none` is error type.
This type is used to interrupt the execution of virtual machine and will not be created by user program.
@ -667,7 +728,7 @@ This type is used to interrupt the execution of virtual machine and will not be
var spc=nil;
```
`vm_num` has 3 formats. Dec, hex and oct. Using IEEE754 double to store.
`vm_num` has 3 formats: `dec`, `hex` and `oct`. Using IEEE754 double to store.
```javascript
var n=1;
@ -679,7 +740,7 @@ var n=0xAA55;
var n=0o170001;
```
`vm_str` has 3 formats. But the third one is often used to declare a character.
`vm_str` has 3 formats. The third one is used to declare a character.
```javascript
var s='str';
@ -701,7 +762,7 @@ var vec=[
append(vec,0,1,2);
```
`vm_hash` is a hashmap that stores values with strings/identifiers as the key.
`vm_hash` is a hashmap(or like a dict in `python`) that stores values with strings/identifiers as the key.
```javascript
var hash={
@ -749,7 +810,7 @@ var my_new_obj=func(){
var obj=my_new_obj();
```
### __Operators__
### __operators__
Nasal has basic math operators `+` `-` `*` `/` and a special operator `~` that links two strings together.
@ -787,7 +848,7 @@ a/=1;
a~='string';
```
### __Definition__
### __definition__
```javascript
var a=1;
@ -797,7 +858,9 @@ var (a,b,c)=(0,1,2);
(var a,b,c)=(0,1,2);
```
### __Multi-Assignment__
### __multi-assignment__
The last one is often used to swap two variables.
```javascript
(a,b[0],c.d)=[0,1,2];
@ -805,7 +868,7 @@ var (a,b,c)=(0,1,2);
(a,b)=(b,a);
```
### __Conditional Expression__
### __conditional expression__
In nasal there's a new key word `elsif`.
It has the same functions as `else if`.
@ -822,7 +885,7 @@ if(1){
}
```
### __Loop__
### __loop__
While loop and for loop is simalar to C/C++.
@ -836,31 +899,31 @@ for(var i=0;i<10;i+=1)
Nasal has another two kinds of loops that iterates through a vector:
`forindex` will get the index of a vector.
`forindex` will get the index of a vector. Index will be `0` to `size(elem)-1`.
```javascript
forindex(var i;elem)
print(elem[i]);
```
`foreach` will get the element of a vector.
`foreach` will get the element of a vector. Element will be `elem[0]` to `elem[size(elem)-1]`.
```javascript
foreach(var i;elem)
print(i);
```
### __Subvec__
### __subvec__
Use index to search one element in the string will get the ascii number of this character.
If you want to get the character,use built-in function chr().
Use index to search one element in the string will get the __ascii number__ of this character.
If you want to get the character, use built-in function chr().
```javascript
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil];
"hello world"[0];
```
### __Special Function Call__
### __special function call__
This is of great use but is not very efficient
(because hashmap use string as the key to compare).
@ -869,7 +932,7 @@ This is of great use but is not very efficient
f(x:0,y:nil,z:[]);
```
### __Lambda__
### __lambda__
Also functions have this kind of use:
@ -878,7 +941,7 @@ func(x,y){return x+y}(0,1);
func(x){return 1/(1+math.exp(-x));}(0.5);
```
There's an interesting test file 'y-combinator.nas',
There's an interesting test file `y-combinator.nas`,
try it for fun:
```javascript
@ -894,7 +957,7 @@ var fib=func(f){
);
```
### __Closure__
### __closure__
Closure means you could get the variable that is not in the local scope of a function that you called.
Here is an example, result is `1`:
@ -922,13 +985,13 @@ var student=func(n,a){
}
```
### __Trait__
### __trait__
Also there's another way to OOP,that is `trait`.
Also there's another way to OOP, that is `trait`.
When a hash has a member named `parents` and the value type is vector,
then when you are trying to find a member that is not in this hash,
virtual machine will search the member is parents.
virtual machine will search the member in `parents`.
If there is a hash that has the member, you will get the member's value.
Using this mechanism, we could OOP like this, the result is `114514`:
@ -959,22 +1022,20 @@ a.set(114514);
println(a.get());
```
### __Native Functions__
### __native functions__
You could add builtin functions of your own
(written in C/C++) to help you calculate things more quickly.
(Advanced usage)
But you should add your own code into the source code of this interpreter and re-compile it.
Check built-in functions in lib.nas!
You could use this file as the example to learn.
If you want to add your own functions __without__ changing the source code of the interpreter, see the __`module`__ after this part.
If you want to add your own built-in functions,
define the function in nasal_builtin.h. (or any other place, but remember to compile it)
If you really want to change source code, check built-in functions in lib.nas and see the example below.
Definition:
```C++
nasal_ref builtin_chr(std::vector<nasal_ref>&,nasal_gc&);
nasal_ref builtin_print(std::vector<nasal_ref>&,nasal_gc&);
```
Then complete this function using C++:
@ -1040,22 +1101,22 @@ var print=func(elems...){
If you don't warp built-in function in a normal nasal function,
this built-in function may cause a fault when searching arguments,
which will segmentation error.
which will cause __segmentation error__.
Use `import(".nas")` to get the nasal file including your built-in functions,
Use `import("filename.nas")` to get the nasal file including your built-in functions,
then you could use it.
version 6.5 update:
Use nasal_gc::builtin_alloc in builtin function if this function uses alloc more than one time.
Use `gc::builtin_alloc` in builtin function if this function uses alloc more than one time.
When running a builtin function,alloc will run more than one time,
this may cause mark-sweep in gc::alloc.
this may cause mark-sweep in `gc::alloc`.
The value got before will be collected,but stil in use in this builtin function,
this is a fatal error.
So use builtin_alloc in builtin functions like this:
So use `builtin_alloc` in builtin functions like this:
```C++
nasal_ref builtin_keys(std::vector<nasal_ref>& local,nasal_gc& gc)
@ -1080,7 +1141,7 @@ nasal_ref builtin_keys(std::vector<nasal_ref>& local,nasal_gc& gc)
}
```
### __Modules(This is for library developers)__
### __modules(for library developers)__
If there is only one way to add your own functions into nasal,
that is really inconvenient.
@ -1146,11 +1207,11 @@ Windows(`.dll`):
`g++ -shared -o libfib.dll fib.o`
Then we write a test nasal file to run this fib function, this example runs on Linux:
Then we write a test nasal file to run this fib function, using `os.platform()` we could write a program that runs on three different OS:
```javascript
import("lib.nas");
var dlhandle=dylib.dlopen("./module/libfib.so");
var dlhandle=dylib.dlopen("./module/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));
@ -1200,7 +1261,9 @@ If get this, Congratulations!
832040
```
## Difference Between Andy's Nasal Interpreter and This Interpreter
## __Difference Between Andy's and This Interpreter__
### 1. must use `var` to define variables
This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
@ -1217,7 +1280,7 @@ But take a look at the iterator 'i',
this symbol is defined in foreach without using keyword 'var'.
I think this design will make programmers filling confused.
This is ambiguous that programmers maybe difficult to find the 'i' is defined here.
Without 'var',programmers may think this 'i' is defined anywhere else.
Without 'var', programmers may think this 'i' is defined anywhere else.
So in this new 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',
@ -1231,7 +1294,9 @@ foreach(i;[0,1,2,3])
print(i)
```
Also there's another difference.
### 2. (now supported) couldn't use variables before definitions
(__Outdated__: this is now supported) Also there's another difference.
In Andy's interpreter:
```javascript
@ -1244,11 +1309,11 @@ This program runs normally with output 1.
But in this new interpreter, it will get:
```javascript
[code] test.nas:1 undefined symbol "print".
[code] test.nas:1 undefined symbol "b".
var a=func {print(b);}
```
(outdated)This difference is caused by different kinds of ways of lexical analysis.
This difference is caused by different kinds of ways of lexical analysis.
In most script language interpreters,
they use dynamic analysis to check if this symbol is defined yet.
However, this kind of analysis is at the cost of lower efficiency.
@ -1267,16 +1332,22 @@ So this difference does not exist from this update.__
But a new difference is that if you call a variable before defining it,
you'll get nil instead of 'undefined error'.
### 3. default dynamic arguments not supported
In this new interpreter,
function doesn't put dynamic arguments into vector 'arg' automatically.
So if you use 'arg' without definition,
you'll get an error of 'undefined symbol'.
function doesn't put dynamic arguments into vector `arg` automatically.
So if you use `arg` without definition,
you'll get an error of `undefined symbol`.
## Trace Back Info
## __Trace Back Info__
Now when the interpreter crashes,
When the interpreter crashes,
it will print trace back information:
### 1. native function `die`
Function `die` is used to throw error and crash immediately.
```javascript
func()
{
@ -1286,8 +1357,6 @@ func()
}();
```
Function 'die' is used to throw error and crash.
```javascript
hello
[vm] error: error occurred this line
@ -1304,6 +1373,8 @@ vm stack(limit 10, total 5):
| func | <0x6c8910> entry:0x2a9
```
### 2. stack overflow crash info
Here is an example of stack overflow:
```javascript
@ -1316,8 +1387,6 @@ func(f){
)();
```
And the trace back info:
```javascript
[vm] stack overflow
trace back:
@ -1339,6 +1408,8 @@ vm stack(limit 10, total 4095):
| addr | pc:0xf
```
### 3. normal vm error crash info
Error will be thrown if there's a fatal error when executing:
```javascript
@ -1347,8 +1418,6 @@ func(){
}()[1];
```
And the trace back info:
```javascript
[vm] callv: must call a vector/hash/string
trace back:
@ -1357,7 +1426,9 @@ vm stack(limit 10, total 1):
| num | 0
```
Use command `-d` or `--detail` the trace back info will be this:
### 4. detailed crash info
Use command __`-d`__ or __`--detail`__ the trace back info will show more details:
```javascript
hello world
@ -1395,7 +1466,7 @@ local:
no upvalue exists
```
## Debugger
## __Debugger__
In nasal v8.0 we added a debugger.
Now we could see both source code and bytecode when testing program.
@ -1429,3 +1500,19 @@ vm stack(limit 5, total 0)
```
If want help, input `h` to get help.
```bash
>> h
<option>
h, help | get help
bt, backtrace | get function call trace
c, continue | run program until break point or exit
g, global | see global values
l, local | see local values
u, upval | see upvalue
a, all | show global,local and upvalue
n, next | execute next bytecode
q, exit | exit debugger
<option> <filename> <line>
bk, break | set break point
```