From 5df79c53745fde5d6c3340a2979b1429cd5892c1 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Sat, 8 Oct 2011 20:30:28 +0200 Subject: Initial import of htcd system 1.0 Signed-off-by: Henrik Rydberg --- src/lang/basic.cc | 315 +++++++++++++++++++++++++++++++++++++ src/lang/db.cc | 237 ++++++++++++++++++++++++++++ src/lang/env.cc | 308 +++++++++++++++++++++++++++++++++++++ src/lang/env.h | 156 +++++++++++++++++++ src/lang/flow.cc | 198 ++++++++++++++++++++++++ src/lang/func.cc | 249 ++++++++++++++++++++++++++++++ src/lang/help.cc | 140 +++++++++++++++++ src/lang/lang.cc | 39 +++++ src/lang/latex.cc | 152 ++++++++++++++++++ src/lang/ops.cc | 150 ++++++++++++++++++ src/lang/parse.cc | 453 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lang/test.cc | 45 ++++++ src/lang/token.cc | 268 ++++++++++++++++++++++++++++++++ src/lang/token.h | 230 +++++++++++++++++++++++++++ 14 files changed, 2940 insertions(+) create mode 100644 src/lang/basic.cc create mode 100644 src/lang/db.cc create mode 100644 src/lang/env.cc create mode 100644 src/lang/env.h create mode 100644 src/lang/flow.cc create mode 100644 src/lang/func.cc create mode 100644 src/lang/help.cc create mode 100644 src/lang/lang.cc create mode 100644 src/lang/latex.cc create mode 100644 src/lang/ops.cc create mode 100644 src/lang/parse.cc create mode 100644 src/lang/test.cc create mode 100644 src/lang/token.cc create mode 100644 src/lang/token.h (limited to 'src/lang') 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +/////////////////////////////////////////////////// + +TOK_IMPL(asis) { out<expr; } +TOK_IMPL(mute) {} +TOK_IMPL(comment) {} + +/////////////////////////////////////////////////// + +TOK_IMPL(cond) { + Cond(*env.toks,env[0],env[1],tok_t::PUBLIC); +} +TOK_IMPL(global) { + Global(*env.toks,env[0],env[1],tok_t::PUBLIC); +} +TOK_IMPL(const) { + Const(*env.toks,env[0],env[1],tok_t::PUBLIC); +} +TOK_IMPL(reconst) { + Const(*env.toks,env[0],env[1],tok_t::PUBLIC,1); +} + +/////////////////////////////////////////////////// + +TOK_IMPL(type) { + tok_t* p=env.toks->get(env[0]); + if(env.is_clear(p)) out<mod; +} +TOK_IMPL(desc) { + tok_t* p=env.toks->get(env[0]); + if(env.is_clear(p)) out<get(env[0]); + if(env.is_clear(p)&&p->is_func()) ((func_t*)p)->x.trav(out,12); +} +TOK_IMPL(clear) { + ClearToken(*env.toks,env[0]); +} + +////////////////////////////////////////////////////// + +TOK_IMPL(exist) { out<parg(out,atoi(env[0])-1); } +TOK_IMPL(args) { if(env.up) out<args; else out<<0; } + +/////////////////////////////////////////////////// + +TOK_IMPL(random) { + out<\n" + "%\n" + "%Generates a list of names, beginning with the partial name,\n" + "%from the same - not nescessarily the current - local stack.\n" + "%\n" + "%Generates all local variables in the current stack if no\n" + "%partial name is given.\n" + "%\n" + "%Generates a list of tables if @ is given.\n" + ); + TOK_ADD(globlist,"?",tok_t::PUBLIC, + "%List global variables\n" + "%Syntax: \\globlist\n" + "%\n" + "%Generates a list of names, beginning with the partial name,\n" + "%from the same - not nescessarily the current - global level.\n" + "%\n" + "%Generates all global variables on the current level if no\n" + "%partial name is given.\n" + ); + TOK_ADD(lrec,"xx?",tok_t::PUBLIC, + "%Create a local record\n" + "%Syntax: \\lrec{partial name}{name list}\n" + "%\n" + "%Creates a set of local variables, all beginning with the\n" + "%partial name, and initializes each variable to init.\n" + ); + + ////////////////////////////////////////////////////// + + TOK_ADD(arg,"x",tok_t::PUBLIC, + "%Show/Evaluate argument #n.\n" + "%Syntax: \\arg{n}\n" + "%\n" + "%Same as the command \\1, \\2 etc, except this\n" + "%may be used to index the arguments e.g. using local variables.\n" + ); + TOK_ADD(args,"",tok_t::PUBLIC, + "%Number of arguments in the current function\n" + "%Syntax: \\args\n" + "%\n" + "%Note that the optional argument will count as one variable,\n" + "%and may be accesed using either \\0 or \\arg{\\args}.\n" + ); + + ////////////////////////////////////////////////////// + + TOK_ADD(random,"x",tok_t::PRIVATE, + "%Get n random digits between 0 and 9 inclusive\n" + "%Syntax: \\random{n}\n" + ); + +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static dbmap* GetDB(Env& env) throw(merror_t) { + if(!env.db) THROW("htc: no open database"); + return env.db; +} + +static int eval(Env& env,expr_t* x,int mod=OP_ALL) { + mswrite out; + if(x->status&OP_PARSE) env.parse(out,x->expr); else x->eval(out,env,mod); + return atob(out.str()); +} + +//////////////////////////////////////////////////////////////////////////// + +TOK_IMPL(create) { + dbref_t* ref=DBRef(*env.toks,env[0],ms_empty,tok_t::PUBLIC,1); + ref->db.Open("/dev/zero",1); + sref first,rest=env[1]; int n=0; + while(Split(first,rest)) ref->db.InsertTable(n++,first); + ref->db.Order(env[2],env[3]); +} + +TOK_IMPL(sort) { + dbref_t* ref=(dbref_t*)env.toks->get_this(env[0]); + if(env.is_clear(ref)&&ref->is_dbref()) { + ref->db.remap(); + ref->db.Sort(env[1],env[2]); + } + else THROW("htc: "<get(env[0]); + if(env.is_clear(ref)&&ref->is_dbref()) { + ref->db.remap(); + Env(env,ref->db).parse(out,env.arg(1)); + } + else THROW("htc: "<=0&&env.req->is_running()&&nenv.dbatsize()) { + int size=db->size(); + if(eval(nenv,x[0],OP_LOCAL)) nenv.parse(out,x[1]); + if(db->size()==size) nenv.dbat++; + } +} + +TOK_IMPL(find_rec) { + dbmap* db=GetDB(env); + Env nenv(env,*db); + nenv.dbat=db->Find(env[0]); + if(nenv.dbat>=0&&nenv.dbatsize()) nenv.parse(out,env.arg(1)); + else env.parg(out,2); +} + +//////////////////////////////////////////////////////////////////// + +TOK_IMPL(first_rec) { env.dbat=0; } +TOK_IMPL(prev_rec) { if(env.dbat>0) env.dbat--; } +TOK_IMPL(next_rec) { if(env.dbatsize()) env.dbat++; } +TOK_IMPL(exist_rec) { out<=0&&env.dbatsize()); } + +TOK_IMPL(rem_rec) { + dbmap* db=GetDB(env); + if(env.dbat>=0&&env.dbatsize()) db->Rem(env.dbat); +} + +//////////////////////////////////////////////////////////////////// + +TOK_IMPL(lastkey) { + dbmap* db=GetDB(env); + if(!db->empty()) out<at(db->size()-1,db->Index()); +} + +TOK_IMPL(prevkey) { + dbmap* db=GetDB(env); + if(env.dbat>0&&env.dbat<=db->size()) out<at(env.dbat-1,db->Index()); +} + +TOK_IMPL(nextkey) { + dbmap* db=GetDB(env); + if(env.dbat>=0&&env.dbat+1size()) out<at(env.dbat+1,db->Index()); +} + +TOK_IMPL(qprevkey) { + dbmap* db=GetDB(env); + expr_t* x=env.arg(0); + Env nenv(env,*db); + nenv.dbat=env.dbat-1; + while(nenv.dbat>=0&&env.req->is_running()&&nenv.dbatsize()) { + if(eval(nenv,x,OP_LOCAL)) { out<at(nenv.dbat,db->Index()); break; } + else nenv.dbat--; + } +} + +TOK_IMPL(qnextkey) { + dbmap* db=GetDB(env); + expr_t* x=env.arg(0); + Env nenv(env,*db); + nenv.dbat=env.dbat+1; + while(nenv.dbat>=0&&env.req->is_running()&&nenv.dbatsize()) { + if(eval(nenv,x,OP_LOCAL)) { out<at(nenv.dbat,db->Index()); break; } + else nenv.dbat++; + } +} + +//////////////////////////////////////////////////////////////////// + +void envAddDB(tokmap& T) +{ + TOK_ADD(create,"xxxx",tok_t::SYSONLY, + "%Create a new database\n" + "%Syntax: \\create{varname}{tables}{index}{comp}\n" + "%\n" + "%Index is the unique, ordered table.\n" + "%Comp is the compare function.\n" + "%A maxmimum of 32 tables are allowed in HDB/1.2\n" + ); + TOK_ADD(sort,"xxx",tok_t::PRIVATE, + "%Sort a database\n" + "%Syntax: \\sort{varname}{index}{comp}\n" + "%\n" + "%Note that only globals on the current level are sortable.\n" + ); + + //////////////////////////////////////////////////////// + + TOK_ADD(db,"xr",tok_t::PRIVATE, + "%Set current database\n" + "%Syntax: \\db{varname}{body}\n" + "%\n" + "%Within body, all database functions apply.\n" + ); + TOK_ADD(query,"xr",tok_t::PRIVATE, + "%Query current database\n" + "%Syntax: \\query{test}{body}\n" + "%\n" + "%Evaluates body for all record where test is true.\n" + "%\n" + "%optimizes globals; it is ok to use full function\n" + "%expressions in the query. Turned out to be better after all.\n" + ); + TOK_ADD(find_rec,"xr?",tok_t::PRIVATE, + "%Find record by key, in current database\n" + "%Syntax: \\find.rec{key}{body}\n" + "%\n" + "%Evaluates body if key is found in the current ordering.\n" + "%Otherwise, evaluates , if exist.\n" + ); + + //////////////////////////////////////////////////////// + + TOK_ADD(first_rec,"",tok_t::PRIVATE, + "%Go to first record\n" + "%Syntax: \\first.rec\n" + ); + TOK_ADD(exist_rec,"",tok_t::PRIVATE, + "%True if current record exist\n" + "%Syntax: \\exist.rec\n" + ); + TOK_ADD(next_rec,"",tok_t::PRIVATE, + "%Go to next record\n" + "%Syntax: \\next.rec\n" + ); + TOK_ADD(prev_rec,"",tok_t::PRIVATE, + "%Go to previous record\n" + "%Syntax: \\prev.rec\n" + ); + TOK_ADD(rem_rec,"",tok_t::SYSONLY, + "%Remove current record\n" + "%Syntax: \\rem.rec\n" + ); + + //////////////////////////////////////////////////////// + + TOK_ADD(lastkey,"",tok_t::PRIVATE, + "%Last key of current order\n" + "%Syntax: \\lastkey\n" + "%\n" + "%Silently ignored if not found.\n" + ); + TOK_ADD(prevkey,"",tok_t::PRIVATE, + "%Previous key of current order\n" + "%Syntax: \\prevkey\n" + "%\n" + "%Silently ignored if not found.\n" + ); + TOK_ADD(nextkey,"",tok_t::PRIVATE, + "%Next key of current order\n" + "%Syntax: \\nextkey\n" + "%\n" + "%Silently ignored if not found.\n" + ); + + //////////////////////////////////////////////////////// + + TOK_ADD(qprevkey,"r",tok_t::PRIVATE, + "%Previous key of current order, satisfying query\n" + "%Syntax: \\qprevkey\n" + "%\n" + "%Silently ignored if not found.\n" + ); + TOK_ADD(qnextkey,"r",tok_t::PRIVATE, + "%Next key of current order, satisfying query\n" + "%Syntax: \\qnextkey\n" + "%\n" + "%Silently ignored if not found.\n" + ); + +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +static unsigned long seed=RandomInit(time(0)); + +mstring Random(int n) +{ + mstring s; + for(int i=0;i9) THROW("env: assertion on lib failed"); + s+=char(48+digit); + } + return s; +} + +/////////////////////////////////////////////////// + +void Env::clear_args() +{ + for(int i=0;i=0&&nresult; + else eval(out,rarg[n-xsize()],0); + } + } + else if(up) up->parg(out,n); +} + +void Env::parg(mstring& tmp,int n) throw(merror_t) +{ + swrite sw(tmp); + parg(sw,n); +} + +mstring Env::parg(int n) throw(merror_t) +{ + mswrite tmp; parg(tmp,n); return tmp.str(); +} + +////////////////////////////////////////////////////// + +int Env::onarg(int n) const +{ + if(args) { + if(n<0) n+=args; + if(n>=0&&nresult.nempty(); + else return rarg[n-xsize()]->expr.nempty(); + } + } + else if(up) return up->onarg(n); + return 0; +} + +expr_t* Env::arg(int n) throw(merror_t) { + if(args) { + if(n<0) n+=args; + if(n>=0&&narg(n); + THROW("env: bad arg number "<=0&&nresult; + else THROW("env: post-eval expression not readable here"); + } + } + else if(up) return up->operator[](n); + THROW("env: bad arg number "<get(t); + if(is_clear(p)&&!p->is_runnable()) return p->body; + THROW("env: could not find global variable "<get(t); + return is_clear(p)&&!p->is_runnable(); +} + +int Env::onglob(const sref& t) const +{ + const tok_t* p=toks->get(t); + return is_clear(p)&&!p->is_runnable()&&p->body.nempty(); +} + +void Env::glob_list(rpile& list,const sref& t) const +{ + list.clear(); + if(t.empty()) { + for(const_iterator p=toks->begin();p!=toks->end();++p) { + if(is_clear(*p)&&!(*p)->is_runnable()) list.push_back((*p)->name); + } + } + else { + int cont=1; + for(const tokmap* tok=toks;tok&&cont;tok=tok->up) { + for(const_iterator p=tok->begin();p!=tok->end();++p) { + if((*p)->name.left(t.size())==t&&is_clear(*p)&&!(*p)->is_runnable()) { + list.push_back((*p)->name); + cont=0; + } + } + } + } +} + +void Env::glob_adb(const sref& fx,const sref& t) throw(merror_t) +{ + if(!db) THROW("htc: no active database"); + rpile list; glob_list(list,t); + mrvec col(list.size()); + int index=-1; + for(int i=0;iIndex(fx+list[i].right(t.size())); + if(col[i]==db->Index()) index=i; + } + if(index<0) THROW("env: index required when adding a db record"); + int row=db->Find(glob(list[index])); + if(row>=0) { + for(int i=0;iSet(row,col[i],glob(list[i])); + } + else { + spile nlist(db->Tables()); + for(int i=0;iInsert(nlist,1); + } + if(row<0) THROW("htc: could not add record - denied"); + dbat=row; +} + +////////////////////////////////////////////////////// + +const sref Env::aloc(const sref& t,const sref& v) throw(merror_t) +{ + if(!is_tokname(t)) THROW("env: bad token name "<body; +} + +const sref Env::sloc(const sref& t,const sref& v) throw(merror_t) +{ + if(db) { + int p=db->FindIndex(*t=='@'?t.popf():t); + if(p>=0) { + if(p==db->Index()) THROW("env: cannot assign to db index"); + if(dbat<0||dbat>=db->size()) THROW("env: not a valid db record"); + db->Set(dbat,p,v); + return db->at(dbat,p); + } + } + return loc.set(new tok_t(t,v,tight()),1,1)->body; +} + +const sref Env::gloc(const sref& t) const throw(merror_t) +{ + if(db) { + int p=db->FindIndex(*t=='@'?t.popf():t); + if(p>=0) { + if(dbat>=0&&dbatsize()) return db->at(dbat,p); + else THROW("env: not a valid database record"); + } + } + const tok_t* p=loc.get(t); + if(is_clear(p)&&!p->is_runnable()) return p->body; + THROW("env: could not find local variable "<FindIndex(*t=='@'?t.popf():t); + if(p>=0) return dbat>=0&&dbatsize(); + } + const tok_t* p=loc.get(t); + return is_clear(p)&&!p->is_runnable(); +} + +int Env::onloc(const sref& t) const +{ + if(db) { + int p=db->FindIndex(*t=='@'?t.popf():t); + if(p>=0) return dbat>=0&&dbatsize()&&db->at(dbat,p).nempty(); + } + const tok_t* p=loc.get(t); + return is_clear(p)&&!p->is_runnable()&&p->body.nempty(); +} + +void Env::gloc_list(rpile& list,const sref& t) const +{ + list.clear(); + if(t.empty()) { + for(const_iterator p=loc.begin();p!=loc.end();++p) { + if(is_clear(*p)&&!(*p)->is_runnable()) list.push_back((*p)->name); + } + } + else if(*t=='@') { + if(db) { + sref tt=t.popf(); + for(int i=0;iTables();i++) { + if(tt.empty()||db->Table(i).left(tt.size())==tt) list.push_back(db->Table(i)); + } + } + else THROW("env: cannot use '@' - no active database"); + } + else { + int cont=1; + for(const tokmap* tok=&loc;tok&&cont;tok=tok->up) { + for(const_iterator p=tok->begin();p!=tok->end();++p) { + if((*p)->name.left(t.size())==t&&is_clear(*p)&&!(*p)->is_runnable()) { + list.push_back((*p)->name); + cont=0; + } + } + } + } +} + +void Env::gloc_adb(const sref& fx,const sref& t) throw(merror_t) +{ + if(!db) THROW("htc: no active database"); + rpile list; gloc_list(list,t); + mrvec col(list.size()); + int index=-1; + for(int i=0;iIndex(fx+list[i].right(t.size())); + if(col[i]==db->Index()) index=i; + } + if(index<0) THROW("env: index required when adding a db record"); + int row=db->Find(gloc(list[index])); + if(row>=0) { + for(int i=0;iSet(row,col[i],gloc(list[i])); + } + else { + spile nlist(db->Tables()); + for(int i=0;iInsert(nlist,1); + } + if(row<0) THROW("htc: could not add record - denied"); + dbat=row; +} + +////////////////////////////////////////////////////// + +const mstring sback="../",sident="./",sdots="..",sslash="//"; + +mstring Env::URI(const sref& raw) +{ + int s; mstring cwd=pwd.left_last('/'),uri=raw; + while(uri.left(sback.size())==sback) { + uri.erase(0,sback.size()); + cwd=cwd.left_last('/'); + } + while((s=uri.find(sback))>=0) { + mstring tmp=uri.left(s).left_last('/').leftand_last('/'); + tmp+=uri.right(s+sback.size()); + uri=tmp; + } + while((s=uri.find(sdots))>=0) uri.erase(s,sdots.size()); + while((s=uri.find(sident))>=0) uri.erase(s,sident.size()); + while((s=uri.find(sslash))>=0) uri.erase(s,sslash.size()); + if(*uri=='/') return uri; + else { cwd+='/'; cwd+=uri; return cwd; } +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef ENVH +#define ENVH + +#include + +mstring Random(int n); + +const int OP_PARSE=8; +const mstring para_def="\n"; + +//////////////////////////////////////////////////////////////////// + +struct Lang { + volatile int running; + opmap ops; + tokmap toks; + Lang(); + virtual ~Lang() {} +}; + +struct req_env { + Lang* glob; + tokmap toks; + mstring uri; + int lang; + volatile int* running; + + int is_running() const { return glob->running&&(!running||*running); } + + req_env(Lang& g,tokmap& p) : glob(&g),toks(p),uri(),lang(0),running(0) {} + virtual ~req_env() {} +}; + +//////////////////////////////////////////////////////////////////// + +struct Env : public op_env { + typedef mrvec xpile; + typedef tokmap::iterator iterator; + typedef tokmap::const_iterator const_iterator; + + Env(opmap& op,req_env& r,unsigned m): + op_env(op),req(&r),toks(&r.toks),up(0),mod(m), + loc(),db(0),dbat(0),pwd(r.uri),para(para_def),args(0) {} + + Env(Env& p,tokmap& tok,const sref& cwd): + op_env(p),req(p.req),toks(&tok),up(p.up),mod(p.mod), + loc(p.loc),db(0),dbat(0),pwd(cwd),para(p.para),args(0) {} + Env(Env& p,unsigned m,const sref& cwd): + op_env(p),req(p.req),toks(p.toks),up(p.up),mod(m), + loc(p.loc),db(0),dbat(0),pwd(cwd),para(p.para),args(0) {} + + Env(Env& p): + op_env(p),req(p.req),toks(p.toks),up(p.up),mod(p.mod), + loc(p.loc),db(p.db),dbat(p.dbat),pwd(p.pwd),para(p.para),args(0) {} + Env(Env& p,dbmap& dbm): + op_env(p),req(p.req),toks(p.toks),up(p.up),mod(p.mod), + loc(p.loc),db(&dbm),dbat(0),pwd(p.pwd),para(p.para),args(0) {} + Env(Env& p,opmap& op): + op_env(op),req(p.req),toks(p.toks),up(p.up),mod(p.mod), + loc(p.loc),db(p.db),dbat(p.dbat),pwd(p.pwd),para(p.para),args(0) {} + + ~Env() { clear_args(); } + + int is_clear(const tok_t* tok) const { return tok&&tok->is_clear(mod); } + unsigned tight() const { return tok_t::tight(mod); } + + int check_arg(const runnable_t& ftok) const; + int is_arg(const sref& t) const { return cset_digit.test(t.front()); } + + int xsize() const { return xarg.size(); } + int rsize() const { return rarg.size(); } + + void xpush(const sref& in) throw(merror_t); + void rpush(const sref& in) throw(merror_t); + void clear_args(); + + void parg(mstream& out,int n) throw(merror_t); + void parg(mstring& buf,int n) throw(merror_t); + mstring parg(int n) throw(merror_t); + + const sref& operator[](int m) const throw(merror_t); + expr_t* arg(int m) throw(merror_t); + + const sref glob(const sref& t) const throw(merror_t); + + int glob_find(const sref& t) const; + void glob_list(rpile& list,const sref& t) const; + void glob_adb(const sref& fx,const sref& t) throw(merror_t); + + const sref aloc(const sref& t,const sref& v) throw(merror_t); + const sref sloc(const sref& t,const sref& v) throw(merror_t); + const sref gloc(const sref& t) const throw(merror_t); + + int gloc_find(const sref& t) const; + void gloc_list(rpile& list,const sref& t) const; + void gloc_adb(const sref& fx,const sref& t) throw(merror_t); + + int onarg(int m) const; + int onglob(const sref& s) const; + int onloc(const sref& s) const; + + void parse(mstream& out,const sref& in) throw(merror_t); + void expr(mstream& out,const sref& in) throw(merror_t); + void parse(mstream& out,expr_t* x) throw(merror_t); + + void eval(mstream& out,const sref& in,int child) throw(merror_t); + void eval(mstream& out,expr_t* x,int child) throw(merror_t); + void eval(mstream& out,runnable_t* ftok,int child) throw(merror_t); + + int split(sref& first,sref& rest) throw(merror_t); + void term(mstream& out,expr_t* x) throw(merror_t); + + mstring URI(const sref& raw); + + Env *up; + req_env* req; + tokmap* toks; + int mod,dbat; + tokmap loc; + dbmap* db; + mstring pwd,para,epilog; + int args; + xpile xarg,rarg; +}; + +///////////////////////////////////////////////////////////////////// + +struct latex_env : public Env { + latex_env(Env& p); + + int split(sref& first,sref& rest) throw(merror_t); + void term(mstream& out,expr_t* x) throw(merror_t); +}; + +///////////////////////////////////////////////////////////////////// + +#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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +using namespace std; + +TOK_IMPL(branch) { + int index=atoi(env[0]); + if(index<0) index+=env.args; + if(index>0&&index=env.args) THROW("htc: bad branching index "<status&OP_PARSE) env.parse(out,x->expr); else x->eval(out,env,mod); + return atob(out.str()); +} + +TOK_IMPL(for) { + expr_t* x[4]={env.arg(0),env.arg(1),env.arg(2),env.arg(3)}; + Env nenv(env); + for(eval(nenv,x[0],OP_ALL); + env.req->is_running()&&eval(nenv,x[1],OP_LOCAL); + eval(nenv,x[2],OP_LOCAL)) + nenv.parse(out,x[3]); +} + +TOK_IMPL(while) { + expr_t* x[2]={env.arg(0),env.arg(1)}; + Env nenv(env); + while(env.req->is_running()&&eval(nenv,x[0],OP_ALL)) nenv.parse(out,x[1]); +} + +///////////////////////////////////////////////////////////////////// + +TOK_IMPL(forall) { + mstring sep; env.parg(sep,-1); + sref s=env[0]; + sref p,e=env[1]; + expr_t* x=env.arg(2); + Env nenv(env); + if(sep.nempty()) { + charset seps(sep.c_str()); + while(Split(p,e,seps)) { + nenv.aloc(s,p); + nenv.parse(out,x); + } + } + else { + while(Split(p,e)) { + nenv.aloc(s,p); + nenv.parse(out,x); + } + } +} + +///////////////////////////////////////////////////////////////////// + +TOK_IMPL(on_arg) { + if(env.up&&env.up->onarg(atoi(env[0])-1)) env.parg(out,1); else env.parg(out,2); +} +TOK_IMPL(on_glob) { if(env.onglob(env[0])) env.parg(out,1); else env.parg(out,2); } +TOK_IMPL(on_loc) { if(env.onloc(env[0])) env.parg(out,1); else env.parg(out,2); } + +///////////////////////////////////////////////////////////////////// + +TOK_IMPL(setlang) { + env.req->lang=Language(env[0]); if(env.req->lang<0) env.req->lang=0; +} + +TOK_IMPL(lang) { + env.parg(out,env.req->lang); +} + +///////////////////////////////////////////////////////////////////// + +void envAddFlow(tokmap& T) +{ + TOK_ADD(branch,"x*",tok_t::PUBLIC, + "%Multiple branch\n" + "%Syntax: \\branch{x}..\n" + "%\n" + "%x==0 means nothing is executed, the first body for x==1, etc\n" + "%Negative values wrap to end\n" + ); + TOK_ADD(if,"xr?",tok_t::PUBLIC, + "%if - logical branch\n" + "%Syntax: \\if{x}{body}\n" + "%\n" + "%if x is non-zero, evaluates body, otherwise else if exist\n" + ); + TOK_ADD(nif,"xr?",tok_t::PUBLIC, + "%nif - logical branch\n" + "%Syntax: \\nif{x}{body}\n" + "%\n" + "%if x is zero, evaluates body, otherwise else if exist\n" + ); + TOK_ADD(switch,"x*",tok_t::PUBLIC, + "%Multiple switch\n" + "%Syntax: \\switch{x}....\n" + "%\n" + "%A sequence of pairs follows x\n" + "%x==val means body is executed\n" + "%\n" + "%An unpaired body at the end is treated as default\n" + ); + + ///////////////////////////////////////////////////////////////////// + + TOK_ADD(for,"rrrr",tok_t::PRIVATE, + "%C style\n" + "%Syntax: \\for{start}{test}{next}{body}\n" + "%\n" + "%Note that test and next are evaluated with optimization: only\n" + "%local variables are updated. Hence, you cannot use a function\n" + "%as the whole test, only as a part of the test, as in i<\\function.\n" + ); + TOK_ADD(while,"rr",tok_t::PRIVATE, + "%C style\n" + "%Syntax: \\while{test}{body}\n" + "%\n" + "%There is no optimization on test - globals are re-evaluated in each step\n" + ); + TOK_ADD(forall,"xx?r",tok_t::PUBLIC, + "%Loop through elements\n" + "%Syntax: \\forall{varname}{list}{body}\n" + "%\n" + "%Default whitespace are ( \\t\\r\\n). Optional whitespace characters\n" + "%may be given. This enables the design of special lists.\n" + ); + + ///////////////////////////////////////////////////////////////////// + + TOK_ADD(on_arg,"xr?",tok_t::PUBLIC, + "%Do if arg exist and is non-empty\n" + "%Syntax: \\on.arg{number}{body}\n" + ); + TOK_ADD(on_glob,"xr?",tok_t::PUBLIC, + "%Do if global variable exist and is non-empty\n" + "%Syntax: \\on.glob{varname}{body}\n" + ); + TOK_ADD(on_loc,"xr?",tok_t::PUBLIC, + "%Do if local variable exist and is non-empty\n" + "%Syntax: \\on.loc{varname}{body}\n" + ); + + ///////////////////////////////////////////////////////////////////// + + TOK_ADD(lang,"r*",tok_t::PUBLIC, + "%Branch on global language\n" + "%Syntax: \\lang{en}{sv}..\n" + "%\n" + "%Currently English (en) and Swedish (sv) are supported\n" + ); + TOK_ADD(setlang,"x",tok_t::PUBLIC, + "%Set global language\n" + "%Syntax: \\setlang{language code list}\n" + "%\n" + "%The language actually set is a negotitation -\n" + "%the first code in the list recognized is set.\n" + "%If not found, the default language English (en) is set.\n" + ); +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +const mstring cron_tag="cron."; +const int SSECS=4; + +TOK_IMPL(def) { + expr_t* x=env.arg(2); + func_t* tok=Func(*env.toks,env[0],x->expr,tok_t::PUBLIC,env[1]); + if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env); +} + +TOK_IMPL(redef) { + expr_t* x=env.arg(2); + func_t* tok=Func(*env.toks,env[0],x->expr,tok_t::PUBLIC,env[1],1); + if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env); +} + +TOK_IMPL(ldef) { + if(env.toks->get(env[0])) THROW("htc: local function would override global token"); + expr_t* x=env.arg(2); + func_t* tok=Func(env.loc,env[0],x->expr,tok_t::PUBLIC,env[1]); + if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env); +} + +TOK_IMPL(cron) { + mstring name=cron_tag+env[0]; + expr_t* x=env.arg(1); + cron_t* tok=Cron(*env.toks,name,x->expr,tok_t::PUBLIC,atoi(env[0])); + if(x->status&OP_PARSE) tok->x.status=OP_PARSE; else tok->x.expand(env); +} + +/////////////////////////////////////////////////// + +static void Run(tokmap& m,mstream& out,Env& env,const sref& s) throw(merror_t) { + tok_t* p=m.get(s); + if(!p) THROW("htc: cannot run "<is_reference()) env.parse(out,((reference_t*)p)->data); + else if(!p->is_runnable()) env.parse(out,p->body); + } + catch(const merror_t& e) { + out< cron; + int cont=1; for(tokmap* tok=env.toks;tok&&cont;tok=tok->up) { + for(Env::iterator p=tok->begin();p!=tok->end();++p) { + if((*p)->name.left(cron_tag.size())==cron_tag&&(*p)->is_cron()) { + cron.push_back((cron_t*)(*p)); + cont=0; + } + } + } + while(env.req->is_running()&&cron.size()) { + int cont=1; + for(int i=0;icron[i]->last+cron[i]->interval) { + mstream ms; + env.eval(ms,&cron[i]->x,0); + cron[i]->last=time(0); + cont=0; + } + } + if(cont) sleep(SSECS); + } +} + +////////////////////////////////////////////////////// + +void envAddFunc(tokmap& T) +{ + TOK_ADD(def,"xxr",tok_t::PUBLIC, + "%Define new global function\n" + "%Syntax: \\def{varname}{argument string [xr?*]}{body}\n" + "%\n" + "%The argument string is a sequence of characters describing\n" + "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n" + "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n" + "%The can be only one optional (?) argument.\n" + "%The body contains the function body. The arguments are retrieved\n" + "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n" + "%regardless of where it stands in the sequence.\n" + "%\n" + "%Generates an error if already defined.\n" + ); + TOK_ADD(redef,"xxr",tok_t::PRIVATE, + "%Redefine global function\n" + "%Syntax: \\def{varname}{argument string [xr?*]}{body}\n" + "%\n" + "%The argument string is a sequence of characters describing\n" + "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n" + "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n" + "%The can be only one optional (?) argument.\n" + "%The body contains the function body. The arguments are retrieved\n" + "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n" + "%regardless of where it stands in the sequence.\n" + "%\n" + "%Takes precedence over any existing function.\n" + ); + TOK_ADD(ldef,"xxr",tok_t::PUBLIC, + "%Define a local function\n" + "%Syntax: \\ldef{varname}{argument string [xr?*]}{body}\n" + "%\n" + "%The argument string is a sequence of characters describing\n" + "%the argument type, which is one of x (pre-evaluate), r (post-evaluate),\n" + "%? (optional post-evaluate) or * (optional at end, post-evaluate).\n" + "%The can be only one optional (?) argument.\n" + "%The body contains the function body. The arguments are retrieved\n" + "%as \\1, \\2 etc, except the optional argument which is retrieved as \\0,\n" + "%regardless of where it stands in the sequence.\n" + "%\n" + "%Local functions work just like global functions, but are only\n" + "%accessible from within the current stack frame.\n" + ); + TOK_ADD(cron,"xr",tok_t::SYSONLY, + "%Define a cron function\n" + "%Syntax: \\cron{interval}{body}\n" + "%\n" + "%A cron function is executed every interval seconds,\n" + "%when running in cron mode.\n" + "%\n" + "%See \\run.cron.\n" + ); + + ////////////////////////////////////////////////////// + + TOK_ADD(run,"x",tok_t::SYSONLY, + "%Evaluate the body of variable in current running mode\n" + "%Syntax: \\run{varname}\n" + "%\n" + "%The running modes are:\n" + "%\n" + "%XINIT Init mode is only used to collect meta information\n" + "%XSYSTEM The system mode - everything is accessible\n" + "%XPRIVATE Private mode - suitable for in-door custom programs\n" + "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n" + "% via the web. This is the default mode for all derivative\n" + "% functions, as well as for globals and constants.\n" + "%\n" + "%IT IS A MAJOR SECURITY HAZARD TO EVALUATE UNKNOWN\n" + "%VARIABLES IN XSYSTEM MODE!!\n" + "%\n" + "%If at all, user-supplied information should only be run\n" + "%in public mode, for instance to allow the expansion of latex\n" + "%commands. NEVER use this mode outside the core.\n" + ); + TOK_ADD(run_private,"x",tok_t::SYSONLY, + "%Evaluate the body of variable in private mode\n" + "%Syntax: \\run.private{varname}\n" + "%\n" + "%The running modes are:\n" + "%\n" + "%XINIT Init mode is only used to collect meta information\n" + "%XSYSONLY The system mode - everything is accessible\n" + "%XPRIVATE Private mode - suitable for in-door custom htc code\n" + "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n" + "% via the web. This is the default mode for all derivative\n" + "% functions, as well as for globals and constants.\n" + "%\n" + "%The execution of sysonly functions is silent, just as it wasn't there.\n" + ); + TOK_ADD(run_public,"x",tok_t::PRIVATE, + "%Evaluate the body of variable in public mode\n" + "%Syntax: \\run.public{varname}\n" + "%\n" + "%The running modes are:\n" + "%\n" + "%XINIT Init mode is only used to collect meta information\n" + "%XSYSONLY The system mode - everything is accessible\n" + "%XPRIVATE Private mode - suitable for in-door custom programs\n" + "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n" + "% via the web. This is the default mode for all derivative\n" + "% functions, as well as for globals and constants.\n" + "%\n" + "%The execution of sysonly or private functions is silent,\n" + "%just as they weren't there.\n" + ); + TOK_ADD(run_local,"x",tok_t::PRIVATE, + "%Evaluate the body of the local variable in public mode\n" + "%Syntax: \\run.local{varname}\n" + "%\n" + "%The running modes are:\n" + "%\n" + "%XINIT Init mode is only used to collect meta information\n" + "%XSYSTEM The system mode - everything is accessible\n" + "%XPRIVATE Private mode - suitable for in-door custom programs\n" + "%XPUBLIC Public mode - quite restrictive, may be run by anyone\n" + "% via the web. This is the default mode for all derivative\n" + "% functions, as well as for globals and constants.\n" + "%\n" + "%The execution of sysonly or private functions is silent,\n" + "%just as they weren't there.\n" + ); + TOK_ADD(run_cron,"",tok_t::SYSONLY, + "%Go into an infinite loop and run the crontab command.\n" + "%Syntax: \\run.cron{}\n" + "%\n" + "%Note that only cron functions from the highest global level\n" + "%where a cron function is defined is run. Hence you may redefine\n" + "%earlier crontabs, but cannot mix between levels.\n" + "%\n" + "%You can only have one crontab of a certain interval.\n" + "%\n" + "%Produces no output.\n" + ); +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +TOK_IMPL(help) { + out<<"HTC/1.2 - HyperText Compiler (H. Rydberg (C) 1998,1999,2000,2001)\n"; + out<<"\n"; + out<<"Try these commands:\n"; + out<<"\\help - This message\n"; + out<<"\\ops - List all operators\n"; + out<<"\\ops{op} - Get info on operator\n"; + out<<"\\tok - List all tokens\n"; + out<<"\\tok{tok} - Get info on token\n"; + out<<"\\src{tok} - View token source\n"; +} + +TOK_IMPL(ops) { + mstring cmd; env.parg(cmd,-1); + if(cmd.nempty()) { + out<get(cmd); + if(p) { + if(p->pre1) out<<"Unary operator, precedence "<pre1<<"\n"; + if(p->pre2) out<<"Binary operator, precedence "<pre2<<"\n"; + out<<"\n"; + out<desc; + } + else { + out<<"(not found)\n"; + } + } + else { + mstring line; + line.convert("%-3s %-3s %s\n","P1","P2","NAME"); + out<begin();p!=env.ops->end();p++) { + line.convert("%-3d %-3d %s\n",(*p)->pre1,(*p)->pre2,(*p)->name.c_str()); + out<get(cmd); + if(p) { + out<<"Mode: "<mod&tok_t::RESTRICT) out<<"public "; + else if(p->mod&tok_t::NORMAL) out<<"normal "; + else if(p->mod&tok_t::SYSONLY) out<<"system "; + else if(p->mod&tok_t::INIT) out<<"init "; + if(p->is_runnable()) { + if(p->is_binary()) out<<"built-in "; + out<<"function\n"; + } + else { + if(p->is_const()) out<<"constant"; else out<<"variable"; + if(p->is_reference()) out<<" reference"; + if(p->is_dbref()) out<<" database reference"; + out<<"\n"; + } + out<<"Usage: "; + if(p->is_runnable()) out<<*(func_t*)p; else out<<"\\"<name; + out<<"\n"; + if(p->is_runnable()) { + mstring desc=Desc(*p); + if(desc.nempty()) { out<<"\n"; out<up) { + line.convert("%-12s %s %d\n","MODE","NAME LEVEL",n); + out<begin();p!=tok->end();p++) { + line.convert("%s %s\n",Mode(*(*p)).c_str(),(*p)->name.c_str()); + out<get(env[0]); + if(!p) out<is_runnable()) { + const func_t* ftok=(func_t*)p; + out<<"{"<name<<"}{"<arg<<"}"; + if(p->is_binary()) out<<" (built-in function)\n"; + else out<<"{"<x.expr<<"}\n"; + } + else out<\n" + ); + TOK_ADD(tok,"?",tok_t::PRIVATE, + "%Help on tokens\n" + "%Syntax: \\tok\n" + ); + TOK_ADD(src,"x",tok_t::SYSONLY, + "%Source of token\n" + "%Syntax: \\src{tokname}\n" + ); +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +void envAddOps(opmap& M,tokmap& T); +void envAddBasic(tokmap& T); +void envAddFunc(tokmap& T); +void envAddFlow(tokmap& T); +void envAddDB(tokmap& T); +void envAddLatex(tokmap& T); +void envAddHelp(tokmap& T); + +Lang::Lang() : running(1) +{ + envAddOps(ops,toks); + envAddBasic(toks); + envAddFunc(toks); + envAddFlow(toks); + envAddDB(toks); + envAddLatex(toks); + envAddHelp(toks); +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +using namespace std; + +static opmap op_latex; + +latex_env::latex_env(Env& p) : Env(p) { ops=&op_latex; } + +////////////////////////////////////////////////////// + +TOK_IMPL(it) { + mstring s; env.parg(s,-1); + if(s.nempty()) { out<<""; out<"; } + else { out<<""; env.epilog=sref("")+env.epilog; } +} + +TOK_IMPL(em) { + mstring s; env.parg(s,-1); + if(s.nempty()) { out<<""; out<"; } + else { out<<""; env.epilog=sref("")+env.epilog; } +} + +TOK_IMPL(bf) { + mstring s; env.parg(s,-1); + if(s.nempty()) { out<<""; out<"; } + else { out<<""; env.epilog=sref("")+env.epilog; } +} + +TOK_IMPL(mathbf) { + mstring s; env.parg(s,-1); + if(s.nempty()) { out<<""; out<"; } + else { out<<""; env.epilog=sref("")+env.epilog; } +} + +TOK_IMPL(rm) { + mstring s; env.parg(s,-1); + if(s.nempty()) { out<expr); + out<\n" + "%\n" + "%Note that the body is optional, just like in latex.\n" + ); + TOK_ADD(em,"?",tok_t::PUBLIC, + "%Set emph mode\n" + "%Syntax: \\em\n" + "%\n" + "%Note that the body is optional, just like in latex.\n" + ); + TOK_ADD(bf,"?",tok_t::PUBLIC, + "%Set Boldface mode\n" + "%Syntax: \\bf\n" + "%\n" + "%Note that the body is optional, just like in latex.\n" + ); + TOK_ADD(mathbf,"?",tok_t::PUBLIC, + "%Set Math boldface mode\n" + "%Syntax: \\mathbf\n" + "%\n" + "%Note that the body is optional, just like in latex.\n" + ); + TOK_ADD(rm,"?",tok_t::PUBLIC, + "%Set rm face mode\n" + "%Syntax: \\rm\n" + "%\n" + "%Note that the body is optional, just like in latex.\n" + ); + + ////////////////////////////////////////////////////////// + + TOK_ADD(setpara,"x",tok_t::PUBLIC, + "%Set paragraph string\n" + "%Syntax: \\setpara{string}\n" + "%\n" + "%Whenever a series of newlines is encountered, it is\n" + "%replaced by this string. The default is a newline.\n" + "%\n" + "%Operates on stack level (may be nested)\n" + ); + TOK_ADD(epilog,"x",tok_t::PUBLIC, + "%Add string to the epilog\n" + "%Syntax: \\epilog{string}\n" + "%\n" + "%The epilog string is output when the current stack frame\n" + "%goes out of scope. For functions like \\bf, which needs\n" + "%an end tag to be inserted at the right place.\n" + "%\n" + "%Operates on stack level (may be nested)\n" + ); + + ////////////////////////////////////////////////////////// + + TOK_ADD(equation,"r",tok_t::PUBLIC, + "%Evaluate equation (LaTeX)\n" + "%Syntax: \\equation{body}\n" + ); + + ////////////////////////////////////////////////////////// + + Const(T,sref("t"),sref("\t"),tok_t::PUBLIC); + Const(T,sref("n"),sref("\n"),tok_t::PUBLIC); + Const(T,sref("f"),sref("\f"),tok_t::PUBLIC); + + ////////////////////////////////////////////////////////// +} + 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +////////////////////////////////////////////////////// + +struct xop_t : public op_t { + typedef void (*xop1_t)(mstream&,Env&,const sref&); + typedef void (*xop2_t)(mstream&,Env&,const sref&,const sref&); + xop_t(const mstring& s,int p1,int p2,int l,int r, + xop1_t op1,xop2_t op2,const mstring& d): + op_t(s,p1,p2,l,r,(op1_t)op1,(op2_t)op2,d) {} +}; + +////////////////////////////////////////////////////// + +#define X1_IMPL(name) \ +static void impl_##name(mstream& out,Env& env,const sref& a) throw(merror_t) +#define X2_IMPL(name) \ +static void impl_##name(mstream& out,Env& env,const sref& L,const sref& R) throw(merror_t) + +#define X1_ADDN(p,l,r,f,n,d) M.add(new xop_t(#n,p,0,l,r,impl_##f,0,d)) +#define X2_ADDN(p,l,r,f,n,d) M.add(new xop_t(#n,0,p,l,r,0,impl_##f,d)) + +////////////////////////////////////////////////////// + +X1_IMPL(exist) { + mstring name; + swrite sw(name); + env.eval(sw,a,0); + out<, + "Copy local record\n" + "Syntax: (partial name) -> (partial name)\n" + "Returns: nothing\n" + "\n" + "The partial name @ for data base records works in both arguments.\n" + "\n" + "Generates an error if any of the expanded names of the right\n" + "partial argument does not exist.\n" + ); +} diff --git a/src/lang/parse.cc b/src/lang/parse.cc new file mode 100644 index 0000000..7e24a11 --- /dev/null +++ b/src/lang/parse.cc @@ -0,0 +1,453 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static const charset escaped("%$[]{};"); +static const charset prename("\"'`^~*,_=!\\"); +static const charset spec("+-*/%<=>^_!~&|,?:;"); +static const charset spec_latex("+-*/<=>^_;"); +static const charset delim("\"{}()\\"); +static const charset delim_latex("{}()[]\\"); +static const charset specws=spec|delim|cset_ws; +static const charset specws_latex=spec_latex|delim_latex|cset_ws; +static const charset tokname=cset_alphareal; +static const charset pretok=tokname|prename; + +const mstring begin_tag="begin",end_left="\\end{",end_right="}"; + +int is_tokname(const sref& s) { + return s.back()!='.'&&pretok.test(s.front())&&s.right(1).find_not_of(tokname)<0; +} + +int is_arglist(const sref& s) +{ + const char* p=s.begin(); int opt=0; + if(*p=='?') { ++p; opt=1; } + while(*p=='x') ++p; + while(*p=='r') ++p; + if(!opt&&*p=='?') ++p; + if(*p=='*') ++p; + return !*p; +} + +/////////////////////////////////////////////////////////// + +static sref getlocal(const sref& in) { + int w=0,i=0; for(;i0&&in[i-1]=='.') return in.left(i-1); + else return in.left(i); +} + +static int skip_quotes(const sref& in) throw(merror_t) { + int s=in.skip(cset_quotes); + if(s<0) THROW("parse: unmatched '\"'"); + return s; +} + +static int skip_braces(const sref& in) throw(merror_t) { + int s=in.skip(cset_braces); + if(s<0) THROW("parse: unmatched '{'"); + return s; +} + +static int skip_para(const sref& in) throw(merror_t) { + int s=in.skip(cset_para); + if(s<0) THROW("parse: unmatched '('"); + return s; +} + +static int skip_indices(const sref& in) throw(merror_t) { + int s=in.skip(cset_indices); + if(s<0) THROW("parse: unmatched '['"); + return s; +} + +static int skip_dollars(const sref& in) throw(merror_t) { + int s=in.skip(cset_dollars); + //if(s<0) THROW("parse: unmatched '$'"); + return s; +} + +/////////////////////////////////////////////////////////// + +static int count_args(const sref& in) throw(merror_t) +{ + sref e=in.right_first_not_of(cset_ws); + int s,n=0; while((s=skip_braces(e))>0||(s=skip_para(e))>0) { + e=e.adv(s).right_first_not_of(cset_ws); n++; + } + return n; +} + +static int past_args(const sref& in) throw(merror_t) +{ + sref p=in,e=in.right_first_not_of(cset_ws); + int s; while((s=skip_braces(e))>0||(s=skip_para(e))>0) { + e=(p=e.adv(s)).right_first_not_of(cset_ws); + } + return p-in; +} + +static int parse_args(Env& env,runnable_t* ftok,const sref& in,int inbegend) throw(merror_t) +{ + env.clear_args(); + sref option,p=in,e=in.right_first_not_of(cset_ws); + int s,narg=0,carg=count_args(e); + while((s=e.skip(cset_braces))>0||(s==0&&(s=e.skip(cset_para))>0)) { + if(ftok->arg[narg]=='?'&&cargarg.size()) narg++; + switch(ftok->arg[narg]){ + case 'x': env.xpush(e.left(s)); break; + case 'r': case '*': env.rpush(e.left(s)); break; + case '?': option=e.left(s); break; + case 0: THROW("htc: too many args in "<name); + default: THROW("htc: bad argument descriptor in "<name); + } + e=(p=e.adv(s)).right_first_not_of(cset_ws); + if(ftok->arg[narg]!='*') narg++; + } + if(s<0) THROW("htc: unmatched parenthesis in function "<name); + if(inbegend) { + mstring tag=end_left+ftok->name+end_right; + s=p.find(tag); if(s<0) THROW("htc: unmatched begin/end in function "<name); + if(ftok->arg[narg]=='?'&&cargarg.size()) narg++; + switch(ftok->arg[narg]){ + case 'x': env.xpush(p.left(s)); break; + case 'r': case '*': env.rpush(p.left(s)); break; + case '?': option=p.left(s); break; + case 0: THROW("htc: too many args in begin/end "<name); + default: THROW("htc: bad argument descriptor in "<name); + } + p=p.adv(s+tag.size()); + } + if(ftok->arg.find('?')>=0&&env.argsarg.size()) env.rpush(option); + return p-in; +} + +int Env::check_arg(const runnable_t& ftok) const +{ + static const charset tag("xr?"); + const char* p=ftok.arg.begin(); int x=0,r=0,o=0; + while(tag.test(*p)) { + switch(*p++) { + case 'x': x++; break; + case 'r': r++; break; + case '?': r++; o++; break; + } + } + if(*p=='*') { o++; ++p; } + if(*p||x!=xsize()||o>1||o==1&&rsize()expand(*this); else x->status=OP_PARSE; + swrite sw(x->result); + eval(sw,x,0); +} + +void Env::rpush(const sref& in) throw(merror_t) +{ + expr_t* x=new expr_t(in(1,-3)); + rarg.push_back(x); args++; + if(*in=='(') x->expand(*this); else x->status=OP_PARSE; +} + +/////////////////////////////////////////////////////////// + +static int indent(sref& p) { + if(cset_newl.test(p.front())) { p=p.right_first_not_of(cset_ws); return 1; } + else return 0; +} + +static int backindent(sref& p) { + if(cset_newl.test(p.leftand_last_not_of(cset_spaces).back())) { + p=p.leftand_last_not_of(cset_ws); return 1; + } + else return 0; +} + +static int pastsemi(sref& p) { + int s=p.find_not_of(cset_spaces); + if(s>=0&&p[s]==';') return indent(p=p.adv(s+1)); else return 0; +} + +static int past_func(const sref& in) throw(merror_t) +{ + sref e=getname(in.popf()),p=in.adv(e); + return p.adv(past_args(p))-in; +} + +static int parse_func(mstream& out,Env& env,const sref& in,int& run) throw(merror_t) +{ + run=0; sref e=getname(in),p=in.adv(e); + mstring name; + swrite sw(name); + env.eval(sw,e,0); + if(env.is_arg(name)) { + //if(count_args(p)) THROW("htc: expected no argument after "<parg(out,atoi(name)-1); + return p-in; + } + int inbegend=0; + if(name==begin_tag) { + sref next=p.right_first_not_of(cset_ws); + int s=skip_braces(next); + if(s>0) { name=next(1,s-2); p=next.adv(s); inbegend=1; } + } + tok_t* tok=env.toks->get(name); + if(!tok) { if(!(tok=env.loc.get(name))||!tok->is_func()) tok=0; } + if(!tok) return 0; + if(!tok->is_runnable()) { + //if(count_args(p)) THROW("htc: expected no argument after "<body; + return p-in; + } + runnable_t* ftok=(runnable_t*)tok; + if(env.is_clear(ftok)) { + p=p.adv(parse_args(env,ftok,p,inbegend)); + run=1; env.eval(out,ftok,1); + } + else p=p.adv(past_args(p)); + return p-in; +} + +void Env::parse(mstream& out,const sref& in) throw(merror_t) +{ + static const charset sensors=escaped|cset_newl|'\\'; + epilog.clear(); + int s,run; + sref p=in; + indent(p); + backindent(p); + sref e=p; + while(*p) { + switch(*p) { + case ';': + case '}': + case ']': + out.put(*p); + p=p.popf(); + break; + case '%': + if(p-e>0) { out.put(*p); p=p.popf(); } + else e=p=p.past_nl().right_first_not_of(cset_ws); + break; + case '\r': + p=p.popf(); + break; + case '\n': + out.put(*p); p=p.popf(); + if(cset_newl.test(*p.right_first_not_of(cset_spaces))) out<0) { + eval(out,p(1,s-2),0); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '[': + if(mod&tok_t::PUBLIC){ + if((s=skip_indices(p))>0) { + expr(out,p(1,s-2)); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '$': + if(mod&tok_t::PUBLIC){ + if((s=skip_dollars(p))>0) { + latex_env(*this).expr(out,p(1,s-2)); + if(pastsemi(p=p.adv(s))) e=p; + } + } + else { out.put(*p); p=p.popf(); } + break; + case '\\': + p=p.popf(); + if(p.empty()) out.put('\\'); + else if(escaped.test(*p)) { out.put(*p); p=p.popf(); } + else { + if((s=parse_func(out,*this,p,run))>0) { if(pastsemi(p=p.adv(s))) e=p; } + else out.put('\\'); + } + break; + default: + if((s=p.find_of(sensors))>=0) { out<status&OP_PARSE) parse(out,x->expr); + else x->eval(out,*this,OP_ALL); +} + +/////////////////////////////////////////////////////////// + +void Env::eval(mstream& out,const sref& in,int child) throw(merror_t) +{ + Env nenv(*this); if(child) nenv.up=this; + nenv.parse(out,in); +} + +void Env::eval(mstream& out,expr_t* x,int child) throw(merror_t) +{ + Env nenv(*this); if(child) nenv.up=this; + nenv.parse(out,x); +} + +void Env::eval(mstream& out,runnable_t* ftok,int child) throw(merror_t) +{ + if(child&&!check_arg(*ftok)) THROW("htc: bad number of args; expected "<<*ftok); + if(ftok->is_binary()) ((binary_t*)ftok)->func(out,*this); + else if(ftok->is_func()) eval(out,&((func_t*)ftok)->x,child); + else THROW("htc: unknown runnable"); +} + +////////////////////////////////////////////////////// + +int Env::split(sref& first,sref& rest) throw(merror_t) +{ + int s; + rest=rest.right_first_not_of(cset_ws); + if(*rest=='\\') { + first=rest.left(past_func(rest)); + rest=rest.adv(first); + } + else if(spec.test(*rest)) { + first=rest.left_first_not_of(spec); + rest=rest.adv(first); + } + else if((s=skip_quotes(rest))>0||(s=skip_braces(rest))>0) { + first=rest.left(s); + rest=rest.adv(first); + } + else if((s=skip_para(rest))>0) { + first=rest(1,s-2); + rest=rest.right(s); + } + else { + first=getlocal(rest); + rest=rest.adv(first); + } + return first.nempty(); +} + +void Env::term(mstream& out,expr_t* x) throw(merror_t) +{ + if(x->expr.empty()) x->status=0; + else if(*x->expr=='\"') { out<expr(1,-3); x->status=0; } + else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; } + else if(x->expr.find_not_of(cset_real)<0) { out<expr; x->status=0; } + else if(*x->expr=='\\') { + sref p=x->expr.popf(); + if(p.empty()) { out.put('\\'); x->status=0; } + else if(escaped.test(*p)) { out.put(*p); x->status=0; } + else { + int run; + if(parse_func(out,*this,p,run)>0) x->status=run?OP_LOCAL:OP_GLOBAL; + else { out.put('\\')<expr; x->status=OP_NINIT; } + } + } + else { + mstring name; + swrite sw(name); + eval(sw,x->expr,0); + out<status=OP_LOCAL; + } +} + +/////////////////////////////////////////////////////////////////// + +int latex_env::split(sref& first,sref& rest) throw(merror_t) +{ + int s; + rest=rest.right_first_not_of(cset_ws); + if(*rest=='\\') { + first=rest.left(past_func(rest)); + rest=rest.adv(first); + } + else if(spec.test(*rest)) { + first=rest.left_first_not_of(spec); + rest=rest.adv(first); + } + else if((s=skip_braces(rest))>0||(s=skip_para(rest))>0||(s=skip_indices(rest))>0) { + first=rest.left(s); + rest=rest.adv(first); + } + else { + first=rest.left_first_of(specws); + rest=rest.adv(first); + } + return first.nempty(); +} + +void latex_env::term(mstream& out,expr_t* x) throw(merror_t) +{ + if(x->expr.empty()) x->status=0; + else if(*x->expr=='\\') { eval(out,x->expr,0); x->status=0; } + else if(*x->expr=='{') { eval(out,x->expr(1,-3),0); x->status=0; } + else if(*x->expr=='('||*x->expr=='[') { + out.put(x->expr.front()); + expr(out,x->expr(1,-3)); + out.put(x->expr.back()); + x->status=0; + } + else { out<expr; x->status=0; } +} 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 @@ +/************************************************************************* + * + * HTCd - Copyright (C) 1998-2006 Henrik Rydberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +static fp_stream mout(stdout),merr(stderr); + +main(int argc,char* argv[]) try +{ + Lang lang; + req_env req(lang,lang.toks); + Env env(lang.ops,req,XSYSTEM); + mstring line; + while(!feof(stdin)) { + merr<<"lang>"; + getline(line,stdin); + try { + env.parse(mout,line); + mout<<"\n"; + } + catch(const merror_t& e) { + merr< +#include +using namespace std; + +/////////////////////////////////////////////////////////////////// + +const unsigned tok_t::CONSTANT=1; +const unsigned tok_t::RESTRICT=2; +const unsigned tok_t::NORMAL=4; +const unsigned tok_t::SYSONLY=8; +const unsigned tok_t::INIT=16; +const unsigned tok_t::RUNNABLE=32; +const unsigned tok_t::FUNCTION=64; +const unsigned tok_t::BINARY=128; +const unsigned tok_t::REFERENCE=256; +const unsigned tok_t::DBREF=512; +const unsigned tok_t::CRON=1024; +const unsigned tok_t::PRIVATE=NORMAL|SYSONLY; +const unsigned tok_t::PUBLIC=RESTRICT|PRIVATE; +const unsigned tok_t::XMODES=PUBLIC|INIT; + +/////////////////////////////////////////////////////////////////// + +uniheap tok_t::tree; +void* tok_t::operator new(size_t n) { return tree.push(); } +void tok_t::operator delete(void* p) { tree.pop(p); } + +uniheap reference_t::tree; +void* reference_t::operator new(size_t n) { return tree.push(); } +void reference_t::operator delete(void* p) { tree.pop(p); } + +uniheap dbref_t::tree; +void* dbref_t::operator new(size_t n) { return tree.push(); } +void dbref_t::operator delete(void* p) { tree.pop(p); } + +uniheap runnable_t::tree; +void* runnable_t::operator new(size_t n) { return tree.push(); } +void runnable_t::operator delete(void* p) { tree.pop(p); } + +uniheap func_t::tree; +void* func_t::operator new(size_t n) { return tree.push(); } +void func_t::operator delete(void* p) { tree.pop(p); } + +uniheap cron_t::tree; +void* cron_t::operator new(size_t n) { return tree.push(); } +void cron_t::operator delete(void* p) { tree.pop(p); } + +uniheap binary_t::tree; +void* binary_t::operator new(size_t n) { return tree.push(); } +void binary_t::operator delete(void* p) { tree.pop(p); } + +/////////////////////////////////////////////////////////////////// + +mstring uscore2dot(const char* s) { + mstring t=s; int p; while((p=t.find('_'))>=0) t[p]='.'; return t; +} + +/////////////////////////////////////////////////////////////////// + +tok_t::~tok_t() {} + +/////////////////////////////////////////////////////////////////// + +void tokmap::clear() +{ + for(iterator p=list.begin();p!=list.end();p++) delete *p; + list.clear(); +} + +tok_t* tokmap::get_this(const sref& t) +{ + pair p=equal_range(list.begin(),list.end(),(val_t)0,comp_t(t)); + return p.first!=p.second?*p.first:0; +} + +const tok_t* tokmap::get_this(const sref& t) const +{ + pair p= + equal_range(list.begin(),list.end(),(val_t)0,comp_t(t)); + return p.first!=p.second?*p.first:0; +} + +const tok_t* tokmap::get(const sref& t) const +{ + const tok_t* p=get_this(t); return p?p:up?up->get(t):0; +} + +tok_t* tokmap::get(const sref& t) +{ + tok_t* p=get_this(t); return p?p:up?up->get(t):0; +} + +tok_t* tokmap::set(tok_t* tok,int replace,int force) throw(merror_t) +{ + pair p= + equal_range(list.begin(),list.end(),(val_t)0,comp_t(tok->name)); + if(p.first!=p.second) { + if(!(*p.first)->is_replaceable(*tok)) { + delete tok; + THROW("token: Could not set "<<(*p.first)->name<<" - protected"); + return 0; + } + else if((*p.first)->is_const()&&!replace) { + delete tok; + if(force) THROW("token: Could not set "<<(*p.first)->name<<" - constant"); + return 0; + } + else { delete *p.first; return *p.first=tok; } + } + else if(up) return up->set(tok,replace,force); + else { + mstring name=tok->name; + delete tok; + if(force) THROW("token: Could not set "<name); + if(old) { + if(!old->is_replaceable(*tok)) { + delete tok; + THROW("token: Could not add "<name<<" - protected"); + return 0; + } + else if(old->is_const()&&!replace) { + delete tok; + if(force) THROW("token: Could not add "<name<<" - constant"); + return 0; + } + } + pair p= + equal_range(list.begin(),list.end(),(val_t)0,comp_t(tok->name)); + if(p.first!=p.second) { delete *p.first; return *p.first=tok; } + else return *list.insert(p.second,tok); +} + +/////////////////////////////////////////////////////////////////// + +static void Check(const sref& s) throw(merror_t) +{ + if(!is_tokname(s)) THROW("token: bad name ("<name.left(s.size())==s) p=map.erase(p); else p++; + } + } +} + +/////////////////////////////////////////////////////////////// + +mstream& operator<<(mstream& out,const runnable_t& ftok) +{ + out<<"\\"<=0;i--) { if(tok.mod&(1< +#include + +#define TOK_ARGS (mstream& out,Env& env) +#define TOK_IMPL(func) static void impl_##func TOK_ARGS throw(merror_t) +#define TOK_ADD(func,a,mod,help) \ +T.add(new binary_t(uscore2dot(#func),sref(help),mod,sref(a),impl_##func),0,1) + +mstring uscore2dot(const char* s); + +class Env; +typedef void (*s_func_t)TOK_ARGS; + +class tok_t { +public: + static const unsigned CONSTANT,RESTRICT,NORMAL,SYSONLY,INIT; + static const unsigned RUNNABLE,FUNCTION,BINARY; + static const unsigned REFERENCE,DBREF,CRON; + static const unsigned PRIVATE,PUBLIC,XMODES; + + static inline unsigned tight(unsigned xmod) { + // INIT is ignored here simply because no mode can + // read variables in INIT mode... defaults to public. + return (xmod&SYSONLY)?SYSONLY:(xmod&NORMAL)?PRIVATE:PUBLIC; + } + + mstring name,body; + unsigned mod; + + int is_clear(int xmod) const { return (mod&xmod)!=0; } + int is_replaceable(const tok_t& t) const { return (mod&t.mod)==t.mod; } + + int is_const() const { return (mod&CONSTANT)!=0; } + int is_runnable() const { return (mod&RUNNABLE)!=0; } + int is_func() const { return (mod&FUNCTION)!=0; } + int is_cron() const { return (mod&CRON)!=0; } + int is_binary() const { return (mod&BINARY)!=0; } + int is_reference() const { return (mod&REFERENCE)!=0; } + int is_dbref() const { return (mod&DBREF)!=0; } + + tok_t(const sref& s,const sref& b,unsigned m) : name(s),body(b),mod(m) {} + virtual ~tok_t(); + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<32> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class reference_t : public tok_t { +public: + mstring data; + reference_t(const sref& s,const sref& b,unsigned m): + tok_t(s,b,m|REFERENCE|CONSTANT) {} + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<44> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class dbref_t : public tok_t { +public: + dbmap db; + dbref_t(const sref& s,const sref& b,unsigned m): + tok_t(s,b,m|DBREF|CONSTANT) {} + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<96> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class runnable_t : public tok_t { +public: + mstring arg; + runnable_t(const sref& s,const sref& b,unsigned m,const sref& a): + tok_t(s,b,m|RUNNABLE|CONSTANT),arg(a) {} + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<44> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class func_t : public runnable_t { +public: + expr_t x; + func_t(const sref& s,const sref& b,unsigned m,const sref& a): + runnable_t(s,b,m|FUNCTION,a) { x=body; } + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<80> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class cron_t : public func_t { +public: + int last,interval; + cron_t(const sref& s,const sref& b,unsigned m,int i): + func_t(s,b,m|CRON,ms_empty),last(0),interval(i) {} + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<88> tree; +}; + +/////////////////////////////////////////////////////////////////// + +class binary_t : public runnable_t { +public: + s_func_t func; + binary_t(const sref& s,const sref& b,unsigned m,const sref& a,s_func_t f): + runnable_t(s,b,m|BINARY,a),func(f) {} + + static void* operator new(size_t n); + static void operator delete(void* p); +private: + static uniheap<48> tree; +}; + +/////////////////////////////////////////////////////////////////// + +mstream& operator<<(mstream&,const runnable_t& ftok); +mstring Mode(const tok_t& tok); +mstring Desc(const tok_t& tok); + +/////////////////////////////////////////////////////////////////// + +struct tokmap { + typedef tok_t* val_t; + typedef mrvec map_t; + typedef map_t::iterator iterator; + typedef map_t::const_iterator const_iterator; + + struct comp_t { + sref key; + comp_t(const sref& skey) : key(skey) {} + bool operator()(val_t a,val_t b) const { + return (a?((const sref&)a->name):key)<(b?((const sref&)b->name):key); + } + }; + + tokmap* up; + map_t list; + + tokmap() : up(0),list() {} + tokmap(tokmap& p) : up(&p),list() {} + ~tokmap() { clear(); } + + iterator begin() { return list.begin(); } + const_iterator begin() const { return list.begin(); } + + iterator end() { return list.end(); } + const_iterator end() const { return list.end(); } + + void clear(); + iterator erase(iterator p) { delete *p; return list.erase(p); } + + tok_t* get_this(const sref& t); + const tok_t* get_this(const sref& t) const; + const tok_t* get(const sref& t) const; + tok_t* get(const sref& t); + + tok_t* set(tok_t* tok,int replace,int force) throw(merror_t); + tok_t* add(tok_t* tok,int replace,int force) throw(merror_t); +}; + +/////////////////////////////////////////////////////////////////// + +int is_tokname(const sref& s); +int is_arglist(const sref& s); + +tok_t* Cond(tokmap& map,const sref& s,const sref& b,unsigned m) throw(merror_t); +tok_t* Global(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t); +tok_t* Const(tokmap& map,const sref& s,const sref& b,unsigned m,int rpl=0) throw(merror_t); +func_t* Func(tokmap& map,const sref& s,const sref& b, + unsigned m,const sref& a,int rpl=0) throw(merror_t); +cron_t* Cron(tokmap& map,const sref& s,const sref& b, + unsigned m,int i,int rpl=0) throw(merror_t); +binary_t* Binary(tokmap& map,const sref& s,const sref& b, + unsigned m,const sref& a,s_func_t f,int rpl=0) throw(merror_t); +reference_t* Reference(tokmap& map,const sref& s,const sref& b, + unsigned m,int rpl=0) throw(merror_t); +dbref_t* DBRef(tokmap& map,const sref& s, + const sref& b,unsigned m,int rpl=0) throw(merror_t); +void ClearToken(tokmap& map,const sref& s) throw(merror_t); + +/////////////////////////////////////////////////////////////////// + +const int XPUBLIC=tok_t::RESTRICT; +const int XPRIVATE=XPUBLIC|tok_t::NORMAL; +const int XSYSTEM=XPRIVATE|tok_t::SYSONLY; +const int XINIT=tok_t::INIT; + +#endif -- cgit v1.2.3