Skip to content

Commit 6b8b334

Browse files
committed
Add More Tests
1 parent 78d70cc commit 6b8b334

3 files changed

Lines changed: 101 additions & 2 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ private static Map<String, Expression> resolveTransitive(Map<String, Expression>
115115
* @return resolved expression
116116
*/
117117
private static Expression lookup(Expression exp, Map<String, Expression> map, Set<String> seen) {
118-
if (!(exp instanceof Var))
118+
String name = substitutionKey(exp);
119+
if (name == null)
119120
return exp;
120121

121-
String name = exp.toString();
122122
if (seen.contains(name))
123123
return exp; // circular reference
124124

liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/ExpressionSimplifierTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,4 +1158,38 @@ void testFunctionInvocationEqualitiesPropagateTransitively() {
11581158

11591159
assertEquals("size(x3) == 0", result.getValue().toString());
11601160
}
1161+
1162+
@Test
1163+
void testFunctionInvocationOnLeftBehavesLikeVariable() {
1164+
// Given: func(a) == func(b) && func(b) == 1
1165+
// Expected: func(a) == 1
1166+
Expression funcA = new FunctionInvocation("func", List.of(new Var("a")));
1167+
Expression funcB = new FunctionInvocation("func", List.of(new Var("b")));
1168+
Expression funcAEqualsFuncB = new BinaryExpression(funcA, "==", funcB);
1169+
Expression funcBEqualsOne = new BinaryExpression(funcB, "==", new LiteralInt(1));
1170+
Expression fullExpression = new BinaryExpression(funcAEqualsFuncB, "&&", funcBEqualsOne);
1171+
1172+
ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression);
1173+
1174+
assertEquals("func(a) == 1", result.getValue().toString());
1175+
}
1176+
1177+
@Test
1178+
void testFunctionInvocationEqualitiesMixWithVariables() {
1179+
// Given: func(a) + x && func(a) == y && y == 1 && x == 2
1180+
// Expected: 3
1181+
Expression funcA = new FunctionInvocation("func", List.of(new Var("a")));
1182+
Expression x = new Var("x");
1183+
Expression y = new Var("y");
1184+
Expression addition = new BinaryExpression(funcA, "+", x);
1185+
Expression funcAEqualsY = new BinaryExpression(funcA, "==", y);
1186+
Expression yEqualsOne = new BinaryExpression(y, "==", new LiteralInt(1));
1187+
Expression xEqualsTwo = new BinaryExpression(x, "==", new LiteralInt(2));
1188+
Expression fullExpression = new BinaryExpression(addition, "&&",
1189+
new BinaryExpression(funcAEqualsY, "&&", new BinaryExpression(yEqualsOne, "&&", xEqualsTwo)));
1190+
1191+
ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression);
1192+
1193+
assertEquals("3", result.getValue().toString());
1194+
}
11611195
}

liquidjava-verifier/src/test/java/liquidjava/rj_language/opt/VariableResolverTest.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import static org.junit.jupiter.api.Assertions.*;
44

5+
import java.util.List;
56
import java.util.Map;
67

78
import org.junit.jupiter.api.Test;
89

910
import liquidjava.rj_language.ast.BinaryExpression;
1011
import liquidjava.rj_language.ast.Expression;
12+
import liquidjava.rj_language.ast.FunctionInvocation;
1113
import liquidjava.rj_language.ast.GroupExpression;
1214
import liquidjava.rj_language.ast.LiteralInt;
1315
import liquidjava.rj_language.ast.UnaryExpression;
@@ -168,4 +170,67 @@ void testFreshVariableIsNotUsedAsSubstitutionTarget() {
168170
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
169171
assertTrue(result.isEmpty(), "Fresh variables should not replace another variable");
170172
}
173+
174+
@Test
175+
void testFunctionInvocationEqualityExtractsFunctionKey() {
176+
// size(stack) > 0 && size(stack) == 1 should extract size(stack) -> 1
177+
Expression stack = new Var("stack");
178+
Expression sizeStack = new FunctionInvocation("size", List.of(stack));
179+
Expression sizeStackGreaterZero = new BinaryExpression(sizeStack, ">", new LiteralInt(0));
180+
Expression sizeStackEqualsOne = new BinaryExpression(sizeStack, "==", new LiteralInt(1));
181+
Expression fullExpr = new BinaryExpression(sizeStackGreaterZero, "&&", sizeStackEqualsOne);
182+
183+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
184+
185+
assertEquals(1, result.size(), "Should extract the function invocation as a substitution key");
186+
assertEquals("1", result.get("size(stack)").toString());
187+
}
188+
189+
@Test
190+
void testLiteralOnLeftExtractsFunctionInvocationKey() {
191+
// size(stack) > 0 && 1 == size(stack) should extract size(stack) -> 1
192+
Expression stack = new Var("stack");
193+
Expression sizeStack = new FunctionInvocation("size", List.of(stack));
194+
Expression sizeStackGreaterZero = new BinaryExpression(sizeStack, ">", new LiteralInt(0));
195+
Expression oneEqualsSizeStack = new BinaryExpression(new LiteralInt(1), "==", sizeStack);
196+
Expression fullExpr = new BinaryExpression(sizeStackGreaterZero, "&&", oneEqualsSizeStack);
197+
198+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
199+
200+
assertEquals(1, result.size(), "Should extract function invocation equalities from either side");
201+
assertEquals("1", result.get("size(stack)").toString());
202+
}
203+
204+
@Test
205+
void testFunctionInvocationEqualitiesResolveTransitively() {
206+
// func(a) > 0 && func(a) == func(b) && func(b) == 1 should extract func(a) -> 1
207+
Expression funcA = new FunctionInvocation("func", List.of(new Var("a")));
208+
Expression funcB = new FunctionInvocation("func", List.of(new Var("b")));
209+
Expression funcAGreaterZero = new BinaryExpression(funcA, ">", new LiteralInt(0));
210+
Expression funcAEqualsFuncB = new BinaryExpression(funcA, "==", funcB);
211+
Expression funcBEqualsOne = new BinaryExpression(funcB, "==", new LiteralInt(1));
212+
Expression fullExpr = new BinaryExpression(funcAGreaterZero, "&&",
213+
new BinaryExpression(funcAEqualsFuncB, "&&", funcBEqualsOne));
214+
215+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
216+
217+
assertEquals(2, result.size(), "Should keep the function invocation chain that was used");
218+
assertEquals("1", result.get("func(a)").toString());
219+
assertEquals("1", result.get("func(b)").toString());
220+
}
221+
222+
@Test
223+
void testUnusedFunctionInvocationEqualityIsIgnored() {
224+
// x > 0 && size(stack) == 1 should not extract size(stack), because it is only defined
225+
Expression x = new Var("x");
226+
Expression stack = new Var("stack");
227+
Expression sizeStack = new FunctionInvocation("size", List.of(stack));
228+
Expression xGreaterZero = new BinaryExpression(x, ">", new LiteralInt(0));
229+
Expression sizeStackEqualsOne = new BinaryExpression(sizeStack, "==", new LiteralInt(1));
230+
Expression fullExpr = new BinaryExpression(xGreaterZero, "&&", sizeStackEqualsOne);
231+
232+
Map<String, Expression> result = VariableResolver.resolve(fullExpr);
233+
234+
assertTrue(result.isEmpty(), "Function invocation definitions with no usage should be ignored");
235+
}
171236
}

0 commit comments

Comments
 (0)