// verifySentence.js
(function () {
/*global adventurejs A*/
var p = adventurejs.Parser.prototype;
/**
* After parse, verify each word in sentence.
* @memberOf adventurejs.Parser
* @method adventurejs.Parser#verifySentence
*/
p.verifySentence = function Parser_verifySentence() {
let this_turn = this.input_history[0];
this.game.log(
"L1284",
"log",
"high",
"verifySentence.js > " + this_turn.input,
"Parser"
);
let count = {
noun: 0,
prep: 0,
verb: 0,
adverb: 0,
adjective: 0,
direction: 0,
exclusion: 0,
unknown: 0,
string: 0,
phrase: 0,
};
let firstverb;
for (
let position = 0;
position < this_turn.parsed_sentence.length;
position++
) {
let this_word = this_turn.parsed_sentence[position];
let last_word = this_turn.parsed_sentence[position - 1];
let word_before_last = this_turn.parsed_sentence[position - 2];
let next_word = this_turn.parsed_sentence[position + 1];
let msg = "";
// this.game.debug(
// `D1203 | verifySentence.js | ${this_word.type}/${this_word.word} `
// );
// ----------------------------------------
// verb
// ----------------------------------------
if (this_word.type === "verb") {
// we don't accept double verbs except for "oops verb"
if (
last_word &&
last_word.type === "verb" &&
last_word.word !== "oops"
) {
// too many verbs
this.game.debug(
`D1198 | verifySentence.js | input found two verbs in a row`
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg);
return false;
}
count.verb++;
this_turn.verified_sentence_structure += "verb ";
if (!this_turn.verified_sentence["verb" + count.verb]) {
this_turn.verified_sentence["verb" + count.verb] = {};
}
this_turn.verified_sentence["verb" + count.verb].verb = this_word.word;
this_turn.verified_sentence["verb" + count.verb].verb_properties =
this_word.properties;
continue;
} // verb
// ----------------------------------------
// adverb
// ----------------------------------------
if (this_word.type === "adverb") {
if (last_word && last_word.type === "adverb") {
// too many adverb
this.game.debug(
`D1650 | verifySentence.js | parser found two or more adverbs`
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg);
return false;
}
count.adverb++;
// an adverb may appear before its verb, as in the case of
// "carefully examine asset"
if (count.verb === 0) {
this_turn.verified_sentence.verb1 = {};
}
// currently we don't show adverbs in the sentence structure because
// the same adverb may appear in any of several different positions
// in a sentence which overly complicates structure handling
// but still may change in future
// this_turn.verified_sentence_structure += "adverb ";
// this should work with
// "carefully look at asset"
// "look carefully at asset"
// "look at asset carefully"
// "tell character to look carefully"
// @TODO it will break with
// "tell character to carefully examine asset"
// because it will record "tell carefully"
this_turn.verified_sentence[
`verb${count.verb === 0 ? 1 : count.verb}`
].adverb = this_word.word;
continue;
} // adverb
// ----------------------------------------
// direction
// ----------------------------------------
// if (this_word.type === "direction") {
// continue;
// } // direction
// ----------------------------------------
// preposition
// ----------------------------------------
if (this_word.type === "preposition") {
if (last_word && last_word.type === "preposition") {
// too many prepositions
// are prepositions identical?
if (last_word.word === this_word.word) {
this.game.debug(
`D1320 | verifySentence.js | parser found two identical prepositions`
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg);
return false;
}
// is it possible that the last preposition was one of: in, out, up, down ?
// we treat these as prepositions, adverbs, or nouns depending on context
// some possible scenarios:
// 1) "go down through manhole" where manhole is an exit down - HANDLED
// 2) "go up on ladder" where player can climb ladder - HANDLED
// 3) "go down on [person]" - currently unhandled
// 4) "look up at [asset]" - currently unhandled
// 5) "throw up in bucket" - currently unhandled
// 6) "turn down bed" - currently unhandled
const last_word_is_direction = this.game.dictionary.getDirection(
last_word.word
);
// 1) do last word preposition and next word noun both map to same exit?
if (
last_word_is_direction &&
word_before_last?.type === "verb" &&
next_word?.type === "noun"
) {
const exit_from_last_word = this.game.getExitFromDirection(
last_word.word
);
const nouns_from_next_word = this.parseNoun(next_word.word);
let match = false;
for (
let n = 0;
n < nouns_from_next_word.matches.qualified.length;
n++
) {
const exit_from_next_word = this.game.getAsset(
nouns_from_next_word.matches.qualified[n]
);
console.warn({ exit_from_last_word, exit_from_next_word });
if (
exit_from_next_word?.direction ===
exit_from_last_word?.direction
) {
// we found a match
match = true;
continue;
}
}
if (match) {
// remove the leading preposition
// remove last preposition from sentence structure
// and save it as an adverb
// - set verb.direction to last preposition
this_turn.verified_sentence[`verb${count.verb}`].adverb =
last_word.word;
// - set last preposition in phrase to this preposition
this_turn.verified_sentence["phrase" + count.phrase].preposition =
this_word.word;
this_turn.verified_sentence[
"phrase" + count.phrase
].preposition_properties = this_word.properties;
// don't advance counts or update structure
continue;
}
}
// 2) does last word preposition map to an aspect on next word noun?
// ex: go up on ladder
if (
last_word_is_direction &&
word_before_last?.type === "verb" &&
next_word?.type === "noun"
) {
// get nouns from next word
const nouns_from_next_word = this.parseNoun(next_word.word);
let found = false;
for (
let n = 0;
n < nouns_from_next_word.matches.qualified.length;
n++
) {
const asset_from_next_word = this.game.getAsset(
nouns_from_next_word.matches.qualified[n]
);
if (
asset_from_next_word?.$is("present") &&
asset_from_next_word.hasAspectAt(this_word.word)
) {
// is player in this aspect or can player enter this aspect?
const aspect = asset_from_next_word.getAspectAt(this_word.word);
if (aspect.player.can.enter) {
found = true;
continue;
}
}
}
// keep last word and discard this word
// ex: treat "go up on ladder" like "go up ladder"
// don't advance counts or update structure
if (found) continue;
}
// is it possible that the last preposition was an adverb?
// ex: walk carefully down stairs
// - was word before last a verb?
// - is next word a noun?
const last_word_is_adverb = this.game.dictionary.getAdverb(
last_word.word
);
if (
last_word_is_adverb &&
word_before_last?.type === "verb" &&
next_word?.type === "noun"
) {
// setting last preposition to adverb means
// - set verb.adverb to last preposition
this_turn.verified_sentence[`verb${count.verb}`].adverb =
last_word.word;
// - set last preposition in phrase to this preposition
this_turn.verified_sentence["phrase" + count.phrase].preposition =
this_word.word;
this_turn.verified_sentence[
"phrase" + count.phrase
].preposition_properties = this_word.properties;
// don't advance counts or update structure
continue;
}
this.game.debug(
`D1030 | verifySentence.js | parser found two or more prepositions`
);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg);
return false;
}
count.prep++;
count.phrase++; // a preposition starts a new phrase
this_turn.verified_sentence_structure += "preposition ";
this_turn.verified_sentence["phrase" + count.phrase] = {
preposition: this_word.word,
preposition_properties: this_word.properties,
};
continue;
} // preposition
// ----------------------------------------
// string
// ----------------------------------------
// if(this_word.type==="string") {
// continue;
// } // string
// ----------------------------------------
// noun
// ----------------------------------------
// noun and itself ----------------------------------------
// @TODO we have no other handling for itself yet
if (this_word.type === "noun" && this_word.word === "itself") {
if (!this_turn.verified_sentence["phrase" + count.phrase]?.noun) {
this.game.debug(`D1862 | verifySentence.js | itself has no referent`);
msg += this.getUnparsedMessage(this_turn.input);
this.game.print(msg);
return false;
}
if (last_word && last_word.type !== "preposition") {
count.phrase++; // a new noun starts a new phrase
}
this_turn.verified_sentence_structure += "noun ";
this_turn.verified_sentence["phrase" + count.phrase].noun = {
noun: this_turn.verified_sentence["phrase" + (count.phrase - 1)].noun,
noun_properties: this_word.properties,
};
// if there is a prior phrase...
if (
this_turn.verified_sentence["phrase" + (count.phrase - 1)].parsedNoun
) {
// copy that phrase's noun to this phrase
this_turn.verified_sentence["phrase" + count.phrase].parsedNoun =
new adventurejs.ParsedNoun().set(
this_turn.verified_sentence["phrase" + (count.phrase - 1)]
.parsedNoun
);
}
continue;
} // itself
// relative_direction aka up/down/in/out ----------------------------------------
if (
this_word.type === "noun" &&
this_word.properties.relative_direction &&
last_word &&
last_word.type === "verb" &&
next_word &&
next_word.type === "noun"
) {
// player has likely said something like "climb up tree"
// we typically parse directions as nouns but in cases like this
// we want to treat the direction as a preposition
this.game.debug(
` | verifySentence.js | ${this_word.word} appears to be a spatial direction / preposition for ${next_word.word}`
);
count.phrase++;
this_turn.verified_sentence["phrase" + count.phrase] = {}; // add new phrase
this_turn.verified_sentence["phrase" + count.phrase].preposition =
this_word.word;
continue;
} // this_word.properties.relative_direction
// noun and this_word.adjective ----------------------------------------
if (
this_word.type === "noun" &&
this_word.adjective &&
next_word &&
next_word.type === "noun"
) {
// word is both noun and adjective
// if next word is noun then it's likely that
// this is an adjective for the next word
// if we treat the two nouns as one phrase,
// do they have an unambiguous match?
// first try getting an asset
let found_asset_id;
let found_asset = this.game.getAsset(
this_word.word + " " + next_word.word
);
if (found_asset) found_asset_id = found_asset.id;
// otherwise parseNoun which does a wider search
if (!found_asset) {
let parse_both = this.parseNoun(
this_word.word + " " + next_word.word
);
found_asset_id = parse_both.matches.unambiguous;
found_asset = this.game.getAsset(found_asset_id);
}
if (!found_asset_id) {
this.game.debug(
`D1219 | verifySentence.js | ${this_word.word} appears to be an adjective for ${next_word.word} and no ${this_word.word} ${next_word.word} was found`
);
msg += `$(We) don't know of any ${this_word.word} ${next_word.word}. `;
this.game.print(msg, this_turn.output_class);
return false;
}
// we found a noun that matches both words
this.game.debug(
`D1863 | verifySentence.js | ${found_asset_id} was found to match ${this_word.word} + ${next_word.word} `
);
this_word.asset = found_asset;
this_word.word = found_asset_id;
position++; // advance past next word
// allow to go on to default noun handling
} // noun and adjective
// noun default ----------------------------------------
if (this_word.type === "noun") {
if (last_word && last_word.type !== "preposition") {
count.phrase++;
this_turn.verified_sentence["phrase" + count.phrase] = {}; // add new phrase
}
// console.warn("this_word", this_word);
// save the noun
this_turn.verified_sentence["phrase" + count.phrase].noun =
this_word.word;
// carry forward properties from parseSentence
this_turn.verified_sentence["phrase" + count.phrase].noun_properties =
this_word.properties;
// save any exclusions
if (this_word.exclusion) {
this_turn.verified_sentence["phrase" + count.phrase].exclusion =
this_word.exclusion;
}
this_turn.verified_sentence_structure += "noun ";
count.noun++;
continue;
} // noun
// ----------------------------------------
// adjective
// ----------------------------------------
if (this_word.type === "adjective") {
// Adjectives are tricky because nouns can have them defined in
// their names, like "red door", and nouns can also have specific
// adjectives assigned to them, so that "red" and "door" will find
// the "door" asset with adjective "red". If player entered an
// adjective that resolved to a noun, that noun will have been recorded
// and probably our sentence structure will have two nouns in it,
// something that we won't catch here, so we'll have to check again
// in handleSentence.js.
// We only end up here if an
// adjective has been typed that is not attached to a noun. For
// instance, no noun in the game uses "violet" but we understand that
// "violet" is an adjective and if a user inputs "x violet door"
// we can say "you don't see any violet door".
if (next_word && next_word.type === "noun") {
this.game.debug(
`D1218 | verifySentence.js | ${this_word.word} appears to be an adjective for ${next_word.word} and no ${this_word.word} ${next_word.word} was found`
);
msg += `$(We) don't know of any ${this_word.word} ${next_word.word}. `;
this.game.print(msg, this_turn.output_class);
return false;
} // adjective + noun
continue;
} // adjective
// ----------------------------------------
// unknown
// ----------------------------------------
if (this_word.type === "unknown") {
// @TODO: soft prompt for "oops"
this_turn.unknown_word = this_word.word;
this.game.debug(
`D1200 | verifySentence.js | ${this_word.word} is unknown`
);
if (position === 0 && this_turn.parsed_sentence.length > 1) {
msg += `$(We) don't know how to ${this_word.word} anything. `;
} else {
msg += `$(We) don't know of anything referred to as ${this_word.word}. `;
}
this.game.print(msg, this_turn.output_class);
return false;
} // unknown
}
// trim the sentence structure
this_turn.verified_sentence_structure =
this_turn.verified_sentence_structure.trim();
return true;
}; // verifySentence
})();