tree_haver 1.0.0 → 2.0.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +47 -3
- data/README.md +130 -76
- data/lib/tree_haver/backends/citrus.rb +302 -0
- data/lib/tree_haver/backends/ffi.rb +75 -17
- data/lib/tree_haver/backends/java.rb +11 -7
- data/lib/tree_haver/backends/mri.rb +10 -20
- data/lib/tree_haver/backends/rust.rb +8 -20
- data/lib/tree_haver/grammar_finder.rb +1 -1
- data/lib/tree_haver/node.rb +376 -0
- data/lib/tree_haver/path_validator.rb +18 -3
- data/lib/tree_haver/tree.rb +205 -0
- data/lib/tree_haver/version.rb +2 -2
- data/lib/tree_haver.rb +44 -229
- data/sig/tree_haver/backends.rbs +68 -1
- data/sig/tree_haver/path_validator.rbs +1 -0
- data/sig/tree_haver.rbs +95 -9
- data.tar.gz.sig +0 -0
- metadata +11 -8
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f711d2013566d14c8ae9d5f3609de3c32f8621e04d573543841c88e04fb6b1fb
|
|
4
|
+
data.tar.gz: '09c5054535b0399848c728302631daf9fdebb6c9d6a1aa3fa7d5428e52875b36'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fd50cd072f0e37bd051e184eb4e8fb2f198f3645f403732d04e73d08990890c28cf30d678a40142b459ea361577cfa1c22a68e1b0050f71d5cc9fe631d1a4f0d
|
|
7
|
+
data.tar.gz: 46072b508a3a538750b202a948a8a9e14dcc0134e659bc481df66de28d334daee69e55cd4c3d7b66bf01fe193f3fed9a419c785eea0a79266680261186367c29
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,50 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [2.0.0] - 2025-12-15
|
|
34
|
+
|
|
35
|
+
- TAG: [v2.0.0][2.0.0t]
|
|
36
|
+
- COVERAGE: 82.78% -- 601/726 lines in 11 files
|
|
37
|
+
- BRANCH COVERAGE: 70.45% -- 186/264 branches in 11 files
|
|
38
|
+
- 91.90% documented
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- Added support for Citrus backend (`backends/citrus.rb`) - a pure Ruby grammar parser with its own distinct grammar structure
|
|
43
|
+
- Added `TreeHaver::Tree` unified wrapper class providing consistent API across all backends
|
|
44
|
+
- Added `TreeHaver::Node` unified wrapper class providing consistent API across all backends
|
|
45
|
+
- Added `TreeHaver::Point` class that works as both object and hash for position compatibility
|
|
46
|
+
- Added passthrough mechanism via `method_missing` for accessing backend-specific features
|
|
47
|
+
- Added `inner_node` accessor on `TreeHaver::Node` for advanced backend-specific usage
|
|
48
|
+
- Added `inner_tree` accessor on `TreeHaver::Tree` for advanced backend-specific usage
|
|
49
|
+
- Added comprehensive test suite for `TreeHaver::Node` wrapper class (88 examples)
|
|
50
|
+
- Added comprehensive test suite for `TreeHaver::Tree` wrapper class (17 examples)
|
|
51
|
+
- Added comprehensive test suite for `TreeHaver::Parser` class (12 examples)
|
|
52
|
+
- Added complete test coverage for Citrus backend (41 examples)
|
|
53
|
+
- Enhanced `TreeHaver::Language` tests for dynamic language helpers
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **BREAKING:** All backends now return `TreeHaver::Tree` from `Parser#parse` and `Parser#parse_string`
|
|
58
|
+
- **BREAKING:** `TreeHaver::Tree#root_node` now returns `TreeHaver::Node` instead of backend-specific node
|
|
59
|
+
- **BREAKING:** All child/sibling/parent methods on nodes now return `TreeHaver::Node` wrappers
|
|
60
|
+
- Updated MRI backend (`backends/mri.rb`) to return wrapped `TreeHaver::Tree` with source
|
|
61
|
+
- Updated Rust backend (`backends/rust.rb`) to return wrapped `TreeHaver::Tree` with source
|
|
62
|
+
- Updated FFI backend (`backends/ffi.rb`) to return wrapped `TreeHaver::Tree` with source
|
|
63
|
+
- Updated Java backend (`backends/java.rb`) to return wrapped `TreeHaver::Tree` with source
|
|
64
|
+
- Updated Citrus backend (`backends/citrus.rb`) to return wrapped `TreeHaver::Tree` with source
|
|
65
|
+
- Disabled old pass-through stub classes in `tree_haver.rb` (wrapped in `if false` for reference)
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- Fixed `TreeHaver::Tree#supports_editing?` and `#edit` to handle Delegator wrappers correctly by using `.method(:edit)` check instead of `respond_to?`
|
|
70
|
+
- Fixed `PathValidator` to accept versioned `.so` files (e.g., `.so.0`, `.so.14`) which are standard on Linux systems
|
|
71
|
+
- Fixed backend portability - code now works identically across MRI, Rust, FFI, Java, and Citrus backends
|
|
72
|
+
- Fixed inconsistent API - `node.type` now works on all backends (was `node.kind` on TreeStump)
|
|
73
|
+
- Fixed position objects - `start_point` and `end_point` now return objects that work as both `.row` and `[:row]`
|
|
74
|
+
- Fixed child iteration - `node.each` and `node.children` now consistently return `TreeHaver::Node` objects
|
|
75
|
+
- Fixed text extraction - `node.text` now works consistently by storing source in `TreeHaver::Tree`
|
|
76
|
+
|
|
33
77
|
## [1.0.0] - 2025-12-15
|
|
34
78
|
|
|
35
79
|
- TAG: [v1.0.0][1.0.0t]
|
|
@@ -41,8 +85,8 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
41
85
|
|
|
42
86
|
- Initial release
|
|
43
87
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
[
|
|
88
|
+
[Unreleased]: https://github.com/kettle-rb/tree_haver/compare/v2.0.0...HEAD
|
|
89
|
+
[2.0.0]: https://github.com/kettle-rb/tree_haver/compare/v1.0.0...v2.0.0
|
|
90
|
+
[2.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v2.0.0
|
|
47
91
|
[1.0.0]: https://github.com/kettle-rb/tree_haver/compare/a89211bff10f4440b96758a8ac9d7d539001b0c8...v1.0.0
|
|
48
92
|
[1.0.0t]: https://github.com/kettle-rb/tree_haver/tags/v1.0.0
|
data/README.md
CHANGED
|
@@ -54,20 +54,20 @@
|
|
|
54
54
|
|
|
55
55
|
## 🌻 Synopsis
|
|
56
56
|
|
|
57
|
-
TreeHaver is a cross-Ruby adapter for the [
|
|
57
|
+
TreeHaver is a cross-Ruby adapter for the [tree-sitter](https://tree-sitter.github.io/tree-sitter/) parsing library that works seamlessly across MRI Ruby, JRuby, and TruffleRuby. It provides a unified API for parsing source code using tree-sitter grammars, regardless of your Ruby implementation.
|
|
58
58
|
|
|
59
59
|
### The Adapter Pattern: Like Faraday, but for Parsing
|
|
60
60
|
|
|
61
61
|
If you've used [Faraday](https://github.com/lostisland/faraday), [multi_json](https://github.com/intridea/multi_json), or [multi_xml](https://github.com/sferik/multi_xml), you'll feel right at home with TreeHaver. These gems share a common philosophy:
|
|
62
62
|
|
|
63
|
-
| Gem
|
|
64
|
-
|
|
65
|
-
| **Faraday**
|
|
66
|
-
| **multi_json** | JSON parsing
|
|
67
|
-
| **multi_xml**
|
|
68
|
-
| **TreeHaver**
|
|
63
|
+
| Gem | Unified API for | Backend Examples |
|
|
64
|
+
|----------------|---------------------|------------------------------------------------------|
|
|
65
|
+
| **Faraday** | HTTP requests | Net::HTTP, Typhoeus, Patron, Excon |
|
|
66
|
+
| **multi_json** | JSON parsing | Oj, Yajl, JSON gem |
|
|
67
|
+
| **multi_xml** | XML parsing | Nokogiri, LibXML, Ox |
|
|
68
|
+
| **TreeHaver** | tree-sitter parsing | ruby_tree_sitter, tree_stump, FFI, Java JARs, Citrus |
|
|
69
69
|
|
|
70
|
-
**Write once, run anywhere.** Just as Faraday lets you swap HTTP adapters without changing your code, TreeHaver lets you swap
|
|
70
|
+
**Write once, run anywhere.** Just as Faraday lets you swap HTTP adapters without changing your code, TreeHaver lets you swap tree-sitter backends. Your parsing code remains the same whether you're running on MRI with native C extensions, JRuby with FFI, or TruffleRuby.
|
|
71
71
|
|
|
72
72
|
```ruby
|
|
73
73
|
# Your code stays the same regardless of backend
|
|
@@ -90,15 +90,16 @@ tree = parser.parse(source_code)
|
|
|
90
90
|
- **Note**: Currently requires [pboling's fork](https://github.com/pboling/tree_stump/tree/tree_haver) until PRs [#5](https://github.com/joker1007/tree_stump/pull/5), [#7](https://github.com/joker1007/tree_stump/pull/7), [#11](https://github.com/joker1007/tree_stump/pull/11), and [#13 (inclusive of the others)](https://github.com/joker1007/tree_stump/pull/13) are merged
|
|
91
91
|
- **FFI Backend**: Pure Ruby FFI bindings to `libtree-sitter` (ideal for JRuby)
|
|
92
92
|
- **Java Backend**: Support for JRuby's native Java integration, and native java-tree-sitter grammar JARs
|
|
93
|
+
- **Citrus Backend**: Pure Ruby parser using [`citrus`](https://github.com/mjackson/citrus) gem (no native dependencies, portable)
|
|
93
94
|
- **Automatic Backend Selection**: Intelligently selects the best backend for your Ruby implementation
|
|
94
|
-
- **Language Agnostic**: Load any
|
|
95
|
+
- **Language Agnostic**: Load any tree-sitter grammar dynamically (TOML, JSON, Ruby, JavaScript, etc.)
|
|
95
96
|
- **Grammar Discovery**: Built-in `GrammarFinder` utility for platform-aware grammar library discovery
|
|
96
97
|
- **Thread-Safe**: Built-in language registry with thread-safe caching
|
|
97
|
-
- **Minimal API Surface**: Simple, focused API that covers the most common
|
|
98
|
+
- **Minimal API Surface**: Simple, focused API that covers the most common tree-sitter use cases
|
|
98
99
|
|
|
99
100
|
### Why TreeHaver?
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
tree-sitter is a powerful parser generator that creates incremental parsers for many programming languages. However, integrating it into Ruby applications can be challenging:
|
|
102
103
|
|
|
103
104
|
- MRI-based C extensions don't work on JRuby
|
|
104
105
|
- FFI-based solutions may not be optimal for MRI
|
|
@@ -106,25 +107,28 @@ Tree-sitter is a powerful parser generator that creates incremental parsers for
|
|
|
106
107
|
|
|
107
108
|
TreeHaver solves these problems by providing a unified API that automatically selects the appropriate backend for your Ruby implementation, allowing you to write code once and run it anywhere.
|
|
108
109
|
|
|
109
|
-
### Comparison with Other Ruby
|
|
110
|
-
|
|
111
|
-
| Feature |
|
|
112
|
-
|
|
113
|
-
| **MRI Ruby** | ✅ Yes
|
|
114
|
-
| **JRuby** | ✅ Yes (FFI or
|
|
115
|
-
| **TruffleRuby** | ✅ Yes (FFI)
|
|
116
|
-
| **Backend** | Multi (MRI C, Rust, FFI, Java) | C extension only | Rust extension |
|
|
117
|
-
| **Incremental Parsing** | ✅ Via MRI C/Rust backend
|
|
118
|
-
| **Query API** | ⚡ Via MRI/Rust backend
|
|
119
|
-
| **Grammar Discovery** | ✅ Built-in `GrammarFinder`
|
|
120
|
-
| **Security Validations** | ✅ `PathValidator`
|
|
121
|
-
| **Language Registration** | ✅ Thread-safe registry
|
|
122
|
-
| **Native Performance** | ⚡ Backend-dependent
|
|
123
|
-
| **Precompiled Binaries** | ⚡ Via Rust backend
|
|
124
|
-
| **
|
|
110
|
+
### Comparison with Other Ruby AST / Parser Bindings
|
|
111
|
+
|
|
112
|
+
| Feature | [tree_haver] (this gem) | [ruby_tree_sitter] | [tree_stump] | [citrus] |
|
|
113
|
+
|---------------------------|----------------------------------------|--------------------|----------------|-------------|
|
|
114
|
+
| **MRI Ruby** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
115
|
+
| **JRuby** | ✅ Yes (FFI, Java, or Citrus backend) | ❌ No | ❌ No | ✅ Yes |
|
|
116
|
+
| **TruffleRuby** | ✅ Yes (FFI or Citrus) | ❌ No | ❓ Unknown | ✅ Yes |
|
|
117
|
+
| **Backend** | Multi (MRI C, Rust, FFI, Java, Citrus) | C extension only | Rust extension | Pure Ruby |
|
|
118
|
+
| **Incremental Parsing** | ✅ Via MRI C/Rust/Java backend | ✅ Yes | ✅ Yes | ❌ No |
|
|
119
|
+
| **Query API** | ⚡ Via MRI/Rust/Java backend | ✅ Yes | ✅ Yes | ❌ No |
|
|
120
|
+
| **Grammar Discovery** | ✅ Built-in `GrammarFinder` | ❌ Manual | ❌ Manual | ❌ Manual |
|
|
121
|
+
| **Security Validations** | ✅ `PathValidator` | ❌ No | ❌ No | ❌ No |
|
|
122
|
+
| **Language Registration** | ✅ Thread-safe registry | ❌ No | ❌ No | ❌ No |
|
|
123
|
+
| **Native Performance** | ⚡ Backend-dependent | ✅ Native C | ✅ Native Rust | ❌ Pure Ruby |
|
|
124
|
+
| **Precompiled Binaries** | ⚡ Via Rust backend | ✅ Yes | ✅ Yes | ✅ Pure Ruby |
|
|
125
|
+
| **Zero Native Deps** | ⚡ Via Citrus backend | ❌ No | ❌ No | ✅ Yes |
|
|
126
|
+
| **Minimum Ruby** | 3.2+ | 3.0+ | 3.1+ | 0+ |
|
|
125
127
|
|
|
126
128
|
[ruby_tree_sitter]: https://github.com/Faveod/ruby-tree-sitter
|
|
127
129
|
[tree_stump]: https://github.com/anthropics/tree_stump
|
|
130
|
+
[citrus]: https://github.com/mjackson/citrus
|
|
131
|
+
[tree_haver]: https://github.com/kettle-rb/tree_haver
|
|
128
132
|
|
|
129
133
|
**Note:** Java backend works with grammar JARs built specifically for java-tree-sitter, or grammar .so files that statically link tree-sitter. This is why FFI is recommended for JRuby & TruffleRuby.
|
|
130
134
|
|
|
@@ -135,6 +139,7 @@ TreeHaver solves these problems by providing a unified API that automatically se
|
|
|
135
139
|
#### When to Use Each
|
|
136
140
|
|
|
137
141
|
**Choose TreeHaver when:**
|
|
142
|
+
|
|
138
143
|
- You need JRuby or TruffleRuby support
|
|
139
144
|
- You're building a library that should work across Ruby implementations
|
|
140
145
|
- You want automatic grammar discovery and security validations
|
|
@@ -142,39 +147,48 @@ TreeHaver solves these problems by providing a unified API that automatically se
|
|
|
142
147
|
- You need incremental parsing with a unified API
|
|
143
148
|
|
|
144
149
|
**Choose ruby_tree_sitter directly when:**
|
|
150
|
+
|
|
145
151
|
- You only target MRI Ruby
|
|
146
152
|
- You need the full Query API without abstraction
|
|
147
153
|
- You want the most battle-tested C bindings
|
|
148
154
|
- You don't need TreeHaver's grammar discovery
|
|
149
155
|
|
|
150
156
|
**Choose tree_stump directly when:**
|
|
157
|
+
|
|
151
158
|
- You only target MRI Ruby
|
|
152
159
|
- You prefer Rust-based native extensions
|
|
153
160
|
- You want precompiled binaries without system dependencies
|
|
154
161
|
- You don't need TreeHaver's grammar discovery
|
|
155
162
|
- **Note:** Use [pboling's fork (tree_haver branch)](https://github.com/pboling/tree_stump/tree/tree_haver) until PRs [#5](https://github.com/joker1007/tree_stump/pull/5), [#7](https://github.com/joker1007/tree_stump/pull/7), [#11](https://github.com/joker1007/tree_stump/pull/11), [#13](https://github.com/joker1007/tree_stump/pull/13) are merged
|
|
156
163
|
|
|
164
|
+
**Choose citrus directly when:**
|
|
165
|
+
|
|
166
|
+
- You need zero native dependencies (pure Ruby)
|
|
167
|
+
- You're using a Citrus grammar (not tree-sitter grammars)
|
|
168
|
+
- Performance is less critical than portability
|
|
169
|
+
- You don't need TreeHaver's unified API
|
|
170
|
+
|
|
157
171
|
## 💡 Info you can shake a stick at
|
|
158
172
|
|
|
159
|
-
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace]
|
|
160
|
-
|
|
161
|
-
| Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]
|
|
162
|
-
| Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf]
|
|
163
|
-
| Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]
|
|
164
|
-
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor]
|
|
165
|
-
| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc]
|
|
166
|
-
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki]
|
|
173
|
+
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
|
|
174
|
+
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
175
|
+
| Works with JRuby | [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] |
|
|
176
|
+
| Works with Truffle Ruby | [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] |
|
|
177
|
+
| Works with MRI Ruby 3 | [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] |
|
|
178
|
+
| Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] |
|
|
179
|
+
| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
|
|
180
|
+
| Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
|
|
167
181
|
| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
|
|
168
|
-
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2]
|
|
169
|
-
| Maintainer 🎖️
|
|
170
|
-
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub]
|
|
182
|
+
| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
|
|
183
|
+
| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼♂️devto-img]][💖💁🏼♂️devto] |
|
|
184
|
+
| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼♂️aboutme-img]][💖💁🏼♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
|
|
171
185
|
|
|
172
186
|
### Compatibility
|
|
173
187
|
|
|
174
188
|
Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRuby.
|
|
175
189
|
|
|
176
|
-
| 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚
|
|
177
|
-
|
|
190
|
+
| 🚚 _Amazing_ test matrix was brought to you by | 🔎 appraisal2 🔎 and the color 💚 green 💚 |
|
|
191
|
+
| ---------------------------------------------- | -------------------------------------------------------- |
|
|
178
192
|
| 👟 Check it out! | ✨ [github.com/appraisal-rb/appraisal2][💎appraisal2] ✨ |
|
|
179
193
|
|
|
180
194
|
### Federated DVCS
|
|
@@ -183,9 +197,9 @@ Compatible with MRI Ruby 3.2.0+, and concordant releases of JRuby, and TruffleRu
|
|
|
183
197
|
<summary>Find this repo on federated forges (Coming soon!)</summary>
|
|
184
198
|
|
|
185
199
|
| Federated [DVCS][💎d-in-dvcs] Repository | Status | Issues | PRs | Wiki | CI | Discussions |
|
|
186
|
-
|
|
187
|
-
| 🧪 [kettle-rb/tree_haver on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖
|
|
188
|
-
| 🧊 [kettle-rb/tree_haver on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖
|
|
200
|
+
| ----------------------------------------------- | --------------------------------------------------------------------- | ------------------------- | ------------------------ | ------------------------- | ------------------------ | ---------------------------- |
|
|
201
|
+
| 🧪 [kettle-rb/tree_haver on GitLab][📜src-gl] | The Truth | [💚][🤝gl-issues] | [💚][🤝gl-pulls] | [💚][📜gl-wiki] | 🐭 Tiny Matrix | ➖ |
|
|
202
|
+
| 🧊 [kettle-rb/tree_haver on CodeBerg][📜src-cb] | An Ethical Mirror ([Donate][🤝cb-donate]) | [💚][🤝cb-issues] | [💚][🤝cb-pulls] | ➖ | ⭕️ No Matrix | ➖ |
|
|
189
203
|
| 🐙 [kettle-rb/tree_haver on GitHub][📜src-gh] | Another Mirror | [💚][🤝gh-issues] | [💚][🤝gh-pulls] | [💚][📜gh-wiki] | 💯 Full Matrix | [💚][gh-discussions] |
|
|
190
204
|
| 🎮️ [Discord Server][✉️discord-invite] | [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] | [Let's][✉️discord-invite] | [talk][✉️discord-invite] | [about][✉️discord-invite] | [this][✉️discord-invite] | [library!][✉️discord-invite] |
|
|
191
205
|
|
|
@@ -206,7 +220,7 @@ The maintainers of this and thousands of other packages are working with Tidelif
|
|
|
206
220
|
|
|
207
221
|
- 💡Subscribe for support guarantees covering _all_ your FLOSS dependencies
|
|
208
222
|
- 💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]
|
|
209
|
-
- 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and
|
|
223
|
+
- 💡Tidelift pays maintainers to maintain the software you depend on!<br/>📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is "[never gonna let you down][🧮kloc]", and _supports_ open source maintainers
|
|
210
224
|
|
|
211
225
|
Alternatively:
|
|
212
226
|
|
|
@@ -245,7 +259,7 @@ Add my public key (if you haven’t already, expires 2045-04-29) as a trusted ce
|
|
|
245
259
|
gem cert --add <(curl -Ls https://raw.github.com/galtzo-floss/certs/main/pboling.pem)
|
|
246
260
|
```
|
|
247
261
|
|
|
248
|
-
You only need to do that once.
|
|
262
|
+
You only need to do that once. Then proceed to install with:
|
|
249
263
|
|
|
250
264
|
```console
|
|
251
265
|
gem install tree_haver -P HighSecurity
|
|
@@ -359,15 +373,18 @@ TreeHaver automatically selects the best backend for your Ruby implementation, b
|
|
|
359
373
|
TreeHaver.backend = :auto
|
|
360
374
|
|
|
361
375
|
# Force a specific backend
|
|
362
|
-
TreeHaver.backend = :mri
|
|
363
|
-
TreeHaver.backend = :rust
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
TreeHaver.backend = :ffi
|
|
367
|
-
TreeHaver.backend = :java
|
|
376
|
+
TreeHaver.backend = :mri # Use ruby_tree_sitter (MRI only, C extension)
|
|
377
|
+
TreeHaver.backend = :rust # Use tree_stump (MRI, Rust extension with precompiled binaries)
|
|
378
|
+
# Note: Requires pboling's fork until PRs #5, #7, #11, #13 are merged
|
|
379
|
+
# See: https://github.com/pboling/tree_stump/tree/tree_haver
|
|
380
|
+
TreeHaver.backend = :ffi # Use FFI bindings (works on MRI and JRuby)
|
|
381
|
+
TreeHaver.backend = :java # Use Java bindings (JRuby only, coming soon)
|
|
382
|
+
TreeHaver.backend = :citrus # Use Citrus pure Ruby parser
|
|
383
|
+
# NOTE: Portable, all Ruby implementations
|
|
384
|
+
# CAVEAT: few major language grammars, but many esoteric grammars
|
|
368
385
|
```
|
|
369
386
|
|
|
370
|
-
**Auto-selection priority on MRI:** MRI → Rust → FFI
|
|
387
|
+
**Auto-selection priority on MRI:** MRI → Rust → FFI → Citrus
|
|
371
388
|
|
|
372
389
|
You can also set the backend via environment variable:
|
|
373
390
|
|
|
@@ -384,6 +401,7 @@ TreeHaver recognizes several environment variables for configuration:
|
|
|
384
401
|
#### Security Configuration
|
|
385
402
|
|
|
386
403
|
- **`TREE_HAVER_TRUSTED_DIRS`**: Comma-separated list of additional trusted directories for grammar libraries
|
|
404
|
+
|
|
387
405
|
```bash
|
|
388
406
|
# For Homebrew on Linux and luarocks
|
|
389
407
|
export TREE_HAVER_TRUSTED_DIRS="/home/linuxbrew/.linuxbrew/Cellar,~/.local/share/mise/installs/lua"
|
|
@@ -399,11 +417,12 @@ TreeHaver recognizes several environment variables for configuration:
|
|
|
399
417
|
```
|
|
400
418
|
|
|
401
419
|
If not set, TreeHaver tries these names in order:
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
420
|
+
|
|
421
|
+
- `tree-sitter`
|
|
422
|
+
- `libtree-sitter.so.0`
|
|
423
|
+
- `libtree-sitter.so`
|
|
424
|
+
- `libtree-sitter.dylib`
|
|
425
|
+
- `libtree-sitter.dll`
|
|
407
426
|
|
|
408
427
|
#### Language Symbol Resolution
|
|
409
428
|
|
|
@@ -468,7 +487,7 @@ if finder.available?
|
|
|
468
487
|
puts "TOML grammar found at: #{finder.find_library_path}"
|
|
469
488
|
else
|
|
470
489
|
puts finder.not_found_message
|
|
471
|
-
# => "
|
|
490
|
+
# => "tree-sitter toml grammar not found. Searched: /usr/lib/libtree-sitter-toml.so, ..."
|
|
472
491
|
end
|
|
473
492
|
|
|
474
493
|
# Register the language if available
|
|
@@ -482,11 +501,11 @@ language = TreeHaver::Language.toml
|
|
|
482
501
|
|
|
483
502
|
Given just the language name, `GrammarFinder` automatically derives:
|
|
484
503
|
|
|
485
|
-
| Property
|
|
486
|
-
|
|
487
|
-
| ENV var
|
|
504
|
+
| Property | Derived Value (for `:toml`) |
|
|
505
|
+
| ---------------- | ---------------------------------------------------- |
|
|
506
|
+
| ENV var | `TREE_SITTER_TOML_PATH` |
|
|
488
507
|
| Library filename | `libtree-sitter-toml.so` (Linux) or `.dylib` (macOS) |
|
|
489
|
-
| Symbol name
|
|
508
|
+
| Symbol name | `tree_sitter_toml` |
|
|
490
509
|
|
|
491
510
|
#### Search Order
|
|
492
511
|
|
|
@@ -496,7 +515,7 @@ Given just the language name, `GrammarFinder` automatically derives:
|
|
|
496
515
|
2. **Extra paths**: Custom paths provided at initialization
|
|
497
516
|
3. **System paths**: Common installation directories (`/usr/lib`, `/usr/local/lib`, `/opt/homebrew/lib`, etc.)
|
|
498
517
|
|
|
499
|
-
#### Usage in
|
|
518
|
+
#### Usage in \*-merge Gems
|
|
500
519
|
|
|
501
520
|
The `GrammarFinder` pattern enables clean integration in language-specific merge gems:
|
|
502
521
|
|
|
@@ -555,6 +574,8 @@ TreeHaver.capabilities
|
|
|
555
574
|
# => { backend: :mri, query: true, bytes_field: true }
|
|
556
575
|
# or
|
|
557
576
|
# => { backend: :ffi, parse: true, query: false, bytes_field: true }
|
|
577
|
+
# or
|
|
578
|
+
# => { backend: :citrus, parse: true, query: false, bytes_field: false }
|
|
558
579
|
```
|
|
559
580
|
|
|
560
581
|
### Compatibility Mode
|
|
@@ -638,7 +659,7 @@ tree = parser.parse(toml_source)
|
|
|
638
659
|
|
|
639
660
|
### Parsing Different Languages
|
|
640
661
|
|
|
641
|
-
TreeHaver works with any
|
|
662
|
+
TreeHaver works with any tree-sitter grammar:
|
|
642
663
|
|
|
643
664
|
```ruby
|
|
644
665
|
# Parse Ruby code
|
|
@@ -704,7 +725,7 @@ tree.edit(
|
|
|
704
725
|
new_tree = parser.parse_string(tree, "x = 42")
|
|
705
726
|
```
|
|
706
727
|
|
|
707
|
-
**Note:** Incremental parsing requires the MRI (`ruby_tree_sitter`), Rust (`tree_stump`), or Java (`java-tree-sitter`) backend. The FFI
|
|
728
|
+
**Note:** Incremental parsing requires the MRI (`ruby_tree_sitter`), Rust (`tree_stump`), or Java (`java-tree-sitter`) backend. The FFI and Citrus backends do not currently support incremental parsing. You can check support with:
|
|
708
729
|
|
|
709
730
|
**Note:** `tree_stump` requires [pboling's fork (tree_haver branch)](https://github.com/pboling/tree_stump/tree/tree_haver) until PRs [#5](https://github.com/joker1007/tree_stump/pull/5), [#7](https://github.com/joker1007/tree_stump/pull/7), [#11](https://github.com/joker1007/tree_stump/pull/11), [#13](https://github.com/joker1007/tree_stump/pull/13) are merged.
|
|
710
731
|
|
|
@@ -724,7 +745,7 @@ end
|
|
|
724
745
|
# Check if a backend is available
|
|
725
746
|
if TreeHaver.backend_module.nil?
|
|
726
747
|
puts "No TreeHaver backend is available!"
|
|
727
|
-
puts "Install ruby_tree_sitter (MRI)
|
|
748
|
+
puts "Install ruby_tree_sitter (MRI), ffi gem with libtree-sitter, or citrus gem"
|
|
728
749
|
end
|
|
729
750
|
```
|
|
730
751
|
|
|
@@ -745,9 +766,9 @@ parser = TreeHaver::Parser.new
|
|
|
745
766
|
|
|
746
767
|
#### JRuby
|
|
747
768
|
|
|
748
|
-
On JRuby, TreeHaver can use
|
|
769
|
+
On JRuby, TreeHaver can use the FFI backend, Java backend, or Citrus backend:
|
|
749
770
|
|
|
750
|
-
**Option 1: FFI Backend (
|
|
771
|
+
**Option 1: FFI Backend (recommended for tree-sitter grammars)**
|
|
751
772
|
|
|
752
773
|
```ruby
|
|
753
774
|
# Gemfile
|
|
@@ -809,19 +830,49 @@ TreeHaver.backend = :ffi
|
|
|
809
830
|
The FFI backend uses Ruby's FFI gem which relies on the system's dynamic linker, correctly resolving symbol dependencies between `libtree-sitter.so` and grammar libraries.
|
|
810
831
|
|
|
811
832
|
The Java backend will work with:
|
|
833
|
+
|
|
812
834
|
- Grammar JARs built specifically for java-tree-sitter (self-contained)
|
|
813
835
|
- Grammar `.so` files that statically link tree-sitter
|
|
814
836
|
|
|
837
|
+
**Option 3: Citrus Backend (pure Ruby, portable)**
|
|
838
|
+
|
|
839
|
+
```ruby
|
|
840
|
+
# Gemfile
|
|
841
|
+
gem "tree_haver"
|
|
842
|
+
gem "citrus" # Pure Ruby parser, zero native dependencies
|
|
843
|
+
|
|
844
|
+
# Code - Force Citrus backend for maximum portability
|
|
845
|
+
TreeHaver.backend = :citrus
|
|
846
|
+
|
|
847
|
+
# Check if Citrus backend is available
|
|
848
|
+
if TreeHaver::Backends::Citrus.available?
|
|
849
|
+
puts "Citrus backend is ready!"
|
|
850
|
+
puts TreeHaver.capabilities
|
|
851
|
+
# => { backend: :citrus, parse: true, query: false, bytes_field: false }
|
|
852
|
+
end
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
**⚠️ Citrus Backend Limitations:**
|
|
856
|
+
|
|
857
|
+
- Uses Citrus grammars (not tree-sitter grammars)
|
|
858
|
+
- No incremental parsing support
|
|
859
|
+
- No query API
|
|
860
|
+
- Pure Ruby performance (slower than native backends)
|
|
861
|
+
- Best for: prototyping, environments without native extension support, teaching
|
|
862
|
+
|
|
815
863
|
#### TruffleRuby
|
|
816
864
|
|
|
817
|
-
TruffleRuby can use
|
|
865
|
+
TruffleRuby can use the MRI, FFI, or Citrus backend:
|
|
818
866
|
|
|
819
867
|
```ruby
|
|
820
|
-
# Use FFI backend (recommended)
|
|
868
|
+
# Use FFI backend (recommended for tree-sitter grammars)
|
|
821
869
|
TreeHaver.backend = :ffi
|
|
822
870
|
|
|
823
871
|
# Or try MRI backend if ruby_tree_sitter compiles on your TruffleRuby version
|
|
824
872
|
TreeHaver.backend = :mri
|
|
873
|
+
|
|
874
|
+
# Or use Citrus backend for zero native dependencies
|
|
875
|
+
TreeHaver.backend = :citrus
|
|
825
876
|
```
|
|
826
877
|
|
|
827
878
|
### Advanced: Testing with Multiple Backends
|
|
@@ -912,7 +963,7 @@ You can support the development of kettle-rb tools via
|
|
|
912
963
|
and [Tidelift][🏙️entsup-tidelift].
|
|
913
964
|
|
|
914
965
|
| 📍 NOTE |
|
|
915
|
-
|
|
966
|
+
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
916
967
|
| If doing a sponsorship in the form of donation is problematic for your company <br/> from an accounting standpoint, we'd recommend the use of Tidelift, <br/> where you can get a support-like subscription instead. |
|
|
917
968
|
|
|
918
969
|
### Open Collective for Individuals
|
|
@@ -922,7 +973,9 @@ Support us with a monthly donation and help us continue our activities. [[Become
|
|
|
922
973
|
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
923
974
|
|
|
924
975
|
<!-- OPENCOLLECTIVE-INDIVIDUALS:START -->
|
|
976
|
+
|
|
925
977
|
No backers yet. Be the first!
|
|
978
|
+
|
|
926
979
|
<!-- OPENCOLLECTIVE-INDIVIDUALS:END -->
|
|
927
980
|
|
|
928
981
|
### Open Collective for Organizations
|
|
@@ -932,14 +985,16 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
|
|
|
932
985
|
NOTE: [kettle-readme-backers][kettle-readme-backers] updates this list every day, automatically.
|
|
933
986
|
|
|
934
987
|
<!-- OPENCOLLECTIVE-ORGANIZATIONS:START -->
|
|
988
|
+
|
|
935
989
|
No sponsors yet. Be the first!
|
|
990
|
+
|
|
936
991
|
<!-- OPENCOLLECTIVE-ORGANIZATIONS:END -->
|
|
937
992
|
|
|
938
993
|
[kettle-readme-backers]: https://github.com/kettle-rb/tree_haver/blob/main/exe/kettle-readme-backers
|
|
939
994
|
|
|
940
995
|
### Another way to support open-source
|
|
941
996
|
|
|
942
|
-
I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small.
|
|
997
|
+
I’m driven by a passion to foster a thriving open-source community – a space where people can tackle complex problems, no matter how small. Revitalizing libraries that have fallen into disrepair, and building new libraries focused on solving real-world challenges, are my passions. I was recently affected by layoffs, and the tech jobs market is unwelcoming. I’m reaching out here because your support would significantly aid my efforts to provide for my family, and my farm (11 🐔 chickens, 2 🐶 dogs, 3 🐰 rabbits, 8 🐈 cats).
|
|
943
998
|
|
|
944
999
|
If you work at a company that uses my work, please encourage them to support me as a corporate sponsor. My work on gems you use might show up in `bundle fund`.
|
|
945
1000
|
|
|
@@ -1010,7 +1065,7 @@ a new version should be immediately released that restores compatibility.
|
|
|
1010
1065
|
Breaking changes to the public API will only be introduced with new major versions.
|
|
1011
1066
|
|
|
1012
1067
|
> dropping support for a platform is both obviously and objectively a breaking change <br/>
|
|
1013
|
-
|
|
1068
|
+
> —Jordan Harband ([@ljharb](https://github.com/ljharb), maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]
|
|
1014
1069
|
|
|
1015
1070
|
I understand that policy doesn't work universally ("exceptions to every rule!"),
|
|
1016
1071
|
but it is the policy here.
|
|
@@ -1027,7 +1082,7 @@ spec.add_dependency("tree_haver", "~> 1.0")
|
|
|
1027
1082
|
<summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
|
|
1028
1083
|
|
|
1029
1084
|
SemVer should, IMO, but doesn't explicitly, say that dropping support for specific Platforms
|
|
1030
|
-
is a
|
|
1085
|
+
is a _breaking change_ to an API, and for that reason the bike shedding is endless.
|
|
1031
1086
|
|
|
1032
1087
|
To get a better understanding of how SemVer is intended to work over a project's lifetime,
|
|
1033
1088
|
read this article from the creator of SemVer:
|
|
@@ -1114,7 +1169,6 @@ Thanks for RTFM. ☺️
|
|
|
1114
1169
|
[✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
|
|
1115
1170
|
[✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
|
|
1116
1171
|
[✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
|
|
1117
|
-
|
|
1118
1172
|
[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
|
|
1119
1173
|
[⛳️gem-namespace]: https://github.com/kettle-rb/tree_haver
|
|
1120
1174
|
[⛳️namespace-img]: https://img.shields.io/badge/namespace-TreeHaver-3C2D2D.svg?style=square&logo=ruby&logoColor=white
|
|
@@ -1238,7 +1292,7 @@ Thanks for RTFM. ☺️
|
|
|
1238
1292
|
[📌gitmoji]: https://gitmoji.dev
|
|
1239
1293
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1240
1294
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1241
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.
|
|
1295
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.726-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1242
1296
|
[🔐security]: SECURITY.md
|
|
1243
1297
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1244
1298
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|