langspec

package
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package langspec is a data-driven framework for line-oriented language parsers: a language is defined by exactly one Spec value (a set of regex Defs plus a qualification mode), not by a new Go type. Adding a language to spectackle means adding one Spec (see python.go, javascript.go) — the indexing pipeline (internal/index) is never touched (SPX-LSP-001).

SpecParser adapts a Spec to index.LanguageParser so it plugs into index.New's parsers slice exactly like a hand-written parser (see index.CudaParser, index.AsmParser). Parsing is a single-pass line scan: no AST, no cgo, no external grammar — deliberately the cheapest possible backend for languages where an approximate symbol table (function/class definitions, not full semantics) is enough for the cross-language graph. Parsing is deterministic: identical bytes yield identical results (SPX-GRA-001).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func All

func All() []index.LanguageParser

All returns a SpecParser for every registered Spec, ready to append to an index.New parsers slice alongside hand-written parsers such as index.GoParser.

Types

type Def

type Def struct {
	Kind graph.NodeKind
	Re   *regexp.Regexp
	Name int // submatch group index (>=1) holding the symbol name
	Sig  int // submatch group index (>=1) holding Node.Sig; 0 = no signature
}

Def is one regex-driven symbol shape within a Spec: a line matching Re mints a node of Kind, with Re's submatch group Name giving the symbol name and (optionally) submatch group Sig giving a compact signature.

type EndSpanSpec

type EndSpanSpec struct {
	Open  *regexp.Regexp
	Close *regexp.Regexp
}

EndSpanSpec is Spec.EndSpan's shape: the keyword regexes cspan.KeywordSpan depth-counts to find where a def's body ends, standing in for the '{'/'}' pair that cspan.Span counts for brace languages. Open matches increment depth, Close matches decrement it; see cspan.KeywordSpan's doc for the exact counting rules (per-line net sum, def line included).

type QualMode

type QualMode uint8

QualMode selects how a matched symbol name is qualified into the "<lang>:<qualified-name>" NodeID (see internal/ids).

const (
	// QualFileStem qualifies with the file's base name minus extension,
	// e.g. app.py's `def run` -> "py:app.run". Matches Python's one file
	// == one importable module convention.
	QualFileStem QualMode = iota
	// QualDirPkg qualifies with the containing directory's base name,
	// e.g. mat/ops.s's TEXT ·mulVec -> "asm:mat.mulVec" (see
	// index.AsmParser, which this mode mirrors for source layouts where
	// a directory is the unit of namespacing, not the file).
	QualDirPkg
	// QualFlat uses the bare matched name with no qualification prefix,
	// e.g. a CUDA kernel -> "cu:saxpy_kernel" (see index.CudaParser).
	QualFlat
)

type Spec

type Spec struct {
	Lang graph.Lang
	Exts []string
	Qual QualMode
	Defs []Def

	// CallRe is the optional call-site regex for LSP-001: capture group 1 is
	// the callee name. Unset (nil) is the framework default and means zero
	// behavior change from before this field existed — Parse emits no edges,
	// exactly like every language that doesn't set it (see
	// TestSpecParserNoEdgesWithoutCallRe). Set it only for brace languages
	// where a def's body is delimited by '{'/'}' (see c.go, cpp.go); a
	// language with no braces has nothing for the brace-counted body span to
	// bound CallRe to, so it simply leaves CallRe nil.
	CallRe *regexp.Regexp
	// Stop lists callee names CallRe must never turn into a call edge —
	// language keywords/operators whose syntax happens to look like a call
	// (`if (`, `sizeof(`, ...). Only consulted when CallRe is set.
	Stop []string

	// EndSpan is the optional keyword-counting alternative to brace-counted
	// body spans, for end-terminated languages (Lua, Ruby, and similar,
	// where a def's body is closed by a keyword like "end" rather than by
	// '}'). Unset (nil) is the framework default and means zero behavior
	// change from before this field existed — Parse bounds a KFunc/KMethod
	// def's body with cspan.Span exactly as it always has (see CallRe's doc
	// for the same nil-means-unchanged guarantee this mirrors). Set it only
	// for languages with no braces at all; a brace language leaves EndSpan
	// nil and CallRe (if set) drives cspan.Span as before.
	EndSpan *EndSpanSpec
}

Spec is the whole definition of a language for langspec purposes: the language tag, the file extensions it claims, how matched names are qualified into NodeIDs, and the ordered list of regex Defs tried against each source line. One Spec value == one language; see python.go and javascript.go for the reference specs this framework ships with.

type SpecParser

type SpecParser struct {
	S Spec
}

SpecParser adapts a Spec to index.LanguageParser.

func (SpecParser) CacheVersion

func (p SpecParser) CacheVersion() string

CacheVersion implements index.CacheVersioner by digesting the Spec itself. A langspec language IS its data, so the data is the version: edit a regex and every cached parse blob for that language — and only that language — stops matching, instead of replaying pre-edit nodes for unchanged files (B-0007). Nothing to bump by hand, which matters because a Spec edit is the single most common change in this package.

Every input is walked in declaration order and never through a map, so the digest is stable across processes; the nil markers keep an unset CallRe or EndSpan from colliding with a set one that happens to stringify empty.

func (SpecParser) Extensions

func (p SpecParser) Extensions() []string

Extensions are the file suffixes this parser claims.

func (SpecParser) Lang

func (p SpecParser) Lang() graph.Lang

Lang identifies the parser's language.

func (SpecParser) Parse

func (p SpecParser) Parse(path string, src []byte) (index.ParseResult, error)

Parse scans one source file line by line, trying each Def in order against every line and minting a node per match. When the Spec sets CallRe, each callable-unit hit — KFunc, KMethod or KKernel, see spanEligibleKinds for why those three and not the rest — also gets its body span scanned for call edges (LSP-001: "WHEN a Spec sets CallRe, the SpecParser SHALL emit ECall edges from each Def's brace-counted body span"): the body span itself is brace-counted via cspan.Span, unless the Spec sets EndSpan, in which case it is keyword-counted via cspan.KeywordSpan instead (for end-terminated languages with no braces at all). With CallRe unset — the default — no edges are emitted and EndLine stays equal to Line, exactly as before this field existed: same-language and cross-language relations are otherwise a resolver's job (see internal/resolve), not a langspec.Spec's.

Jump to

Keyboard shortcuts

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