compiler

package
v0.0.0-...-b91d579 Latest Latest
Warning

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

Go to latest
Published: Aug 16, 2026 License: BSD-3-Clause Imports: 20 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BraceDepth

func BraceDepth(code string) int

BraceDepth returns the net brace depth ({ minus }) of code, counting only real `{`/`}` tokens -- unlike a plain character count, one sitting inside a string/rune literal or a comment is correctly ignored, since this tokenizes with go/scanner (the real Go lexer) instead of scanning raw characters. A positive result means code has more unclosed `{` than `}` (a statement or declaration isn't finished yet); zero or negative means it's balanced (or over-closed, a real syntax error the compiler will report on its own terms -- this function only answers "is it still open," not "is it valid").

Used both by ParseCell (to tell a still-open type/func block from a finished one) and by cmd/gocell-repl (to tell when to keep showing the "...>" continuation prompt) -- previously two separate, naive `strings.Count(line, "{")` implementations, each breaking the same way on a `{` inside a string (e.g. `fmt.Println("Result: {")` desynced both: ParseCell silently dropped the rest of the cell, and the REPL hung forever waiting for a `}` that already existed, textually, inside the string).

code need not be syntactically valid Go -- it's frequently mid-typed, incomplete input by design (that's the whole reason this function exists). The scanner is given a nil error handler, so it never stops or panics on malformed input (an unterminated string literal, for instance, is tolerated: it's treated as running to EOF rather than emitting a hard error), it just does its best-effort token classification.

func ResolveMembers

func ResolveMembers(codeBeforeDot string, reg *runtime.Registry, importTracker *ImportTracker, typeRegistry *runtime.TypeRegistry) ([]string, error)

ResolveMembers answers "what's valid after `foo.`" for a cell that's still being typed: codeBeforeDot is everything up to (not including) the dot, which must end in a bare expression statement naming the value being completed (typically a single identifier, e.g. "x := &Foo{}\nx" for completing "x."). It reuses AnalyzeCell's own machinery (buildAnalysisFile, rewriteTopLevelRedefinitions) so `foo` resolves exactly as it would in a real cell -- whether it's an existing Registry symbol from an earlier cell or one just declared earlier in this same, not-yet-submitted one -- then reads its go/types type off the trailing expression and lists that type's fields and methods.

Returns (nil, err) if codeBeforeDot doesn't parse, has no statements, or its type can't be resolved -- callers should treat that as "no member completions available", not an error to surface to the user (the code is, by definition, still being typed).

Types

type AnalysisResult

type AnalysisResult struct {
	UsedSymbols  map[string]*runtime.Symbol
	NewVariables []string

	// InjectedInterruptLines[i] holds the original line (within cell.Fset) of every loop in
	// cell.Stmts[i] that injectInterruptChecks added a cooperative interrupt check to, sorted
	// ascending. GeneratePluginCode copies this into the matching LineMapping entry so
	// remapPanicError can correct for the extra generated lines.
	InjectedInterruptLines [][]int

	// LastExprIsConversion reports that the cell's last statement is a type conversion --
	// `int64(5)`, `[]byte("hi")`, `Celsius(20)`. Syntactically that is an *ast.CallExpr, which
	// the generator otherwise leaves alone on the grounds that a bare call is already a valid
	// statement; a conversion is not, and would fail to compile with "is not used". Only
	// go/types can tell the two apart, hence recording it here.
	LastExprIsConversion bool
}

AnalysisResult holds the results of analyzing a cell.

func AnalyzeCell

func AnalyzeCell(cell *CellContent, reg *runtime.Registry, importTracker *ImportTracker, typeRegistry *runtime.TypeRegistry) (*AnalysisResult, error)

AnalyzeCell decides, for every symbol the cell touches, whether it refers to a variable already in the Registry (to hydrate, and for value types to write back) or declares a new top-level one (to export).

It answers that by type-checking a throwaway copy of the cell rather than by matching identifier names: an identifier only counts as referring to a Registry symbol when go/types resolves it to that exact object. Name matching cannot make that call -- an identifier may coincide with a Registry symbol's name while referring to something else entirely (a range variable, a closure parameter, a `:=` nested in a block, a struct literal's field key, a label), and hydrating a symbol that the generated code never really references fails to compile ("declared and not used") for pointer-typed symbols, which have no write-back to otherwise reference them. It returns an error only if the cell could not be analyzed at all, which leaves no honest answer to give: guessing by name would resurrect exactly the ambiguity described above, and an empty analysis would silently drop the session's state from the generated code.

type Builder

type Builder struct {
	// contains filtered or unexported fields
}

Builder orchestrates compiling a Go file into a .so plugin.

func NewBuilder

func NewBuilder(moduleRoot string) (*Builder, error)

NewBuilder creates a new Builder by discovering the host gocell module.

func (*Builder) BuildPlugin

func (b *Builder) BuildPlugin(cellDir string, sourceCode string) (string, error)

BuildPlugin writes go.mod, main.go, and runs 'go build -mod=mod -buildmode=plugin'.

func (*Builder) GoVersion

func (b *Builder) GoVersion() string

GoVersion returns the Go toolchain version every cell plugin is built with -- the same one read from the host module's go.mod at NewBuilder time (see detectGoVersion), so callers reporting the kernel's language version don't need to duplicate that detection themselves.

func (*Builder) ModuleRoot

func (b *Builder) ModuleRoot() string

ModuleRoot returns the root directory of the host gocell module used to build cells.

type CellContent

type CellContent struct {
	Imports   []*ast.ImportSpec
	TypeDecls []ast.Decl
	FuncDecls []ast.Decl
	Stmts     []ast.Stmt
	RawCode   string
	// Fset is the FileSet that Imports/TypeDecls/FuncDecls/Stmts' positions are
	// relative to. AnalyzeCell reuses it (rather than reparsing the cell) so that
	// go/types resolves identifiers against these same node objects, not disconnected
	// copies -- required for AnalyzeCell's rewrite pass to mutate the real nodes that
	// GeneratePluginCode will later print.
	Fset *token.FileSet
}

CellContent holds the result of breaking a cell down into an AST.

func ParseCell

func ParseCell(code string) (*CellContent, error)

ParseCell parses a cell's code, intelligently splitting imports, types, functions and statements.

type ImportSpec

type ImportSpec struct {
	Alias string
	Path  string
}

ImportSpec represents an import specification with its optional alias.

type ImportTracker

type ImportTracker struct {
	// contains filtered or unexported fields
}

ImportTracker thread-safely tracks the set of imports for a session.

func NewImportTracker

func NewImportTracker() *ImportTracker

NewImportTracker creates a new import manager.

func (*ImportTracker) AddImport

func (it *ImportTracker) AddImport(alias, importPath string)

AddImport adds an import to the session.

func (*ImportTracker) AllImports

func (it *ImportTracker) AllImports() map[string]*ImportSpec

AllImports returns the full list of registered imports.

func (*ImportTracker) CollectImportsFromFile

func (it *ImportTracker) CollectImportsFromFile(node *ast.File)

CollectImportsFromFile extracts the imports from an AST file and adds them to the tracker.

func (*ImportTracker) GenerateImportBlockForCode

func (it *ImportTracker) GenerateImportBlockForCode(codeBody string) string

GenerateImportBlockForCode produces the Go import block for the cell. goimports takes care of sanitizing and removing unused imports via the AST.

type LineMapping

type LineMapping struct {
	GeneratedLine int
	OriginalLine  int

	// InjectedAtOriginalLines holds the original (pre-injection) line, within this statement,
	// of every loop injectInterruptChecks added a check to -- sorted ascending. Each one added
	// exactly 3 generated lines that don't exist in the cell's own source, so
	// remapPanicError's otherwise-uniform generated->original interpolation needs to subtract
	// 3 for every entry at or before the line it's resolving.
	InjectedAtOriginalLines []int
}

LineMapping records that GeneratedLine, in the plugin source GeneratePluginCode produced, prints the start of the same statement that begins at OriginalLine in the cell's own source (cell.RawCode). Only top-level cell statements are covered -- not re-injected declarations from earlier cells, whose position in the generated file doesn't correspond to any single cell's line numbers at all.

func GeneratePluginCode

func GeneratePluginCode(
	cell *CellContent,
	analysis *AnalysisResult,
	importTracker *ImportTracker,
	typeRegistry *runtime.TypeRegistry,
) (string, []LineMapping)

GeneratePluginCode generates the complete Go source of a cell plugin for compilation, along with a mapping from generated-file line numbers back to the cell's own source lines (used to report a panic's location in terms the user actually typed, not the generated file's).

Jump to

Keyboard shortcuts

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