Files
skills/docs/foundation/flow/cbor/src/cddl/parser.md
2026-05-29 08:20:56 +01:00

2.5 KiB

parser

Purpose

CDDL parser per RFC 8610 §3 grammar. Consumes the token stream from the tokenize function and produces a typed AST (CddlAst) that the validator walks. Supports the working subset of CDDL needed by real consumers (CWT, COSE_Sign1, WebAuthn attestation, vault manifests, drive templates).

Features

  • Rule definitions: name = T, name<T> = ... (generic params).
  • Type expressions: primitive prelude, named references, generic instantiation.
  • Type composition: choice (/), inclusive (..) + exclusive (...) ranges, parenthesised grouping.
  • Group composition: arrays ([ ... ]), maps ({ ... }), occurrence indicators (?, *, +, *N, M*N, M*), group choice (//).
  • Map keys: identifier-:-T (text key), "quoted": T (text key with quoted syntax), <int> => T (numeric key), "quoted" => T.
  • Tagged items: #6.<tag>(T) produces { kind: "tagged", tag, inner }.
  • Literal values: text strings, byte strings (h'…'), integers (positive and negative), true/false, null/nil, undefined.
  • Control operators applied to types: tstr .size 1..32, tstr .regexp "pattern", bstr .cbor T. Both literal and range right-hand sides supported on .size.

Usage: Basic Example

import { parseCddl } from "@suaveplan/cbor/cddl";

const ast = parseCddl(`
  cwt = { iss: tstr, ? exp: int }
`);

Usage: Advanced Example

import { parseCddl } from "@suaveplan/cbor/cddl";

const ast = parseCddl(`
  attested-key = #6.18(cose-sign1)
  cose-sign1   = [ protected: bstr, unprotected: { * any => any }, payload: bstr / nil, signature: bstr ]
  protected    = bstr .size 0..1024
  pair<K, V>   = [ K, V ]
`);

API Reference

  • parseCddl(source: string): CddlAst

Throws CborError(CDDL_PARSE_FAILED) with { line, col } for: empty source (no rules), missing =, unexpected token, non-integer range bound, #N with N != 6.

Implementation Notes

The parser is a hand-written recursive descent. Operator precedence mirrors RFC 8610 §3:

  • type (top): handles / choice
  • type1: handles .. / ... ranges and control operators (.size, .regexp, etc.); the .size case can take a range right-hand side
  • type2 (atom): primitives, references, literals, parenthesised groups, arrays, maps, tagged items

Group choice (//) is implemented at the [/{ body level — when the parser encounters // between two sets of entries, it returns a { kind: "choice", alternatives: [arrayA, arrayB] } so the validator can short-circuit on the first matching alternative.