Skip to content

Commit

Permalink
Merge pull request #22 from seart-group/enhancement/treecursor
Browse files Browse the repository at this point in the history
  • Loading branch information
dabico committed Sep 28, 2023
2 parents d6513f2 + 60a7e4d commit bc2b5e8
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 8 deletions.
57 changes: 56 additions & 1 deletion lib/ch_usi_si_seart_treesitter_TreeCursor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ JNIEXPORT jobject JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_getCurrentT
);
}

JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild(
JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__(
JNIEnv* env, jobject thisObject) {
TSTreeCursor* cursor = (TSTreeCursor*)__getPointer(env, thisObject);
bool result = ts_tree_cursor_goto_first_child(cursor);
Expand All @@ -52,6 +52,61 @@ JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstC
return result ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__I(
JNIEnv* env, jobject thisObject, jint offset) {
if (offset < 0) {
__throwIAE(env, "Byte offset must not be negative!");
return JNI_FALSE;
}
// Not sure why I need to multiply by two, again probably because of utf-16
uint32_t childStart = (uint32_t)offset * 2;
TSTreeCursor* cursor = (TSTreeCursor*)__getPointer(env, thisObject);
TSNode node = ts_tree_cursor_current_node(cursor);
uint32_t nodeStart = ts_node_start_byte(node);
if (childStart < nodeStart) {
__throwIOB(env, offset);
return JNI_FALSE;
}
uint32_t nodeEnd = ts_node_end_byte(node);
if (childStart > nodeEnd) {
__throwIOB(env, offset);
return JNI_FALSE;
}
int64_t result = ts_tree_cursor_goto_first_child_for_byte(cursor, childStart);
env->SetIntField(thisObject, _treeCursorContext0Field, cursor->context[0]);
env->SetIntField(thisObject, _treeCursorContext1Field, cursor->context[1]);
return (result > -1) ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoFirstChild__Lch_usi_si_seart_treesitter_Point_2(
JNIEnv* env, jobject thisObject, jobject pointObject) {
if (pointObject == NULL) {
__throwNPE(env, "Point must not be null!");
return JNI_FALSE;
}
TSPoint point = __unmarshalPoint(env, pointObject);
if (point.row < 0 || point.column < 0) {
__throwIAE(env, "Point can not have negative coordinates!");
return JNI_FALSE;
}
TSTreeCursor* cursor = (TSTreeCursor*)__getPointer(env, thisObject);
TSNode node = ts_tree_cursor_current_node(cursor);
TSPoint lowerBound = ts_node_start_point(node);
if (__comparePoints(lowerBound, point) == GT) {
__throwIAE(env, "Point can not be outside of current node bounds!");
return JNI_FALSE;
}
TSPoint upperBound = ts_node_end_point(node);
if (__comparePoints(point, upperBound) == GT) {
__throwIAE(env, "Point can not be outside of current node bounds!");
return JNI_FALSE;
}
int64_t result = ts_tree_cursor_goto_first_child_for_point(cursor, point);
env->SetIntField(thisObject, _treeCursorContext0Field, cursor->context[0]);
env->SetIntField(thisObject, _treeCursorContext1Field, cursor->context[1]);
return (result > -1) ? JNI_TRUE : JNI_FALSE;
}

JNIEXPORT jboolean JNICALL Java_ch_usi_si_seart_treesitter_TreeCursor_gotoNextSibling(
JNIEnv* env, jobject thisObject) {
TSTreeCursor* cursor = (TSTreeCursor*)__getPointer(env, thisObject);
Expand Down
18 changes: 17 additions & 1 deletion lib/ch_usi_si_seart_treesitter_TreeCursor.h

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

10 changes: 10 additions & 0 deletions src/main/java/ch/usi/si/seart/treesitter/OffsetTreeCursor.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public boolean gotoFirstChild() {
return cursor.gotoFirstChild();
}

@Override
public boolean gotoFirstChild(int offset) {
throw new UnsupportedOperationException(UOE_MESSAGE_2);
}

@Override
public boolean gotoFirstChild(@NotNull Point point) {
return cursor.gotoFirstChild(point.subtract(offset));
}

@Override
public boolean gotoNextSibling() {
return cursor.gotoNextSibling();
Expand Down
38 changes: 32 additions & 6 deletions src/main/java/ch/usi/si/seart/treesitter/TreeCursor.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ protected TreeCursor() {
*/
public native boolean gotoFirstChild();

/**
* Move the cursor to the first child of its current node
* that extends beyond the given byte offset.
*
* @param offset the starting byte of the child
* @return true if the cursor successfully moved,
* and false if no such child was found
* @throws IllegalArgumentException if {@code offset} is negative
* @throws IndexOutOfBoundsException if {@code offset} is outside
* the current node's byte range
* @since 1.7.0
*/
public native boolean gotoFirstChild(int offset);

/**
* Move the cursor to the first child of its current node
* that extends beyond the given row-column offset.
*
* @param point the starting row-column position of the child
* @return true if the cursor successfully moved,
* and false if no such child was found
* @throws NullPointerException if {@code point} is null
* @throws IllegalArgumentException if {@code point} is
* outside the current node's positional span
* @since 1.7.0
*/
public native boolean gotoFirstChild(@NotNull Point point);

/**
* Move the cursor to the next sibling of its current node.
*
Expand Down Expand Up @@ -105,13 +133,11 @@ protected TreeCursor() {
public void preorderTraversal(@NotNull Consumer<Node> callback) {
Objects.requireNonNull(callback, "Callback must not be null!");
for (;;) {
callback.accept(this.getCurrentNode());
if (this.gotoFirstChild() || this.gotoNextSibling())
continue;
callback.accept(getCurrentNode());
if (gotoFirstChild() || gotoNextSibling()) continue;
do {
if (!this.gotoParent())
return;
} while (!this.gotoNextSibling());
if (!gotoParent()) return;
} while (!gotoNextSibling());
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/test/java/ch/usi/si/seart/treesitter/TreeCursorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,54 @@ void testWalk() {
Assertions.assertTrue(cursor.gotoFirstChild());
}

@Test
void testGotoFirstChildByteOffset() {
cursor.gotoFirstChild(); // function_definition
cursor.gotoFirstChild(4);
Assertions.assertEquals("identifier", cursor.getCurrentNode().getType());
cursor.gotoParent();
cursor.gotoFirstChild(21);
Assertions.assertEquals("block", cursor.getCurrentNode().getType());
cursor.gotoFirstChild(34);
Assertions.assertEquals("expression_statement", cursor.getCurrentNode().getType());
}

@Test
void testGotoFirstChildByteOffsetThrows() {
cursor.gotoFirstChild(); // function_definition
cursor.gotoFirstChild(); // identifier
cursor.gotoNextSibling(); // parameters
Assertions.assertThrows(IllegalArgumentException.class, () -> cursor.gotoFirstChild(-1));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> cursor.gotoFirstChild(0));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> cursor.gotoFirstChild(20));
}

@Test
void testGotoFirstChildPositionOffset() {
cursor.gotoFirstChild(); // function_definition
cursor.gotoFirstChild(new Point(0, 4));
Assertions.assertEquals("identifier", cursor.getCurrentNode().getType());
cursor.gotoParent();
cursor.gotoFirstChild(new Point(1, 2));
Assertions.assertEquals("block", cursor.getCurrentNode().getType());
cursor.gotoFirstChild(new Point(2, 2));
Assertions.assertEquals("expression_statement", cursor.getCurrentNode().getType());
}

@Test
void testGotoFirstChildPositionOffsetThrows() {
cursor.gotoFirstChild(); // function_definition
cursor.gotoFirstChild(); // identifier
cursor.gotoNextSibling(); // parameters
Point negative = new Point(0, -1);
Point illegal = new Point(1, 2);
Assertions.assertThrows(NullPointerException.class, () -> cursor.gotoFirstChild(null));
Assertions.assertThrows(IllegalArgumentException.class, () -> cursor.gotoFirstChild(negative));
Assertions.assertThrows(IllegalArgumentException.class, () -> cursor.gotoFirstChild(_0_0_));
Assertions.assertThrows(IllegalArgumentException.class, () -> cursor.gotoFirstChild(illegal));

}

@Test
void testPreorderTraversal() {
AtomicInteger count = new AtomicInteger();
Expand Down

0 comments on commit bc2b5e8

Please sign in to comment.