/****************************************************************************** * TinTin++ * * Copyright (C) 2004 (See CREDITS file) * * * * This program is protected under the GNU GPL (See COPYING) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ******************************************************************************/ /****************************************************************************** * (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t * * * * coded by Igor van den Hoven 2004 * ******************************************************************************/ #include "tintin.h" #define EXP_VARIABLE 0 #define EXP_STRING 1 #define EXP_OPERATOR 2 #define EXP_PR_DICE 0 #define EXP_PR_INTMUL 1 #define EXP_PR_INTADD 2 #define EXP_PR_BITSHIFT 3 #define EXP_PR_LOGLTGT 4 #define EXP_PR_LOGCOMP 5 #define EXP_PR_BITAND 6 #define EXP_PR_BITXOR 7 #define EXP_PR_BITOR 8 #define EXP_PR_LOGAND 9 #define EXP_PR_LOGXOR 10 #define EXP_PR_LOGOR 11 #define EXP_PR_VAR 12 #define EXP_PR_LVL 13 struct link_data *math_head; struct link_data *math_tail; struct link_data *mathnode_s; struct link_data *mathnode_e; int precision; DO_COMMAND(do_math) { char left[BUFFER_SIZE], right[BUFFER_SIZE]; struct listroot *root; struct listnode *node; root = ses->list[LIST_VARIABLE]; arg = get_arg_in_braces(arg, left, GET_NST); arg = get_arg_in_braces(arg, right, GET_ALL); if (*left == 0 || *right == 0) { tintin_printf2(ses, "#SYNTAX: #MATH {variable} {expression}."); } else { node = set_nest_node(ses->list[LIST_VARIABLE], left, "%.*f", precision, get_number(ses, right)); show_message(ses, LIST_VARIABLE, "#MATH: VARIABLE {%s} HAS BEEN SET TO {%s}.", left, node->right); } return ses; } double get_number(struct session *ses, char *str) { double val; char result[BUFFER_SIZE]; mathexp(ses, str, result); val = tintoi(result); return val; } void get_number_string(struct session *ses, char *str, char *result) { sprintf(result, "%.*f", precision, get_number(ses, str)); } char *get_alnum(struct session *ses, char *str) { char buffer[BUFFER_SIZE]; static char result[BUFFER_SIZE]; int sign; sign = *str == '-' || *str == '+' ? TRUE : FALSE; mathexp(ses, str, buffer); if (is_number(buffer)) { if (sign) { sprintf(result, "%+.*f", precision, tintoi(buffer)); } else { sprintf(result, "%.*f", precision, tintoi(buffer)); } } else { strcpy(result, buffer); } return result; } double mathswitch(struct session *ses, char *left, char *right) { char shift[BUFFER_SIZE]; sprintf(shift, "%s == %s", left, right); srand48(gtd->time); return get_number(ses, shift); } /* Flexible tokenized mathematical expression interpreter */ void mathexp(struct session *ses, char *str, char *result) { struct link_data *node; substitute(ses, str, result, SUB_VAR|SUB_FUN); if (mathexp_tokenize(ses, result) == FALSE) { return; } node = math_head; while (node->prev || node->next) { if (node->next == NULL || atoi(node->next->str1) < atoi(node->str1)) { mathexp_level(ses, node); node = math_head; } else { node = node->next; } } strcpy(result, node->str3); } #define MATH_NODE(buffercheck, priority, newstatus) \ { \ if (buffercheck && (pta == buf3 || valid == FALSE)) \ { \ return FALSE; \ } \ *pta = 0; \ sprintf(buf1, "%02d", level); \ sprintf(buf2, "%02d", priority); \ add_math_node(buf1, buf2, buf3); \ status = newstatus; \ pta = buf3; \ valid = FALSE; \ point = -1; \ } void add_math_node(char *arg1, char *arg2, char *arg3) { struct link_data *link; link = (struct link_data *) calloc(1, sizeof(struct link_data)); link->str1 = strdup(arg1); link->str2 = strdup(arg2); link->str3 = strdup(arg3); LINK(link, math_head, math_tail); } void del_math_node(struct link_data *node) { UNLINK(node, math_head, math_tail); free(node->str1); free(node->str2); free(node->str3); free(node); } int mathexp_tokenize(struct session *ses, char *str) { char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE], buf3[STRING_SIZE], *pti, *pta; int level, status, valid, point, colon; level = 0; point = -1; colon = 0; status = EXP_VARIABLE; valid = FALSE; precision = 0; pta = buf3; pti = str; while (math_head) { del_math_node(math_head); } while (*pti) { switch (status) { case EXP_VARIABLE: switch (*pti) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *pta++ = *pti++; valid = TRUE; if (point >= 0) { point++; if (precision < point) { precision = point; } } break; case '!': case '~': case '+': case '-': if (pta != buf3) { MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR); } else { *pta++ = *pti++; } break; case '"': if (pta != buf3) { show_message(ses, -1, "MATH EXP: \" FOUND INSIDE A NUMBER"); return FALSE; } *pta++ = *pti++; status = EXP_STRING; break; case '(': if (pta != buf3) { show_message(ses, -1, "#MATH EXP: PARANTESES FOUND INSIDE A NUMBER"); return FALSE; } *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LVL, EXP_VARIABLE); level++; break; case ',': pti++; break; case ':': *pta++ = *pti++; if (colon >= 3) { show_message(ses, -1, "#MATH EXP: MORE THAN THREE COLONS FOUND INSIDE A NUMBER"); return FALSE; } colon++; break; case '.': *pta++ = *pti++; if (point >= 0) { show_message(ses, -1, "#MATH EXP: MORE THAN ONE POINT FOUND INSIDE A NUMBER"); precision = 0; return FALSE; } point++; break; case ' ': case '\t': pti++; break; default: MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR); break; } break; case EXP_STRING: switch (*pti) { case '"': *pta++ = *pti++; valid = TRUE; MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR); break; default: *pta++ = *pti++; break; } break; case EXP_OPERATOR: switch (*pti) { case ' ': pti++; break; case ')': *pta++ = *pti++; level--; MATH_NODE(FALSE, EXP_PR_LVL, EXP_OPERATOR); break; case 'd': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_DICE, EXP_VARIABLE); break; case '*': case '/': case '%': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE); break; case '+': case '-': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_INTADD, EXP_VARIABLE); break; case '<': *pta++ = *pti++; switch (*pti) { case '<': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE); break; case '=': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE); break; default: MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE); break; } break; case '>': *pta++ = *pti++; switch (*pti) { case '>': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_BITSHIFT, EXP_VARIABLE); break; case '=': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE); break; default: MATH_NODE(FALSE, EXP_PR_LOGLTGT, EXP_VARIABLE); break; } break; case '&': *pta++ = *pti++; switch (*pti) { case '&': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGAND, EXP_VARIABLE); break; default: MATH_NODE(FALSE, EXP_PR_BITAND, EXP_VARIABLE); break; } break; case '^': *pta++ = *pti++; switch (*pti) { case '^': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGXOR, EXP_VARIABLE); break; default: MATH_NODE(FALSE, EXP_PR_BITXOR, EXP_VARIABLE); break; } break; case '|': *pta++ = *pti++; switch (*pti) { case '|': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGOR, EXP_VARIABLE); break; default: MATH_NODE(FALSE, EXP_PR_BITOR, EXP_VARIABLE); break; } break; case '=': case '!': *pta++ = *pti++; switch (*pti) { case '=': *pta++ = *pti++; MATH_NODE(FALSE, EXP_PR_LOGCOMP, EXP_VARIABLE); break; default: tintin_printf2(ses, "#MATH EXP: UNKNOWN OPERATOR: %c%c", pti[-1], pti[0]); return FALSE; } break; default: // show_message(ses, -1, "#MATH EXP: UNKNOWN OPERATOR: %c", *pti); return FALSE; } break; } } if (level != 0) { show_message(ses, -1, "#MATH EXP: UNMATCHED PARENTHESES, LEVEL: %d", level); return FALSE; } if (status != EXP_OPERATOR) { MATH_NODE(TRUE, EXP_PR_VAR, EXP_OPERATOR); } return TRUE; } void mathexp_level(struct session *ses, struct link_data *node) { int priority; mathnode_e = node; while (node->prev) { if (atoi(node->prev->str1) < atoi(node->str1)) { break; } node = node->prev; } mathnode_s = node; for (priority = 0 ; priority < EXP_PR_VAR ; priority++) { for (node = mathnode_s ; node ; node = node->next) { if (atoi(node->str2) == priority) { mathexp_compute(ses, node); } if (node == mathnode_e) { break; } } } node = mathnode_s; while (node->prev && node->next) { if (atoi(node->prev->str2) == EXP_PR_LVL && atoi(node->next->str2) == EXP_PR_LVL) { free(node->str1); node->str1 = strdup(node->next->str1); del_math_node(node->next); del_math_node(node->prev); } else { break; } } return; } void mathexp_compute(struct session *ses, struct link_data *node) { char temp[BUFFER_SIZE]; double value; switch (node->str3[0]) { case 'd': if (tintoi(node->next->str3) <= 0) { show_message(ses, -1, "#MATHEXP: INVALID DICE: %s", node->next->str3); value = 0; } else { value = tindice(node->prev->str3, node->next->str3); } break; case '*': value = tintoi(node->prev->str3) * tintoi(node->next->str3); break; case '/': if (tintoi(node->next->str3) == 0) { show_message(ses, -1, "#MATH ERROR: DIVISION ZERO."); value = tintoi(node->prev->str3); } else { if (precision) { value = tintoi(node->prev->str3) / tintoi(node->next->str3); } else { value = (long long) tintoi(node->prev->str3) / (long long) tintoi(node->next->str3); } } break; case '%': if (tintoi(node->next->str3) == 0) { show_message(ses, -1, "#MATH ERROR: MODULO ZERO."); value = tintoi(node->prev->str3); } else { value = (long long) tintoi(node->prev->str3) % (long long) tintoi(node->next->str3); } break; case '+': value = tintoi(node->prev->str3) + tintoi(node->next->str3); break; case '-': value = tintoi(node->prev->str3) - tintoi(node->next->str3); break; case '<': switch (node->str3[1]) { case '=': value = tincmp(node->prev->str3, node->next->str3) <= 0; break; case '<': value = (long long) tintoi(node->prev->str3) << (long long) tintoi(node->next->str3); break; default: value = tincmp(node->prev->str3, node->next->str3) < 0; break; } break; case '>': switch (node->str3[1]) { case '=': value = tincmp(node->prev->str3, node->next->str3) >= 0; break; case '>': value = (long long) tintoi(node->prev->str3) >> (long long) tintoi(node->next->str3); break; default: value = tincmp(node->prev->str3, node->next->str3) > 0; break; } break; case '&': switch (node->str3[1]) { case '&': value = tintoi(node->prev->str3) && tintoi(node->next->str3); break; default: value = (long long) tintoi(node->prev->str3) & (long long) tintoi(node->next->str3); break; } break; case '^': switch (node->str3[1]) { case '^': value = ((tintoi(node->prev->str3) || tintoi(node->next->str3)) && (!tintoi(node->prev->str3) != !tintoi(node->next->str3))); break; default: value = (long long) tintoi(node->prev->str3) ^ (long long) tintoi(node->next->str3); break; } break; case '|': switch (node->str3[1]) { case '|': value = tintoi(node->prev->str3) || tintoi(node->next->str3); break; default: value = (long long) tintoi(node->prev->str3) | (long long) tintoi(node->next->str3); break; } break; case '=': value = (tineval(ses, node->prev->str3, node->next->str3) != 0); break; case '!': value = (tineval(ses, node->prev->str3, node->next->str3) == 0); break; default: show_message(ses, -1, "#COMPUTE EXP: UNKNOWN OPERATOR: %c%c", node->str3[0], node->str3[1]); value = 0; break; } if (node->prev == mathnode_s) { mathnode_s = node; } if (node->next == mathnode_e) { mathnode_e = node; } del_math_node(node->next); del_math_node(node->prev); sprintf(temp, "%d", EXP_PR_VAR); free(node->str2); node->str2 = strdup(temp); sprintf(temp, "%f", value); free(node->str3); node->str3 = strdup(temp); } /* Keep synced with is_number() */ double tintoi(char *str) { char *ptr = str; double values[5] = {0, 0, 0, 0, 0}, m = 1; int i = 1, d = 0; if (*ptr == 0) { return 0; } switch (*ptr) { case '!': case '~': case '+': case '-': ptr++; break; } ptr = str + strlen(str); while (TRUE) { ptr--; switch (*ptr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': values[i] += (*ptr - '0') * m; m *= 10; break; case '.': if (d) { return 0; } d = 1; values[0] = values[1] / m; values[1] = 0; m = 1; break; case ':': switch (i) { case 2: values[i] *= 60; break; case 3: values[i] *= 60 * 60; break; case 4: return 0; } i++; m = 1; break; case '!': case '~': case '+': case '-': if (ptr == str) { break; } return 0; default: return 0; } if (ptr == str) { switch (i) { case 2: values[i] *= 60; break; case 3: values[i] *= 60 * 60; break; case 4: values[i] *= 60 * 60 * 24; break; } break; } } switch (*str) { case '!': return !(values[0] + values[1] + values[2] + values[3] + values[4]); case '~': return ~ (long long) (values[0] + values[1] + values[2] + values[3] + values[4]); case '+': return +(values[0] + values[1] + values[2] + values[3] + values[4]); case '-': return -(values[0] + values[1] + values[2] + values[3] + values[4]); default: return (values[0] + values[1] + values[2] + values[3] + values[4]); } switch (str[0]) { case '!': return !atof(&str[1]); case '~': return (double) ~atoll(&str[1]); case '+': return +atof(&str[1]); case '-': return -atof(&str[1]); default: return atof(str); } while (*ptr) { if (!isdigit((int) *ptr)) { if (*ptr != '.') { return 0; } ptr++; while (*ptr) { if (!isdigit((int) *ptr)) { return 0; } ptr++; } } else { ptr++; } } switch (str[0]) { case '!': return !atof(&str[1]); case '~': return (double) ~atoll(&str[1]); case '+': return +atof(&str[1]); case '-': return -atof(&str[1]); default: return atof(str); } } double tincmp(char *left, char *right) { switch (left[0]) { case '"': return strcmp(left, right); default: return tintoi(left) - tintoi(right); } } double tineval(struct session *ses, char *left, char *right) { switch (left[0]) { case '"': return match(NULL, left, right); default: return tintoi(left) == tintoi(right); } } double tindice(char *left, char *right) { long long cnt, numdice, sizedice, sum; numdice = (long long) tintoi(left); sizedice = (long long) tintoi(right); for (cnt = sum = 0 ; cnt < numdice ; cnt++) { sum += lrand48() % sizedice + 1; } return (double) sum; }