ruby_tree_sitter 1.6.0-x86_64-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,83 @@
1
+ #include "tree_sitter.h"
2
+
3
+ extern VALUE mTreeSitter;
4
+
5
+ VALUE cQueryPredicateStep;
6
+
7
+ const char *done = "Done";
8
+ const char *capture = "capture";
9
+ const char *string = "String";
10
+
11
+ DATA_WRAP(QueryPredicateStep, query_predicate_step)
12
+
13
+ VALUE new_query_predicate_step_type(TSQueryPredicateStepType type) {
14
+ switch (type) {
15
+ case TSQueryPredicateStepTypeDone:
16
+ return ID2SYM(rb_intern(done));
17
+ case TSQueryPredicateStepTypeCapture:
18
+ return ID2SYM(rb_intern(capture));
19
+ case TSQueryPredicateStepTypeString:
20
+ return ID2SYM(rb_intern(string));
21
+ default:
22
+ return Qnil;
23
+ }
24
+ }
25
+
26
+ static const char *query_predicate_type_string(TSQueryPredicateStepType type) {
27
+ switch (type) {
28
+ case TSQueryPredicateStepTypeDone:
29
+ return done;
30
+ case TSQueryPredicateStepTypeCapture:
31
+ return capture;
32
+ case TSQueryPredicateStepTypeString:
33
+ return string;
34
+ default:
35
+ return "???";
36
+ }
37
+ }
38
+
39
+ TSQueryPredicateStepType value_to_query_predicate_step_type(VALUE step_type) {
40
+ VALUE type = SYM2ID(step_type);
41
+ VALUE c = rb_const_get_at(cQueryPredicateStep, rb_intern(capture));
42
+ VALUE s = rb_const_get_at(cQueryPredicateStep, rb_intern(string));
43
+
44
+ // NOTE: should we emit a warning instead of defaulting to done?
45
+ if (type == c) {
46
+ return TSQueryPredicateStepTypeCapture;
47
+ } else if (type == s) {
48
+ return TSQueryPredicateStepTypeString;
49
+ } else {
50
+ return TSQueryPredicateStepTypeDone;
51
+ }
52
+ }
53
+
54
+ static VALUE query_predicate_step_inspect(VALUE self) {
55
+ query_predicate_step_t *step = unwrap(self);
56
+ return rb_sprintf("{value_id=%i, type=%s}", step->data.value_id,
57
+ query_predicate_type_string(step->data.type));
58
+ }
59
+
60
+ DATA_DEFINE_ACCESSOR(query_predicate_step, type, new_query_predicate_step_type,
61
+ value_to_query_predicate_step_type)
62
+ DATA_DEFINE_ACCESSOR(query_predicate_step, value_id, UINT2NUM, NUM2UINT)
63
+
64
+ void init_query_predicate_step(void) {
65
+ cQueryPredicateStep =
66
+ rb_define_class_under(mTreeSitter, "QueryPredicateStep", rb_cObject);
67
+
68
+ rb_define_alloc_func(cQueryPredicateStep, query_predicate_step_allocate);
69
+
70
+ /* Constants */
71
+ rb_define_const(cQueryPredicateStep, "DONE", ID2SYM(rb_intern(done)));
72
+ rb_define_const(cQueryPredicateStep, "CAPTURE", ID2SYM(rb_intern(capture)));
73
+ rb_define_const(cQueryPredicateStep, "STRING", ID2SYM(rb_intern(string)));
74
+
75
+ /* Class methods */
76
+ DECLARE_ACCESSOR(cQueryPredicateStep, query_predicate_step, type)
77
+ DECLARE_ACCESSOR(cQueryPredicateStep, query_predicate_step, value_id)
78
+
79
+ rb_define_method(cQueryPredicateStep, "inspect", query_predicate_step_inspect,
80
+ 0);
81
+ rb_define_method(cQueryPredicateStep, "to_s", query_predicate_step_inspect,
82
+ 0);
83
+ }
@@ -0,0 +1,35 @@
1
+ #include "tree_sitter.h"
2
+
3
+ extern VALUE mTreeSitter;
4
+
5
+ VALUE cRange;
6
+
7
+ DATA_WRAP(Range, range)
8
+ DATA_DEFINE_ACCESSOR(range, start_point, new_point_by_val, value_to_point)
9
+ DATA_DEFINE_ACCESSOR(range, end_point, new_point_by_val, value_to_point)
10
+ DATA_DEFINE_ACCESSOR(range, start_byte, UINT2NUM, NUM2UINT)
11
+ DATA_DEFINE_ACCESSOR(range, end_byte, UINT2NUM, NUM2UINT)
12
+
13
+ static VALUE range_inspect(VALUE self) {
14
+ range_t *range = unwrap(self);
15
+ return rb_sprintf("{start_point= %+" PRIsVALUE ", end_point=%+" PRIsVALUE
16
+ ", start_byte=%i, end_byte=%i}",
17
+ new_point_by_val(range->data.start_point),
18
+ new_point_by_val(range->data.end_point),
19
+ range->data.start_byte, range->data.end_byte);
20
+ }
21
+
22
+ void init_range(void) {
23
+ cRange = rb_define_class_under(mTreeSitter, "Range", rb_cObject);
24
+
25
+ rb_define_alloc_func(cRange, range_allocate);
26
+
27
+ /* Class methods */
28
+ DECLARE_ACCESSOR(cRange, range, start_point)
29
+ DECLARE_ACCESSOR(cRange, range, end_point)
30
+ DECLARE_ACCESSOR(cRange, range, start_byte)
31
+ DECLARE_ACCESSOR(cRange, range, end_byte)
32
+
33
+ rb_define_method(cRange, "inspect", range_inspect, 0);
34
+ rb_define_method(cRange, "to_s", range_inspect, 0);
35
+ }
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/tree_sitter/version'
4
+
5
+ module TreeSitter
6
+ # Fetches tree-sitter sources.
7
+ class Repo
8
+ attr_reader :exe, :src, :url, :version
9
+
10
+ def initialize
11
+ @version = TREESITTER_VERSION
12
+
13
+ # `tree-sitter-@version` is the name produced by tagged releases of sources
14
+ # by git, so we use it everywhere, including when cloning from git.
15
+ @src = Pathname.pwd / "tree-sitter-#{@version}"
16
+
17
+ @url = {
18
+ git: 'https://github.com/tree-sitter/tree-sitter',
19
+ tar: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.tar.gz",
20
+ zip: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.zip",
21
+ }
22
+
23
+ @exe = {}
24
+ %i[curl git tar wget zip].each do |cmd|
25
+ @exe[cmd] = find_executable(cmd.to_s)
26
+ end
27
+ end
28
+
29
+ def compile
30
+ # We need to clean because the same folder is used over and over
31
+ # by rake-compiler-dock
32
+ ar = RbConfig::MAKEFILE_CONFIG['AR']
33
+ ar = " AR=#{ar}" if ar
34
+ sh "cd #{src} && make clean &&#{ar} make libtree-sitter.a"
35
+ end
36
+
37
+ def exe?(name)
38
+ @exe[name]
39
+ end
40
+
41
+ def extract?
42
+ !exe.filter { |k, v| %i[tar zip].include?(k) && v }.empty?
43
+ end
44
+
45
+ def download
46
+ # TODO: should we force re-download? Maybe with a flag?
47
+ return true if Dir.exist? src
48
+
49
+ res = false
50
+ %w[git curl wget].each do |cmd|
51
+ res =
52
+ if find_executable(cmd)
53
+ send("sources_from_#{cmd}")
54
+ else
55
+ false
56
+ end
57
+ break if res
58
+ end
59
+
60
+ res
61
+ end
62
+
63
+ def include_and_lib_dirs
64
+ [[(src / 'lib' / 'include').to_s], [src.to_s]]
65
+ end
66
+
67
+ def patch
68
+ root = Pathname.new(File.expand_path(__dir__)).realpath.parent.parent
69
+ patch = root / 'patches' / 'cross-compile.patch'
70
+
71
+ sh "patch --forward #{src / 'Makefile'} #{patch}"
72
+ end
73
+
74
+ def sh(cmd)
75
+ return if system(cmd)
76
+
77
+ abort <<~MSG
78
+
79
+ Failed to run: #{cmd}
80
+
81
+ exiting …
82
+
83
+ MSG
84
+ end
85
+
86
+ def sources_from_curl
87
+ return false if !exe?(:curl) || !extract?
88
+
89
+ if exe?(:tar)
90
+ sh "curl -L #{url[:tar]} -o tree-sitter-v#{version}.tar.gz"
91
+ sh "tar -xf tree-sitter-v#{version}.tar.gz"
92
+ elsif exe?(:zip)
93
+ sh "curl -L #{url[:zip]} -o tree-sitter-v#{version}.zip"
94
+ sh "unzip -q tree-sitter-v#{version}.zip"
95
+ end
96
+
97
+ true
98
+ end
99
+
100
+ def sources_from_git
101
+ return false if !exe?(:git)
102
+
103
+ sh <<~SHELL.chomp
104
+ mkdir #{src} \\
105
+ && cd #{src} \\
106
+ && git init \\
107
+ && git remote add origin "#{url[:git]}" \\
108
+ && git fetch origin --depth 1 tags/v#{version} \\
109
+ && git reset --hard FETCH_HEAD
110
+ SHELL
111
+ true
112
+ end
113
+
114
+ def sources_from_wget
115
+ return false if !exe?(:wget) || !extract?
116
+
117
+ if exe?(:tar)
118
+ sh "wget #{url[:tar]} -O tree-sitter-v#{version}.tar.gz"
119
+ sh "tar -xf tree-sitter-v#{version}.tar.gz"
120
+ elsif exe?(:zip)
121
+ sh "wget #{url[:zip]} -O tree-sitter-v#{version}.zip"
122
+ sh "unzip -q tree-sitter-v#{version}.zip"
123
+ end
124
+
125
+ true
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,46 @@
1
+ #include "tree_sitter.h"
2
+
3
+ extern VALUE mTreeSitter;
4
+
5
+ VALUE mSymbolType;
6
+
7
+ const char *regular = "regular";
8
+ const char *anonymous = "anonymous";
9
+ const char *auxiliary = "auxiliary";
10
+
11
+ TSSymbolType value_to_symbol_type(VALUE symbol_type) {
12
+ VALUE sym = SYM2ID(symbol_type);
13
+ VALUE anon = rb_const_get_at(mSymbolType, rb_intern(anonymous));
14
+ VALUE aux = rb_const_get_at(mSymbolType, rb_intern(auxiliary));
15
+
16
+ // NOTE: should we emit a warning instead of defaulting to regular?
17
+ if (sym == anon) {
18
+ return TSSymbolTypeAnonymous;
19
+ } else if (sym == aux) {
20
+ return TSSymbolTypeAuxiliary;
21
+ } else {
22
+ return TSSymbolTypeRegular;
23
+ }
24
+ }
25
+
26
+ VALUE new_symbol_type(TSSymbolType symbol_type) {
27
+ switch (symbol_type) {
28
+ case TSSymbolTypeRegular:
29
+ return ID2SYM(rb_intern(regular));
30
+ case TSSymbolTypeAnonymous:
31
+ return ID2SYM(rb_intern(anonymous));
32
+ case TSSymbolTypeAuxiliary:
33
+ return ID2SYM(rb_intern(auxiliary));
34
+ default:
35
+ return ID2SYM(rb_intern("this_should_never_get_reached"));
36
+ }
37
+ }
38
+
39
+ void init_symbol_type(void) {
40
+ mSymbolType = rb_define_module_under(mTreeSitter, "SymbolType");
41
+
42
+ /* Constants */
43
+ rb_define_const(mSymbolType, "REGULAR", ID2SYM(rb_intern(regular)));
44
+ rb_define_const(mSymbolType, "ANONYMOUS", ID2SYM(rb_intern(anonymous)));
45
+ rb_define_const(mSymbolType, "AUXILIARY", ID2SYM(rb_intern(auxiliary)));
46
+ }
@@ -0,0 +1,234 @@
1
+ #include "tree_sitter.h"
2
+
3
+ extern VALUE mTreeSitter;
4
+
5
+ VALUE cTree;
6
+
7
+ int tree_rc_free(const TSTree *tree) {
8
+ VALUE ptr = ULONG2NUM((uintptr_t)tree);
9
+ VALUE rc = rb_cv_get(cTree, "@@rc");
10
+ VALUE val = rb_hash_lookup(rc, ptr);
11
+
12
+ if (!NIL_P(val)) {
13
+ unsigned int count = NUM2UINT(val);
14
+ --count;
15
+ if (count < 1) {
16
+ rb_hash_delete(rc, ptr);
17
+ ts_tree_delete((TSTree *)tree);
18
+ return 1;
19
+ } else {
20
+ rb_hash_aset(rc, ptr, ULONG2NUM(count));
21
+ return 0;
22
+ }
23
+ } else {
24
+ return 1;
25
+ }
26
+ }
27
+
28
+ void tree_rc_new(const TSTree *tree) {
29
+ VALUE ptr = ULONG2NUM((uintptr_t)tree);
30
+ VALUE rc = rb_cv_get(cTree, "@@rc");
31
+ VALUE val = rb_hash_lookup(rc, ptr);
32
+
33
+ if (NIL_P(val)) {
34
+ rb_hash_aset(rc, ptr, UINT2NUM(1));
35
+ } else {
36
+ rb_hash_aset(rc, ptr, UINT2NUM(NUM2UINT(val) + 1));
37
+ }
38
+ }
39
+
40
+ DATA_TYPE(TSTree *, tree)
41
+ static void tree_free(void *ptr) {
42
+ tree_t *type = (tree_t *)ptr;
43
+ if (tree_rc_free(type->data)) {
44
+ xfree(ptr);
45
+ }
46
+ }
47
+
48
+ DATA_MEMSIZE(tree)
49
+ DATA_DECLARE_DATA_TYPE(tree)
50
+ DATA_ALLOCATE(tree)
51
+ DATA_UNWRAP(tree)
52
+
53
+ VALUE new_tree(TSTree *ptr) {
54
+ if (ptr == NULL) {
55
+ return Qnil;
56
+ }
57
+ VALUE res = tree_allocate(cTree);
58
+ tree_t *type = unwrap(res);
59
+ type->data = ptr;
60
+ tree_rc_new(ptr);
61
+ return res;
62
+ }
63
+
64
+ DATA_FROM_VALUE(TSTree *, tree)
65
+
66
+ /**
67
+ * Compare an old edited syntax tree to a new syntax tree representing the same
68
+ * document, returning an array of ranges whose syntactic structure has changed.
69
+ *
70
+ * For this to work correctly, the old syntax tree must have been edited such
71
+ * that its ranges match up to the new tree. Generally, you'll want to call
72
+ * this function right after calling one of the {Parser#parse} functions.
73
+ * You need to pass the old tree that was passed to parse, as well as the new
74
+ * tree that was returned from that function.
75
+ *
76
+ * @param old_tree [Tree]
77
+ * @param new_tree [Tree]
78
+ *
79
+ * @return [Array<Range>]
80
+ */
81
+ static VALUE tree_changed_ranges(VALUE _self, VALUE old_tree, VALUE new_tree) {
82
+ TSTree *old = unwrap(old_tree)->data;
83
+ TSTree *new = unwrap(new_tree)->data;
84
+ uint32_t length;
85
+ TSRange *ranges = ts_tree_get_changed_ranges(old, new, &length);
86
+ VALUE res = rb_ary_new_capa(length);
87
+
88
+ for (uint32_t i = 0; i < length; i++) {
89
+ rb_ary_push(res, new_range(&ranges[i]));
90
+ }
91
+
92
+ if (ranges) {
93
+ free(ranges);
94
+ }
95
+
96
+ return res;
97
+ }
98
+
99
+ static VALUE tree_finalizer(VALUE _self) {
100
+ VALUE rc = rb_cv_get(cTree, "@@rc");
101
+ VALUE keys = rb_funcall(rc, rb_intern("keys"), 0);
102
+ long len = RARRAY_LEN(keys);
103
+
104
+ for (long i = 0; i < len; ++i) {
105
+ VALUE curr = RARRAY_AREF(keys, i);
106
+ unsigned int val = NUM2UINT(rb_hash_lookup(rc, curr));
107
+ if (val > 0) {
108
+ ts_tree_delete((TSTree *)NUM2ULONG(curr));
109
+ }
110
+
111
+ rb_hash_delete(rc, curr);
112
+ }
113
+
114
+ return Qnil;
115
+ }
116
+
117
+ /**
118
+ * Create a shallow copy of the syntax tree. This is very fast.
119
+ *
120
+ * You need to copy a syntax tree in order to use it on more than one thread at
121
+ * a time, as syntax trees are not thread safe.
122
+ *
123
+ * @return [Tree]
124
+ */
125
+ static VALUE tree_copy(VALUE self) { return new_tree(ts_tree_copy(SELF)); }
126
+
127
+ /**
128
+ * Edit the syntax tree to keep it in sync with source code that has been
129
+ * edited.
130
+ *
131
+ * You must describe the edit both in terms of byte offsets and in terms of
132
+ * (row, column) coordinates.
133
+ *
134
+ * @param edit [InputEdit]
135
+ *
136
+ * @return [nil]
137
+ */
138
+ static VALUE tree_edit(VALUE self, VALUE edit) {
139
+ TSInputEdit in = value_to_input_edit(edit);
140
+ ts_tree_edit(SELF, &in);
141
+ return Qnil;
142
+ }
143
+
144
+ /**
145
+ * Get the array of included ranges that was used to parse the syntax tree.
146
+ *
147
+ * @return [Array<Range>]
148
+ */
149
+ static VALUE included_ranges(VALUE self) {
150
+ uint32_t length;
151
+ const TSRange *ranges = ts_tree_included_ranges(SELF, &length);
152
+ VALUE res = rb_ary_new_capa(length);
153
+ for (uint32_t i = 0; i < length; i++) {
154
+ rb_ary_push(res, new_range(&ranges[i]));
155
+ }
156
+ return res;
157
+ }
158
+
159
+ /**
160
+ * Get the language that was used to parse the syntax tree.
161
+ *
162
+ * @return [Language]
163
+ */
164
+ static VALUE tree_language(VALUE self) {
165
+ return new_language(ts_tree_language(SELF));
166
+ }
167
+
168
+ /**
169
+ * Write a DOT graph describing the syntax tree to the given file.
170
+ *
171
+ * @param file [String]
172
+ *
173
+ * @return [nil]
174
+ */
175
+ static VALUE tree_print_dot_graph(VALUE self, VALUE file) {
176
+ Check_Type(file, T_STRING);
177
+ char *path = StringValueCStr(file);
178
+ int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC,
179
+ S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
180
+ if (fd < 0) {
181
+ rb_raise(rb_eRuntimeError, "Could not open file `%s'.\nReason:\n%s", path,
182
+ strerror(fd));
183
+ return Qnil;
184
+ }
185
+ ts_tree_print_dot_graph(SELF, fd);
186
+ close(fd);
187
+ return Qnil;
188
+ }
189
+
190
+ /**
191
+ * Get the root node of the syntax tree.
192
+ *
193
+ * @return [Node]
194
+ */
195
+ static VALUE tree_root_node(VALUE self) {
196
+ return new_node_by_val(ts_tree_root_node(SELF));
197
+ }
198
+
199
+ /**
200
+ * Get the root node of the syntax tree, but with its position
201
+ * shifted forward by the given offset.
202
+ *
203
+ * @return [Node]
204
+ */
205
+ static VALUE tree_root_node_with_offset(VALUE self, VALUE offset_bytes,
206
+ VALUE offset_extent) {
207
+ uint32_t bytes = NUM2UINT(offset_bytes);
208
+ TSPoint extent = value_to_point(offset_extent);
209
+ return new_node_by_val(ts_tree_root_node_with_offset(SELF, bytes, extent));
210
+ }
211
+
212
+ void init_tree(void) {
213
+ cTree = rb_define_class_under(mTreeSitter, "Tree", rb_cObject);
214
+
215
+ rb_undef_alloc_func(cTree);
216
+
217
+ /* Module methods */
218
+ rb_define_module_function(cTree, "changed_ranges", tree_changed_ranges, 2);
219
+ rb_define_module_function(cTree, "finalizer", tree_finalizer, 0);
220
+
221
+ /* Class methods */
222
+ rb_define_method(cTree, "copy", tree_copy, 0);
223
+ rb_define_method(cTree, "edit", tree_edit, 1);
224
+ rb_define_method(cTree, "included_ranges", included_ranges, 0);
225
+ rb_define_method(cTree, "language", tree_language, 0);
226
+ rb_define_method(cTree, "print_dot_graph", tree_print_dot_graph, 1);
227
+ rb_define_method(cTree, "root_node", tree_root_node, 0);
228
+ rb_define_method(cTree, "root_node_with_offset", tree_root_node_with_offset,
229
+ 2);
230
+
231
+ // Reference-count created trees
232
+ VALUE rc = rb_hash_new();
233
+ rb_cv_set(cTree, "@@rc", rc);
234
+ }