-
Notifications
You must be signed in to change notification settings - Fork 35
Propagate Function Invocations #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
b3c83ce
c7afdde
1516285
9970654
d7dbef0
2072c59
2696792
78d70cc
6b8b334
91d8699
b7c463a
647f13c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
|
|
||
| import liquidjava.rj_language.ast.BinaryExpression; | ||
| import liquidjava.rj_language.ast.Expression; | ||
| import liquidjava.rj_language.ast.FunctionInvocation; | ||
| import liquidjava.rj_language.ast.Var; | ||
|
|
||
| public class VariableResolver { | ||
|
|
@@ -45,33 +46,49 @@ private static void resolveRecursive(Expression exp, Map<String, Expression> map | |
| if ("&&".equals(op)) { | ||
| resolveRecursive(be.getFirstOperand(), map); | ||
| resolveRecursive(be.getSecondOperand(), map); | ||
| } else if ("==".equals(op)) { | ||
| Expression left = be.getFirstOperand(); | ||
| Expression right = be.getSecondOperand(); | ||
| if (left instanceof Var var && right.isLiteral()) { | ||
| map.put(var.getName(), right.clone()); | ||
| } else if (right instanceof Var var && left.isLiteral()) { | ||
| map.put(var.getName(), left.clone()); | ||
| } else if (left instanceof Var leftVar && right instanceof Var rightVar) { | ||
| // to substitute internal variable with user-facing variable | ||
| if (isInternal(leftVar) && !isInternal(rightVar) && !isReturnVar(leftVar)) { | ||
| map.put(leftVar.getName(), right.clone()); | ||
| } else if (isInternal(rightVar) && !isInternal(leftVar) && !isReturnVar(rightVar)) { | ||
| map.put(rightVar.getName(), left.clone()); | ||
| } else if (isInternal(leftVar) && isInternal(rightVar)) { | ||
| // to substitute the lower-counter variable with the higher-counter one | ||
| boolean isLeftCounterLower = getCounter(leftVar) <= getCounter(rightVar); | ||
| Var lowerVar = isLeftCounterLower ? leftVar : rightVar; | ||
| Var higherVar = isLeftCounterLower ? rightVar : leftVar; | ||
| if (!isReturnVar(lowerVar) && !isFreshVar(higherVar)) | ||
| map.putIfAbsent(lowerVar.getName(), higherVar.clone()); | ||
| } | ||
| } else if (left instanceof Var var && !(right instanceof Var) && canSubstitute(var, right)) { | ||
| map.put(var.getName(), right.clone()); | ||
| return; | ||
| } | ||
| if (!"==".equals(op)) | ||
| return; | ||
|
|
||
| Expression left = be.getFirstOperand(); | ||
| Expression right = be.getSecondOperand(); | ||
| String leftKey = substitutionKey(left); | ||
| String rightKey = substitutionKey(right); | ||
|
|
||
| if (leftKey != null && right.isLiteral()) { | ||
| map.put(leftKey, right.clone()); | ||
| } else if (rightKey != null && left.isLiteral()) { | ||
| map.put(rightKey, left.clone()); | ||
| } else if (left instanceof Var leftVar && right instanceof Var rightVar) { | ||
| // to substitute internal variable with user-facing variable | ||
| if (isInternal(leftVar) && !isInternal(rightVar) && !isReturnVar(leftVar)) { | ||
| map.put(leftVar.getName(), right.clone()); | ||
| } else if (isInternal(rightVar) && !isInternal(leftVar) && !isReturnVar(rightVar)) { | ||
| map.put(rightVar.getName(), left.clone()); | ||
| } else if (isInternal(leftVar) && isInternal(rightVar)) { | ||
| // to substitute the lower-counter variable with the higher-counter one | ||
| boolean isLeftCounterLower = getCounter(leftVar) <= getCounter(rightVar); | ||
| Var lowerVar = isLeftCounterLower ? leftVar : rightVar; | ||
| Var higherVar = isLeftCounterLower ? rightVar : leftVar; | ||
| if (!isReturnVar(lowerVar) && !isFreshVar(higherVar)) | ||
| map.putIfAbsent(lowerVar.getName(), higherVar.clone()); | ||
| } | ||
| } else if (left instanceof Var var && canSubstitute(var, right)) { | ||
| map.put(var.getName(), right.clone()); | ||
| } else if (left instanceof FunctionInvocation && !right.toString().contains(leftKey)) { | ||
| map.put(leftKey, right.clone()); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this means we would not substitute something like: because the string on the right contains f(a) even though they are not related, right?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, I'll replace that string check with an AST check. |
||
| } | ||
| } | ||
|
|
||
| private static String substitutionKey(Expression exp) { | ||
| if (exp instanceof Var var) | ||
| return var.getName(); | ||
| if (exp instanceof FunctionInvocation) | ||
| return exp.toString(); | ||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Handles transitive variable equalities in the map (e.g. map: x -> y, y -> 1 => map: x -> 1, y -> 1) | ||
| * | ||
|
|
@@ -129,14 +146,22 @@ private static boolean hasUsage(Expression exp, String name) { | |
| if (left instanceof Var v && v.getName().equals(name) | ||
| && (right.isLiteral() || (!(right instanceof Var) && canSubstitute(v, right)))) | ||
| return false; | ||
| if (left instanceof FunctionInvocation && left.toString().equals(name) | ||
| && (right.isLiteral() || (!(right instanceof Var) && !right.toString().contains(name)))) | ||
| return false; | ||
| if (right instanceof Var v && v.getName().equals(name) && left.isLiteral()) | ||
| return false; | ||
| if (right instanceof FunctionInvocation && right.toString().equals(name) && left.isLiteral()) | ||
| return false; | ||
| } | ||
|
|
||
| // usage found | ||
| if (exp instanceof Var var && var.getName().equals(name)) { | ||
| return true; | ||
| } | ||
| if (exp instanceof FunctionInvocation && exp.toString().equals(name)) { | ||
| return true; | ||
| } | ||
|
|
||
| // recurse children | ||
| if (exp.hasChildren()) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| import liquidjava.rj_language.ast.AliasInvocation; | ||
| import liquidjava.rj_language.ast.BinaryExpression; | ||
| import liquidjava.rj_language.ast.Expression; | ||
| import liquidjava.rj_language.ast.FunctionInvocation; | ||
| import liquidjava.rj_language.ast.Ite; | ||
| import liquidjava.rj_language.ast.LiteralBoolean; | ||
| import liquidjava.rj_language.ast.LiteralInt; | ||
|
|
@@ -1133,4 +1134,28 @@ void testFoldsAdjacentIntegerConstantsInLeftAssociatedArithmetic() { | |
| assertEquals("x + 3", ExpressionSimplifier.simplify(xPlus1Plus2).getValue().toString()); | ||
| assertEquals("x", ExpressionSimplifier.simplify(xPlus1Minus1).getValue().toString()); | ||
| } | ||
|
|
||
| @Test | ||
| void testFunctionInvocationEqualitiesPropagateTransitively() { | ||
| // Given: size(x3) == size(x2) - 1 && size(x2) == size(x1) + 1 && size(x1) == 0 | ||
| // Expected: size(x3) == 0 | ||
| Expression x1 = new Var("x1"); | ||
| Expression x2 = new Var("x2"); | ||
| Expression x3 = new Var("x3"); | ||
| Expression sizeX1 = new FunctionInvocation("size", List.of(x1)); | ||
| Expression sizeX2 = new FunctionInvocation("size", List.of(x2)); | ||
| Expression sizeX3 = new FunctionInvocation("size", List.of(x3)); | ||
|
|
||
| Expression sizeX3EqualsSizeX2Minus1 = new BinaryExpression(sizeX3, "==", | ||
| new BinaryExpression(sizeX2, "-", new LiteralInt(1))); | ||
| Expression sizeX2EqualsSizeX1Plus1 = new BinaryExpression(sizeX2, "==", | ||
| new BinaryExpression(sizeX1, "+", new LiteralInt(1))); | ||
| Expression sizeX1Equals0 = new BinaryExpression(sizeX1, "==", new LiteralInt(0)); | ||
| Expression fullExpression = new BinaryExpression(sizeX3EqualsSizeX2Minus1, "&&", | ||
| new BinaryExpression(sizeX2EqualsSizeX1Plus1, "&&", sizeX1Equals0)); | ||
|
|
||
| ValDerivationNode result = ExpressionSimplifier.simplify(fullExpression); | ||
|
|
||
| assertEquals("size(x3) == 0", result.getValue().toString()); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we need a couple more tests for this, I'm not very convinced |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is using strings for substitution really the best approach?