11package org .variantsync .diffdetective .feature .jpp ;
22
3+ import org .antlr .v4 .runtime .ParserRuleContext ;
34import org .antlr .v4 .runtime .tree .AbstractParseTreeVisitor ;
4- import org .variantsync .diffdetective .feature .antlr .CExpressionParser ;
5- import org .variantsync .diffdetective .feature .antlr .CExpressionVisitor ;
5+ import org .antlr .v4 .runtime .tree .ParseTree ;
6+ import org .antlr .v4 .runtime .tree .TerminalNode ;
7+ import org .variantsync .diffdetective .feature .BooleanAbstraction ;
68import org .variantsync .diffdetective .feature .antlr .JPPExpressionParser ;
79import org .variantsync .diffdetective .feature .antlr .JPPExpressionVisitor ;
810
11+ import java .util .function .Function ;
12+
913public class AbstractingJPPExpressionVisitor extends AbstractParseTreeVisitor <StringBuilder > implements JPPExpressionVisitor <StringBuilder > {
1014 // expression
1115 // : logicalOrExpression
1216 // ;
1317 @ Override
1418 public StringBuilder visitExpression (JPPExpressionParser .ExpressionContext ctx ) {
15- return null ;
19+ return ctx . logicalOrExpression (). accept ( this ) ;
1620 }
1721
1822 // logicalOrExpression
1923 // : logicalAndExpression (OR logicalAndExpression)*
2024 // ;
2125 @ Override
2226 public StringBuilder visitLogicalOrExpression (JPPExpressionParser .LogicalOrExpressionContext ctx ) {
23- return null ;
27+ return visitLogicalExpression (ctx ,
28+ childExpression -> childExpression instanceof JPPExpressionParser .LogicalAndExpressionContext );
2429 }
2530
2631 // logicalAndExpression
2732 // : primaryExpression (AND primaryExpression)*
2833 // ;
2934 @ Override
3035 public StringBuilder visitLogicalAndExpression (JPPExpressionParser .LogicalAndExpressionContext ctx ) {
31- return null ;
36+ return visitLogicalExpression (ctx ,
37+ childExpression -> childExpression instanceof JPPExpressionParser .PrimaryExpressionContext );
3238 }
3339
3440 // primaryExpression
@@ -38,15 +44,24 @@ public StringBuilder visitLogicalAndExpression(JPPExpressionParser.LogicalAndExp
3844 // ;
3945 @ Override
4046 public StringBuilder visitPrimaryExpression (JPPExpressionParser .PrimaryExpressionContext ctx ) {
41- return null ;
47+ if (ctx .definedExpression () != null ) {
48+ return ctx .definedExpression ().accept (this );
49+ }
50+ if (ctx .undefinedExpression () != null ) {
51+ return ctx .undefinedExpression ().accept (this );
52+ }
53+ if (ctx .comparisonExpression () != null ) {
54+ return ctx .comparisonExpression ().accept (this );
55+ }
56+ throw new IllegalStateException ("Unreachable code" );
4257 }
4358
4459 // comparisonExpression
4560 // : operand ((LT|GT|LEQ|GEQ|EQ|NEQ) operand)?
4661 // ;
4762 @ Override
4863 public StringBuilder visitComparisonExpression (JPPExpressionParser .ComparisonExpressionContext ctx ) {
49- return null ;
64+ return visitExpression ( ctx , childExpression -> childExpression instanceof JPPExpressionParser . OperandContext ) ;
5065 }
5166
5267 // operand
@@ -57,31 +72,58 @@ public StringBuilder visitComparisonExpression(JPPExpressionParser.ComparisonExp
5772 // ;
5873 @ Override
5974 public StringBuilder visitOperand (JPPExpressionParser .OperandContext ctx ) {
60- return null ;
75+ // propertyExpression
76+ if (ctx .propertyExpression () != null ) {
77+ return ctx .propertyExpression ().accept (this );
78+ }
79+ // unaryOperator Constant
80+ if (ctx .unaryOperator () != null ) {
81+ StringBuilder sb = ctx .unaryOperator ().accept (this );
82+ sb .append (BooleanAbstraction .abstractAll (ctx .Constant ().getText ().trim ()));
83+ return sb ;
84+ }
85+ // Constant
86+ if (ctx .Constant () != null ) {
87+ return new StringBuilder (BooleanAbstraction .abstractAll (ctx .Constant ().getText ().trim ()));
88+ }
89+ // StringLiteral+
90+ if (!ctx .StringLiteral ().isEmpty ()) {
91+ StringBuilder sb = new StringBuilder ();
92+ ctx .StringLiteral ().stream ().map (ParseTree ::getText ).map (String ::trim ).map (BooleanAbstraction ::abstractAll ).forEach (sb ::append );
93+ return sb ;
94+ }
95+ // Unreachable
96+ throw new IllegalStateException ("Unreachable code." );
6197 }
6298
6399 // definedExpression
64100 // : 'defined' '(' Identifier ')'
65101 // ;
66102 @ Override
67103 public StringBuilder visitDefinedExpression (JPPExpressionParser .DefinedExpressionContext ctx ) {
68- return null ;
104+ StringBuilder sb = new StringBuilder ("DEFINED_" );
105+ sb .append (ctx .Identifier ().getText ().trim ());
106+ return sb ;
69107 }
70108
71109 // undefinedExpression
72110 // : NOT 'defined' '(' Identifier ')'
73111 // ;
74112 @ Override
75113 public StringBuilder visitUndefinedExpression (JPPExpressionParser .UndefinedExpressionContext ctx ) {
76- return null ;
114+ StringBuilder sb = new StringBuilder ();
115+ sb .append (BooleanAbstraction .U_NOT );
116+ sb .append ("DEFINED_" );
117+ sb .append (ctx .Identifier ().getText ().trim ());
118+ return sb ;
77119 }
78120
79121 // propertyExpression
80122 // : '${' Identifier '}'
81123 // ;
82124 @ Override
83125 public StringBuilder visitPropertyExpression (JPPExpressionParser .PropertyExpressionContext ctx ) {
84- return null ;
126+ return new StringBuilder ( ctx . Identifier (). getText (). trim ()) ;
85127 }
86128
87129 // unaryOperator
@@ -90,6 +132,65 @@ public StringBuilder visitPropertyExpression(JPPExpressionParser.PropertyExpress
90132 // ;
91133 @ Override
92134 public StringBuilder visitUnaryOperator (JPPExpressionParser .UnaryOperatorContext ctx ) {
93- return null ;
135+ switch (ctx .getText ().trim ()) {
136+ case "+" -> {
137+ return new StringBuilder (BooleanAbstraction .U_PLUS );
138+ }
139+ case "-" -> {
140+ return new StringBuilder (BooleanAbstraction .U_MINUS );
141+ }
142+ }
143+ throw new IllegalStateException ("Unreachable code" );
144+ }
145+
146+ // logicalOrExpression
147+ // : logicalAndExpression (OR logicalAndExpression)*
148+ // ;
149+ // logicalAndExpression
150+ // : primaryExpression (AND primaryExpression)*
151+ // ;
152+ private StringBuilder visitLogicalExpression (ParserRuleContext expressionContext , Function <ParseTree , Boolean > instanceCheck ) {
153+ StringBuilder sb = new StringBuilder ();
154+ for (ParseTree subtree : expressionContext .children ) {
155+ if (instanceCheck .apply (subtree )) {
156+ // logicalAndExpression | InclusiveOrExpression
157+ sb .append (subtree .accept (this ));
158+ } else if (subtree instanceof TerminalNode terminal ) {
159+ // '&&' | '||'
160+ switch (subtree .getText ()) {
161+ case "and" -> sb .append ("&&" );
162+ case "or" -> sb .append ("||" );
163+ default -> throw new IllegalStateException ();
164+ }
165+ } else {
166+ // loop does not work as expected
167+ throw new IllegalStateException ();
168+ }
169+ }
170+ return sb ;
171+ }
172+
173+ /**
174+ * Abstract all child nodes in the parse tree.
175+ *
176+ * @param expressionContext The root of the subtree to abstract
177+ * @param instanceCheck A check for expected child node types
178+ * @return The abstracted formula of the subtree
179+ */
180+ private StringBuilder visitExpression (ParserRuleContext expressionContext , Function <ParseTree , Boolean > instanceCheck ) {
181+ StringBuilder sb = new StringBuilder ();
182+ for (ParseTree subtree : expressionContext .children ) {
183+ if (instanceCheck .apply (subtree )) {
184+ // Some operand (i.e., a subtree) that we have to visit
185+ sb .append (subtree .accept (this ));
186+ } else if (subtree instanceof TerminalNode terminal ) {
187+ // Some operator (i.e., a leaf node) that requires direct abstraction
188+ sb .append (BooleanAbstraction .abstractToken (terminal .getText ().trim ()));
189+ } else {
190+ // sanity check: loop does not work as expected
191+ throw new IllegalStateException ();
192+ }
193+ }
194+ return sb ;
94195 }
95196}
0 commit comments