diff options
author | Alon Zakai <azakai@mozilla.com> | 2010-11-13 14:45:22 -0800 |
---|---|---|
committer | Alon Zakai <azakai@mozilla.com> | 2010-11-13 14:45:22 -0800 |
commit | 1b153b74662b198f99a1b7e21d8bc60f562f6feb (patch) | |
tree | c82af930836346c2da71d62ca10b5db749865e9f /tests/cubescript | |
parent | cc66f5d1e8e2aa9c0fc57a78b1b896f0a6cb8581 (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/cubescript')
-rw-r--r-- | tests/cubescript/README | 32 | ||||
-rw-r--r-- | tests/cubescript/command.cpp | 1427 | ||||
-rw-r--r-- | tests/cubescript/command.h | 204 | ||||
-rw-r--r-- | tests/cubescript/tools.h | 878 |
4 files changed, 2541 insertions, 0 deletions
diff --git a/tests/cubescript/README b/tests/cubescript/README new file mode 100644 index 00000000..c3a09528 --- /dev/null +++ b/tests/cubescript/README @@ -0,0 +1,32 @@ +This directory contains files from Sauerbraten/Cube 2, http://sauerbraten.org/, whose license is: + +Sauerbraten source code license, usage, and documentation. + +You may use the Sauerbraten source code if you abide by the ZLIB license +http://www.opensource.org/licenses/zlib-license.php +(very similar to the BSD license): + + +LICENSE +======= + +Sauerbraten game engine source code, any release. + +Copyright (C) 2001-2010 Wouter van Oortmerssen, Lee Salzman, Mike Dysart, Robert Pointon, and Quinton Reeves + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/tests/cubescript/command.cpp b/tests/cubescript/command.cpp new file mode 100644 index 00000000..cfd3d116 --- /dev/null +++ b/tests/cubescript/command.cpp @@ -0,0 +1,1427 @@ +// 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 = listdel(list, del)); +ICOMMAND(indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem)))); +ICOMMAND(listfind, "sss", (char *var, char *list, char *body), looplist(var, list, body, true)); +ICOMMAND(looplist, "sss", (char *var, char *list, char *body), looplist(var, list, body, false)); + +ICOMMAND(+, "ii", (int *a, int *b), intret(*a + *b)); +ICOMMAND(*, "ii", (int *a, int *b), intret(*a * *b)); +ICOMMAND(-, "ii", (int *a, int *b), intret(*a - *b)); +ICOMMAND(+f, "ff", (float *a, float *b), floatret(*a + *b)); +ICOMMAND(*f, "ff", (float *a, float *b), floatret(*a * *b)); +ICOMMAND(-f, "ff", (float *a, float *b), floatret(*a - *b)); +ICOMMAND(=, "ii", (int *a, int *b), intret((int)(*a == *b))); +ICOMMAND(!=, "ii", (int *a, int *b), intret((int)(*a != *b))); +ICOMMAND(<, "ii", (int *a, int *b), intret((int)(*a < *b))); +ICOMMAND(>, "ii", (int *a, int *b), intret((int)(*a > *b))); +ICOMMAND(<=, "ii", (int *a, int *b), intret((int)(*a <= *b))); +ICOMMAND(>=, "ii", (int *a, int *b), intret((int)(*a >= *b))); +ICOMMAND(=f, "ff", (float *a, float *b), intret((int)(*a == *b))); +ICOMMAND(!=f, "ff", (float *a, float *b), intret((int)(*a != *b))); +ICOMMAND(<f, "ff", (float *a, float *b), intret((int)(*a < *b))); +ICOMMAND(>f, "ff", (float *a, float *b), intret((int)(*a > *b))); +ICOMMAND(<=f, "ff", (float *a, float *b), intret((int)(*a <= *b))); +ICOMMAND(>=f, "ff", (float *a, float *b), intret((int)(*a >= *b))); +ICOMMAND(^, "ii", (int *a, int *b), intret(*a ^ *b)); +ICOMMAND(!, "i", (int *a), intret(*a == 0)); +ICOMMAND(&, "ii", (int *a, int *b), intret(*a & *b)); +ICOMMAND(|, "ii", (int *a, int *b), intret(*a | *b)); +ICOMMAND(~, "i", (int *a), intret(~*a)); +ICOMMAND(^~, "ii", (int *a, int *b), intret(*a ^ ~*b)); +ICOMMAND(&~, "ii", (int *a, int *b), intret(*a & ~*b)); +ICOMMAND(|~, "ii", (int *a, int *b), intret(*a | ~*b)); +ICOMMAND(<<, "ii", (int *a, int *b), intret(*a << *b)); +ICOMMAND(>>, "ii", (int *a, int *b), intret(*a >> *b)); +ICOMMAND(&&, "V", (char **args, int *numargs), +{ + int val = 1; + loopi(*numargs) { val = execute(args[i]); if(!val) break; } + intret(val); +}); +ICOMMAND(||, "V", (char **args, int *numargs), +{ + int val = 0; + loopi(*numargs) { val = execute(args[i]); if(val) break; } + intret(val); +}); + +ICOMMAND(div, "ii", (int *a, int *b), intret(*b ? *a / *b : 0)); +ICOMMAND(mod, "ii", (int *a, int *b), intret(*b ? *a % *b : 0)); +ICOMMAND(divf, "ff", (float *a, float *b), floatret(*b ? *a / *b : 0)); +ICOMMAND(modf, "ff", (float *a, float *b), floatret(*b ? fmod(*a, *b) : 0)); +ICOMMAND(sin, "f", (float *a), floatret(sin(*a*RAD))); +ICOMMAND(cos, "f", (float *a), floatret(cos(*a*RAD))); +ICOMMAND(tan, "f", (float *a), floatret(tan(*a*RAD))); +ICOMMAND(asin, "f", (float *a), floatret(asin(*a)/RAD)); +ICOMMAND(acos, "f", (float *a), floatret(acos(*a)/RAD)); +ICOMMAND(atan, "f", (float *a), floatret(atan(*a)/RAD)); +ICOMMAND(sqrt, "f", (float *a), floatret(sqrt(*a))); +ICOMMAND(pow, "ff", (float *a, float *b), floatret(pow(*a, *b))); +ICOMMAND(loge, "f", (float *a), floatret(log(*a))); +ICOMMAND(log2, "f", (float *a), floatret(log(*a)/M_LN2)); +ICOMMAND(log10, "f", (float *a), floatret(log10(*a))); +ICOMMAND(exp, "f", (float *a), floatret(exp(*a))); +ICOMMAND(min, "V", (char **args, int *numargs), +{ + int val = *numargs > 0 ? parseint(args[*numargs - 1]) : 0; + loopi(*numargs - 1) val = min(val, parseint(args[i])); + intret(val); +}); +ICOMMAND(max, "V", (char **args, int *numargs), +{ + int val = *numargs > 0 ? parseint(args[*numargs - 1]) : 0; + loopi(*numargs - 1) val = max(val, parseint(args[i])); + intret(val); +}); +ICOMMAND(minf, "V", (char **args, int *numargs), +{ + float val = *numargs > 0 ? parsefloat(args[*numargs - 1]) : 0.0f; + loopi(*numargs - 1) val = min(val, parsefloat(args[i])); + floatret(val); +}); +ICOMMAND(maxf, "V", (char **args, int *numargs), +{ + float val = *numargs > 0 ? parsefloat(args[*numargs - 1]) : 0.0f; + loopi(*numargs - 1) val = max(val, parsefloat(args[i])); + floatret(val); +}); + +ICOMMAND(cond, "V", (char **args, int *numargs), +{ + for(int i = 0; i < *numargs; i += 2) + { + if(execute(args[i])) + { + if(i+1 < *numargs) commandret = executeret(args[i+1]); + break; + } + } +}); +#define CASECOMMAND(name, fmt, type, compare) \ + ICOMMAND(name, fmt "V", (type *val, char **args, int *numargs), \ + { \ + int i; \ + for(i = 1; i+1 < *numargs; i += 2) \ + { \ + if(compare) \ + { \ + commandret = executeret(args[i+1]); \ + return; \ + } \ + } \ + if(i < *numargs) commandret = executeret(args[i]); \ + }) +CASECOMMAND(case, "i", int, parseint(args[i]) == *val); +CASECOMMAND(casef, "f", float, parsefloat(args[i]) == *val); +CASECOMMAND(cases, "s", char, !strcmp(args[i], val)); + +ICOMMAND(rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b)); +ICOMMAND(strcmp, "ss", (char *a, char *b), intret(strcmp(a,b)==0)); +ICOMMAND(=s, "ss", (char *a, char *b), intret(strcmp(a,b)==0)); +ICOMMAND(!=s, "ss", (char *a, char *b), intret(strcmp(a,b)!=0)); +ICOMMAND(<s, "ss", (char *a, char *b), intret(strcmp(a,b)<0)); +ICOMMAND(>s, "ss", (char *a, char *b), intret(strcmp(a,b)>0)); +ICOMMAND(<=s, "ss", (char *a, char *b), intret(strcmp(a,b)<=0)); +ICOMMAND(>=s, "ss", (char *a, char *b), intret(strcmp(a,b)>=0)); +ICOMMAND(echo, "C", (char *s), conoutf("%s", s)); +ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, s)); +ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); }); +ICOMMAND(strlen, "s", (char *s), intret(strlen(s))); + +char *strreplace(const char *s, const char *oldval, const char *newval) +{ + vector<char> buf; + + int oldlen = strlen(oldval); + if(!oldlen) return newstring(s); + for(;;) + { + const char *found = strstr(s, oldval); + if(found) + { + while(s < found) buf.add(*s++); + for(const char *n = newval; *n; n++) buf.add(*n); + s = found + oldlen; + } + else + { + while(*s) buf.add(*s++); + buf.add('\0'); + return newstring(buf.getbuf(), buf.length()); + } + } +} + +ICOMMAND(strreplace, "sss", (char *s, char *o, char *n), commandret = strreplace(s, o, n)); + +#ifndef STANDALONE +ICOMMAND(getmillis, "i", (int *total), intret(*total ? totalmillis : lastmillis)); + +struct sleepcmd +{ + int delay, millis; + char *command; + bool override, persist; +}; +vector<sleepcmd> sleepcmds; + +void addsleep(int *msec, char *cmd) +{ + sleepcmd &s = sleepcmds.add(); + s.delay = max(*msec, 1); + s.millis = lastmillis; + s.command = newstring(cmd); + s.override = overrideidents; + s.persist = persistidents; +} + +COMMANDN(sleep, addsleep, "is"); + +void checksleep(int millis) +{ + loopv(sleepcmds) + { + sleepcmd &s = sleepcmds[i]; + if(millis - s.millis >= s.delay) + { + char *cmd = s.command; // execute might create more sleep commands + s.command = NULL; + bool waspersisting = persistidents, wasoverriding = overrideidents; + persistidents = s.persist; + overrideidents = s.override; + execute(cmd); + persistidents = waspersisting; + overrideidents = wasoverriding; + delete[] cmd; + if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--); + } + } +} + +void clearsleep(bool clearoverrides) +{ + int len = 0; + loopv(sleepcmds) if(sleepcmds[i].command) + { + if(clearoverrides && !sleepcmds[i].override) sleepcmds[len++] = sleepcmds[i]; + else delete[] sleepcmds[i].command; + } + sleepcmds.shrink(len); +} + +void clearsleep_(int *clearoverrides) +{ + clearsleep(*clearoverrides!=0 || overrideidents); +} + +COMMANDN(clearsleep, clearsleep_, "i"); +#endif + + +// XXX Emscripten: console.cpp + +struct cline { char *line; int type, outtime; }; +vector<cline> conlines; + +int commandmillis = -1; +string commandbuf; +char *commandaction = NULL, *commandprompt = NULL; +int commandpos = -1; +int totalmillis = -1; // XXX Emscripten + +VARFP(maxcon, 10, 200, 1000, { while(conlines.length() > maxcon) delete[] conlines.pop().line; }); + +#define CONSTRLEN 512 + +void conline(int type, const char *sf) // add a line to the console buffer +{ + cline cl; + cl.line = conlines.length()>maxcon ? conlines.pop().line : newstring("", CONSTRLEN-1); // constrain the buffer size + cl.type = type; + cl.outtime = totalmillis; // for how long to keep line on screen + conlines.insert(0, cl); + copystring(cl.line, sf, CONSTRLEN); +} + +void conoutfv(int type, const char *fmt, va_list args) +{ + static char buf[CONSTRLEN]; + vformatstring(buf, fmt, args, ES_SIZEOF(char)*CONSTRLEN); // XXX Emscripten + conline(type, buf); + //filtertext(buf, buf); // XXX Emscripten + puts(buf); +} + +void conoutf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + conoutfv(CON_INFO, fmt, args); + va_end(args); +} + +void conoutf(int type, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + conoutfv(type, fmt, args); + va_end(args); +} +// XXX ======================================= + +// XXX Emscripten: tools.cpp + +#define N (624) +#define M (397) +#define K (0x9908B0DFU) +#define hiBit(u) ((u) & 0x80000000U) +#define loBit(u) ((u) & 0x00000001U) +#define loBits(u) ((u) & 0x7FFFFFFFU) +#define mixBits(u, v) (hiBit(u)|loBits(v)) + +static uint state[N+1]; +static uint *next; +static int left = -1; + +void seedMT(uint seed) +{ + register uint x = (seed | 1U) & 0xFFFFFFFFU, *s = state; + register int j; + for(left=0, *s++=x, j=N; --j; *s++ = (x*=69069U) & 0xFFFFFFFFU); +} + +uint reloadMT(void) +{ + register uint *p0=state, *p2=state+2, *pM=state+M, s0, s1; + register int j; + if(left < -1) seedMT(time(NULL)); + left=N-1, next=state+1; + for(s0=state[0], s1=state[1], j=N-M+1; --j; s0=s1, s1=*p2++) *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + for(pM=state, j=M; --j; s0=s1, s1=*p2++) *p0++ = *pM++ ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + s1=state[0], *p0 = *pM ^ (mixBits(s0, s1) >> 1) ^ (loBit(s1) ? K : 0U); + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9D2C5680U; + s1 ^= (s1 << 15) & 0xEFC60000U; + return(s1 ^ (s1 >> 18)); +} + +uint randomMT(void) +{ + uint y; + if(--left < 0) return(reloadMT()); + y = *next++; + y ^= (y >> 11); + y ^= (y << 7) & 0x9D2C5680U; + y ^= (y << 15) & 0xEFC60000U; + return(y ^ (y >> 18)); +} + +// XXX ============================================== + +// XXX Emscripten: main.cpp + +void fatal(const char *s, ...) // failure exit +{ + static int errors = 0; + errors++; + + if(errors <= 2) // print up to one extra recursive error + { + defvformatstring(msg,s,s); + puts(msg); + } + + exit(EXIT_FAILURE); +} + +VARP(somevar, 0, 0, 1024); + +int main() +{ + printf("*\n"); + execute("somevar 9"); + execute("temp = (+ 22 $somevar)"); + execute("if (> $temp 30) [ temp = (+ $temp 1) ] [ temp = (* $temp 2) ]"); + execute("if (< $temp 30) [ temp = 0 ] [ temp = (+ $temp 1) ]"); + execute("echo [Temp is] $temp"); + printf("%d\n", getvar("somevar")); + execute("x = 2"); + execute("push x 5"); + execute("push x 11"); + execute("pop x"); + execute("echo $x"); + execute("greet = [ echo hello, $arg1 ]"); + execute("greet everyone"); + printf("*\n"); + return 0; +} + +// XXX =============================== diff --git a/tests/cubescript/command.h b/tests/cubescript/command.h new file mode 100644 index 00000000..86b10885 --- /dev/null +++ b/tests/cubescript/command.h @@ -0,0 +1,204 @@ +// script binding functionality + + +enum { ID_VAR, ID_FVAR, ID_SVAR, ID_COMMAND, ID_CCOMMAND, ID_ALIAS }; + +enum { NO_OVERRIDE = INT_MAX, OVERRIDDEN = 0 }; + +enum { IDF_PERSIST = 1<<0, IDF_OVERRIDE = 1<<1, IDF_HEX = 1<<2, IDF_READONLY = 1<<3 }; + +struct identstack +{ + char *action; + identstack *next; +}; + +union identval +{ + int i; // ID_VAR + float f; // ID_FVAR + char *s; // ID_SVAR +}; + +union identvalptr +{ + int *i; // ID_VAR + float *f; // ID_FVAR + char **s; // ID_SVAR +}; + +struct ident +{ + int type; // one of ID_* above + const char *name; + union + { + int minval; // ID_VAR + float minvalf; // ID_FVAR + }; + union + { + int maxval; // ID_VAR + float maxvalf; // ID_FVAR + }; + int override; // either NO_OVERRIDE, OVERRIDDEN, or value + union + { + void (__cdecl *fun)(); // ID_VAR, ID_COMMAND, ID_CCOMMAND + identstack *stack; // ID_ALIAS + }; + union + { + const char *narg; // ID_COMMAND, ID_CCOMMAND + char *action; // ID_ALIAS + identval val; // ID_VAR, ID_FVAR, ID_SVAR + }; + union + { + void *self; // ID_COMMAND, ID_CCOMMAND + char *isexecuting; // ID_ALIAS + identval overrideval; // ID_VAR, ID_FVAR, ID_SVAR + }; + identvalptr storage; // ID_VAR, ID_FVAR, ID_SVAR + int flags; + + ident() {} + // ID_VAR + ident(int t, const char *n, int m, int c, int x, int *s, void *f = NULL, int flags = 0) + : type(t), name(n), minval(m), maxval(x), override(NO_OVERRIDE), fun((void (__cdecl *)())f), flags(flags | (m > x ? IDF_READONLY : 0)) + { val.i = c; storage.i = s; } + // ID_FVAR + ident(int t, const char *n, float m, float c, float x, float *s, void *f = NULL, int flags = 0) + : type(t), name(n), minvalf(m), maxvalf(x), override(NO_OVERRIDE), fun((void (__cdecl *)())f), flags(flags | (m > x ? IDF_READONLY : 0)) + { val.f = c; storage.f = s; } + // ID_SVAR + ident(int t, const char *n, char *c, char **s, void *f = NULL, int flags = 0) + : type(t), name(n), override(NO_OVERRIDE), fun((void (__cdecl *)())f), flags(flags) + { val.s = c; storage.s = s; } + // ID_ALIAS + ident(int t, const char *n, char *a, int flags) + : type(t), name(n), override(NO_OVERRIDE), stack(NULL), action(a), isexecuting(NULL), flags(flags) {} + // ID_COMMAND, ID_CCOMMAND + ident(int t, const char *n, const char *narg, void *f = NULL, void *s = NULL, int flags = 0) + : type(t), name(n), override(NO_OVERRIDE), fun((void (__cdecl *)(void))f), narg(narg), self(s), flags(flags) {} + + virtual ~ident() {} + + ident &operator=(const ident &o) { memcpy(this, &o, ES_SIZEOF(ident)); return *this; } // force vtable copy, ugh + + virtual void changed() { if(fun) fun(); } +}; + +extern void addident(const char *name, ident *id); +extern const char *intstr(int v); +extern void intret(int v); +extern const char *floatstr(float v); +extern void floatret(float v); +extern void result(const char *s); + +static inline int parseint(const char *s) +{ + return int(strtol(s, NULL, 0)); +} + +static inline float parsefloat(const char *s) +{ + // not all platforms (windows) can parse hexadecimal integers via strtod + char *end; + double val = strtod(s, &end); + return val || end==s || (*end!='x' && *end!='X') ? float(val) : float(parseint(s)); +} + +// nasty macros for registering script functions, abuses globals to avoid excessive infrastructure +#define COMMANDN(name, fun, nargs) static bool __dummy_##fun = addcommand(#name, (void (*)())fun, nargs) +#define COMMAND(name, nargs) COMMANDN(name, name, nargs) + +#define _VAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist) +#define VARN(name, global, min, cur, max) _VAR(name, global, min, cur, max, 0) +#define VARNP(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_PERSIST) +#define VARNR(name, global, min, cur, max) _VAR(name, global, min, cur, max, IDF_OVERRIDE) +#define VAR(name, min, cur, max) _VAR(name, name, min, cur, max, 0) +#define VARP(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_PERSIST) +#define VARR(name, min, cur, max) _VAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _VARF(name, global, min, cur, max, body, persist) void var_##name(); int global = variable(#name, min, cur, max, &global, var_##name, persist); void var_##name() { body; } +#define VARFN(name, global, min, cur, max, body) _VARF(name, global, min, cur, max, body, 0) +#define VARF(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, 0) +#define VARFP(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_PERSIST) +#define VARFR(name, min, cur, max, body) _VARF(name, name, min, cur, max, body, IDF_OVERRIDE) + +#define _HVAR(name, global, min, cur, max, persist) int global = variable(#name, min, cur, max, &global, NULL, persist | IDF_HEX) +#define HVARN(name, global, min, cur, max) _HVAR(name, global, min, cur, max, 0) +#define HVARNP(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_PERSIST) +#define HVARNR(name, global, min, cur, max) _HVAR(name, global, min, cur, max, IDF_OVERRIDE) +#define HVAR(name, min, cur, max) _HVAR(name, name, min, cur, max, 0) +#define HVARP(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_PERSIST) +#define HVARR(name, min, cur, max) _HVAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _HVARF(name, global, min, cur, max, body, persist) void var_##name(); int global = variable(#name, min, cur, max, &global, var_##name, persist | IDF_HEX); void var_##name() { body; } +#define HVARFN(name, global, min, cur, max, body) _HVARF(name, global, min, cur, max, body, 0) +#define HVARF(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, 0) +#define HVARFP(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_PERSIST) +#define HVARFR(name, min, cur, max, body) _HVARF(name, name, min, cur, max, body, IDF_OVERRIDE) + +#define _FVAR(name, global, min, cur, max, persist) float global = fvariable(#name, min, cur, max, &global, NULL, persist) +#define FVARN(name, global, min, cur, max) _FVAR(name, global, min, cur, max, 0) +#define FVARNP(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_PERSIST) +#define FVARNR(name, global, min, cur, max) _FVAR(name, global, min, cur, max, IDF_OVERRIDE) +#define FVAR(name, min, cur, max) _FVAR(name, name, min, cur, max, 0) +#define FVARP(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_PERSIST) +#define FVARR(name, min, cur, max) _FVAR(name, name, min, cur, max, IDF_OVERRIDE) +#define _FVARF(name, global, min, cur, max, body, persist) void var_##name(); float global = fvariable(#name, min, cur, max, &global, var_##name, persist); void var_##name() { body; } +#define FVARFN(name, global, min, cur, max, body) _FVARF(name, global, min, cur, max, body, 0) +#define FVARF(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, 0) +#define FVARFP(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_PERSIST) +#define FVARFR(name, min, cur, max, body) _FVARF(name, name, min, cur, max, body, IDF_OVERRIDE) + +#define _SVAR(name, global, cur, persist) char *global = svariable(#name, cur, &global, NULL, persist) +#define SVARN(name, global, cur) _SVAR(name, global, cur, 0) +#define SVARNP(name, global, cur) _SVAR(name, global, cur, IDF_PERSIST) +#define SVARNR(name, global, cur) _SVAR(name, global, cur, IDF_OVERRIDE) +#define SVAR(name, cur) _SVAR(name, name, cur, 0) +#define SVARP(name, cur) _SVAR(name, name, cur, IDF_PERSIST) +#define SVARR(name, cur) _SVAR(name, name, cur, IDF_OVERRIDE) +#define _SVARF(name, global, cur, body, persist) void var_##name(); char *global = svariable(#name, cur, &global, var_##name, persist); void var_##name() { body; } +#define SVARFN(name, global, cur, body) _SVARF(name, global, cur, body, 0) +#define SVARF(name, cur, body) _SVARF(name, name, cur, body, 0) +#define SVARFP(name, cur, body) _SVARF(name, name, cur, body, IDF_PERSIST) +#define SVARFR(name, cur, body) _SVARF(name, name, cur, body, IDF_OVERRIDE) + +// new style macros, have the body inline, and allow binds to happen anywhere, even inside class constructors, and access the surrounding class +#define _CCOMMAND(idtype, tv, n, g, proto, b) \ + struct _ccmd_##n : ident \ + { \ + _ccmd_##n(void *self = NULL) : ident(idtype, #n, g, (void *)run, self) \ + { \ + addident(name, this); \ + } \ + static void run proto { b; } \ + } __ccmd_##n tv +#define CCOMMAND(n, g, proto, b) _CCOMMAND(ID_CCOMMAND, (this), n, g, proto, b) + +// anonymous inline commands, uses nasty template trick with line numbers to keep names unique +#define _ICOMMAND(cmdname, name, nargs, proto, b) template<int N> struct cmdname; template<> struct cmdname<__LINE__> { static bool init; static void run proto; }; bool cmdname<__LINE__>::init = addcommand(name, (void (*)())cmdname<__LINE__>::run, nargs); void cmdname<__LINE__>::run proto \ + { b; } +#define ICOMMANDNAME(name) _icmd_##name +#define ICOMMAND(name, nargs, proto, b) _ICOMMAND(ICOMMANDNAME(name), #name, nargs, proto, b) +#define ICOMMANDSNAME _icmds_ +#define ICOMMANDS(name, nargs, proto, b) _ICOMMAND(ICOMMANDSNAME, name, nargs, proto, b) + +#define _IVAR(n, m, c, x, b, p) \ + struct var_##n : ident \ + { \ + var_##n() : ident(ID_VAR, #n, m, c, x, &val.i, NULL, p) \ + { \ + addident(name, this); \ + } \ + int operator()() { return val.i; } \ + b \ + } n +#define IVAR(n, m, c, x) _IVAR(n, m, c, x, , 0) +#define IVARF(n, m, c, x, b) _IVAR(n, m, c, x, void changed() { b; }, 0) +#define IVARP(n, m, c, x) _IVAR(n, m, c, x, , IDF_PERSIST) +#define IVARR(n, m, c, x) _IVAR(n, m, c, x, , IDF_OVERRIDE) +#define IVARFP(n, m, c, x, b) _IVAR(n, m, c, x, void changed() { b; }, IDF_PERSIST) +#define IVARFR(n, m, c, x, b) _IVAR(n, m, c, x, void changed() { b; }, IDF_OVERRIDE) + diff --git a/tests/cubescript/tools.h b/tests/cubescript/tools.h new file mode 100644 index 00000000..08b528e2 --- /dev/null +++ b/tests/cubescript/tools.h @@ -0,0 +1,878 @@ +// generic useful stuff for any C++ program + +#ifndef _TOOLS_H +#define _TOOLS_H + +#include "emscripten.h" // XXX Emscripten + +#ifdef NULL +#undef NULL +#endif +#define NULL 0 + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; + +#ifdef _DEBUG +#ifdef __GNUC__ +#define ASSERT(c) if(!(c)) { asm("int $3"); } +#else +#define ASSERT(c) if(!(c)) { __asm int 3 } +#endif +#else +#define ASSERT(c) if(c) {} +#endif + +#if defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1400) +#define RESTRICT __restrict +#else +#define RESTRICT +#endif + +#ifdef swap +#undef swap +#endif +template<class T> +static inline void swap(T &a, T &b) +{ + T t = a; + a = b; + b = t; +} +#ifdef max +#undef max +#endif +#ifdef min +#undef min +#endif +template<class T> +static inline T max(T a, T b) +{ + return a > b ? a : b; +} +template<class T> +static inline T min(T a, T b) +{ + return a < b ? a : b; +} + +#define clamp(a,b,c) (max(b, min(a, c))) +#define rnd(x) ((int)(randomMT()&0xFFFFFF)%(x)) +#define rndscale(x) (float((randomMT()&0xFFFFFF)*double(x)/double(0xFFFFFF))) +#define detrnd(s, x) ((int)(((((uint)(s))*1103515245+12345)>>16)%(x))) + +#define loop(v,m) for(int v = 0; v<int(m); v++) +#define loopi(m) loop(i,m) +#define loopj(m) loop(j,m) +#define loopk(m) loop(k,m) +#define loopl(m) loop(l,m) + + +#define DELETEP(p) if(p) { delete p; p = 0; } +#define DELETEA(p) if(p) { delete[] p; p = 0; } + +#define PI (3.1415927f) +#define PI2 (2*PI) +#define SQRT2 (1.4142136f) +#define SQRT3 (1.7320508f) +#define RAD (PI / 180.0f) + +#ifdef WIN32 +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_LN2 +#define M_LN2 0.693147180559945309417 +#endif + +#ifndef __GNUC__ +#pragma warning (3: 4189) // local variable is initialized but not referenced +#pragma warning (disable: 4244) // conversion from 'int' to 'float', possible loss of data +#pragma warning (disable: 4267) // conversion from 'size_t' to 'int', possible loss of data +#pragma warning (disable: 4355) // 'this' : used in base member initializer list +#pragma warning (disable: 4996) // 'strncpy' was declared deprecated +#endif + +#define strcasecmp _stricmp +#define PATHDIV '\\' +#else +#define __cdecl +#define _vsnprintf vsnprintf +#define PATHDIV '/' +#endif + +// easy safe strings + +#define MAXSTRLEN 260 +typedef char string[MAXSTRLEN]; + +inline void vformatstring(char *d, const char *fmt, va_list v, int len = MAXSTRLEN) { _vsnprintf(d, len, fmt, v); d[len-1] = 0; } +inline char *copystring(char *d, const char *s, size_t len = MAXSTRLEN) { strncpy(d, s, len); d[len-1] = 0; return d; } +inline char *concatstring(char *d, const char *s, size_t len = MAXSTRLEN) { size_t used = strlen(d); return used < len ? copystring(d+used, s, len-used) : d; } + +struct stringformatter +{ + char *buf; + stringformatter(char *buf): buf((char *)buf) {} + void operator()(const char *fmt, ...) + { + va_list v; + va_start(v, fmt); + vformatstring(buf, fmt, v); + va_end(v); + } +}; + +#define formatstring(d) stringformatter((char *)d) +#define defformatstring(d) string d; formatstring(d) +#define defvformatstring(d,last,fmt) string d; { va_list ap; va_start(ap, last); vformatstring(d, fmt, ap); va_end(ap); } + +#define loopv(v) for(int i = 0; i<(v).length(); i++) +#define loopvj(v) for(int j = 0; j<(v).length(); j++) +#define loopvk(v) for(int k = 0; k<(v).length(); k++) +#define loopvrev(v) for(int i = (v).length()-1; i>=0; i--) + +template <class T> +struct databuf +{ + enum + { + OVERREAD = 1<<0, + OVERWROTE = 1<<1 + }; + + T *buf; + int len, maxlen; + uchar flags; + + databuf() : buf(NULL), len(0), maxlen(0), flags(0) {} + + template<class U> + databuf(T *buf, U maxlen) : buf(buf), len(0), maxlen((int)maxlen), flags(0) {} + + const T &get() + { + static T overreadval; + if(len<maxlen) return buf[len++]; + flags |= OVERREAD; + return overreadval; + } + + databuf subbuf(int sz) + { + sz = min(sz, maxlen-len); + len += sz; + return databuf(&buf[len-sz], sz); + } + + void put(const T &val) + { + if(len<maxlen) buf[len++] = val; + else flags |= OVERWROTE; + } + + void put(const T *vals, int numvals) + { + if(maxlen-len<numvals) flags |= OVERWROTE; + memcpy(&buf[len], vals, min(maxlen-len, numvals)*ES_SIZEOF(T)); + len += min(maxlen-len, numvals); + } + + int get(T *vals, int numvals) + { + int read = min(maxlen-len, numvals); + if(read<numvals) flags |= OVERREAD; + memcpy(vals, &buf[len], read*ES_SIZEOF(T)); + len += read; + return read; + } + + int length() const { return len; } + int remaining() const { return maxlen-len; } + bool overread() const { return (flags&OVERREAD)!=0; } + bool overwrote() const { return (flags&OVERWROTE)!=0; } + + void forceoverread() + { + len = maxlen; + flags |= OVERREAD; + } +}; + +typedef databuf<char> charbuf; +typedef databuf<uchar> ucharbuf; + +template<class T> +static inline float heapscore(const T &n) { return n; } + +template<class T, class U> +static inline void quicksort(T *buf, int n, int (__cdecl *func)(U *, U *)) +{ + qsort(buf, n, ES_SIZEOF(T), (int (__cdecl *)(const void *,const void *))func); +} + +template <class T> struct vector +{ + static const int MINSIZE = 8; + + T *buf; + int alen, ulen; + + vector() : buf(NULL), alen(0), ulen(0) + { + } + + vector(const vector &v) : buf(NULL), alen(0), ulen(0) + { + *this = v; + } + + ~vector() { shrink(0); if(buf) delete[] (uchar *)buf; } + + vector<T> &operator=(const vector<T> &v) + { + shrink(0); + if(v.length() > alen) growbuf(v.length()); + loopv(v) add(v[i]); + return *this; + } + + T &add(const T &x) + { + if(ulen==alen) growbuf(ulen+1); + new (&buf[ulen]) T(x); + return buf[ulen++]; + } + + T &add() + { + if(ulen==alen) growbuf(ulen+1); + new (&buf[ulen]) T; + return buf[ulen++]; + } + + T &dup() + { + if(ulen==alen) growbuf(ulen+1); + new (&buf[ulen]) T(buf[ulen-1]); + return buf[ulen++]; + } + + void move(vector<T> &v) + { + if(!ulen) + { + swap(buf, v.buf); + swap(ulen, v.ulen); + swap(alen, v.alen); + } + else + { + growbuf(ulen+v.ulen); + if(v.ulen) memcpy(&buf[ulen], v.buf, v.ulen*ES_SIZEOF(T)); + ulen += v.ulen; + v.ulen = 0; + } + } + + bool inrange(size_t i) const { return i<size_t(ulen); } + bool inrange(int i) const { return i>=0 && i<ulen; } + + T &pop() { return buf[--ulen]; } + T &last() { return buf[ulen-1]; } + void drop() { ulen--; buf[ulen].~T(); } + bool empty() const { return ulen==0; } + + int capacity() const { return alen; } + int length() const { return ulen; } + T &operator[](int i) { ASSERT(i>=0 && i<ulen); return buf[i]; } + const T &operator[](int i) const { ASSERT(i >= 0 && i<ulen); return buf[i]; } + + void shrink(int i) { ASSERT(i<=ulen); while(ulen>i) drop(); } + void setsize(int i) { ASSERT(i<=ulen); ulen = i; } + + void deletecontents() { while(!empty()) delete pop(); } + void deletearrays() { while(!empty()) delete[] pop(); } + + T *getbuf() { return buf; } + const T *getbuf() const { return buf; } + bool inbuf(const T *e) const { return e >= buf && e < &buf[ulen]; } + + template<class ST> + void sort(int (__cdecl *cf)(ST *, ST *), int i = 0, int n = -1) + { + quicksort(&buf[i], n < 0 ? ulen : n, cf); + } + + void growbuf(int sz) + { + int olen = alen; + if(!alen) alen = max(MINSIZE, sz); + else while(alen < sz) alen *= 2; + if(alen <= olen) return; + uchar *newbuf = new uchar[alen*ES_SIZEOF(T)]; + if(olen > 0) + { + memcpy(newbuf, buf, olen*ES_SIZEOF(T)); + delete[] (uchar *)buf; + } + buf = (T *)newbuf; + } + + databuf<T> reserve(int sz) + { + if(ulen+sz > alen) growbuf(ulen+sz); + return databuf<T>(&buf[ulen], sz); + } + + void advance(int sz) + { + ulen += sz; + } + + void addbuf(const databuf<T> &p) + { + advance(p.length()); + } + + T *pad(int n) + { + T *buf = reserve(n).buf; + advance(n); + return buf; + } + + void put(const T &v) { add(v); } + + void put(const T *v, int n) + { + databuf<T> buf = reserve(n); + buf.put(v, n); + addbuf(buf); + } + + void remove(int i, int n) + { + for(int p = i+n; p<ulen; p++) buf[p-n] = buf[p]; + ulen -= n; + } + + T remove(int i) + { + T e = buf[i]; + for(int p = i+1; p<ulen; p++) buf[p-1] = buf[p]; + ulen--; + return e; + } + + T removeunordered(int i) + { + T e = buf[i]; + ulen--; + if(ulen>0) buf[i] = buf[ulen]; + return e; + } + + template<class U> + int find(const U &o) + { + loopi(ulen) if(buf[i]==o) return i; + return -1; + } + + void removeobj(const T &o) + { + loopi(ulen) if(buf[i]==o) remove(i--); + } + + void replacewithlast(const T &o) + { + if(!ulen) return; + loopi(ulen-1) if(buf[i]==o) + { + buf[i] = buf[ulen-1]; + } + ulen--; + } + + T &insert(int i, const T &e) + { + add(T()); + for(int p = ulen-1; p>i; p--) buf[p] = buf[p-1]; + buf[i] = e; + return buf[i]; + } + + T *insert(int i, const T *e, int n) + { + if(ulen+n>alen) growbuf(ulen+n); + loopj(n) add(T()); + for(int p = ulen-1; p>=i+n; p--) buf[p] = buf[p-n]; + loopj(n) buf[i+j] = e[j]; + return &buf[i]; + } + + void reverse() + { + loopi(ulen/2) swap(buf[i], buf[ulen-1-i]); + } + + static int heapparent(int i) { return (i - 1) >> 1; } + static int heapchild(int i) { return (i << 1) + 1; } + + void buildheap() + { + for(int i = ulen/2; i >= 0; i--) downheap(i); + } + + int upheap(int i) + { + float score = heapscore(buf[i]); + while(i > 0) + { + int pi = heapparent(i); + if(score >= heapscore(buf[pi])) break; + swap(buf[i], buf[pi]); + i = pi; + } + return i; + } + + T &addheap(const T &x) + { + add(x); + return buf[upheap(ulen-1)]; + } + + int downheap(int i) + { + float score = heapscore(buf[i]); + for(;;) + { + int ci = heapchild(i); + if(ci >= ulen) break; + float cscore = heapscore(buf[ci]); + if(score > cscore) + { + if(ci+1 < ulen && heapscore(buf[ci+1]) < cscore) { swap(buf[ci+1], buf[i]); i = ci+1; } + else { swap(buf[ci], buf[i]); i = ci; } + } + else if(ci+1 < ulen && heapscore(buf[ci+1]) < score) { swap(buf[ci+1], buf[i]); i = ci+1; } + else break; + } + return i; + } + + T removeheap() + { + T e = removeunordered(0); + if(ulen) downheap(0); + return e; + } +}; + +static inline uint hthash(const char *key) +{ + uint h = 5381; + for(int i = 0, k; (k = key[i]); i++) h = ((h<<5)+h)^k; // bernstein k=33 xor + return h; +} + +static inline bool htcmp(const char *x, const char *y) +{ + return !strcmp(x, y); +} + +static inline uint hthash(int key) +{ + return key; +} + +static inline bool htcmp(int x, int y) +{ + return x==y; +} + +template<class T> struct hashset +{ + typedef T elem; + typedef const T const_elem; + + enum { CHUNKSIZE = 64 }; + + struct chain { T elem; chain *next; }; + struct chainchunk { chain chains[CHUNKSIZE]; chainchunk *next; }; + + int size; + int numelems; + chain **chains; + + chainchunk *chunks; + chain *unused; + + hashset(int size = 1<<10) + : size(size) + { + numelems = 0; + chunks = NULL; + unused = NULL; + chains = new chain *[size]; + loopi(size) chains[i] = NULL; + } + + ~hashset() + { + DELETEA(chains); + deletechunks(); + } + + chain *insert(uint h) + { + if(!unused) + { + chainchunk *chunk = new chainchunk; + chunk->next = chunks; + chunks = chunk; + loopi(CHUNKSIZE-1) chunk->chains[i].next = &chunk->chains[i+1]; + chunk->chains[CHUNKSIZE-1].next = unused; + unused = chunk->chains; + } + chain *c = unused; + unused = unused->next; + c->next = chains[h]; + chains[h] = c; + numelems++; + return c; + } + + #define HTFIND(key, success, fail) \ + uint h = hthash(key)&(this->size-1); \ + for(chain *c = this->chains[h]; c; c = c->next) \ + { \ + if(htcmp(key, c->elem)) return (success); \ + } \ + return (fail); + + template<class K> + T *access(const K &key) + { + HTFIND(key, &c->elem, NULL); + } + + template<class K> + T &access(const K &key, const T &elem) + { + HTFIND(key, c->elem, insert(h)->elem = elem); + } + + template<class K> + T &operator[](const K &key) + { + HTFIND(key, c->elem, insert(h)->elem); + } + + template<class K> + bool remove(const K &key) + { + uint h = hthash(key)&(size-1); + for(chain **p = &chains[h], *c = chains[h]; c; p = &c->next, c = c->next) + { + if(htcmp(key, c->elem)) + { + *p = c->next; + c->elem.~T(); + new (&c->elem) T; + c->next = unused; + unused = c; + numelems--; + return true; + } + } + return false; + } + + void deletechunks() + { + for(chainchunk *nextchunk; chunks; chunks = nextchunk) + { + nextchunk = chunks->next; + delete chunks; + } + } + + void clear() + { + if(!numelems) return; + loopi(size) chains[i] = NULL; + numelems = 0; + unused = NULL; + deletechunks(); + } + + static inline chain *getnext(void *i) { return ((chain *)i)->next; } + static inline T &getdata(void *i) { return ((chain *)i)->elem; } +}; + +template<class K, class T> struct hashtableentry +{ + K key; + T data; + + hashtableentry() {} + hashtableentry(const K &key, const T &data) : key(key), data(data) {} +}; + +template<class U, class K, class T> +static inline bool htcmp(const U *x, const hashtableentry<K, T> &y) +{ + return htcmp(x, y.key); +} + +template<class U, class K, class T> +static inline bool htcmp(const U &x, const hashtableentry<K, T> &y) +{ + return htcmp(x, y.key); +} + +template<class K, class T> struct hashtable : hashset<hashtableentry<K, T> > +{ + typedef hashtableentry<K, T> entry; + typedef struct hashset<entry>::chain chain; + typedef K key; + typedef T value; + + hashtable(int size = 1<<10) : hashset<entry>(size) {} + + entry &insert(const K &key, uint h) + { + chain *c = hashset<entry>::insert(h); + c->elem.key = key; + return c->elem; + } + + T *access(const K &key) + { + HTFIND(key, &c->elem.data, NULL); + } + + T &access(const K &key, const T &data) + { + HTFIND(key, c->elem.data, insert(key, h).data = data); + } + + T &operator[](const K &key) + { + HTFIND(key, c->elem.data, insert(key, h).data); + } + + static inline chain *getnext(void *i) { return ((chain *)i)->next; } + static inline K &getkey(void *i) { return ((chain *)i)->elem.key; } + static inline T &getdata(void *i) { return ((chain *)i)->elem.data; } +}; + +#define enumerates(ht,t,e,b) loopi((ht).size) for(hashset<t>::chain *enumc = (ht).chains[i]; enumc;) { t &e = enumc->elem; enumc = enumc->next; b; } +#define enumeratekt(ht,k,e,t,f,b) loopi((ht).size) for(hashtable<k,t>::chain *enumc = (ht).chains[i]; enumc;) { const hashtable<k,t>::key &e = enumc->elem.key; t &f = enumc->elem.data; enumc = enumc->next; b; } +#define enumerate(ht,t,e,b) loopi((ht).size) for(void *enumc = (ht).chains[i]; enumc;) { t &e = (ht).getdata(enumc); enumc = (ht).getnext(enumc); b; } + +struct unionfind +{ + struct ufval + { + int rank, next; + + ufval() : rank(0), next(-1) {} + }; + + vector<ufval> ufvals; + + int find(int k) + { + if(k>=ufvals.length()) return k; + while(ufvals[k].next>=0) k = ufvals[k].next; + return k; + } + + int compressfind(int k) + { + if(ufvals[k].next<0) return k; + return ufvals[k].next = compressfind(ufvals[k].next); + } + + void unite (int x, int y) + { + while(ufvals.length() <= max(x, y)) ufvals.add(); + x = compressfind(x); + y = compressfind(y); + if(x==y) return; + ufval &xval = ufvals[x], &yval = ufvals[y]; + if(xval.rank < yval.rank) xval.next = y; + else + { + yval.next = x; + if(xval.rank==yval.rank) yval.rank++; + } + } +}; + +template <class T, int SIZE> struct ringbuf +{ + int index, len; + T data[SIZE]; + + ringbuf() { clear(); } + + void clear() + { + index = len = 0; + } + + bool empty() const { return !len; } + + const int length() const { return len; } + + T &add(const T &e) + { + T &t = (data[index] = e); + index++; + if(index >= SIZE) index -= SIZE; + if(len < SIZE) len++; + return t; + } + + T &add() { return add(T()); } + + T &operator[](int i) + { + i += index - len; + return data[i < 0 ? i + SIZE : i%SIZE]; + } + + const T &operator[](int i) const + { + i += index - len; + return data[i < 0 ? i + SIZE : i%SIZE]; + } +}; + +template <class T, int SIZE> struct queue +{ + int head, tail, len; + T data[SIZE]; + + queue() { clear(); } + + void clear() { head = tail = len = 0; } + + int length() const { return len; } + bool empty() const { return !len; } + bool full() const { return len == SIZE; } + + T &added() { return data[tail > 0 ? tail-1 : SIZE-1]; } + T &added(int offset) { return data[tail-offset > 0 ? tail-offset-1 : tail-offset-1 + SIZE]; } + T &adding() { return data[tail]; } + T &adding(int offset) { return data[tail+offset >= SIZE ? tail+offset - SIZE : tail+offset]; } + T &add() + { + ASSERT(len < SIZE); + T &t = data[tail]; + tail = (tail + 1)%SIZE; + len++; + return t; + } + + T &removing() { return data[head]; } + T &removing(int offset) { return data[head+offset >= SIZE ? head+offset - SIZE : head+offset]; } + T &remove() + { + ASSERT(len > 0); + T &t = data[head]; + head = (head + 1)%SIZE; + len--; + return t; + } +}; + +inline char *newstring(size_t l) { return new char[l+1]; } +inline char *newstring(const char *s, size_t l) { return copystring(newstring(l), s, l+1); } +inline char *newstring(const char *s) { return newstring(s, strlen(s)); } +inline char *newstringbuf(const char *s) { return newstring(s, MAXSTRLEN-1); } + +#if defined(WIN32) && !defined(__GNUC__) +#ifdef _DEBUG +//#define _CRTDBG_MAP_ALLOC +#include <crtdbg.h> +inline void *__cdecl operator new(size_t n, const char *fn, int l) { return ::operator new(n, 1, fn, l); } +inline void __cdecl operator delete(void *p, const char *fn, int l) { ::operator delete(p, 1, fn, l); } +#define new new(__FILE__,__LINE__) +#endif +#endif + +const int islittleendian = 1; +#ifdef SDL_BYTEORDER +#define endianswap16 SDL_Swap16 +#define endianswap32 SDL_Swap32 +#else +inline ushort endianswap16(ushort n) { return (n<<8) | (n>>8); } +inline uint endianswap32(uint n) { return (n<<24) | (n>>24) | ((n>>8)&0xFF00) | ((n<<8)&0xFF0000); } +#endif +template<class T> inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.t; } +template<> inline ushort endianswap<ushort>(ushort n) { return endianswap16(n); } +template<> inline short endianswap<short>(short n) { return endianswap16(n); } +template<> inline uint endianswap<uint>(uint n) { return endianswap32(n); } +template<> inline int endianswap<int>(int n) { return endianswap32(n); } +template<class T> inline void endianswap(T *buf, int len) { for(T *end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); } +template<class T> inline T endiansame(T n) { return n; } +template<class T> inline void endiansame(T *buf, int len) {} +#ifdef SDL_BYTEORDER +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define lilswap endiansame +#define bigswap endianswap +#else +#define lilswap endianswap +#define bigswap endiansame +#endif +#else +template<class T> inline T lilswap(T n) { return *(const uchar *)&islittleendian ? n : endianswap(n); } +template<class T> inline void lilswap(T *buf, int len) { if(!*(const uchar *)&islittleendian) endianswap(buf, len); } +template<class T> inline T bigswap(T n) { return *(const uchar *)&islittleendian ? endianswap(n) : n; } +template<class T> inline void bigswap(T *buf, int len) { if(*(const uchar *)&islittleendian) endianswap(buf, len); } +#endif + +/* workaround for some C platforms that have these two functions as macros - not used anywhere */ +#ifdef getchar +#undef getchar +#endif +#ifdef putchar +#undef putchar +#endif + +struct stream +{ + virtual ~stream() {} + virtual void close() = 0; + virtual bool end() = 0; + virtual long tell() { return -1; } + virtual bool seek(long offset, int whence = SEEK_SET) { return false; } + virtual long size(); + virtual int read(void *buf, int len) { return 0; } + virtual int write(const void *buf, int len) { return 0; } + virtual int getchar() { uchar c; return read(&c, 1) == 1 ? c : -1; } + virtual bool putchar(int n) { uchar c = n; return write(&c, 1) == 1; } + virtual bool getline(char *str, int len); + virtual bool putstring(const char *str) { int len = (int)strlen(str); return write(str, len) == len; } + virtual bool putline(const char *str) { return putstring(str) && putchar('\n'); } + virtual int printf(const char *fmt, ...) { return -1; } + virtual uint getcrc() { return 0; } + + template<class T> bool put(T n) { return write(&n, ES_SIZEOV(n)) == ES_SIZEOV(n); } + template<class T> bool putlil(T n) { return put<T>(lilswap(n)); } + template<class T> bool putbig(T n) { return put<T>(bigswap(n)); } + + template<class T> T get() { T n; return read(&n, ES_SIZEOV(n)) == ES_SIZEOV(n) ? n : 0; } + template<class T> T getlil() { return lilswap(get<T>()); } + template<class T> T getbig() { return bigswap(get<T>()); } +}; + +#endif + |