//***************************************************************************** // // parse.h // // Player commands usually each take their own strict format. For instance, // the give command takes the format give <player> <object>. However, having to // parse out these arguments by hand in each command is a bit of a hassle and // something we can probably cut out most of the legwork for. That's what the // functions in this header are designed to do. // //***************************************************************************** #include "mud.h" #include "utils.h" #include "handler.h" #include "inform.h" #include "parse.h" // mandatory modules #include "scripts/scripts.h" #include "scripts/pyexit.h" //***************************************************************************** // local datastructures, variables, and defines //***************************************************************************** #define PARSE_TOKEN_CHAR 0 #define PARSE_TOKEN_OBJ 1 #define PARSE_TOKEN_ROOM 2 #define PARSE_TOKEN_EXIT 3 #define PARSE_TOKEN_MULTI 4 #define PARSE_TOKEN_FLAVOR 5 #define PARSE_TOKEN_WORD 6 #define PARSE_TOKEN_DOUBLE 7 #define PARSE_TOKEN_INT 8 #define PARSE_TOKEN_BOOL 9 #define PARSE_TOKEN_OPTIONAL 10 #define PARSE_TOKEN_STRING 11 #define PARSE_VAR_BOOL 0 #define PARSE_VAR_INT 1 #define PARSE_VAR_DOUBLE 2 #define PARSE_VAR_CHAR 4 #define PARSE_VAR_OBJ 5 #define PARSE_VAR_ROOM 6 #define PARSE_VAR_EXIT 7 #define PARSE_VAR_STRING 8 // // data for one format argument typedef struct { int type; // ch, obj, room, string, int, double, etc...? bitvector_t scope; // what scope arguments are supplied to find? bool all_ok; // is all. syntax allowed? bool self_ok; // if we're looking for a char, can we find ourself? LIST *token_list; // if we're a multi-type, a list of our possible format char *flavor; // if we're a flavor type, the string we accept bool flavor_optional; // pretty self-explanatory :) } PARSE_TOKEN; // // create a new parse token of the specified type PARSE_TOKEN *newParseToken(int type) { PARSE_TOKEN *token = calloc(1, sizeof(PARSE_TOKEN)); token->type = type; if(type == PARSE_TOKEN_MULTI) token->token_list = newList(); else if(type == PARSE_TOKEN_OBJ) SET_BIT(token->scope, FIND_SCOPE_VISIBLE); else if(type == PARSE_TOKEN_EXIT) { SET_BIT(token->scope, FIND_SCOPE_VISIBLE); SET_BIT(token->scope, FIND_SCOPE_ROOM); } else if(type == PARSE_TOKEN_CHAR) { SET_BIT(token->scope, FIND_SCOPE_VISIBLE); token->self_ok = TRUE; } return token; } // // free a token from memory void deleteParseToken(PARSE_TOKEN *arg) { if(arg->token_list) deleteListWith(arg->token_list, deleteParseToken); if(arg->flavor) free(arg->flavor); free(arg); } // // data for one parsed variable typedef struct { int type; // the type we parsed out bool bool_val; // bool value int int_val; // integer value double dbl_val; // double value void *ptr_val; // pointer value (room, ch, obj, exit) int disambiguated_type; // our parse type (in parse.h) if we were parsed // from a set of multiple possible types bool multiple_possible; // was it possible to parse multiple variables bool multiple; // were multiple things parsed } PARSE_VAR; // // create a new parse var of the specified type PARSE_VAR *newParseVar(int type) { PARSE_VAR *var = calloc(1, sizeof(PARSE_VAR)); var->disambiguated_type = PARSE_NONE; var->type = type; return var; } // // delete a parse var void deleteParseVar(PARSE_VAR *var) { free(var); } //***************************************************************************** // local functions //***************************************************************************** // // makes a char token, and parses options. Returns NULL if there's bad options PARSE_TOKEN *parse_token_char(const char *options) { // make it PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_CHAR); // search for options while(*options != '\0') { if(!strncasecmp(options, ".room", 5)) { options = options + 5; SET_BIT(token->scope, FIND_SCOPE_ROOM); } else if(!strncasecmp(options, ".world", 6)) { options = options + 6; SET_BIT(token->scope, FIND_SCOPE_WORLD); } else if(!strncasecmp(options, ".multiple", 9)) { options = options + 9; token->all_ok = TRUE; } else if(!strncasecmp(options, ".noself", 7)) { options = options + 7; token->self_ok = FALSE; } else if(!strncasecmp(options, ".invis_ok", 9)) { options = options + 9; REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE); } // didn't recognize the option else { deleteParseToken(token); token = NULL; break; } } // if we successfully parsed the token, make sure there's a scope to look in if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM|FIND_SCOPE_WORLD)) { deleteParseToken(token); token = NULL; } return token; } // // makes an obj token, and parses options. Returns NULL if there's bad options PARSE_TOKEN *parse_token_obj(const char *options) { // make it PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_OBJ); // search for options while(*options != '\0') { if(!strncasecmp(options, ".room", 5)) { options = options + 5; SET_BIT(token->scope, FIND_SCOPE_ROOM); } else if(!strncasecmp(options, ".world", 6)) { options = options + 6; SET_BIT(token->scope, FIND_SCOPE_WORLD); } else if(!strncasecmp(options, ".inv", 4)) { options = options + 4; SET_BIT(token->scope, FIND_SCOPE_INV); } else if(!strncasecmp(options, ".eq", 3)) { options = options + 3; SET_BIT(token->scope, FIND_SCOPE_WORN); } else if(!strncasecmp(options, ".multiple", 9)) { options = options + 9; token->all_ok = TRUE; } else if(!strncasecmp(options, ".invis_ok", 9)) { options = options + 9; REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE); } // didn't recognize the option else { deleteParseToken(token); token = NULL; break; } } // if we successfully parsed the token, make sure there's a scope to look in if(token != NULL && !IS_SET(token->scope, FIND_SCOPE_ROOM | FIND_SCOPE_WORLD | FIND_SCOPE_INV | FIND_SCOPE_WORN)){ deleteParseToken(token); token = NULL; } return token; } // // makes an exit token, and parses options. Returns NULL if there's bad options PARSE_TOKEN *parse_token_exit(const char *options) { // make it PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_EXIT); // search for options while(*options != '\0') { if(!strncasecmp(options, ".multiple", 9)) { options = options + 9; token->all_ok = TRUE; } else if(!strncasecmp(options, ".invis_ok", 9)) { options = options + 9; REMOVE_BIT(token->scope, FIND_SCOPE_VISIBLE); } // didn't recognize the option else { deleteParseToken(token); token = NULL; break; } } return token; } // // parses one actual thing (int, double, bool, string, ch, obj, room) marker. // Does not understand { }, ( ), [ ], |, or *. Assumes no leading whitespaces. PARSE_TOKEN *parse_one_datatype(const char **format) { char buf[SMALL_BUFFER]; const char *fmt = *format; PARSE_TOKEN *token = NULL; int i = 0; // copy what we're trying to parse for(i = 0; isalpha(*fmt) || *fmt == '_' || *fmt == '.'; i++, fmt++) buf[i] = *fmt; buf[i] = '\0'; // figure out our type if(!strcasecmp(buf, "int")) token = newParseToken(PARSE_TOKEN_INT); else if(!strcasecmp(buf, "double")) token = newParseToken(PARSE_TOKEN_DOUBLE); else if(!strcasecmp(buf, "bool")) token = newParseToken(PARSE_TOKEN_BOOL); else if(!strcasecmp(buf, "word")) token = newParseToken(PARSE_TOKEN_WORD); else if(!strcasecmp(buf, "string")) token = newParseToken(PARSE_TOKEN_STRING); else if(!strcasecmp(buf, "room")) token = newParseToken(PARSE_TOKEN_ROOM); // exits, chars, and objs can have arguments tagged to the end of them. For // exits, it is optional. For both chars and objs, it is neccessary to specify // at least the scope of search (e.g. ch.room, obj.inv, obj.room.inv) else if(!strcasecmp(buf, "exit") || !strncasecmp(buf, "exit.", 5)) token = parse_token_exit(buf+4); else if(!strncasecmp(buf, "ch.", 3)) token = parse_token_char(buf+2); else if(!strncasecmp(buf, "obj.", 4)) token = parse_token_obj(buf+3); // up the location of format if we successfully parsed something if(token != NULL) *format = fmt; // return whatever we found return token; } // // parses a multi token assumes no leading whitespace PARSE_TOKEN *parse_multi_token(const char **format) { const char *fmt = *format; bool close_found = FALSE; // skip our opening { fmt++; // skip any leading whitespace while(isspace(*fmt)) fmt++; // make our multi-token PARSE_TOKEN *multi_token = newParseToken(PARSE_TOKEN_MULTI); // parse all of our tokens and add them to the multi-list while(*fmt != '\0' && !close_found) { PARSE_TOKEN *one_token = parse_one_datatype(&fmt); // did we parse the token alright? if(one_token != NULL) { listQueue(multi_token->token_list, one_token); // up the positioning of format *format = fmt; } // syntactic error. break out! else break; // skip any whitespace while(isspace(*fmt)) fmt++; // have we encountered the closing bracket? if(*fmt == '}') close_found = TRUE; } // if we never found a close, or we didn't parse any arguments, // delete the token because it was not finished if(close_found == FALSE || listSize(multi_token->token_list) == 0) { deleteParseToken(multi_token); multi_token = NULL; } // otherwise, skip the closing bracket and up the position of format else { fmt++; *format = fmt; } return multi_token; } // // parses a flavor token PARSE_TOKEN *parse_flavor_token(const char **format, bool optional) { // make it char buf[SMALL_BUFFER]; PARSE_TOKEN *token = newParseToken(PARSE_TOKEN_FLAVOR); token->flavor_optional = optional; char open_marker = (optional ? '[' : '<'); char close_marker = (optional ? ']' : '>'); const char *fmt = *format; int i, open_count = 1; // skip the opening marker fmt++; // copy the flavor over to our buffer for(i = 0; *fmt != '\0'; i++, fmt++) { // did we encounter an open marker? if so, up our open count if(*fmt == open_marker) open_count++; // did we encounter a close marker? if so, lower our open count else if(*fmt == close_marker) open_count--; // did we just close everything off? if so, break out and skip last token if(open_count == 0) { fmt++; break; } buf[i] = *fmt; } buf[i] = '\0'; // make sure we closed everything off if(open_count > 0) { deleteParseToken(token); token = NULL; } // move our format up and copy over the flavor string else { token->flavor = strdup(buf); *format = fmt; } return token; } // // parse an optional marker PARSE_TOKEN *parse_optional_token(const char **format) { // skip over the optional marker (*format)++; return newParseToken(PARSE_TOKEN_OPTIONAL); } // // parses out one argument. Returns NULL if an error was encountered. If there // was no error, ups the location of fmt to the end of the last argument parsed PARSE_TOKEN *parse_one_token(const char **format) { const char *fmt = *format; PARSE_TOKEN *token = NULL; // skip whitespaces while(isspace(*fmt)) fmt++; // are we trying to parse multiple arguments? if(*fmt == '{') token = parse_multi_token(&fmt); // optional flavor format else if(*fmt == '[') token = parse_flavor_token(&fmt, TRUE); // mandatory flavor format else if(*fmt == '<') token = parse_flavor_token(&fmt, FALSE); // optional marker else if(*fmt == '|') token = parse_optional_token(&fmt); // a datatype token else token = parse_one_datatype(&fmt); // up the location of our format *format = fmt; return token; } // // breaks up a format string into its parts and returns a list of them LIST *decompose_parse_format(const char *format) { const char *fmt = format; bool error = FALSE; LIST *token_list = newList(); // try to parse all of our format down into tokens while(*fmt != '\0' && !error) { PARSE_TOKEN *token = parse_one_token(&fmt); // did the token parse OK? if(token != NULL) listQueue(token_list, token); else error = TRUE; } // did we encounter an error? if(error == TRUE) { deleteListWith(token_list, deleteParseToken); token_list = NULL; } return token_list; } // // turns a string into a boolean bool string_to_bool(const char *string) { if(!strcasecmp(string, "yes") || !strcasecmp(string, "true")) return TRUE; return FALSE; } // // returns whether or not there is a boolean value available at the // head of the string. bool string_is_bool(const char *string) { return (!strcasecmp(string, "yes") || !strcasecmp(string, "true") || !strcasecmp(string, "no") || !strcasecmp(string, "false")); } // // returns whether or not the string is a double value bool string_is_double(const char *string) { bool deci_found = FALSE; int len = 0; // check if it's negative if(*string == '-') string++; // it's a double if we only encounter digits and a single dicimal for(; *string != '\0'; string++, len++) { if(isdigit(*string)) continue; // only allowed decimals, and only one of them... else if(*string != '.' || deci_found == TRUE) return FALSE; deci_found = TRUE; } // found no problems, and actually found _something_ return (len > 0); } // // returns whether or not the string is an integer bool string_is_int(const char *string) { int len = 0; // check if it's a negative if(*string == '-') string++; // it's an int if all we encounter is digits for(; *string != '\0'; string++, len++) if(!isdigit(*string)) return FALSE; // found no problems, and actually found _something_ return (len > 0); } // // tries to make an int parse var PARSE_VAR *use_one_parse_token_int(const char *buf) { PARSE_VAR *var = NULL; if(string_is_int(buf)) { var = newParseVar(PARSE_VAR_INT); var->int_val = atoi(buf); } return var; } // // tries to make a double parse var PARSE_VAR *use_one_parse_token_double(const char *buf) { PARSE_VAR *var = NULL; if(string_is_double(buf)) { var = newParseVar(PARSE_VAR_DOUBLE); var->dbl_val = atof(buf); } return var; } // // tries to make a boolean parse var PARSE_VAR *use_one_parse_token_bool(const char *buf) { PARSE_VAR *var = NULL; if(string_is_bool(buf)) { var = newParseVar(PARSE_VAR_BOOL); var->bool_val = string_to_bool(buf); } return var; } // // tries to make a char parse var PARSE_VAR *use_one_parse_token_char(CHAR_DATA *looker, PARSE_TOKEN *tok, const char *name) { int type = FOUND_NONE; void *found = generic_find(looker, name, FIND_TYPE_CHAR, tok->scope, tok->all_ok, &type); // make sure we found something... if(found == NULL) return NULL; else { PARSE_VAR *var = newParseVar(PARSE_VAR_CHAR); // if multiple vals were possible, flag it var->multiple_possible = tok->all_ok; // make sure it's not us if that's not allowed if(type == FOUND_CHAR) { if(tok->self_ok || looker != found) var->ptr_val = found; else { deleteParseVar(var); var = NULL; } } // if we got a list, make sure we remove ourself as neccessary else if(type == FOUND_LIST) { if(!tok->self_ok) listRemove(found, looker); // make sure we're not empty... if(listSize(found) > 1) { var->ptr_val = found; var->multiple = TRUE; } else if(listSize(found) == 1) { var->ptr_val = listPop(found); deleteList(found); } else { deleteList(found); deleteParseVar(var); var = NULL; } } // this shouldn't happen... else { deleteParseVar(var); var = NULL; } // return what we found, if anything return var; } } // // tries to make an obj parse var PARSE_VAR *use_one_parse_token_obj(CHAR_DATA *looker, PARSE_TOKEN *tok, const char *name) { int type = FOUND_NONE; void *found = generic_find(looker, name, FIND_TYPE_OBJ, tok->scope, tok->all_ok, &type); // make sure we found something if(found == NULL) return NULL; else { PARSE_VAR *var = newParseVar(PARSE_VAR_OBJ); // if multiple vals were possible, flag it var->multiple_possible = tok->all_ok; // Is it a single item? if(type == FOUND_OBJ) var->ptr_val = found; // or is it multiple items? else if(type == FOUND_LIST) { if(listSize(found) > 1) { var->ptr_val = found; var->multiple = TRUE; } else if(listSize(found) == 1) { var->ptr_val = listPop(found); deleteList(found); } else { deleteList(found); deleteParseVar(var); var = NULL; } } // We should never reach this case else { deleteParseVar(var); var = NULL; } // return whatever we found return var; } } // // tries to make an exit parse var PARSE_VAR *use_one_parse_token_exit(CHAR_DATA *looker, PARSE_TOKEN *tok, const char *name) { int type = FOUND_NONE; void *found = generic_find(looker, name, FIND_TYPE_EXIT, tok->scope, tok->all_ok, &type); // make sure we found something if(found == NULL) return NULL; else { PARSE_VAR *var = newParseVar(PARSE_VAR_EXIT); // if multiple vals were possible, flag it var->multiple_possible = tok->all_ok; // Is it a single exit? if(type == FOUND_EXIT) var->ptr_val = found; // or is it multiple exits? else if(type == FOUND_LIST) { if(listSize(found) > 1) { var->ptr_val = found; var->multiple = TRUE; } else if(listSize(found) == 1) { var->ptr_val = listPop(found); deleteList(found); } else { deleteList(found); deleteParseVar(var); var = NULL; } } // We should never reach this case else { deleteParseVar(var); var = NULL; } // return whatever we found return var; } } // // tries to make a room parse var PARSE_VAR *use_one_parse_token_room(CHAR_DATA *looker, PARSE_TOKEN *tok, const char *name) { ROOM_DATA *room = generic_find(looker, name, FIND_TYPE_ROOM, FIND_SCOPE_WORLD, FALSE, NULL); // did we find something? if(room == NULL) return NULL; else { PARSE_VAR *var = newParseVar(PARSE_VAR_ROOM); var->ptr_val = room; return var; } } // // Tries to apply a token to args, to parse something out. Returns a PARSE_VAR // if the token's usage resulted in the creation of a variable. If an error was // encountered, make this apparent by setting the value of the variable at error PARSE_VAR *use_one_parse_token(CHAR_DATA *looker, PARSE_TOKEN *tok, char **args, bool *error, char *err_buf) { char buf[SMALL_BUFFER]; PARSE_VAR *var = NULL; char *arg = *args; // skip over any leading spaces we might have while(isspace(*arg)) arg++; switch(tok->type) { case PARSE_TOKEN_MULTI: { // make a proxy error value and error buf so we don't accidentally fill // these up when it turns out we find something on a var past the first bool multi_err = FALSE; char multi_err_buf[SMALL_BUFFER] = ""; // go through all of our possible types until we find something LIST_ITERATOR *multi_i = newListIterator(tok->token_list); PARSE_TOKEN *mtok = NULL; bool multiple_possible = FALSE; ITERATE_LIST(mtok, multi_i) { if(mtok->all_ok) multiple_possible = TRUE; var = use_one_parse_token(looker, mtok, &arg, &multi_err, multi_err_buf); // reset our error value for the next pass at it... if(var == NULL) multi_err = FALSE; // found something! Disambiguate the type else { switch(mtok->type) { case PARSE_TOKEN_CHAR: var->disambiguated_type = PARSE_CHAR; break; case PARSE_TOKEN_ROOM: var->disambiguated_type = PARSE_ROOM; break; case PARSE_TOKEN_EXIT: var->disambiguated_type = PARSE_EXIT; break; case PARSE_TOKEN_OBJ: var->disambiguated_type = PARSE_OBJ; break; case PARSE_TOKEN_WORD: var->disambiguated_type = PARSE_STRING; break; case PARSE_TOKEN_STRING: var->disambiguated_type = PARSE_STRING; break; case PARSE_TOKEN_INT: var->disambiguated_type = PARSE_INT; break; case PARSE_TOKEN_DOUBLE: var->disambiguated_type = PARSE_DOUBLE; break; case PARSE_TOKEN_BOOL: var->disambiguated_type = PARSE_BOOL; break; } // break out of the loop... we found something break; } } deleteListIterator(multi_i); // did we manage not to find something? if(var != NULL) var->multiple_possible = multiple_possible; else { one_arg(arg, buf); // get the first arg, for reporting... sprintf(err_buf, "Your argument '%s' was invalid or could not be found.", buf); *error = TRUE; } break; } case PARSE_TOKEN_FLAVOR: { int len = strlen(tok->flavor); // have we found the flavor text? if(strncasecmp(tok->flavor, arg, len) == 0 && (arg[len] == '\0' || isspace(arg[len]))) arg = arg + len; // do we need to do something about it? else if(!tok->flavor_optional) *error = TRUE; break; } // parse out a char value case PARSE_TOKEN_CHAR: arg = one_arg(arg, buf); var = use_one_parse_token_char(looker, tok, buf); if(var == NULL) { sprintf(err_buf, "Could not find person, '%s'.", buf); *error = TRUE; } break; // parse out an obj value case PARSE_TOKEN_OBJ: arg = one_arg(arg, buf); var = use_one_parse_token_obj(looker, tok, buf); if(var == NULL) { sprintf(err_buf, "Could not find object, '%s'.", buf); *error = TRUE; } break; // parse out a room value case PARSE_TOKEN_ROOM: arg = one_arg(arg, buf); var = use_one_parse_token_room(looker, tok, buf); if(var == NULL) { sprintf(err_buf, "Could not find room, '%s'.", buf); *error = TRUE; } break; // parse out an exit value case PARSE_TOKEN_EXIT: arg = one_arg(arg, buf); var = use_one_parse_token_exit(looker, tok, buf); if(var == NULL) { sprintf(err_buf, "Could not find door or direction named '%s'.", buf); *error = TRUE; } break; // try to parse out a double value case PARSE_TOKEN_DOUBLE: arg = one_arg(arg, buf); var = use_one_parse_token_double(buf); if(var == NULL) { sprintf(err_buf, "'%s' is not a decimal value.", buf); *error = TRUE; } break; // try to parse out an integer value case PARSE_TOKEN_INT: arg = one_arg(arg, buf); var = use_one_parse_token_int(buf); if(var == NULL) { sprintf(err_buf, "'%s' is not a%s number.", buf, (string_is_double(buf) ? "n acceptable" : "")); *error = TRUE; } break; // try to parse out a boolean value case PARSE_TOKEN_BOOL: arg = one_arg(arg, buf); var = use_one_parse_token_bool(buf); if(var == NULL) { sprintf(err_buf, "'%s' is not a yes/no value.", buf); *error = TRUE; } break; // parse out a single word case PARSE_TOKEN_WORD: { var = newParseVar(PARSE_VAR_STRING); var->ptr_val = arg; bool multi_word = FALSE; // are we using quotation marks to specify multiple words? if(*arg == '\"') { multi_word = TRUE; arg++; var->ptr_val = arg; } // go through arg to the next space, and delimit the word for(; *arg != '\0'; arg++) { if((multi_word && *arg == '\"') || (!multi_word && isspace(*arg))) { *arg = '\0'; arg++; break; } } break; } // copies whatever is left case PARSE_TOKEN_STRING: var = newParseVar(PARSE_VAR_STRING); var->ptr_val = arg; // skip up the place of the arg... while(*arg != '\0') arg++; break; // since this doesn't really parse a value... case PARSE_TOKEN_OPTIONAL: break; } // up the placement of our arg if we didn't encounter an error if(!*error) *args = arg; return var; } // // gets the name of the type for printing in syntax error messages const char *get_datatype_format_error_mssg(PARSE_TOKEN *tok) { switch(tok->type) { case PARSE_TOKEN_CHAR: return "person"; case PARSE_TOKEN_ROOM: return "room"; case PARSE_TOKEN_EXIT: return "direction"; case PARSE_TOKEN_OBJ: return "object"; case PARSE_TOKEN_INT: return "number"; case PARSE_TOKEN_DOUBLE: return "decimal"; case PARSE_TOKEN_BOOL: return "yes/no"; case PARSE_TOKEN_WORD: return "word"; case PARSE_TOKEN_STRING: return "text"; default: return "UNKNOWN_TYPE"; } } // // Takes a list of tokens, and builds the proper syntax for the command and // then sends it to the character. void show_parse_syntax_error(CHAR_DATA *ch, const char *cmd, LIST *tokens) { BUFFER *buf = newBuffer(1); LIST_ITERATOR *tok_i = newListIterator(tokens); PARSE_TOKEN *tok = NULL; bool optional_found = FALSE; int count = 0; // go through all of our tokens, and append their syntax to the buf ITERATE_LIST(tok, tok_i) { // make sure we add a space before anything else... if(count > 0) bprintf(buf, " "); count++; // have we encountered the "optional" marker? if so, switch our open/close if(tok->type == PARSE_TOKEN_OPTIONAL) { bprintf(buf, "["); optional_found = TRUE; // we don't want to put a space right after this [ count = 0; continue; } // append our message switch(tok->type) { case PARSE_TOKEN_MULTI: { bprintf(buf, "<"); LIST_ITERATOR *multi_i = newListIterator(tok->token_list); PARSE_TOKEN *mtok = NULL; int m_count = 0; ITERATE_LIST(mtok, multi_i) { if(m_count > 0) bprintf(buf, ", "); m_count++; bprintf(buf, "%s", get_datatype_format_error_mssg(mtok)); } deleteListIterator(multi_i); bprintf(buf, ">"); break; } case PARSE_TOKEN_FLAVOR: bprintf(buf, "%s%s%s", (tok->flavor_optional ? "[" : ""), tok->flavor, (tok->flavor_optional ? "]" : "")); break; default: bprintf(buf, "<%s>", get_datatype_format_error_mssg(tok)); break; } } deleteListIterator(tok_i); // send the message send_to_char(ch, "Proper syntax is: %s %s%s\r\n", cmd, bufferString(buf), (optional_found ? "]" : "")); deleteBuffer(buf); } // // builds up a list of variables out of the token list, and arguments. If we // encounter an error and we need to show it to the looker, do so. LIST *compose_variable_list(CHAR_DATA *looker, LIST *tokens, char *args, char *err_buf) { LIST *variables = newList(); LIST_ITERATOR *tok_i = newListIterator(tokens); PARSE_TOKEN *tok = NULL; bool error = FALSE; bool optional_found = FALSE; // go through our list of tokens, and try dealing with the args ITERATE_LIST(tok, tok_i) { PARSE_VAR *var = NULL; // did we just encounter an optional value? if(tok->type == PARSE_TOKEN_OPTIONAL) { optional_found = TRUE; continue; } // can we still use tokens to process stuff? if(*args != '\0') var = use_one_parse_token(looker, tok, &args, &error, err_buf); // we haven't found an "optional" marker yet - this isn't allowed else if(optional_found == FALSE) error = TRUE; // we have found an "optional" marker. Just break out of the loop else break; // if use of the token returned a new variable, append it if(var != NULL) listQueue(variables, var); // if we enountered an error, tell the person if neccessary else if(error == TRUE) { deleteListWith(variables, deleteParseVar); variables = NULL; break; } } deleteListIterator(tok_i); return variables; } // // Goes through the list of variables and fills up vargs with them as needed void parse_assign_vars(LIST *variables, va_list vargs) { LIST_ITERATOR *var_i = newListIterator(variables); PARSE_VAR *one_var = NULL; // go through each variable and assign to vargs as needed ITERATE_LIST(one_var, var_i) { // first, do our basic type switch(one_var->type) { case PARSE_VAR_BOOL: *va_arg(vargs, bool *) = one_var->bool_val; break; case PARSE_VAR_INT: *va_arg(vargs, int *) = one_var->int_val; break; case PARSE_VAR_DOUBLE: *va_arg(vargs, double *) = one_var->dbl_val; break; case PARSE_VAR_STRING: case PARSE_VAR_OBJ: case PARSE_VAR_CHAR: case PARSE_VAR_EXIT: case PARSE_VAR_ROOM: *va_arg(vargs, void **) = one_var->ptr_val; break; // this should never happen... default: break; } // now see if we have a multi_type if(one_var->disambiguated_type != PARSE_NONE) *va_arg(vargs, int *) = one_var->disambiguated_type; // and if we parsed multiple occurences if(one_var->multiple_possible == TRUE) *va_arg(vargs, bool *) = one_var->multiple; } deleteListIterator(var_i); } // // Goes through the list of variables and make a python list with them PyObject *parse_create_py_vars(LIST *variables) { LIST_ITERATOR *var_i = newListIterator(variables); PARSE_VAR *one_var = NULL; PyObject *list = PyList_New(0); PyObject *pyval = NULL; // go through each variable and assign to vargs as needed ITERATE_LIST(one_var, var_i) { // first, do our basic type switch(one_var->type) { case PARSE_VAR_BOOL: pyval = Py_BuildValue("b", one_var->bool_val); PyList_Append(list, pyval); Py_DECREF(pyval); break; case PARSE_VAR_INT: pyval = Py_BuildValue("i", one_var->int_val); PyList_Append(list, pyval); Py_DECREF(pyval); break; case PARSE_VAR_DOUBLE: pyval = Py_BuildValue("f", one_var->dbl_val); PyList_Append(list, pyval); Py_DECREF(pyval); break; case PARSE_VAR_STRING: pyval = Py_BuildValue("s", one_var->ptr_val); PyList_Append(list, pyval); Py_DECREF(pyval); break; case PARSE_VAR_OBJ: if(one_var->multiple == FALSE) PyList_Append(list, objGetPyFormBorrowed(one_var->ptr_val)); else { pyval = PyList_fromList(one_var->ptr_val, objGetPyForm); PyList_Append(list, pyval); Py_DECREF(pyval); } break; case PARSE_VAR_CHAR: if(one_var->multiple == FALSE) PyList_Append(list, charGetPyFormBorrowed(one_var->ptr_val)); else { pyval = PyList_fromList(one_var->ptr_val, charGetPyForm); PyList_Append(list, pyval); Py_DECREF(pyval); } break; case PARSE_VAR_EXIT: pyval = newPyExit(one_var->ptr_val); PyList_Append(list, pyval); Py_DECREF(pyval); break; case PARSE_VAR_ROOM: PyList_Append(list, roomGetPyFormBorrowed(one_var->ptr_val)); break; // this should never happen... default: break; } // now see if we have a multi_type if(one_var->disambiguated_type != PARSE_NONE) { pyval = Py_BuildValue("s", (one_var->disambiguated_type==PARSE_CHAR?"char": (one_var->disambiguated_type==PARSE_ROOM?"room": (one_var->disambiguated_type==PARSE_OBJ?"obj": (one_var->disambiguated_type==PARSE_EXIT?"exit": NULL))))); PyList_Append(list, pyval); Py_DECREF(pyval); } // and if we parsed multiple occurences if(one_var->multiple_possible == TRUE) { pyval = Py_BuildValue("b", one_var->multiple); PyList_Append(list, pyval); Py_DECREF(pyval); } } deleteListIterator(var_i); return list; } //***************************************************************************** // implementation of parse.h //***************************************************************************** int parse_expected_py_args(const char *syntax) { LIST *tokens = NULL; if((tokens = decompose_parse_format(syntax)) == NULL) return -1; int count = 0; LIST_ITERATOR *token_i = newListIterator(tokens); PARSE_TOKEN *token = NULL; ITERATE_LIST(token, token_i) { if(token->type == PARSE_TOKEN_FLAVOR || token->type == PARSE_TOKEN_OPTIONAL) continue; // one for a returnable type count++; // one to denote what kind in an ambiguous case if(token->type == PARSE_TOKEN_MULTI) count++; // one to denote if we had multiples if(token->all_ok) count++; } deleteListIterator(token_i); deleteListWith(tokens, deleteParseToken); return count; } void *Py_parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd, char *args, const char *syntax) { char err_buf[SMALL_BUFFER] = ""; bool parse_ok = TRUE; LIST *tokens = NULL; LIST *variables = NULL; PyObject *list = NULL; // get our list of tokens if((tokens = decompose_parse_format(syntax)) == NULL) { log_string("Command '%s', format error in argument parsing: %s",cmd,syntax); parse_ok = FALSE; } // try to use our tokens to compose a variable list else if((variables = compose_variable_list(looker, tokens, args, err_buf)) == NULL) parse_ok = FALSE; else { // go through all of our vars and make python forms for them list = parse_create_py_vars(variables); // fill up optional spots at the end we didn't parse args for int expected = parse_expected_py_args(syntax); while(PyList_Size(list) < expected) PyList_Append(list, Py_None); } // did we encounter an error with the arguments and need to mssg someone? if(tokens != NULL && !parse_ok && show_errors) { // do we have a specific error message? if(*err_buf) send_to_char(looker, "%s\r\n", err_buf); // assume a syntax error else show_parse_syntax_error(looker, cmd, tokens); } // clean up our mess if(tokens != NULL) deleteListWith(tokens, deleteParseToken); if(variables != NULL) deleteListWith(variables, deleteParseVar); // return our parse status return list; } bool parse_args(CHAR_DATA *looker, bool show_errors, const char *cmd, char *args, const char *syntax, ...) { char err_buf[SMALL_BUFFER] = ""; bool parse_ok = TRUE; LIST *tokens = NULL; LIST *variables = NULL; // get our list of tokens if((tokens = decompose_parse_format(syntax)) == NULL) { log_string("Command '%s', format error in argument parsing: %s",cmd,syntax); parse_ok = FALSE; } // try to use our tokens to compose a variable list else if((variables = compose_variable_list(looker, tokens, args, err_buf)) == NULL) parse_ok = FALSE; else { // go through all of our vars and assign them to the proper args va_list vargs; va_start(vargs, syntax); parse_assign_vars(variables, vargs); va_end(vargs); } // did we encounter an error with the arguments and need to mssg someone? if(tokens != NULL && !parse_ok && show_errors) { // do we have a specific error message? if(*err_buf) send_to_char(looker, "%s\r\n", err_buf); // assume a syntax error else show_parse_syntax_error(looker, cmd, tokens); } // clean up our mess if(tokens != NULL) deleteListWith(tokens, deleteParseToken); if(variables != NULL) deleteListWith(variables, deleteParseVar); // return our parse status return parse_ok; }