Skip to content

Commit b458497

Browse files
authored
Merge pull request #35 from Universal-Variability-Language/issue-32
Issue 32
2 parents 3dcb9c5 + 18795c2 commit b458497

3 files changed

Lines changed: 187 additions & 49 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package de.vill.main;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Deque;
5+
6+
import org.antlr.v4.runtime.tree.ErrorNode;
7+
import org.antlr.v4.runtime.tree.ParseTree;
8+
import org.antlr.v4.runtime.tree.ParseTreeListener;
9+
import org.antlr.v4.runtime.tree.ParseTreeWalker;
10+
import org.antlr.v4.runtime.tree.RuleNode;
11+
import org.antlr.v4.runtime.tree.TerminalNode;
12+
13+
/**
14+
* Iterative variant of ANTLR's ParseTreeWalker.
15+
*
16+
* ANTLR's default ParseTreeWalker recursively walks the parse tree.
17+
* Deeply nested constraints such as F0 | F1 | ... | F6999 can therefore
18+
* overflow the Java call stack before the UVL model is constructed.
19+
*/
20+
final class IterativeParseTreeWalker extends ParseTreeWalker {
21+
22+
@Override
23+
public void walk(ParseTreeListener listener, ParseTree tree) {
24+
final Deque<Frame> stack = new ArrayDeque<>();
25+
stack.push(new Frame(tree));
26+
27+
while (!stack.isEmpty()) {
28+
final Frame frame = stack.peek();
29+
final ParseTree current = frame.tree;
30+
31+
if (current instanceof ErrorNode) {
32+
listener.visitErrorNode((ErrorNode) current);
33+
stack.pop();
34+
continue;
35+
}
36+
37+
if (current instanceof TerminalNode) {
38+
listener.visitTerminal((TerminalNode) current);
39+
stack.pop();
40+
continue;
41+
}
42+
43+
final RuleNode ruleNode = (RuleNode) current;
44+
45+
if (!frame.entered) {
46+
enterRule(listener, ruleNode);
47+
frame.entered = true;
48+
}
49+
50+
if (frame.nextChildIndex < current.getChildCount()) {
51+
stack.push(new Frame(current.getChild(frame.nextChildIndex)));
52+
frame.nextChildIndex++;
53+
} else {
54+
exitRule(listener, ruleNode);
55+
stack.pop();
56+
}
57+
}
58+
}
59+
60+
private static final class Frame {
61+
private final ParseTree tree;
62+
private boolean entered;
63+
private int nextChildIndex;
64+
65+
private Frame(ParseTree tree) {
66+
this.tree = tree;
67+
}
68+
}
69+
}

src/main/java/de/vill/main/UVLModelFactory.java

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.antlr.v4.runtime.ConsoleErrorListener;
3434
import org.antlr.v4.runtime.RecognitionException;
3535
import org.antlr.v4.runtime.Recognizer;
36-
import org.antlr.v4.runtime.tree.ParseTreeWalker;
3736

3837
import java.io.IOException;
3938
import java.nio.file.FileSystems;
@@ -134,7 +133,8 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
134133
});
135134

136135
UVLListener uvlListener = createUVLListener();
137-
ParseTreeWalker walker = new ParseTreeWalker();
136+
IterativeParseTreeWalker walker = new IterativeParseTreeWalker();
137+
138138
walker.walk(uvlListener, UVLJavaParser.constraintLine());
139139

140140
return uvlListener.getConstraint();
@@ -314,8 +314,10 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
314314
});
315315

316316

317+
317318
UVLListener uvlListener = createUVLListener();
318-
ParseTreeWalker walker = new ParseTreeWalker();
319+
IterativeParseTreeWalker walker = new IterativeParseTreeWalker();
320+
319321
walker.walk(uvlListener, UVLJavaParser.featureModel());
320322
FeatureModel featureModel = null;
321323

@@ -388,42 +390,55 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
388390
}
389391

390392
private void resolveImportPlaceholders(Constraint constraint, FeatureModel featureModel) {
391-
if (constraint instanceof AndConstraint || constraint instanceof OrConstraint || constraint instanceof NotConstraint || constraint instanceof ImplicationConstraint || constraint instanceof ParenthesisConstraint || constraint instanceof EquivalenceConstraint) {
392-
for (Constraint subPart : constraint.getConstraintSubParts()) {
393-
resolveImportPlaceholders(subPart, featureModel);
393+
final Deque<Constraint> stack = new ArrayDeque<>();
394+
stack.push(constraint);
395+
396+
while (!stack.isEmpty()) {
397+
final Constraint current = stack.pop();
398+
399+
if (current instanceof ExpressionConstraint) {
400+
ExpressionConstraint expressionConstraint = (ExpressionConstraint) current;
401+
resolveImportPlaceholders(expressionConstraint.getLeft(), featureModel);
402+
resolveImportPlaceholders(expressionConstraint.getRight(), featureModel);
403+
} else if (current instanceof LiteralConstraint) {
404+
LiteralConstraint literalConstraint = (LiteralConstraint) current;
405+
if (literalConstraint.getReference() instanceof ImportedVariablePlaceholder) {
406+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalConstraint.getReference();
407+
literalConstraint.setReference(resolvePlaceholder(placeholder, featureModel));
408+
}
394409
}
395-
} else if (constraint instanceof ExpressionConstraint) {
396-
ExpressionConstraint expressionConstraint = (ExpressionConstraint) constraint;
397-
resolveImportPlaceholders(expressionConstraint.getLeft(), featureModel);
398-
resolveImportPlaceholders(expressionConstraint.getRight(), featureModel);
399-
} else if (constraint instanceof LiteralConstraint) {
400-
LiteralConstraint literalConstraint = (LiteralConstraint) constraint;
401-
if (literalConstraint.getReference() instanceof ImportedVariablePlaceholder) {
402-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalConstraint.getReference();
403-
literalConstraint.setReference(resolvePlaceholder(placeholder, featureModel));
410+
411+
final List<Constraint> subConstraints = current.getConstraintSubParts();
412+
for (int i = subConstraints.size() - 1; i >= 0; i--) {
413+
stack.push(subConstraints.get(i));
404414
}
405415
}
406416
}
407417

408418
private void resolveImportPlaceholders(Expression expression, FeatureModel featureModel) {
409-
if (expression instanceof BinaryExpression) {
410-
BinaryExpression binaryExpression = (BinaryExpression) expression;
411-
resolveImportPlaceholders(binaryExpression.getLeft(), featureModel);
412-
resolveImportPlaceholders(binaryExpression.getRight(), featureModel);
413-
} else if (expression instanceof ParenthesisExpression) {
414-
ParenthesisExpression parenthesisExpression = (ParenthesisExpression) expression;
415-
resolveImportPlaceholders(parenthesisExpression.getContent(), featureModel);
416-
} else if (expression instanceof LengthAggregateFunctionExpression) {
417-
LengthAggregateFunctionExpression lengthAggregateFunctionExpression = (LengthAggregateFunctionExpression) expression;
418-
if (lengthAggregateFunctionExpression.getReference() instanceof ImportedVariablePlaceholder) {
419-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) lengthAggregateFunctionExpression.getReference();
420-
lengthAggregateFunctionExpression.setReference(resolvePlaceholder(placeholder, featureModel));
419+
final Deque<Expression> stack = new ArrayDeque<>();
420+
stack.push(expression);
421+
422+
while (!stack.isEmpty()) {
423+
final Expression current = stack.pop();
424+
425+
if (current instanceof LengthAggregateFunctionExpression) {
426+
LengthAggregateFunctionExpression lengthAggregateFunctionExpression = (LengthAggregateFunctionExpression) current;
427+
if (lengthAggregateFunctionExpression.getReference() instanceof ImportedVariablePlaceholder) {
428+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) lengthAggregateFunctionExpression.getReference();
429+
lengthAggregateFunctionExpression.setReference(resolvePlaceholder(placeholder, featureModel));
430+
}
431+
} else if (current instanceof LiteralExpression) {
432+
LiteralExpression literalExpression = (LiteralExpression) current;
433+
if (literalExpression.getContent() instanceof ImportedVariablePlaceholder) {
434+
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalExpression.getContent();
435+
literalExpression.setContent(resolvePlaceholder(placeholder, featureModel));
436+
}
421437
}
422-
} else if (expression instanceof LiteralExpression) {
423-
LiteralExpression literalExpression = (LiteralExpression) expression;
424-
if (literalExpression.getContent() instanceof ImportedVariablePlaceholder) {
425-
ImportedVariablePlaceholder placeholder = (ImportedVariablePlaceholder) literalExpression.getContent();
426-
literalExpression.setContent(resolvePlaceholder(placeholder, featureModel));
438+
439+
final List<Expression> subExpressions = current.getExpressionSubParts();
440+
for (int i = subExpressions.size() - 1; i >= 0; i--) {
441+
stack.push(subExpressions.get(i));
427442
}
428443
}
429444
}
@@ -521,28 +536,38 @@ private void validateTypeLevelConstraints(final FeatureModel featureModel) {
521536
}
522537

523538
private boolean validateTypeLevelConstraint(final Constraint constraint) {
524-
boolean result = true;
525-
if (constraint instanceof ExpressionConstraint) {
526-
String leftReturnType = ((ExpressionConstraint) constraint).getLeft().getReturnType();
527-
String rightReturnType = ((ExpressionConstraint) constraint).getRight().getReturnType();
539+
final Deque<Constraint> stack = new ArrayDeque<>();
540+
stack.push(constraint);
528541

529-
if (!(leftReturnType.equalsIgnoreCase(Constants.TRUE) || rightReturnType.equalsIgnoreCase(Constants.TRUE))) {
530-
// if not attribute constraint
531-
result = result && ((ExpressionConstraint) constraint).getLeft().getReturnType().equalsIgnoreCase(((ExpressionConstraint) constraint).getRight().getReturnType());
532-
}
533-
if (!result) {
534-
return false;
535-
}
536-
for (final Expression expr: ((ExpressionConstraint) constraint).getExpressionSubParts()) {
537-
result = result && validateTypeLevelExpression(expr);
542+
while (!stack.isEmpty()) {
543+
final Constraint current = stack.pop();
544+
545+
if (current instanceof ExpressionConstraint) {
546+
final ExpressionConstraint expressionConstraint = (ExpressionConstraint) current;
547+
548+
final String leftReturnType = expressionConstraint.getLeft().getReturnType();
549+
final String rightReturnType = expressionConstraint.getRight().getReturnType();
550+
551+
if (!(leftReturnType.equalsIgnoreCase(Constants.TRUE) || rightReturnType.equalsIgnoreCase(Constants.TRUE))) {
552+
if (!leftReturnType.equalsIgnoreCase(rightReturnType)) {
553+
return false;
554+
}
555+
}
556+
557+
for (final Expression expr : expressionConstraint.getExpressionSubParts()) {
558+
if (!validateTypeLevelExpression(expr)) {
559+
return false;
560+
}
561+
}
538562
}
539-
}
540563

541-
for (final Constraint subCons: constraint.getConstraintSubParts()) {
542-
result = result && validateTypeLevelConstraint(subCons);
564+
final List<Constraint> subConstraints = current.getConstraintSubParts();
565+
for (int i = subConstraints.size() - 1; i >= 0; i--) {
566+
stack.push(subConstraints.get(i));
567+
}
543568
}
544569

545-
return result;
570+
return true;
546571
}
547572

548573
private boolean validateTypeLevelExpression(final Expression expression) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package de.vill.parsing;
2+
3+
import de.vill.main.UVLModelFactory;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
7+
8+
class LongConstraintParsingTest {
9+
10+
@Test
11+
void parsesLongOrConstraint() {
12+
final int numberOfLiterals = 7000;
13+
final String model = createModel(numberOfLiterals);
14+
15+
final UVLModelFactory factory = new UVLModelFactory();
16+
17+
assertDoesNotThrow(() -> factory.parse(model));
18+
}
19+
20+
private String createModel(int numberOfLiterals) {
21+
final StringBuilder builder = new StringBuilder();
22+
23+
builder.append("features\n");
24+
builder.append(" Root\n");
25+
builder.append(" optional\n");
26+
27+
for (int i = 0; i < numberOfLiterals; i++) {
28+
builder.append(" F").append(i).append("\n");
29+
}
30+
31+
builder.append("constraints\n");
32+
builder.append(" ");
33+
34+
for (int i = 0; i < numberOfLiterals; i++) {
35+
if (i > 0) {
36+
builder.append(" | ");
37+
}
38+
builder.append("F").append(i);
39+
}
40+
41+
builder.append("\n");
42+
return builder.toString();
43+
}
44+
}

0 commit comments

Comments
 (0)