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 "engine.h"
5
6 hashnameset<ident> idents; // contains ALL vars/commands/aliases
7 vector<ident *> identmap;
8 ident *dummyident = NULL;
9
10 int identflags = 0;
11
12 enum
13 {
14 MAXARGS = 25,
15 MAXRESULTS = 7,
16 MAXCOMARGS = 12
17 };
18
19 VARN(numargs, _numargs, MAXARGS, 0, 0);
20
freearg(tagval & v)21 static inline void freearg(tagval &v)
22 {
23 switch(v.type)
24 {
25 case VAL_STR: delete[] v.s; break;
26 case VAL_CODE: if(v.code[-1] == CODE_START) delete[] (uchar *)&v.code[-1]; break;
27 }
28 }
29
forcenull(tagval & v)30 static inline void forcenull(tagval &v)
31 {
32 switch(v.type)
33 {
34 case VAL_NULL: return;
35 }
36 freearg(v);
37 v.setnull();
38 }
39
forcefloat(tagval & v)40 static inline float forcefloat(tagval &v)
41 {
42 float f = 0.0f;
43 switch(v.type)
44 {
45 case VAL_INT: f = v.i; break;
46 case VAL_STR: case VAL_MACRO: case VAL_CSTR: f = parsefloat(v.s); break;
47 case VAL_FLOAT: return v.f;
48 }
49 freearg(v);
50 v.setfloat(f);
51 return f;
52 }
53
forceint(tagval & v)54 static inline int forceint(tagval &v)
55 {
56 int i = 0;
57 switch(v.type)
58 {
59 case VAL_FLOAT: i = v.f; break;
60 case VAL_STR: case VAL_MACRO: case VAL_CSTR: i = parseint(v.s); break;
61 case VAL_INT: return v.i;
62 }
63 freearg(v);
64 v.setint(i);
65 return i;
66 }
67
forcestr(tagval & v)68 static inline const char *forcestr(tagval &v)
69 {
70 const char *s = "";
71 switch(v.type)
72 {
73 case VAL_FLOAT: s = floatstr(v.f); break;
74 case VAL_INT: s = intstr(v.i); break;
75 case VAL_MACRO: case VAL_CSTR: s = v.s; break;
76 case VAL_STR: return v.s;
77 }
78 freearg(v);
79 v.setstr(newstring(s));
80 return s;
81 }
82
forcearg(tagval & v,int type)83 static inline void forcearg(tagval &v, int type)
84 {
85 switch(type)
86 {
87 case RET_STR: if(v.type != VAL_STR) forcestr(v); break;
88 case RET_INT: if(v.type != VAL_INT) forceint(v); break;
89 case RET_FLOAT: if(v.type != VAL_FLOAT) forcefloat(v); break;
90 }
91 }
92
cleanup()93 void tagval::cleanup()
94 {
95 freearg(*this);
96 }
97
freeargs(tagval * args,int & oldnum,int newnum)98 static inline void freeargs(tagval *args, int &oldnum, int newnum)
99 {
100 for(int i = newnum; i < oldnum; i++) freearg(args[i]);
101 oldnum = newnum;
102 }
103
cleancode(ident & id)104 static inline void cleancode(ident &id)
105 {
106 if(id.code)
107 {
108 id.code[0] -= 0x100;
109 if(int(id.code[0]) < 0x100) delete[] id.code;
110 id.code = NULL;
111 }
112 }
113
114 struct nullval : tagval
115 {
nullvalnullval116 nullval() { setnull(); }
117 } nullval;
118 tagval noret = nullval, *commandret = &noret;
119
clear_command()120 void clear_command()
121 {
122 enumerate(idents, ident, i,
123 {
124 if(i.type==ID_ALIAS)
125 {
126 DELETEA(i.name);
127 i.forcenull();
128 DELETEA(i.code);
129 }
130 });
131 }
132
clearoverride(ident & i)133 void clearoverride(ident &i)
134 {
135 if(!(i.flags&IDF_OVERRIDDEN)) return;
136 switch(i.type)
137 {
138 case ID_ALIAS:
139 if(i.valtype==VAL_STR)
140 {
141 if(!i.val.s[0]) break;
142 delete[] i.val.s;
143 }
144 cleancode(i);
145 i.valtype = VAL_STR;
146 i.val.s = newstring("");
147 break;
148 case ID_VAR:
149 *i.storage.i = i.overrideval.i;
150 i.changed();
151 break;
152 case ID_FVAR:
153 *i.storage.f = i.overrideval.f;
154 i.changed();
155 break;
156 case ID_SVAR:
157 delete[] *i.storage.s;
158 *i.storage.s = i.overrideval.s;
159 i.changed();
160 break;
161 }
162 i.flags &= ~IDF_OVERRIDDEN;
163 }
164
clearoverrides()165 void clearoverrides()
166 {
167 enumerate(idents, ident, i, clearoverride(i));
168 }
169
170 static bool initedidents = false;
171 static vector<ident> *identinits = NULL;
172
addident(const ident & id)173 static inline ident *addident(const ident &id)
174 {
175 if(!initedidents)
176 {
177 if(!identinits) identinits = new vector<ident>;
178 identinits->add(id);
179 return NULL;
180 }
181 ident &def = idents.access(id.name, id);
182 def.index = identmap.length();
183 return identmap.add(&def);
184 }
185
initidents()186 static bool initidents()
187 {
188 initedidents = true;
189 for(int i = 0; i < MAXARGS; i++)
190 {
191 defformatstring(argname, "arg%d", i+1);
192 newident(argname, IDF_ARG);
193 }
194 dummyident = newident("//dummy", IDF_UNKNOWN);
195 if(identinits)
196 {
197 loopv(*identinits) addident((*identinits)[i]);
198 DELETEP(identinits);
199 }
200 return true;
201 }
202 UNUSED static bool forceinitidents = initidents();
203
204 static const char *sourcefile = NULL, *sourcestr = NULL;
205
debugline(const char * p,const char * fmt)206 static const char *debugline(const char *p, const char *fmt)
207 {
208 if(!sourcestr) return fmt;
209 int num = 1;
210 const char *line = sourcestr;
211 for(;;)
212 {
213 const char *end = strchr(line, '\n');
214 if(!end) end = line + strlen(line);
215 if(p >= line && p <= end)
216 {
217 static string buf;
218 if(sourcefile) formatstring(buf, "%s:%d: %s", sourcefile, num, fmt);
219 else formatstring(buf, "%d: %s", num, fmt);
220 return buf;
221 }
222 if(!*end) break;
223 line = end + 1;
224 num++;
225 }
226 return fmt;
227 }
228
229 static struct identlink
230 {
231 ident *id;
232 identlink *next;
233 int usedargs;
234 identstack *argstack;
235 } noalias = { NULL, NULL, (1<<MAXARGS)-1, NULL }, *aliasstack = &noalias;
236
237 VAR(dbgalias, 0, 4, 1000);
238
debugalias()239 static void debugalias()
240 {
241 if(!dbgalias) return;
242 int total = 0, depth = 0;
243 for(identlink *l = aliasstack; l != &noalias; l = l->next) total++;
244 for(identlink *l = aliasstack; l != &noalias; l = l->next)
245 {
246 ident *id = l->id;
247 ++depth;
248 if(depth < dbgalias) conoutf(CON_ERROR, " %d) %s", total-depth+1, id->name);
249 else if(l->next == &noalias) conoutf(CON_ERROR, depth == dbgalias ? " %d) %s" : " ..%d) %s", total-depth+1, id->name);
250 }
251 }
252
253 static int nodebug = 0;
254
255 static void debugcode(const char *fmt, ...) PRINTFARGS(1, 2);
256
debugcode(const char * fmt,...)257 static void debugcode(const char *fmt, ...)
258 {
259 if(nodebug) return;
260
261 va_list args;
262 va_start(args, fmt);
263 conoutfv(CON_ERROR, fmt, args);
264 va_end(args);
265
266 debugalias();
267 }
268
269 static void debugcodeline(const char *p, const char *fmt, ...) PRINTFARGS(2, 3);
270
debugcodeline(const char * p,const char * fmt,...)271 static void debugcodeline(const char *p, const char *fmt, ...)
272 {
273 if(nodebug) return;
274
275 va_list args;
276 va_start(args, fmt);
277 conoutfv(CON_ERROR, debugline(p, fmt), args);
278 va_end(args);
279
280 debugalias();
281 }
282
283 ICOMMAND(nodebug, "e", (uint *body), { nodebug++; executeret(body, *commandret); nodebug--; });
284
addident(ident * id)285 void addident(ident *id)
286 {
287 addident(*id);
288 }
289
pusharg(ident & id,const tagval & v,identstack & stack)290 static inline void pusharg(ident &id, const tagval &v, identstack &stack)
291 {
292 stack.val = id.val;
293 stack.valtype = id.valtype;
294 stack.next = id.stack;
295 id.stack = &stack;
296 id.setval(v);
297 cleancode(id);
298 }
299
poparg(ident & id)300 static inline void poparg(ident &id)
301 {
302 if(!id.stack) return;
303 identstack *stack = id.stack;
304 if(id.valtype == VAL_STR) delete[] id.val.s;
305 id.setval(*stack);
306 cleancode(id);
307 id.stack = stack->next;
308 }
309
undoarg(ident & id,identstack & stack)310 static inline void undoarg(ident &id, identstack &stack)
311 {
312 identstack *prev = id.stack;
313 stack.val = id.val;
314 stack.valtype = id.valtype;
315 stack.next = prev;
316 id.stack = prev->next;
317 id.setval(*prev);
318 cleancode(id);
319 }
320
321 #define UNDOARGS \
322 identstack argstack[MAXARGS]; \
323 for(int argmask = aliasstack->usedargs, i = 0; argmask; argmask >>= 1, i++) if(argmask&1) \
324 undoarg(*identmap[i], argstack[i]); \
325 identlink *prevstack = aliasstack->next; \
326 identlink aliaslink = { aliasstack->id, aliasstack, prevstack->usedargs, prevstack->argstack }; \
327 aliasstack = &aliaslink;
328
redoarg(ident & id,const identstack & stack)329 static inline void redoarg(ident &id, const identstack &stack)
330 {
331 identstack *prev = stack.next;
332 prev->val = id.val;
333 prev->valtype = id.valtype;
334 id.stack = prev;
335 id.setval(stack);
336 cleancode(id);
337 }
338
339 #define REDOARGS \
340 prevstack->usedargs = aliaslink.usedargs; \
341 aliasstack = aliaslink.next; \
342 for(int argmask = aliasstack->usedargs, i = 0; argmask; argmask >>= 1, i++) if(argmask&1) \
343 redoarg(*identmap[i], argstack[i]);
344
345 ICOMMAND(push, "rTe", (ident *id, tagval *v, uint *code),
346 {
347 if(id->type != ID_ALIAS || id->index < MAXARGS) return;
348 identstack stack;
349 pusharg(*id, *v, stack);
350 v->type = VAL_NULL;
351 id->flags &= ~IDF_UNKNOWN;
352 executeret(code, *commandret);
353 poparg(*id);
354 });
355
pushalias(ident & id,identstack & stack)356 static inline void pushalias(ident &id, identstack &stack)
357 {
358 if(id.type == ID_ALIAS && id.index >= MAXARGS)
359 {
360 pusharg(id, nullval, stack);
361 id.flags &= ~IDF_UNKNOWN;
362 }
363 }
364
popalias(ident & id)365 static inline void popalias(ident &id)
366 {
367 if(id.type == ID_ALIAS && id.index >= MAXARGS) poparg(id);
368 }
369
370 KEYWORD(local, ID_LOCAL);
371
checknumber(const char * s)372 static inline bool checknumber(const char *s)
373 {
374 if(isdigit(s[0])) return true;
375 else switch(s[0])
376 {
377 case '+': case '-': return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
378 case '.': return isdigit(s[1]) != 0;
379 default: return false;
380 }
381 }
checknumber(const stringslice & s)382 static inline bool checknumber(const stringslice &s) { return checknumber(s.str); }
383
newident(const T & name,int flags)384 template<class T> static inline ident *newident(const T &name, int flags)
385 {
386 ident *id = idents.access(name);
387 if(!id)
388 {
389 if(checknumber(name))
390 {
391 debugcode("number %.*s is not a valid identifier name", stringlen(name), stringptr(name));
392 return dummyident;
393 }
394 id = addident(ident(ID_ALIAS, newstring(name), flags));
395 }
396 return id;
397 }
398
forceident(tagval & v)399 static inline ident *forceident(tagval &v)
400 {
401 switch(v.type)
402 {
403 case VAL_IDENT: return v.id;
404 case VAL_MACRO: case VAL_CSTR:
405 {
406 ident *id = newident(v.s, IDF_UNKNOWN);
407 v.setident(id);
408 return id;
409 }
410 case VAL_STR:
411 {
412 ident *id = newident(v.s, IDF_UNKNOWN);
413 delete[] v.s;
414 v.setident(id);
415 return id;
416 }
417 }
418 freearg(v);
419 v.setident(dummyident);
420 return dummyident;
421 }
422
newident(const char * name,int flags)423 ident *newident(const char *name, int flags)
424 {
425 return newident<const char *>(name, flags);
426 }
427
writeident(const char * name,int flags)428 ident *writeident(const char *name, int flags)
429 {
430 ident *id = newident(name, flags);
431 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
432 {
433 pusharg(*id, nullval, aliasstack->argstack[id->index]);
434 aliasstack->usedargs |= 1<<id->index;
435 }
436 return id;
437 }
438
readident(const char * name)439 ident *readident(const char *name)
440 {
441 ident *id = idents.access(name);
442 if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
443 return NULL;
444 return id;
445 }
446
resetvar(char * name)447 void resetvar(char *name)
448 {
449 ident *id = idents.access(name);
450 if(!id) return;
451 if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
452 else clearoverride(*id);
453 }
454
455 COMMAND(resetvar, "s");
456
setarg(ident & id,tagval & v)457 static inline void setarg(ident &id, tagval &v)
458 {
459 if(aliasstack->usedargs&(1<<id.index))
460 {
461 if(id.valtype == VAL_STR) delete[] id.val.s;
462 id.setval(v);
463 cleancode(id);
464 }
465 else
466 {
467 pusharg(id, v, aliasstack->argstack[id.index]);
468 aliasstack->usedargs |= 1<<id.index;
469 }
470 }
471
setalias(ident & id,tagval & v)472 static inline void setalias(ident &id, tagval &v)
473 {
474 if(id.valtype == VAL_STR) delete[] id.val.s;
475 id.setval(v);
476 cleancode(id);
477 id.flags = (id.flags & identflags) | identflags;
478 }
479
setalias(const char * name,tagval & v)480 static void setalias(const char *name, tagval &v)
481 {
482 ident *id = idents.access(name);
483 if(id)
484 {
485 switch(id->type)
486 {
487 case ID_ALIAS:
488 if(id->index < MAXARGS) setarg(*id, v); else setalias(*id, v);
489 return;
490 case ID_VAR:
491 setvarchecked(id, v.getint());
492 break;
493 case ID_FVAR:
494 setfvarchecked(id, v.getfloat());
495 break;
496 case ID_SVAR:
497 setsvarchecked(id, v.getstr());
498 break;
499 default:
500 debugcode("cannot redefine builtin %s with an alias", id->name);
501 break;
502 }
503 freearg(v);
504 }
505 else if(checknumber(name))
506 {
507 debugcode("cannot alias number %s", name);
508 freearg(v);
509 }
510 else
511 {
512 addident(ident(ID_ALIAS, newstring(name), v, identflags));
513 }
514 }
515
alias(const char * name,const char * str)516 void alias(const char *name, const char *str)
517 {
518 tagval v;
519 v.setstr(newstring(str));
520 setalias(name, v);
521 }
522
alias(const char * name,tagval & v)523 void alias(const char *name, tagval &v)
524 {
525 setalias(name, v);
526 }
527
528 ICOMMAND(alias, "sT", (const char *name, tagval *v),
529 {
530 setalias(name, *v);
531 v->type = VAL_NULL;
532 });
533
534 // variable's and commands are registered through globals, see cube.h
535
variable(const char * name,int min,int cur,int max,int * storage,identfun fun,int flags)536 int variable(const char *name, int min, int cur, int max, int *storage, identfun fun, int flags)
537 {
538 addident(ident(ID_VAR, name, min, max, storage, (void *)fun, flags));
539 return cur;
540 }
541
fvariable(const char * name,float min,float cur,float max,float * storage,identfun fun,int flags)542 float fvariable(const char *name, float min, float cur, float max, float *storage, identfun fun, int flags)
543 {
544 addident(ident(ID_FVAR, name, min, max, storage, (void *)fun, flags));
545 return cur;
546 }
547
svariable(const char * name,const char * cur,char ** storage,identfun fun,int flags)548 char *svariable(const char *name, const char *cur, char **storage, identfun fun, int flags)
549 {
550 addident(ident(ID_SVAR, name, storage, (void *)fun, flags));
551 return newstring(cur);
552 }
553
554 struct defvar : identval
555 {
556 char *name;
557 uint *onchange;
558
defvardefvar559 defvar() : name(NULL), onchange(NULL) {}
560
~defvardefvar561 ~defvar()
562 {
563 DELETEA(name);
564 if(onchange) freecode(onchange);
565 }
566
changeddefvar567 static void changed(ident *id)
568 {
569 defvar *v = (defvar *)id->storage.p;
570 if(v->onchange) execute(v->onchange);
571 }
572 };
573
574 hashnameset<defvar> defvars;
575
576 #define DEFVAR(cmdname, fmt, args, body) \
577 ICOMMAND(cmdname, fmt, args, \
578 { \
579 if(idents.access(name)) { debugcode("cannot redefine %s as a variable", name); return; } \
580 name = newstring(name); \
581 defvar &def = defvars[name]; \
582 def.name = name; \
583 def.onchange = onchange[0] ? compilecode(onchange) : NULL; \
584 body; \
585 });
586 #define DEFIVAR(cmdname, flags) \
587 DEFVAR(cmdname, "siiis", (char *name, int *min, int *cur, int *max, char *onchange), \
588 def.i = variable(name, *min, *cur, *max, &def.i, def.onchange ? defvar::changed : NULL, flags))
589 #define DEFFVAR(cmdname, flags) \
590 DEFVAR(cmdname, "sfffs", (char *name, float *min, float *cur, float *max, char *onchange), \
591 def.f = fvariable(name, *min, *cur, *max, &def.f, def.onchange ? defvar::changed : NULL, flags))
592 #define DEFSVAR(cmdname, flags) \
593 DEFVAR(cmdname, "sss", (char *name, char *cur, char *onchange), \
594 def.s = svariable(name, cur, &def.s, def.onchange ? defvar::changed : NULL, flags))
595
596 DEFIVAR(defvar, 0);
597 DEFIVAR(defvarp, IDF_PERSIST);
598 DEFFVAR(deffvar, 0);
599 DEFFVAR(deffvarp, IDF_PERSIST);
600 DEFSVAR(defsvar, 0);
601 DEFSVAR(defsvarp, IDF_PERSIST);
602
603 #define _GETVAR(id, vartype, name, retval) \
604 ident *id = idents.access(name); \
605 if(!id || id->type!=vartype) return retval;
606 #define GETVAR(id, name, retval) _GETVAR(id, ID_VAR, name, retval)
607 #define OVERRIDEVAR(errorval, saveval, resetval, clearval) \
608 if(identflags&IDF_OVERRIDDEN || id->flags&IDF_OVERRIDE) \
609 { \
610 if(id->flags&IDF_PERSIST) \
611 { \
612 debugcode("cannot override persistent variable %s", id->name); \
613 errorval; \
614 } \
615 if(!(id->flags&IDF_OVERRIDDEN)) { saveval; id->flags |= IDF_OVERRIDDEN; } \
616 else { clearval; } \
617 } \
618 else \
619 { \
620 if(id->flags&IDF_OVERRIDDEN) { resetval; id->flags &= ~IDF_OVERRIDDEN; } \
621 clearval; \
622 }
623
setvar(const char * name,int i,bool dofunc,bool doclamp)624 void setvar(const char *name, int i, bool dofunc, bool doclamp)
625 {
626 GETVAR(id, name, );
627 OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
628 if(doclamp) *id->storage.i = clamp(i, id->minval, id->maxval);
629 else *id->storage.i = i;
630 if(dofunc) id->changed();
631 }
setfvar(const char * name,float f,bool dofunc,bool doclamp)632 void setfvar(const char *name, float f, bool dofunc, bool doclamp)
633 {
634 _GETVAR(id, ID_FVAR, name, );
635 OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
636 if(doclamp) *id->storage.f = clamp(f, id->minvalf, id->maxvalf);
637 else *id->storage.f = f;
638 if(dofunc) id->changed();
639 }
setsvar(const char * name,const char * str,bool dofunc)640 void setsvar(const char *name, const char *str, bool dofunc)
641 {
642 _GETVAR(id, ID_SVAR, name, );
643 OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
644 *id->storage.s = newstring(str);
645 if(dofunc) id->changed();
646 }
getvar(const char * name)647 int getvar(const char *name)
648 {
649 GETVAR(id, name, 0);
650 return *id->storage.i;
651 }
getvarmin(const char * name)652 int getvarmin(const char *name)
653 {
654 GETVAR(id, name, 0);
655 return id->minval;
656 }
getvarmax(const char * name)657 int getvarmax(const char *name)
658 {
659 GETVAR(id, name, 0);
660 return id->maxval;
661 }
getfvarmin(const char * name)662 float getfvarmin(const char *name)
663 {
664 _GETVAR(id, ID_FVAR, name, 0);
665 return id->minvalf;
666 }
getfvarmax(const char * name)667 float getfvarmax(const char *name)
668 {
669 _GETVAR(id, ID_FVAR, name, 0);
670 return id->maxvalf;
671 }
672
673 ICOMMAND(getvarmin, "s", (char *s), intret(getvarmin(s)));
674 ICOMMAND(getvarmax, "s", (char *s), intret(getvarmax(s)));
675 ICOMMAND(getfvarmin, "s", (char *s), floatret(getfvarmin(s)));
676 ICOMMAND(getfvarmax, "s", (char *s), floatret(getfvarmax(s)));
677
identexists(const char * name)678 bool identexists(const char *name) { return idents.access(name)!=NULL; }
679 ICOMMAND(identexists, "s", (char *s), intret(identexists(s) ? 1 : 0));
680
getident(const char * name)681 ident *getident(const char *name) { return idents.access(name); }
682
touchvar(const char * name)683 void touchvar(const char *name)
684 {
685 ident *id = idents.access(name);
686 if(id) switch(id->type)
687 {
688 case ID_VAR:
689 case ID_FVAR:
690 case ID_SVAR:
691 id->changed();
692 break;
693 }
694 }
695
getalias(const char * name)696 const char *getalias(const char *name)
697 {
698 ident *i = idents.access(name);
699 return i && i->type==ID_ALIAS && (i->index >= MAXARGS || aliasstack->usedargs&(1<<i->index)) ? i->getstr() : "";
700 }
701
702 ICOMMAND(getalias, "s", (char *s), result(getalias(s)));
703
clampvar(ident * id,int val,int minval,int maxval)704 int clampvar(ident *id, int val, int minval, int maxval)
705 {
706 if(val < minval) val = minval;
707 else if(val > maxval) val = maxval;
708 else return val;
709 debugcode(id->flags&IDF_HEX ?
710 (minval <= 255 ? "valid range for %s is %d..0x%X" : "valid range for %s is 0x%X..0x%X") :
711 "valid range for %s is %d..%d",
712 id->name, minval, maxval);
713 return val;
714 }
715
setvarchecked(ident * id,int val)716 void setvarchecked(ident *id, int val)
717 {
718 if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
719 #ifndef STANDALONE
720 else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
721 #else
722 else
723 #endif
724 {
725 OVERRIDEVAR(return, id->overrideval.i = *id->storage.i, , )
726 if(val < id->minval || val > id->maxval) val = clampvar(id, val, id->minval, id->maxval);
727 *id->storage.i = val;
728 id->changed(); // call trigger function if available
729 #ifndef STANDALONE
730 if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
731 #endif
732 }
733 }
734
setvarchecked(ident * id,tagval * args,int numargs)735 static inline void setvarchecked(ident *id, tagval *args, int numargs)
736 {
737 int val = forceint(args[0]);
738 if(id->flags&IDF_HEX && numargs > 1)
739 {
740 val = (val << 16) | (forceint(args[1])<<8);
741 if(numargs > 2) val |= forceint(args[2]);
742 }
743 setvarchecked(id, val);
744 }
745
clampfvar(ident * id,float val,float minval,float maxval)746 float clampfvar(ident *id, float val, float minval, float maxval)
747 {
748 if(val < minval) val = minval;
749 else if(val > maxval) val = maxval;
750 else return val;
751 debugcode("valid range for %s is %s..%s", id->name, floatstr(minval), floatstr(maxval));
752 return val;
753 }
754
setfvarchecked(ident * id,float val)755 void setfvarchecked(ident *id, float val)
756 {
757 if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
758 #ifndef STANDALONE
759 else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
760 #else
761 else
762 #endif
763 {
764 OVERRIDEVAR(return, id->overrideval.f = *id->storage.f, , );
765 if(val < id->minvalf || val > id->maxvalf) val = clampfvar(id, val, id->minvalf, id->maxvalf);
766 *id->storage.f = val;
767 id->changed();
768 #ifndef STANDALONE
769 if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
770 #endif
771 }
772 }
773
setsvarchecked(ident * id,const char * val)774 void setsvarchecked(ident *id, const char *val)
775 {
776 if(id->flags&IDF_READONLY) debugcode("variable %s is read-only", id->name);
777 #ifndef STANDALONE
778 else if(!(id->flags&IDF_OVERRIDE) || identflags&IDF_OVERRIDDEN || game::allowedittoggle())
779 #else
780 else
781 #endif
782 {
783 OVERRIDEVAR(return, id->overrideval.s = *id->storage.s, delete[] id->overrideval.s, delete[] *id->storage.s);
784 *id->storage.s = newstring(val);
785 id->changed();
786 #ifndef STANDALONE
787 if(id->flags&IDF_OVERRIDE && !(identflags&IDF_OVERRIDDEN)) game::vartrigger(id);
788 #endif
789 }
790 }
791
addcommand(const char * name,identfun fun,const char * args,int type)792 bool addcommand(const char *name, identfun fun, const char *args, int type)
793 {
794 uint argmask = 0;
795 int numargs = 0;
796 bool limit = true;
797 if(args) for(const char *fmt = args; *fmt; fmt++) switch(*fmt)
798 {
799 case 'i': case 'b': case 'f': case 'F': case 't': case 'T': case 'E': case 'N': case 'D': if(numargs < MAXARGS) numargs++; break;
800 case 'S': case 's': case 'e': case 'r': case '$': if(numargs < MAXARGS) { argmask |= 1<<numargs; numargs++; } break;
801 case '1': case '2': case '3': case '4': if(numargs < MAXARGS) fmt -= *fmt-'0'+1; break;
802 case 'C': case 'V': limit = false; break;
803 default: fatal("builtin %s declared with illegal type: %s", name, args); break;
804 }
805 if(limit && numargs > MAXCOMARGS) fatal("builtin %s declared with too many args: %d", name, numargs);
806 addident(ident(type, name, args, argmask, numargs, (void *)fun));
807 return false;
808 }
809
parsestring(const char * p)810 const char *parsestring(const char *p)
811 {
812 for(; *p; p++) switch(*p)
813 {
814 case '\r':
815 case '\n':
816 case '\"':
817 return p;
818 case '^':
819 if(*++p) break;
820 return p;
821 }
822 return p;
823 }
824
unescapestring(char * dst,const char * src,const char * end)825 int unescapestring(char *dst, const char *src, const char *end)
826 {
827 char *start = dst;
828 while(src < end)
829 {
830 int c = *src++;
831 if(c == '^')
832 {
833 if(src >= end) break;
834 int e = *src++;
835 switch(e)
836 {
837 case 'n': *dst++ = '\n'; break;
838 case 't': *dst++ = '\t'; break;
839 case 'f': *dst++ = '\f'; break;
840 default: *dst++ = e; break;
841 }
842 }
843 else *dst++ = c;
844 }
845 *dst = '\0';
846 return dst - start;
847 }
848
conc(vector<char> & buf,tagval * v,int n,bool space,const char * prefix=NULL,int prefixlen=0)849 static char *conc(vector<char> &buf, tagval *v, int n, bool space, const char *prefix = NULL, int prefixlen = 0)
850 {
851 if(prefix)
852 {
853 buf.put(prefix, prefixlen);
854 if(space && n) buf.add(' ');
855 }
856 loopi(n)
857 {
858 const char *s = "";
859 int len = 0;
860 switch(v[i].type)
861 {
862 case VAL_INT: s = intstr(v[i].i); break;
863 case VAL_FLOAT: s = floatstr(v[i].f); break;
864 case VAL_STR: case VAL_CSTR: s = v[i].s; break;
865 case VAL_MACRO: s = v[i].s; len = v[i].code[-1]>>8; goto haslen;
866 }
867 len = int(strlen(s));
868 haslen:
869 buf.put(s, len);
870 if(i == n-1) break;
871 if(space) buf.add(' ');
872 }
873 buf.add('\0');
874 return buf.getbuf();
875 }
876
conc(tagval * v,int n,bool space,const char * prefix,int prefixlen)877 static char *conc(tagval *v, int n, bool space, const char *prefix, int prefixlen)
878 {
879 static int vlen[MAXARGS];
880 static char numbuf[3*MAXSTRLEN];
881 int len = prefixlen, numlen = 0, i = 0;
882 for(; i < n; i++) switch(v[i].type)
883 {
884 case VAL_MACRO: len += (vlen[i] = v[i].code[-1]>>8); break;
885 case VAL_STR: case VAL_CSTR: len += (vlen[i] = int(strlen(v[i].s))); break;
886 case VAL_INT:
887 if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
888 intformat(&numbuf[numlen], v[i].i);
889 numlen += (vlen[i] = strlen(&numbuf[numlen]));
890 break;
891 case VAL_FLOAT:
892 if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
893 floatformat(&numbuf[numlen], v[i].f);
894 numlen += (vlen[i] = strlen(&numbuf[numlen]));
895 break;
896 default: vlen[i] = 0; break;
897 }
898 overflow:
899 if(space) len += max(prefix ? i : i-1, 0);
900 char *buf = newstring(len + numlen);
901 int offset = 0, numoffset = 0;
902 if(prefix)
903 {
904 memcpy(buf, prefix, prefixlen);
905 offset += prefixlen;
906 if(space && i) buf[offset++] = ' ';
907 }
908 loopj(i)
909 {
910 if(v[j].type == VAL_INT || v[j].type == VAL_FLOAT)
911 {
912 memcpy(&buf[offset], &numbuf[numoffset], vlen[j]);
913 numoffset += vlen[j];
914 }
915 else if(vlen[j]) memcpy(&buf[offset], v[j].s, vlen[j]);
916 offset += vlen[j];
917 if(j==i-1) break;
918 if(space) buf[offset++] = ' ';
919 }
920 buf[offset] = '\0';
921 if(i < n)
922 {
923 char *morebuf = conc(&v[i], n-i, space, buf, offset);
924 delete[] buf;
925 return morebuf;
926 }
927 return buf;
928 }
929
conc(tagval * v,int n,bool space)930 static inline char *conc(tagval *v, int n, bool space)
931 {
932 return conc(v, n, space, NULL, 0);
933 }
934
conc(tagval * v,int n,bool space,const char * prefix)935 static inline char *conc(tagval *v, int n, bool space, const char *prefix)
936 {
937 return conc(v, n, space, prefix, strlen(prefix));
938 }
939
skipcomments(const char * & p)940 static inline void skipcomments(const char *&p)
941 {
942 for(;;)
943 {
944 p += strspn(p, " \t\r");
945 if(p[0]!='/' || p[1]!='/') break;
946 p += strcspn(p, "\n\0");
947 }
948 }
949
950 static vector<char> strbuf[4];
951 static int stridx = 0;
952
cutstring(const char * & p,stringslice & s)953 static inline void cutstring(const char *&p, stringslice &s)
954 {
955 p++;
956 const char *end = parsestring(p);
957 int maxlen = int(end-p) + 1;
958
959 stridx = (stridx + 1)%4;
960 vector<char> &buf = strbuf[stridx];
961 if(buf.alen < maxlen) buf.growbuf(maxlen);
962
963 s.str = buf.buf;
964 s.len = unescapestring(buf.buf, p, end);
965 p = end;
966 if(*p=='\"') p++;
967 }
968
cutstring(const char * & p)969 static inline char *cutstring(const char *&p)
970 {
971 p++;
972 const char *end = parsestring(p);
973 char *buf = newstring(end-p);
974 unescapestring(buf, p, end);
975 p = end;
976 if(*p=='\"') p++;
977 return buf;
978 }
979
parseword(const char * p)980 static inline const char *parseword(const char *p)
981 {
982 const int maxbrak = 100;
983 static char brakstack[maxbrak];
984 int brakdepth = 0;
985 for(;; p++)
986 {
987 p += strcspn(p, "\"/;()[] \t\r\n\0");
988 switch(p[0])
989 {
990 case '"': case ';': case ' ': case '\t': case '\r': case '\n': case '\0': return p;
991 case '/': if(p[1] == '/') return p; break;
992 case '[': case '(': if(brakdepth >= maxbrak) return p; brakstack[brakdepth++] = p[0]; break;
993 case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return p; break;
994 case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return p; break;
995 }
996 }
997 return p;
998 }
999
cutword(const char * & p,stringslice & s)1000 static inline void cutword(const char *&p, stringslice &s)
1001 {
1002 s.str = p;
1003 p = parseword(p);
1004 s.len = int(p-s.str);
1005 }
1006
cutword(const char * & p)1007 static inline char *cutword(const char *&p)
1008 {
1009 const char *word = p;
1010 p = parseword(p);
1011 return p!=word ? newstring(word, p-word) : NULL;
1012 }
1013
1014 #define retcode(type, defaultret) ((type) >= VAL_ANY ? ((type) == VAL_CSTR ? RET_STR : (defaultret)) : (type) << CODE_RET)
1015 #define retcodeint(type) retcode(type, RET_INT)
1016 #define retcodefloat(type) retcode(type, RET_FLOAT)
1017 #define retcodeany(type) retcode(type, 0)
1018 #define retcodestr(type) ((type) >= VAL_ANY ? RET_STR : (type) << CODE_RET)
1019
compilestr(vector<uint> & code,const char * word,int len,bool macro=false)1020 static inline void compilestr(vector<uint> &code, const char *word, int len, bool macro = false)
1021 {
1022 if(len <= 3 && !macro)
1023 {
1024 uint op = CODE_VALI|RET_STR;
1025 loopi(len) op |= uint(uchar(word[i]))<<((i+1)*8);
1026 code.add(op);
1027 return;
1028 }
1029 code.add((macro ? CODE_MACRO : CODE_VAL|RET_STR)|(len<<8));
1030 code.put((const uint *)word, len/sizeof(uint));
1031 size_t endlen = len%sizeof(uint);
1032 union
1033 {
1034 char c[sizeof(uint)];
1035 uint u;
1036 } end;
1037 end.u = 0;
1038 memcpy(end.c, word + len - endlen, endlen);
1039 code.add(end.u);
1040 }
1041
compilestr(vector<uint> & code)1042 static inline void compilestr(vector<uint> &code) { code.add(CODE_VALI|RET_STR); }
compilestr(vector<uint> & code,const stringslice & word,bool macro=false)1043 static inline void compilestr(vector<uint> &code, const stringslice &word, bool macro = false) { compilestr(code, word.str, word.len, macro); }
compilestr(vector<uint> & code,const char * word,bool macro=false)1044 static inline void compilestr(vector<uint> &code, const char *word, bool macro = false) { compilestr(code, word, int(strlen(word)), macro); }
1045
compileunescapestring(vector<uint> & code,const char * & p,bool macro=false)1046 static inline void compileunescapestring(vector<uint> &code, const char *&p, bool macro = false)
1047 {
1048 p++;
1049 const char *end = parsestring(p);
1050 code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
1051 char *buf = (char *)code.reserve(int(end-p)/sizeof(uint) + 1).buf;
1052 int len = unescapestring(buf, p, end);
1053 memset(&buf[len], 0, sizeof(uint) - len%sizeof(uint));
1054 code.last() |= len<<8;
1055 code.advance(len/sizeof(uint) + 1);
1056 p = end;
1057 if(*p == '\"') p++;
1058 }
1059
compileint(vector<uint> & code,int i=0)1060 static inline void compileint(vector<uint> &code, int i = 0)
1061 {
1062 if(i >= -0x800000 && i <= 0x7FFFFF)
1063 code.add(CODE_VALI|RET_INT|(i<<8));
1064 else
1065 {
1066 code.add(CODE_VAL|RET_INT);
1067 code.add(i);
1068 }
1069 }
1070
compilenull(vector<uint> & code)1071 static inline void compilenull(vector<uint> &code)
1072 {
1073 code.add(CODE_VALI|RET_NULL);
1074 }
1075
1076 static uint emptyblock[VAL_ANY][2] =
1077 {
1078 { CODE_START + 0x100, CODE_EXIT|RET_NULL },
1079 { CODE_START + 0x100, CODE_EXIT|RET_INT },
1080 { CODE_START + 0x100, CODE_EXIT|RET_FLOAT },
1081 { CODE_START + 0x100, CODE_EXIT|RET_STR }
1082 };
1083
compileblock(vector<uint> & code)1084 static inline void compileblock(vector<uint> &code)
1085 {
1086 code.add(CODE_EMPTY);
1087 }
1088
1089 static void compilestatements(vector<uint> &code, const char *&p, int rettype, int brak = '\0', int prevargs = 0);
1090
compileblock(vector<uint> & code,const char * p,int rettype=RET_NULL,int brak='\\0')1091 static inline const char *compileblock(vector<uint> &code, const char *p, int rettype = RET_NULL, int brak = '\0')
1092 {
1093 int start = code.length();
1094 code.add(CODE_BLOCK);
1095 code.add(CODE_OFFSET|((start+2)<<8));
1096 if(p) compilestatements(code, p, VAL_ANY, brak);
1097 if(code.length() > start + 2)
1098 {
1099 code.add(CODE_EXIT|rettype);
1100 code[start] |= uint(code.length() - (start + 1))<<8;
1101 }
1102 else
1103 {
1104 code.setsize(start);
1105 code.add(CODE_EMPTY|rettype);
1106 }
1107 return p;
1108 }
1109
compileident(vector<uint> & code,ident * id=dummyident)1110 static inline void compileident(vector<uint> &code, ident *id = dummyident)
1111 {
1112 code.add((id->index < MAXARGS ? CODE_IDENTARG : CODE_IDENT)|(id->index<<8));
1113 }
1114
compileident(vector<uint> & code,const stringslice & word)1115 static inline void compileident(vector<uint> &code, const stringslice &word)
1116 {
1117 compileident(code, newident(word, IDF_UNKNOWN));
1118 }
1119
compileint(vector<uint> & code,const stringslice & word)1120 static inline void compileint(vector<uint> &code, const stringslice &word)
1121 {
1122 compileint(code, word.len ? parseint(word.str) : 0);
1123 }
1124
compilefloat(vector<uint> & code,float f=0.0f)1125 static inline void compilefloat(vector<uint> &code, float f = 0.0f)
1126 {
1127 if(int(f) == f && f >= -0x800000 && f <= 0x7FFFFF)
1128 code.add(CODE_VALI|RET_FLOAT|(int(f)<<8));
1129 else
1130 {
1131 union { float f; uint u; } conv;
1132 conv.f = f;
1133 code.add(CODE_VAL|RET_FLOAT);
1134 code.add(conv.u);
1135 }
1136 }
1137
compilefloat(vector<uint> & code,const stringslice & word)1138 static inline void compilefloat(vector<uint> &code, const stringslice &word)
1139 {
1140 compilefloat(code, word.len ? parsefloat(word.str) : 0.0f);
1141 }
1142
getbool(const char * s)1143 static inline bool getbool(const char *s)
1144 {
1145 switch(s[0])
1146 {
1147 case '+': case '-':
1148 switch(s[1])
1149 {
1150 case '0': break;
1151 case '.': return !isdigit(s[2]) || parsefloat(s) != 0;
1152 default: return true;
1153 }
1154 // fall-through
1155 case '0':
1156 {
1157 char *end;
1158 int val = int(strtoul((char *)s, &end, 0));
1159 if(val) return true;
1160 switch(*end)
1161 {
1162 case 'e': case '.': return parsefloat(s) != 0;
1163 default: return false;
1164 }
1165 }
1166 case '.': return !isdigit(s[1]) || parsefloat(s) != 0;
1167 case '\0': return false;
1168 default: return true;
1169 }
1170 }
1171
getbool(const tagval & v)1172 static inline bool getbool(const tagval &v)
1173 {
1174 switch(v.type)
1175 {
1176 case VAL_FLOAT: return v.f!=0;
1177 case VAL_INT: return v.i!=0;
1178 case VAL_STR: case VAL_MACRO: case VAL_CSTR: return getbool(v.s);
1179 default: return false;
1180 }
1181 }
1182
compileval(vector<uint> & code,int wordtype,const stringslice & word=stringslice (NULL,0))1183 static inline void compileval(vector<uint> &code, int wordtype, const stringslice &word = stringslice(NULL, 0))
1184 {
1185 switch(wordtype)
1186 {
1187 case VAL_CANY: if(word.len) compilestr(code, word, true); else compilenull(code); break;
1188 case VAL_CSTR: compilestr(code, word, true); break;
1189 case VAL_ANY: if(word.len) compilestr(code, word); else compilenull(code); break;
1190 case VAL_STR: compilestr(code, word); break;
1191 case VAL_FLOAT: compilefloat(code, word); break;
1192 case VAL_INT: compileint(code, word); break;
1193 case VAL_COND: if(word.len) compileblock(code, word.str); else compilenull(code); break;
1194 case VAL_CODE: compileblock(code, word.str); break;
1195 case VAL_IDENT: compileident(code, word); break;
1196 default: break;
1197 }
1198 }
1199
1200 static stringslice unusedword(NULL, 0);
1201 static bool compilearg(vector<uint> &code, const char *&p, int wordtype, int prevargs = MAXRESULTS, stringslice &word = unusedword);
1202
compilelookup(vector<uint> & code,const char * & p,int ltype,int prevargs=MAXRESULTS)1203 static void compilelookup(vector<uint> &code, const char *&p, int ltype, int prevargs = MAXRESULTS)
1204 {
1205 stringslice lookup;
1206 switch(*++p)
1207 {
1208 case '(':
1209 case '[':
1210 if(!compilearg(code, p, VAL_CSTR, prevargs)) goto invalid;
1211 break;
1212 case '$':
1213 compilelookup(code, p, VAL_CSTR, prevargs);
1214 break;
1215 case '\"':
1216 cutstring(p, lookup);
1217 goto lookupid;
1218 default:
1219 {
1220 cutword(p, lookup);
1221 if(!lookup.len) goto invalid;
1222 lookupid:
1223 ident *id = newident(lookup, IDF_UNKNOWN);
1224 if(id) switch(id->type)
1225 {
1226 case ID_VAR:
1227 code.add(CODE_IVAR|retcodeint(ltype)|(id->index<<8));
1228 switch(ltype)
1229 {
1230 case VAL_POP: code.pop(); break;
1231 case VAL_CODE: code.add(CODE_COMPILE); break;
1232 case VAL_IDENT: code.add(CODE_IDENTU); break;
1233 }
1234 return;
1235 case ID_FVAR:
1236 code.add(CODE_FVAR|retcodefloat(ltype)|(id->index<<8));
1237 switch(ltype)
1238 {
1239 case VAL_POP: code.pop(); break;
1240 case VAL_CODE: code.add(CODE_COMPILE); break;
1241 case VAL_IDENT: code.add(CODE_IDENTU); break;
1242 }
1243 return;
1244 case ID_SVAR:
1245 switch(ltype)
1246 {
1247 case VAL_POP: return;
1248 case VAL_CANY: case VAL_CSTR: case VAL_CODE: case VAL_IDENT: case VAL_COND:
1249 code.add(CODE_SVARM|(id->index<<8));
1250 break;
1251 default:
1252 code.add(CODE_SVAR|retcodestr(ltype)|(id->index<<8));
1253 break;
1254 }
1255 goto done;
1256 case ID_ALIAS:
1257 switch(ltype)
1258 {
1259 case VAL_POP: return;
1260 case VAL_CANY: case VAL_COND:
1261 code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|(id->index<<8));
1262 break;
1263 case VAL_CSTR: case VAL_CODE: case VAL_IDENT:
1264 code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|RET_STR|(id->index<<8));
1265 break;
1266 default:
1267 code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|retcodestr(ltype)|(id->index<<8));
1268 break;
1269 }
1270 goto done;
1271 case ID_COMMAND:
1272 {
1273 int comtype = CODE_COM, numargs = 0;
1274 if(prevargs >= MAXRESULTS) code.add(CODE_ENTER);
1275 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
1276 {
1277 case 'S': compilestr(code); numargs++; break;
1278 case 's': compilestr(code, NULL, 0, true); numargs++; break;
1279 case 'i': compileint(code); numargs++; break;
1280 case 'b': compileint(code, INT_MIN); numargs++; break;
1281 case 'f': compilefloat(code); numargs++; break;
1282 case 'F': code.add(CODE_DUP|RET_FLOAT); numargs++; break;
1283 case 'E':
1284 case 'T':
1285 case 't': compilenull(code); numargs++; break;
1286 case 'e': compileblock(code); numargs++; break;
1287 case 'r': compileident(code); numargs++; break;
1288 case '$': compileident(code, id); numargs++; break;
1289 case 'N': compileint(code, -1); numargs++; break;
1290 #ifndef STANDALONE
1291 case 'D': comtype = CODE_COMD; numargs++; break;
1292 #endif
1293 case 'C': comtype = CODE_COMC; goto compilecomv;
1294 case 'V': comtype = CODE_COMV; goto compilecomv;
1295 case '1': case '2': case '3': case '4': break;
1296 }
1297 code.add(comtype|retcodeany(ltype)|(id->index<<8));
1298 code.add((prevargs >= MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG) | retcodeany(ltype));
1299 goto done;
1300 compilecomv:
1301 code.add(comtype|retcodeany(ltype)|(numargs<<8)|(id->index<<13));
1302 code.add((prevargs >= MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG) | retcodeany(ltype));
1303 goto done;
1304 }
1305 default: goto invalid;
1306 }
1307 compilestr(code, lookup, true);
1308 break;
1309 }
1310 }
1311 switch(ltype)
1312 {
1313 case VAL_CANY: case VAL_COND:
1314 code.add(CODE_LOOKUPMU);
1315 break;
1316 case VAL_CSTR: case VAL_CODE: case VAL_IDENT:
1317 code.add(CODE_LOOKUPMU|RET_STR);
1318 break;
1319 default:
1320 code.add(CODE_LOOKUPU|retcodeany(ltype));
1321 break;
1322 }
1323 done:
1324 switch(ltype)
1325 {
1326 case VAL_POP: code.add(CODE_POP); break;
1327 case VAL_CODE: code.add(CODE_COMPILE); break;
1328 case VAL_COND: code.add(CODE_COND); break;
1329 case VAL_IDENT: code.add(CODE_IDENTU); break;
1330 }
1331 return;
1332 invalid:
1333 switch(ltype)
1334 {
1335 case VAL_POP: break;
1336 case VAL_NULL: case VAL_ANY: case VAL_CANY: case VAL_WORD: case VAL_COND: compilenull(code); break;
1337 default: compileval(code, ltype); break;
1338 }
1339 }
1340
compileblockstr(vector<uint> & code,const char * str,const char * end,bool macro)1341 static bool compileblockstr(vector<uint> &code, const char *str, const char *end, bool macro)
1342 {
1343 int start = code.length();
1344 code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
1345 char *buf = (char *)code.reserve((end-str)/sizeof(uint)+1).buf;
1346 int len = 0;
1347 while(str < end)
1348 {
1349 int n = strcspn(str, "\r/\"@]\0");
1350 memcpy(&buf[len], str, n);
1351 len += n;
1352 str += n;
1353 switch(*str)
1354 {
1355 case '\r': str++; break;
1356 case '\"':
1357 {
1358 const char *start = str;
1359 str = parsestring(str+1);
1360 if(*str=='\"') str++;
1361 memcpy(&buf[len], start, str-start);
1362 len += str-start;
1363 break;
1364 }
1365 case '/':
1366 if(str[1] == '/') str += strcspn(str, "\n\0");
1367 else buf[len++] = *str++;
1368 break;
1369 case '@':
1370 case ']':
1371 if(str < end) { buf[len++] = *str++; break; }
1372 case '\0': goto done;
1373 }
1374 }
1375 done:
1376 memset(&buf[len], '\0', sizeof(uint)-len%sizeof(uint));
1377 code.advance(len/sizeof(uint)+1);
1378 code[start] |= len<<8;
1379 return true;
1380 }
1381
compileblocksub(vector<uint> & code,const char * & p,int prevargs)1382 static bool compileblocksub(vector<uint> &code, const char *&p, int prevargs)
1383 {
1384 stringslice lookup;
1385 switch(*p)
1386 {
1387 case '(':
1388 if(!compilearg(code, p, VAL_CANY, prevargs)) return false;
1389 break;
1390 case '[':
1391 if(!compilearg(code, p, VAL_CSTR, prevargs)) return false;
1392 code.add(CODE_LOOKUPMU);
1393 break;
1394 case '\"':
1395 cutstring(p, lookup);
1396 goto lookupid;
1397 default:
1398 {
1399
1400 lookup.str = p;
1401 while(iscubealnum(*p) || *p=='_') p++;
1402 lookup.len = int(p-lookup.str);
1403 if(!lookup.len) return false;
1404 lookupid:
1405 ident *id = newident(lookup, IDF_UNKNOWN);
1406 if(id) switch(id->type)
1407 {
1408 case ID_VAR: code.add(CODE_IVAR|(id->index<<8)); goto done;
1409 case ID_FVAR: code.add(CODE_FVAR|(id->index<<8)); goto done;
1410 case ID_SVAR: code.add(CODE_SVARM|(id->index<<8)); goto done;
1411 case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|(id->index<<8)); goto done;
1412 }
1413 compilestr(code, lookup, true);
1414 code.add(CODE_LOOKUPMU);
1415 done:
1416 break;
1417 }
1418 }
1419 return true;
1420 }
1421
compileblockmain(vector<uint> & code,const char * & p,int wordtype,int prevargs)1422 static void compileblockmain(vector<uint> &code, const char *&p, int wordtype, int prevargs)
1423 {
1424 const char *line = p, *start = p;
1425 int concs = 0;
1426 for(int brak = 1; brak;)
1427 {
1428 p += strcspn(p, "@\"/[]\0");
1429 int c = *p++;
1430 switch(c)
1431 {
1432 case '\0':
1433 debugcodeline(line, "missing \"]\"");
1434 p--;
1435 goto done;
1436 case '\"':
1437 p = parsestring(p);
1438 if(*p=='\"') p++;
1439 break;
1440 case '/':
1441 if(*p=='/') p += strcspn(p, "\n\0");
1442 break;
1443 case '[': brak++; break;
1444 case ']': brak--; break;
1445 case '@':
1446 {
1447 const char *esc = p;
1448 while(*p == '@') p++;
1449 int level = p - (esc - 1);
1450 if(brak > level) continue;
1451 else if(brak < level) debugcodeline(line, "too many @s");
1452 if(!concs && prevargs >= MAXRESULTS) code.add(CODE_ENTER);
1453 if(concs + 2 > MAXARGS)
1454 {
1455 code.add(CODE_CONCW|RET_STR|(concs<<8));
1456 concs = 1;
1457 }
1458 if(compileblockstr(code, start, esc-1, true)) concs++;
1459 if(compileblocksub(code, p, prevargs + concs)) concs++;
1460 if(concs) start = p;
1461 else if(prevargs >= MAXRESULTS) code.pop();
1462 break;
1463 }
1464 }
1465 }
1466 done:
1467 if(p-1 > start)
1468 {
1469 if(!concs) switch(wordtype)
1470 {
1471 case VAL_POP:
1472 return;
1473 case VAL_CODE: case VAL_COND:
1474 p = compileblock(code, start, RET_NULL, ']');
1475 return;
1476 case VAL_IDENT:
1477 compileident(code, stringslice(start, p-1));
1478 return;
1479 }
1480 switch(wordtype)
1481 {
1482 case VAL_CSTR: case VAL_CODE: case VAL_IDENT: case VAL_CANY: case VAL_COND:
1483 compileblockstr(code, start, p-1, true);
1484 break;
1485 default:
1486 compileblockstr(code, start, p-1, concs > 0);
1487 break;
1488 }
1489 if(concs > 1) concs++;
1490 }
1491 if(concs)
1492 {
1493 if(prevargs >= MAXRESULTS)
1494 {
1495 code.add(CODE_CONCM|retcodeany(wordtype)|(concs<<8));
1496 code.add(CODE_EXIT|retcodeany(wordtype));
1497 }
1498 else code.add(CODE_CONCW|retcodeany(wordtype)|(concs<<8));
1499 }
1500 switch(wordtype)
1501 {
1502 case VAL_POP: if(concs || p-1 > start) code.add(CODE_POP); break;
1503 case VAL_COND: if(!concs && p-1 <= start) compilenull(code); else code.add(CODE_COND); break;
1504 case VAL_CODE: if(!concs && p-1 <= start) compileblock(code); else code.add(CODE_COMPILE); break;
1505 case VAL_IDENT: if(!concs && p-1 <= start) compileident(code); else code.add(CODE_IDENTU); break;
1506 case VAL_CSTR: case VAL_CANY:
1507 if(!concs && p-1 <= start) compilestr(code, NULL, 0, true);
1508 break;
1509 case VAL_STR: case VAL_NULL: case VAL_ANY: case VAL_WORD:
1510 if(!concs && p-1 <= start) compilestr(code);
1511 break;
1512 default:
1513 if(!concs)
1514 {
1515 if(p-1 <= start) compileval(code, wordtype);
1516 else code.add(CODE_FORCE|(wordtype<<CODE_RET));
1517 }
1518 break;
1519 }
1520 }
1521
compilearg(vector<uint> & code,const char * & p,int wordtype,int prevargs,stringslice & word)1522 static bool compilearg(vector<uint> &code, const char *&p, int wordtype, int prevargs, stringslice &word)
1523 {
1524 skipcomments(p);
1525 switch(*p)
1526 {
1527 case '\"':
1528 switch(wordtype)
1529 {
1530 case VAL_POP:
1531 p = parsestring(p+1);
1532 if(*p == '\"') p++;
1533 break;
1534 case VAL_COND:
1535 {
1536 char *s = cutstring(p);
1537 if(s[0]) compileblock(code, s);
1538 else compilenull(code);
1539 delete[] s;
1540 break;
1541 }
1542 case VAL_CODE:
1543 {
1544 char *s = cutstring(p);
1545 compileblock(code, s);
1546 delete[] s;
1547 break;
1548 }
1549 case VAL_WORD:
1550 cutstring(p, word);
1551 break;
1552 case VAL_ANY:
1553 case VAL_STR:
1554 compileunescapestring(code, p);
1555 break;
1556 case VAL_CANY:
1557 case VAL_CSTR:
1558 compileunescapestring(code, p, true);
1559 break;
1560 default:
1561 {
1562 stringslice s;
1563 cutstring(p, s);
1564 compileval(code, wordtype, s);
1565 break;
1566 }
1567 }
1568 return true;
1569 case '$': compilelookup(code, p, wordtype, prevargs); return true;
1570 case '(':
1571 p++;
1572 if(prevargs >= MAXRESULTS)
1573 {
1574 code.add(CODE_ENTER);
1575 compilestatements(code, p, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')');
1576 code.add(CODE_EXIT|retcodeany(wordtype));
1577 }
1578 else
1579 {
1580 int start = code.length();
1581 compilestatements(code, p, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')', prevargs);
1582 if(code.length() > start) code.add(CODE_RESULT_ARG|retcodeany(wordtype));
1583 else { compileval(code, wordtype); return true; }
1584 }
1585 switch(wordtype)
1586 {
1587 case VAL_POP: code.add(CODE_POP); break;
1588 case VAL_COND: code.add(CODE_COND); break;
1589 case VAL_CODE: code.add(CODE_COMPILE); break;
1590 case VAL_IDENT: code.add(CODE_IDENTU); break;
1591 }
1592 return true;
1593 case '[':
1594 p++;
1595 compileblockmain(code, p, wordtype, prevargs);
1596 return true;
1597 default:
1598 switch(wordtype)
1599 {
1600 case VAL_POP:
1601 {
1602 const char *s = p;
1603 p = parseword(p);
1604 return p != s;
1605 }
1606 case VAL_COND:
1607 {
1608 char *s = cutword(p);
1609 if(!s) return false;
1610 compileblock(code, s);
1611 delete[] s;
1612 return true;
1613 }
1614 case VAL_CODE:
1615 {
1616 char *s = cutword(p);
1617 if(!s) return false;
1618 compileblock(code, s);
1619 delete[] s;
1620 return true;
1621 }
1622 case VAL_WORD:
1623 cutword(p, word);
1624 return word.len!=0;
1625 default:
1626 {
1627 stringslice s;
1628 cutword(p, s);
1629 if(!s.len) return false;
1630 compileval(code, wordtype, s);
1631 return true;
1632 }
1633 }
1634 }
1635 }
1636
compilestatements(vector<uint> & code,const char * & p,int rettype,int brak,int prevargs)1637 static void compilestatements(vector<uint> &code, const char *&p, int rettype, int brak, int prevargs)
1638 {
1639 const char *line = p;
1640 stringslice idname;
1641 int numargs;
1642 for(;;)
1643 {
1644 skipcomments(p);
1645 idname.str = NULL;
1646 bool more = compilearg(code, p, VAL_WORD, prevargs, idname);
1647 if(!more) goto endstatement;
1648 skipcomments(p);
1649 if(p[0] == '=') switch(p[1])
1650 {
1651 case '/':
1652 if(p[2] != '/') break;
1653 case ';': case ' ': case '\t': case '\r': case '\n': case '\0':
1654 p++;
1655 if(idname.str)
1656 {
1657 ident *id = newident(idname, IDF_UNKNOWN);
1658 if(id) switch(id->type)
1659 {
1660 case ID_ALIAS:
1661 if(!(more = compilearg(code, p, VAL_ANY, prevargs))) compilestr(code);
1662 code.add((id->index < MAXARGS ? CODE_ALIASARG : CODE_ALIAS)|(id->index<<8));
1663 goto endstatement;
1664 case ID_VAR:
1665 if(!(more = compilearg(code, p, VAL_INT, prevargs))) compileint(code);
1666 code.add(CODE_IVAR1|(id->index<<8));
1667 goto endstatement;
1668 case ID_FVAR:
1669 if(!(more = compilearg(code, p, VAL_FLOAT, prevargs))) compilefloat(code);
1670 code.add(CODE_FVAR1|(id->index<<8));
1671 goto endstatement;
1672 case ID_SVAR:
1673 if(!(more = compilearg(code, p, VAL_CSTR, prevargs))) compilestr(code);
1674 code.add(CODE_SVAR1|(id->index<<8));
1675 goto endstatement;
1676 }
1677 compilestr(code, idname, true);
1678 }
1679 if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
1680 code.add(CODE_ALIASU);
1681 goto endstatement;
1682 }
1683 numargs = 0;
1684 if(!idname.str)
1685 {
1686 noid:
1687 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++;
1688 code.add(CODE_CALLU|(numargs<<8));
1689 }
1690 else
1691 {
1692 ident *id = idents.access(idname);
1693 if(!id)
1694 {
1695 if(!checknumber(idname)) { compilestr(code, idname, true); goto noid; }
1696 switch(rettype)
1697 {
1698 case VAL_ANY:
1699 case VAL_CANY:
1700 {
1701 char *end = (char *)idname.str;
1702 int val = int(strtoul(idname.str, &end, 0));
1703 if(end < idname.end()) compilestr(code, idname, rettype==VAL_CANY);
1704 else compileint(code, val);
1705 break;
1706 }
1707 default:
1708 compileval(code, rettype, idname);
1709 break;
1710 }
1711 code.add(CODE_RESULT);
1712 }
1713 else switch(id->type)
1714 {
1715 case ID_ALIAS:
1716 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY, prevargs+numargs))) numargs++;
1717 code.add((id->index < MAXARGS ? CODE_CALLARG : CODE_CALL)|(numargs<<8)|(id->index<<13));
1718 break;
1719 case ID_COMMAND:
1720 {
1721 int comtype = CODE_COM, fakeargs = 0;
1722 bool rep = false;
1723 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
1724 {
1725 case 'S':
1726 case 's':
1727 if(more) more = compilearg(code, p, *fmt == 's' ? VAL_CSTR : VAL_STR, prevargs+numargs);
1728 if(!more)
1729 {
1730 if(rep) break;
1731 compilestr(code, NULL, 0, *fmt=='s');
1732 fakeargs++;
1733 }
1734 else if(!fmt[1])
1735 {
1736 int numconc = 1;
1737 while(numargs + numconc < MAXARGS && (more = compilearg(code, p, VAL_CSTR, prevargs+numargs+numconc))) numconc++;
1738 if(numconc > 1) code.add(CODE_CONC|RET_STR|(numconc<<8));
1739 }
1740 numargs++;
1741 break;
1742 case 'i': if(more) more = compilearg(code, p, VAL_INT, prevargs+numargs); if(!more) { if(rep) break; compileint(code); fakeargs++; } numargs++; break;
1743 case 'b': if(more) more = compilearg(code, p, VAL_INT, prevargs+numargs); if(!more) { if(rep) break; compileint(code, INT_MIN); fakeargs++; } numargs++; break;
1744 case 'f': if(more) more = compilearg(code, p, VAL_FLOAT, prevargs+numargs); if(!more) { if(rep) break; compilefloat(code); fakeargs++; } numargs++; break;
1745 case 'F': if(more) more = compilearg(code, p, VAL_FLOAT, prevargs+numargs); if(!more) { if(rep) break; code.add(CODE_DUP|RET_FLOAT); fakeargs++; } numargs++; break;
1746 case 'T':
1747 case 't': if(more) more = compilearg(code, p, *fmt == 't' ? VAL_CANY : VAL_ANY, prevargs+numargs); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
1748 case 'E': if(more) more = compilearg(code, p, VAL_COND, prevargs+numargs); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
1749 case 'e': if(more) more = compilearg(code, p, VAL_CODE, prevargs+numargs); if(!more) { if(rep) break; compileblock(code); fakeargs++; } numargs++; break;
1750 case 'r': if(more) more = compilearg(code, p, VAL_IDENT, prevargs+numargs); if(!more) { if(rep) break; compileident(code); fakeargs++; } numargs++; break;
1751 case '$': compileident(code, id); numargs++; break;
1752 case 'N': compileint(code, numargs-fakeargs); numargs++; break;
1753 #ifndef STANDALONE
1754 case 'D': comtype = CODE_COMD; numargs++; break;
1755 #endif
1756 case 'C': comtype = CODE_COMC; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++; goto compilecomv;
1757 case 'V': comtype = CODE_COMV; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++; goto compilecomv;
1758 case '1': case '2': case '3': case '4': if(more) { fmt -= *fmt-'0'+1; rep = true; } break;
1759 }
1760 code.add(comtype|retcodeany(rettype)|(id->index<<8));
1761 break;
1762 compilecomv:
1763 code.add(comtype|retcodeany(rettype)|(numargs<<8)|(id->index<<13));
1764 break;
1765 }
1766 case ID_LOCAL:
1767 if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_IDENT, prevargs+numargs))) numargs++;
1768 if(more) while((more = compilearg(code, p, VAL_POP)));
1769 code.add(CODE_LOCAL|(numargs<<8));
1770 break;
1771 case ID_DO:
1772 if(more) more = compilearg(code, p, VAL_CODE, prevargs);
1773 code.add((more ? CODE_DO : CODE_NULL) | retcodeany(rettype));
1774 break;
1775 case ID_DOARGS:
1776 if(more) more = compilearg(code, p, VAL_CODE, prevargs);
1777 code.add((more ? CODE_DOARGS : CODE_NULL) | retcodeany(rettype));
1778 break;
1779 case ID_IF:
1780 if(more) more = compilearg(code, p, VAL_CANY, prevargs);
1781 if(!more) code.add(CODE_NULL | retcodeany(rettype));
1782 else
1783 {
1784 int start1 = code.length();
1785 more = compilearg(code, p, VAL_CODE, prevargs+1);
1786 if(!more) { code.add(CODE_POP); code.add(CODE_NULL | retcodeany(rettype)); }
1787 else
1788 {
1789 int start2 = code.length();
1790 more = compilearg(code, p, VAL_CODE, prevargs+2);
1791 uint inst1 = code[start1], op1 = inst1&~CODE_RET_MASK, len1 = start2 - (start1+1);
1792 if(!more)
1793 {
1794 if(op1 == (CODE_BLOCK|(len1<<8)))
1795 {
1796 code[start1] = (len1<<8) | CODE_JUMP_FALSE;
1797 code[start1+1] = CODE_ENTER_RESULT;
1798 code[start1+len1] = (code[start1+len1]&~CODE_RET_MASK) | retcodeany(rettype);
1799 break;
1800 }
1801 compileblock(code);
1802 }
1803 else
1804 {
1805 uint inst2 = code[start2], op2 = inst2&~CODE_RET_MASK, len2 = code.length() - (start2+1);
1806 if(op2 == (CODE_BLOCK|(len2<<8)))
1807 {
1808 if(op1 == (CODE_BLOCK|(len1<<8)))
1809 {
1810 code[start1] = ((start2-start1)<<8) | CODE_JUMP_FALSE;
1811 code[start1+1] = CODE_ENTER_RESULT;
1812 code[start1+len1] = (code[start1+len1]&~CODE_RET_MASK) | retcodeany(rettype);
1813 code[start2] = (len2<<8) | CODE_JUMP;
1814 code[start2+1] = CODE_ENTER_RESULT;
1815 code[start2+len2] = (code[start2+len2]&~CODE_RET_MASK) | retcodeany(rettype);
1816 break;
1817 }
1818 else if(op1 == (CODE_EMPTY|(len1<<8)))
1819 {
1820 code[start1] = CODE_NULL | (inst2&CODE_RET_MASK);
1821 code[start2] = (len2<<8) | CODE_JUMP_TRUE;
1822 code[start2+1] = CODE_ENTER_RESULT;
1823 code[start2+len2] = (code[start2+len2]&~CODE_RET_MASK) | retcodeany(rettype);
1824 break;
1825 }
1826 }
1827 }
1828 code.add(CODE_COM|retcodeany(rettype)|(id->index<<8));
1829 }
1830 }
1831 break;
1832 case ID_RESULT:
1833 if(more) more = compilearg(code, p, VAL_ANY, prevargs);
1834 code.add((more ? CODE_RESULT : CODE_NULL) | retcodeany(rettype));
1835 break;
1836 case ID_NOT:
1837 if(more) more = compilearg(code, p, VAL_CANY, prevargs);
1838 code.add((more ? CODE_NOT : CODE_TRUE) | retcodeany(rettype));
1839 break;
1840 case ID_AND:
1841 case ID_OR:
1842 if(more) more = compilearg(code, p, VAL_COND, prevargs);
1843 if(!more) { code.add((id->type == ID_AND ? CODE_TRUE : CODE_FALSE) | retcodeany(rettype)); }
1844 else
1845 {
1846 numargs++;
1847 int start = code.length(), end = start;
1848 while(numargs < MAXARGS)
1849 {
1850 more = compilearg(code, p, VAL_COND, prevargs+numargs);
1851 if(!more) break;
1852 numargs++;
1853 if((code[end]&~CODE_RET_MASK) != (CODE_BLOCK|(uint(code.length()-(end+1))<<8))) break;
1854 end = code.length();
1855 }
1856 if(more)
1857 {
1858 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_COND, prevargs+numargs))) numargs++;
1859 code.add(CODE_COMV|retcodeany(rettype)|(numargs<<8)|(id->index<<13));
1860 }
1861 else
1862 {
1863 uint op = id->type == ID_AND ? CODE_JUMP_RESULT_FALSE : CODE_JUMP_RESULT_TRUE;
1864 code.add(op);
1865 end = code.length();
1866 while(start+1 < end)
1867 {
1868 uint len = code[start]>>8;
1869 code[start] = ((end-(start+1))<<8) | op;
1870 code[start+1] = CODE_ENTER;
1871 code[start+len] = (code[start+len]&~CODE_RET_MASK) | retcodeany(rettype);
1872 start += len+1;
1873 }
1874 }
1875 }
1876 break;
1877 case ID_VAR:
1878 if(!(more = compilearg(code, p, VAL_INT, prevargs))) code.add(CODE_PRINT|(id->index<<8));
1879 else if(!(id->flags&IDF_HEX) || !(more = compilearg(code, p, VAL_INT, prevargs+1))) code.add(CODE_IVAR1|(id->index<<8));
1880 else if(!(more = compilearg(code, p, VAL_INT, prevargs+2))) code.add(CODE_IVAR2|(id->index<<8));
1881 else code.add(CODE_IVAR3|(id->index<<8));
1882 break;
1883 case ID_FVAR:
1884 if(!(more = compilearg(code, p, VAL_FLOAT, prevargs))) code.add(CODE_PRINT|(id->index<<8));
1885 else code.add(CODE_FVAR1|(id->index<<8));
1886 break;
1887 case ID_SVAR:
1888 if(!(more = compilearg(code, p, VAL_CSTR, prevargs))) code.add(CODE_PRINT|(id->index<<8));
1889 else
1890 {
1891 do ++numargs;
1892 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs)));
1893 if(numargs > 1) code.add(CODE_CONC|RET_STR|(numargs<<8));
1894 code.add(CODE_SVAR1|(id->index<<8));
1895 }
1896 break;
1897 }
1898 }
1899 endstatement:
1900 if(more) while(compilearg(code, p, VAL_POP));
1901 p += strcspn(p, ")];/\n\0");
1902 int c = *p++;
1903 switch(c)
1904 {
1905 case '\0':
1906 if(c != brak) debugcodeline(line, "missing \"%c\"", brak);
1907 p--;
1908 return;
1909
1910 case ')':
1911 case ']':
1912 if(c == brak) return;
1913 debugcodeline(line, "unexpected \"%c\"", c);
1914 break;
1915
1916 case '/':
1917 if(*p == '/') p += strcspn(p, "\n\0");
1918 goto endstatement;
1919 }
1920 }
1921 }
1922
compilemain(vector<uint> & code,const char * p,int rettype=VAL_ANY)1923 static void compilemain(vector<uint> &code, const char *p, int rettype = VAL_ANY)
1924 {
1925 code.add(CODE_START);
1926 compilestatements(code, p, VAL_ANY);
1927 code.add(CODE_EXIT|(rettype < VAL_ANY ? rettype<<CODE_RET : 0));
1928 }
1929
compilecode(const char * p)1930 uint *compilecode(const char *p)
1931 {
1932 vector<uint> buf;
1933 buf.reserve(64);
1934 compilemain(buf, p);
1935 uint *code = new uint[buf.length()];
1936 memcpy(code, buf.getbuf(), buf.length()*sizeof(uint));
1937 code[0] += 0x100;
1938 return code;
1939 }
1940
forcecode(tagval & v)1941 static inline const uint *forcecode(tagval &v)
1942 {
1943 if(v.type != VAL_CODE)
1944 {
1945 vector<uint> buf;
1946 buf.reserve(64);
1947 compilemain(buf, v.getstr());
1948 freearg(v);
1949 v.setcode(buf.disown()+1);
1950 }
1951 return v.code;
1952 }
1953
forcecond(tagval & v)1954 static inline void forcecond(tagval &v)
1955 {
1956 switch(v.type)
1957 {
1958 case VAL_STR: case VAL_MACRO: case VAL_CSTR:
1959 if(v.s[0]) forcecode(v);
1960 else v.setint(0);
1961 break;
1962 }
1963 }
1964
keepcode(uint * code)1965 void keepcode(uint *code)
1966 {
1967 if(!code) return;
1968 switch(*code&CODE_OP_MASK)
1969 {
1970 case CODE_START:
1971 *code += 0x100;
1972 return;
1973 }
1974 switch(code[-1]&CODE_OP_MASK)
1975 {
1976 case CODE_START:
1977 code[-1] += 0x100;
1978 break;
1979 case CODE_OFFSET:
1980 code -= int(code[-1]>>8);
1981 *code += 0x100;
1982 break;
1983 }
1984 }
1985
freecode(uint * code)1986 void freecode(uint *code)
1987 {
1988 if(!code) return;
1989 switch(*code&CODE_OP_MASK)
1990 {
1991 case CODE_START:
1992 *code -= 0x100;
1993 if(int(*code) < 0x100) delete[] code;
1994 return;
1995 }
1996 switch(code[-1]&CODE_OP_MASK)
1997 {
1998 case CODE_START:
1999 code[-1] -= 0x100;
2000 if(int(code[-1]) < 0x100) delete[] &code[-1];
2001 break;
2002 case CODE_OFFSET:
2003 code -= int(code[-1]>>8);
2004 *code -= 0x100;
2005 if(int(*code) < 0x100) delete[] code;
2006 break;
2007 }
2008 }
2009
printvar(ident * id,int i)2010 void printvar(ident *id, int i)
2011 {
2012 if(i < 0) conoutf("%s = %d", id->name, i);
2013 else if(id->flags&IDF_HEX && id->maxval==0xFFFFFF)
2014 conoutf("%s = 0x%.6X (%d, %d, %d)", id->name, i, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
2015 else
2016 conoutf(id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", id->name, i);
2017 }
2018
printfvar(ident * id,float f)2019 void printfvar(ident *id, float f)
2020 {
2021 conoutf("%s = %s", id->name, floatstr(f));
2022 }
2023
printsvar(ident * id,const char * s)2024 void printsvar(ident *id, const char *s)
2025 {
2026 conoutf(strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
2027 }
2028
printvar(ident * id)2029 void printvar(ident *id)
2030 {
2031 switch(id->type)
2032 {
2033 case ID_VAR: printvar(id, *id->storage.i); break;
2034 case ID_FVAR: printfvar(id, *id->storage.f); break;
2035 case ID_SVAR: printsvar(id, *id->storage.s); break;
2036 }
2037 }
2038
2039 typedef void (__cdecl *comfun)();
2040 typedef void (__cdecl *comfun1)(void *);
2041 typedef void (__cdecl *comfun2)(void *, void *);
2042 typedef void (__cdecl *comfun3)(void *, void *, void *);
2043 typedef void (__cdecl *comfun4)(void *, void *, void *, void *);
2044 typedef void (__cdecl *comfun5)(void *, void *, void *, void *, void *);
2045 typedef void (__cdecl *comfun6)(void *, void *, void *, void *, void *, void *);
2046 typedef void (__cdecl *comfun7)(void *, void *, void *, void *, void *, void *, void *);
2047 typedef void (__cdecl *comfun8)(void *, void *, void *, void *, void *, void *, void *, void *);
2048 typedef void (__cdecl *comfun9)(void *, void *, void *, void *, void *, void *, void *, void *, void *);
2049 typedef void (__cdecl *comfun10)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2050 typedef void (__cdecl *comfun11)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2051 typedef void (__cdecl *comfun12)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2052 typedef void (__cdecl *comfunv)(tagval *, int);
2053
skipcode(const uint * code,tagval & result=noret)2054 static const uint *skipcode(const uint *code, tagval &result = noret)
2055 {
2056 int depth = 0;
2057 for(;;)
2058 {
2059 uint op = *code++;
2060 switch(op&0xFF)
2061 {
2062 case CODE_MACRO:
2063 case CODE_VAL|RET_STR:
2064 {
2065 uint len = op>>8;
2066 code += len/sizeof(uint) + 1;
2067 continue;
2068 }
2069 case CODE_BLOCK:
2070 case CODE_JUMP:
2071 case CODE_JUMP_TRUE:
2072 case CODE_JUMP_FALSE:
2073 case CODE_JUMP_RESULT_TRUE:
2074 case CODE_JUMP_RESULT_FALSE:
2075 {
2076 uint len = op>>8;
2077 code += len;
2078 continue;
2079 }
2080 case CODE_ENTER:
2081 case CODE_ENTER_RESULT:
2082 ++depth;
2083 continue;
2084 case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
2085 if(depth <= 0)
2086 {
2087 if(&result != &noret) forcearg(result, op&CODE_RET_MASK);
2088 return code;
2089 }
2090 --depth;
2091 continue;
2092 }
2093 }
2094 }
2095
2096 #ifndef STANDALONE
copycode(const uint * src)2097 static inline uint *copycode(const uint *src)
2098 {
2099 const uint *end = skipcode(src);
2100 size_t len = end - src;
2101 uint *dst = new uint[len + 1];
2102 *dst++ = CODE_START;
2103 memcpy(dst, src, len*sizeof(uint));
2104 return dst;
2105 }
2106
copyarg(tagval & dst,const tagval & src)2107 static inline void copyarg(tagval &dst, const tagval &src)
2108 {
2109 switch(src.type)
2110 {
2111 case VAL_INT:
2112 case VAL_FLOAT:
2113 case VAL_IDENT:
2114 dst = src;
2115 break;
2116 case VAL_STR:
2117 case VAL_MACRO:
2118 case VAL_CSTR:
2119 dst.setstr(newstring(src.s));
2120 break;
2121 case VAL_CODE:
2122 dst.setcode(copycode(src.code));
2123 break;
2124 default:
2125 dst.setnull();
2126 break;
2127 }
2128 }
2129
addreleaseaction(ident * id,tagval * args,int numargs)2130 static inline void addreleaseaction(ident *id, tagval *args, int numargs)
2131 {
2132 tagval *dst = addreleaseaction(id, numargs+1);
2133 if(dst) { args[numargs].setint(1); loopi(numargs+1) copyarg(dst[i], args[i]); }
2134 else args[numargs].setint(0);
2135 }
2136 #endif
2137
callcommand(ident * id,tagval * args,int numargs,bool lookup=false)2138 static inline void callcommand(ident *id, tagval *args, int numargs, bool lookup = false)
2139 {
2140 int i = -1, fakeargs = 0;
2141 bool rep = false;
2142 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
2143 {
2144 case 'i': if(++i >= numargs) { if(rep) break; args[i].setint(0); fakeargs++; } else forceint(args[i]); break;
2145 case 'b': if(++i >= numargs) { if(rep) break; args[i].setint(INT_MIN); fakeargs++; } else forceint(args[i]); break;
2146 case 'f': if(++i >= numargs) { if(rep) break; args[i].setfloat(0.0f); fakeargs++; } else forcefloat(args[i]); break;
2147 case 'F': if(++i >= numargs) { if(rep) break; args[i].setfloat(args[i-1].getfloat()); fakeargs++; } else forcefloat(args[i]); break;
2148 case 'S': if(++i >= numargs) { if(rep) break; args[i].setstr(newstring("")); fakeargs++; } else forcestr(args[i]); break;
2149 case 's': if(++i >= numargs) { if(rep) break; args[i].setcstr(""); fakeargs++; } else forcestr(args[i]); break;
2150 case 'T':
2151 case 't': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } break;
2152 case 'E': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } else forcecond(args[i]); break;
2153 case 'e':
2154 if(++i >= numargs)
2155 {
2156 if(rep) break;
2157 args[i].setcode(emptyblock[VAL_NULL]+1);
2158 fakeargs++;
2159 }
2160 else forcecode(args[i]);
2161 break;
2162 case 'r': if(++i >= numargs) { if(rep) break; args[i].setident(dummyident); fakeargs++; } else forceident(args[i]); break;
2163 case '$': if(++i < numargs) freearg(args[i]); args[i].setident(id); break;
2164 case 'N': if(++i < numargs) freearg(args[i]); args[i].setint(lookup ? -1 : i-fakeargs); break;
2165 #ifndef STANDALONE
2166 case 'D': if(++i < numargs) freearg(args[i]); addreleaseaction(id, args, i); fakeargs++; break;
2167 #endif
2168 case 'C': { i = max(i+1, numargs); vector<char> buf; ((comfun1)id->fun)(conc(buf, args, i, true)); goto cleanup; }
2169 case 'V': i = max(i+1, numargs); ((comfunv)id->fun)(args, i); goto cleanup;
2170 case '1': case '2': case '3': case '4': if(i+1 < numargs) { fmt -= *fmt-'0'+1; rep = true; } break;
2171 }
2172 ++i;
2173 #define OFFSETARG(n) n
2174 #define ARG(n) (id->argmask&(1<<(n)) ? (void *)args[OFFSETARG(n)].s : (void *)&args[OFFSETARG(n)].i)
2175 #define CALLCOM(n) \
2176 switch(n) \
2177 { \
2178 case 0: ((comfun)id->fun)(); break; \
2179 case 1: ((comfun1)id->fun)(ARG(0)); break; \
2180 case 2: ((comfun2)id->fun)(ARG(0), ARG(1)); break; \
2181 case 3: ((comfun3)id->fun)(ARG(0), ARG(1), ARG(2)); break; \
2182 case 4: ((comfun4)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3)); break; \
2183 case 5: ((comfun5)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4)); break; \
2184 case 6: ((comfun6)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5)); break; \
2185 case 7: ((comfun7)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6)); break; \
2186 case 8: ((comfun8)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7)); break; \
2187 case 9: ((comfun9)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8)); break; \
2188 case 10: ((comfun10)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9)); break; \
2189 case 11: ((comfun11)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10)); break; \
2190 case 12: ((comfun12)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11)); break; \
2191 }
2192 CALLCOM(i)
2193 #undef OFFSETARG
2194 cleanup:
2195 loopk(i) freearg(args[k]);
2196 for(; i < numargs; i++) freearg(args[i]);
2197 }
2198
2199 #define MAXRUNDEPTH 255
2200 static int rundepth = 0;
2201
runcode(const uint * code,tagval & result)2202 static const uint *runcode(const uint *code, tagval &result)
2203 {
2204 result.setnull();
2205 if(rundepth >= MAXRUNDEPTH)
2206 {
2207 debugcode("exceeded recursion limit");
2208 return skipcode(code, result);
2209 }
2210 ++rundepth;
2211 int numargs = 0;
2212 tagval args[MAXARGS+MAXRESULTS], *prevret = commandret;
2213 commandret = &result;
2214 for(;;)
2215 {
2216 uint op = *code++;
2217 switch(op&0xFF)
2218 {
2219 case CODE_START: case CODE_OFFSET: continue;
2220
2221 #define RETOP(op, val) \
2222 case op: \
2223 freearg(result); \
2224 val; \
2225 continue;
2226
2227 RETOP(CODE_NULL|RET_NULL, result.setnull())
2228 RETOP(CODE_NULL|RET_STR, result.setstr(newstring("")))
2229 RETOP(CODE_NULL|RET_INT, result.setint(0))
2230 RETOP(CODE_NULL|RET_FLOAT, result.setfloat(0.0f))
2231
2232 RETOP(CODE_FALSE|RET_STR, result.setstr(newstring("0")))
2233 case CODE_FALSE|RET_NULL:
2234 RETOP(CODE_FALSE|RET_INT, result.setint(0))
2235 RETOP(CODE_FALSE|RET_FLOAT, result.setfloat(0.0f))
2236
2237 RETOP(CODE_TRUE|RET_STR, result.setstr(newstring("1")))
2238 case CODE_TRUE|RET_NULL:
2239 RETOP(CODE_TRUE|RET_INT, result.setint(1))
2240 RETOP(CODE_TRUE|RET_FLOAT, result.setfloat(1.0f))
2241
2242 #define RETPOP(op, val) \
2243 RETOP(op, { --numargs; val; freearg(args[numargs]); })
2244
2245 RETPOP(CODE_NOT|RET_STR, result.setstr(newstring(getbool(args[numargs]) ? "0" : "1")))
2246 case CODE_NOT|RET_NULL:
2247 RETPOP(CODE_NOT|RET_INT, result.setint(getbool(args[numargs]) ? 0 : 1))
2248 RETPOP(CODE_NOT|RET_FLOAT, result.setfloat(getbool(args[numargs]) ? 0.0f : 1.0f))
2249
2250 case CODE_POP:
2251 freearg(args[--numargs]);
2252 continue;
2253 case CODE_ENTER:
2254 code = runcode(code, args[numargs++]);
2255 continue;
2256 case CODE_ENTER_RESULT:
2257 freearg(result);
2258 code = runcode(code, result);
2259 continue;
2260 case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
2261 forcearg(result, op&CODE_RET_MASK);
2262 // fall-through
2263 case CODE_EXIT|RET_NULL:
2264 goto exit;
2265 case CODE_RESULT_ARG|RET_STR: case CODE_RESULT_ARG|RET_INT: case CODE_RESULT_ARG|RET_FLOAT:
2266 forcearg(result, op&CODE_RET_MASK);
2267 // fall-through
2268 case CODE_RESULT_ARG|RET_NULL:
2269 args[numargs++] = result;
2270 result.setnull();
2271 continue;
2272 case CODE_PRINT:
2273 printvar(identmap[op>>8]);
2274 continue;
2275
2276 case CODE_LOCAL:
2277 {
2278 freearg(result);
2279 int numlocals = op>>8, offset = numargs-numlocals;
2280 identstack locals[MAXARGS];
2281 loopi(numlocals) pushalias(*args[offset+i].id, locals[i]);
2282 code = runcode(code, result);
2283 for(int i = offset; i < numargs; i++) popalias(*args[i].id);
2284 goto exit;
2285 }
2286
2287 case CODE_DOARGS|RET_NULL: case CODE_DOARGS|RET_STR: case CODE_DOARGS|RET_INT: case CODE_DOARGS|RET_FLOAT:
2288 if(aliasstack != &noalias)
2289 {
2290 UNDOARGS
2291 freearg(result);
2292 runcode(args[--numargs].code, result);
2293 freearg(args[numargs]);
2294 forcearg(result, op&CODE_RET_MASK);
2295 REDOARGS
2296 continue;
2297 }
2298 // fall-through
2299
2300 case CODE_DO|RET_NULL: case CODE_DO|RET_STR: case CODE_DO|RET_INT: case CODE_DO|RET_FLOAT:
2301 freearg(result);
2302 runcode(args[--numargs].code, result);
2303 freearg(args[numargs]);
2304 forcearg(result, op&CODE_RET_MASK);
2305 continue;
2306
2307 case CODE_JUMP:
2308 {
2309 uint len = op>>8;
2310 code += len;
2311 continue;
2312 }
2313 case CODE_JUMP_TRUE:
2314 {
2315 uint len = op>>8;
2316 if(getbool(args[--numargs])) code += len;
2317 freearg(args[numargs]);
2318 continue;
2319 }
2320 case CODE_JUMP_FALSE:
2321 {
2322 uint len = op>>8;
2323 if(!getbool(args[--numargs])) code += len;
2324 freearg(args[numargs]);
2325 continue;
2326 }
2327 case CODE_JUMP_RESULT_TRUE:
2328 {
2329 uint len = op>>8;
2330 freearg(result);
2331 --numargs;
2332 if(args[numargs].type == VAL_CODE) { runcode(args[numargs].code, result); freearg(args[numargs]); }
2333 else result = args[numargs];
2334 if(getbool(result)) code += len;
2335 continue;
2336 }
2337 case CODE_JUMP_RESULT_FALSE:
2338 {
2339 uint len = op>>8;
2340 freearg(result);
2341 --numargs;
2342 if(args[numargs].type == VAL_CODE) { runcode(args[numargs].code, result); freearg(args[numargs]); }
2343 else result = args[numargs];
2344 if(!getbool(result)) code += len;
2345 continue;
2346 }
2347
2348 case CODE_MACRO:
2349 {
2350 uint len = op>>8;
2351 args[numargs++].setmacro(code);
2352 code += len/sizeof(uint) + 1;
2353 continue;
2354 }
2355
2356 case CODE_VAL|RET_STR:
2357 {
2358 uint len = op>>8;
2359 args[numargs++].setstr(newstring((const char *)code, len));
2360 code += len/sizeof(uint) + 1;
2361 continue;
2362 }
2363 case CODE_VALI|RET_STR:
2364 {
2365 char s[4] = { char((op>>8)&0xFF), char((op>>16)&0xFF), char((op>>24)&0xFF), '\0' };
2366 args[numargs++].setstr(newstring(s));
2367 continue;
2368 }
2369 case CODE_VAL|RET_NULL:
2370 case CODE_VALI|RET_NULL: args[numargs++].setnull(); continue;
2371 case CODE_VAL|RET_INT: args[numargs++].setint(int(*code++)); continue;
2372 case CODE_VALI|RET_INT: args[numargs++].setint(int(op)>>8); continue;
2373 case CODE_VAL|RET_FLOAT: args[numargs++].setfloat(*(const float *)code++); continue;
2374 case CODE_VALI|RET_FLOAT: args[numargs++].setfloat(float(int(op)>>8)); continue;
2375
2376 case CODE_DUP|RET_NULL: args[numargs-1].getval(args[numargs]); numargs++; continue;
2377 case CODE_DUP|RET_INT: args[numargs].setint(args[numargs-1].getint()); numargs++; continue;
2378 case CODE_DUP|RET_FLOAT: args[numargs].setfloat(args[numargs-1].getfloat()); numargs++; continue;
2379 case CODE_DUP|RET_STR: args[numargs].setstr(newstring(args[numargs-1].getstr())); numargs++; continue;
2380
2381 case CODE_FORCE|RET_STR: forcestr(args[numargs-1]); continue;
2382 case CODE_FORCE|RET_INT: forceint(args[numargs-1]); continue;
2383 case CODE_FORCE|RET_FLOAT: forcefloat(args[numargs-1]); continue;
2384
2385 case CODE_RESULT|RET_NULL:
2386 freearg(result);
2387 result = args[--numargs];
2388 continue;
2389 case CODE_RESULT|RET_STR: case CODE_RESULT|RET_INT: case CODE_RESULT|RET_FLOAT:
2390 freearg(result);
2391 result = args[--numargs];
2392 forcearg(result, op&CODE_RET_MASK);
2393 continue;
2394
2395 case CODE_EMPTY|RET_NULL: args[numargs++].setcode(emptyblock[VAL_NULL]+1); break;
2396 case CODE_EMPTY|RET_STR: args[numargs++].setcode(emptyblock[VAL_STR]+1); break;
2397 case CODE_EMPTY|RET_INT: args[numargs++].setcode(emptyblock[VAL_INT]+1); break;
2398 case CODE_EMPTY|RET_FLOAT: args[numargs++].setcode(emptyblock[VAL_FLOAT]+1); break;
2399 case CODE_BLOCK:
2400 {
2401 uint len = op>>8;
2402 args[numargs++].setcode(code+1);
2403 code += len;
2404 continue;
2405 }
2406 case CODE_COMPILE:
2407 {
2408 tagval &arg = args[numargs-1];
2409 vector<uint> buf;
2410 switch(arg.type)
2411 {
2412 case VAL_INT: buf.reserve(8); buf.add(CODE_START); compileint(buf, arg.i); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2413 case VAL_FLOAT: buf.reserve(8); buf.add(CODE_START); compilefloat(buf, arg.f); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2414 case VAL_STR: case VAL_MACRO: case VAL_CSTR: buf.reserve(64); compilemain(buf, arg.s); freearg(arg); break;
2415 default: buf.reserve(8); buf.add(CODE_START); compilenull(buf); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2416 }
2417 arg.setcode(buf.disown()+1);
2418 continue;
2419 }
2420 case CODE_COND:
2421 {
2422 tagval &arg = args[numargs-1];
2423 switch(arg.type)
2424 {
2425 case VAL_STR: case VAL_MACRO: case VAL_CSTR:
2426 if(arg.s[0])
2427 {
2428 vector<uint> buf;
2429 buf.reserve(64);
2430 compilemain(buf, arg.s);
2431 freearg(arg);
2432 arg.setcode(buf.disown()+1);
2433 }
2434 else forcenull(arg);
2435 break;
2436 }
2437 continue;
2438 }
2439
2440 case CODE_IDENT:
2441 args[numargs++].setident(identmap[op>>8]);
2442 continue;
2443 case CODE_IDENTARG:
2444 {
2445 ident *id = identmap[op>>8];
2446 if(!(aliasstack->usedargs&(1<<id->index)))
2447 {
2448 pusharg(*id, nullval, aliasstack->argstack[id->index]);
2449 aliasstack->usedargs |= 1<<id->index;
2450 }
2451 args[numargs++].setident(id);
2452 continue;
2453 }
2454 case CODE_IDENTU:
2455 {
2456 tagval &arg = args[numargs-1];
2457 ident *id = arg.type == VAL_STR || arg.type == VAL_MACRO || arg.type == VAL_CSTR ? newident(arg.s, IDF_UNKNOWN) : dummyident;
2458 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
2459 {
2460 pusharg(*id, nullval, aliasstack->argstack[id->index]);
2461 aliasstack->usedargs |= 1<<id->index;
2462 }
2463 freearg(arg);
2464 arg.setident(id);
2465 continue;
2466 }
2467
2468 case CODE_LOOKUPU|RET_STR:
2469 #define LOOKUPU(aval, sval, ival, fval, nval) { \
2470 tagval &arg = args[numargs-1]; \
2471 if(arg.type != VAL_STR && arg.type != VAL_MACRO && arg.type != VAL_CSTR) continue; \
2472 ident *id = idents.access(arg.s); \
2473 if(id) switch(id->type) \
2474 { \
2475 case ID_ALIAS: \
2476 if(id->flags&IDF_UNKNOWN) break; \
2477 freearg(arg); \
2478 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
2479 aval; \
2480 continue; \
2481 case ID_SVAR: freearg(arg); sval; continue; \
2482 case ID_VAR: freearg(arg); ival; continue; \
2483 case ID_FVAR: freearg(arg); fval; continue; \
2484 case ID_COMMAND: \
2485 { \
2486 freearg(arg); \
2487 arg.setnull(); \
2488 commandret = &arg; \
2489 tagval buf[MAXARGS]; \
2490 callcommand(id, buf, 0, true); \
2491 forcearg(arg, op&CODE_RET_MASK); \
2492 commandret = &result; \
2493 continue; \
2494 } \
2495 default: freearg(arg); nval; continue; \
2496 } \
2497 debugcode("unknown alias lookup: %s", arg.s); \
2498 freearg(arg); \
2499 nval; \
2500 continue; \
2501 }
2502 LOOKUPU(arg.setstr(newstring(id->getstr())),
2503 arg.setstr(newstring(*id->storage.s)),
2504 arg.setstr(newstring(intstr(*id->storage.i))),
2505 arg.setstr(newstring(floatstr(*id->storage.f))),
2506 arg.setstr(newstring("")));
2507 case CODE_LOOKUP|RET_STR:
2508 #define LOOKUP(aval) { \
2509 ident *id = identmap[op>>8]; \
2510 if(id->flags&IDF_UNKNOWN) debugcode("unknown alias lookup: %s", id->name); \
2511 aval; \
2512 continue; \
2513 }
2514 LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
2515 case CODE_LOOKUPARG|RET_STR:
2516 #define LOOKUPARG(aval, nval) { \
2517 ident *id = identmap[op>>8]; \
2518 if(!(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
2519 aval; \
2520 continue; \
2521 }
2522 LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
2523 case CODE_LOOKUPU|RET_INT:
2524 LOOKUPU(arg.setint(id->getint()),
2525 arg.setint(parseint(*id->storage.s)),
2526 arg.setint(*id->storage.i),
2527 arg.setint(int(*id->storage.f)),
2528 arg.setint(0));
2529 case CODE_LOOKUP|RET_INT:
2530 LOOKUP(args[numargs++].setint(id->getint()));
2531 case CODE_LOOKUPARG|RET_INT:
2532 LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
2533 case CODE_LOOKUPU|RET_FLOAT:
2534 LOOKUPU(arg.setfloat(id->getfloat()),
2535 arg.setfloat(parsefloat(*id->storage.s)),
2536 arg.setfloat(float(*id->storage.i)),
2537 arg.setfloat(*id->storage.f),
2538 arg.setfloat(0.0f));
2539 case CODE_LOOKUP|RET_FLOAT:
2540 LOOKUP(args[numargs++].setfloat(id->getfloat()));
2541 case CODE_LOOKUPARG|RET_FLOAT:
2542 LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
2543 case CODE_LOOKUPU|RET_NULL:
2544 LOOKUPU(id->getval(arg),
2545 arg.setstr(newstring(*id->storage.s)),
2546 arg.setint(*id->storage.i),
2547 arg.setfloat(*id->storage.f),
2548 arg.setnull());
2549 case CODE_LOOKUP|RET_NULL:
2550 LOOKUP(id->getval(args[numargs++]));
2551 case CODE_LOOKUPARG|RET_NULL:
2552 LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
2553
2554 case CODE_LOOKUPMU|RET_STR:
2555 LOOKUPU(id->getcstr(arg),
2556 arg.setcstr(*id->storage.s),
2557 arg.setstr(newstring(intstr(*id->storage.i))),
2558 arg.setstr(newstring(floatstr(*id->storage.f))),
2559 arg.setcstr(""));
2560 case CODE_LOOKUPM|RET_STR:
2561 LOOKUP(id->getcstr(args[numargs++]));
2562 case CODE_LOOKUPMARG|RET_STR:
2563 LOOKUPARG(id->getcstr(args[numargs++]), args[numargs++].setcstr(""));
2564 case CODE_LOOKUPMU|RET_NULL:
2565 LOOKUPU(id->getcval(arg),
2566 arg.setcstr(*id->storage.s),
2567 arg.setint(*id->storage.i),
2568 arg.setfloat(*id->storage.f),
2569 arg.setnull());
2570 case CODE_LOOKUPM|RET_NULL:
2571 LOOKUP(id->getcval(args[numargs++]));
2572 case CODE_LOOKUPMARG|RET_NULL:
2573 LOOKUPARG(id->getcval(args[numargs++]), args[numargs++].setnull());
2574
2575 case CODE_SVAR|RET_STR: case CODE_SVAR|RET_NULL: args[numargs++].setstr(newstring(*identmap[op>>8]->storage.s)); continue;
2576 case CODE_SVAR|RET_INT: args[numargs++].setint(parseint(*identmap[op>>8]->storage.s)); continue;
2577 case CODE_SVAR|RET_FLOAT: args[numargs++].setfloat(parsefloat(*identmap[op>>8]->storage.s)); continue;
2578 case CODE_SVARM: args[numargs++].setcstr(*identmap[op>>8]->storage.s); continue;
2579 case CODE_SVAR1: setsvarchecked(identmap[op>>8], args[--numargs].s); freearg(args[numargs]); continue;
2580
2581 case CODE_IVAR|RET_INT: case CODE_IVAR|RET_NULL: args[numargs++].setint(*identmap[op>>8]->storage.i); continue;
2582 case CODE_IVAR|RET_STR: args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->storage.i))); continue;
2583 case CODE_IVAR|RET_FLOAT: args[numargs++].setfloat(float(*identmap[op>>8]->storage.i)); continue;
2584 case CODE_IVAR1: setvarchecked(identmap[op>>8], args[--numargs].i); continue;
2585 case CODE_IVAR2: numargs -= 2; setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)); continue;
2586 case CODE_IVAR3: numargs -= 3; setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)|args[numargs+2].i); continue;
2587
2588 case CODE_FVAR|RET_FLOAT: case CODE_FVAR|RET_NULL: args[numargs++].setfloat(*identmap[op>>8]->storage.f); continue;
2589 case CODE_FVAR|RET_STR: args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->storage.f))); continue;
2590 case CODE_FVAR|RET_INT: args[numargs++].setint(int(*identmap[op>>8]->storage.f)); continue;
2591 case CODE_FVAR1: setfvarchecked(identmap[op>>8], args[--numargs].f); continue;
2592
2593 #define OFFSETARG(n) offset+n
2594 case CODE_COM|RET_NULL: case CODE_COM|RET_STR: case CODE_COM|RET_FLOAT: case CODE_COM|RET_INT:
2595 {
2596 ident *id = identmap[op>>8];
2597 int offset = numargs-id->numargs;
2598 forcenull(result);
2599 CALLCOM(id->numargs)
2600 forcearg(result, op&CODE_RET_MASK);
2601 freeargs(args, numargs, offset);
2602 continue;
2603 }
2604 #ifndef STANDALONE
2605 case CODE_COMD|RET_NULL: case CODE_COMD|RET_STR: case CODE_COMD|RET_FLOAT: case CODE_COMD|RET_INT:
2606 {
2607 ident *id = identmap[op>>8];
2608 int offset = numargs-(id->numargs-1);
2609 addreleaseaction(id, &args[offset], id->numargs-1);
2610 CALLCOM(id->numargs)
2611 forcearg(result, op&CODE_RET_MASK);
2612 freeargs(args, numargs, offset);
2613 continue;
2614 }
2615 #endif
2616 #undef OFFSETARG
2617
2618 case CODE_COMV|RET_NULL: case CODE_COMV|RET_STR: case CODE_COMV|RET_FLOAT: case CODE_COMV|RET_INT:
2619 {
2620 ident *id = identmap[op>>13];
2621 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
2622 forcenull(result);
2623 ((comfunv)id->fun)(&args[offset], callargs);
2624 forcearg(result, op&CODE_RET_MASK);
2625 freeargs(args, numargs, offset);
2626 continue;
2627 }
2628 case CODE_COMC|RET_NULL: case CODE_COMC|RET_STR: case CODE_COMC|RET_FLOAT: case CODE_COMC|RET_INT:
2629 {
2630 ident *id = identmap[op>>13];
2631 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
2632 forcenull(result);
2633 {
2634 vector<char> buf;
2635 buf.reserve(MAXSTRLEN);
2636 ((comfun1)id->fun)(conc(buf, &args[offset], callargs, true));
2637 }
2638 forcearg(result, op&CODE_RET_MASK);
2639 freeargs(args, numargs, offset);
2640 continue;
2641 }
2642
2643 case CODE_CONC|RET_NULL: case CODE_CONC|RET_STR: case CODE_CONC|RET_FLOAT: case CODE_CONC|RET_INT:
2644 case CODE_CONCW|RET_NULL: case CODE_CONCW|RET_STR: case CODE_CONCW|RET_FLOAT: case CODE_CONCW|RET_INT:
2645 {
2646 int numconc = op>>8;
2647 char *s = conc(&args[numargs-numconc], numconc, (op&CODE_OP_MASK)==CODE_CONC);
2648 freeargs(args, numargs, numargs-numconc);
2649 args[numargs].setstr(s);
2650 forcearg(args[numargs], op&CODE_RET_MASK);
2651 numargs++;
2652 continue;
2653 }
2654
2655 case CODE_CONCM|RET_NULL: case CODE_CONCM|RET_STR: case CODE_CONCM|RET_FLOAT: case CODE_CONCM|RET_INT:
2656 {
2657 int numconc = op>>8;
2658 char *s = conc(&args[numargs-numconc], numconc, false);
2659 freeargs(args, numargs, numargs-numconc);
2660 result.setstr(s);
2661 forcearg(result, op&CODE_RET_MASK);
2662 continue;
2663 }
2664
2665 case CODE_ALIAS:
2666 setalias(*identmap[op>>8], args[--numargs]);
2667 continue;
2668 case CODE_ALIASARG:
2669 setarg(*identmap[op>>8], args[--numargs]);
2670 continue;
2671 case CODE_ALIASU:
2672 numargs -= 2;
2673 setalias(args[numargs].getstr(), args[numargs+1]);
2674 freearg(args[numargs]);
2675 continue;
2676
2677 #define SKIPARGS(offset) offset
2678 case CODE_CALL|RET_NULL: case CODE_CALL|RET_STR: case CODE_CALL|RET_FLOAT: case CODE_CALL|RET_INT:
2679 {
2680 #define FORCERESULT { \
2681 freeargs(args, numargs, SKIPARGS(offset)); \
2682 forcearg(result, op&CODE_RET_MASK); \
2683 continue; \
2684 }
2685 #define CALLALIAS { \
2686 identstack argstack[MAXARGS]; \
2687 for(int i = 0; i < callargs; i++) \
2688 pusharg(*identmap[i], args[offset + i], argstack[i]); \
2689 int oldargs = _numargs; \
2690 _numargs = callargs; \
2691 int oldflags = identflags; \
2692 identflags |= id->flags&IDF_OVERRIDDEN; \
2693 identlink aliaslink = { id, aliasstack, (1<<callargs)-1, argstack }; \
2694 aliasstack = &aliaslink; \
2695 if(!id->code) id->code = compilecode(id->getstr()); \
2696 uint *code = id->code; \
2697 code[0] += 0x100; \
2698 runcode(code+1, result); \
2699 code[0] -= 0x100; \
2700 if(int(code[0]) < 0x100) delete[] code; \
2701 aliasstack = aliaslink.next; \
2702 identflags = oldflags; \
2703 for(int i = 0; i < callargs; i++) \
2704 poparg(*identmap[i]); \
2705 for(int argmask = aliaslink.usedargs&(~0<<callargs), i = callargs; argmask; i++) \
2706 if(argmask&(1<<i)) { poparg(*identmap[i]); argmask &= ~(1<<i); } \
2707 forcearg(result, op&CODE_RET_MASK); \
2708 _numargs = oldargs; \
2709 numargs = SKIPARGS(offset); \
2710 }
2711 forcenull(result);
2712 ident *id = identmap[op>>13];
2713 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
2714 if(id->flags&IDF_UNKNOWN)
2715 {
2716 debugcode("unknown command: %s", id->name);
2717 FORCERESULT;
2718 }
2719 CALLALIAS;
2720 continue;
2721 }
2722 case CODE_CALLARG|RET_NULL: case CODE_CALLARG|RET_STR: case CODE_CALLARG|RET_FLOAT: case CODE_CALLARG|RET_INT:
2723 {
2724 forcenull(result);
2725 ident *id = identmap[op>>13];
2726 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
2727 if(!(aliasstack->usedargs&(1<<id->index))) FORCERESULT;
2728 CALLALIAS;
2729 continue;
2730 }
2731 #undef SKIPARGS
2732
2733 #define SKIPARGS(offset) offset-1
2734 case CODE_CALLU|RET_NULL: case CODE_CALLU|RET_STR: case CODE_CALLU|RET_FLOAT: case CODE_CALLU|RET_INT:
2735 {
2736 int callargs = op>>8, offset = numargs-callargs;
2737 tagval &idarg = args[offset-1];
2738 if(idarg.type != VAL_STR && idarg.type != VAL_MACRO && idarg.type != VAL_CSTR)
2739 {
2740 litval:
2741 freearg(result);
2742 result = idarg;
2743 forcearg(result, op&CODE_RET_MASK);
2744 while(--numargs >= offset) freearg(args[numargs]);
2745 continue;
2746 }
2747 ident *id = idents.access(idarg.s);
2748 if(!id)
2749 {
2750 noid:
2751 if(checknumber(idarg.s)) goto litval;
2752 debugcode("unknown command: %s", idarg.s);
2753 forcenull(result);
2754 FORCERESULT;
2755 }
2756 forcenull(result);
2757 switch(id->type)
2758 {
2759 default:
2760 if(!id->fun) FORCERESULT;
2761 // fall-through
2762 case ID_COMMAND:
2763 freearg(idarg);
2764 callcommand(id, &args[offset], callargs);
2765 forcearg(result, op&CODE_RET_MASK);
2766 numargs = offset - 1;
2767 continue;
2768 case ID_LOCAL:
2769 {
2770 identstack locals[MAXARGS];
2771 freearg(idarg);
2772 loopj(callargs) pushalias(*forceident(args[offset+j]), locals[j]);
2773 code = runcode(code, result);
2774 loopj(callargs) popalias(*args[offset+j].id);
2775 goto exit;
2776 }
2777 case ID_VAR:
2778 if(callargs <= 0) printvar(id); else setvarchecked(id, &args[offset], callargs);
2779 FORCERESULT;
2780 case ID_FVAR:
2781 if(callargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[offset]));
2782 FORCERESULT;
2783 case ID_SVAR:
2784 if(callargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[offset]));
2785 FORCERESULT;
2786 case ID_ALIAS:
2787 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) FORCERESULT;
2788 if(id->valtype==VAL_NULL) goto noid;
2789 freearg(idarg);
2790 CALLALIAS;
2791 continue;
2792 }
2793 }
2794 #undef SKIPARGS
2795 }
2796 }
2797 exit:
2798 commandret = prevret;
2799 --rundepth;
2800 return code;
2801 }
2802
executeret(const uint * code,tagval & result)2803 void executeret(const uint *code, tagval &result)
2804 {
2805 runcode(code, result);
2806 }
2807
executeret(const char * p,tagval & result)2808 void executeret(const char *p, tagval &result)
2809 {
2810 vector<uint> code;
2811 code.reserve(64);
2812 compilemain(code, p, VAL_ANY);
2813 runcode(code.getbuf()+1, result);
2814 if(int(code[0]) >= 0x100) code.disown();
2815 }
2816
executeret(ident * id,tagval * args,int numargs,bool lookup,tagval & result)2817 void executeret(ident *id, tagval *args, int numargs, bool lookup, tagval &result)
2818 {
2819 result.setnull();
2820 ++rundepth;
2821 tagval *prevret = commandret;
2822 commandret = &result;
2823 if(rundepth > MAXRUNDEPTH) debugcode("exceeded recursion limit");
2824 else if(id) switch(id->type)
2825 {
2826 default:
2827 if(!id->fun) break;
2828 // fall-through
2829 case ID_COMMAND:
2830 if(numargs < id->numargs)
2831 {
2832 tagval buf[MAXARGS];
2833 memcpy(buf, args, numargs*sizeof(tagval));
2834 callcommand(id, buf, numargs, lookup);
2835 }
2836 else callcommand(id, args, numargs, lookup);
2837 numargs = 0;
2838 break;
2839 case ID_VAR:
2840 if(numargs <= 0) printvar(id); else setvarchecked(id, args, numargs);
2841 break;
2842 case ID_FVAR:
2843 if(numargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[0]));
2844 break;
2845 case ID_SVAR:
2846 if(numargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[0]));
2847 break;
2848 case ID_ALIAS:
2849 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) break;
2850 if(id->valtype==VAL_NULL) break;
2851 #define callargs numargs
2852 #define offset 0
2853 #define op RET_NULL
2854 #define SKIPARGS(offset) offset
2855 CALLALIAS;
2856 #undef callargs
2857 #undef offset
2858 #undef op
2859 #undef SKIPARGS
2860 break;
2861 }
2862 freeargs(args, numargs, 0);
2863 commandret = prevret;
2864 --rundepth;
2865 }
2866
executestr(const uint * code)2867 char *executestr(const uint *code)
2868 {
2869 tagval result;
2870 runcode(code, result);
2871 if(result.type == VAL_NULL) return NULL;
2872 forcestr(result);
2873 return result.s;
2874 }
2875
executestr(const char * p)2876 char *executestr(const char *p)
2877 {
2878 tagval result;
2879 executeret(p, result);
2880 if(result.type == VAL_NULL) return NULL;
2881 forcestr(result);
2882 return result.s;
2883 }
2884
executestr(ident * id,tagval * args,int numargs,bool lookup)2885 char *executestr(ident *id, tagval *args, int numargs, bool lookup)
2886 {
2887 tagval result;
2888 executeret(id, args, numargs, lookup, result);
2889 if(result.type == VAL_NULL) return NULL;
2890 forcestr(result);
2891 return result.s;
2892 }
2893
execidentstr(const char * name,bool lookup)2894 char *execidentstr(const char *name, bool lookup)
2895 {
2896 ident *id = idents.access(name);
2897 return id ? executestr(id, NULL, 0, lookup) : NULL;
2898 }
2899
execute(const uint * code)2900 int execute(const uint *code)
2901 {
2902 tagval result;
2903 runcode(code, result);
2904 int i = result.getint();
2905 freearg(result);
2906 return i;
2907 }
2908
execute(const char * p)2909 int execute(const char *p)
2910 {
2911 vector<uint> code;
2912 code.reserve(64);
2913 compilemain(code, p, VAL_INT);
2914 tagval result;
2915 runcode(code.getbuf()+1, result);
2916 if(int(code[0]) >= 0x100) code.disown();
2917 int i = result.getint();
2918 freearg(result);
2919 return i;
2920 }
2921
execute(ident * id,tagval * args,int numargs,bool lookup)2922 int execute(ident *id, tagval *args, int numargs, bool lookup)
2923 {
2924 tagval result;
2925 executeret(id, args, numargs, lookup, result);
2926 int i = result.getint();
2927 freearg(result);
2928 return i;
2929 }
2930
execident(const char * name,int noid,bool lookup)2931 int execident(const char *name, int noid, bool lookup)
2932 {
2933 ident *id = idents.access(name);
2934 return id ? execute(id, NULL, 0, lookup) : noid;
2935 }
2936
executefloat(const uint * code)2937 float executefloat(const uint *code)
2938 {
2939 tagval result;
2940 runcode(code, result);
2941 float f = result.getfloat();
2942 freearg(result);
2943 return f;
2944 }
2945
executefloat(const char * p)2946 float executefloat(const char *p)
2947 {
2948 tagval result;
2949 executeret(p, result);
2950 float f = result.getfloat();
2951 freearg(result);
2952 return f;
2953 }
2954
executefloat(ident * id,tagval * args,int numargs,bool lookup)2955 float executefloat(ident *id, tagval *args, int numargs, bool lookup)
2956 {
2957 tagval result;
2958 executeret(id, args, numargs, lookup, result);
2959 float f = result.getfloat();
2960 freearg(result);
2961 return f;
2962 }
2963
execidentfloat(const char * name,float noid,bool lookup)2964 float execidentfloat(const char *name, float noid, bool lookup)
2965 {
2966 ident *id = idents.access(name);
2967 return id ? executefloat(id, NULL, 0, lookup) : noid;
2968 }
2969
executebool(const uint * code)2970 bool executebool(const uint *code)
2971 {
2972 tagval result;
2973 runcode(code, result);
2974 bool b = getbool(result);
2975 freearg(result);
2976 return b;
2977 }
2978
executebool(const char * p)2979 bool executebool(const char *p)
2980 {
2981 tagval result;
2982 executeret(p, result);
2983 bool b = getbool(result);
2984 freearg(result);
2985 return b;
2986 }
2987
executebool(ident * id,tagval * args,int numargs,bool lookup)2988 bool executebool(ident *id, tagval *args, int numargs, bool lookup)
2989 {
2990 tagval result;
2991 executeret(id, args, numargs, lookup, result);
2992 bool b = getbool(result);
2993 freearg(result);
2994 return b;
2995 }
2996
execidentbool(const char * name,bool noid,bool lookup)2997 bool execidentbool(const char *name, bool noid, bool lookup)
2998 {
2999 ident *id = idents.access(name);
3000 return id ? executebool(id, NULL, 0, lookup) : noid;
3001 }
3002
execfile(const char * cfgfile,bool msg)3003 bool execfile(const char *cfgfile, bool msg)
3004 {
3005 string s;
3006 copystring(s, cfgfile);
3007 char *buf = loadfile(path(s), NULL);
3008 if(!buf)
3009 {
3010 if(msg) conoutf(CON_ERROR, "could not read \"%s\"", cfgfile);
3011 return false;
3012 }
3013 const char *oldsourcefile = sourcefile, *oldsourcestr = sourcestr;
3014 sourcefile = cfgfile;
3015 sourcestr = buf;
3016 execute(buf);
3017 sourcefile = oldsourcefile;
3018 sourcestr = oldsourcestr;
3019 delete[] buf;
3020 return true;
3021 }
3022 ICOMMAND(exec, "sb", (char *file, int *msg), intret(execfile(file, *msg != 0) ? 1 : 0));
3023
escapestring(const char * s)3024 const char *escapestring(const char *s)
3025 {
3026 stridx = (stridx + 1)%4;
3027 vector<char> &buf = strbuf[stridx];
3028 buf.setsize(0);
3029 buf.add('"');
3030 for(; *s; s++) switch(*s)
3031 {
3032 case '\n': buf.put("^n", 2); break;
3033 case '\t': buf.put("^t", 2); break;
3034 case '\f': buf.put("^f", 2); break;
3035 case '"': buf.put("^\"", 2); break;
3036 case '^': buf.put("^^", 2); break;
3037 default: buf.add(*s); break;
3038 }
3039 buf.put("\"\0", 2);
3040 return buf.getbuf();
3041 }
3042
3043 ICOMMAND(escape, "s", (char *s), result(escapestring(s)));
3044 ICOMMAND(unescape, "s", (char *s),
3045 {
3046 int len = strlen(s);
3047 char *d = newstring(len);
3048 unescapestring(d, s, &s[len]);
3049 stringret(d);
3050 });
3051
escapeid(const char * s)3052 const char *escapeid(const char *s)
3053 {
3054 const char *end = s + strcspn(s, "\"/;()[]@ \f\t\r\n\0");
3055 return *end ? escapestring(s) : s;
3056 }
3057
validateblock(const char * s)3058 bool validateblock(const char *s)
3059 {
3060 const int maxbrak = 100;
3061 static char brakstack[maxbrak];
3062 int brakdepth = 0;
3063 for(; *s; s++) switch(*s)
3064 {
3065 case '[': case '(': if(brakdepth >= maxbrak) return false; brakstack[brakdepth++] = *s; break;
3066 case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return false; break;
3067 case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return false; break;
3068 case '"': s = parsestring(s + 1); if(*s != '"') return false; break;
3069 case '/': if(s[1] == '/') return false; break;
3070 case '@': case '\f': return false;
3071 }
3072 return brakdepth == 0;
3073 }
3074
3075 #ifndef STANDALONE
writecfg(const char * name)3076 void writecfg(const char *name)
3077 {
3078 stream *f = openutf8file(path(name && name[0] ? name : game::savedconfig(), true), "w");
3079 if(!f) return;
3080 f->printf("// automatically written on exit, DO NOT MODIFY\n// delete this file to have %s overwrite these settings\n// modify settings in game, or put settings in %s to override anything\n\n", game::defaultconfig(), game::autoexec());
3081 game::writeclientinfo(f);
3082 f->printf("\n");
3083 writecrosshairs(f);
3084 vector<ident *> ids;
3085 enumerate(idents, ident, id, ids.add(&id));
3086 ids.sortname();
3087 loopv(ids)
3088 {
3089 ident &id = *ids[i];
3090 if(id.flags&IDF_PERSIST) switch(id.type)
3091 {
3092 case ID_VAR: f->printf("%s %d\n", escapeid(id), *id.storage.i); break;
3093 case ID_FVAR: f->printf("%s %s\n", escapeid(id), floatstr(*id.storage.f)); break;
3094 case ID_SVAR: f->printf("%s %s\n", escapeid(id), escapestring(*id.storage.s)); break;
3095 }
3096 }
3097 f->printf("\n");
3098 writebinds(f);
3099 f->printf("\n");
3100 loopv(ids)
3101 {
3102 ident &id = *ids[i];
3103 if(id.type==ID_ALIAS && id.flags&IDF_PERSIST && !(id.flags&IDF_OVERRIDDEN)) switch(id.valtype)
3104 {
3105 case VAL_STR:
3106 if(!id.val.s[0]) break;
3107 if(!validateblock(id.val.s)) { f->printf("%s = %s\n", escapeid(id), escapestring(id.val.s)); break; }
3108 case VAL_FLOAT:
3109 case VAL_INT:
3110 f->printf("%s = [%s]\n", escapeid(id), id.getstr()); break;
3111 }
3112 }
3113 f->printf("\n");
3114 writecompletions(f);
3115 delete f;
3116 }
3117
3118 COMMAND(writecfg, "s");
3119 #endif
3120
3121 // below the commands that implement a small imperative language. thanks to the semantics of
3122 // () and [] expressions, any control construct can be defined trivially.
3123
3124 static string retbuf[4];
3125 static int retidx = 0;
3126
intstr(int v)3127 const char *intstr(int v)
3128 {
3129 retidx = (retidx + 1)%4;
3130 intformat(retbuf[retidx], v);
3131 return retbuf[retidx];
3132 }
3133
intret(int v)3134 void intret(int v)
3135 {
3136 commandret->setint(v);
3137 }
3138
floatstr(float v)3139 const char *floatstr(float v)
3140 {
3141 retidx = (retidx + 1)%4;
3142 floatformat(retbuf[retidx], v);
3143 return retbuf[retidx];
3144 }
3145
floatret(float v)3146 void floatret(float v)
3147 {
3148 commandret->setfloat(v);
3149 }
3150
numberstr(double v)3151 const char *numberstr(double v)
3152 {
3153 retidx = (retidx + 1)%4;
3154 numberformat(retbuf[retidx], v);
3155 return retbuf[retidx];
3156 }
3157
numberret(double v)3158 void numberret(double v)
3159 {
3160 int i = int(v);
3161 if(v == i) commandret->setint(i);
3162 else commandret->setfloat(v);
3163 }
3164
3165 #undef ICOMMANDNAME
3166 #define ICOMMANDNAME(name) _stdcmd
3167 #undef ICOMMANDSNAME
3168 #define ICOMMANDSNAME _stdcmd
3169
3170 ICOMMANDK(do, ID_DO, "e", (uint *body), executeret(body, *commandret));
3171
doargs(uint * body)3172 static void doargs(uint *body)
3173 {
3174 if(aliasstack != &noalias)
3175 {
3176 UNDOARGS
3177 executeret(body, *commandret);
3178 REDOARGS
3179 }
3180 else executeret(body, *commandret);
3181 }
3182 COMMANDK(doargs, ID_DOARGS, "e");
3183
3184 ICOMMANDK(if, ID_IF, "tee", (tagval *cond, uint *t, uint *f), executeret(getbool(*cond) ? t : f, *commandret));
3185 ICOMMAND(?, "tTT", (tagval *cond, tagval *t, tagval *f), result(*(getbool(*cond) ? t : f)));
3186
3187 ICOMMAND(pushif, "rTe", (ident *id, tagval *v, uint *code),
3188 {
3189 if(id->type != ID_ALIAS || id->index < MAXARGS) return;
3190 if(getbool(*v))
3191 {
3192 identstack stack;
3193 pusharg(*id, *v, stack);
3194 v->type = VAL_NULL;
3195 id->flags &= ~IDF_UNKNOWN;
3196 executeret(code, *commandret);
3197 poparg(*id);
3198 }
3199 });
3200
loopiter(ident * id,identstack & stack,const tagval & v)3201 void loopiter(ident *id, identstack &stack, const tagval &v)
3202 {
3203 if(id->stack != &stack)
3204 {
3205 pusharg(*id, v, stack);
3206 id->flags &= ~IDF_UNKNOWN;
3207 }
3208 else
3209 {
3210 if(id->valtype == VAL_STR) delete[] id->val.s;
3211 cleancode(*id);
3212 id->setval(v);
3213 }
3214 }
3215
loopend(ident * id,identstack & stack)3216 void loopend(ident *id, identstack &stack)
3217 {
3218 if(id->stack == &stack) poparg(*id);
3219 }
3220
setiter(ident & id,int i,identstack & stack)3221 static inline void setiter(ident &id, int i, identstack &stack)
3222 {
3223 if(i)
3224 {
3225 if(id.valtype != VAL_INT)
3226 {
3227 if(id.valtype == VAL_STR) delete[] id.val.s;
3228 cleancode(id);
3229 id.valtype = VAL_INT;
3230 }
3231 id.val.i = i;
3232 }
3233 else
3234 {
3235 tagval zero;
3236 zero.setint(0);
3237 pusharg(id, zero, stack);
3238 id.flags &= ~IDF_UNKNOWN;
3239 }
3240 }
3241
doloop(ident & id,int offset,int n,int step,uint * body)3242 static inline void doloop(ident &id, int offset, int n, int step, uint *body)
3243 {
3244 if(n <= 0 || id.type != ID_ALIAS) return;
3245 identstack stack;
3246 loopi(n)
3247 {
3248 setiter(id, offset + i*step, stack);
3249 execute(body);
3250 }
3251 poparg(id);
3252 }
3253 ICOMMAND(loop, "rie", (ident *id, int *n, uint *body), doloop(*id, 0, *n, 1, body));
3254 ICOMMAND(loop+, "riie", (ident *id, int *offset, int *n, uint *body), doloop(*id, *offset, *n, 1, body));
3255 ICOMMAND(loop*, "riie", (ident *id, int *step, int *n, uint *body), doloop(*id, 0, *n, *step, body));
3256 ICOMMAND(loop+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), doloop(*id, *offset, *n, *step, body));
3257
loopwhile(ident & id,int offset,int n,int step,uint * cond,uint * body)3258 static inline void loopwhile(ident &id, int offset, int n, int step, uint *cond, uint *body)
3259 {
3260 if(n <= 0 || id.type!=ID_ALIAS) return;
3261 identstack stack;
3262 loopi(n)
3263 {
3264 setiter(id, offset + i*step, stack);
3265 if(!executebool(cond)) break;
3266 execute(body);
3267 }
3268 poparg(id);
3269 }
3270 ICOMMAND(loopwhile, "riee", (ident *id, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, 1, cond, body));
3271 ICOMMAND(loopwhile+, "riiee", (ident *id, int *offset, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, 1, cond, body));
3272 ICOMMAND(loopwhile*, "riiee", (ident *id, int *step, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, *step, cond, body));
3273 ICOMMAND(loopwhile+*, "riiiee", (ident *id, int *offset, int *step, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, *step, cond, body));
3274
3275 ICOMMAND(while, "ee", (uint *cond, uint *body), while(executebool(cond)) execute(body));
3276
loopconc(ident & id,int offset,int n,int step,uint * body,bool space)3277 static inline void loopconc(ident &id, int offset, int n, int step, uint *body, bool space)
3278 {
3279 if(n <= 0 || id.type != ID_ALIAS) return;
3280 identstack stack;
3281 vector<char> s;
3282 loopi(n)
3283 {
3284 setiter(id, offset + i*step, stack);
3285 tagval v;
3286 executeret(body, v);
3287 const char *vstr = v.getstr();
3288 int len = strlen(vstr);
3289 if(space && i) s.add(' ');
3290 s.put(vstr, len);
3291 freearg(v);
3292 }
3293 if(n > 0) poparg(id);
3294 s.add('\0');
3295 commandret->setstr(s.disown());
3296 }
3297 ICOMMAND(loopconcat, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, body, true));
3298 ICOMMAND(loopconcat+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, body, true));
3299 ICOMMAND(loopconcat*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, body, true));
3300 ICOMMAND(loopconcat+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, body, true));
3301 ICOMMAND(loopconcatword, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, body, false));
3302 ICOMMAND(loopconcatword+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, body, false));
3303 ICOMMAND(loopconcatword*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, body, false));
3304 ICOMMAND(loopconcatword+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, body, false));
3305
concat(tagval * v,int n)3306 void concat(tagval *v, int n)
3307 {
3308 commandret->setstr(conc(v, n, true));
3309 }
3310 COMMAND(concat, "V");
3311
concatword(tagval * v,int n)3312 void concatword(tagval *v, int n)
3313 {
3314 commandret->setstr(conc(v, n, false));
3315 }
3316 COMMAND(concatword, "V");
3317
result(tagval & v)3318 void result(tagval &v)
3319 {
3320 *commandret = v;
3321 v.type = VAL_NULL;
3322 }
3323
stringret(char * s)3324 void stringret(char *s)
3325 {
3326 commandret->setstr(s);
3327 }
3328
result(const char * s)3329 void result(const char *s)
3330 {
3331 commandret->setstr(newstring(s));
3332 }
3333
3334 ICOMMANDK(result, ID_RESULT, "T", (tagval *v),
3335 {
3336 *commandret = *v;
3337 v->type = VAL_NULL;
3338 });
3339
format(tagval * args,int numargs)3340 void format(tagval *args, int numargs)
3341 {
3342 vector<char> s;
3343 const char *f = args[0].getstr();
3344 while(*f)
3345 {
3346 int c = *f++;
3347 if(c == '%')
3348 {
3349 int i = *f++;
3350 if(i >= '1' && i <= '9')
3351 {
3352 i -= '0';
3353 const char *sub = i < numargs ? args[i].getstr() : "";
3354 while(*sub) s.add(*sub++);
3355 }
3356 else s.add(i);
3357 }
3358 else s.add(c);
3359 }
3360 s.add('\0');
3361 commandret->setstr(s.disown());
3362 }
3363 COMMAND(format, "V");
3364
3365 static const char *liststart = NULL, *listend = NULL, *listquotestart = NULL, *listquoteend = NULL;
3366
skiplist(const char * & p)3367 static inline void skiplist(const char *&p)
3368 {
3369 for(;;)
3370 {
3371 p += strspn(p, " \t\r\n");
3372 if(p[0]!='/' || p[1]!='/') break;
3373 p += strcspn(p, "\n\0");
3374 }
3375 }
3376
parselist(const char * & s,const char * & start=liststart,const char * & end=listend,const char * & quotestart=listquotestart,const char * & quoteend=listquoteend)3377 static bool parselist(const char *&s, const char *&start = liststart, const char *&end = listend, const char *"estart = listquotestart, const char *"eend = listquoteend)
3378 {
3379 skiplist(s);
3380 switch(*s)
3381 {
3382 case '"': quotestart = s++; start = s; s = parsestring(s); end = s; if(*s == '"') s++; quoteend = s; break;
3383 case '(': case '[':
3384 quotestart = s;
3385 start = s+1;
3386 for(int braktype = *s++, brak = 1;;)
3387 {
3388 s += strcspn(s, "\"/;()[]\0");
3389 int c = *s++;
3390 switch(c)
3391 {
3392 case '\0': s--; quoteend = end = s; return true;
3393 case '"': s = parsestring(s); if(*s == '"') s++; break;
3394 case '/': if(*s == '/') s += strcspn(s, "\n\0"); break;
3395 case '(': case '[': if(c == braktype) brak++; break;
3396 case ')': if(braktype == '(' && --brak <= 0) goto endblock; break;
3397 case ']': if(braktype == '[' && --brak <= 0) goto endblock; break;
3398 }
3399 }
3400 endblock:
3401 end = s-1;
3402 quoteend = s;
3403 break;
3404 case '\0': case ')': case ']': return false;
3405 default: quotestart = start = s; s = parseword(s); quoteend = end = s; break;
3406 }
3407 skiplist(s);
3408 if(*s == ';') s++;
3409 return true;
3410 }
3411
listelem(const char * start=liststart,const char * end=listend,const char * quotestart=listquotestart)3412 static inline char *listelem(const char *start = liststart, const char *end = listend, const char *quotestart = listquotestart)
3413 {
3414 size_t len = end-start;
3415 char *s = newstring(len);
3416 if(*quotestart == '"') unescapestring(s, start, end);
3417 else copystring(s, start, len+1);
3418 return s;
3419 }
3420
explodelist(const char * s,vector<char * > & elems,int limit)3421 void explodelist(const char *s, vector<char *> &elems, int limit)
3422 {
3423 const char *start, *end, *qstart;
3424 while((limit < 0 || elems.length() < limit) && parselist(s, start, end, qstart))
3425 elems.add(listelem(start, end, qstart));
3426 }
3427
indexlist(const char * s,int pos)3428 char *indexlist(const char *s, int pos)
3429 {
3430 loopi(pos) if(!parselist(s)) return newstring("");
3431 const char *start, *end, *qstart;
3432 return parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring("");
3433 }
3434
listlen(const char * s)3435 int listlen(const char *s)
3436 {
3437 int n = 0;
3438 while(parselist(s)) n++;
3439 return n;
3440 }
3441 ICOMMAND(listlen, "s", (char *s), intret(listlen(s)));
3442
at(tagval * args,int numargs)3443 void at(tagval *args, int numargs)
3444 {
3445 if(!numargs) return;
3446 const char *start = args[0].getstr(), *end = start + strlen(start), *qstart = "";
3447 for(int i = 1; i < numargs; i++)
3448 {
3449 const char *list = start;
3450 int pos = args[i].getint();
3451 for(; pos > 0; pos--) if(!parselist(list)) break;
3452 if(pos > 0 || !parselist(list, start, end, qstart)) start = end = qstart = "";
3453 }
3454 commandret->setstr(listelem(start, end, qstart));
3455 }
3456 COMMAND(at, "si1V");
3457
substr(char * s,int * start,int * count,int * numargs)3458 void substr(char *s, int *start, int *count, int *numargs)
3459 {
3460 int len = strlen(s), offset = clamp(*start, 0, len);
3461 commandret->setstr(newstring(&s[offset], *numargs >= 3 ? clamp(*count, 0, len - offset) : len - offset));
3462 }
3463 COMMAND(substr, "siiN");
3464
sublist(const char * s,int * skip,int * count,int * numargs)3465 void sublist(const char *s, int *skip, int *count, int *numargs)
3466 {
3467 int offset = max(*skip, 0), len = *numargs >= 3 ? max(*count, 0) : -1;
3468 loopi(offset) if(!parselist(s)) break;
3469 if(len < 0) { if(offset > 0) skiplist(s); commandret->setstr(newstring(s)); return; }
3470 const char *list = s, *start, *end, *qstart, *qend = s;
3471 if(len > 0 && parselist(s, start, end, list, qend)) while(--len > 0 && parselist(s, start, end, qstart, qend));
3472 commandret->setstr(newstring(list, qend - list));
3473 }
3474 COMMAND(sublist, "siiN");
3475
3476 ICOMMAND(stripcolors, "s", (char *s),
3477 {
3478 int len = strlen(s);
3479 char *d = newstring(len);
3480 filtertext(d, s, true, len);
3481 stringret(d);
3482 });
3483
setiter(ident & id,char * val,identstack & stack)3484 static inline void setiter(ident &id, char *val, identstack &stack)
3485 {
3486 if(id.stack == &stack)
3487 {
3488 if(id.valtype == VAL_STR) delete[] id.val.s;
3489 else id.valtype = VAL_STR;
3490 cleancode(id);
3491 id.val.s = val;
3492 }
3493 else
3494 {
3495 tagval t;
3496 t.setstr(val);
3497 pusharg(id, t, stack);
3498 id.flags &= ~IDF_UNKNOWN;
3499 }
3500 }
3501
listfind(ident * id,const char * list,const uint * body)3502 void listfind(ident *id, const char *list, const uint *body)
3503 {
3504 if(id->type!=ID_ALIAS) { intret(-1); return; }
3505 identstack stack;
3506 int n = -1;
3507 for(const char *s = list, *start, *end; parselist(s, start, end);)
3508 {
3509 ++n;
3510 setiter(*id, newstring(start, end-start), stack);
3511 if(executebool(body)) { intret(n); goto found; }
3512 }
3513 intret(-1);
3514 found:
3515 if(n >= 0) poparg(*id);
3516 }
3517 COMMAND(listfind, "rse");
3518
listassoc(ident * id,const char * list,const uint * body)3519 void listassoc(ident *id, const char *list, const uint *body)
3520 {
3521 if(id->type!=ID_ALIAS) return;
3522 identstack stack;
3523 int n = -1;
3524 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);)
3525 {
3526 ++n;
3527 setiter(*id, newstring(start, end-start), stack);
3528 if(executebool(body)) { if(parselist(s, start, end, qstart)) stringret(listelem(start, end, qstart)); break; }
3529 if(!parselist(s)) break;
3530 }
3531 if(n >= 0) poparg(*id);
3532 }
3533 COMMAND(listassoc, "rse");
3534
3535 #define LISTFIND(name, fmt, type, init, cmp) \
3536 ICOMMAND(name, "s" fmt "i", (char *list, type *val, int *skip), \
3537 { \
3538 int n = 0; \
3539 init; \
3540 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++) \
3541 { \
3542 if(cmp) { intret(n); return; } \
3543 loopi(*skip) { if(!parselist(s)) goto notfound; n++; } \
3544 } \
3545 notfound: \
3546 intret(-1); \
3547 });
3548 LISTFIND(listfind=, "i", int, , parseint(start) == *val);
3549 LISTFIND(listfind=f, "f", float, , parsefloat(start) == *val);
3550 LISTFIND(listfind=s, "s", char, int len = (int)strlen(val), int(end-start) == len && !memcmp(start, val, len));
3551
3552 #define LISTASSOC(name, fmt, type, init, cmp) \
3553 ICOMMAND(name, "s" fmt, (char *list, type *val), \
3554 { \
3555 init; \
3556 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);) \
3557 { \
3558 if(cmp) { if(parselist(s, start, end, qstart)) stringret(listelem(start, end, qstart)); return; } \
3559 if(!parselist(s)) break; \
3560 } \
3561 });
3562 LISTASSOC(listassoc=, "i", int, , parseint(start) == *val);
3563 LISTASSOC(listassoc=f, "f", float, , parsefloat(start) == *val);
3564 LISTASSOC(listassoc=s, "s", char, int len = (int)strlen(val), int(end-start) == len && !memcmp(start, val, len));
3565
looplist(ident * id,const char * list,const uint * body)3566 void looplist(ident *id, const char *list, const uint *body)
3567 {
3568 if(id->type!=ID_ALIAS) return;
3569 identstack stack;
3570 int n = 0;
3571 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
3572 {
3573 setiter(*id, listelem(start, end, qstart), stack);
3574 execute(body);
3575 }
3576 if(n) poparg(*id);
3577 }
3578 COMMAND(looplist, "rse");
3579
looplist2(ident * id,ident * id2,const char * list,const uint * body)3580 void looplist2(ident *id, ident *id2, const char *list, const uint *body)
3581 {
3582 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS) return;
3583 identstack stack, stack2;
3584 int n = 0;
3585 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 2)
3586 {
3587 setiter(*id, listelem(start, end, qstart), stack);
3588 setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
3589 execute(body);
3590 }
3591 if(n) { poparg(*id); poparg(*id2); }
3592 }
3593 COMMAND(looplist2, "rrse");
3594
looplist3(ident * id,ident * id2,ident * id3,const char * list,const uint * body)3595 void looplist3(ident *id, ident *id2, ident *id3, const char *list, const uint *body)
3596 {
3597 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS || id3->type!=ID_ALIAS) return;
3598 identstack stack, stack2, stack3;
3599 int n = 0;
3600 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 3)
3601 {
3602 setiter(*id, listelem(start, end, qstart), stack);
3603 setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
3604 setiter(*id3, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack3);
3605 execute(body);
3606 }
3607 if(n) { poparg(*id); poparg(*id2); poparg(*id3); }
3608 }
3609 COMMAND(looplist3, "rrrse");
3610
looplistconc(ident * id,const char * list,const uint * body,bool space)3611 void looplistconc(ident *id, const char *list, const uint *body, bool space)
3612 {
3613 if(id->type!=ID_ALIAS) return;
3614 identstack stack;
3615 vector<char> r;
3616 int n = 0;
3617 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
3618 {
3619 char *val = listelem(start, end, qstart);
3620 setiter(*id, val, stack);
3621
3622 if(n && space) r.add(' ');
3623
3624 tagval v;
3625 executeret(body, v);
3626 const char *vstr = v.getstr();
3627 int len = strlen(vstr);
3628 r.put(vstr, len);
3629 freearg(v);
3630 }
3631 if(n) poparg(*id);
3632 r.add('\0');
3633 commandret->setstr(r.disown());
3634 }
3635 ICOMMAND(looplistconcat, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, true));
3636 ICOMMAND(looplistconcatword, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, false));
3637
listfilter(ident * id,const char * list,const uint * body)3638 void listfilter(ident *id, const char *list, const uint *body)
3639 {
3640 if(id->type!=ID_ALIAS) return;
3641 identstack stack;
3642 vector<char> r;
3643 int n = 0;
3644 for(const char *s = list, *start, *end, *qstart, *qend; parselist(s, start, end, qstart, qend); n++)
3645 {
3646 char *val = newstring(start, end-start);
3647 setiter(*id, val, stack);
3648
3649 if(executebool(body))
3650 {
3651 if(r.length()) r.add(' ');
3652 r.put(qstart, qend-qstart);
3653 }
3654 }
3655 if(n) poparg(*id);
3656 r.add('\0');
3657 commandret->setstr(r.disown());
3658 }
3659 COMMAND(listfilter, "rse");
3660
listcount(ident * id,const char * list,const uint * body)3661 void listcount(ident *id, const char *list, const uint *body)
3662 {
3663 if(id->type!=ID_ALIAS) return;
3664 identstack stack;
3665 int n = 0, r = 0;
3666 for(const char *s = list, *start, *end; parselist(s, start, end); n++)
3667 {
3668 char *val = newstring(start, end-start);
3669 setiter(*id, val, stack);
3670 if(executebool(body)) r++;
3671 }
3672 if(n) poparg(*id);
3673 intret(r);
3674 }
3675 COMMAND(listcount, "rse");
3676
prettylist(const char * s,const char * conj)3677 void prettylist(const char *s, const char *conj)
3678 {
3679 vector<char> p;
3680 const char *start, *end, *qstart;
3681 for(int len = listlen(s), n = 0; parselist(s, start, end, qstart); n++)
3682 {
3683 if(*qstart == '"') p.advance(unescapestring(p.reserve(end - start).buf, start, end));
3684 else p.put(start, end - start);
3685 if(n+1 < len)
3686 {
3687 if(len > 2 || !conj[0]) p.add(',');
3688 if(n+2 == len && conj[0])
3689 {
3690 p.add(' ');
3691 p.put(conj, strlen(conj));
3692 }
3693 p.add(' ');
3694 }
3695 }
3696 p.add('\0');
3697 commandret->setstr(p.disown());
3698 }
3699 COMMAND(prettylist, "ss");
3700
listincludes(const char * list,const char * needle,int needlelen)3701 int listincludes(const char *list, const char *needle, int needlelen)
3702 {
3703 int offset = 0;
3704 for(const char *s = list, *start, *end; parselist(s, start, end);)
3705 {
3706 int len = end - start;
3707 if(needlelen == len && !strncmp(needle, start, len)) return offset;
3708 offset++;
3709 }
3710 return -1;
3711 }
3712 ICOMMAND(indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem))));
3713
3714 #define LISTMERGECMD(name, init, iter, filter, dir) \
3715 ICOMMAND(name, "ss", (const char *list, const char *elems), \
3716 { \
3717 vector<char> p; \
3718 init; \
3719 for(const char *start, *end, *qstart, *qend; parselist(iter, start, end, qstart, qend);) \
3720 { \
3721 int len = end - start; \
3722 if(listincludes(filter, start, len) dir 0) \
3723 { \
3724 if(!p.empty()) p.add(' '); \
3725 p.put(qstart, qend-qstart); \
3726 } \
3727 } \
3728 p.add('\0'); \
3729 commandret->setstr(p.disown()); \
3730 })
3731
3732 LISTMERGECMD(listdel, , list, elems, <);
3733 LISTMERGECMD(listintersect, , list, elems, >=);
3734 LISTMERGECMD(listunion, p.put(list, strlen(list)), elems, list, <);
3735
listsplice(const char * s,const char * vals,int * skip,int * count,int * numargs)3736 void listsplice(const char *s, const char *vals, int *skip, int *count, int *numargs)
3737 {
3738 int offset = max(*skip, 0), len = *numargs >= 4 ? max(*count, 0) : -1;
3739 const char *list = s, *start, *end, *qstart, *qend = s;
3740 loopi(offset) if(!parselist(s, start, end, qstart, qend)) break;
3741 vector<char> p;
3742 if(qend > list) p.put(list, qend-list);
3743 if(*vals)
3744 {
3745 if(!p.empty()) p.add(' ');
3746 p.put(vals, strlen(vals));
3747 }
3748 while(len-- > 0) if(!parselist(s)) break;
3749 skiplist(s);
3750 switch(*s)
3751 {
3752 case '\0': case ')': case ']': break;
3753 default:
3754 if(!p.empty()) p.add(' ');
3755 p.put(s, strlen(s));
3756 break;
3757 }
3758 p.add('\0');
3759 commandret->setstr(p.disown());
3760 }
3761 COMMAND(listsplice, "ssiiN");
3762
3763 ICOMMAND(loopfiles, "rsse", (ident *id, char *dir, char *ext, uint *body),
3764 {
3765 if(id->type!=ID_ALIAS) return;
3766 identstack stack;
3767 vector<char *> files;
3768 listfiles(dir, ext[0] ? ext : NULL, files);
3769 files.sort();
3770 files.uniquedeletearrays();
3771 loopv(files)
3772 {
3773 setiter(*id, files[i], stack);
3774 execute(body);
3775 }
3776 if(files.length()) poparg(*id);
3777 });
3778
3779 ICOMMAND(findfile, "s", (char *name),
3780 {
3781 string fname;
3782 copystring(fname, name);
3783 path(fname);
3784 intret(fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0);
3785 });
3786
3787 struct sortitem
3788 {
3789 const char *str, *quotestart, *quoteend;
3790
quotelengthsortitem3791 int quotelength() const { return int(quoteend-quotestart); }
3792 };
3793
3794 struct sortfun
3795 {
3796 ident *x, *y;
3797 uint *body;
3798
operator ()sortfun3799 bool operator()(const sortitem &xval, const sortitem &yval)
3800 {
3801 if(x->valtype != VAL_CSTR) x->valtype = VAL_CSTR;
3802 cleancode(*x);
3803 x->val.code = (const uint *)xval.str;
3804 if(y->valtype != VAL_CSTR) y->valtype = VAL_CSTR;
3805 cleancode(*y);
3806 y->val.code = (const uint *)yval.str;
3807 return executebool(body);
3808 }
3809 };
3810
sortlist(char * list,ident * x,ident * y,uint * body,uint * unique)3811 void sortlist(char *list, ident *x, ident *y, uint *body, uint *unique)
3812 {
3813 if(x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) return;
3814
3815 vector<sortitem> items;
3816 int clen = strlen(list), total = 0;
3817 char *cstr = newstring(list, clen);
3818 const char *curlist = list, *start, *end, *quotestart, *quoteend;
3819 while(parselist(curlist, start, end, quotestart, quoteend))
3820 {
3821 cstr[end - list] = '\0';
3822 sortitem item = { &cstr[start - list], quotestart, quoteend };
3823 items.add(item);
3824 total += item.quotelength();
3825 }
3826
3827 if(items.empty())
3828 {
3829 commandret->setstr(cstr);
3830 return;
3831 }
3832
3833 identstack xstack, ystack;
3834 pusharg(*x, nullval, xstack); x->flags &= ~IDF_UNKNOWN;
3835 pusharg(*y, nullval, ystack); y->flags &= ~IDF_UNKNOWN;
3836
3837 int totalunique = total, numunique = items.length();
3838 if(body)
3839 {
3840 sortfun f = { x, y, body };
3841 items.sort(f);
3842 if((*unique&CODE_OP_MASK) != CODE_EXIT)
3843 {
3844 f.body = unique;
3845 totalunique = items[0].quotelength();
3846 numunique = 1;
3847 for(int i = 1; i < items.length(); i++)
3848 {
3849 sortitem &item = items[i];
3850 if(f(items[i-1], item)) item.quotestart = NULL;
3851 else { totalunique += item.quotelength(); numunique++; }
3852 }
3853 }
3854 }
3855 else
3856 {
3857 sortfun f = { x, y, unique };
3858 totalunique = items[0].quotelength();
3859 numunique = 1;
3860 for(int i = 1; i < items.length(); i++)
3861 {
3862 sortitem &item = items[i];
3863 loopj(i)
3864 {
3865 sortitem &prev = items[j];
3866 if(prev.quotestart && f(item, prev)) { item.quotestart = NULL; break; }
3867 }
3868 if(item.quotestart) { totalunique += item.quotelength(); numunique++; }
3869 }
3870 }
3871
3872 poparg(*x);
3873 poparg(*y);
3874
3875 char *sorted = cstr;
3876 int sortedlen = totalunique + max(numunique - 1, 0);
3877 if(clen < sortedlen)
3878 {
3879 delete[] cstr;
3880 sorted = newstring(sortedlen);
3881 }
3882
3883 int offset = 0;
3884 loopv(items)
3885 {
3886 sortitem &item = items[i];
3887 if(!item.quotestart) continue;
3888 int len = item.quotelength();
3889 if(i) sorted[offset++] = ' ';
3890 memcpy(&sorted[offset], item.quotestart, len);
3891 offset += len;
3892 }
3893 sorted[offset] = '\0';
3894
3895 commandret->setstr(sorted);
3896 }
3897 COMMAND(sortlist, "srree");
3898 ICOMMAND(uniquelist, "srre", (char *list, ident *x, ident *y, uint *body), sortlist(list, x, y, NULL, body));
3899
3900 #define MATHCMD(name, fmt, type, op, initval, unaryop) \
3901 ICOMMANDS(name, #fmt "1V", (tagval *args, int numargs), \
3902 { \
3903 type val; \
3904 if(numargs >= 2) \
3905 { \
3906 val = args[0].fmt; \
3907 type val2 = args[1].fmt; \
3908 op; \
3909 for(int i = 2; i < numargs; i++) { val2 = args[i].fmt; op; } \
3910 } \
3911 else { val = numargs > 0 ? args[0].fmt : initval; unaryop; } \
3912 type##ret(val); \
3913 })
3914 #define MATHICMDN(name, op, initval, unaryop) MATHCMD(#name, i, int, val = val op val2, initval, unaryop)
3915 #define MATHICMD(name, initval, unaryop) MATHICMDN(name, name, initval, unaryop)
3916 #define MATHFCMDN(name, op, initval, unaryop) MATHCMD(#name "f", f, float, val = val op val2, initval, unaryop)
3917 #define MATHFCMD(name, initval, unaryop) MATHFCMDN(name, name, initval, unaryop)
3918
3919 #define CMPCMD(name, fmt, type, op) \
3920 ICOMMANDS(name, #fmt "1V", (tagval *args, int numargs), \
3921 { \
3922 bool val; \
3923 if(numargs >= 2) \
3924 { \
3925 val = args[0].fmt op args[1].fmt; \
3926 for(int i = 2; i < numargs && val; i++) val = args[i-1].fmt op args[i].fmt; \
3927 } \
3928 else val = (numargs > 0 ? args[0].fmt : 0) op 0; \
3929 intret(int(val)); \
3930 })
3931 #define CMPICMDN(name, op) CMPCMD(#name, i, int, op)
3932 #define CMPICMD(name) CMPICMDN(name, name)
3933 #define CMPFCMDN(name, op) CMPCMD(#name "f", f, float, op)
3934 #define CMPFCMD(name) CMPFCMDN(name, name)
3935
3936 MATHICMD(+, 0, );
3937 MATHICMD(*, 1, );
3938 MATHICMD(-, 0, val = -val);
3939 CMPICMDN(=, ==);
3940 CMPICMD(!=);
3941 CMPICMD(<);
3942 CMPICMD(>);
3943 CMPICMD(<=);
3944 CMPICMD(>=);
3945 MATHICMD(^, 0, val = ~val);
3946 MATHICMDN(~, ^, 0, val = ~val);
3947 MATHICMD(&, 0, );
3948 MATHICMD(|, 0, );
3949 MATHICMD(^~, 0, );
3950 MATHICMD(&~, 0, );
3951 MATHICMD(|~, 0, );
3952 MATHICMD(<<, 0, );
3953 MATHICMD(>>, 0, );
3954
3955 MATHFCMD(+, 0, );
3956 MATHFCMD(*, 1, );
3957 MATHFCMD(-, 0, val = -val);
3958 CMPFCMDN(=, ==);
3959 CMPFCMD(!=);
3960 CMPFCMD(<);
3961 CMPFCMD(>);
3962 CMPFCMD(<=);
3963 CMPFCMD(>=);
3964
3965 ICOMMANDK(!, ID_NOT, "t", (tagval *a), intret(getbool(*a) ? 0 : 1));
3966 ICOMMANDK(&&, ID_AND, "E1V", (tagval *args, int numargs),
3967 {
3968 if(!numargs) intret(1);
3969 else loopi(numargs)
3970 {
3971 if(i) freearg(*commandret);
3972 if(args[i].type == VAL_CODE) executeret(args[i].code, *commandret);
3973 else *commandret = args[i];
3974 if(!getbool(*commandret)) break;
3975 }
3976 });
3977 ICOMMANDK(||, ID_OR, "E1V", (tagval *args, int numargs),
3978 {
3979 if(!numargs) intret(0);
3980 else loopi(numargs)
3981 {
3982 if(i) freearg(*commandret);
3983 if(args[i].type == VAL_CODE) executeret(args[i].code, *commandret);
3984 else *commandret = args[i];
3985 if(getbool(*commandret)) break;
3986 }
3987 });
3988
3989
3990 #define DIVCMD(name, fmt, type, op) MATHCMD(#name, fmt, type, { if(val2) op; else val = 0; }, 0, )
3991
3992 DIVCMD(div, i, int, val /= val2);
3993 DIVCMD(mod, i, int, val %= val2);
3994 DIVCMD(divf, f, float, val /= val2);
3995 DIVCMD(modf, f, float, val = fmod(val, val2));
3996 MATHCMD("pow", f, float, val = pow(val, val2), 0, );
3997
3998 ICOMMAND(sin, "f", (float *a), floatret(sin(*a*RAD)));
3999 ICOMMAND(cos, "f", (float *a), floatret(cos(*a*RAD)));
4000 ICOMMAND(tan, "f", (float *a), floatret(tan(*a*RAD)));
4001 ICOMMAND(asin, "f", (float *a), floatret(asin(*a)/RAD));
4002 ICOMMAND(acos, "f", (float *a), floatret(acos(*a)/RAD));
4003 ICOMMAND(atan, "f", (float *a), floatret(atan(*a)/RAD));
4004 ICOMMAND(sqrt, "f", (float *a), floatret(sqrt(*a)));
4005 ICOMMAND(loge, "f", (float *a), floatret(log(*a)));
4006 ICOMMAND(log2, "f", (float *a), floatret(log(*a)/M_LN2));
4007 ICOMMAND(log10, "f", (float *a), floatret(log10(*a)));
4008 ICOMMAND(exp, "f", (float *a), floatret(exp(*a)));
4009
4010 #define MINMAXCMD(name, fmt, type, op) \
4011 ICOMMAND(name, #fmt "1V", (tagval *args, int numargs), \
4012 { \
4013 type val = numargs > 0 ? args[0].fmt : 0; \
4014 for(int i = 1; i < numargs; i++) val = op(val, args[i].fmt); \
4015 type##ret(val); \
4016 })
4017
4018 MINMAXCMD(min, i, int, min);
4019 MINMAXCMD(max, i, int, max);
4020 MINMAXCMD(minf, f, float, min);
4021 MINMAXCMD(maxf, f, float, max);
4022
4023 ICOMMAND(abs, "i", (int *n), intret(abs(*n)));
4024 ICOMMAND(absf, "f", (float *n), floatret(fabs(*n)));
4025
4026 ICOMMAND(floor, "f", (float *n), floatret(floor(*n)));
4027 ICOMMAND(ceil, "f", (float *n), floatret(ceil(*n)));
4028 ICOMMAND(round, "f", (float *n), floatret(floor(*n + 0.5)));
4029
4030 ICOMMAND(cond, "ee2V", (tagval *args, int numargs),
4031 {
4032 for(int i = 0; i < numargs; i += 2)
4033 {
4034 if(i+1 < numargs)
4035 {
4036 if(executebool(args[i].code))
4037 {
4038 executeret(args[i+1].code, *commandret);
4039 break;
4040 }
4041 }
4042 else
4043 {
4044 executeret(args[i].code, *commandret);
4045 break;
4046 }
4047 }
4048 });
4049
4050 #define CASECOMMAND(name, fmt, type, acc, compare) \
4051 ICOMMAND(name, fmt "te2V", (tagval *args, int numargs), \
4052 { \
4053 type val = acc; \
4054 int i; \
4055 for(i = 1; i+1 < numargs; i += 2) \
4056 { \
4057 if(compare) \
4058 { \
4059 executeret(args[i+1].code, *commandret); \
4060 return; \
4061 } \
4062 } \
4063 })
4064
4065 CASECOMMAND(case, "i", int, args[0].getint(), args[i].type == VAL_NULL || args[i].getint() == val);
4066 CASECOMMAND(casef, "f", float, args[0].getfloat(), args[i].type == VAL_NULL || args[i].getfloat() == val);
4067 CASECOMMAND(cases, "s", const char *, args[0].getstr(), args[i].type == VAL_NULL || !strcmp(args[i].getstr(), val));
4068
4069 ICOMMAND(rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b));
4070
4071 ICOMMAND(tohex, "ii", (int *n, int *p),
4072 {
4073 const int len = 20;
4074 char *buf = newstring(len);
4075 nformatstring(buf, len, "0x%.*X", max(*p, 1), *n);
4076 stringret(buf);
4077 });
4078
4079 #define CMPSCMD(name, op) \
4080 ICOMMAND(name, "s1V", (tagval *args, int numargs), \
4081 { \
4082 bool val; \
4083 if(numargs >= 2) \
4084 { \
4085 val = strcmp(args[0].s, args[1].s) op 0; \
4086 for(int i = 2; i < numargs && val; i++) val = strcmp(args[i-1].s, args[i].s) op 0; \
4087 } \
4088 else val = (numargs > 0 ? args[0].s[0] : 0) op 0; \
4089 intret(int(val)); \
4090 })
4091
4092 CMPSCMD(strcmp, ==);
4093 CMPSCMD(=s, ==);
4094 CMPSCMD(!=s, !=);
4095 CMPSCMD(<s, <);
4096 CMPSCMD(>s, >);
4097 CMPSCMD(<=s, <=);
4098 CMPSCMD(>=s, >=);
4099
4100 ICOMMAND(echo, "C", (char *s), conoutf("\f1%s", s));
4101 ICOMMAND(error, "C", (char *s), conoutf(CON_ERROR, "%s", s));
4102 ICOMMAND(strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); });
4103 ICOMMAND(strlen, "s", (char *s), intret(strlen(s)));
4104 ICOMMAND(strcode, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : uchar(s[*i])) : uchar(s[0])));
4105 ICOMMAND(codestr, "i", (int *i), { char *s = newstring(1); s[0] = char(*i); s[1] = '\0'; stringret(s); });
4106 ICOMMAND(struni, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : cube2uni(s[*i])) : cube2uni(s[0])));
4107 ICOMMAND(unistr, "i", (int *i), { char *s = newstring(1); s[0] = uni2cube(*i); s[1] = '\0'; stringret(s); });
4108
4109 #define STRMAPCOMMAND(name, map) \
4110 ICOMMAND(name, "s", (char *s), \
4111 { \
4112 int len = strlen(s); \
4113 char *m = newstring(len); \
4114 loopi(len) m[i] = map(s[i]); \
4115 m[len] = '\0'; \
4116 stringret(m); \
4117 })
4118
4119 STRMAPCOMMAND(strlower, cubelower);
4120 STRMAPCOMMAND(strupper, cubeupper);
4121
strreplace(const char * s,const char * oldval,const char * newval,const char * newval2)4122 char *strreplace(const char *s, const char *oldval, const char *newval, const char *newval2)
4123 {
4124 vector<char> buf;
4125
4126 int oldlen = strlen(oldval);
4127 if(!oldlen) return newstring(s);
4128 for(int i = 0;; i++)
4129 {
4130 const char *found = strstr(s, oldval);
4131 if(found)
4132 {
4133 while(s < found) buf.add(*s++);
4134 for(const char *n = i&1 ? newval2 : newval; *n; n++) buf.add(*n);
4135 s = found + oldlen;
4136 }
4137 else
4138 {
4139 while(*s) buf.add(*s++);
4140 buf.add('\0');
4141 return newstring(buf.getbuf(), buf.length());
4142 }
4143 }
4144 }
4145
4146 ICOMMAND(strreplace, "ssss", (char *s, char *o, char *n, char *n2), commandret->setstr(strreplace(s, o, n, n2[0] ? n2 : n)));
4147
4148 #ifndef STANDALONE
4149 ICOMMAND(getmillis, "i", (int *total), intret(*total ? totalmillis : lastmillis));
4150
4151 struct sleepcmd
4152 {
4153 int delay, millis, flags;
4154 char *command;
4155 };
4156 vector<sleepcmd> sleepcmds;
4157
addsleep(int * msec,char * cmd)4158 void addsleep(int *msec, char *cmd)
4159 {
4160 sleepcmd &s = sleepcmds.add();
4161 s.delay = max(*msec, 1);
4162 s.millis = lastmillis;
4163 s.command = newstring(cmd);
4164 s.flags = identflags;
4165 }
4166
4167 COMMANDN(sleep, addsleep, "is");
4168
checksleep(int millis)4169 void checksleep(int millis)
4170 {
4171 loopv(sleepcmds)
4172 {
4173 sleepcmd &s = sleepcmds[i];
4174 if(millis - s.millis >= s.delay)
4175 {
4176 char *cmd = s.command; // execute might create more sleep commands
4177 s.command = NULL;
4178 int oldflags = identflags;
4179 identflags = s.flags;
4180 execute(cmd);
4181 identflags = oldflags;
4182 delete[] cmd;
4183 if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--);
4184 }
4185 }
4186 }
4187
clearsleep(bool clearoverrides)4188 void clearsleep(bool clearoverrides)
4189 {
4190 int len = 0;
4191 loopv(sleepcmds) if(sleepcmds[i].command)
4192 {
4193 if(clearoverrides && !(sleepcmds[i].flags&IDF_OVERRIDDEN)) sleepcmds[len++] = sleepcmds[i];
4194 else delete[] sleepcmds[i].command;
4195 }
4196 sleepcmds.shrink(len);
4197 }
4198
clearsleep_(int * clearoverrides)4199 void clearsleep_(int *clearoverrides)
4200 {
4201 clearsleep(*clearoverrides!=0 || identflags&IDF_OVERRIDDEN);
4202 }
4203
4204 COMMANDN(clearsleep, clearsleep_, "i");
4205 #endif
4206
4207