Skip to content

Commit

Permalink
Merge pull request #87 from pollyvolk/syntax
Browse files Browse the repository at this point in the history
#46 - Implement syntax to specify a variable size children list of same type
  • Loading branch information
kniazkov authored Sep 20, 2022
2 parents 4481034 + 7a4fdb9 commit fbfa243
Show file tree
Hide file tree
Showing 16 changed files with 226 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
- uses: codecov/codecov-action@v2
with:
name: Code coverage report
fail_ci_if_error: true
fail_ci_if_error: false
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ private void createStaticFields() {
)
);
this.klass.addField(type);
if (!this.descriptor.hasEllipsisHole()) {
if (!this.descriptor.hasEllipsisOrTypedHole()) {
final Field count = new Field(
"Expected number of child nodes",
MatcherClassFiller.TYPE_INT,
Expand Down Expand Up @@ -266,7 +266,7 @@ private String createCondition() {
final String name = this.klass.getName();
int count = 1;
String common = String.format("node.belongsToGroup(%s.EXPECTED_TYPE)", name);
if (!this.descriptor.hasEllipsisHole()) {
if (!this.descriptor.hasEllipsisOrTypedHole()) {
common = common.concat(
String.format("\n\t&& node.getChildCount() == %s.EXPECTED_COUNT", name)
);
Expand Down
64 changes: 44 additions & 20 deletions src/main/java/org/cqfn/astranaut/interpreter/Matcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ private boolean checkType(final Node node) {
*/
private boolean checkChildCount(final Node node) {
final boolean result;
if (this.descriptor.hasEllipsisHole()) {
if (this.descriptor.hasEllipsisOrTypedHole()) {
result = true;
} else {
result = node.getChildCount() == this.descriptor.getParameters().size();
Expand Down Expand Up @@ -125,24 +125,9 @@ private boolean checkAndExtractChildren(final Node node, final Map<Integer,
for (final Parameter parameter : this.descriptor.getParameters()) {
if (parameter instanceof Hole) {
final Hole hole = (Hole) parameter;
switch (hole.getAttribute()) {
case NONE:
children.put(
hole.getValue(),
Collections.singletonList(node.getChild(index))
);
break;
case ELLIPSIS:
final int count = node.getChildCount();
final List<Node> list = new ArrayList<>(count - index);
for (int position = index; position < count; position += 1) {
list.add(node.getChild(position));
}
children.put(hole.getValue(), list);
break;
default:
break;
}
final List<Node> list = Matcher.checkAndExtractChildrenFromHole(hole, node, index);
children.put(hole.getValue(), list);
index = index + list.size();
} else if (parameter instanceof Descriptor) {
final Matcher matcher;
if (this.subs[index] == null) {
Expand All @@ -155,9 +140,48 @@ private boolean checkAndExtractChildren(final Node node, final Map<Integer,
if (!result) {
break;
}
index = index + 1;
}
index = index + 1;
}
return result;
}

/**
* Checks if the child nodes matches a hole, extracts the children.
* @param hole The hole
* @param node The node
* @param index The index of a child
* @return The list of extracted children
*/
private static List<Node> checkAndExtractChildrenFromHole(
final Hole hole,
final Node node,
final int index) {
List<Node> list = null;
switch (hole.getAttribute()) {
case NONE:
list = Collections.singletonList(node.getChild(index));
break;
case ELLIPSIS:
final int count = node.getChildCount();
list = new ArrayList<>(count - index);
for (int position = index; position < count; position += 1) {
list.add(node.getChild(position));
}
break;
case TYPED:
final int number = node.getChildCount();
list = new ArrayList<>(number - index);
for (int position = index; position < number; position += 1) {
final Node child = node.getChild(position);
if (hole.getType().equals(child.getTypeName())) {
list.add(child);
}
}
break;
default:
break;
}
return list;
}
}
7 changes: 4 additions & 3 deletions src/main/java/org/cqfn/astranaut/rules/Descriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,18 @@ public boolean hasHole() {
}

/**
* Checks whether the descriptor has a hole with ellipsis.
* Checks whether the descriptor has a hole with ellipsis or a node type.
* @return Checking result, {@code true} if the descriptor has a hole with ellipsis
*/
public boolean hasEllipsisHole() {
public boolean hasEllipsisOrTypedHole() {
boolean result = false;
final List<Parameter> parameters = this.getParameters();
final ListIterator<Parameter> iterator = parameters.listIterator(parameters.size());
while (iterator.hasPrevious()) {
final Parameter parameter = iterator.previous();
if (parameter instanceof Hole
&& ((Hole) parameter).getAttribute() == HoleAttribute.ELLIPSIS) {
&& (((Hole) parameter).getAttribute() == HoleAttribute.ELLIPSIS
|| ((Hole) parameter).getAttribute() == HoleAttribute.TYPED)) {
result = true;
break;
}
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/org/cqfn/astranaut/rules/Hole.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.util.Objects;

/**
* Hole, i.e. #1, #2, etc.
* Hole, i.e. #1, #2, Modifier#3, etc.
*
* @since 0.1.5
*/
Expand All @@ -41,14 +41,21 @@ public final class Hole implements Data, Parameter {
*/
private final HoleAttribute attribute;

/**
* Type of the node that is matched by a hole.
*/
private final String type;

/**
* Constructor.
* @param value Value of the hole
* @param attribute Attribute
* @param type Type of the node
*/
public Hole(final int value, final HoleAttribute attribute) {
public Hole(final int value, final HoleAttribute attribute, final String type) {
this.value = value;
this.attribute = attribute;
this.type = type;
}

/**
Expand All @@ -59,6 +66,14 @@ public int getValue() {
return this.value;
}

/**
* Returns the type of the hole.
* @return The type
*/
public String getType() {
return this.type;
}

/**
* Returns the attribute of the hole.
* @return The attribute
Expand All @@ -70,6 +85,7 @@ public HoleAttribute getAttribute() {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder()
.append(this.type)
.append('#')
.append(this.value);
if (this.attribute == HoleAttribute.ELLIPSIS) {
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/cqfn/astranaut/rules/HoleAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,10 @@ public enum HoleAttribute {
/**
* The hole replaces all remaining children.
*/
ELLIPSIS
ELLIPSIS,

/**
* The hole replaces all remaining children of the specified type.
*/
TYPED
}
12 changes: 10 additions & 2 deletions src/main/java/org/cqfn/astranaut/scanner/HoleMarker.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,27 @@ public class HoleMarker implements Token {
*/
private final HoleAttribute attribute;

/**
* Type of the node that is matched by a hole.
*/
private final String type;

/**
* Constructor.
* @param value Value of the hole
* @param attribute Attribute
* @param type Type of the node
*/
public HoleMarker(final int value, final HoleAttribute attribute) {
public HoleMarker(final int value, final HoleAttribute attribute, final String type) {
this.value = value;
this.attribute = attribute;
this.type = type;
}

@Override
public final String toString() {
final StringBuilder builder = new StringBuilder()
.append(this.type)
.append('#')
.append(this.value);
if (this.attribute == HoleAttribute.ELLIPSIS) {
Expand All @@ -69,6 +77,6 @@ public final String toString() {
* @return A hole
*/
public Hole createHole() {
return new Hole(this.value, this.attribute);
return new Hole(this.value, this.attribute, this.type);
}
}
24 changes: 17 additions & 7 deletions src/main/java/org/cqfn/astranaut/scanner/Scanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ public Token getToken() throws ParserException {
}
final Token result;
if (Char.isLetter(symbol)) {
result = this.parseIdentifier(symbol);
final String identifier = this.parseIdentifier(symbol);
symbol = this.getChar();
if (symbol == '#') {
result = this.parseHoleMarker(identifier);
} else {
result = new Identifier(identifier);
}
} else if (Char.isBracket(symbol)) {
this.nextChar();
result = BracketFactory.INSTANCE.getObject(symbol);
Expand Down Expand Up @@ -110,16 +116,16 @@ private char nextChar() {
/**
* Parses an identifier.
* @param first The first symbol
* @return A token
* @return An identifier string
*/
private Identifier parseIdentifier(final char first) {
private String parseIdentifier(final char first) {
final StringBuilder builder = new StringBuilder();
char symbol = first;
do {
builder.append(symbol);
symbol = this.nextChar();
} while (Char.isLetter(symbol) || Char.isDigit(symbol));
return new Identifier(builder.toString());
return builder.toString();
}

/**
Expand All @@ -136,7 +142,7 @@ private Token parseTokenByFirstSymbol(final char symbol) throws ParserException
result = Null.INSTANCE;
break;
case '#':
result = this.parseHoleMarker();
result = this.parseHoleMarker("");
break;
case '\"':
result = this.parseString();
Expand Down Expand Up @@ -172,10 +178,11 @@ private Token parseTokenByFirstSymbol(final char symbol) throws ParserException

/**
* Parses a hole marker.
* @param type The type of node identifier
* @return A token
* @throws ParserException Parser exception
*/
private HoleMarker parseHoleMarker() throws ParserException {
private HoleMarker parseHoleMarker(final String type) throws ParserException {
final StringBuilder number = new StringBuilder();
char symbol = this.nextChar();
while (symbol >= '0' && symbol <= '9') {
Expand All @@ -198,8 +205,11 @@ private HoleMarker parseHoleMarker() throws ParserException {
throw IncorrectEllipsis.INSTANCE;
}
}
if (!type.isEmpty()) {
attribute = HoleAttribute.TYPED;
}
final int value = Integer.parseInt(number.toString());
return new HoleMarker(value, attribute);
return new HoleMarker(value, attribute, type);
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/org/cqfn/astranaut/interpreter/InterpreterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ void removeParentTest(@TempDir final Path temp) {
Assertions.assertTrue(result);
}

/**
* Testing holes with a node type.
* @param temp A temporary directory
*/
@Test
void typedHoleTest(@TempDir final Path temp) {
final boolean result = this.test("test_3", temp);
Assertions.assertTrue(result);
}

/**
* Testing running interpreter without a destination specified.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/cqfn/astranaut/interpreter/MatcherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ void testDataExtracting() {
final Node node = ctor.createNode();
final LabelFactory labels = new LabelFactory();
final DescriptorFactory factory = new DescriptorFactory(labels.getLabel(), type);
factory.setData(new Hole(0, HoleAttribute.NONE));
factory.setData(new Hole(0, HoleAttribute.NONE, ""));
final Descriptor descriptor = factory.createDescriptor();
final Matcher matcher = new Matcher(descriptor);
final Map<Integer, String> collection = new TreeMap<>();
Expand Down Expand Up @@ -170,7 +170,7 @@ void testChildrenExtracting() {
final Node node = ctor.createNode();
final LabelFactory labels = new LabelFactory();
final DescriptorFactory factory = new DescriptorFactory(labels.getLabel(), type);
factory.addParameter(new Hole(1, HoleAttribute.NONE));
factory.addParameter(new Hole(1, HoleAttribute.NONE, ""));
final Map<Integer, List<Node>> extracted = new TreeMap<>();
final boolean result = new Matcher(factory.createDescriptor())
.match(node, extracted, Collections.emptyMap());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ void moreComplexTransformation() {
Assertions.assertTrue(result);
}

/**
* Test case: transformation with a variable size children list of same type.
*/
@Test
void variableChildrenSizeTransformation() {
final boolean result = this.run(
String.format(
"%s -> %s",
"MethodDeclaration(Modifier#1, SimpleName, ClassType, Parameter#2)",
"MethodDeclaration(ModifierBlock(#1), SimpleName, ClassType, ParamBlock(#2))"
)
);
Assertions.assertTrue(result);
}

/**
* Test case: DSL line with exception.
*/
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/org/cqfn/astranaut/rules/DescriptorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ void hole() {
DescriptorTest.LABEL,
"numericLiteral"
);
factory.setData(new Hole(1, HoleAttribute.NONE));
factory.setData(new Hole(1, HoleAttribute.NONE, ""));
final Descriptor descriptor = factory.createDescriptor();
Assertions.assertEquals("numericLiteral<#1>", descriptor.toString());
}
Expand Down Expand Up @@ -158,8 +158,8 @@ void holeAsParameter() {
DescriptorTest.LABEL,
"simpleExpression"
);
factory.addParameter(new Hole(1, HoleAttribute.NONE));
factory.addParameter(new Hole(2, HoleAttribute.NONE));
factory.addParameter(new Hole(1, HoleAttribute.NONE, ""));
factory.addParameter(new Hole(2, HoleAttribute.NONE, ""));
final Descriptor descriptor = factory.createDescriptor();
Assertions.assertEquals(
"simpleExpression(#1, #2)",
Expand Down
Loading

0 comments on commit fbfa243

Please sign in to comment.