---
source_path: "reference/grammar.md"
canonical_url: "https://doc.sensory.com/tnl/7.9/reference/grammar/"
---

# Grammar-based recognition _(TrulyNatural only)_ _(STT only)_

Grammar-based recognition constrains a recognizer to a set of words and
structures defined by a grammar. Focusing recognition on a limited set of
phrases can improve speed and accuracy at the expense of recognizing arbitrary
input.

LVCSR models support grammar-based recognition with build-capable models. STT
models that support grammar decoding use the same grammar syntax. Some special
symbols are model-specific; those cases are noted below.

## Syntax

A [context-free grammar] is a set of rules that describes the sequences of
words that a recognizer can match.

### Definition

1. Grammars use [UTF-8][] encoding.
1. `#` marks the start of a comment, which extends to the end of the line.
1. A _grammar_ is a series of _rules_ representing variable definitions.
  The final rule in a grammar specifies the recognition vocabulary and typically
  references rules defined earlier. It should include the sentence start (`<s>`)
  and end (`</s>`) markers.
1. A _rule_ is an assignment of the form `name = expr ;` where
  `name` is a _symbol_ and `expr` is a sequence of _symbols_ and _operators_.
  `expr` is a type of [regular expression][].
1. A _symbol_ is a sequence of characters that does not include any whitespace
  or operators, optionally prefixed by sigils `$` or `~`. A symbol without a
  sigil is called a _terminal_ and is part of the recognition vocabulary,
  for example `temperature`. [Special symbols](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-special) are predefined
  terminals that describe input characteristics such as pauses and the edges of
  an utterance.
1. The `$` sigil does rule substitution _at build time_. The parser substitutes
  the value of the rule named `name` for `$name`. Substitutions include an
  implicit _grouping_ operator: Grammar `a = 1 | 2 | 3; b = <s> $a </s>;`
  is equivalent to `b = <s> (1 | 2 | 3) </s>;`.
1. The `~` sigil substitutes a named recognition
  class _at runtime_.
    - Each class is a recognizer with its own grammar, separate from the main
      grammar.
    - All references to a class use instances of the same class recognizer.
    - You can update each class in isolation, without having to recompile the
      main grammar.
    - If you have a large rule that's referenced multiple times, converting it
      to a class can speed up build time significantly.
    - Use classes to augment a recognition vocabulary at runtime. In a voice
      dialing application, for example, you can define the entire recognition
      grammar at build time but use `~contacts` instead of a predefined list of
      contact names. Once loaded, the application can scan the address book and
      build only the `~contacts` class.
    - Specify class definitions with [grammar-stream.classname](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#grammar-stream)
      or [phrases-stream.classname](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#phrases-stream), for example
      `phrases-stream.contacts`.
    - LVCSR models can also use [class libraries](https://doc.sensory.com/tnl/7.9/models/types/lvcsr.md#grammar-class-libraries),
      which are pre-built binary class repositories supplied separately from a
      grammar. Class libraries are LVCSR-only.
2. Operators include _grouping_ parentheses, brackets, and braces, _infix_
   operators that indicate logical AND and OR between symbols, and _postfix_
   operators that change how the preceding symbol matches input. The
   [operator precedence](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-op-precedence) table lists the order and
   direction in which the parser applies operators.
3. Grouping
    - `( )` Parentheses enclose items that are grouped together.
    - `[ ]` Square brackets enclose optional items.
      `[...]` is equivalent to `(...)?`.
    - `{ }` Braces implement slot-capturing lightweight NLU
      markup.
        - `{slotName a b c}` makes `a b c` available as the
          [nlu-slot-value](https://doc.sensory.com/tnl/7.9/api/setting-keys/results.md#nlu-slot-value) of [nlu-slot-name](https://doc.sensory.com/tnl/7.9/api/setting-keys/results.md#nlu-slot-name) `slotName` when the recognizer
          matches `a b c` to the input audio.
        - You can nest NLU slots to an arbitrary depth.
        - The outermost slots are defined as [intents](https://doc.sensory.com/tnl/7.9/api/setting-keys/results.md#nlu-intent-name) and all
          the nested slots in each intent as [entities](https://doc.sensory.com/tnl/7.9/api/setting-keys/results.md#nlu-entity-name).
        - Each identified intent invokes handlers registered for
          [^nlu-intent](https://doc.sensory.com/tnl/7.9/api/setting-keys/events.md#nlu-intent) and [^nlu-slot](https://doc.sensory.com/tnl/7.9/api/setting-keys/events.md#nlu-slot).
        - `{rule}` is shorthand for `{rule $rule}`.
        - With this grammar:
          ```
          seconds = 1 | 2 | 4 | 8 | half:0.5 a:? | a:? quarter:0.25 [of: a:];
          shutterSpeed = set shutter speed to {seconds} ( second | seconds );
          cmd = <s> {shutterSpeed} </s>;
          ```
          an utterance of "set shutter speed to a quarter of a second" will
          produce `set shutter speed to 0.25 second` as recognition output, with
          an additional [^nlu-intent](https://doc.sensory.com/tnl/7.9/api/setting-keys/events.md#nlu-intent) callback for the top-level
          `shutterSpeed` slot:
          ```
          NLU intent: shutterSpeed (0) = set shutter speed to 0.25 second
          NLU entity:   seconds (0) = 0.25
          ```
4. Infix operators
    - These are valid between symbols and may be surrounded by whitespace.
    - `^` is the conjunction operator and is implied between adjacent terminals:
      Grammar `g = one two three;` will recognize only the sequence
      "one two three".
    - `|` is the disjunctive operator. It separates alternative items.
      Grammar `g = one | two | three;` will recognize "one", or "two", or
      "three".
5. Postfix operators
    - These directly follow a symbol without any intervening whitespace.
    - `?` A question mark following a symbol makes that symbol optional:
      It requires zero or one repetitions of the symbol.
    - `+` A plus sign following a symbol or a group requires one or more
      repetitions of it.
    - `*` An asterisk following a symbol or a group requires zero or more
      repetitions.
    - `:` is the rewrite operator.
        - `left:right` recognizes symbol `left` but produces terminal `right`
          as a recognition result.
        - `left:` recognizes symbol `left` but rewrites that to an empty
          string, eliding `left` from the recognition result.
        - `:right` inserts `right` into the recognition result.
          If you say "one two three", grammar
          `g = <s> one :mississippi two :mississippi three </s>;` produces
          "one mississippi two mississippi three".
    - `/` A forward slash following a symbol followed by a floating point number
      defines a weight to be associated with that symbol. If there's a rewrite
      operator (`:`) the slash must follow the rewritten-to terminal, for
      example: `one:een/0.123` Weights are in the logprob domain, convert from a
      $[0, 1]$ probability to a weight with $w = -log_{10}(p)$.
      The default symbol weight is `0` for a probability of `1.0`.
6. `\` escape symbol. To include a literal special character in a grammar
  specification, escape it with a backslash. The list of characters that support
  this include: `^`, `|`, `*`, `+`, `?`, `=`, `[ ]`, `( )`, `;`, `#`, and `:`.

**Also see these related items:** [grammar-stream](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#grammar-stream), [phrases-stream](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#phrases-stream), [nlu-grammar-stream](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#nlu-grammar-stream),
[^nlu-intent](https://doc.sensory.com/tnl/7.9/api/setting-keys/events.md#nlu-intent), [^nlu-slot](https://doc.sensory.com/tnl/7.9/api/setting-keys/events.md#nlu-slot)

### Operator precedence

The following table lists the precedence and associativity of grammar
operators. Operators are listed in descending precedence: level `0`
is applied first and level `5` last.

Precedence | Operator | Description | Associativity
:---------:|:--------:|-------------|--------------
0 | `:`    | Rewrite output
0 | `/`    | Symbol weight
1 | `( )`  | Grouping
1 | `[ ]`  | Optional group
1 | `{ }`  | Slot-capturing semantic markup
2 | `?`    | Zero-or-one symbol           | left-to-right
2 | `+`    | One-or-more symbols          | left-to-right
2 | `*`    | Zero-or-more symbols         | left-to-right
3 | `^`    | And, implied between symbols | right-to-left
4 | `|`    | Alternative                  | right-to-left
5 | `=`    | Rule assignment              | right-to-left

This grammar:

```
a = one | two three four;
g = <s> ( $a | five six) </s>;
```

will recognize only these phrases:

```
one
two three four
five six
```

### Special symbols

A grammar can include these special symbols:

- `<s>` - The silence at the start of a sentence.
- `</s>` - The silence at the end of a sentence.
- `<wp>` - Short pauses between words. The grammar compiler automatically
  adds these where needed, so there is no need to do so explicitly.
  Do **not** add `<wp>` to NLU grammars, use `<pause/>` instead.
- `<pause/>` - An explicit short pause.
- `<no-match/>` _(TrulyNatural only)_ - Matches when none of the alternatives are likely
  (i.e. "none of the above").
    + Recognition results at the phrase level can include `<no-match/>` even
      if this symbol was not explicitly used in the grammar. This is an
      indication that the result was rejected due to [search.frame-nota](https://doc.sensory.com/tnl/7.9/api/setting-keys/configuration.md#searchframe-nota), or
      that RAM or CPU constraints limited the recognizer's ability to produce a
      result.
- `<unknown/>` _(TrulyNatural only)_ - Similar to `<no-match/>`. In *some* LVCSR models the
  threshold for determining whether this symbol matches better than any other
  is different from that of `<no-match/>`.
    + _(STT only)_ In STT grammars, `<unknown/>` matches an out-of-grammar word span at
      a specific point. Use this when the grammar should keep matching even if
      part of the utterance is not in the fixed vocabulary. In word-level STT
      models, `<unknown/>` expands to the primitive wildcard sequence
      `<unknown-start/> <unknown-cont/>*`. In character-level STT models, it
      expands to `(<unknown-start/> | <unknown-cont/>)`.
    + _(STT only)_ By default, `<unknown/>` has an OOV penalty so in-grammar words win
      when they fit the audio. Add an explicit symbol weight, for example
      `<unknown/>/2`, to tune this penalty in a grammar.
    + _(STT only)_ In NLU output and in the final recognition result, the matched word is
      emitted unless the grammar uses a rewrite. Use `<unknown/>:` to match and
      drop it from these results.
- `<unknown-start/>` / `<unknown-cont/>` _(STT only)_ - Primitive OOV wildcard symbols
  used by `<unknown/>`. Use them directly only when you need explicit control
  over word-boundary matching or continuation weighting.
- `<dictation/>` _(STT only)_ - Hands off recognition to the STT model's statistical
  language component for free-form speech. This is a one-way hand-off; matching
  does not return to the grammar afterward.
- `.` - When used with lightweight [NLU grammars](https://doc.sensory.com/tnl/7.9/api/setting-keys/runtime.md#nlu-grammar-stream), a single
  period matches any input word. If `.` is also used as the output label, the
  matched input word is echoed. Use `.:*` to match any input words and remove
  them from the NLU result.

### Out-of-grammar words in STT grammars

Use `<unknown/>` to allow an STT grammar slot to absorb words outside the fixed
grammar vocabulary. Add the empty rewrite operator (`:`) when the grammar should
match the unknown word but omit it from the captured value:

```text
digit = one | two | three | four | five | six | seven | eight | nine | zero | <unknown/>:;
g = <s> {number $digit+} </s>;
```

A non-zero weight overrides the default OOV penalty for that arc:

```text
item = <unknown/>/2;
```

### Formal syntax

The following [ABNF][] (per [RFC 5234][]) describes the surface syntax of the
native grammar language: which sequences of tokens are well formed. It is a
convenience for authors building editor support or validation tooling and is
not a substitute for the descriptions above.

Two things it deliberately does not express:

- **Operator precedence and associativity.** ABNF cannot capture these, so the
  `expr` and `concatenation` rules below are intentionally permissive. The
  binding of operators is fixed by the [operator precedence](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-op-precedence)
  table, which is authoritative.
- **Static validation.** Constraints such as unique rule names, resolvable
  `$name` references, and a non-empty start rule are enforced by the compiler,
  not by this grammar.

Grammars are [UTF-8][] encoded. The character classes below enumerate ASCII for
readability; any non-ASCII UTF-8 code point is also an unreserved character.

```abnf
; ---- top level ----
grammar         = *ws *( rule *ws )
rule            = rule-name *ws "=" *ws expr *ws ";"
rule-name       = name

; ---- expressions ----
; One or more alternatives separated by "|".
expr            = concatenation *( *ws "|" *ws concatenation )
; A sequence of terms. Adjacent terms concatenate implicitly (whitespace);
; the explicit "^" conjunction is equivalent to juxtaposition.
concatenation   = term *( ( 1*ws / *ws "^" *ws ) term )
; A primary optionally followed by postfix repetition operators. Postfix
; operators immediately follow the operand with no intervening whitespace.
term            = primary *postfix-op
postfix-op      = "?" / "+" / "*"

primary         = group / optional-group / slot / symbol
group           = "(" *ws expr *ws ")"
; "[ ... ]" is equivalent to "( ... )?".
optional-group  = "[" *ws expr *ws "]"
; "{ slotName a b c }" captures "a b c" as slot "slotName";
; "{ rule }" is shorthand for "{ rule $rule }". Slots may nest.
slot            = "{" *ws [ slot-name 1*ws ] expr *ws "}"
slot-name       = name

; ---- symbols ----
; Any symbol may carry an optional rewrite and/or weight. A bare "/float"
; is an epsilon symbol carrying only a weight.
symbol          = recognized [ rewrite ] [ weight ]
                / rewrite [ weight ]
                / weight
recognized      = rule-ref / class-ref / terminal
rule-ref        = "$" name          ; build-time rule substitution
class-ref       = "~" name          ; runtime recognition-class substitution
terminal        = name              ; a word in the recognition vocabulary
; "left:right" recognizes left, emits right; "left:" emits nothing;
; ":right" is a pure insertion (no recognized input).
rewrite         = ":" [ name ]
; Weight in the log-prob domain (w = -log10(p)); default 0.
weight          = "/" float

; ---- names and lexical primitives ----
; ":" and "/" are ordinary name characters; they take on their rewrite/weight
; meaning only when a symbol is decomposed. A literal ":" is written "\:".
name            = 1*name-char
name-char       = unreserved / escaped
; Any single character that is not reserved. Reserved characters are
; whitespace, ";", "#", and the operator/grouping characters
; ^ | * + ? = [ ] ( ). Any non-ASCII UTF-8 code point is also unreserved.
unreserved      = %x21-22 / %x24-27 / %x2C-3A / %x3C / %x3E / %x40-5A
                / %x5C / %x5F-7B / %x7D-7E / %x80-10FFFF
; A backslash-escaped reserved character. The backslash is removed.
escaped         = "\" escapable
escapable       = "^" / "|" / "*" / "+" / "?" / "=" / "[" / "]"
                / "(" / ")" / ";" / "#" / ":"

float           = [ sign ] ( 1*DIGIT [ "." *DIGIT ] / "." 1*DIGIT ) [ exponent ]
exponent        = ( "e" / "E" ) [ sign ] 1*DIGIT
sign            = "+" / "-"

; ---- whitespace and comments ----
; A "#" begins a comment that runs to end of line. Newlines are ordinary
; whitespace and do not terminate a rule; only ";" does.
ws              = WSP / newline / comment
comment         = "#" *( %x00-09 / %x0B-FF ) ( newline / end-of-input )
newline         = CR / LF / CRLF
end-of-input    = ""

; Core rules from RFC 5234 Appendix B: WSP, CR, LF, CRLF, DIGIT.
```

The predefined [special symbols](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-special) (`<s>`, `</s>`, `<pause/>`,
`<unknown/>`, and so on) lex as ordinary `terminal`s.

## BNF+EM v2.1 grammars _(TrulyNatural only)_ _(STT only)_

In addition to the native syntax described above, the SDK accepts grammars
written in the [BNF+EM v2.1][] format used by other speech vendors. Support for
this format eases migration of existing Cerence-style grammars.

### Format detection

A grammar is parsed as BNF+EM when its first non-comment content is a
`#BNF+EM` header, for example `#BNF+EM V2.1;`. Otherwise the [native
syntax](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-syntax) is used. A single grammar is entirely one format or the
other; you cannot mix the two.

```text
#BNF+EM V2.1;
!grammar lights;
!start <command>;
<command>: ( turn ) ( on | off ) [ the ] ( <NULL> | living room ) lights ;
```

### Supported subset

The SDK compiles the subset of BNF+EM most commonly used in command-and-control
grammars. Each construct maps onto an equivalent in the native syntax, so its
recognition behavior matches the corresponding native form:

BNF+EM | Native equivalent | Notes
-------|-------------------|------
`<rule>` reference | `$rule` | Rule substitution.
`!grammar NAME ;` | — | Names the grammar. Quoted names are accepted.
`!start <rule> ;` | final rule | Declares the single entry-point rule.
`<NULL>` | `:` | Matches with no speech (epsilon).
`<VOID>` | `~void` | Never matches (an unmatchable alternative).
`a | b` | `a | b` | Alternatives.
`( ... )` | `( ... )` | Grouping.
`[ x ]`, `!optional(x)` | `[ x ]` | Optional group.
`x*`, `x+`, `!repeat(x, min, max)` | `x*`, `x+`, bounded closure | Repetition.
`!tag(LABEL, expr)` | `{ LABEL expr }` | [NLU slot markup](https://doc.sensory.com/tnl/7.9/reference/grammar.md#nlu-markup).
`<...>` | `<unknown/>` | [Out-of-grammar words](https://doc.sensory.com/tnl/7.9/reference/grammar.md#grammar-unknown).

### Unsupported constructs

The following BNF+EM constructs are not yet supported. A grammar that uses one
is rejected with an error that identifies the construct and its position, rather
than compiling to something unexpected:

- `!import` and cross-grammar references (`<grammar#rule>`).
- `!start` with more than one entry-point rule.
- `!slot` runtime-attachable rules.
- `!id(...)` user identifiers.
- `!pronounce`, both the inline modifier and the grammar-wide statement.
- Quoted-string terminals (`"..."`).
- Other inline `!` modifiers, and `!language`.

Support for these constructs may be added in a future release.

<!-- Reference definitions from includes/links.md -->
[ABNF]: https://en.wikipedia.org/wiki/Augmented_Backus%E2%80%93Naur_form "Augmented Backus-Naur Form"
[BNF+EM v2.1]: https://docs.vdk.vivoka.com/vsdk/v4/vsdk_tutorials_vsdk-csdk_grammar_formalism.pdf "BNF+EM v2.1 grammar formalism"
[regular expression]: https://en.wikipedia.org/wiki/Regular_expression
[RFC 5234]: https://www.rfc-editor.org/rfc/rfc5234 "RFC 5234: Augmented BNF for Syntax Specifications: ABNF"
[UTF-8]: https://en.wikipedia.org/wiki/UTF-8

<!-- Abbreviation definitions from includes/abbreviations.md -->
*[API]: Application Programming Interface
*[LVCSR]: Large Vocabulary Continuous Speech Recognition model, feed-forward neural net acoustic model with FST decoder
*[NLU]: Natural Language Understanding model
*[RAM]: Random Access Memory
*[SDK]: Software Development Kit
*[STT]: Speech To Text: transformers with language model and CTC decoding
*[TNL]: TrulyNatural, Sensory's large-vocabulary speech recognition technology
