change mcall to call->mcall&allow differen lvalue in assignment
This commit is contained in:
parent
ae0dae5956
commit
ab99d2d1ed
86
README.md
86
README.md
|
@ -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)
|
||||
|
|
149
nasal_codegen.h
149
nasal_codegen.h
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
67
nasal_vm.h
67
nasal_vm.h
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue