diff --git a/eo-parser/PARSER_SPEC.md b/eo-parser/PARSER_SPEC.md index 50db26cc4b7..b87d08b4159 100644 --- a/eo-parser/PARSER_SPEC.md +++ b/eo-parser/PARSER_SPEC.md @@ -168,6 +168,7 @@ The parser recognises the following lexical tokens: | `ROOT` | `Q` | | `XI` | `$` | | `TERM` | `T` — the bottom term (§9.3), similar to `⊥` in 𝜑-calculus. A self-contained single-character token; carries no arguments and no chain. | +| `VOID` | `?` — the vertical-void marker (§3.4). A `? > name` body line declares a void attribute, equivalent to listing `name` in `[…]`. | | `INT` | optional sign, then `0` or non-zero digit string. | | `FLOAT` | optional sign, digits, `.`, digits, optional exponent. | | `HEX` | `0x` followed by hex digits. | @@ -269,6 +270,17 @@ R-3.4.3. `^` (`RHO` token) is rejected as a parameter name; only `NAME` and `PHI R-3.4.4. No leading/trailing space inside `[ ]`. R-3.4.5. No double space between parameter names. R-3.4.6. The formation line may end with one of the optional name suffixes (§3.10). +R-3.4.7. A void attribute may also be declared **vertically** as a `? > name` body line of the formation (the `?` is the `VOID` token of §2.3). It is equivalent to listing `name` among the bracket parameters: it emits the same void child (§9.4), and `move-voids-up` hoists it among the head voids in source order, so `[name] > foo` with body lines `? > bar` and `? > test` is identical in XMIR to `[name bar test] > foo`. `? > name` is the **only** shape the `?` marker may take: the marker is not a value, so it may not appear as an argument (`foo ? bar`), a method receiver (`?.read`), or a reversed-dispatch argument (`foo. ? q`), and a bare `?` or any other trailing tokens are an error. The form requires a name suffix and is legal only as a direct child of a formation, which has no children of its own (a deeper-indent line under a void is rejected). Reverse printing canonicalises every void to the bracket form, since the two are indistinguishable in XMIR. +R-3.4.8. In an **atom** (a formation whose head carries `/sig`), a vertical void may carry a brace-delimited forma-list — `? > name /{forma (SPACE forma)*}` — declaring the formas the atom supplies as the arguments of that error branch. Each `forma` is a dotted name (`NAME ('.' NAME)*`), optionally rooted at `Q`. The list emits as a `@types` attribute on the void's ``, whose space-separated tokens are resolved exactly like an `@atom`: a leading `Q.` is promoted to `Φ.` at parse (R-9.3), then each token is alias-expanded and, when dotless and non-special, prefixed with `Φ.`. Example: `? > not-found /{string io.file-error}` emits ``. A `/{…}` forma-list on a void outside an atom is an error. +R-3.4.9. Vertical voids must stay **on top**: every `? > name` line must precede all non-void attributes of the formation body. A void that follows a non-void child — or sits between non-void children — is an error (`a void attribute must be declared above all other attributes`). For example, + +``` +[] > foo + 6 > six + ? > x +``` + +is rejected, whereas `? > x` above `6 > six` is accepted. (Bracket-head voids are always above the body, so the rule constrains only the relative order of body lines.) Outer kind: **`bare-formation`** (master; openness `open` for body). diff --git a/eo-parser/src/main/java/org/eolang/parser/Emit.java b/eo-parser/src/main/java/org/eolang/parser/Emit.java index 44f05d81c30..808bbe92fe9 100644 --- a/eo-parser/src/main/java/org/eolang/parser/Emit.java +++ b/eo-parser/src/main/java/org/eolang/parser/Emit.java @@ -277,6 +277,17 @@ void star() { this.append(new Directives().attr("star", "")); } + /** + * Add the {@code @types="forma …"} attribute to the most recently + * opened {@code } — the space-separated forma-list of an atom's + * vertical void error-branch (R-3.4.8). Each token is resolved like + * an {@code @atom} by later passes. + * @param formas Space-separated forma list + */ + void types(final String formas) { + this.append(new Directives().attr("types", formas)); + } + /** * Add the {@code @as=tag} attribute to the most recently opened * {@code } — inline-binding marker per §3.12 / §9.4. Numeric diff --git a/eo-parser/src/main/java/org/eolang/parser/Eo.java b/eo-parser/src/main/java/org/eolang/parser/Eo.java index 3bd4e2a1e89..5ec6ad3685d 100644 --- a/eo-parser/src/main/java/org/eolang/parser/Eo.java +++ b/eo-parser/src/main/java/org/eolang/parser/Eo.java @@ -339,7 +339,6 @@ private static boolean closesTextBlock(final Span span, final Globals globals) { * @return The classified line * @checkstyle NPathComplexityCheck (60 lines) */ - @SuppressWarnings("PMD.NPathComplexity") private static Line classify(final Span span) { final Line line; if (span.blank()) { @@ -352,16 +351,10 @@ private static Line classify(final Span span) { line = new LnFormation(span); } else if (span.head() == '.') { line = new LnMethod(span); + } else if (span.head() == '?') { + line = new LnVoid(span); } else if (span.head() >= 'a' && span.head() <= 'z') { - if (Eo.reversedDispatch(span)) { - line = new LnReversed(span); - } else if (Eo.onlyPhi(span)) { - line = new LnOnlyPhi(span); - } else if (Eo.compactTuple(span)) { - line = new LnCompactTuple(span); - } else { - line = new LnApplication(span); - } + line = Eo.applicative(span, Eo.reversedDispatch(span)); } else if (span.head() == '*' || span.head() == '"' || span.head() == '(' @@ -374,15 +367,7 @@ private static Line classify(final Span span) { || span.head() >= 'A' && span.head() <= 'F' || span.head() == '-' || Eo.signedDigit(span)) { - if (Eo.rootReversedDispatch(span)) { - line = new LnReversed(span); - } else if (Eo.onlyPhi(span)) { - line = new LnOnlyPhi(span); - } else if (Eo.compactTuple(span)) { - line = new LnCompactTuple(span); - } else { - line = new LnApplication(span); - } + line = Eo.applicative(span, Eo.rootReversedDispatch(span)); } else if (span.body().codePoints().findFirst().orElse(0) == 0x1F335) { line = (stack, globals, emit) -> { throw new ParseError( @@ -401,6 +386,29 @@ private static Line classify(final Span span) { return line; } + /** + * Classify an application-like line — the inner dispatch shared by + * the identifier-headed and root-headed line groups (§3.1): + * reversed dispatch, only-phi formation, compact tuple, or plain + * application. + * @param span The line span + * @param reversed Whether the line is a reversed dispatch + * @return The line shape + */ + private static Line applicative(final Span span, final boolean reversed) { + final Line line; + if (reversed) { + line = new LnReversed(span); + } else if (Eo.onlyPhi(span)) { + line = new LnOnlyPhi(span); + } else if (Eo.compactTuple(span)) { + line = new LnCompactTuple(span); + } else { + line = new LnApplication(span); + } + return line; + } + /** * End-of-stream checks — §8 of the spec. * diff --git a/eo-parser/src/main/java/org/eolang/parser/Kind.java b/eo-parser/src/main/java/org/eolang/parser/Kind.java index be48a54ec62..f9711f72949 100644 --- a/eo-parser/src/main/java/org/eolang/parser/Kind.java +++ b/eo-parser/src/main/java/org/eolang/parser/Kind.java @@ -98,7 +98,13 @@ enum Kind { /** * Triple-quoted {@code """…"""} text block. */ - TEXT_BLOCK; + TEXT_BLOCK, + + /** + * Vertical void attribute {@code ? > name} (R-3.4.7). A closed leaf + * that must precede every non-void child of its formation. + */ + VOID; /** * Whether this kind is in the horizontally-completed set. diff --git a/eo-parser/src/main/java/org/eolang/parser/Level.java b/eo-parser/src/main/java/org/eolang/parser/Level.java index 2a3c6b23300..3b2a36b5f31 100644 --- a/eo-parser/src/main/java/org/eolang/parser/Level.java +++ b/eo-parser/src/main/java/org/eolang/parser/Level.java @@ -78,6 +78,13 @@ final class Level { */ private boolean taken; + /** + * True once a non-void child has been added under this entry — + * after which a {@link Kind#VOID} child is rejected (R-3.4.7, + * voids must precede every other attribute). + */ + private boolean plain; + /** * For {@link Kind#COMPACT_TUPLE}: the {@code N} count from {@code *N}. */ @@ -280,6 +287,27 @@ void consumeReceiver() { this.taken = true; } + /** + * Observe a child of the given {@code kind} being added under this + * entry, enforcing the void-ordering rule (R-3.4.7): a + * {@link Kind#VOID} child is rejected once a non-void child has + * appeared, and every non-void child records that fact. + * @param shape Kind of the child being added + * @param line Source line of the child (for the error) + * @param column Source indent of the child (for the error) + */ + void observeVoid(final Kind shape, final int line, final int column) { + if (shape == Kind.VOID && this.plain) { + throw new ParseError( + line, column, + "a void attribute must be declared above all other attributes" + ); + } + if (shape != Kind.VOID) { + this.plain = true; + } + } + /** * Set the compact-tuple {@code N} count. * @param value N diff --git a/eo-parser/src/main/java/org/eolang/parser/LnVoid.java b/eo-parser/src/main/java/org/eolang/parser/LnVoid.java new file mode 100644 index 00000000000..a27d55021da --- /dev/null +++ b/eo-parser/src/main/java/org/eolang/parser/LnVoid.java @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.parser; + +/** + * A vertical void-attribute line — R-3.4.7 / R-3.4.8 of the spec. + * + *

Form: {@code ? > name} with an optional atom-only forma-list tail + * {@code /{forma …}}. The {@code ?} declares a void attribute + * on the enclosing formation, equivalent to listing {@code name} among + * the bracket parameters; it emits the same {@code } void child (§9.4), which {@code move-voids-up} hoists + * among the head voids.

+ * + *

In an atom (a formation whose head carries {@code /sig}) the void + * may carry the forma-list tail — the formas the atom supplies as the + * arguments of that error branch. They emit as a {@code @types} + * attribute (R-3.4.8) whose space-separated tokens are resolved like an + * {@code @atom} by later passes. The forma-list is rejected outside an + * atom.

+ * + *

{@code ? > name} (with the optional tail) is the only shape the + * {@code ?} marker may take — never an argument, a method receiver, or + * anywhere else a value is expected. The marker is therefore + * not a {@link Value} kind; this line is its sole producer. + * Cross-line behaviour: a closed leaf + * ({@link Openness#VERTICAL_COMPLETED}), so a void has no children.

+ * + * @since 0.1 + */ +final class LnVoid implements Line { + + /** + * The line's source span. + */ + private final Span span; + + /** + * Ctor. + * @param source The source span + */ + LnVoid(final Span source) { + this.span = source; + } + + @Override + public void into(final Stack stack, final Globals globals, final Emit emit) { + Blanks.checkPlain(this.span, globals, emit); + final String tail = this.span.body().substring(1); + final int brace = tail.indexOf("/{"); + final String formas; + final String head; + if (brace < 0) { + formas = ""; + head = tail; + } else { + formas = LnVoid.formas(tail, brace, this.span); + head = tail.substring(0, brace); + } + final Suffix suffix = new Suffix(head, this.span, this.span.indent() + 1); + if (suffix.form() != Suffix.Form.NAME || suffix.constant() || suffix.atom()) { + throw new ParseError( + this.span.line(), this.span.indent(), + "a void attribute must be written as `? > name`" + ); + } + Comments.attach(globals, emit, this.span, suffix.present()); + final Level level = new Transition(stack, this.span).apply( + Kind.VOID, Openness.VERTICAL_COMPLETED, suffix.present() + ); + if (!formas.isEmpty() && !level.patom()) { + throw new ParseError( + this.span.line(), this.span.indent(), + "a `/{…}` forma-list is allowed only inside an atom" + ); + } + globals.clearBlanks(); + globals.markEmitted(); + emit.object( + suffix.attribute(this.span.line(), this.span.indent()), + "∅", this.span.line(), this.span.indent() + ); + if (!formas.isEmpty()) { + emit.types(formas); + } + } + + /** + * Extract and validate the forma-list tail, returning + * the space-separated formas with each leading {@code Q.} promoted + * to {@code Φ.} (R-9.3), exactly as an {@code /sig} signature is + * promoted. + * @param tail The line body after the {@code ?} + * @param brace Index of the forma-list marker in {@code tail} + * @param span The source span (for errors) + * @return Space-separated promoted formas + */ + private static String formas(final String tail, final int brace, final Span span) { + final int close = tail.indexOf('}', brace + 2); + if (close < 0) { + throw new ParseError( + span.line(), span.indent(), + "a `/{…}` forma-list must end with `}`" + ); + } + int after = close + 1; + while (after < tail.length() && tail.charAt(after) == ' ') { + after = after + 1; + } + if (after < tail.length()) { + throw new ParseError( + span.line(), span.indent(), + "trailing garbage after a `/{…}` forma-list" + ); + } + return LnVoid.promoted(tail.substring(brace + 2, close), span); + } + + /** + * Split the forma-list body on single spaces, promoting each + * {@code Q.}-rooted forma to {@code Φ.} and rejecting empty or + * double-spaced entries. + * @param inside The text inside the forma-list braces + * @param span The source span (for errors) + * @return Space-separated promoted formas + */ + private static String promoted(final String inside, final Span span) { + if (inside.isEmpty()) { + throw new ParseError( + span.line(), span.indent(), + "a `/{…}` forma-list must name at least one forma" + ); + } + final StringBuilder out = new StringBuilder(inside.length()); + int idx = 0; + while (idx < inside.length()) { + int end = idx; + while (end < inside.length() && inside.charAt(end) != ' ') { + end = end + 1; + } + if (end == idx) { + throw new ParseError( + span.line(), span.indent(), + "forma names in a `/{…}` list must be separated by exactly one space" + ); + } + if (out.length() > 0) { + out.append(' '); + } + out.append(LnVoid.root(inside.substring(idx, end))); + idx = end + 1; + } + return out.toString(); + } + + /** + * Promote a leading {@code Q.} to {@code Φ.} per R-9.3; other formas + * pass through unchanged. + * @param forma Raw forma name + * @return Promoted forma + */ + private static String root(final String forma) { + final String promoted; + if (forma.startsWith("Q.")) { + promoted = "Φ".concat(forma.substring(1)); + } else { + promoted = forma; + } + return promoted; + } +} diff --git a/eo-parser/src/main/java/org/eolang/parser/Stack.java b/eo-parser/src/main/java/org/eolang/parser/Stack.java index 22c159ae921..8bd4152778b 100644 --- a/eo-parser/src/main/java/org/eolang/parser/Stack.java +++ b/eo-parser/src/main/java/org/eolang/parser/Stack.java @@ -26,7 +26,6 @@ * * @since 0.1 */ -@SuppressWarnings("PMD.UnnecessaryLocalRule") final class Stack { /** @@ -189,6 +188,7 @@ Level push( } parent = under.kind(); patom = under.atom(); + under.observeVoid(kind, line, indent); } if (!this.levels.isEmpty()) { this.opener.beforeChild(this.top()); @@ -251,6 +251,7 @@ Level replace(final int line, final Kind kind, final Openness openness) { final Level under = this.top(); parent = under.kind(); patom = under.atom(); + under.observeVoid(kind, line, indent); this.opener.beforeChild(under); } final Level fresh = new Level(indent, line, kind, openness, parent, patom); diff --git a/eo-parser/src/main/resources/org/eolang/parser/parse/add-default-package.xsl b/eo-parser/src/main/resources/org/eolang/parser/parse/add-default-package.xsl index 62aff7e395a..43e6bfade7f 100644 --- a/eo-parser/src/main/resources/org/eolang/parser/parse/add-default-package.xsl +++ b/eo-parser/src/main/resources/org/eolang/parser/parse/add-default-package.xsl @@ -30,6 +30,11 @@ + + + + + diff --git a/eo-parser/src/main/resources/org/eolang/parser/parse/resolve-aliases.xsl b/eo-parser/src/main/resources/org/eolang/parser/parse/resolve-aliases.xsl index 32d309007af..1ef79d14740 100644 --- a/eo-parser/src/main/resources/org/eolang/parser/parse/resolve-aliases.xsl +++ b/eo-parser/src/main/resources/org/eolang/parser/parse/resolve-aliases.xsl @@ -44,6 +44,11 @@ + + + + + diff --git a/eo-parser/src/test/java/org/eolang/parser/EoTest.java b/eo-parser/src/test/java/org/eolang/parser/EoTest.java index ff74df27773..5695f450c37 100644 --- a/eo-parser/src/test/java/org/eolang/parser/EoTest.java +++ b/eo-parser/src/test/java/org/eolang/parser/EoTest.java @@ -158,6 +158,73 @@ void parsesFormationWithVoidParams() { ); } + @Test + void parsesVerticalVoidAttribute() { + MatcherAssert.assertThat( + "a `? > x` body line must emit a void param inside the formation", + EoTest.render("[] > foo", " ? > x"), + XhtmlMatchers.hasXPath("/object/o[@name='foo']/o[@name='x' and @base='∅']") + ); + } + + @Test + void rejectsVoidWithMethodDispatch() { + MatcherAssert.assertThat( + "a `?.method` dispatch on the void marker must be rejected", + EoTest.render("[] > foo", " ?.read > x"), + XhtmlMatchers.hasXPath("/object/errors/error") + ); + } + + @Test + void rejectsVoidAsArgument() { + MatcherAssert.assertThat( + "the void marker `?` must not be accepted as a horizontal argument", + EoTest.render("[] > foo", " bar ? baz > x"), + XhtmlMatchers.hasXPath("/object/errors/error") + ); + } + + @Test + void rejectsVoidInReversedArgument() { + MatcherAssert.assertThat( + "the void marker `?` must not be accepted as a reversed-dispatch argument", + EoTest.render("[] > foo", " bar. ? q > m"), + XhtmlMatchers.hasXPath("/object/errors/error") + ); + } + + @Test + void parsesAtomVoidWithFormaList() { + MatcherAssert.assertThat( + "a void in an atom must carry its `/{…}` forma-list as a raw @types attribute", + EoTest.render("[] > fopen /file", " ? > not-found /{string io.file-error}"), + XhtmlMatchers.hasXPath( + "/object/o[@name='fopen']/o[@name='not-found' and @base='∅' and @types='string io.file-error']" + ) + ); + } + + @Test + void rejectsFormaListOutsideAtom() { + MatcherAssert.assertThat( + "a `/{…}` forma-list on a void outside an atom must be rejected", + EoTest.render("[] > foo", " ? > x /{string}"), + XhtmlMatchers.hasXPath("/object/errors/error") + ); + } + + @Test + void rejectsVoidBelowRegularAttribute() { + MatcherAssert.assertThat( + "a void declared after a regular attribute must be rejected", + EoTest.render("[] > foo", " 6 > six", " ? > x", " 5 > five", " ? > y"), + XhtmlMatchers.hasXPath( + "/object/errors/error[contains(text(),'void attribute must be declared above')]" + ) + ); + } + @Test void parsesAtomDeclaration() { MatcherAssert.assertThat( diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-formas.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-formas.yaml new file mode 100644 index 00000000000..56ec0fa8f6a --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-formas.yaml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +sheets: + - /org/eolang/parser/parse/wrap-method-calls.xsl + - /org/eolang/parser/parse/vars-float-up.xsl + - /org/eolang/parser/parse/move-voids-up.xsl + - /org/eolang/parser/parse/build-fqns.xsl + - /org/eolang/parser/parse/expand-aliases.xsl + - /org/eolang/parser/parse/resolve-aliases.xsl + - /org/eolang/parser/parse/add-default-package.xsl + - /org/eolang/parser/parse/roll-bases.xsl + - /org/eolang/parser/parse/decorate.xsl + - /org/eolang/parser/parse/mandatory-as.xsl +asserts: + - /object[not(errors)] + - /object/o[@name='fopen']/o[@name='not-found' and @base='∅' and @types='Φ.string io.file-error'] + - /object/o[@name='fopen']/o[@name='cant-open' and @base='∅' and @types='Φ.a.b.c'] +input: | + +alias foo a.b.c + + [] > fopen /file + ? > not-found /{string io.file-error} + ? > cant-open /{foo} diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-single-alias.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-single-alias.yaml new file mode 100644 index 00000000000..c1bcb34f464 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/atom-void-single-alias.yaml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +sheets: + - /org/eolang/parser/parse/wrap-method-calls.xsl + - /org/eolang/parser/parse/vars-float-up.xsl + - /org/eolang/parser/parse/move-voids-up.xsl + - /org/eolang/parser/parse/build-fqns.xsl + - /org/eolang/parser/parse/expand-aliases.xsl + - /org/eolang/parser/parse/resolve-aliases.xsl + - /org/eolang/parser/parse/add-default-package.xsl + - /org/eolang/parser/parse/roll-bases.xsl + - /org/eolang/parser/parse/decorate.xsl + - /org/eolang/parser/parse/mandatory-as.xsl +asserts: + - /object[not(errors)] + - /object/o[@name='fopen']/o[@name='e' and @base='∅' and @types='Φ.foo'] +input: | + +alias foo + + [] > fopen /file + ? > e /{foo} diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/vertical-voids.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/vertical-voids.yaml new file mode 100644 index 00000000000..32e8deee229 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-packs/parse/vertical-voids.yaml @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +sheets: + - /org/eolang/parser/parse/wrap-method-calls.xsl + - /org/eolang/parser/parse/vars-float-up.xsl + - /org/eolang/parser/parse/move-voids-up.xsl + - /org/eolang/parser/parse/build-fqns.xsl + - /org/eolang/parser/parse/expand-aliases.xsl + - /org/eolang/parser/parse/resolve-aliases.xsl + - /org/eolang/parser/parse/add-default-package.xsl + - /org/eolang/parser/parse/roll-bases.xsl + - /org/eolang/parser/parse/decorate.xsl + - /org/eolang/parser/parse/mandatory-as.xsl +asserts: + - /object[not(errors)] + - /object/o[@name='foo'][count(o[@base='∅'])=3] + - /object/o[@name='foo']/o[1][@name='name' and @base='∅'] + - /object/o[@name='foo']/o[2][@name='bar' and @base='∅'] + - /object/o[@name='foo']/o[3][@name='test' and @base='∅'] + - /object/o[@name='foo']/o[4][@name='φ' and @base='Φ.hello'] +input: | + [name] > foo + ? > bar + ? > test + hello > @ diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-argument.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-argument.yaml new file mode 100644 index 00000000000..f182c7704a7 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-argument.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +line: 3 +message: "expected value" +input: | + # No comments. + [] > foo + bar ? baz > x diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-reversed-argument.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-reversed-argument.yaml new file mode 100644 index 00000000000..e6083062cc0 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/as-reversed-argument.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +line: 3 +message: "expected value" +input: | + # No comments. + [] > foo + bar. ? q > m diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/below-regular.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/below-regular.yaml new file mode 100644 index 00000000000..4cd464afb75 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/below-regular.yaml @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +line: 4 +message: "void attribute must be declared above" +input: | + # No comments. + [] > foo + 6 > six + ? > x + 5 > five + ? > y diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/forma-list-outside-atom.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/forma-list-outside-atom.yaml new file mode 100644 index 00000000000..915a9c6c966 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/forma-list-outside-atom.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +line: 3 +message: "allowed only inside an atom" +input: | + # No comments. + [] > foo + ? > x /{string} diff --git a/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/method-dispatch.yaml b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/method-dispatch.yaml new file mode 100644 index 00000000000..206679df193 --- /dev/null +++ b/eo-parser/src/test/resources/org/eolang/parser/eo-typos/void/method-dispatch.yaml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com +# SPDX-License-Identifier: MIT +--- +# yamllint disable rule:line-length +line: 3 +message: "a void attribute must be written as" +input: | + # No comments. + [] > foo + ?.read > x