Module: TreeHaver::Backends::FFI
- Defined in:
- lib/tree_haver/backends/ffi.rb
Overview
Requires the ffi gem and libtree-sitter shared library to be installed
FFI-based backend for calling libtree-sitter directly
This backend uses Ruby FFI (JNR-FFI on JRuby) to call the native tree-sitter
C library without requiring MRI C extensions.
The FFI backend currently supports:
- Parsing source code
- AST node traversal
- Accessing node types and children
Not yet supported:
- Query API (tree-sitter queries/patterns)
== Tree/Node Architecture
This backend defines raw FFI::Tree and FFI::Node wrapper classes that
provide minimal FFI bindings to the tree-sitter C structs. These are not
intended for direct use by application code.
The wrapping hierarchy is:
FFI::Tree/Node (raw FFI wrappers) → TreeHaver::Tree/Node → Base::Tree/Node
When you use TreeHaver::Parser#parse:
FFI::Parser#parsereturns anFFI::Tree(raw pointer wrapper)TreeHaver::Parserwraps it inTreeHaver::Tree(adds source storage)TreeHaver::Tree#root_nodewrapsFFI::NodeinTreeHaver::Node
The TreeHaver::Tree and TreeHaver::Node wrappers provide the full unified
API including #children, #text, #source, #source_position, etc.
This differs from pure-Ruby backends (Citrus, Parslet, Prism, Psych) which
define Tree/Node classes that directly inherit from Base::Tree/Base::Node.
== Platform Compatibility
- MRI Ruby: ✓ Full support
- JRuby: ✓ Full support (uses JNR-FFI)
- TruffleRuby: ✗ TruffleRuby’s FFI doesn’t support STRUCT_BY_VALUE return types
(used by ts_tree_root_node, ts_node_child, ts_node_start_point, etc.)
Defined Under Namespace
Modules: Native Classes: Language, Node, Parser, Tree
Class Method Summary collapse
-
.available? ⇒ Boolean
Check if the FFI backend is available.
-
.capabilities ⇒ Hash{Symbol => Object}
Get capabilities supported by this backend.
-
.ffi_gem_available? ⇒ Boolean
private
Check if the FFI gem can be loaded and is usable for tree-sitter.
-
.reset! ⇒ void
private
Reset the load state (primarily for testing).
Class Method Details
.available? ⇒ Boolean
Check if the FFI backend is available
The FFI backend requires:
- The ffi gem to be installed
- NOT running on TruffleRuby (STRUCT_BY_VALUE limitation)
- MRI backend (ruby_tree_sitter) not already loaded (symbol conflicts)
70 71 72 73 74 75 |
# File 'lib/tree_haver/backends/ffi.rb', line 70 def available? return false unless ffi_gem_available? # Check if MRI backend has been loaded (which blocks FFI) !defined?(::TreeSitter::Parser) end |
.capabilities ⇒ Hash{Symbol => Object}
Get capabilities supported by this backend
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/tree_haver/backends/ffi.rb', line 123 def capabilities return {} unless available? { backend: :ffi, parse: true, query: false, # Query API not yet implemented in FFI backend bytes_field: true, incremental: false, } end |
.ffi_gem_available? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns false on TruffleRuby because TruffleRuby’s FFI doesn’t support
STRUCT_BY_VALUE return types (used by ts_tree_root_node, ts_node_child, etc.)
Check if the FFI gem can be loaded and is usable for tree-sitter
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/tree_haver/backends/ffi.rb', line 83 def ffi_gem_available? return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable @loaded = begin # rubocop:disable ThreadSafety/ClassInstanceVariable # TruffleRuby's FFI doesn't support STRUCT_BY_VALUE return types # which tree-sitter uses extensively (ts_tree_root_node, ts_node_child, etc.) # :nocov: TruffleRuby returns false early - subsequent FFI code paths unreachable on TruffleRuby if RUBY_ENGINE == "truffleruby" false # :nocov: else require "ffi" true end rescue LoadError false # :nocov: defensive code - StandardError during require is extremely rare rescue StandardError false # :nocov: end @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable end |
.reset! ⇒ void
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This method returns an undefined value.
Reset the load state (primarily for testing)
112 113 114 115 |
# File 'lib/tree_haver/backends/ffi.rb', line 112 def reset! @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable end |