Repository navigation
v0.105.0
Changed
- Serializer pattern emission refactored to classification-based dispatch (REFACTOR-SERIALIZER-PATTERN-001):
- Replaced three interleaved methods (
_serialize_pattern,_serialize_text_element,_pattern_needs_separate_line) with a unified continuation line classification model - New
_LineKindenum (EMPTY,WHITESPACE_ONLY,SYNTAX_LEADING,NORMAL) classifies each continuation line exactly once; a singlematch/casedispatch point selects the correct wrapping strategy _classify_line()pure function and_escape_text()(brace-only escaping) extracted as module-level functions, enabling independent testing and reuse_emit_classified_line()static method serves as the single dispatch point for all continuation line ambiguity classes- All
match/casedispatches now useassert_never()exhaustiveness guards (_serialize_entry,_serialize_expression,_emit_classified_line) to catch missing cases at type-check time when new AST variants are added _serialize_expressionuses full structural destructuring in patterns (StringLiteral(value=value),TermReference(id=Identifier(name=name), ...)) instead of body-level attribute access- No public API changes;
serialize()andFluentSerializer.serialize()semantics preserved - Location:
syntax/serializer.py
- Replaced three interleaved methods (
Fixed
-
Serializer roundtrip idempotence failure on
SYNTAX_LEADINGcontinuation lines with content whitespace (BUG-SERIALIZER-WS-SYNTAX-002):- Same bug class as BUG-SERIALIZER-WS-SYNTAX-001 (fixed in 0.104.0): syntactically ambiguous whitespace in serializer output; residual case missed by the original fix and inherited by the 0.105.0 classification refactor
- When a continuation line had content spaces preceding a syntax character (
.,*,[), e.g.,"dS7aQ\n .h?Q", theSYNTAX_LEADINGemission branch wrapped the syntax character in aStringLiteralplaceable ({ "." }) but emitted the preceding content spaces as raw text - The FTL parser computes common indent as the minimum indent across all continuation lines; structural indent (4 spaces) plus raw content spaces (e.g., 6 spaces = 10 total) became the common indent, and all 10 spaces were stripped during common-indent removal, losing the 6 content spaces
- Re-serializing the parsed AST produced only the 4-space structural indent before the placeable, breaking idempotence:
S(AST) != S(P(S(AST))) - Fix:
_emit_classified_lineSYNTAX_LEADINGbranch now wraps leading content spaces in aStringLiteralplaceable ({ " " }{ "." }...), matching the treatment already used byWHITESPACE_ONLYand leading-whitespace-after-=; the parser sees the spaces as expression content (opaque to indent stripping), preserving them through roundtrip - Invariant enforced: all content whitespace preceding the first non-whitespace character on a continuation line must be placeable-wrapped; raw spaces are indistinguishable from structural indent
- Location:
syntax/serializer.py_emit_classified_line()
-
Diagnostic.__str__returns raw dataclass repr instead of human-readable message (BUG-DIAGNOSTIC-STR-001):Diagnostic(frozen dataclass) had no__str__method;str(diagnostic)returned the fullrepr()with all fields includingNonevalues, e.g.,Diagnostic(code=<DiagnosticCode.VARIABLE_NOT_PROVIDED: 1005>, message="...", span=None, ...)- All
FrozenFluentErrorconstruction sites usestr(diag)as themessageparameter, soerror.messageandstr(error)both returned the raw repr - Users calling
str(error)in logging, error display, or exception handlers saw opaque dataclass output instead of the human-readable message - Fix: Added
__str__toDiagnosticreturningself.message;repr()unchanged for debugging - Location:
diagnostics/codes.pyDiagnostic.__str__()