Use EBNF to support any file format including re-output or JSON output.
File format may be:
- Block DSL or
- Non-block DSL
Block assumes the following DSL:
block → statements → (assignment | block | list)
Non-block DSL may have:
- INI: infers DSL thru flat sections, key=value pair
- OpenSSL: infers DSL thru section, key=value pair
- YAML: infers DSL thru indentation-driven tree
This design will not support Python config due to arbitrary expressions such as a = function(b).
Working example:
ISC Bind9 (`/etc/bind/named.conf`)
nftables (`/etc/nft/nftables.nft`)
OpenSSL (`/etc/ssl/openssl.cnf`)
YAML
INI
EBNF (meta grammar, ebnf-flexible.dhparser)
│
▼
[Stage 1] Parse EBNF
│
▼
AST-EBNF (syntax-only IR)
│
▼
S-Expression (normalized grammar IR)
│
▼
[Stage 2] DSL Parse Engine (generated/interpreted)
│
▼
AST-DSL (domain-neutral structure + tokens + comments)
│
▼
DSL-SPEC (constraint-registry, nftablesglobal.py/DSL)
│
▼
Shared Structural Validation Layer
│
▼
Grammar Constraints Layer (context, multiplicity)
│
▼
Semantic Mapping Layer (typed model)
│
▼
Validation / Lint Layer
│
▼
Formatter / Emitter
│
▼
Output / Migration Engine
Focus is on ingesting the syntax of the target using EBNF notations detailing its target (ie., nftables).
EBNF of all forms of known EBNF variants, in dhparse format.
Parse the EBNF-flexible.dhparse (we could hardcode those but this is a demonstrator of EBNF flexibility).
AST describing grammar only (no semantics).
Purpose
- Normalize grammar into execution-ready structure
- Remove DHParser-specific structure
(def <rule-name>
(seq
(lit "zone")
(sym string)
(sym block)
)
)
S-Expression Schema
classRuleIR:
name: strexpr: tuple# ('seq', [...]), ('alt', [...]), ('lit', str), ('sym', name)AST-EBNF Schema
classEBNFNode:
type: str# definition | sequence | choice | literal | regex | symbolvalue: str|Nonechildren: listStage-2 AST must be: structure-first, syntax-agnostic, not block-centric.
Stage-2 AST cannot do uniform-model.
Contract
Input
Grammar IR (S-expr)
DSL text (named.conf / nftables / openssl.cnf)
Output
AST-DSL (syntax tree + tokens + comments)
Core Requirement
Must support:
- Bind9 (named.conf)
- nftables
- OpenSSL config
- YAML
- INI
Base Node would look like:
classASTNode:
type: strvalue: str|Nonechildren: list["ASTNode"]
# cross-cutting concernscomments: list["Comment"]
meta: dict# line, col, file# grammar-level annotationsrule: str|Nonemultiplicity: str|None# "1", "?", "*", "+"Specialized node Types would be:
- Block
type="block"value= {
"name": "options"|"zone"|"server"| ...
}- Assignment
type="assignment"value= {
"key": str,
"operator": "="|None
}
children= [value_node]- List
type="list"children= [ASTNode...]- Literal / Token
type="literal"|"identifier"|"number"|"string"value=str- Comment
classComment:
text: strposition: intinline: boolPurpose
- Grammar-independent checks
Examples
duplicate keys in same scope
empty blocks
invalid nesting depth
Interface
defvalidate_structure(ast: ASTNode) ->list[Issue]Derived from Grammar IR
Adds:
- allowed children per node
- multiplicity constraints
- context restrictions
Example Constraint Model
classConstraint:
parent: strchild: strmultiplicity: str# "1", "?", "*", "+"Example
zone:
file → 1
type → 1
allow-transfer → *