fix: align element content models with W3C schema - #33
Merged
Conversation
…d_content) 31 element-only base files were declared as mixed_content even though the MathML 3 XSD and MathML 4 RelaxNG define them as element-only content models (ImpliedMrow, MathExpression sequences, MstackExpression*, TableRowExpression*, etc.). Under mixed_content, lutaml-model's each_mixed_content iteration yields inter-element whitespace as String children, which shifts positional indices in consumers like Plurimath (msub_to_base reads children[0] as base, children[1] as subscript). Switching to ordered preserves iteration order but skips whitespace-only text nodes. For mrow, munder, mfenced, msgroup the stray content (or msgroup_text) attribute and map_content declaration are also removed: the schema does not permit text content in these elements. Schema verification: schemas/mathml3/mathml3-presentation.xsd, schemas/mathml4/mathml4-core.rnc, schemas/mathml4/mathml4-presentation.rnc.
…chema Token elements (mi, mn, mo, ms, mtext) accept (#PCDATA | mglyph | malignmark)* per the MathML schema. The gem's mi and mn already mapped mglyph; malignmark was missing everywhere, and mo/mtext/ms were missing one or both inline element mappings. All five token elements now consistently declare mglyph_value and malignmark_value collections and map them via map_element. mo and mtext also gain mixed_content (required for the inline-element interleaving use case). Because mixed_content requires the content attribute to be a collection, Mo#value and Mtext#value are now String collections (matching Mi, Mn, Ms). Text split by an inline element is captured as multiple entries, e.g. <mi>x<malignmark/>y</mi> yields value == ['x', 'y']. Plurimath's text_value helper handles both String and Array inputs.
Follows the token-element schema fix: Mo#value and Mtext#value are now String collections (matching Mi, Mn, Ms). Spec assertions across v2/v3/v4 element specs, adapter_configuration_spec, and context_support_spec are updated to expect Array values where they previously expected String.
Locks in the mixed_content vs ordered distinction so future drift is caught immediately. The existing suite uses canon's spec_friendly profile which is whitespace-tolerant, so positional-index bugs are invisible without explicit assertions. Covers three scenarios across Mml::V3 and Mml::V4: 1. Positional elements (msub, msup, msubsup, mfrac, mroot, mover, munder, munderover) with whitespace between children: each_mixed_content yields no whitespace-only Strings, round-trip preserves children. 2. Container elements (mrow, mfenced, msgroup) with whitespace between children: same invariants. 3. Token elements (mi, mn, mo, ms, mtext) with inline mglyph or malignmark: the inline element is captured alongside text, value is split into a collection when interrupted.
Document the mixed_content vs ordered vs map_content decision based on W3C schema authority, with verification workflow pointing at schemas/mathml3 and schemas/mathml4. Document token element pattern (mglyph + malignmark inline children, value as collection). Reference spec/mml/ordered_content_spec.rb as the regression test. List reference-docs and schemas as authoritative sources.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Aligns the gem's element content models with the W3C MathML 3 (XSD) and MathML 4 (RelaxNG) schemas. Three independent correctness fixes, plus regression specs and CLAUDE.md documentation.
1. Element-only containers migrated from
mixed_contenttoordered31 base files were declared
mixed_contenteven though their schema content models are element-only (ImpliedMrow,MathExpression, MathExpression,MstackExpression*,TableRowExpression*, etc.). Undermixed_content, lutaml-model'seach_mixed_contentiteration yields inter-element whitespace asStringchildren, which shifts positional indices in consumers like Plurimath (msub_to_basereadschildren[0]as base,children[1]as subscript — a phantom whitespace String at position 0 silently corrupts the tree).Under
ordered, iteration order is preserved but whitespace-only text nodes are skipped.Files: 27 originally flagged by the audit + 4 additional (
munder,mrow,mfenced,msgroup). The four extras also drop the straycontent(ormsgroup_text) attribute andmap_contentdeclaration — the schema forbids text content there.2. Token elements capture inline
mglyphandmalignmarkPer MathML schema, token elements (
mi,mn,mo,ms,mtext) accept(#PCDATA | mglyph | malignmark)*. The gem'smi/mnalready mappedmglyph;malignmarkwas missing everywhere, andmo/mtext/mswere missing one or both inline element mappings.All five token elements now consistently declare
mglyph_valueandmalignmark_valuecollections and map them viamap_element.moandmtextalso gainmixed_content(required for inline-element interleaving).3.
Mo#valueandMtext#valueare now String collectionsRequired by
mixed_content(lutaml-model enforces this). MatchesMi,Mn,Ms. Text split by an inline element is captured as multiple entries:<mi>x<malignmark/>y</mi>yieldsvalue == ["x", "y"]. Plurimath'stext_valuehelper already handles both String and Array inputs.Schema verification
Each modified element was checked against the authoritative schemas in this repo:
Test plan
bundle exec rspec— 2926 examples, 0 failures, 36 pre-existing pendingbundle exec rubocop— 493 files, no offensesspec/mml/ordered_content_spec.rb(54 specs across V3/V4) — covers positional-index whitespace handling for 11 element types + inlinemglyph/malignmarkround-trip for all 5 token elementsMo#valueandMtext#valueOut of scope
ensure_registered!) is in a separate open PR (feat: Opal boot file + lazy registration for v2/v3/v4 #31). This PR is based onmainand does not touch that infrastructure.register_models!block (MECE consistency with V2/V4) — same reason; belongs with PR feat: Opal boot file + lazy registration for v2/v3/v4 #31.ensure_registered!already exploitsType.const_get's segment-by-segment resolution to register siblings.Breaking changes
Mrow#content,Munder#content,Mfenced#content,Msgroup#msgroup_text— removed (element-only schemas; no consumer in Plurimath uses them).Mo#value,Mtext#value— returnArray<String>instead ofString. Consumers that treat these as String will need to join (Plurimath already does viatext_value).