Skip to content

Commit 1bd08b4

Browse files
authored
Add Support for Char Literals (#153)
1 parent 556cfa6 commit 1bd08b4

File tree

11 files changed

+142
-4
lines changed

11 files changed

+142
-4
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package testSuite;
2+
3+
import liquidjava.specification.Refinement;
4+
5+
@SuppressWarnings("unused")
6+
public class CorrectChars {
7+
8+
@Refinement("_ == 65")
9+
int getA() {
10+
return 'A';
11+
}
12+
13+
void test() {
14+
printLetter('A');
15+
printLetter('Z');
16+
printLetter('a');
17+
printLetter('z');
18+
}
19+
20+
void printLetter(@Refinement("_ >= 65 && _ <= 90 || _ >= 97 && _ <= 122") char c) {
21+
System.out.println(c);
22+
}
23+
24+
void test2() {
25+
@Refinement("_ == 97")
26+
char c = 'a';
27+
28+
@Refinement("_ == 98")
29+
int res = inc(c);
30+
}
31+
32+
@Refinement("_ == x + 1")
33+
int inc(int x) {
34+
return x + 1;
35+
}
36+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Refinement Error
2+
package testSuite;
3+
4+
import liquidjava.specification.Refinement;
5+
6+
public class ErrorChars {
7+
8+
void test() {
9+
printLetter('$'); // error
10+
}
11+
12+
void printLetter(@Refinement("_ >= 65 && _ <= 90 || _ >= 97 && _ <= 122") char c) {
13+
System.out.println(c);
14+
}
15+
}

liquidjava-verifier/src/main/java/liquidjava/processor/refinement_checker/object_checkers/AuxStateHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public static void setDefaultState(RefinedFunction f, TypeChecker tc) {
9494
String retType = sg.getReturnType().toString();
9595
Predicate typePredicate = switch (retType) {
9696
case "int" -> Predicate.createLit("0", Types.INT);
97+
case "char" -> Predicate.createLit("\u0000", Types.CHAR);
9798
case "boolean" -> Predicate.createLit("false", Types.BOOLEAN);
9899
case "double" -> Predicate.createLit("0.0", Types.DOUBLE);
99100
default -> throw new RuntimeException("Ghost not implemented for type " + retType);

liquidjava-verifier/src/main/java/liquidjava/rj_language/Predicate.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import liquidjava.rj_language.ast.GroupExpression;
2121
import liquidjava.rj_language.ast.Ite;
2222
import liquidjava.rj_language.ast.LiteralBoolean;
23+
import liquidjava.rj_language.ast.LiteralChar;
2324
import liquidjava.rj_language.ast.LiteralInt;
2425
import liquidjava.rj_language.ast.LiteralLong;
2526
import liquidjava.rj_language.ast.LiteralReal;
@@ -236,6 +237,7 @@ public static Predicate createLit(String value, String type) {
236237
case Types.INT, Types.SHORT -> new LiteralInt(value);
237238
case Types.LONG -> new LiteralLong(value);
238239
case Types.DOUBLE, Types.FLOAT -> new LiteralReal(value);
240+
case Types.CHAR -> new LiteralChar(value);
239241
default -> throw new IllegalArgumentException("Unsupported literal type: " + type);
240242
};
241243
return new Predicate(exp);

liquidjava-verifier/src/main/java/liquidjava/rj_language/ast/Expression.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void setChild(int index, Expression element) {
5656

5757
public boolean isLiteral() {
5858
return this instanceof LiteralInt || this instanceof LiteralLong || this instanceof LiteralReal
59-
|| this instanceof LiteralBoolean;
59+
|| this instanceof LiteralBoolean || this instanceof LiteralChar;
6060
}
6161

6262
/**
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package liquidjava.rj_language.ast;
2+
3+
import java.util.List;
4+
5+
import liquidjava.diagnostics.errors.LJError;
6+
import liquidjava.rj_language.visitors.ExpressionVisitor;
7+
8+
public class LiteralChar extends Expression {
9+
10+
private final char value;
11+
12+
public LiteralChar(char value) {
13+
this.value = value;
14+
}
15+
16+
public LiteralChar(String value) {
17+
this.value = value.charAt(0);
18+
}
19+
20+
@Override
21+
public <T> T accept(ExpressionVisitor<T> visitor) throws LJError {
22+
return visitor.visitLiteralChar(this);
23+
}
24+
25+
public char getValue() {
26+
return value;
27+
}
28+
29+
@Override
30+
public String toString() {
31+
return "'" + value + "'";
32+
}
33+
34+
@Override
35+
public void getVariableNames(List<String> toAdd) {
36+
// end leaf
37+
}
38+
39+
@Override
40+
public void getStateInvocations(List<String> toAdd, List<String> all) {
41+
// end leaf
42+
}
43+
44+
@Override
45+
public Expression clone() {
46+
return new LiteralChar(value);
47+
}
48+
49+
@Override
50+
public boolean isBooleanTrue() {
51+
return false;
52+
}
53+
54+
@Override
55+
public int hashCode() {
56+
return Character.hashCode(value);
57+
}
58+
59+
@Override
60+
public boolean equals(Object obj) {
61+
if (this == obj)
62+
return true;
63+
if (obj == null)
64+
return false;
65+
if (getClass() != obj.getClass())
66+
return false;
67+
LiteralChar other = (LiteralChar) obj;
68+
return value == other.value;
69+
}
70+
}

liquidjava-verifier/src/main/java/liquidjava/rj_language/ast/typing/TypeInfer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import liquidjava.rj_language.ast.GroupExpression;
1111
import liquidjava.rj_language.ast.Ite;
1212
import liquidjava.rj_language.ast.LiteralBoolean;
13+
import liquidjava.rj_language.ast.LiteralChar;
1314
import liquidjava.rj_language.ast.LiteralInt;
1415
import liquidjava.rj_language.ast.LiteralLong;
1516
import liquidjava.rj_language.ast.LiteralReal;
@@ -38,6 +39,8 @@ else if (e instanceof LiteralLong)
3839
return Optional.of(Utils.getType("long", factory));
3940
else if (e instanceof LiteralReal)
4041
return Optional.of(Utils.getType("double", factory));
42+
else if (e instanceof LiteralChar)
43+
return Optional.of(Utils.getType("char", factory));
4144
else if (e instanceof LiteralBoolean)
4245
return boolType(factory);
4346
else if (e instanceof Var)

liquidjava-verifier/src/main/java/liquidjava/rj_language/visitors/ExpressionVisitor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import liquidjava.rj_language.ast.GroupExpression;
88
import liquidjava.rj_language.ast.Ite;
99
import liquidjava.rj_language.ast.LiteralBoolean;
10+
import liquidjava.rj_language.ast.LiteralChar;
1011
import liquidjava.rj_language.ast.LiteralInt;
1112
import liquidjava.rj_language.ast.LiteralLong;
1213
import liquidjava.rj_language.ast.LiteralReal;
@@ -31,6 +32,8 @@ public interface ExpressionVisitor<T> {
3132

3233
T visitLiteralBoolean(LiteralBoolean lit) throws LJError;
3334

35+
T visitLiteralChar(LiteralChar lit) throws LJError;
36+
3437
T visitLiteralReal(LiteralReal lit) throws LJError;
3538

3639
T visitLiteralString(LiteralString lit) throws LJError;

liquidjava-verifier/src/main/java/liquidjava/smt/ExpressionToZ3Visitor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import liquidjava.rj_language.ast.GroupExpression;
1010
import liquidjava.rj_language.ast.Ite;
1111
import liquidjava.rj_language.ast.LiteralBoolean;
12+
import liquidjava.rj_language.ast.LiteralChar;
1213
import liquidjava.rj_language.ast.LiteralInt;
1314
import liquidjava.rj_language.ast.LiteralLong;
1415
import liquidjava.rj_language.ast.LiteralReal;
@@ -96,6 +97,11 @@ public Expr<?> visitLiteralBoolean(LiteralBoolean lit) {
9697
return ctx.makeBooleanLiteral(lit.isBooleanTrue());
9798
}
9899

100+
@Override
101+
public Expr<?> visitLiteralChar(LiteralChar lit) {
102+
return ctx.makeIntegerLiteral(lit.getValue());
103+
}
104+
99105
@Override
100106
public Expr<?> visitLiteralReal(LiteralReal lit) {
101107
return ctx.makeDoubleLiteral(lit.getValue());

liquidjava-verifier/src/main/java/liquidjava/smt/TranslatorContextToZ3.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public static void storeVariablesSubtypes(Context z3, List<RefinedVariable> vari
4242
private static Expr<?> getExpr(Context z3, String name, CtTypeReference<?> type) {
4343
String typeName = type.getQualifiedName();
4444
return switch (typeName) {
45-
case "int", "short", "java.lang.Integer", "java.lang.Short" -> z3.mkIntConst(name);
45+
case "int", "short", "char", "java.lang.Integer", "java.lang.Short", "java.lang.Character" -> z3
46+
.mkIntConst(name);
4647
case "boolean", "java.lang.Boolean" -> z3.mkBoolConst(name);
4748
case "long", "java.lang.Long" -> z3.mkRealConst(name);
4849
case "float", "double", "java.lang.Float", "java.lang.Double" -> (FPExpr) z3.mkConst(name, z3.mkFPSort64());
@@ -81,7 +82,7 @@ private static void addBuiltinFunctions(Context z3, Map<String, FuncDecl<?>> fun
8182

8283
static Sort getSort(Context z3, String sort) {
8384
return switch (sort) {
84-
case "int", "short", "java.lang.Integer", "java.lang.Short" -> z3.getIntSort();
85+
case "int", "short", "char", "java.lang.Integer", "java.lang.Short", "java.lang.Character" -> z3.getIntSort();
8586
case "boolean", "java.lang.Boolean" -> z3.getBoolSort();
8687
case "long", "java.lang.Long" -> z3.getRealSort();
8788
case "float", "java.lang.Float" -> z3.mkFPSort32();

0 commit comments

Comments
 (0)