/**
* This is an inheritable to handle certain sorts of expressions in stuff.
* These expression can be setup by players (or creators) and then run
* or evaluated. It allows controlling the types usable by the expression
* handlers, strings, arrays, mappings, integers and floats.
* @author Pinkfish
* @started Thu May 4 22:44:34 PDT 2000
*/
#include <money.h>
#include <expressions.h>
#define EXPRESSION_AND -1
#define EXPRESSION_OR -2
#define EXPRESSION_NOT -3
#define EXPRESSION_FALSE -4
#define EXPRESSION_TRUE -5
#define EXPRESSION_GREATOR_THAN -6
#define EXPRESSION_LESS_THAN -7
#define EXPRESSION_EQUAL_TO -8
#define EXPRESSION_GREATOR_OR_EQUAL -9
#define EXPRESSION_LESS_OR_EQUAL -10
#define EXPRESSION_PLUS -11
#define EXPRESSION_MINUS -12
#define EXPRESSION_MULTIPLY -13
#define EXPRESSION_DIVIDE -14
#define EXPRESSION_IF -15
#define EXPRESSION_NOT_EQUAL_TO -16
#define EXPRESSION_TREE 0
#define EXPRESSION_PARSE_STRING 1
#define EXPRESSION_TYPE 2
#define EXPRESSION_FUNC_NAME 0
#define EXPRESSION_FUNC_NO_ARGS 1
class variable_thing {
int type;
function value;
}
class function_thing {
int type;
function value;
int* arg_types;
}
private mixed* parse_operator(string str, string token);
string query_expression_string(class parse_node* expr, int brief);
class parse_node evaluate_expression(class parse_node* expr, mixed args ...);
mixed query_property(string name);
private nosave mapping _variables;
private nosave mapping _functions;
private nosave string _error_string;
void create() {
_variables = ([ ]);
_functions = ([ ]);
_error_string = "no error";
} /* create() */
/**
* This method returns the last error if there was an error in the parsing.
* @return the last error
*/
string query_last_expression_error() {
return _error_string;
} /* query_last_expression_error() */
/**
* This method adds in an allowed variable and specifies it's type.
* @param name the name of the variable
* @param type the type of the variable
* @param value the value of the variable
*/
void add_allowed_variable(string name, int type, function value) {
class variable_thing bing;
bing = new(class variable_thing);
bing->type = type;
bing->value = value;
_variables[name] = bing;
} /* add_allowed_variable() */
/**
* This method adds in an allowe function and specifies the types it
* takes.
* @param name the name of the function
* @param type the return type of the function
* @param args the types of the arguements (an array)
* @param value the function to call to get the value
*/
void add_allowed_function(string name, int type, int* args, function value) {
class function_thing bing;
bing = new(class function_thing);
bing->type = type;
bing->arg_types = args;
bing->value = value;
_functions[name] = bing;
} /* add_allowed_function() */
/**
* This method returns the type of the variable.
* @param name the name of the variable to check
* @return the type of the variable, EXPRESSION_TYPE_ERROR if there is no
* variable
*/
int query_variable_type(string name) {
if (_variables[name]) {
return _variables[name]->type;
}
return EXPRESSION_TYPE_ERROR;
} /* query_variable_type() */
/**
* This method returns the value of the variable.
* @param name the name of the variable to find
* @return the value of the variable, 0 if not found
*/
function query_variable_value(string name) {
if (_variables[name]) {
return _variables[name]->value;
}
return 0;
} /* query_variable_value() */
/**
* This method returns the type of the function.
* @param name the name of the function to check
* @return the type of the function, EXPRESSION_TYPE_ERROR if there is no
* function
*/
int query_function_type(string name) {
if (_functions[name]) {
return _functions[name]->type;
}
return EXPRESSION_TYPE_ERROR;
} /* query_function_type() */
/**
* This method returns the value of the function.
* @param name the name of the function to find
* @return the value of the function, 0 if not found
*/
function query_function_value(string name) {
if (_functions[name]) {
return _functions[name]->value;
}
return 0;
} /* query_function_value() */
/**
* This method returns the name of the type.
* @param type the type to get the string name of
* @return the string name of the type
*/
string query_type_name(int type) {
switch (type) {
case EXPRESSION_TYPE_INTEGER :
return "integer";
case EXPRESSION_TYPE_STRING :
return "string";
case EXPRESSION_TYPE_ARRAY :
return "array";
case EXPRESSION_TYPE_MAPPING :
return "mapping";
case EXPRESSION_TYPE_FLOAT :
return "float";
case EXPRESSION_TYPE_BOOLEAN :
return "boolean";
case EXPRESSION_TYPE_MONEY :
return "boolean";
default :
return "error";
}
} /* query_type_name() */
/**
* This method returns the string value of the operator name.
* @param operator the operator name to return
* @return the string name of the operator
*/
string query_operator_name(int operator) {
switch (operator) {
case EXPRESSION_AND :
return "and";
case EXPRESSION_OR :
return "or";
case EXPRESSION_NOT :
return "not";
case EXPRESSION_FALSE :
return "false";
case EXPRESSION_TRUE :
return "true";
case EXPRESSION_GREATOR_THAN :
return ">";
case EXPRESSION_LESS_THAN :
return "<";
case EXPRESSION_EQUAL_TO :
return "=";
case EXPRESSION_NOT_EQUAL_TO :
return "<>";
case EXPRESSION_GREATOR_OR_EQUAL :
return ">=";
case EXPRESSION_LESS_OR_EQUAL :
return "<=";
case EXPRESSION_PLUS :
return "+";
case EXPRESSION_MINUS :
return "-";
case EXPRESSION_MULTIPLY :
return "*";
case EXPRESSION_DIVIDE :
return "/";
case EXPRESSION_IF :
return "if";
default :
return "unknown";
}
} /* query_operator_name() */
private class parse_node make_node(int type, mixed value) {
class parse_node bing;
bing = new(class parse_node);
bing->type = type;
bing->value = value;
return bing;
} /* make_parse_node() */
/**
* This method returns a null object of the specified type.
* @param type the type to get the null object of
* @return the null object
*/
class parse_node query_null_type(int type) {
switch (type) {
case EXPRESSION_TYPE_INTEGER :
return make_node(type, 0);
case EXPRESSION_TYPE_MONEY :
return make_node(type, 0);
case EXPRESSION_TYPE_STRING :
return make_node(type, "");
case EXPRESSION_TYPE_ARRAY :
return make_node(type, ({ }));
case EXPRESSION_TYPE_MAPPING :
return make_node(type, ([ ]));
case EXPRESSION_TYPE_FLOAT :
return make_node(type, 0.0);
case EXPRESSION_TYPE_BOOLEAN :
return make_node(EXPRESSION_TYPE_OPERATOR, EXPRESSION_FALSE);
default :
return 0;
}
} /* query_null_type() */
/**
* This method checks to see if the passed in character is an alpha
* or not.
* @param alpha the character to check
* @return 1 if it alpha, 0 if not
*/
protected int is_alpha(int alpha) {
if ((alpha >= 'a' && alpha <= 'z') ||
(alpha >= 'A' && alpha <= 'Z')) {
return 1;
}
return 0;
} /* isalpha() */
/**
* This method checks to see if the passed in character is a number
* or not.
* @param number the character to check
* @return 1 if it is a number, 0 if not
*/
protected int is_number(int number) {
if (number >= '0' && number <= '9') {
return 1;
}
return 0;
} /* is_number() */
/**
* This method checks to see if the passed in character is a space or
* a space equivilant.
* @param space the character to check
* @return 1 if it is a space, 0 if not
*/
protected int is_space(int space) {
if (space == ' ' || space == '\t') {
return 1;
}
return 0;
} /* is_space() */
/**
* This checks to make sure that the type is a number based type.
* This can be controlled to make sure that any added types are also
* allowed to be treated as numbers.
* @param type the type to check
* @return 1 if it is a number, 0 if not
*/
protected int is_number_type(int type) {
return type == EXPRESSION_TYPE_INTEGER ||
type == EXPRESSION_TYPE_FLOAT ||
type == EXPRESSION_TYPE_MONEY;
} /* is_number_type() */
private int is_valid_variable_name(int type) {
return type == '_' ||
is_alpha(type) ||
is_number(type);
} /* is_number_type() */
/**
* This method find the next token. It can be overrideen in higher things
* to deal with special token types (ie: money).
* @param str the input string
* @return ({ token, rest })
*/
protected string* query_token(string str) {
int i = 0;
int j;
while (strlen(str) > 1 && is_space(str[0])) {
str = str[1..];
}
if (!strlen(str)) {
return ({ "", str });
}
// parse money as money... Make them enclose money inside two $'s.
// ie: $4 dollars$
if (str[0] == '$') {
i = 1;
while (strlen(str) > i &&
(str[i] != '$')) {
i++;
}
return ({ str[0..i], str[i+1..] });
}
if (str[0] == '-' || is_number(str[0])) {
i = 0;
while (strlen(str) > i + 1 &&
is_number(str[i + 1])) {
i++;
}
return ({ str[0..i], str[i+1..] });
}
if (is_alpha(str[0]) || str[0] == '_') {
//
// Zip through till we find a non-alpha, non-number.
//
while (strlen(str) > i + 1 &&
is_valid_variable_name(str[i + 1])) {
i++;
}
return ({ str[0..i], str[i+1..] });
}
if (str[0] == '"') {
do {
j = strsrch(str[i + 1..], "\"");
if (j == -1) {
_error_string = "Missing close \"\n";
return 0;
}
i += j + 1;
} while (str[i - 1] == '\\');
return ({ str[0..i], str[i+1..] });
}
if (str[0] == '\'') {
do {
j = strsrch(str[i + 1..], "'");
if (j == -1) {
_error_string = "Missing close '\n";
return 0;
}
i += j + 1;
} while (str[i - 1] == '\\');
return ({ str[0..i], str[i+1..] });
}
while (strlen(str) > i + 1 &&
!is_alpha(str[i + 1]) &&
!is_number(str[i + 1]) &&
!is_space(str[i + 1]) &&
member_array(str[i + 1], ({ '>', '<', '=', '!', '-', '$', '\'',
'"', '_' })) == -1)
{
i++;
}
if (i) {
return ({ str[0..i], str[i+1..] });
}
return ({ str[0..0], str[1..] });
} /* query_token() */
private mixed* parse_node(string str, string token) {
class parse_node num;
int type;
int bing;
int pos;
string place;
string fname;
string* token_ret;
mixed *stuff;
mixed *args;
while (strlen(str) &&
str[0] == ' ') {
str = str[1..];
}
if (_functions[token]) {
fname = token;
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
if (token_ret[0] != "(") {
_error_string = "Expected (, got " + token_ret[0] + " rest: " +
token_ret[1];
return 0;
}
str = token_ret[1];
// Get all the args...
args = ({ });
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
if (token_ret[0] != ")") {
do {
stuff = parse_operator(token_ret[1], token_ret[0]);
if (!stuff) {
return 0;
}
pos = sizeof(args);
if (sizeof(_functions[fname]->arg_types) <= pos) {
_error_string = "To many arguments to " + token + " expected " +
sizeof(_functions[fname]->arg_types);
return 0;
}
if (_functions[fname]->arg_types[pos] != stuff[EXPRESSION_TYPE]) {
_error_string = "Expected arg " + (pos + 1) + " to be " +
query_type_name(_functions[fname]->arg_types[pos]) +
" not " + query_type_name(stuff[EXPRESSION_TYPE]);
return 0;
}
str = stuff[EXPRESSION_PARSE_STRING];
args += stuff[EXPRESSION_TREE];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
if (token_ret[0] == ",") {
token_ret = query_token(token_ret[1]);
}
} while (token_ret[0] != ")");
if (token_ret[0] != ")") {
_error_string = "Expected ')' got " + token_ret[0];
return 0;
}
if (sizeof(args) < sizeof(_functions[fname]->arg_types)) {
_error_string = "To few arguments to " + token + " expected " +
sizeof(_functions[fname]->arg_types);
return 0;
}
return ({ args +
({ make_node(EXPRESSION_TYPE_FUNCTION,
({ fname, sizeof(args) })) }),
token_ret[1],
_functions[fname]->type });
}
}
else switch (token) {
case "true" :
num = make_node(EXPRESSION_TYPE_OPERATOR, EXPRESSION_TRUE);
type = EXPRESSION_TYPE_BOOLEAN;
break;
case "false" :
num = make_node(EXPRESSION_TYPE_OPERATOR, EXPRESSION_FALSE);
type = EXPRESSION_TYPE_BOOLEAN;
break;
case "" :
_error_string = "No token found at: " + str;
return 0;
default :
if (token[0] == '-' || is_number(token[0])) {
sscanf(token, "%d", bing);
num = make_node(EXPRESSION_TYPE_INTEGER, bing);
type = EXPRESSION_TYPE_INTEGER;
} else if (token[0] == '$') {
place = query_property("place");
if (!place) {
place = "default";
}
bing = MONEY_HAND->value_from_string(token[1..<2], place);
if (!bing) {
bing = MONEY_HAND->value_from_string(token[0..<2], place);
}
if (bing) {
num = make_node(EXPRESSION_TYPE_MONEY, bing);
type = EXPRESSION_TYPE_MONEY;
} else {
_error_string = "Money value is invalid: " + token[1..<2] + " and " +
token[0..<2];
return 0;
}
} else if (token[0] == '\"' || token[0] == '\'') {
num = make_node(EXPRESSION_TYPE_STRING, token[1..<2]);
type = EXPRESSION_TYPE_STRING;
} else if (query_variable_type(token)) {
num = make_node(EXPRESSION_TYPE_VARIABLE, token);
type = query_variable_type(token);
}
break;
}
return ({ ({ num }), str, type });
} /* parse_node() */
private mixed* parse_bracket(string str, string token) {
mixed* stuff;
string* token_ret;
switch (token) {
case "(" :
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
stuff = parse_operator(token_ret[1], token_ret[0]);
if (stuff) {
str = stuff[EXPRESSION_PARSE_STRING];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
if (token_ret[0] != ")") {
_error_string = "Could not find closing bracket at " + str;
return 0;
}
str = token_ret[1];
return ({ stuff[EXPRESSION_TREE], str,
stuff[EXPRESSION_TYPE] });
}
break;
default :
stuff = parse_node(str, token);
if (stuff) {
return ({ stuff[EXPRESSION_TREE],
stuff[EXPRESSION_PARSE_STRING],
stuff[EXPRESSION_TYPE] });
}
break;
}
} /* parse_bracket() */
private mixed* parse_plus(string str, string token) {
mixed *stuff;
mixed *stuff2;
int type;
string* token_ret;
string blue;
stuff = parse_bracket(str, token);
if (!stuff) {
return 0;
}
str = stuff[EXPRESSION_PARSE_STRING];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
switch (token_ret[0]) {
case "+" :
type = EXPRESSION_PLUS;
break;
case "-" :
type = EXPRESSION_MINUS;
break;
}
blue = token_ret[0];
if (type) {
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff2 = parse_plus(token_ret[1], token_ret[0]);
if (!stuff2) {
return 0;
}
if ((!is_number_type(stuff[EXPRESSION_TYPE]) ||
!is_number_type(stuff2[EXPRESSION_TYPE])) &&
type != EXPRESSION_PLUS) {
_error_string = "Invalid types to " + blue +
" expected number got: " +
query_type_name(stuff[EXPRESSION_TYPE]) + " and " +
query_type_name(stuff2[EXPRESSION_TYPE]);
return 0;
}
return ({ stuff[EXPRESSION_TREE] + stuff2[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, type) }),
stuff2[EXPRESSION_PARSE_STRING], stuff[EXPRESSION_TYPE] });
} else {
return stuff;
}
} /* parse_plus() */
private mixed* parse_multiply(string str, string token) {
mixed *stuff;
mixed *stuff2;
string* token_ret;
int type;
string blue;
stuff = parse_plus(str, token);
if (!stuff) {
return 0;
}
str = stuff[EXPRESSION_PARSE_STRING];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
switch (token_ret[0]) {
case "*" :
type = EXPRESSION_MULTIPLY;
break;
case "/" :
type = EXPRESSION_DIVIDE;
break;
}
blue = token_ret[0];
if (type) {
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff2 = parse_multiply(token_ret[1], token_ret[0]);
if (!stuff2) {
return 0;
}
if (!is_number_type(stuff[EXPRESSION_TYPE]) ||
!is_number_type(stuff2[EXPRESSION_TYPE])) {
_error_string = "Invalid types to " + blue +
" expected number got: " +
query_type_name(stuff[EXPRESSION_TYPE]) + " and " +
query_type_name(stuff2[EXPRESSION_TYPE]);
return 0;
}
return ({ stuff[EXPRESSION_TREE] + stuff2[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, type) }),
stuff2[EXPRESSION_PARSE_STRING], stuff[EXPRESSION_TYPE] });
} else {
return stuff;
}
} /* parse_multiply() */
private mixed* parse_compare(string str, string token) {
mixed *stuff;
mixed *stuff2;
string* token_ret;
int type;
string blue;
stuff = parse_multiply(str, token);
if (!stuff) {
return 0;
}
str = stuff[EXPRESSION_PARSE_STRING];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
switch (token_ret[0]) {
case ">=" :
type = EXPRESSION_GREATOR_OR_EQUAL;
break;
case "<=" :
type = EXPRESSION_LESS_OR_EQUAL;
break;
case ">" :
type = EXPRESSION_GREATOR_THAN;
break;
case "<" :
type = EXPRESSION_LESS_THAN;
break;
case "<>" :
case "!=" :
type = EXPRESSION_NOT_EQUAL_TO;
break;
case "==" :
case "=" :
type = EXPRESSION_EQUAL_TO;
break;
}
blue = token_ret[0];
if (type) {
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff2 = parse_compare(token_ret[1], token_ret[0]);
if (!stuff2) {
return 0;
}
if ((!is_number_type(stuff[EXPRESSION_TYPE]) ||
!is_number_type(stuff2[EXPRESSION_TYPE])) &&
type != EXPRESSION_EQUAL_TO &&
type != EXPRESSION_NOT_EQUAL_TO)
{
_error_string = "Invalid types to " + blue +
" expected number got: " +
query_type_name(stuff[EXPRESSION_TYPE]) + " and " +
query_type_name(stuff2[EXPRESSION_TYPE]);
return 0;
}
return ({ stuff[EXPRESSION_TREE] + stuff2[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, type) }),
stuff2[EXPRESSION_PARSE_STRING],
EXPRESSION_TYPE_BOOLEAN });
} else {
return stuff;
}
} /* parse_compare() */
private mixed* parse_not(string str, string token) {
mixed *stuff;
if (token == "not") {
stuff = query_token(str);
if (!stuff) {
return 0;
}
stuff = parse_not(stuff[1], stuff[0]);
if (!stuff) {
return 0;
}
if (stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_BOOLEAN) {
_error_string = "Invalid type to not expected boolean got: " +
query_type_name(stuff[EXPRESSION_TYPE]);
return 0;
}
return ({ stuff[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, EXPRESSION_NOT) }),
stuff[EXPRESSION_PARSE_STRING],
EXPRESSION_TYPE_BOOLEAN });
} else {
return parse_compare(str, token);
}
} /* parse_not() */
private mixed* parse_boolean(string str, string token) {
mixed *stuff;
mixed *stuff2;
string* token_ret;
string blue;
int type;
stuff = parse_not(str, token);
if (!stuff) {
return 0;
}
str = stuff[EXPRESSION_PARSE_STRING];
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
switch (token_ret[0]) {
case "and" :
type = EXPRESSION_AND;
break;
case "or" :
type = EXPRESSION_OR;
break;
}
blue = token_ret[0];
if (type) {
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff2 = parse_boolean(token_ret[1], token_ret[0]);
if (!stuff2) {
return 0;
}
if (stuff[EXPRESSION_TYPE] != stuff2[EXPRESSION_TYPE] ||
stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_BOOLEAN) {
_error_string = "Invalid types to " + blue +
" expected boolean got: " +
query_type_name(stuff[EXPRESSION_TYPE]) + " and " +
query_type_name(stuff2[EXPRESSION_TYPE]);
return 0;
}
return ({ stuff[EXPRESSION_TREE] + stuff2[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, type) }),
stuff2[EXPRESSION_PARSE_STRING],
stuff2[EXPRESSION_TYPE] });
} else {
return stuff;
}
} /* parse_boolean() */
private mixed* parse_operator(string str, string token) {
mixed *stuff;
mixed *stuff2;
mixed *stuff3;
string* token_ret;
switch (token) {
case "if" :
token_ret = query_token(str);
if (!token_ret) {
return 0;
}
stuff = parse_operator(token_ret[1], token_ret[0]);
if (!stuff) {
return 0;
}
if (stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_BOOLEAN) {
_error_string = "Invalid type to if expected boolean got: " +
query_type_name(stuff[EXPRESSION_TYPE]);
return 0;
}
token_ret = query_token(stuff[EXPRESSION_PARSE_STRING]);
if (!token_ret) {
return 0;
}
if (token_ret[0] != "then") {
_error_string = "Expected 'then' got " + token_ret[0];
return 0;
}
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff2 = parse_operator(token_ret[1], token_ret[0]);
if (!stuff2) {
return 0;
}
token_ret = query_token(stuff2[EXPRESSION_PARSE_STRING]);
if (!token_ret) {
return 0;
}
if (token_ret[0] == "else") {
token_ret = query_token(token_ret[1]);
if (!token_ret) {
return 0;
}
stuff3 = parse_operator(token_ret[1], token_ret[0]);
if (!stuff3) {
return 0;
}
if (stuff2[EXPRESSION_TYPE] != stuff3[EXPRESSION_TYPE]) {
_error_string = "Types on both sides of an if must match: " +
query_type_name(stuff2[EXPRESSION_TYPE]) + " and " +
query_type_name(stuff3[EXPRESSION_TYPE]);
return 0;
}
token_ret = query_token(stuff3[EXPRESSION_PARSE_STRING]);
if (!token_ret) {
return 0;
}
} else {
stuff3 = ({ ({ query_null_type(stuff2[EXPRESSION_TYPE]) }) });
}
if (token_ret[0] != "endif") {
_error_string = "Expected 'endif' got " + token_ret[0];
return 0;
}
return ({ stuff[EXPRESSION_TREE] +
stuff2[EXPRESSION_TREE] +
stuff3[EXPRESSION_TREE] +
({ make_node(EXPRESSION_TYPE_OPERATOR, EXPRESSION_IF) }),
token_ret[1],
stuff2[EXPRESSION_TYPE] });
break;
default :
stuff = parse_boolean(str, token);
if (!stuff) {
return 0;
}
return stuff;
}
} /* parse_operator() */
/**
* A very small recursive decent parser which must return a boolean
* value.
* @param str the input string
* @return the array control structure
*/
class parse_node* parse_boolean_string(string str) {
mixed* stuff;
string* token_ret;
token_ret = query_token(lower_case(str));
if (!token_ret) {
return 0;
}
stuff = parse_operator(token_ret[1], token_ret[0]);
if (!stuff) {
return ({ });
}
if (strlen(stuff[EXPRESSION_PARSE_STRING])) {
_error_string = "Unable to parse the rest of: " +
stuff[EXPRESSION_PARSE_STRING];
return ({ });
}
if (stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_BOOLEAN) {
_error_string = "Invalid return type, expected boolean got " +
query_type_name(stuff[EXPRESSION_TYPE]);
return ({ });
}
return stuff[EXPRESSION_TREE];
} /* parse_boolean_string() */
/**
* A very small recursive decent parser which must return a integer
* value.
* @param str the input string
* @return the array control structure
*/
class parse_node* parse_integer_string(string str) {
mixed* stuff;
string* token_ret;
token_ret = query_token(lower_case(str));
if (!token_ret) {
return 0;
}
stuff = parse_operator(token_ret[1], token_ret[0]);
if (!stuff) {
//printf("%O", _error_string);
return ({ });
}
if (strlen(stuff[EXPRESSION_PARSE_STRING])) {
_error_string = "Unable to parse the rest of: " +
stuff[EXPRESSION_PARSE_STRING];
//printf("%O", _error_string);
return ({ });
}
if (stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_INTEGER) {
_error_string = "Invalid return type, expected integer got " +
query_type_name(stuff[EXPRESSION_TYPE]);
//printf("%O", query_expression_string(stuff[EXPRESSION_TREE], 0));
//printf("%O", evaluate_expression(stuff[EXPRESSION_TREE]));
return ({ });
}
//printf("%O", query_expression_string(stuff[EXPRESSION_TREE], 0));
//printf("%O", evaluate_expression(stuff[EXPRESSION_TREE]));
return stuff[EXPRESSION_TREE];
} /* parse_integer_string() */
/**
* A very small recursive decent parser which must return a money
* value.
* @param str the input string
* @return the array control structure
*/
class parse_node* parse_money_string(string str) {
mixed* stuff;
string* token_ret;
token_ret = query_token(lower_case(str));
if (!token_ret) {
return 0;
}
stuff = parse_operator(token_ret[1], token_ret[0]);
if (!stuff) {
//printf("%O", _error_string);
return ({ });
}
if (strlen(stuff[EXPRESSION_PARSE_STRING])) {
_error_string = "Unable to parse the rest of: " +
stuff[EXPRESSION_PARSE_STRING];
//printf("%O", _error_string);
return ({ });
}
if (stuff[EXPRESSION_TYPE] != EXPRESSION_TYPE_MONEY) {
_error_string = "Invalid return type, expected money got " +
query_type_name(stuff[EXPRESSION_TYPE]);
//printf("%O", query_expression_string(stuff[EXPRESSION_TREE], 0));
//printf("%O", evaluate_expression(stuff[EXPRESSION_TREE]));
return ({ });
}
//printf("%O", stuff[EXPRESSION_TREE]);
//printf("%O", query_expression_string(stuff[EXPRESSION_TREE], 0));
//printf("%O", evaluate_expression(stuff[EXPRESSION_TREE]));
return stuff[EXPRESSION_TREE];
} /* parse_integer_string() */
/**
* This method returns the expresion as a string.
* @param expr the expression
* @param brief don't expand the variable names
* @return the expression as a string
*/
string query_expression_string(class parse_node* expr, int brief) {
string str;
int i;
string* stack;
int* thing;
string tmp;
string place;
int value;
string indent;
if (!sizeof(expr)) {
return "No expression set.";
}
place = query_property("place");
if (!place) {
place = "default";
}
indent = "";
str = "";
stack = ({ });
thing = ({ });
for (i = 0; i < sizeof(expr); i++) {
if (!classp(expr[i])) {
stack += ({ "Broken element" });
thing += ({ 0 });
continue;
}
switch (expr[i]->type) {
case EXPRESSION_TYPE_OPERATOR :
value = expr[i]->value;
switch (expr[i]->value) {
case EXPRESSION_NOT :
str = " " + query_operator_name(expr[i]->value) + " ";
if (thing[<1] && thing[<1] != expr[i]->value) {
str += "(" + stack[<1] + ")";
} else {
str += stack[<1];
}
stack = stack[0..<2];
thing = thing[0..<2];
break;
case EXPRESSION_AND :
case EXPRESSION_OR :
case EXPRESSION_PLUS :
case EXPRESSION_MINUS :
case EXPRESSION_DIVIDE :
case EXPRESSION_MULTIPLY :
case EXPRESSION_GREATOR_THAN :
case EXPRESSION_LESS_THAN :
case EXPRESSION_EQUAL_TO :
case EXPRESSION_GREATOR_OR_EQUAL :
case EXPRESSION_LESS_OR_EQUAL :
case EXPRESSION_NOT_EQUAL_TO :
tmp = stack[<2];
if (thing[<2] && thing[<2] != expr[i]->value) {
str = "(" + stack[<2] + ")";
} else {
str = stack[<2];
}
str += " " + query_operator_name(expr[i]->value) + " ";
if (thing[<1] && thing[<1] != expr[i]->value) {
str += "(" + stack[<1] + ")";
} else {
str += stack[<1];
}
stack = stack[0..<3];
thing = thing[0..<3];
break;
case EXPRESSION_TRUE :
str = "true";
value = 0;
break;
case EXPRESSION_FALSE :
str = "false";
value = 0;
break;
case EXPRESSION_IF :
if (brief) {
str = "if " + stack[<3] + " then " + stack[<2] + " else " +
stack[<1] + " endif";
} else {
str = "if " + stack[<3] + " then\n" +
" " + replace_string(stack[<2], "\n", "\n ") +
"\nelse\n" +
" " + replace_string(stack[<1], "\n", "\n ") +
"\nendif";
}
stack = stack[0..<4];
thing = thing[0..<4];
value = 0;
break;
default :
str = "Error!";
break;
}
stack += ({ str });
thing += ({ value });
break;
case EXPRESSION_TYPE_MONEY :
stack += ({ MONEY_HAND->money_value_string(expr[i]->value, place) });
thing += ({ 0 });
break;
case EXPRESSION_TYPE_INTEGER :
stack += ({ "" + expr[i]->value });
thing += ({ 0 });
break;
case EXPRESSION_TYPE_STRING :
stack += ({ "\"" + expr[i]->value + "\"" });
thing += ({ 0 });
break;
case EXPRESSION_TYPE_VARIABLE :
stack += ({ expr[i]->value });
thing += ({ 0 });
break;
case EXPRESSION_TYPE_FUNCTION :
str = expr[i]->value[EXPRESSION_FUNC_NAME] + "(";
str += implode(stack[<expr[i]->value[EXPRESSION_FUNC_NO_ARGS]..],
", ");
str += ")";
stack = stack[0..<expr[i]->value[EXPRESSION_FUNC_NO_ARGS] + 1];
thing = thing[0..<expr[i]->value[EXPRESSION_FUNC_NO_ARGS] + 1];
stack += ({ str });
thing += ({ 0 });
break;
default :
stack += ({ "Unknown: " + expr[i]->type + " (" + expr[i]->value + ")" });
thing += ({ 0 });
break;
}
}
return stack[<1];
} /* query_expression_string() */
/**
* This method evaluates the expression and creates a nice end result
* thingy.
* @param expr the exrpession to evaluate
* @param args the optional args parsed into the various function calls
* @return the type and value of the expression, 0 if failed
*/
class parse_node evaluate_expression(class parse_node* expr, mixed args ...) {
class parse_node bing;
class parse_node new_node;
class parse_node* stack;
string fname;
mixed* fargs;
if (!sizeof(expr)) {
return make_node(EXPRESSION_TYPE_BOOLEAN, 0);
}
stack = ({ });
foreach (bing in expr) {
if (!classp(bing)) {
continue;
}
switch (bing->type) {
case EXPRESSION_TYPE_OPERATOR :
switch (bing->value) {
case EXPRESSION_NOT :
stack[<1] = make_node(EXPRESSION_TYPE_BOOLEAN, !stack[<1]->value);
break;
case EXPRESSION_AND :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<1]->value &&
stack[<2]->value);
stack = stack[0..<2];
break;
case EXPRESSION_OR :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<1]->value ||
stack[<2]->value);
stack = stack[0..<2];
break;
case EXPRESSION_PLUS :
stack[<2] = make_node(stack[<1]->type, stack[<2]->value +
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_MINUS :
stack[<2] = make_node(stack[<1]->type, stack[<2]->value -
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_DIVIDE :
stack[<2] = make_node(stack[<1]->type, stack[<2]->value /
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_MULTIPLY :
stack[<2] = make_node(stack[<1]->type, stack[<2]->value *
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_GREATOR_THAN :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value >
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_LESS_THAN :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value <
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_EQUAL_TO :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value ==
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_GREATOR_OR_EQUAL :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value >=
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_LESS_OR_EQUAL :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value <=
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_NOT_EQUAL_TO :
stack[<2] = make_node(EXPRESSION_TYPE_BOOLEAN, stack[<2]->value !=
stack[<1]->value);
stack = stack[0..<2];
break;
case EXPRESSION_IF :
if (stack[<3]->value) {
new_node = stack[<2];
} else {
new_node = stack[<1];
}
stack = stack[0..<4] + ({ new_node });
break;
case EXPRESSION_TRUE :
stack += ({ make_node(EXPRESSION_TYPE_BOOLEAN, 1) });
break;
case EXPRESSION_FALSE :
stack += ({ make_node(EXPRESSION_TYPE_BOOLEAN, 0) });
break;
default :
printf("Unknown operator %O\n", bing);
break;
}
break;
case EXPRESSION_TYPE_INTEGER :
stack += ({ copy(bing) });
break;
case EXPRESSION_TYPE_STRING :
stack += ({ copy(bing) });
break;
case EXPRESSION_TYPE_MONEY :
stack += ({ copy(bing) });
break;
case EXPRESSION_TYPE_VARIABLE :
stack += ({ make_node(query_variable_type(bing->value),
evaluate(query_variable_value(bing->value),
args ...)) });
break;
case EXPRESSION_TYPE_FUNCTION :
fname = bing->value[EXPRESSION_FUNC_NAME];
fargs = stack[<bing->value[EXPRESSION_FUNC_NO_ARGS]..];
stack = stack[0..<bing->value[EXPRESSION_FUNC_NO_ARGS] + 1];
stack += ({ make_node(query_function_type(fname),
evaluate(query_function_value(fname),
map(fargs, (: $1->value :)) +
args ...)) });
break;
default :
printf("Unknown type %O\n", bing);
break;
}
//printf("%O: %O\n", bing, stack);
}
return stack[<1];
} /* evaluate_expression() */