5 // ========================================
6 // RPN command code, written by divVerent
7 // Last updated: December 28th, 2011
8 // ========================================
14 return rpn_stack[rpn_sp];
16 LOG_INFO("rpn: stack underflow");
21 void rpn_push(string s)
23 if (rpn_sp < MAX_RPN_STACK) {
24 rpn_stack[rpn_sp] = s;
27 LOG_INFO("rpn: stack overflow");
34 return rpn_stack[rpn_sp - 1];
36 LOG_INFO("rpn: empty stack");
41 void rpn_set(string s)
44 rpn_stack[rpn_sp - 1] = s;
46 LOG_INFO("rpn: empty stack");
51 float rpn_getf() { return stof(rpn_get()); }
52 float rpn_popf() { return stof(rpn_pop()); }
53 void rpn_pushf(float f) { return rpn_push(sprintf("%.9g", f)); }
54 void rpn_setf(float f) { return rpn_set(sprintf("%.9g", f)); }
56 void GenericCommand_rpn(float request, float argc, string command)
59 case CMD_REQUEST_COMMAND:
61 float i, j, f, f2, f3, rpnpos;
67 db_put(rpn_db, "stack.pointer", "0");
68 db_put(rpn_db, "stack.pos", "-1");
74 for (rpnpos = 1; rpnpos < argc; ++rpnpos) {
75 rpncmd = argv(rpnpos);
77 if (rpncmd == "") {} else if (stof(substring(rpncmd, 0, 1)) > 0) {
79 } else if (substring(rpncmd, 0, 1) == "0") {
81 } else if (f >= 2 && substring(rpncmd, 0, 1) == "+") {
83 } else if (f >= 2 && substring(rpncmd, 0, 1) == "-") {
85 } else if (f >= 2 && substring(rpncmd, 0, 1) == "/") {
86 rpn_push(substring(rpncmd, 1, strlen(rpncmd) - 1));
87 } else if (rpncmd == "clear") {
89 } else if (rpncmd == "def" || rpncmd == "=") {
95 registercvar(s2, "", 0);
99 if (!rpn_error) { // don't change cvars if a stack error had happened!
103 LOG_INFO("rpn: empty cvar name for 'def'");
106 } else if (rpncmd == "defs" || rpncmd == "@") {
110 while (rpn_sp > 1 && (j || i > 0)) {
111 s = strcat("/", rpn_pop(), " ", s);
117 registercvar(s2, "", 0);
119 registercvar(s2, "");
121 if (!rpn_error) { // don't change cvars if a stack error had happened!
125 LOG_INFO("rpn: empty cvar name for 'defs'");
128 } else if (rpncmd == "load") {
129 rpn_set(cvar_string(rpn_get()));
130 } else if (rpncmd == "exch") {
135 } else if (rpncmd == "dup") {
137 } else if (rpncmd == "pop") {
139 } else if (rpncmd == "add" || rpncmd == "+") {
141 rpn_setf(rpn_getf() + f);
142 } else if (rpncmd == "sub" || rpncmd == "-") {
144 rpn_setf(rpn_getf() - f);
145 } else if (rpncmd == "mul" || rpncmd == "*") {
147 rpn_setf(rpn_getf() * f);
148 } else if (rpncmd == "div" || rpncmd == "/") {
150 rpn_setf(rpn_getf() / f);
151 } else if (rpncmd == "mod" || rpncmd == "%") {
154 rpn_setf(f2 - f * floor(f2 / f));
155 } else if (rpncmd == "pow" || rpncmd == "**") {
157 rpn_setf(POW(rpn_getf(), f));
158 } else if (rpncmd == "bitand" || rpncmd == "&") {
160 rpn_setf(rpn_getf() & f);
161 } else if (rpncmd == "bitor" || rpncmd == "|") {
163 rpn_setf(rpn_getf() | f);
164 } else if (rpncmd == "bitxor" || rpncmd == "^") {
166 rpn_setf(rpn_getf() ^ f);
167 } else if (rpncmd == "and" || rpncmd == "&&") {
169 rpn_setf(rpn_getf() && f);
170 } else if (rpncmd == "or" || rpncmd == "||") {
172 rpn_setf(rpn_getf() || f);
173 } else if (rpncmd == "xor" || rpncmd == "^^") {
175 rpn_setf(!rpn_getf() != !f);
176 } else if (rpncmd == "bitnot") {
177 rpn_setf(~rpn_popf());
178 } else if (rpncmd == "not") {
179 rpn_setf(!rpn_popf());
180 } else if (rpncmd == "abs") {
181 rpn_setf(fabs(rpn_getf()));
182 } else if (rpncmd == "sgn") {
191 } else if (rpncmd == "neg" || rpncmd == "~") {
192 rpn_setf(-rpn_getf());
193 } else if (rpncmd == "floor" || rpncmd == "f") {
194 rpn_setf(floor(rpn_getf()));
195 } else if (rpncmd == "ceil" || rpncmd == "c") {
196 rpn_setf(ceil(rpn_getf()));
197 } else if (rpncmd == "exp") {
198 rpn_setf(exp(rpn_getf()));
199 } else if (rpncmd == "log") {
200 rpn_setf(exp(rpn_getf()));
201 } else if (rpncmd == "sin") {
202 rpn_setf(sin(rpn_getf()));
203 } else if (rpncmd == "cos") {
204 rpn_setf(cos(rpn_getf()));
205 } else if (rpncmd == "max") {
208 rpn_setf(max(f2, f));
209 } else if (rpncmd == "min") {
212 rpn_setf(min(f2, f));
213 } else if (rpncmd == "bound") {
217 rpn_setf(bound(f3, f2, f));
218 } else if (rpncmd == "when") {
227 } else if (rpncmd == ">" || rpncmd == "gt") {
229 rpn_setf(rpn_getf() > f);
230 } else if (rpncmd == "<" || rpncmd == "lt") {
232 rpn_setf(rpn_getf() < f);
233 } else if (rpncmd == "==" || rpncmd == "eq") {
235 rpn_setf(rpn_getf() == f);
236 } else if (rpncmd == ">=" || rpncmd == "ge") {
238 rpn_setf(rpn_getf() >= f);
239 } else if (rpncmd == "<=" || rpncmd == "le") {
241 rpn_setf(rpn_getf() <= f);
242 } else if (rpncmd == "!=" || rpncmd == "ne") {
244 rpn_setf(rpn_getf() != f);
245 } else if (rpncmd == "rand") {
246 rpn_setf(ceil(random() * rpn_getf()) - 1);
247 } else if (rpncmd == "crc16") {
248 rpn_setf(crc16(false, rpn_get()));
249 } else if (rpncmd == "put") {
254 db_put(rpn_db, s, s2);
257 } else if (rpncmd == "get") {
260 rpn_push(db_get(rpn_db, s));
262 } else if (rpncmd == "dbpush") {
265 i = stof(db_get(rpn_db, "stack.pointer"));
266 db_put(rpn_db, "stack.pointer", ftos(i + 1));
267 db_put(rpn_db, strcat("stack.", ftos(i)), s);
269 db_put(rpn_db, "stack.pos", "0");
272 } else if (rpncmd == "dbpop") {
273 i = stof(db_get(rpn_db, "stack.pointer"));
276 db_put(rpn_db, "stack.pointer", s);
277 rpn_push(db_get(rpn_db, strcat("stack.", s)));
278 j = stof(db_get(rpn_db, "stack.pos"));
280 db_put(rpn_db, "stack.pos", ftos(i - 2));
284 LOG_INFO("rpn: database underflow");
286 } else if (rpncmd == "dbget") {
287 i = stof(db_get(rpn_db, "stack.pointer"));
289 rpn_push(db_get(rpn_db, strcat("stack.", ftos(i - 1))));
292 LOG_INFO("rpn: database empty");
294 } else if (rpncmd == "dblen") {
295 rpn_push(db_get(rpn_db, "stack.pointer"));
296 } else if (rpncmd == "dbclr") {
298 rpn_db = db_create();
299 db_put(rpn_db, "stack.pointer", "0");
300 db_put(rpn_db, "stack.pos", "-1");
301 } else if (rpncmd == "dbsave") {
306 } else if (rpncmd == "dbload") {
312 } else if (rpncmd == "dbins") {
316 j = stof(db_get(rpn_db, "stack.pointer"));
317 i = stof(db_get(rpn_db, "stack.pos"));
321 db_put(rpn_db, "stack.pos", "0");
324 db_put(rpn_db, "stack.pointer", ftos(j + 1));
325 for (--j; j >= i; --j) {
326 db_put(rpn_db, strcat("stack.", ftos(j + 1)),
327 db_get(rpn_db, (strcat("stack.", ftos(j))))
330 db_put(rpn_db, strcat("stack.", ftos(i)), s);
332 } else if (rpncmd == "dbext") {
333 j = stof(db_get(rpn_db, "stack.pointer"));
334 i = stof(db_get(rpn_db, "stack.pos"));
337 LOG_INFO("rpn: empty database");
340 rpn_push(db_get(rpn_db, strcat("stack.", ftos(i))));
341 db_put(rpn_db, "stack.pointer", ftos(j));
343 db_put(rpn_db, "stack.pos", ftos(j - 1));
346 db_put(rpn_db, strcat("stack.", ftos(i)),
347 db_get(rpn_db, (strcat("stack.", ftos(i + 1))))
353 } else if (rpncmd == "dbread") {
354 s = db_get(rpn_db, "stack.pos");
356 rpn_push(db_get(rpn_db, strcat("stack.", s)));
359 LOG_INFO("rpn: empty database");
361 } else if (rpncmd == "dbat") {
362 rpn_push(db_get(rpn_db, "stack.pos"));
363 } else if (rpncmd == "dbmov") {
364 j = stof(db_get(rpn_db, "stack.pointer"));
365 i = stof(db_get(rpn_db, "stack.pos"));
368 if (i < 0 || i >= j) {
369 LOG_INFO("rpn: database cursor out of bounds");
373 db_put(rpn_db, "stack.pos", ftos(i));
376 } else if (rpncmd == "dbgoto") {
378 j = stof(db_get(rpn_db, "stack.pointer"));
381 LOG_INFO("rpn: empty database, cannot move cursor");
385 i = stof(db_get(rpn_db, "stack.pointer")) - 1;
386 } else if (s == "beg") {
392 j = stof(db_get(rpn_db, "stack.pointer"));
393 if (i < 0 || i >= j) {
394 LOG_INFO("rpn: database cursor destination out of bounds");
398 db_put(rpn_db, "stack.pos", ftos(i));
401 } else if (rpncmd == "union") {
405 f = tokenize_console(s);
406 f2 = tokenize_console(strcat(s, " ", s2));
407 // tokens 0..(f-1) represent s
408 // tokens f..f2 represent s2
409 // UNION: add all tokens to s that are in s2 but not in s
411 for (i = 0; i < f; ++i) {
412 s = strcat(s, " ", argv(i));
414 for (i = f; i < f2; ++i) {
415 for (j = 0; j < f; ++j) {
416 if (argv(i) == argv(j)) {
420 s = strcat(s, " ", argv(i));
423 if (substring(s, 0, 1) == " ") {
424 s = substring(s, 1, 99999);
427 tokenize_console(command);
428 } else if (rpncmd == "intersection") {
432 f = tokenize_console(s);
433 f2 = tokenize_console(strcat(s, " ", s2));
434 // tokens 0..(f-1) represent s
435 // tokens f..f2 represent s2
436 // INTERSECTION: keep only the tokens from s that are also in s2
438 for (i = 0; i < f; ++i) {
439 for (j = f; j < f2; ++j) {
440 if (argv(i) == argv(j)) {
441 s = strcat(s, " ", argv(i));
446 if (substring(s, 0, 1) == " ") {
447 s = substring(s, 1, 99999);
450 tokenize_console(command);
451 } else if (rpncmd == "difference") {
455 f = tokenize_console(s);
456 f2 = tokenize_console(strcat(s, " ", s2));
457 // tokens 0..(f-1) represent s
458 // tokens f..f2 represent s2
459 // DIFFERENCE: keep only the tokens from s that are not in s2
461 for (i = 0; i < f; ++i) {
462 for (j = f; j < f2; ++j) {
463 if (argv(i) == argv(j)) {
464 goto skip_difference;
467 s = strcat(s, " ", argv(i));
468 LABEL(skip_difference)
470 if (substring(s, 0, 1) == " ") {
471 s = substring(s, 1, 99999);
474 tokenize_console(command);
475 } else if (rpncmd == "shuffle") {
478 f = tokenize_console(s);
480 for (i = 0; i < f - 1; ++i) {
481 // move a random item from i..f-1 to position i
483 f2 = floor(random() * (f - i) + i);
484 for (j = 0; j < i; ++j) {
485 s = strcat(s, " ", argv(j));
487 s = strcat(s, " ", argv(f2));
488 for (j = i; j < f; ++j) {
490 s = strcat(s, " ", argv(j));
493 f = tokenize_console(s);
496 if (substring(s, 0, 1) == " ") {
497 s = substring(s, 1, 99999);
500 tokenize_console(command);
501 } else if (rpncmd == "fexists_assert") {
505 LOG_INFO("rpn: ERROR: ", s, " does not exist!");
509 } else if (rpncmd == "fexists") {
518 } else if (rpncmd == "localtime") {
519 rpn_set(strftime(true, rpn_get()));
520 } else if (rpncmd == "gmtime") {
521 rpn_set(strftime(false, rpn_get()));
522 } else if (rpncmd == "time") {
524 } else if (rpncmd == "digest") {
526 rpn_set(digest_hex(s, rpn_get()));
527 } else if (rpncmd == "sprintf1s") {
529 rpn_set(sprintf(s, rpn_get()));
530 } else if (rpncmd == "eval") {
532 command = strcat(s, substring(command, argv_end_index(rpnpos), -1));
533 argc = tokenize_console(command);
536 rpn_push(cvar_string(rpncmd));
544 LOG_INFO("rpn: still on stack: ", s);
552 case CMD_REQUEST_USAGE:
554 LOG_INFO("Usage:^3 ", GetProgramCommandPrefix(), " rpn EXPRESSION...");
555 LOG_INFO(" Operator description (x: string, s: set, f: float):");
556 LOG_INFO(" x pop -----------------------------> : removes the top");
557 LOG_INFO(" x dup -----------------------------> x x : duplicates the top");
558 LOG_INFO(" x x exch --------------------------> x x : swap the top two");
559 LOG_INFO(" /cvarname load --------------------> x : loads a cvar");
560 LOG_INFO(" /cvarname x def -------------------> : writes to a cvar");
561 LOG_INFO(" f f add|sub|mul|div|mod|pow -------> f : adds/... two numbers");
562 LOG_INFO(" f f and|or|xor|bitand|bitor|bitxor > f : logical and bitwise operations");
563 LOG_INFO(" f f eq|ne|gt|ge|lt|le|max|min -----> f : compares two numbers");
564 LOG_INFO(" f neg|abs|sgn|rand|floor|ceil------> f : negates/... a number");
565 LOG_INFO(" f not|bitnot ----------------------> f : logical and bitwise negation");
566 LOG_INFO(" f exp|log|sin|cos -----------------> f : exponential function & Co.");
567 LOG_INFO(" f f f bound -----------------------> f : bounds the middle number");
568 LOG_INFO(" f1 f2 b when ----------------------> f : f1 if b, f2 otherwise");
569 LOG_INFO(" s s union|intersection|difference -> s : set operations");
570 LOG_INFO(" s shuffle -------------------------> s : randomly arrange elements");
571 LOG_INFO(" /key /value put -------------------> : set a database key");
572 LOG_INFO(" /key get --------------------------> s : get a database value");
573 LOG_INFO(" x dbpush --------------------------> : pushes the top onto the database");
574 LOG_INFO(" dbpop|dbget -----------------------> x : removes/reads DB's top");
575 LOG_INFO(" dblen|dbat ------------------------> f : gets the DB's size/cursor pos");
576 LOG_INFO(" dbclr -----------------------------> : clear the DB");
577 LOG_INFO(" s dbsave|dbload--------------------> : save/load the DB to/from a file");
578 LOG_INFO(" x dbins ---------------------------> : moves the top into the DB");
579 LOG_INFO(" dbext|dbread ----------------------> x : extract/get from the DB's cursor");
580 LOG_INFO(" f dbmov|dbgoto --------------------> : move or set the DB's cursor");
581 LOG_INFO(" s localtime -----------------------> s : formats the current local time");
582 LOG_INFO(" s gmtime --------------------------> s : formats the current UTC time");
583 LOG_INFO(" time ------------------------------> f : seconds since VM start");
584 LOG_INFO(" s /MD4 digest ---------------------> s : MD4 digest");
585 LOG_INFO(" s /SHA256 digest ------------------> s : SHA256 digest");
586 LOG_INFO(" s /formatstring sprintf1s ---------> s : sprintf with 1 string (pad, cut)");
587 LOG_INFO(" s eval ----------------------------> : does something eval");
588 LOG_INFO(" Set operations operate on 'such''strings'.");
589 LOG_INFO(" Unknown tokens insert their cvar value.");