Module: TreeHaver::LanguageRegistry Private

Defined in:
lib/tree_haver/language_registry.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Thread-safe language registrations and cache for loaded Language handles

The LanguageRegistry provides two main functions:

  1. Registrations: Store mappings from language names to backend-specific configurations

  2. Cache: Memoize loaded Language objects to avoid repeated dlopen calls

The registry supports multiple backends for the same language, allowing runtime switching, benchmarking, and fallback scenarios.

Supported Backend Types

The registry is extensible and supports any backend type. Common types include:

  • :tree_sitter - Native tree-sitter grammars (.so files)

  • :citrus - Citrus PEG parser grammars (pure Ruby)

  • :prism - Ruby’s Prism parser (Ruby source only)

  • :psych - Ruby’s Psych parser (YAML only)

  • :commonmarker - Commonmarker gem (Markdown)

  • :markly - Markly gem (Markdown/GFM)

  • :rbs - RBS gem (RBS type signatures) - registered externally by rbs-merge

External gems can register their own backend types using the same API.

Registration structure: “‘ruby } “`

“‘ruby

TreeHaver::LanguageRegistry.register(:toml, :tree_sitter,
  path: "/path/to/lib.so", symbol: "tree_sitter_toml")

“‘

“‘ruby

TreeHaver::LanguageRegistry.register(:toml, :citrus,
  grammar_module: TomlRB::Document, gem_name: "toml-rb")

“‘

“‘ruby

TreeHaver::LanguageRegistry.register(:rbs, :rbs,
  backend_module: Rbs::Merge::Backends::RbsBackend,
  gem_name: "rbs")

“‘

Examples:

Register tree-sitter grammar

Register Citrus grammar

Register a pure Ruby backend (internal or external)

Class Method Summary collapse

Class Method Details

.clearvoid

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.

Clear all registrations and cache

Removes all language registrations and cached Language objects. Primarily used in tests to reset state between test cases.

Examples:

LanguageRegistry.clear


182
183
184
185
186
187
188
# File 'lib/tree_haver/language_registry.rb', line 182

def clear
  @mutex.synchronize do
    @registrations.clear
    @cache.clear
  end
  nil
end

.clear_cache!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.

Clear the language cache

Removes all cached Language objects. The next call to fetch for any key will recompute the value. Does not clear registrations.

Examples:

LanguageRegistry.clear_cache!


169
170
171
172
# File 'lib/tree_haver/language_registry.rb', line 169

def clear_cache!
  @mutex.synchronize { @cache.clear }
  nil
end

.fetch(key) ⇒ Object

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.

Fetch a cached language by key or compute and store it

This method provides thread-safe memoization for loaded Language objects. If the key exists in the cache, the cached value is returned immediately. Otherwise, the block is called to compute the value, which is then cached.

Examples:

language = LanguageRegistry.fetch(["/path/lib.so", "symbol", "toml"]) do
  expensive_language_load_operation
end

Parameters:

  • key (Array)

    cache key, typically [path, symbol, name]

Yield Returns:

  • (Object)

    the computed language handle (called only on cache miss)

Returns:

  • (Object)

    the cached or computed language handle



153
154
155
156
157
158
159
# File 'lib/tree_haver/language_registry.rb', line 153

def fetch(key)
  @mutex.synchronize do
    return @cache[key] if @cache.key?(key)
    value = yield
    @cache[key] = value
  end
end

.register(name, backend_type, **config) ⇒ 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.

Register a language for a specific backend

Stores backend-specific configuration for a language. Multiple backends can be registered for the same language without conflict.

Examples:

Register tree-sitter grammar

LanguageRegistry.register(:toml, :tree_sitter,
  path: "/usr/local/lib/libtree-sitter-toml.so", symbol: "tree_sitter_toml")

Register Citrus grammar

LanguageRegistry.register(:toml, :citrus,
  grammar_module: TomlRB::Document, gem_name: "toml-rb")

Register pure Ruby backend (external gem)

LanguageRegistry.register(:rbs, :rbs,
  backend_module: Rbs::Merge::Backends::RbsBackend, gem_name: "rbs")

Parameters:

  • name (Symbol, String)

    language identifier (e.g., :toml, :json, :ruby, :yaml, :rbs)

  • backend_type (Symbol)

    backend type (:tree_sitter, :citrus, :prism, :psych, :commonmarker, :markly, or custom)

  • config (Hash)

    backend-specific configuration

Options Hash (**config):

  • :path (String)

    tree-sitter library path (for tree-sitter backends)

  • :symbol (String)

    exported symbol name (for tree-sitter backends)

  • :grammar_module (Module)

    Citrus grammar module (for Citrus backend)

  • :backend_module (Module)

    backend module with Language/Parser classes (for pure Ruby backends)

  • :gem_name (String)

    gem name for error messages and availability checks



100
101
102
103
104
105
106
107
108
109
# File 'lib/tree_haver/language_registry.rb', line 100

def register(name, backend_type, **config)
  key = name.to_sym
  backend_key = backend_type.to_sym

  @mutex.synchronize do
    @registrations[key] ||= {}
    @registrations[key][backend_key] = config.compact
  end
  nil
end

.registered(name, backend_type = nil) ⇒ Hash{Symbol => Hash}, ...

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.

Fetch registration entries for a language

Returns all backend-specific configurations for a language.

Examples:

Get all backends

entries = LanguageRegistry.registered(:toml)
# => {
#   tree_sitter: { path: "/usr/local/lib/libtree-sitter-toml.so", symbol: "tree_sitter_toml" },
#   citrus: { grammar_module: TomlRB::Document, gem_name: "toml-rb" }
# }

Get specific backend

entry = LanguageRegistry.registered(:toml, :citrus)
# => { grammar_module: TomlRB::Document, gem_name: "toml-rb" }

Parameters:

  • name (Symbol, String)

    language identifier

  • backend_type (Symbol, nil) (defaults to: nil)

    optional backend type to filter by

Returns:

  • (Hash{Symbol => Hash}, Hash, nil)

    all backends or specific backend config



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/tree_haver/language_registry.rb', line 127

def registered(name, backend_type = nil)
  @mutex.synchronize do
    lang_config = @registrations[name.to_sym]
    return unless lang_config

    if backend_type
      lang_config[backend_type.to_sym]
    else
      lang_config
    end
  end
end