ruby_tree_sitter 1.6.0-arm64-darwin

Sign up to get free protection for your applications and to get access to all the features.
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.bundle +0 -0
  29. data/lib/tree_sitter/3.1/tree_sitter.bundle +0 -0
  30. data/lib/tree_sitter/3.2/tree_sitter.bundle +0 -0
  31. data/lib/tree_sitter/3.3/tree_sitter.bundle +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
@@ -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