Custom parser types - ThePix/QuestJS GitHub Wiki

The parser is built around matching commands, and then matching items within the commands. What if you want your command to match something other than items? For example, FILL GOBLET WITH WINE. FILL and WITH are fine, they are part of the command matching. The GOBLET is an item. But what about WINE? You want the command to match other liquids too, but wine is not an item.

Every command has an "objects" attribute; an array or dictionary of dictionaries. Usually this tells Quest about what sort of objects to expect - hence the name. However, you can also use it to tell Quest to expect something else altogether, using the "special" attribute.

This example is taken from the SAY command. It says the first bit will be text (the verb; SAY, SHOUT, etc.), and the second bit is also text, what is being said.

    objects:[
      {special:'text'},
      {special:'text'},
    ]

This example is for pushing an object in a direction, and shows how we can mix it up.

    objects:[
      {special:'text'},
      {scope:parser.isHere, attName:"shiftable"},
      {special:'direction'},
    ]
  })

Built-in parser specials

A number of options are built-in. In the table below, the name is what you need to use in the "objects" array, as seen in the examples above. The "Returns" indicates what value will be added to the array passed to the command's "script" function (on a successful match).

Name Matches Returns
ignore Anything Nothing
text Anything The full text
direction Any direction, as set up in lang.exit_list The full name of the direction (eg "northwest")
number Any number in digits or any number as a word from zero to twenty The number (as a number, not a string)
fluid Any fluid listed in settings.fluids The fluid
tone Any fluid listed in settings.tones The tone

Simple parser specials

You can add your own types. Let us say you want to add adverbs. It is debatable if it is a good idea at all, but it does illustrate the technique.

We set up the adverbs in the "setup" function in settings.js. If you already have a set up function, you just want to insert this - without the first and last line - into that.

settings.setup = function() {
  parser.generateSpecialText(
    'adverb',
    ['carefully', 'quickly', 'elegantly'],
    'Not recognising the adverb there.'
  )
}

The parser.generateSpecialText function takes three parameters; a name, an array of allowed values and a message to use when there is no match.

Then (in code.js, perhaps), add your command, setting the "objects" array to use a special with the name you used before.

new Cmd('TalkToAdverb', {
  regex:/^(?:talk to|speak to|talk|speak) (.+) (.+)$/,
  objects:[
    {scope:parser.isNpcAndHere},
    {special:'adverb'},
  ],
  script:function(objects) {
    msg('You talk to {nm:npc:the} {show:text}', {npc:objects[0][0], text:objects[1]})
    return world.SUCCESS
  },
})

Note that in the script, the item is an element in an array, which is itself in an array (objects[0][0]). This is because the player can use commands with several items at once in some cases, such as GET ALL. However, the special text is just an element in an array, so is objects[1], not objects[1][0].

We can make it better! The parser.generateSpecialText can handle arrays in the array, allowing for synonyms. The value passed to the command's "script" function will always be the first element of the array. We can also use the text processor in the error message.

settings.setup = function() {
  parser.generateSpecialText(
    'adverb',
    [['carefully', 'cautiously', 'c'], ['quickly', 'fast', 'q'], 'elegantly'],
    'Not recognising the adverb "{show:text}".'
  )
}

We can also modify the command to allow the adverb at the start. This is best done using named capture groups, so the "objects" attribute is now a dictionary.

new Cmd('TalkToAdverb', {
  regexes:[
    /^(?:talk to|speak to|talk|speak) (?<npc>.+) (?<adverb>.+)$/,
    /^(?<adverb>.+) (?:talk to|speak to|talk|speak) (?<npc>.+)$/,
  ],
  objects:{
    npc:{scope:parser.isNpcAndHere},
    adverb:{special:'adverb'},
  },
  script:function(objects) {
    msg('You talk to {nm:npc:the} {show:text}', {npc:objects[0][0], text:objects[1]})
    return world.SUCCESS
  },
})

Complex parser specials

What if you want something more complicated? We can add a new attribute to parser.specialText. This has to have two function attributes, "error" and "exec", both of which will be sent the text. The "error" function should either return false if it is not an error - the text is acceptable - or a message otherwise (it should not print anything itself).

The "exec" function should return whatever is to be added to the list of matched items. In this example (which is doing adverbs the long way), it is the text, but you could look up a value in a dictionary and return that. If this returns false, nothing gets added to the list of items.

parser.specialText.adverb = {
  error:function(text) {
    if (settings.adverbs.includes(text)) return false
    return processText("Hmm, I was expecting an adverb, and I got {show:text}. Not one I know.", {text:text})
  },
  exec:function(text) {
    return text
  },
}
⚠️ **GitHub.com Fallback** ⚠️