aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/parser
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/parser')
-rw-r--r--engines/sci/parser/grammar.cpp88
-rw-r--r--engines/sci/parser/said.cpp40
-rw-r--r--engines/sci/parser/vocabulary.cpp236
-rw-r--r--engines/sci/parser/vocabulary.h53
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()