SRGS Best Practices

SRGS grammars, especially when combined with SISR, allow the creation of very complex and flexible documents. The underlying logic of rule expansions and rule references makes writing SRGS grammars almost like developing small applications — and adding JavaScript with SISR makes this even more true.

There are a number of traps new grammar writers often fall into that can be avoided with a few best practices. Consider a grammar designed to capture a two-digit number:

root $TwoDigits;

$TwoDigits = {out=0}

(

[$TensDigit {out+=rules.latest()}] [$OnesDigit {out+=rules.latest()}] |  

$TeensDigit{out+=rules.latest()} |  

[$OnesDigit {out+=rules.latest()*10}] [$OnesDigit {out+=rules.latest()}]

) {out=out.toString()};

$TensDigit =

ten {out=10} | twenty {out=20} | thirty {out=30} | forty {out=40} | fifty {out=50} | sixty {out=60} | seventy {out=70} | eighty {out=80} | ninety {out=90};

$TeensDigit =

eleven {out=11} | twelve {out=12} | thirteen {out=13} | fourteen {out=14} | fifteen {out=15} | sixteen {out=16} | seventeen {out=17} | eighteen {out=18} | nineteen {out=19};

$OnesDigit =

zero {out=0} | one {out=1} | two {out=2} | three {out=3} | four {out=4} | five {out=5} | six {out=6} | seven{out=7} | eight {out=8} | nine {out=9};

Avoid Ambiguity

Any input to a grammar should have only one valid parse. (The Grammar Tester in the Deployment Portal can show how many parses a grammar returns for a given input.)

The more parses a grammar has, the longer the Engine takes to decode an utterance against it — and the lower the accuracy. As the number of valid parses increases, decode time can increase dramatically.

The grammar above correctly handles parses such as "two one" or "twenty one." But if a caller says just "one," it allows two valid parses, because the last part of the root rule allows two optional $OnesDigit matches. Each parse has a different interpretation: the first $OnesDigit match multiplies the interpretation by 10, returning 10, while the second returns 1.

This sort of ambiguity not only increases decode time while decreasing accuracy, it also makes it harder for your application to handle results correctly. You would probably not expect a caller saying "one" to return a result of "10," but that is precisely one of the things this grammar allows.

Eliminate Unwanted Parses

You would obviously want to disallow the case above, where "one" has a valid parse that returns "ten." But even allowing "one" to be a valid parse is quite possibly a bad idea if all you want to capture are two-digit strings.

The grammar also allows other parses such as "twenty zero" (returning an interpretation of "20") or "ten two" (returning "12"). Even a null input is a valid parse ("" returns "0").

Unwanted parses slow down decodes and reduce accuracy. It is unlikely that a caller would ever say "twenty zero," or that a developer would want to allow that input. Accounting for such unlikely cases increases the chance that a caller behaving appropriately will be misrecognized — for example, someone who says "twenty two" might be mistaken for the unreasonable "twenty zero."

Keep Rules Compact

The larger and more complex rules are, the longer it takes to compile a grammar or decode against it. One good trick for keeping rules short is to combine rules with common words where possible. For instance, this rule:

$name = James Anderson | Jim Anderson | Jimmy Anderson | James | Jim | Jimmy;

...can be combined into:

$name = (James | Jim | Jimmy) [Anderson];

While the savings are small for one rule, across large grammars this kind of compactness adds up, decreasing load and decode times.

Be Careful with Recursion

SRGS allows recursive rules — that is, rules with references to themselves. Any time you work with recursion, be careful to avoid infinite loops. Because the grammar parser parses from left to right, always avoid left-hand recursion.

For instance, the following rule matches the word "foo" any number of times:

$rule = foo ($rule | $NULL);

If the input is "foo foo," the Engine parses the rule, expanding the $rule reference each time until it matches $NULL and terminates. On the other hand, if the rule is written:

$rule = ($rule | $NULL) foo;

...the parser gets caught in an infinite loop. The first thing it tries to do is expand the $rule reference, only to expand it again, and again, ad infinitum.


Related Articles


Was this article helpful?