From 5df79c53745fde5d6c3340a2979b1429cd5892c1 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 8 Oct 2011 20:30:28 +0200 Subject: Initial import of htcd system 1.0 Signed-off-by: Henrik Rydberg --- src/lang/parse.cc | 453 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 src/lang/parse.cc (limited to 'src/lang/parse.cc') diff --git a/src/lang/parse.cc b/src/lang/parse.cc new file mode 100644 index 0000000..7e24a11 --- /dev/null +++ b/src/lang/parse.cc @@ -0,0 +1,453 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static const charset escaped("%$[]{};"); +static const charset prename("\"'`^~*,_=!\\"); +static const charset spec("+-*/%<=>^_!~&|,?:;"); +static const charset spec_latex("+-*/<=>^_;"); +static const charset delim("\"{}()\\"); +static const charset delim_latex("{}()[]\\"); +static const charset specws=spec|delim|cset_ws; +static const charset specws_latex=spec_latex|delim_latex|cset_ws; +static const charset tokname=cset_alphareal; +static const charset pretok=tokname|prename; + +const mstring begin_tag="begin",end_left="\\end{",end_right="}"; + +int is_tokname(const sref& s) { + return s.back()!='.'&&pretok.test(s.front())&&s.right(1).find_not_of(tokname)<0; +} + +int is_arglist(const sref& s) +{ + const char* p=s.begin(); int opt=0; + if(*p=='?') { ++p; opt=1; } + while(*p=='x') ++p; + while(*p=='r') ++p; + if(!opt&&*p=='?') ++p; + if(*p=='*') ++p; + return !*p; +} + +/////////////////////////////////////////////////////////// + +static sref getlocal(const sref& in) { + int w=0,i=0; for(;i0&&in[i-1]=='.') return in.left(i-1); + else return in.left(i); +} + +static int skip_quotes(const sref& in) throw(merror_t) { + int s=in.skip(cset_quotes); + if(s<0) THROW("parse: unmatched '\"'"); + return s; +} + +static int skip_braces(const sref& in) throw(merror_t) { + int s=in.skip(cset_braces); + if(s<0) THROW("parse: unmatched '{'"); + return s; +} + +static int skip_para(const sref& in) throw(merror_t) { + int s=in.skip(cset_para); + if(s<0) THROW("parse: unmatched '('"); + return s; +} + +static int skip_indices(const sref& in) throw(merror_t) { + int s=in.skip(cset_indices); + if(s<0) THROW("parse: unmatched '['"); + return s; +} + +static int skip_dollars(const sref& in) throw(merror_t) { + int s=in.skip(cset_dollars); + //if(s<0) THROW("parse: unmatched '$'"); + return s; +} + +/////////////////////////////////////////////////////////// + +static int count_args(const sref& in) throw(merror_t) +{ + sref e=in.right_first_not_of(cset_ws); + int s,n=0; while((s=skip_braces(e))>0||(s=skip_para(e))>0) { + e=e.adv(s).right_first_not_of(cset_ws); n++; + } + return n; +} + +static int past_args(const sref& in) throw(merror_t) +{ + sref p=in,e=in.right_first_not_of(cset_ws); + int s; while((s=skip_braces(e))>0||(s=skip_para(e))>0) { + e=(p=e.adv(s)).right_first_not_of(cset_ws); + } + return p-in; +} + +static int parse_args(Env& env,runnable_t* ftok,const sref& in,int inbegend) throw(merror_t) +{ + env.clear_args(); + sref option,p=in,e=in.right_first_not_of(cset_ws); + int s,narg=0,carg=count_args(e); + while((s=e.skip(cset_braces))>0||(s==0&&(s=e.skip(cset_para))>0)) { + if(ftok->arg[narg]=='?'&&cargarg.size()) narg++; + switch(ftok->arg[narg]){ + case 'x': env.xpush(e.left(s)); break; + case 'r': case '*': env.rpush(e.left(s)); break; + case '?': option=e.left(s); break; + case 0: THROW("htc: too many args in "<name); + default: THROW("htc: bad argument descriptor in "<name); + } + e=(p=e.adv(s)).right_first_not_of(cset_ws); + if(ftok->arg[narg]!='*') narg++; + } + if(s<0) THROW("htc: unmatched parenthesis in function "<name); + if(inbegend) { + mstring tag=end_left+ftok->name+end_right; + s=p.find(tag); if(s<0) THROW("htc: unmatched begin/end in function "<name); + if(ftok->arg[narg]=='?'&&cargarg.size()) narg++; + switch(ftok->arg[narg]){ + case 'x': env.xpush(p.left(s)); break; + case 'r': case '*': env.rpush(p.left(s)); break; + case '?': option=p.left(s); break; + case 0: THROW("htc: too many args in begin/end "<name); + default: THROW("htc: bad argument descriptor in "<name); + } + p=p.adv(s+tag.size()); + } + if(ftok->arg.find('?')>=0&&env.argsarg.size()) env.rpush(option); + return p-in; +} + +int Env::check_arg(const runnable_t& ftok) const +{ + static const charset tag("xr?"); + const char* p=ftok.arg.begin(); int x=0,r=0,o=0; + while(tag.test(*p)) { + switch(*p++) { + case 'x': x++; break; + case 'r': r++; break; + case '?': r++; o++; break; + } + } + if(*p=='*') { o++; ++p; } + if(*p||x!=xsize()||o>1||o==1&&rsize()expand(*this); else x->status=OP_PARSE; + swrite sw(x->result); + eval(sw,x,0); +} + +void Env::rpush(const sref& in) throw(merror_t) +{ + expr_t* x=new expr_t(in(1,-3)); + rarg.push_back(x); args++; + if(*in=='(') x->expand(*this); else x->status=OP_PARSE; +} + +/////////////////////////////////////////////////////////// + +static int indent(sref& p) { + if(cset_newl.test(p.front())) { p=p.right_first_not_of(cset_ws); return 1; } + else return 0; +} + +static int backindent(sref& p) { + if(cset_newl.test(p.leftand_last_not_of(cset_spaces).back())) { + p=p.leftand_last_not_of(cset_ws); return 1; + } + else return 0; +} + +static int pastsemi(sref& p) { + int s=p.find_not_of(cset_spaces); + if(s>=0&&p[s]==';') return indent(p=p.adv(s+1)); else return 0; +} + +static int past_func(const sref& in) throw(merror_t) +{ + sref e=getname(in.popf()),p=in.adv(e); + return p.adv(past_args(p))-in; +} + +static int parse_func(mstream& out,Env& env,const sref& in,int& run) throw(merror_t) +{ + run=0; sref e=getname(in),p=in.adv(e); + mstring name; + swrite sw(name); + env.eval(sw,e,0); + if(env.is_arg(name)) { + //if(count_args(p)) THROW("htc: expected no argument after "<parg(out,atoi(name)-1); + return p-in; + } + int inbegend=0; + if(name==begin_tag) { + sref next=p.right_first_not_of(cset_ws); + int s=skip_braces(next); + if(s>0) { name=next(1,s-2); p=next.adv(s); inbegend=1; } + } + tok_t* tok=env.toks->get(name); + if(!tok) { if(!(tok=env.loc.get(name))||!tok->is_func()) tok=0; } + if(!tok) return 0; + if(!tok->is_runnable()) { + //if(count_args(p)) THROW("htc: expected no argument after "<body; + return p-in; + } + runnable_t* ftok=(runnable_t*)tok; + if(env.is_clear(ftok)) { + p=p.adv(parse_args(env,ftok,p,inbegend)); + run=1; env.eval(out,ftok,1); + } + else p=p.adv(past_args(p)); + return p-in; +} + +void Env::parse(mstream& out,const sref& in) throw(merror_t) +{ + static const charset sensors=escaped|cset_newl|'\\'; + epilog.clear(); + int s,run; + sref p=in; + indent(p); + backindent(p); + sref e=p; + while(*p) { + switch(*p) { + case ';': + case '}': + case ']': + out.put(*p); + p=p.popf(); + break; + case '%': + if(p-e>0) { out.put(*p); p=p.popf(); } + else e=p=p.past_nl().right_first_not_of(cset_ws); + break; + case '\r': + p=p.popf(); + break; + case '\n': + out.put(*p); p=p.popf(); + if(cset_newl.test(*p.right_first_not_of(cset_spaces))) out<0) { + eval(out,p(1,s-2),0); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '[': + if(mod&tok_t::PUBLIC){ + if((s=skip_indices(p))>0) { + expr(out,p(1,s-2)); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '$': + if(mod&tok_t::PUBLIC){ + if((s=skip_dollars(p))>0) { + latex_env(*this).expr(out,p(1,s-2)); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '\\': + p=p.popf(); + if(p.empty()) out.put('\\'); + else if(escaped.test(*p)) { out.put(*p); p=p.popf(); } + else { + if((s=parse_func(out,*this,p,run))>0) { if(pastsemi(p=p.adv(s))) e=p; } + else out.put('\\'); + } + break; + default: + if((s=p.find_of(sensors))>=0) { out<status&OP_PARSE) parse(out,x->expr); + else x->eval(out,*this,OP_ALL); +} + +/////////////////////////////////////////////////////////// + +void Env::eval(mstream& out,const sref& in,int child) throw(merror_t) +{ + Env nenv(*this); if(child) nenv.up=this; + nenv.parse(out,in); +} + +void Env::eval(mstream& out,expr_t* x,int child) throw(merror_t) +{ + Env nenv(*this); if(child) nenv.up=this; + nenv.parse(out,x); +} + +void Env::eval(mstream& out,runnable_t* ftok,int child) throw(merror_t) +{ + if(child&&!check_arg(*ftok)) THROW("htc: bad number of args; expected "<<*ftok); + if(ftok->is_binary()) ((binary_t*)ftok)->func(out,*this); + else if(ftok->is_func()) eval(out,&((func_t*)ftok)->x,child); + else THROW("htc: unknown runnable"); +} + +////////////////////////////////////////////////////// + +int Env::split(sref& first,sref& rest) throw(merror_t) +{ + int s; + rest=rest.right_first_not_of(cset_ws); + if(*rest=='\\') { + first=rest.left(past_func(rest)); + rest=rest.adv(first); + } + else if(spec.test(*rest)) { + first=rest.left_first_not_of(spec); + rest=rest.adv(first); + } + else if((s=skip_quotes(rest))>0||(s=skip_braces(rest))>0) { + first=rest.left(s); + rest=rest.adv(first); + } + else if((s=skip_para(rest))>0) { + first=rest(1,s-2); + rest=rest.right(s); + } + else { + first=getlocal(rest); + rest=rest.adv(first); + } + return first.nempty(); +} + +void Env::term(mstream& out,expr_t* x) throw(merror_t) +{ + if(x->expr.empty()) x->status=0; + else if(*x->expr=='\"') { out<expr(1,-3); x->status=0; } + else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; } + else if(x->expr.find_not_of(cset_real)<0) { out<expr; x->status=0; } + else if(*x->expr=='\\') { + sref p=x->expr.popf(); + if(p.empty()) { out.put('\\'); x->status=0; } + else if(escaped.test(*p)) { out.put(*p); x->status=0; } + else { + int run; + if(parse_func(out,*this,p,run)>0) x->status=run?OP_LOCAL:OP_GLOBAL; + else { out.put('\\')<expr; x->status=OP_NINIT; } + } + } + else { + mstring name; + swrite sw(name); + eval(sw,x->expr,0); + out<status=OP_LOCAL; + } +} + +/////////////////////////////////////////////////////////////////// + +int latex_env::split(sref& first,sref& rest) throw(merror_t) +{ + int s; + rest=rest.right_first_not_of(cset_ws); + if(*rest=='\\') { + first=rest.left(past_func(rest)); + rest=rest.adv(first); + } + else if(spec.test(*rest)) { + first=rest.left_first_not_of(spec); + rest=rest.adv(first); + } + else if((s=skip_braces(rest))>0||(s=skip_para(rest))>0||(s=skip_indices(rest))>0) { + first=rest.left(s); + rest=rest.adv(first); + } + else { + first=rest.left_first_of(specws); + rest=rest.adv(first); + } + return first.nempty(); +} + +void latex_env::term(mstream& out,expr_t* x) throw(merror_t) +{ + if(x->expr.empty()) x->status=0; + else if(*x->expr=='\\') { eval(out,x->expr,0); x->status=0; } + else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; } + else if(*x->expr=='('||*x->expr=='[') { + out.put(x->expr.front()); + expr(out,x->expr(1,-3)); + out.put(x->expr.back()); + x->status=0; + } + else { out<expr; x->status=0; } +} -- cgit v1.2.3