Skip to content

refactor(method): model SimaPro method parser state as one Stage ADT#96

Merged
ccomb merged 3 commits into
advanced_haskellfrom
refactor/simapro-method-parser
May 28, 2026
Merged

refactor(method): model SimaPro method parser state as one Stage ADT#96
ccomb merged 3 commits into
advanced_haskellfrom
refactor/simapro-method-parser

Conversation

@ccomb
Copy link
Copy Markdown
Owner

@ccomb ccomb commented May 28, 2026

Summary

Continues the PR #87 line of cleanup, this time on Method.ParserSimaPro — the
12-phase fold that parses SimaPro LCIA method CSV exports.

The phase lived in a Phase enum next to a flat 13-field ParseState whose
per-section accumulators were always present, so the type permitted a phase to
disagree with the data it held. This change folds phase + accumulator into a
single Stage sum type whose constructors carry exactly what each phase needs,
making those impossible states unrepresentable. It also factors out the
duplicated leaf logic and removes a partial function.

What changed

  • Stage ADT replaces Phase + the flat accumulator fields. CatAccum /
    DamageAccum / NWAccum hold the in-progress block; completed lists and the
    constant methodology name move to a slim ParseState (so step drops its
    threaded methodology argument).
  • Shared helpers pulled out of the step case: parseCFRow (the inlined
    ~35-line CF builder), one parseNameValue for the three identical
    name;value rows, parseNameUnit for both Name;Unit headers, buildMethod,
    and detectMarker/stageFor mirroring the sibling SimaPro.Parser.detectSection.
  • finalize collapsed from re-implemented builders to a 6-line read-out that
    reuses the same finishCat/finishDamage/finishNW finishers as step, via
    one finishCurrent dispatcher.
  • Partial function removed: parseNameUnit is total, deleting head' and its
    error call.

Behaviour

Output is unchanged on well-formed SimaPro files (verified by tracing the
fixture through every transition). Three harmonisations are unreachable on valid
exports: empty blocks are no longer emitted, and a truncated file lacking a
terminal End now keeps its trailing block and its methodology instead of
dropping them.

Net: 216 insertions, 233 deletions in one file.

Test plan

  • cabal build lib:volca — clean, no incomplete-pattern warnings on the new Stage/step
  • cabal run lca-tests -- --match "/Method" — 115 examples, 0 failures (14 SimaPro method cases + standalone NW parser among them)

ccomb added 3 commits May 28, 2026 15:48
The 12-phase fold-based parser kept its phase in a `Phase` enum alongside a
flat 13-field `ParseState` whose per-section accumulators (factors, damage
impacts, norm/weight maps) were always present, so a phase could disagree with
the data it carried. Fold both into a single `Stage` sum type whose
constructors hold exactly the accumulator each phase needs, making those
impossible combinations unrepresentable. Completed lists and the constant
methodology name move to a slim `ParseState`, dropping `step`'s threaded arg.

Extract the duplicated leaf logic into pure helpers: `parseCFRow` (the inlined
35-line CF builder), one `parseNameValue` for the three identical name;value
rows, `parseNameUnit` for both Name;Unit headers, `buildMethod`, and a
`detectMarker`/`stageFor` pair mirroring `SimaPro.Parser.detectSection`.
`finalize` now reuses the same `finishCat`/`finishDamage`/`finishNW` finishers
via one `finishCurrent` dispatcher instead of re-implementing the builders.

`parseNameUnit` is total, removing the partial `head'`/`error`. The finishers
guard on non-empty content and `finishCurrent` flushes any trailing block at
EOF, so empty blocks are never emitted and a truncated file no longer drops its
last block or its methodology — all unreachable on well-formed SimaPro exports,
where output is unchanged.
Replace the wildcard arm with explicit no-op cases for the eight stages
that carry no in-progress block. The wildcard suppressed the very
incomplete-pattern warning the Stage ADT exists to surface: a future
accumulator-carrying stage could silently skip its end-of-input flush.
Bind the MethodCF with a bang before wrapping it in Just, restoring the
per-row strictness the pre-refactor inline builder had. Without it the
spine-strict factor list still held each row as a thunk retaining slices
of the source line, accumulating over large method files.
@ccomb ccomb merged commit 829b74c into advanced_haskell May 28, 2026
@ccomb ccomb deleted the refactor/simapro-method-parser branch May 28, 2026 15:51
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