diff options
Diffstat (limited to 'src/lang')
| -rw-r--r-- | src/lang/basic.cc | 315 | ||||
| -rw-r--r-- | src/lang/db.cc | 237 | ||||
| -rw-r--r-- | src/lang/env.cc | 308 | ||||
| -rw-r--r-- | src/lang/env.h | 156 | ||||
| -rw-r--r-- | src/lang/flow.cc | 198 | ||||
| -rw-r--r-- | src/lang/func.cc | 249 | ||||
| -rw-r--r-- | src/lang/help.cc | 140 | ||||
| -rw-r--r-- | src/lang/lang.cc | 39 | ||||
| -rw-r--r-- | src/lang/latex.cc | 152 | ||||
| -rw-r--r-- | src/lang/ops.cc | 150 | ||||
| -rw-r--r-- | src/lang/parse.cc | 453 | ||||
| -rw-r--r-- | src/lang/test.cc | 45 | ||||
| -rw-r--r-- | src/lang/token.cc | 268 | ||||
| -rw-r--r-- | src/lang/token.h | 230 |
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 | |||
| 24 | TOK_IMPL(asis) { out<<env.arg(0)->expr; } | ||
| 25 | TOK_IMPL(mute) {} | ||
| 26 | TOK_IMPL(comment) {} | ||
| 27 | |||
| 28 | /////////////////////////////////////////////////// | ||
| 29 | |||
| 30 | TOK_IMPL(cond) { | ||
| 31 | Cond(*env.toks,env[0],env[1],tok_t::PUBLIC); | ||
| 32 | } | ||
| 33 | TOK_IMPL(global) { | ||
| 34 | Global(*env.toks,env[0],env[1],tok_t::PUBLIC); | ||
| 35 | } | ||
| 36 | TOK_IMPL(const) { | ||
| 37 | Const(*env.toks,env[0],env[1],tok_t::PUBLIC); | ||
| 38 | } | ||
| 39 | TOK_IMPL(reconst) { | ||
| 40 | Const(*env.toks,env[0],env[1],tok_t::PUBLIC,1); | ||
| 41 | } | ||
| 42 | |||
| 43 | /////////////////////////////////////////////////// | ||
| 44 | |||
| 45 | TOK_IMPL(type) { | ||
| 46 | tok_t* p=env.toks->get(env[0]); | ||
| 47 | if(env.is_clear(p)) out<<p->mod; | ||
| 48 | } | ||
| 49 | TOK_IMPL(desc) { | ||
| 50 | tok_t* p=env.toks->get(env[0]); | ||
| 51 | if(env.is_clear(p)) out<<Desc(*p); | ||
| 52 | } | ||
| 53 | TOK_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 | } | ||
| 57 | TOK_IMPL(clear) { | ||
| 58 | ClearToken(*env.toks,env[0]); | ||
| 59 | } | ||
| 60 | |||
| 61 | ////////////////////////////////////////////////////// | ||
| 62 | |||
| 63 | TOK_IMPL(exist) { out<<env.glob_find(env[0]); } | ||
| 64 | TOK_IMPL(nexist) { out<<!env.glob_find(env[0]); } | ||
| 65 | TOK_IMPL(empty) { out<<env.glob(env[0]).empty(); } | ||
| 66 | TOK_IMPL(nempty) { out<<env.glob(env[0]).nempty(); } | ||
| 67 | |||
| 68 | /////////////////////////////////////////////////// | ||
| 69 | |||
| 70 | TOK_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 | |||
| 77 | TOK_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 | |||
| 86 | TOK_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 | |||
| 93 | TOK_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 | |||
| 100 | TOK_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 | |||
| 108 | TOK_IMPL(arg) { if(env.up) env.up->parg(out,atoi(env[0])-1); } | ||
| 109 | TOK_IMPL(args) { if(env.up) out<<env.up->args; else out<<0; } | ||
| 110 | |||
| 111 | /////////////////////////////////////////////////// | ||
| 112 | |||
| 113 | TOK_IMPL(random) { | ||
| 114 | out<<Random(atoi(env[0])); | ||
| 115 | } | ||
| 116 | |||
| 117 | ////////////////////////////////////////////////////// | ||
| 118 | |||
| 119 | void 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 | |||
| 22 | static dbmap* GetDB(Env& env) throw(merror_t) { | ||
| 23 | if(!env.db) THROW("htc: no open database"); | ||
| 24 | return env.db; | ||
| 25 | } | ||
| 26 | |||
| 27 | static 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 | |||
| 35 | TOK_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 | |||
| 43 | TOK_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 | |||
| 52 | TOK_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 | |||
| 63 | TOK_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 | |||
| 75 | TOK_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 | |||
| 85 | TOK_IMPL(first_rec) { env.dbat=0; } | ||
| 86 | TOK_IMPL(prev_rec) { if(env.dbat>0) env.dbat--; } | ||
| 87 | TOK_IMPL(next_rec) { if(env.dbat<GetDB(env)->size()) env.dbat++; } | ||
| 88 | TOK_IMPL(exist_rec) { out<<bool(env.dbat>=0&&env.dbat<GetDB(env)->size()); } | ||
| 89 | |||
| 90 | TOK_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 | |||
| 97 | TOK_IMPL(lastkey) { | ||
| 98 | dbmap* db=GetDB(env); | ||
| 99 | if(!db->empty()) out<<db->at(db->size()-1,db->Index()); | ||
| 100 | } | ||
| 101 | |||
| 102 | TOK_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 | |||
| 107 | TOK_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 | |||
| 112 | TOK_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 | |||
| 123 | TOK_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 | |||
| 136 | void 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 | |||
| 23 | static unsigned long seed=RandomInit(time(0)); | ||
| 24 | |||
| 25 | mstring 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 | |||
| 38 | void 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 | |||
| 45 | void 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 | |||
| 57 | void Env::parg(mstring& tmp,int n) throw(merror_t) | ||
| 58 | { | ||
| 59 | swrite sw(tmp); | ||
| 60 | parg(sw,n); | ||
| 61 | } | ||
| 62 | |||
| 63 | mstring Env::parg(int n) throw(merror_t) | ||
| 64 | { | ||
| 65 | mswrite tmp; parg(tmp,n); return tmp.str(); | ||
| 66 | } | ||
| 67 | |||
| 68 | ////////////////////////////////////////////////////// | ||
| 69 | |||
| 70 | int 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 | |||
| 83 | expr_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 | |||
| 96 | const 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 | |||
| 111 | const 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 | |||
| 119 | int 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 | |||
| 125 | int 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 | |||
| 131 | void 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 | |||
| 152 | void 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 | |||
| 178 | const 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 | |||
| 184 | const 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 | |||
| 198 | const 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 | |||
| 213 | int 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 | |||
| 223 | int 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 | |||
| 233 | void 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 | |||
| 263 | void 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 | |||
| 289 | const mstring sback="../",sident="./",sdots="..",sslash="//"; | ||
| 290 | |||
| 291 | mstring 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 | |||
| 25 | mstring Random(int n); | ||
| 26 | |||
| 27 | const int OP_PARSE=8; | ||
| 28 | const mstring para_def="\n"; | ||
| 29 | |||
| 30 | //////////////////////////////////////////////////////////////////// | ||
| 31 | |||
| 32 | struct Lang { | ||
| 33 | volatile int running; | ||
| 34 | opmap ops; | ||
| 35 | tokmap toks; | ||
| 36 | Lang(); | ||
| 37 | virtual ~Lang() {} | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct 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 | |||
| 55 | struct 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 | |||
| 147 | struct 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> | ||
| 22 | using namespace std; | ||
| 23 | |||
| 24 | TOK_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 | |||
| 31 | TOK_IMPL(if) { | ||
| 32 | if(atob(env[0])) env.parg(out,1); else env.parg(out,2); | ||
| 33 | } | ||
| 34 | |||
| 35 | TOK_IMPL(nif) { | ||
| 36 | if(!atob(env[0])) env.parg(out,1); else env.parg(out,2); | ||
| 37 | } | ||
| 38 | |||
| 39 | TOK_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 | |||
| 48 | static 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 | |||
| 54 | TOK_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 | |||
| 63 | TOK_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 | |||
| 71 | TOK_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 | |||
| 94 | TOK_IMPL(on_arg) { | ||
| 95 | if(env.up&&env.up->onarg(atoi(env[0])-1)) env.parg(out,1); else env.parg(out,2); | ||
| 96 | } | ||
| 97 | TOK_IMPL(on_glob) { if(env.onglob(env[0])) env.parg(out,1); else env.parg(out,2); } | ||
| 98 | TOK_IMPL(on_loc) { if(env.onloc(env[0])) env.parg(out,1); else env.parg(out,2); } | ||
| 99 | |||
| 100 | ///////////////////////////////////////////////////////////////////// | ||
| 101 | |||
| 102 | TOK_IMPL(setlang) { | ||
| 103 | env.req->lang=Language(env[0]); if(env.req->lang<0) env.req->lang=0; | ||
| 104 | } | ||
| 105 | |||
| 106 | TOK_IMPL(lang) { | ||
| 107 | env.parg(out,env.req->lang); | ||
| 108 | } | ||
| 109 | |||
| 110 | ///////////////////////////////////////////////////////////////////// | ||
| 111 | |||
| 112 | void 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 | |||
| 23 | const mstring cron_tag="cron."; | ||
| 24 | const int SSECS=4; | ||
| 25 | |||
| 26 | TOK_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 | |||
| 32 | TOK_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 | |||
| 38 | TOK_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 | |||
| 45 | TOK_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 | |||
| 54 | static 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 | |||
| 66 | TOK_IMPL(run) { | ||
| 67 | Env e(env,env.mod,env.pwd); | ||
| 68 | Run(*env.toks,out,e,env[0]); | ||
| 69 | } | ||
| 70 | TOK_IMPL(run_private) { | ||
| 71 | Env e(env,XPRIVATE,env.pwd); | ||
| 72 | Run(*env.toks,out,e,env[0]); | ||
| 73 | } | ||
| 74 | TOK_IMPL(run_public) { | ||
| 75 | Env e(env,XPUBLIC,env.pwd); | ||
| 76 | Run(*env.toks,out,e,env[0]); | ||
| 77 | } | ||
| 78 | TOK_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 | } | ||
| 87 | TOK_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 | |||
| 113 | void 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 | |||
| 22 | TOK_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 | |||
| 34 | TOK_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 | |||
| 60 | TOK_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 | |||
| 108 | TOK_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 | |||
| 122 | void 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 | |||
| 22 | void envAddOps(opmap& M,tokmap& T); | ||
| 23 | void envAddBasic(tokmap& T); | ||
| 24 | void envAddFunc(tokmap& T); | ||
| 25 | void envAddFlow(tokmap& T); | ||
| 26 | void envAddDB(tokmap& T); | ||
| 27 | void envAddLatex(tokmap& T); | ||
| 28 | void envAddHelp(tokmap& T); | ||
| 29 | |||
| 30 | Lang::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> | ||
| 21 | using namespace std; | ||
| 22 | |||
| 23 | static opmap op_latex; | ||
| 24 | |||
| 25 | latex_env::latex_env(Env& p) : Env(p) { ops=&op_latex; } | ||
| 26 | |||
| 27 | ////////////////////////////////////////////////////// | ||
| 28 | |||
| 29 | TOK_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 | |||
| 35 | TOK_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 | |||
| 41 | TOK_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 | |||
| 47 | TOK_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 | |||
| 53 | TOK_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 | |||
| 60 | TOK_IMPL(setpara) { | ||
| 61 | env.para=env[0]; | ||
| 62 | } | ||
| 63 | |||
| 64 | TOK_IMPL(epilog) { | ||
| 65 | env.epilog=env[0]+env.epilog; | ||
| 66 | } | ||
| 67 | |||
| 68 | ////////////////////////////////////////////////////////// | ||
| 69 | |||
| 70 | TOK_IMPL(equation) { | ||
| 71 | out<<env.para; | ||
| 72 | latex_env(env).expr(out,env.arg(0)->expr); | ||
| 73 | out<<env.para; | ||
| 74 | } | ||
| 75 | |||
| 76 | ////////////////////////////////////////////////////////// | ||
| 77 | |||
| 78 | void 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 | |||
| 24 | struct 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) \ | ||
| 35 | static void impl_##name(mstream& out,Env& env,const sref& a) throw(merror_t) | ||
| 36 | #define X2_IMPL(name) \ | ||
| 37 | static 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 | |||
| 44 | X1_IMPL(exist) { | ||
| 45 | mstring name; | ||
| 46 | swrite sw(name); | ||
| 47 | env.eval(sw,a,0); | ||
| 48 | out<<env.gloc_find(name); | ||
| 49 | } | ||
| 50 | |||
| 51 | X2_IMPL(new) { | ||
| 52 | mstring name; | ||
| 53 | swrite sw(name); | ||
| 54 | env.eval(sw,L,0); | ||
| 55 | env.aloc(name,R); | ||
| 56 | } | ||
| 57 | |||
| 58 | X2_IMPL(assign) { | ||
| 59 | mstring name; | ||
| 60 | swrite sw(name); | ||
| 61 | env.eval(sw,L,0); | ||
| 62 | env.sloc(name,R); | ||
| 63 | } | ||
| 64 | |||
| 65 | X2_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 | |||
| 72 | X2_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 | |||
| 81 | X2_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 | |||
| 90 | X2_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 | |||
| 98 | void 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 | |||
| 22 | static const charset escaped("%$[]{};"); | ||
| 23 | static const charset prename("\"'`^~*,_=!\\"); | ||
| 24 | static const charset spec("+-*/%<=>^_!~&|,?:;"); | ||
| 25 | static const charset spec_latex("+-*/<=>^_;"); | ||
| 26 | static const charset delim("\"{}()\\"); | ||
| 27 | static const charset delim_latex("{}()[]\\"); | ||
| 28 | static const charset specws=spec|delim|cset_ws; | ||
| 29 | static const charset specws_latex=spec_latex|delim_latex|cset_ws; | ||
| 30 | static const charset tokname=cset_alphareal; | ||
| 31 | static const charset pretok=tokname|prename; | ||
| 32 | |||
| 33 | const mstring begin_tag="begin",end_left="\\end{",end_right="}"; | ||
| 34 | |||
| 35 | int is_tokname(const sref& s) { | ||
| 36 | return s.back()!='.'&&pretok.test(s.front())&&s.right(1).find_not_of(tokname)<0; | ||
| 37 | } | ||
| 38 | |||
| 39 | int 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 | |||
| 52 | static 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 | |||
| 61 | static 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 | |||
| 75 | static 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 | |||
| 81 | static 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 | |||
| 87 | static 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 | |||
| 93 | static 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 | |||
| 99 | static 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 | |||
| 107 | static 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 | |||
| 116 | static 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 | |||
| 125 | static 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 | |||
| 160 | int 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 | |||
| 178 | void 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 | |||
| 185 | void 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 | |||
| 194 | void 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 | |||
| 203 | static 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 | |||
| 208 | static 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 | |||
| 215 | static 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 | |||
| 220 | static 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 | |||
| 226 | static 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 | |||
| 260 | void 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 | |||
| 334 | void 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 | |||
| 342 | void 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 | |||
| 348 | void 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 | |||
| 354 | void 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 | |||
| 364 | int 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 | |||
| 391 | void 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 | |||
| 418 | int 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 | |||
| 441 | void 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 | |||
| 22 | static fp_stream mout(stdout),merr(stderr); | ||
| 23 | |||
| 24 | main(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 | } | ||
| 43 | catch(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> | ||
| 22 | using namespace std; | ||
| 23 | |||
| 24 | /////////////////////////////////////////////////////////////////// | ||
| 25 | |||
| 26 | const unsigned tok_t::CONSTANT=1; | ||
| 27 | const unsigned tok_t::RESTRICT=2; | ||
| 28 | const unsigned tok_t::NORMAL=4; | ||
| 29 | const unsigned tok_t::SYSONLY=8; | ||
| 30 | const unsigned tok_t::INIT=16; | ||
| 31 | const unsigned tok_t::RUNNABLE=32; | ||
| 32 | const unsigned tok_t::FUNCTION=64; | ||
| 33 | const unsigned tok_t::BINARY=128; | ||
| 34 | const unsigned tok_t::REFERENCE=256; | ||
| 35 | const unsigned tok_t::DBREF=512; | ||
| 36 | const unsigned tok_t::CRON=1024; | ||
| 37 | const unsigned tok_t::PRIVATE=NORMAL|SYSONLY; | ||
| 38 | const unsigned tok_t::PUBLIC=RESTRICT|PRIVATE; | ||
| 39 | const unsigned tok_t::XMODES=PUBLIC|INIT; | ||
| 40 | |||
| 41 | /////////////////////////////////////////////////////////////////// | ||
| 42 | |||
| 43 | uniheap<sizeof(tok_t)> tok_t::tree; | ||
| 44 | void* tok_t::operator new(size_t n) { return tree.push(); } | ||
| 45 | void tok_t::operator delete(void* p) { tree.pop(p); } | ||
| 46 | |||
| 47 | uniheap<sizeof(reference_t)> reference_t::tree; | ||
| 48 | void* reference_t::operator new(size_t n) { return tree.push(); } | ||
| 49 | void reference_t::operator delete(void* p) { tree.pop(p); } | ||
| 50 | |||
| 51 | uniheap<sizeof(dbref_t)> dbref_t::tree; | ||
| 52 | void* dbref_t::operator new(size_t n) { return tree.push(); } | ||
| 53 | void dbref_t::operator delete(void* p) { tree.pop(p); } | ||
| 54 | |||
| 55 | uniheap<sizeof(runnable_t)> runnable_t::tree; | ||
| 56 | void* runnable_t::operator new(size_t n) { return tree.push(); } | ||
| 57 | void runnable_t::operator delete(void* p) { tree.pop(p); } | ||
| 58 | |||
| 59 | uniheap<sizeof(func_t)> func_t::tree; | ||
| 60 | void* func_t::operator new(size_t n) { return tree.push(); } | ||
| 61 | void func_t::operator delete(void* p) { tree.pop(p); } | ||
| 62 | |||
| 63 | uniheap<sizeof(cron_t)> cron_t::tree; | ||
| 64 | void* cron_t::operator new(size_t n) { return tree.push(); } | ||
| 65 | void cron_t::operator delete(void* p) { tree.pop(p); } | ||
| 66 | |||
| 67 | uniheap<sizeof(binary_t)> binary_t::tree; | ||
| 68 | void* binary_t::operator new(size_t n) { return tree.push(); } | ||
| 69 | void binary_t::operator delete(void* p) { tree.pop(p); } | ||
| 70 | |||
| 71 | /////////////////////////////////////////////////////////////////// | ||
| 72 | |||
| 73 | mstring uscore2dot(const char* s) { | ||
| 74 | mstring t=s; int p; while((p=t.find('_'))>=0) t[p]='.'; return t; | ||
| 75 | } | ||
| 76 | |||
| 77 | /////////////////////////////////////////////////////////////////// | ||
| 78 | |||
| 79 | tok_t::~tok_t() {} | ||
| 80 | |||
| 81 | /////////////////////////////////////////////////////////////////// | ||
| 82 | |||
| 83 | void tokmap::clear() | ||
| 84 | { | ||
| 85 | for(iterator p=list.begin();p!=list.end();p++) delete *p; | ||
| 86 | list.clear(); | ||
| 87 | } | ||
| 88 | |||
| 89 | tok_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 | |||
| 95 | const 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 | |||
| 102 | const 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 | |||
| 107 | tok_t* tokmap::get(const sref& t) | ||
| 108 | { | ||
| 109 | tok_t* p=get_this(t); return p?p:up?up->get(t):0; | ||
| 110 | } | ||
| 111 | |||
| 112 | tok_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 | |||
| 138 | tok_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 | |||
| 161 | static void Check(const sref& s) throw(merror_t) | ||
| 162 | { | ||
| 163 | if(!is_tokname(s)) THROW("token: bad name ("<<s<<")"); | ||
| 164 | } | ||
| 165 | |||
| 166 | static void CheckArg(const sref& s) throw(merror_t) | ||
| 167 | { | ||
| 168 | if(!is_arglist(s)) THROW("ftok: bad argspec ("<<s<<")"); | ||
| 169 | } | ||
| 170 | |||
| 171 | /////////////////////////////////////////////////////////////////// | ||
| 172 | |||
| 173 | tok_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 | |||
| 179 | tok_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 | |||
| 185 | tok_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 | |||
| 191 | func_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 | |||
| 199 | cron_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 | |||
| 206 | binary_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 | |||
| 214 | reference_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 | |||
| 221 | dbref_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 | |||
| 228 | void 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 | |||
| 240 | mstream& 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 | |||
| 247 | mstring 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 | |||
| 254 | mstring 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) \ | ||
| 29 | T.add(new binary_t(uscore2dot(#func),sref(help),mod,sref(a),impl_##func),0,1) | ||
| 30 | |||
| 31 | mstring uscore2dot(const char* s); | ||
| 32 | |||
| 33 | class Env; | ||
| 34 | typedef void (*s_func_t)TOK_ARGS; | ||
| 35 | |||
| 36 | class tok_t { | ||
| 37 | public: | ||
| 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); | ||
| 68 | private: | ||
| 69 | static uniheap<32> tree; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /////////////////////////////////////////////////////////////////// | ||
| 73 | |||
| 74 | class reference_t : public tok_t { | ||
| 75 | public: | ||
| 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); | ||
| 82 | private: | ||
| 83 | static uniheap<44> tree; | ||
| 84 | }; | ||
| 85 | |||
| 86 | /////////////////////////////////////////////////////////////////// | ||
| 87 | |||
| 88 | class dbref_t : public tok_t { | ||
| 89 | public: | ||
| 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); | ||
| 96 | private: | ||
| 97 | static uniheap<96> tree; | ||
| 98 | }; | ||
| 99 | |||
| 100 | /////////////////////////////////////////////////////////////////// | ||
| 101 | |||
| 102 | class runnable_t : public tok_t { | ||
| 103 | public: | ||
| 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); | ||
| 110 | private: | ||
| 111 | static uniheap<44> tree; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /////////////////////////////////////////////////////////////////// | ||
| 115 | |||
| 116 | class func_t : public runnable_t { | ||
| 117 | public: | ||
| 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); | ||
| 124 | private: | ||
| 125 | static uniheap<80> tree; | ||
| 126 | }; | ||
| 127 | |||
| 128 | /////////////////////////////////////////////////////////////////// | ||
| 129 | |||
| 130 | class cron_t : public func_t { | ||
| 131 | public: | ||
| 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); | ||
| 138 | private: | ||
| 139 | static uniheap<88> tree; | ||
| 140 | }; | ||
| 141 | |||
| 142 | /////////////////////////////////////////////////////////////////// | ||
| 143 | |||
| 144 | class binary_t : public runnable_t { | ||
| 145 | public: | ||
| 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); | ||
| 152 | private: | ||
| 153 | static uniheap<48> tree; | ||
| 154 | }; | ||
| 155 | |||
| 156 | /////////////////////////////////////////////////////////////////// | ||
| 157 | |||
| 158 | mstream& operator<<(mstream&,const runnable_t& ftok); | ||
| 159 | mstring Mode(const tok_t& tok); | ||
| 160 | mstring Desc(const tok_t& tok); | ||
| 161 | |||
| 162 | /////////////////////////////////////////////////////////////////// | ||
| 163 | |||
| 164 | struct 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 | |||
| 205 | int is_tokname(const sref& s); | ||
| 206 | int is_arglist(const sref& s); | ||
| 207 | |||
| 208 | tok_t* Cond(tokmap& map,const sref& s,const sref& b,unsigned m) throw(merror_t); | ||
| 209 | tok_t* Global(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t); | ||
| 210 | tok_t* Const(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t); | ||
| 211 | func_t* Func(tokmap& map,const sref& s,const sref& b, | ||
| 212 | unsigned m,const sref& a,int rpl=0) throw(merror_t); | ||
| 213 | cron_t* Cron(tokmap& map,const sref& s,const sref& b, | ||
| 214 | unsigned m,int i,int rpl=0) throw(merror_t); | ||
| 215 | binary_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); | ||
| 217 | reference_t* Reference(tokmap& map,const sref& s,const sref& b, | ||
| 218 | unsigned m,int rpl=0) throw(merror_t); | ||
| 219 | dbref_t* DBRef(tokmap& map,const sref& s, | ||
| 220 | const sref& b,unsigned m,int rpl=0) throw(merror_t); | ||
| 221 | void ClearToken(tokmap& map,const sref& s) throw(merror_t); | ||
| 222 | |||
| 223 | /////////////////////////////////////////////////////////////////// | ||
| 224 | |||
| 225 | const int XPUBLIC=tok_t::RESTRICT; | ||
| 226 | const int XPRIVATE=XPUBLIC|tok_t::NORMAL; | ||
| 227 | const int XSYSTEM=XPRIVATE|tok_t::SYSONLY; | ||
| 228 | const int XINIT=tok_t::INIT; | ||
| 229 | |||
| 230 | #endif | ||
