change format of error info

parser now can report exact place where is lacking ';' or ','
This commit is contained in:
ValKmjolnir 2022-06-30 23:15:10 +08:00
parent a4a7f5075a
commit c993c77b78
5 changed files with 71 additions and 46 deletions

11
nasal.h
View File

@ -148,6 +148,14 @@ int utf8_hdchk(char head)
return nbytes; return nbytes;
} }
std::string chrhex(const char c)
{
std::string res="";
res+="0123456789abcdef"[(c&0xf0)>>4];
res+="0123456789abcdef"[c&0x0f];
return res;
}
std::string rawstr(const std::string& str) std::string rawstr(const std::string& str)
{ {
std::string ret(""); std::string ret("");
@ -159,8 +167,7 @@ std::string rawstr(const std::string& str)
if(i<=0) if(i<=0)
{ {
ret+="\\x"; ret+="\\x";
ret+="0123456789abcdef"[(i>>4)&15]; ret+=chrhex(i);
ret+="0123456789abcdef"[i&15];
continue; continue;
} }
#endif #endif

View File

@ -46,31 +46,24 @@ private:
uint32_t error; uint32_t error;
public: public:
nasal_err():error(0){} nasal_err():error(0){}
void err(const char* stage,const std::string& info,const char end='\n') void err(const char* stage,const std::string& info)
{ {
++error; ++error;
std::cerr<<"["<<stage<<"] "<<info<<end; std::cerr<<"["<<stage<<"] "<<info<<"\n";
} }
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info) void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
{ {
++error; ++error;
if(!line) const std::string& code=res[line-1];
{ std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<code<<"\n";
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
return;
}
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<res[line-1]<<'\n';
for(int i=0;i<(int)column-1;++i) for(int i=0;i<(int)column-1;++i)
std::cerr<<char(" \t"[res[line-1][i]=='\t']); std::cerr<<char(" \t"[code[i]=='\t']);
std::cerr<<"^\n"; std::cerr<<"^\n";
} }
void err(const char* stage,uint32_t line,const std::string& info) void err(const char* stage,uint32_t line,const std::string& info)
{ {
++error; ++error;
if(!line) std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
else
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
} }
void chkerr(){if(error)std::exit(1);} void chkerr(){if(error)std::exit(1);}
}; };

View File

@ -87,10 +87,14 @@ nasal_ast nasal_import::lib_import()
const std::vector<std::string> libpath= const std::vector<std::string> libpath=
{ {
"lib.nas", #ifdef __WIN32
"stl/lib.nas" ".\\lib.nas",
".\\stl\\lib.nas"
#else
"./lib.nas",
"./stl/lib.nas"
#endif
}; };
std::string filename=""; std::string filename="";
for(auto& i:libpath) for(auto& i:libpath)
if(access(i.c_str(),F_OK)!=-1) if(access(i.c_str(),F_OK)!=-1)
@ -103,7 +107,7 @@ nasal_ast nasal_import::lib_import()
std::string paths=""; std::string paths="";
for(auto& i:libpath) for(auto& i:libpath)
paths+=" "+i+"\n"; paths+=" "+i+"\n";
nerr.err("link","cannot find lib file in these paths:\n"+paths,' '); nerr.err("link","cannot find lib file in these paths:\n"+paths);
nerr.chkerr(); nerr.chkerr();
return {0,ast_root}; return {0,ast_root};
} }

View File

@ -15,11 +15,14 @@
enum token_type enum token_type
{ {
tok_null=0,// null token default token type tok_null=0, // null token (default token type)
tok_num, // number basic token type tok_num, // number basic token type
tok_str, // string basic token type tok_str, // string basic token type
tok_id, // identifier basic token type tok_id, // identifier basic token type
tok_for,tok_forindex,tok_foreach,tok_while, tok_for, // loop keyword for
tok_forindex,// loop keyword forindex
tok_foreach, // loop keyword foreach
tok_while, // loop keyword while
tok_var,tok_func,tok_break,tok_continue, tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil, tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve, tok_lcurve,tok_rcurve,
@ -106,20 +109,24 @@ class nasal_lexer
private: private:
uint32_t line; uint32_t line;
uint32_t column; uint32_t column;
uint32_t ptr; size_t ptr;
nasal_err& nerr; nasal_err& nerr;
std::string res; std::string res;
std::vector<token> tokens; std::vector<token> tokens;
uint32_t get_type(const std::string&); uint32_t get_type(const std::string&);
void die(std::string info){nerr.err("lexer",line,column,info);}; void die(const std::string& info){nerr.err("lexer",line,column,info);}
void open(const std::string&); void open(const std::string&);
std::string utf8_gen(); std::string utf8_gen();
std::string id_gen(); std::string id_gen();
std::string num_gen(); std::string num_gen();
std::string str_gen(); std::string str_gen();
public: public:
nasal_lexer(nasal_err& e):line(0),column(0),ptr(0),nerr(e){} nasal_lexer(nasal_err& e):
line(1),
column(0),
ptr(0),
nerr(e){}
void scan(const std::string&); void scan(const std::string&);
void print(); void print();
const std::vector<token>& get_tokens() const {return tokens;} const std::vector<token>& get_tokens() const {return tokens;}
@ -162,16 +169,25 @@ std::string nasal_lexer::utf8_gen()
if(nbytes) if(nbytes)
{ {
tmp+=res[ptr++]; tmp+=res[ptr++];
for(uint32_t i=0;i<nbytes;++i,++ptr) ++column;
for(uint32_t i=0;i<nbytes;++i,++ptr,++column)
if(ptr<res.size() && (res[ptr]&0xc0)==0x80) if(ptr<res.size() && (res[ptr]&0xc0)==0x80)
tmp+=res[ptr]; tmp+=res[ptr];
if(tmp.length()!=1+nbytes) if(tmp.length()!=1+nbytes)
die("invalid utf-8 character here"); {
std::string utf_info="0x"+chrhex(tmp[0]);
for(uint32_t i=1;i<tmp.size();++i)
utf_info+=" 0x"+chrhex(tmp[i]);
die("invalid utf-8 character `"+utf_info+"`, make sure this is a text file.");
std::exit(1);
}
str+=tmp; str+=tmp;
++column;
} }
else else
{
++ptr; ++ptr;
++column;
}
} }
return str; return str;
} }
@ -203,7 +219,7 @@ std::string nasal_lexer::num_gen()
str+=res[ptr++]; str+=res[ptr++];
column+=str.length(); column+=str.length();
if(str.length()<3)// "0x" if(str.length()<3)// "0x"
die("invalid number:"+str); die("invalid number `"+str+"`.");
return str; return str;
} }
// generate oct number // generate oct number
@ -215,7 +231,7 @@ std::string nasal_lexer::num_gen()
str+=res[ptr++]; str+=res[ptr++];
column+=str.length(); column+=str.length();
if(str.length()<3)// "0o" if(str.length()<3)// "0o"
die("invalid number:"+str); die("invalid number `"+str+"`.");
return str; return str;
} }
// generate dec number // generate dec number
@ -232,7 +248,7 @@ std::string nasal_lexer::num_gen()
if(str.back()=='.') if(str.back()=='.')
{ {
column+=str.length(); column+=str.length();
die("invalid number:"+str); die("invalid number `"+str+"`.");
return "0"; return "0";
} }
} }
@ -247,7 +263,7 @@ std::string nasal_lexer::num_gen()
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
{ {
column+=str.length(); column+=str.length();
die("invalid number:"+str); die("invalid number `"+str+"`.");
return "0"; return "0";
} }
} }
@ -315,7 +331,7 @@ void nasal_lexer::scan(const std::string& file)
std::string str; std::string str;
while(ptr<res.size()) while(ptr<res.size())
{ {
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r'))// || res[ptr]<0)) while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]==0))
{ {
// these characters will be ignored, and '\n' will cause ++line // these characters will be ignored, and '\n' will cause ++line
++column; ++column;
@ -348,7 +364,7 @@ void nasal_lexer::scan(const std::string& file)
++column; ++column;
uint32_t type=get_type(str); uint32_t type=get_type(str);
if(!type) if(!type)
die("invalid operator:"+str); die("invalid operator `"+str+"`.");
tokens.push_back({line,column,type,str}); tokens.push_back({line,column,type,str});
++ptr; ++ptr;
} }
@ -375,8 +391,8 @@ void nasal_lexer::scan(const std::string& file)
else else
{ {
++column; ++column;
++ptr; char c=res[ptr++];
die("unknown character."); die("invalid character 0x"+chrhex(c)+".");
} }
} }
tokens.push_back({line,column,tok_eof,"eof"}); tokens.push_back({line,column,tok_eof,"eof"});

View File

@ -48,7 +48,7 @@ private:
nasal_ast root; nasal_ast root;
nasal_err& nerr; nasal_err& nerr;
void die(uint32_t,std::string); void die(uint32_t,std::string,bool);
void match(uint32_t type,const char* info=nullptr); void match(uint32_t type,const char* info=nullptr);
bool check_comma(const uint32_t*); bool check_comma(const uint32_t*);
bool check_multi_scalar(); bool check_multi_scalar();
@ -117,15 +117,20 @@ void nasal_parse::compile(const nasal_lexer& lexer)
match(tok_semi); match(tok_semi);
// the last expression can be recognized without semi // the last expression can be recognized without semi
else if(need_semi_check(root.child().back()) && tokens[ptr].type!=tok_eof) else if(need_semi_check(root.child().back()) && tokens[ptr].type!=tok_eof)
die(error_line,"expected \";\""); die(error_line,"expected \";\"",true);
} }
nerr.chkerr(); nerr.chkerr();
} }
void nasal_parse::die(uint32_t line,std::string info) void nasal_parse::die(uint32_t line,std::string info,bool report_prev=false)
{ {
int col=(int)tokens[ptr].column-(int)tokens[ptr].str.length(); int col=(int)tokens[ptr].column-(int)tokens[ptr].str.length();
if(tokens[ptr].type==tok_str) if(tokens[ptr].type==tok_str)
col-=2; // tok_str's str has no \" col-=2; // tok_str's str has no \"
if(report_prev && ptr-1>=0) // used to report lack of ',' ';'
{
line=tokens[ptr-1].line;
col=tokens[ptr-1].column+1;
}
nerr.err("parse",line,col<0?0:col,info); nerr.err("parse",line,col<0?0:col,info);
} }
void nasal_parse::match(uint32_t type,const char* info) void nasal_parse::match(uint32_t type,const char* info)
@ -155,7 +160,7 @@ bool nasal_parse::check_comma(const uint32_t* panic_set)
for(uint32_t i=0;panic_set[i];++i) for(uint32_t i=0;panic_set[i];++i)
if(tokens[ptr].type==panic_set[i]) if(tokens[ptr].type==panic_set[i])
{ {
die(error_line,"expected \',\' between scalars"); die(error_line,"expected \',\' between scalars",true);
return true; return true;
} }
return false; return false;
@ -309,7 +314,7 @@ nasal_ast nasal_parse::hash()
if(tokens[ptr].type==tok_comma) if(tokens[ptr].type==tok_comma)
match(tok_comma); match(tok_comma);
else if(tokens[ptr].type==tok_id || tokens[ptr].type==tok_str)// first set of hashmember else if(tokens[ptr].type==tok_id || tokens[ptr].type==tok_str)// first set of hashmember
die(error_line,"expected \',\' between hash members"); die(error_line,"expected \',\' between hash members",true);
else else
break; break;
} }
@ -377,7 +382,7 @@ nasal_ast nasal_parse::args()
if(tokens[ptr].type==tok_comma) if(tokens[ptr].type==tok_comma)
match(tok_comma); match(tok_comma);
else if(tokens[ptr].type==tok_id)// first set of identifier else if(tokens[ptr].type==tok_id)// first set of identifier
die(error_line,"expected \',\' between identifiers"); die(error_line,"expected \',\' between identifiers",true);
else else
break; break;
} }
@ -484,7 +489,7 @@ nasal_ast nasal_parse::exprs()
match(tok_semi); match(tok_semi);
// the last expression can be recognized without semi // the last expression can be recognized without semi
else if(need_semi_check(node.child().back()) && tokens[ptr].type!=tok_rbrace) else if(need_semi_check(node.child().back()) && tokens[ptr].type!=tok_rbrace)
die(error_line,"expected \";\""); die(error_line,"expected \';\'",true);
} }
match(tok_rbrace,"expected \'}\' when generating expressions"); match(tok_rbrace,"expected \'}\' when generating expressions");
} }
@ -796,7 +801,7 @@ nasal_ast nasal_parse::multi_id()
if(tokens[ptr].type==tok_comma) if(tokens[ptr].type==tok_comma)
match(tok_comma); match(tok_comma);
else if(tokens[ptr].type==tok_id)// first set of identifier else if(tokens[ptr].type==tok_id)// first set of identifier
die(error_line,"expected \',\' between identifiers"); die(error_line,"expected \',\' between identifiers",true);
else else
break; break;
} }