summaryrefslogtreecommitdiff
path: root/src/lang/parse.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lang/parse.cc')
-rw-r--r--src/lang/parse.cc453
1 files changed, 453 insertions, 0 deletions
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 @@
1/*************************************************************************
2 *
3 * HTCd - Copyright (C) 1998-2006 Henrik Rydberg
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include <lang/env.h>
21
22static const charset escaped("%$[]{};");
23static const charset prename("\"'`^~*,_=!\\");
24static const charset spec("+-*/%<=>^_!~&|,?:;");
25static const charset spec_latex("+-*/<=>^_;");
26static const charset delim("\"{}()\\");
27static const charset delim_latex("{}()[]\\");
28static const charset specws=spec|delim|cset_ws;
29static const charset specws_latex=spec_latex|delim_latex|cset_ws;
30static const charset tokname=cset_alphareal;
31static const charset pretok=tokname|prename;
32
33const mstring begin_tag="begin",end_left="\\end{",end_right="}";
34
35int is_tokname(const sref& s) {
36 return s.back()!='.'&&pretok.test(s.front())&&s.right(1).find_not_of(tokname)<0;
37}
38
39int is_arglist(const sref& s)
40{
41 const char* p=s.begin(); int opt=0;
42 if(*p=='?') { ++p; opt=1; }
43 while(*p=='x') ++p;
44 while(*p=='r') ++p;
45 if(!opt&&*p=='?') ++p;
46 if(*p=='*') ++p;
47 return !*p;
48}
49
50///////////////////////////////////////////////////////////
51
52static sref getlocal(const sref& in) {
53 int w=0,i=0; for(;i<in.size();i++) {
54 if(in[i]=='[') w++;
55 else if(in[i]==']') w--;
56 else if(!w&&specws.test(in[i])) break;
57 }
58 return in.left(i);
59}
60
61static sref getname(const sref& in) {
62 int i=0;
63 if(pretok.test(*in)) {
64 int w=0;
65 for(i=1;i<in.size();i++) {
66 if(in[i]=='[') w++;
67 else if(in[i]==']') w--;
68 else if(!w&&!tokname.test(in[i])) break;
69 }
70 }
71 if(i>0&&in[i-1]=='.') return in.left(i-1);
72 else return in.left(i);
73}
74
75static int skip_quotes(const sref& in) throw(merror_t) {
76 int s=in.skip(cset_quotes);
77 if(s<0) THROW("parse: unmatched '\"'");
78 return s;
79}
80
81static int skip_braces(const sref& in) throw(merror_t) {
82 int s=in.skip(cset_braces);
83 if(s<0) THROW("parse: unmatched '{'");
84 return s;
85}
86
87static int skip_para(const sref& in) throw(merror_t) {
88 int s=in.skip(cset_para);
89 if(s<0) THROW("parse: unmatched '('");
90 return s;
91}
92
93static int skip_indices(const sref& in) throw(merror_t) {
94 int s=in.skip(cset_indices);
95 if(s<0) THROW("parse: unmatched '['");
96 return s;
97}
98
99static int skip_dollars(const sref& in) throw(merror_t) {
100 int s=in.skip(cset_dollars);
101 //if(s<0) THROW("parse: unmatched '$'");
102 return s;
103}
104
105///////////////////////////////////////////////////////////
106
107static int count_args(const sref& in) throw(merror_t)
108{
109 sref e=in.right_first_not_of(cset_ws);
110 int s,n=0; while((s=skip_braces(e))>0||(s=skip_para(e))>0) {
111 e=e.adv(s).right_first_not_of(cset_ws); n++;
112 }
113 return n;
114}
115
116static int past_args(const sref& in) throw(merror_t)
117{
118 sref p=in,e=in.right_first_not_of(cset_ws);
119 int s; while((s=skip_braces(e))>0||(s=skip_para(e))>0) {
120 e=(p=e.adv(s)).right_first_not_of(cset_ws);
121 }
122 return p-in;
123}
124
125static int parse_args(Env& env,runnable_t* ftok,const sref& in,int inbegend) throw(merror_t)
126{
127 env.clear_args();
128 sref option,p=in,e=in.right_first_not_of(cset_ws);
129 int s,narg=0,carg=count_args(e);
130 while((s=e.skip(cset_braces))>0||(s==0&&(s=e.skip(cset_para))>0)) {
131 if(ftok->arg[narg]=='?'&&carg<ftok->arg.size()) narg++;
132 switch(ftok->arg[narg]){
133 case 'x': env.xpush(e.left(s)); break;
134 case 'r': case '*': env.rpush(e.left(s)); break;
135 case '?': option=e.left(s); break;
136 case 0: THROW("htc: too many args in "<<ftok->name);
137 default: THROW("htc: bad argument descriptor in "<<ftok->name);
138 }
139 e=(p=e.adv(s)).right_first_not_of(cset_ws);
140 if(ftok->arg[narg]!='*') narg++;
141 }
142 if(s<0) THROW("htc: unmatched parenthesis in function "<<ftok->name);
143 if(inbegend) {
144 mstring tag=end_left+ftok->name+end_right;
145 s=p.find(tag); if(s<0) THROW("htc: unmatched begin/end in function "<<ftok->name);
146 if(ftok->arg[narg]=='?'&&carg<ftok->arg.size()) narg++;
147 switch(ftok->arg[narg]){
148 case 'x': env.xpush(p.left(s)); break;
149 case 'r': case '*': env.rpush(p.left(s)); break;
150 case '?': option=p.left(s); break;
151 case 0: THROW("htc: too many args in begin/end "<<ftok->name);
152 default: THROW("htc: bad argument descriptor in "<<ftok->name);
153 }
154 p=p.adv(s+tag.size());
155 }
156 if(ftok->arg.find('?')>=0&&env.args<ftok->arg.size()) env.rpush(option);
157 return p-in;
158}
159
160int Env::check_arg(const runnable_t& ftok) const
161{
162 static const charset tag("xr?");
163 const char* p=ftok.arg.begin(); int x=0,r=0,o=0;
164 while(tag.test(*p)) {
165 switch(*p++) {
166 case 'x': x++; break;
167 case 'r': r++; break;
168 case '?': r++; o++; break;
169 }
170 }
171 if(*p=='*') { o++; ++p; }
172 if(*p||x!=xsize()||o>1||o==1&&rsize()<r||o==0&&r!=rsize()) return 0;
173 return 1;
174}
175
176///////////////////////////////////////////////////////////
177
178void Env::expr(mstream& out,const sref& in) throw(merror_t)
179{
180 expr_t x(in);
181 x.expand(*this);
182 x.eval(out,*this,OP_ALL);
183}
184
185void Env::xpush(const sref& in) throw(merror_t)
186{
187 expr_t* x=new expr_t(in(1,-3));
188 xarg.push_back(x); args++;
189 if(*in=='(') x->expand(*this); else x->status=OP_PARSE;
190 swrite sw(x->result);
191 eval(sw,x,0);
192}
193
194void Env::rpush(const sref& in) throw(merror_t)
195{
196 expr_t* x=new expr_t(in(1,-3));
197 rarg.push_back(x); args++;
198 if(*in=='(') x->expand(*this); else x->status=OP_PARSE;
199}
200
201///////////////////////////////////////////////////////////
202
203static int indent(sref& p) {
204 if(cset_newl.test(p.front())) { p=p.right_first_not_of(cset_ws); return 1; }
205 else return 0;
206}
207
208static int backindent(sref& p) {
209 if(cset_newl.test(p.leftand_last_not_of(cset_spaces).back())) {
210 p=p.leftand_last_not_of(cset_ws); return 1;
211 }
212 else return 0;
213}
214
215static int pastsemi(sref& p) {
216 int s=p.find_not_of(cset_spaces);
217 if(s>=0&&p[s]==';') return indent(p=p.adv(s+1)); else return 0;
218}
219
220static int past_func(const sref& in) throw(merror_t)
221{
222 sref e=getname(in.popf()),p=in.adv(e);
223 return p.adv(past_args(p))-in;
224}
225
226static int parse_func(mstream& out,Env& env,const sref& in,int& run) throw(merror_t)
227{
228 run=0; sref e=getname(in),p=in.adv(e);
229 mstring name;
230 swrite sw(name);
231 env.eval(sw,e,0);
232 if(env.is_arg(name)) {
233 //if(count_args(p)) THROW("htc: expected no argument after "<<name);
234 if(env.up) env.up->parg(out,atoi(name)-1);
235 return p-in;
236 }
237 int inbegend=0;
238 if(name==begin_tag) {
239 sref next=p.right_first_not_of(cset_ws);
240 int s=skip_braces(next);
241 if(s>0) { name=next(1,s-2); p=next.adv(s); inbegend=1; }
242 }
243 tok_t* tok=env.toks->get(name);
244 if(!tok) { if(!(tok=env.loc.get(name))||!tok->is_func()) tok=0; }
245 if(!tok) return 0;
246 if(!tok->is_runnable()) {
247 //if(count_args(p)) THROW("htc: expected no argument after "<<name);
248 if(env.is_clear(tok)) out<<tok->body;
249 return p-in;
250 }
251 runnable_t* ftok=(runnable_t*)tok;
252 if(env.is_clear(ftok)) {
253 p=p.adv(parse_args(env,ftok,p,inbegend));
254 run=1; env.eval(out,ftok,1);
255 }
256 else p=p.adv(past_args(p));
257 return p-in;
258}
259
260void Env::parse(mstream& out,const sref& in) throw(merror_t)
261{
262 static const charset sensors=escaped|cset_newl|'\\';
263 epilog.clear();
264 int s,run;
265 sref p=in;
266 indent(p);
267 backindent(p);
268 sref e=p;
269 while(*p) {
270 switch(*p) {
271 case ';':
272 case '}':
273 case ']':
274 out.put(*p);
275 p=p.popf();
276 break;
277 case '%':
278 if(p-e>0) { out.put(*p); p=p.popf(); }
279 else e=p=p.past_nl().right_first_not_of(cset_ws);
280 break;
281 case '\r':
282 p=p.popf();
283 break;
284 case '\n':
285 out.put(*p); p=p.popf();
286 if(cset_newl.test(*p.right_first_not_of(cset_spaces))) out<<para;
287 e=p=p.right_first_not_of(cset_ws);
288 break;
289 case '{':
290 if(mod&tok_t::PUBLIC){
291 if((s=skip_braces(p))>0) {
292 eval(out,p(1,s-2),0);
293 if(pastsemi(p=p.adv(s))) e=p;
294 }
295 }
296 else { out.put(*p); p=p.popf(); }
297 break;
298 case '[':
299 if(mod&tok_t::PUBLIC){
300 if((s=skip_indices(p))>0) {
301 expr(out,p(1,s-2));
302 if(pastsemi(p=p.adv(s))) e=p;
303 }
304 }
305 else { out.put(*p); p=p.popf(); }
306 break;
307 case '$':
308 if(mod&tok_t::PUBLIC){
309 if((s=skip_dollars(p))>0) {
310 latex_env(*this).expr(out,p(1,s-2));
311 if(pastsemi(p=p.adv(s))) e=p;
312 }
313 }
314 else { out.put(*p); p=p.popf(); }
315 break;
316 case '\\':
317 p=p.popf();
318 if(p.empty()) out.put('\\');
319 else if(escaped.test(*p)) { out.put(*p); p=p.popf(); }
320 else {
321 if((s=parse_func(out,*this,p,run))>0) { if(pastsemi(p=p.adv(s))) e=p; }
322 else out.put('\\');
323 }
324 break;
325 default:
326 if((s=p.find_of(sensors))>=0) { out<<p.left(s); p=p.adv(s); }
327 else { out<<p; p.clear(); }
328 break;
329 }
330 }
331 out<<epilog;
332}
333
334void Env::parse(mstream& out,expr_t* x) throw(merror_t)
335{
336 if(x->status&OP_PARSE) parse(out,x->expr);
337 else x->eval(out,*this,OP_ALL);
338}
339
340///////////////////////////////////////////////////////////
341
342void Env::eval(mstream& out,const sref& in,int child) throw(merror_t)
343{
344 Env nenv(*this); if(child) nenv.up=this;
345 nenv.parse(out,in);
346}
347
348void Env::eval(mstream& out,expr_t* x,int child) throw(merror_t)
349{
350 Env nenv(*this); if(child) nenv.up=this;
351 nenv.parse(out,x);
352}
353
354void Env::eval(mstream& out,runnable_t* ftok,int child) throw(merror_t)
355{
356 if(child&&!check_arg(*ftok)) THROW("htc: bad number of args; expected "<<*ftok);
357 if(ftok->is_binary()) ((binary_t*)ftok)->func(out,*this);
358 else if(ftok->is_func()) eval(out,&((func_t*)ftok)->x,child);
359 else THROW("htc: unknown runnable");
360}
361
362//////////////////////////////////////////////////////
363
364int Env::split(sref& first,sref& rest) throw(merror_t)
365{
366 int s;
367 rest=rest.right_first_not_of(cset_ws);
368 if(*rest=='\\') {
369 first=rest.left(past_func(rest));
370 rest=rest.adv(first);
371 }
372 else if(spec.test(*rest)) {
373 first=rest.left_first_not_of(spec);
374 rest=rest.adv(first);
375 }
376 else if((s=skip_quotes(rest))>0||(s=skip_braces(rest))>0) {
377 first=rest.left(s);
378 rest=rest.adv(first);
379 }
380 else if((s=skip_para(rest))>0) {
381 first=rest(1,s-2);
382 rest=rest.right(s);
383 }
384 else {
385 first=getlocal(rest);
386 rest=rest.adv(first);
387 }
388 return first.nempty();
389}
390
391void Env::term(mstream& out,expr_t* x) throw(merror_t)
392{
393 if(x->expr.empty()) x->status=0;
394 else if(*x->expr=='\"') { out<<x->expr(1,-3); x->status=0; }
395 else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; }
396 else if(x->expr.find_not_of(cset_real)<0) { out<<x->expr; x->status=0; }
397 else if(*x->expr=='\\') {
398 sref p=x->expr.popf();
399 if(p.empty()) { out.put('\\'); x->status=0; }
400 else if(escaped.test(*p)) { out.put(*p); x->status=0; }
401 else {
402 int run;
403 if(parse_func(out,*this,p,run)>0) x->status=run?OP_LOCAL:OP_GLOBAL;
404 else { out.put('\\')<<x->expr; x->status=OP_NINIT; }
405 }
406 }
407 else {
408 mstring name;
409 swrite sw(name);
410 eval(sw,x->expr,0);
411 out<<gloc(name);
412 x->status=OP_LOCAL;
413 }
414}
415
416///////////////////////////////////////////////////////////////////
417
418int latex_env::split(sref& first,sref& rest) throw(merror_t)
419{
420 int s;
421 rest=rest.right_first_not_of(cset_ws);
422 if(*rest=='\\') {
423 first=rest.left(past_func(rest));
424 rest=rest.adv(first);
425 }
426 else if(spec.test(*rest)) {
427 first=rest.left_first_not_of(spec);
428 rest=rest.adv(first);
429 }
430 else if((s=skip_braces(rest))>0||(s=skip_para(rest))>0||(s=skip_indices(rest))>0) {
431 first=rest.left(s);
432 rest=rest.adv(first);
433 }
434 else {
435 first=rest.left_first_of(specws);
436 rest=rest.adv(first);
437 }
438 return first.nempty();
439}
440
441void latex_env::term(mstream& out,expr_t* x) throw(merror_t)
442{
443 if(x->expr.empty()) x->status=0;
444 else if(*x->expr=='\\') { eval(out,x->expr,0); x->status=0; }
445 else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; }
446 else if(*x->expr=='('||*x->expr=='[') {
447 out.put(x->expr.front());
448 expr(out,x->expr(1,-3));
449 out.put(x->expr.back());
450 x->status=0;
451 }
452 else { out<<x->expr; x->status=0; }
453}