100% round-trip across four EDA formats on the maintainer's local production corpus: 7,217 / 7,217 files (GDSII / LEF / DEF / Tcl) parse → save → re-parse to a structurally equivalent AST.
Tcl reaches full round-trip — 3,273 / 3,273
The Tcl parser group landed in 0.1.5 as parse-only. This release wires the inverse:
- 10
unparseoverrides on every bound terminal (comment,brace_group,quoted_string,bracket_sub,bare_word,var_name,escape,literal_run,newline,expand_marker). - New
tcl.command_sepparser — accepts\nor;on parse, emits canonical\n. tcl.bare_wordnow handles\Xescapes (Dodekalogue rule 9):MACC_\{CONFIG\}parses as a single word.- Grammar restructures:
WORDSgains aspacepostfix,SCRIPTinlines oldCOMMANDSwith acommands[]=@list binding so separators stay structural,VAR_INDEXbecomes a non-container sequence so its:index=@flows up intoVAR_SEG, andVAR_SEGsplits into bare vs. braced forms keyed by aformdiscriminator — eliminating the${name}_suffixround-trip ambiguity.
LEF gains three real-world producer forms — 822 / 822
DATABASE MICRONSaccepts<NUMBER>(asap7 emits1000.0, not1000).- New
UNITS_COMPACT_CMDfor the one-lineUNITS DATABASE MICRONS N ;form OpenROAD's PCB writer emits. BUSBITCHARS/DIVIDERCHAR/NAMESCASESENSITIVE/NOWIREEXTENSIONATPINnow compose order-free inHDR_CMD.
Engine fixes (shared infrastructure)
- Loader drops the
type="var"back-compat that silently consumed legitimate user constants.parser:type="var":field=@now binds both fields cleanly. Rawsave-dispatch tolerates non-Stringvalues when#subparseis set — unblocks?<RULE>round-trip on any grammar using the*:field=@:#subparse="<RULE>"shape.
Scope parser group
New scope.{paren,bracket,brace,angle} parsers for depth-balanced bracket capture as a #subparse source. Single ScopeParser<Start, Stop> class template with four explicit instantiations; eight unit tests.
Branch reorganisation
SystemVerilog work (grammar + parsers + tests) moved off main to a dedicated feat/sv branch. SV is comprehensive parse-side but the round-trip story isn't yet wired; keeping it on main inflated the test matrix without contributing to the shippable round-trip surface. Five existing feat/sv-* branches preserved as historical snapshots.
main retains the generic preprocessor mechanism — grammar-agnostic, works against any grammar whose directive rules carry :#role=... annotations.
Pre-release corpus harness
New local_tests/ folder (developer-local, excluded via .git/info/exclude) holds the corpus path lists and round-trip harness used to verify each release. See local_tests/README.md.
Verification
- 250 C++ tests / 973 assertions — all green
- 49 Python tests — all green
- Production corpus: 7,217 / 7,217 (100%) across GDSII / LEF / DEF / Tcl