Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Quantifier enum #133

Merged
merged 5 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}
}
Loading