Part of the fragile-objects epic #5261. This ticket covers the two ? -headed body-line forms the epic introduces: plain vertical void attributes (any formation) and atom error-formas (/{…}, atoms only).
1. Vertical void attributes — ? > name
The epic makes the two snippets equivalent:
[name] > foo | [name bar test] > foo
? > bar |
? > test |
A ? > name body line declares a void attribute on the enclosing formation. The ? glyph is currently free — it was a NAME terminator only, and its legacy atom-return-type use was dropped from the grammar in #5078 — so this repurposes it.
XMIR contract
A vertical void emits exactly the element a bracket param does (§9.4):
It is therefore indistinguishable in XMIR from [… bar …]. Consequences:
move-voids-up.xsl already hoists every ∅ child to the front, so a body-declared void lands among the head voids with no XSL change. The equivalence [name] + ? > bar + ? > test ≡ [name bar test] falls out of that pass.
- No runtime or transpiler work —
∅ voids are handled end to end already.
- Reverse printing canonicalizes to the bracket form; the printer cannot recover which form was written, and the two are equivalent by definition. Round-trip tests expect the bracket form.
Surface syntax
- A
?-headed body line with a mandatory > name suffix: ? > bar.
- Legal only as a direct child of a formation. A
? line elsewhere, or a ? without a > name, is a parse error.
? > @ is allowed (a void named φ), mirroring R-3.4.2 which admits @ as a bracket void param.
2. Atom error-formas — ? > name /{forma …}
In an atom, a vertical void may carry a brace-delimited forma-list:
[name] > fopen /file
? > not-found /{string io.file-error}
? > cant-open /{io.foo.bar.super-error}
Here /{string io.file-error} are the formas the fopen atom provides as the arguments for not-found when it triggers that branch. Grammar: /{ forma (SPACE forma)* }, where each forma is a dotted name (NAME ('.' NAME)*, optionally Q-rooted) — the same shape as an atom /sig.
Atoms only
The /{…} suffix is legal only on a vertical void whose enclosing formation is an atom (its head carries /sig). A /{…} in a non-atom formation is a parse error. This restriction must be stated in the spec.
XMIR contract — the @types attribute
The forma-list maps to a new multi-valued @types attribute on the void — not child objects. Each space-separated forma is resolved exactly the way @atom is (alias expansion + default-package prefixing, with Q→Φ per §9.3), so the parsed XMIR holds fully-qualified, Φ-rooted names:
<o name="not-found" base="∅" types="Φ.string Φ.io.file-error"/>
- The parser first emits the raw, space-separated formas (e.g.
types="string io.file-error"); the existing FQN passes then resolve each token.
- Because
@types is multi-valued (unlike single-valued @atom), resolve-aliases.xsl and add-default-package.xsl must split @types, resolve each token the same way they resolve @atom, and rejoin. Aliases (+alias) are honoured per token, exactly as for @atom.
- The void keeps
@base='∅', so eo:void detection and move-voids-up are unaffected.
Implementation sketch (spec-driven parser)
PARSER_SPEC.md — add ? (VOID) to §2.3; a vertical-void clause and the /{…} forma-list grammar to §3.x; the atoms-only restriction; and the §9.x emission for both forms (the ∅ void and the @types attribute, noting @types resolves like @atom).
- Recognize a
?-headed line in Eo.classify; parse ? > name and the optional /{…} suffix; emit the ∅ void child, plus the raw @types attribute when a forma-list is present.
- Extend
resolve-aliases.xsl and add-default-package.xsl to resolve each space-separated token of @types like an @atom.
- Close-time validation:
? outside a formation, ? without a name, and /{…} outside an atom are all reported.
- Verify the rest of the parse pipeline leaves the new emission intact (
move-voids-up, mandatory-as, build-fqns).
Tests
- Parse pack:
[name] > foo + ? > bar + ? > test ≡ [name bar test] > foo (same three ∅ voids, in order), surviving the full pipeline.
- Parse pack: atom
fopen with ? > not-found /{string io.file-error} emits the void with @types="Φ.string Φ.io.file-error" (FQN-resolved); a variant with an +alias confirms per-token alias resolution.
- Parse pack / negative:
/{…} in a non-atom formation is rejected.
EoTest: ? > x emits <o name='x' base='∅'/>.
- Round-trip test documenting that vertical voids print back as bracket params.
Out of scope
The ?. operator, fragile-object detection, and ⊥ runtime semantics — separate #5261 sub-tickets.
Part of the fragile-objects epic #5261. This ticket covers the two
?-headed body-line forms the epic introduces: plain vertical void attributes (any formation) and atom error-formas (/{…}, atoms only).1. Vertical void attributes —
? > nameThe epic makes the two snippets equivalent:
A
? > namebody line declares a void attribute on the enclosing formation. The?glyph is currently free — it was a NAME terminator only, and its legacy atom-return-type use was dropped from the grammar in #5078 — so this repurposes it.XMIR contract
A vertical void emits exactly the element a bracket param does (§9.4):
It is therefore indistinguishable in XMIR from
[… bar …]. Consequences:move-voids-up.xslalready hoists every∅child to the front, so a body-declared void lands among the head voids with no XSL change. The equivalence[name] + ? > bar + ? > test ≡ [name bar test]falls out of that pass.∅voids are handled end to end already.Surface syntax
?-headed body line with a mandatory> namesuffix:? > bar.?line elsewhere, or a?without a> name, is a parse error.? > @is allowed (a void namedφ), mirroring R-3.4.2 which admits@as a bracket void param.2. Atom error-formas —
? > name /{forma …}In an atom, a vertical void may carry a brace-delimited forma-list:
Here
/{string io.file-error}are the formas thefopenatom provides as the arguments fornot-foundwhen it triggers that branch. Grammar:/{ forma (SPACE forma)* }, where eachformais a dotted name (NAME ('.' NAME)*, optionallyQ-rooted) — the same shape as an atom/sig.Atoms only
The
/{…}suffix is legal only on a vertical void whose enclosing formation is an atom (its head carries/sig). A/{…}in a non-atom formation is a parse error. This restriction must be stated in the spec.XMIR contract — the
@typesattributeThe forma-list maps to a new multi-valued
@typesattribute on the void — not child objects. Each space-separated forma is resolved exactly the way@atomis (alias expansion + default-package prefixing, withQ→Φper §9.3), so the parsed XMIR holds fully-qualified,Φ-rooted names:types="string io.file-error"); the existing FQN passes then resolve each token.@typesis multi-valued (unlike single-valued@atom),resolve-aliases.xslandadd-default-package.xslmust split@types, resolve each token the same way they resolve@atom, and rejoin. Aliases (+alias) are honoured per token, exactly as for@atom.@base='∅', soeo:voiddetection andmove-voids-upare unaffected.Implementation sketch (spec-driven parser)
PARSER_SPEC.md— add?(VOID) to §2.3; a vertical-void clause and the/{…}forma-list grammar to §3.x; the atoms-only restriction; and the §9.x emission for both forms (the∅void and the@typesattribute, noting@typesresolves like@atom).?-headed line inEo.classify; parse? > nameand the optional/{…}suffix; emit the∅void child, plus the raw@typesattribute when a forma-list is present.resolve-aliases.xslandadd-default-package.xslto resolve each space-separated token of@typeslike an@atom.?outside a formation,?without a name, and/{…}outside an atom are all reported.move-voids-up,mandatory-as,build-fqns).Tests
[name] > foo+? > bar+? > test≡[name bar test] > foo(same three∅voids, in order), surviving the full pipeline.fopenwith? > not-found /{string io.file-error}emits the void with@types="Φ.string Φ.io.file-error"(FQN-resolved); a variant with an+aliasconfirms per-token alias resolution./{…}in a non-atom formation is rejected.EoTest:? > xemits<o name='x' base='∅'/>.Out of scope
The
?.operator, fragile-object detection, and⊥runtime semantics — separate #5261 sub-tickets.