1

build: upgrade Java

This commit is contained in:
2026-04-15 23:31:24 +03:00
parent 2ff9281704
commit 97f36cb809
17 changed files with 167 additions and 174 deletions

View File

@@ -7,7 +7,7 @@ group = "ru.di9.fluent"
version = "1.0-SNAPSHOT" version = "1.0-SNAPSHOT"
java.toolchain { java.toolchain {
languageVersion = JavaLanguageVersion.of(17) languageVersion = JavaLanguageVersion.of(21)
} }
repositories { repositories {

View File

@@ -5,7 +5,7 @@ import ru.di9.fluent.syntax.ast.*;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import static ru.di9.fluent.syntax.StringUtils.inRange_09; import static ru.di9.fluent.syntax.utils.StringUtils.inRange_09;
import static ru.di9.fluent.syntax.parser.FluentStream.*; import static ru.di9.fluent.syntax.parser.FluentStream.*;
import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.*; import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.*;
@@ -18,13 +18,13 @@ public class FluentParser {
int spanStart = ps.index; int spanStart = ps.index;
List<TopLevel> entries = new ArrayList<>(); List<TopLevel> entries = new ArrayList<>();
Comment lastComment = null; Comment lastComment = null;
var blankLines = ps.skipBlankBlock(); String blankLines = ps.skipBlankBlock();
if (!blankLines.isEmpty()) { if (!blankLines.isEmpty()) {
entries.add(new Whitespace(blankLines)); entries.add(new Whitespace(blankLines));
} }
while (ps.currentChar().isPresent()) { while (ps.currentChar().isPresent()) {
var entry = getEntryOrJunk(ps); TopLevel entry = getEntryOrJunk(ps);
blankLines = ps.skipBlankBlock(); blankLines = ps.skipBlankBlock();
// Regular Comments require special logic. Comments may be attached to // Regular Comments require special logic. Comments may be attached to
@@ -78,7 +78,7 @@ public class FluentParser {
int entryStartPos = ps.index; int entryStartPos = ps.index;
try { try {
var entry = getEntry(ps); Entry entry = getEntry(ps);
ps.expectLineEnd(); ps.expectLineEnd();
return entry; return entry;
} catch (ParseException e) { } catch (ParseException e) {
@@ -91,7 +91,7 @@ public class FluentParser {
} }
// Create a Junk instance // Create a Junk instance
var slice = ps.string.substring(entryStartPos, nextEntryStart); String slice = ps.string.substring(entryStartPos, nextEntryStart);
var junk = new Junk(slice); var junk = new Junk(slice);
if (withSpans) { if (withSpans) {
junk.addSpan(entryStartPos, nextEntryStart); junk.addSpan(entryStartPos, nextEntryStart);
@@ -107,7 +107,7 @@ public class FluentParser {
} }
private Entry getEntry(FluentStream ps) { private Entry getEntry(FluentStream ps) {
var currentChar = ps.currentChar(); Optional<Character> currentChar = ps.currentChar();
if (currentChar.filter(v -> v == '#').isPresent()) { if (currentChar.filter(v -> v == '#').isPresent()) {
return getComment(ps); return getComment(ps);
@@ -126,13 +126,13 @@ public class FluentParser {
private Message getMessage(FluentStream ps) { private Message getMessage(FluentStream ps) {
int spanStart = ps.index; int spanStart = ps.index;
var id = getIdentifier(ps); Identifier id = getIdentifier(ps);
ps.skipBlankInline(); ps.skipBlankInline();
ps.expectChar('='); ps.expectChar('=');
var value = maybeGetPattern(ps); Pattern value = maybeGetPattern(ps);
var attrs = getAttributes(ps); Collection<Attribute> attrs = getAttributes(ps);
if (value == null && attrs.isEmpty()) { if (value == null && attrs.isEmpty()) {
throw new ParseException(E0005, id.getName()); throw new ParseException(E0005, id.getName());
@@ -150,17 +150,17 @@ public class FluentParser {
int spanStart = ps.index; int spanStart = ps.index;
ps.expectChar('-'); ps.expectChar('-');
var id = getIdentifier(ps); Identifier id = getIdentifier(ps);
ps.skipBlankInline(); ps.skipBlankInline();
ps.expectChar('='); ps.expectChar('=');
var value = maybeGetPattern(ps); Pattern value = maybeGetPattern(ps);
if (value == null) { if (value == null) {
throw new ParseException(E0006, id.getName()); throw new ParseException(E0006, id.getName());
} }
var attrs = getAttributes(ps); Collection<Attribute> attrs = getAttributes(ps);
var term = new Term(id, value); var term = new Term(id, value);
term.getAttributes().addAll(attrs); term.getAttributes().addAll(attrs);
@@ -176,7 +176,7 @@ public class FluentParser {
ps.peekBlank(); ps.peekBlank();
while (ps.isAttributeStart()) { while (ps.isAttributeStart()) {
ps.skipToPeek(); ps.skipToPeek();
var attr = getAttribute(ps); Attribute attr = getAttribute(ps);
attrs.add(attr); attrs.add(attr);
ps.peekBlank(); ps.peekBlank();
} }
@@ -187,12 +187,12 @@ public class FluentParser {
int spanStart = ps.index; int spanStart = ps.index;
ps.expectChar('.'); ps.expectChar('.');
var key = getIdentifier(ps); Identifier key = getIdentifier(ps);
ps.skipBlankInline(); ps.skipBlankInline();
ps.expectChar('='); ps.expectChar('=');
var value = maybeGetPattern(ps); Pattern value = maybeGetPattern(ps);
if (value == null) { if (value == null) {
throw new ParseException(E0012); throw new ParseException(E0012);
} }
@@ -234,8 +234,8 @@ public class FluentParser {
if (isBlock) { if (isBlock) {
// A block pattern is a pattern which starts on a new line. Store and // A block pattern is a pattern which starts on a new line. Store and
// measure the indent of this first line for the dedentation logic. // measure the indent of this first line for the dedentation logic.
var blankStart = ps.index; int blankStart = ps.index;
var firstIndent = ps.skipBlankInline(); String firstIndent = ps.skipBlankInline();
elements.add(getIndent(ps, firstIndent, blankStart)); elements.add(getIndent(ps, firstIndent, blankStart));
commonIndentLength = firstIndent.length(); commonIndentLength = firstIndent.length();
} }
@@ -243,14 +243,14 @@ public class FluentParser {
Optional<Character> opt; Optional<Character> opt;
elements: elements:
while ((opt = ps.currentChar()).isPresent()) { while ((opt = ps.currentChar()).isPresent()) {
var ch = opt.get(); Character ch = opt.get();
switch (ch) { switch (ch) {
case EOL -> { case EOL -> {
var blankStart = ps.index; int blankStart = ps.index;
var blankLines = ps.peekBlankBlock(); String blankLines = ps.peekBlankBlock();
if (ps.isValueContinuation()) { if (ps.isValueContinuation()) {
ps.skipToPeek(); ps.skipToPeek();
var indent = ps.skipBlankInline(); String indent = ps.skipBlankInline();
commonIndentLength = Math.min(commonIndentLength, indent.length()); commonIndentLength = Math.min(commonIndentLength, indent.length());
elements.add(getIndent(ps, blankLines + indent, blankStart)); elements.add(getIndent(ps, blankLines + indent, blankStart));
continue; continue;
@@ -267,7 +267,7 @@ public class FluentParser {
} }
} }
var dedented = dedent(elements, commonIndentLength); List<PatternElement> dedented = dedent(elements, commonIndentLength);
var pattern = new Pattern(dedented); var pattern = new Pattern(dedented);
if (withSpans) { if (withSpans) {
pattern.addSpan(spanStart, ps.index); pattern.addSpan(spanStart, ps.index);
@@ -281,7 +281,7 @@ public class FluentParser {
private List<PatternElement> dedent(Collection<PatternElement> elements, int commonIndent) { private List<PatternElement> dedent(Collection<PatternElement> elements, int commonIndent) {
ArrayList<PatternElement> trimmed = new ArrayList<>(); ArrayList<PatternElement> trimmed = new ArrayList<>();
for (var element : elements) { for (PatternElement element : elements) {
if (element instanceof Placeable pl) { if (element instanceof Placeable pl) {
trimmed.add(pl); trimmed.add(pl);
continue; continue;
@@ -296,22 +296,10 @@ public class FluentParser {
} }
if (!trimmed.isEmpty()) { if (!trimmed.isEmpty()) {
var prev = trimmed.get(trimmed.size() - 1); PatternElement prev = trimmed.getLast();
if (prev instanceof TextElement prevTE) { if (prev instanceof TextElement prevTE) {
// Join adjacent TextElements by replacing them with their sum. // Join adjacent TextElements by replacing them with their sum.
String newVal; TextElement sum = getTextElement(element, prevTE);
if (element instanceof TextElement elmTE) {
newVal = elmTE.getValue();
} else if (element instanceof Indent elmInd) {
newVal = elmInd.getValue();
} else {
throw new IllegalStateException("Unexpected PatternElement type");
}
var sum = new TextElement(prevTE.getValue() + newVal);
if (withSpans && prevTE.getSpan().isPresent() && element.getSpan().isPresent()) {
sum.addSpan(prevTE.getSpan().get().getStart(), element.getSpan().get().getEnd());
}
trimmed.set(trimmed.size() - 1, sum); trimmed.set(trimmed.size() - 1, sum);
continue; continue;
@@ -335,17 +323,34 @@ public class FluentParser {
} }
// Trim trailing whitespace from the Pattern. // Trim trailing whitespace from the Pattern.
var lastElement = trimmed.get(trimmed.size() - 1); PatternElement lastElement = trimmed.getLast();
if (lastElement instanceof TextElement lastElmTE) { if (lastElement instanceof TextElement lastElmTE) {
lastElmTE.setValue(TRAILING_WS_RE.matcher(lastElmTE.getValue()).replaceAll("")); lastElmTE.setValue(TRAILING_WS_RE.matcher(lastElmTE.getValue()).replaceAll(""));
if (lastElmTE.getValue().isEmpty()) { if (lastElmTE.getValue().isEmpty()) {
trimmed.remove(trimmed.size() - 1); trimmed.removeLast();
} }
} }
return trimmed; return trimmed;
} }
private TextElement getTextElement(PatternElement element, TextElement prevTE) {
String newVal;
if (element instanceof TextElement elmTE) {
newVal = elmTE.getValue();
} else if (element instanceof Indent elmInd) {
newVal = elmInd.getValue();
} else {
throw new IllegalStateException("Unexpected PatternElement type");
}
var sum = new TextElement(prevTE.getValue() + newVal);
if (withSpans && prevTE.getSpan().isPresent() && element.getSpan().isPresent()) {
sum.addSpan(prevTE.getSpan().get().getStart(), element.getSpan().get().getEnd());
}
return sum;
}
private TextElement getTextElement(FluentStream ps) { private TextElement getTextElement(FluentStream ps) {
var buffer = new StringBuilder(); var buffer = new StringBuilder();
int spanStart = ps.index; int spanStart = ps.index;
@@ -375,13 +380,13 @@ public class FluentParser {
} }
private PatternElement getPlaceable(FluentStream ps) { private PatternElement getPlaceable(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
ps.expectChar('{'); ps.expectChar('{');
ps.skipBlank(); ps.skipBlank();
SyntaxNode expression; SyntaxNode expression;
if (ps.currentChar().filter(v -> v == '{').isPresent()) { if (ps.currentChar().filter(v -> v == '{').isPresent()) {
var child = getPlaceable(ps); PatternElement child = getPlaceable(ps);
ps.skipBlank(); ps.skipBlank();
expression = child; expression = child;
} else { } else {
@@ -401,7 +406,7 @@ public class FluentParser {
private Expression getExpression(FluentStream ps) { private Expression getExpression(FluentStream ps) {
int spanStart = ps.index; int spanStart = ps.index;
var selector = getInlineExpression(ps); Expression selector = getInlineExpression(ps);
ps.skipBlank(); ps.skipBlank();
if (ps.currentChar().filter(v -> v == '-').isPresent()) { if (ps.currentChar().filter(v -> v == '-').isPresent()) {
@@ -425,7 +430,7 @@ public class FluentParser {
ps.skipBlankInline(); ps.skipBlankInline();
ps.expectLineEnd(); ps.expectLineEnd();
var variants = getVariants(ps); List<Variant> variants = getVariants(ps);
var selectExpression = new SelectExpression(selector, variants); var selectExpression = new SelectExpression(selector, variants);
if (withSpans) { if (withSpans) {
@@ -448,7 +453,7 @@ public class FluentParser {
ps.skipBlank(); ps.skipBlank();
while (ps.isVariantStart()) { while (ps.isVariantStart()) {
var variant = getVariant(ps, hasDefault); Variant variant = getVariant(ps, hasDefault);
if (variant.isDefault()) { if (variant.isDefault()) {
hasDefault = true; hasDefault = true;
@@ -486,13 +491,13 @@ public class FluentParser {
ps.skipBlank(); ps.skipBlank();
var key = getVariantKey(ps); VariantKey key = getVariantKey(ps);
ps.skipBlank(); ps.skipBlank();
ps.expectChar(']'); ps.expectChar(']');
//val value = this.maybeGetPattern(ps) ?: throw ParseError("E0012") //val value = this.maybeGetPattern(ps) ?: throw ParseError("E0012")
var value = maybeGetPattern(ps); Pattern value = maybeGetPattern(ps);
if (value == null) { if (value == null) {
throw new ParseException(E0012); throw new ParseException(E0012);
} }
@@ -515,7 +520,7 @@ public class FluentParser {
} }
private Expression getInlineExpression(FluentStream ps) { private Expression getInlineExpression(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
if (ps.isNumberStart()) { if (ps.isNumberStart()) {
return getNumber(ps); return getNumber(ps);
@@ -527,7 +532,7 @@ public class FluentParser {
if (ps.currentChar().filter(v -> v == '$').isPresent()) { if (ps.currentChar().filter(v -> v == '$').isPresent()) {
ps.next(); ps.next();
var id = getIdentifier(ps); Identifier id = getIdentifier(ps);
var variableReference = new VariableReference(id); var variableReference = new VariableReference(id);
if (withSpans) { if (withSpans) {
@@ -539,7 +544,7 @@ public class FluentParser {
if (ps.currentChar().filter(v -> v == '-').isPresent()) { if (ps.currentChar().filter(v -> v == '-').isPresent()) {
ps.next(); ps.next();
var id = getIdentifier(ps); Identifier id = getIdentifier(ps);
Identifier attr = null; Identifier attr = null;
if (ps.currentChar().filter(v -> v == '.').isPresent()) { if (ps.currentChar().filter(v -> v == '.').isPresent()) {
@@ -563,7 +568,7 @@ public class FluentParser {
} }
if (ps.isIdentifierStart()) { if (ps.isIdentifierStart()) {
var id = getIdentifier(ps); Identifier id = getIdentifier(ps);
ps.peekBlank(); ps.peekBlank();
if (ps.currentPeek().filter(v -> v == '(').isPresent()) { if (ps.currentPeek().filter(v -> v == '(').isPresent()) {
@@ -573,7 +578,7 @@ public class FluentParser {
} }
ps.skipToPeek(); ps.skipToPeek();
var args = getCallArguments(ps); CallArguments args = getCallArguments(ps);
var functionReference = new FunctionReference(id, args); var functionReference = new FunctionReference(id, args);
if (withSpans) { if (withSpans) {
functionReference.addSpan(spanStart, ps.index); functionReference.addSpan(spanStart, ps.index);
@@ -599,7 +604,7 @@ public class FluentParser {
} }
private CallArguments getCallArguments(FluentStream ps) { private CallArguments getCallArguments(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
List<Expression> positional = new ArrayList<>(); List<Expression> positional = new ArrayList<>();
List<NamedArgument> named = new ArrayList<>(); List<NamedArgument> named = new ArrayList<>();
Set<String> argumentNames = new HashSet<>(); Set<String> argumentNames = new HashSet<>();
@@ -612,7 +617,7 @@ public class FluentParser {
break; break;
} }
var arg = getCallArgument(ps); CallArgument arg = getCallArgument(ps);
if (arg instanceof NamedArgument na) { if (arg instanceof NamedArgument na) {
if (argumentNames.contains(na.getName().getName())) { if (argumentNames.contains(na.getName().getName())) {
throw new ParseException(E0022); throw new ParseException(E0022);
@@ -648,8 +653,8 @@ public class FluentParser {
} }
private CallArgument getCallArgument(FluentStream ps) { private CallArgument getCallArgument(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
var exp = getInlineExpression(ps); Expression exp = getInlineExpression(ps);
ps.skipBlank(); ps.skipBlank();
@@ -661,7 +666,7 @@ public class FluentParser {
ps.next(); ps.next();
ps.skipBlank(); ps.skipBlank();
var value = getLiteral(ps); Literal value = getLiteral(ps);
var namedArgument = new NamedArgument(mr.getId(), value); var namedArgument = new NamedArgument(mr.getId(), value);
if (withSpans) { if (withSpans) {
@@ -687,7 +692,7 @@ public class FluentParser {
} }
private StringLiteral getString(FluentStream ps) { private StringLiteral getString(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
ps.expectChar('"'); ps.expectChar('"');
var value = new StringBuilder(); var value = new StringBuilder();
@@ -695,7 +700,7 @@ public class FluentParser {
Predicate<Character> filter = x -> x != '"' && x != EOL; Predicate<Character> filter = x -> x != '"' && x != EOL;
Optional<Character> opt; Optional<Character> opt;
while ((opt = ps.takeChar(filter)).isPresent()) { while ((opt = ps.takeChar(filter)).isPresent()) {
var ch = opt.get(); Character ch = opt.get();
value.append(ch == '\\' ? getEscapeSequence(ps) : ch); value.append(ch == '\\' ? getEscapeSequence(ps) : ch);
} }
@@ -714,12 +719,12 @@ public class FluentParser {
} }
private String getEscapeSequence(FluentStream ps) { private String getEscapeSequence(FluentStream ps) {
var nextOpt = ps.currentChar(); Optional<Character> nextOpt = ps.currentChar();
if (nextOpt.isEmpty()) { if (nextOpt.isEmpty()) {
throw new ParseException(E0025, (Character) null); throw new ParseException(E0025, (Character) null);
} }
var next = nextOpt.get(); Character next = nextOpt.get();
return switch (next) { return switch (next) {
case '\\', '"' -> { case '\\', '"' -> {
ps.next(); ps.next();
@@ -736,7 +741,7 @@ public class FluentParser {
var sequence = new StringBuilder(); var sequence = new StringBuilder();
for (int i = 0; i < digits; i++) { for (int i = 0; i < digits; i++) {
var opt = ps.takeHexDigit(); Optional<Character> opt = ps.takeHexDigit();
if (opt.isEmpty()) { if (opt.isEmpty()) {
throw new ParseException(E0026, "\\%s%s%s".formatted(u, sequence, ps.currentChar().orElseThrow())); throw new ParseException(E0026, "\\%s%s%s".formatted(u, sequence, ps.currentChar().orElseThrow()));
} }
@@ -747,7 +752,7 @@ public class FluentParser {
} }
private NumberLiteral getNumber(FluentStream ps) { private NumberLiteral getNumber(FluentStream ps) {
var spanStart = ps.index; int spanStart = ps.index;
var value = new StringBuilder(); var value = new StringBuilder();
if (ps.currentChar().filter(v -> v == '-').isPresent()) { if (ps.currentChar().filter(v -> v == '-').isPresent()) {
@@ -794,7 +799,7 @@ public class FluentParser {
private Identifier getIdentifier(FluentStream ps) { private Identifier getIdentifier(FluentStream ps) {
int spanStart = ps.index; int spanStart = ps.index;
var name = new StringBuilder().append(ps.takeIDStart()); StringBuilder name = new StringBuilder().append(ps.takeIDStart());
Optional<Character> opt; Optional<Character> opt;
while ((opt = ps.takeIDChar()).isPresent()) { while ((opt = ps.takeIDChar()).isPresent()) {
name.append(opt.get()); name.append(opt.get());
@@ -816,11 +821,11 @@ public class FluentParser {
final int GROUP_COMMENT = 1; final int GROUP_COMMENT = 1;
final int RESOURCE_COMMENT = 2; final int RESOURCE_COMMENT = 2;
var level = ANY; int level = ANY;
var content = new StringBuilder(); var content = new StringBuilder();
while (true) { while (true) {
var i = -1; int i = -1;
int thisLevel; int thisLevel;
if (level == ANY) { if (level == ANY) {

View File

@@ -1,12 +1,12 @@
package ru.di9.fluent.syntax.parser; package ru.di9.fluent.syntax.parser;
import ru.di9.fluent.syntax.MathUtils; import ru.di9.fluent.syntax.utils.MathUtils;
import java.util.Optional; import java.util.Optional;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static ru.di9.fluent.syntax.StringUtils.*; import static ru.di9.fluent.syntax.utils.StringUtils.*;
import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.E0003; import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.E0003;
import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.E0004; import static ru.di9.fluent.syntax.parser.ParseException.ErrorCode.E0004;
@@ -29,17 +29,17 @@ public class FluentStream extends ParserStream {
} }
public String skipBlankInline() { public String skipBlankInline() {
var blank = peekBlankInline(); String blank = peekBlankInline();
skipToPeek(); skipToPeek();
return blank; return blank;
} }
public String peekBlankBlock() { public String peekBlankBlock() {
var blank = new StringBuilder(); StringBuilder blank = new StringBuilder();
while (true) { while (true) {
var lineStart = peekOffset; int lineStart = peekOffset;
peekBlankInline(); peekBlankInline();
var currentPeek = currentPeek(); Optional<Character> currentPeek = currentPeek();
if (currentPeek.filter(v -> v == EOL).isPresent()) { if (currentPeek.filter(v -> v == EOL).isPresent()) {
blank.append(EOL); blank.append(EOL);
peek(); peek();
@@ -56,7 +56,7 @@ public class FluentStream extends ParserStream {
} }
public String skipBlankBlock() { public String skipBlankBlock() {
var blank = peekBlankBlock(); String blank = peekBlankBlock();
skipToPeek(); skipToPeek();
return blank; return blank;
} }
@@ -79,7 +79,7 @@ public class FluentStream extends ParserStream {
} }
public void expectLineEnd() { public void expectLineEnd() {
var opt = currentChar(); Optional<Character> opt = currentChar();
if (opt.isEmpty()/*EOF*/) { if (opt.isEmpty()/*EOF*/) {
// EOF is a valid line end in Fluent. // EOF is a valid line end in Fluent.
@@ -97,7 +97,7 @@ public class FluentStream extends ParserStream {
} }
public Optional<Character> takeChar(Predicate<Character> f) { public Optional<Character> takeChar(Predicate<Character> f) {
var ch = currentChar().filter(f); Optional<Character> ch = currentChar().filter(f);
ch.ifPresent(x -> next()); ch.ifPresent(x -> next());
return ch; return ch;
} }
@@ -107,7 +107,7 @@ public class FluentStream extends ParserStream {
} }
public boolean isNumberStart() { public boolean isNumberStart() {
var opt = currentChar(); Optional<Character> opt = currentChar();
if (opt.filter(v -> v == '-').isPresent()) { if (opt.filter(v -> v == '-').isPresent()) {
opt = peek(); opt = peek();
} }
@@ -123,7 +123,7 @@ public class FluentStream extends ParserStream {
} }
public boolean isValueContinuation() { public boolean isValueContinuation() {
var column1 = peekOffset; int column1 = peekOffset;
peekBlankInline(); peekBlankInline();
if (currentPeek().filter(v -> v == '{').isPresent()) { if (currentPeek().filter(v -> v == '{').isPresent()) {
@@ -156,7 +156,7 @@ public class FluentStream extends ParserStream {
} }
int lvl = MathUtils.clamp(level, -1, 2); int lvl = MathUtils.clamp(level, -1, 2);
var i = 0; int i = 0;
while (i <= lvl || (lvl == -1 && i < 3)) { while (i <= lvl || (lvl == -1 && i < 3)) {
if (peek().filter(v -> v != '#').isPresent()) { if (peek().filter(v -> v != '#').isPresent()) {
if (i <= lvl && lvl != -1) { if (i <= lvl && lvl != -1) {
@@ -175,7 +175,7 @@ public class FluentStream extends ParserStream {
} }
public boolean isVariantStart() { public boolean isVariantStart() {
var currentPeekOffset = peekOffset; int currentPeekOffset = peekOffset;
if (currentPeek().filter(v -> v == '*').isPresent()) { if (currentPeek().filter(v -> v == '*').isPresent()) {
peek(); peek();
} }
@@ -190,7 +190,7 @@ public class FluentStream extends ParserStream {
} }
public void skipToNextEntryStart(int junkStart) { public void skipToNextEntryStart(int junkStart) {
var lastNewline = string.lastIndexOf(EOL, index); int lastNewline = string.lastIndexOf(EOL, index);
if (junkStart < lastNewline) { if (junkStart < lastNewline) {
// Last seen newline is _after_ the junk start. It's safe to rewind // Last seen newline is _after_ the junk start. It's safe to rewind
// without the risk of resuming at the same broken entry. // without the risk of resuming at the same broken entry.
@@ -205,7 +205,7 @@ public class FluentStream extends ParserStream {
} }
// Break if the first char in this line looks like an entry start. // Break if the first char in this line looks like an entry start.
var firstOpt = next(); Optional<Character> firstOpt = next();
if (firstOpt.isEmpty()/*EOF*/) { if (firstOpt.isEmpty()/*EOF*/) {
continue; continue;
} }
@@ -219,7 +219,7 @@ public class FluentStream extends ParserStream {
public char takeIDStart() { public char takeIDStart() {
if (currentChar().map(this::isCharIdStart).orElse(false)) { if (currentChar().map(this::isCharIdStart).orElse(false)) {
var ret = currentChar(); Optional<Character> ret = currentChar();
if (ret.isPresent()) { if (ret.isPresent()) {
next(); next();
return ret.get(); return ret.get();

View File

@@ -2,7 +2,7 @@ package ru.di9.fluent.syntax.parser;
import java.util.Optional; import java.util.Optional;
import static ru.di9.fluent.syntax.StringUtils.getCharAt; import static ru.di9.fluent.syntax.utils.StringUtils.getCharAt;
public class ParserStream { public class ParserStream {
@@ -71,9 +71,10 @@ public class ParserStream {
// beginning of the compound CRLF sequence. This ensures slices of // beginning of the compound CRLF sequence. This ensures slices of
// [inclusive, exclusive) continue to work properly. // [inclusive, exclusive) continue to work properly.
var ch = getCharAt(string, offset); Optional<Character> ch = getCharAt(string, offset);
var opt = ch.filter(v -> v == '\r') Optional<Character> opt = ch.filter(v -> v == '\r')
.flatMap(x -> getCharAt(string, offset + 1).filter(v -> v == '\n')); .flatMap(x -> getCharAt(string, offset + 1)
.filter(v -> v == '\n'));
return opt.isPresent() ? opt : ch; return opt.isPresent() ? opt : ch;
} }

View File

@@ -17,33 +17,25 @@ public class FluentSerializer {
} }
public String serialize(TopLevel topLevel) { public String serialize(TopLevel topLevel) {
if (topLevel instanceof Entry entry) { return switch (topLevel) {
return serializeEntry(entry); case Entry entry -> serializeEntry(entry);
} else if (topLevel instanceof Whitespace whitespace) { case Whitespace whitespace -> whitespace.getContent();
return whitespace.getContent(); case Junk junk -> withJunk ? junk.getContent() : "";
} else if (topLevel instanceof Junk junk) { default -> throw new SerializeException("Unknown top-level entry type '%s'".formatted(topLevel.getClass()));
return withJunk ? junk.getContent() : ""; };
} else {
throw new SerializeException("Unknown top-level entry type '%s'".formatted(topLevel.getClass()));
}
} }
/// PRIVATE //////////////////////////////////////////////////////////////////////////////////////////////////////// /// PRIVATE ////////////////////////////////////////////////////////////////////////////////////////////////////////
private String serializeEntry(Entry entry) { private String serializeEntry(Entry entry) {
if (entry instanceof Message message) { return switch (entry) {
return serializeMessage(message); case Message message -> serializeMessage(message);
} else if (entry instanceof Term term) { case Term term -> serializeTerm(term);
return serializeTerm(term); case Comment comment -> serializeComment(comment, "#");
} else if (entry instanceof Comment comment) { case GroupComment comment -> serializeComment(comment, "##");
return serializeComment(comment, "#"); case ResourceComment comment -> serializeComment(comment, "###");
} else if (entry instanceof GroupComment comment) { default -> throw new SerializeException("Unknown entry type '%s'".formatted(entry.getClass()));
return serializeComment(comment, "##"); };
} else if (entry instanceof ResourceComment comment) {
return serializeComment(comment, "###");
} else {
throw new SerializeException("Unknown entry type '%s'".formatted(entry.getClass()));
}
} }
private String serializeMessage(Message message) { private String serializeMessage(Message message) {
@@ -129,51 +121,47 @@ public class FluentSerializer {
private String serializePlaceable(Placeable placeable) { private String serializePlaceable(Placeable placeable) {
InsidePlaceable expression = placeable.getExpression(); InsidePlaceable expression = placeable.getExpression();
if (expression instanceof Placeable placeable1) { return switch (expression) {
return "{" + serializePlaceable(placeable1) + "}"; case Placeable placeable1 -> "{" + serializePlaceable(placeable1) + "}";
} else if (expression instanceof SelectExpression selectExpression) { case SelectExpression selectExpression -> "{ " + serializeExpression(selectExpression) + "}";
return "{ " + serializeExpression(selectExpression) + "}"; case Expression expression1 -> "{ " + serializeExpression(expression1) + " }";
} else if (expression instanceof Expression expression1) { default -> throw new SerializeException("Unknown placeable type '%s'".formatted(expression.getClass()));
return "{ " + serializeExpression(expression1) + " }"; };
} else {
throw new SerializeException("Unknown placeable type '%s'".formatted(expression.getClass()));
}
} }
private String serializeExpression(Expression expression) { private String serializeExpression(Expression expression) {
var builder = new StringBuilder(); var builder = new StringBuilder();
if (expression instanceof StringLiteral stringLiteral) { switch (expression) {
builder.append('"').append(stringLiteral.getValue()).append('"'); case StringLiteral stringLiteral -> builder.append('"').append(stringLiteral.getValue()).append('"');
} else if (expression instanceof NumberLiteral numberLiteral) { case NumberLiteral numberLiteral -> builder.append(numberLiteral.getValue());
builder.append(numberLiteral.getValue()); case VariableReference variableReference -> builder.append('$').append(variableReference.getId().getName());
} else if (expression instanceof VariableReference variableReference) { case TermReference termReference -> {
builder.append('$').append(variableReference.getId().getName()); builder.append('-').append(termReference.getId().getName());
} else if (expression instanceof TermReference termReference) {
builder.append('-').append(termReference.getId().getName());
termReference.getAttribute().ifPresent(attribute -> termReference.getAttribute().ifPresent(attribute ->
builder.append('.').append(attribute.getName())); builder.append('.').append(attribute.getName()));
termReference.getArguments().ifPresent(arguments -> termReference.getArguments().ifPresent(arguments ->
builder.append(serializeCallArguments(arguments))); builder.append(serializeCallArguments(arguments)));
} else if (expression instanceof MessageReference messageReference) { }
builder.append(messageReference.getId().getName()); case MessageReference messageReference -> {
builder.append(messageReference.getId().getName());
messageReference.getAttribute().ifPresent(attribute -> messageReference.getAttribute().ifPresent(attribute ->
builder.append('.').append(attribute.getName())); builder.append('.').append(attribute.getName()));
} else if (expression instanceof FunctionReference functionReference) { }
builder case FunctionReference functionReference -> builder
.append(functionReference.getId().getName()) .append(functionReference.getId().getName())
.append(serializeCallArguments(functionReference.getArguments())); .append(serializeCallArguments(functionReference.getArguments()));
} else if (expression instanceof SelectExpression selectExpression) { case SelectExpression selectExpression -> {
builder.append(serializeExpression(selectExpression.getSelector())).append(" ->"); builder.append(serializeExpression(selectExpression.getSelector())).append(" ->");
for (Variant variant : selectExpression.getVariants()) { for (Variant variant : selectExpression.getVariants()) {
builder.append(serializeVariant(variant)); builder.append(serializeVariant(variant));
}
builder.append('\n');
} }
builder.append('\n'); default -> throw new SerializeException("Unknown expression type '%s".formatted(expression.getClass()));
} else {
throw new SerializeException("Unknown expression type '%s".formatted(expression.getClass()));
} }
return builder.toString(); return builder.toString();
@@ -185,7 +173,7 @@ public class FluentSerializer {
var builder = new StringBuilder("("); var builder = new StringBuilder("(");
if (hasPositional) { if (hasPositional) {
var positional = callArguments.getPositional() String positional = callArguments.getPositional()
.stream() .stream()
.map(this::serializeExpression) .map(this::serializeExpression)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
@@ -193,7 +181,7 @@ public class FluentSerializer {
} }
if (hasNamed) { if (hasNamed) {
var named = callArguments.getNamed() String named = callArguments.getNamed()
.stream() .stream()
.map(this::serializeNamedArgument) .map(this::serializeNamedArgument)
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
@@ -209,8 +197,8 @@ public class FluentSerializer {
} }
private String serializeVariant(Variant variant) { private String serializeVariant(Variant variant) {
var key = serializeVariantKey(variant.getKey()); String key = serializeVariantKey(variant.getKey());
var value = serializePattern(variant.getValue()).replaceAll("\n", "\n "); String value = serializePattern(variant.getValue()).replaceAll("\n", "\n ");
var builder = new StringBuilder("\n "); var builder = new StringBuilder("\n ");
if (variant.isDefault()) { if (variant.isDefault()) {
@@ -240,7 +228,7 @@ public class FluentSerializer {
boolean isMultiline = pattern.getElements().stream().anyMatch(it -> isSelectExpr(it) || includesLine(it)); boolean isMultiline = pattern.getElements().stream().anyMatch(it -> isSelectExpr(it) || includesLine(it));
if (isMultiline) { if (isMultiline) {
if (!pattern.getElements().isEmpty()) { if (!pattern.getElements().isEmpty()) {
var firstElement = pattern.getElements().get(0); PatternElement firstElement = pattern.getElements().getFirst();
if (firstElement instanceof TextElement te) { if (firstElement instanceof TextElement te) {
if (!te.getValue().isEmpty()) { if (!te.getValue().isEmpty()) {
char firstChar = te.getValue().charAt(0); char firstChar = te.getValue().charAt(0);

View File

@@ -1,4 +1,4 @@
package ru.di9.fluent.syntax; package ru.di9.fluent.syntax.utils;
public interface MathUtils { public interface MathUtils {
static int clamp(int value, int min, int max) { static int clamp(int value, int min, int max) {

View File

@@ -1,4 +1,4 @@
package ru.di9.fluent.syntax; package ru.di9.fluent.syntax.utils;
import java.util.Optional; import java.util.Optional;

View File

@@ -2,8 +2,7 @@ package ru.di9.fluent.syntax.ast;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
class BaseNodeTest { class BaseNodeTest {
@@ -13,12 +12,11 @@ class BaseNodeTest {
var m11 = new Message(new Identifier("test-id"), new Pattern(new TextElement("localized"))); var m11 = new Message(new Identifier("test-id"), new Pattern(new TextElement("localized")));
var m2 = new Message(new Identifier("test-id"), new Pattern(new TextElement("different"))); var m2 = new Message(new Identifier("test-id"), new Pattern(new TextElement("different")));
assertEquals(m1, m11); assertThat(m1)
assertNotEquals(m1, m2); .isEqualTo(m11)
assertEquals(m1.getId(), m2.getId()); .isNotEqualTo(m2);
assertNotEquals(m1.getValue(), m2.getValue());
//Шта? assertThat(m1.getId()).isEqualTo(m2.getId());
assertNotEquals(m1, null); assertThat(m1.getValue()).isNotEqualTo(m2.getValue());
assertNotEquals(null, m1);
} }
} }

View File

@@ -2,7 +2,8 @@ package ru.di9.fluent.syntax.parser;
import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory; import org.junit.jupiter.api.TestFactory;
import ru.di9.fluent.test.utils.AstAssert; import ru.di9.fluent.syntax.ast.Resource;
import ru.di9.fluent.syntax.test.utils.AstAssert;
import ru.di9.fluent.test.utils.Tuple3; import ru.di9.fluent.test.utils.Tuple3;
import java.io.IOException; import java.io.IOException;
@@ -43,7 +44,7 @@ public abstract class AbstractFixturesTest {
var parser = new FluentParser(); var parser = new FluentParser();
parser.withSpans = isWithSpans(tuple.value1()); parser.withSpans = isWithSpans(tuple.value1());
parser.withJunkAnnotations = isWithJunkAnnotations(tuple.value1()); parser.withJunkAnnotations = isWithJunkAnnotations(tuple.value1());
var resource = parser.parse(tuple.value2()); Resource resource = parser.parse(tuple.value2());
AstAssert.assertThat(resource) AstAssert.assertThat(resource)
.isEqualAstJson(tuple.value3()); .isEqualAstJson(tuple.value3());

View File

@@ -42,7 +42,7 @@ class FluentStreamTest {
assertThatNoException().isThrownBy(ps2::expectLineEnd); assertThatNoException().isThrownBy(ps2::expectLineEnd);
var ps3 = new FluentStream(" "); var ps3 = new FluentStream(" ");
var catchException = catchThrowableOfType(ps3::expectLineEnd, ParseException.class); ParseException catchException = catchThrowableOfType(ps3::expectLineEnd, ParseException.class);
assertThat(catchException.getCode()).isEqualTo(E0003); assertThat(catchException.getCode()).isEqualTo(E0003);
} }
@@ -50,7 +50,7 @@ class FluentStreamTest {
void testExpectChar() { void testExpectChar() {
var ps = new FluentStream("z"); var ps = new FluentStream("z");
assertThatNoException().isThrownBy(() -> ps.expectChar('z')); assertThatNoException().isThrownBy(() -> ps.expectChar('z'));
var catchException = catchThrowableOfType(() -> ps.expectChar('a'), ParseException.class); ParseException catchException = catchThrowableOfType(() -> ps.expectChar('a'), ParseException.class);
assertThat(catchException.getCode()).isEqualTo(E0003); assertThat(catchException.getCode()).isEqualTo(E0003);
} }

View File

@@ -1,6 +1,7 @@
package ru.di9.fluent.syntax.serializer; package ru.di9.fluent.syntax.serializer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import ru.di9.fluent.syntax.ast.TopLevel;
import ru.di9.fluent.syntax.parser.FluentParser; import ru.di9.fluent.syntax.parser.FluentParser;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@@ -14,10 +15,10 @@ class SerializeEntryTest {
key = Value"""; key = Value""";
var parser = new FluentParser(); var parser = new FluentParser();
var topLevel = parser.parse(input).getBody().get(0); TopLevel topLevel = parser.parse(input).getBody().getFirst();
var serializer = new FluentSerializer(); var serializer = new FluentSerializer();
var serialized = serializer.serialize(topLevel).trim(); String serialized = serializer.serialize(topLevel).trim();
assertThat(serialized).isEqualTo(input); assertThat(serialized).isEqualTo(input);
} }

View File

@@ -1,4 +1,4 @@
package ru.di9.fluent.test.utils; package ru.di9.fluent.syntax.test.utils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@@ -7,6 +7,8 @@ import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode; import org.skyscreamer.jsonassert.JSONCompareMode;
import ru.di9.fluent.syntax.ast.BaseNode; import ru.di9.fluent.syntax.ast.BaseNode;
import ru.di9.fluent.test.utils.BaseNodeJsonSerializer;
import ru.di9.fluent.test.utils.NullIgnoreComparator;
import java.io.PrintStream; import java.io.PrintStream;
@@ -26,13 +28,13 @@ public class AstAssert extends AbstractAssert<AstAssert, BaseNode> {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public AstAssert printAstJson(PrintStream printStream) { public AstAssert printAstJson(PrintStream printStream) {
var json = gson.toJson(actual); String json = gson.toJson(actual);
printStream.println(json); printStream.println(json);
return this; return this;
} }
public AstAssert isEqualAstJson(String astJson) { public AstAssert isEqualAstJson(String astJson) {
var actualJson = gson.toJson(actual); String actualJson = gson.toJson(actual);
try { try {
JSONAssert.assertEquals(astJson, actualJson, nullIgnoreComparator); JSONAssert.assertEquals(astJson, actualJson, nullIgnoreComparator);

View File

@@ -1,4 +1,4 @@
package ru.di9.fluent.syntax; package ru.di9.fluent.syntax.utils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@@ -1,4 +1,4 @@
package ru.di9.fluent.syntax; package ru.di9.fluent.syntax.utils;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@@ -3,10 +3,7 @@ package ru.di9.fluent.syntax.visitor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import ru.di9.fluent.syntax.ast.Identifier; import ru.di9.fluent.syntax.ast.*;
import ru.di9.fluent.syntax.ast.Pattern;
import ru.di9.fluent.syntax.ast.TextElement;
import ru.di9.fluent.syntax.ast.Variant;
import ru.di9.fluent.syntax.parser.FluentParser; import ru.di9.fluent.syntax.parser.FluentParser;
import java.util.Iterator; import java.util.Iterator;
@@ -29,7 +26,7 @@ class VisitorTest {
msg = foo {$var -> msg = foo {$var ->
*[other] bar *[other] bar
} baz"""; } baz""";
var res = parser.parse(source); Resource res = parser.parse(source);
visitor.visit(res); visitor.visit(res);
assertEquals(3, visitor.wordCount); assertEquals(3, visitor.wordCount);
assertEquals(2, visitor.patternCount); assertEquals(2, visitor.patternCount);

View File

@@ -16,7 +16,7 @@ public class BaseNodeJsonSerializer implements JsonSerializer<BaseNode> {
var root = new JsonObject(); var root = new JsonObject();
root.add("type", new JsonPrimitive(baseNode.getClass().getSimpleName())); root.add("type", new JsonPrimitive(baseNode.getClass().getSimpleName()));
var fields = Reflect.on(baseNode).fields(); Map<String, Reflect> fields = Reflect.on(baseNode).fields();
for (Map.Entry<String, Reflect> field : fields.entrySet()) { for (Map.Entry<String, Reflect> field : fields.entrySet()) {
String name = field.getKey().equalsIgnoreCase("isDefault") ? "default" : field.getKey(); String name = field.getKey().equalsIgnoreCase("isDefault") ? "default" : field.getKey();

View File

@@ -5,13 +5,13 @@ import java.nio.file.Path;
public interface FileUtils { public interface FileUtils {
static String getExt(String fileName) { static String getExt(String fileName) {
var idx = fileName.lastIndexOf('.'); int idx = fileName.lastIndexOf('.');
if (idx < 0) return ""; if (idx < 0) return "";
return fileName.substring(idx + 1); return fileName.substring(idx + 1);
} }
static String getName(String fileName) { static String getName(String fileName) {
var idx = fileName.lastIndexOf('.'); int idx = fileName.lastIndexOf('.');
if (idx < 0) return fileName; if (idx < 0) return fileName;
return fileName.substring(0, idx); return fileName.substring(0, idx);
} }