match

package
v0.17.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package match compiles search patterns (regex or fixed strings) into a Matcher: rg's "grep-regex" equivalent. A Matcher extracts required literals from the pattern (prefix/inner/suffix), runs a SIMD-backed literal prefilter over whole buffers via bytes.Index/bytes.IndexByte, and falls through to a real regex engine only to confirm candidate hits on a single line. Plain literal patterns skip the regex engine entirely.

Every hot-path method takes []byte, never string, and is designed to run allocation-free in steady state once a Matcher is constructed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CandidateKind

type CandidateKind uint8

CandidateKind distinguishes a genuine match from a literal-prefilter hit that still needs full-regex confirmation on its enclosing line.

const (
	// Confirmed means FindCandidate ran the real pattern directly (no
	// separate prefilter regex exists, e.g. a pure-literal pattern); the
	// hit is a genuine match and needs no further verification.
	Confirmed CandidateKind = iota
	// Candidate means FindCandidate matched only a literal prefilter;
	// the caller must locate the enclosing line and call Verify on it
	// before treating this as a real match.
	Candidate
)

type CaseMode

type CaseMode uint8

CaseMode selects how case is handled while matching.

const (
	// CaseSensitive matches patterns exactly as written.
	CaseSensitive CaseMode = iota
	// CaseInsensitive folds case on both pattern and haystack.
	CaseInsensitive
	// CaseSmart is case-insensitive unless the pattern contains an
	// uppercase literal character, in which case it is case-sensitive.
	CaseSmart
)

type Config

type Config struct {
	// Patterns are combined as an alternation (like ripgrep's -e).
	Patterns []string
	CaseMode CaseMode
	// Word wraps each pattern in word-boundary looks (rg's -w).
	Word bool
	// Fixed treats Patterns as literal strings rather than regexes (-F).
	Fixed bool
	// LineRegexp anchors the combined pattern to whole lines (rg's -x):
	// equivalent to wrapping it in ^(?:...)$ with per-line (not
	// per-text) anchor semantics. Callers must never set both Word and
	// LineRegexp -- they mirror rg's single shared BoundaryMode field,
	// where the last of -w/-x given wins outright (see strategy.go's New
	// doc for how this is implemented).
	LineRegexp bool
	// MultiLine compiles the pattern with Go's (?m) flag so `^`/`$` bind to
	// '\n' line boundaries anywhere within a search window, not just its
	// very start/end (rg's --null-data behavior: a record may span '\n', so
	// `foo$` must anchor before an interior '\n', while `.` still does NOT
	// match '\n'). No effect on -F/literal patterns (they carry no
	// anchors) and redundant under LineRegexp (which already wraps in
	// (?m)^...$). See strategy.go's newRegexMatcher.
	MultiLine bool
}

Config describes how to compile one or more patterns into a Matcher. It is the sole construction-time input to New; Matcher implementations expose no runtime setters for case/word/fixed-string behavior, only query methods (see NonMatchingLineTerm).

type Matcher

type Matcher interface {
	// FindCandidate scans buf starting at byte offset start for the next
	// possible match and reports its offset plus whether it is Confirmed
	// or merely a Candidate. ok is false once no further candidates
	// exist in buf[start:]. Implementations must not allocate in steady
	// state — this is the whole-buffer hot-path scan (rg's
	// find_candidate_line).
	FindCandidate(buf []byte, start int) (off int, kind CandidateKind, ok bool)

	// Verify reports whether the full pattern matches anywhere within
	// line. Used to confirm a Candidate hit against exactly the one
	// line that contains it.
	Verify(line []byte) bool

	// Find returns the leftmost match's byte bounds [s, e) within line.
	// Callers that only need a yes/no + line (the common "path:line:text"
	// case) should prefer Verify and skip Find entirely to avoid the
	// extra work of locating exact bounds.
	Find(line []byte) (s, e int, ok bool)

	// NonMatchingLineTerm reports whether the compiled pattern is
	// provably unable to match across a line-terminator byte ('\n').
	// When true, a search.Searcher may use the fast whole-buffer
	// candidate path (FindCandidate over the whole buffer, then expand
	// to line boundaries); when false it must fall back to scanning
	// line-by-line. This is the only capability a Searcher queries on a
	// Matcher at runtime — all other behavior (case, word, fixed) is
	// baked in at construction via Config.
	NonMatchingLineTerm() bool
}

Matcher is a compiled pattern ready to search []byte haystacks. Every method operates on []byte only — implementations and callers must never convert to string on a hot path. A Matcher's compiled state is read-only after construction, so a single Matcher may be shared and called concurrently by multiple goroutines; any per-call scratch space an implementation needs must not be stored on the Matcher itself (pool it in the caller, e.g. per search.Searcher worker).

func New

func New(cfg Config) (Matcher, error)

New compiles cfg into a Matcher.

Pipeline: parse patterns (or take them as literal strings under -F) -> resolve smart case -> combine into one alternation -> try the pure-literal fast path (Strategy 1) -> else run inner-literal extraction for a prefiltered-regex path (Strategy 2) -> else fall back to running the engine over the whole buffer (Strategy 3). Word wrapping (-w) is never baked into the compiled pattern; it is applied uniformly as a post-match boundary check (see word.go) regardless of which strategy compiled, since Go's regexp/syntax has no equivalent of rg's asymmetric half-word-boundary look used for -w.

LineRegexp (-x), unlike -w, IS baked directly into the compiled pattern text as a real ^(?:...)$ anchor pair, using Go's (?m) multi-line mode so ^/$ bind to LINE boundaries within a whole multi-line search buffer (rather than (?)-default text boundaries, which would only ever match at the very start/end of an entire file) -- see newRegexMatcher's doc. Wrapping the pattern text itself, rather than post-filtering match bounds the way -w does, is deliberate: it lets the ordinary regex engine (or FindAllIndex's anchor-aware scan, already used for any anchored pattern) enforce correctness even for patterns whose match depends on trying multiple alternatives per start position (e.g. `-x -e 'a|aa'` against "aa"), which a simple "restart at s+1 if the bounds don't span the whole line" retry loop -- as word.go uses for -w -- cannot get right in general.

Jump to

Keyboard shortcuts

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