// license:GPL-2.0+// copyright-holders:Couriersud#include"ppreprocessor.h"#include"palloc.h"#include"pstonum.h"#include"pstrutil.h"#include"putil.h"namespaceplib{// ----------------------------------------------------------------------------------------// A simple preprocessor// ----------------------------------------------------------------------------------------ppreprocessor::ppreprocessor(psource_collection_t<>&sources,defines_map_type*defines):std::istream(newreadbuffer(this)),m_sources(sources),m_if_flag(0),m_if_seen(0),m_elif(0),m_if_level(0),m_pos(0),m_state(PROCESS),m_comment(false){m_expr_sep.emplace_back("!");m_expr_sep.emplace_back("(");m_expr_sep.emplace_back(")");m_expr_sep.emplace_back("+");m_expr_sep.emplace_back("-");m_expr_sep.emplace_back("*");m_expr_sep.emplace_back("/");m_expr_sep.emplace_back("&&");m_expr_sep.emplace_back("||");m_expr_sep.emplace_back("==");m_expr_sep.emplace_back(",");m_expr_sep.emplace_back(";");m_expr_sep.emplace_back(".");m_expr_sep.emplace_back("##");m_expr_sep.emplace_back("#");m_expr_sep.emplace_back(" ");m_expr_sep.emplace_back("\t");m_expr_sep.emplace_back("\"");if(defines!=nullptr)m_defines=*defines;m_defines.insert({"__PLIB_PREPROCESSOR__",define_t("__PLIB_PREPROCESSOR__","1")});autoidx=m_defines.find("__PREPROCESSOR_DEBUG__");m_debug_out=idx!=m_defines.end();}voidppreprocessor::error(constpstring&err){pstrings("");pstringtrail(" from ");pstringtrail_first("In file included from ");pstringe=plib::pfmt("{1}:{2}:0: error: {3}\n")(m_stack.back().m_name,m_stack.back().m_lineno,err);m_stack.pop_back();while(!m_stack.empty()){if(m_stack.size()==1)trail=trail_first;s=plib::pfmt("{1}{2}:{3}:0\n{4}")(trail,m_stack.back().m_name,m_stack.back().m_lineno,s);m_stack.pop_back();}throwpexception("\n"+s+e+" "+m_line+"\n");}template<typenamePP,typenameL=ppreprocessor::string_list>structsimple_iter{simple_iter(PP*parent,constL&tokens):m_tokens(tokens),m_parent(parent),m_pos(0){}/// \brief skip white space in token list///voidskip_ws(){while(m_pos<m_tokens.size()&&(m_tokens[m_pos]==" "||m_tokens[m_pos]=="\t"))m_pos++;}/// \brief return next token skipping white space////// \return next token///pstringnext(){skip_ws();if(m_pos>=m_tokens.size())error("unexpected end of line");returnm_tokens[m_pos++];}/// \brief return next token including white space////// \return next token///pstringnext_ws(){if(m_pos>=m_tokens.size())error("unexpected end of line");returnm_tokens[m_pos++];}pstringpeek_ws(){if(m_pos>=m_tokens.size())error("unexpected end of line");returnm_tokens[m_pos];}pstringlast(){if(m_pos==0)error("no last token at beginning of line");if(m_pos>m_tokens.size())error("unexpected end of line");returnm_tokens[m_pos-1];}booleod(){return(m_pos>=m_tokens.size());}voiderror(pstringerr){m_parent->error(err);}private:Lm_tokens;PP*m_parent;std::size_tm_pos;};#define CHECKTOK2(p_op, p_prio) \ else if (tok == # p_op) \ { \ if (!has_val) \ { sexpr.error("parsing error!"); return 1;} \ if (prio < (p_prio)) \ return val; \ sexpr.next(); \ const auto v2 = prepro_expr(sexpr, (p_prio)); \ val = (val p_op v2); \ } \// Operator precedence see https://en.cppreference.com/w/cpp/language/operator_precedencetemplate<typenamePP>staticintprepro_expr(simple_iter<PP>&sexpr,intprio){intval(0);boolhas_val(false);pstringtok=sexpr.peek_ws();if(tok=="("){sexpr.next();val=prepro_expr(sexpr,255);if(sexpr.next()!=")")sexpr.error("expected ')'");has_val=true;}while(!sexpr.eod()){tok=sexpr.peek_ws();if(tok==")"){if(!has_val)sexpr.error("Found ')' but have no value computed");elsereturnval;}elseif(tok=="!"){if(prio<3){if(!has_val)sexpr.error("parsing error!");elsereturnval;}sexpr.next();val=!prepro_expr(sexpr,3);has_val=true;}CHECKTOK2(*,5)CHECKTOK2(/,5)// NOLINT(clang-analyzer-core.DivideZero)CHECKTOK2(+,6)CHECKTOK2(-,6)CHECKTOK2(==,10)CHECKTOK2(&&,14)CHECKTOK2(||,15)else{try{val=plib::pstonum<decltype(val)>(tok);}catch(pexception&e){sexpr.error(e.text());}has_val=true;sexpr.next();}}if(!has_val)sexpr.error("No value computed. Empty expression ?");returnval;}ppreprocessor::define_t*ppreprocessor::get_define(constpstring&name){autoidx=m_defines.find(name);return(idx!=m_defines.end())?&idx->second:nullptr;}ppreprocessor::string_listppreprocessor::tokenize(constpstring&str,conststring_list&sep,boolremove_ws,boolconcat){constpstringSTR="\"";string_listtmpret;string_listtmp(psplit(str,sep));std::size_tpi(0);while(pi<tmp.size()){if(tmp[pi]==STR){pstrings(STR);pi++;while(pi<tmp.size()&&tmp[pi]!=STR){s+=tmp[pi];pi++;}s+=STR;tmpret.push_back(s);}else{pstringtok=tmp[pi];if(tok.size()>=2&&pi<tmp.size()-2){autosc=tok.substr(0,1);autoec=tok.substr(tok.size()-1,1);if((sc=="."||(sc>="0"&&sc<="9"))&&(ec=="e"||ec=="E")){// looks like an incomplete float due splitting by - or +tok=tok+tmp[pi+1]+tmp[pi+2];pi+=2;}}if(!remove_ws||(tok!=" "&&tok!="\t"))tmpret.push_back(tok);}pi++;}if(!concat)returntmpret;// FIXME: error if concat at beginning or endstring_listret;pi=0;while(pi<tmpret.size()){if(tmpret[pi]=="##"){while(ret.back()==" "||ret.back()=="\t")ret.pop_back();pstringcc=ret.back();ret.pop_back();pi++;while(pi<tmpret.size()&&(tmpret[pi]==" "||tmpret[pi]=="\t"))pi++;if(pi==tmpret.size())error("## found at end of sequence");ret.push_back(cc+tmpret[pi]);}elseret.push_back(tmpret[pi]);pi++;}returnret;}boolppreprocessor::is_valid_token(constpstring&str){if(str.length()==0)returnfalse;pstring::value_typec(str.at(0));return((c>='a'&&c<='z')||(c>='A'&&c<='Z')||c=='_');}pstringppreprocessor::replace_macros(constpstring&line){//std::vector<pstring> elems(psplit(line, m_expr_sep));boolrepeat(false);pstringtmpret(line);do{repeat=false;simple_iter<ppreprocessor>elems(this,tokenize(tmpret,m_expr_sep,false,true));tmpret="";while(!elems.eod()){autotoken(elems.next_ws());define_t*def=get_define(token);if(def==nullptr)tmpret+=token;elseif(!def->m_has_params){tmpret+=def->m_replace;repeat=true;}else{token=elems.next();if(token!="(")error("expected '(' in macro expansion of "+def->m_name);string_listrep;token=elems.next();while(token!=")"){pstringpar("");intpcnt(1);while(true){if(pcnt==1&&token==","){token=elems.next();break;}if(token=="(")pcnt++;if(token==")")if(--pcnt==0)break;par+=token;token=elems.next();}rep.push_back(par);}repeat=true;if(def->m_params.size()!=rep.size())error(pfmt("Expected {1} parameters, got {2}")(def->m_params.size(),rep.size()));simple_iter<ppreprocessor>r(this,tokenize(def->m_replace,m_expr_sep,false,false));boolstringify_next=false;while(!r.eod()){token=r.next();if(token=="#")stringify_next=true;elseif(token!=" "&&token!="\t"){for(std::size_ti=0;i<def->m_params.size();i++)if(def->m_params[i]==token){if(stringify_next){stringify_next=false;token="\""+rep[i]+"\"";}elsetoken=rep[i];break;}if(stringify_next)error("'#' is not followed by a macro parameter");tmpret+=token;tmpret+=" ";// make sure this is not concatenated with next token}elsetmpret+=token;}}}}while(repeat);returntmpret;}staticpstringcatremainder(conststd::vector<pstring>&elems,std::size_tstart,constpstring&sep){pstringret("");for(std::size_ti=start;i<elems.size();i++){ret+=elems[i];ret+=sep;}returnret;}pstringppreprocessor::process_comments(pstringline){boolin_string=false;std::size_te=line.size();pstringret="";for(std::size_ti=0;i<e;){pstringc=plib::left(line,1);line=line.substr(1);if(!m_comment){if(c=="\""){in_string=!in_string;ret+=c;}elseif(in_string&&c=="\\"){i++;ret+=(c+plib::left(line,1));line=line.substr(1);}elseif(!in_string&&c=="/"&&plib::left(line,1)=="*")m_comment=true;elseif(!in_string&&c=="/"&&plib::left(line,1)=="/")break;elseret+=c;}elseif(c=="*"&&plib::left(line,1)=="/"){i++;line=line.substr(1);m_comment=false;}i++;}returnret;}std::pair<pstring,bool>ppreprocessor::process_line(pstringline){boolline_cont=plib::right(line,1)=="\\";if(line_cont)line=plib::left(line,line.size()-1);if(m_state==LINE_CONTINUATION)m_line+=line;elsem_line=line;if(line_cont){m_state=LINE_CONTINUATION;return{"",false};}m_state=PROCESS;line=process_comments(m_line);pstringlt=plib::trim(plib::replace_all(line,"\t"," "));if(plib::startsWith(lt,"#")){string_listlti(psplit(lt," ",true));if(lti[0]=="#if"){m_if_level++;m_if_seen|=(1<<m_if_level);if(m_if_flag==0){lt=replace_macros(lt);simple_iter<ppreprocessor>t(this,tokenize(lt.substr(3),m_expr_sep,true,true));autoval=narrow_cast<int>(prepro_expr(t,255));t.skip_ws();if(!t.eod())error("found unprocessed content at end of line");if(val==0)m_if_flag|=(1<<m_if_level);elsem_elif|=(1<<m_if_level);}}elseif(lti[0]=="#ifdef"){m_if_level++;m_if_seen|=(1<<m_if_level);if(get_define(lti[1])==nullptr)m_if_flag|=(1<<m_if_level);elsem_elif|=(1<<m_if_level);}elseif(lti[0]=="#ifndef"){m_if_level++;m_if_seen|=(1<<m_if_level);if(get_define(lti[1])!=nullptr)m_if_flag|=(1<<m_if_level);elsem_elif|=(1<<m_if_level);}elseif(lti[0]=="#else"){if(!(m_if_seen&(1<<m_if_level)))error("#else without #if");m_if_flag^=(1<<m_if_level);m_elif&=~(1<<m_if_level);}elseif(lti[0]=="#elif"){if(!(m_if_seen&(1<<m_if_level)))error("#elif without #if");//if ((m_if_flag & (1 << m_if_level)) == 0)// m_if_flag ^= (1 << m_if_level);if(m_elif&(1<<m_if_level))m_if_flag|=(1<<m_if_level);elsem_if_flag&=~(1<<m_if_level);if(m_if_flag==0){//m_if_flag ^= (1 << m_if_level);lt=replace_macros(lt);simple_iter<ppreprocessor>t(this,tokenize(lt.substr(5),m_expr_sep,true,true));autoval=narrow_cast<int>(prepro_expr(t,255));t.skip_ws();if(!t.eod())error("found unprocessed content at end of line");if(val==0)m_if_flag|=(1<<m_if_level);elsem_elif|=~(1<<m_if_level);}}elseif(lti[0]=="#endif"){if(!(m_if_seen&(1<<m_if_level)))error("#else without #if");m_if_seen&=~(1<<m_if_level);m_elif&=~(1<<m_if_level);m_if_flag&=~(1<<m_if_level);m_if_level--;}elseif(lti[0]=="#include"){if(m_if_flag==0){pstringarg("");for(std::size_ti=1;i<lti.size();i++)arg+=(lti[i]+" ");arg=plib::trim(arg);if(startsWith(arg,"\"")&&endsWith(arg,"\"")){arg=arg.substr(1,arg.length()-2);// first try local contextautol(plib::util::buildpath({m_stack.back().m_local_path,arg}));autolstrm(m_sources.get_stream(l));if(!lstrm.empty()){m_stack.emplace_back(input_context(lstrm.release_stream(),plib::util::path(l),l));}else{autostrm(m_sources.get_stream(arg));if(!strm.empty()){m_stack.emplace_back(input_context(strm.release_stream(),plib::util::path(arg),arg));}elseerror("include not found:"+arg);}}elseerror("include misspelled:"+arg);pstringlinemarker=pfmt("# {1} \"{2}\" 1\n")(m_stack.back().m_lineno,m_stack.back().m_name);push_out(linemarker);}}elseif(lti[0]=="#pragma"){if(m_if_flag==0&<i.size()>3&<i[1]=="NETLIST"){if(lti[2]=="warning")error("NETLIST: "+catremainder(lti,3," "));}}elseif(lti[0]=="#define"){if(m_if_flag==0){if(lti.size()<2)error("define needs at least one argument");simple_iter<ppreprocessor>args(this,tokenize(lt.substr(8),m_expr_sep,false,false));pstringn=args.next();if(!is_valid_token(n))error("define expected identifier");auto*prevdef=get_define(n);if(lti.size()==2){if(prevdef!=nullptr&&!prevdef->m_replace.empty())error("redefinition of "+n);m_defines.insert({n,define_t(n,"")});}elseif(args.next_ws()=="("){define_tdef(n);def.m_has_params=true;autotoken(args.next());while(true){if(token==")")break;def.m_params.push_back(token);token=args.next();if(token!=","&&token!=")")error(pfmt("expected , or ), found <{1}>")(token));if(token==",")token=args.next();}pstringr;while(!args.eod())r+=args.next_ws();def.m_replace=r;if(prevdef!=nullptr&&prevdef->m_replace!=r)error("redefinition of "+n);m_defines.insert({n,def});}else{pstringr;while(!args.eod())r+=args.next_ws();if(prevdef!=nullptr&&prevdef->m_replace!=r)error("redefinition of "+n);m_defines.insert({n,define_t(n,r)});}}}elseif(lti[0]=="#undef"){if(m_if_flag==0){if(lti.size()<2)error("undef needs at least one argument");simple_iter<ppreprocessor>args(this,tokenize(lt.substr(7),m_expr_sep,false,false));pstringn=args.next();if(!is_valid_token(n))error("undef expected identifier");m_defines.erase(n);}}else{if(m_if_flag==0)error("unknown directive");}return{"",false};}if(m_if_flag==0)return{replace_macros(lt),true};return{"",false};}voidppreprocessor::push_out(constpstring&s){m_outbuf+=decltype(m_outbuf)(s.c_str());if(m_debug_out)std::cerr<<s;}voidppreprocessor::process_stack(){while(!m_stack.empty()){pstringline;pstringlinemarker=pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno,m_stack.back().m_name);push_out(linemarker);boollast_skipped=false;while(m_stack.back().m_reader.readline(line)){m_stack.back().m_lineno++;autor(process_line(line));if(r.second){if(last_skipped)push_out(pfmt("# {1} \"{2}\"\n")(m_stack.back().m_lineno,m_stack.back().m_name));push_out(r.first+"\n");last_skipped=false;}elselast_skipped=true;}m_stack.pop_back();if(!m_stack.empty()){linemarker=pfmt("# {1} \"{2}\" 2\n")(m_stack.back().m_lineno,m_stack.back().m_name);push_out(linemarker);}}}}// namespace plib