SI script by Example
This example builds semantic interpretation into the numbers grammar from Intro to Semantic Interpretation, turning a spoken number into an actual value. It assumes you are comfortable with Rule Variables and the earlier pages in this series.
Tag Format Declaration
| #ABNF 1.0; language en-US; mode voice; tag-format <semantics/1.0>; |
The header declares semantics/1.0 as the tag format, which tells the recognizer to use the final SISR 1.0 Recommendation with SI Script tags rather than string literals. If you have disabled STRICT_SISR_COMPLIANCE, specify semantics/1.0.2006 instead — for backward compatibility with older drafts, Capacity Private Cloud requires "1.0.2006" to select the final 1.0 version. See Tag Format for the full set of format strings.
Setting Semantic Interpretation
As we build the rules, each rule that can match needs to set its rule variable:
| $base = (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}); $teen = ten {out = 10} | 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}; |
The out identifier names the current rule’s variable. Here we set $base and $teen to the integer value of the word that was spoken — as integers, not strings, so we can do arithmetic on them later.
Modifying Rule Variables
out can be operated on like any other variable, and we reach other rules’ variables with rules.rulename. In $twenty_to_ninetynine, we add the value of $base to the rule’s own variable:
| $twenty_to_ninetynine = ( twenty {out = 20} | thirty {out = 30} | forty {out = 40} | fifty {out = 50} | sixty {out = 60} | seventy {out = 70} | eighty {out = 80} | ninety {out = 90} ) [$base { out += rules.base }]; |
First out is set to 20, 30, … 90; then, if the optional $base matched, its value is added to out. So "twenty" yields 20 and "twenty four" yields 24.
The rules.latest() Object
A rule’s variable can be read by name with rules.rulename once it has matched — but when a rule has many alternatives, naming each one is tedious, and some matched rules cannot be referenced by name at all. rules.latest() solves both: it always holds the variable of the most recently matched rule. With it, $tens, $hundred, and $small_number become:
| $tens = ( $base | $teen | $twenty_to_ninetynine ) { out = rules.latest() }; $hundred = ( [a] hundred {out = 100} | $base hundred {out = 100 * rules.base} ); $small_number = $hundred {out = rules.latest()} [[and] $tens {out += rules.latest()}] | $tens { out = rules.latest() }; |
Composite Return Types
The grammar now returns a single integer. Often you want more than one piece of information back — and by default a grammar rule returns an object, which can carry additional properties.
Suppose we also want the exact phrase that was spoken, for transcription or to read it back to the caller. Every rule reference has a companion meta variable with a text property: read a specific rule’s text with meta.rulename.text, or the current rule’s with meta.current().text.
This addition produces a composite return type carrying both the spoken text and its numeric value:
| root $small_number_and_text; $small_number_and_text = $small_number { out.number = rules.latest(); out.text = meta.current().text }; |
A successful match now returns an object with two properties, number and text.
Here is the complete grammar:
| #ABNF 1.0; language en-US; mode voice; tag-format <semantics/1.0>; root $small_number_and_text; $base = (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}); $teen = ten {out = 10} | 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}; $twenty_to_ninetynine = (twenty {out = 20} | thirty {out = 30} | forty {out = 40} | fifty {out = 50} | sixty {out = 60} | seventy {out = 70} | eighty {out = 80} | ninety {out = 90}) [$base { out += rules.base }]; $tens = ($base | $teen | $twenty_to_ninetynine) { out = rules.latest() }; $hundred = ([a] hundred {out = 100} | $base hundred {out = 100 * rules.base}); $small_number = $hundred {out = rules.latest()} [[and] $tens {out += rules.latest()}] | $tens { out = rules.latest() }; $small_number_and_text = $small_number {out.number = rules.latest(); out.text = meta.current().text}; |
