Byte-oriented ABNF grammar AST, validation, budgeted recognition and input derivation trees
| 交付物 | 能力与工具链 | 入口 |
|---|---|---|
| 当前源码 | Bytes API、预算内左递归识别 / 输入树、独立 Unicode scalar API、显式组合 prose;工具链锁定 moonc 0.10.18+6c83a7f62 | 技术证据与复现、scalar / prose、左递归边界 |
| Mooncakes 0.3.0 | 已发布的 Bytes API、输入派生树、字段提取与有界失败诊断;历史锁定工具链为 0.10.14+7d59c7ec9 | 0.3.0 版本说明 |
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 nativemoon 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 上验证。协议集成方式见下文。
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)
}
}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}")
_ => ()
}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=truescripts/cli.sh check fixtures/protocol.abnf
scripts/cli.sh match fixtures/protocol.abnf field fixtures/protocol-valid.binmoon run examples/diagnostics --target js
moon run cmd/main --target js -- diagnose grammar.abnf rule input.bin| 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组合;组合可表达无限语言,但执行仍有预算 |
python3 scripts/acceptance.py --output /tmp/moonabnf-acceptance-new-run
python3 scripts/protocol_record_test.py --diagnostics --output /tmp/moonabnf-protocol-new-run| 场景 | 输入 | JS | native |
|---|---|---|---|
| 编译 URI 文法 | — | 122 µs | 74.5 µs |
| 识别 URI(IPv6 主机) | 40 B | 56.1 µs | 40.6 µs |
| 构建 URI 派生树 | 40 B | 152 µs | 119 µs |
| 编译 Cookie 文法 | — | 21.1 µs | 15.7 µs |
| 识别 Cookie 头 | 33 B | 13.0 µs | 10.8 µs |
| 构建 Cookie 派生树 | 33 B | 38.0 µs | 32.4 µs |
| 识别定长二进制字段 | 4096 B | 255 µs | 192 µs |
| 构建定长二进制派生树 | 4096 B | 2.21 ms | 1.59 ms |
| 32 个 Cookie 对的子树查询 | 32 对 | 89.4 µs | 51.8 µs |
| 路径 | 内容 |
|---|---|
| parser.mbt types.mbt | ABNF 文本 → 文法 AST |
| compiler.mbt analysis.mbt core.mbt | 规则合并、核心规则、引用 / 生产性 / 左递归检查 |
| recognizer.mbt | 有预算的整段识别 |
| input_tree.mbt input_query.mbt | 输入派生树、子树查询、字段提取 |
| cmd/main | JSON 命令行 |
| examples/ | 可运行示例与黄金输出 |
| benchmarks/ | moon bench 基准 |
| fixtures/ | 测试文法与 RFC 文法语料(来源见 fixtures/protocols/SOURCES.md) |
pub(all) struct ExpectedInput {
expectation : InputExpectation
grammar_offset : Int?
} derive(Eq, Debug)pub(all) struct InputDiagnosticReport {
report : MatchReport
failure : InputFailure?
explanation_status : String
explanation_work : Int
} derive(Eq, Debug)pub(all) struct InputFailure {
offset : Int
actual : Int?
expected : Array[ExpectedInput]
truncated : Bool
} derive(Eq, Debug)pub(all) struct InputNode {
kind : InputNodeKind
start : Int
end : Int
source_offset : Int
children : Array[Int]
} derive(Eq, Debug)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")])
}pub(all) struct InputTreeLimits {
matching : MatchLimits
max_nodes : Int
max_edges : Int
} derive(Eq, Debug)pub(all) struct InputTreeReport {
status : MatchStatus
tree : InputTree?
offset : Int
work : Int
states : Int
nodes : Int
edges : Int
} derive(Eq, Debug)pub(all) struct MatchReport {
status : MatchStatus
offset : Int
work : Int
states : Int
} derive(Eq, Debug)pub struct Program {
// private fields
}fn Program::diagnose_input(self : Program, start : String, input : Bytes, limits? : MatchLimits, diagnostic_limits? : InputDiagnosticLimits) -> InputDiagnosticReportfn Program::parse_input(self : Program, start : String, input : Bytes, limits? : InputTreeLimits) -> InputTreeReportfn Program::recognize(self : Program, start : String, input : Bytes, limits? : MatchLimits) -> MatchReportpub(all) enum ProsePredicate {
Words(Array[Array[Int]])
SymbolRange(Int, Int)
Concat(Array[ProsePredicate])
Alternative(Array[ProsePredicate])
Repetition(Int, Int?, ProsePredicate)
} derive(Eq, Debug)pub(all) struct ScalarInputTreeLimits {
matching : ScalarMatchLimits
max_nodes : Int
max_edges : Int
} derive(Eq, Debug)pub(all) struct ScalarInputTreeReport {
status : MatchStatus
tree : ScalarInputTree?
offset : Int
work : Int
states : Int
nodes : Int
edges : Int
} derive(Eq, Debug)pub struct ScalarProgram {
// private fields
}fn ScalarProgram::parse_input(self : ScalarProgram, start : String, input : Array[Int], limits? : ScalarInputTreeLimits) -> Result[ScalarInputTreeReport, ScalarInputError]fn ScalarProgram::recognize(self : ScalarProgram, start : String, input : Array[Int], limits? : ScalarMatchLimits) -> Result[MatchReport, ScalarInputError]fn compile_scalar(source : Bytes, bindings? : Array[ProseBinding], limits? : ParseLimits, max_work? : Int) -> Result[ScalarProgram, Diagnostic]fn compile_with_prose(source : Bytes, bindings : Array[ProseBinding], limits? : ParseLimits, max_work? : Int) -> Result[Program, Diagnostic]Install
Download zipByte-oriented ABNF grammar AST, validation, budgeted recognition and input derivation trees