moonabnf

    Byte-oriented ABNF grammar AST, validation, budgeted recognition and input derivation trees

    abnf
    rfc5234
    rfc7405
    parser
    Download zip
    Version
    0.4.0
    License
    MIT
    Last updated
    21 hours ago
    Downloads
    137

    No dependencies

    #MoonABNF

    CI Fresh clone License: MIT

    MoonABNF 用 MoonBit 实现 ABNF 解析,保留字节 API,当前源码另有独立 Unicode scalar 应用域。调用方提供文法,编译后可识别完整输入、生成输入派生树并提取字段。

    文法文本 → 文法 AST → 编译校验 → 整段识别 / 输入派生树 → 按规则名取出原字节字段。 实现依据 RFC 5234(ABNF)与 RFC 7405(大小写敏感字符串)。

    适合:协议字段离线诊断、配置格式校验、二进制 fixture 回归、把 RFC 文法变成可执行的测试。

    #先选择源码还是已发布包

    交付物能力与工具链入口
    当前源码Bytes API、预算内左递归识别 / 输入树、独立 Unicode scalar API、显式组合 prose;工具链锁定 moonc 0.10.18+6c83a7f62技术证据与复现、scalar / prose、左递归边界
    Mooncakes 0.3.0已发布的 Bytes API、输入派生树、字段提取与有界失败诊断;历史锁定工具链为 0.10.14+7d59c7ec90.3.0 版本说明

    源码新增能力不属于已发布的 0.3.0。 当前源码的 moon.mod 仍保留 0.3.0;这个版本字段不是再次发布或覆盖旧包的凭据。源码分支、GitHub Release 和 Mooncakes 发布应分别核验。

    #当前源码快速开始

    准备 TOOLCHAIN.lock 中的已有 MoonBit SDK,以及 Node.js、Python 3 和 native 后端需要的 C 工具链。以下示例假定 POSIX shell;不是 Windows 原生执行验证。

    git clone https://github.com/sundaysebasidian-byte/MoonABNF.git cd MoonABNF export MOONABNF_SDK=/absolute/path/to/existing/locked-sdk scripts/moon-local.sh version --all scripts/moon-local.sh test --target js scripts/moon-local.sh run examples --target js scripts/moon-local.sh run examples/protocols/cmd --target js scripts/moon-local.sh run examples/scalar_prose --target native

    moon-local.sh 会核对编译器版本。复现特定结果时还须检出对应提交,并保留本地补丁;验收脚本会记录 Git HEAD、工作树状态与源码 SHA-256,不把旧分支日志当作当前源码的测试结果。

    #Mooncakes 0.3.0 快速开始

    只需已发布功能时,在自己的项目里指定精确版本:

    moon add sundaysebasidian-byte/moonabnf@0.3.0

    // moon.pkg
    import { "sundaysebasidian-byte/moonabnf" @abnf }

    mooncakes 上的 0.3.0 包含 parse / compile / recognize、输入派生树与字段提取(parse_input、rule_nodes_in、extract)和 diagnose_input,已从 registry 全新安装并在 Wasm、Wasm GC、JS、native 上验证。协议集成方式见下文。

    #用法

    #1. 校验一个字段

    match @abnf.compile(b"field = %i\"Trace\" \":\" SP 1*8HEXDIG CRLF\r\n") {
    Err(d) => println("\{d.code} at line \{d.line}, column \{d.column}")
    Ok(program) => {
    let r = program.recognize("field", b"TRACE: aF12\r\n")
    assert_true(r.status is @abnf.Matched)
    }
    }

    文法写错时得到带错误码、字节偏移和行列号的 Diagnostic,例如 undefined-rule、prose-unsupported、duplicate-rule。

    #2. 取出字段的原始字节

    let program = @abnf.compile(
    b"record = key \":\" value\r\nkey = 1*ALPHA\r\nvalue = 1*DIGIT\r\n",
    ).unwrap()
    let input = b"Age:42"
    let report = program.parse_input("record", input)
    match report.status {
    @abnf.Matched => {
    let tree = report.tree.unwrap()
    let ids = tree.rule_nodes_in("value", tree.root).unwrap()
    assert_eq(tree.extract(input, ids[0]), Some(b"42"))
    }
    @abnf.NoMatch => println("no-match at byte \{report.offset}")
    @abnf.BudgetExceeded(why) => println("indeterminate: \{why}")
    _ => ()
    }

    本页代码片段都在 readme_test.mbt 中作为测试运行;完整示例见 examples/input_tree/main.mbt。

    #3. 用真实 RFC 文法

    examples/protocols 直接使用 RFC 3986(URI)和 RFC 6265(Cookie)的 ABNF,输出输入树前三层和各组件的字节跨度:

    URI components (syntax only, no percent decoding): scheme=[0,5) raw=b"https" host=[8,21) raw=b"[2001:db8::1]" port=[22,26) raw=b"8443" ... Cookie pairs (duplicates and quotes preserved): cookie-name=[0,7) raw=b"session" cookie-value=[8,11) raw=b"abc" ... Invalid Cookie: no-match at byte 11 Limited Cookie: indeterminate (tree-nodes), tree absent=true

    另有 RFC 9110 ETag(examples/http_etag)与 RFC 9112 请求行(examples/request_line)示例,输出都与仓库里的 expected_output.txt 逐字节比对。

    实际应用:离线采集记录导入门禁复用独立URI/Cookie消费者, 把规范原例子转换为带字节span的JSON字段;用不同退出码区分语法拒绝、预算未判定和配置错误。 只有两字段均匹配才导出数据,并与仅识别的现有API作具体对照;不宣称完整HTTP支持或生产采用。

    #4. 命令行

    scripts/cli.sh check fixtures/protocol.abnf scripts/cli.sh match fixtures/protocol.abnf field fixtures/protocol-valid.bin

    输出 JSON,退出码可直接用于脚本门禁。

    #5. 解释为什么不匹配

    program.diagnose_input(rule, input) 在不匹配时返回局部最远失败字节、实际输入字节或 EOF,以及有限期望集合。诊断使用独立预算,不改变原 MatchReport;BudgetExceeded 为未判定,而非 NoMatch。局部解释不代表唯一修复。

    moon run examples/diagnostics --target js moon run cmd/main --target js -- diagnose grammar.abnf rule input.bin

    示例覆盖 URI %GG、非法 Cookie 空格、截断与尾随字节。完整契约见 0.3.0 说明和公开接口。

    协议 JSON 适配器是 integration/protocol_consumer 下可复制的 JS 示例,不是根库导出;诊断扩展模板和运行方式见协议案例。

    #规范覆盖

    RFC 5234 / 7405支持
    规则定义 =、增量替代 =/,规则名大小写不敏感✅
    替代 /、串联、分组 ( )、可选 [ ]✅
    重复 n*m、*m、n*、*、n✅
    数值 %b %d %x:单值、点连接序列、区间✅
    字符串:默认大小写不敏感,%i / %s(RFC 7405)✅
    注释、CRLF 行结构✅
    附录 B 核心规则 ALPHA … WSP(16 条)✅ 内置,可被同名规则覆盖
    prose-val <...>原compile仍拒绝;compile_with_prose/compile_scalar接受显式词表、单符号范围及Concat/Alternative/Repetition组合;组合可表达无限语言,但执行仍有预算

    编译阶段检查未定义规则和重复定义;无有限终结推导的闭合规则视为空语言;左递归 / 可空前缀循环改用有预算不动点识别与输入树路径,不再直接拒绝。

    #设计取舍

    • 两棵树分开:文法 AST 描述文法本身,输入派生树描述某段输入怎样被匹配。
    • ABNF 不是 PEG:替代分支会保留全部可能的结束位置,不会因为先匹配到短的分支就错判。
    • 按入口选择算法:只有静态引用可达左递归环的入口使用不动点求解;不相关规则不改变普通入口的预算和局部诊断。已消耗前缀后的左递归仍正确支持,0 次重复不会引入调用。
    • 资源有预算:输入长度、工作量、状态数、深度、树节点都有上限。预算用完返回 BudgetExceeded(未判定),不会被当成“不匹配”。
    • 偏移单位按入口分开:Bytes树跨度是半开字节区间,ScalarInputTree跨度是scalar索引;文法位置始终是字节。extract使用对应输入类型,不做URI/UTF解码。
    • 不做:任意外部编码/无限整数、自然语言prose解释或通用回调、错误恢复、代码生成、流式输入、歧义全枚举,以及URI/Cookie语义层规则。

    详细契约见 边界说明 和 输入树上手,公开接口见 pkg.generated.mbti。

    #测试

    测试结果须绑定实际检出与补丁。历史 c89f804 的 90 / 93 组测试不是当前版本的验收结论;当前数量和退出码以本次运行输出为准。技术证据索引 给出完整验收、独立源码消费者及证据读取方法。

    • 单元与场景测试:解析器、编译器、识别器、输入树、边界、鲁棒性(1024 个变异文法、预算终止)。
    • 差分测试:随机生成 80 个文法、5440 次比对,结果与独立的正则 oracle 一致(scripts/differential.py)。
    • 真实协议语料:URI、Cookie、ETag、HTTP 请求行。
    • CI:ci.yml 在官方 SDK 上跑完整验收(JS / Wasm / native 的 check、build、test,Wasm GC 的 test,以及示例、CLI、差分、下游包消费);fresh-clone.yml 模拟新用户 git clone 后直接 moon check、moon test 和本页的快速开始。

    设置上述 MOONABNF_SDK 后运行完整验收,输出路径必须尚不存在(包括空目录):

    python3 scripts/acceptance.py --output /tmp/moonabnf-acceptance-new-run python3 scripts/protocol_record_test.py --diagnostics --output /tmp/moonabnf-protocol-new-run

    第一项保存逐阶段原始 stdout / stderr、退出码、summary.json 与 SHA256.json;超时、监控、Git 元数据或源码读取失败,以及运行前后源码变动,均不能标为通过;须在无并发写入的隔离副本执行。第二项测试实际打包后的源码绑定消费者,不是 registry 安装。每次使用新的路径,保留失败回执后再修复重验;不要覆盖历史证据。

    #性能

    以下是旧发布版历史性能数据,不是当前源码的重新测量。moon bench --release 实测,每项 5 组取平均;GitHub Actions ubuntu-24.04(AMD EPYC 9V45 96-Core Processor,4 个逻辑 CPU),moonc v0.10.14。

    场景输入JSnative
    编译 URI 文法—122 µs74.5 µs
    识别 URI(IPv6 主机)40 B56.1 µs40.6 µs
    构建 URI 派生树40 B152 µs119 µs
    编译 Cookie 文法—21.1 µs15.7 µs
    识别 Cookie 头33 B13.0 µs10.8 µs
    构建 Cookie 派生树33 B38.0 µs32.4 µs
    识别定长二进制字段4096 B255 µs192 µs
    构建定长二进制派生树4096 B2.21 ms1.59 ms
    32 个 Cookie 对的子树查询32 对89.4 µs51.8 µs

    数字只说明数量级,换机器会变。复现:moon bench benchmarks --target native --release;CI 见 bench.yml,本表来自这次运行。

    #目录

    路径内容
    parser.mbt types.mbtABNF 文本 → 文法 AST
    compiler.mbt analysis.mbt core.mbt规则合并、核心规则、引用 / 生产性 / 左递归检查
    recognizer.mbt有预算的整段识别
    input_tree.mbt input_query.mbt输入派生树、子树查询、字段提取
    cmd/mainJSON 命令行
    examples/可运行示例与黄金输出
    benchmarks/moon bench 基准
    fixtures/测试文法与 RFC 文法语料(来源见 fixtures/protocols/SOURCES.md)

    #许可与来源

    代码 MIT(LICENSE)。从 RFC 摘录的 URI / Cookie / HTTP 文法保留原有归属,不改称 MIT 原创,出处与哈希见 SOURCES.md。 开发中 AI 的参与方式见 AI_USAGE.md,设计说明见 docs/UNDERSTANDING.md。

    重复次数的十进制语法计数目前限制为 1,000,000;超出会被实现拒绝。这是资源实现边界,不是 RFC 5234 对合法 ABNF 的限制。

    #CLI I/O error records

    For valid check, match, diagnose, and batch invocations, a failed grammar/input regular-file read emits a JSON object with status: "io-error", stage: "grammar" or "input", and file. The detailed operating-system cause remains on stderr. Batch input read failures keep their original position and do not suppress later results. Exit 2 signals usage/grammar/I/O errors; an encountered indeterminate budget result keeps exit 3 even when another input fails to read. A missing/unrecognized command prints human-readable usage rather than a JSON result; consumers must check exit status and stderr. Build output goes to stderr. This does not change FIFO/nonregular-file rejection or the bounded reader.

    Read completion includes closing the opened descriptor: a close failure produces the same structured I/O refusal, never a successful match. If both reading and closing fail, both causes remain on stderr and later batch inputs still run. This is a CLI filesystem-adapter guarantee, not a change to grammar recognition.

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    message : String
    offset : Int
    line : Int
    column : Int
    } derive(Eq,
    Debug
    )

    Source positions are zero-based byte offsets; line/column are one-based.

    Diagnostic::equal

    fn Diagnostic::equal(Diagnostic, Diagnostic) -> Bool

    Diagnostic::not_equal

    fn Diagnostic::not_equal(x : Diagnostic, y : Diagnostic) -> Bool

    ExpectedInput

    pub(all) struct ExpectedInput {
    expectation : InputExpectation
    grammar_offset : Int?
    } derive(Eq,
    Debug
    )

    grammar_offset is the terminal expression's original grammar byte offset. None denotes implicit core terminals or an end-of-input requirement.

    ExpectedInput::equal

    ExpectedInput::not_equal

    fn ExpectedInput::not_equal(x : ExpectedInput, y : ExpectedInput) -> Bool

    Expr

    pub(all) enum Expr {
    Ref(String, Int)
    Literal(Bytes, Bool, Int)
    Range(Int, Int, Int)
    Sequence(Array[Expr], Int)
    Choice(Array[Expr], Int)
    Repeat(Int, Int?, Expr, Int)
    Prose(String, Int)
    } derive(Eq,
    Debug
    )

    Public AST. Repeat maximum None means unbounded; positions refer to source.

    Expr::equal

    fn Expr::equal(Expr, Expr) -> Bool

    Expr::not_equal

    fn Expr::not_equal(x : Expr, y : Expr) -> Bool

    Expr::to_repr

    Grammar

    pub(all) struct Grammar {
    rules : Array[Rule]
    } derive(Eq,
    Debug
    )

    Grammar::equal

    fn Grammar::equal(Grammar, Grammar) -> Bool

    Grammar::not_equal

    fn Grammar::not_equal(x : Grammar, y : Grammar) -> Bool

    Grammar::to_repr

    InputDiagnosticLimits

    pub(all) struct InputDiagnosticLimits {
    max_work : Int
    max_expectations : Int
    } derive(Eq,
    Debug
    )

    InputDiagnosticLimits::equal

    InputDiagnosticLimits::not_equal

    InputDiagnosticReport

    pub(all) struct InputDiagnosticReport {
    report : MatchReport
    failure : InputFailure?
    explanation_status : String
    explanation_work : Int
    } derive(Eq,
    Debug
    )

    match is the unchanged recognition result. explanation_status describes only the separately budgeted explanatory replay (not another acceptance verdict).

    InputDiagnosticReport::equal

    InputDiagnosticReport::not_equal

    InputDiagnosticReport::to_json

    Optional adapter representation, separate from the existing match JSON schema.

    InputExpectation

    pub(all) enum InputExpectation {
    ByteRange(Int, Int, Bool)
    EndOfInput
    } derive(Eq,
    Debug
    )

    ByteRange's third value denotes ASCII case-insensitive matching, not Unicode.

    InputExpectation::equal

    InputExpectation::not_equal

    fn InputExpectation::not_equal(x : InputExpectation, y : InputExpectation) -> Bool

    InputFailure

    pub(all) struct InputFailure {
    offset : Int
    actual : Int?
    expected : Array[ExpectedInput]
    truncated : Bool
    } derive(Eq,
    Debug
    )

    InputFailure::equal

    InputFailure::not_equal

    fn InputFailure::not_equal(x : InputFailure, y : InputFailure) -> Bool

    InputNode

    pub(all) struct InputNode {
    kind : InputNodeKind
    start : Int
    end : Int
    source_offset : Int
    children : Array[Int]
    } derive(Eq,
    Debug
    )

    Half-open input byte span. source_offset refers to the grammar expression; implicit core expressions have synthetic offset 0. Children are node IDs.

    InputNode::equal

    fn InputNode::equal(InputNode, InputNode) -> Bool

    InputNode::not_equal

    fn InputNode::not_equal(x : InputNode, y : InputNode) -> Bool

    InputNodeKind

    pub(all) enum InputNodeKind {
    RuleNode(String)
    LiteralNode
    RangeNode
    SequenceNode
    ChoiceNode(Int)
    RepeatNode(Int)
    } derive(Eq,
    Debug
    )

    A witness of an input derivation, distinct from the grammar Expr AST.

    InputNodeKind::equal

    InputNodeKind::not_equal

    fn InputNodeKind::not_equal(x : InputNodeKind, y : InputNodeKind) -> Bool

    InputTree

    pub(all) struct InputTree {
    nodes : Array[InputNode]
    root : Int
    } derive(Eq,
    Debug
    )

    Flat, reachable-only tree; root is always 0. No input bytes are copied.

    InputTree::equal

    fn InputTree::equal(InputTree, InputTree) -> Bool

    InputTree::extract

    fn InputTree::extract(self : InputTree, input : Bytes, node : Int) -> Bytes?

    Copies raw bytes from the caller's original input; does not decode UTF-8, URI escapes or quoted strings. Input identity remains caller responsibility. Invalid IDs/spans or an input shorter than the span return None. A different input of sufficient length is accepted, even if its bytes or total length differ. Use the exact original Bytes; this is not validation.

    InputTree::not_equal

    fn InputTree::not_equal(x : InputTree, y : InputTree) -> Bool

    InputTree::rule_nodes

    fn InputTree::rule_nodes(self : InputTree, name : String) -> Array[Int]

    Matching rule nodes in the tree's stable storage order (breadth-first for parse_input results). Rule names are ASCII case-insensitive as in ABNF. Does not traverse caller-mutable children, so malformed cycles cannot loop.

    InputTree::rule_nodes_in

    fn InputTree::rule_nodes_in(self : InputTree, name : String, root : Int) -> Array[Int]?

    Named nodes within a subtree, including root, in depth-first pre-order. Unlike rule_nodes' storage order, this follows the derivation's child order: outer captures precede inner captures, and siblings follow input order. Returns None for an invalid ID, span, arity, cycle or shared descendant in the selected subtree. Unreachable nodes outside it are not inspected. Iterative; storage is proportional to visited descendants, not the entire tree. Never schedules a child twice. This checks structure, not grammar membership or original-input identity.

    test {
    let p = @moonabnf.compile(b"s = item item\r\nitem = DIGIT\r\n").unwrap()
    let tree = p.parse_input("s", b"42").tree.unwrap()
    let ids = tree.rule_nodes_in("item", tree.root).unwrap()
    assert_eq(ids.map(id => tree.extract(b"42", id)), [Some(b"4"), Some(b"2")])
    }

    InputTree::span

    fn InputTree::span(self : InputTree, node : Int) -> (Int, Int)?

    Half-open byte span, or None for an invalid node ID or malformed span. Zero-width spans are valid. This local check does not validate topology, the root, grammar membership or the span against an input length.

    InputTreeLimits

    pub(all) struct InputTreeLimits {
    matching : MatchLimits
    max_nodes : Int
    max_edges : Int
    } derive(Eq,
    Debug
    )

    Allocation budgets count all explored witnesses, paths and final compaction, not only the returned tree. Match limits retain their existing meaning.

    InputTreeLimits::equal

    InputTreeLimits::not_equal

    fn InputTreeLimits::not_equal(x : InputTreeLimits, y : InputTreeLimits) -> Bool

    InputTreeReport

    pub(all) struct InputTreeReport {
    status : MatchStatus
    tree : InputTree?
    offset : Int
    work : Int
    states : Int
    nodes : Int
    edges : Int
    } derive(Eq,
    Debug
    )

    tree is Some exactly when status is Matched. Budget failure discards trees.

    InputTreeReport::equal

    InputTreeReport::not_equal

    fn InputTreeReport::not_equal(x : InputTreeReport, y : InputTreeReport) -> Bool

    MatchLimits

    pub(all) struct MatchLimits {
    max_input_bytes : Int
    max_work : Int
    max_states : Int
    max_depth : Int
    } derive(Eq,
    Debug
    )

    Work counts expression dispatches, byte comparisons and endpoint operations.

    MatchLimits::equal

    fn MatchLimits::equal(MatchLimits, MatchLimits) -> Bool

    MatchLimits::not_equal

    fn MatchLimits::not_equal(x : MatchLimits, y : MatchLimits) -> Bool

    MatchReport

    pub(all) struct MatchReport {
    status : MatchStatus
    offset : Int
    work : Int
    states : Int
    } derive(Eq,
    Debug
    )

    offset is a zero-based furthest attempted byte; full match offset is length. Budget exhaustion is indeterminate, even if an explored branch matched.

    MatchReport::equal

    fn MatchReport::equal(MatchReport, MatchReport) -> Bool

    MatchReport::not_equal

    fn MatchReport::not_equal(x : MatchReport, y : MatchReport) -> Bool

    MatchStatus

    pub(all) enum MatchStatus {
    Matched
    NoMatch
    BudgetExceeded(String)
    UnknownRule
    InvalidLimits
    } derive(Eq,
    Debug
    )

    MatchStatus::equal

    fn MatchStatus::equal(MatchStatus, MatchStatus) -> Bool

    MatchStatus::not_equal

    fn MatchStatus::not_equal(x : MatchStatus, y : MatchStatus) -> Bool

    ParseLimits

    pub(all) struct ParseLimits {
    max_bytes : Int
    max_rules : Int
    max_nodes : Int
    max_depth : Int
    } derive(Eq,
    Debug
    )

    Hard implementation ceilings prevent caller budgets from risking the stack.

    ParseLimits::equal

    fn ParseLimits::equal(ParseLimits, ParseLimits) -> Bool

    ParseLimits::not_equal

    fn ParseLimits::not_equal(x : ParseLimits, y : ParseLimits) -> Bool

    Program

    pub struct Program {
    // private fields
    }

    Compiled rules are private. Each compile parses its own AST, so caller-owned mutable AST arrays cannot invalidate the compiled program.

    Program::diagnose_input

    fn Program::diagnose_input(self : Program, start : String, input : Bytes, limits? : MatchLimits, diagnostic_limits? : InputDiagnosticLimits) -> InputDiagnosticReport

    Explain a NoMatch using bounded local expectations at the farthest observed failed terminal/complete-prefix endpoint. Not a unique repair or full trace. Successful/unknown recognition is never overwritten by exploratory failure.

    Program::parse_input

    fn Program::parse_input(self : Program, start : String, input : Bytes, limits? : InputTreeLimits) -> InputTreeReport

    Program::recognize

    fn Program::recognize(self : Program, start : String, input : Bytes, limits? : MatchLimits) -> MatchReport

    Program::rule_names

    fn Program::rule_names(self : Program) -> Array[String]

    Deterministic inventory useful to grammar-checking tools.

    ProseBinding

    pub(all) struct ProseBinding {
    name : String
    predicate : ProsePredicate
    } derive(Eq,
    Debug
    )

    Names equal the exact ASCII text inside <...>, case and spaces preserved. Empty names are allowed: <> is a legal prose token. Duplicate and invalid bindings are errors even if unused. Compiled programs own a lowered copy.

    ProseBinding::equal

    ProseBinding::not_equal

    fn ProseBinding::not_equal(x : ProseBinding, y : ProseBinding) -> Bool

    ProsePredicate

    pub(all) enum ProsePredicate {
    Words(Array[Array[Int]])
    SymbolRange(Int, Int)
    Concat(Array[ProsePredicate])
    Alternative(Array[ProsePredicate])
    Repetition(Int, Int?, ProsePredicate)
    } derive(Eq,
    Debug
    )

    Declarative predicates: words/ranges and bounded-depth composition. Words([]) is the empty language; Words([[]]) matches epsilon. Range matches exactly one symbol, never epsilon. Concat([]) is epsilon; Alternative([]) is empty. Repetition permits unbounded languages, not executable callbacks.

    ProsePredicate::equal

    ProsePredicate::not_equal

    fn ProsePredicate::not_equal(x : ProsePredicate, y : ProsePredicate) -> Bool

    Rule

    pub(all) struct Rule {
    name : String
    expression : Expr
    incremental : Bool
    offset : Int
    } derive(Eq,
    Debug
    )

    Rule::equal

    fn Rule::equal(Rule, Rule) -> Bool

    Rule::not_equal

    fn Rule::not_equal(x : Rule, y : Rule) -> Bool

    Rule::to_repr

    ScalarInputError

    pub(all) enum ScalarInputError {
    InvalidScalar(Int, Int)
    } derive(Eq,
    Debug
    )

    No UTF-8/UTF-16 decoding is performed: one Int is one scalar value.

    ScalarInputError::equal

    ScalarInputError::not_equal

    fn ScalarInputError::not_equal(x : ScalarInputError, y : ScalarInputError) -> Bool

    ScalarInputTree

    pub(all) struct ScalarInputTree {
    nodes : Array[InputNode]
    root : Int
    } derive(Eq,
    Debug
    )

    A distinct tree type prevents accidental use of byte extract() with scalar indices. start/end are scalar positions; source_offset stays a grammar byte position. Prose bindings are represented by lowered Choice/Sequence/Range.

    ScalarInputTree::equal

    ScalarInputTree::extract

    fn ScalarInputTree::extract(self : ScalarInputTree, input : Array[Int], node : Int) -> Array[Int]?

    Copies the selected scalar span, returning None for invalid spans.

    ScalarInputTree::not_equal

    fn ScalarInputTree::not_equal(x : ScalarInputTree, y : ScalarInputTree) -> Bool

    ScalarInputTree::rule_nodes

    fn ScalarInputTree::rule_nodes(self : ScalarInputTree, name : String) -> Array[Int]

    ScalarInputTree::rule_nodes_in

    fn ScalarInputTree::rule_nodes_in(self : ScalarInputTree, name : String, root : Int) -> Array[Int]?

    ScalarInputTree::span

    fn ScalarInputTree::span(self : ScalarInputTree, node : Int) -> (Int, Int)?

    ScalarInputTreeLimits

    pub(all) struct ScalarInputTreeLimits {
    matching : ScalarMatchLimits
    max_nodes : Int
    max_edges : Int
    } derive(Eq,
    Debug
    )

    ScalarInputTreeLimits::equal

    ScalarInputTreeLimits::not_equal

    ScalarInputTreeReport

    pub(all) struct ScalarInputTreeReport {
    status : MatchStatus
    tree : ScalarInputTree?
    offset : Int
    work : Int
    states : Int
    nodes : Int
    edges : Int
    } derive(Eq,
    Debug
    )

    ScalarInputTreeReport::equal

    ScalarInputTreeReport::not_equal

    ScalarMatchLimits

    pub(all) struct ScalarMatchLimits {
    max_input_scalars : Int
    max_work : Int
    max_states : Int
    max_depth : Int
    } derive(Eq,
    Debug
    )

    ScalarMatchLimits::equal

    ScalarMatchLimits::not_equal

    fn ScalarMatchLimits::not_equal(x : ScalarMatchLimits, y : ScalarMatchLimits) -> Bool

    ScalarProgram

    pub struct ScalarProgram {
    // private fields
    }

    An explicitly selected Unicode-scalar application domain, not an ABNF mandated external encoding. Byte Program and ScalarProgram cannot be mixed.

    ScalarProgram::parse_input

    fn ScalarProgram::parse_input(self : ScalarProgram, start : String, input : Array[Int], limits? : ScalarInputTreeLimits) -> Result[ScalarInputTreeReport, ScalarInputError]

    ScalarProgram::recognize

    fn ScalarProgram::recognize(self : ScalarProgram, start : String, input : Array[Int], limits? : ScalarMatchLimits) -> Result[MatchReport, ScalarInputError]

    All report offsets are scalar indices. Validation costs one work unit per scalar, before matching. This synchronous method neither copies nor retains the input array and never invokes user callbacks. Exhausted validation is indeterminate, not a claim that the unchecked suffix is valid.

    ScalarProgram::rule_names

    fn ScalarProgram::rule_names(self : ScalarProgram) -> Array[String]

    compile

    fn compile(source : Bytes, limits? : ParseLimits, max_work? : Int) -> Result[Program, Diagnostic]

    Legacy byte compiler. Prose remains rejected unless explicitly bound using compile_with_prose. Byte behavior and the numeric ceiling are unchanged.

    compile_scalar

    fn compile_scalar(source : Bytes, bindings? : Array[ProseBinding], limits? : ParseLimits, max_work? : Int) -> Result[ScalarProgram, Diagnostic]

    compile_with_prose

    fn compile_with_prose(source : Bytes, bindings : Array[ProseBinding], limits? : ParseLimits, max_work? : Int) -> Result[Program, Diagnostic]

    Bind exact prose names to finite languages or single-symbol ranges. No natural-language interpretation or user callback is executed.

    default_input_diagnostic_limits

    fn default_input_diagnostic_limits() -> InputDiagnosticLimits

    default_input_tree_limits

    fn default_input_tree_limits() -> InputTreeLimits

    default_match_limits

    fn default_match_limits() -> MatchLimits

    default_parse_limits

    fn default_parse_limits() -> ParseLimits

    default_scalar_input_tree_limits

    fn default_scalar_input_tree_limits() -> ScalarInputTreeLimits

    default_scalar_match_limits

    fn default_scalar_match_limits() -> ScalarMatchLimits

    parse

    fn parse(source : Bytes, limits? : ParseLimits) -> Result[Grammar, Diagnostic]

    Legacy byte-domain grammar parser, with numeric values limited to 255.

    parse_numeric

    fn parse_numeric(source : Bytes, limits? : ParseLimits) -> Result[Grammar, Diagnostic]

    Encoding-independent numeric AST, restricted to nonnegative Int32 values. RFC 5234 does not prescribe an external character encoding. This parser deliberately does not imply Unicode or byte recognition. Source offsets remain byte offsets into the ASCII grammar source.