optimize nasal_ast and fix bug in opr_slc2

This commit is contained in:
ValKmjolnir 2021-10-20 20:54:23 +08:00
parent 4f0acc4d63
commit 540aeb73f4
8 changed files with 418 additions and 404 deletions

View File

@ -1,14 +1,31 @@
.PHONY=test
nasal:main.cpp nasal_ast.h nasal_builtin.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal.h
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -Wshadow
test:nasal
./nasal test/ascii-art.nas
./nasal test/bp.nas
./nasal -c test/bf.nas
./nasal -c test/bfconvertor.nas
./nasal test/bfs.nas
./nasal -t test/bigloop.nas
./nasal -t test/fib.nas
./nasal test/bp.nas
./nasal test/calc.nas
./nasal test/choice.nas
./nasal test/class.nas
# ./nasal test/exception.nas
./nasal -t test/fib.nas
./nasal test/hexdump.nas
./nasal test/json.nas
./nasal test/leetcode1319.nas
./nasal test/lexer.nas
./nasal -c test/life.nas
./nasal -t test/loop.nas
./nasal -c test/mandel.nas
./nasal -t test/mandelbrot.nas
./nasal test/nasal_test.nas
./nasal -t test/pi.nas
./nasal -t test/prime.nas
./nasal -t test/quick_sort.nas
./nasal test/scalar.nas
./nasal test/trait.nas
./nasal -t test/ycombinator.nas
./nasal test/exception.nas

View File

@ -4,17 +4,25 @@
enum ast_node
{
ast_null=0,
ast_root,ast_block,
ast_root,
ast_block,
ast_file, // ast_file is only used to store which file the subtree is on,codegen will generate nothing
ast_nil,ast_num,ast_str,ast_id,ast_func,ast_hash,ast_vec,
ast_hashmember,ast_call,ast_callh,ast_callv,ast_callf,ast_subvec,
ast_args,ast_default_arg,ast_dynamic_id,
ast_hashmember,
ast_call,ast_callh,ast_callv,ast_callf,
ast_subvec,
ast_args,ast_default,ast_dynamic,
ast_and,ast_or,
ast_equal,ast_addeq,ast_subeq,ast_multeq,ast_diveq,ast_lnkeq,
ast_equal,
ast_addeq,ast_subeq,
ast_multeq,ast_diveq,
ast_lnkeq,
ast_cmpeq,ast_neq,
ast_less,ast_leq,
ast_grt,ast_geq,
ast_add,ast_sub,ast_mult,ast_div,ast_link,
ast_add,ast_sub,
ast_mult,ast_div,
ast_link,
ast_neg,ast_not,
ast_trino,
ast_for,ast_forindex,ast_foreach,ast_while,ast_new_iter,
@ -27,17 +35,25 @@ enum ast_node
const char* ast_name[]=
{
"null",
"root","block",
"root",
"block",
"file",
"nil","node_num","node_str","id","func","hash","vec",
"hashmember","call","callh","callv","callf","subvec",
"args","deflt_arg","dyn_id",
"nil","num","str","id","func","hash","vec",
"hashmember",
"call","callh","callv","callf",
"subvec",
"args","default","dynamic",
"and","or",
"=","+=","-=","*=","/=","~=",
"=",
"+=","-=",
"*=","/=",
"~=",
"==","!=",
"<","<=",
">",">=",
"+","-","*","/","~",
"+","-",
"*","/",
"~",
"unary-","unary!",
"trino",
"for","forindex","foreach","while","iter",
@ -59,10 +75,15 @@ public:
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):text_line(l),node_type(t){}
nasal_ast(const nasal_ast&);
nasal_ast(nasal_ast&&);
nasal_ast& operator=(const nasal_ast&);
nasal_ast& operator=(nasal_ast&&);
void print(const int);
void clear();
nasal_ast& operator=(const nasal_ast&);
nasal_ast& operator=(nasal_ast&&);
nasal_ast& operator[](const int index){return node_child[index];}
const nasal_ast& operator[](const int index) const {return node_child[index];}
size_t size() const {return node_child.size();}
void add(nasal_ast&& ast){node_child.push_back(std::move(ast));}
void add(const nasal_ast& ast){node_child.push_back(ast);}
void set_line(const uint32_t l){text_line=l;}
@ -133,8 +154,8 @@ void nasal_ast::print(const int depth)
if(
node_type==ast_str ||
node_type==ast_id ||
node_type==ast_default_arg ||
node_type==ast_dynamic_id ||
node_type==ast_default ||
node_type==ast_dynamic ||
node_type==ast_callh)
std::cout<<":"<<raw_string(node_str);
else if(node_type==ast_num || node_type==ast_file)

View File

@ -63,11 +63,11 @@ nasal_ref builtin_err(const char* func_name,std::string info)
// register builtin function's name and it's address here in this table below
// this table must end with {nullptr,nullptr}
struct func
struct
{
const char* name;
nasal_ref (*func)(std::vector<nasal_ref>&,nasal_gc&);
} builtin_func[]=
} builtin[]=
{
{"__builtin_print", builtin_print },
{"__builtin_append", builtin_append },

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,7 @@ struct nasal_hash// 56 bytes
struct nasal_func// 112 bytes
{
int32_t dynpara; // dynamic parameter name index in hash.
int32_t dynpara; // dynamic parameter name index in hash.
uint32_t entry; // pc will set to entry-1 to call this function
std::vector<nasal_ref> local; // local scope with default value(nasal_ref)
std::vector<nasal_ref> upvalue; // closure
@ -116,11 +116,11 @@ struct nasal_func// 112 bytes
void clear();
};
constexpr uint8_t GC_UNCOLLECTED=0;
constexpr uint8_t GC_COLLECTED =1;
constexpr uint8_t GC_FOUND =2;
struct nasal_val// 16 bytes
{
#define GC_UNCOLLECTED 0
#define GC_COLLECTED 1
#define GC_FOUND 2
uint8_t mark;
uint8_t type;
union

View File

@ -37,14 +37,13 @@ only this kind of node can be recognized as 'import':
*/
if(node.type()!=ast_call)
return false;
const std::vector<nasal_ast>& vec=node.child();
if(vec.size()!=2)
if(node.size()!=2)
return false;
if(vec[0].str()!="import")
if(node[0].str()!="import")
return false;
if(vec[1].type()!=ast_callf)
if(node[1].type()!=ast_callf)
return false;
if(vec[1].child().size()!=1 || vec[1].child()[0].type()!=ast_str)
if(node[1].size()!=1 || node[1][0].type()!=ast_str)
return false;
return true;
}
@ -69,7 +68,7 @@ void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
nasal_ast nasal_import::file_import(nasal_ast& node)
{
// get filename and set node to ast_null
std::string filename=node.child()[1].child()[0].str();
std::string filename=node[1][0].str();
node.clear();
// avoid infinite loading loop

View File

@ -43,8 +43,8 @@ class nasal_parse
private:
uint32_t ptr;
uint32_t error;
uint32_t in_func; // count when generating function block,used to check return-expression
uint32_t in_loop; // count when generating loop block,used to check break/continue-expression
uint32_t in_func; // count when generating function block
uint32_t in_loop; // count when generating loop block
nasal_ast root;
std::vector<token> tokens;
std::vector<token> error_token;
@ -53,7 +53,7 @@ private:
void match(uint32_t type,const char* info=nullptr);
bool check_comma(const uint32_t*);
bool check_multi_scalar();
bool check_function_end(const nasal_ast&);
bool check_func_end(const nasal_ast&);
bool check_special_call();
bool need_semi_check(const nasal_ast&);
void check_memory_reachable(const nasal_ast&);
@ -93,7 +93,7 @@ private:
nasal_ast while_loop();
nasal_ast for_loop();
nasal_ast forei_loop();
nasal_ast new_iter_gen();
nasal_ast iter_gen();
nasal_ast conditional();
nasal_ast continue_expr();
nasal_ast break_expr();
@ -197,7 +197,7 @@ bool nasal_parse::check_multi_scalar()
}
return false;
}
bool nasal_parse::check_function_end(const nasal_ast& node)
bool nasal_parse::check_func_end(const nasal_ast& node)
{
uint32_t type=node.type();
if(type==ast_func)
@ -218,7 +218,7 @@ bool nasal_parse::check_function_end(const nasal_ast& node)
)
return false;
else
return check_function_end(node.child().back());
return check_func_end(node.child().back());
return false;
}
bool nasal_parse::check_special_call()
@ -250,7 +250,7 @@ bool nasal_parse::need_semi_check(const nasal_ast& node)
uint32_t type=node.type();
if(type==ast_for || type==ast_foreach || type==ast_forindex || type==ast_while || type==ast_conditional)
return false;
return !check_function_end(node);
return !check_func_end(node);
}
void nasal_parse::check_memory_reachable(const nasal_ast& node)
{
@ -259,7 +259,7 @@ void nasal_parse::check_memory_reachable(const nasal_ast& node)
const nasal_ast& tmp=node.child().back();
if(tmp.type()==ast_callf)
die(tmp.line(),"bad left-value");
if(tmp.type()==ast_callv && (tmp.child().size()>1 || tmp.child()[0].type()==ast_subvec))
if(tmp.type()==ast_callv && (tmp.size()>1 || tmp[0].type()==ast_subvec))
die(tmp.line(),"bad left-value");
}
else if(node.type()!=ast_id)
@ -380,14 +380,14 @@ nasal_ast nasal_parse::args()
{
match(tok_eq);
special_arg=std::move(tmp);
special_arg.set_type(ast_default_arg);
special_arg.set_type(ast_default);
special_arg.add(calc());
}
else
{
match(tok_ellipsis);
special_arg=std::move(tmp);
special_arg.set_type(ast_dynamic_id);
special_arg.set_type(ast_dynamic);
}
node.add(std::move(special_arg));
}
@ -405,40 +405,41 @@ nasal_ast nasal_parse::args()
std::string format="func(";
for(auto& tmp:node.child())
{
format+=tmp.str();
switch(tmp.type())
{
case ast_id: format+=tmp.str();break;
case ast_default_arg: format+=tmp.str()+"=val";break;
case ast_dynamic_id: format+=tmp.str()+"...";break;
case ast_id: break;
case ast_default: format+="=val";break;
case ast_dynamic: format+="..."; break;
}
format+=",)"[&tmp==&node.child().back()];
}
bool checked_default_val=false,checked_dynamic_ids=false;
bool checked_default=false,checked_dynamic=false;
for(auto& tmp:node.child())
{
if(tmp.type()==ast_default_arg)
checked_default_val=true;
else if(tmp.type()==ast_dynamic_id)
checked_dynamic_ids=true;
if(checked_default_val && tmp.type()!=ast_default_arg)
die(tmp.line(),"must use default paras after using it once: "+format);
if(checked_dynamic_ids && &tmp!=&node.child().back())
if(tmp.type()==ast_default)
checked_default=true;
else if(tmp.type()==ast_dynamic)
checked_dynamic=true;
if(checked_default && tmp.type()!=ast_default)
die(tmp.line(),"must use default paras after using once: "+format);
if(checked_dynamic && &tmp!=&node.child().back())
die(tmp.line(),"dynamic para must be the end: "+format);
}
std::unordered_map<std::string,bool> argname_table;
std::unordered_map<std::string,bool> argname;
for(auto& tmp:node.child())
{
std::string new_name;
std::string name;
switch(tmp.type())
{
case ast_dynamic_id:
case ast_id: new_name=tmp.str();break;
case ast_default_arg:new_name=tmp.str();break;
case ast_dynamic:
case ast_id: name=tmp.str();break;
case ast_default:name=tmp.str();break;
}
if(argname_table.count(new_name))
die(tmp.line(),"parameter's name repeats: "+new_name);
if(argname.count(name))
die(tmp.line(),"parameter's name repeats: "+name);
else
argname_table[new_name]=true;
argname[name]=true;
}
return node;
}
@ -766,9 +767,9 @@ nasal_ast nasal_parse::definition()
match(tok_var);
switch(tokens[ptr].type)
{
case tok_id: node.add(id());match(tok_id); break;
case tok_lcurve: node.add(var_outcurve_def()); break;
default: die(error_line,"expected identifier"); break;
case tok_id: node.add(id());match(tok_id);break;
case tok_lcurve: node.add(var_outcurve_def());break;
default: die(error_line,"expected identifier");break;
}
}
else if(tokens[ptr].type==tok_lcurve)
@ -778,11 +779,11 @@ nasal_ast nasal_parse::definition()
node.add(check_multi_scalar()?multi_scalar(false):calc());
else
node.add(calc());
if(node.child()[0].type()==ast_id && node.child()[1].type()==ast_multi_scalar)
die(node.child()[1].line(),"one identifier cannot accept too many values");
else if(node.child()[0].type()==ast_multi_id && node.child()[1].type()==ast_multi_scalar)
if(node.child()[0].child().size()!=node.child()[1].child().size())
die(node.child()[0].line(),"too much or lack values in multi-definition");
if(node[0].type()==ast_id && node[1].type()==ast_multi_scalar)
die(node[1].line(),"one identifier cannot accept too many values");
else if(node[0].type()==ast_multi_id && node[1].type()==ast_multi_scalar)
if(node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-definition");
return node;
}
nasal_ast nasal_parse::var_incurve_def()
@ -861,9 +862,9 @@ nasal_ast nasal_parse::multi_assgin()
node.add(check_multi_scalar()?multi_scalar(false):calc());
else
node.add(calc());
if(node.child()[1].type()==ast_multi_scalar
&& node.child()[0].child().size()!=node.child()[1].child().size())
die(node.child()[0].line(),"too much or lack values in multi-assignment");
if(node[1].type()==ast_multi_scalar
&& node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-assignment");
return node;
}
nasal_ast nasal_parse::loop()
@ -933,15 +934,15 @@ nasal_ast nasal_parse::forei_loop()
nasal_ast node(tokens[ptr].line,ast_null);
switch(tokens[ptr].type)
{
case tok_forindex: node.set_type(ast_forindex);match(tok_forindex); break;
case tok_foreach: node.set_type(ast_foreach); match(tok_foreach); break;
case tok_forindex:node.set_type(ast_forindex);match(tok_forindex);break;
case tok_foreach: node.set_type(ast_foreach); match(tok_foreach); break;
}
match(tok_lcurve);
// first expression
// foreach/forindex must have an iterator to loop through
if(tokens[ptr].type!=tok_var && tokens[ptr].type!=tok_id)
die(error_line,"expected iterator");
node.add(new_iter_gen());
node.add(iter_gen());
// check semi
match(tok_semi,"expected \';\' in foreach/forindex(iter;vector)");
// check vector
@ -952,7 +953,7 @@ nasal_ast nasal_parse::forei_loop()
node.add(exprs());
return node;
}
nasal_ast nasal_parse::new_iter_gen()
nasal_ast nasal_parse::iter_gen()
{
nasal_ast node(tokens[ptr].line,ast_null);
if(tokens[ptr].type==tok_var)
@ -975,30 +976,29 @@ nasal_ast nasal_parse::new_iter_gen()
nasal_ast nasal_parse::conditional()
{
nasal_ast node(tokens[ptr].line,ast_conditional);
nasal_ast tmp(tokens[ptr].line,ast_if);
nasal_ast ifnode(tokens[ptr].line,ast_if);
match(tok_if);
match(tok_lcurve);
tmp.add(calc());
ifnode.add(calc());
match(tok_rcurve);
tmp.add(exprs());
node.add(std::move(tmp));
// end of if-expression
ifnode.add(exprs());
node.add(std::move(ifnode));
while(tokens[ptr].type==tok_elsif)
{
nasal_ast tmp(tokens[ptr].line,ast_elsif);
nasal_ast elsifnode(tokens[ptr].line,ast_elsif);
match(tok_elsif);
match(tok_lcurve);
tmp.add(calc());
elsifnode.add(calc());
match(tok_rcurve);
tmp.add(exprs());
node.add(std::move(tmp));
elsifnode.add(exprs());
node.add(std::move(elsifnode));
}
if(tokens[ptr].type==tok_else)
{
nasal_ast tmp(tokens[ptr].line,ast_else);
nasal_ast elsenode(tokens[ptr].line,ast_else);
match(tok_else);
tmp.add(exprs());
node.add(std::move(tmp));
elsenode.add(exprs());
node.add(std::move(elsenode));
}
return node;
}
@ -1019,8 +1019,17 @@ nasal_ast nasal_parse::ret_expr()
nasal_ast node(tokens[ptr].line,ast_ret);
match(tok_ret);
uint32_t type=tokens[ptr].type;
if(type==tok_nil || type==tok_num || type==tok_str || type==tok_id || type==tok_func ||
type==tok_sub || type==tok_not || type==tok_lcurve || type==tok_lbracket || type==tok_lbrace)
if(
type==tok_nil ||
type==tok_num ||
type==tok_str ||
type==tok_id ||
type==tok_func ||
type==tok_sub ||
type==tok_not ||
type==tok_lcurve ||
type==tok_lbracket ||
type==tok_lbrace)
node.add(calc());
return node;
}

View File

@ -165,7 +165,7 @@ void nasal_vm::bytecodeinfo(const uint32_t p)
const opcode& code=bytecode[p];
printf("\t0x%.8x: %s 0x%x",p,code_table[code.op].name,code.num);
if(code.op==op_callb)
printf(" <%s>",builtin_func[code.num].name);
printf(" <%s>",builtin[code.num].name);
printf(" (<%s> line %d)\n",files[code.fidx].c_str(),code.line);
}
void nasal_vm::traceback()
@ -286,10 +286,9 @@ inline bool nasal_vm::condition(nasal_ref val)
return val.value.num;
else if(val.type==vm_str)
{
const std::string& str=*val.str();
double num=str2num(str.c_str());
double num=str2num(val.str()->c_str());
if(std::isnan(num))
return str.empty();
return val.str()->empty();
return num;
}
return false;
@ -560,7 +559,7 @@ inline void nasal_vm::opr_findex()
pc=imm[pc]-1;
return;
}
(++top)[0]={vm_num,static_cast<double>(counter.top())};
(++top)[0]={vm_num,(double)counter.top()};
}
inline void nasal_vm::opr_feach()
{
@ -642,7 +641,7 @@ inline void nasal_vm::opr_callh()
}
inline void nasal_vm::opr_callfv()
{
size_t args_size=imm[pc];
uint32_t args_size=imm[pc];
nasal_ref* args=top-args_size+1;
if(args[-1].type!=vm_func)
die("callfv: must call a function");
@ -655,13 +654,13 @@ inline void nasal_vm::opr_callfv()
// load parameters
auto& closure=gc.local.back().vec()->elems;
size_t para_size=func.keys.size();
uint32_t para_size=func.keys.size();
// load arguments
// if the first default value is not vm_none,then values after it are not nullptr
if(args_size<para_size && func.local[args_size+1/*1 is reserved for 'me'*/].type==vm_none)
die("callfv: lack argument(s)");
// if args_size>para_size,for 0 to args_size will cause corruption
size_t min_size=std::min(para_size,args_size);
uint32_t min_size=std::min(para_size,args_size);
for(uint32_t i=0;i<min_size;++i)
closure[i+1]=args[i];
// load dynamic argument if args_size>=para_size
@ -707,7 +706,7 @@ inline void nasal_vm::opr_callfh()
}
inline void nasal_vm::opr_callb()
{
(++top)[0]=(*builtin_func[imm[pc]].func)(gc.local.back().vec()->elems,gc);
(++top)[0]=(*builtin[imm[pc]].func)(gc.local.back().vec()->elems,gc);
if(top[0].type==vm_none)
die("native function error.");
}
@ -744,7 +743,7 @@ inline void nasal_vm::opr_slc2()
uint8_t type1=val1.type,type2=val2.type;
int num1=val1.to_number();
int num2=val2.to_number();
size_t size=ref.size();
int size=ref.size();
if(type1==vm_nil && type2==vm_nil)
{
num1=0;
@ -799,7 +798,7 @@ inline void nasal_vm::opr_mcallv()
mem_addr=ref.get_mem(str);
if(!mem_addr)
{
ref.elems[str]=gc.nil;
ref.elems[str]={vm_nil};
mem_addr=ref.get_mem(str);
}
}
@ -816,7 +815,7 @@ inline void nasal_vm::opr_mcallh()
mem_addr=ref.get_mem(str);
if(!mem_addr) // create a new key
{
ref.elems[str]=gc.nil;
ref.elems[str]={vm_nil};
mem_addr=ref.get_mem(str);
}
}