aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/adrift/scparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/glk/adrift/scparser.cpp')
-rw-r--r--engines/glk/adrift/scparser.cpp3170
1 files changed, 1508 insertions, 1662 deletions
diff --git a/engines/glk/adrift/scparser.cpp b/engines/glk/adrift/scparser.cpp
index 6f7642c3a7..9372b5e39e 100644
--- a/engines/glk/adrift/scparser.cpp
+++ b/engines/glk/adrift/scparser.cpp
@@ -48,37 +48,35 @@ static const sc_char *const WHITESPACE = "\t\n\v\f\r ";
static sc_bool uip_trace = FALSE;
/* Enumeration of tokens. TOK_NONE represents a non-occurring token. */
-typedef enum
-{
- TOK_NONE = 0,
- TOK_CHOICE, TOK_CHOICE_END, TOK_OPTIONAL, TOK_OPTIONAL_END,
- TOK_ALTERNATES_SEPARATOR,
- TOK_WILDCARD, TOK_WHITESPACE, TOK_WORD, TOK_VARIABLE,
- TOK_CHARACTER_REFERENCE, TOK_OBJECT_REFERENCE, TOK_NUMBER_REFERENCE,
- TOK_TEXT_REFERENCE, TOK_EOS
+typedef enum {
+ TOK_NONE = 0,
+ TOK_CHOICE, TOK_CHOICE_END, TOK_OPTIONAL, TOK_OPTIONAL_END,
+ TOK_ALTERNATES_SEPARATOR,
+ TOK_WILDCARD, TOK_WHITESPACE, TOK_WORD, TOK_VARIABLE,
+ TOK_CHARACTER_REFERENCE, TOK_OBJECT_REFERENCE, TOK_NUMBER_REFERENCE,
+ TOK_TEXT_REFERENCE, TOK_EOS
} sc_uip_tok_t;
/*
* Small table tying token strings to tokens. Anything not whitespace and
* not caught by the table is a plain TOK_WORD.
*/
-typedef struct
-{
- const sc_char *const name;
- const sc_int length;
- const sc_uip_tok_t token;
+typedef struct {
+ const sc_char *const name;
+ const sc_int length;
+ const sc_uip_tok_t token;
} sc_uip_token_entry_t;
static const sc_uip_token_entry_t UIP_TOKENS[] = {
- {"[", 1, TOK_CHOICE}, {"]", 1, TOK_CHOICE_END},
- {"{", 1, TOK_OPTIONAL}, {"}", 1, TOK_OPTIONAL_END},
- {"/", 1, TOK_ALTERNATES_SEPARATOR},
- {"*", 1, TOK_WILDCARD},
- {"%character%", 11, TOK_CHARACTER_REFERENCE},
- {"%object%", 8, TOK_OBJECT_REFERENCE},
- {"%number%", 8, TOK_NUMBER_REFERENCE},
- {"%text%", 6, TOK_TEXT_REFERENCE},
- {NULL, 0, TOK_NONE}
+ {"[", 1, TOK_CHOICE}, {"]", 1, TOK_CHOICE_END},
+ {"{", 1, TOK_OPTIONAL}, {"}", 1, TOK_OPTIONAL_END},
+ {"/", 1, TOK_ALTERNATES_SEPARATOR},
+ {"*", 1, TOK_WILDCARD},
+ {"%character%", 11, TOK_CHARACTER_REFERENCE},
+ {"%object%", 8, TOK_OBJECT_REFERENCE},
+ {"%number%", 8, TOK_NUMBER_REFERENCE},
+ {"%text%", 6, TOK_TEXT_REFERENCE},
+ {NULL, 0, TOK_NONE}
};
@@ -101,49 +99,44 @@ static sc_char *uip_temporary = NULL;
* Start and wrap up pattern string tokenization.
*/
static void
-uip_tokenize_start (const sc_char *pattern)
-{
- static sc_bool initialized = FALSE;
- sc_int required;
-
- /* On first call only, verify the string lengths in the table. */
- if (!initialized)
- {
- const sc_uip_token_entry_t *entry;
-
- /* Compare table lengths with string lengths. */
- for (entry = UIP_TOKENS; entry->name; entry++)
- {
- if (entry->length != (sc_int) strlen (entry->name))
- {
- sc_fatal ("uip_tokenize_start:"
- " table string length is wrong for \"%s\"\n",
- entry->name);
- }
- }
-
- initialized = TRUE;
- }
-
- /* Save pattern, and restart index. */
- uip_pattern = pattern;
- uip_index = 0;
-
- /* Set up temporary; static if long enough, otherwise allocated. */
- required = strlen (pattern) + 1;
- uip_temporary = (required > UIP_ALLOCATION_AVOIDANCE_SIZE)
- ? (sc_char *)sc_malloc (required) : uip_static_temporary;
+uip_tokenize_start(const sc_char *pattern) {
+ static sc_bool initialized = FALSE;
+ sc_int required;
+
+ /* On first call only, verify the string lengths in the table. */
+ if (!initialized) {
+ const sc_uip_token_entry_t *entry;
+
+ /* Compare table lengths with string lengths. */
+ for (entry = UIP_TOKENS; entry->name; entry++) {
+ if (entry->length != (sc_int) strlen(entry->name)) {
+ sc_fatal("uip_tokenize_start:"
+ " table string length is wrong for \"%s\"\n",
+ entry->name);
+ }
+ }
+
+ initialized = TRUE;
+ }
+
+ /* Save pattern, and restart index. */
+ uip_pattern = pattern;
+ uip_index = 0;
+
+ /* Set up temporary; static if long enough, otherwise allocated. */
+ required = strlen(pattern) + 1;
+ uip_temporary = (required > UIP_ALLOCATION_AVOIDANCE_SIZE)
+ ? (sc_char *)sc_malloc(required) : uip_static_temporary;
}
static void
-uip_tokenize_end (void)
-{
- /* Deallocate temporary if required, and clear pattern and index. */
- if (uip_temporary != uip_static_temporary)
- sc_free (uip_temporary);
- uip_temporary = NULL;
- uip_pattern = NULL;
- uip_index = 0;
+uip_tokenize_end(void) {
+ /* Deallocate temporary if required, and clear pattern and index. */
+ if (uip_temporary != uip_static_temporary)
+ sc_free(uip_temporary);
+ uip_temporary = NULL;
+ uip_pattern = NULL;
+ uip_index = 0;
}
@@ -153,68 +146,62 @@ uip_tokenize_end (void)
* Return the next token from the current pattern.
*/
static sc_uip_tok_t
-uip_next_token (void)
-{
- const sc_uip_token_entry_t *entry;
- sc_char close;
- assert (uip_pattern);
-
- /* Get next character, return EOS if at pattern end. */
- if (uip_pattern[uip_index] == NUL)
- {
- uip_token_value = NULL;
- return TOK_EOS;
- }
-
- /* If whitespace, skip it, then return a whitespace token. */
- if (sc_isspace (uip_pattern[uip_index]))
- {
- uip_index++;
- while (sc_isspace (uip_pattern[uip_index])
- && uip_pattern[uip_index] != NUL)
- uip_index++;
- uip_token_value = NULL;
- return TOK_WHITESPACE;
- }
-
- /* Search the table for matching strings. */
- for (entry = UIP_TOKENS; entry->name; entry++)
- {
- if (strncmp (uip_pattern + uip_index, entry->name, entry->length) == 0)
- break;
- }
- if (entry->name)
- {
- /* Advance over string, and return token. */
- uip_index += entry->length;
- uip_token_value = NULL;
- return entry->token;
- }
-
- /*
- * Search for a non-special variable reference. This is apparently an
- * Adrift extension to the standard pattern match, allowing %user_var% to
- * be used in patterns. If found, return a variable with the name as the
- * token value. We can't interpolate the value into the string either
- * here or earlier, so we have to save the variable's name, and retrieve
- * it when we come to try the match.
- */
- if (sscanf (uip_pattern + uip_index, "%%%[^%]%c", uip_temporary, &close) == 2
- && close == PERCENT)
- {
- uip_index += strlen (uip_temporary) + 2;
- uip_token_value = uip_temporary;
- return TOK_VARIABLE;
- }
-
- /*
- * Return a word. This is a contiguous run of non-pattern-special, non-
- * whitespace, non-percent characters
- */
- sscanf (uip_pattern + uip_index, "%[^][/{}*% \f\n\r\t\v]", uip_temporary);
- uip_token_value = uip_temporary;
- uip_index += strlen (uip_temporary);
- return TOK_WORD;
+uip_next_token(void) {
+ const sc_uip_token_entry_t *entry;
+ sc_char close;
+ assert(uip_pattern);
+
+ /* Get next character, return EOS if at pattern end. */
+ if (uip_pattern[uip_index] == NUL) {
+ uip_token_value = NULL;
+ return TOK_EOS;
+ }
+
+ /* If whitespace, skip it, then return a whitespace token. */
+ if (sc_isspace(uip_pattern[uip_index])) {
+ uip_index++;
+ while (sc_isspace(uip_pattern[uip_index])
+ && uip_pattern[uip_index] != NUL)
+ uip_index++;
+ uip_token_value = NULL;
+ return TOK_WHITESPACE;
+ }
+
+ /* Search the table for matching strings. */
+ for (entry = UIP_TOKENS; entry->name; entry++) {
+ if (strncmp(uip_pattern + uip_index, entry->name, entry->length) == 0)
+ break;
+ }
+ if (entry->name) {
+ /* Advance over string, and return token. */
+ uip_index += entry->length;
+ uip_token_value = NULL;
+ return entry->token;
+ }
+
+ /*
+ * Search for a non-special variable reference. This is apparently an
+ * Adrift extension to the standard pattern match, allowing %user_var% to
+ * be used in patterns. If found, return a variable with the name as the
+ * token value. We can't interpolate the value into the string either
+ * here or earlier, so we have to save the variable's name, and retrieve
+ * it when we come to try the match.
+ */
+ if (sscanf(uip_pattern + uip_index, "%%%[^%]%c", uip_temporary, &close) == 2
+ && close == PERCENT) {
+ uip_index += strlen(uip_temporary) + 2;
+ uip_token_value = uip_temporary;
+ return TOK_VARIABLE;
+ }
+
+ /*
+ * Return a word. This is a contiguous run of non-pattern-special, non-
+ * whitespace, non-percent characters
+ */
+ sscanf(uip_pattern + uip_index, "%[^][/{}*% \f\n\r\t\v]", uip_temporary);
+ uip_token_value = uip_temporary;
+ uip_index += strlen(uip_temporary);
+ return TOK_WORD;
}
@@ -225,17 +212,15 @@ uip_next_token (void)
* here if the current token is not a TOK_WORD or TOK_VARIABLE.
*/
static const sc_char *
-uip_current_token_value (void)
-{
- /* If the token value is NULL, the current token isn't a word. */
- if (!uip_token_value)
- {
- sc_fatal ("uip_current_token_value:"
- " attempt to take undefined token value\n");
- }
-
- /* Return value. */
- return uip_token_value;
+uip_current_token_value(void) {
+ /* If the token value is NULL, the current token isn't a word. */
+ if (!uip_token_value) {
+ sc_fatal("uip_current_token_value:"
+ " attempt to take undefined token value\n");
+ }
+
+ /* Return value. */
+ return uip_token_value;
}
@@ -245,21 +230,19 @@ uip_current_token_value (void)
* NODE_UNUSED must be zero to ensure that the statically allocated array that
* forms the node pool appears initially as containing only unused nodes.
*/
-typedef enum
-{
- NODE_UNUSED = 0,
- NODE_CHOICE, NODE_OPTIONAL, NODE_WILDCARD, NODE_WHITESPACE,
- NODE_CHARACTER_REFERENCE, NODE_OBJECT_REFERENCE, NODE_TEXT_REFERENCE,
- NODE_NUMBER_REFERENCE, NODE_WORD, NODE_VARIABLE, NODE_LIST, NODE_EOS
+typedef enum {
+ NODE_UNUSED = 0,
+ NODE_CHOICE, NODE_OPTIONAL, NODE_WILDCARD, NODE_WHITESPACE,
+ NODE_CHARACTER_REFERENCE, NODE_OBJECT_REFERENCE, NODE_TEXT_REFERENCE,
+ NODE_NUMBER_REFERENCE, NODE_WORD, NODE_VARIABLE, NODE_LIST, NODE_EOS
} sc_pttype_t;
-typedef struct sc_ptnode_s
-{
- struct sc_ptnode_s *left_child;
- struct sc_ptnode_s *right_sibling;
-
- sc_pttype_t type;
- sc_char *word;
- sc_bool is_allocated;
+typedef struct sc_ptnode_s {
+ struct sc_ptnode_s *left_child;
+ struct sc_ptnode_s *right_sibling;
+
+ sc_pttype_t type;
+ sc_char *word;
+ sc_bool is_allocated;
} sc_ptnode_t;
typedef sc_ptnode_t *sc_ptnoderef_t;
@@ -271,7 +254,7 @@ static jmp_buf uip_parse_error;
/* Parse tree for cleanup, and forward declaration of pattern list parser. */
static sc_ptnoderef_t uip_parse_tree = NULL;
-static void uip_parse_list (sc_ptnoderef_t list);
+static void uip_parse_list(sc_ptnoderef_t list);
/*
* Pool of statically allocated nodes, for faster allocations. Nodes are
@@ -290,10 +273,9 @@ static sc_int uip_node_pool_available = UIP_NODE_POOL_SIZE;
* first, then by straight malloc() should the pool empty.
*/
enum { UIP_WORD_POOL_SIZE = 64, UIP_SHORT_WORD_SIZE = 16 };
-typedef struct
-{
- sc_bool is_in_use;
- sc_char word[UIP_SHORT_WORD_SIZE];
+typedef struct {
+ sc_bool is_in_use;
+ sc_char word[UIP_SHORT_WORD_SIZE];
} sc_ptshortword_t;
typedef sc_ptshortword_t *sc_ptshortwordref_t;
static sc_ptshortword_t uip_word_pool[UIP_WORD_POOL_SIZE];
@@ -306,17 +288,15 @@ static sc_int uip_word_pool_available = UIP_WORD_POOL_SIZE;
* Match a token to the lookahead, then advance lookahead.
*/
static void
-uip_parse_match (sc_uip_tok_t token)
-{
- if (uip_parse_lookahead == token)
- uip_parse_lookahead = uip_next_token ();
- else
- {
- /* Syntax error. */
- sc_error ("uip_parse_match: syntax error, expected %ld, got %ld\n",
- (sc_int) uip_parse_lookahead, (sc_int) token);
- longjmp (uip_parse_error, 1);
- }
+uip_parse_match(sc_uip_tok_t token) {
+ if (uip_parse_lookahead == token)
+ uip_parse_lookahead = uip_next_token();
+ else {
+ /* Syntax error. */
+ sc_error("uip_parse_match: syntax error, expected %ld, got %ld\n",
+ (sc_int) uip_parse_lookahead, (sc_int) token);
+ longjmp(uip_parse_error, 1);
+ }
}
@@ -328,51 +308,46 @@ uip_parse_match (sc_uip_tok_t token)
* exhausted, backs off to standard allocation.
*/
static sc_char *
-uip_new_word (const sc_char *word)
-{
- sc_int required;
-
- /*
- * Unless the pool is empty, search forwards from the next cursor position
- * until an unused slot is found, or until the index wraps to the cursor.
- */
- required = strlen (word) + 1;
- if (uip_word_pool_available > 0 && required <= UIP_SHORT_WORD_SIZE)
- {
- sc_int index_;
- sc_ptshortwordref_t shortword;
-
- index_ = (uip_word_pool_cursor + 1) % UIP_WORD_POOL_SIZE;
- while (index_ != uip_word_pool_cursor)
- {
- if (!uip_word_pool[index_].is_in_use)
- break;
- index_ = (index_ + 1) % UIP_WORD_POOL_SIZE;
- }
-
- if (uip_word_pool[index_].is_in_use)
- sc_fatal ("uip_new_word: no free slot found in the words pool\n");
-
- /* Use the slot and update the pool cursor and free count. */
- shortword = uip_word_pool + index_;
- strcpy (shortword->word, word);
- shortword->is_in_use = TRUE;
-
- uip_word_pool_cursor = index_;
- uip_word_pool_available--;
-
- /* Return the address of the copied string. */
- return shortword->word;
- }
- else
- {
- sc_char *word_copy;
-
- /* Fall back to less efficient allocations. */
- word_copy = (sc_char *)sc_malloc (required);
- strcpy (word_copy, word);
- return word_copy;
- }
+uip_new_word(const sc_char *word) {
+ sc_int required;
+
+ /*
+ * Unless the pool is empty, search forwards from the next cursor position
+ * until an unused slot is found, or until the index wraps to the cursor.
+ */
+ required = strlen(word) + 1;
+ if (uip_word_pool_available > 0 && required <= UIP_SHORT_WORD_SIZE) {
+ sc_int index_;
+ sc_ptshortwordref_t shortword;
+
+ index_ = (uip_word_pool_cursor + 1) % UIP_WORD_POOL_SIZE;
+ while (index_ != uip_word_pool_cursor) {
+ if (!uip_word_pool[index_].is_in_use)
+ break;
+ index_ = (index_ + 1) % UIP_WORD_POOL_SIZE;
+ }
+
+ if (uip_word_pool[index_].is_in_use)
+ sc_fatal("uip_new_word: no free slot found in the words pool\n");
+
+ /* Use the slot and update the pool cursor and free count. */
+ shortword = uip_word_pool + index_;
+ strcpy(shortword->word, word);
+ shortword->is_in_use = TRUE;
+
+ uip_word_pool_cursor = index_;
+ uip_word_pool_available--;
+
+ /* Return the address of the copied string. */
+ return shortword->word;
+ } else {
+ sc_char *word_copy;
+
+ /* Fall back to less efficient allocations. */
+ word_copy = (sc_char *)sc_malloc(required);
+ strcpy(word_copy, word);
+ return word_copy;
+ }
}
@@ -383,33 +358,30 @@ uip_new_word (const sc_char *word)
* pool entry and return it to the pool.
*/
static void
-uip_free_word (sc_char *word)
-{
- const sc_char *first_in_pool, *last_in_pool;
-
- /* Obtain the range of valid addresses for words from the word pool. */
- first_in_pool = uip_word_pool[0].word;
- last_in_pool = uip_word_pool[UIP_WORD_POOL_SIZE - 1].word;
-
- /* If from the pool, mark the entry as no longer in use, otherwise free. */
- if (word >= first_in_pool && word <= last_in_pool)
- {
- sc_int index_;
- sc_ptshortwordref_t shortword;
-
- /*
- * Calculate the index to the word pool entry from which this short
- * word was allocated.
- */
- index_ = (word - first_in_pool) / sizeof (uip_word_pool[0]);
- shortword = uip_word_pool + index_;
- assert (shortword->word == word);
-
- shortword->is_in_use = FALSE;
- uip_word_pool_available++;
- }
- else
- sc_free (word);
+uip_free_word(sc_char *word) {
+ const sc_char *first_in_pool, *last_in_pool;
+
+ /* Obtain the range of valid addresses for words from the word pool. */
+ first_in_pool = uip_word_pool[0].word;
+ last_in_pool = uip_word_pool[UIP_WORD_POOL_SIZE - 1].word;
+
+ /* If from the pool, mark the entry as no longer in use, otherwise free. */
+ if (word >= first_in_pool && word <= last_in_pool) {
+ sc_int index_;
+ sc_ptshortwordref_t shortword;
+
+ /*
+ * Calculate the index to the word pool entry from which this short
+ * word was allocated.
+ */
+ index_ = (word - first_in_pool) / sizeof(uip_word_pool[0]);
+ shortword = uip_word_pool + index_;
+ assert(shortword->word == word);
+
+ shortword->is_in_use = FALSE;
+ uip_word_pool_available++;
+ } else
+ sc_free(word);
}
@@ -421,50 +393,45 @@ uip_free_word (sc_char *word)
* exhausted, backs off to standard allocation.
*/
static sc_ptnoderef_t
-uip_new_node (sc_pttype_t type)
-{
- sc_ptnoderef_t node;
-
- /*
- * Unless the pool is empty, search forwards from the next cursor position
- * until an unused slot is found, or until the index wraps to the cursor.
- */
- if (uip_node_pool_available > 0)
- {
- sc_int index_;
-
- index_ = (uip_node_pool_cursor + 1) % UIP_NODE_POOL_SIZE;
- while (index_ != uip_node_pool_cursor)
- {
- if (uip_node_pool[index_].type == NODE_UNUSED)
- break;
- index_ = (index_ + 1) % UIP_NODE_POOL_SIZE;
- }
-
- if (uip_node_pool[index_].type != NODE_UNUSED)
- sc_fatal ("uip_new_node: no free slot found in the nodes pool\n");
-
- /* Use the slot and update the pool cursor and free count. */
- node = uip_node_pool + index_;
- node->is_allocated = FALSE;
-
- uip_node_pool_cursor = index_;
- uip_node_pool_available--;
- }
- else
- {
- /* Fall back to less efficient allocations. */
- node = (sc_ptnoderef_t)sc_malloc(sizeof (*node));
- node->is_allocated = TRUE;
- }
-
- /* Fill in the remaining fields and return the new node. */
- node->left_child = NULL;
- node->right_sibling = NULL;
- node->type = type;
- node->word = NULL;
-
- return node;
+uip_new_node(sc_pttype_t type) {
+ sc_ptnoderef_t node;
+
+ /*
+ * Unless the pool is empty, search forwards from the next cursor position
+ * until an unused slot is found, or until the index wraps to the cursor.
+ */
+ if (uip_node_pool_available > 0) {
+ sc_int index_;
+
+ index_ = (uip_node_pool_cursor + 1) % UIP_NODE_POOL_SIZE;
+ while (index_ != uip_node_pool_cursor) {
+ if (uip_node_pool[index_].type == NODE_UNUSED)
+ break;
+ index_ = (index_ + 1) % UIP_NODE_POOL_SIZE;
+ }
+
+ if (uip_node_pool[index_].type != NODE_UNUSED)
+ sc_fatal("uip_new_node: no free slot found in the nodes pool\n");
+
+ /* Use the slot and update the pool cursor and free count. */
+ node = uip_node_pool + index_;
+ node->is_allocated = FALSE;
+
+ uip_node_pool_cursor = index_;
+ uip_node_pool_available--;
+ } else {
+ /* Fall back to less efficient allocations. */
+ node = (sc_ptnoderef_t)sc_malloc(sizeof(*node));
+ node->is_allocated = TRUE;
+ }
+
+ /* Fill in the remaining fields and return the new node. */
+ node->left_child = NULL;
+ node->right_sibling = NULL;
+ node->type = type;
+ node->word = NULL;
+
+ return node;
}
@@ -475,27 +442,23 @@ uip_new_node (sc_pttype_t type)
* free its memory; if not, return it to the pool.
*/
static void
-uip_destroy_node (sc_ptnoderef_t node)
-{
- /* Free any word contained at this node. */
- if (node->word)
- uip_free_word (node->word);
-
- /*
- * If the node was allocated, poison memory and free it. If it came from
- * the node pool, set it to unused and update the availability count for
- * the pool.
- */
- if (node->is_allocated)
- {
- memset (node, 0xaa, sizeof (*node));
- sc_free (node);
- }
- else
- {
- node->type = NODE_UNUSED;
- uip_node_pool_available++;
- }
+uip_destroy_node(sc_ptnoderef_t node) {
+ /* Free any word contained at this node. */
+ if (node->word)
+ uip_free_word(node->word);
+
+ /*
+ * If the node was allocated, poison memory and free it. If it came from
+ * the node pool, set it to unused and update the availability count for
+ * the pool.
+ */
+ if (node->is_allocated) {
+ memset(node, 0xaa, sizeof(*node));
+ sc_free(node);
+ } else {
+ node->type = NODE_UNUSED;
+ uip_node_pool_available++;
+ }
}
@@ -507,30 +470,27 @@ uip_destroy_node (sc_ptnoderef_t node)
* first function is a helper, returning a newly constructed parsed list.
*/
static sc_ptnoderef_t
-uip_parse_new_list (void)
-{
- sc_ptnoderef_t list;
-
- /* Create a new list node, parse into it, and return it. */
- list = uip_new_node (NODE_LIST);
- uip_parse_list (list);
- return list;
+uip_parse_new_list(void) {
+ sc_ptnoderef_t list;
+
+ /* Create a new list node, parse into it, and return it. */
+ list = uip_new_node(NODE_LIST);
+ uip_parse_list(list);
+ return list;
}
static void
-uip_parse_alternatives (sc_ptnoderef_t node)
-{
- sc_ptnoderef_t child;
-
- /* Parse initial alternative, then add other listed alternatives. */
- node->left_child = uip_parse_new_list ();
- child = node->left_child;
- while (uip_parse_lookahead == TOK_ALTERNATES_SEPARATOR)
- {
- uip_parse_match (TOK_ALTERNATES_SEPARATOR);
- child->right_sibling = uip_parse_new_list ();
- child = child->right_sibling;
- }
+uip_parse_alternatives(sc_ptnoderef_t node) {
+ sc_ptnoderef_t child;
+
+ /* Parse initial alternative, then add other listed alternatives. */
+ node->left_child = uip_parse_new_list();
+ child = node->left_child;
+ while (uip_parse_lookahead == TOK_ALTERNATES_SEPARATOR) {
+ uip_parse_match(TOK_ALTERNATES_SEPARATOR);
+ child->right_sibling = uip_parse_new_list();
+ child = child->right_sibling;
+ }
}
@@ -540,107 +500,102 @@ uip_parse_alternatives (sc_ptnoderef_t node)
* Parse a single pattern element.
*/
static sc_ptnoderef_t
-uip_parse_element (void)
-{
- sc_ptnoderef_t node = NULL;
- sc_uip_tok_t token;
-
- /* Handle pattern element based on lookahead token. */
- switch (uip_parse_lookahead)
- {
- case TOK_WHITESPACE:
- uip_parse_match (TOK_WHITESPACE);
- node = uip_new_node (NODE_WHITESPACE);
- break;
-
- case TOK_CHOICE:
- /* Parse a [...[/.../...]] choice. */
- uip_parse_match (TOK_CHOICE);
- node = uip_new_node (NODE_CHOICE);
- uip_parse_alternatives (node);
- uip_parse_match (TOK_CHOICE_END);
- break;
-
- case TOK_OPTIONAL:
- /* Parse a {...[/.../...]} optional element. */
- uip_parse_match (TOK_OPTIONAL);
- node = uip_new_node (NODE_OPTIONAL);
- uip_parse_alternatives (node);
- uip_parse_match (TOK_OPTIONAL_END);
- break;
-
- case TOK_WILDCARD:
- case TOK_CHARACTER_REFERENCE:
- case TOK_OBJECT_REFERENCE:
- case TOK_NUMBER_REFERENCE:
- case TOK_TEXT_REFERENCE:
- /* Parse %mumble% references and * wildcards. */
- token = uip_parse_lookahead;
- uip_parse_match (token);
- switch (token)
- {
- case TOK_WILDCARD:
- node = uip_new_node (NODE_WILDCARD);
- break;
- case TOK_CHARACTER_REFERENCE:
- node = uip_new_node (NODE_CHARACTER_REFERENCE);
- break;
- case TOK_OBJECT_REFERENCE:
- node = uip_new_node (NODE_OBJECT_REFERENCE);
- break;
- case TOK_NUMBER_REFERENCE:
- node = uip_new_node (NODE_NUMBER_REFERENCE);
- break;
- case TOK_TEXT_REFERENCE:
- node = uip_new_node (NODE_TEXT_REFERENCE);
- break;
- default:
- sc_fatal ("uip_parse_element: invalid token, %ld\n", (sc_int) token);
- }
- break;
-
- case TOK_WORD:
- {
- const sc_char *token_value;
- sc_char *word;
-
- /* Take a copy of the token's word value. */
- token_value = uip_current_token_value ();
- word = uip_new_word (token_value);
-
- /* Store details in a word node. */
- uip_parse_match (TOK_WORD);
- node = uip_new_node (NODE_WORD);
- node->word = word;
- break;
- }
-
- case TOK_VARIABLE:
- {
- const sc_char *token_value;
- sc_char *name;
-
- /* Take a copy of the token's variable name value. */
- token_value = uip_current_token_value ();
- name = uip_new_word (token_value);
-
- /* Store details in a variable node, overloading word. */
- uip_parse_match (TOK_VARIABLE);
- node = uip_new_node (NODE_VARIABLE);
- node->word = name;
- break;
- }
-
- default:
- /* Syntax error. */
- sc_error ("uip_parse_element: syntax error,"
- " unexpected token, %ld\n", (sc_int) uip_parse_lookahead);
- longjmp (uip_parse_error, 1);
- }
-
- /* Return the newly created node. */
- assert (node);
- return node;
+uip_parse_element(void) {
+ sc_ptnoderef_t node = NULL;
+ sc_uip_tok_t token;
+
+ /* Handle pattern element based on lookahead token. */
+ switch (uip_parse_lookahead) {
+ case TOK_WHITESPACE:
+ uip_parse_match(TOK_WHITESPACE);
+ node = uip_new_node(NODE_WHITESPACE);
+ break;
+
+ case TOK_CHOICE:
+ /* Parse a [...[/.../...]] choice. */
+ uip_parse_match(TOK_CHOICE);
+ node = uip_new_node(NODE_CHOICE);
+ uip_parse_alternatives(node);
+ uip_parse_match(TOK_CHOICE_END);
+ break;
+
+ case TOK_OPTIONAL:
+ /* Parse a {...[/.../...]} optional element. */
+ uip_parse_match(TOK_OPTIONAL);
+ node = uip_new_node(NODE_OPTIONAL);
+ uip_parse_alternatives(node);
+ uip_parse_match(TOK_OPTIONAL_END);
+ break;
+
+ case TOK_WILDCARD:
+ case TOK_CHARACTER_REFERENCE:
+ case TOK_OBJECT_REFERENCE:
+ case TOK_NUMBER_REFERENCE:
+ case TOK_TEXT_REFERENCE:
+ /* Parse %mumble% references and * wildcards. */
+ token = uip_parse_lookahead;
+ uip_parse_match(token);
+ switch (token) {
+ case TOK_WILDCARD:
+ node = uip_new_node(NODE_WILDCARD);
+ break;
+ case TOK_CHARACTER_REFERENCE:
+ node = uip_new_node(NODE_CHARACTER_REFERENCE);
+ break;
+ case TOK_OBJECT_REFERENCE:
+ node = uip_new_node(NODE_OBJECT_REFERENCE);
+ break;
+ case TOK_NUMBER_REFERENCE:
+ node = uip_new_node(NODE_NUMBER_REFERENCE);
+ break;
+ case TOK_TEXT_REFERENCE:
+ node = uip_new_node(NODE_TEXT_REFERENCE);
+ break;
+ default:
+ sc_fatal("uip_parse_element: invalid token, %ld\n", (sc_int) token);
+ }
+ break;
+
+ case TOK_WORD: {
+ const sc_char *token_value;
+ sc_char *word;
+
+ /* Take a copy of the token's word value. */
+ token_value = uip_current_token_value();
+ word = uip_new_word(token_value);
+
+ /* Store details in a word node. */
+ uip_parse_match(TOK_WORD);
+ node = uip_new_node(NODE_WORD);
+ node->word = word;
+ break;
+ }
+
+ case TOK_VARIABLE: {
+ const sc_char *token_value;
+ sc_char *name;
+
+ /* Take a copy of the token's variable name value. */
+ token_value = uip_current_token_value();
+ name = uip_new_word(token_value);
+
+ /* Store details in a variable node, overloading word. */
+ uip_parse_match(TOK_VARIABLE);
+ node = uip_new_node(NODE_VARIABLE);
+ node->word = name;
+ break;
+ }
+
+ default:
+ /* Syntax error. */
+ sc_error("uip_parse_element: syntax error,"
+ " unexpected token, %ld\n", (sc_int) uip_parse_lookahead);
+ longjmp(uip_parse_error, 1);
+ }
+
+ /* Return the newly created node. */
+ assert(node);
+ return node;
}
@@ -650,63 +605,56 @@ uip_parse_element (void)
* Parse a list of pattern elements.
*/
static void
-uip_parse_list (sc_ptnoderef_t list)
-{
- sc_ptnoderef_t child, node;
-
- /* Add elements until a list terminator token is encountered. */
- child = list;
- while (TRUE)
- {
- switch (uip_parse_lookahead)
- {
- case TOK_CHOICE_END:
- case TOK_OPTIONAL_END:
- case TOK_ALTERNATES_SEPARATOR:
- /* Terminate list building and return. */
- return;
-
- case TOK_EOS:
- /* Place EOS at the appropriate link and return. */
- node = uip_new_node (NODE_EOS);
- if (child == list)
- child->left_child = node;
- else
- child->right_sibling = node;
- return;
-
- default:
- /* Add the next node at the appropriate link. */
- node = uip_parse_element ();
- if (child == list)
- {
- child->left_child = node;
- child = child->left_child;
- }
- else
- {
- /*
- * Make a special case of a choice or option next to another
- * choice or option. In this case, add an (invented) whitespace
- * node, to ensure a match with suitable input.
- */
- if ((child->type == NODE_OPTIONAL || child->type == NODE_CHOICE)
- && (node->type == NODE_OPTIONAL || node->type == NODE_CHOICE))
- {
- sc_ptnoderef_t whitespace;
-
- /* Interpose invented whitespace. */
- whitespace = uip_new_node (NODE_WHITESPACE);
- child->right_sibling = whitespace;
- child = child->right_sibling;
- }
-
- child->right_sibling = node;
- child = child->right_sibling;
- }
- continue;
- }
- }
+uip_parse_list(sc_ptnoderef_t list) {
+ sc_ptnoderef_t child, node;
+
+ /* Add elements until a list terminator token is encountered. */
+ child = list;
+ while (TRUE) {
+ switch (uip_parse_lookahead) {
+ case TOK_CHOICE_END:
+ case TOK_OPTIONAL_END:
+ case TOK_ALTERNATES_SEPARATOR:
+ /* Terminate list building and return. */
+ return;
+
+ case TOK_EOS:
+ /* Place EOS at the appropriate link and return. */
+ node = uip_new_node(NODE_EOS);
+ if (child == list)
+ child->left_child = node;
+ else
+ child->right_sibling = node;
+ return;
+
+ default:
+ /* Add the next node at the appropriate link. */
+ node = uip_parse_element();
+ if (child == list) {
+ child->left_child = node;
+ child = child->left_child;
+ } else {
+ /*
+ * Make a special case of a choice or option next to another
+ * choice or option. In this case, add an (invented) whitespace
+ * node, to ensure a match with suitable input.
+ */
+ if ((child->type == NODE_OPTIONAL || child->type == NODE_CHOICE)
+ && (node->type == NODE_OPTIONAL || node->type == NODE_CHOICE)) {
+ sc_ptnoderef_t whitespace;
+
+ /* Interpose invented whitespace. */
+ whitespace = uip_new_node(NODE_WHITESPACE);
+ child->right_sibling = whitespace;
+ child = child->right_sibling;
+ }
+
+ child->right_sibling = node;
+ child = child->right_sibling;
+ }
+ continue;
+ }
+ }
}
@@ -716,17 +664,15 @@ uip_parse_list (sc_ptnoderef_t list)
* Free and destroy a parsed pattern tree.
*/
static void
-uip_destroy_tree (sc_ptnoderef_t node)
-{
- if (node)
- {
- /* Recursively destroy siblings, then left child. */
- uip_destroy_tree (node->right_sibling);
- uip_destroy_tree (node->left_child);
-
- /* Destroy the node itself. */
- uip_destroy_node (node);
- }
+uip_destroy_tree(sc_ptnoderef_t node) {
+ if (node) {
+ /* Recursively destroy siblings, then left child. */
+ uip_destroy_tree(node->right_sibling);
+ uip_destroy_tree(node->left_child);
+
+ /* Destroy the node itself. */
+ uip_destroy_node(node);
+ }
}
@@ -737,84 +683,78 @@ uip_destroy_tree (sc_ptnoderef_t node)
* Print out a pattern match tree.
*/
static void
-uip_debug_dump_node (sc_ptnoderef_t node, sc_int depth)
-{
- /* End recursion on null node. */
- if (node)
- {
- sc_int index_;
-
- sc_trace (" ");
- for (index_ = 0; index_ < depth; index_++)
- sc_trace (" ");
-
- sc_trace ("%p", (void *) node);
- switch (node->type)
- {
- case NODE_CHOICE:
- sc_trace (", choice");
- break;
- case NODE_OPTIONAL:
- sc_trace (", optional");
- break;
- case NODE_WILDCARD:
- sc_trace (", wildcard");
- break;
- case NODE_WHITESPACE:
- sc_trace (", whitespace");
- break;
- case NODE_CHARACTER_REFERENCE:
- sc_trace (", character");
- break;
- case NODE_OBJECT_REFERENCE:
- sc_trace (", object");
- break;
- case NODE_TEXT_REFERENCE:
- sc_trace (", text");
- break;
- case NODE_NUMBER_REFERENCE:
- sc_trace (", number");
- break;
- case NODE_WORD:
- sc_trace (", word \"%s\"", node->word);
- break;
- case NODE_VARIABLE:
- sc_trace (", variable \"%s\"", node->word);
- break;
- case NODE_LIST:
- sc_trace (", list");
- break;
- case NODE_EOS:
- sc_trace (", <eos>");
- break;
- default:
- sc_trace (", unknown type %ld", (sc_int) node->type);
- break;
- }
- if (node->left_child)
- sc_trace (", left child %p", (void *) node->left_child);
- if (node->right_sibling)
- sc_trace (", right sibling %p", (void *) node->right_sibling);
- sc_trace ("\n");
-
- /* Recursively dump left child, then siblings. */
- uip_debug_dump_node (node->left_child, depth + 1);
- uip_debug_dump_node (node->right_sibling, depth);
- }
+uip_debug_dump_node(sc_ptnoderef_t node, sc_int depth) {
+ /* End recursion on null node. */
+ if (node) {
+ sc_int index_;
+
+ sc_trace(" ");
+ for (index_ = 0; index_ < depth; index_++)
+ sc_trace(" ");
+
+ sc_trace("%p", (void *) node);
+ switch (node->type) {
+ case NODE_CHOICE:
+ sc_trace(", choice");
+ break;
+ case NODE_OPTIONAL:
+ sc_trace(", optional");
+ break;
+ case NODE_WILDCARD:
+ sc_trace(", wildcard");
+ break;
+ case NODE_WHITESPACE:
+ sc_trace(", whitespace");
+ break;
+ case NODE_CHARACTER_REFERENCE:
+ sc_trace(", character");
+ break;
+ case NODE_OBJECT_REFERENCE:
+ sc_trace(", object");
+ break;
+ case NODE_TEXT_REFERENCE:
+ sc_trace(", text");
+ break;
+ case NODE_NUMBER_REFERENCE:
+ sc_trace(", number");
+ break;
+ case NODE_WORD:
+ sc_trace(", word \"%s\"", node->word);
+ break;
+ case NODE_VARIABLE:
+ sc_trace(", variable \"%s\"", node->word);
+ break;
+ case NODE_LIST:
+ sc_trace(", list");
+ break;
+ case NODE_EOS:
+ sc_trace(", <eos>");
+ break;
+ default:
+ sc_trace(", unknown type %ld", (sc_int) node->type);
+ break;
+ }
+ if (node->left_child)
+ sc_trace(", left child %p", (void *) node->left_child);
+ if (node->right_sibling)
+ sc_trace(", right sibling %p", (void *) node->right_sibling);
+ sc_trace("\n");
+
+ /* Recursively dump left child, then siblings. */
+ uip_debug_dump_node(node->left_child, depth + 1);
+ uip_debug_dump_node(node->right_sibling, depth);
+ }
}
static void
-uip_debug_dump (void)
-{
- sc_trace ("UIParser: debug dump follows...\n");
- if (uip_parse_tree)
- {
- sc_trace ("uip_parse_tree = {\n");
- uip_debug_dump_node (uip_parse_tree, 0);
- sc_trace ("}\n");
- }
- else
- sc_trace ("uip_parse_tree = (nil)\n");
+uip_debug_dump(void) {
+ sc_trace("UIParser: debug dump follows...\n");
+ if (uip_parse_tree) {
+ sc_trace("uip_parse_tree = {\n");
+ uip_debug_dump_node(uip_parse_tree, 0);
+ sc_trace("}\n");
+ } else
+ sc_trace("uip_parse_tree = (nil)\n");
}
@@ -830,23 +770,21 @@ static sc_gameref_t uip_game = NULL;
* Set up a string for matching to a pattern tree, and wrap up matching.
*/
static void
-uip_match_start (const sc_char *string, sc_gameref_t game)
-{
- /* Save string, and restart index. */
- uip_string = string;
- uip_posn = 0;
-
- /* Save the game we're working on. */
- uip_game = game;
+uip_match_start(const sc_char *string, sc_gameref_t game) {
+ /* Save string, and restart index. */
+ uip_string = string;
+ uip_posn = 0;
+
+ /* Save the game we're working on. */
+ uip_game = game;
}
static void
-uip_match_end (void)
-{
- /* Clear match target string, and variable set. */
- uip_string = NULL;
- uip_posn = 0;
- uip_game = NULL;
+uip_match_end(void) {
+ /* Clear match target string, and variable set. */
+ uip_string = NULL;
+ uip_posn = 0;
+ uip_game = NULL;
}
@@ -857,15 +795,14 @@ uip_match_end (void)
* one.
*/
static sc_gameref_t
-uip_get_game (void)
-{
- assert (gs_is_game_valid (uip_game));
- return uip_game;
+uip_get_game(void) {
+ assert(gs_is_game_valid(uip_game));
+ return uip_game;
}
/* Forward declaration of low level node matcher. */
-static sc_bool uip_match_node (sc_ptnoderef_t node);
+static sc_bool uip_match_node(sc_ptnoderef_t node);
/*
* uip_match_eos()
@@ -883,298 +820,276 @@ static sc_bool uip_match_node (sc_ptnoderef_t node);
* unchanged.
*/
static sc_bool
-uip_match_eos (void)
-{
- /* Check that we hit the string's end. */
- return uip_string[uip_posn] == NUL;
+uip_match_eos(void) {
+ /* Check that we hit the string's end. */
+ return uip_string[uip_posn] == NUL;
}
static sc_bool
-uip_match_word (sc_ptnoderef_t node)
-{
- sc_int length;
- const sc_char *word;
-
- /* Get the word to match. */
- assert (node->word);
- word = node->word;
-
- /* Compare string text with this node's word, ignore case. */
- length = strlen (word);
- if (sc_strncasecmp (uip_string + uip_posn, word, length) == 0)
- {
- /* Word match, advance position and return. */
- uip_posn += length;
- return TRUE;
- }
-
- /* No match. */
- return FALSE;
+uip_match_word(sc_ptnoderef_t node) {
+ sc_int length;
+ const sc_char *word;
+
+ /* Get the word to match. */
+ assert(node->word);
+ word = node->word;
+
+ /* Compare string text with this node's word, ignore case. */
+ length = strlen(word);
+ if (sc_strncasecmp(uip_string + uip_posn, word, length) == 0) {
+ /* Word match, advance position and return. */
+ uip_posn += length;
+ return TRUE;
+ }
+
+ /* No match. */
+ return FALSE;
}
static sc_bool
-uip_match_variable (sc_ptnoderef_t node)
-{
- const sc_gameref_t game = uip_get_game ();
- const sc_var_setref_t vars = gs_get_vars (game);
- sc_int type;
- sc_vartype_t vt_rvalue;
- const sc_char *name;
-
- /* Get the variable name to match, from overloaded word. */
- assert (node->word);
- name = node->word;
-
- /* Get the variable's value. */
- if (var_get (vars, name, &type, &vt_rvalue))
- {
- sc_int length;
-
- /* Compare the value against the current string position. */
- switch (type)
- {
- case VAR_INTEGER:
- {
- sc_char value[32];
-
- /* Compare numeric against the current string position. */
- sprintf (value, "%ld", vt_rvalue.integer);
- length = strlen (value);
- if (strncmp (uip_string + uip_posn, value, length) == 0)
- {
- /* Integer match, advance position and return. */
- uip_posn += length;
- return TRUE;
- }
- break;
- }
-
- case VAR_STRING:
- /* Compare string value against the current string position. */
- length = strlen (vt_rvalue.string);
- if (sc_strncasecmp (uip_string + uip_posn,
- vt_rvalue.string, length) == 0)
- {
- /* String match, advance position and return. */
- uip_posn += length;
- return TRUE;
- }
- break;
-
- default:
- sc_fatal ("uip_match_variable: invalid variable type, %ld\n", type);
- }
- }
-
- /* No match, or no such variable. */
- return FALSE;
+uip_match_variable(sc_ptnoderef_t node) {
+ const sc_gameref_t game = uip_get_game();
+ const sc_var_setref_t vars = gs_get_vars(game);
+ sc_int type;
+ sc_vartype_t vt_rvalue;
+ const sc_char *name;
+
+ /* Get the variable name to match, from overloaded word. */
+ assert(node->word);
+ name = node->word;
+
+ /* Get the variable's value. */
+ if (var_get(vars, name, &type, &vt_rvalue)) {
+ sc_int length;
+
+ /* Compare the value against the current string position. */
+ switch (type) {
+ case VAR_INTEGER: {
+ sc_char value[32];
+
+ /* Compare numeric against the current string position. */
+ sprintf(value, "%ld", vt_rvalue.integer);
+ length = strlen(value);
+ if (strncmp(uip_string + uip_posn, value, length) == 0) {
+ /* Integer match, advance position and return. */
+ uip_posn += length;
+ return TRUE;
+ }
+ break;
+ }
+
+ case VAR_STRING:
+ /* Compare string value against the current string position. */
+ length = strlen(vt_rvalue.string);
+ if (sc_strncasecmp(uip_string + uip_posn,
+ vt_rvalue.string, length) == 0) {
+ /* String match, advance position and return. */
+ uip_posn += length;
+ return TRUE;
+ }
+ break;
+
+ default:
+ sc_fatal("uip_match_variable: invalid variable type, %ld\n", type);
+ }
+ }
+
+ /* No match, or no such variable. */
+ return FALSE;
}
static sc_bool
-uip_match_whitespace (void)
-{
- /* If next character is space, read whitespace and return. */
- if (sc_isspace (uip_string[uip_posn]))
- {
- /* Space match, advance position and return. */
- while (uip_string[uip_posn] != NUL && sc_isspace (uip_string[uip_posn]))
- uip_posn++;
- return TRUE;
- }
-
- /*
- * No match. However, if we're trying to match space, this is a word
- * boundary. So... even though we're not sitting on a space, if the string
- * prior character is whitespace, "double-match" the space.
- *
- * Also, match if we haven't yet matched any text. In effect, this means
- * leading spaces on patterns will be ignored.
- *
- * TODO Is this what we want to happen? It seems harmless, even useful.
- */
- if (uip_posn == 0 || sc_isspace (uip_string[uip_posn - 1]))
- return TRUE;
-
- /*
- * And that's not all. We also want to match whitespace if we're at the end
- * of a string (another word boundary). This will permit patterns that end
- * in optional elements to succeed since options and wildcards always match,
- * even if to no text.
- */
- if (uip_string[uip_posn] == NUL)
- return TRUE;
-
- /* No match. Really. */
- return FALSE;
+uip_match_whitespace(void) {
+ /* If next character is space, read whitespace and return. */
+ if (sc_isspace(uip_string[uip_posn])) {
+ /* Space match, advance position and return. */
+ while (uip_string[uip_posn] != NUL && sc_isspace(uip_string[uip_posn]))
+ uip_posn++;
+ return TRUE;
+ }
+
+ /*
+ * No match. However, if we're trying to match space, this is a word
+ * boundary. So... even though we're not sitting on a space, if the string
+ * prior character is whitespace, "double-match" the space.
+ *
+ * Also, match if we haven't yet matched any text. In effect, this means
+ * leading spaces on patterns will be ignored.
+ *
+ * TODO Is this what we want to happen? It seems harmless, even useful.
+ */
+ if (uip_posn == 0 || sc_isspace(uip_string[uip_posn - 1]))
+ return TRUE;
+
+ /*
+ * And that's not all. We also want to match whitespace if we're at the end
+ * of a string (another word boundary). This will permit patterns that end
+ * in optional elements to succeed since options and wildcards always match,
+ * even if to no text.
+ */
+ if (uip_string[uip_posn] == NUL)
+ return TRUE;
+
+ /* No match. Really. */
+ return FALSE;
}
static sc_bool
-uip_match_list (sc_ptnoderef_t node)
-{
- sc_ptnoderef_t child;
-
- /*
- * If this list is empty, fail the match. This special-case handling is
- * what catches constructed temporary lists for wildcard-like items that
- * don't actually encompass anything.
- */
- if (!node->left_child)
- return FALSE;
-
- /* Match everything listed sequentially. */
- for (child = node->left_child; child; child = child->right_sibling)
- {
- if (!uip_match_node (child))
- {
- /* No match. */
- return FALSE;
- }
- }
-
- /* Matched. */
- return TRUE;
+uip_match_list(sc_ptnoderef_t node) {
+ sc_ptnoderef_t child;
+
+ /*
+ * If this list is empty, fail the match. This special-case handling is
+ * what catches constructed temporary lists for wildcard-like items that
+ * don't actually encompass anything.
+ */
+ if (!node->left_child)
+ return FALSE;
+
+ /* Match everything listed sequentially. */
+ for (child = node->left_child; child; child = child->right_sibling) {
+ if (!uip_match_node(child)) {
+ /* No match. */
+ return FALSE;
+ }
+ }
+
+ /* Matched. */
+ return TRUE;
}
static sc_bool
-uip_match_alternatives (sc_ptnoderef_t node)
-{
- sc_ptnoderef_t child;
- sc_int start_posn, extent;
- sc_bool matched;
-
- /* Note the start position for rewind between tries. */
- start_posn = uip_posn;
-
- /*
- * Try a match on each of the children, looking to see which one moves the
- * position on the furthest. Match on this one. This is a "maximal munch".
- */
- extent = uip_posn;
- matched = FALSE;
- for (child = node->left_child; child; child = child->right_sibling)
- {
- uip_posn = start_posn;
- if (uip_match_node (child))
- {
- /* Matched. */
- matched = TRUE;
- if (uip_posn > extent)
- extent = uip_posn;
- }
- }
-
- /* If matched, set position to extent; if not, back to start. */
- uip_posn = matched ? extent : start_posn;
-
- /* Return match status. */
- return matched;
+uip_match_alternatives(sc_ptnoderef_t node) {
+ sc_ptnoderef_t child;
+ sc_int start_posn, extent;
+ sc_bool matched;
+
+ /* Note the start position for rewind between tries. */
+ start_posn = uip_posn;
+
+ /*
+ * Try a match on each of the children, looking to see which one moves the
+ * position on the furthest. Match on this one. This is a "maximal munch".
+ */
+ extent = uip_posn;
+ matched = FALSE;
+ for (child = node->left_child; child; child = child->right_sibling) {
+ uip_posn = start_posn;
+ if (uip_match_node(child)) {
+ /* Matched. */
+ matched = TRUE;
+ if (uip_posn > extent)
+ extent = uip_posn;
+ }
+ }
+
+ /* If matched, set position to extent; if not, back to start. */
+ uip_posn = matched ? extent : start_posn;
+
+ /* Return match status. */
+ return matched;
}
static sc_bool
-uip_match_choice (sc_ptnoderef_t node)
-{
- /*
- * Return the result of matching alternatives. The choice will therefore
- * fail if none of the alternatives match.
- */
- return uip_match_alternatives (node);
+uip_match_choice(sc_ptnoderef_t node) {
+ /*
+ * Return the result of matching alternatives. The choice will therefore
+ * fail if none of the alternatives match.
+ */
+ return uip_match_alternatives(node);
}
static sc_bool
-uip_match_optional (sc_ptnoderef_t node)
-{
- sc_int start_posn;
- sc_ptnoderef_t list;
- sc_bool matched;
-
- /* Note the start position for rewind on empty match. */
- start_posn = uip_posn;
-
- /*
- * Look ahead to see if we can match to nothing, and still have the main
- * pattern match. If we can, we'll go with this. It's a "minimal munch"-ish
- * strategy, but seems to be what Adrift does in this situation.
- */
- list = uip_new_node (NODE_LIST);
- list->left_child = node->right_sibling;
-
- /* Match on the temporary list. */
- matched = uip_match_node (list);
-
- /* Free the temporary list node. */
- uip_destroy_node (list);
-
- /*
- * If the temporary matched and consumed text, rewind position to match
- * nothing. If it didn't, match alternatives to consume anything that may
- * match our options.
- */
- if (matched && uip_posn > start_posn)
- uip_posn = start_posn;
- else
- uip_match_alternatives (node);
-
- /* Return TRUE no matter what. */
- return TRUE;
+uip_match_optional(sc_ptnoderef_t node) {
+ sc_int start_posn;
+ sc_ptnoderef_t list;
+ sc_bool matched;
+
+ /* Note the start position for rewind on empty match. */
+ start_posn = uip_posn;
+
+ /*
+ * Look ahead to see if we can match to nothing, and still have the main
+ * pattern match. If we can, we'll go with this. It's a "minimal munch"-ish
+ * strategy, but seems to be what Adrift does in this situation.
+ */
+ list = uip_new_node(NODE_LIST);
+ list->left_child = node->right_sibling;
+
+ /* Match on the temporary list. */
+ matched = uip_match_node(list);
+
+ /* Free the temporary list node. */
+ uip_destroy_node(list);
+
+ /*
+ * If the temporary matched and consumed text, rewind position to match
+ * nothing. If it didn't, match alternatives to consume anything that may
+ * match our options.
+ */
+ if (matched && uip_posn > start_posn)
+ uip_posn = start_posn;
+ else
+ uip_match_alternatives(node);
+
+ /* Return TRUE no matter what. */
+ return TRUE;
}
static sc_bool
-uip_match_wildcard (sc_ptnoderef_t node)
-{
- sc_int start_posn, limit, index_;
- sc_bool matched;
- sc_ptnoderef_t list;
-
- /*
- * At least one game uses patterns like "thing******...". Why? Who knows.
- * But if we're in a list of wildcards, and not the first, ignore the call;
- * only the final one needs handling.
- */
- if (node->right_sibling && node->right_sibling->type == NODE_WILDCARD)
- return TRUE;
-
- /* Note the start position for rewind on no match. */
- start_posn = uip_posn;
-
- /*
- * To make life a little easier, we'll match on the tree to the right of
- * this node by constructing a temporary list node, containing stuff to the
- * right of the wildcard, and then matching on that.
- */
- list = uip_new_node (NODE_LIST);
- list->left_child = node->right_sibling;
-
- /*
- * Repeatedly try to match the rest of the tree at successive character
- * positions, and stop if we succeed. This is a "minimal munch", which may
- * or may not be the right thing to be doing here.
- *
- * When scanning forward, take care to include the NUL, needed to match
- * TOK_EOS.
- */
- matched = FALSE;
- limit = strlen (uip_string) + 1;
- for (index_ = uip_posn + 1; index_ < limit; index_++)
- {
- uip_posn = index_;
- if (uip_match_node (list))
- {
- /* Wildcard match at this point. */
- uip_posn = index_;
- matched = TRUE;
- break;
- }
- }
-
- /* Free the temporary list node. */
- uip_destroy_node (list);
-
- /* If we didn't match in the loop, restore position. */
- if (!matched)
- uip_posn = start_posn;
-
- /* Return TRUE whether we matched text or not. */
- return TRUE;
+uip_match_wildcard(sc_ptnoderef_t node) {
+ sc_int start_posn, limit, index_;
+ sc_bool matched;
+ sc_ptnoderef_t list;
+
+ /*
+ * At least one game uses patterns like "thing******...". Why? Who knows.
+ * But if we're in a list of wildcards, and not the first, ignore the call;
+ * only the final one needs handling.
+ */
+ if (node->right_sibling && node->right_sibling->type == NODE_WILDCARD)
+ return TRUE;
+
+ /* Note the start position for rewind on no match. */
+ start_posn = uip_posn;
+
+ /*
+ * To make life a little easier, we'll match on the tree to the right of
+ * this node by constructing a temporary list node, containing stuff to the
+ * right of the wildcard, and then matching on that.
+ */
+ list = uip_new_node(NODE_LIST);
+ list->left_child = node->right_sibling;
+
+ /*
+ * Repeatedly try to match the rest of the tree at successive character
+ * positions, and stop if we succeed. This is a "minimal munch", which may
+ * or may not be the right thing to be doing here.
+ *
+ * When scanning forward, take care to include the NUL, needed to match
+ * TOK_EOS.
+ */
+ matched = FALSE;
+ limit = strlen(uip_string) + 1;
+ for (index_ = uip_posn + 1; index_ < limit; index_++) {
+ uip_posn = index_;
+ if (uip_match_node(list)) {
+ /* Wildcard match at this point. */
+ uip_posn = index_;
+ matched = TRUE;
+ break;
+ }
+ }
+
+ /* Free the temporary list node. */
+ uip_destroy_node(list);
+
+ /* If we didn't match in the loop, restore position. */
+ if (!matched)
+ uip_posn = start_posn;
+
+ /* Return TRUE whether we matched text or not. */
+ return TRUE;
}
@@ -1185,100 +1100,92 @@ uip_match_wildcard (sc_ptnoderef_t node)
* Attempt to match a number, or a word, from the string.
*/
static sc_bool
-uip_match_number (void)
-{
- const sc_gameref_t game = uip_get_game ();
- const sc_var_setref_t vars = gs_get_vars (game);
- sc_int number;
-
- /* Attempt to read a number from input. */
- if (sscanf (uip_string + uip_posn, "%ld", &number) == 1)
- {
- /* Advance position over the number. */
- while (uip_string[uip_posn] == MINUS || uip_string[uip_posn] == PLUS)
- uip_posn++;
- while (sc_isdigit (uip_string[uip_posn]))
- uip_posn++;
-
- /* Set number reference in variables and return. */
- var_set_ref_number (vars, number);
- return TRUE;
- }
-
- /* No match. */
- return FALSE;
+uip_match_number(void) {
+ const sc_gameref_t game = uip_get_game();
+ const sc_var_setref_t vars = gs_get_vars(game);
+ sc_int number;
+
+ /* Attempt to read a number from input. */
+ if (sscanf(uip_string + uip_posn, "%ld", &number) == 1) {
+ /* Advance position over the number. */
+ while (uip_string[uip_posn] == MINUS || uip_string[uip_posn] == PLUS)
+ uip_posn++;
+ while (sc_isdigit(uip_string[uip_posn]))
+ uip_posn++;
+
+ /* Set number reference in variables and return. */
+ var_set_ref_number(vars, number);
+ return TRUE;
+ }
+
+ /* No match. */
+ return FALSE;
}
static sc_bool
-uip_match_text (sc_ptnoderef_t node)
-{
- const sc_gameref_t game = uip_get_game ();
- const sc_var_setref_t vars = gs_get_vars (game);
- sc_int start_posn, limit, index_;
- sc_bool matched;
- sc_ptnoderef_t list;
-
- /* Note the start position for rewind on no match. */
- start_posn = uip_posn;
-
- /*
- * As with wildcards, create a temporary list of the stuff to the right of
- * the reference node, and match on that.
- */
- list = uip_new_node (NODE_LIST);
- list->left_child = node->right_sibling;
-
- /*
- * Again, as with wildcards, repeatedly try to match the rest of the tree at
- * successive character positions, stopping if we succeed.
- */
- matched = FALSE;
- limit = strlen (uip_string) + 1;
- for (index_ = uip_posn + 1; index_ < limit; index_++)
- {
- uip_posn = index_;
- if (uip_match_node (list))
- {
- /* Text reference match at this point. */
- uip_posn = index_;
- matched = TRUE;
- break;
- }
- }
-
- /* Free the temporary list node. */
- uip_destroy_node (list);
-
- /* See if we found a match in the loop. */
- if (matched)
- {
- sc_char *string;
-
- /* Found a match; create a string and save the text. */
- string = (sc_char *)sc_malloc (uip_posn - start_posn + 1);
- memcpy (string, uip_string + start_posn, uip_posn - start_posn);
- string[uip_posn - start_posn] = NUL;
-
- /*
- * Adrift seems to save referenced text as all-lowercase; we need to do
- * the same.
- */
- for (index_ = 0; string[index_] != NUL; index_++)
- string[index_] = sc_tolower (string[index_]);
- var_set_ref_text (vars, string);
- sc_free (string);
-
- /* Return TRUE since we matched text. */
- return TRUE;
- }
- else
- {
- /* We didn't match in the loop; restore position. */
- uip_posn = start_posn;
-
- /* Return FALSE on no match. */
- return FALSE;
- }
+uip_match_text(sc_ptnoderef_t node) {
+ const sc_gameref_t game = uip_get_game();
+ const sc_var_setref_t vars = gs_get_vars(game);
+ sc_int start_posn, limit, index_;
+ sc_bool matched;
+ sc_ptnoderef_t list;
+
+ /* Note the start position for rewind on no match. */
+ start_posn = uip_posn;
+
+ /*
+ * As with wildcards, create a temporary list of the stuff to the right of
+ * the reference node, and match on that.
+ */
+ list = uip_new_node(NODE_LIST);
+ list->left_child = node->right_sibling;
+
+ /*
+ * Again, as with wildcards, repeatedly try to match the rest of the tree at
+ * successive character positions, stopping if we succeed.
+ */
+ matched = FALSE;
+ limit = strlen(uip_string) + 1;
+ for (index_ = uip_posn + 1; index_ < limit; index_++) {
+ uip_posn = index_;
+ if (uip_match_node(list)) {
+ /* Text reference match at this point. */
+ uip_posn = index_;
+ matched = TRUE;
+ break;
+ }
+ }
+
+ /* Free the temporary list node. */
+ uip_destroy_node(list);
+
+ /* See if we found a match in the loop. */
+ if (matched) {
+ sc_char *string;
+
+ /* Found a match; create a string and save the text. */
+ string = (sc_char *)sc_malloc(uip_posn - start_posn + 1);
+ memcpy(string, uip_string + start_posn, uip_posn - start_posn);
+ string[uip_posn - start_posn] = NUL;
+
+ /*
+ * Adrift seems to save referenced text as all-lowercase; we need to do
+ * the same.
+ */
+ for (index_ = 0; string[index_] != NUL; index_++)
+ string[index_] = sc_tolower(string[index_]);
+ var_set_ref_text(vars, string);
+ sc_free(string);
+
+ /* Return TRUE since we matched text. */
+ return TRUE;
+ } else {
+ /* We didn't match in the loop; restore position. */
+ uip_posn = start_posn;
+
+ /* Return FALSE on no match. */
+ return FALSE;
+ }
}
@@ -1289,25 +1196,24 @@ uip_match_text (sc_ptnoderef_t node)
* %character% and %object% matchers. Returns the revised string position.
*/
static sc_int
-uip_skip_article (const sc_char *string, sc_int start)
-{
- sc_int posn;
-
- /* Skip over articles. */
- posn = start;
- if (sc_compare_word (string + posn, "a", 1))
- posn += 1;
- else if (sc_compare_word (string + posn, "an", 2))
- posn += 2;
- else if (sc_compare_word (string + posn, "the", 3))
- posn += 3;
- else if (sc_compare_word (string + posn, "some", 4))
- posn += 4;
-
- /* Skip any whitespace, and return. */
- while (sc_isspace (string[posn]) && string[posn] != NUL)
- posn++;
- return posn;
+uip_skip_article(const sc_char *string, sc_int start) {
+ sc_int posn;
+
+ /* Skip over articles. */
+ posn = start;
+ if (sc_compare_word(string + posn, "a", 1))
+ posn += 1;
+ else if (sc_compare_word(string + posn, "an", 2))
+ posn += 2;
+ else if (sc_compare_word(string + posn, "the", 3))
+ posn += 3;
+ else if (sc_compare_word(string + posn, "some", 4))
+ posn += 4;
+
+ /* Skip any whitespace, and return. */
+ while (sc_isspace(string[posn]) && string[posn] != NUL)
+ posn++;
+ return posn;
}
@@ -1320,51 +1226,49 @@ uip_skip_article (const sc_char *string, sc_int start)
* the words passed in (the new value of uip_posn on match).
*/
static sc_int
-uip_compare_reference (const sc_char *words)
-{
- sc_int wpos, posn;
-
- /* Skip articles and lead in space on words and string. */
- wpos = uip_skip_article (words, 0);
- posn = uip_skip_article (uip_string, uip_posn);
-
- /* Match characters from words with the string at position. */
- while (TRUE)
- {
- /* Any character mismatch means no words match. */
- if (sc_tolower (words[wpos]) != sc_tolower (uip_string[posn]))
- return 0;
-
- /* Move to next character in each. */
- wpos++;
- posn++;
-
- /*
- * If at space, advance over whitespace in words list. Stop when we
- * hit the end of the words list.
- */
- while (sc_isspace (words[wpos]) && words[wpos] != NUL)
- wpos++;
- if (words[wpos] == NUL)
- break;
-
- /*
- * About to match another word, so advance over whitespace in the
- * current string too.
- */
- while (sc_isspace (uip_string[posn]) && uip_string[posn] != NUL)
- posn++;
- }
-
- /*
- * We reached the end of words. If we're at the end of the match string, or
- * at spaces, we've matched.
- */
- if (sc_isspace (uip_string[posn]) || uip_string[posn] == NUL)
- return posn;
-
- /* More text after the match, so it's not quite a match. */
- return 0;
+uip_compare_reference(const sc_char *words) {
+ sc_int wpos, posn;
+
+ /* Skip articles and lead in space on words and string. */
+ wpos = uip_skip_article(words, 0);
+ posn = uip_skip_article(uip_string, uip_posn);
+
+ /* Match characters from words with the string at position. */
+ while (TRUE) {
+ /* Any character mismatch means no words match. */
+ if (sc_tolower(words[wpos]) != sc_tolower(uip_string[posn]))
+ return 0;
+
+ /* Move to next character in each. */
+ wpos++;
+ posn++;
+
+ /*
+ * If at space, advance over whitespace in words list. Stop when we
+ * hit the end of the words list.
+ */
+ while (sc_isspace(words[wpos]) && words[wpos] != NUL)
+ wpos++;
+ if (words[wpos] == NUL)
+ break;
+
+ /*
+ * About to match another word, so advance over whitespace in the
+ * current string too.
+ */
+ while (sc_isspace(uip_string[posn]) && uip_string[posn] != NUL)
+ posn++;
+ }
+
+ /*
+ * We reached the end of words. If we're at the end of the match string, or
+ * at spaces, we've matched.
+ */
+ if (sc_isspace(uip_string[posn]) || uip_string[posn] == NUL)
+ return posn;
+
+ /* More text after the match, so it's not quite a match. */
+ return 0;
}
@@ -1376,28 +1280,27 @@ uip_compare_reference (const sc_char *words)
* Returns the extent of the match, or zero if no match.
*/
static sc_int
-uip_compare_prefixed_name (const sc_char *prefix, const sc_char *name)
-{
- sc_char buffer[UIP_SHORT_WORD_SIZE + UIP_SHORT_WORD_SIZE + 1];
- sc_char *string;
- sc_int required, extent;
-
- /* Create a prefixed string, using the local buffer if possible. */
- required = strlen (prefix) + strlen (name) + 2;
- string = required > (sc_int) sizeof (buffer) ? (sc_char *)sc_malloc (required) : buffer;
- sprintf (string, "%s %s", prefix, name);
-
- /* Check against the prefixed name first, free string if required. */
- extent = uip_compare_reference (string);
- if (string != buffer)
- sc_free (string);
-
- /* If no match there, retry with just the plain name. */
- if (extent == 0)
- extent = uip_compare_reference (name);
-
- /* Return the count of characters consumed in matching. */
- return extent;
+uip_compare_prefixed_name(const sc_char *prefix, const sc_char *name) {
+ sc_char buffer[UIP_SHORT_WORD_SIZE + UIP_SHORT_WORD_SIZE + 1];
+ sc_char *string;
+ sc_int required, extent;
+
+ /* Create a prefixed string, using the local buffer if possible. */
+ required = strlen(prefix) + strlen(name) + 2;
+ string = required > (sc_int) sizeof(buffer) ? (sc_char *)sc_malloc(required) : buffer;
+ sprintf(string, "%s %s", prefix, name);
+
+ /* Check against the prefixed name first, free string if required. */
+ extent = uip_compare_reference(string);
+ if (string != buffer)
+ sc_free(string);
+
+ /* If no match there, retry with just the plain name. */
+ if (extent == 0)
+ extent = uip_compare_reference(name);
+
+ /* Return the count of characters consumed in matching. */
+ return extent;
}
@@ -1409,32 +1312,31 @@ uip_compare_prefixed_name (const sc_char *prefix, const sc_char *name)
* "table".
*/
static sc_bool
-uip_match_remainder (sc_ptnoderef_t node, sc_int extent)
-{
- sc_ptnoderef_t list;
- sc_int start_posn;
- sc_bool matched;
-
- /* Note the start position, then advance to the given extent. */
- start_posn = uip_posn;
- uip_posn = extent;
-
- /*
- * Try to match everything after the node passed in, at this position in the
- * string.
- */
- list = uip_new_node (NODE_LIST);
- list->left_child = node->right_sibling;
-
- /* Match on the temporary list. */
- matched = uip_match_node (list);
-
- /* Free the temporary list node, and restore position. */
- uip_destroy_node (list);
- uip_posn = start_posn;
-
- /* Return TRUE if the pattern remainder matched. */
- return matched;
+uip_match_remainder(sc_ptnoderef_t node, sc_int extent) {
+ sc_ptnoderef_t list;
+ sc_int start_posn;
+ sc_bool matched;
+
+ /* Note the start position, then advance to the given extent. */
+ start_posn = uip_posn;
+ uip_posn = extent;
+
+ /*
+ * Try to match everything after the node passed in, at this position in the
+ * string.
+ */
+ list = uip_new_node(NODE_LIST);
+ list->left_child = node->right_sibling;
+
+ /* Match on the temporary list. */
+ matched = uip_match_node(list);
+
+ /* Free the temporary list node, and restore position. */
+ uip_destroy_node(list);
+ uip_posn = start_posn;
+
+ /* Return TRUE if the pattern remainder matched. */
+ return matched;
}
@@ -1446,100 +1348,94 @@ uip_match_remainder (sc_ptnoderef_t node, sc_int extent)
* for any that match. The final one to match is also stored in variables.
*/
static sc_bool
-uip_match_character (sc_ptnoderef_t node)
-{
- const sc_gameref_t game = uip_get_game ();
- const sc_prop_setref_t bundle = gs_get_bundle (game);
- const sc_var_setref_t vars = gs_get_vars (game);
- sc_int npc_count, npc, max_extent;
-
- if (uip_trace)
- sc_trace ("UIParser: attempting to match %%character%%\n");
-
- /* Clear all current character references. */
- gs_clear_npc_references (game);
-
- /* Iterate characters, looking for a name or alias match. */
- max_extent = 0;
- npc_count = gs_npc_count (game);
- for (npc = 0; npc < npc_count; npc++)
- {
- sc_vartype_t vt_key[4];
- const sc_char *prefix, *name;
- sc_int alias_count, alias, extent;
-
- /* Get the NPC's prefix and name. */
- vt_key[0].string = "NPCs";
- vt_key[1].integer = npc;
- vt_key[2].string = "Prefix";
- prefix = prop_get_string (bundle, "S<-sis", vt_key);
- vt_key[2].string = "Name";
- name = prop_get_string (bundle, "S<-sis", vt_key);
-
- if (uip_trace)
- sc_trace ("UIParser: trying %s\n", name);
-
- /* Compare this name, both prefixed and not. */
- extent = uip_compare_prefixed_name (prefix, name);
- if (extent > 0 && uip_match_remainder (node, extent))
- {
- if (uip_trace)
- sc_trace ("UIParser: matched\n");
-
- /* Increase the maximum match extent if required. */
- max_extent = (extent > max_extent) ? extent : max_extent;
-
- /* Save match in variables and game. */
- var_set_ref_character (vars, npc);
- game->npc_references[npc] = TRUE;
- }
-
- /* Now compare against all NPC aliases. */
- vt_key[2].string = "Alias";
- alias_count = prop_get_child_count (bundle, "I<-sis", vt_key);
-
- for (alias = 0; alias < alias_count; alias++)
- {
- const sc_char *alias_name;
-
- /*
- * Get the NPC alias. Version 3.9 games introduce empty aliases,
- * so check here.
- */
- vt_key[3].integer = alias;
- alias_name = prop_get_string (bundle, "S<-sisi", vt_key);
- if (sc_strempty (alias_name))
- continue;
-
- if (uip_trace)
- sc_trace ("UIParser: trying alias %s\n", alias_name);
-
- /* Compare this alias name, both prefixed and not. */
- extent = uip_compare_prefixed_name (prefix, alias_name);
- if (extent > 0 && uip_match_remainder (node, extent))
- {
- if (uip_trace)
- sc_trace ("UIParser: matched\n");
-
- /* Increase the maximum match extent if required. */
- max_extent = (extent > max_extent) ? extent : max_extent;
-
- /* Save match in variables and game. */
- var_set_ref_character (vars, npc);
- game->npc_references[npc] = TRUE;
- }
- }
- }
-
- /* On match, advance position and return successfully. */
- if (max_extent > 0)
- {
- uip_posn = max_extent;
- return TRUE;
- }
-
- /* No match. */
- return FALSE;
+uip_match_character(sc_ptnoderef_t node) {
+ const sc_gameref_t game = uip_get_game();
+ const sc_prop_setref_t bundle = gs_get_bundle(game);
+ const sc_var_setref_t vars = gs_get_vars(game);
+ sc_int npc_count, npc, max_extent;
+
+ if (uip_trace)
+ sc_trace("UIParser: attempting to match %%character%%\n");
+
+ /* Clear all current character references. */
+ gs_clear_npc_references(game);
+
+ /* Iterate characters, looking for a name or alias match. */
+ max_extent = 0;
+ npc_count = gs_npc_count(game);
+ for (npc = 0; npc < npc_count; npc++) {
+ sc_vartype_t vt_key[4];
+ const sc_char *prefix, *name;
+ sc_int alias_count, alias, extent;
+
+ /* Get the NPC's prefix and name. */
+ vt_key[0].string = "NPCs";
+ vt_key[1].integer = npc;
+ vt_key[2].string = "Prefix";
+ prefix = prop_get_string(bundle, "S<-sis", vt_key);
+ vt_key[2].string = "Name";
+ name = prop_get_string(bundle, "S<-sis", vt_key);
+
+ if (uip_trace)
+ sc_trace("UIParser: trying %s\n", name);
+
+ /* Compare this name, both prefixed and not. */
+ extent = uip_compare_prefixed_name(prefix, name);
+ if (extent > 0 && uip_match_remainder(node, extent)) {
+ if (uip_trace)
+ sc_trace("UIParser: matched\n");
+
+ /* Increase the maximum match extent if required. */
+ max_extent = (extent > max_extent) ? extent : max_extent;
+
+ /* Save match in variables and game. */
+ var_set_ref_character(vars, npc);
+ game->npc_references[npc] = TRUE;
+ }
+
+ /* Now compare against all NPC aliases. */
+ vt_key[2].string = "Alias";
+ alias_count = prop_get_child_count(bundle, "I<-sis", vt_key);
+
+ for (alias = 0; alias < alias_count; alias++) {
+ const sc_char *alias_name;
+
+ /*
+ * Get the NPC alias. Version 3.9 games introduce empty aliases,
+ * so check here.
+ */
+ vt_key[3].integer = alias;
+ alias_name = prop_get_string(bundle, "S<-sisi", vt_key);
+ if (sc_strempty(alias_name))
+ continue;
+
+ if (uip_trace)
+ sc_trace("UIParser: trying alias %s\n", alias_name);
+
+ /* Compare this alias name, both prefixed and not. */
+ extent = uip_compare_prefixed_name(prefix, alias_name);
+ if (extent > 0 && uip_match_remainder(node, extent)) {
+ if (uip_trace)
+ sc_trace("UIParser: matched\n");
+
+ /* Increase the maximum match extent if required. */
+ max_extent = (extent > max_extent) ? extent : max_extent;
+
+ /* Save match in variables and game. */
+ var_set_ref_character(vars, npc);
+ game->npc_references[npc] = TRUE;
+ }
+ }
+ }
+
+ /* On match, advance position and return successfully. */
+ if (max_extent > 0) {
+ uip_posn = max_extent;
+ return TRUE;
+ }
+
+ /* No match. */
+ return FALSE;
}
@@ -1551,100 +1447,94 @@ uip_match_character (sc_ptnoderef_t node)
* for any that match. The final one to match is also stored in variables.
*/
static sc_bool
-uip_match_object (sc_ptnoderef_t node)
-{
- const sc_gameref_t game = uip_get_game ();
- const sc_prop_setref_t bundle = gs_get_bundle (game);
- const sc_var_setref_t vars = gs_get_vars (game);
- sc_int object_count, object, max_extent;
-
- if (uip_trace)
- sc_trace ("UIParser: attempting to match %%object%%\n");
-
- /* Clear all current object references. */
- gs_clear_object_references (game);
-
- /* Iterate objects, looking for a name or alias match. */
- max_extent = 0;
- object_count = gs_object_count (game);
- for (object = 0; object < object_count; object++)
- {
- sc_vartype_t vt_key[4];
- const sc_char *prefix, *name;
- sc_int alias_count, alias, extent;
-
- /* Get the object's prefix and name. */
- vt_key[0].string = "Objects";
- vt_key[1].integer = object;
- vt_key[2].string = "Prefix";
- prefix = prop_get_string (bundle, "S<-sis", vt_key);
- vt_key[2].string = "Short";
- name = prop_get_string (bundle, "S<-sis", vt_key);
-
- if (uip_trace)
- sc_trace ("UIParser: trying %s\n", name);
-
- /* Compare this name, both prefixed and not. */
- extent = uip_compare_prefixed_name (prefix, name);
- if (extent > 0 && uip_match_remainder (node, extent))
- {
- if (uip_trace)
- sc_trace ("UIParser: matched\n");
-
- /* Increase the maximum match extent if required. */
- max_extent = (extent > max_extent) ? extent : max_extent;
-
- /* Save match in variables and game. */
- var_set_ref_object (vars, object);
- game->object_references[object] = TRUE;
- }
-
- /* Now compare against all object aliases. */
- vt_key[2].string = "Alias";
- alias_count = prop_get_child_count (bundle, "I<-sis", vt_key);
-
- for (alias = 0; alias < alias_count; alias++)
- {
- const sc_char *alias_name;
-
- /*
- * Get the object alias. Version 3.9 games introduce empty aliases,
- * so check here.
- */
- vt_key[3].integer = alias;
- alias_name = prop_get_string (bundle, "S<-sisi", vt_key);
- if (sc_strempty (alias_name))
- continue;
-
- if (uip_trace)
- sc_trace ("UIParser: trying alias %s\n", alias_name);
-
- /* Compare this alias name, both prefixed and not. */
- extent = uip_compare_prefixed_name (prefix, alias_name);
- if (extent > 0 && uip_match_remainder (node, extent))
- {
- if (uip_trace)
- sc_trace ("UIParser: matched\n");
-
- /* Increase the maximum match extent if required. */
- max_extent = (extent > max_extent) ? extent : max_extent;
-
- /* Save match in variables and game. */
- var_set_ref_object (vars, object);
- game->object_references[object] = TRUE;
- }
- }
- }
-
- /* On match, advance position and return successfully. */
- if (max_extent > 0)
- {
- uip_posn = max_extent;
- return TRUE;
- }
-
- /* No match. */
- return FALSE;
+uip_match_object(sc_ptnoderef_t node) {
+ const sc_gameref_t game = uip_get_game();
+ const sc_prop_setref_t bundle = gs_get_bundle(game);
+ const sc_var_setref_t vars = gs_get_vars(game);
+ sc_int object_count, object, max_extent;
+
+ if (uip_trace)
+ sc_trace("UIParser: attempting to match %%object%%\n");
+
+ /* Clear all current object references. */
+ gs_clear_object_references(game);
+
+ /* Iterate objects, looking for a name or alias match. */
+ max_extent = 0;
+ object_count = gs_object_count(game);
+ for (object = 0; object < object_count; object++) {
+ sc_vartype_t vt_key[4];
+ const sc_char *prefix, *name;
+ sc_int alias_count, alias, extent;
+
+ /* Get the object's prefix and name. */
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Prefix";
+ prefix = prop_get_string(bundle, "S<-sis", vt_key);
+ vt_key[2].string = "Short";
+ name = prop_get_string(bundle, "S<-sis", vt_key);
+
+ if (uip_trace)
+ sc_trace("UIParser: trying %s\n", name);
+
+ /* Compare this name, both prefixed and not. */
+ extent = uip_compare_prefixed_name(prefix, name);
+ if (extent > 0 && uip_match_remainder(node, extent)) {
+ if (uip_trace)
+ sc_trace("UIParser: matched\n");
+
+ /* Increase the maximum match extent if required. */
+ max_extent = (extent > max_extent) ? extent : max_extent;
+
+ /* Save match in variables and game. */
+ var_set_ref_object(vars, object);
+ game->object_references[object] = TRUE;
+ }
+
+ /* Now compare against all object aliases. */
+ vt_key[2].string = "Alias";
+ alias_count = prop_get_child_count(bundle, "I<-sis", vt_key);
+
+ for (alias = 0; alias < alias_count; alias++) {
+ const sc_char *alias_name;
+
+ /*
+ * Get the object alias. Version 3.9 games introduce empty aliases,
+ * so check here.
+ */
+ vt_key[3].integer = alias;
+ alias_name = prop_get_string(bundle, "S<-sisi", vt_key);
+ if (sc_strempty(alias_name))
+ continue;
+
+ if (uip_trace)
+ sc_trace("UIParser: trying alias %s\n", alias_name);
+
+ /* Compare this alias name, both prefixed and not. */
+ extent = uip_compare_prefixed_name(prefix, alias_name);
+ if (extent > 0 && uip_match_remainder(node, extent)) {
+ if (uip_trace)
+ sc_trace("UIParser: matched\n");
+
+ /* Increase the maximum match extent if required. */
+ max_extent = (extent > max_extent) ? extent : max_extent;
+
+ /* Save match in variables and game. */
+ var_set_ref_object(vars, object);
+ game->object_references[object] = TRUE;
+ }
+ }
+ }
+
+ /* On match, advance position and return successfully. */
+ if (max_extent > 0) {
+ uip_posn = max_extent;
+ return TRUE;
+ }
+
+ /* No match. */
+ return FALSE;
}
@@ -1656,54 +1546,52 @@ uip_match_object (sc_ptnoderef_t node)
* position unchanged.
*/
static sc_bool
-uip_match_node (sc_ptnoderef_t node)
-{
- sc_bool match = FALSE;
-
- /* Match depending on node type. */
- switch (node->type)
- {
- case NODE_EOS:
- match = uip_match_eos ();
- break;
- case NODE_WORD:
- match = uip_match_word (node);
- break;
- case NODE_VARIABLE:
- match = uip_match_variable (node);
- break;
- case NODE_WHITESPACE:
- match = uip_match_whitespace ();
- break;
- case NODE_LIST:
- match = uip_match_list (node);
- break;
- case NODE_CHOICE:
- match = uip_match_choice (node);
- break;
- case NODE_OPTIONAL:
- match = uip_match_optional (node);
- break;
- case NODE_WILDCARD:
- match = uip_match_wildcard (node);
- break;
- case NODE_CHARACTER_REFERENCE:
- match = uip_match_character (node);
- break;
- case NODE_OBJECT_REFERENCE:
- match = uip_match_object (node);
- break;
- case NODE_NUMBER_REFERENCE:
- match = uip_match_number ();
- break;
- case NODE_TEXT_REFERENCE:
- match = uip_match_text (node);
- break;
- default:
- sc_fatal ("uip_match_node: invalid type, %ld\n", (sc_int) node->type);
- }
-
- return match;
+uip_match_node(sc_ptnoderef_t node) {
+ sc_bool match = FALSE;
+
+ /* Match depending on node type. */
+ switch (node->type) {
+ case NODE_EOS:
+ match = uip_match_eos();
+ break;
+ case NODE_WORD:
+ match = uip_match_word(node);
+ break;
+ case NODE_VARIABLE:
+ match = uip_match_variable(node);
+ break;
+ case NODE_WHITESPACE:
+ match = uip_match_whitespace();
+ break;
+ case NODE_LIST:
+ match = uip_match_list(node);
+ break;
+ case NODE_CHOICE:
+ match = uip_match_choice(node);
+ break;
+ case NODE_OPTIONAL:
+ match = uip_match_optional(node);
+ break;
+ case NODE_WILDCARD:
+ match = uip_match_wildcard(node);
+ break;
+ case NODE_CHARACTER_REFERENCE:
+ match = uip_match_character(node);
+ break;
+ case NODE_OBJECT_REFERENCE:
+ match = uip_match_object(node);
+ break;
+ case NODE_NUMBER_REFERENCE:
+ match = uip_match_number();
+ break;
+ case NODE_TEXT_REFERENCE:
+ match = uip_match_text(node);
+ break;
+ default:
+ sc_fatal("uip_match_node: invalid type, %ld\n", (sc_int) node->type);
+ }
+
+ return match;
}
@@ -1718,33 +1606,31 @@ uip_match_node (sc_ptnoderef_t node)
* buffer passed in), or call uip_free_cleansed_string.
*/
static sc_char *
-uip_cleanse_string (const sc_char *original, sc_char *buffer, sc_int length)
-{
- sc_int required;
- sc_char *string;
-
- /*
- * Use the supplied buffer if it is long enough, otherwise allocate, and
- * copy the string.
- */
- required = strlen (original) + 1;
- string = (required < length) ? buffer : (sc_char *)sc_malloc (required);
- strcpy (string, original);
-
- /* Trim, and return the string. */
- sc_trim_string (string);
- return string;
+uip_cleanse_string(const sc_char *original, sc_char *buffer, sc_int length) {
+ sc_int required;
+ sc_char *string;
+
+ /*
+ * Use the supplied buffer if it is long enough, otherwise allocate, and
+ * copy the string.
+ */
+ required = strlen(original) + 1;
+ string = (required < length) ? buffer : (sc_char *)sc_malloc(required);
+ strcpy(string, original);
+
+ /* Trim, and return the string. */
+ sc_trim_string(string);
+ return string;
}
static sc_char *
-uip_free_cleansed_string (sc_char *string, const sc_char *buffer)
-{
- /* Free if the string was allocated by the function above. */
- if (string != buffer)
- sc_free (string);
-
- /* Always returns NULL, for the syntactic convenience of the caller. */
- return NULL;
+uip_free_cleansed_string(sc_char *string, const sc_char *buffer) {
+ /* Free if the string was allocated by the function above. */
+ if (string != buffer)
+ sc_free(string);
+
+ /* Always returns NULL, for the syntactic convenience of the caller. */
+ return NULL;
}
@@ -1754,9 +1640,8 @@ uip_free_cleansed_string (sc_char *string, const sc_char *buffer)
* Set pattern match tracing on/off.
*/
void
-uip_debug_trace (sc_bool flag)
-{
- uip_trace = flag;
+uip_debug_trace(sc_bool flag) {
+ uip_trace = flag;
}
@@ -1768,60 +1653,56 @@ uip_debug_trace (sc_bool flag)
* need to copy each of the pattern and match strings passed in.
*/
sc_bool
-uip_match (const sc_char *pattern, const sc_char *string, sc_gameref_t game)
-{
- static sc_char *cleansed; /* For setjmp safety. */
- sc_char buffer[UIP_ALLOCATION_AVOIDANCE_SIZE];
- sc_bool match;
- assert (pattern && string && game);
-
- /* Start tokenizer. */
- cleansed = uip_cleanse_string (pattern, buffer, sizeof (buffer));
- if (uip_trace)
- sc_trace ("UIParser: pattern \"%s\"\n", cleansed);
- uip_tokenize_start (cleansed);
-
- /* Try parsing the pattern, and catch errors. */
- if (setjmp (uip_parse_error) == 0)
- {
- /* Parse the pattern into a match tree. */
- uip_parse_lookahead = uip_next_token ();
- uip_parse_tree = uip_new_node (NODE_LIST);
- uip_parse_list (uip_parse_tree);
- uip_tokenize_end ();
- cleansed = uip_free_cleansed_string (cleansed, buffer);
- }
- else
- {
- /* Parse error -- clean up and fail. */
- uip_tokenize_end ();
- uip_destroy_tree (uip_parse_tree);
- uip_parse_tree = NULL;
- cleansed = uip_free_cleansed_string (cleansed, buffer);
- return FALSE;
- }
-
- /* Dump out the pattern tree if requested. */
- if (if_get_trace_flag (SC_DUMP_PARSER_TREES))
- uip_debug_dump ();
-
- /* Match the string to the pattern tree. */
- cleansed = uip_cleanse_string (string, buffer, sizeof (buffer));
- if (uip_trace)
- sc_trace ("UIParser: string \"%s\"\n", cleansed);
- uip_match_start (cleansed, game);
- match = uip_match_node (uip_parse_tree);
-
- /* Clean up matching and free the parsed pattern tree. */
- uip_match_end ();
- cleansed = uip_free_cleansed_string (cleansed, buffer);
- uip_destroy_tree (uip_parse_tree);
- uip_parse_tree = NULL;
-
- /* Return result of matching. */
- if (uip_trace)
- sc_trace ("UIParser: %s\n", match ? "MATCHED!" : "No match");
- return match;
+uip_match(const sc_char *pattern, const sc_char *string, sc_gameref_t game) {
+ static sc_char *cleansed; /* For setjmp safety. */
+ sc_char buffer[UIP_ALLOCATION_AVOIDANCE_SIZE];
+ sc_bool match;
+ assert(pattern && string && game);
+
+ /* Start tokenizer. */
+ cleansed = uip_cleanse_string(pattern, buffer, sizeof(buffer));
+ if (uip_trace)
+ sc_trace("UIParser: pattern \"%s\"\n", cleansed);
+ uip_tokenize_start(cleansed);
+
+ /* Try parsing the pattern, and catch errors. */
+ if (setjmp(uip_parse_error) == 0) {
+ /* Parse the pattern into a match tree. */
+ uip_parse_lookahead = uip_next_token();
+ uip_parse_tree = uip_new_node(NODE_LIST);
+ uip_parse_list(uip_parse_tree);
+ uip_tokenize_end();
+ cleansed = uip_free_cleansed_string(cleansed, buffer);
+ } else {
+ /* Parse error -- clean up and fail. */
+ uip_tokenize_end();
+ uip_destroy_tree(uip_parse_tree);
+ uip_parse_tree = NULL;
+ cleansed = uip_free_cleansed_string(cleansed, buffer);
+ return FALSE;
+ }
+
+ /* Dump out the pattern tree if requested. */
+ if (if_get_trace_flag(SC_DUMP_PARSER_TREES))
+ uip_debug_dump();
+
+ /* Match the string to the pattern tree. */
+ cleansed = uip_cleanse_string(string, buffer, sizeof(buffer));
+ if (uip_trace)
+ sc_trace("UIParser: string \"%s\"\n", cleansed);
+ uip_match_start(cleansed, game);
+ match = uip_match_node(uip_parse_tree);
+
+ /* Clean up matching and free the parsed pattern tree. */
+ uip_match_end();
+ cleansed = uip_free_cleansed_string(cleansed, buffer);
+ uip_destroy_tree(uip_parse_tree);
+ uip_parse_tree = NULL;
+
+ /* Return result of matching. */
+ if (uip_trace)
+ sc_trace("UIParser: %s\n", match ? "MATCHED!" : "No match");
+ return match;
}
@@ -1833,157 +1714,137 @@ uip_match (const sc_char *pattern, const sc_char *string, sc_gameref_t game)
* return string is malloc'ed, so the caller needs to remember to free it.
*/
sc_char *
-uip_replace_pronouns (sc_gameref_t game, const sc_char *string)
-{
- const sc_prop_setref_t bundle = gs_get_bundle (game);
- sc_int buffer_allocation;
- sc_char *buffer;
- const sc_char *current;
- assert (string);
-
- if (uip_trace)
- sc_trace ("UIParser: pronoun search \"%s\"\n", string);
-
- /* Begin with a NULL buffer for lazy allocation. */
- buffer_allocation = 0;
- buffer = NULL;
-
- /* Search for pronouns until no more string remains. */
- current = string + strspn (string, WHITESPACE);
- while (current[0] != NUL)
- {
- sc_vartype_t vt_key[3];
- sc_int object, npc, extent;
- const sc_char *prefix, *name;
-
- /* Initially, no object or NPC, no names, and a zero extent. */
- object = npc = -1;
- prefix = name = NULL;
- extent = 0;
-
- /*
- * Search for pronouns where we have an assigned object or NPC. We
- * can't be sure of getting plurality right, and it's not always
- * intuitive even in English -- is "a pair of scissors" an "it", or
- * a "them"? Because of this, we just treat "it" and "them" equally
- * for now.
- */
- if (game->it_object != -1 && sc_compare_word (current, "it", 2))
- {
- object = game->it_object;
- extent = 2;
- }
- else if (game->it_object != -1 && sc_compare_word (current, "them", 4))
- {
- object = game->it_object;
- extent = 4;
- }
- else if (game->him_npc != -1 && sc_compare_word (current, "him", 3))
- {
- npc = game->him_npc;
- extent = 3;
- }
- else if (game->her_npc != -1 && sc_compare_word (current, "her", 3))
- {
- npc = game->her_npc;
- extent = 3;
- }
- else if (game->it_npc != -1 && sc_compare_word (current, "it", 2))
- {
- npc = game->it_npc;
- extent = 2;
- }
-
- /* Assign prefix and name to the full object or NPC name, if any. */
- if (object > -1)
- {
- vt_key[0].string = "Objects";
- vt_key[1].integer = object;
- vt_key[2].string = "Prefix";
- prefix = prop_get_string (bundle, "S<-sis", vt_key);
- vt_key[2].string = "Short";
- name = prop_get_string (bundle, "S<-sis", vt_key);
- }
- else if (npc > -1)
- {
- vt_key[0].string = "NPCs";
- vt_key[1].integer = npc;
- vt_key[2].string = "Prefix";
- prefix = prop_get_string (bundle, "S<-sis", vt_key);
- vt_key[2].string = "Name";
- name = prop_get_string (bundle, "S<-sis", vt_key);
- }
-
- /*
- * If a pronoun was found, prefix and name indicate what to insert, and
- * extent shows how much of the buffer to replace with them.
- */
- if (prefix && name && extent > 0)
- {
- sc_char *position;
- sc_int prefix_length, name_length, length, final;
-
- /*
- * If not yet allocated, allocate a buffer now, and copy the input
- * string into it. Then switch current to the equivalent location
- * in the allocated buffer; basic copy-on-write.
- */
- if (!buffer)
- {
- buffer_allocation = strlen (string) + 1;
- buffer = (sc_char *)sc_malloc (buffer_allocation);
- strcpy (buffer, string);
- current = buffer + (current - string);
- }
-
- /*
- * If necessary, grow the output buffer for the replacement,
- * remembering to adjust current for the new buffer allocated.
- * At the same time, note the last character index for the move.
- */
- prefix_length = strlen (prefix);
- name_length = strlen (name);
- length = prefix_length + name_length + 1;
- if (length > extent)
- {
- sc_int offset;
-
- offset = current - buffer;
- buffer_allocation += length - extent;
- buffer = (sc_char *)sc_realloc (buffer, buffer_allocation);
- current = buffer + offset;
- final = length;
- }
- else
- final = extent;
-
- /* Insert the replacement strings into the buffer. */
- position = buffer + (current - buffer);
- memmove (position + length,
- position + extent,
- buffer_allocation - (current - buffer) - final);
- memcpy (position, prefix, prefix_length);
- position[prefix_length] = ' ';
- memcpy (position + prefix_length + 1, name, name_length);
-
- /* Adjust current to skip over the replacement. */
- current += length;
-
- if (uip_trace)
- sc_trace ("Parser: pronoun \"%s\"\n", buffer);
- }
- else
- {
- /* If no match, advance current over the unmatched word. */
- current += strcspn (current, WHITESPACE);
- }
-
- /* Set current to the next word start. */
- current += strspn (current, WHITESPACE);
- }
-
- /* Return the final string, or NULL if no pronoun replacements. */
- return buffer;
+uip_replace_pronouns(sc_gameref_t game, const sc_char *string) {
+ const sc_prop_setref_t bundle = gs_get_bundle(game);
+ sc_int buffer_allocation;
+ sc_char *buffer;
+ const sc_char *current;
+ assert(string);
+
+ if (uip_trace)
+ sc_trace("UIParser: pronoun search \"%s\"\n", string);
+
+ /* Begin with a NULL buffer for lazy allocation. */
+ buffer_allocation = 0;
+ buffer = NULL;
+
+ /* Search for pronouns until no more string remains. */
+ current = string + strspn(string, WHITESPACE);
+ while (current[0] != NUL) {
+ sc_vartype_t vt_key[3];
+ sc_int object, npc, extent;
+ const sc_char *prefix, *name;
+
+ /* Initially, no object or NPC, no names, and a zero extent. */
+ object = npc = -1;
+ prefix = name = NULL;
+ extent = 0;
+
+ /*
+ * Search for pronouns where we have an assigned object or NPC. We
+ * can't be sure of getting plurality right, and it's not always
+ * intuitive even in English -- is "a pair of scissors" an "it", or
+ * a "them"? Because of this, we just treat "it" and "them" equally
+ * for now.
+ */
+ if (game->it_object != -1 && sc_compare_word(current, "it", 2)) {
+ object = game->it_object;
+ extent = 2;
+ } else if (game->it_object != -1 && sc_compare_word(current, "them", 4)) {
+ object = game->it_object;
+ extent = 4;
+ } else if (game->him_npc != -1 && sc_compare_word(current, "him", 3)) {
+ npc = game->him_npc;
+ extent = 3;
+ } else if (game->her_npc != -1 && sc_compare_word(current, "her", 3)) {
+ npc = game->her_npc;
+ extent = 3;
+ } else if (game->it_npc != -1 && sc_compare_word(current, "it", 2)) {
+ npc = game->it_npc;
+ extent = 2;
+ }
+
+ /* Assign prefix and name to the full object or NPC name, if any. */
+ if (object > -1) {
+ vt_key[0].string = "Objects";
+ vt_key[1].integer = object;
+ vt_key[2].string = "Prefix";
+ prefix = prop_get_string(bundle, "S<-sis", vt_key);
+ vt_key[2].string = "Short";
+ name = prop_get_string(bundle, "S<-sis", vt_key);
+ } else if (npc > -1) {
+ vt_key[0].string = "NPCs";
+ vt_key[1].integer = npc;
+ vt_key[2].string = "Prefix";
+ prefix = prop_get_string(bundle, "S<-sis", vt_key);
+ vt_key[2].string = "Name";
+ name = prop_get_string(bundle, "S<-sis", vt_key);
+ }
+
+ /*
+ * If a pronoun was found, prefix and name indicate what to insert, and
+ * extent shows how much of the buffer to replace with them.
+ */
+ if (prefix && name && extent > 0) {
+ sc_char *position;
+ sc_int prefix_length, name_length, length, final;
+
+ /*
+ * If not yet allocated, allocate a buffer now, and copy the input
+ * string into it. Then switch current to the equivalent location
+ * in the allocated buffer; basic copy-on-write.
+ */
+ if (!buffer) {
+ buffer_allocation = strlen(string) + 1;
+ buffer = (sc_char *)sc_malloc(buffer_allocation);
+ strcpy(buffer, string);
+ current = buffer + (current - string);
+ }
+
+ /*
+ * If necessary, grow the output buffer for the replacement,
+ * remembering to adjust current for the new buffer allocated.
+ * At the same time, note the last character index for the move.
+ */
+ prefix_length = strlen(prefix);
+ name_length = strlen(name);
+ length = prefix_length + name_length + 1;
+ if (length > extent) {
+ sc_int offset;
+
+ offset = current - buffer;
+ buffer_allocation += length - extent;
+ buffer = (sc_char *)sc_realloc(buffer, buffer_allocation);
+ current = buffer + offset;
+ final = length;
+ } else
+ final = extent;
+
+ /* Insert the replacement strings into the buffer. */
+ position = buffer + (current - buffer);
+ memmove(position + length,
+ position + extent,
+ buffer_allocation - (current - buffer) - final);
+ memcpy(position, prefix, prefix_length);
+ position[prefix_length] = ' ';
+ memcpy(position + prefix_length + 1, name, name_length);
+
+ /* Adjust current to skip over the replacement. */
+ current += length;
+
+ if (uip_trace)
+ sc_trace("Parser: pronoun \"%s\"\n", buffer);
+ } else {
+ /* If no match, advance current over the unmatched word. */
+ current += strcspn(current, WHITESPACE);
+ }
+
+ /* Set current to the next word start. */
+ current += strspn(current, WHITESPACE);
+ }
+
+ /* Return the final string, or NULL if no pronoun replacements. */
+ return buffer;
}
@@ -1996,143 +1857,128 @@ uip_replace_pronouns (sc_gameref_t game, const sc_char *string)
* earlier ones if there is more than one in the string.
*/
void
-uip_assign_pronouns (sc_gameref_t game, const sc_char *string)
-{
- const sc_prop_setref_t bundle = gs_get_bundle (game);
- const sc_var_setref_t vars = gs_get_vars (game);
- const sc_char *current;
- sc_int saved_ref_object, saved_ref_character;
- assert (string);
-
- if (uip_trace)
- sc_trace ("UIParser: pronoun assignment \"%s\"\n", string);
-
- /* Save var references so we can restore them later. */
- saved_ref_object = var_get_ref_object (vars);
- saved_ref_character = var_get_ref_character (vars);
-
- /* Search for object and NPC names until no more string remains. */
- current = string + strspn (string, WHITESPACE);
- while (current[0] != NUL)
- {
- if (uip_match ("%object% *", current, game))
- {
- sc_int count, index_, object;
-
- /*
- * "Disambiguate" by rejecting objects that the player hasn't seen
- * or can't see. If the reference is unique, assign to the 'it'
- * object pronoun.
- */
- count = 0;
- object = -1;
- for (index_ = 0; index_ < gs_object_count (game); index_++)
- {
- if (game->object_references[index_]
- && gs_object_seen (game, index_)
- && obj_indirectly_in_room (game,
- index_, gs_playerroom (game)))
- {
- count++;
- object = index_;
- }
- }
-
- if (count == 1)
- {
- game->it_object = object;
- game->it_npc = -1;
-
- if (uip_trace)
- sc_trace ("UIParser: object 'it/them' assigned %ld\n", object);
- }
- }
-
- if (uip_match ("%character% *", current, game))
- {
- sc_int count, index_, npc;
-
- /* Do the same "disambiguation" as for objects above. */
- count = 0;
- npc = -1;
- for (index_ = 0; index_ < gs_npc_count (game); index_++)
- {
- if (game->npc_references[index_]
- && gs_npc_seen (game, index_)
- && npc_in_room (game, index_, gs_playerroom (game)))
- {
- count++;
- npc = index_;
- }
- }
-
- if (count == 1)
- {
- sc_vartype_t vt_key[3];
- sc_int version, gender;
-
- /*
- * Version 3.8 games lack NPC gender information, so for this
- * case set "him"/"her" on each match, and never set "it"; this
- * matches the version 3.8 runner.
- */
- vt_key[0].string = "Version";
- version = prop_get_integer (bundle, "I<-s", vt_key);
- if (version == TAF_VERSION_380)
- {
- game->him_npc = npc;
- game->her_npc = npc;
- game->it_npc = -1;
-
- if (uip_trace)
- {
- sc_trace ("UIParser: 3.8 pronouns"
- " 'him' and 'her' assigned %ld\n", npc);
- }
- }
- else
- {
- /* Find the NPC gender, so we know the pronoun to assign. */
- vt_key[0].string = "NPCs";
- vt_key[1].integer = npc;
- vt_key[2].string = "Gender";
- gender = prop_get_integer (bundle, "I<-sis", vt_key);
-
- switch (gender)
- {
- case NPC_MALE:
- game->him_npc = npc;
- break;
- case NPC_FEMALE:
- game->her_npc = npc;
- break;
- case NPC_NEUTER:
- game->it_npc = npc;
- game->it_object = -1;
- break;
- default:
- sc_error ("uip_assign_pronouns:"
- " unknown gender, %ld\n", gender);
- }
-
- if (uip_trace)
- sc_trace ("UIParser: NPC 'him/her/it' assigned %ld\n", npc);
- }
- }
- }
-
- /*
- * Advance the string position by a complete word. This saves a lot
- * of time -- there's no point looking for an object or NPC name in
- * mid-word, and anyway it's not the right thing to do.
- */
- current += strcspn (current, WHITESPACE);
- current += strspn (current, WHITESPACE);
- }
-
- /* Restore variables references. */
- var_set_ref_object (vars, saved_ref_object);
- var_set_ref_character (vars, saved_ref_character);
+uip_assign_pronouns(sc_gameref_t game, const sc_char *string) {
+ const sc_prop_setref_t bundle = gs_get_bundle(game);
+ const sc_var_setref_t vars = gs_get_vars(game);
+ const sc_char *current;
+ sc_int saved_ref_object, saved_ref_character;
+ assert(string);
+
+ if (uip_trace)
+ sc_trace("UIParser: pronoun assignment \"%s\"\n", string);
+
+ /* Save var references so we can restore them later. */
+ saved_ref_object = var_get_ref_object(vars);
+ saved_ref_character = var_get_ref_character(vars);
+
+ /* Search for object and NPC names until no more string remains. */
+ current = string + strspn(string, WHITESPACE);
+ while (current[0] != NUL) {
+ if (uip_match("%object% *", current, game)) {
+ sc_int count, index_, object;
+
+ /*
+ * "Disambiguate" by rejecting objects that the player hasn't seen
+ * or can't see. If the reference is unique, assign to the 'it'
+ * object pronoun.
+ */
+ count = 0;
+ object = -1;
+ for (index_ = 0; index_ < gs_object_count(game); index_++) {
+ if (game->object_references[index_]
+ && gs_object_seen(game, index_)
+ && obj_indirectly_in_room(game,
+ index_, gs_playerroom(game))) {
+ count++;
+ object = index_;
+ }
+ }
+
+ if (count == 1) {
+ game->it_object = object;
+ game->it_npc = -1;
+
+ if (uip_trace)
+ sc_trace("UIParser: object 'it/them' assigned %ld\n", object);
+ }
+ }
+
+ if (uip_match("%character% *", current, game)) {
+ sc_int count, index_, npc;
+
+ /* Do the same "disambiguation" as for objects above. */
+ count = 0;
+ npc = -1;
+ for (index_ = 0; index_ < gs_npc_count(game); index_++) {
+ if (game->npc_references[index_]
+ && gs_npc_seen(game, index_)
+ && npc_in_room(game, index_, gs_playerroom(game))) {
+ count++;
+ npc = index_;
+ }
+ }
+
+ if (count == 1) {
+ sc_vartype_t vt_key[3];
+ sc_int version, gender;
+
+ /*
+ * Version 3.8 games lack NPC gender information, so for this
+ * case set "him"/"her" on each match, and never set "it"; this
+ * matches the version 3.8 runner.
+ */
+ vt_key[0].string = "Version";
+ version = prop_get_integer(bundle, "I<-s", vt_key);
+ if (version == TAF_VERSION_380) {
+ game->him_npc = npc;
+ game->her_npc = npc;
+ game->it_npc = -1;
+
+ if (uip_trace) {
+ sc_trace("UIParser: 3.8 pronouns"
+ " 'him' and 'her' assigned %ld\n", npc);
+ }
+ } else {
+ /* Find the NPC gender, so we know the pronoun to assign. */
+ vt_key[0].string = "NPCs";
+ vt_key[1].integer = npc;
+ vt_key[2].string = "Gender";
+ gender = prop_get_integer(bundle, "I<-sis", vt_key);
+
+ switch (gender) {
+ case NPC_MALE:
+ game->him_npc = npc;
+ break;
+ case NPC_FEMALE:
+ game->her_npc = npc;
+ break;
+ case NPC_NEUTER:
+ game->it_npc = npc;
+ game->it_object = -1;
+ break;
+ default:
+ sc_error("uip_assign_pronouns:"
+ " unknown gender, %ld\n", gender);
+ }
+
+ if (uip_trace)
+ sc_trace("UIParser: NPC 'him/her/it' assigned %ld\n", npc);
+ }
+ }
+ }
+
+ /*
+ * Advance the string position by a complete word. This saves a lot
+ * of time -- there's no point looking for an object or NPC name in
+ * mid-word, and anyway it's not the right thing to do.
+ */
+ current += strcspn(current, WHITESPACE);
+ current += strspn(current, WHITESPACE);
+ }
+
+ /* Restore variables references. */
+ var_set_ref_object(vars, saved_ref_object);
+ var_set_ref_character(vars, saved_ref_character);
}
} // End of namespace Adrift