diff options
Diffstat (limited to 'engines/sci/parser')
| -rw-r--r-- | engines/sci/parser/grammar.cpp | 88 | ||||
| -rw-r--r-- | engines/sci/parser/said.cpp | 40 | ||||
| -rw-r--r-- | engines/sci/parser/vocabulary.cpp | 236 | ||||
| -rw-r--r-- | engines/sci/parser/vocabulary.h | 53 |
4 files changed, 319 insertions, 98 deletions
diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp index 6f37b49919..03e9d29660 100644 --- a/engines/sci/parser/grammar.cpp +++ b/engines/sci/parser/grammar.cpp @@ -38,8 +38,9 @@ namespace Sci { #define TOKEN_CPAREN 0xfe000000 #define TOKEN_TERMINAL_CLASS 0x10000 #define TOKEN_TERMINAL_GROUP 0x20000 -#define TOKEN_STUFFING_WORD 0x40000 -#define TOKEN_NON_NT (TOKEN_OPAREN | TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP | TOKEN_STUFFING_WORD) +#define TOKEN_STUFFING_LEAF 0x40000 +#define TOKEN_STUFFING_WORD 0x80000 +#define TOKEN_NON_NT (TOKEN_OPAREN | TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP | TOKEN_STUFFING_LEAF | TOKEN_STUFFING_WORD) #define TOKEN_TERMINAL (TOKEN_TERMINAL_CLASS | TOKEN_TERMINAL_GROUP) static int _allocd_rules = 0; // FIXME: Avoid non-const global vars @@ -122,8 +123,10 @@ static void vocab_print_rule(ParseRule *rule) { printf("C(%04x)", token & 0xffff); else if (token & TOKEN_TERMINAL_GROUP) printf("G(%04x)", token & 0xffff); - else if (token & TOKEN_STUFFING_WORD) + else if (token & TOKEN_STUFFING_LEAF) printf("%03x", token & 0xffff); + else if (token & TOKEN_STUFFING_WORD) + printf("{%03x}", token & 0xffff); else printf("[%03x]", token); /* non-terminal */ wspace = 1; @@ -206,8 +209,8 @@ static ParseRule *_vbuild_rule(const parse_tree_branch_t *branch) { rule->_data[tokens++] = value | TOKEN_STUFFING_WORD; else { // normal inductive rule rule->_data[tokens++] = TOKEN_OPAREN; - rule->_data[tokens++] = type | TOKEN_STUFFING_WORD; - rule->_data[tokens++] = value | TOKEN_STUFFING_WORD; + rule->_data[tokens++] = type | TOKEN_STUFFING_LEAF; + rule->_data[tokens++] = value | TOKEN_STUFFING_LEAF; if (i == 0) rule->_firstSpecial = tokens; @@ -220,7 +223,7 @@ static ParseRule *_vbuild_rule(const parse_tree_branch_t *branch) { return rule; } -static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWord &input) { +static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWordList &input) { int dep; if (!rule->_numSpecials) @@ -228,11 +231,32 @@ static ParseRule *_vsatisfy_rule(ParseRule *rule, const ResultWord &input) { dep = rule->_data[rule->_firstSpecial]; - if (((dep & TOKEN_TERMINAL_CLASS) && ((dep & 0xffff) & input._class)) || - ((dep & TOKEN_TERMINAL_GROUP) && ((dep & 0xffff) & input._group))) { + int count = 0; + int match = 0; + ResultWordList::const_iterator iter; + // TODO: Inserting an array in the middle of another array is slow + Common::Array<int> matches; + matches.reserve(input.size()); + + // We store the first match in 'match', and any subsequent matches in + // 'matches'. 'match' replaces the special in the rule, and 'matches' gets + // inserted after it. + for (iter = input.begin(); iter != input.end(); ++iter) + if (((dep & TOKEN_TERMINAL_CLASS) && ((dep & 0xffff) & iter->_class)) || + ((dep & TOKEN_TERMINAL_GROUP) && ((dep & 0xffff) & iter->_group))) { + if (count == 0) + match = TOKEN_STUFFING_WORD | iter->_group; + else + matches.push_back(TOKEN_STUFFING_WORD | iter->_group); + count++; + } + + if (count) { ParseRule *retval = new ParseRule(*rule); ++_allocd_rules; - retval->_data[rule->_firstSpecial] = TOKEN_STUFFING_WORD | input._group; + retval->_data[rule->_firstSpecial] = match; + if (count > 1) + retval->_data.insert_at(rule->_firstSpecial+1, matches); retval->_numSpecials--; retval->_firstSpecial = 0; @@ -277,10 +301,8 @@ static ParseRuleList *_vocab_add_rule(ParseRuleList *list, ParseRule *rule) { while (seeker->next/* && seeker->next->terminal <= term*/) { if (seeker->next->terminal == term) { if (*(seeker->next->rule) == *rule) { - delete rule; - // FIXME: not sure about this change, fixes pq2 crashing when having opened the cabinet - // and typing "go to bains" - delete rule deletes part of new_elem - //delete new_elem; + delete new_elem; // NB: This also deletes 'rule' + return list; // No duplicate rules } } @@ -445,6 +467,7 @@ static int _vbpt_append(ParseTreeNode *nodes, int *pos, int base, int value) { nodes[base].left = &nodes[++(*pos)]; nodes[*pos].type = kParseTreeLeafNode; nodes[*pos].value = value; + nodes[*pos].right = 0; nodes[base].right = &nodes[++(*pos)]; nodes[*pos].type = kParseTreeBranchNode; nodes[*pos].left = 0; @@ -456,9 +479,29 @@ static int _vbpt_terminate(ParseTreeNode *nodes, int *pos, int base, int value) // Terminates, overwriting a nextwrite forknode nodes[base].type = kParseTreeLeafNode; nodes[base].value = value; + nodes[base].right = 0; + return *pos; +} +static int _vbpt_append_word(ParseTreeNode *nodes, int *pos, int base, int value) { + // writes one value to an existing node and creates a sibling for writing + nodes[base].type = kParseTreeWordNode; + nodes[base].value = value; + nodes[base].right = &nodes[++(*pos)]; + nodes[*pos].type = kParseTreeBranchNode; + nodes[*pos].left = 0; + nodes[*pos].right = 0; + return *pos; +} + +static int _vbpt_terminate_word(ParseTreeNode *nodes, int *pos, int base, int value) { + // Terminates, overwriting a nextwrite forknode + nodes[base].type = kParseTreeWordNode; + nodes[base].value = value; + nodes[base].right = 0; return *pos; } + static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule *rule, uint rulepos, int writepos) { uint token; @@ -470,11 +513,16 @@ static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule * nexttoken = (rulepos < rule->_data.size()) ? rule->_data[rulepos] : TOKEN_CPAREN; if (nexttoken != TOKEN_CPAREN) writepos = _vbpt_parenc(nodes, pos, writepos); - } else if (token & TOKEN_STUFFING_WORD) { + } else if (token & TOKEN_STUFFING_LEAF) { if (nexttoken == TOKEN_CPAREN) writepos = _vbpt_terminate(nodes, pos, writepos, token & 0xffff); else writepos = _vbpt_append(nodes, pos, writepos, token & 0xffff); + } else if (token & TOKEN_STUFFING_WORD) { + if (nexttoken == TOKEN_CPAREN) + writepos = _vbpt_terminate_word(nodes, pos, writepos, token & 0xffff); + else + writepos = _vbpt_append_word(nodes, pos, writepos, token & 0xffff); } else { printf("\nError in parser (grammar.cpp, _vbpt_write_subexpression()): Rule data broken in rule "); vocab_print_rule(rule); @@ -486,16 +534,16 @@ static int _vbpt_write_subexpression(ParseTreeNode *nodes, int *pos, ParseRule * return rulepos; } -int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) { +int Vocabulary::parseGNF(const ResultWordListList &words, bool verbose) { Console *con = g_sci->getSciDebugger(); // Get the start rules: ParseRuleList *work = _vocab_clone_rule_list_by_id(_parserRules, _parserBranches[0].data[1]); ParseRuleList *results = NULL; uint word = 0; const uint words_nr = words.size(); - ResultWordList::const_iterator word_iter = words.begin(); + ResultWordListList::const_iterator words_iter; - for (word_iter = words.begin(); word_iter != words.end(); ++word_iter, ++word) { + for (words_iter = words.begin(); words_iter != words.end(); ++words_iter, ++word) { ParseRuleList *new_work = NULL; ParseRuleList *reduced_rules = NULL; ParseRuleList *seeker, *subseeker; @@ -505,8 +553,9 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) { seeker = work; while (seeker) { - if (seeker->rule->_numSpecials <= (words_nr - word)) - reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, *word_iter)); + if (seeker->rule->_numSpecials <= (words_nr - word)) { + reduced_rules = _vocab_add_rule(reduced_rules, _vsatisfy_rule(seeker->rule, *words_iter)); + } seeker = seeker->next; } @@ -570,6 +619,7 @@ int Vocabulary::parseGNF(const ResultWordList &words, bool verbose) { _parserNodes[1].type = kParseTreeLeafNode; _parserNodes[1].value = 0x141; + _parserNodes[1].right = 0; _parserNodes[2].type = kParseTreeBranchNode; _parserNodes[2].left = 0; diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index 9c07be2dff..7393874856 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -94,6 +94,7 @@ static ParseTreeNode* said_next_node() { static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) { pos->type = kParseTreeLeafNode; pos->value = value; + pos->right = 0; return pos; } @@ -101,6 +102,7 @@ static ParseTreeNode* said_leaf_node(ParseTreeNode* pos, int value) { static ParseTreeNode* said_word_node(ParseTreeNode* pos, int value) { pos->type = kParseTreeWordNode; pos->value = value; + pos->right = 0; return pos; } @@ -780,17 +782,39 @@ static int matchTrees(ParseTreeNode* parseT, ParseTreeNode* saidT) // both saidT and parseT are terminals int said_val = node_terminal_value(saidT); - int parse_val = node_terminal_value(parseT); - if (said_val != WORD_NONE && - (said_val == parse_val || said_val == WORD_ANY || - parse_val == WORD_ANY)) +#ifdef SCI_DEBUG_PARSE_TREE_AUGMENTATION + scidprintf("%*smatchTrees matching terminals: %03x", outputDepth, "", node_terminal_value(parseT)); + ParseTreeNode* t = parseT->right->right; + while (t) { + scidprintf(",%03x", t->value); + t = t->right; + } + scidprintf(" vs %03x", said_val); +#endif + + if (said_val == WORD_NONE) { + ret = -1; + } else if (said_val == WORD_ANY) { ret = 1; - else + } else { ret = -1; - scidprintf("%*smatchTrees matching terminals: %03x vs %03x (%d)\n", - outputDepth, "", parse_val, said_val, ret); + // scan through the word group ids in the parse tree leaf to see if + // one matches the word group in the said tree + parseT = parseT->right->right; + do { + assert(parseT->type != kParseTreeBranchNode); + int parse_val = parseT->value; + if (parse_val == WORD_ANY || parse_val == said_val) { + ret = 1; + break; + } + parseT = parseT->right; + } while (parseT); + } + + scidprintf(" (ret %d)\n", ret); } else if (node_is_terminal(saidT) && !node_is_terminal(parseT)) { @@ -1107,7 +1131,7 @@ True said put washer on shaft & 455 , ( 3fa < cb ) / 8c6 True -said depth correct & [!*] < 8b1 / 22 +said depth correct & [!*] < 8b1 / 22b True said depth acknowledged & / 46d , 460 , 44d < 8b1 diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index 20436d5b30..f9989b22a8 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -40,6 +40,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), // Mark parse tree as unused _parserNodes[0].type = kParseTreeLeafNode; _parserNodes[0].value = 0; + _parserNodes[0].right = 0; _synonyms.clear(); // No synonyms @@ -72,6 +73,8 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), _parserRules = NULL; } + loadAltInputs(); + parser_base = NULL_REG; parser_event = NULL_REG; parserIsValid = false; @@ -80,6 +83,7 @@ Vocabulary::Vocabulary(ResourceManager *resMan, bool foreign) : _resMan(resMan), Vocabulary::~Vocabulary() { freeRuleList(_parserRules); freeSuffixes(); + freeAltInputs(); } void Vocabulary::reset() { @@ -165,8 +169,14 @@ bool Vocabulary::loadParserWords() { newWord._class = ((resource->data[seeker]) << 4) | ((c & 0xf0) >> 4); newWord._group = (resource->data[seeker + 2]) | ((c & 0x0f) << 8); - // Add the word to the list - _parserWords[currentWord] = newWord; + // SCI01 was the first version to support multiple class/group pairs + // per word, so we clear the list in earlier versions + // in earlier versions. + if (getSciVersion() < SCI_VERSION_01) + _parserWords[currentWord].clear(); + + // Add this to the list of possible class,group pairs for this word + _parserWords[currentWord].push_back(newWord); seeker += 3; } @@ -181,8 +191,9 @@ const char *Vocabulary::getAnyWordFromGroup(int group) { return "{nothing}"; for (WordMap::const_iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) { - if (i->_value._group == group) - return i->_key.c_str(); + for (ResultWordList::const_iterator j = i->_value.begin(); j != i->_value.end(); ++j) + if (j->_group == group) + return i->_key.c_str(); } return "{invalid}"; @@ -264,8 +275,108 @@ bool Vocabulary::loadBranches() { return true; } +bool Vocabulary::loadAltInputs() { + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 1); + + if (!resource) + return true; // it's not a problem if this resource doesn't exist + + const char *data = (const char*)resource->data; + const char *data_end = data + resource->size; + + _altInputs.clear(); + _altInputs.resize(256); + + while (data < data_end && *data) { + AltInput t; + t._input = data; + + unsigned int l = strlen(data); + t._inputLength = l; + data += l + 1; + + t._replacement = data; + l = strlen(data); + data += l + 1; + + if (data < data_end && strncmp(data, t._input, t._inputLength) == 0) + t._prefix = true; + else + t._prefix = false; + + unsigned char firstChar = t._input[0]; + _altInputs[firstChar].push_front(t); + } + + return true; +} + +void Vocabulary::freeAltInputs() { + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_ALT_INPUTS), 0); + if (resource) + _resMan->unlockResource(resource); + + _altInputs.clear(); +} + +bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { + if (_altInputs.empty()) + return false; + if (SELECTOR(parseLang) == -1) + return false; + if (readSelectorValue(g_sci->getEngineState()->_segMan, g_sci->getGameObject(), SELECTOR(parseLang)) == 1) + return false; + + bool ret = false; + unsigned int loopCount = 0; + bool changed; + do { + changed = false; + + const char* t = text.c_str(); + unsigned int tlen = text.size(); + + for (unsigned int p = 0; p < tlen && !changed; ++p) { + unsigned char s = t[p]; + if (s >= _altInputs.size() || _altInputs[s].empty()) + continue; + Common::List<AltInput>::iterator i; + for (i = _altInputs[s].begin(); i != _altInputs[s].end(); ++i) { + if (p + i->_inputLength > tlen) + continue; + if (i->_prefix && cursorPos > p && cursorPos <= p + i->_inputLength) + continue; + if (strncmp(i->_input, t+p, i->_inputLength) == 0) { + // replace + if (cursorPos > p + i->_inputLength) { + cursorPos += strlen(i->_replacement) - i->_inputLength; + } else if (cursorPos > p) { + cursorPos = p + strlen(i->_replacement); + } + + for (unsigned int j = 0; j < i->_inputLength; ++j) + text.deleteChar(p); + const char *r = i->_replacement; + while (*r) + text.insertChar(*r++, p++); + + assert(cursorPos <= text.size()); + + changed = true; + ret = true; + break; + } + } + } + } while (changed && loopCount < 10); + + return ret; +} + // we assume that *word points to an already lowercased word -ResultWord Vocabulary::lookupWord(const char *word, int word_len) { +void Vocabulary::lookupWord(ResultWordList& retval, const char *word, int word_len) { + retval.clear(); + Common::String tempword(word, word_len); // Remove all dashes from tempword @@ -277,15 +388,22 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) { } // Look it up: - WordMap::iterator dict_word = _parserWords.find(tempword); + WordMap::iterator dict_words = _parserWords.find(tempword); // Match found? Return it! - if (dict_word != _parserWords.end()) { - return dict_word->_value; + if (dict_words != _parserWords.end()) { + retval = dict_words->_value; + + // SCI01 was the first version to support + // multiple matches, so no need to look further + // in earlier versions. + if (getSciVersion() < SCI_VERSION_01) + return; + } // Now try all suffixes - for (SuffixList::const_iterator suffix = _parserSuffixes.begin(); suffix != _parserSuffixes.end(); ++suffix) + for (SuffixList::const_iterator suffix = _parserSuffixes.begin(); suffix != _parserSuffixes.end(); ++suffix) { if (suffix->alt_suffix_length <= word_len) { int suff_index = word_len - suffix->alt_suffix_length; @@ -298,27 +416,38 @@ ResultWord Vocabulary::lookupWord(const char *word, int word_len) { // ...and append "correct" suffix tempword2 += Common::String(suffix->word_suffix, suffix->word_suffix_length); - dict_word = _parserWords.find(tempword2); - - if ((dict_word != _parserWords.end()) && (dict_word->_value._class & suffix->class_mask)) { // Found it? - // Use suffix class - ResultWord tmp = dict_word->_value; - tmp._class = suffix->result_class; - return tmp; + dict_words = _parserWords.find(tempword2); + + if (dict_words != _parserWords.end()) { + for (ResultWordList::const_iterator j = dict_words->_value.begin(); j != dict_words->_value.end(); ++j) { + if (j->_class & suffix->class_mask) { // Found it? + // Use suffix class + ResultWord tmp = *j; + tmp._class = suffix->result_class; + retval.push_back(tmp); + + // SCI01 was the first version to support + // multiple matches, so no need to look further + // in earlier versions. + if (getSciVersion() < SCI_VERSION_01) + return; + } + } } } } + } + + if (!retval.empty()) + return; // No match so far? Check if it's a number. - ResultWord retval = { -1, -1 }; char *tester; if ((strtol(tempword.c_str(), &tester, 10) >= 0) && (*tester == '\0')) { // Do we have a complete number here? ResultWord tmp = { VOCAB_CLASS_NUMBER, VOCAB_MAGIC_NUMBER_GROUP }; - retval = tmp; + retval.push_back(tmp); } - - return retval; } void Vocabulary::debugDecipherSaidBlock(const byte *addr) { @@ -397,7 +526,7 @@ static const byte lowerCaseMap[256] = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // 0xf0 }; -bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, char **error) { +bool Vocabulary::tokenizeString(ResultWordListList &retval, const char *sentence, char **error) { char currentWord[VOCAB_MAX_WORDLENGTH] = ""; int pos_in_sentence = 0; unsigned char c; @@ -418,10 +547,12 @@ bool Vocabulary::tokenizeString(ResultWordList &retval, const char *sentence, ch else { if (wordLen) { // Finished a word? - ResultWord lookup_result = lookupWord(currentWord, wordLen); + ResultWordList lookup_result; + // Look it up + lookupWord(lookup_result, currentWord, wordLen); - if (lookup_result._class == -1) { // Not found? + if (lookup_result.empty()) { // Not found? *error = (char *)calloc(wordLen + 1, 1); strncpy(*error, currentWord, wordLen); // Set the offending word retval.clear(); @@ -459,43 +590,19 @@ void Vocabulary::printSuffixes() const { void Vocabulary::printParserWords() const { Console *con = g_sci->getSciDebugger(); - int j = 0; + int n = 0; for (WordMap::iterator i = _parserWords.begin(); i != _parserWords.end(); ++i) { - con->DebugPrintf("%4d: %03x [%03x] %20s |", j, i->_value._class, i->_value._group, i->_key.c_str()); - if (j % 3 == 0) - con->DebugPrintf("\n"); - j++; + for (ResultWordList::iterator j = i->_value.begin(); j != i->_value.end(); ++j) { + con->DebugPrintf("%4d: %03x [%03x] %20s |", n, j->_class, j->_group, i->_key.c_str()); + if (n % 3 == 0) + con->DebugPrintf("\n"); + n++; + } } con->DebugPrintf("\n"); } -void _vocab_recursive_ptree_dump_treelike(ParseTreeNode *tree) { - assert(tree); - - if (tree->type == kParseTreeLeafNode) - printf("%x", tree->value); - else { - ParseTreeNode* lbranch = tree->left; - ParseTreeNode* rbranch = tree->right; - printf("<"); - - if (lbranch) - _vocab_recursive_ptree_dump_treelike(lbranch); - else - printf("NULL"); - - printf(","); - - if (rbranch) - _vocab_recursive_ptree_dump_treelike(rbranch); - else - printf("NULL"); - - printf(">"); - } -} - void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) { assert(tree); @@ -526,33 +633,37 @@ void _vocab_recursive_ptree_dump(ParseTreeNode *tree, int blanks) { if (rbranch) { if (rbranch->type == kParseTreeBranchNode) _vocab_recursive_ptree_dump(rbranch, blanks); - else + else { printf("%x", rbranch->value); + while (rbranch->right) { + rbranch = rbranch->right; + printf("/%x", rbranch->value); + } + } }/* else printf("nil");*/ } void vocab_dump_parse_tree(const char *tree_name, ParseTreeNode *nodes) { - //_vocab_recursive_ptree_dump_treelike(nodes, 0, 0); printf("(setq %s \n'(", tree_name); _vocab_recursive_ptree_dump(nodes, 1); printf("))\n"); } void Vocabulary::dumpParseTree() { - //_vocab_recursive_ptree_dump_treelike(nodes, 0, 0); printf("(setq parse-tree \n'("); _vocab_recursive_ptree_dump(_parserNodes, 1); printf("))\n"); } -void Vocabulary::synonymizeTokens(ResultWordList &words) { +void Vocabulary::synonymizeTokens(ResultWordListList &words) { if (_synonyms.empty()) return; // No synonyms: Nothing to check - for (ResultWordList::iterator i = words.begin(); i != words.end(); ++i) - for (SynonymList::const_iterator sync = _synonyms.begin(); sync != _synonyms.end(); ++sync) - if (i->_group == sync->replaceant) - i->_group = sync->replacement; + for (ResultWordListList::iterator i = words.begin(); i != words.end(); ++i) + for (ResultWordList::iterator j = i->begin(); j != i->end(); ++j) + for (SynonymList::const_iterator sync = _synonyms.begin(); sync != _synonyms.end(); ++sync) + if (j->_group == sync->replaceant) + j->_group = sync->replacement; } void Vocabulary::printParserNodes(int num) { @@ -578,6 +689,7 @@ int Vocabulary::parseNodes(int *i, int *pos, int type, int nr, int argc, const c if (type == kParseNumber) { _parserNodes[*pos += 1].type = kParseTreeLeafNode; _parserNodes[*pos].value = nr; + _parserNodes[*pos].right = 0; return *pos; } if (type == kParseEndOfInput) { diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h index d4df8af715..620d50c09d 100644 --- a/engines/sci/parser/vocabulary.h +++ b/engines/sci/parser/vocabulary.h @@ -49,7 +49,9 @@ enum { VOCAB_RESOURCE_SCI1_MAIN_VOCAB = 900, VOCAB_RESOURCE_SCI1_PARSE_TREE_BRANCHES = 901, - VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902 + VOCAB_RESOURCE_SCI1_SUFFIX_VOCAB = 902, + + VOCAB_RESOURCE_ALT_INPUTS = 913 }; @@ -117,8 +119,9 @@ struct ResultWord { }; typedef Common::List<ResultWord> ResultWordList; +typedef Common::List<ResultWordList> ResultWordListList; -typedef Common::HashMap<Common::String, ResultWord, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap; +typedef Common::HashMap<Common::String, ResultWordList, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> WordMap; struct ParseRuleList; @@ -146,6 +149,15 @@ struct synonym_t { typedef Common::List<synonym_t> SynonymList; + +struct AltInput { + const char *_input; + const char *_replacement; + unsigned int _inputLength; + bool _prefix; +}; + + struct parse_tree_branch_t { int id; int data[10]; @@ -161,7 +173,7 @@ struct ParseTreeNode { ParseTypes type; /**< leaf or branch */ int value; /**< For leaves */ ParseTreeNode* left; /**< Left child, for branches */ - ParseTreeNode* right; /**< Right child, for branches */ + ParseTreeNode* right; /**< Right child, for branches (and word leaves) */ }; enum VocabularyVersions { @@ -186,11 +198,11 @@ public: /** * Looks up a single word in the words and suffixes list. + * @param retval the list of matches * @param word pointer to the word to look up * @param word_len length of the word to look up - * @return the matching word (or (-1,-1) if there was no match) */ - ResultWord lookupWord(const char *word, int word_len); + void lookupWord(ResultWordList &retval, const char *word, int word_len); /** @@ -204,7 +216,7 @@ public: * contain any useful words; if not, *error points to a malloc'd copy of * the offending word. The returned list may contain anywords. */ - bool tokenizeString(ResultWordList &retval, const char *sentence, char **error); + bool tokenizeString(ResultWordListList &retval, const char *sentence, char **error); /** * Builds a parse tree from a list of words, using a set of Greibach Normal @@ -215,7 +227,7 @@ public: * nodes or if the sentence structure in 'words' is not part of the * language described by the grammar passed in 'rules'. */ - int parseGNF(const ResultWordList &words, bool verbose = false); + int parseGNF(const ResultWordListList &words, bool verbose = false); /** * Constructs the Greibach Normal Form of the grammar supplied in 'branches'. @@ -262,9 +274,9 @@ public: /** * Synonymizes a token list - * Parameters: (ResultWordList &) words: The word list to synonymize + * Parameters: (ResultWordListList &) words: The word list to synonymize */ - void synonymizeTokens(ResultWordList &words); + void synonymizeTokens(ResultWordListList &words); void printParserNodes(int num); @@ -272,6 +284,14 @@ public: int parseNodes(int *i, int *pos, int type, int nr, int argc, const char **argv); + /** + * Check text input against alternative inputs. + * @param text The text to process. It will be modified in-place + * @param cursorPos The cursor position + * @return true if anything changed + */ + bool checkAltInput(Common::String& text, uint16& cursorPos); + private: /** * Loads all words from the main vocabulary. @@ -304,6 +324,20 @@ private: */ void freeRuleList(ParseRuleList *rule_list); + + /** + * Retrieves all alternative input combinations from vocab 913. + * @return true on success, false on error + */ + bool loadAltInputs(); + + /** + * Frees all alternative input combinations. + */ + void freeAltInputs(); + + + ResourceManager *_resMan; VocabularyVersions _vocabVersion; @@ -318,6 +352,7 @@ private: Common::Array<parse_tree_branch_t> _parserBranches; WordMap _parserWords; SynonymList _synonyms; /**< The list of synonyms */ + Common::Array<Common::List<AltInput> > _altInputs; public: // Accessed by said() |
