module Noir::TreeSitter

Overview

Thin high-level facade. Keeps tree lifetime tied to an object so callers don't have to think about ts_tree_delete.

Defined in:

ext/tree_sitter/tree_sitter.cr

Constant Summary

MAX_AST_DEPTH = 1024

Recursion guard for AST walkers. Crystal's default fiber stack is generous (~8 MB) but a malicious source file with deeply nested syntax — (((((((...))))))) chains, deeply nested object literals, recursive template expressions — could cascade through a custom walker until the stack runs out. Real production code rarely nests beyond ~100 levels, so 1024 is comfortably above legitimate input and well below the stack ceiling.

NAMED_CHILD_CURSOR_THRESHOLD = 8

Above this many named children, switch from indexed access to a tree cursor. ts_node_named_child(node, i) is O(i) (it re-walks the sibling list each call), so the indexed loop is O(n^2) in the child count; a cursor walks each child in O(1) amortised but costs one allocation to set up. Small nodes stay on the allocation-free indexed path; wide nodes (large class bodies, program roots on big files) take the cursor and avoid the quadratic. Both paths yield the exact same named children in the same order.

Class Method Summary

Class Method Detail

def self.each_named_child(node : LibTreeSitter::TSNode, &) #

Iterates named children without allocating an array.

The cursor path lives in a separate @[NoInline] method on purpose: these extractors recurse through .each_named_child's block, so any local this method reserves (notably the 32-byte TSTreeCursor and the ensure machinery) is paid at every recursion level. Keeping the cursor out of this frame preserves the tiny original frame for the common narrow-node path, so deeply nested inputs don't overflow the stack (guarded by spec/unit_test/miniparser/extractor_recursion_depth_spec).


[View source]
def self.field(node : LibTreeSitter::TSNode, name : String) : LibTreeSitter::TSNode | Nil #

[View source]
def self.node_end_row(node : LibTreeSitter::TSNode) : Int32 #

[View source]
def self.node_start_row(node : LibTreeSitter::TSNode) : Int32 #

[View source]
def self.node_text(node : LibTreeSitter::TSNode, source : String) : String #

[View source]
def self.node_type(node : LibTreeSitter::TSNode) : String #

[View source]
def self.parse(source : String, language : LibTreeSitter::TSLanguage, &) #

Parses source with the given language and yields the root LibTreeSitter::TSNode. The parser is checked out from a per-language pool and returned when the block exits; the tree is freed in the same ensure.


[View source]
def self.parse_go(source : String, &) #

Parses source with the Go grammar and yields the root node.


[View source]
def self.parse_java(source : String, &) #

Parses source with the Java grammar and yields the root node.


[View source]
def self.parse_javascript(source : String, &) #

Parses source with the JavaScript grammar and yields the root node. Covers .js / .mjs / .cjs files; the JSX superset is recognised too — JSX-bearing TypeScript needs parse_typescript (not yet vendored).


[View source]
def self.parse_kotlin(source : String, &) #

Parses source with the Kotlin grammar and yields the root node.


[View source]
def self.parse_python(source : String, &) #

Parses source with the Python grammar and yields the root node.


[View source]
def self.parse_rust(source : String, &) #

Parses source with the Rust grammar and yields the root node. Covers .rs files. Used by the Rust framework analyzers (axum, actix-web, rocket, …) and the Rust callee extractor.


[View source]
def self.parser_pool_size(language : LibTreeSitter::TSLanguage) : Int32 #

Idle parser count for a given language. Exposed for tests and diagnostics; not part of the public API contract.


[View source]
def self.python_sexp(source : String) : String #

Convenience: returns the root-node s-expression for source.


[View source]