Skip to content

Commit a5ffc31

Browse files
Add hint for implies (using --> and not ->)
1 parent 447b469 commit a5ffc31

4 files changed

Lines changed: 72 additions & 29 deletions

File tree

liquidjava-verifier/src/main/java/liquidjava/rj_language/opt/VariablePropagation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class VariablePropagation {
2323
*/
2424
public static ValDerivationNode propagate(Expression exp, ValDerivationNode previousOrigin) {
2525
Map<String, Expression> substitutions = VariableResolver.resolve(exp);
26-
Map<String, Expression> directSubstitutions = new HashMap<>(); // var == literal or var == var
26+
Map<String, Expression> directSubstitutions = new HashMap<>(); // var == literal or var == var
2727
Map<String, Expression> expressionSubstitutions = new HashMap<>(); // var == expression
2828
for (Map.Entry<String, Expression> entry : substitutions.entrySet()) {
2929
Expression value = entry.getValue();

liquidjava-verifier/src/main/java/liquidjava/rj_language/parsing/RJErrorListener.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@
88
import org.antlr.v4.runtime.Parser;
99
import org.antlr.v4.runtime.RecognitionException;
1010
import org.antlr.v4.runtime.Recognizer;
11+
import org.antlr.v4.runtime.Token;
1112
import org.antlr.v4.runtime.atn.ATNConfigSet;
1213
import org.antlr.v4.runtime.dfa.DFA;
1314

1415
public class RJErrorListener implements ANTLRErrorListener {
1516

1617
private int errors;
1718
public List<String> msgs;
19+
private String hint;
1820

1921
public RJErrorListener() {
2022
super();
@@ -25,16 +27,30 @@ public RJErrorListener() {
2527
@Override
2628
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
2729
String msg, RecognitionException e) {
28-
// Hint for == instead of =
29-
String hint = null;
30-
if (e instanceof LexerNoViableAltException l) {
31-
char c = l.getInputStream().toString().charAt(charPositionInLine);
32-
if (c == '=')
33-
hint = "Predicates must be compared with == instead of =";
30+
if (hint == null) {
31+
String input = inputFor(offendingSymbol, e);
32+
int pos = charPositionInLine;
33+
if (input != null && pos >= 0 && pos < input.length()) {
34+
char c = input.charAt(pos);
35+
// Hint for == instead of =
36+
if (c == '=')
37+
hint = "Predicates must be compared with == instead of =";
38+
// Hint for -> instead of --> (logical implication)
39+
else if (c == '>' && pos >= 1 && input.charAt(pos - 1) == '-'
40+
&& (pos < 2 || input.charAt(pos - 2) != '-'))
41+
hint = "Logical implication is --> (two dashes), not ->";
42+
}
3443
}
3544
errors++;
36-
String ms = "Error in " + msg + ", in the position " + charPositionInLine;
37-
msgs.add(ms + (hint == null ? "" : "\n\tHint: " + hint));
45+
msgs.add("Error in " + msg + ", in the position " + charPositionInLine);
46+
}
47+
48+
private static String inputFor(Object offendingSymbol, RecognitionException e) {
49+
if (e instanceof LexerNoViableAltException l)
50+
return l.getInputStream().toString();
51+
if (offendingSymbol instanceof Token t && t.getInputStream() != null)
52+
return t.getInputStream().toString();
53+
return null;
3854
}
3955

4056
@Override
@@ -56,6 +72,10 @@ public int getErrors() {
5672
return errors;
5773
}
5874

75+
public String getHint() {
76+
return hint;
77+
}
78+
5979
public String getMessages() {
6080
StringBuilder sb = new StringBuilder();
6181
String pl = errors == 1 ? "" : "s";

liquidjava-verifier/src/main/java/liquidjava/rj_language/parsing/RefinementsParser.java

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package liquidjava.rj_language.parsing;
22

3-
import java.util.Optional;
4-
53
import liquidjava.diagnostics.errors.SyntaxError;
64
import liquidjava.processor.facade.AliasDTO;
75
import liquidjava.processor.facade.GhostDTO;
@@ -56,25 +54,14 @@ public static AliasDTO parseAliasDefinition(String toParse) throws SyntaxError {
5654
* Compiles the given string into a parse tree
5755
*/
5856
private static ParseTree compile(String toParse, String errorMessage) throws SyntaxError {
59-
Optional<String> s = getErrors(toParse);
60-
if (s.isPresent())
61-
throw new SyntaxError(errorMessage, toParse);
62-
63-
RJErrorListener err = new RJErrorListener();
64-
RJParser parser = createParser(toParse, err);
65-
return parser.prog();
66-
}
67-
68-
/**
69-
* Checks if the given string can be parsed without syntax errors, returning the error messages if any
70-
*/
71-
private static Optional<String> getErrors(String toParse) {
7257
RJErrorListener err = new RJErrorListener();
73-
RJParser parser = createParser(toParse, err);
74-
parser.prog(); // all consumed
75-
if (err.getErrors() > 0)
76-
return Optional.of(err.getMessages());
77-
return Optional.empty();
58+
ParseTree tree = createParser(toParse, err).prog();
59+
if (err.getErrors() > 0) {
60+
SyntaxError e = new SyntaxError(errorMessage, toParse);
61+
e.setHint(err.getHint());
62+
throw e;
63+
}
64+
return tree;
7865
}
7966

8067
/**
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package liquidjava.rj_language.parsing;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import liquidjava.diagnostics.errors.SyntaxError;
9+
10+
class RefinementsParserTest {
11+
12+
@Test
13+
void hintsArrowVsImplication() {
14+
SyntaxError e = assertThrows(SyntaxError.class,
15+
() -> RefinementsParser.createAST("_ == true -> markSupported(this)", ""));
16+
assertEquals("Logical implication is --> (two dashes), not ->", e.getDetails());
17+
}
18+
19+
@Test
20+
void hintsAssignVsEquals() {
21+
SyntaxError e = assertThrows(SyntaxError.class, () -> RefinementsParser.createAST("_ = 1", ""));
22+
assertEquals("Predicates must be compared with == instead of =", e.getDetails());
23+
}
24+
25+
@Test
26+
void noHintForValidImplication() {
27+
// double-dash is the real operator — should parse cleanly, no exception
28+
RefinementsParser.createAST("_ == true --> markSupported(this)", "");
29+
}
30+
31+
@Test
32+
void noHintForUnrelatedSyntaxError() {
33+
SyntaxError e = assertThrows(SyntaxError.class, () -> RefinementsParser.createAST("a +", ""));
34+
assertEquals("", e.getDetails());
35+
}
36+
}

0 commit comments

Comments
 (0)