change mcall to call->mcall&allow differen lvalue in assignment

This commit is contained in:
ValKmjolnir 2021-06-21 16:46:47 +08:00
parent ae0dae5956
commit ab99d2d1ed
4 changed files with 206 additions and 120 deletions

View File

@ -243,6 +243,92 @@ 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:
```javascript
var f=func()
{
var _=[{_:0},{_:1}];
return func(x)
{
return _[x];
}
}
var m=f();
m(0)._=m(1)._=10;
[0,1,2][1:2][0]=0;
```
In the old version,parser will check this left-value and tells that these kinds of left-value are not allowed(bad lvalue).
But now it can work.And you could see its use by reading the code above.To make sure this assignment works correctly,codegen will generate byte code by 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
.number 2
.symbol _
.symbol x
0x00000000: intg 0x00000002
0x00000001: newf 0x00000005
0x00000002: intl 0x00000002
0x00000003: offset 0x00000001
0x00000004: jmp 0x00000017
0x00000005: newh 0x00000000
0x00000006: pzero 0x00000000
0x00000007: happ 0x00000000 (_)
0x00000008: newh 0x00000000
0x00000009: pone 0x00000000
0x0000000a: happ 0x00000000 (_)
0x0000000b: newv 0x00000002
0x0000000c: loadl 0x00000001
0x0000000d: newf 0x00000012
0x0000000e: intl 0x00000003
0x0000000f: offset 0x00000002
0x00000010: para 0x00000001 (x)
0x00000011: jmp 0x00000016
0x00000012: calll 0x00000001
0x00000013: calll 0x00000002
0x00000014: callv 0x00000000
0x00000015: ret 0x00000000
0x00000016: ret 0x00000000
0x00000017: loadg 0x00000000
0x00000018: callg 0x00000000
0x00000019: callfv 0x00000000
0x0000001a: loadg 0x00000001
0x0000001b: pnum 0x00000000 (10.000000)
0x0000001c: callg 0x00000001
0x0000001d: pone 0x00000000
0x0000001e: callfv 0x00000001
0x0000001f: mcallh 0x00000000 (_)
0x00000020: meq 0x00000000
0x00000021: callg 0x00000001
0x00000022: pzero 0x00000000
0x00000023: callfv 0x00000001
0x00000024: mcallh 0x00000000 (_)
0x00000025: meq 0x00000000
0x00000026: pop 0x00000000
0x00000027: pzero 0x00000000
0x00000028: pzero 0x00000000
0x00000029: pone 0x00000000
0x0000002a: pnum 0x00000001 (2.000000)
0x0000002b: newv 0x00000003
0x0000002c: slcbeg 0x00000000
0x0000002d: pone 0x00000000
0x0000002e: pnum 0x00000001 (2.000000)
0x0000002f: slc2 0x00000000
0x00000030: slcend 0x00000000
0x00000031: pzero 0x00000000
0x00000032: mcallv 0x00000000
0x00000033: meq 0x00000000
0x00000034: pop 0x00000000
0x00000035: nop 0x00000000
```
As you could see from the bytecode above,mcall/mcallv/mcallh operands' using frequency will reduce,call/callv/callh/callfv/callfh at the opposite.
And because of the new structure of mcall, addr_stack, a stack used to store the memory address, is deleted from 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.
## Test data
### version 6.5(i5-8250U windows10 2021/6/19)

View File

@ -3,68 +3,68 @@
enum op_code
{
op_nop, // do nothing and end the vm main loop
op_intg, // global scope size
op_intl, // local scope size
op_offset, // offset of local scope of function in closure
op_loadg, // load global symbol value
op_loadl, // load local symbol value
op_pnum, // push constant number to the stack
op_pone, // push 1 to the stack
op_pzero, // push 0 to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_defpara, // default parameter
op_dynpara, // dynamic parameter
op_unot, // !
op_usub, // -
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addeq, // +=
op_subeq, // -=
op_muleq, // *=
op_diveq, // /=
op_lnkeq, // ~=
op_meq, // =
op_eq, // ==
op_neq, // !=
op_less, // <
op_leq, // <=
op_grt, // >
op_geq, // >=
op_pop, // pop a value from stack
op_jmp, // jump with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_cntpop, // pop counter
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // call value in global scope
op_calll, // call value in local scope
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call builtin-function
op_slcbegin, // begin of slice like: vec[1,2,3:6,0,-1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
op_nop, // do nothing and end the vm main loop
op_intg, // global scope size
op_intl, // local scope size
op_offset, // offset of local scope of function in closure
op_loadg, // load global symbol value
op_loadl, // load local symbol value
op_pnum, // push constant number to the stack
op_pone, // push 1 to the stack
op_pzero, // push 0 to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_defpara, // default parameter
op_dynpara, // dynamic parameter
op_unot, // !
op_usub, // -
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addeq, // +=
op_subeq, // -=
op_muleq, // *=
op_diveq, // /=
op_lnkeq, // ~=
op_meq, // =
op_eq, // ==
op_neq, // !=
op_less, // <
op_leq, // <=
op_grt, // >
op_geq, // >=
op_pop, // pop a value from stack
op_jmp, // jump with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_cntpop, // pop counter
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // call value in global scope
op_calll, // call value in local scope
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call builtin-function
op_slcbegin,// begin of slice like: vec[1,2,3:6,0,-1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
};
struct
@ -499,16 +499,27 @@ void nasal_codegen::call_func(nasal_ast& ast)
void nasal_codegen::mcall(nasal_ast& ast)
{
mcall_id(ast.get_type()==ast_id?ast:ast.get_children()[0]);
int child_size=ast.get_children().size();
for(int i=1;i<child_size;++i)
if(ast.get_type()==ast_id)
{
mcall_id(ast);
return;
}
calc_gen(ast.get_children()[0]);
for(int i=1;i<ast.get_children().size()-1;++i)
{
nasal_ast& tmp=ast.get_children()[i];
if(tmp.get_type()==ast_callh)
mcall_hash(tmp);
else if(tmp.get_type()==ast_callv)
mcall_vec(tmp);
switch(tmp.get_type())
{
case ast_callh:call_hash(tmp);break;
case ast_callv:call_vec(tmp); break;
case ast_callf:call_func(tmp);break;
}
}
nasal_ast& tmp=ast.get_children().back();
if(tmp.get_type()==ast_callh)
mcall_hash(tmp);
else if(tmp.get_type()==ast_callv)
mcall_vec(tmp);
return;
}

View File

@ -264,18 +264,14 @@ void nasal_parse::check_memory_reachable(nasal_ast& node)
{
if(node.get_type()==ast_call)
{
if(node.get_children()[0].get_type()!=ast_id)
die(node.get_line(),"cannot get memory of temp data");
for(auto& tmp:node.get_children())
{
if(tmp.get_type()==ast_callf)
die(tmp.get_line(),"cannot get memory of func-returned value");
if(tmp.get_type()==ast_callv && (tmp.get_children().size()>1 || tmp.get_children()[0].get_type()==ast_subvec))
die(tmp.get_line(),"cannot get memory of temp sliced vector");
}
nasal_ast& tmp=node.get_children().back();
if(tmp.get_type()==ast_callf)
die(tmp.get_line(),"bad left-value");
if(tmp.get_type()==ast_callv && (tmp.get_children().size()>1 || tmp.get_children()[0].get_type()==ast_subvec))
die(tmp.get_line(),"bad left-value");
}
else if(node.get_type()!=ast_id)
die(node.get_line(),"cannot get memory of temp data");
die(node.get_line(),"bad left-value");
return;
}
nasal_ast nasal_parse::null_node_gen()
@ -428,9 +424,9 @@ nasal_ast nasal_parse::args_gen()
else if(tmp.get_type()==ast_dynamic_id)
checked_dynamic_ids=true;
if(checked_default_val && tmp.get_type()!=ast_default_arg)
die(tmp.get_line(),"must use default parameters after using it once: "+args_format);
die(tmp.get_line(),"must use default paras after using it once: "+args_format);
if(checked_dynamic_ids && &tmp!=&node.get_children().back())
die(tmp.get_line(),"dynamic parameter must be the end: "+args_format);
die(tmp.get_line(),"dynamic para must be the end: "+args_format);
}
std::unordered_map<std::string,bool> argname_table;
for(auto& tmp:node.get_children())
@ -439,8 +435,8 @@ nasal_ast nasal_parse::args_gen()
switch(tmp.get_type())
{
case ast_dynamic_id:
case ast_id:new_name=tmp.get_str();break;
case ast_default_arg:tmp.get_children()[0].get_str();break;
case ast_id: new_name=tmp.get_str();break;
case ast_default_arg:new_name=tmp.get_children()[0].get_str();break;
}
if(argname_table.count(new_name))
die(tmp.get_line(),"parameter's name repeats: "+new_name);

View File

@ -5,16 +5,16 @@ class nasal_vm
{
private:
/* reference from nasal_gc */
nasal_val**& stack_top; // stack top
nasal_val**& stack_top;// stack top
/* values of nasal_vm */
int pc; // program counter
bool loop_mark; // when mark is false,break the main loop
std::stack<int> ret; // ptr stack stores address for function to return
std::stack<int> counter; // iterator stack for forindex/foreach
std::vector<std::string> str_table; // symbols used in process
std::vector<opcode> exec_code; // byte codes store here
std::stack<nasal_val**> addr_stack;// used for mem_call
nasal_gc gc; // garbage collector
int pc; // program counter
bool loop_mark;// when mark is false,break the main loop
std::stack<int> ret; // ptr stack stores address for function to return
std::stack<int> counter; // iterator stack for forindex/foreach
std::vector<std::string> str_table;// symbols used in process
std::vector<opcode> exec_code;// byte codes store here
nasal_val** mem_addr; // used for mem_call
nasal_gc gc; // garbage collector
void die(std::string);
bool condition(nasal_val*);
@ -308,48 +308,42 @@ void nasal_vm::opr_lnk()
}
void nasal_vm::opr_addeq()
{
nasal_val** mem_addr=addr_stack.top();addr_stack.pop();
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()+stack_top[0]->to_number();
stack_top[0]=mem_addr[0]=new_val;
new_val->ptr.num=mem_addr[0]->to_number()+stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_subeq()
{
nasal_val** mem_addr=addr_stack.top();addr_stack.pop();
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()-stack_top[0]->to_number();
stack_top[0]=mem_addr[0]=new_val;
new_val->ptr.num=mem_addr[0]->to_number()-stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_muleq()
{
nasal_val** mem_addr=addr_stack.top();addr_stack.pop();
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()*stack_top[0]->to_number();
stack_top[0]=mem_addr[0]=new_val;
new_val->ptr.num=mem_addr[0]->to_number()*stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_diveq()
{
nasal_val** mem_addr=addr_stack.top();addr_stack.pop();
nasal_val* new_val=gc.gc_alloc(vm_num);
new_val->ptr.num=mem_addr[0]->to_number()/stack_top[0]->to_number();
stack_top[0]=mem_addr[0]=new_val;
new_val->ptr.num=mem_addr[0]->to_number()/stack_top[-1]->to_number();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_lnkeq()
{
nasal_val** mem_addr=addr_stack.top();addr_stack.pop();
nasal_val* new_val=gc.gc_alloc(vm_str);
*new_val->ptr.str=mem_addr[0]->to_string()+stack_top[0]->to_string();
stack_top[0]=mem_addr[0]=new_val;
*new_val->ptr.str=mem_addr[0]->to_string()+stack_top[-1]->to_string();
(--stack_top)[0]=mem_addr[0]=new_val;
return;
}
void nasal_vm::opr_meq()
{
addr_stack.top()[0]=stack_top[0];
addr_stack.pop();
mem_addr[0]=(--stack_top)[0];
return;
}
void nasal_vm::opr_eq()
@ -738,18 +732,20 @@ void nasal_vm::opr_slc2()
}
void nasal_vm::opr_mcallg()
{
addr_stack.push(&gc.global[exec_code[pc].num]);
mem_addr=&gc.global[exec_code[pc].num];
(++stack_top)[0]=mem_addr[0];
return;
}
void nasal_vm::opr_mcalll()
{
addr_stack.push(&gc.local.back()[exec_code[pc].num]);
mem_addr=&gc.local.back()[exec_code[pc].num];
(++stack_top)[0]=mem_addr[0];
return;
}
void nasal_vm::opr_mcallv()
{
nasal_val* val=*stack_top--;
nasal_val* vec_addr=*addr_stack.top();
nasal_val* val=stack_top[0];
nasal_val* vec_addr=(--stack_top)[0];
int type=vec_addr->type;
if(type==vm_vec)
{
@ -760,10 +756,9 @@ void nasal_vm::opr_mcallv()
case vm_str:num=(int)str2num(val->ptr.str->c_str());break;
default:die("mcallv: error value type");break;
}
nasal_val** mem_addr=vec_addr->ptr.vec->get_mem(num);
mem_addr=vec_addr->ptr.vec->get_mem(num);
if(!mem_addr)
die("mcallv: index out of range:"+num2str(num));
addr_stack.top()=mem_addr;
}
else if(type==vm_hash)
{
@ -774,13 +769,12 @@ void nasal_vm::opr_mcallv()
}
nasal_hash& ref=*vec_addr->ptr.hash;
std::string& str=*val->ptr.str;
nasal_val** mem_addr=ref.get_mem(str);
mem_addr=ref.get_mem(str);
if(!mem_addr)
{
ref.elems[str]=gc.nil_addr;
mem_addr=ref.get_mem(str);
}
addr_stack.top()=mem_addr;
}
else
die("mcallv: cannot get memory space in other types");
@ -788,7 +782,7 @@ void nasal_vm::opr_mcallv()
}
void nasal_vm::opr_mcallh()
{
nasal_val* hash_addr=*addr_stack.top();
nasal_val* hash_addr=stack_top[0];
if(hash_addr->type!=vm_hash)
{
die("mcallh: must call a hash");
@ -796,13 +790,12 @@ void nasal_vm::opr_mcallh()
}
nasal_hash& ref=*hash_addr->ptr.hash;
std::string& str=str_table[exec_code[pc].num];
nasal_val** mem_addr=ref.get_mem(str);
mem_addr=ref.get_mem(str);
if(!mem_addr) // create a new key
{
ref.elems[str]=gc.nil_addr;
mem_addr=ref.get_mem(str);
}
addr_stack.top()=mem_addr;
return;
}
void nasal_vm::opr_ret()