vm_nil,vm_num changed to no-gcobject

This commit is contained in:
ValKmjolnir 2021-10-08 23:18:26 +08:00
parent d71b4f09e2
commit 1733ac0573
5 changed files with 695 additions and 585 deletions

224
README.md
View File

@ -2,31 +2,46 @@
## Introduction
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language) is a script language that used in [FlightGear](https://www.flightgear.org/).
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
is a script language that used in [FlightGear](https://www.flightgear.org/).
The interpreter is totally rewritten by ValKmjolnir using C++(standard 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.
The interpreter is totally rewritten by ValKmjolnir using C++(standard 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.
The interpreter is still in development(now it works well --2021/2/15). We really need your support!
The interpreter is still in development(now it works well --2021/2/15)
We really need your support!
Also,i am a member of [FGPRC](https://www.fgprc.org/), welcome to join us!
Also,i am a member of [FGPRC](https://www.fgprc.org/),
welcome to join us!
(2021/5/4) Now this project uses MIT license.Edit it if you want, use this project to learn or create more interesting things(But don't forget me XD).
(2021/5/4) Now this project uses MIT license.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
Nasal is a script language first used in Flightgear, created by Andy Ross(<https://github.com/andyross>).
Nasal is a script language first used in Flightgear,
created by Andy Ross(<https://github.com/andyross>).
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.
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.
So i tried to write a new interpreter to help them checking syntax error and even,
runtime error.
I wrote the lexer, parser and runtimebytecode virtual machine(there was an ast-interpreter,but i deleted it after version4.0) to help checking errors.
I wrote the lexer,
parser and runtimebytecode virtual machine(there was an ast-interpreter,
but i deleted it after version4.0) to help checking errors.
They found it much easier to check syntax and runtime errors before copying nasal-codes in nasal-console in Flightgear to test.
They found it much easier to check syntax and runtime
errors before copying nasal-codes in nasal-console in Flightgear to test.
Also, you could use this language to write some interesting programs and run them without the lib of Flightgear.
Also, you could use this language to write some
interesting programs and run them without the lib of Flightgear.
You could add your own built-in functions to change this interpreter to a useful tool in your own projects(such as a script in your own game).
You could add your own built-in functions to change
this interpreter to a useful tool in your own projects(such as a script in your own game).
## How to Compile
@ -139,7 +154,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.
## Byte Code Interpreter
## Byte Code VM
### Version 4.0 (last update 2020/12/17)
@ -225,17 +240,29 @@ for(var i=0;i<4000000;i+=1);
### Version 6.5 (last update 2021/6/24)
2021/5/31 update: Now gc can collect garbage correctly without re-collecting,which will cause fatal error.
2021/5/31 update:
Add builtin_alloc to avoid mark-sweep when running a built-in function,which will mark useful items as useless garbage to collect.
Now gc can collect garbage correctly without re-collecting,
which will cause fatal error.
Better use setsize and assignment to get a big array,append is very slow in this situation.
Add builtin_alloc to avoid mark-sweep when running a built-in function,
which will mark useful items as useless garbage to collect.
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.
Better use setsize and assignment to get a big array,
append is very slow in this situation.
Change callf to callfv and callfh.And callfv fetches arguments from val_stack directly instead of using vm_vec,a not very efficient way.
2021/6/3 update:
Better use callfv instead of callfh,callfh will fetch a vm_hash from stack and parse it,making this process slow.
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;}
@ -267,7 +294,9 @@ f(1024,2048);
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:
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()
@ -284,9 +313,15 @@ 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).
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 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:
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().
So the bytecode is totally different now:
```asm
.number 10
@ -349,23 +384,40 @@ But now it can work.And you could see its use by reading the code above.To make
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.
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 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.
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 (latest)
### version 7.0 (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 nasal_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.
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 nasal_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)
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 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).
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).
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.
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:
@ -386,9 +438,12 @@ Now the bytecode of test/bigloop.nas seems like this:
0x0000000b: nop 0x00000000
```
And this test file runs in 0.1s after this update.Most of the calculations are accelerated.
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.
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);
@ -409,6 +464,17 @@ a=b=0;
0x00000009: nop 0x00000000
```
### version 8.0 (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.
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.
## Test data
### version 6.5(i5-8250U windows10 2021/6/19)
@ -622,7 +688,8 @@ foreach(var i;elem)
### 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];
@ -687,36 +754,37 @@ If you want to add your own built-in functions,define the function in nasal_buil
Definition:
```C++
nasal_val* builtin_chr(std::vector<nasal_val*>&,nasal_gc&);
nasal_ref builtin_chr(std::vector<nasal_ref>&,nasal_gc&);
```
Then complete this function using C++:
```C++
nasal_val* builtin_print(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
nasal_ref builtin_print(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
// get arguments by using builtin_find
// find value with index begin from 1
// because local_scope[0] is reserved for value 'me'
nasal_val* vector_value=local_scope[1];
nasal_ref vec_addr=local_scope[1];
// main process
// also check number of arguments and type here
// if get a type error,use builtin_err and return nullptr
for(auto i:vec_addr->ptr.vec->elems)
switch(i->type)
for(auto i:vec_addr.vec()->elems)
switch(i.type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<*i.str(); break;
case vm_vec: i.vec()->print(); break;
case vm_hash: i.hash()->print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
case vm_obj: std::cout<<"<obj>"; break;
}
std::cout<<std::flush;
// if a nasal value is not in use,use gc::del_reference to delete it
// generate return value,use gc::gc_alloc(type) to make a new value
// or use reserved reference gc.nil_addr/gc.one_addr/gc.zero_addr
return gc.nil_addr;
// or use reserved reference gc.nil/gc.one/gc.zero
return gc.nil;
}
```
@ -726,7 +794,7 @@ After that, write the built-in function's name(in nasal) and the function's poin
struct FUNC_TABLE
{
const char* name;
nasal_val* (*func)(std::vector<nasal_val*>&,nasal_gc&);
nasal_ref (*func)(std::vector<nasal_ref>&,nasal_gc&);
} builtin_func[]=
{
{"__builtin_print",builtin_print},
@ -752,38 +820,46 @@ var print=func(elems...)
};
```
In version 5.0,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 cause SIGSEGV segmentation error(maybe).
In version 5.0,
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 cause SIGSEGV segmentation error(maybe).
Use import("") to get the nasal file including your built-in functions,then you could use it.
Use import("") 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.
When running a builtin function,alloc will run more than one time,this may cause mark-sweep in gc_alloc.
When running a builtin function,alloc will run more than one time,
this may cause mark-sweep in gc_alloc.
The value got before will be collected,but stil in use in this builtin function,this is a fatal error.
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:
```C++
nasal_val* builtin_keys(std::vector<nasal_val*>& local_scope,nasal_gc& gc)
nasal_ref builtin_keys(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_val* hash_addr=local_scope[1];
if(hash_addr->type!=vm_hash)
nasal_ref hash_addr=local_scope[1];
if(hash_addr.type!=vm_hash)
{
builtin_err("keys","\"hash\" must be hash");
return nullptr;
return nasal_ref(vm_none);
}
nasal_val* ret_addr=gc.builtin_alloc(vm_vec);
std::vector<nasal_val*>& ref_vec=ret_addr->ptr.vec->elems;
for(auto iter:hash_addr->ptr.hash->elems)
// push vector into local scope to avoid being sweeped
local_scope.push_back(gc.gc_alloc(vm_vec));
std::vector<nasal_ref>& vec=local_scope.back().vec()->elems;
for(auto iter:hash_addr.hash()->elems)
{
nasal_val* str_addr=gc.builtin_alloc(vm_str);
*str_addr->ptr.str=iter.first;
ref_vec.push_back(str_addr);
nasal_ref str_addr=gc.builtin_alloc(vm_str);
*str_addr.str()=iter.first;
vec.push_back(str_addr);
}
return ret_addr;
return local_scope.back();
}
```
@ -799,13 +875,16 @@ foreach(i;[0,1,2,3])
```
This program can run normally with output 0 1 2 3.
But take a look at the iterator 'i',this symbol is defined in foreach without using keyword 'var'.
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.
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', and you haven't defined this symbol before, you will get this:
If you forget to add the keyword 'var',
and you haven't defined this symbol before,
you will get this:
```javascript
[code] <test.nas> line 1: undefined symbol "i".
@ -830,26 +909,33 @@ But in this new interpreter, it will get:
```
(outdated)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.
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.
To make sure the interpreter runs at higher efficiency, i choose static analysis to manage the memory space of each symbol.
To make sure the interpreter runs at higher efficiency,
i choose static analysis to manage the memory space of each symbol.
By this way, runtime will never need to check if a symbol exists or not.
But this causes a difference.
You will get an error of 'undefined symbol', instead of nothing happening in most script language interpreters.
You will get an error of 'undefined symbol',
instead of nothing happening in most script language interpreters.
This change is __controversial__ among FGPRC's members.
So maybe in the future i will use dynamic analysis again to cater to the habits of senior programmers.
(2021/8/3 update) __Now i use scanning ast twice to reload symbols.
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'.
But a new difference is that if you call a variable before defining it,
you'll get nil instead of 'undefined error'.
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'.
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'.
## Trace Back Info
Now when the interpreter crashes,it will print trace back information:
Now when the interpreter crashes,
it will print trace back information:
```javascript
func()
@ -907,4 +993,4 @@ trace back:
vm stack(limit 10):
0x7fcc3110ad00 func | func(1 para){..}
0x7fcc3110ad00 ... | 9 same value(s)
```
```

View File

@ -34,7 +34,7 @@ void logo()
<<" / \\/ / _` / __|/ _` | | \n"
<<" / /\\ / (_| \\__ \\ (_| | | \n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"nasal interpreter ver 7.0\n"
<<"nasal interpreter ver 8.0\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"

View File

@ -116,110 +116,111 @@ nasal_ref builtin_print(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
// local_scope[0] is reserved for 'me'
nasal_ref vec_addr=local_scope[1];
// main process
for(auto i:vec_addr->ptr.vec->elems)
switch(i->type)
for(auto i:vec_addr.vec()->elems)
switch(i.type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<*i.str(); break;
case vm_vec: i.vec()->print(); break;
case vm_hash: i.hash()->print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
case vm_obj: std::cout<<"<obj>"; break;
}
std::cout<<std::flush;
// generate return value
return gc.nil_addr;
return gc.nil;
}
nasal_ref builtin_append(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref vec_addr=local_scope[1];
nasal_ref elem_addr=local_scope[2];
if(vec_addr->type!=vm_vec)
if(vec_addr.type!=vm_vec)
{
builtin_err("append","\"vector\" must be vector");
return nullptr;
return nasal_ref(vm_none);
}
std::vector<nasal_ref>& ref_vec=vec_addr->ptr.vec->elems;
for(auto i:elem_addr->ptr.vec->elems)
std::vector<nasal_ref>& ref_vec=vec_addr.vec()->elems;
for(auto i:elem_addr.vec()->elems)
ref_vec.push_back(i);
return gc.nil_addr;
return gc.nil;
}
nasal_ref builtin_setsize(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref vec_addr=local_scope[1];
nasal_ref size_addr=local_scope[2];
if(vec_addr->type!=vm_vec)
if(vec_addr.type!=vm_vec)
{
builtin_err("setsize","\"vector\" must be vector");
return nullptr;
return nasal_ref(vm_none);
}
if(size_addr->type!=vm_num)
if(size_addr.type!=vm_num)
{
builtin_err("setsize","\"size\" is not a number");
return nullptr;
return nasal_ref(vm_none);
}
int num=size_addr->ptr.num;
int num=size_addr.num();
if(num<0)
{
builtin_err("setsize","\"size\" must be greater than -1");
return nullptr;
return nasal_ref(vm_none);
}
vec_addr->ptr.vec->elems.resize(num,gc.nil_addr);
return gc.nil_addr;
vec_addr.vec()->elems.resize(num,gc.nil);
return gc.nil;
}
nasal_ref builtin_system(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref ret_addr=gc.gc_alloc(vm_num);
nasal_ref ret_addr(vm_num);
nasal_ref str_addr=local_scope[1];
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("system","\"str\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
ret_addr->ptr.num=(double)system(str_addr->ptr.str->c_str());
ret_addr.num()=(double)system(str_addr.str()->c_str());
return ret_addr;
}
nasal_ref builtin_input(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref ret_addr=gc.gc_alloc(vm_str);
std::cin>>*ret_addr->ptr.str;
std::cin>>*ret_addr.str();
return ret_addr;
}
nasal_ref builtin_sleep(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("sleep","\"duration\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
// sleep in unistd.h will make this progress sleep sleep_time seconds.
sleep((unsigned int)val_addr->ptr.num);
return gc.nil_addr;
sleep((unsigned int)val_addr.num());
return gc.nil;
}
nasal_ref builtin_fin(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_str)
if(val_addr.type!=vm_str)
{
builtin_err("io.fin","\"filename\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
std::string& filename=*val_addr->ptr.str;
std::string& filename=*val_addr.str();
std::ifstream fin(filename);
nasal_ref ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str="";
*ret_addr.str()="";
if(!fin.fail())
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
ret_addr->ptr.str->push_back(c);
ret_addr.str()->push_back(c);
}
else
builtin_err("io.fin","cannot open \""+filename+"\"");
@ -231,56 +232,56 @@ nasal_ref builtin_fout(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
nasal_ref str_addr=local_scope[2];
if(val_addr->type!=vm_str)
if(val_addr.type!=vm_str)
{
builtin_err("io.fout","\"filename\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("io.fout","\"str\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
std::ofstream fout(*val_addr->ptr.str);
std::ofstream fout(*val_addr.str());
if(fout.fail())
{
builtin_err("io.fout","cannot open \""+*val_addr->ptr.str+"\"");
return nullptr;
builtin_err("io.fout","cannot open \""+*val_addr.str()+"\"");
return nasal_ref(vm_none);
}
fout<<*str_addr->ptr.str;
fout<<*str_addr.str();
fout.close();
return gc.nil_addr;
return gc.nil;
}
nasal_ref builtin_split(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref deli_val_addr=local_scope[1];
nasal_ref str_val_addr=local_scope[2];
if(deli_val_addr->type!=vm_str)
if(deli_val_addr.type!=vm_str)
{
builtin_err("split","\"delimeter\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(str_val_addr->type!=vm_str)
if(str_val_addr.type!=vm_str)
{
builtin_err("split","\"string\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
std::string& delimeter=*deli_val_addr->ptr.str;
std::string& source=*str_val_addr->ptr.str;
std::string& delimeter=*deli_val_addr.str();
std::string& source=*str_val_addr.str();
size_t delimeter_len=delimeter.length();
size_t source_len=source.length();
// push it to local scope to avoid being sweeped
local_scope.push_back(gc.gc_alloc(vm_vec));
std::vector<nasal_ref>& vec=local_scope.back()->ptr.vec->elems;
std::vector<nasal_ref>& vec=local_scope.back().vec()->elems;
if(!delimeter_len)
{
for(int i=0;i<source_len;++i)
{
vec.push_back(gc.gc_alloc(vm_str));
*vec.back()->ptr.str=source[i];
*vec.back().str()=source[i];
}
return local_scope.back();
}
@ -302,7 +303,7 @@ nasal_ref builtin_split(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
if(tmp.length())
{
vec.push_back(gc.gc_alloc(vm_str));
*vec.back()->ptr.str=tmp;
*vec.back().str()=tmp;
tmp="";
}
i+=delimeter_len-1;
@ -313,7 +314,7 @@ nasal_ref builtin_split(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
if(tmp.length())
{
vec.push_back(gc.gc_alloc(vm_str));
*vec.back()->ptr.str=tmp;
*vec.back().str()=tmp;
tmp="";
}
return local_scope.back();
@ -321,21 +322,21 @@ nasal_ref builtin_split(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
nasal_ref builtin_rand(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num && val_addr->type!=vm_nil)
if(val_addr.type!=vm_num && val_addr.type!=vm_nil)
{
builtin_err("rand","\"seed\" must be nil or number");
return nullptr;
return nasal_ref(vm_none);
}
if(val_addr->type==vm_num)
if(val_addr.type==vm_num)
{
srand((unsigned int)val_addr->ptr.num);
return gc.nil_addr;
srand((unsigned int)val_addr.num());
return gc.nil;
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=0;
nasal_ref ret_addr(vm_num);
ret_addr.num()=0;
for(int i=0;i<5;++i)
ret_addr->ptr.num=(ret_addr->ptr.num+rand())*(1.0/(RAND_MAX+1.0));
ret_addr.num()=(ret_addr.num()+rand())*(1.0/(RAND_MAX+1.0));
return ret_addr;
}
nasal_ref builtin_id(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
@ -343,69 +344,72 @@ nasal_ref builtin_id(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
nasal_ref val_addr=local_scope[1];
nasal_ref ret_addr=gc.gc_alloc(vm_str);
char buf[32];
sprintf(buf,"%p",val_addr);
*ret_addr->ptr.str=buf;
if(val_addr.type>vm_num)
sprintf(buf,"%p",val_addr.value.gcobj);
else
sprintf(buf,"0");
*ret_addr.str()=buf;
return ret_addr;
}
nasal_ref builtin_int(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
return gc.nil_addr;
int number=(int)val_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(double)number;
if(val_addr.type!=vm_num)
return gc.nil;
int number=(int)val_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(double)number;
return ret_addr;
}
nasal_ref builtin_num(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_str)
return gc.nil_addr;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=val_addr->to_number();
if(val_addr.type!=vm_str)
return gc.nil;
nasal_ref ret_addr(vm_num);
ret_addr.num()=val_addr.to_number();
return ret_addr;
}
nasal_ref builtin_pop(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_vec)
if(val_addr.type!=vm_vec)
{
builtin_err("pop","\"vector\" must be vector");
return nullptr;
return nasal_ref(vm_none);
}
if(val_addr->ptr.vec->elems.size())
if(val_addr.vec()->elems.size())
{
nasal_ref tmp=val_addr->ptr.vec->elems.back();
val_addr->ptr.vec->elems.pop_back();
nasal_ref tmp=val_addr.vec()->elems.back();
val_addr.vec()->elems.pop_back();
return tmp;
}
return gc.nil_addr;
return gc.nil;
}
nasal_ref builtin_str(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("str","\"number\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=std::to_string(val_addr->ptr.num);
*ret_addr.str()=std::to_string(val_addr.num());
return ret_addr;
}
nasal_ref builtin_size(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
nasal_ref ret_addr=gc.gc_alloc(vm_num);
switch(val_addr->type)
nasal_ref ret_addr(vm_num);
switch(val_addr.type)
{
case vm_nil: ret_addr->ptr.num=0; break;
case vm_num: ret_addr->ptr.num=val_addr->ptr.num; break;
case vm_func: ret_addr->ptr.num=0; break;
case vm_str: ret_addr->ptr.num=val_addr->ptr.str->length(); break;
case vm_vec: ret_addr->ptr.num=val_addr->ptr.vec->elems.size(); break;
case vm_hash: ret_addr->ptr.num=val_addr->ptr.hash->elems.size();break;
case vm_nil: ret_addr.num()=0; break;
case vm_num: ret_addr.num()=val_addr.num(); break;
case vm_func: ret_addr.num()=0; break;
case vm_str: ret_addr.num()=val_addr.str()->length(); break;
case vm_vec: ret_addr.num()=val_addr.vec()->elems.size(); break;
case vm_hash: ret_addr.num()=val_addr.hash()->elems.size();break;
}
return ret_addr;
}
@ -413,255 +417,255 @@ nasal_ref builtin_xor(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
if(a_addr->type!=vm_num)
if(a_addr.type!=vm_num)
{
builtin_err("xor","\"a\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(b_addr->type!=vm_num)
if(b_addr.type!=vm_num)
{
builtin_err("xor","\"b\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a^number_b);
int number_a=(int)a_addr.num();
int number_b=(int)b_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(number_a^number_b);
return ret_addr;
}
nasal_ref builtin_and(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
if(a_addr->type!=vm_num)
if(a_addr.type!=vm_num)
{
builtin_err("and","\"a\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(b_addr->type!=vm_num)
if(b_addr.type!=vm_num)
{
builtin_err("and","\"b\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a&number_b);
int number_a=(int)a_addr.num();
int number_b=(int)b_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(number_a&number_b);
return ret_addr;
}
nasal_ref builtin_or(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
if(a_addr->type!=vm_num)
if(a_addr.type!=vm_num)
{
builtin_err("or","\"a\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(b_addr->type!=vm_num)
if(b_addr.type!=vm_num)
{
builtin_err("or","\"b\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(number_a|number_b);
int number_a=(int)a_addr.num();
int number_b=(int)b_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(number_a|number_b);
return ret_addr;
}
nasal_ref builtin_nand(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
if(a_addr->type!=vm_num)
if(a_addr.type!=vm_num)
{
builtin_err("nand","\"a\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(b_addr->type!=vm_num)
if(b_addr.type!=vm_num)
{
builtin_err("nand","\"b\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
int number_a=(int)a_addr->ptr.num;
int number_b=(int)b_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(~(number_a&number_b));
int number_a=(int)a_addr.num();
int number_b=(int)b_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(~(number_a&number_b));
return ret_addr;
}
nasal_ref builtin_not(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
if(a_addr->type!=vm_num)
if(a_addr.type!=vm_num)
{
builtin_err("not","\"a\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
int number=(int)a_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(~number);
int number=(int)a_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=(~number);
return ret_addr;
}
nasal_ref builtin_sin(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("sin","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=sin(val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=sin(val_addr.num());
return ret_addr;
}
nasal_ref builtin_cos(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("cos","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=cos(val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=cos(val_addr.num());
return ret_addr;
}
nasal_ref builtin_tan(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("tan","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=tan(val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=tan(val_addr.num());
return ret_addr;
}
nasal_ref builtin_exp(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("exp","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=exp(val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=exp(val_addr.num());
return ret_addr;
}
nasal_ref builtin_ln(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("ln","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(log(val_addr->ptr.num)/log(2.7182818284590452354));
nasal_ref ret_addr(vm_num);
ret_addr.num()=(log(val_addr.num())/log(2.7182818284590452354));
return ret_addr;
}
nasal_ref builtin_sqrt(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("sqrt","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=sqrt(val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=sqrt(val_addr.num());
return ret_addr;
}
nasal_ref builtin_atan2(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref x_val_addr=local_scope[1];
nasal_ref y_val_addr=local_scope[2];
if(x_val_addr->type!=vm_num)
if(x_val_addr.type!=vm_num)
{
builtin_err("atan2","\"x\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(y_val_addr->type!=vm_num)
if(y_val_addr.type!=vm_num)
{
builtin_err("atan2","\"y\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=atan2(y_val_addr->ptr.num,x_val_addr->ptr.num);
nasal_ref ret_addr(vm_num);
ret_addr.num()=atan2(y_val_addr.num(),x_val_addr.num());
return ret_addr;
}
nasal_ref builtin_isnan(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref x=local_scope[1];
if(x->type==vm_num && std::isnan(x->ptr.num))
return gc.one_addr;
return gc.zero_addr;
if(x.type==vm_num && std::isnan(x.num()))
return gc.one;
return gc.zero;
}
nasal_ref builtin_time(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
if(val_addr->type!=vm_num)
if(val_addr.type!=vm_num)
{
builtin_err("time","\"begin_time\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
time_t begin_time=(time_t)val_addr->ptr.num;
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=time(&begin_time);
time_t begin_time=(time_t)val_addr.num();
nasal_ref ret_addr(vm_num);
ret_addr.num()=time(&begin_time);
return ret_addr;
}
nasal_ref builtin_contains(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref hash_addr=local_scope[1];
nasal_ref key_addr=local_scope[2];
if(hash_addr->type!=vm_hash)
if(hash_addr.type!=vm_hash)
{
builtin_err("contains","\"hash\" must be hash");
return nullptr;
return nasal_ref(vm_none);
}
if(key_addr->type!=vm_str)
if(key_addr.type!=vm_str)
{
builtin_err("contains","\"key\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
return hash_addr->ptr.hash->elems.count(*key_addr->ptr.str)?gc.one_addr:gc.zero_addr;
return hash_addr.hash()->elems.count(*key_addr.str())?gc.one:gc.zero;
}
nasal_ref builtin_delete(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref hash_addr=local_scope[1];
nasal_ref key_addr=local_scope[2];
if(hash_addr->type!=vm_hash)
if(hash_addr.type!=vm_hash)
{
builtin_err("delete","\"hash\" must be hash");
return nullptr;
return nasal_ref(vm_none);
}
if(key_addr->type!=vm_str)
if(key_addr.type!=vm_str)
{
builtin_err("delete","\"key\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(hash_addr->ptr.hash->elems.count(*key_addr->ptr.str))
hash_addr->ptr.hash->elems.erase(*key_addr->ptr.str);
return gc.nil_addr;
if(hash_addr.hash()->elems.count(*key_addr.str()))
hash_addr.hash()->elems.erase(*key_addr.str());
return gc.nil;
}
nasal_ref builtin_keys(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref hash_addr=local_scope[1];
if(hash_addr->type!=vm_hash)
if(hash_addr.type!=vm_hash)
{
builtin_err("keys","\"hash\" must be hash");
return nullptr;
return nasal_ref(vm_none);
}
// push vector into local scope to avoid being sweeped
local_scope.push_back(gc.gc_alloc(vm_vec));
std::vector<nasal_ref>& vec=local_scope.back()->ptr.vec->elems;
for(auto iter:hash_addr->ptr.hash->elems)
std::vector<nasal_ref>& vec=local_scope.back().vec()->elems;
for(auto iter:hash_addr.hash()->elems)
{
nasal_ref str_addr=gc.gc_alloc(vm_str);
*str_addr->ptr.str=iter.first;
*str_addr.str()=iter.first;
vec.push_back(str_addr);
}
return local_scope.back();
@ -671,31 +675,31 @@ nasal_ref builtin_import(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
// this function is used in preprocessing.
// this function will return nothing when running.
builtin_err("import","must use this function in global scope");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref builtin_die(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref str_addr=local_scope[1];
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("die","\"str\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
std::cout<<"[vm] error: "<<*str_addr->ptr.str<<'\n';
return nullptr;
std::cout<<"[vm] error: "<<*str_addr.str()<<'\n';
return nasal_ref(vm_none);
}
nasal_ref builtin_type(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref val_addr=local_scope[1];
nasal_ref ret_addr=gc.gc_alloc(vm_str);
switch(val_addr->type)
switch(val_addr.type)
{
case vm_nil: *ret_addr->ptr.str="nil"; break;
case vm_num: *ret_addr->ptr.str="num"; break;
case vm_str: *ret_addr->ptr.str="str"; break;
case vm_vec: *ret_addr->ptr.str="vec"; break;
case vm_hash: *ret_addr->ptr.str="hash"; break;
case vm_func: *ret_addr->ptr.str="func"; break;
case vm_nil: *ret_addr.str()="nil"; break;
case vm_num: *ret_addr.str()="num"; break;
case vm_str: *ret_addr.str()="str"; break;
case vm_vec: *ret_addr.str()="vec"; break;
case vm_hash: *ret_addr.str()="hash"; break;
case vm_func: *ret_addr.str()="func"; break;
}
return ret_addr;
}
@ -704,106 +708,106 @@ nasal_ref builtin_substr(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
nasal_ref str_addr=local_scope[1];
nasal_ref beg_addr=local_scope[2];
nasal_ref len_addr=local_scope[3];
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("substr","\"str\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(beg_addr->type!=vm_num)
if(beg_addr.type!=vm_num)
{
builtin_err("substr","\"begin\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
if(len_addr->type!=vm_num)
if(len_addr.type!=vm_num)
{
builtin_err("substr","\"length\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
std::string& str=*str_addr->ptr.str;
int beg=(int)beg_addr->ptr.num;
int len=(int)len_addr->ptr.num;
std::string& str=*str_addr.str();
int beg=(int)beg_addr.num();
int len=(int)len_addr.num();
if(beg>=str.length() || beg+len-1>=str.length())
{
builtin_err("susbtr","index out of range");
return nullptr;
return nasal_ref(vm_none);
}
if(len<0)
len=0;
nasal_ref ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(beg,len);
*ret_addr.str()=str.substr(beg,len);
return ret_addr;
}
nasal_ref builtin_streq(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=(a_addr->type!=vm_str || b_addr->type!=vm_str)?0:(*a_addr->ptr.str==*b_addr->ptr.str);
nasal_ref ret_addr(vm_num);
ret_addr.num()=(a_addr.type!=vm_str || b_addr.type!=vm_str)?0:(*a_addr.str()==*b_addr.str());
return ret_addr;
}
nasal_ref builtin_left(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref str_addr=local_scope[1];
nasal_ref len_addr=local_scope[2];
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("left","\"string\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(len_addr->type!=vm_num)
if(len_addr.type!=vm_num)
{
builtin_err("left","\"length\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
std::string& str=*str_addr->ptr.str;
int len=(int)len_addr->ptr.num;
std::string& str=*str_addr.str();
int len=(int)len_addr.num();
if(len<0)
len=0;
nasal_ref ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(0, len);
*ret_addr.str()=str.substr(0, len);
return ret_addr;
}
nasal_ref builtin_right(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref str_addr=local_scope[1];
nasal_ref len_addr=local_scope[2];
if(str_addr->type!=vm_str)
if(str_addr.type!=vm_str)
{
builtin_err("right","\"string\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(len_addr->type!=vm_num)
if(len_addr.type!=vm_num)
{
builtin_err("right","\"length\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
std::string& str=*str_addr->ptr.str;
int len=(int)len_addr->ptr.num;
std::string& str=*str_addr.str();
int len=(int)len_addr.num();
int srclen=str.length();
if(len>srclen)
len=srclen;
if(len<0)
len=0;
nasal_ref ret_addr=gc.gc_alloc(vm_str);
*ret_addr->ptr.str=str.substr(srclen-len, srclen);
*ret_addr.str()=str.substr(srclen-len, srclen);
return ret_addr;
}
nasal_ref builtin_cmp(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
{
nasal_ref a_addr=local_scope[1];
nasal_ref b_addr=local_scope[2];
if(a_addr->type!=vm_str)
if(a_addr.type!=vm_str)
{
builtin_err("cmp","\"a\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
if(b_addr->type!=vm_str)
if(b_addr.type!=vm_str)
{
builtin_err("cmp","\"b\" must be string");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_num);
ret_addr->ptr.num=strcmp(a_addr->ptr.str->c_str(),b_addr->ptr.str->c_str());
nasal_ref ret_addr(vm_num);
ret_addr.num()=strcmp(a_addr.str()->c_str(),b_addr.str()->c_str());
return ret_addr;
}
nasal_ref builtin_chr(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
@ -827,19 +831,19 @@ nasal_ref builtin_chr(std::vector<nasal_ref>& local_scope,nasal_gc& gc)
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
nasal_ref code_addr=local_scope[1];
if(code_addr->type!=vm_num)
if(code_addr.type!=vm_num)
{
builtin_err("chr","\"code\" must be number");
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref ret_addr=gc.gc_alloc(vm_str);
int num=code_addr->ptr.num;
int num=code_addr.num();
if(0<=num && num<128)
*ret_addr->ptr.str=(char)num;
*ret_addr.str()=(char)num;
else if(128<=num && num<256)
*ret_addr->ptr.str=extend[num-128];
*ret_addr.str()=extend[num-128];
else
*ret_addr->ptr.str=" ";
*ret_addr.str()=" ";
return ret_addr;
}

View File

@ -3,8 +3,11 @@
enum nasal_type
{
vm_nil=0,
// none-gc object
vm_none=0,
vm_nil,
vm_num,
// gc object
vm_str,
vm_func,
vm_vec,
@ -15,10 +18,13 @@ enum nasal_type
// change parameters here to make your own efficient gc
// better set bigger number on vm_num and vm_vec
const int increment[vm_type_size]=
const uint32_t increment[vm_type_size]=
{
0, // vm_nil,in fact it is not in use
65536,// vm_num
// none-gc object
0, // vm_none, error type
0, // vm_nil
0, // vm_num
// gc object
2048, // vm_str
512, // vm_func
8192, // vm_vec
@ -26,12 +32,12 @@ const int increment[vm_type_size]=
0 // vm_obj
};
// declaration of nasal_val
// declaration of nasal value type
struct nasal_vec;
struct nasal_hash;
struct nasal_func;
struct nasal_val;
// define nasal_ref => nasal_val*
typedef nasal_val* nasal_ref;
#ifdef __NASAL_REF__
// declaration of nasal_ref
struct nasal_ref
{
uint8_t type;
@ -40,8 +46,44 @@ struct nasal_ref
double num;
nasal_val* gcobj;
}value;
nasal_ref(const uint8_t t=vm_none):type(t){}
nasal_ref(const uint8_t t,const double n):type(t){value.num=n;}
nasal_ref(const uint8_t t,nasal_val* n):type(t){value.gcobj=n;}
nasal_ref(const nasal_ref& nr)
{
type=nr.type;
value=nr.value;
}
nasal_ref(const nasal_ref&& nr)
{
type=nr.type;
value=nr.value;
}
nasal_ref& operator=(const nasal_ref& nr)
{
type=nr.type;
value=nr.value;
return *this;
}
nasal_ref& operator=(const nasal_ref&& nr)
{
type=nr.type;
value=nr.value;
return *this;
}
bool operator==(const nasal_ref& nr){return type==nr.type && value.gcobj==nr.value.gcobj;}
bool operator!=(const nasal_ref& nr){return type!=nr.type || value.gcobj!=nr.value.gcobj;}
// nasal is a weak-type programming language because number and string can be translated to each other
double to_number();
std::string to_string();
// inline function to get number and pointers, make it easier to read the code
inline double& num ();
inline std::string* str ();
inline nasal_vec* vec ();
inline nasal_hash* hash();
inline nasal_func* func();
inline void* obj ();
};
#endif
struct nasal_vec// 24 bytes
{
@ -83,7 +125,6 @@ struct nasal_val// 16 bytes
uint8_t type;
union
{
double num;
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
@ -91,10 +132,8 @@ struct nasal_val// 16 bytes
void* obj;
}ptr;
nasal_val(int);
nasal_val(uint8_t);
~nasal_val();
double to_number();
std::string to_string();
};
/*functions of nasal_vec*/
@ -102,7 +141,7 @@ nasal_ref nasal_vec::get_val(int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return nasal_ref(vm_none);
return elems[index>=0?index:index+vec_size];
}
nasal_ref* nasal_vec::get_mem(int index)
@ -128,16 +167,17 @@ void nasal_vec::print()
}
ssize_t iter=0;
std::cout<<'[';
for(auto i:elems)
for(auto& i:elems)
{
switch(i->type)
switch(i.type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
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: i.vec()->print(); break;
case vm_hash: i.hash()->print(); break;
case vm_func: std::cout<<"func(..){..}";break;
}
std::cout<<",]"[(++iter)==elems.size()];
}
@ -152,18 +192,18 @@ nasal_ref nasal_hash::get_val(std::string& key)
return elems[key];
else if(elems.count("parents"))
{
nasal_ref ret_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
nasal_ref ret(vm_none);
nasal_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
{
if(i->type==vm_hash)
ret_addr=i->ptr.hash->get_val(key);
if(ret_addr)
return ret_addr;
if(i.type==vm_hash)
ret=i.hash()->get_val(key);
if(ret.type!=vm_none)
return ret;
}
}
return nullptr;
return nasal_ref(vm_none);
}
nasal_ref* nasal_hash::get_mem(std::string& key)
{
@ -172,12 +212,12 @@ nasal_ref* nasal_hash::get_mem(std::string& key)
else if(elems.count("parents"))
{
nasal_ref* mem_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
nasal_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
{
if(i->type==vm_hash)
mem_addr=i->ptr.hash->get_mem(key);
if(i.type==vm_hash)
mem_addr=i.hash()->get_mem(key);
if(mem_addr)
return mem_addr;
}
@ -204,14 +244,15 @@ void nasal_hash::print()
{
std::cout<<i.first<<':';
nasal_ref tmp=i.second;
switch(tmp->type)
switch(tmp.type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->ptr.num; break;
case vm_str: std::cout<<*tmp->ptr.str; break;
case vm_vec: tmp->ptr.vec->print(); break;
case vm_hash: tmp->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp.num(); break;
case vm_str: std::cout<<*tmp.str(); break;
case vm_vec: tmp.vec()->print(); break;
case vm_hash: tmp.hash()->print(); break;
case vm_func: std::cout<<"func(..){..}";break;
}
std::cout<<",}"[(++iter)==elems.size()];
}
@ -234,13 +275,12 @@ void nasal_func::clear()
}
/*functions of nasal_val*/
nasal_val::nasal_val(int val_type)
nasal_val::nasal_val(uint8_t val_type)
{
mark=GC_COLLECTED;
type=val_type;
switch(type)
{
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec; break;
case vm_hash: ptr.hash=new nasal_hash; break;
@ -260,40 +300,47 @@ nasal_val::~nasal_val()
type=vm_nil;
return;
}
double nasal_val::to_number()
/* functions of nasal_ref */
double nasal_ref::to_number()
{
if(type==vm_str)
return str2num(ptr.str->c_str());
return ptr.num;
return str2num(str()->c_str());
return num();
}
std::string nasal_val::to_string()
std::string nasal_ref::to_string()
{
if(type==vm_str)
return *ptr.str;
return *str();
else if(type==vm_num)
return std::to_string(ptr.num);
return std::to_string(num());
return "";
}
inline double& nasal_ref::num (){return value.num;}
inline std::string* nasal_ref::str (){return value.gcobj->ptr.str;}
inline nasal_vec* nasal_ref::vec (){return value.gcobj->ptr.vec;}
inline nasal_hash* nasal_ref::hash(){return value.gcobj->ptr.hash;}
inline nasal_func* nasal_ref::func(){return value.gcobj->ptr.func;}
inline void* nasal_ref::obj (){return value.gcobj->ptr.obj;}
struct nasal_gc
{
#define STACK_MAX_DEPTH (4095)
nasal_ref zero_addr; // reserved address of nasal_val,type vm_num, 0
nasal_ref one_addr; // reserved address of nasal_val,type vm_num, 1
nasal_ref nil_addr; // reserved address of nasal_val,type vm_nil
nasal_ref val_stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow, stack grows 1 each time
nasal_ref* stack_top; // stack top
std::vector<nasal_ref> num_addrs; // reserved address for const vm_num
std::vector<nasal_ref> str_addrs; // reserved address for const vm_str
std::vector<nasal_ref> memory; // gc memory
std::queue <nasal_ref> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
void mark();
void sweep();
void gc_init(const std::vector<double>&,const std::vector<std::string>&);
void gc_clear();
nasal_ref gc_alloc(int);
nasal_ref builtin_alloc(int);
nasal_ref zero; // reserved address of nasal_val,type vm_num, 0
nasal_ref one; // reserved address of nasal_val,type vm_num, 1
nasal_ref nil; // reserved address of nasal_val,type vm_nil
nasal_ref val_stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow, stack grows 1 each time
nasal_ref* stack_top; // stack top
std::vector<nasal_ref> str_addrs; // reserved address for const vm_str
std::vector<nasal_val*> memory; // gc memory
std::queue<nasal_val*> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
void mark();
void sweep();
void gc_init(const std::vector<std::string>&);
void gc_clear();
nasal_ref gc_alloc(const uint8_t);
nasal_ref builtin_alloc(const uint8_t);
};
/* gc functions */
@ -308,22 +355,22 @@ void nasal_gc::mark()
{
nasal_ref tmp=bfs.front();
bfs.pop();
if(tmp->mark) continue;
tmp->mark=GC_FOUND;
switch(tmp->type)
if(tmp.type<=vm_num || tmp.value.gcobj->mark) continue;
tmp.value.gcobj->mark=GC_FOUND;
switch(tmp.type)
{
case vm_vec:
for(auto i:tmp->ptr.vec->elems)
for(auto& i:tmp.vec()->elems)
bfs.push(i);
break;
case vm_hash:
for(auto& i:tmp->ptr.hash->elems)
for(auto& i:tmp.hash()->elems)
bfs.push(i.second);
break;
case vm_func:
bfs.push(tmp->ptr.func->closure);
for(auto i:tmp->ptr.func->default_para)
if(i)
bfs.push(tmp.func()->closure);
for(auto& i:tmp.func()->default_para)
if(i.type>vm_num)
bfs.push(i);
break;
}
@ -351,39 +398,28 @@ void nasal_gc::sweep()
}
return;
}
void nasal_gc::gc_init(const std::vector<double>& nums,const std::vector<std::string>& strs)
void nasal_gc::gc_init(const std::vector<std::string>& strs)
{
for(int i=vm_num;i<vm_type_size;++i)
for(int j=0;j<increment[i];++j)
for(uint8_t i=vm_str;i<vm_type_size;++i)
for(uint32_t j=0;j<increment[i];++j)
{
nasal_ref tmp=new nasal_val(i);
memory.push_back(tmp);
free_list[i].push(tmp);
nasal_ref tmp={i,new nasal_val(i)};
memory.push_back(tmp.value.gcobj);
free_list[i].push(tmp.value.gcobj);
}
stack_top=val_stack; // set stack_top to val_stack
zero_addr=new nasal_val(vm_num); // init constant 0
zero_addr->ptr.num=0;
zero={vm_num,(double)0}; // init constant 0
one ={vm_num,(double)1}; // init constant 1
nil.type=vm_nil; // init constant nil
one_addr=new nasal_val(vm_num); // init constant 1
one_addr->ptr.num=1;
nil_addr=new nasal_val(vm_nil); // init nil
// init constant numbers
num_addrs.resize(nums.size());
for(int i=0;i<nums.size();++i)
{
num_addrs[i]=new nasal_val(vm_num);
num_addrs[i]->ptr.num=nums[i];
}
// init constant strings
str_addrs.resize(strs.size());
for(int i=0;i<strs.size();++i)
{
str_addrs[i]=new nasal_val(vm_str);
*str_addrs[i]->ptr.str=strs[i];
str_addrs[i]={vm_str,new nasal_val(vm_str)};
*str_addrs[i].str()=strs[i];
}
return;
}
@ -392,23 +428,17 @@ void nasal_gc::gc_clear()
for(auto i:memory)
delete i;
memory.clear();
for(int i=0;i<vm_type_size;++i)
for(uint8_t i=0;i<vm_type_size;++i)
while(!free_list[i].empty())
free_list[i].pop();
local.clear();
delete nil_addr;
delete one_addr;
delete zero_addr;
for(auto i:num_addrs)
delete i;
num_addrs.clear();
for(auto i:str_addrs)
delete i;
for(auto& i:str_addrs)
delete i.value.gcobj;
str_addrs.clear();
return;
}
nasal_ref nasal_gc::gc_alloc(int type)
nasal_ref nasal_gc::gc_alloc(uint8_t type)
{
if(free_list[type].empty())
{
@ -416,32 +446,32 @@ nasal_ref nasal_gc::gc_alloc(int type)
sweep();
}
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
for(uint32_t i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
nasal_ref tmp={type,new nasal_val(type)};
memory.push_back(tmp.value.gcobj);
free_list[type].push(tmp.value.gcobj);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
nasal_ref nasal_gc::builtin_alloc(int type)
nasal_ref nasal_gc::builtin_alloc(uint8_t type)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc_alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
for(uint32_t i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
nasal_ref tmp={type,new nasal_val(type)};
memory.push_back(tmp.value.gcobj);
free_list[type].push(tmp.value.gcobj);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}

View File

@ -117,8 +117,8 @@ void nasal_vm::init(
const std::vector<double>& nums,
const std::vector<std::string>& filenames)
{
gc.gc_init(nums,strs);
gc.val_stack[STACK_MAX_DEPTH-1]=nullptr;
gc.gc_init(strs);
gc.val_stack[STACK_MAX_DEPTH-1].value.gcobj=nullptr;
num_table=nums; // get constant numbers
str_table=strs; // get constant strings & symbols
files=filenames;// get filenames for debugger
@ -173,7 +173,7 @@ void nasal_vm::stackinfo(int limit)
{
printf("vm stack(limit %d):\n",limit);
uint32_t same_cnt=0;
nasal_ref last_ptr=(nasal_ref)0xffff;
nasal_ref last_ptr={vm_none,0xffffffff};
for(int i=0;i<limit && stack_top-i>=gc.val_stack;++i)
{
if(stack_top[-i]==last_ptr)
@ -183,28 +183,27 @@ void nasal_vm::stackinfo(int limit)
}
if(same_cnt)
{
printf("\t%p ... | %d same value(s)\n",last_ptr,same_cnt);
printf("\t%p ... | %d same value(s)\n",last_ptr.value.gcobj,same_cnt);
same_cnt=0;
}
last_ptr=stack_top[-i];
printf("\t%p ",stack_top[-i]);
if(!stack_top[-i])
printf("nullptr");
else
switch(stack_top[-i]->type)
{
case vm_nil: printf("nil | gc.nil_addr");break;
case vm_num: printf("num | %lf",stack_top[-i]->ptr.num);break;
case vm_str: printf("str | ");raw_string(*stack_top[-i]->ptr.str);break;
case vm_func: printf("func | func(%lu para){..}",stack_top[-i]->ptr.func->key_table.size());break;
case vm_vec: printf("vec | [%lu val]",stack_top[-i]->ptr.vec->elems.size());break;
case vm_hash: printf("hash | {%lu member}",stack_top[-i]->ptr.hash->elems.size());break;
default: printf("unknown");break;
}
printf("\t%p ",stack_top[-i].value.gcobj);
switch(stack_top[-i].type)
{
case vm_none: printf("undefined");break;
case vm_nil: printf("nil | gc.nil");break;
case vm_num: printf("num | %lf",stack_top[-i].value.num);break;
case vm_str: printf("str | ");raw_string(*stack_top[-i].value.gcobj->ptr.str);break;
case vm_func: printf("func | func(%lu para){..}",stack_top[-i].value.gcobj->ptr.func->key_table.size());break;
case vm_vec: printf("vec | [%lu val]",stack_top[-i].value.gcobj->ptr.vec->elems.size());break;
case vm_hash: printf("hash | {%lu member}",stack_top[-i].value.gcobj->ptr.hash->elems.size());break;
case vm_obj: printf("user data");break;
default: printf("unknown");break;
}
putchar('\n');
}
if(same_cnt)
printf("\t%p ... | %d same value(s)\n",last_ptr,same_cnt);
printf("\t%p ... | %d same value(s)\n",last_ptr.value.gcobj,same_cnt);
return;
}
void nasal_vm::die(std::string str)
@ -225,13 +224,13 @@ void nasal_vm::stackoverflow()
exit(1);
return;
}
inline bool nasal_vm::condition(nasal_ref val_addr)
inline bool nasal_vm::condition(nasal_ref val)
{
if(val_addr->type==vm_num)
return val_addr->ptr.num;
else if(val_addr->type==vm_str)
if(val.type==vm_num)
return val.value.num;
else if(val.type==vm_str)
{
std::string& str=*val_addr->ptr.str;
std::string& str=*val.str();
double num=str2num(str.c_str());
if(std::isnan(num))
return str.empty();
@ -243,23 +242,23 @@ inline void nasal_vm::opr_intg()
{
// global values store on stack
for(uint32_t i=0;i<imm[pc];++i)
(stack_top++)[0]=gc.nil_addr;
(stack_top++)[0].type=vm_nil;
--stack_top;// point to the top
return;
}
inline void nasal_vm::opr_intl()
{
auto& vec=stack_top[0]->ptr.func->closure->ptr.vec->elems;
auto& vec=stack_top[0].func()->closure.vec()->elems;
// if many functions share the same closure
// resize will break the size of vector and cause exe_bad_access
// so choose the maximum size as the size of this closure
if(vec.size()<imm[pc])
vec.resize(imm[pc],gc.nil_addr);
vec.resize(imm[pc],gc.nil);
return;
}
inline void nasal_vm::opr_offset()
{
stack_top[0]->ptr.func->offset=imm[pc];
stack_top[0].func()->offset=imm[pc];
return;
}
inline void nasal_vm::opr_loadg()
@ -269,27 +268,27 @@ inline void nasal_vm::opr_loadg()
}
inline void nasal_vm::opr_loadl()
{
gc.local.back()->ptr.vec->elems[imm[pc]]=(stack_top--)[0];
gc.local.back().vec()->elems[imm[pc]]=(stack_top--)[0];
return;
}
inline void nasal_vm::opr_pnum()
{
(++stack_top)[0]=gc.num_addrs[imm[pc]];
(++stack_top)[0]={vm_num,num_table[imm[pc]]};
return;
}
inline void nasal_vm::opr_pone()
{
(++stack_top)[0]=gc.one_addr;
(++stack_top)[0]={vm_num,(double)1};
return;
}
inline void nasal_vm::opr_pzero()
{
(++stack_top)[0]=gc.zero_addr;
(++stack_top)[0]={vm_num,(double)0};
return;
}
inline void nasal_vm::opr_pnil()
{
(++stack_top)[0]=gc.nil_addr;
(++stack_top)[0].type=vm_nil;
return;
}
inline void nasal_vm::opr_pstr()
@ -301,7 +300,7 @@ inline void nasal_vm::opr_newv()
{
nasal_ref vec_addr=gc.gc_alloc(vm_vec);
nasal_ref* begin=stack_top-imm[pc]+1;
auto& vec=vec_addr->ptr.vec->elems;// stack_top-imm[pc] stores the vector
auto& vec=vec_addr.vec()->elems;// stack_top-imm[pc] stores the vector
vec.resize(imm[pc]);
for(uint32_t i=0;i<imm[pc];++i)
vec[i]=begin[i];
@ -317,56 +316,56 @@ inline void nasal_vm::opr_newh()
inline void nasal_vm::opr_newf()
{
(++stack_top)[0]=gc.gc_alloc(vm_func);
stack_top[0]->ptr.func->entry=imm[pc];
stack_top[0]->ptr.func->closure=gc.nil_addr;
stack_top[0].func()->entry=imm[pc];
stack_top[0].func()->closure.type=vm_nil;
if(gc.local.empty())
stack_top[0]->ptr.func->closure=gc.gc_alloc(vm_vec);
stack_top[0].func()->closure=gc.gc_alloc(vm_vec);
else
stack_top[0]->ptr.func->closure=gc.local.back();// local contains 'me'
stack_top[0].func()->closure=gc.local.back();// local contains 'me'
return;
}
inline void nasal_vm::opr_happ()
{
nasal_ref val=stack_top[0];
(--stack_top)[0]->ptr.hash->elems[str_table[imm[pc]]]=val;
stack_top[-1].hash()->elems[str_table[imm[pc]]]=stack_top[0];
--stack_top;
return;
}
inline void nasal_vm::opr_para()
{
nasal_func* func=stack_top[0]->ptr.func;
int size=func->key_table.size();
nasal_func* func=stack_top[0].func();
size_t size=func->key_table.size();
func->key_table[str_table[imm[pc]]]=size;
func->default_para.push_back(nullptr);
func->default_para.push_back({vm_none});
return;
}
inline void nasal_vm::opr_defpara()
{
nasal_ref def_val=stack_top[0];
nasal_func* func=(--stack_top)[0]->ptr.func;
int size=func->key_table.size();
nasal_func* func=(--stack_top)[0].func();
size_t size=func->key_table.size();
func->key_table[str_table[imm[pc]]]=size;
func->default_para.push_back(def_val);
return;
}
inline void nasal_vm::opr_dynpara()
{
stack_top[0]->ptr.func->dynpara=imm[pc];
stack_top[0].func()->dynpara=imm[pc];
return;
}
inline void nasal_vm::opr_unot()
{
nasal_ref val=stack_top[0];
switch(val->type)
switch(val.type)
{
case vm_nil:stack_top[0]=gc.one_addr;break;
case vm_num:stack_top[0]=val->ptr.num?gc.zero_addr:gc.one_addr;break;
case vm_nil:stack_top[0]=gc.zero;break;
case vm_num:stack_top[0]=val.num()?gc.zero:gc.one;break;
case vm_str:
{
double num=str2num(val->ptr.str->c_str());
double num=str2num(val.str()->c_str());
if(std::isnan(num))
stack_top[0]=val->ptr.str->empty()?gc.one_addr:gc.zero_addr;
stack_top[0]=val.str()->empty()?gc.one:gc.zero;
else
stack_top[0]=num?gc.zero_addr:gc.one_addr;
stack_top[0]=num?gc.zero:gc.one;
}
break;
default:die("unot: incorrect value type");break;
@ -375,15 +374,12 @@ inline void nasal_vm::opr_unot()
}
inline void nasal_vm::opr_usub()
{
nasal_ref new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=-stack_top[0]->to_number();
stack_top[0]=new_val;
stack_top[0]={vm_num,-stack_top[0].to_number()};
return;
}
#define op_calc(type)\
nasal_ref new_val=gc.gc_alloc(vm_num);\
new_val->ptr.num=stack_top[-1]->to_number() type stack_top[0]->to_number();\
nasal_ref new_val(vm_num,stack_top[-1].to_number() type stack_top[0].to_number());\
(--stack_top)[0]=new_val;
inline void nasal_vm::opr_add(){op_calc(+);}
@ -393,14 +389,13 @@ inline void nasal_vm::opr_div(){op_calc(/);}
inline void nasal_vm::opr_lnk()
{
nasal_ref new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=stack_top[-1]->to_string()+stack_top[0]->to_string();
*new_val.str()=stack_top[-1].to_string()+stack_top[0].to_string();
(--stack_top)[0]=new_val;
return;
}
#define op_calc_const(type)\
nasal_ref new_val=gc.gc_alloc(vm_num);\
new_val->ptr.num=stack_top[0]->to_number() type num_table[imm[pc]];\
nasal_ref new_val(vm_num,stack_top[0].to_number() type num_table[imm[pc]]);\
stack_top[0]=new_val;
inline void nasal_vm::opr_addc(){op_calc_const(+);}
@ -410,14 +405,13 @@ inline void nasal_vm::opr_divc(){op_calc_const(/);}
inline void nasal_vm::opr_lnkc()
{
nasal_ref new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=stack_top[0]->to_string()+str_table[imm[pc]];
*new_val.str()=stack_top[0].to_string()+str_table[imm[pc]];
stack_top[0]=new_val;
return;
}
#define op_calc_eq(type)\
nasal_ref new_val=gc.gc_alloc(vm_num);\
new_val->ptr.num=mem_addr[0]->to_number() type stack_top[-1]->to_number();\
nasal_ref new_val(vm_num,mem_addr[0].to_number() type stack_top[-1].to_number());\
(--stack_top)[0]=mem_addr[0]=new_val;
inline void nasal_vm::opr_addeq(){op_calc_eq(+);}
@ -427,14 +421,13 @@ inline void nasal_vm::opr_diveq(){op_calc_eq(/);}
inline void nasal_vm::opr_lnkeq()
{
nasal_ref new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=mem_addr[0]->to_string()+stack_top[-1]->to_string();
*new_val.str()=mem_addr[0].to_string()+stack_top[-1].to_string();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
#define op_calc_eq_const(type)\
nasal_ref new_val=gc.gc_alloc(vm_num);\
new_val->ptr.num=mem_addr[0]->to_number() type num_table[imm[pc]];\
nasal_ref new_val(vm_num,mem_addr[0].to_number() type num_table[imm[pc]]);\
stack_top[0]=mem_addr[0]=new_val;
inline void nasal_vm::opr_addeqc(){op_calc_eq_const(+);}
@ -444,7 +437,7 @@ inline void nasal_vm::opr_diveqc(){op_calc_eq_const(/);}
inline void nasal_vm::opr_lnkeqc()
{
nasal_ref new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=mem_addr[0]->to_string()+str_table[imm[pc]];
*new_val.str()=mem_addr[0].to_string()+str_table[imm[pc]];
stack_top[0]=mem_addr[0]=new_val;
return;
}
@ -458,45 +451,45 @@ inline void nasal_vm::opr_eq()
{
nasal_ref val2=stack_top[0];
nasal_ref val1=(--stack_top)[0];
int a_type=val1->type;
int b_type=val2->type;
uint8_t a_type=val1.type;
uint8_t b_type=val2.type;
if(a_type==vm_nil && b_type==vm_nil)
stack_top[0]=gc.one_addr;
stack_top[0]=gc.one;
else if(a_type==vm_str && b_type==vm_str)
stack_top[0]=(*val1->ptr.str==*val2->ptr.str)?gc.one_addr:gc.zero_addr;
stack_top[0]=(*val1.str()==*val2.str())?gc.one:gc.zero;
else if(a_type==vm_num || b_type==vm_num)
stack_top[0]=(val1->to_number()==val2->to_number())?gc.one_addr:gc.zero_addr;
stack_top[0]=(val1.to_number()==val2.to_number())?gc.one:gc.zero;
else
stack_top[0]=(val1==val2)?gc.one_addr:gc.zero_addr;
stack_top[0]=(val1==val2)?gc.one:gc.zero;
return;
}
inline void nasal_vm::opr_neq()
{
nasal_ref val2=stack_top[0];
nasal_ref val1=(--stack_top)[0];
int a_type=val1->type;
int b_type=val2->type;
uint8_t a_type=val1.type;
uint8_t b_type=val2.type;
if(a_type==vm_nil && b_type==vm_nil)
stack_top[0]=gc.zero_addr;
stack_top[0]=gc.zero;
else if(a_type==vm_str && b_type==vm_str)
stack_top[0]=(*val1->ptr.str!=*val2->ptr.str)?gc.one_addr:gc.zero_addr;
stack_top[0]=(*val1.str()!=*val2.str())?gc.one:gc.zero;
else if(a_type==vm_num || b_type==vm_num)
stack_top[0]=(val1->to_number()!=val2->to_number())?gc.one_addr:gc.zero_addr;
stack_top[0]=(val1.to_number()!=val2.to_number())?gc.one:gc.zero;
else
stack_top[0]=(val1!=val2)?gc.one_addr:gc.zero_addr;
stack_top[0]=(val1!=val2)?gc.one:gc.zero;
return;
}
#define op_cmp(type)\
--stack_top;\
stack_top[0]=(stack_top[0]->to_number() type stack_top[1]->to_number())?gc.one_addr:gc.zero_addr;
stack_top[0]=(stack_top[0].to_number() type stack_top[1].to_number())?gc.one:gc.zero;
inline void nasal_vm::opr_less(){op_cmp(<);}
inline void nasal_vm::opr_leq(){op_cmp(<=);}
inline void nasal_vm::opr_grt(){op_cmp(>);}
inline void nasal_vm::opr_geq(){op_cmp(>=);}
#define op_cmp_const(type) stack_top[0]=(stack_top[0]->to_number() type num_table[imm[pc]])?gc.one_addr:gc.zero_addr;
#define op_cmp_const(type) stack_top[0]=(stack_top[0].to_number() type num_table[imm[pc]])?gc.one:gc.zero;
inline void nasal_vm::opr_lessc(){op_cmp_const(<);}
inline void nasal_vm::opr_leqc(){op_cmp_const(<=);}
@ -529,7 +522,7 @@ inline void nasal_vm::opr_jf()
inline void nasal_vm::opr_counter()
{
counter.push(-1);
if(stack_top[0]->type!=vm_vec)
if(stack_top[0].type!=vm_vec)
die("cnt: must use vector in forindex/foreach");
return;
}
@ -540,18 +533,17 @@ inline void nasal_vm::opr_cntpop()
}
inline void nasal_vm::opr_findex()
{
if(++counter.top()>=stack_top[0]->ptr.vec->elems.size())
if(++counter.top()>=stack_top[0].vec()->elems.size())
{
pc=imm[pc]-1;
return;
}
(++stack_top)[0]=gc.gc_alloc(vm_num);
stack_top[0]->ptr.num=counter.top();
(++stack_top)[0]={vm_num,static_cast<double>(counter.top())};
return;
}
inline void nasal_vm::opr_feach()
{
std::vector<nasal_ref>& ref=stack_top[0]->ptr.vec->elems;
std::vector<nasal_ref>& ref=stack_top[0].vec()->elems;
if(++counter.top()>=ref.size())
{
pc=imm[pc]-1;
@ -567,47 +559,46 @@ inline void nasal_vm::opr_callg()
}
inline void nasal_vm::opr_calll()
{
(++stack_top)[0]=gc.local.back()->ptr.vec->elems[imm[pc]];
(++stack_top)[0]=gc.local.back().vec()->elems[imm[pc]];
return;
}
inline void nasal_vm::opr_callv()
{
nasal_ref val=stack_top[0];
nasal_ref vec_addr=(--stack_top)[0];
if(vec_addr->type==vm_vec)
nasal_ref vec=(--stack_top)[0];
if(vec.type==vm_vec)
{
stack_top[0]=vec_addr->ptr.vec->get_val(val->to_number());
if(!stack_top[0])
die("callv: index out of range:"+std::to_string(val->to_number()));
stack_top[0]=vec.vec()->get_val(val.to_number());
if(stack_top[0].type==vm_none)
die("callv: index out of range:"+std::to_string(val.to_number()));
}
else if(vec_addr->type==vm_hash)
else if(vec.type==vm_hash)
{
if(val->type!=vm_str)
if(val.type!=vm_str)
{
die("callv: must use string as the key");
return;
}
stack_top[0]=vec_addr->ptr.hash->get_val(*val->ptr.str);
if(!stack_top[0])
stack_top[0]=vec.hash()->get_val(*val.value.gcobj->ptr.str);
if(stack_top[0].type==vm_none)
{
die("callv: cannot find member \""+*val->ptr.str+"\" of this hash");
die("callv: cannot find member \""+*val.str()+"\" of this hash");
return;
}
if(stack_top[0]->type==vm_func)
stack_top[0]->ptr.func->closure->ptr.vec->elems[0]=val;// me
if(stack_top[0].type==vm_func)
stack_top[0].func()->closure.vec()->elems[0]=val;// me
}
else if(vec_addr->type==vm_str)
else if(vec.type==vm_str)
{
std::string& str=*vec_addr->ptr.str;
int num=val->to_number();
std::string& str=*vec.str();
int num=val.to_number();
int str_size=str.length();
if(num<-str_size || num>=str_size)
{
die("callv: index out of range:"+std::to_string(val->to_number()));
die("callv: index out of range:"+std::to_string(val.to_number()));
return;
}
stack_top[0]=gc.gc_alloc(vm_num);
stack_top[0]->ptr.num=(str[num>=0? num:num+str_size]);
stack_top[0]={vm_num,static_cast<double>(str[num>=0? num:num+str_size])};
}
else
die("callv: must call a vector/hash/string");
@ -616,33 +607,33 @@ inline void nasal_vm::opr_callv()
inline void nasal_vm::opr_callvi()
{
nasal_ref val=stack_top[0];
if(val->type!=vm_vec)
if(val.type!=vm_vec)
{
die("callvi: must use a vector");
return;
}
// cannot use operator[],because this may cause overflow
(++stack_top)[0]=val->ptr.vec->get_val(imm[pc]);
if(!stack_top[0])
(++stack_top)[0]=val.vec()->get_val(imm[pc]);
if(stack_top[0].type==vm_none)
die("callvi: index out of range:"+std::to_string(imm[pc]));
return;
}
inline void nasal_vm::opr_callh()
{
nasal_ref val=stack_top[0];
if(val->type!=vm_hash)
if(val.type!=vm_hash)
{
die("callh: must call a hash");
return;
}
stack_top[0]=val->ptr.hash->get_val(str_table[imm[pc]]);
if(!stack_top[0])
stack_top[0]=val.hash()->get_val(str_table[imm[pc]]);
if(stack_top[0].type==vm_none)
{
die("callh: member \""+str_table[imm[pc]]+"\" does not exist");
return;
}
if(stack_top[0]->type==vm_func)
stack_top[0]->ptr.func->closure->ptr.vec->elems[0]=val;// me
if(stack_top[0].type==vm_func)
stack_top[0].func()->closure.vec()->elems[0]=val;// me
return;
}
inline void nasal_vm::opr_callfv()
@ -651,26 +642,26 @@ inline void nasal_vm::opr_callfv()
uint32_t args_size=imm[pc];
nasal_ref* vec=stack_top-args_size+1;
nasal_ref func_addr=vec[-1];
if(func_addr->type!=vm_func)
if(func_addr.type!=vm_func)
{
die("callfv: must call a function");
return;
}
// push new local scope
auto& ref_func=*func_addr->ptr.func;
cls_stk.push(func_addr->ptr.func->closure);
auto& ref_func=*func_addr.func();
cls_stk.push(func_addr.func()->closure);
gc.local.push_back(gc.gc_alloc(vm_vec));
gc.local.back()->ptr.vec->elems=ref_func.closure->ptr.vec->elems;
gc.local.back().vec()->elems=ref_func.closure.vec()->elems;
// load parameters
auto& ref_default=ref_func.default_para;
auto& ref_closure=gc.local.back()->ptr.vec->elems;
auto& ref_closure=gc.local.back().vec()->elems;
uint32_t offset=ref_func.offset;
uint32_t para_size=ref_func.key_table.size();
// load arguments
if(args_size<para_size && !ref_default[args_size])
if(args_size<para_size && ref_default[args_size].type==vm_none)
{
// if the first default value is not nullptr,then values after it are not nullptr
// if the first default value is not vm_none,then values after it are not nullptr
die("callfv: lack argument(s)");
return;
}
@ -685,7 +676,7 @@ inline void nasal_vm::opr_callfv()
{
nasal_ref vec_addr=gc.gc_alloc(vm_vec);
for(uint32_t i=para_size;i<args_size;++i)
vec_addr->ptr.vec->elems.push_back(vec[i]);
vec_addr.vec()->elems.push_back(vec[i]);
ref_closure[para_size+offset]=vec_addr;
}
@ -697,21 +688,21 @@ inline void nasal_vm::opr_callfv()
inline void nasal_vm::opr_callfh()
{
// get parameter list and function value
auto& ref_hash=stack_top[0]->ptr.hash->elems;
auto& ref_hash=stack_top[0].hash()->elems;
nasal_ref func_addr=stack_top[-1];
if(func_addr->type!=vm_func)
if(func_addr.type!=vm_func)
{
die("callfh: must call a function");
return;
}
// push new local scope
auto& ref_func=*func_addr->ptr.func;
cls_stk.push(func_addr->ptr.func->closure);
auto& ref_func=*func_addr.func();
cls_stk.push(func_addr.func()->closure);
gc.local.push_back(gc.gc_alloc(vm_vec));
gc.local.back()->ptr.vec->elems=ref_func.closure->ptr.vec->elems;
gc.local.back().vec()->elems=ref_func.closure.vec()->elems;
// load parameters
auto& ref_default=ref_func.default_para;
auto& ref_closure=gc.local.back()->ptr.vec->elems;
auto& ref_closure=gc.local.back().vec()->elems;
if(ref_func.dynpara>=0)
{
@ -723,7 +714,7 @@ inline void nasal_vm::opr_callfh()
{
if(ref_hash.count(i.first))
ref_closure[i.second+offset]=ref_hash[i.first];
else if(ref_default[i.second])
else if(ref_default[i.second].type!=vm_none)
ref_closure[i.second+offset]=ref_default[i.second];
else
{
@ -739,8 +730,8 @@ inline void nasal_vm::opr_callfh()
}
inline void nasal_vm::opr_callb()
{
(++stack_top)[0]=(*builtin_func[imm[pc]].func)(gc.local.back()->ptr.vec->elems,gc);
if(!stack_top[0])
(++stack_top)[0]=(*builtin_func[imm[pc]].func)(gc.local.back().vec()->elems,gc);
if(stack_top[0].type==vm_none)
die("native function error.");
return;
}
@ -751,7 +742,7 @@ inline void nasal_vm::opr_slcbegin()
// | resource_vec | <-- stack_top[-1]
// ----------------
(++stack_top)[0]=gc.gc_alloc(vm_vec);
if(stack_top[-1]->type!=vm_vec)
if(stack_top[-1].type!=vm_vec)
die("slcbegin: must slice a vector");
return;
}
@ -764,22 +755,22 @@ inline void nasal_vm::opr_slcend()
inline void nasal_vm::opr_slc()
{
nasal_ref val=(stack_top--)[0];
nasal_ref res=stack_top[-1]->ptr.vec->get_val(val->to_number());
if(!res)
die("slc: index out of range:"+std::to_string(val->to_number()));
stack_top[0]->ptr.vec->elems.push_back(res);
nasal_ref res=stack_top[-1].vec()->get_val(val.to_number());
if(res.type==vm_none)
die("slc: index out of range:"+std::to_string(val.to_number()));
stack_top[0].vec()->elems.push_back(res);
return;
}
inline void nasal_vm::opr_slc2()
{
nasal_ref val2=(stack_top--)[0];
nasal_ref val1=(stack_top--)[0];
std::vector<nasal_ref>& ref=stack_top[-1]->ptr.vec->elems;
std::vector<nasal_ref>& aim=stack_top[0]->ptr.vec->elems;
std::vector<nasal_ref>& ref=stack_top[-1].vec()->elems;
std::vector<nasal_ref>& aim=stack_top[0].vec()->elems;
int type1=val1->type,type2=val2->type;
int num1=val1->to_number();
int num2=val2->to_number();
uint8_t type1=val1.type,type2=val2.type;
int num1=val1.to_number();
int num2=val2.to_number();
int ref_size=ref.size();
if(type1==vm_nil && type2==vm_nil)
{
@ -810,7 +801,7 @@ inline void nasal_vm::opr_mcallg()
}
inline void nasal_vm::opr_mcalll()
{
mem_addr=&(gc.local.back()->ptr.vec->elems[imm[pc]]);
mem_addr=&(gc.local.back().vec()->elems[imm[pc]]);
(++stack_top)[0]=mem_addr[0];
return;
}
@ -818,26 +809,25 @@ inline void nasal_vm::opr_mcallv()
{
nasal_ref val=stack_top[0];
nasal_ref vec_addr=(--stack_top)[0];
int type=vec_addr->type;
if(type==vm_vec)
if(vec_addr.type==vm_vec)
{
mem_addr=vec_addr->ptr.vec->get_mem(val->to_number());
mem_addr=vec_addr.vec()->get_mem(val.to_number());
if(!mem_addr)
die("mcallv: index out of range:"+std::to_string(val->to_number()));
die("mcallv: index out of range:"+std::to_string(val.to_number()));
}
else if(type==vm_hash)
else if(vec_addr.type==vm_hash)
{
if(val->type!=vm_str)
if(val.type!=vm_str)
{
die("mcallv: must use string as the key");
return;
}
nasal_hash& ref=*vec_addr->ptr.hash;
std::string& str=*val->ptr.str;
nasal_hash& ref=*vec_addr.hash();
std::string& str=*val.str();
mem_addr=ref.get_mem(str);
if(!mem_addr)
{
ref.elems[str]=gc.nil_addr;
ref.elems[str]=gc.nil;
mem_addr=ref.get_mem(str);
}
}
@ -848,24 +838,24 @@ inline void nasal_vm::opr_mcallv()
inline void nasal_vm::opr_mcallh()
{
nasal_ref hash_addr=stack_top[0];
if(hash_addr->type!=vm_hash)
if(hash_addr.type!=vm_hash)
{
die("mcallh: must call a hash");
return;
}
nasal_hash& ref=*hash_addr->ptr.hash;
nasal_hash& ref=*hash_addr.hash();
std::string& str=str_table[imm[pc]];
mem_addr=ref.get_mem(str);
if(!mem_addr) // create a new key
{
ref.elems[str]=gc.nil_addr;
ref.elems[str]=gc.nil;
mem_addr=ref.get_mem(str);
}
return;
}
inline void nasal_vm::opr_ret()
{
nasal_func* func=stack_top[-1]->ptr.func;
nasal_func* func=stack_top[-1].func();
uint32_t offset=func->offset;
nasal_ref cls=cls_stk.top();cls_stk.pop();
// same closure detected,update the last local scope instead of the closure
@ -875,20 +865,20 @@ inline void nasal_vm::opr_ret()
// this condition in fact is that two called function are using the same closure
// if this copy of closure is changed, the closure will be updated at the same time
// also the copy of closure that still in using will alse be updated
auto& vec=gc.local.back()->ptr.vec->elems;
auto& func_vec=func->closure->ptr.vec->elems;
auto& vec=gc.local.back().vec()->elems;
auto& func_vec=func->closure.vec()->elems;
gc.local.pop_back();
for(uint32_t i=0;i<offset;++i)
gc.local.back()->ptr.vec->elems[i]=func_vec[i]=vec[i];
gc.local.back().vec()->elems[i]=func_vec[i]=vec[i];
}
else
{
// two closures are not the same, update the func's closure and drop gc.local.back()
auto& vec=func->closure->ptr.vec->elems;
auto& vec=func->closure.vec()->elems;
for(uint32_t i=0;i<offset;++i)
vec[i]=gc.local.back()->ptr.vec->elems[i];
vec[i]=gc.local.back().vec()->elems[i];
gc.local.pop_back();
vec[0]=gc.nil_addr;// set 'me' to nil
vec[0].type=vm_nil;// set 'me' to nil
}
pc=ret.top();ret.pop();// fetch pc
@ -937,7 +927,7 @@ void nasal_vm::run(std::vector<opcode>& exec,bool op_cnt)
goto *code[pc];
nop:
if(canary)
if(canary.value.gcobj)
stackoverflow();
if(op_cnt)
{
@ -957,7 +947,7 @@ nop:
}
return;
// may cause stackoverflow
#define exec_operand(op,num) {op();++count[num];if(!canary)goto *code[++pc];goto nop;}
#define exec_operand(op,num) {op();++count[num];if(!canary.value.gcobj)goto *code[++pc];goto nop;}
// do not cause stackoverflow
#define exec_opnodie(op,num) {op();++count[num];goto *code[++pc];}