Skip to content

Commit

Permalink
Merge pull request #133 from seart-group/feature/quantifier
Browse files Browse the repository at this point in the history
  • Loading branch information
dabico committed Feb 14, 2024
2 parents 0a5bce1 + 2ef629d commit 3924946
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 0 deletions.
10 changes: 10 additions & 0 deletions lib/ch_usi_si_seart_treesitter_Query.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ JNIEXPORT void JNICALL Java_ch_usi_si_seart_treesitter_Query_delete(
ts_query_delete(query);
__clearPointer(env, thisObject);
}

JNIEXPORT jint JNICALL Java_ch_usi_si_seart_treesitter_Query_getQuantifier(
JNIEnv* env, jobject thisObject, jint patternIndex, jint captureIndex) {
TSQuery* query = (TSQuery*)__getPointer(env, thisObject);
return (jint)ts_query_capture_quantifier_for_id(
query,
(uint32_t)patternIndex,
(uint32_t)captureIndex
);
}
8 changes: 8 additions & 0 deletions lib/ch_usi_si_seart_treesitter_Query.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions src/main/java/ch/usi/si/seart/treesitter/Capture.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import lombok.experimental.NonFinal;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* Represents the named capture of a {@link Query}. Captures are used
Expand Down Expand Up @@ -51,6 +53,33 @@ public class Capture {
*/
public native void disable();

/**
* Get the capture quantifier for a given query {@link Pattern}.
*
* @param pattern the query pattern
* @return the quantifier
* @throws NullPointerException if pattern is {@code null}
* @throws IllegalArgumentException if the pattern is not present in the query
* @since 1.12.0
*/
public Quantifier getQuantifier(@NotNull Pattern pattern) {
return query.getQuantifier(pattern, this);
}

/**
* Get the capture quantifiers for all {@link Query} patterns.
* The order of the quantifiers in the returned list corresponds
* to the {@link Pattern} order in the query.
*
* @return the quantifiers
* @since 1.12.0
*/
public List<Quantifier> getQuantifiers() {
return query.getPatterns().stream()
.map(this::getQuantifier)
.collect(Collectors.toUnmodifiableList());
}

@Override
@Generated
public boolean equals(Object o) {
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/ch/usi/si/seart/treesitter/Quantifier.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ch.usi.si.seart.treesitter;

/**
* Represents the {@link Capture} quantifier in a {@link Pattern},
* i.e. the number of nodes that a capture should contain. Within a
* query, a capture can have different quantifiers for each pattern.
*
* @since 1.12.0
* @author Ozren Dabić
*/
public enum Quantifier {

/**
* The capture will not match any nodes,
* as said capture is not present in a
* specific pattern.
*/
ZERO,
/**
* The capture will match at most one node.
* Example:
* <pre>{@code
* ((_)? @capture)
* }</pre>
*/
ZERO_OR_ONE,
/**
* The capture will match any number of nodes.
* Example:
* <pre>{@code
* ((_)* @capture)
* }</pre>
*/
ZERO_OR_MORE,
/**
* The capture will match exactly one node.
* Example:
* <pre>{@code
* ((_) @capture)
* }</pre>
*/
ONE,
/**
* The capture will match at least one node.
* Example:
* <pre>{@code
* ((_)+ @capture)
* }</pre>
*/
ONE_OR_MORE
}
12 changes: 12 additions & 0 deletions src/main/java/ch/usi/si/seart/treesitter/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,16 @@ public String getPattern() {
.map(Pattern::toString)
.collect(Collectors.joining(" "));
}

Quantifier getQuantifier(@NotNull Pattern pattern, @NotNull Capture capture) {
Objects.requireNonNull(pattern, "Pattern must not be null!");
Objects.requireNonNull(capture, "Capture must not be null!");
if (!patterns.contains(pattern)) throw new IllegalArgumentException("Pattern not present in query!");
if (!captures.contains(capture)) throw new IllegalArgumentException("Capture not present in query!");
Quantifier[] quantifiers = Quantifier.values();
int ordinal = getQuantifier(pattern.getIndex(), capture.getIndex());
return quantifiers[ordinal];
}

private native int getQuantifier(int patternIndex, int captureIndex);
}
84 changes: 84 additions & 0 deletions src/test/java/ch/usi/si/seart/treesitter/QuantifierTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package ch.usi.si.seart.treesitter;

import lombok.Cleanup;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

class QuantifierTest extends BaseTest {

private static final Language language = Language.JAVA;

private static final Map<Quantifier, String> symbols = Map.of(
Quantifier.ONE, "",
Quantifier.ONE_OR_MORE, "+",
Quantifier.ZERO_OR_ONE, "?",
Quantifier.ZERO_OR_MORE, "*"
);

private static final class QuantifierArgumentsProvider implements ArgumentsProvider {

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return symbols.entrySet().stream()
.map(entry -> {
Quantifier quantifier = entry.getKey();
String symbol = entry.getValue();
String pattern = "((_)" + symbol + " @capture)";
Query query = Query.getFor(language, pattern);
List<Capture> captures = query.getCaptures();
Capture capture = captures.stream().findFirst().orElseThrow();
return Arguments.of(quantifier, capture, query);
});
}
}

@ParameterizedTest(name = "[{index}] {0}")
@ArgumentsSource(QuantifierArgumentsProvider.class)
void testGetQuantifiers(Quantifier expected, Capture capture, Query query) {
List<Quantifier> quantifiers = capture.getQuantifiers();
Assertions.assertNotNull(quantifiers);
Assertions.assertFalse(quantifiers.isEmpty());
Assertions.assertEquals(1, quantifiers.size());
Quantifier actual = quantifiers.stream()
.findFirst()
.orElseGet(Assertions::fail);
Assertions.assertEquals(expected, actual);
query.close();
}

@Test
void testGetQuantifier() {
@Cleanup Query query = Query.builder()
.language(language)
.pattern("((_) @capture)")
.pattern("(identifier)")
.build();
List<Capture> captures = query.getCaptures();
List<Pattern> patterns = query.getPatterns();
Capture capture = captures.stream().findFirst().orElseThrow();
Pattern pattern = patterns.stream().skip(1).findFirst().orElseThrow();
Quantifier quantifier = capture.getQuantifier(pattern);
Assertions.assertEquals(Quantifier.ZERO, quantifier);
}

@Test
void testGetQuantifierThrows() {
@Cleanup Query original = Query.getFor(language, "((_) @capture)");
@Cleanup Query copy = Query.getFor(language, "((_) @capture)");
List<Capture> captures = original.getCaptures();
Capture capture = captures.stream().findFirst().orElseThrow();
List<Pattern> patterns = copy.getPatterns();
Pattern pattern = patterns.stream().findFirst().orElseThrow();
Assertions.assertThrows(NullPointerException.class, () -> capture.getQuantifier(null));
Assertions.assertThrows(IllegalArgumentException.class, () -> capture.getQuantifier(pattern));
}
}

0 comments on commit 3924946

Please sign in to comment.