update
This commit is contained in:
parent
bb746dfbfb
commit
125d6d3a7d
340
README.md
340
README.md
|
@ -6,49 +6,41 @@
|
|||
|
||||
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language) is a script language that used in [FlightGear](https://www.flightgear.org/).
|
||||
|
||||
There is a Nasal console in FlightGear but sometimes it is not so easy for every developer to use.
|
||||
The interpreter is still in development. We really need your support!
|
||||
|
||||
So this is an interpreter for Nasal written by C++.
|
||||
|
||||
The interpreter is still in development.We really need your support!
|
||||
Also,i am a member of [FGPRC](https://www.fgprc.org/), welcome to join us!
|
||||
|
||||
# Why Writing Nasal Interpreter
|
||||
|
||||
Nasal is a script language first used in Flightgear.
|
||||
|
||||
But in last summer holiday, members in FGPRC told me that it is hard to debug with nasal-console in Flightgear, especially when checking syntax error.
|
||||
But in last summer holiday, members in FGPRC 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.
|
||||
|
||||
I wrote the lexer, parser and runtime(nasal virtual machine/ast-runtime virtual machine) to help checking errors.
|
||||
I wrote the lexer, parser and runtime(bytecode virtual machine/ast-runtime virtual machine) to help checking errors.
|
||||
|
||||
They found it easier for them to check errors before copying nasal-codes in nasal-console in Flightgear to test.
|
||||
|
||||
# How to Compile
|
||||
|
||||
> g++ -std=c++11 main.cpp -o main.exe
|
||||
|
||||
# Lexical Analysis
|
||||
|
||||
The flow chart of lexer is here:
|
||||
|
||||
[![nasal_lexer.png](pic/nasal_lexer.png?raw=true)](https://github.com/ValKmjolnir/Nasal-Interpreter/blob/master/pic/nasal_lexer.png)
|
||||
|
||||
This picture seems ugly. I will re-draw it later(maybe 1000 years later).
|
||||
|
||||
# Parser
|
||||
|
||||
## Version 3.0
|
||||
LL(k) parser.
|
||||
|
||||
I refactored parser and make it easier to maintain.
|
||||
```javascript
|
||||
|
||||
The EBNF is also refactored.
|
||||
(var a,b,c)=[{b:nil},[1,2],func{return 0;}];
|
||||
|
||||
## Version 4.0
|
||||
(a.b,b[0],c)=(1,2,3);
|
||||
|
||||
Parser in this version will pre-calculate some mathematical equations.
|
||||
```
|
||||
|
||||
This will make bytecode vm running more quickly.
|
||||
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.
|
||||
|
||||
# Abstract Syntax Tree
|
||||
|
||||
|
@ -89,27 +81,307 @@ 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
|
||||
var (a,b,c)=(1,2,3);
|
||||
for(var i=0;i<4000000;i+=1);
|
||||
```
|
||||
|
||||
```asm
|
||||
.number 0
|
||||
.number 4e+006
|
||||
.number 1
|
||||
.number 2
|
||||
.number 3
|
||||
.symbol a
|
||||
.symbol b
|
||||
.symbol c
|
||||
0x00000000: pone 0x00000000
|
||||
0x00000001: load 0x00000000 (a)
|
||||
0x00000002: pnum 0x00000001 (2)
|
||||
0x00000003: load 0x00000001 (b)
|
||||
0x00000004: pnum 0x00000002 (3)
|
||||
0x00000005: load 0x00000002 (c)
|
||||
0x00000006: nop 0x00000000
|
||||
.symbol i
|
||||
0x00000000: pzero 0x00000000
|
||||
0x00000001: load 0x00000000 (i)
|
||||
0x00000002: call 0x00000000 (i)
|
||||
0x00000003: pnum 0x00000001 (4e+006)
|
||||
0x00000004: less 0x00000000
|
||||
0x00000005: jf 0x0000000b
|
||||
0x00000006: pone 0x00000000
|
||||
0x00000007: mcall 0x00000000 (i)
|
||||
0x00000008: addeq 0x00000000
|
||||
0x00000009: pop 0x00000000
|
||||
0x0000000a: jmp 0x00000002
|
||||
0x0000000b: nop 0x00000000
|
||||
```
|
||||
|
||||
## Version 5.0
|
||||
|
||||
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.
|
||||
|
||||
# How to Use Nasal to Program
|
||||
|
||||
## basic value type
|
||||
|
||||
nasal has 6 value types.Number,string,vector,hash,function,nil.
|
||||
|
||||
Number has 3 formats.Dec,hex and oct;
|
||||
|
||||
String has 3 formats.But the third one is often used to declare a character.
|
||||
|
||||
Vector has unlimited length and can store all types of values.
|
||||
|
||||
Hash is a hashmap that stores values with strings/identifiers as the key.
|
||||
|
||||
Function is also a value type in nasal.
|
||||
|
||||
```javascript
|
||||
var spc=nil;
|
||||
|
||||
var a=1;
|
||||
a=0x7fffffff;
|
||||
a=0o170001;
|
||||
|
||||
var b='str';
|
||||
b="another string";
|
||||
b=`c`;
|
||||
|
||||
var c=[];
|
||||
c=[0,nil,{},[],func(){return 0;}];
|
||||
append(c,0,1,2);
|
||||
|
||||
var d={
|
||||
member1:nil,
|
||||
member2:'str',
|
||||
'member3':'member\'s name can also be a string constant',
|
||||
"member4":"also this"
|
||||
};
|
||||
|
||||
var e=func(x,y,z)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
e=func
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
e=func(x,y,z,default_parameter1=1,default_parameter2=2)
|
||||
{
|
||||
return x+y+z+default_parameter1+default_parameter2;
|
||||
}
|
||||
e=func(x,y,z,dynamic_parameter...)
|
||||
{
|
||||
var sum=0;
|
||||
foreach(var i;dynamic_parameter)
|
||||
sum+=i;
|
||||
return sum+x+y+z;
|
||||
}
|
||||
```
|
||||
|
||||
## operators
|
||||
|
||||
```javascript
|
||||
1+2;
|
||||
1-2;
|
||||
1*2;
|
||||
1/2;
|
||||
'str1'~'str2';
|
||||
|
||||
1+1 and 0;
|
||||
1+2*3 or 0;
|
||||
1<0;
|
||||
1>0;
|
||||
1<=0;
|
||||
1>=0;
|
||||
1==0;
|
||||
1!=0;
|
||||
|
||||
-1;
|
||||
!0;
|
||||
|
||||
a=b=c=d=1;
|
||||
a+=1;
|
||||
a-=1;
|
||||
a*=1;
|
||||
a/=1;
|
||||
a~='string';
|
||||
```
|
||||
|
||||
## definition
|
||||
|
||||
```javascript
|
||||
var a=1;
|
||||
var (a,b,c)=[0,1,2];
|
||||
var (a,b,c)=(0,1,2);
|
||||
(var a,b,c)=[0,1,2];
|
||||
(var a,b,c)=(0,1,2);
|
||||
```
|
||||
|
||||
## multi-assignment
|
||||
|
||||
```javascript
|
||||
(a,b[0],c.d)=[0,1,2];
|
||||
(a,b[1],c.e)=(0,1,2);
|
||||
```
|
||||
|
||||
## conditional expression
|
||||
|
||||
```javascript
|
||||
if(1)
|
||||
{
|
||||
;
|
||||
}
|
||||
elsif(2)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if(3)
|
||||
{
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
;
|
||||
}
|
||||
```
|
||||
|
||||
## loop
|
||||
|
||||
```javascript
|
||||
while(condition)
|
||||
continue;
|
||||
|
||||
for(var i=0;i<10;i+=1)
|
||||
break;
|
||||
|
||||
forindex(var i;elem)
|
||||
print(elem[i]);
|
||||
|
||||
foreach(var i;elem)
|
||||
print(i);
|
||||
```
|
||||
|
||||
## subvec
|
||||
|
||||
```javascript
|
||||
a[-1,1,0:2,0:,:3,:,nil:8,3:nil,nil:nil];
|
||||
```
|
||||
|
||||
## special function call
|
||||
|
||||
```javascript
|
||||
a(x:0,y:1,z:2);
|
||||
```
|
||||
|
||||
## often used builtin functions
|
||||
|
||||
Must import lib.nas or has these functions' definitions inside your code.
|
||||
|
||||
Also you could add builtin functions of your own(written in C/C++) to help you calculate things more quickly.(Advanced usage)
|
||||
|
||||
```javascript
|
||||
|
||||
var import=func(filename)
|
||||
{
|
||||
nasal_call_import(filename);
|
||||
return nil;
|
||||
}
|
||||
var print=func(elements...)
|
||||
{
|
||||
nasal_call_builtin_std_cout(elements);
|
||||
return nil;
|
||||
};
|
||||
var append=func(vector,elements...)
|
||||
{
|
||||
nasal_call_builtin_push_back(vector,elements);
|
||||
return nil;
|
||||
}
|
||||
var setsize=func(vector,size)
|
||||
{
|
||||
nasal_call_builtin_set_size(vector,size);
|
||||
return nil;
|
||||
}
|
||||
var split=func(delimeter,string)
|
||||
{
|
||||
return nasal_call_builtin_split(delimeter,string);
|
||||
}
|
||||
var rand=func(seed=nil)
|
||||
{
|
||||
return nasal_call_builtin_rand(seed);
|
||||
}
|
||||
var id=func(thing)
|
||||
{
|
||||
return nasal_call_builtin_get_id(thing);
|
||||
}
|
||||
var int=func(value)
|
||||
{
|
||||
return nasal_call_builtin_trans_int(value);
|
||||
}
|
||||
var num=func(value)
|
||||
{
|
||||
return nasal_call_builtin_trans_num(value);
|
||||
}
|
||||
var pop=func(vector)
|
||||
{
|
||||
return nasal_call_builtin_pop_back(vector);
|
||||
}
|
||||
var str=func(number)
|
||||
{
|
||||
return nasal_call_builtin_trans_str(number);
|
||||
}
|
||||
var size=func(object)
|
||||
{
|
||||
return nasal_call_builtin_size(object);
|
||||
}
|
||||
var contains=func(hash,key)
|
||||
{
|
||||
return nasal_call_builtin_contains(hash,key);
|
||||
}
|
||||
var delete=func(hash,key)
|
||||
{
|
||||
nasal_call_builtin_delete(hash,key);
|
||||
return;
|
||||
}
|
||||
var keys=func(hash)
|
||||
{
|
||||
return nasal_call_builtin_get_keys(hash);
|
||||
}
|
||||
var die=func(str)
|
||||
{
|
||||
nasal_call_builtin_die(str);
|
||||
return nil;
|
||||
}
|
||||
var typeof=func(object)
|
||||
{
|
||||
return nasal_call_builtin_type(object);
|
||||
}
|
||||
var substr=func(str,begin,length)
|
||||
{
|
||||
return nasal_call_builtin_substr(str,begin,length);
|
||||
}
|
||||
var math=
|
||||
{
|
||||
e:2.7182818284590452354,
|
||||
pi:3.14159265358979323846264338327950288,
|
||||
sin:func(x)
|
||||
{
|
||||
return nasal_call_builtin_sin(x);
|
||||
},
|
||||
cos:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cos(x);
|
||||
},
|
||||
tan:func(x)
|
||||
{
|
||||
return nasal_call_builtin_tan(x);
|
||||
},
|
||||
exp:func(x)
|
||||
{
|
||||
return nasal_call_builtin_exp(x);
|
||||
},
|
||||
ln:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cpp_math_ln(x);
|
||||
},
|
||||
sqrt:func(x)
|
||||
{
|
||||
return nasal_call_builtin_cpp_math_sqrt(x);
|
||||
},
|
||||
atan2:func(x,y)
|
||||
{
|
||||
return nasal_call_builtin_cpp_atan2(x,y);
|
||||
},
|
||||
};
|
||||
```
|
Binary file not shown.
Before Width: | Height: | Size: 62 KiB |
Loading…
Reference in New Issue