Module: TreeHaver::BackendAPI

Defined in:
lib/tree_haver/backend_api.rb

Overview

Backend API contract definitions and validation

This module defines the expected API surface for TreeHaver backends. Each backend must provide Parser, Language, Tree, and Node classes/objects that conform to these interfaces.

Architecture

TreeHaver backends fall into two categories:

  1. **Raw backends** (MRI, FFI, Rust) - Return raw tree-sitter objects (e.g., ::TreeSitter::Node). TreeHaver::Node wraps these and provides a unified API via method delegation.

  2. **Wrapper backends** (Java, Citrus, Prism, Psych, Commonmarker, Markly) - Return their own wrapper objects that must implement the expected API directly, since TreeHaver::Node will delegate to them.

Usage

# Validate a backend's API compliance
TreeHaver::BackendAPI.validate!(backend_module)

# Check specific class compliance
TreeHaver::BackendAPI.validate_node!(node_instance)

Constant Summary collapse

LANGUAGE_CLASS_METHODS =

Required methods for Language class/instances

All backends MUST implement from_library for API consistency. Language-specific backends (Psych, Prism, Commonmarker, Markly) should implement from_library to accept (and ignore) path/symbol parameters, returning their single supported language.

This ensures ‘TreeHaver.parser_for(:yaml)` works regardless of backend - tree-sitter backends load the YAML grammar, while Psych returns its built-in YAML support.

Convenience methods (yaml, ruby, markdown) are OPTIONAL and only make sense on backends that only support one language family.

i[
  from_library
].freeze
LANGUAGE_OPTIONAL_CLASS_METHODS =

Optional convenience methods for language-specific backends These are NOT required - they’re just shortcuts for single-language backends

i[
  yaml
  ruby
  markdown
].freeze
LANGUAGE_INSTANCE_METHODS =
i[
  backend
].freeze
PARSER_CLASS_METHODS =

Required methods for Parser class/instances

i[
  new
].freeze
PARSER_INSTANCE_METHODS =
i[
  language=
  parse
].freeze
PARSER_OPTIONAL_METHODS =

Optional Parser methods (for incremental parsing)

i[
  parse_string
].freeze
TREE_INSTANCE_METHODS =

Required methods for Tree instances Note: Tree is returned by Parser#parse, not instantiated directly

i[
  root_node
].freeze
TREE_OPTIONAL_METHODS =

Optional Tree methods (for incremental parsing)

i[
  edit
].freeze
NODE_INSTANCE_METHODS =

Required methods for Node instances returned by wrapper backends These are the methods TreeHaver::Node delegates to inner_node

Raw backends (MRI, FFI, Rust) return tree-sitter native nodes which have their own API. TreeHaver::Node handles the translation.

Wrapper backends (Java, Citrus, etc.) must implement these methods on their Node class since TreeHaver::Node delegates to them.

i[
  type
  child_count
  child
  start_byte
  end_byte
].freeze
NODE_OPTIONAL_METHODS =

Optional Node methods - should return nil if not supported

i[
  parent
  next_sibling
  prev_sibling
  named?
  has_error?
  missing?
  text
  child_by_field_name
  start_point
  end_point
].freeze
NODE_ALIASES =

Methods that have common aliases across backends

{
  type: i[kind],
  named?: i[is_named? is_named],
  has_error?: i[has_error],
  missing?: i[is_missing? is_missing],
  next_sibling: i[next_named_sibling],
  prev_sibling: i[previous_sibling previous_named_sibling prev_named_sibling],
}.freeze

Class Method Summary collapse

Class Method Details

.validate(backend_module, strict: false) ⇒ Hash

Validate a backend module for API compliance

Parameters:

  • backend_module (Module)

    The backend module (e.g., TreeHaver::Backends::Java)

  • strict (Boolean) (defaults to: false)

    If true, raise on missing optional methods

Returns:

  • (Hash)

    Validation results with :valid, :errors, :warnings keys



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/tree_haver/backend_api.rb', line 132

def validate(backend_module, strict: false)
  results = {
    valid: true,
    errors: [],
    warnings: [],
    capabilities: {},
  }

  # Check module-level methods
  validate_module_methods(backend_module, results)

  # Check Language class
  if backend_module.const_defined?(:Language)
    validate_language(backend_module::Language, results)
  else
    results[:errors] << "Missing Language class"
    results[:valid] = false
  end

  # Check Parser class
  if backend_module.const_defined?(:Parser)
    validate_parser(backend_module::Parser, results)
  else
    results[:errors] << "Missing Parser class"
    results[:valid] = false
  end

  # Check Tree class if present (some backends return raw trees)
  if backend_module.const_defined?(:Tree)
    validate_tree(backend_module::Tree, results)
  else
    results[:warnings] << "No Tree class (backend returns raw trees)"
  end

  # Check Node class if present (wrapper backends)
  if backend_module.const_defined?(:Node)
    validate_node_class(backend_module::Node, results, strict: strict)
  else
    results[:warnings] << "No Node class (backend returns raw nodes, TreeHaver::Node will wrap)"
  end

  # Fail on warnings in strict mode
  if strict && results[:warnings].any?
    results[:valid] = false
  end

  results
end

.validate!(backend_module, strict: false) ⇒ Hash

Validate and raise on failure

Parameters:

  • backend_module (Module)

    The backend module to validate

  • strict (Boolean) (defaults to: false)

    If true, treat warnings as errors

Returns:

  • (Hash)

    Validation results if valid

Raises:



187
188
189
190
191
192
193
194
195
196
# File 'lib/tree_haver/backend_api.rb', line 187

def validate!(backend_module, strict: false)
  results = validate(backend_module, strict: strict)
  unless results[:valid]
    raise TreeHaver::Error,
      "Backend #{backend_module.name} API validation failed:\n  " \
        "Errors: #{results[:errors].join(", ")}\n  " \
        "Warnings: #{results[:warnings].join(", ")}"
  end
  results
end

.validate_node_instance(node) ⇒ Hash

Validate a Node instance for API compliance

Parameters:

  • node (Object)

    A node instance to validate

Returns:

  • (Hash)

    Validation results



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/tree_haver/backend_api.rb', line 202

def validate_node_instance(node)
  results = {
    valid: true,
    errors: [],
    warnings: [],
    supported_methods: [],
    unsupported_methods: [],
  }

  # Check required methods
  NODE_INSTANCE_METHODS.each do |method|
    if responds_to_with_aliases?(node, method)
      results[:supported_methods] << method
    else
      results[:errors] << "Missing required method: #{method}"
      results[:valid] = false
    end
  end

  # Check optional methods
  NODE_OPTIONAL_METHODS.each do |method|
    if responds_to_with_aliases?(node, method)
      results[:supported_methods] << method
    else
      results[:unsupported_methods] << method
      results[:warnings] << "Missing optional method: #{method}"
    end
  end

  results
end