summaryrefslogtreecommitdiff
path: root/src/lang
diff options
context:
space:
mode:
Diffstat (limited to 'src/lang')
-rw-r--r--src/lang/basic.cc315
-rw-r--r--src/lang/db.cc237
-rw-r--r--src/lang/env.cc308
-rw-r--r--src/lang/env.h156
-rw-r--r--src/lang/flow.cc198
-rw-r--r--src/lang/func.cc249
-rw-r--r--src/lang/help.cc140
-rw-r--r--src/lang/lang.cc39
-rw-r--r--src/lang/latex.cc152
-rw-r--r--src/lang/ops.cc150
-rw-r--r--src/lang/parse.cc453
-rw-r--r--src/lang/test.cc45
-rw-r--r--src/lang/token.cc268
-rw-r--r--src/lang/token.h230
14 files changed, 2940 insertions, 0 deletions
diff --git a/src/lang/basic.cc b/src/lang/basic.cc
new file mode 100644
index 0000000..54d16a9
--- /dev/null
+++ b/src/lang/basic.cc
@@ -0,0 +1,315 @@
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
22///////////////////////////////////////////////////
23
24TOK_IMPL(asis) { out<<env.arg(0)->expr; }
25TOK_IMPL(mute) {}
26TOK_IMPL(comment) {}
27
28///////////////////////////////////////////////////
29
30TOK_IMPL(cond) {
31 Cond(*env.toks,env[0],env[1],tok_t::PUBLIC);
32}
33TOK_IMPL(global) {
34 Global(*env.toks,env[0],env[1],tok_t::PUBLIC);
35}
36TOK_IMPL(const) {
37 Const(*env.toks,env[0],env[1],tok_t::PUBLIC);
38}
39TOK_IMPL(reconst) {
40 Const(*env.toks,env[0],env[1],tok_t::PUBLIC,1);
41}
42
43///////////////////////////////////////////////////
44
45TOK_IMPL(type) {
46 tok_t* p=env.toks->get(env[0]);
47 if(env.is_clear(p)) out<<p->mod;
48}
49TOK_IMPL(desc) {
50 tok_t* p=env.toks->get(env[0]);
51 if(env.is_clear(p)) out<<Desc(*p);
52}
53TOK_IMPL(expr) {
54 tok_t* p=env.toks->get(env[0]);
55 if(env.is_clear(p)&&p->is_func()) ((func_t*)p)->x.trav(out,12);
56}
57TOK_IMPL(clear) {
58 ClearToken(*env.toks,env[0]);
59}
60
61//////////////////////////////////////////////////////
62
63TOK_IMPL(exist) { out<<env.glob_find(env[0]); }
64TOK_IMPL(nexist) { out<<!env.glob_find(env[0]); }
65TOK_IMPL(empty) { out<<env.glob(env[0]).empty(); }
66TOK_IMPL(nempty) { out<<env.glob(env[0]).nempty(); }
67
68///////////////////////////////////////////////////
69
70TOK_IMPL(export) {
71 rpile list; env.gloc_list(list,env[1]);
72 int n=*env[1]=='@'?env[1].size()-1:env[1].size();
73 for(int i=0;i<list.size();i++)
74 Global(*env.toks,env[0]+list[i].right(n),env.gloc(list[i]),tok_t::PUBLIC);
75}
76
77TOK_IMPL(import) {
78 if(*env[0]=='@') env.glob_adb(env[0].popf(),env[1]);
79 else {
80 rpile list; env.glob_list(list,env[1]);
81 for(int i=0;i<list.size();i++)
82 env.aloc(env[0]+list[i].right(env[1].size()),env.glob(list[i]));
83 }
84}
85
86TOK_IMPL(list) {
87 mstring fx; env.parg(fx,-1);
88 rpile list; env.gloc_list(list,fx);
89 int n=*fx=='@'?fx.size()-1:fx.size();
90 for(int i=0;i<list.size();i++) out<<list[i].right(n)<<"\n";
91}
92
93TOK_IMPL(globlist) {
94 mstring fx; env.parg(fx,-1);
95 rpile list; env.glob_list(list,fx);
96 int n=*fx=='@'?fx.size()-1:fx.size();
97 for(int i=0;i<list.size();i++) out<<list[i].right(n)<<"\n";
98}
99
100TOK_IMPL(lrec) {
101 sref name=env[0],first,rest=env[1];
102 mstring init; env.parg(init,-1);
103 while(Split(first,rest)) env.aloc(name+first,init);
104}
105
106///////////////////////////////////////////////////
107
108TOK_IMPL(arg) { if(env.up) env.up->parg(out,atoi(env[0])-1); }
109TOK_IMPL(args) { if(env.up) out<<env.up->args; else out<<0; }
110
111///////////////////////////////////////////////////
112
113TOK_IMPL(random) {
114 out<<Random(atoi(env[0]));
115}
116
117//////////////////////////////////////////////////////
118
119void envAddBasic(tokmap& T)
120{
121 TOK_ADD(asis,"r",tok_t::PUBLIC,
122 "%Display body as is\n"
123 "%Syntax: \\asis{body}\n"
124 );
125 TOK_ADD(mute,"x",tok_t::PUBLIC,
126 "%Evaluate but don't display\n"
127 "%Syntax: \\mute{body}\n"
128 );
129 T.add(new binary_t(sref("*"),
130 sref("%Commentary - don't show or evaluate body\n"
131 "%Syntax: \\*{body}\n"),
132 tok_t::PUBLIC,sref("r"),impl_comment),0,1);
133
134 //////////////////////////////////////////////////////
135
136 TOK_ADD(global,"xx",tok_t::PUBLIC,
137 "%Set global variable\n"
138 "%Syntax: \\global{varname}{expr}\n"
139 );
140 TOK_ADD(cond,"xx",tok_t::PUBLIC,
141 "%Set global variable if not already defined\n"
142 "%Syntax: \\cond{varname}{expr}\n"
143 );
144 TOK_ADD(const,"xx",tok_t::PUBLIC,
145 "%Set global constant\n"
146 "%Syntax: \\cond{varname}{expr}\n"
147 "%\n"
148 "%Generates an error if already defined\n"
149 );
150 TOK_ADD(reconst,"xx",tok_t::PRIVATE,
151 "%Redefine global constant\n"
152 "%Syntax: \\cond{varname}{expr}\n"
153 "%\n"
154 "%Takes precedence over any existing constant\n"
155 );
156
157 //////////////////////////////////////////////////////
158
159 TOK_ADD(type,"x",tok_t::PRIVATE,
160 "%Show variable type\n"
161 "%Syntax: \\type{varname}\n"
162 "%\n"
163 "%The type is an integer bit-field consisting of:\n"
164 "%\n"
165 "%1 CONSTANT\n"
166 "%2 RESTRICT\n"
167 "%4 NORMAL\n"
168 "%8 SYSONLY\n"
169 "%16 INIT\n"
170 "%32 RUNNABLE\n"
171 "%64 FUNCTION\n"
172 "%128 BINARY\n"
173 "%\n"
174 "%The access modes consists of\n"
175 "%\n"
176 "%INIT\n"
177 "%SYSONLY\n"
178 "%PRIVATE = NORMAL|SYSONLY\n"
179 "%PUBLIC = PRIVATE|RESTRICT\n"
180 "%\n"
181 "%Those values are matched against the running modes\n"
182 "%to determine access. See run.\n"
183 );
184 TOK_ADD(desc,"x",tok_t::PRIVATE,
185 "%Show variable description\n"
186 "%Syntax: \\desc{varname}\n"
187 "%\n"
188 "%Collects all lines starting with %.\n"
189 );
190 TOK_ADD(expr,"x",tok_t::PRIVATE,
191 "%Show the expression tree of function\n"
192 "%Syntax: \\expr{varname}\n"
193 "%\n"
194 "%If desc is a function, the expression tree is shown.\n"
195 "%Mostly interesting for real expression, i.e., when the\n"
196 "%function body was defined within parenthesis.\n"
197 );
198 TOK_ADD(clear,"x",tok_t::SYSONLY,
199 "%Clear variables\n"
200 "%Syntax: \\clear{partial varname}\n"
201 "%\n"
202 "%Clears all global variables on the current level\n"
203 "%beginning with the partial variable name.\n"
204 );
205
206 //////////////////////////////////////////////////////
207
208 TOK_ADD(exist,"x",tok_t::PUBLIC,
209 "%True if variable exist\n"
210 "%Syntax: \\exist{varname}\n"
211 );
212 TOK_ADD(nexist,"x",tok_t::PUBLIC,
213 "%True if variable does not exist\n"
214 "%Syntax: \\nexist{varname}\n"
215 );
216 TOK_ADD(empty,"x",tok_t::PUBLIC,
217 "%True if variable is empty\n"
218 "%Syntax: \\empty{varname}\n"
219 "%\n"
220 "%Generates an error if varname does not exist.\n"
221 );
222 TOK_ADD(nempty,"x",tok_t::PUBLIC,
223 "%True if variable is non-empty\n"
224 "%Syntax: \\nempty{varname}\n"
225 "%\n"
226 "%Generates an error if varname does not exist.\n"
227 );
228
229 //////////////////////////////////////////////////////
230
231 TOK_ADD(export,"xx",tok_t::SYSONLY,
232 "%Export local variables\n"
233 "%Syntax: \\export{global partial name}{local partial name}\n"
234 "%\n"
235 "%Copies a sequence of local variables, beginning with the\n"
236 "%local partial name, from the same - not nescessarily the current -\n"
237 "%local stack into a set of global variables beginning with\n"
238 "%the global partial name.\n"
239 "%\n"
240 "%Copies all local variables in the current stack if no\n"
241 "%partial name is given.\n"
242 "%\n"
243 "%Copies all variables in the current database record\n"
244 "%if the partial name @ is given.\n"
245 );
246 TOK_ADD(import,"xx",tok_t::SYSONLY,
247 "%Import global variables\n"
248 "%Syntax: \\import{local partial name}{global partial name}\n"
249 "%\n"
250 "%Copies a sequence of global variables, beginning with the\n"
251 "%global partial name, from the same - not nescessarily the\n"
252 "%current - global level into a set of local variables beginning\n"
253 "%with the local partial name.\n"
254 "%\n"
255 "%Copies all global variables on the current level if no\n"
256 "%partial name is given.\n"
257 "%\n"
258 "%Creates a new or updates an existing database record if\n"
259 "%the partial name @ is given.\n"
260 );
261 TOK_ADD(list,"?",tok_t::PUBLIC,
262 "%List local variables\n"
263 "%Syntax: \\list<partial name>\n"
264 "%\n"
265 "%Generates a list of names, beginning with the partial name,\n"
266 "%from the same - not nescessarily the current - local stack.\n"
267 "%\n"
268 "%Generates all local variables in the current stack if no\n"
269 "%partial name is given.\n"
270 "%\n"
271 "%Generates a list of tables if @ is given.\n"
272 );
273 TOK_ADD(globlist,"?",tok_t::PUBLIC,
274 "%List global variables\n"
275 "%Syntax: \\globlist<partial name>\n"
276 "%\n"
277 "%Generates a list of names, beginning with the partial name,\n"
278 "%from the same - not nescessarily the current - global level.\n"
279 "%\n"
280 "%Generates all global variables on the current level if no\n"
281 "%partial name is given.\n"
282 );
283 TOK_ADD(lrec,"xx?",tok_t::PUBLIC,
284 "%Create a local record\n"
285 "%Syntax: \\lrec{partial name}{name list}<init>\n"
286 "%\n"
287 "%Creates a set of local variables, all beginning with the\n"
288 "%partial name, and initializes each variable to init.\n"
289 );
290
291 //////////////////////////////////////////////////////
292
293 TOK_ADD(arg,"x",tok_t::PUBLIC,
294 "%Show/Evaluate argument #n.\n"
295 "%Syntax: \\arg{n}\n"
296 "%\n"
297 "%Same as the command \\1, \\2 etc, except this\n"
298 "%may be used to index the arguments e.g. using local variables.\n"
299 );
300 TOK_ADD(args,"",tok_t::PUBLIC,
301 "%Number of arguments in the current function\n"
302 "%Syntax: \\args\n"
303 "%\n"
304 "%Note that the optional argument will count as one variable,\n"
305 "%and may be accesed using either \\0 or \\arg{\\args}.\n"
306 );
307
308 //////////////////////////////////////////////////////
309
310 TOK_ADD(random,"x",tok_t::PRIVATE,
311 "%Get n random digits between 0 and 9 inclusive\n"
312 "%Syntax: \\random{n}\n"
313 );
314
315}
diff --git a/src/lang/db.cc b/src/lang/db.cc
new file mode 100644
index 0000000..9192d09
--- /dev/null
+++ b/src/lang/db.cc
@@ -0,0 +1,237 @@
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 dbmap* GetDB(Env& env) throw(merror_t) {
23 if(!env.db) THROW("htc: no open database");
24 return env.db;
25}
26
27static int eval(Env& env,expr_t* x,int mod=OP_ALL) {
28 mswrite out;
29 if(x->status&OP_PARSE) env.parse(out,x->expr); else x->eval(out,env,mod);
30 return atob(out.str());
31}
32
33////////////////////////////////////////////////////////////////////////////
34
35TOK_IMPL(create) {
36 dbref_t* ref=DBRef(*env.toks,env[0],ms_empty,tok_t::PUBLIC,1);
37 ref->db.Open("/dev/zero",1);
38 sref first,rest=env[1]; int n=0;
39 while(Split(first,rest)) ref->db.InsertTable(n++,first);
40 ref->db.Order(env[2],env[3]);
41}
42
43TOK_IMPL(sort) {
44 dbref_t* ref=(dbref_t*)env.toks->get_this(env[0]);
45 if(env.is_clear(ref)&&ref->is_dbref()) {
46 ref->db.remap();
47 ref->db.Sort(env[1],env[2]);
48 }
49 else THROW("htc: "<<env[0]<<" is not a sortable on the current level");
50}
51
52TOK_IMPL(db) {
53 dbref_t* ref=(dbref_t*)env.toks->get(env[0]);
54 if(env.is_clear(ref)&&ref->is_dbref()) {
55 ref->db.remap();
56 Env(env,ref->db).parse(out,env.arg(1));
57 }
58 else THROW("htc: "<<env[0]<<" is not a db reference");
59}
60
61////////////////////////////////////////////////////////////////////////////
62
63TOK_IMPL(query) {
64 dbmap* db=GetDB(env);
65 expr_t* x[2]={env.arg(0),env.arg(1)};
66 Env nenv(env,*db);
67 nenv.dbat=0;
68 while(nenv.dbat>=0&&env.req->is_running()&&nenv.dbat<db->size()) {
69 int size=db->size();
70 if(eval(nenv,x[0],OP_LOCAL)) nenv.parse(out,x[1]);
71 if(db->size()==size) nenv.dbat++;
72 }
73}
74
75TOK_IMPL(find_rec) {
76 dbmap* db=GetDB(env);
77 Env nenv(env,*db);
78 nenv.dbat=db->Find(env[0]);
79 if(nenv.dbat>=0&&nenv.dbat<db->size()) nenv.parse(out,env.arg(1));
80 else env.parg(out,2);
81}
82
83////////////////////////////////////////////////////////////////////
84
85TOK_IMPL(first_rec) { env.dbat=0; }
86TOK_IMPL(prev_rec) { if(env.dbat>0) env.dbat--; }
87TOK_IMPL(next_rec) { if(env.dbat<GetDB(env)->size()) env.dbat++; }
88TOK_IMPL(exist_rec) { out<<bool(env.dbat>=0&&env.dbat<GetDB(env)->size()); }
89
90TOK_IMPL(rem_rec) {
91 dbmap* db=GetDB(env);
92 if(env.dbat>=0&&env.dbat<env.db->size()) db->Rem(env.dbat);
93}
94
95////////////////////////////////////////////////////////////////////
96
97TOK_IMPL(lastkey) {
98 dbmap* db=GetDB(env);
99 if(!db->empty()) out<<db->at(db->size()-1,db->Index());
100}
101
102TOK_IMPL(prevkey) {
103 dbmap* db=GetDB(env);
104 if(env.dbat>0&&env.dbat<=db->size()) out<<db->at(env.dbat-1,db->Index());
105}
106
107TOK_IMPL(nextkey) {
108 dbmap* db=GetDB(env);
109 if(env.dbat>=0&&env.dbat+1<db->size()) out<<db->at(env.dbat+1,db->Index());
110}
111
112TOK_IMPL(qprevkey) {
113 dbmap* db=GetDB(env);
114 expr_t* x=env.arg(0);
115 Env nenv(env,*db);
116 nenv.dbat=env.dbat-1;
117 while(nenv.dbat>=0&&env.req->is_running()&&nenv.dbat<db->size()) {
118 if(eval(nenv,x,OP_LOCAL)) { out<<db->at(nenv.dbat,db->Index()); break; }
119 else nenv.dbat--;
120 }
121}
122
123TOK_IMPL(qnextkey) {
124 dbmap* db=GetDB(env);
125 expr_t* x=env.arg(0);
126 Env nenv(env,*db);
127 nenv.dbat=env.dbat+1;
128 while(nenv.dbat>=0&&env.req->is_running()&&nenv.dbat<db->size()) {
129 if(eval(nenv,x,OP_LOCAL)) { out<<db->at(nenv.dbat,db->Index()); break; }
130 else nenv.dbat++;
131 }
132}
133
134////////////////////////////////////////////////////////////////////
135
136void envAddDB(tokmap& T)
137{
138 TOK_ADD(create,"xxxx",tok_t::SYSONLY,
139 "%Create a new database\n"
140 "%Syntax: \\create{varname}{tables}{index}{comp}\n"
141 "%\n"
142 "%Index is the unique, ordered table.\n"
143 "%Comp is the compare function.\n"
144 "%A maxmimum of 32 tables are allowed in HDB/1.2\n"
145 );
146 TOK_ADD(sort,"xxx",tok_t::PRIVATE,
147 "%Sort a database\n"
148 "%Syntax: \\sort{varname}{index}{comp}\n"
149 "%\n"
150 "%Note that only globals on the current level are sortable.\n"
151 );
152
153 ////////////////////////////////////////////////////////
154
155 TOK_ADD(db,"xr",tok_t::PRIVATE,
156 "%Set current database\n"
157 "%Syntax: \\db{varname}{body}\n"
158 "%\n"
159 "%Within body, all database functions apply.\n"
160 );
161 TOK_ADD(query,"xr",tok_t::PRIVATE,
162 "%Query current database\n"
163 "%Syntax: \\query{test}{body}\n"
164 "%\n"
165 "%Evaluates body for all record where test is true.\n"
166 "%\n"
167 "%optimizes globals; it is ok to use full function\n"
168 "%expressions in the query. Turned out to be better after all.\n"
169 );
170 TOK_ADD(find_rec,"xr?",tok_t::PRIVATE,
171 "%Find record by key, in current database\n"
172 "%Syntax: \\find.rec{key}{body}<else>\n"
173 "%\n"
174 "%Evaluates body if key is found in the current ordering.\n"
175 "%Otherwise, evaluates <else>, if exist.\n"
176 );
177
178 ////////////////////////////////////////////////////////
179
180 TOK_ADD(first_rec,"",tok_t::PRIVATE,
181 "%Go to first record\n"
182 "%Syntax: \\first.rec\n"
183 );
184 TOK_ADD(exist_rec,"",tok_t::PRIVATE,
185 "%True if current record exist\n"
186 "%Syntax: \\exist.rec\n"
187 );
188 TOK_ADD(next_rec,"",tok_t::PRIVATE,
189 "%Go to next record\n"
190 "%Syntax: \\next.rec\n"
191 );
192 TOK_ADD(prev_rec,"",tok_t::PRIVATE,
193 "%Go to previous record\n"
194 "%Syntax: \\prev.rec\n"
195 );
196 TOK_ADD(rem_rec,"",tok_t::SYSONLY,
197 "%Remove current record\n"
198 "%Syntax: \\rem.rec\n"
199 );
200
201 ////////////////////////////////////////////////////////
202
203 TOK_ADD(lastkey,"",tok_t::PRIVATE,
204 "%Last key of current order\n"
205 "%Syntax: \\lastkey\n"
206 "%\n"
207 "%Silently ignored if not found.\n"
208 );
209 TOK_ADD(prevkey,"",tok_t::PRIVATE,
210 "%Previous key of current order\n"
211 "%Syntax: \\prevkey\n"
212 "%\n"
213 "%Silently ignored if not found.\n"
214 );
215 TOK_ADD(nextkey,"",tok_t::PRIVATE,
216 "%Next key of current order\n"
217 "%Syntax: \\nextkey\n"
218 "%\n"
219 "%Silently ignored if not found.\n"
220 );
221
222 ////////////////////////////////////////////////////////
223
224 TOK_ADD(qprevkey,"r",tok_t::PRIVATE,
225 "%Previous key of current order, satisfying query\n"
226 "%Syntax: \\qprevkey\n"
227 "%\n"
228 "%Silently ignored if not found.\n"
229 );
230 TOK_ADD(qnextkey,"r",tok_t::PRIVATE,
231 "%Next key of current order, satisfying query\n"
232 "%Syntax: \\qnextkey\n"
233 "%\n"
234 "%Silently ignored if not found.\n"
235 );
236
237}
diff --git a/src/lang/env.cc b/src/lang/env.cc
new file mode 100644
index 0000000..83f1a7e
--- /dev/null
+++ b/src/lang/env.cc
@@ -0,0 +1,308 @@
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#include <mt/random.h>
22
23static unsigned long seed=RandomInit(time(0));
24
25mstring Random(int n)
26{
27 mstring s;
28 for(int i=0;i<n;i++) {
29 int digit=int(10*Uniform());
30 if(digit>9) THROW("env: assertion on lib failed");
31 s+=char(48+digit);
32 }
33 return s;
34}
35
36///////////////////////////////////////////////////
37
38void Env::clear_args()
39{
40 for(int i=0;i<xarg.size();i++) delete xarg[i]; xarg.clear();
41 for(int i=0;i<rarg.size();i++) delete rarg[i]; rarg.clear();
42 args=0;
43}
44
45void Env::parg(mstream& out,int n) throw(merror_t)
46{
47 if(args) {
48 if(n<0) n+=args;
49 if(n>=0&&n<args) {
50 if(n<xsize()) out<<xarg[n]->result;
51 else eval(out,rarg[n-xsize()],0);
52 }
53 }
54 else if(up) up->parg(out,n);
55}
56
57void Env::parg(mstring& tmp,int n) throw(merror_t)
58{
59 swrite sw(tmp);
60 parg(sw,n);
61}
62
63mstring Env::parg(int n) throw(merror_t)
64{
65 mswrite tmp; parg(tmp,n); return tmp.str();
66}
67
68//////////////////////////////////////////////////////
69
70int Env::onarg(int n) const
71{
72 if(args) {
73 if(n<0) n+=args;
74 if(n>=0&&n<args) {
75 if(n<xsize()) return xarg[n]->result.nempty();
76 else return rarg[n-xsize()]->expr.nempty();
77 }
78 }
79 else if(up) return up->onarg(n);
80 return 0;
81}
82
83expr_t* Env::arg(int n) throw(merror_t) {
84 if(args) {
85 if(n<0) n+=args;
86 if(n>=0&&n<args) {
87 if(n<xsize()) return xarg[n];
88 else return rarg[n-xsize()];
89 }
90 }
91 else if(up) return up->arg(n);
92 THROW("env: bad arg number "<<n);
93 return 0;
94}
95
96const sref& Env::operator[](int n) const throw(merror_t) {
97 if(args) {
98 if(n<0) n+=args;
99 if(n>=0&&n<args) {
100 if(n<xsize()) return xarg[n]->result;
101 else THROW("env: post-eval expression not readable here");
102 }
103 }
104 else if(up) return up->operator[](n);
105 THROW("env: bad arg number "<<n);
106 return ms_empty;
107}
108
109//////////////////////////////////////////////////////
110
111const sref Env::glob(const sref& t) const throw(merror_t)
112{
113 const tok_t* p=toks->get(t);
114 if(is_clear(p)&&!p->is_runnable()) return p->body;
115 THROW("env: could not find global variable "<<t);
116 return ms_empty;
117}
118
119int Env::glob_find(const sref& t) const
120{
121 const tok_t* p=toks->get(t);
122 return is_clear(p)&&!p->is_runnable();
123}
124
125int Env::onglob(const sref& t) const
126{
127 const tok_t* p=toks->get(t);
128 return is_clear(p)&&!p->is_runnable()&&p->body.nempty();
129}
130
131void Env::glob_list(rpile& list,const sref& t) const
132{
133 list.clear();
134 if(t.empty()) {
135 for(const_iterator p=toks->begin();p!=toks->end();++p) {
136 if(is_clear(*p)&&!(*p)->is_runnable()) list.push_back((*p)->name);
137 }
138 }
139 else {
140 int cont=1;
141 for(const tokmap* tok=toks;tok&&cont;tok=tok->up) {
142 for(const_iterator p=tok->begin();p!=tok->end();++p) {
143 if((*p)->name.left(t.size())==t&&is_clear(*p)&&!(*p)->is_runnable()) {
144 list.push_back((*p)->name);
145 cont=0;
146 }
147 }
148 }
149 }
150}
151
152void Env::glob_adb(const sref& fx,const sref& t) throw(merror_t)
153{
154 if(!db) THROW("htc: no active database");
155 rpile list; glob_list(list,t);
156 mrvec<int> col(list.size());
157 int index=-1;
158 for(int i=0;i<col.size();i++) {
159 col[i]=db->Index(fx+list[i].right(t.size()));
160 if(col[i]==db->Index()) index=i;
161 }
162 if(index<0) THROW("env: index required when adding a db record");
163 int row=db->Find(glob(list[index]));
164 if(row>=0) {
165 for(int i=0;i<list.size();i++) if(i!=index) db->Set(row,col[i],glob(list[i]));
166 }
167 else {
168 spile nlist(db->Tables());
169 for(int i=0;i<list.size();i++) nlist[col[i]]=glob(list[i]);
170 row=db->Insert(nlist,1);
171 }
172 if(row<0) THROW("htc: could not add record - denied");
173 dbat=row;
174}
175
176//////////////////////////////////////////////////////
177
178const sref Env::aloc(const sref& t,const sref& v) throw(merror_t)
179{
180 if(!is_tokname(t)) THROW("env: bad token name "<<t);
181 return loc.add(new tok_t(t,v,tight()),1,1)->body;
182}
183
184const sref Env::sloc(const sref& t,const sref& v) throw(merror_t)
185{
186 if(db) {
187 int p=db->FindIndex(*t=='@'?t.popf():t);
188 if(p>=0) {
189 if(p==db->Index()) THROW("env: cannot assign to db index");
190 if(dbat<0||dbat>=db->size()) THROW("env: not a valid db record");
191 db->Set(dbat,p,v);
192 return db->at(dbat,p);
193 }
194 }
195 return loc.set(new tok_t(t,v,tight()),1,1)->body;
196}
197
198const sref Env::gloc(const sref& t) const throw(merror_t)
199{
200 if(db) {
201 int p=db->FindIndex(*t=='@'?t.popf():t);
202 if(p>=0) {
203 if(dbat>=0&&dbat<db->size()) return db->at(dbat,p);
204 else THROW("env: not a valid database record");
205 }
206 }
207 const tok_t* p=loc.get(t);
208 if(is_clear(p)&&!p->is_runnable()) return p->body;
209 THROW("env: could not find local variable "<<t);
210 return ms_empty;
211}
212
213int Env::gloc_find(const sref& t) const
214{
215 if(db) {
216 int p=db->FindIndex(*t=='@'?t.popf():t);
217 if(p>=0) return dbat>=0&&dbat<db->size();
218 }
219 const tok_t* p=loc.get(t);
220 return is_clear(p)&&!p->is_runnable();
221}
222
223int Env::onloc(const sref& t) const
224{
225 if(db) {
226 int p=db->FindIndex(*t=='@'?t.popf():t);
227 if(p>=0) return dbat>=0&&dbat<db->size()&&db->at(dbat,p).nempty();
228 }
229 const tok_t* p=loc.get(t);
230 return is_clear(p)&&!p->is_runnable()&&p->body.nempty();
231}
232
233void Env::gloc_list(rpile& list,const sref& t) const
234{
235 list.clear();
236 if(t.empty()) {
237 for(const_iterator p=loc.begin();p!=loc.end();++p) {
238 if(is_clear(*p)&&!(*p)->is_runnable()) list.push_back((*p)->name);
239 }
240 }
241 else if(*t=='@') {
242 if(db) {
243 sref tt=t.popf();
244 for(int i=0;i<db->Tables();i++) {
245 if(tt.empty()||db->Table(i).left(tt.size())==tt) list.push_back(db->Table(i));
246 }
247 }
248 else THROW("env: cannot use '@' - no active database");
249 }
250 else {
251 int cont=1;
252 for(const tokmap* tok=&loc;tok&&cont;tok=tok->up) {
253 for(const_iterator p=tok->begin();p!=tok->end();++p) {
254 if((*p)->name.left(t.size())==t&&is_clear(*p)&&!(*p)->is_runnable()) {
255 list.push_back((*p)->name);
256 cont=0;
257 }
258 }
259 }
260 }
261}
262
263void Env::gloc_adb(const sref& fx,const sref& t) throw(merror_t)
264{
265 if(!db) THROW("htc: no active database");
266 rpile list; gloc_list(list,t);
267 mrvec<int> col(list.size());
268 int index=-1;
269 for(int i=0;i<col.size();i++) {
270 col[i]=db->Index(fx+list[i].right(t.size()));
271 if(col[i]==db->Index()) index=i;
272 }
273 if(index<0) THROW("env: index required when adding a db record");
274 int row=db->Find(gloc(list[index]));
275 if(row>=0) {
276 for(int i=0;i<list.size();i++) if(i!=index) db->Set(row,col[i],gloc(list[i]));
277 }
278 else {
279 spile nlist(db->Tables());
280 for(int i=0;i<list.size();i++) nlist[col[i]]=gloc(list[i]);
281 row=db->Insert(nlist,1);
282 }
283 if(row<0) THROW("htc: could not add record - denied");
284 dbat=row;
285}
286
287//////////////////////////////////////////////////////
288
289const mstring sback="../",sident="./",sdots="..",sslash="//";
290
291mstring Env::URI(const sref& raw)
292{
293 int s; mstring cwd=pwd.left_last('/'),uri=raw;
294 while(uri.left(sback.size())==sback) {
295 uri.erase(0,sback.size());
296 cwd=cwd.left_last('/');
297 }
298 while((s=uri.find(sback))>=0) {
299 mstring tmp=uri.left(s).left_last('/').leftand_last('/');
300 tmp+=uri.right(s+sback.size());
301 uri=tmp;
302 }
303 while((s=uri.find(sdots))>=0) uri.erase(s,sdots.size());
304 while((s=uri.find(sident))>=0) uri.erase(s,sident.size());
305 while((s=uri.find(sslash))>=0) uri.erase(s,sslash.size());
306 if(*uri=='/') return uri;
307 else { cwd+='/'; cwd+=uri; return cwd; }
308}
diff --git a/src/lang/env.h b/src/lang/env.h
new file mode 100644
index 0000000..1313d18
--- /dev/null
+++ b/src/lang/env.h
@@ -0,0 +1,156 @@
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#ifndef ENVH
21#define ENVH
22
23#include <lang/token.h>
24
25mstring Random(int n);
26
27const int OP_PARSE=8;
28const mstring para_def="\n";
29
30////////////////////////////////////////////////////////////////////
31
32struct Lang {
33 volatile int running;
34 opmap ops;
35 tokmap toks;
36 Lang();
37 virtual ~Lang() {}
38};
39
40struct req_env {
41 Lang* glob;
42 tokmap toks;
43 mstring uri;
44 int lang;
45 volatile int* running;
46
47 int is_running() const { return glob->running&&(!running||*running); }
48
49 req_env(Lang& g,tokmap& p) : glob(&g),toks(p),uri(),lang(0),running(0) {}
50 virtual ~req_env() {}
51};
52
53////////////////////////////////////////////////////////////////////
54
55struct Env : public op_env {
56 typedef mrvec<expr_t*> xpile;
57 typedef tokmap::iterator iterator;
58 typedef tokmap::const_iterator const_iterator;
59
60 Env(opmap& op,req_env& r,unsigned m):
61 op_env(op),req(&r),toks(&r.toks),up(0),mod(m),
62 loc(),db(0),dbat(0),pwd(r.uri),para(para_def),args(0) {}
63
64 Env(Env& p,tokmap& tok,const sref& cwd):
65 op_env(p),req(p.req),toks(&tok),up(p.up),mod(p.mod),
66 loc(p.loc),db(0),dbat(0),pwd(cwd),para(p.para),args(0) {}
67 Env(Env& p,unsigned m,const sref& cwd):
68 op_env(p),req(p.req),toks(p.toks),up(p.up),mod(m),
69 loc(p.loc),db(0),dbat(0),pwd(cwd),para(p.para),args(0) {}
70
71 Env(Env& p):
72 op_env(p),req(p.req),toks(p.toks),up(p.up),mod(p.mod),
73 loc(p.loc),db(p.db),dbat(p.dbat),pwd(p.pwd),para(p.para),args(0) {}
74 Env(Env& p,dbmap& dbm):
75 op_env(p),req(p.req),toks(p.toks),up(p.up),mod(p.mod),
76 loc(p.loc),db(&dbm),dbat(0),pwd(p.pwd),para(p.para),args(0) {}
77 Env(Env& p,opmap& op):
78 op_env(op),req(p.req),toks(p.toks),up(p.up),mod(p.mod),
79 loc(p.loc),db(p.db),dbat(p.dbat),pwd(p.pwd),para(p.para),args(0) {}
80
81 ~Env() { clear_args(); }
82
83 int is_clear(const tok_t* tok) const { return tok&&tok->is_clear(mod); }
84 unsigned tight() const { return tok_t::tight(mod); }
85
86 int check_arg(const runnable_t& ftok) const;
87 int is_arg(const sref& t) const { return cset_digit.test(t.front()); }
88
89 int xsize() const { return xarg.size(); }
90 int rsize() const { return rarg.size(); }
91
92 void xpush(const sref& in) throw(merror_t);
93 void rpush(const sref& in) throw(merror_t);
94 void clear_args();
95
96 void parg(mstream& out,int n) throw(merror_t);
97 void parg(mstring& buf,int n) throw(merror_t);
98 mstring parg(int n) throw(merror_t);
99
100 const sref& operator[](int m) const throw(merror_t);
101 expr_t* arg(int m) throw(merror_t);
102
103 const sref glob(const sref& t) const throw(merror_t);
104
105 int glob_find(const sref& t) const;
106 void glob_list(rpile& list,const sref& t) const;
107 void glob_adb(const sref& fx,const sref& t) throw(merror_t);
108
109 const sref aloc(const sref& t,const sref& v) throw(merror_t);
110 const sref sloc(const sref& t,const sref& v) throw(merror_t);
111 const sref gloc(const sref& t) const throw(merror_t);
112
113 int gloc_find(const sref& t) const;
114 void gloc_list(rpile& list,const sref& t) const;
115 void gloc_adb(const sref& fx,const sref& t) throw(merror_t);
116
117 int onarg(int m) const;
118 int onglob(const sref& s) const;
119 int onloc(const sref& s) const;
120
121 void parse(mstream& out,const sref& in) throw(merror_t);
122 void expr(mstream& out,const sref& in) throw(merror_t);
123 void parse(mstream& out,expr_t* x) throw(merror_t);
124
125 void eval(mstream& out,const sref& in,int child) throw(merror_t);
126 void eval(mstream& out,expr_t* x,int child) throw(merror_t);
127 void eval(mstream& out,runnable_t* ftok,int child) throw(merror_t);
128
129 int split(sref& first,sref& rest) throw(merror_t);
130 void term(mstream& out,expr_t* x) throw(merror_t);
131
132 mstring URI(const sref& raw);
133
134 Env *up;
135 req_env* req;
136 tokmap* toks;
137 int mod,dbat;
138 tokmap loc;
139 dbmap* db;
140 mstring pwd,para,epilog;
141 int args;
142 xpile xarg,rarg;
143};
144
145/////////////////////////////////////////////////////////////////////
146
147struct latex_env : public Env {
148 latex_env(Env& p);
149
150 int split(sref& first,sref& rest) throw(merror_t);
151 void term(mstream& out,expr_t* x) throw(merror_t);
152};
153
154/////////////////////////////////////////////////////////////////////
155
156#endif
diff --git a/src/lang/flow.cc b/src/lang/flow.cc
new file mode 100644
index 0000000..c7c70a0
--- /dev/null
+++ b/src/lang/flow.cc
@@ -0,0 +1,198 @@
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#include <mt/dates.h>
22using namespace std;
23
24TOK_IMPL(branch) {
25 int index=atoi(env[0]);
26 if(index<0) index+=env.args;
27 if(index>0&&index<env.args) env.parg(out,index);
28 else if(index<0||index>=env.args) THROW("htc: bad branching index "<<index);
29}
30
31TOK_IMPL(if) {
32 if(atob(env[0])) env.parg(out,1); else env.parg(out,2);
33}
34
35TOK_IMPL(nif) {
36 if(!atob(env[0])) env.parg(out,1); else env.parg(out,2);
37}
38
39TOK_IMPL(switch) {
40 sref s=env[0];
41 int i;
42 for(i=1;i+1<env.args;i+=2) if(env.parg(i)==s) { env.parg(out,i+1); return; }
43 if(i<env.args) env.parg(out,i);
44}
45
46/////////////////////////////////////////////////////////////////////
47
48static int eval(Env& env,expr_t* x,int mod) {
49 mswrite out;
50 if(x->status&OP_PARSE) env.parse(out,x->expr); else x->eval(out,env,mod);
51 return atob(out.str());
52}
53
54TOK_IMPL(for) {
55 expr_t* x[4]={env.arg(0),env.arg(1),env.arg(2),env.arg(3)};
56 Env nenv(env);
57 for(eval(nenv,x[0],OP_ALL);
58 env.req->is_running()&&eval(nenv,x[1],OP_LOCAL);
59 eval(nenv,x[2],OP_LOCAL))
60 nenv.parse(out,x[3]);
61}
62
63TOK_IMPL(while) {
64 expr_t* x[2]={env.arg(0),env.arg(1)};
65 Env nenv(env);
66 while(env.req->is_running()&&eval(nenv,x[0],OP_ALL)) nenv.parse(out,x[1]);
67}
68
69/////////////////////////////////////////////////////////////////////
70
71TOK_IMPL(forall) {
72 mstring sep; env.parg(sep,-1);
73 sref s=env[0];
74 sref p,e=env[1];
75 expr_t* x=env.arg(2);
76 Env nenv(env);
77 if(sep.nempty()) {
78 charset seps(sep.c_str());
79 while(Split(p,e,seps)) {
80 nenv.aloc(s,p);
81 nenv.parse(out,x);
82 }
83 }
84 else {
85 while(Split(p,e)) {
86 nenv.aloc(s,p);
87 nenv.parse(out,x);
88 }
89 }
90}
91
92/////////////////////////////////////////////////////////////////////
93
94TOK_IMPL(on_arg) {
95 if(env.up&&env.up->onarg(atoi(env[0])-1)) env.parg(out,1); else env.parg(out,2);
96}
97TOK_IMPL(on_glob) { if(env.onglob(env[0])) env.parg(out,1); else env.parg(out,2); }
98TOK_IMPL(on_loc) { if(env.onloc(env[0])) env.parg(out,1); else env.parg(out,2); }
99
100/////////////////////////////////////////////////////////////////////
101
102TOK_IMPL(setlang) {
103 env.req->lang=Language(env[0]); if(env.req->lang<0) env.req->lang=0;
104}
105
106TOK_IMPL(lang) {
107 env.parg(out,env.req->lang);
108}
109
110/////////////////////////////////////////////////////////////////////
111
112void envAddFlow(tokmap& T)
113{
114 TOK_ADD(branch,"x*",tok_t::PUBLIC,
115 "%Multiple branch\n"
116 "%Syntax: \\branch{x}<body>..<body>\n"
117 "%\n"
118 "%x==0 means nothing is executed, the first body for x==1, etc\n"
119 "%Negative values wrap to end\n"
120 );
121 TOK_ADD(if,"xr?",tok_t::PUBLIC,
122 "%if - logical branch\n"
123 "%Syntax: \\if{x}{body}<else>\n"
124 "%\n"
125 "%if x is non-zero, evaluates body, otherwise else if exist\n"
126 );
127 TOK_ADD(nif,"xr?",tok_t::PUBLIC,
128 "%nif - logical branch\n"
129 "%Syntax: \\nif{x}{body}<else>\n"
130 "%\n"
131 "%if x is zero, evaluates body, otherwise else if exist\n"
132 );
133 TOK_ADD(switch,"x*",tok_t::PUBLIC,
134 "%Multiple switch\n"
135 "%Syntax: \\switch{x}<val><body>..<val><body>..<default>\n"
136 "%\n"
137 "%A sequence of pairs <val><body> follows x\n"
138 "%x==val means body is executed\n"
139 "%\n"
140 "%An unpaired body at the end is treated as default\n"
141 );
142
143 /////////////////////////////////////////////////////////////////////
144
145 TOK_ADD(for,"rrrr",tok_t::PRIVATE,
146 "%C style\n"
147 "%Syntax: \\for{start}{test}{next}{body}\n"
148 "%\n"
149 "%Note that test and next are evaluated with optimization: only\n"
150 "%local variables are updated. Hence, you cannot use a function\n"
151 "%as the whole test, only as a part of the test, as in i<\\function.\n"
152 );
153 TOK_ADD(while,"rr",tok_t::PRIVATE,
154 "%C style\n"
155 "%Syntax: \\while{test}{body}\n"
156 "%\n"
157 "%There is no optimization on test - globals are re-evaluated in each step\n"
158 );
159 TOK_ADD(forall,"xx?r",tok_t::PUBLIC,
160 "%Loop through elements\n"
161 "%Syntax: \\forall{varname}{list}<additional whitespace>{body}\n"
162 "%\n"
163 "%Default whitespace are ( \\t\\r\\n). Optional whitespace characters\n"
164 "%may be given. This enables the design of special lists.\n"
165 );
166
167 /////////////////////////////////////////////////////////////////////
168
169 TOK_ADD(on_arg,"xr?",tok_t::PUBLIC,
170 "%Do if arg exist and is non-empty\n"
171 "%Syntax: \\on.arg{number}{body}<else>\n"
172 );
173 TOK_ADD(on_glob,"xr?",tok_t::PUBLIC,
174 "%Do if global variable exist and is non-empty\n"
175 "%Syntax: \\on.glob{varname}{body}<else>\n"
176 );
177 TOK_ADD(on_loc,"xr?",tok_t::PUBLIC,
178 "%Do if local variable exist and is non-empty\n"
179 "%Syntax: \\on.loc{varname}{body}<else>\n"
180 );
181
182 /////////////////////////////////////////////////////////////////////
183
184 TOK_ADD(lang,"r*",tok_t::PUBLIC,
185 "%Branch on global language\n"
186 "%Syntax: \\lang{en}{sv}..\n"
187 "%\n"
188 "%Currently English (en) and Swedish (sv) are supported\n"
189 );
190 TOK_ADD(setlang,"x",tok_t::PUBLIC,
191 "%Set global language\n"
192 "%Syntax: \\setlang{language code list}\n"
193 "%\n"
194 "%The language actually set is a negotitation -\n"
195 "%the first code in the list recognized is set.\n"
196 "%If not found, the default language English (en) is set.\n"
197 );
198}
diff --git a/src/lang/func.cc b/src/lang/func.cc
new file mode 100644
index 0000000..98cc04c
--- /dev/null
+++ b/src/lang/func.cc
@@ -0,0 +1,249 @@
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#include <unistd.h>
22
23const mstring cron_tag="cron.";
24const int SSECS=4;
25
26TOK_IMPL(def) {
27 expr_t* x=env.arg(2);
28 func_t* tok=Func(*env.toks,env[0],x->expr,tok_t::PUBLIC,env[1]);
29 if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env);
30}
31
32TOK_IMPL(redef) {
33 expr_t* x=env.arg(2);
34 func_t* tok=Func(*env.toks,env[0],x->expr,tok_t::PUBLIC,env[1],1);
35 if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env);
36}
37
38TOK_IMPL(ldef) {
39 if(env.toks->get(env[0])) THROW("htc: local function would override global token");
40 expr_t* x=env.arg(2);
41 func_t* tok=Func(env.loc,env[0],x->expr,tok_t::PUBLIC,env[1]);
42 if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env);
43}
44
45TOK_IMPL(cron) {
46 mstring name=cron_tag+env[0];
47 expr_t* x=env.arg(1);
48 cron_t* tok=Cron(*env.toks,name,x->expr,tok_t::PUBLIC,atoi(env[0]));
49 if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env);
50}
51
52///////////////////////////////////////////////////
53
54static void Run(tokmap& m,mstream& out,Env& env,const sref& s) throw(merror_t) {
55 tok_t* p=m.get(s);
56 if(!p) THROW("htc: cannot run "<<s<<"; does not exist");
57 try {
58 if(p->is_reference()) env.parse(out,((reference_t*)p)->data);
59 else if(!p->is_runnable()) env.parse(out,p->body);
60 }
61 catch(const merror_t& e) {
62 out<<e.desc;
63 }
64}
65
66TOK_IMPL(run) {
67 Env e(env,env.mod,env.pwd);
68 Run(*env.toks,out,e,env[0]);
69}
70TOK_IMPL(run_private) {
71 Env e(env,XPRIVATE,env.pwd);
72 Run(*env.toks,out,e,env[0]);
73}
74TOK_IMPL(run_public) {
75 Env e(env,XPUBLIC,env.pwd);
76 Run(*env.toks,out,e,env[0]);
77}
78TOK_IMPL(run_local) {
79 try {
80 Env e(env,XPUBLIC,env.pwd);
81 e.parse(out,env.gloc(env[0]));
82 }
83 catch(const merror_t& e) {
84 out<<e.desc;
85 }
86}
87TOK_IMPL(run_cron) {
88 mrvec<cron_t*> cron;
89 int cont=1; for(tokmap* tok=env.toks;tok&&cont;tok=tok->up) {
90 for(Env::iterator p=tok->begin();p!=tok->end();++p) {
91 if((*p)->name.left(cron_tag.size())==cron_tag&&(*p)->is_cron()) {
92 cron.push_back((cron_t*)(*p));
93 cont=0;
94 }
95 }
96 }
97 while(env.req->is_running()&&cron.size()) {
98 int cont=1;
99 for(int i=0;i<cron.size()&&cont;i++) {
100 if(time(0)>cron[i]->last+cron[i]->interval) {
101 mstream ms;
102 env.eval(ms,&cron[i]->x,0);
103 cron[i]->last=time(0);
104 cont=0;
105 }
106 }
107 if(cont) sleep(SSECS);
108 }
109}
110
111//////////////////////////////////////////////////////
112
113void envAddFunc(tokmap& T)
114{
115 TOK_ADD(def,"xxr",tok_t::PUBLIC,
116 "%Define new global function\n"
117 "%Syntax: \\def{varname}{argument string [xr?*]}{body}\n"
118 "%\n"
119 "%The argument string is a sequence of characters describing\n"
120 "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n"
121 "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n"
122 "%The can be only one optional (?) argument.\n"
123 "%The body contains the function body. The arguments are retrieved\n"
124 "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n"
125 "%regardless of where it stands in the sequence.\n"
126 "%\n"
127 "%Generates an error if already defined.\n"
128 );
129 TOK_ADD(redef,"xxr",tok_t::PRIVATE,
130 "%Redefine global function\n"
131 "%Syntax: \\def{varname}{argument string [xr?*]}{body}\n"
132 "%\n"
133 "%The argument string is a sequence of characters describing\n"
134 "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n"
135 "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n"
136 "%The can be only one optional (?) argument.\n"
137 "%The body contains the function body. The arguments are retrieved\n"
138 "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n"
139 "%regardless of where it stands in the sequence.\n"
140 "%\n"
141 "%Takes precedence over any existing function.\n"
142 );
143 TOK_ADD(ldef,"xxr",tok_t::PUBLIC,
144 "%Define a local function\n"
145 "%Syntax: \\ldef{varname}{argument string [xr?*]}{body}\n"
146 "%\n"
147 "%The argument string is a sequence of characters describing\n"
148 "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n"
149 "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n"
150 "%The can be only one optional (?) argument.\n"
151 "%The body contains the function body. The arguments are retrieved\n"
152 "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n"
153 "%regardless of where it stands in the sequence.\n"
154 "%\n"
155 "%Local functions work just like global functions, but are only\n"
156 "%accessible from within the current stack frame.\n"
157 );
158 TOK_ADD(cron,"xr",tok_t::SYSONLY,
159 "%Define a cron function\n"
160 "%Syntax: \\cron{interval}{body}\n"
161 "%\n"
162 "%A cron function is executed every interval seconds,\n"
163 "%when running in cron mode.\n"
164 "%\n"
165 "%See \\run.cron.\n"
166 );
167
168 //////////////////////////////////////////////////////
169
170 TOK_ADD(run,"x",tok_t::SYSONLY,
171 "%Evaluate the body of variable in current running mode\n"
172 "%Syntax: \\run{varname}\n"
173 "%\n"
174 "%The running modes are:\n"
175 "%\n"
176 "%XINIT Init mode is only used to collect meta information\n"
177 "%XSYSTEM The system mode - everything is accessible\n"
178 "%XPRIVATE Private mode - suitable for in-door custom programs\n"
179 "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n"
180 "% via the web. This is the default mode for all derivative\n"
181 "% functions, as well as for globals and constants.\n"
182 "%\n"
183 "%IT IS A MAJOR SECURITY HAZARD TO EVALUATE UNKNOWN\n"
184 "%VARIABLES IN XSYSTEM MODE!!\n"
185 "%\n"
186 "%If at all, user-supplied information should only be run\n"
187 "%in public mode, for instance to allow the expansion of latex\n"
188 "%commands. NEVER use this mode outside the core.\n"
189 );
190 TOK_ADD(run_private,"x",tok_t::SYSONLY,
191 "%Evaluate the body of variable in private mode\n"
192 "%Syntax: \\run.private{varname}\n"
193 "%\n"
194 "%The running modes are:\n"
195 "%\n"
196 "%XINIT Init mode is only used to collect meta information\n"
197 "%XSYSONLY The system mode - everything is accessible\n"
198 "%XPRIVATE Private mode - suitable for in-door custom htc code\n"
199 "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n"
200 "% via the web. This is the default mode for all derivative\n"
201 "% functions, as well as for globals and constants.\n"
202 "%\n"
203 "%The execution of sysonly functions is silent, just as it wasn't there.\n"
204 );
205 TOK_ADD(run_public,"x",tok_t::PRIVATE,
206 "%Evaluate the body of variable in public mode\n"
207 "%Syntax: \\run.public{varname}\n"
208 "%\n"
209 "%The running modes are:\n"
210 "%\n"
211 "%XINIT Init mode is only used to collect meta information\n"
212 "%XSYSONLY The system mode - everything is accessible\n"
213 "%XPRIVATE Private mode - suitable for in-door custom programs\n"
214 "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n"
215 "% via the web. This is the default mode for all derivative\n"
216 "% functions, as well as for globals and constants.\n"
217 "%\n"
218 "%The execution of sysonly or private functions is silent,\n"
219 "%just as they weren't there.\n"
220 );
221 TOK_ADD(run_local,"x",tok_t::PRIVATE,
222 "%Evaluate the body of the local variable in public mode\n"
223 "%Syntax: \\run.local{varname}\n"
224 "%\n"
225 "%The running modes are:\n"
226 "%\n"
227 "%XINIT Init mode is only used to collect meta information\n"
228 "%XSYSTEM The system mode - everything is accessible\n"
229 "%XPRIVATE Private mode - suitable for in-door custom programs\n"
230 "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n"
231 "% via the web. This is the default mode for all derivative\n"
232 "% functions, as well as for globals and constants.\n"
233 "%\n"
234 "%The execution of sysonly or private functions is silent,\n"
235 "%just as they weren't there.\n"
236 );
237 TOK_ADD(run_cron,"",tok_t::SYSONLY,
238 "%Go into an infinite loop and run the crontab command.\n"
239 "%Syntax: \\run.cron{}\n"
240 "%\n"
241 "%Note that only cron functions from the highest global level\n"
242 "%where a cron function is defined is run. Hence you may redefine\n"
243 "%earlier crontabs, but cannot mix between levels.\n"
244 "%\n"
245 "%You can only have one crontab of a certain interval.\n"
246 "%\n"
247 "%Produces no output.\n"
248 );
249}
diff --git a/src/lang/help.cc b/src/lang/help.cc
new file mode 100644
index 0000000..750a13a
--- /dev/null
+++ b/src/lang/help.cc
@@ -0,0 +1,140 @@
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
22TOK_IMPL(help) {
23 out<<"HTC/1.2 - HyperText Compiler (H. Rydberg (C) 1998,1999,2000,2001)\n";
24 out<<"\n";
25 out<<"Try these commands:\n";
26 out<<"\\help - This message\n";
27 out<<"\\ops - List all operators\n";
28 out<<"\\ops{op} - Get info on operator\n";
29 out<<"\\tok - List all tokens\n";
30 out<<"\\tok{tok} - Get info on token\n";
31 out<<"\\src{tok} - View token source\n";
32}
33
34TOK_IMPL(ops) {
35 mstring cmd; env.parg(cmd,-1);
36 if(cmd.nempty()) {
37 out<<cmd<<"\n";
38 const op_t* p=env.ops->get(cmd);
39 if(p) {
40 if(p->pre1) out<<"Unary operator, precedence "<<p->pre1<<"\n";
41 if(p->pre2) out<<"Binary operator, precedence "<<p->pre2<<"\n";
42 out<<"\n";
43 out<<p->desc;
44 }
45 else {
46 out<<"(not found)\n";
47 }
48 }
49 else {
50 mstring line;
51 line.convert("%-3s %-3s %s\n","P1","P2","NAME");
52 out<<line;
53 for(opmap::iterator p=env.ops->begin();p!=env.ops->end();p++) {
54 line.convert("%-3d %-3d %s\n",(*p)->pre1,(*p)->pre2,(*p)->name.c_str());
55 out<<line;
56 }
57 }
58}
59
60TOK_IMPL(tok) {
61 mstring cmd; env.parg(cmd,-1);
62 if(cmd.nempty()) {
63 out<<cmd<<"\n";
64 const tok_t* p=env.toks->get(cmd);
65 if(p) {
66 out<<"Mode: "<<Mode(*p)<<"\n";
67 if(p->mod&tok_t::RESTRICT) out<<"public ";
68 else if(p->mod&tok_t::NORMAL) out<<"normal ";
69 else if(p->mod&tok_t::SYSONLY) out<<"system ";
70 else if(p->mod&tok_t::INIT) out<<"init ";
71 if(p->is_runnable()) {
72 if(p->is_binary()) out<<"built-in ";
73 out<<"function\n";
74 }
75 else {
76 if(p->is_const()) out<<"constant"; else out<<"variable";
77 if(p->is_reference()) out<<" reference";
78 if(p->is_dbref()) out<<" database reference";
79 out<<"\n";
80 }
81 out<<"Usage: ";
82 if(p->is_runnable()) out<<*(func_t*)p; else out<<"\\"<<p->name;
83 out<<"\n";
84 if(p->is_runnable()) {
85 mstring desc=Desc(*p);
86 if(desc.nempty()) { out<<"\n"; out<<desc; }
87 }
88 }
89 else {
90 out<<"(not found)\n";
91 }
92 }
93 else {
94 mstring line;
95 int n=0;
96 for(tokmap* tok=env.toks;tok;tok=tok->up) {
97 line.convert("%-12s %s %d\n","MODE","NAME LEVEL",n);
98 out<<line;
99 for(tokmap::iterator p=tok->begin();p!=tok->end();p++) {
100 line.convert("%s %s\n",Mode(*(*p)).c_str(),(*p)->name.c_str());
101 out<<line;
102 }
103 n--;
104 }
105 }
106}
107
108TOK_IMPL(src) {
109 const tok_t* p=env.toks->get(env[0]);
110 if(!p) out<<env[0]<<" (not found)\n";
111 else {
112 if(p->is_runnable()) {
113 const func_t* ftok=(func_t*)p;
114 out<<"{"<<ftok->name<<"}{"<<ftok->arg<<"}";
115 if(p->is_binary()) out<<" (built-in function)\n";
116 else out<<"{"<<ftok->x.expr<<"}\n";
117 }
118 else out<<env[0]<<" (not a function)\n";
119 }
120}
121
122void envAddHelp(tokmap& T)
123{
124 TOK_ADD(help,"",tok_t::PRIVATE,
125 "%Help message\n"
126 "%Syntax: \\help\n"
127 );
128 TOK_ADD(ops,"?",tok_t::PRIVATE,
129 "%Help on operators\n"
130 "%Syntax: \\ops<opname>\n"
131 );
132 TOK_ADD(tok,"?",tok_t::PRIVATE,
133 "%Help on tokens\n"
134 "%Syntax: \\tok<tokname>\n"
135 );
136 TOK_ADD(src,"x",tok_t::SYSONLY,
137 "%Source of token\n"
138 "%Syntax: \\src{tokname}\n"
139 );
140}
diff --git a/src/lang/lang.cc b/src/lang/lang.cc
new file mode 100644
index 0000000..28e56f3
--- /dev/null
+++ b/src/lang/lang.cc
@@ -0,0 +1,39 @@
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
22void envAddOps(opmap& M,tokmap& T);
23void envAddBasic(tokmap& T);
24void envAddFunc(tokmap& T);
25void envAddFlow(tokmap& T);
26void envAddDB(tokmap& T);
27void envAddLatex(tokmap& T);
28void envAddHelp(tokmap& T);
29
30Lang::Lang() : running(1)
31{
32 envAddOps(ops,toks);
33 envAddBasic(toks);
34 envAddFunc(toks);
35 envAddFlow(toks);
36 envAddDB(toks);
37 envAddLatex(toks);
38 envAddHelp(toks);
39}
diff --git a/src/lang/latex.cc b/src/lang/latex.cc
new file mode 100644
index 0000000..d423230
--- /dev/null
+++ b/src/lang/latex.cc
@@ -0,0 +1,152 @@
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>
21using namespace std;
22
23static opmap op_latex;
24
25latex_env::latex_env(Env& p) : Env(p) { ops=&op_latex; }
26
27//////////////////////////////////////////////////////
28
29TOK_IMPL(it) {
30 mstring s; env.parg(s,-1);
31 if(s.nempty()) { out<<"<i>"; out<<s.right_first_not_of(cset_ws); out<<"</i>"; }
32 else { out<<"<i>"; env.epilog=sref("</i>")+env.epilog; }
33}
34
35TOK_IMPL(em) {
36 mstring s; env.parg(s,-1);
37 if(s.nempty()) { out<<"<em>"; out<<s.right_first_not_of(cset_ws); out<<"</em>"; }
38 else { out<<"<em>"; env.epilog=sref("</em>")+env.epilog; }
39}
40
41TOK_IMPL(bf) {
42 mstring s; env.parg(s,-1);
43 if(s.nempty()) { out<<"<b>"; out<<s.right_first_not_of(cset_ws); out<<"</b>"; }
44 else { out<<"<b>"; env.epilog=sref("</b>")+env.epilog; }
45}
46
47TOK_IMPL(mathbf) {
48 mstring s; env.parg(s,-1);
49 if(s.nempty()) { out<<"<b>"; out<<s.right_first_not_of(cset_ws); out<<"</b>"; }
50 else { out<<"<b>"; env.epilog=sref("</b>")+env.epilog; }
51}
52
53TOK_IMPL(rm) {
54 mstring s; env.parg(s,-1);
55 if(s.nempty()) { out<<s.right_first_not_of(cset_ws); }
56}
57
58//////////////////////////////////////////////////////////
59
60TOK_IMPL(setpara) {
61 env.para=env[0];
62}
63
64TOK_IMPL(epilog) {
65 env.epilog=env[0]+env.epilog;
66}
67
68//////////////////////////////////////////////////////////
69
70TOK_IMPL(equation) {
71 out<<env.para;
72 latex_env(env).expr(out,env.arg(0)->expr);
73 out<<env.para;
74}
75
76//////////////////////////////////////////////////////////
77
78void envAddLatex(tokmap& T)
79{
80 static int init; if(!init) { init=1; opAddLatex(op_latex); }
81
82 //////////////////////////////////////////////////////////
83
84 TOK_ADD(it,"?",tok_t::PUBLIC,
85 "%Set Italics mode\n"
86 "%Syntax: \\it<body>\n"
87 "%\n"
88 "%Note that the body is optional, just like in latex.\n"
89 );
90 TOK_ADD(em,"?",tok_t::PUBLIC,
91 "%Set emph mode\n"
92 "%Syntax: \\em<body>\n"
93 "%\n"
94 "%Note that the body is optional, just like in latex.\n"
95 );
96 TOK_ADD(bf,"?",tok_t::PUBLIC,
97 "%Set Boldface mode\n"
98 "%Syntax: \\bf<body>\n"
99 "%\n"
100 "%Note that the body is optional, just like in latex.\n"
101 );
102 TOK_ADD(mathbf,"?",tok_t::PUBLIC,
103 "%Set Math boldface mode\n"
104 "%Syntax: \\mathbf<body>\n"
105 "%\n"
106 "%Note that the body is optional, just like in latex.\n"
107 );
108 TOK_ADD(rm,"?",tok_t::PUBLIC,
109 "%Set rm face mode\n"
110 "%Syntax: \\rm<body>\n"
111 "%\n"
112 "%Note that the body is optional, just like in latex.\n"
113 );
114
115 //////////////////////////////////////////////////////////
116
117 TOK_ADD(setpara,"x",tok_t::PUBLIC,
118 "%Set paragraph string\n"
119 "%Syntax: \\setpara{string}\n"
120 "%\n"
121 "%Whenever a series of newlines is encountered, it is\n"
122 "%replaced by this string. The default is a newline.\n"
123 "%\n"
124 "%Operates on stack level (may be nested)\n"
125 );
126 TOK_ADD(epilog,"x",tok_t::PUBLIC,
127 "%Add string to the epilog\n"
128 "%Syntax: \\epilog{string}\n"
129 "%\n"
130 "%The epilog string is output when the current stack frame\n"
131 "%goes out of scope. For functions like \\bf, which needs\n"
132 "%an end tag to be inserted at the right place.\n"
133 "%\n"
134 "%Operates on stack level (may be nested)\n"
135 );
136
137 //////////////////////////////////////////////////////////
138
139 TOK_ADD(equation,"r",tok_t::PUBLIC,
140 "%Evaluate equation (LaTeX)\n"
141 "%Syntax: \\equation{body}\n"
142 );
143
144 //////////////////////////////////////////////////////////
145
146 Const(T,sref("t"),sref("\t"),tok_t::PUBLIC);
147 Const(T,sref("n"),sref("\n"),tok_t::PUBLIC);
148 Const(T,sref("f"),sref("\f"),tok_t::PUBLIC);
149
150 //////////////////////////////////////////////////////////
151}
152
diff --git a/src/lang/ops.cc b/src/lang/ops.cc
new file mode 100644
index 0000000..7916d96
--- /dev/null
+++ b/src/lang/ops.cc
@@ -0,0 +1,150 @@
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
22//////////////////////////////////////////////////////
23
24struct xop_t : public op_t {
25 typedef void (*xop1_t)(mstream&,Env&,const sref&);
26 typedef void (*xop2_t)(mstream&,Env&,const sref&,const sref&);
27 xop_t(const mstring& s,int p1,int p2,int l,int r,
28 xop1_t op1,xop2_t op2,const mstring& d):
29 op_t(s,p1,p2,l,r,(op1_t)op1,(op2_t)op2,d) {}
30};
31
32//////////////////////////////////////////////////////
33
34#define X1_IMPL(name) \
35static void impl_##name(mstream& out,Env& env,const sref& a) throw(merror_t)
36#define X2_IMPL(name) \
37static void impl_##name(mstream& out,Env& env,const sref& L,const sref& R) throw(merror_t)
38
39#define X1_ADDN(p,l,r,f,n,d) M.add(new xop_t(#n,p,0,l,r,impl_##f,0,d))
40#define X2_ADDN(p,l,r,f,n,d) M.add(new xop_t(#n,0,p,l,r,0,impl_##f,d))
41
42//////////////////////////////////////////////////////
43
44X1_IMPL(exist) {
45 mstring name;
46 swrite sw(name);
47 env.eval(sw,a,0);
48 out<<env.gloc_find(name);
49}
50
51X2_IMPL(new) {
52 mstring name;
53 swrite sw(name);
54 env.eval(sw,L,0);
55 env.aloc(name,R);
56}
57
58X2_IMPL(assign) {
59 mstring name;
60 swrite sw(name);
61 env.eval(sw,L,0);
62 env.sloc(name,R);
63}
64
65X2_IMPL(incr) {
66 mstring name;
67 swrite sw(name);
68 env.eval(sw,L,0);
69 env.sloc(name,ftoa(atof(env.gloc(name))+atof(R)));
70}
71
72X2_IMPL(decr) {
73 mstring name;
74 swrite sw(name);
75 env.eval(sw,L,0);
76 env.sloc(name,ftoa(atof(env.gloc(name))-atof(R)));
77}
78
79//////////////////////////////////////////////////////
80
81X2_IMPL(new_rec) {
82 if(*L=='@') env.gloc_adb(L.popf(),R);
83 else {
84 rpile list; env.gloc_list(list,R);
85 int n=*R=='@'?R.size()-1:R.size();
86 for(int i=0;i<list.size();i++) env.aloc(L+list[i].right(n),env.gloc(list[i]));
87 }
88}
89
90X2_IMPL(copy_rec) {
91 rpile list; env.gloc_list(list,L);
92 int n=*L=='@'?L.size()-1:L.size();
93 for(int i=0;i<list.size();i++) env.sloc(R+list[i].right(n),env.gloc(list[i]));
94}
95
96//////////////////////////////////////////////////////
97
98void envAddOps(opmap& M,tokmap& T)
99{
100 opAddHEXP(M);
101 X1_ADDN(40,0,1,exist,?,
102 "True if a exist\n"
103 "Syntax: ? (varname)\n"
104 "Returns: (int)\n"
105 );
106 X2_ADDN(4,1,2,new,:=,
107 "New assignment\n"
108 "Syntax: (varname) := (expr)\n"
109 "Returns: nothing\n"
110 );
111 X2_ADDN(4,1,2,assign,=,
112 "Assignment to existing variable\n"
113 "Syntax: (varname) = (expr)\n"
114 "Returns: nothing\n"
115 );
116 X2_ADDN(4,1,2,incr,+=,
117 "Increment operator\n"
118 "Syntax: (varname) += (real)\n"
119 "Returns: nothing\n"
120 );
121 X2_ADDN(4,1,2,decr,-=,
122 "Decrement operator\n"
123 "Syntax: (varname) -= (real)\n"
124 "Returns: nothing\n"
125 );
126
127 X2_ADDN(4,1,1,new_rec,<-,
128 "New local record\n"
129 "Syntax: (partial name) <- (partial name)\n"
130 "Returns: nothing\n"
131 "\n"
132 "If the partial name @ is given as the left argument,\n"
133 "a new record is created or silently updated, depending\n"
134 "on whether it is unique or not according to the default order.\n"
135 "The index table must however exist in the right argument.\n"
136 "\n"
137 "The default order may only be set at creation or when saving\n"
138 "a database to disk.\n"
139 );
140 X2_ADDN(4,1,1,copy_rec,->,
141 "Copy local record\n"
142 "Syntax: (partial name) -> (partial name)\n"
143 "Returns: nothing\n"
144 "\n"
145 "The partial name @ for data base records works in both arguments.\n"
146 "\n"
147 "Generates an error if any of the expanded names of the right\n"
148 "partial argument does not exist.\n"
149 );
150}
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}
diff --git a/src/lang/test.cc b/src/lang/test.cc
new file mode 100644
index 0000000..e59a719
--- /dev/null
+++ b/src/lang/test.cc
@@ -0,0 +1,45 @@
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 fp_stream mout(stdout),merr(stderr);
23
24main(int argc,char* argv[]) try
25{
26 Lang lang;
27 req_env req(lang,lang.toks);
28 Env env(lang.ops,req,XSYSTEM);
29 mstring line;
30 while(!feof(stdin)) {
31 merr<<"lang>";
32 getline(line,stdin);
33 try {
34 env.parse(mout,line);
35 mout<<"\n";
36 }
37 catch(const merror_t& e) {
38 merr<<e.desc<<"\n";
39 }
40 }
41 return 0;
42}
43catch(const merror_t& e) {
44 merr<<"ERROR: ["<<e.desc<<"]\n";
45}
diff --git a/src/lang/token.cc b/src/lang/token.cc
new file mode 100644
index 0000000..5bee9ca
--- /dev/null
+++ b/src/lang/token.cc
@@ -0,0 +1,268 @@
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/token.h>
21#include <algorithm>
22using namespace std;
23
24///////////////////////////////////////////////////////////////////
25
26const unsigned tok_t::CONSTANT=1;
27const unsigned tok_t::RESTRICT=2;
28const unsigned tok_t::NORMAL=4;
29const unsigned tok_t::SYSONLY=8;
30const unsigned tok_t::INIT=16;
31const unsigned tok_t::RUNNABLE=32;
32const unsigned tok_t::FUNCTION=64;
33const unsigned tok_t::BINARY=128;
34const unsigned tok_t::REFERENCE=256;
35const unsigned tok_t::DBREF=512;
36const unsigned tok_t::CRON=1024;
37const unsigned tok_t::PRIVATE=NORMAL|SYSONLY;
38const unsigned tok_t::PUBLIC=RESTRICT|PRIVATE;
39const unsigned tok_t::XMODES=PUBLIC|INIT;
40
41///////////////////////////////////////////////////////////////////
42
43uniheap<sizeof(tok_t)> tok_t::tree;
44void* tok_t::operator new(size_t n) { return tree.push(); }
45void tok_t::operator delete(void* p) { tree.pop(p); }
46
47uniheap<sizeof(reference_t)> reference_t::tree;
48void* reference_t::operator new(size_t n) { return tree.push(); }
49void reference_t::operator delete(void* p) { tree.pop(p); }
50
51uniheap<sizeof(dbref_t)> dbref_t::tree;
52void* dbref_t::operator new(size_t n) { return tree.push(); }
53void dbref_t::operator delete(void* p) { tree.pop(p); }
54
55uniheap<sizeof(runnable_t)> runnable_t::tree;
56void* runnable_t::operator new(size_t n) { return tree.push(); }
57void runnable_t::operator delete(void* p) { tree.pop(p); }
58
59uniheap<sizeof(func_t)> func_t::tree;
60void* func_t::operator new(size_t n) { return tree.push(); }
61void func_t::operator delete(void* p) { tree.pop(p); }
62
63uniheap<sizeof(cron_t)> cron_t::tree;
64void* cron_t::operator new(size_t n) { return tree.push(); }
65void cron_t::operator delete(void* p) { tree.pop(p); }
66
67uniheap<sizeof(binary_t)> binary_t::tree;
68void* binary_t::operator new(size_t n) { return tree.push(); }
69void binary_t::operator delete(void* p) { tree.pop(p); }
70
71///////////////////////////////////////////////////////////////////
72
73mstring uscore2dot(const char* s) {
74 mstring t=s; int p; while((p=t.find('_'))>=0) t[p]='.'; return t;
75}
76
77///////////////////////////////////////////////////////////////////
78
79tok_t::~tok_t() {}
80
81///////////////////////////////////////////////////////////////////
82
83void tokmap::clear()
84{
85 for(iterator p=list.begin();p!=list.end();p++) delete *p;
86 list.clear();
87}
88
89tok_t* tokmap::get_this(const sref& t)
90{
91 pair<iterator,iterator> p=equal_range(list.begin(),list.end(),(val_t)0,comp_t(t));
92 return p.first!=p.second?*p.first:0;
93}
94
95const tok_t* tokmap::get_this(const sref& t) const
96{
97 pair<const_iterator,const_iterator> p=
98 equal_range(list.begin(),list.end(),(val_t)0,comp_t(t));
99 return p.first!=p.second?*p.first:0;
100}
101
102const tok_t* tokmap::get(const sref& t) const
103{
104 const tok_t* p=get_this(t); return p?p:up?up->get(t):0;
105}
106
107tok_t* tokmap::get(const sref& t)
108{
109 tok_t* p=get_this(t); return p?p:up?up->get(t):0;
110}
111
112tok_t* tokmap::set(tok_t* tok,int replace,int force) throw(merror_t)
113{
114 pair<iterator,iterator> p=
115 equal_range(list.begin(),list.end(),(val_t)0,comp_t(tok->name));
116 if(p.first!=p.second) {
117 if(!(*p.first)->is_replaceable(*tok)) {
118 delete tok;
119 THROW("token: Could not set "<<(*p.first)->name<<" - protected");
120 return 0;
121 }
122 else if((*p.first)->is_const()&&!replace) {
123 delete tok;
124 if(force) THROW("token: Could not set "<<(*p.first)->name<<" - constant");
125 return 0;
126 }
127 else { delete *p.first; return *p.first=tok; }
128 }
129 else if(up) return up->set(tok,replace,force);
130 else {
131 mstring name=tok->name;
132 delete tok;
133 if(force) THROW("token: Could not set "<<name<<" - not found");
134 return 0;
135 }
136}
137
138tok_t* tokmap::add(tok_t* tok,int replace,int force) throw(merror_t)
139{
140 tok_t* old=get(tok->name);
141 if(old) {
142 if(!old->is_replaceable(*tok)) {
143 delete tok;
144 THROW("token: Could not add "<<old->name<<" - protected");
145 return 0;
146 }
147 else if(old->is_const()&&!replace) {
148 delete tok;
149 if(force) THROW("token: Could not add "<<old->name<<" - constant");
150 return 0;
151 }
152 }
153 pair<iterator,iterator> p=
154 equal_range(list.begin(),list.end(),(val_t)0,comp_t(tok->name));
155 if(p.first!=p.second) { delete *p.first; return *p.first=tok; }
156 else return *list.insert(p.second,tok);
157}
158
159///////////////////////////////////////////////////////////////////
160
161static void Check(const sref& s) throw(merror_t)
162{
163 if(!is_tokname(s)) THROW("token: bad name ("<<s<<")");
164}
165
166static void CheckArg(const sref& s) throw(merror_t)
167{
168 if(!is_arglist(s)) THROW("ftok: bad argspec ("<<s<<")");
169}
170
171///////////////////////////////////////////////////////////////////
172
173tok_t* Cond(tokmap& map,const sref& s,const sref& b,unsigned m) throw(merror_t)
174{
175 Check(s);
176 return map.add(new tok_t(s,b,m),0,0);
177}
178
179tok_t* Global(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl) throw(merror_t)
180{
181 Check(s);
182 return map.add(new tok_t(s,b,m),rpl,1);
183}
184
185tok_t* Const(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl) throw(merror_t)
186{
187 Check(s);
188 return map.add(new tok_t(s,b,m|tok_t::CONSTANT),rpl,1);
189}
190
191func_t* Func(tokmap& map,const sref& s,const sref& b,
192 unsigned m,const sref& a,int rpl) throw(merror_t)
193{
194 Check(s);
195 CheckArg(a);
196 return (func_t*)map.add(new func_t(s,b,m,a),rpl,1);
197}
198
199cron_t* Cron(tokmap& map,const sref& s,const sref& b,
200 unsigned m,int i,int rpl) throw(merror_t)
201{
202 Check(s);
203 return (cron_t*)map.add(new cron_t(s,b,m,i),rpl,1);
204}
205
206binary_t* Binary(tokmap& map,const sref& s,const sref& b,
207 unsigned m,const sref& a,s_func_t f,int rpl) throw(merror_t)
208{
209 Check(s);
210 CheckArg(a);
211 return (binary_t*)map.add(new binary_t(s,b,m,a,f),rpl,1);
212}
213
214reference_t* Reference(tokmap& map,const sref& s,
215 const sref& b,unsigned m,int rpl) throw(merror_t)
216{
217 Check(s);
218 return (reference_t*)map.add(new reference_t(s,b,m),rpl,1);
219}
220
221dbref_t* DBRef(tokmap& map,const sref& s,
222 const sref& b,unsigned m,int rpl) throw(merror_t)
223{
224 Check(s);
225 return (dbref_t*)map.add(new dbref_t(s,b,m),rpl,1);
226}
227
228void ClearToken(tokmap& map,const sref& s) throw(merror_t)
229{
230 if(s.nempty()) {
231 tokmap::iterator p=map.begin();
232 while(p<map.end()) {
233 if((*p)->name.left(s.size())==s) p=map.erase(p); else p++;
234 }
235 }
236}
237
238///////////////////////////////////////////////////////////////
239
240mstream& operator<<(mstream& out,const runnable_t& ftok)
241{
242 out<<"\\"<<ftok.name;
243 for(const char* p=ftok.arg.begin();p!=ftok.arg.end();p++) out.put('{').put(*p).put('}');
244 return out;
245}
246
247mstring Mode(const tok_t& tok)
248{
249 mstring m;
250 for(int i=11;i>=0;i--) { if(tok.mod&(1<<i)) m+='1'; else m+='0'; }
251 return m;
252}
253
254mstring Desc(const tok_t& tok)
255{
256 mstring m;
257 sref first,rest=tok.body;
258 while(Splitln(first,rest)) {
259 first=first.right_first_not_of(cset_ws);
260 if(*first=='%') {
261 first=first.popf().right_first_not_of(cset_ws);
262 m+=first;
263 m+='\n';
264 }
265 }
266 return m;
267}
268
diff --git a/src/lang/token.h b/src/lang/token.h
new file mode 100644
index 0000000..bf32820
--- /dev/null
+++ b/src/lang/token.h
@@ -0,0 +1,230 @@
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#ifndef TOKENH
21#define TOKENH
22
23#include <ops/express.h>
24#include <hdb/db.h>
25
26#define TOK_ARGS (mstream& out,Env& env)
27#define TOK_IMPL(func) static void impl_##func TOK_ARGS throw(merror_t)
28#define TOK_ADD(func,a,mod,help) \
29T.add(new binary_t(uscore2dot(#func),sref(help),mod,sref(a),impl_##func),0,1)
30
31mstring uscore2dot(const char* s);
32
33class Env;
34typedef void (*s_func_t)TOK_ARGS;
35
36class tok_t {
37public:
38 static const unsigned CONSTANT,RESTRICT,NORMAL,SYSONLY,INIT;
39 static const unsigned RUNNABLE,FUNCTION,BINARY;
40 static const unsigned REFERENCE,DBREF,CRON;
41 static const unsigned PRIVATE,PUBLIC,XMODES;
42
43 static inline unsigned tight(unsigned xmod) {
44 // INIT is ignored here simply because no mode can
45 // read variables in INIT mode... defaults to public.
46 return (xmod&SYSONLY)?SYSONLY:(xmod&NORMAL)?PRIVATE:PUBLIC;
47 }
48
49 mstring name,body;
50 unsigned mod;
51
52 int is_clear(int xmod) const { return (mod&xmod)!=0; }
53 int is_replaceable(const tok_t& t) const { return (mod&t.mod)==t.mod; }
54
55 int is_const() const { return (mod&CONSTANT)!=0; }
56 int is_runnable() const { return (mod&RUNNABLE)!=0; }
57 int is_func() const { return (mod&FUNCTION)!=0; }
58 int is_cron() const { return (mod&CRON)!=0; }
59 int is_binary() const { return (mod&BINARY)!=0; }
60 int is_reference() const { return (mod&REFERENCE)!=0; }
61 int is_dbref() const { return (mod&DBREF)!=0; }
62
63 tok_t(const sref& s,const sref& b,unsigned m) : name(s),body(b),mod(m) {}
64 virtual ~tok_t();
65
66 static void* operator new(size_t n);
67 static void operator delete(void* p);
68private:
69 static uniheap<32> tree;
70};
71
72///////////////////////////////////////////////////////////////////
73
74class reference_t : public tok_t {
75public:
76 mstring data;
77 reference_t(const sref& s,const sref& b,unsigned m):
78 tok_t(s,b,m|REFERENCE|CONSTANT) {}
79
80 static void* operator new(size_t n);
81 static void operator delete(void* p);
82private:
83 static uniheap<44> tree;
84};
85
86///////////////////////////////////////////////////////////////////
87
88class dbref_t : public tok_t {
89public:
90 dbmap db;
91 dbref_t(const sref& s,const sref& b,unsigned m):
92 tok_t(s,b,m|DBREF|CONSTANT) {}
93
94 static void* operator new(size_t n);
95 static void operator delete(void* p);
96private:
97 static uniheap<96> tree;
98};
99
100///////////////////////////////////////////////////////////////////
101
102class runnable_t : public tok_t {
103public:
104 mstring arg;
105 runnable_t(const sref& s,const sref& b,unsigned m,const sref& a):
106 tok_t(s,b,m|RUNNABLE|CONSTANT),arg(a) {}
107
108 static void* operator new(size_t n);
109 static void operator delete(void* p);
110private:
111 static uniheap<44> tree;
112};
113
114///////////////////////////////////////////////////////////////////
115
116class func_t : public runnable_t {
117public:
118 expr_t x;
119 func_t(const sref& s,const sref& b,unsigned m,const sref& a):
120 runnable_t(s,b,m|FUNCTION,a) { x=body; }
121
122 static void* operator new(size_t n);
123 static void operator delete(void* p);
124private:
125 static uniheap<80> tree;
126};
127
128///////////////////////////////////////////////////////////////////
129
130class cron_t : public func_t {
131public:
132 int last,interval;
133 cron_t(const sref& s,const sref& b,unsigned m,int i):
134 func_t(s,b,m|CRON,ms_empty),last(0),interval(i) {}
135
136 static void* operator new(size_t n);
137 static void operator delete(void* p);
138private:
139 static uniheap<88> tree;
140};
141
142///////////////////////////////////////////////////////////////////
143
144class binary_t : public runnable_t {
145public:
146 s_func_t func;
147 binary_t(const sref& s,const sref& b,unsigned m,const sref& a,s_func_t f):
148 runnable_t(s,b,m|BINARY,a),func(f) {}
149
150 static void* operator new(size_t n);
151 static void operator delete(void* p);
152private:
153 static uniheap<48> tree;
154};
155
156///////////////////////////////////////////////////////////////////
157
158mstream& operator<<(mstream&,const runnable_t& ftok);
159mstring Mode(const tok_t& tok);
160mstring Desc(const tok_t& tok);
161
162///////////////////////////////////////////////////////////////////
163
164struct tokmap {
165 typedef tok_t* val_t;
166 typedef mrvec<val_t> map_t;
167 typedef map_t::iterator iterator;
168 typedef map_t::const_iterator const_iterator;
169
170 struct comp_t {
171 sref key;
172 comp_t(const sref& skey) : key(skey) {}
173 bool operator()(val_t a,val_t b) const {
174 return (a?((const sref&)a->name):key)<(b?((const sref&)b->name):key);
175 }
176 };
177
178 tokmap* up;
179 map_t list;
180
181 tokmap() : up(0),list() {}
182 tokmap(tokmap& p) : up(&p),list() {}
183 ~tokmap() { clear(); }
184
185 iterator begin() { return list.begin(); }
186 const_iterator begin() const { return list.begin(); }
187
188 iterator end() { return list.end(); }
189 const_iterator end() const { return list.end(); }
190
191 void clear();
192 iterator erase(iterator p) { delete *p; return list.erase(p); }
193
194 tok_t* get_this(const sref& t);
195 const tok_t* get_this(const sref& t) const;
196 const tok_t* get(const sref& t) const;
197 tok_t* get(const sref& t);
198
199 tok_t* set(tok_t* tok,int replace,int force) throw(merror_t);
200 tok_t* add(tok_t* tok,int replace,int force) throw(merror_t);
201};
202
203///////////////////////////////////////////////////////////////////
204
205int is_tokname(const sref& s);
206int is_arglist(const sref& s);
207
208tok_t* Cond(tokmap& map,const sref& s,const sref& b,unsigned m) throw(merror_t);
209tok_t* Global(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t);
210tok_t* Const(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t);
211func_t* Func(tokmap& map,const sref& s,const sref& b,
212 unsigned m,const sref& a,int rpl=0) throw(merror_t);
213cron_t* Cron(tokmap& map,const sref& s,const sref& b,
214 unsigned m,int i,int rpl=0) throw(merror_t);
215binary_t* Binary(tokmap& map,const sref& s,const sref& b,
216 unsigned m,const sref& a,s_func_t f,int rpl=0) throw(merror_t);
217reference_t* Reference(tokmap& map,const sref& s,const sref& b,
218 unsigned m,int rpl=0) throw(merror_t);
219dbref_t* DBRef(tokmap& map,const sref& s,
220 const sref& b,unsigned m,int rpl=0) throw(merror_t);
221void ClearToken(tokmap& map,const sref& s) throw(merror_t);
222
223///////////////////////////////////////////////////////////////////
224
225const int XPUBLIC=tok_t::RESTRICT;
226const int XPRIVATE=XPUBLIC|tok_t::NORMAL;
227const int XSYSTEM=XPRIVATE|tok_t::SYSONLY;
228const int XINIT=tok_t::INIT;
229
230#endif