Current section
6 Versions
Jump to
Current section
6 Versions
Compare versions
4
files changed
+286
additions
-33
deletions
| @@ -2,7 +2,7 @@ | |
| 2 2 | [{<<"issues">>,<<"https://codeberg.org/edwinvanl/treesitter_elixir/issues">>}, |
| 3 3 | {<<"source">>,<<"https://codeberg.org/edwinvanl/treesitter_elixir">>}]}. |
| 4 4 | {<<"name">>,<<"treesitter_elixir">>}. |
| 5 | - {<<"version">>,<<"0.1.1">>}. |
| 5 | + {<<"version">>,<<"0.1.2">>}. |
| 6 6 | {<<"description">>, |
| 7 7 | <<"Tree-sitter bindings (nif) for elixir. Currently small subset of the tree-sitter api is supported, but happy to accept merge requests/patches">>}. |
| 8 8 | {<<"elixir">>,<<"~> 1.18">>}. |
| @@ -26,11 +26,11 @@ defmodule TreeSitter.Nif do | |
| 26 26 | end |
| 27 27 | |
| 28 28 | def parser_new() do |
| 29 | - raise "NIF not implemented" |
| 29 | + :erlang.nif_error(:nif_not_loaded) |
| 30 30 | end |
| 31 31 | |
| 32 32 | @doc """ |
| 33 | - Set the langage given a full name of the language and the path. |
| 33 | + Get the langage given a full name of the language and the path to the shared library implementing the language. |
| 34 34 | |
| 35 35 | `language_path` is a useful helper function to get the full language name and path if needed |
| 36 36 | |
| @@ -38,57 +38,94 @@ defmodule TreeSitter.Nif do | |
| 38 38 | |
| 39 39 | TreeSitter.Nif.language("tree_sitter_c", "/usr/lib/libtree-sitter-c.so") |
| 40 40 | """ |
| 41 | - def parser_set_language(parser, language, full_path) |
| 41 | + def language(language, full_path) |
| 42 42 | |
| 43 | - def parser_set_language(_, _, _) do |
| 44 | - raise "NIF not implemented" |
| 43 | + def language(_, _) do |
| 44 | + :erlang.nif_error(:nif_not_loaded) |
| 45 | + end |
| 46 | + |
| 47 | + def parser_set_language(parser, language) |
| 48 | + |
| 49 | + def parser_set_language(_, _) do |
| 50 | + :erlang.nif_error(:nif_not_loaded) |
| 45 51 | end |
| 46 52 | |
| 47 53 | def parser_parse_string(_, _) do |
| 48 | - raise "NIF not implemented" |
| 54 | + :erlang.nif_error(:nif_not_loaded) |
| 49 55 | end |
| 50 56 | |
| 51 57 | def parse(_, _) do |
| 52 | - raise "NIF not implemented" |
| 58 | + :erlang.nif_error(:nif_not_loaded) |
| 53 59 | end |
| 54 60 | |
| 55 61 | def tree_root_node(_) do |
| 56 | - raise "NIF not implemented" |
| 62 | + :erlang.nif_error(:nif_not_loaded) |
| 57 63 | end |
| 58 64 | |
| 59 65 | def node_parent(_) do |
| 60 | - raise "NIF not implemented" |
| 66 | + :erlang.nif_error(:nif_not_loaded) |
| 61 67 | end |
| 62 68 | |
| 63 69 | def node_child(_) do |
| 64 | - raise "NIF not implemented" |
| 70 | + :erlang.nif_error(:nif_not_loaded) |
| 65 71 | end |
| 66 72 | |
| 67 73 | def node_next_sibling(_) do |
| 68 | - raise "NIF not implemented" |
| 74 | + :erlang.nif_error(:nif_not_loaded) |
| 69 75 | end |
| 70 76 | |
| 71 77 | def node_prev_sibling(_) do |
| 72 | - raise "NIF not implemented" |
| 78 | + :erlang.nif_error(:nif_not_loaded) |
| 73 79 | end |
| 74 80 | |
| 75 81 | def node_string(_) do |
| 76 | - raise "NIF not implemented" |
| 82 | + :erlang.nif_error(:nif_not_loaded) |
| 77 83 | end |
| 78 84 | |
| 79 85 | def node_type(_) do |
| 80 | - raise "NIF not implemented" |
| 86 | + :erlang.nif_error(:nif_not_loaded) |
| 81 87 | end |
| 82 88 | |
| 83 89 | @doc """ |
| 84 90 | Return the start and end index of the bytestring |
| 85 91 | """ |
| 86 92 | def node_byte_range(_) do |
| 87 | - raise "NIF not implemented" |
| 93 | + :erlang.nif_error(:nif_not_loaded) |
| 94 | + # :erlang.nif_error(:nif_not_loaded) |
| 88 95 | end |
| 89 96 | |
| 90 97 | def node_point_range(_) do |
| 91 | - raise "NIF not implemented" |
| 98 | + :erlang.nif_error(:nif_not_loaded) |
| 99 | + end |
| 100 | + |
| 101 | + def query_new(language, query_string) |
| 102 | + |
| 103 | + def query_new(_, _) do |
| 104 | + :erlang.nif_error(:nif_not_loaded) |
| 105 | + end |
| 106 | + |
| 107 | + def query_cursor_exec(query, node) |
| 108 | + |
| 109 | + def query_cursor_exec(_, _) do |
| 110 | + :erlang.nif_error(:nif_not_loaded) |
| 111 | + end |
| 112 | + |
| 113 | + def query_cursor_next_match(cursor) |
| 114 | + |
| 115 | + def query_cursor_next_match(_) do |
| 116 | + :erlang.nif_error(:nif_not_loaded) |
| 117 | + end |
| 118 | + |
| 119 | + def query_pattern_byte_range(query, pattern_index) |
| 120 | + |
| 121 | + def query_pattern_byte_range(_, _) do |
| 122 | + :erlang.nif_error(:nif_not_loaded) |
| 123 | + end |
| 124 | + |
| 125 | + def query_capture_name_for_id(query, pattern_index) |
| 126 | + |
| 127 | + def query_capture_name_for_id(_, _) do |
| 128 | + :erlang.nif_error(:nif_not_loaded) |
| 92 129 | end |
| 93 130 | end |
| @@ -6,7 +6,7 @@ defmodule Treesitter.MixProject do | |
| 6 6 | app: :treesitter_elixir, |
| 7 7 | description: |
| 8 8 | "Tree-sitter bindings (nif) for elixir. Currently small subset of the tree-sitter api is supported, but happy to accept merge requests/patches", |
| 9 | - version: "0.1.1", |
| 9 | + version: "0.1.2", |
| 10 10 | elixir: "~> 1.18", |
| 11 11 | compilers: [:elixir_make] ++ Mix.compilers(), |
| 12 12 | start_permanent: Mix.env() == :prod, |
| @@ -1,11 +1,16 @@ | |
| 1 1 | #include "erl_nif.h" |
| 2 2 | #include "tree_sitter/api.h" |
| 3 3 | #include <dlfcn.h> |
| 4 | + #include <stdint.h> |
| 4 5 | |
| 5 6 | // Setup external resources |
| 6 7 | static ErlNifResourceType *parser_resource_type = NULL; |
| 7 8 | static ErlNifResourceType *tree_resource_type = NULL; |
| 8 9 | static ErlNifResourceType *node_resource_type = NULL; |
| 10 | + static ErlNifResourceType *language_resource_type = NULL; |
| 11 | + static ErlNifResourceType *query_resource_type = NULL; |
| 12 | + static ErlNifResourceType *querycursor_resource_type = NULL; |
| 13 | + |
| 9 14 | // Wrappers |
| 10 15 | typedef struct { |
| 11 16 | TSParser *data; |
| @@ -20,9 +25,22 @@ typedef struct { | |
| 20 25 | // freed first. We could have a wrapper that has the parser, the tree, and the |
| 21 26 | // current node? |
| 22 27 | // Could also hold the source_code/text |
| 28 | + // I think this is why the cursor stuff exists, so maybe rely on that. |
| 23 29 | TSNode data; |
| 24 30 | } WRNode; |
| 25 31 | |
| 32 | + typedef struct { |
| 33 | + TSLanguage *data; |
| 34 | + } WRLanguage; |
| 35 | + |
| 36 | + typedef struct { |
| 37 | + TSQuery *data; |
| 38 | + } WRQuery; |
| 39 | + |
| 40 | + typedef struct { |
| 41 | + TSQueryCursor *data; |
| 42 | + } WRQueryCursor; |
| 43 | + |
| 26 44 | static void parser_dtor(ErlNifEnv *env, void *obj) { |
| 27 45 | WRParser *my_struct = (WRParser *)obj; |
| 28 46 | if (my_struct->data) { |
| @@ -45,6 +63,30 @@ static void node_dtor(ErlNifEnv *env, void *obj) { | |
| 45 63 | enif_release_resource(my_struct); |
| 46 64 | } |
| 47 65 | |
| 66 | + static void language_dtor(ErlNifEnv *env, void *obj) { |
| 67 | + WRLanguage *my_struct = (WRLanguage *)obj; |
| 68 | + if (my_struct->data) { |
| 69 | + ts_language_delete(my_struct->data); |
| 70 | + } |
| 71 | + enif_release_resource(my_struct); |
| 72 | + } |
| 73 | + |
| 74 | + static void query_dtor(ErlNifEnv *env, void *obj) { |
| 75 | + WRQuery *my_struct = (WRQuery *)obj; |
| 76 | + if (my_struct->data) { |
| 77 | + ts_query_delete(my_struct->data); |
| 78 | + } |
| 79 | + enif_release_resource(my_struct); |
| 80 | + } |
| 81 | + |
| 82 | + static void querycursor_dtor(ErlNifEnv *env, void *obj) { |
| 83 | + WRQueryCursor *my_struct = (WRQueryCursor *)obj; |
| 84 | + if (my_struct->data) { |
| 85 | + ts_query_cursor_delete(my_struct->data); |
| 86 | + } |
| 87 | + enif_release_resource(my_struct); |
| 88 | + } |
| 89 | + |
| 48 90 | static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { |
| 49 91 | ErlNifResourceFlags flags = |
| 50 92 | (ErlNifResourceFlags)(ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER); |
| @@ -56,6 +98,12 @@ static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { | |
| 56 98 | |
| 57 99 | node_resource_type = enif_open_resource_type(env, NULL, "node_resource", |
| 58 100 | node_dtor, flags, NULL); |
| 101 | + language_resource_type = enif_open_resource_type( |
| 102 | + env, NULL, "language_resource", language_dtor, flags, NULL); |
| 103 | + query_resource_type = enif_open_resource_type(env, NULL, "query_resource", |
| 104 | + query_dtor, flags, NULL); |
| 105 | + querycursor_resource_type = enif_open_resource_type( |
| 106 | + env, NULL, "querycursor_resource", querycursor_dtor, flags, NULL); |
| 59 107 | return 0; |
| 60 108 | } |
| 61 109 | |
| @@ -86,23 +134,17 @@ static ERL_NIF_TERM parser_new(ErlNifEnv *env, int argc, | |
| 86 134 | } |
| 87 135 | |
| 88 136 | typedef TSLanguage *(tree_sitter_lang)(void); |
| 89 | - static ERL_NIF_TERM parser_set_language(ErlNifEnv *env, int argc, |
| 90 | - const ERL_NIF_TERM argv[]) { |
| 137 | + static ERL_NIF_TERM language(ErlNifEnv *env, int argc, |
| 138 | + const ERL_NIF_TERM argv[]) { |
| 91 139 | char language[1024]; |
| 92 140 | char lib_path[1024]; |
| 93 | - if (!enif_get_string(env, argv[1], language, 1024, ERL_NIF_UTF8)) { |
| 141 | + if (!enif_get_string(env, argv[0], language, 1024, ERL_NIF_UTF8)) { |
| 94 142 | return to_error_string(env, language); |
| 95 143 | } |
| 96 | - if (!enif_get_string(env, argv[2], lib_path, 1024, ERL_NIF_UTF8)) { |
| 144 | + if (!enif_get_string(env, argv[1], lib_path, 1024, ERL_NIF_UTF8)) { |
| 97 145 | return to_error_string(env, lib_path); |
| 98 146 | } |
| 99 147 | |
| 100 | - WRParser *wrap; |
| 101 | - if (!enif_get_resource(env, argv[0], parser_resource_type, (void **)&wrap)) { |
| 102 | - return to_error_string(env, "Unable to unwrap parser"); |
| 103 | - } |
| 104 | - |
| 105 | - // Close lib properly, currently seems to result in segmentation vault. |
| 106 148 | void *lib = dlopen(lib_path, RTLD_NOW); |
| 107 149 | const char *err = dlerror(); |
| 108 150 | if (err != NULL) { |
| @@ -110,9 +152,26 @@ static ERL_NIF_TERM parser_set_language(ErlNifEnv *env, int argc, | |
| 110 152 | } |
| 111 153 | |
| 112 154 | tree_sitter_lang *make_ts_language = (tree_sitter_lang *)dlsym(lib, language); |
| 113 | - TSLanguage *lang = make_ts_language(); |
| 114 | - if (!ts_parser_set_language(wrap->data, lang)) { |
| 115 | - dlclose(lib); |
| 155 | + WRLanguage *wrap = |
| 156 | + enif_alloc_resource(language_resource_type, sizeof(WRLanguage)); |
| 157 | + wrap->data = make_ts_language(); |
| 158 | + return to_ok_term(env, enif_make_resource(env, wrap)); |
| 159 | + } |
| 160 | + |
| 161 | + static ERL_NIF_TERM parser_set_language(ErlNifEnv *env, int argc, |
| 162 | + const ERL_NIF_TERM argv[]) { |
| 163 | + WRParser *wrap; |
| 164 | + if (!enif_get_resource(env, argv[0], parser_resource_type, (void **)&wrap)) { |
| 165 | + return to_error_string(env, "Unable to unwrap parser"); |
| 166 | + } |
| 167 | + |
| 168 | + WRLanguage *lang_wrap; |
| 169 | + if (!enif_get_resource(env, argv[1], language_resource_type, |
| 170 | + (void **)&lang_wrap)) { |
| 171 | + return to_error_string(env, "Unable to unwrap language"); |
| 172 | + } |
| 173 | + |
| 174 | + if (!ts_parser_set_language(wrap->data, lang_wrap->data)) { |
| 116 175 | return to_error_string(env, "Unable to set the language to the parser"); |
| 117 176 | } |
| 118 177 | |
| @@ -142,6 +201,11 @@ static ERL_NIF_TERM parse(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { | |
| 142 201 | free(source_code); |
| 143 202 | return to_ok_term(env, enif_make_resource(env, wraptree)); |
| 144 203 | } |
| 204 | + static ERL_NIF_TERM enif_make_node_wrap(ErlNifEnv *env, const TSNode node) { |
| 205 | + WRNode *wrapnode = enif_alloc_resource(node_resource_type, sizeof(WRNode)); |
| 206 | + wrapnode->data = node; |
| 207 | + return enif_make_resource(env, wrapnode); |
| 208 | + } |
| 145 209 | |
| 146 210 | static ERL_NIF_TERM tree_root_node(ErlNifEnv *env, int argc, |
| 147 211 | const ERL_NIF_TERM argv[]) { |
| @@ -283,9 +347,156 @@ static ERL_NIF_TERM node_point_range(ErlNifEnv *env, int argc, | |
| 283 347 | return enif_make_tuple2(env, point_to_tuple(env, i), point_to_tuple(env, j)); |
| 284 348 | } |
| 285 349 | |
| 350 | + /** |
| 351 | + * Query |
| 352 | + */ |
| 353 | + // TODO: Have a tslanguage wrapper and support getting it |
| 354 | + // integrate this above |
| 355 | + // TSQueryCapture to ERL_NIF_TERM (list of nodes) |
| 356 | + // TSQueryMatch to ERL_NIF_TERM (dict with pattern_index and the list of |
| 357 | + // nodes(tsqueraycapture) |
| 358 | + |
| 359 | + // TSQuery *ts_query_new( |
| 360 | + // const TSLanguage *language, |
| 361 | + // const char *source, |
| 362 | + // uint32_t source_len, |
| 363 | + // uint32_t *error_offset, |
| 364 | + // TSQueryError *error_type |
| 365 | + // ); |
| 366 | + |
| 367 | + static ERL_NIF_TERM query_new(ErlNifEnv *env, int argc, |
| 368 | + const ERL_NIF_TERM argv[]) { |
| 369 | + WRLanguage *lang_wrap; |
| 370 | + if (!enif_get_resource(env, argv[0], language_resource_type, |
| 371 | + (void **)&lang_wrap)) { |
| 372 | + return to_error_string(env, "Unable to unwrap language"); |
| 373 | + } |
| 374 | + unsigned int len; |
| 375 | + enif_get_string_length(env, argv[1], &len, ERL_NIF_UTF8); |
| 376 | + char *source_code = malloc(len + 1); |
| 377 | + if (!enif_get_string(env, argv[1], source_code, len + 1, ERL_NIF_UTF8)) { |
| 378 | + return to_error_string(env, "Failed loading source code"); |
| 379 | + } |
| 380 | + |
| 381 | + WRQuery *wrap = enif_alloc_resource(query_resource_type, sizeof(WRQuery)); |
| 382 | + uint32_t error_offset; |
| 383 | + TSQueryError error_type; |
| 384 | + wrap->data = ts_query_new(lang_wrap->data, source_code, len, &error_offset, |
| 385 | + &error_type); |
| 386 | + if (wrap->data) { |
| 387 | + return to_ok_term(env, enif_make_resource(env, wrap)); |
| 388 | + } |
| 389 | + char error_string[50]; |
| 390 | + sprintf(error_string, "Found an error in the query at %d, type %d", |
| 391 | + error_offset, error_type); |
| 392 | + |
| 393 | + return to_error_string(env, error_string); |
| 394 | + } |
| 395 | + |
| 396 | + // void ts_query_cursor_exec(TSQueryCursor *, const TSQuery *, TSNode); |
| 397 | + static ERL_NIF_TERM query_cursor_exec(ErlNifEnv *env, int argc, |
| 398 | + const ERL_NIF_TERM argv[]) { |
| 399 | + WRQuery *query_wrap; |
| 400 | + if (!enif_get_resource(env, argv[0], query_resource_type, |
| 401 | + (void **)&query_wrap)) { |
| 402 | + return to_error_string(env, "Unable to unwrap query"); |
| 403 | + } |
| 404 | + WRNode *node_wrap; |
| 405 | + if (!enif_get_resource(env, argv[1], node_resource_type, |
| 406 | + (void **)&node_wrap)) { |
| 407 | + return to_error_string(env, "Unable to unwrap node"); |
| 408 | + } |
| 409 | + WRQueryCursor *wrap = |
| 410 | + enif_alloc_resource(querycursor_resource_type, sizeof(WRQueryCursor)); |
| 411 | + wrap->data = ts_query_cursor_new(); |
| 412 | + if (wrap->data) { |
| 413 | + ts_query_cursor_exec(wrap->data, query_wrap->data, node_wrap->data); |
| 414 | + |
| 415 | + return to_ok_term(env, enif_make_resource(env, wrap)); |
| 416 | + } |
| 417 | + return to_error_string(env, "Unable to create cursor"); |
| 418 | + } |
| 419 | + |
| 420 | + // bool ts_query_cursor_next_match(TSQueryCursor *, TSQueryMatch *match); |
| 421 | + // TODO: Return the cursor? |
| 422 | + static ERL_NIF_TERM query_cursor_next_match(ErlNifEnv *env, int argc, |
| 423 | + const ERL_NIF_TERM argv[]) { |
| 424 | + WRQueryCursor *querycursor_wrap; |
| 425 | + if (!enif_get_resource(env, argv[0], querycursor_resource_type, |
| 426 | + (void **)&querycursor_wrap)) { |
| 427 | + return to_error_string(env, "Unable to unwrap querycursor"); |
| 428 | + } |
| 429 | + TSQueryMatch match; |
| 430 | + if (!ts_query_cursor_next_match(querycursor_wrap->data, &match)) { |
| 431 | + return to_error_nil(env); |
| 432 | + } |
| 433 | + // Add them to an list |
| 434 | + ERL_NIF_TERM *lst = |
| 435 | + (ERL_NIF_TERM *)malloc(match.capture_count * sizeof(ERL_NIF_TERM)); |
| 436 | + for (int i = 0; i < match.capture_count; ++i) { |
| 437 | + lst[i] = enif_make_node_wrap(env, match.captures[i].node); |
| 438 | + } |
| 439 | + ERL_NIF_TERM arr = enif_make_list_from_array(env, lst, match.capture_count); |
| 440 | + free(lst); |
| 441 | + // TODO: Do we ever need the match id and pattern_index? |
| 442 | + return enif_make_tuple2( |
| 443 | + env, enif_make_atom(env, "ok"), |
| 444 | + enif_make_tuple3(env, enif_make_int(env, match.id), |
| 445 | + enif_make_int(env, match.pattern_index), arr)); |
| 446 | + } |
| 447 | + |
| 448 | + static ERL_NIF_TERM query_pattern_byte_range(ErlNifEnv *env, int argc, |
| 449 | + const ERL_NIF_TERM argv[]) { |
| 450 | + // TODO: Can we make this into a function |
| 451 | + WRQuery *query_wrap; |
| 452 | + if (!enif_get_resource(env, argv[0], query_resource_type, |
| 453 | + (void **)&query_wrap)) { |
| 454 | + return to_error_string(env, "Unable to unwrap query"); |
| 455 | + } |
| 456 | + |
| 457 | + uint32_t pattern_index; |
| 458 | + enif_get_uint(env, argv[1], &pattern_index); |
| 459 | + |
| 460 | + uint32_t byte_start = |
| 461 | + ts_query_start_byte_for_pattern(query_wrap->data, pattern_index); |
| 462 | + uint32_t byte_end = |
| 463 | + ts_query_end_byte_for_pattern(query_wrap->data, pattern_index); |
| 464 | + return enif_make_tuple2(env, enif_make_int(env, byte_start), |
| 465 | + enif_make_int(env, byte_end)); |
| 466 | + } |
| 467 | + |
| 468 | + /** |
| 469 | + * Get the name and length of one of the query's captures, or one of the |
| 470 | + * query's string literals. Each capture and string is associated with a |
| 471 | + * numeric id based on the order that it appeared in the query's source. |
| 472 | + */ |
| 473 | + // const char *ts_query_capture_name_for_id( |
| 474 | + // const TSQuery *self, |
| 475 | + // uint32_t index, |
| 476 | + // uint32_t *length |
| 477 | + // ); |
| 478 | + static ERL_NIF_TERM query_capture_name_for_id(ErlNifEnv *env, int argc, |
| 479 | + const ERL_NIF_TERM argv[]) { |
| 480 | + // TODO: Can we make this into a function |
| 481 | + WRQuery *query_wrap; |
| 482 | + if (!enif_get_resource(env, argv[0], query_resource_type, |
| 483 | + (void **)&query_wrap)) { |
| 484 | + return to_error_string(env, "Unable to unwrap query"); |
| 485 | + } |
| 486 | + |
| 487 | + uint32_t pattern_index; |
| 488 | + enif_get_uint(env, argv[1], &pattern_index); |
| 489 | + int len; |
| 490 | + const char *name = |
| 491 | + ts_query_capture_name_for_id(query_wrap->data, pattern_index, &len); |
| 492 | + return enif_make_tuple2(env, enif_make_atom(env, "ok"), |
| 493 | + enif_make_string(env, name, ERL_NIF_UTF8)); |
| 494 | + } |
| 495 | + |
| 286 496 | static ErlNifFunc nif_funcs[] = { |
| 287 497 | {"parser_new", 0, parser_new, 0}, |
| 288 | - {"parser_set_language", 3, parser_set_language, 0}, |
| 498 | + {"language", 2, language, 0}, |
| 499 | + {"parser_set_language", 2, parser_set_language, 0}, |
| 289 500 | {"parse", 2, parse, 0}, |
| 290 501 | {"tree_root_node", 1, tree_root_node, 0}, |
| 291 502 | {"node_parent", 1, node_parent, 0}, |
| @@ -295,6 +506,11 @@ static ErlNifFunc nif_funcs[] = { | |
| 295 506 | {"node_string", 1, node_string, 0}, |
| 296 507 | {"node_type", 1, node_type, 0}, |
| 297 508 | {"node_byte_range", 1, node_byte_range, 0}, |
| 298 | - {"node_point_range", 1, node_point_range, 0}}; |
| 509 | + {"node_point_range", 1, node_point_range, 0}, |
| 510 | + {"query_new", 2, query_new, 0}, |
| 511 | + {"query_cursor_exec", 2, query_cursor_exec, 0}, |
| 512 | + {"query_cursor_next_match", 1, query_cursor_next_match, 0}, |
| 513 | + {"query_pattern_byte_range", 2, query_pattern_byte_range, 0}, |
| 514 | + {"query_capture_name_for_id", 2, query_capture_name_for_id, 0}}; |
| 299 515 | |
| 300 516 | ERL_NIF_INIT(Elixir.TreeSitter.Nif, nif_funcs, load, NULL, NULL, NULL) |