← All examples
25

Canonical Grid model

Grammar Parsing

First-class grammars and parsers: CFG and PEG declarations, generator values, total PARSE results, and parse-tree extraction.

Scale
Medium
Source
25-grammar-parsing.grid
Length
68 lines
Collection
Advanced language
Level
Advanced
Runtime
Portable
Version
1.0.0

Watch it in Grid

See this model in motion.

Watch the model respond in the product, then inspect the exact source and checkpoints on this page.

Companion film

Parse, don’t split

Change a valid arithmetic expression to invalid syntax and watch Grid return structured rejection evidence instead of turning the parse into a model error.

47 secGrid 0.63.2
Open film page

What this model gives you

Three first-class grammar styles—CFG, PEG, and regular—compiled into bounded parser values with total results, structured errors, source-preserving trees, and named captures.

35 min to study · Source reviewed 2026-08-26

Continue with guided practice

What to notice

  • CFG, PEG, and regular grammar declarations
  • LALR, Packrat, and DFA generator values
  • Total parse-result handling
  • Parse-tree traversal and named captures

Requirements

  • Runtime with native grammar, parser-generator, and parse-tree support
  • Inputs and grammars must remain within authored and process ceilings
  • No connector or network

Expected checkpoint

A known state for this walkthrough.

With the source strings exactly as authored and the default process ceilings, lowered only by the 4,096-byte DFA generator bound. These semantic outputs follow the documented parser contract; the current verification scenario certifies compilation only.

Arithmetic parse
TRUE; source 2 * (3 + 4)accepted_ok / source_text
Number captures
3 nodes: 2, 3, 4number_count / number_texts
Invalid arithmetic
FALSE with structured offset, expected, and found fieldsrejected_ok / failure
Command captures
stop / feed_pumpissued_action / issued_name
Regular identifier
TRUEidentifier_ok
01 · CFG

Use a CFG when alternatives are unordered

The arithmetic grammar names tokens, whitespace, start rule, precedence layers, and a value capture. Applying LALR produces a parser value; left recursion remains grammar structure rather than a copied formula parser.

02 · Result

Branch on a total parse result

PARSE returns success or language rejection as a value. Check PARSE_RESULT_OK before projecting TREE or ERROR; a rejected sentence is not a failed workbook evaluation, while a budget breach remains a diagnostic.

03 · Tree

Read source spans and captures from the native tree

Successful trees retain the original source and nested node records. PARSE_TREE_CAPTURE returns every matching node in source order, so ordinary vector and record operations can traverse the syntax without reparsing text.

04 · PEG

Use ordered choice for command languages

The command PEG tries start, stop, and status in order and captures both action and identifier. Packrat supplies deterministic memoized parsing under the same authored work and node ceilings.

05 · Regular

Use a bounded DFA for lexical shapes

The regular identifier grammar uses a generator value whose 4,096-byte input limit can lower but never raise the host ceiling. Persisted models store grammar definitions and recipes; process-local automata are reconstructed after hydration.

25-grammar-parsing.grid
Get Grid
MODEL "Grammar Parsing"
DESCRIPTION "First-class grammars and parsers: CFG and PEG declarations, generator values, total PARSE results, and parse-tree extraction."
VERSION "1.0.0"
AUTHOR "Grid Team"
TAGS "canonical", "grammar", "parser", "parse-tree"

# A CFG for arithmetic: unordered alternatives with named captures on numbers.
GRAMMAR arithmetic IS CFG
  TOKEN NUMBER = /(?:0|[1-9][0-9]*)(?:\.[0-9]+)?/
  SKIP /\s+/
  START expression
  expression = expression "+" term | term
  term = term "*" primary | primary
  primary = value:NUMBER | "(" expression ")"
END

# Applying a generator to the grammar produces a parser value.
fast = arithmetic USING LALR

# PARSE is total: a language rejection is a parse-result value, not a failure.
accepted = PARSE("2 * (3 + 4)", fast)
accepted_ok = PARSE_RESULT_OK(accepted)
tree = PARSE_RESULT_TREE(accepted)

# Successful trees retain their source and expose nested node records.
source_text = PARSE_TREE_SOURCE(tree)
root = PARSE_TREE_ROOT(tree)
root_symbol = root.symbol
child_count = VECTOR_LENGTH(root.children)

# Named captures return matching node records in source order.
numbers = PARSE_TREE_CAPTURE(tree, "value")
number_count = VECTOR_LENGTH(numbers)
number_texts = MAP(numbers, node => node.text)

# Rejection produces a structured error record with exact coordinates.
rejected = PARSE("2 + * 3", fast)
rejected_ok = PARSE_RESULT_OK(rejected)
failure = PARSE_RESULT_ERROR(rejected)
failure_offset = failure.offset
failure_found = failure.found

# A PEG command language: choice is ordered, first success wins.
GRAMMAR command IS PEG
  SKIP /\s+/
  START command
  command = action:("start" / "stop" / "status") name:/[a-z_]+/
END

controller = command USING PACKRAT
issued = PARSE("stop feed_pump", controller)
issued_tree = PARSE_RESULT_TREE(issued)
issued_action = VECTOR_GET(PARSE_TREE_CAPTURE(issued_tree, "action"), 1).text
issued_name = VECTOR_GET(PARSE_TREE_CAPTURE(issued_tree, "name"), 1).text

# Generators are values: authored limits lower, never raise, process ceilings.
bounded = GENERATOR DFA(max_input_bytes = 4096)

GRAMMAR identifier IS REGULAR
  MATCH /[A-Za-z_][A-Za-z0-9_]*/
END

checker = identifier USING bounded
identifier_ok = PARSE_RESULT_OK(PARSE("feed_pump_21", checker))

summary = `parsed={accepted_ok} numbers={number_count} action={issued_action} name={issued_name}`

END MODEL