This is the first very much work-in-progress of an integration test of PEG.js, by David Majda, to simulate a parser like Inform6, by Graham Nelson, but using js/TS language.
Working version: https://creativaweb.it/parser/
The complete project involves the creation of a development tool for interactive fiction that reproduces the functions of Inform 6 (at least the most importants) in Typescript language.
This is only the integration test environment for the use of PEG.js as parser generator in the different natural languages like english, italian, etc.
There are no library dependencies except on PEG.js ver. 0.10.0. (current release) here used in the minified version.
At this moment there are only some test available in the italian language. If you want to change the language to be loaded, change the meta
line in the <head>
element with the name
="meta-lang". Only the first two letters will be considered as key to access the specific language (i.e. ITalian).
For example:
<head>
<meta name="meta-lang" content="ITalian">
</head>
When the index.html file is loaded the page is divided into three areas:
-
Main Area: the command line input and result output.
It can take on four colors:- white: when empty,
- green: when the command has been correctly understood and executed,
- orange: when the command has been parserized but not understood or is not actionable,
- red: when the command has not been parserized because it is not defined in the grammar.
You can clear entirly the command line by click the "C" button.
-
World in scope: the simplified world in scope currently considered.
It can be "the whole world" or refer to the actor to whom the command refers (the Player is the default).
It is possible scroll up and down the scope world definition. -
Verb definitions: the verb grammar definitions in Inform 6 style.
It is possible scroll up and down the list to view all the verb definition.
To define a verb you have to supply the follow informations:
{
meta: true / false, //(optional)
words: ['word1', 'word2', ...], //(optional)
patterns: [
tokens: ['token1', 'token2', ...] //(optional)
action: Action,
reverse: true / false //(optional)
]
}
This is an optional flag. You can omit meta or set its value to false
. Otherwise you can set its value to true
to tell the system the verb is a meta verb (see DM4 Inform 6 Manual for other informations).
This is an array of string delimited with a single apostrophe. These are multi choices to define the same verb with a given pattern.
This is an array of patterns to apply at the user input to identify the correct action
to activate.
Each pattern is composed of
tokens
: an optional array of token (see below other infos)action
: a js function that accept aparams
argument, an object with all the phrase infos (see below other infos)reverse
: is an optional flag to specify the inverse order of params (noun and second, see below). You can omit reverse or set its value tofalse
.
Tokens is an optional array to identify the correct action
to activate.
A token is defined always as a single apostrophe string but:
- string token (as verbs or other words), must be delimited interior with also quotation marks (i.e.
'"word"'
) - normal token (as noun, multi, etc), must be delimited only with single apostrophe (i.e.
'multi'
)
For a list of string token option use a slash / (i.e. '"fullscore" / "punteggio"'
); each word is delimited with a quotation mark couple, all the optional words are delimited as a unique token with an apostrophe couple. Spaces can be omitted.
The follow example
{
meta: true,
words: ['score', 'punteggio'],
patterns: [
{ tokens: ['"completo" / "pieno"'], action: 'FullScore', reverse: false },
{ action: 'Score' },
]
}
is generated as
Verb meta 'score', 'punteggio'
* 'completo' / 'pieno' -> FullScore
* -> Score;
For all token options and semanthic see DM4 Inform 6 Manual.
The verb definition order define the priority of trying apply the words patterns.
The pattern order is relevant for the priority of trying apply the token patterns.
Always put more complete and accurate (longest) patterns before (see the example below).
The internal token order in the array is semanthic relevant.
{
words: ['prendi', 'trasporta', 'afferra', 'raccogli'],
patterns: [
{ tokens: ['multiInside', 'DaPrep', 'noun'], action: 'Remove' },
{ tokens: ['multi'], action: 'Take' },
]
}
The apply and choice strategy in the parsing phase is slightly different from Inform 6 but simpler to predict in effects.
Each action
is the name of a js function that receives an object as parameter called params
.
// "prendi la giacca e i pantaloni dall'armadio"
function action(params) {
console.log(params); // see below
}
{
"actor": "Player",
"verb": "prendi",
"action": "Remove",
"noun": {
"multiInside": [
"giacca",
"pantaloni"
]
},
"second": {
"noun": "armadio"
}
}
The params
object is defined as below:
{
actor: who activate the action (Player is the default), always defined,
verb: the verb string used in the command,
action: the function to activate, always defined,
noun: the first token in the command,
second: the second token in the command,
}
Not all the fields are always defined when the action is called. Articles and prepositions are skipped.
There are many types of tokens for the placeholder noun
and second
as descripted in DM4 Inform 6 Manual. For the token multi*
(multi, multiInside, multiExcept) is adopted the js convention (camelcase except the first letter) and the placeholder (noun or second) is an array
(i.e. {"multiInside": ["giacca","pantaloni"]}
).
Otherwise, if the token get a single value (noun, held) the placeholder get a string.
The possible results of parsering are:
- successful result: the user command is fully understood and an action activated
- warning result: the user command is clear but something is not compatible (an object not found in the scope, no object compatible with the command, ...)
- error result: no pattern found compatible with the user command (syntax error)
If you want to avoid having an headache, it is very helpful to follow few simple rules to obtain only successful or warning results so the user experience can be nice.
- use always a single apostrophe couple to delimit a token (i.e.
'noun', 'prep'
) - add an internal quotation mark couple to delimit a specific string token (i.e.
'"score"'
) - if you define more patterns for the same words in a single verb definition, put first the longest and more accurate definition. The more precise comes first
{
words: ['prendi', 'trasporta', 'afferra', 'raccogli'],
patterns: [
{ tokens: ['multiInside', 'DaPrep', 'noun'], action: 'Remove' },
{ tokens: ['multi'], action: 'Take' },
]
}
multi
skip the articles but don't manage the preposition and "eat" al tokens until the end of command if no preposition are found: patterns withmulti
always at last position as last chance- the patters like
* 'noun' 'prep' 'second'
always before - if you have two pattern very similar but with two different preposition and the same action you can reduce them in a single pattern like
* 'noun' ('prep1' / 'prep2') 'second' -> 'action'
{
words: ['siediti' ,'siedi', 'sdraiati' ],
patterns: [
{ tokens: ['SuPrep / APrep', noun], action: 'Enter', reverse: false },
]
}
- if two or more items in the words list have the same radix (i.e.
inventory
,inv
,i
), put always before the longest one
To create a new language version
- create a dir under
"lang"
with the same name of language label (IT, EN, ...). - create the rules definition file
./js/lang/XX/rules-XX.js
for prepositions, articles, conjunctions a decimal separator - create the words definition file
./js/lang/XX/words-XX.js
for compass and other words - create the verbs definition file
./js/lang/XX/verbs-XX.js
for actions activation - create the world definition file
./js/lang/XX/world-XX.js
for a testing world definition
This is not a complete system as a replacement for Inform 6. This is just a test environment with some useful algorithms reuseble in the complete design.
If you have any suggestions or problem reports, please, check the project Issues section.