🎨 improve error report

This commit is contained in:
ValKmjolnir 2022-11-19 22:47:19 +08:00
parent 309a57070c
commit 9196d7815f
6 changed files with 356 additions and 363 deletions

View File

@ -170,6 +170,7 @@ public:
<<ins.nums[num]<<")";break;
case op_callvi:case op_newv: case op_callfv:
case op_intg: case op_intl:
case op_findex:case op_feach:
case op_newf: case op_jmp: case op_jt: case op_jf:
case op_callg: case op_mcallg: case op_loadg:
case op_calll: case op_mcalll: case op_loadl:
@ -220,7 +221,7 @@ private:
std::stack<u32> fbstk;
std::stack<u32> festk;
void die(const string&,const u32,const u32);
void die(const string&,const u32,const u32,const u32);
void regist_num(const f64);
void regist_str(const string&);
void find_symbol(const ast&);
@ -271,13 +272,10 @@ public:
const std::vector<opcode>& codes() const {return code;}
};
void codegen::die(const string& info,const u32 line,const u32 col)
void codegen::die(const string& info,const u32 line,const u32 col,const u32 len=1)
{
err.load(file[fileindex]);
if(col)
err.err("code",line,col,info);
else
err.err("code",line,info);
err.err("code",line,col,len,info);
}
void codegen::regist_num(const f64 num)
@ -406,6 +404,32 @@ void codegen::hash_gen(const ast& node)
void codegen::func_gen(const ast& node)
{
// parameter list format check
bool checked_default=false;
bool checked_dynamic=false;
std::unordered_map<string,bool> argname;
for(auto& tmp:node[0].child()){
if(tmp.type()==ast_default){
checked_default=true;
}else if(tmp.type()==ast_dynamic){
checked_dynamic=true;
}
// check default parameter and dynamic parameter
if(checked_default && tmp.type()!=ast_default){
die("must use default parameters here",tmp.line(),tmp.col(),tmp.str().length());
}
if(checked_dynamic && &tmp!=&node[0].child().back()){
die("dynamic parameter must be the last one",tmp.line(),tmp.col(),tmp.str().length());
}
// check redefinition
string name=tmp.str();
if(argname.count(name)){
die("redefinition of parameter: "+name,tmp.line(),tmp.col(),name.length());
}else{
argname[name]=true;
}
}
usize newf=code.size();
gen(op_newf,0,node.line());
usize lsize=code.size();
@ -423,7 +447,7 @@ void codegen::func_gen(const ast& node)
{
const string& str=tmp.str();
if(str=="me")
die("\"me\" should not be a parameter",tmp.line(),tmp.col());
die("\"me\" should not be a parameter",tmp.line(),tmp.col(),tmp.str().length());
regist_str(str);
switch(tmp.type())
{
@ -486,7 +510,7 @@ void codegen::call_id(const ast& node)
{
gen(op_callb,i,node.line());
if(local.empty())
die("should warp native function in local scope",node.line(),node.col());
die("should warp native function in local scope",node.line(),node.col(),node.str().length());
return;
}
i32 index;
@ -505,7 +529,7 @@ void codegen::call_id(const ast& node)
gen(op_callg,index,node.line());
return;
}
die("undefined symbol \""+str+"\"",node.line(),node.col());
die("undefined symbol \""+str+"\"",node.line(),node.col(),node.str().length());
}
void codegen::call_hash(const ast& node)
@ -602,7 +626,7 @@ void codegen::mcall_id(const ast& node)
for(u32 i=0;builtin[i].name;++i)
if(builtin[i].name==str)
{
die("cannot modify native function",node.line(),node.col());
die("cannot modify native function",node.line(),node.col(),node.str().length());
return;
}
i32 index;
@ -621,7 +645,7 @@ void codegen::mcall_id(const ast& node)
gen(op_mcallg,index,node.line());
return;
}
die("undefined symbol \""+str+"\"",node.line(),node.col());
die("undefined symbol \""+str+"\"",node.line(),node.col(),node.str().length());
}
void codegen::mcall_vec(const ast& node)
@ -677,11 +701,23 @@ void codegen::multi_def(const ast& node)
void codegen::def_gen(const ast& node)
{
if(node[0].type()==ast_id && node[1].type()==ast_tuple){
die("cannot accept too many values",node[1].line(),node[1].col(),1);
}else if(node[0].type()==ast_multi_id && node[1].type()==ast_tuple && node[0].size()<node[1].size()){
die("lack values in multi-definition",node[1].line(),node[1].col(),1);
}else if(node[0].type()==ast_multi_id && node[1].type()==ast_tuple && node[0].size()>node[1].size()){
die("too many values in multi-definition",node[1].line(),node[1].col(),1);
}
node[0].type()==ast_id?single_def(node):multi_def(node);
}
void codegen::multi_assign_gen(const ast& node)
{
if(node[1].type()==ast_tuple && node[0].size()<node[1].size()){
die("lack values in multi-assignment",node[1].line(),node[1].col(),1);
}else if(node[1].type()==ast_tuple && node[0].size()>node[1].size()){
die("too many values in multi-assignment",node[1].line(),node[1].col(),1);
}
i32 size=node[0].size();
if(node[1].type()==ast_tuple)
{
@ -1244,10 +1280,14 @@ const error& codegen::compile(const parse& parse,const linker& import)
gen(op_intg,global.size(),0);
block_gen(parse.tree()); // generate main block
gen(op_exit,0,0);
if(global.size()>=STACK_DEPTH)
die("too many global variants: "+std::to_string(global.size()),0,0);
if(code.size()>0xffffffff)
die("too large generated bytecode file: "+std::to_string(code.size()),0,0);
if(global.size()>=STACK_DEPTH){
err.load(file[fileindex]);
err.err("code","too many global variants: "+std::to_string(global.size()));
}
if(code.size()>0xffffffff){
err.load(file[fileindex]);
err.err("code","too large generated bytecode file: "+std::to_string(code.size()));
}
return err;
}

View File

@ -77,20 +77,24 @@ protected:
string file;
std::vector<string> res;
public:
flstream():file("<error-file-path>"){}
void load(const string& f)
{
if(file==f) return; // don't need to load a loaded file
if(file==f){ // don't need to load a loaded file
return;
}else{
file=f;
}
res.clear();
std::ifstream in(f,std::ios::binary);
if(in.fail())
{
if(in.fail()){
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::exit(1);
}
while(!in.eof()){
string line;
while(!in.eof())
{
std::getline(in,line);
res.push_back(line);
}
@ -112,54 +116,41 @@ private:
}
public:
error():cnt(0){}
void fatal(const string& stage,const string& info)
{
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<red<<file<<"\n\n";
std::exit(1);
}
void err(const string& stage,const string& info)
{
++cnt;
std::cerr
<<red<<stage<<": "
<<white<<info<<reset<<"\n\n";
<<red<<stage<<": "<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
}
void err(const string& stage,u32 line,u32 col,const string& info)
void err(const string& stage,u32 line,u32 col,u32 len,const string& info)
{
++cnt;
const string& code=res[line-1];
col=col?col:1;
len=len?len:1;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<red<<file<<":"<<line<<":"<<col<<reset<<"\n";
const string& code=line?res[line-1]:"# empty line";
const string iden=identation(std::to_string(line).length());
std::cerr
<<red<<stage<<": "
<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<reset
<<orange<<file<<":"<<line<<":"<<col<<"\n";
if(!line)
{
std::cerr<<"\n";
return;
}
std::cerr
<<cyan<<iden<<" | "<<reset<<"\n"
<<cyan<<line<<" | "<<reset<<code<<"\n"
<<cyan<<iden<<" | "<<reset;
for(i32 i=0;i<(i32)col-1;++i)
for(i32 i=0;i<(i32)col-(i32)len;++i)
std::cerr<<char(" \t"[code[i]=='\t']);
std::cerr<<red<<"^ "<<info<<reset<<"\n\n";
}
void err(const string& stage,u32 line,const string& info)
{
++cnt;
const string iden=identation(std::to_string(line).length());
std::cerr
<<red<<stage<<": "
<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<reset
<<orange<<file<<":"<<line<<"\n";
if(!line)
{
std::cerr<<"\n";
return;
}
std::cerr
<<cyan<<iden<<" | "<<reset<<"\n"
<<cyan<<line<<" | "<<reset<<res[line-1]<<"\n"
<<cyan<<iden<<" | "<<reset<<"\n\n";
for(u32 i=0;i<len;++i)
std::cerr<<red<<"^";
std::cerr<<red<<" "<<info<<reset<<"\n\n";
}
void chkerr() const {if(cnt)std::exit(1);}
};

View File

@ -669,15 +669,19 @@ void gc::info()
len=std::to_string(ini[i]+size[i]*incr[i]).length();
maxlen=maxlen<len?len:maxlen;
}
double total=0;
for(u8 i=0;i<gc_tsize;++i)
if(gcnt[i] || acnt[i] || ini[i] || size[i])
{
total+=gcnt[i];
std::cout<<" "<<name[i]<<" | "<<std::left<<std::setw(maxlen)<<std::setfill(' ')<<gcnt[i];
std::cout<<" | "<<std::left<<std::setw(maxlen)<<std::setfill(' ')<<acnt[i];
std::cout<<" | "<<std::left<<std::setw(maxlen)<<std::setfill(' ')<<ini[i]+size[i]*incr[i]<<" (+"<<size[i]<<")\n";
}
double t=worktime*1.0/1000000000;
double t=worktime*1.0/1000000000; // seconds
std::cout<<" time | "<<(t<0.1? t*1000:t)<<(t<0.1? "ms\n":"s\n");
if(total)
std::cout<<" avg | "<<t/total*1000<<" ms\n";
}
var gc::alloc(u8 type)
{

View File

@ -137,7 +137,6 @@ private:
bool is_str(char);
bool is_single_opr(char);
bool is_calc_opr(char);
void die(const string&);
void open(const string&);
string utf8_gen();
string id_gen();
@ -199,10 +198,10 @@ bool lexer::is_calc_opr(char c)
);
}
void lexer::die(const string& info)
{
err.err("lexer",line,column,info);
}
// void lexer::die(const string& info)
// {
// err.err("lexer",line,column,1,info);
// }
void lexer::open(const string& file)
{
@ -246,9 +245,8 @@ string lexer::utf8_gen()
string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i)
utf_info+=" 0x"+chrhex(tmp[i]);
die("invalid utf-8 <"+utf_info+">");
err.err("lexer","fatal error occurred, stop");
std::exit(1);
err.err("lexer",line,column,1,"invalid utf-8 <"+utf_info+">");
err.fatal("lexer","fatal error occurred, stop");
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
@ -289,7 +287,7 @@ string lexer::num_gen()
str+=res[ptr++];
column+=str.length();
if(str.length()<3)// "0x"
die("invalid number `"+str+"`");
err.err("lexer",line,column,str.length(),"invalid number `"+str+"`");
return str;
}
// generate oct number
@ -299,9 +297,15 @@ string lexer::num_gen()
ptr+=2;
while(ptr<res.size() && is_oct(res[ptr]))
str+=res[ptr++];
bool erfmt=false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr])))
{
erfmt=true;
str+=res[ptr++];
}
column+=str.length();
if(str.length()<3)// "0o"
die("invalid number `"+str+"`");
if(str.length()==2 || erfmt)
err.err("lexer",line,column,str.length(),"invalid number `"+str+"`");
return str;
}
// generate dec number
@ -318,7 +322,7 @@ string lexer::num_gen()
if(str.back()=='.')
{
column+=str.length();
die("invalid number `"+str+"`");
err.err("lexer",line,column,str.length(),"invalid number `"+str+"`");
return "0";
}
}
@ -333,7 +337,7 @@ string lexer::num_gen()
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
{
column+=str.length();
die("invalid number `"+str+"`");
err.err("lexer",line,column,str.length(),"invalid number `"+str+"`");
return "0";
}
}
@ -382,12 +386,12 @@ string lexer::str_gen()
// check if this string ends with a " or '
if(ptr++>=res.size())
{
die("get EOF when generating string");
err.err("lexer",line,column,1,"get EOF when generating string");
return str;
}
++column;
if(begin=='`' && str.length()!=1)
die("\'`\' is used for string that includes one character");
err.err("lexer",line,column,1,"\'`\' is used for string that includes one character");
return str;
}
@ -434,7 +438,7 @@ const error& lexer::scan(const string& file)
++column;
u32 type=get_type(str);
if(!type)
die("invalid operator `"+str+"`");
err.err("lexer",line,column,str.length(),"invalid operator `"+str+"`");
toks.push_back({line,column,type,str});
++ptr;
}
@ -462,7 +466,8 @@ const error& lexer::scan(const string& file)
{
++column;
char c=res[ptr++];
die("invalid character 0x"+chrhex(c));
err.err("lexer",line,column,1,"invalid character 0x"+chrhex(c));
err.fatal("lexer","fatal error occurred, stop");
}
}
toks.push_back({line,column,tok_eof,"<eof>"});

View File

@ -39,7 +39,9 @@
class parse
{
#define err_line (toks[ptr].line)
#define thisline (toks[ptr].line)
#define thiscol (toks[ptr].col)
#define thislen (toks[ptr].str.length())
private:
u32 ptr;
u32 in_func; // count function block
@ -95,7 +97,7 @@ private:
{tok_geq ,">=" }
};
void die(u32,string,bool);
void die(u32,u32,u32,string,bool);
void next(){++ptr;};
void match(u32 type,const char* info=nullptr);
bool lookahead(u32);
@ -163,49 +165,53 @@ const error& parse::compile(const lexer& lexer)
ptr=in_func=in_loop=0;
root={0,0,ast_root};
while(!lookahead(tok_eof))
{
while(!lookahead(tok_eof)){
root.add(expr());
if(lookahead(tok_semi))
if(lookahead(tok_semi)){
match(tok_semi);
}else if(need_semi_check(root.child().back()) && !lookahead(tok_eof)){
// the last expression can be recognized without semi
else if(need_semi_check(root.child().back()) && !lookahead(tok_eof))
die(err_line,"expected \";\"",true);
die(thisline,thiscol,thislen,"expected \";\"",true);
}
}
return err;
}
void parse::die(u32 line,string info,bool report_prev=false)
void parse::die(u32 line,u32 col,u32 len,string info,bool prev=false)
{
i32 col=(i32)toks[ptr].col-(lookahead(tok_eof)?0:(i32)toks[ptr].str.length());
if(lookahead(tok_str))
col-=2; // tok_str's str has no \"
if(report_prev && ptr) // used to report lack of ',' ';'
{
line=toks[ptr-1].line;
col=toks[ptr-1].col+1;
// tok_str's str has no \"
if(lookahead(tok_str)){
col-=2;
len+=2;
}
err.err("parse",line,col<0?0:col,info);
// used to report lack of ',' ';'
if(prev && ptr){
line=toks[ptr-1].line;
col=toks[ptr-1].col;
len=toks[ptr-1].str.length();
len+=toks[ptr-1].type==tok_str?2:0;
}
err.err("parse",line,col,lookahead(tok_eof)?1:len,info);
}
void parse::match(u32 type,const char* info)
{
if(!lookahead(type))
{
if(info)
{
die(err_line,info);
if(!lookahead(type)){
if(info){
die(thisline,thiscol,thislen,info);
return;
}
switch(type)
{
case tok_num:die(err_line,"expected number"); break;
case tok_str:die(err_line,"expected string"); break;
case tok_id: die(err_line,"expected identifier");break;
default: die(err_line,"expected '"+tokname[type]+"'"); break;
switch(type){
case tok_num:die(thisline,thiscol,thislen,"expected number"); break;
case tok_str:die(thisline,thiscol,thislen,"expected string"); break;
case tok_id: die(thisline,thiscol,thislen,"expected identifier");break;
default: die(thisline,thiscol,thislen,"expected '"+tokname[type]+"'"); break;
}
return;
}
if(lookahead(tok_eof))
if(lookahead(tok_eof)){
return;
}
next();
}
bool parse::lookahead(u32 type)
@ -218,21 +224,19 @@ bool parse::is_call(u32 type)
}
bool parse::check_comma(const u32* panic_set)
{
for(u32 i=0;panic_set[i];++i)
if(lookahead(panic_set[i]))
{
die(err_line,"expected ',' between scalars",true);
for(u32 i=0;panic_set[i];++i){
if(lookahead(panic_set[i])){
die(thisline,thiscol,thislen,"expected ',' between scalars",true);
return true;
}
}
return false;
}
bool parse::check_tuple()
{
u32 check_ptr=ptr,curve=1,bracket=0,brace=0;
while(toks[++check_ptr].type!=tok_eof && curve)
{
switch(toks[check_ptr].type)
{
while(toks[++check_ptr].type!=tok_eof && curve){
switch(toks[check_ptr].type){
case tok_lcurve: ++curve; break;
case tok_lbracket: ++bracket; break;
case tok_lbrace: ++brace; break;
@ -240,21 +244,21 @@ bool parse::check_tuple()
case tok_rbracket: --bracket; break;
case tok_rbrace: --brace; break;
}
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_comma)
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_comma){
return true;
}
}
return false;
}
bool parse::check_func_end(const ast& node)
{
u32 type=node.type();
if(type==ast_func)
if(type==ast_func){
return true;
else if(type==ast_num || type==ast_id || type==ast_str || type==ast_nil || type==ast_vec || type==ast_hash)
}else if(type==ast_num || type==ast_id || type==ast_str || type==ast_nil || type==ast_vec || type==ast_hash){
return false;
if(
node.child().empty() ||
(
}
if(node.child().empty() || (
type!=ast_def &&
type!=ast_equal &&
type!=ast_addeq &&
@ -263,20 +267,19 @@ bool parse::check_func_end(const ast& node)
type!=ast_diveq &&
type!=ast_lnkeq
)
)
){
return false;
else
}else{
return check_func_end(node.child().back());
}
return false;
}
bool parse::check_special_call()
{
// special call means like this: function_name(a:1,b:2,c:3);
u32 check_ptr=ptr,curve=1,bracket=0,brace=0;
while(toks[++check_ptr].type!=tok_eof && curve)
{
switch(toks[check_ptr].type)
{
while(toks[++check_ptr].type!=tok_eof && curve){
switch(toks[check_ptr].type){
case tok_lcurve: ++curve; break;
case tok_lbracket: ++bracket;break;
case tok_lbrace: ++brace; break;
@ -285,32 +288,36 @@ bool parse::check_special_call()
case tok_rbrace: --brace; break;
}
// m?1:0 will be recognized as normal parameter
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_quesmark)
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_quesmark){
return false;
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_colon)
}
if(curve==1 && !bracket && !brace && toks[check_ptr].type==tok_colon){
return true;
}
}
return false;
}
bool parse::need_semi_check(const ast& node)
{
u32 type=node.type();
if(type==ast_for || type==ast_foreach || type==ast_forindex || type==ast_while || type==ast_cond)
if(type==ast_for || type==ast_foreach || type==ast_forindex || type==ast_while || type==ast_cond){
return false;
}
return !check_func_end(node);
}
void parse::check_memory_reachable(const ast& node)
{
if(node.type()==ast_call)
{
if(node.type()==ast_call){
const ast& tmp=node.child().back();
if(tmp.type()==ast_callf)
die(tmp.line(),"bad left-value");
if(tmp.type()==ast_callv && (tmp.size()==0 || tmp.size()>1 || tmp[0].type()==ast_subvec))
die(tmp.line(),"bad left-value");
if(tmp.type()==ast_callf){
die(tmp.line(),tmp.col(),1,"bad left-value");
}
if(tmp.type()==ast_callv && (tmp.size()==0 || tmp.size()>1 || tmp[0].type()==ast_subvec)){
die(tmp.line(),tmp.col(),1,"bad left-value");
}
}else if(node.type()!=ast_id){
die(node.line(),node.col(),1,"bad left-value");
}
else if(node.type()!=ast_id)
die(node.line(),"bad left-value");
}
ast parse::null()
{
@ -354,16 +361,16 @@ ast parse::vec()
};
ast node(toks[ptr].line,toks[ptr].col,ast_vec);
match(tok_lbracket);
while(!lookahead(tok_rbracket))
{
while(!lookahead(tok_rbracket)){
node.add(calc());
if(lookahead(tok_comma))
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_eof))
}else if(lookahead(tok_eof)){
break;
else if(!lookahead(tok_rbracket) && !check_comma(panic_set))
}else if(!lookahead(tok_rbracket) && !check_comma(panic_set)){
break;
}
}
match(tok_rbracket,"expected ']' when generating vector");
return node;
}
@ -371,28 +378,29 @@ ast parse::hash()
{
ast node(toks[ptr].line,toks[ptr].col,ast_hash);
match(tok_lbrace);
while(!lookahead(tok_rbrace))
{
while(!lookahead(tok_rbrace)){
node.add(pair());
if(lookahead(tok_comma))
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_id) || lookahead(tok_str))// first set of hashmember
die(err_line,"expected ',' between hash members",true);
else
}else if(lookahead(tok_id) || lookahead(tok_str)){ // first set of hashmember
die(thisline,thiscol,thislen,"expected ',' between hash members",true);
}else{
break;
}
}
match(tok_rbrace,"expected '}' when generating hash");
return node;
}
ast parse::pair()
{
ast node(toks[ptr].line,toks[ptr].col,ast_pair);
if(lookahead(tok_id))
if(lookahead(tok_id)){
node.add(id());
else if(lookahead(tok_str))
}else if(lookahead(tok_str)){
node.add(str());
else
}else{
match(tok_id,"expected hashmap key");
}
match(tok_colon);
node.add(calc());
return node;
@ -402,10 +410,11 @@ ast parse::func()
++in_func;
ast node(toks[ptr].line,toks[ptr].col,ast_func);
match(tok_func);
if(lookahead(tok_lcurve))
if(lookahead(tok_lcurve)){
node.add(args());
else
}else{
node.add(null());
}
node.add(exprs());
--in_func;
return node;
@ -414,77 +423,33 @@ ast parse::args()
{
ast node(toks[ptr].line,toks[ptr].col,ast_args);
match(tok_lcurve);
while(!lookahead(tok_rcurve))
{
while(!lookahead(tok_rcurve)){
ast tmp=id();
if(lookahead(tok_eq) || lookahead(tok_ellipsis))
{
if(lookahead(tok_eq) || lookahead(tok_ellipsis)){
ast special_arg(toks[ptr].line,toks[ptr].col,ast_null);
if(lookahead(tok_eq))
{
if(lookahead(tok_eq)){
match(tok_eq);
special_arg=std::move(tmp);
special_arg.set_type(ast_default);
special_arg.add(calc());
}
else
{
}else{
match(tok_ellipsis);
special_arg=std::move(tmp);
special_arg.set_type(ast_dynamic);
}
node.add(std::move(special_arg));
}
else
}else{
node.add(std::move(tmp));
if(lookahead(tok_comma))
}
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_id))// first set of identifier
die(err_line,"expected ',' between identifiers",true);
else
}else if(lookahead(tok_id)){ // first set of identifier
die(thisline,thiscol,thislen,"expected ',' between identifiers",true);
}else{
break;
}
}
match(tok_rcurve,"expected ')' after parameter list");
string format="func(";
for(auto& tmp:node.child())
{
format+=tmp.str();
switch(tmp.type())
{
case ast_id: break;
case ast_default: format+="=val";break;
case ast_dynamic: format+="..."; break;
}
format+=",)"[&tmp==&node.child().back()];
}
bool checked_default=false,checked_dynamic=false;
for(auto& tmp:node.child())
{
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<string,bool> argname;
for(auto& tmp:node.child())
{
string name;
switch(tmp.type())
{
case ast_dynamic:
case ast_id: name=tmp.str();break;
case ast_default:name=tmp.str();break;
}
if(argname.count(name))
die(tmp.line(),"parameter's name repeats: "+name);
else
argname[name]=true;
}
return node;
}
ast parse::lcurve_expr()
@ -496,12 +461,13 @@ ast parse::lcurve_expr()
ast parse::expr()
{
u32 type=toks[ptr].type;
if((type==tok_break || type==tok_continue) && !in_loop)
die(err_line,"should use break/continue in loops");
if(type==tok_ret && !in_func)
die(err_line,"should use return in functions");
switch(type)
{
if((type==tok_break || type==tok_continue) && !in_loop){
die(thisline,thiscol,thislen,"must use break/continue in loops");
}
if(type==tok_ret && !in_func){
die(thisline,thiscol,thislen,"must use return in functions");
}
switch(type){
case tok_nil:
case tok_num:
case tok_str:
@ -523,7 +489,7 @@ ast parse::expr()
case tok_ret: return ret_expr(); break;
case tok_semi: break;
default:
die(err_line,"incorrect token <"+toks[ptr].str+">");
die(thisline,thiscol,thislen,"incorrect token <"+toks[ptr].str+">");
next();
break;
}
@ -531,28 +497,24 @@ ast parse::expr()
}
ast parse::exprs()
{
if(lookahead(tok_eof))
{
die(err_line,"expected expression block");
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected expression block");
return null();
}
ast node(toks[ptr].line,toks[ptr].col,ast_block);
if(lookahead(tok_lbrace))
{
if(lookahead(tok_lbrace)){
match(tok_lbrace);
while(!lookahead(tok_rbrace) && !lookahead(tok_eof))
{
while(!lookahead(tok_rbrace) && !lookahead(tok_eof)){
node.add(expr());
if(lookahead(tok_semi))
if(lookahead(tok_semi)){
match(tok_semi);
}else if(need_semi_check(node.child().back()) && !lookahead(tok_rbrace)){
// the last expression can be recognized without semi
else if(need_semi_check(node.child().back()) && !lookahead(tok_rbrace))
die(err_line,"expected ';'",true);
die(thisline,thiscol,thislen,"expected ';'",true);
}
}
match(tok_rbrace,"expected '}' when generating expressions");
}
else
{
}else{
node.add(expr());
if(lookahead(tok_semi))
match(tok_semi);
@ -562,8 +524,7 @@ ast parse::exprs()
ast parse::calc()
{
ast node=or_expr();
if(lookahead(tok_quesmark))
{
if(lookahead(tok_quesmark)){
// trinocular calculation
ast tmp(toks[ptr].line,toks[ptr].col,ast_trino);
match(tok_quesmark);
@ -572,9 +533,7 @@ ast parse::calc()
match(tok_colon);
tmp.add(calc());
node=std::move(tmp);
}
else if(tok_eq<=toks[ptr].type && toks[ptr].type<=tok_lnkeq)
{
}else if(tok_eq<=toks[ptr].type && toks[ptr].type<=tok_lnkeq){
check_memory_reachable(node);
// tok_eq~tok_lnkeq is 37 to 42,ast_equal~ast_lnkeq is 21~26
ast tmp(toks[ptr].line,toks[ptr].col,toks[ptr].type-tok_eq+ast_equal);
@ -588,8 +547,7 @@ ast parse::calc()
ast parse::or_expr()
{
ast node=and_expr();
while(lookahead(tok_or))
{
while(lookahead(tok_or)){
ast tmp(toks[ptr].line,toks[ptr].col,ast_or);
tmp.add(std::move(node));
match(tok_or);
@ -601,8 +559,7 @@ ast parse::or_expr()
ast parse::and_expr()
{
ast node=cmp_expr();
while(lookahead(tok_and))
{
while(lookahead(tok_and)){
ast tmp(toks[ptr].line,toks[ptr].col,ast_and);
tmp.add(std::move(node));
match(tok_and);
@ -614,8 +571,7 @@ ast parse::and_expr()
ast parse::cmp_expr()
{
ast node=additive_expr();
while(tok_cmpeq<=toks[ptr].type && toks[ptr].type<=tok_geq)
{
while(tok_cmpeq<=toks[ptr].type && toks[ptr].type<=tok_geq){
// tok_cmpeq~tok_geq is 43~48,ast_cmpeq~ast_geq is 27~32
ast tmp(toks[ptr].line,toks[ptr].col,toks[ptr].type-tok_cmpeq+ast_cmpeq);
tmp.add(std::move(node));
@ -628,11 +584,9 @@ ast parse::cmp_expr()
ast parse::additive_expr()
{
ast node=multive_expr();
while(lookahead(tok_add) || lookahead(tok_sub) || lookahead(tok_link))
{
while(lookahead(tok_add) || lookahead(tok_sub) || lookahead(tok_link)){
ast tmp(toks[ptr].line,toks[ptr].col,ast_null);
switch(toks[ptr].type)
{
switch(toks[ptr].type){
case tok_add: tmp.set_type(ast_add); break;
case tok_sub: tmp.set_type(ast_sub); break;
case tok_link: tmp.set_type(ast_link); break;
@ -647,8 +601,7 @@ ast parse::additive_expr()
ast parse::multive_expr()
{
ast node=(lookahead(tok_sub) || lookahead(tok_not))?unary():scalar();
while(lookahead(tok_mult) || lookahead(tok_div))
{
while(lookahead(tok_mult) || lookahead(tok_div)){
ast tmp(toks[ptr].line,toks[ptr].col,toks[ptr].type-tok_mult+ast_mult);
tmp.add(std::move(node));
match(toks[ptr].type);
@ -660,8 +613,7 @@ ast parse::multive_expr()
ast parse::unary()
{
ast node(toks[ptr].line,toks[ptr].col,ast_null);
switch(toks[ptr].type)
{
switch(toks[ptr].type){
case tok_sub:node.set_type(ast_neg);match(tok_sub);break;
case tok_not:node.set_type(ast_not);match(tok_not);break;
}
@ -671,47 +623,49 @@ ast parse::unary()
ast parse::scalar()
{
ast node(toks[ptr].line,toks[ptr].col,ast_null);
if(lookahead(tok_nil)) {node=nil();match(tok_nil);}
else if(lookahead(tok_num)) node=num();
else if(lookahead(tok_str)) node=str();
else if(lookahead(tok_id)) node=id();
else if(lookahead(tok_func)) node=func();
else if(lookahead(tok_lbracket)) node=vec();
else if(lookahead(tok_lbrace)) node=hash();
else if(lookahead(tok_lcurve))
{
if(lookahead(tok_nil)){
node=nil();
match(tok_nil);
}else if(lookahead(tok_num)){
node=num();
}else if(lookahead(tok_str)){
node=str();
}else if(lookahead(tok_id)){
node=id();
}else if(lookahead(tok_func)){
node=func();
}else if(lookahead(tok_lbracket)){
node=vec();
}else if(lookahead(tok_lbrace)){
node=hash();
}else if(lookahead(tok_lcurve)){
match(tok_lcurve);
node=calc();
match(tok_rcurve);
}
else if(lookahead(tok_var))
{
}else if(lookahead(tok_var)){
match(tok_var);
node.set_type(ast_def);
node.add(id());
match(tok_eq);
node.add(calc());
}
else
{
die(err_line,"expected scalar");
}else{
die(thisline,thiscol,thislen,"expected scalar");
return node;
}
// check call and avoid ambiguous syntax
if(is_call(toks[ptr].type) && !(lookahead(tok_lcurve) && toks[ptr+1].type==tok_var))
{
if(is_call(toks[ptr].type) && !(lookahead(tok_lcurve) && toks[ptr+1].type==tok_var)){
ast tmp=std::move(node);
node={toks[ptr].line,toks[ptr].col,ast_call};
node.add(std::move(tmp));
while(is_call(toks[ptr].type))
while(is_call(toks[ptr].type)){
node.add(call_scalar());
}
}
return node;
}
ast parse::call_scalar()
{
switch(toks[ptr].type)
{
switch(toks[ptr].type){
case tok_lcurve: return callf(); break;
case tok_lbracket: return callv(); break;
case tok_dot: return callh(); break;
@ -741,18 +695,19 @@ ast parse::callv()
};
ast node(toks[ptr].line,toks[ptr].col,ast_callv);
match(tok_lbracket);
while(!lookahead(tok_rbracket))
{
while(!lookahead(tok_rbracket)){
node.add(subvec());
if(lookahead(tok_comma))
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_eof))
}else if(lookahead(tok_eof)){
break;
else if(!lookahead(tok_rbracket) && !check_comma(panic_set))
}else if(!lookahead(tok_rbracket) && !check_comma(panic_set)){
break;
}
if(node.size()==0)
die(node.line(),"expected index value");
}
if(node.size()==0){
die(node.line(),node.col(),1,"expected index value");
}
match(tok_rbracket,"expected ']' when calling vector");
return node;
}
@ -770,8 +725,7 @@ ast parse::callf()
ast node(toks[ptr].line,toks[ptr].col,ast_callf);
bool special_call=check_special_call();
match(tok_lcurve);
while(!lookahead(tok_rcurve))
{
while(!lookahead(tok_rcurve)){
node.add(special_call?pair():calc());
if(lookahead(tok_comma))
match(tok_comma);
@ -786,8 +740,7 @@ ast parse::callf()
ast parse::subvec()
{
ast node=lookahead(tok_colon)?nil():calc();
if(lookahead(tok_colon))
{
if(lookahead(tok_colon)){
ast tmp(node.line(),node.col(),ast_subvec);
match(tok_colon);
tmp.add(std::move(node));
@ -799,27 +752,22 @@ ast parse::subvec()
ast parse::definition()
{
ast node(toks[ptr].line,toks[ptr].col,ast_def);
if(lookahead(tok_var))
{
if(lookahead(tok_var)){
match(tok_var);
switch(toks[ptr].type)
{
switch(toks[ptr].type){
case tok_id: node.add(id());break;
case tok_lcurve: node.add(outcurve_def());break;
default: die(err_line,"expected identifier");break;
default: die(thisline,thiscol,thislen,"expected identifier");break;
}
}
else if(lookahead(tok_lcurve))
}else if(lookahead(tok_lcurve)){
node.add(incurve_def());
}
match(tok_eq);
if(lookahead(tok_lcurve))
if(lookahead(tok_lcurve)){
node.add(check_tuple()?multi_scalar(false):calc());
else
}else{
node.add(calc());
if(node[0].type()==ast_id && node[1].type()==ast_tuple)
die(node[1].line(),"one variable cannot accept too many values");
else if(node[0].type()==ast_multi_id && node[1].type()==ast_tuple && node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-definition");
}
return node;
}
ast parse::incurve_def()
@ -840,21 +788,20 @@ ast parse::outcurve_def()
ast parse::multi_id()
{
ast node(toks[ptr].line,toks[ptr].col,ast_multi_id);
while(!lookahead(tok_eof))
{
while(!lookahead(tok_eof)){
node.add(id());
if(is_call(toks[ptr].type))
{
call_scalar();// recognize calls but this is still a syntax error
die(err_line,"cannot call identifier in multi-definition");
if(is_call(toks[ptr].type)){
ast tmp=call_scalar();// recognize calls but this is still a syntax error
die(tmp.line(),tmp.col(),1,"cannot call identifier in multi-definition");
}
if(lookahead(tok_comma))
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_id))// first set of identifier
die(err_line,"expected ',' between identifiers",true);
else
}else if(lookahead(tok_id)){ // first set of identifier
die(thisline,thiscol,thislen,"expected ',' between identifiers",true);
}else{
break;
}
}
return node;
}
ast parse::multi_scalar(bool check_call_memory)
@ -868,18 +815,19 @@ ast parse::multi_scalar(bool check_call_memory)
};
ast node(toks[ptr].line,toks[ptr].col,ast_tuple);
match(tok_lcurve);
while(!lookahead(tok_rcurve))
{
while(!lookahead(tok_rcurve)){
node.add(calc());
if(check_call_memory)
if(check_call_memory){
check_memory_reachable(node.child().back());
if(lookahead(tok_comma))
}
if(lookahead(tok_comma)){
match(tok_comma);
else if(lookahead(tok_eof))
}else if(lookahead(tok_eof)){
break;
else if(!lookahead(tok_rcurve) && !check_comma(panic_set))
}else if(!lookahead(tok_rcurve) && !check_comma(panic_set)){
break;
}
}
match(tok_rcurve,"expected ')' after multi-scalar");
return node;
}
@ -888,26 +836,22 @@ ast parse::multi_assgin()
ast node(toks[ptr].line,toks[ptr].col,ast_multi_assign);
node.add(multi_scalar(true));
match(tok_eq);
if(lookahead(tok_eof))
{
die(err_line,"expected value list");
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected value list");
return node;
}
if(lookahead(tok_lcurve))
if(lookahead(tok_lcurve)){
node.add(check_tuple()?multi_scalar(false):calc());
else
}else{
node.add(calc());
if(node[1].type()==ast_tuple
&& node[0].size()!=node[1].size())
die(node[0].line(),"too much or lack values in multi-assignment");
}
return node;
}
ast parse::loop()
{
++in_loop;
ast node(0,0,ast_null);
switch(toks[ptr].type)
{
switch(toks[ptr].type){
case tok_while: node=while_loop(); break;
case tok_for: node=for_loop(); break;
case tok_forindex:
@ -932,32 +876,38 @@ ast parse::for_loop()
match(tok_for);
match(tok_lcurve);
// first expression
if(lookahead(tok_eof))
die(err_line,"expected definition");
if(lookahead(tok_semi))
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected definition");
}
if(lookahead(tok_semi)){
node.add(null());
else if(lookahead(tok_var))
}else if(lookahead(tok_var)){
node.add(definition());
else if(lookahead(tok_lcurve))
}else if(lookahead(tok_lcurve)){
node.add(lcurve_expr());
else
}else{
node.add(calc());
}
match(tok_semi,"expected ';' in for(;;)");
// conditional expression
if(lookahead(tok_eof))
die(err_line,"expected conditional expr");
if(lookahead(tok_semi))
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected conditional expr");
}
if(lookahead(tok_semi)){
node.add(null());
else
}else{
node.add(calc());
}
match(tok_semi,"expected ';' in for(;;)");
//after loop expression
if(lookahead(tok_eof))
die(err_line,"expected calculation");
if(lookahead(tok_rcurve))
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected calculation");
}
if(lookahead(tok_rcurve)){
node.add(null());
else
}else{
node.add(calc());
}
match(tok_rcurve);
node.add(exprs());
return node;
@ -965,20 +915,21 @@ ast parse::for_loop()
ast parse::forei_loop()
{
ast node(toks[ptr].line,toks[ptr].col,ast_null);
switch(toks[ptr].type)
{
switch(toks[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;
}
match(tok_lcurve);
// first expression
// foreach/forindex must have an iterator to loop through
if(!lookahead(tok_var) && !lookahead(tok_id))
die(err_line,"expected iterator");
if(!lookahead(tok_var) && !lookahead(tok_id)){
die(thisline,thiscol,thislen,"expected iterator");
}
node.add(iter_gen());
match(tok_semi,"expected ';' in foreach/forindex(iter;vector)");
if(lookahead(tok_eof))
die(err_line,"expected vector");
if(lookahead(tok_eof)){
die(thisline,thiscol,thislen,"expected vector");
}
node.add(calc());
match(tok_rcurve);
node.add(exprs());
@ -987,18 +938,16 @@ ast parse::forei_loop()
ast parse::iter_gen()
{
ast node(toks[ptr].line,toks[ptr].col,ast_null);
if(lookahead(tok_var))
{
if(lookahead(tok_var)){
match(tok_var);
node.set_type(ast_iter);
node.add(id());
}
else
{
}else{
node.set_type(ast_call);
node.add(id());
while(is_call(toks[ptr].type))
while(is_call(toks[ptr].type)){
node.add(call_scalar());
}
check_memory_reachable(node);
}
return node;
@ -1013,8 +962,7 @@ ast parse::cond()
match(tok_rcurve);
ifnode.add(exprs());
node.add(std::move(ifnode));
while(lookahead(tok_elsif))
{
while(lookahead(tok_elsif)){
ast elsifnode(toks[ptr].line,toks[ptr].col,ast_elsif);
match(tok_elsif);
match(tok_lcurve);
@ -1023,8 +971,7 @@ ast parse::cond()
elsifnode.add(exprs());
node.add(std::move(elsifnode));
}
if(lookahead(tok_else))
{
if(lookahead(tok_else)){
ast elsenode(toks[ptr].line,toks[ptr].col,ast_else);
match(tok_else);
elsenode.add(exprs());
@ -1049,11 +996,11 @@ ast parse::ret_expr()
ast node(toks[ptr].line,toks[ptr].col,ast_ret);
match(tok_ret);
u32 type=toks[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

@ -51,19 +51,25 @@ libfib.close();
var speed_test=func(){
var d=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
println("[dylib ] ",d);
var fd=d.quick_fib;
var vec_call=dylib.dlcall;
var invoke=dylib.limitcall(1);
var tm=maketimestamp();
var duration=0;
for(var t=0;t<10;t+=1){
tm.stamp();
for(var i=0;i<1e7;i+=1)
for(var i=0;i<5e6;i+=1)
invoke(fd,40);
println("[time ] limited call: ",tm.elapsedMSec()," ms");
duration=tm.elapsedMSec();
println("[time ] limited call: ",duration," ms avg ",5e6/duration," call/ms");
tm.stamp();
for(var i=0;i<1e7;i+=1)
for(var i=0;i<5e6;i+=1)
vec_call(fd,40);
println("[time ] dynamic call: ",tm.elapsedMSec()," ms");
duration=tm.elapsedMSec();
println("[time ] dynamic call: ",duration," ms avg ",5e6/duration," call/ms");
}
}
speed_test();