Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions src/main/java/org/eolang/lints/LtSyntaxVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.lints;

import com.github.lombrozo.xnav.Xnav;
import com.jcabi.xml.XML;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.cactoos.io.ResourceOf;
import org.cactoos.text.IoCheckedText;
import org.cactoos.text.TextOf;
import org.eolang.parser.OnDefault;

/**
* Checks that the version specified in the +syntax meta is not
* newer than the current parser version.
*
* <p>If the +syntax meta specifies a version higher than the parser's
* version, it means the code requires a newer parser, and this
* lint reports an error.</p>
*
* @since 0.1.0
*/
final class LtSyntaxVersion implements Lint<XML> {

/**
* The parser version.
*/
private final String parserVersion;

/**
* Default ctor.
*/
LtSyntaxVersion() {
this.parserVersion = "0.0.0";
}

/**
* Ctor.
* @param parserVersion The parser version.
*/
LtSyntaxVersion(final String parserVersion) {
this.parserVersion = parserVersion;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

@Override
public Collection<Defect> defects(final XML xmir) throws IOException {
final Collection<Defect> defects = new ArrayList<>(0);
final Xnav xml = new Xnav(xmir.inner());
final List<Xnav> metas = xml.path("/object/metas/meta[head='syntax']")
.collect(Collectors.toList());
for (final Xnav meta : metas) {
final String tail = meta.element("tail").text().orElse("");
final String line = meta.attribute("line").text().orElse("0");
if (!LtSyntaxVersion.valid(tail)) {
defects.add(
new Defect.Default(
this.name(),
Severity.ERROR,
new OnDefault(xmir).get(),
Integer.parseInt(line),
String.format(
"The format of the +syntax meta is wrong: %s (SemVer expected instead)",
tail
)
)
);
continue;
}
if (this.compareVersions(tail) < 0) {
defects.add(
new Defect.Default(
this.name(),
Severity.ERROR,
new OnDefault(xmir).get(),
Integer.parseInt(line),
String.format(
"The +syntax meta requires version %s, but the current parser version is %s (older)",
tail,
this.parserVersion
)
)
);
}
}
return defects;
}

@Override
public String name() {
return "syntax-version-mismatch";
}

@Override
public String motive() throws IOException {
return new IoCheckedText(
new TextOf(
new ResourceOf(
"org/eolang/motives/metas/syntax-version-mismatch.md"
)
)
).asString();
}

/**
* Check if the version string is a valid SemVer.
* @param version The version to validate.
* @return True if valid.
*/
private static boolean valid(final String version) {
return version.matches("^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9-]+)?$");
}
Comment thread
Daniilmipt marked this conversation as resolved.
Outdated

/**
* Compare parser version to the given syntax version.
* @param syntaxVersion The version from +syntax meta.
* @return -1 if parser is older, 0 if equal, 1 if parser is newer.
*/
private int compareVersions(final String syntaxVersion) {
final int[] syntaxVer = LtSyntaxVersion.parts(syntaxVersion);
final int[] parserVer = LtSyntaxVersion.parts(this.parserVersion);
for (int i = 0; i < syntaxVer.length; i++) {
if (parserVer[i] < syntaxVer[i]) {
return -1;
}
if (parserVer[i] > syntaxVer[i]) {
return 1;
}
}
return 0;
}
Comment thread
Daniilmipt marked this conversation as resolved.

/**
* Parse the numeric parts of a SemVer string.
* @param version The version string.
* @return Array of [major, minor, patch].
*/
private static int[] parts(final String version) {
final String[] segments = version.split("-", 2)[0].split("\\.");
return new int[] {
segments.length > 0 ? Integer.parseInt(segments[0]) : 0,
segments.length > 1 ? Integer.parseInt(segments[1]) : 0,
segments.length > 2 ? Integer.parseInt(segments[2]) : 0
};
}
}
66 changes: 66 additions & 0 deletions src/main/java/org/eolang/lints/MonoLints copy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2026 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.lints;

import com.jcabi.xml.XML;
import java.util.List;
import java.util.stream.Collectors;
import org.cactoos.iterable.IterableEnvelope;
import org.cactoos.iterable.Joined;
import org.cactoos.iterable.Shuffled;
import org.cactoos.list.ListOf;

/**
* Mono lints.
* Mono lints represent a list of lints for single XMIR scope. This class is required
* in order to provide more fine-grained access and be reused by other classes, including
* {@link PkMono} and {@link PkWpa} via {@link MonoLintNames}, to mutually ignore other
* scope lints in both: {@link LtUnlintNonExistingDefect} and {@link LtUnlintNonExistingDefectWpa}
* without causing recursion errors.
* @since 0.0.43
*/
final class MonoLints extends IterableEnvelope<Lint<XML>> {

/**
* All XML-based lints.
*/
private static final Iterable<Lint<XML>> LINTS = new Shuffled<>(
new Joined<Lint<XML>>(
new PkByXsl(),
List.of(
new LtAsciiOnly(),
new LtReservedName(),
new LtSyntaxVersion()
)
)
);

/**
* Ctor.
*/
MonoLints() {
super(
new Joined<Lint<XML>>(
MonoLints.LINTS,
List.of(
new LtIncorrectUnlint(
new ListOf<>(
new Joined<>(
MonoLints.LINTS, new WpaLints(),
new ListOf<>(
new LtUnlintNonExistingDefect(
MonoLints.LINTS, new ListOf<>(new WpaLintNames())
)
)
)
).stream()
.map(Lint::name)
.collect(Collectors.toList())
)
)
)
);
}
}
Comment thread
Daniilmipt marked this conversation as resolved.
Outdated
2 changes: 1 addition & 1 deletion src/main/resources/org/eolang/lints/metas/unique-metas.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<xsl:import href="/org/eolang/funcs/lineno.xsl"/>
<xsl:import href="/org/eolang/funcs/defect-context.xsl"/>
<xsl:output encoding="UTF-8"/>
<xsl:variable name="unique" select="('version', 'architect', 'home', 'package')"/>
<xsl:variable name="unique" select="('version', 'architect', 'home', 'package', 'syntax')"/>
<xsl:variable name="metas" select="/object/metas/meta"/>
<xsl:variable name="heads" select="/object/metas/meta/head"/>
<xsl:template match="/">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<defects>
<xsl:for-each select="/object/metas/meta">
<xsl:variable name="meta-head" select="head"/>
<xsl:variable name="predefined" select="('package', 'alias', 'version', 'rt', 'architect', 'home', 'unlint', 'probe', 'spdx')"/>
<xsl:variable name="predefined" select="('package', 'alias', 'version', 'rt', 'architect', 'home', 'unlint', 'probe', 'spdx', 'syntax')"/>
<xsl:if test="not($meta-head = $predefined)">
<xsl:element name="defect">
<xsl:variable name="line" select="eo:lineno(@line)"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Syntax version mismatch

The `+syntax` meta specifies the version of the EO language that
the source code was written for. If the parser version is older
than the version specified in `+syntax`, the code may use features
that are not supported by the parser, leading to compilation errors.

The parser will refuse to process such files.

Incorrect (if parser version is 0.58.0):

```eo
+syntax 0.59.0

[] > foo
```

Correct (if parser version is 0.59.0 or newer):

```eo
+syntax 0.59.0

[] > foo
```
2 changes: 2 additions & 0 deletions src/main/resources/org/eolang/motives/metas/unknown-metas.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The following metas are supported:
* `+home`
* `+unlint`
* `+probe`
* `+syntax`

Incorrect:

Expand All @@ -29,6 +30,7 @@ Correct:
+architect yegor256@gmail.com
+rt jvm
+home https://earth.com
+syntax 0.59.0
+unlint unsorted-metas

[] > foo
Expand Down
Loading
Loading