grep

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 17, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package grep provides structural code search, match, and rewrite using tree-sitter parse trees. It is an AST-grep-inspired pattern matching engine built on gotreesitter's query system.

Code patterns use metavariables ($NAME, $$$ARGS, $_, $E:type) that match AST nodes structurally. Patterns are parsed as real code in the target language, then compiled to tree-sitter S-expression queries.

Basic usage:

lang := grammars.DetectLanguageByName("go").Language()
results, err := grep.Match(lang, `func $NAME($$$) error`, source)
for _, r := range results {
    fmt.Printf("found: %s\n", r.Captures["NAME"].Text)
}

Rewrite usage:

result, err := grep.Replace(lang, `$E.unwrap()`, `$E.expect("failed")`, source)
output := grep.ApplyEdits(source, result.Edits)

Full query syntax:

find <lang>::<pattern> [where { <constraints> }] [replace { <template> }]

Package grep implements a structural code search engine.

The query language supports finding code patterns, optionally scoped to a language, with constraint and replacement blocks:

find <lang>::<code-pattern> [where { <constraints> }] [replace { <template> }]

The find keyword and language prefix are both optional.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyEdits

func ApplyEdits(source []byte, edits []Edit) []byte

ApplyEdits applies non-overlapping edits to source, returning new source. Edits are applied back-to-front (by StartByte descending) so that earlier byte offsets remain valid as later portions of the source are modified.

func Preprocess

func Preprocess(pattern string) (string, map[string]*MetaVar, error)

Preprocess replaces metavariables in a code pattern with language-valid placeholder identifiers so tree-sitter can parse the result.

It returns the modified pattern, a map from placeholder identifier to MetaVar descriptor, and any error encountered.

Metavariable conventions:

$NAME       → __GREP_CAP_NAME__    (single capture)
$$$ITEMS    → __GREP_VAR_ITEMS__   (variadic capture)
$_          → __GREP_WILD_1__      (wildcard, numbered for uniqueness)
$NAME:type  → __GREP_TYPED_NAME__T__type__ (typed capture)

Types

type Capture

type Capture struct {
	// Name is the user-facing metavariable name (e.g., "NAME", "PARAMS").
	Name string

	// Text is the source text of the captured node.
	Text []byte

	// StartByte is the byte offset where the capture begins.
	StartByte uint32

	// EndByte is the byte offset where the capture ends (exclusive).
	EndByte uint32

	// Node is the captured syntax tree node.
	Node *gotreesitter.Node
}

Capture holds information about a single captured node within a match.

type CompiledPattern

type CompiledPattern struct {
	// Query is the compiled tree-sitter S-expression query ready for
	// matching against syntax trees.
	Query *gotreesitter.Query

	// MetaVars maps placeholder identifier to its MetaVar descriptor.
	// The keys are the same as in the Preprocess output.
	MetaVars map[string]*MetaVar

	// Lang is the language the pattern was compiled for.
	Lang *gotreesitter.Language

	// SExpr is the generated S-expression query string (useful for debugging).
	SExpr string
}

CompiledPattern holds a compiled tree-sitter query along with metavariable metadata from the original code pattern.

func Compile

func Compile(lang *gotreesitter.Language, pattern string) (*CompiledPattern, error)

Compile parses a code pattern into a reusable CompiledPattern. The returned pattern can be matched against multiple source files via its Match method without re-compilation.

func CompilePattern

func CompilePattern(lang *gotreesitter.Language, pattern string) (*CompiledPattern, error)

CompilePattern compiles a code pattern string into a tree-sitter query for the given language. The pattern may contain metavariables ($NAME, $$$NAME, $_, $NAME:type) that are converted into query captures.

The compilation pipeline:

  1. Preprocess: replace metavariables with language-valid placeholders
  2. Parse: parse the preprocessed string using the language's grammar
  3. Translate: walk the parse tree and emit an S-expression query

func CompilePatternForLang

func CompilePatternForLang(langName, pattern string) (*CompiledPattern, error)

CompilePatternForLang is a convenience that looks up a language by name before compiling.

func (*CompiledPattern) Match

func (cp *CompiledPattern) Match(source []byte) ([]Result, error)

Match executes this compiled pattern against source code, returning all structural matches. Returns an empty slice when no matches are found.

type Diagnostic

type Diagnostic struct {
	Message   string
	StartByte uint32
	EndByte   uint32
}

Diagnostic reports a non-fatal issue encountered during rewriting.

type Edit

type Edit struct {
	StartByte   uint32
	EndByte     uint32
	Replacement []byte
}

Edit describes a byte-range replacement in source text.

type LangResolver

type LangResolver func(name string) *gotreesitter.Language

LangResolver maps a language name to a tree-sitter Language object. It returns nil if the language is not available.

func DefaultResolver

func DefaultResolver() LangResolver

DefaultResolver returns a LangResolver that uses the grammars registry to look up languages by name.

type MetaVar

type MetaVar struct {
	// Name is the user-facing name (e.g., "NAME", "PARAMS", "_").
	Name string

	// Placeholder is the language-valid identifier that replaced the
	// metavariable in the preprocessed pattern (e.g., "__GREP_CAP_NAME__").
	Placeholder string

	// Variadic is true for $$$ captures (zero or more).
	Variadic bool

	// Wildcard is true for $_ (anonymous wildcard).
	Wildcard bool

	// TypeConstraint is the node type constraint for $NAME:type captures,
	// or empty if unconstrained.
	TypeConstraint string
}

MetaVar describes a metavariable found in a code pattern.

type Query

type Query struct {
	// Lang is the language name (e.g. "go", "rust", "sexp"), or empty if
	// unspecified.
	Lang string

	// Pattern is the code pattern or S-expression to match against.
	Pattern string

	// Where is the raw constraint block content (text between the braces in
	// a where { ... } clause), or empty if no where clause was given.
	Where string

	// Replace is the raw replacement template content (text between the
	// braces in a replace { ... } clause), or empty if no replace clause
	// was given.
	Replace string
}

Query is the parsed representation of a structural grep query.

func ParseQuery

func ParseQuery(input string) (Query, error)

ParseQuery parses a structural grep query string into a Query.

Accepted forms:

find go::func $NAME($$$) error           — full form
go::func $NAME($$$) error                — shorthand (no find keyword)
func $NAME($$$) error                    — bare pattern (no language prefix)
find sexp::(function_definition)         — S-expression mode

The where { ... } and replace { ... } blocks are optional and support nested braces.

func (Query) String

func (q Query) String() string

String returns the canonical string form of the query.

type QueryResult

type QueryResult struct {
	// Matches contains the structural match results after any where-clause
	// filtering has been applied.
	Matches []Result

	// ReplaceResult holds the computed edits if a replace clause was present
	// in the query. It is nil when no replace clause was specified.
	ReplaceResult *ReplaceResult
}

QueryResult holds the results of executing a full structural grep query.

func RunQuery

func RunQuery(query string, source []byte, resolver LangResolver) (*QueryResult, error)

RunQuery executes a full structural grep query string against source code. The resolver maps language names (e.g., "go", "javascript") to Language objects.

The query string follows the format:

find <lang>::<pattern> [where { <constraints> }] [replace { <template> }]

The pipeline:

  1. Parse the query string
  2. Resolve the language using the resolver
  3. Match the pattern against the source
  4. Apply where-clause filtering (if present)
  5. Compute replacement edits (if present)

func RunQueryWithLang

func RunQueryWithLang(query string, source []byte, lang *gotreesitter.Language) (*QueryResult, error)

RunQueryWithLang executes a query when the language is already known. The query string can omit the language prefix — the provided lang is used directly for parsing and matching.

This is useful for:

  • Bare patterns without a language prefix (e.g., "func $NAME()")
  • When the caller already has a Language object from prior detection

type ReplaceResult

type ReplaceResult struct {
	Edits       []Edit
	Diagnostics []Diagnostic
}

ReplaceResult holds the computed edits and any diagnostics produced by a Replace operation.

func Replace

func Replace(lang *gotreesitter.Language, pattern string, replacement string, source []byte) (*ReplaceResult, error)

Replace finds all matches of pattern in source and computes replacement edits by substituting capture references ($NAME, $$$NAME) in the replacement template with matched text.

Overlapping matches are discarded (the earlier match wins) and reported as diagnostics. After computing edits, the result is validated by re-parsing with tree-sitter; new ERROR nodes are reported as diagnostics.

type Result

type Result struct {
	// StartByte is the byte offset where the overall match begins.
	StartByte uint32

	// EndByte is the byte offset where the overall match ends (exclusive).
	EndByte uint32

	// Captures maps user-facing metavariable names (e.g., "NAME", "PARAMS")
	// to the captured node information.
	Captures map[string]Capture
}

Result represents a single structural match of a pattern against source code.

func Match

func Match(lang *gotreesitter.Language, pattern string, source []byte) ([]Result, error)

Match finds all structural matches of a code pattern in source code. The pattern may contain metavariables ($NAME, $$$NAME, $_, $NAME:type). Returns an empty slice (not an error) when no matches are found.

func MatchSexp

func MatchSexp(lang *gotreesitter.Language, sexp string, source []byte) ([]Result, error)

MatchSexp finds matches using a raw S-expression query string. This bypasses the code-pattern compiler and uses the S-expression directly as a tree-sitter query. Returns an empty slice when no matches are found.

type WhereFilter

type WhereFilter func(result *Result, source []byte, lang *gotreesitter.Language) bool

WhereFilter is a predicate that tests whether a match result satisfies a where-clause constraint. It returns true if the result passes the filter.

func CompileWhere

func CompileWhere(where string) (WhereFilter, error)

CompileWhere compiles a where-clause string into a WhereFilter function.

Supported constraint forms:

contains($CAP, <text>)          — capture text contains literal text
not contains($CAP, <text>)      — capture text does NOT contain literal text
matches($CAP, "regex")          — capture text matches regex
not matches($CAP, "regex")      — capture text does NOT match regex

Multiple constraints can be combined with semicolons or newlines; all must pass (logical AND).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL