66import java .util .Set ;
77
88import liquidjava .rj_language .ast .BinaryExpression ;
9+ import liquidjava .rj_language .ast .Enum ;
910import liquidjava .rj_language .ast .Expression ;
11+ import liquidjava .rj_language .ast .FunctionInvocation ;
1012import liquidjava .rj_language .ast .Var ;
1113
1214public class VariableResolver {
@@ -25,7 +27,7 @@ public static Map<String, Expression> resolve(Expression exp) {
2527 resolveRecursive (exp , map );
2628
2729 // remove variables that were not used in the expression
28- map .entrySet ().removeIf (entry -> !hasUsage (exp , entry .getKey ()));
30+ map .entrySet ().removeIf (entry -> !hasUsage (exp , entry .getKey (), entry . getValue () ));
2931
3032 // transitively resolve variables
3133 return resolveTransitive (map );
@@ -45,33 +47,49 @@ private static void resolveRecursive(Expression exp, Map<String, Expression> map
4547 if ("&&" .equals (op )) {
4648 resolveRecursive (be .getFirstOperand (), map );
4749 resolveRecursive (be .getSecondOperand (), map );
48- } else if ("==" .equals (op )) {
49- Expression left = be .getFirstOperand ();
50- Expression right = be .getSecondOperand ();
51- if (left instanceof Var var && right .isLiteral ()) {
52- map .put (var .getName (), right .clone ());
53- } else if (right instanceof Var var && left .isLiteral ()) {
54- map .put (var .getName (), left .clone ());
55- } else if (left instanceof Var leftVar && right instanceof Var rightVar ) {
56- // to substitute internal variable with user-facing variable
57- if (isInternal (leftVar ) && !isInternal (rightVar ) && !isReturnVar (leftVar )) {
58- map .put (leftVar .getName (), right .clone ());
59- } else if (isInternal (rightVar ) && !isInternal (leftVar ) && !isReturnVar (rightVar )) {
60- map .put (rightVar .getName (), left .clone ());
61- } else if (isInternal (leftVar ) && isInternal (rightVar )) {
62- // to substitute the lower-counter variable with the higher-counter one
63- boolean isLeftCounterLower = getCounter (leftVar ) <= getCounter (rightVar );
64- Var lowerVar = isLeftCounterLower ? leftVar : rightVar ;
65- Var higherVar = isLeftCounterLower ? rightVar : leftVar ;
66- if (!isReturnVar (lowerVar ) && !isFreshVar (higherVar ))
67- map .putIfAbsent (lowerVar .getName (), higherVar .clone ());
68- }
69- } else if (left instanceof Var var && !(right instanceof Var ) && canSubstitute (var , right )) {
70- map .put (var .getName (), right .clone ());
50+ return ;
51+ }
52+ if (!"==" .equals (op ))
53+ return ;
54+
55+ Expression left = be .getFirstOperand ();
56+ Expression right = be .getSecondOperand ();
57+ String leftKey = substitutionKey (left );
58+ String rightKey = substitutionKey (right );
59+
60+ if (leftKey != null && isConstant (right )) {
61+ map .put (leftKey , right .clone ());
62+ } else if (rightKey != null && isConstant (left )) {
63+ map .put (rightKey , left .clone ());
64+ } else if (left instanceof Var leftVar && right instanceof Var rightVar ) {
65+ // to substitute internal variable with user-facing variable
66+ if (isInternal (leftVar ) && !isInternal (rightVar ) && !isReturnVar (leftVar )) {
67+ map .put (leftVar .getName (), right .clone ());
68+ } else if (isInternal (rightVar ) && !isInternal (leftVar ) && !isReturnVar (rightVar )) {
69+ map .put (rightVar .getName (), left .clone ());
70+ } else if (isInternal (leftVar ) && isInternal (rightVar )) {
71+ // to substitute the lower-counter variable with the higher-counter one
72+ boolean isLeftCounterLower = getCounter (leftVar ) <= getCounter (rightVar );
73+ Var lowerVar = isLeftCounterLower ? leftVar : rightVar ;
74+ Var higherVar = isLeftCounterLower ? rightVar : leftVar ;
75+ if (!isReturnVar (lowerVar ) && !isFreshVar (higherVar ))
76+ map .putIfAbsent (lowerVar .getName (), higherVar .clone ());
7177 }
78+ } else if (left instanceof Var var && canSubstitute (var , right )) {
79+ map .put (var .getName (), right .clone ());
80+ } else if (left instanceof FunctionInvocation && !containsExpression (right , left )) {
81+ map .put (leftKey , right .clone ());
7282 }
7383 }
7484
85+ private static String substitutionKey (Expression exp ) {
86+ if (exp instanceof Var var )
87+ return var .getName ();
88+ if (exp instanceof FunctionInvocation )
89+ return exp .toString ();
90+ return null ;
91+ }
92+
7593 /**
7694 * Handles transitive variable equalities in the map (e.g. map: x -> y, y -> 1 => map: x -> 1, y -> 1)
7795 *
@@ -98,10 +116,10 @@ private static Map<String, Expression> resolveTransitive(Map<String, Expression>
98116 * @return resolved expression
99117 */
100118 private static Expression lookup (Expression exp , Map <String , Expression > map , Set <String > seen ) {
101- if (!(exp instanceof Var ))
119+ String name = substitutionKey (exp );
120+ if (name == null )
102121 return exp ;
103122
104- String name = exp .toString ();
105123 if (seen .contains (name ))
106124 return exp ; // circular reference
107125
@@ -121,27 +139,36 @@ private static Expression lookup(Expression exp, Map<String, Expression> map, Se
121139 *
122140 * @return true if used, false otherwise
123141 */
124- private static boolean hasUsage (Expression exp , String name ) {
142+ private static boolean hasUsage (Expression exp , String name , Expression value ) {
125143 // exclude own definitions
126144 if (exp instanceof BinaryExpression binary && "==" .equals (binary .getOperator ())) {
127145 Expression left = binary .getFirstOperand ();
128146 Expression right = binary .getSecondOperand ();
129- if (left instanceof Var v && v .getName ().equals (name )
130- && (right .isLiteral () || (!(right instanceof Var ) && canSubstitute (v , right ))))
147+ if (left instanceof Var v && v .getName ().equals (name ) && right .equals (value )
148+ && (isConstant (right ) || (!(right instanceof Var ) && canSubstitute (v , right ))))
149+ return false ;
150+ if (left instanceof FunctionInvocation && left .toString ().equals (name ) && right .equals (value )
151+ && (isConstant (right ) || (!(right instanceof Var ) && !containsExpression (right , left ))))
152+ return false ;
153+ if (right instanceof Var v && v .getName ().equals (name ) && left .equals (value ) && isConstant (left ))
131154 return false ;
132- if (right instanceof Var v && v .getName ().equals (name ) && left .isLiteral ())
155+ if (right instanceof FunctionInvocation && right .toString ().equals (name ) && left .equals (value )
156+ && isConstant (left ))
133157 return false ;
134158 }
135159
136160 // usage found
137161 if (exp instanceof Var var && var .getName ().equals (name )) {
138162 return true ;
139163 }
164+ if (exp instanceof FunctionInvocation && exp .toString ().equals (name )) {
165+ return true ;
166+ }
140167
141168 // recurse children
142169 if (exp .hasChildren ()) {
143170 for (Expression child : exp .getChildren ())
144- if (hasUsage (child , name ))
171+ if (hasUsage (child , name , value ))
145172 return true ;
146173 }
147174
@@ -172,6 +199,10 @@ private static boolean canSubstitute(Var var, Expression value) {
172199 return !isReturnVar (var ) && !isFreshVar (var ) && !containsVariable (value , var .getName ());
173200 }
174201
202+ private static boolean isConstant (Expression exp ) {
203+ return exp .isLiteral () || exp instanceof Enum ;
204+ }
205+
175206 private static boolean containsVariable (Expression exp , String name ) {
176207 if (exp instanceof Var var )
177208 return var .getName ().equals (name );
@@ -185,4 +216,18 @@ private static boolean containsVariable(Expression exp, String name) {
185216 }
186217 return false ;
187218 }
219+
220+ private static boolean containsExpression (Expression exp , Expression target ) {
221+ if (exp .equals (target ))
222+ return true ;
223+
224+ if (!exp .hasChildren ())
225+ return false ;
226+
227+ for (Expression child : exp .getChildren ()) {
228+ if (containsExpression (child , target ))
229+ return true ;
230+ }
231+ return false ;
232+ }
188233}
0 commit comments