Byte-oriented ABNF grammar AST, validation, budgeted recognition and input derivation trees
git clone https://github.com/sundaysebasidian-byte/MoonABNF.git
cd MoonABNF
moon test # 全部单元测试
moon run examples --target js # 协议 / 配置 / 二进制三个小场景
moon run examples/protocols/cmd --target js # URI 与 Cookie:输入树和字段提取moon add sundaysebasidian-byte/moonabnf@0.2.1// moon.pkg
import { "sundaysebasidian-byte/moonabnf" @abnf }0.2.0 已发布到 Mooncakes:包含 parse / compile / recognize 以及输入派生树与字段提取(parse_input、rule_nodes_in、extract)。全新 registry 消费者已验证 Wasm、Wasm GC、JS 和 native;JS JSON 协议适配为需复制的 集成示例,使用公开核心 API 和 examples/protocols 文法子包,并非根包 JSON 导出。
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.bin| RFC 5234 / 7405 | 支持 |
|---|---|
| 规则定义 =、增量替代 =/,规则名大小写不敏感 | ✅ |
| 替代 /、串联、分组 ( )、可选 [ ] | ✅ |
| 重复 n*m、*m、n*、*、n | ✅ |
| 数值 %b %d %x:单值、点连接序列、区间 | ✅ |
| 字符串:默认大小写不敏感,%i / %s(RFC 7405) | ✅ |
| 注释、CRLF 行结构 | ✅ |
| 附录 B 核心规则 ALPHA … WSP(16 条) | ✅ 内置,可被同名规则覆盖 |
| prose-val <...> | 解析进 AST;编译时报 prose-unsupported |
| 路径 | 内容 |
|---|---|
| parser.mbt types.mbt | ABNF 文本 → 文法 AST |
| compiler.mbt analysis.mbt core.mbt | 规则合并、核心规则、引用 / 生产性 / 左递归检查 |
| recognizer.mbt | 有预算的整段识别 |
| input_tree.mbt input_query.mbt | 输入派生树、子树查询、字段提取 |
| cmd/main | JSON 命令行 |
| examples/ | 可运行示例与黄金输出 |
| 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) -> InputTreeReporttest {
let p = @moonabnf.compile(b"s = 1*DIGIT\r\n").unwrap()
let report = p.parse_input("s", b"42")
assert_true(report.status is @moonabnf.Matched)
assert_eq(report.tree.unwrap().nodes[0].end, 2)
}fn Program::recognize(self : Program, start : String, input : Bytes, limits? : MatchLimits) -> MatchReportInstall
Download zipByte-oriented ABNF grammar AST, validation, budgeted recognition and input derivation trees