Skip to content

Commit d239664

Browse files
feat: first working version of JavaPP parsing
1 parent cbbb3b4 commit d239664

4 files changed

Lines changed: 119 additions & 114 deletions

File tree

src/main/antlr4/org/variantsync/diffdetective/feature/antlr/JPPExpression.g4

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ comparisonExpression
2626

2727
operand
2828
: propertyExpression
29+
| unaryOperator Constant
2930
| Constant
3031
| StringLiteral+
31-
| unaryOperator Constant
3232
;
3333

3434
definedExpression
@@ -66,9 +66,9 @@ NEQ : '!=';
6666
DOT : '.';
6767

6868
Identifier
69-
: ('\\')? ( IdentifierNondigit
69+
: IdentifierNondigit ( IdentifierNondigit
7070
| Digit
71-
)+
71+
)*
7272
;
7373

7474
fragment
Lines changed: 113 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
11
package org.variantsync.diffdetective.feature.jpp;
22

3+
import org.antlr.v4.runtime.ParserRuleContext;
34
import 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;
68
import org.variantsync.diffdetective.feature.antlr.JPPExpressionParser;
79
import org.variantsync.diffdetective.feature.antlr.JPPExpressionVisitor;
810

11+
import java.util.function.Function;
12+
913
public 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
}

src/main/java/org/variantsync/diffdetective/feature/jpp/ControllingJPPExpressionVisitor.java

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/main/java/org/variantsync/diffdetective/feature/jpp/JPPDiffLineFormulaExtractor.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ public JPPDiffLineFormulaExtractor() {
2929
/**
3030
* Abstract the given formula.
3131
* <p>
32-
* First, the visitor uses ANTLR to parse the formula into a parse tree gives the tree to a {@link ControllingJPPExpressionVisitor}.
32+
* First, the visitor uses ANTLR to parse the formula into a parse tree gives the tree to a {@link AbstractingJPPExpressionVisitor}.
3333
* The visitor traverses the tree starting from the root, searching for subtrees that must be abstracted.
34-
* If such a subtree is found, the visitor calls an {@link AbstractingJPPExpressionVisitor} to abstract the part of
35-
* the formula in the subtree.
34+
* If such a subtree is found, the visitor abstracts the part of the formula in the subtree.
3635
* </p>
3736
*
3837
* @param formula that is to be abstracted
@@ -45,6 +44,6 @@ protected String abstractFormula(String formula) {
4544
JPPExpressionParser parser = new JPPExpressionParser(tokens);
4645
parser.addErrorListener(new ParseErrorListener(formula));
4746
ParseTree tree = parser.expression();
48-
return tree.accept(new ControllingJPPExpressionVisitor()).toString();
47+
return tree.accept(new AbstractingJPPExpressionVisitor()).toString();
4948
}
5049
}

0 commit comments

Comments
 (0)