home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / command.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-01  |  13.6 KB  |  360 lines

  1. // command.cpp: implements the parsing and execution of a tiny script language which
  2. // is largely backwards compatible with the quake console language.
  3.  
  4. #include "cube.h"
  5.  
  6. void itoa(char *s, int i) { s_sprintf(s)("%d", i); }
  7. char *exchangestr(char *o, char *n) { delete[] o; return newstring(n); }
  8.  
  9. hashtable<char *, ident> *idents = NULL;        // contains ALL vars/commands/aliases
  10.  
  11. bool persistidents = true;
  12.  
  13. void alias(char *name, char *action)
  14. {
  15.     ident *b = idents->access(name);
  16.     if(!b)
  17.     {
  18.         name = newstring(name);
  19.         ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), 0, persistidents };
  20.         idents->access(name, &b);
  21.     }
  22.     else if(b->type==ID_ALIAS)
  23.     {
  24.         if(b->action!=b->executing) delete[] b->action;
  25.         b->action = newstring(action);
  26.         if(b->persist!=persistidents) b->persist = persistidents;
  27.     }
  28.     else conoutf("cannot redefine builtin %s with an alias", name);
  29. }
  30.  
  31. COMMAND(alias, ARG_2STR);
  32.  
  33. // variable's and commands are registered through globals, see cube.h
  34.  
  35. int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist)
  36. {
  37.     if(!idents) idents = new hashtable<char *, ident>;
  38.     ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, 0, persist };
  39.     idents->access(name, &v);
  40.     return cur;
  41. }
  42.  
  43. void setvar(char *name, int i) { *idents->access(name)->storage = i; }
  44. int getvar(char *name) { return *idents->access(name)->storage; }
  45. bool identexists(char *name) { return idents->access(name)!=NULL; }
  46.  
  47. char *getalias(char *name)
  48. {
  49.     ident *i = idents->access(name);
  50.     return i && i->type==ID_ALIAS ? i->action : NULL;
  51. }
  52.  
  53. bool addcommand(char *name, void (*fun)(), int narg)
  54. {
  55.     if(!idents) idents = new hashtable<char *, ident>;
  56.     ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, 0, false };
  57.     idents->access(name, &c);
  58.     return false;
  59. }
  60.  
  61. char *parseexp(char *&p, int right)             // parse any nested set of () or []
  62. {
  63.     int left = *p++;
  64.     char *word = p;
  65.     for(int brak = 1; brak; )
  66.     {
  67.         int c = *p++;
  68.         if(c==left) brak++;
  69.         else if(c==right) brak--;
  70.         else if(!c) { p--; conoutf("missing \"%c\"", right); return NULL; }
  71.     }
  72.     char *s = newstring(word, p-word-1);
  73.     if(left=='(')
  74.     {
  75.         string t;
  76.         itoa(t, execute(s));                    // evaluate () exps directly, and substitute result
  77.         s = exchangestr(s, t);
  78.     }
  79.     return s;
  80. }
  81.  
  82. char *parseword(char *&p)                       // parse single argument, including expressions
  83. {
  84.     p += strspn(p, " \t\r");
  85.     if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\0");  
  86.     if(*p=='\"')
  87.     {
  88.         p++;
  89.         char *word = p;
  90.         p += strcspn(p, "\"\r\n\0");
  91.         char *s = newstring(word, p-word);
  92.         if(*p=='\"') p++;
  93.         return s;
  94.     }
  95.     if(*p=='(') return parseexp(p, ')');
  96.     if(*p=='[') return parseexp(p, ']');
  97.     char *word = p;
  98.     p += strcspn(p, "; \t\r\n\0");
  99.     if(p-word==0) return NULL;
  100.     return newstring(word, p-word);
  101. }
  102.  
  103. char *lookup(char *n)                           // find value of ident referenced with $ in exp
  104. {
  105.     ident *id = idents->access(n+1);
  106.     if(id) switch(id->type)
  107.     {
  108.         case ID_VAR: string t; itoa(t, *(id->storage)); return exchangestr(n, t);
  109.         case ID_ALIAS: return exchangestr(n, id->action);
  110.     }
  111.     conoutf("unknown alias lookup: %s", n+1);
  112.     return n;
  113. }
  114.  
  115. int execute(char *p)                            // all evaluation happens here, recursively
  116. {
  117.     const int MAXWORDS = 25;                    // limit, remove
  118.     char *w[MAXWORDS];
  119.     int val = 0;
  120.     for(bool cont = true; cont;)                // for each ; seperated statement
  121.     {
  122.         int numargs = MAXWORDS;
  123.         loopi(MAXWORDS)                         // collect all argument values
  124.         {
  125.             w[i] = "";
  126.             if(i>numargs) continue;
  127.             char *s = parseword(p);             // parse and evaluate exps
  128.             if(!s) { numargs = i; s = ""; }
  129.             if(*s=='$') s = lookup(s);          // substitute variables
  130.             w[i] = s;
  131.         }
  132.         
  133.         p += strcspn(p, ";\n\0");
  134.         cont = *p++!=0;                         // more statements if this isn't the end of the string
  135.         char *c = w[0];
  136.         if(*c=='/') c++;                        // strip irc-style command prefix
  137.         if(!*c) continue;                       // empty statement
  138.         
  139.         ident *id = idents->access(c);
  140.         if(!id)
  141.         {
  142.             val = ATOI(c);
  143.             if(!val && *c!='0') conoutf("unknown command: %s", c);
  144.         }
  145.         else switch(id->type)
  146.         {
  147.             case ID_COMMAND:                    // game defined commands       
  148.                 switch(id->narg)                // use very ad-hoc function signature, and just call it
  149.                 { 
  150.                     case ARG_1INT: ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break;
  151.                     case ARG_2INT: ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break;
  152.                     case ARG_3INT: ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break;
  153.                     case ARG_4INT: ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break;
  154.                     case ARG_NONE: ((void (__cdecl *)())id->fun)(); break;
  155.                     case ARG_1STR: ((void (__cdecl *)(char *))id->fun)(w[1]); break;
  156.                     case ARG_2STR: ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break;
  157.                     case ARG_3STR: ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break;
  158.                     case ARG_4STR: ((void (__cdecl *)(char *, char *, char*, char*))id->fun)(w[1], w[2], w[3], w[4]); break;
  159.                     case ARG_5STR: ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break;
  160.                     case ARG_6STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6]); break;
  161.                     case ARG_7STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7]); break;
  162.                     case ARG_8STR: ((void (__cdecl *)(char *, char *, char*, char*, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8]); break;
  163.                     case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(addreleaseaction(id->name)!=NULL); break;
  164.                     case ARG_1EXP: val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break;
  165.                     case ARG_2EXP: val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break;
  166.                     case ARG_1EST: val = ((int (__cdecl *)(char *))id->fun)(w[1]); break;
  167.                     case ARG_2EST: val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break;
  168.                     case ARG_VARI:
  169.                     {
  170.                         int len = max(numargs-2, 0);
  171.                         for(int i = 1; i<numargs; i++) len += (int)strlen(w[i]);
  172.                         char *r = newstring("", len);
  173.                         for(int i = 1; i<numargs; i++)       
  174.                         {
  175.                             strcat(r, w[i]); // make string-list out of all arguments
  176.                             if(i==numargs-1) break;
  177.                             strcat(r, " ");
  178.                         }
  179.                         ((void (__cdecl *)(char *))id->fun)(r);
  180.                         delete[] r;
  181.                         break;
  182.                     }
  183.                 }
  184.                 break;
  185.        
  186.             case ID_VAR:                        // game defined variables
  187.                 if(!w[1][0]) conoutf("%s = %d", c, *id->storage);      // var with no value just prints its current value
  188.                 else if(id->min>id->max) conoutf("variable %s is read-only", id->name);
  189.                 else 
  190.                 {
  191.                     int i1 = ATOI(w[1]);
  192.                     if(i1<id->min || i1>id->max)
  193.                     {
  194.                         i1 = i1<id->min ? id->min : id->max;                // clamp to valid range
  195.                         conoutf("valid range for %s is %d..%d", id->name, id->min, id->max);
  196.                     }
  197.                     *id->storage = i1;
  198.                     if(id->fun) ((void (__cdecl *)())id->fun)();            // call trigger function if available
  199.                 }
  200.                 break;
  201.                 
  202.             case ID_ALIAS:                              // alias, also used as functions and (global) variables
  203.                 for(int i = 1; i<numargs; i++)
  204.                 {
  205.                     s_sprintfd(t)("arg%d", i);          // set any arguments as (global) arg values so functions can access them
  206.                     alias(t, w[i]);
  207.                 }
  208.                 char *wasexecuting = id->executing;
  209.                 id->executing = id->action;
  210.                 val = execute(id->action);
  211.                 if(id->executing!=id->action && id->executing!=wasexecuting) delete[] id->executing;
  212.                 id->executing = wasexecuting;
  213.                 break;
  214.         }
  215.         loopj(numargs) delete[] w[j];
  216.     }
  217.     return val;
  218. }
  219.  
  220. // tab-completion of all idents
  221.  
  222. int completesize = 0, completeidx = 0;
  223.  
  224. void resetcomplete() { completesize = 0; }
  225.  
  226. void complete(char *s)
  227. {
  228.     if(*s!='/')
  229.     {
  230.         string t;
  231.         s_strcpy(t, s);
  232.         s_strcpy(s, "/");
  233.         s_strcat(s, t);
  234.     }
  235.     if(!s[1]) return;
  236.     if(!completesize) { completesize = (int)strlen(s)-1; completeidx = 0; }
  237.     int idx = 0;
  238.     enumerate(*idents, ident, id,
  239.         if(strncmp(id.name, s+1, completesize)==0 && idx++==completeidx)
  240.         {
  241.             s_strcpy(s, "/");
  242.             s_strcat(s, id.name);
  243.         }
  244.     );
  245.     completeidx++;
  246.     if(completeidx>=idx) completeidx = 0;
  247. }
  248.  
  249. bool execfile(char *cfgfile)
  250. {
  251.     string s;
  252.     s_strcpy(s, cfgfile);
  253.     char *buf = loadfile(path(s), NULL);
  254.     if(!buf) return false;
  255.     execute(buf);
  256.     delete[] buf;
  257.     return true;
  258. }
  259.  
  260. void exec(char *cfgfile)
  261. {
  262.     if(!execfile(cfgfile)) conoutf("could not read \"%s\"", cfgfile);
  263. }
  264.  
  265. // below the commands that implement a small imperative language. thanks to the semantics of
  266. // () and [] expressions, any control construct can be defined trivially.
  267.  
  268. void intset(char *name, int v) { string b; itoa(b, v); alias(name, b); }
  269.  
  270. void ifthen(char *cond, char *thenp, char *elsep) { execute(cond[0]!='0' ? thenp : elsep); }
  271. void loopa(char *times, char *body) { int t = atoi(times); loopi(t) { intset("i", i); execute(body); } }
  272. void whilea(char *cond, char *body) { while(execute(cond)) execute(body); }    // can't get any simpler than this :)
  273.  
  274. void concat(char *s) { alias("s", s); }
  275.  
  276. void concatword(char *s)
  277. {
  278.     for(char *a = s, *b = s; (*a = *b); b++) if(*a!=' ') a++;   
  279.     concat(s);
  280. }
  281.  
  282. int listlen(char *a)
  283. {
  284.     if(!*a) return 0;
  285.     int n = 0;
  286.     while(*a) if(*a++==' ') n++;
  287.     return n+1;
  288. }
  289.  
  290. void at(char *s, char *pos)
  291. {
  292.     int n = atoi(pos);
  293.     loopi(n) 
  294.     {
  295.         s += strcspn(s, " \0");
  296.         s += strspn(s, " ");
  297.     }
  298.     s[strcspn(s, " \0")] = 0;
  299.     concat(s);
  300. }
  301.  
  302. COMMANDN(loop, loopa, ARG_2STR);
  303. COMMANDN(while, whilea, ARG_2STR);
  304. COMMANDN(if, ifthen, ARG_3STR); 
  305. COMMAND(exec, ARG_1STR);
  306. COMMAND(concat, ARG_VARI);
  307. COMMAND(concatword, ARG_VARI);
  308. COMMAND(at, ARG_2STR);
  309. COMMAND(listlen, ARG_1EST);
  310.  
  311. int add(int a, int b)   { return a+b; }         COMMANDN(+, add, ARG_2EXP);
  312. int mul(int a, int b)   { return a*b; }         COMMANDN(*, mul, ARG_2EXP);
  313. int sub(int a, int b)   { return a-b; }         COMMANDN(-, sub, ARG_2EXP);
  314. int divi(int a, int b)  { return b ? a/b : 0; } COMMANDN(div, divi, ARG_2EXP);
  315. int mod(int a, int b)   { return b ? a%b : 0; } COMMAND(mod, ARG_2EXP);
  316. int equal(int a, int b) { return (int)(a==b); } COMMANDN(=, equal, ARG_2EXP);
  317. int lt(int a, int b)    { return (int)(a<b); }  COMMANDN(<, lt, ARG_2EXP);
  318. int gt(int a, int b)    { return (int)(a>b); }  COMMANDN(>, gt, ARG_2EXP);
  319.  
  320. int strcmpa(char *a, char *b) { return strcmp(a,b)==0; }  COMMANDN(strcmp, strcmpa, ARG_2EST);
  321.  
  322. int rndn(int a)    { return a>0 ? rnd(a) : 0; }  COMMANDN(rnd, rndn, ARG_1EXP);
  323.  
  324. void writecfg()
  325. {
  326.     s_sprintfd(cfgname)("config%csaved.cfg", PATHDIV);
  327.     FILE *f = fopen(cfgname, "w");
  328.     if(!f) return;
  329.     fprintf(f, "// automatically written on exit, DO NOT MODIFY\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n");
  330.     fprintf(f, "name %s\nteam %s\nskin %d\n", player1->name, player1->team, player1->nextskin);
  331.     fprintf(f, "loadcrosshair %s\n", crosshair->name+strlen("packages/misc/crosshairs/"));
  332.     extern int lowfps, highfps;
  333.     fprintf(f, "fpsrange %d %d\n", lowfps, highfps);
  334.     fprintf(f, "\n");
  335.     enumerate(*idents, ident, id,
  336.         if(id.type==ID_VAR && id.persist)
  337.         {
  338.             fprintf(f, "%s %d\n", id.name, *id.storage);
  339.         }
  340.     );
  341.     fprintf(f, "\n");
  342.     writebinds(f);
  343.     fprintf(f, "\n");
  344.     enumerate(*idents, ident, id,
  345.         if(id.type==ID_ALIAS && id.persist && id.action[0])
  346.         {
  347.             fprintf(f, "alias \"%s\" [%s]\n", id.name, id.action);
  348.         }
  349.     );
  350.     fprintf(f, "\n");
  351.     fclose(f);
  352. }
  353.  
  354. COMMAND(writecfg, ARG_NONE);
  355.  
  356. void identnames(vector<char *> &names, bool builtinonly)
  357. {
  358.     enumerateht(*idents) if(!builtinonly || idents->enumc->data.type != ID_ALIAS) names.add(idents->enumc->key);
  359. }
  360.