ruby_tree_sitter 1.6.0-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +213 -0
  4. data/ext/tree_sitter/encoding.c +29 -0
  5. data/ext/tree_sitter/extconf.rb +149 -0
  6. data/ext/tree_sitter/input.c +127 -0
  7. data/ext/tree_sitter/input_edit.c +42 -0
  8. data/ext/tree_sitter/language.c +219 -0
  9. data/ext/tree_sitter/logger.c +228 -0
  10. data/ext/tree_sitter/macros.h +163 -0
  11. data/ext/tree_sitter/node.c +623 -0
  12. data/ext/tree_sitter/parser.c +398 -0
  13. data/ext/tree_sitter/point.c +26 -0
  14. data/ext/tree_sitter/quantifier.c +43 -0
  15. data/ext/tree_sitter/query.c +289 -0
  16. data/ext/tree_sitter/query_capture.c +28 -0
  17. data/ext/tree_sitter/query_cursor.c +231 -0
  18. data/ext/tree_sitter/query_error.c +41 -0
  19. data/ext/tree_sitter/query_match.c +44 -0
  20. data/ext/tree_sitter/query_predicate_step.c +83 -0
  21. data/ext/tree_sitter/range.c +35 -0
  22. data/ext/tree_sitter/repo.rb +128 -0
  23. data/ext/tree_sitter/symbol_type.c +46 -0
  24. data/ext/tree_sitter/tree.c +234 -0
  25. data/ext/tree_sitter/tree_cursor.c +269 -0
  26. data/ext/tree_sitter/tree_sitter.c +44 -0
  27. data/ext/tree_sitter/tree_sitter.h +107 -0
  28. data/lib/tree_sitter/3.0/tree_sitter.so +0 -0
  29. data/lib/tree_sitter/3.1/tree_sitter.so +0 -0
  30. data/lib/tree_sitter/3.2/tree_sitter.so +0 -0
  31. data/lib/tree_sitter/3.3/tree_sitter.so +0 -0
  32. data/lib/tree_sitter/helpers.rb +23 -0
  33. data/lib/tree_sitter/mixins/language.rb +167 -0
  34. data/lib/tree_sitter/node.rb +167 -0
  35. data/lib/tree_sitter/query.rb +191 -0
  36. data/lib/tree_sitter/query_captures.rb +30 -0
  37. data/lib/tree_sitter/query_cursor.rb +27 -0
  38. data/lib/tree_sitter/query_match.rb +100 -0
  39. data/lib/tree_sitter/query_matches.rb +39 -0
  40. data/lib/tree_sitter/query_predicate.rb +14 -0
  41. data/lib/tree_sitter/text_predicate_capture.rb +37 -0
  42. data/lib/tree_sitter/version.rb +8 -0
  43. data/lib/tree_sitter.rb +34 -0
  44. data/lib/tree_stand/ast_modifier.rb +30 -0
  45. data/lib/tree_stand/breadth_first_visitor.rb +54 -0
  46. data/lib/tree_stand/config.rb +19 -0
  47. data/lib/tree_stand/node.rb +351 -0
  48. data/lib/tree_stand/parser.rb +87 -0
  49. data/lib/tree_stand/range.rb +55 -0
  50. data/lib/tree_stand/tree.rb +123 -0
  51. data/lib/tree_stand/utils/printer.rb +73 -0
  52. data/lib/tree_stand/version.rb +7 -0
  53. data/lib/tree_stand/visitor.rb +127 -0
  54. data/lib/tree_stand/visitors/tree_walker.rb +37 -0
  55. data/lib/tree_stand.rb +48 -0
  56. data/tree_sitter.gemspec +34 -0
  57. metadata +135 -0
@@ -0,0 +1,269 @@
1
+ #include "tree_sitter.h"
2
+
3
+ extern VALUE mTreeSitter;
4
+
5
+ VALUE cTreeCursor;
6
+
7
+ DATA_TYPE(TSTreeCursor, tree_cursor)
8
+ static void tree_cursor_free(void *ptr) {
9
+ tree_cursor_t *type = (tree_cursor_t *)ptr;
10
+ ts_tree_cursor_delete(&type->data);
11
+ xfree(ptr);
12
+ }
13
+ DATA_MEMSIZE(tree_cursor)
14
+ DATA_DECLARE_DATA_TYPE(tree_cursor)
15
+ DATA_ALLOCATE(tree_cursor)
16
+ DATA_UNWRAP(tree_cursor)
17
+ DATA_NEW(cTreeCursor, TSTreeCursor, tree_cursor)
18
+ DATA_FROM_VALUE(TSTreeCursor, tree_cursor)
19
+
20
+ /**
21
+ * Safely copy a tree cursor.
22
+ *
23
+ * @return [TreeCursor]
24
+ */
25
+ static VALUE tree_cursor_copy(VALUE self) {
26
+ VALUE res = tree_cursor_allocate(cTreeCursor);
27
+ tree_cursor_t *ptr = unwrap(res);
28
+ ptr->data = ts_tree_cursor_copy(&SELF);
29
+ return res;
30
+ }
31
+
32
+ /**
33
+ * Get the depth of the cursor's current node relative to the original
34
+ * node that the cursor was constructed with.
35
+ *
36
+ * @return [Integer]
37
+ */
38
+ static VALUE tree_cursor_current_depth(VALUE self) {
39
+ return UINT2NUM(ts_tree_cursor_current_depth(&SELF));
40
+ }
41
+
42
+ /**
43
+ * Get the index of the cursor's current node out of all of the
44
+ * descendants of the original node that the cursor was constructed with.
45
+ *
46
+ * @return [Integer]
47
+ */
48
+ static VALUE tree_cursor_current_descendant_index(VALUE self) {
49
+ return UINT2NUM(ts_tree_cursor_current_descendant_index(&SELF));
50
+ }
51
+
52
+ /**
53
+ * Get the field id of the tree cursor's current node.
54
+ *
55
+ * This returns zero if the current node doesn't have a field.
56
+ *
57
+ * @see Node#child_by_field_id
58
+ * @see Node#field_id_for_name
59
+ *
60
+ * @return [Integer]
61
+ */
62
+ static VALUE tree_cursor_current_field_id(VALUE self) {
63
+ return UINT2NUM(ts_tree_cursor_current_field_id(&SELF));
64
+ }
65
+
66
+ /**
67
+ * Get the field name of the tree cursor's current node.
68
+ *
69
+ * This returns +nil+ if the current node doesn't have a field.
70
+ *
71
+ * @see Node#child_by_field_name
72
+ *
73
+ * @return [String]
74
+ */
75
+ static VALUE tree_cursor_current_field_name(VALUE self) {
76
+ return safe_str(ts_tree_cursor_current_field_name(&SELF));
77
+ }
78
+
79
+ /**
80
+ * Get the tree cursor's current node.
81
+ *
82
+ * @return [Node]
83
+ */
84
+ static VALUE tree_cursor_current_node(VALUE self) {
85
+ TSNode node = ts_tree_cursor_current_node(&SELF);
86
+ return new_node(&node);
87
+ }
88
+
89
+ /**
90
+ * Move the cursor to the node that is the nth descendant of
91
+ * the original node that the cursor was constructed with, where
92
+ * zero represents the original node itself.
93
+ *
94
+ * @return [nil]
95
+ */
96
+ static VALUE tree_cursor_goto_descendant(VALUE self, VALUE descendant_idx) {
97
+ uint32_t idx = NUM2UINT(descendant_idx);
98
+ ts_tree_cursor_goto_descendant(&SELF, idx);
99
+ return Qnil;
100
+ }
101
+
102
+ /**
103
+ * Move the cursor to the first child of its current node.
104
+ *
105
+ * This returns +true+ if the cursor successfully moved, and returns +false+
106
+ * if there were no children.
107
+ *
108
+ * @return [Boolean]
109
+ */
110
+ static VALUE tree_cursor_goto_first_child(VALUE self) {
111
+ return ts_tree_cursor_goto_first_child(&SELF) ? Qtrue : Qfalse;
112
+ }
113
+
114
+ /**
115
+ * Move the cursor to the first child of its current node that extends beyond
116
+ * the given byte offset.
117
+ *
118
+ * This returns the index of the child node if one was found, and returns -1
119
+ * if no such child was found.
120
+ *
121
+ * @return [Integer]
122
+ */
123
+ static VALUE tree_cursor_goto_first_child_for_byte(VALUE self, VALUE byte) {
124
+ return LL2NUM(
125
+ ts_tree_cursor_goto_first_child_for_byte(&SELF, NUM2UINT(byte)));
126
+ }
127
+
128
+ /**
129
+ * Move the cursor to the first child of its current node that extends beyond
130
+ * the given or point.
131
+ *
132
+ * This returns the index of the child node if one was found, and returns -1
133
+ * if no such child was found.
134
+ *
135
+ * @return [Integer]
136
+ */
137
+ static VALUE tree_cursor_goto_first_child_for_point(VALUE self, VALUE point) {
138
+ return LL2NUM(
139
+ ts_tree_cursor_goto_first_child_for_point(&SELF, value_to_point(point)));
140
+ }
141
+
142
+ /**
143
+ * Move the cursor to the last child of its current node.
144
+ *
145
+ * This returns +true+ if the cursor successfully moved, and returns +false+ if
146
+ * there were no children.
147
+ *
148
+ * Note that this function may be slower than {#goto_first_child}
149
+ * because it needs to iterate through all the children to compute the child's
150
+ * position.
151
+ */
152
+ static VALUE tree_cursor_goto_last_child(VALUE self) {
153
+ return ts_tree_cursor_goto_last_child(&SELF) ? Qtrue : Qfalse;
154
+ }
155
+
156
+ /**
157
+ * Move the cursor to the next sibling of its current node.
158
+ *
159
+ * This returns +true+ if the cursor successfully moved, and returns +false+
160
+ * if there was no next sibling node.
161
+ *
162
+ * @return Boolean
163
+ */
164
+ static VALUE tree_cursor_goto_next_sibling(VALUE self) {
165
+ return ts_tree_cursor_goto_next_sibling(&SELF) ? Qtrue : Qfalse;
166
+ }
167
+
168
+ /**
169
+ * Move the cursor to the parent of its current node.
170
+ *
171
+ * This returns +true+ if the cursor successfully moved, and returns +false+
172
+ * if there was no parent node (the cursor was already on the root node).
173
+ *
174
+ * @return [Boolean]
175
+ */
176
+ static VALUE tree_cursor_goto_parent(VALUE self) {
177
+ return ts_tree_cursor_goto_parent(&SELF) ? Qtrue : Qfalse;
178
+ }
179
+
180
+ /**
181
+ * Move the cursor to the previous sibling of its current node.
182
+ *
183
+ * This returns +true+ if the cursor successfully moved, and returns +false+ if
184
+ * there was no previous sibling node.
185
+ *
186
+ * Note, that this function may be slower than
187
+ * {#goto_next_sibling} due to how node positions are stored. In
188
+ * the worst case, this will need to iterate through all the children upto the
189
+ * previous sibling node to recalculate its position.
190
+ *
191
+ * @return [Boolean]
192
+ */
193
+ static VALUE tree_cursor_goto_previous_sibling(VALUE self) {
194
+ return ts_tree_cursor_goto_previous_sibling(&SELF) ? Qtrue : Qfalse;
195
+ }
196
+
197
+ /**
198
+ * Create a new tree cursor starting from the given node.
199
+ *
200
+ * A tree cursor allows you to walk a syntax tree more efficiently than is
201
+ * possible using the {Node} functions. It is a mutable object that is always
202
+ * on a certain syntax node, and can be moved imperatively to different nodes.
203
+ *
204
+ * @return [TreeCursor]
205
+ */
206
+ static VALUE tree_cursor_initialize(VALUE self, VALUE node) {
207
+ TSNode n = value_to_node(node);
208
+ tree_cursor_t *ptr = unwrap(self);
209
+ ptr->data = ts_tree_cursor_new(n);
210
+ return self;
211
+ }
212
+
213
+ /**
214
+ * Re-initialize a tree cursor to start at a different node.
215
+ *
216
+ * @return [nil]
217
+ */
218
+ static VALUE tree_cursor_reset(VALUE self, VALUE node) {
219
+ ts_tree_cursor_reset(&SELF, value_to_node(node));
220
+ return Qnil;
221
+ }
222
+
223
+ /**
224
+ * Re-initialize a tree cursor to the same position as another cursor.
225
+ *
226
+ * Unlike {#reset}, this will not lose parent information and allows reusing
227
+ * already created cursors.
228
+ *
229
+ * @return [nil]
230
+ */
231
+ VALUE tree_cursor_reset_to(VALUE self, VALUE src) {
232
+ ts_tree_cursor_reset_to(&SELF, &unwrap(src)->data);
233
+ return Qnil;
234
+ }
235
+
236
+ void init_tree_cursor(void) {
237
+ cTreeCursor = rb_define_class_under(mTreeSitter, "TreeCursor", rb_cObject);
238
+
239
+ rb_define_alloc_func(cTreeCursor, tree_cursor_allocate);
240
+
241
+ /* Class methods */
242
+ rb_define_method(cTreeCursor, "copy", tree_cursor_copy, 0);
243
+ rb_define_method(cTreeCursor, "current_depth", tree_cursor_current_depth, 0);
244
+ rb_define_method(cTreeCursor, "current_descendant_index",
245
+ tree_cursor_current_descendant_index, 0);
246
+ rb_define_method(cTreeCursor, "current_field_id",
247
+ tree_cursor_current_field_id, 0);
248
+ rb_define_method(cTreeCursor, "current_field_name",
249
+ tree_cursor_current_field_name, 0);
250
+ rb_define_method(cTreeCursor, "current_node", tree_cursor_current_node, 0);
251
+ rb_define_method(cTreeCursor, "goto_descendant", tree_cursor_goto_descendant,
252
+ 1);
253
+ rb_define_method(cTreeCursor, "goto_first_child",
254
+ tree_cursor_goto_first_child, 0);
255
+ rb_define_method(cTreeCursor, "goto_first_child_for_byte",
256
+ tree_cursor_goto_first_child_for_byte, 1);
257
+ rb_define_method(cTreeCursor, "goto_first_child_for_point",
258
+ tree_cursor_goto_first_child_for_point, 1);
259
+ rb_define_method(cTreeCursor, "goto_last_child", tree_cursor_goto_last_child,
260
+ 0);
261
+ rb_define_method(cTreeCursor, "goto_next_sibling",
262
+ tree_cursor_goto_next_sibling, 0);
263
+ rb_define_method(cTreeCursor, "goto_parent", tree_cursor_goto_parent, 0);
264
+ rb_define_method(cTreeCursor, "goto_previous_sibling",
265
+ tree_cursor_goto_previous_sibling, 0);
266
+ rb_define_method(cTreeCursor, "initialize", tree_cursor_initialize, 1);
267
+ rb_define_method(cTreeCursor, "reset", tree_cursor_reset, 1);
268
+ rb_define_method(cTreeCursor, "reset_to", tree_cursor_reset_to, 1);
269
+ }
@@ -0,0 +1,44 @@
1
+ #include "tree_sitter.h"
2
+
3
+ VALUE mTreeSitter;
4
+
5
+ void Init_tree_sitter() {
6
+ mTreeSitter = rb_define_module("TreeSitter");
7
+
8
+ /**
9
+ * The latest ABI version that is supported by the current version of the
10
+ * library. When Languages are generated by the Tree-sitter CLI, they are
11
+ * assigned an ABI version number that corresponds to the current CLI version.
12
+ * The Tree-sitter library is generally backwards-compatible with languages
13
+ * generated using older CLI versions, but is not forwards-compatible.
14
+ */
15
+ rb_define_const(mTreeSitter, "LANGUAGE_VERSION",
16
+ INT2NUM(TREE_SITTER_LANGUAGE_VERSION));
17
+
18
+ /**
19
+ * The earliest ABI version that is supported by the current version of the
20
+ * library.
21
+ */
22
+ rb_define_const(mTreeSitter, "MIN_COMPATIBLE_LANGUAGE_VERSION",
23
+ TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
24
+
25
+ init_encoding();
26
+ init_input();
27
+ init_input_edit();
28
+ init_language();
29
+ init_logger();
30
+ init_node();
31
+ init_parser();
32
+ init_point();
33
+ init_quantifier();
34
+ init_query();
35
+ init_query_capture();
36
+ init_query_cursor();
37
+ init_query_error();
38
+ init_query_match();
39
+ init_query_predicate_step();
40
+ init_range();
41
+ init_symbol_type();
42
+ init_tree();
43
+ init_tree_cursor();
44
+ }
@@ -0,0 +1,107 @@
1
+ #ifndef _RB_TREE_SITTER_H
2
+ #define _RB_TREE_SITTER_H
3
+
4
+ #include "macros.h"
5
+ #include <dlfcn.h>
6
+ #include <fcntl.h>
7
+ #include <ruby.h>
8
+ #include <stdio.h>
9
+ #include <string.h>
10
+ #include <tree_sitter/api.h>
11
+
12
+ static inline VALUE safe_str(const char *str) {
13
+ if (str == NULL) {
14
+ return Qnil;
15
+ } else {
16
+ return rb_utf8_str_new_cstr(str);
17
+ }
18
+ }
19
+
20
+ static inline VALUE safe_str2(const char *str, uint32_t len) {
21
+ if (str == NULL) {
22
+ return Qnil;
23
+ } else if (len == 0) {
24
+ return rb_utf8_str_new_cstr("");
25
+ } else {
26
+ return rb_utf8_str_new(str, len);
27
+ }
28
+ }
29
+
30
+ static inline VALUE safe_symbol(const char *str) {
31
+ if (str == NULL) {
32
+ return Qnil;
33
+ } else {
34
+ return ID2SYM(rb_intern(str));
35
+ }
36
+ }
37
+
38
+ // VALUE to TS* converters
39
+
40
+ TSInput value_to_input(VALUE);
41
+ TSInputEdit value_to_input_edit(VALUE);
42
+ TSInputEncoding value_to_encoding(VALUE);
43
+ TSLanguage *value_to_language(VALUE);
44
+ TSLogger value_to_logger(VALUE);
45
+ TSNode value_to_node(VALUE);
46
+ TSPoint value_to_point(VALUE);
47
+ TSQuantifier value_to_quantifier(VALUE);
48
+ TSQuery *value_to_query(VALUE);
49
+ TSQueryCursor *value_to_query_cursor(VALUE);
50
+ TSQueryError value_to_query_error(VALUE);
51
+ TSQueryMatch value_to_query_match(VALUE);
52
+ TSQueryPredicateStep value_to_query_predicate_step(VALUE);
53
+ TSQueryPredicateStepType value_to_query_predicate_step_type(VALUE);
54
+ TSRange value_to_range(VALUE);
55
+ TSSymbolType value_to_symbol_type(VALUE);
56
+ TSTree *value_to_tree(VALUE);
57
+ TSTreeCursor value_to_tree_cursor(VALUE);
58
+
59
+ // TS* to VALUE converters
60
+ VALUE new_input(const TSInput *);
61
+ VALUE new_language(const TSLanguage *);
62
+ VALUE new_logger(const TSLogger *);
63
+ VALUE new_logger_by_val(TSLogger);
64
+ VALUE new_node(const TSNode *);
65
+ VALUE new_node_by_val(TSNode);
66
+ VALUE new_point(const TSPoint *);
67
+ VALUE new_point_by_val(TSPoint);
68
+ VALUE new_query_capture(const TSQueryCapture *);
69
+ VALUE new_query_match(const TSQueryMatch *);
70
+ VALUE new_query_predicate_step(const TSQueryPredicateStep *);
71
+ VALUE new_range(const TSRange *);
72
+ VALUE new_symbol_type(TSSymbolType);
73
+ VALUE new_tree(TSTree *);
74
+
75
+ // All init_* functions are called from Init_tree_sitter
76
+ void init_encoding(void);
77
+ void init_input(void);
78
+ void init_input_edit(void);
79
+ void init_language(void);
80
+ void init_logger(void);
81
+ void init_node(void);
82
+ void init_parser(void);
83
+ void init_point(void);
84
+ void init_quantifier(void);
85
+ void init_query(void);
86
+ void init_query_capture(void);
87
+ void init_query_cursor(void);
88
+ void init_query_error(void);
89
+ void init_query_match(void);
90
+ void init_query_predicate_step(void);
91
+ void init_range(void);
92
+ void init_symbol_type(void);
93
+ void init_tree(void);
94
+ void init_tree_cursor(void);
95
+
96
+ // Other helpers
97
+ const char *quantifier_str(TSQuantifier);
98
+ const char *query_error_str(TSQueryError);
99
+
100
+ // TSTree reference counting
101
+ int tree_rc_free(const TSTree *);
102
+ void tree_rc_new(const TSTree *);
103
+
104
+ // This is a special entry-point for the extension
105
+ void Init_tree_sitter(void);
106
+
107
+ #endif
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # splits an array like [rust](https://doc.rust-lang.org/std/primitive.slice.html#method.split)
4
+ def array_split_like_rust(array, &block)
5
+ return enum_for(__method__, array) if !block_given?
6
+
7
+ return [] if array.empty?
8
+
9
+ result = []
10
+ current_slice = []
11
+
12
+ array.each do |element|
13
+ if yield(element)
14
+ result << current_slice
15
+ current_slice = []
16
+ else
17
+ current_slice << element
18
+ end
19
+ end
20
+
21
+ result << current_slice
22
+ result
23
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A colon-separated list of paths pointing to directories that can contain parsers.
5
+ # Order matters.
6
+ # Takes precedence over default lookup paths.
7
+ ENV_PARSERS =
8
+ ENV['TREE_SITTER_PARSERS']
9
+ &.split(':')
10
+ &.map { |v| Pathname(v) }
11
+ .freeze
12
+
13
+ # The default paths we use to lookup parsers.
14
+ # Order matters.
15
+ LIBDIRS = [
16
+ '.vendor/parsers',
17
+ '.vendor/tree-sitter-parsers',
18
+ 'vendor/parsers',
19
+ 'vendor/tree-sitter-parsers',
20
+ 'parsers',
21
+ 'tree-sitter-parsers',
22
+ '.',
23
+ '/opt/local/lib',
24
+ '/opt/lib',
25
+ '/usr/local/lib',
26
+ '/usr/lib',
27
+ ].map { |p| Pathname(p) }.freeze
28
+
29
+ # Mixins.
30
+ module Mixins
31
+ # Language Mixin.
32
+ module Language
33
+ # Load a language from configuration or default lookup paths.
34
+ #
35
+ # @example Load java from default paths
36
+ # # This will look for:
37
+ # #
38
+ # # .vendor/tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
39
+ # # .vendor/parsers/(java/)?(libtree-sitter-)?java.{ext}
40
+ # # vendor/tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
41
+ # # vendor/parsers/(java/)?(libtree-sitter-)?java.{ext}
42
+ # # parsers/(java/)?(libtree-sitter-)?java.{ext}
43
+ # # tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
44
+ # # /opt/local/lib/(java/)?(libtree-sitter-)?java.{ext}
45
+ # # /opt/lib/(java/)?(libtree-sitter-)?java.{ext}
46
+ # # /usr/local/lib/(java/)?(libtree-sitter-)?java.{ext}
47
+ # # /usr/lib/(java/)?(libtree-sitter-)?java.{ext}
48
+ # #
49
+ # java = TreeSitter.language('java')
50
+ #
51
+ # @example (TreeStand) Load java from a configured path
52
+ # # This will look for:
53
+ # #
54
+ # # /my/path/(java/)?(libtree-sitter-)?java.{ext}
55
+ # #
56
+ # TreeStand.config.parser_path = '/my/path'
57
+ # java = TreeStand::Parser.language('java')
58
+ #
59
+ # @example (TreeStand) Load java from environment variables
60
+ # # This will look for:
61
+ # #
62
+ # # /my/forced/env/path/(java/)?(libtree-sitter-)?java.{ext}
63
+ # # /my/path/(java/)?(libtree-sitter-)?java.{ext}
64
+ # #
65
+ # # … and the same works for the default paths if `TreeStand.config.parser_path`
66
+ # # was `nil`
67
+ # ENV['TREE_SITTER_PARSERS'] = '/my/forced/env/path'
68
+ # TreeStand.config.parser_path = '/my/path'
69
+ # java = TreeStand::Parser.language('java')
70
+ #
71
+ # @note the name is case sensitive, but library lookup is not: if your parser is defined as `COBOL`,
72
+ # then you have to call `language('COBOL')`, but the parser can be `cobol.so/COBOL.so/…`.
73
+ #
74
+ # @param name [String] the name of the parser.
75
+ # This name is used to load the symbol from the compiled parser, replacing `-` with `_`.
76
+ #
77
+ # @return [TreeSitter:language] a language object to use in your parsers.
78
+ #
79
+ # @raise [RuntimeError] if the parser was not found.
80
+ #
81
+ # @see search_for_lib
82
+ def language(name)
83
+ lib = search_for_lib(name)
84
+
85
+ if lib.nil?
86
+ raise <<~MSG.chomp
87
+ Failed to load a parser for #{name}.
88
+
89
+ #{search_lib_message}
90
+ MSG
91
+ end
92
+
93
+ # We know that the bindings will accept `lib`, but I don't know how to tell sorbet
94
+ # the types in ext/tree_sitter where `load` is defined.
95
+ TreeSitter::Language.load(name.tr('-', '_'), lib)
96
+ end
97
+
98
+ # The platform-specific extension of the parser.
99
+ # @return [String] `dylib` or `so` for mac or linux.
100
+ def ext
101
+ case Gem::Platform.local.os
102
+ in /darwin/ then 'dylib'
103
+ else 'so'
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ # The library directories we need to look into.
110
+ #
111
+ # @return [Array<Pathname>] the list of candidate places to use when searching for parsers.
112
+ #
113
+ # @see ENV_PARSERS
114
+ # @see LIBDIRS
115
+ def lib_dirs = [*TreeSitter::ENV_PARSERS, *TreeSitter::LIBDIRS]
116
+
117
+ # Lookup a parser by name.
118
+ #
119
+ # Precedence:
120
+ # 1. `Env['TREE_SITTER_PARSERS]`.
121
+ # 2. {TreeStand::Config#parser_path} if using {TreeStand}.
122
+ # 3. {LIBDIRS}.
123
+ #
124
+ # If a {TreeStand::Config#parser_path} is `nil`, {LIBDIRS} is used.
125
+ # If a {TreeStand::Config#parser_path} is a {::Pathname}, {LIBDIRS} is ignored.
126
+ def search_for_lib(name)
127
+ files =
128
+ [name, name.upcase, name.downcase]
129
+ .uniq
130
+ .flat_map do |n|
131
+ base = "#{n}.#{ext}"
132
+ [base, "tree-sitter-#{base}", "libtree-sitter-#{base}"]
133
+ end
134
+
135
+ lib_dirs
136
+ .product(files)
137
+ .find do |dir, so|
138
+ path = dir / so
139
+ path = dir / name / so if !path.exist?
140
+ break path.expand_path if path.exist?
141
+ end
142
+ end
143
+
144
+ # Generates a string message on where parser lookup happens.
145
+ #
146
+ # @return [String] A pretty message.
147
+ def search_lib_message
148
+ indent = 2
149
+ pretty = ->(arr) {
150
+ if arr
151
+ arr
152
+ .compact
153
+ .map { |v| "#{' ' * indent}#{v.expand_path}" }
154
+ .join("\n")
155
+ end
156
+ }
157
+ <<~MSG.chomp
158
+ From ENV['TREE_SITTER_PARSERS']:
159
+ #{pretty.call(ENV_PARSERS)}
160
+
161
+ From Defaults:
162
+ #{pretty.call(lib_dirs)}
163
+ MSG
164
+ end
165
+ end
166
+ end
167
+ end