aboutsummaryrefslogtreecommitdiff
path: root/tests/sauer/command.cpp
diff options
context:
space:
mode:
authorAlon Zakai <azakai@mozilla.com>2010-11-13 14:45:22 -0800
committerAlon Zakai <azakai@mozilla.com>2010-11-13 14:45:22 -0800
commit1b153b74662b198f99a1b7e21d8bc60f562f6feb (patch)
treec82af930836346c2da71d62ca10b5db749865e9f /tests/sauer/command.cpp
parentcc66f5d1e8e2aa9c0fc57a78b1b896f0a6cb8581 (diff)
rename sauer to cubescript
--HG-- rename : tests/sauer/README => tests/cubescript/README rename : tests/sauer/command.cpp => tests/cubescript/command.cpp rename : tests/sauer/command.h => tests/cubescript/command.h rename : tests/sauer/tools.h => tests/cubescript/tools.h
Diffstat (limited to 'tests/sauer/command.cpp')
-rw-r--r--tests/sauer/command.cpp1427
1 files changed, 0 insertions, 1427 deletions
diff --git a/tests/sauer/command.cpp b/tests/sauer/command.cpp
deleted file mode 100644
index cfd3d116..00000000
--- a/tests/sauer/command.cpp
+++ /dev/null
@@ -1,1427 +0,0 @@
-// command.cpp: implements the parsing and execution of a tiny script language which
-// is largely backwards compatible with the quake console language.
-
-// XXX Emscripten: changed all sizeof to ES_SIZEOF
-
-// XXX Emscripten
- #define STANDALONE
-
- #include "limits.h"
- #include "stdarg.h"
- #include "string.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include "ctype.h"
- #include "math.h"
- #include "time.h"
- #include <new>
-
- #include "tools.h"
- #include "command.h"
-
- // console
- enum
- {
- CON_INFO = 1<<0,
- CON_WARN = 1<<1,
- CON_ERROR = 1<<2,
- CON_DEBUG = 1<<3,
- CON_INIT = 1<<4,
- CON_ECHO = 1<<5
- };
- extern void conoutf(const char *s, ...);
- extern void conoutf(int type, const char *s, ...);
-
- // command
- extern int variable(const char *name, int min, int cur, int max, int *storage, void (*fun)(), int flags);
- extern float fvariable(const char *name, float min, float cur, float max, float *storage, void (*fun)(), int flags);
- extern char *svariable(const char *name, const char *cur, char **storage, void (*fun)(), int flags);
- extern void setvar(const char *name, int i, bool dofunc = true, bool doclamp = true);
- extern void setfvar(const char *name, float f, bool dofunc = true, bool doclamp = true);
- extern void setsvar(const char *name, const char *str, bool dofunc = true);
- extern void setvarchecked(ident *id, int val);
- extern void setfvarchecked(ident *id, float val);
- extern void setsvarchecked(ident *id, const char *val);
- extern void touchvar(const char *name);
- extern int getvar(const char *name);
- extern int getvarmin(const char *name);
- extern int getvarmax(const char *name);
- extern bool identexists(const char *name);
- extern ident *getident(const char *name);
- extern ident *newident(const char *name);
- extern bool addcommand(const char *name, void (*fun)(), const char *narg);
- extern int execute(const char *p);
- extern char *executeret(const char *p);
- extern bool execfile(const char *cfgfile, bool msg = true);
- extern void alias(const char *name, const char *action);
- extern const char *getalias(const char *name);
-
- // main
- extern void fatal(const char *s, ...);
-
- extern char *path(char *s);
- extern char *path(const char *s, bool copy);
- extern const char *parentdir(const char *directory);
- extern bool fileexists(const char *path, const char *mode);
- extern bool createdir(const char *path);
- extern size_t fixpackagedir(char *dir);
- extern void sethomedir(const char *dir);
- extern void addpackagedir(const char *dir);
- extern const char *findfile(const char *filename, const char *mode);
- extern stream *openrawfile(const char *filename, const char *mode);
- extern stream *openzipfile(const char *filename, const char *mode);
- extern stream *openfile(const char *filename, const char *mode);
- extern stream *opentempfile(const char *filename, const char *mode);
- extern char *loadfile(const char *fn, int *size);
- extern bool listdir(const char *dir, const char *ext, vector<char *> &files);
- extern int listfiles(const char *dir, const char *ext, vector<char *> &files);
- extern int listzipfiles(const char *dir, const char *ext, vector<char *> &files);
- extern void seedMT(uint seed);
- extern uint randomMT(void);
-// XXX =========================
-
-char *exchangestr(char *o, const char *n) { delete[] o; return newstring(n); }
-
-typedef hashtable<const char *, ident> identtable;
-
-identtable *idents = NULL; // contains ALL vars/commands/aliases
-
-bool overrideidents = false, persistidents = true;
-
-void clearstack(ident &id)
-{
- identstack *stack = id.stack;
- while(stack)
- {
- delete[] stack->action;
- identstack *tmp = stack;
- stack = stack->next;
- delete tmp;
- }
- id.stack = NULL;
-}
-
-void clear_command()
-{
- enumerate(*idents, ident, i, if(i.type==ID_ALIAS) { DELETEA(i.name); DELETEA(i.action); if(i.stack) clearstack(i); });
- if(idents) idents->clear();
-}
-
-void clearoverride(ident &i)
-{
- if(i.override==NO_OVERRIDE) return;
- switch(i.type)
- {
- case ID_ALIAS:
- if(i.action[0])
- {
- if(i.action != i.isexecuting) delete[] i.action;
- i.action = newstring("");
- }
- break;
- case ID_VAR:
- *i.storage.i = i.overrideval.i;
- i.changed();
- break;
- case ID_FVAR:
- *i.storage.f = i.overrideval.f;
- i.changed();
- break;
- case ID_SVAR:
- delete[] *i.storage.s;
- *i.storage.s = i.overrideval.s;
- i.changed();
- break;
- }
- i.override = NO_OVERRIDE;
-}
-
-void clearoverrides()
-{
- enumerate(*idents, ident, i, clearoverride(i));
-}
-
-void pushident(ident &id, char *val)
-{
- if(id.type != ID_ALIAS) return;
- identstack *stack = new identstack;
- stack->action = id.isexecuting==id.action ? newstring(id.action) : id.action;
- stack->next = id.stack;
- id.stack = stack;
- id.action = val;
-}
-
-void popident(ident &id)
-{
- if(id.type != ID_ALIAS || !id.stack) return;
- if(id.action != id.isexecuting) delete[] id.action;
- identstack *stack = id.stack;
- id.action = stack->action;
- id.stack = stack->next;
- delete stack;
-}
-
-ident *newident(const char *name)
-{
- ident *id = idents->access(name);
- if(!id)
- {
- ident init(ID_ALIAS, newstring(name), newstring(""), persistidents ? IDF_PERSIST : 0);
- id = &idents->access(init.name, init);
- }
- return id;
-}
-
-void pusha(const char *name, char *action)
-{
- pushident(*newident(name), action);
-}
-
-void push(char *name, char *action)
-{
- pusha(name, newstring(action));
-}
-
-void pop(char *name)
-{
- ident *id = idents->access(name);
- if(id) popident(*id);
-}
-
-void resetvar(char *name)
-{
- ident *id = idents->access(name);
- if(!id) return;
- if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
- else clearoverride(*id);
-}
-
-COMMAND(push, "ss");
-COMMAND(pop, "s");
-COMMAND(resetvar, "s");
-
-void aliasa(const char *name, char *action)
-{
- ident *b = idents->access(name);
- if(!b)
- {
- ident b(ID_ALIAS, newstring(name), action, persistidents ? IDF_PERSIST : 0);
- if(overrideidents) b.override = OVERRIDDEN;
- idents->access(b.name, b);
- }
- else if(b->type != ID_ALIAS)
- {
- conoutf(CON_ERROR, "cannot redefine builtin %s with an alias", name);
- delete[] action;
- }
- else
- {
- if(b->action != b->isexecuting) delete[] b->action;
- b->action = action;
- if(overrideidents) b->override = OVERRIDDEN;
- else
- {
- if(b->override != NO_OVERRIDE) b->override = NO_OVERRIDE;
- if(persistidents)
- {
- if(!(b->flags & IDF_PERSIST)) b->flags |= IDF_PERSIST;
- }
- else if(b->flags & IDF_PERSIST) b->flags &= ~IDF_PERSIST;
- }
- }
-}
-
-void alias(const char *name, const char *action) { aliasa(name, newstring(action)); }
-
-COMMAND(alias, "ss");
-
-// variable's and commands are registered through globals, see cube.h
-
-int variable(const char *name, int min, int cur, int max, int *storage, void (*fun)(), int flags)
-{
- if(!idents) idents = new identtable;
- ident v(ID_VAR, name, min, cur, max, storage, (void *)fun, flags);
- idents->access(name, v);
- return cur;
-}
-
-float fvariable(const char *name, float min, float cur, float max, float *storage, void (*fun)(), int flags)
-{
- if(!idents) idents = new identtable;
- ident v(ID_FVAR, name, min, cur, max, storage, (void *)fun, flags);
- idents->access(name, v);
- return cur;
-}
-
-char *svariable(const char *name, const char *cur, char **storage, void (*fun)(), int flags)
-{
- if(!idents) idents = new identtable;
- ident v(ID_SVAR, name, newstring(cur), storage, (void *)fun, flags);
- idents->access(name, v);
- return v.val.s;
-}
-
-#define _GETVAR(id, vartype, name, retval) \
- ident *id = idents->access(name); \
- if(!id || id->type!=vartype) return retval;
-#define GETVAR(id, name, retval) _GETVAR(id, ID_VAR, name, retval)
-#define OVERRIDEVAR(errorval, saveval, resetval, clearval) \
- if(overrideidents || id->flags&IDF_OVERRIDE) \
- { \
- if(id->flags&IDF_PERSIST) \
- { \
- conoutf(CON_ERROR, "cannot override persistent variable %s", id->name); \
- errorval; \
- } \
- if(id->override==NO_OVERRIDE) { saveval; id->override = OVERRIDDEN; } \
- else { clearval; } \
- } \
- else \
- { \
- if(id->override!=NO_OVERRIDE) { resetval; id->override = NO_OVERRIDE; } \
- clearval; \
- }
-
-void setvar(const char *name, int i, bool dofunc, bool doclamp)
-{
- GETVAR(id, name, );
- OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
- if(doclamp) *id->storage.i = clamp(i, id->minval, id->maxval);
- else *id->storage.i = i;
- if(dofunc) id->changed();
-}
-void setfvar(const char *name, float f, bool dofunc, bool doclamp)
-{
- _GETVAR(id, ID_FVAR, name, );
- OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
- if(doclamp) *id->storage.f = clamp(f, id->minvalf, id->maxvalf);
- else *id->storage.f = f;
- if(dofunc) id->changed();
-}
-void setsvar(const char *name, const char *str, bool dofunc)
-{
- _GETVAR(id, ID_SVAR, name, );
- OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
- *id->storage.s = newstring(str);
- if(dofunc) id->changed();
-}
-int getvar(const char *name)
-{
- GETVAR(id, name, 0);
- return *id->storage.i;
-}
-int getvarmin(const char *name)
-{
- GETVAR(id, name, 0);
- return id->minval;
-}
-int getvarmax(const char *name)
-{
- GETVAR(id, name, 0);
- return id->maxval;
-}
-bool identexists(const char *name) { return idents->access(name)!=NULL; }
-ident *getident(const char *name) { return idents->access(name); }
-
-void touchvar(const char *name)
-{
- ident *id = idents->access(name);
- if(id) switch(id->type)
- {
- case ID_VAR:
- case ID_FVAR:
- case ID_SVAR:
- id->changed();
- break;
- }
-}
-
-const char *getalias(const char *name)
-{
- ident *i = idents->access(name);
- return i && i->type==ID_ALIAS ? i->action : "";
-}
-
-void setvarchecked(ident *id, int val)
-{
- if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
-#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
-#else
- else
-#endif
- {
- OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
- if(val<id->minval || val>id->maxval)
- {
- val = val<id->minval ? id->minval : id->maxval; // clamp to valid range
- conoutf(CON_ERROR,
- id->flags&IDF_HEX ?
- (id->minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
- "valid range for %s is %d..%d",
- id->name, id->minval, id->maxval);
- }
- *id->storage.i = val;
- id->changed(); // call trigger function if available
-#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
-#endif
- }
-}
-
-void setfvarchecked(ident *id, float val)
-{
- if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
-#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
-#else
- else
-#endif
- {
- OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
- if(val<id->minvalf || val>id->maxvalf)
- {
- val = val<id->minvalf ? id->minvalf : id->maxvalf; // clamp to valid range
- conoutf(CON_ERROR, "valid range for %s is %s..%s", id->name, floatstr(id->minvalf), floatstr(id->maxvalf));
- }
- *id->storage.f = val;
- id->changed();
-#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
-#endif
- }
-}
-
-void setsvarchecked(ident *id, const char *val)
-{
- if(id->flags&IDF_READONLY) conoutf(CON_ERROR, "variable %s is read-only", id->name);
-#ifndef STANDALONE
- else if(!(id->flags&IDF_OVERRIDE) || overrideidents || game::allowedittoggle())
-#else
- else
-#endif
- {
- OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
- *id->storage.s = newstring(val);
- id->changed();
-#ifndef STANDALONE
- if(id->flags&IDF_OVERRIDE && !overrideidents) game::vartrigger(id);
-#endif
- }
-}
-
-bool addcommand(const char *name, void (*fun)(), const char *narg)
-{
- if(!idents) idents = new identtable;
- ident c(ID_COMMAND, name, narg, (void *)fun);
- idents->access(name, c);
- return false;
-}
-
-void addident(const char *name, ident *id)
-{
- if(!idents) idents = new identtable;
- idents->access(name, *id);
-}
-
-static vector<vector<char> *> wordbufs;
-static int bufnest = 0;
-
-char *parseexp(const char *&p, int right);
-
-void parsemacro(const char *&p, int level, vector<char> &wordbuf)
-{
- int escape = 1;
- while(*p=='@') p++, escape++;
- if(level > escape)
- {
- while(escape--) wordbuf.add('@');
- return;
- }
- if(*p=='(')
- {
- char *ret = parseexp(p, ')');
- if(ret)
- {
- for(char *sub = ret; *sub; ) wordbuf.add(*sub++);
- delete[] ret;
- }
- return;
- }
- static vector<char> ident;
- ident.setsize(0);
- while(isalnum(*p) || *p=='_') ident.add(*p++);
- ident.add(0);
- const char *alias = getalias(ident.getbuf());
- while(*alias) wordbuf.add(*alias++);
-}
-
-const char *parsestring(const char *p)
-{
- for(; *p; p++) switch(*p)
- {
- case '\r':
- case '\n':
- case '\"':
- return p;
- case '^':
- if(*++p) break;
- return p;
- }
- return p;
-}
-
-int escapestring(char *dst, const char *src, const char *end)
-{
- char *start = dst;
- while(src < end)
- {
- int c = *src++;
- if(c == '^')
- {
- if(src >= end) break;
- int e = *src++;
- switch(e)
- {
- case 'n': *dst++ = '\n'; break;
- case 't': *dst++ = '\t'; break;
- case 'f': *dst++ = '\f'; break;
- default: *dst++ = e; break;
- }
- }
- else *dst++ = c;
- }
- return dst - start;
-}
-
-char *parseexp(const char *&p, int right) // parse any nested set of () or []
-{
- if(bufnest++>=wordbufs.length()) wordbufs.add(new vector<char>);
- vector<char> &wordbuf = *wordbufs[bufnest-1];
- int left = *p++;
- for(int brak = 1; brak; )
- {
- size_t n = strcspn(p, "\r@\"/()[]");
- wordbuf.put(p, n);
- p += n;
-
- int c = *p++;
- switch(c)
- {
- case '\r': continue;
- case '@':
- if(left == '[') { parsemacro(p, brak, wordbuf); continue; }
- break;
- case '\"':
- {
- wordbuf.add(c);
- const char *end = parsestring(p);
- wordbuf.put(p, end - p);
- p = end;
- if(*p=='\"') wordbuf.add(*p++);
- continue;
- }
- case '/':
- if(*p=='/')
- {
- p += strcspn(p, "\n\0");
- continue;
- }
- break;
- case '\0':
- p--;
- conoutf(CON_ERROR, "missing \"%c\"", right);
- wordbuf.setsize(0);
- bufnest--;
- return NULL;
- case '(': case '[': if(c==left) brak++; break;
- case ')': case ']': if(c==right) brak--; break;
- }
- wordbuf.add(c);
- }
- wordbuf.pop();
- char *s;
- if(left=='(')
- {
- wordbuf.add(0);
- char *ret = executeret(wordbuf.getbuf()); // evaluate () exps directly, and substitute result
- wordbuf.pop();
- s = ret ? ret : newstring("");
- }
- else
- {
- s = newstring(wordbuf.getbuf(), wordbuf.length());
- }
- wordbuf.setsize(0);
- bufnest--;
- return s;
-}
-
-char *lookup(char *n) // find value of ident referenced with $ in exp
-{
- ident *id = idents->access(n+1);
- if(id) switch(id->type)
- {
- case ID_VAR: return exchangestr(n, intstr(*id->storage.i));
- case ID_FVAR: return exchangestr(n, floatstr(*id->storage.f));
- case ID_SVAR: return exchangestr(n, *id->storage.s);
- case ID_ALIAS: return exchangestr(n, id->action);
- }
- conoutf(CON_ERROR, "unknown alias lookup: %s", n+1);
- return n;
-}
-
-char *parseword(const char *&p, int arg, int &infix) // parse single argument, including expressions
-{
- for(;;)
- {
- p += strspn(p, " \t\r");
- if(p[0]!='/' || p[1]!='/') break;
- p += strcspn(p, "\n\0");
- }
- if(*p=='\"')
- {
- p++;
- const char *end = parsestring(p);
- char *s = newstring(end - p);
- s[escapestring(s, p, end)] = '\0';
- p = end;
- if(*p=='\"') p++;
- return s;
- }
- if(*p=='(') return parseexp(p, ')');
- if(*p=='[') return parseexp(p, ']');
- const char *word = p;
- for(;;)
- {
- p += strcspn(p, "/; \t\r\n\0");
- if(p[0]!='/' || p[1]=='/') break;
- else if(p[1]=='\0') { p++; break; }
- p += 2;
- }
- if(p-word==0) return NULL;
- if(arg==1 && p-word==1) switch(*word)
- {
- case '=': infix = *word; break;
- }
- char *s = newstring(word, p-word);
- if(*s=='$') return lookup(s); // substitute variables
- return s;
-}
-
-char *conc(char **w, int n, bool space)
-{
- int len = space ? max(n-1, 0) : 0;
- loopj(n) len += (int)strlen(w[j]);
- char *r = newstring("", len);
- loopi(n)
- {
- strcat(r, w[i]); // make string-list out of all arguments
- if(i==n-1) break;
- if(space) strcat(r, " ");
- }
- return r;
-}
-
-VARN(numargs, _numargs, 25, 0, 0);
-
-static inline bool isinteger(char *c)
-{
- return isdigit(c[0]) || ((c[0]=='+' || c[0]=='-' || c[0]=='.') && isdigit(c[1]));
-}
-
-char *commandret = NULL;
-
-char *executeret(const char *p) // all evaluation happens here, recursively
-{
- const int MAXWORDS = 25; // limit, remove
- char *w[MAXWORDS];
- char *retval = NULL;
- #define setretval(v) { char *rv = v; if(rv) retval = rv; }
- for(bool cont = true; cont;) // for each ; seperated statement
- {
- int numargs = MAXWORDS, infix = 0;
- loopi(MAXWORDS) // collect all argument values
- {
- w[i] = parseword(p, i, infix); // parse and evaluate exps
- if(!w[i]) { numargs = i; break; }
- }
-
- p += strcspn(p, ";\n\0");
- cont = *p++!=0; // more statements if this isn't the end of the string
- char *c = w[0];
- if(!c || !*c) continue; // empty statement
-
- DELETEA(retval);
-
- if(infix)
- {
- switch(infix)
- {
- case '=':
- aliasa(c, numargs>2 ? w[2] : newstring(""));
- w[2] = NULL;
- break;
- }
- }
- else
- {
- ident *id = idents->access(c);
- if(!id)
- {
- if(!isinteger(c))
- conoutf(CON_ERROR, "unknown command: %s", c);
- setretval(newstring(c));
- }
- else switch(id->type)
- {
- case ID_CCOMMAND:
- case ID_COMMAND: // game defined commands
- {
- void *v[MAXWORDS];
- union
- {
- int i;
- float f;
- } nstor[MAXWORDS];
- int n = 0, wn = 0;
- char *cargs = NULL;
- if(id->type==ID_CCOMMAND) v[n++] = id->self;
- for(const char *a = id->narg; *a; a++, n++) switch(*a)
- {
- case 's': v[n] = ++wn < numargs ? w[wn] : (char *)""; break;
- case 'i': nstor[n].i = ++wn < numargs ? parseint(w[wn]) : 0; v[n] = &nstor[n].i; break;
- case 'f': nstor[n].f = ++wn < numargs ? parsefloat(w[wn]) : 0.0f; v[n] = &nstor[n].f; break;
-#ifndef STANDALONE
- case 'D': nstor[n].i = addreleaseaction(id->name) ? 1 : 0; v[n] = &nstor[n].i; break;
-#endif
- case 'V': v[n++] = w+1; nstor[n].i = numargs-1; v[n] = &nstor[n].i; break;
- case 'C': if(!cargs) cargs = conc(w+1, numargs-1, true); v[n] = cargs; break;
- default: fatal("builtin declared with illegal type");
- }
- switch(n)
- {
- case 0: ((void (__cdecl *)() )id->fun)(); break;
- case 1: ((void (__cdecl *)(void *) )id->fun)(v[0]); break;
- case 2: ((void (__cdecl *)(void *, void *) )id->fun)(v[0], v[1]); break;
- case 3: ((void (__cdecl *)(void *, void *, void *) )id->fun)(v[0], v[1], v[2]); break;
- case 4: ((void (__cdecl *)(void *, void *, void *, void *) )id->fun)(v[0], v[1], v[2], v[3]); break;
- case 5: ((void (__cdecl *)(void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4]); break;
- case 6: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5]); break;
- case 7: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); break;
- case 8: ((void (__cdecl *)(void *, void *, void *, void *, void *, void *, void *, void *))id->fun)(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); break;
- default: fatal("builtin declared with too many args (use V?)");
- }
- if(cargs) delete[] cargs;
- setretval(commandret);
- commandret = NULL;
- break;
- }
-
- case ID_VAR: // game defined variables
- if(numargs <= 1)
- {
- if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
- conoutf("%s = 0x%.6X (%d, %d, %d)", c, *id->storage.i, (*id->storage.i>>16)&0xFF, (*id->storage.i>>8)&0xFF, *id->storage.i&0xFF);
- else
- conoutf(id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", c, *id->storage.i); // var with no value just prints its current value
- }
- else
- {
- int val = parseint(w[1]);
- if(id->flags&IDF_HEX && numargs > 2)
- {
- val <<= 16;
- val |= parseint(w[2])<<8;
- if(numargs > 3) val |= parseint(w[3]);
- }
- setvarchecked(id, val);
- }
- break;
-
- case ID_FVAR:
- if(numargs <= 1) conoutf("%s = %s", c, floatstr(*id->storage.f));
- else setfvarchecked(id, parsefloat(w[1]));
- break;
-
- case ID_SVAR:
- if(numargs <= 1) conoutf(strchr(*id->storage.s, '"') ? "%s = [%s]" : "%s = \"%s\"", c, *id->storage.s);
- else setsvarchecked(id, w[1]);
- break;
-
- case ID_ALIAS: // alias, also used as functions and (global) variables
- {
- delete[] w[0];
- static vector<ident *> argids;
- for(int i = 1; i<numargs; i++)
- {
- if(i > argids.length())
- {
- defformatstring(argname)("arg%d", i);
- argids.add(newident(argname));
- }
- pushident(*argids[i-1], w[i]); // set any arguments as (global) arg values so functions can access them
- }
- _numargs = numargs-1;
- bool wasoverriding = overrideidents;
- if(id->override!=NO_OVERRIDE) overrideidents = true;
- char *wasexecuting = id->isexecuting;
- id->isexecuting = id->action;
- setretval(executeret(id->action));
- if(id->isexecuting != id->action && id->isexecuting != wasexecuting) delete[] id->isexecuting;
- id->isexecuting = wasexecuting;
- overrideidents = wasoverriding;
- for(int i = 1; i<numargs; i++) popident(*argids[i-1]);
- continue;
- }
- }
- }
- loopj(numargs) if(w[j]) delete[] w[j];
- }
- return retval;
-}
-
-int execute(const char *p)
-{
- char *ret = executeret(p);
- int i = 0;
- if(ret) { i = parseint(ret); delete[] ret; }
- return i;
-}
-
-#ifndef STANDALONE
-static int sortidents(ident **x, ident **y)
-{
- return strcmp((*x)->name, (*y)->name);
-}
-
-void writeescapedstring(stream *f, const char *s)
-{
- f->putchar('"');
- for(; *s; s++) switch(*s)
- {
- case '\n': f->write("^n", 2); break;
- case '\t': f->write("^t", 2); break;
- case '\f': f->write("^f", 2); break;
- case '"': f->write("^\"", 2); break;
- default: f->putchar(*s); break;
- }
- f->putchar('"');
-}
-#endif
-
-// below the commands that implement a small imperative language. thanks to the semantics of
-// () and [] expressions, any control construct can be defined trivially.
-
-static string retbuf[3];
-static int retidx = 0;
-
-const char *intstr(int v)
-{
- retidx = (retidx + 1)%3;
- formatstring(retbuf[retidx])("%d", v);
- return retbuf[retidx];
-}
-
-void intret(int v)
-{
- commandret = newstring(intstr(v));
-}
-
-const char *floatstr(float v)
-{
- retidx = (retidx + 1)%3;
- formatstring(retbuf[retidx])(v==int(v) ? "%.1f" : "%.7g", v);
- return retbuf[retidx];
-}
-
-void floatret(float v)
-{
- commandret = newstring(floatstr(v));
-}
-
-#undef ICOMMANDNAME
-#define ICOMMANDNAME(name) _stdcmd
-
-ICOMMAND(if, "sss", (char *cond, char *t, char *f), commandret = executeret(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f));
-ICOMMAND(?, "sss", (char *cond, char *t, char *f), result(cond[0] && (!isinteger(cond) || parseint(cond)) ? t : f));
-ICOMMAND(loop, "sis", (char *var, int *n, char *body),
-{
- if(*n<=0) return;
- ident *id = newident(var);
- if(id->type!=ID_ALIAS) return;
- loopi(*n)
- {
- if(i) sprintf(id->action, "%d", i);
- else pushident(*id, newstring("0", 16));
- execute(body);
- }
- popident(*id);
-});
-ICOMMAND(loopwhile, "siss", (char *var, int *n, char *cond, char *body),
-{
- if(*n<=0) return;
- ident *id = newident(var);
- if(id->type!=ID_ALIAS) return;
- loopi(*n)
- {
- if(i) sprintf(id->action, "%d", i);
- else pushident(*id, newstring("0", 16));
- if(!execute(cond)) break;
- execute(body);
- }
- popident(*id);
-});
-ICOMMAND(while, "ss", (char *cond, char *body), while(execute(cond)) execute(body)); // can't get any simpler than this :)
-
-void concat(const char *s) { commandret = newstring(s); }
-void result(const char *s) { commandret = newstring(s); }
-
-void concatword(char **args, int *numargs)
-{
- commandret = conc(args, *numargs, false);
-}
-
-void format(char **args, int *numargs)
-{
- vector<char> s;
- char *f = args[0];
- while(*f)
- {
- int c = *f++;
- if(c == '%')
- {
- int i = *f++;
- if(i >= '1' && i <= '9')
- {
- i -= '0';
- const char *sub = i < *numargs ? args[i] : "";
- while(*sub) s.add(*sub++);
- }
- else s.add(i);
- }
- else s.add(c);
- }
- s.add('\0');
- result(s.getbuf());
-}
-
-#define whitespaceskip s += strspn(s, "\n\t ")
-#define elementskip *s=='"' ? (++s, s += strcspn(s, "\"\n\0"), s += *s=='"') : s += strcspn(s, "\n\t \0")
-
-void explodelist(const char *s, vector<char *> &elems)
-{
- whitespaceskip;
- while(*s)
- {
- const char *elem = s;
- elementskip;
- elems.add(*elem=='"' ? newstring(elem+1, s-elem-(s[-1]=='"' ? 2 : 1)) : newstring(elem, s-elem));
- whitespaceskip;
- }
-}
-
-char *indexlist(const char *s, int pos)
-{
- whitespaceskip;
- loopi(pos)
- {
- elementskip;
- whitespaceskip;
- if(!*s) break;
- }
- const char *e = s;
- elementskip;
- if(*e=='"')
- {
- e++;
- if(s[-1]=='"') --s;
- }
- return newstring(e, s-e);
-}
-
-int listlen(const char *s)
-{
- int n = 0;
- whitespaceskip;
- for(; *s; n++) elementskip, whitespaceskip;
- return n;
-}
-
-void at(char *s, int *pos)
-{
- commandret = indexlist(s, *pos);
-}
-
-void substr(char *s, int *start, char *count)
-{
- int len = strlen(s), offset = clamp(*start, 0, len);
- commandret = newstring(&s[offset], count[0] ? clamp(parseint(count), 0, len - offset) : len - offset);
-}
-
-void getalias_(char *s)
-{
- result(getalias(s));
-}
-
-COMMAND(concat, "C");
-COMMAND(result, "s");
-COMMAND(concatword, "V");
-COMMAND(format, "V");
-COMMAND(at, "si");
-COMMAND(substr, "sis");
-ICOMMAND(listlen, "s", (char *s), intret(listlen(s)));
-COMMANDN(getalias, getalias_, "s");
-
-void looplist(const char *var, const char *list, const char *body, bool search)
-{
- ident *id = newident(var);
- if(id->type!=ID_ALIAS) { if(search) intret(-1); return; }
- int n = 0;
- for(const char *s = list;;)
- {
- whitespaceskip;
- if(!*s) { if(search) intret(-1); break; }
- const char *start = s;
- elementskip;
- const char *end = s;
- if(*start=='"') { start++; if(end[-1]=='"') --end; }
- char *val = newstring(start, end-start);
- if(n++) aliasa(id->name, val);
- else pushident(*id, val);
- if(execute(body) && search) { intret(n-1); break; }
- }
- if(n) popident(*id);
-}
-
-void prettylist(const char *s, const char *conj)
-{
- vector<char> p;
- whitespaceskip;
- for(int len = listlen(s), n = 0; *s; n++)
- {
- const char *elem = s;
- elementskip;
- p.put(elem, s - elem);
- if(n+1 < len)
- {
- if(len > 2 || !conj[0]) p.add(',');
- if(n+2 == len && conj[0])
- {
- p.add(' ');
- p.put(conj, strlen(conj));
- }
- p.add(' ');
- }
- whitespaceskip;
- }
- p.add('\0');
- result(p.getbuf());
-}
-COMMAND(prettylist, "ss");
-
-int listincludes(const char *list, const char *needle, int needlelen)
-{
- const char *s = list;
- whitespaceskip;
- int offset = 0;
- while(*s)
- {
- const char *elem = s;
- elementskip;
- int len = s-elem;
- if(*elem=='"')
- {
- elem++;
- len -= s[-1]=='"' ? 2 : 1;
- }
- if(needlelen == len && !strncmp(needle, elem, len)) return offset;
- whitespaceskip;
- offset++;
- }
- return -1;
-}
-
-char *listdel(const char *s, const char *del)
-{
- vector<char> p;
- whitespaceskip;
- while(*s)
- {
- const char *elem = s;
- elementskip;
- int len = s-elem;
- if(*elem=='"')
- {
- elem++;
- len -= s[-1]=='"' ? 2 : 1;
- }
- if(listincludes(del, elem, len) < 0)
- {
- if(!p.empty()) p.add(' ');
- p.put(elem, len);
- }
- whitespaceskip;
- }
- p.add('\0');
- return newstring(p.getbuf());
-}
-
-ICOMMAND(listdel, "ss", (char *list, char *del), commandret = l