Skip to content

Preserve protocol blocks and quoted whitespace during BNGL round-trip#88

Open
wshlavacek wants to merge 1 commit intoRuleWorld:mainfrom
wshlavacek:modelapi-protocol-block-and-whitespace-parser
Open

Preserve protocol blocks and quoted whitespace during BNGL round-trip#88
wshlavacek wants to merge 1 commit intoRuleWorld:mainfrom
wshlavacek:modelapi-protocol-block-and-whitespace-parser

Conversation

@wshlavacek
Copy link
Copy Markdown

Summary

Two coupled fidelity fixes in the BNGL → BNGL round-trip path. The two share the same `parse_actions` refactor, so they're bundled.

1. `begin protocol` / `end protocol` blocks are now preserved

Protocol blocks live INSIDE `begin model` / `end model` and hold a sequence of state-mutating action lines (`setParameter`, `setConcentration`, `simulate`, etc.) that BNG2.pl executes when `parameter_scan({method=>"protocol"})` is invoked.

Previously, `BNGFile.strip_actions` extracted every action-shaped line from the file — including those inside a protocol block — and `BNGParser` routed them into the top-level `ActionBlock`. The regenerated BNGL had no protocol block at all, and BNG2.pl ran the actions at the wrong level.

Fix:

  • New `ProtocolBlock(ActionBlock)` in `blocks.py`: same item-list shape as `ActionBlock` but emits its content wrapped in `begin protocol` / `end protocol`.
  • `bngmodel` registers `"protocol"` in `_block_order` between `rules` and `actions` so it lands inside the model body, and exposes `add_protocol_block` for the parser's dispatch by name.
  • `BNGFile.strip_actions` walks lines tracking an `in_protocol` flag and routes action-shaped lines into `parsed_protocol_actions` instead of `parsed_actions` while inside a protocol block. The `begin protocol` / `end protocol` delimiter lines themselves stay in the stripped output so BNG2.pl re-emits the wrapper.
  • `BNGParser.parse_actions` invokes a shared `_parse_action_block` helper twice — once with `ActionBlock` for the top-level actions and once with `ProtocolBlock` for the protocol-block actions. The per-action dispatch logic is factored out into `_parse_action_line`.

2. Quoted whitespace inside action arguments is preserved

Previously `strip_actions` ran `line.replace(" ", "")` on each action line, and `BNGParser.parse_actions` re-ran `re.sub(r"\s", "", action)`. Both were quote-naive, so an action like

```
simulate({method=>"nf",param=>"-v -gml 1000000"})
```

came out as

```
simulate({method=>"nf",param=>"-v-gml1000000"})
```

and NFsim choked on the broken argument string.

The space-collapse is now a quote-aware pass (`_normalize_action_text` + `_strip_comment_outside_quotes` + `_collapse_unquoted_whitespace`) that lives module-level in `bngparser.py`. Tokens inside `"..."` / `'...'` survive intact; unquoted whitespace and trailing `# comments` are still stripped as before.

Test plan

  • Existing CI passes
  • A model with a `begin protocol`...`end protocol` block round-trips through write_model and re-parses correctly
  • `simulate({method=>"nf",param=>"-v -gml 1000000"})` survives round-trip with the quoted spaces intact

Two coupled fidelity fixes in the BNGL → BNGL round-trip path:

1. ``begin protocol`` / ``end protocol`` blocks are now preserved.

Protocol blocks live INSIDE ``begin model`` / ``end model`` and hold a
sequence of state-mutating action lines (``setParameter``,
``setConcentration``, ``simulate``, etc.) that BNG2.pl executes when
``parameter_scan({method=>"protocol"})`` is invoked. Previously,
``BNGFile.strip_actions`` would extract every action-shaped line from
the file — including those inside a protocol block — and BNGParser
would route them into the top-level ``ActionBlock``, so the
regenerated BNGL had no protocol block at all and BNG2.pl ran the
actions at the wrong level.

Fix:
- New ``ProtocolBlock(ActionBlock)`` in ``blocks.py``: same item-list
  shape as ``ActionBlock`` but emits its content wrapped in
  ``begin protocol`` / ``end protocol``.
- ``bngmodel`` registers ``"protocol"`` in ``_block_order`` between
  ``rules`` and ``actions`` so it lands inside the model body, and
  exposes ``add_protocol_block`` for the parser's dispatch by name.
- ``BNGFile.strip_actions`` walks lines tracking an ``in_protocol``
  flag and routes action-shaped lines into ``parsed_protocol_actions``
  instead of ``parsed_actions`` while inside a protocol block. The
  ``begin protocol`` / ``end protocol`` delimiter lines themselves
  stay in the stripped output so BNG2.pl re-emits the wrapper.
- ``BNGParser.parse_actions`` now invokes a shared
  ``_parse_action_block`` helper twice — once with ``ActionBlock`` for
  the top-level actions and once with ``ProtocolBlock`` for the
  protocol-block actions.

2. Quoted whitespace inside action arguments is preserved.

Previously ``strip_actions`` ran ``line.replace(" ", "")`` on each
action line, and ``BNGParser.parse_actions`` re-ran ``re.sub(r"\s", "",
action)``. Both were quote-naive, so an action like

    simulate({method=>"nf",param=>"-v -gml 1000000"})

came out as

    simulate({method=>"nf",param=>"-v-gml1000000"})

and NFsim choked on the broken argument string.

The space-collapse is now a quote-aware pass
(``_normalize_action_text`` + ``_strip_comment_outside_quotes`` +
``_collapse_unquoted_whitespace``) that lives module-level in
``bngparser.py``. Tokens inside ``"..."`` / ``'...'`` survive intact;
unquoted whitespace and trailing ``# comments`` are still stripped as
before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant