Documentation
¶
Overview ¶
Package lang holds the unobin source language: PEG grammar, lexer, AST.
Scope: parsing of main.ub, kind-prefixed library type bodies, and config.ub into a typed AST. Multi-error reporting with line/column from the parser.
Companion packages:
- pkg/types - type system and type-expression evaluation
- pkg/codegen - AST to Go source
- pkg/resolve - import resolution and lock file
Index ¶
- Constants
- Variables
- func ConstraintFieldRoots(c ConstraintEntry) []string
- func FieldMap(obj *ObjectLit) map[string]Expr
- func FieldsConstraintKinds() []string
- func Format(file *File) ([]byte, error)
- func FormatWith(file *File, opts FormatOptions) ([]byte, error)
- func OutputDescription(e Expr) string
- func ParseSpecs(specs []ConstraintSpec) ([]ConstraintEntry, *ErrorList)
- func Render(v any) string
- func RenderKey(k string) string
- func RenderPretty(v any) string
- func SensitiveInputs(block *ObjectLit) map[string]bool
- func SensitiveOutputs(block *ObjectLit) map[string]bool
- func TypeMessage(v any) string
- func Walk(e Expr, visit func(Expr))
- type ArrayLit
- type BoolLit
- type Call
- type Comment
- type Comprehension
- type ComprehensionKind
- type Conditional
- type ConstraintEntry
- type ConstraintEvalFunc
- type ConstraintSpec
- type DefaultSpec
- type DotPath
- type DotSegment
- type EachBinding
- type EachValue
- type Error
- type ErrorKind
- type ErrorList
- func CheckConstraintEntries(entries []ConstraintEntry, values map[string]any, ...) *ErrorList
- func CheckConstraintEntry(idx int, c ConstraintEntry, values map[string]any, ...) *ErrorList
- func CheckConstraints(block *ArrayLit, values map[string]any, evalAgainstInputs ConstraintEvalFunc, ...) *ErrorList
- func ValidateActions(block *ObjectLit) *ErrorList
- func ValidateCalls(f *File) *ErrorList
- func ValidateComprehensionBindings(f *File) *ErrorList
- func ValidateConfigInputs(block *ObjectLit) *ErrorList
- func ValidateConfigurations(block *ObjectLit) *ErrorList
- func ValidateConstraintReferences(constraints *ArrayLit, inputs *ObjectLit) *ErrorList
- func ValidateConstraints(arr *ArrayLit) *ErrorList
- func ValidateDataSources(block *ObjectLit) *ErrorList
- func ValidateEncryptionConfig(block *ObjectLit) *ErrorList
- func ValidateFactoryConfigurations(block *ObjectLit) *ErrorList
- func ValidateFile(f *File) *ErrorList
- func ValidateImports(block *ObjectLit) *ErrorList
- func ValidateInputDeclarations(block *ObjectLit) *ErrorList
- func ValidateInputs(decl *ObjectLit, values map[string]any, evalDefault EvalFunc) (map[string]any, *ErrorList)
- func ValidateLocals(block *ObjectLit) *ErrorList
- func ValidateManifestReplace(block *ObjectLit) *ErrorList
- func ValidateManifestRequires(block *ObjectLit) *ErrorList
- func ValidateOutputs(block *ObjectLit) *ErrorList
- func ValidateResources(block *ObjectLit) *ErrorList
- func ValidateStateConfig(block *ObjectLit) *ErrorList
- func ValidateTopLevelKeys(f *File) *ErrorList
- type EvalFunc
- type Expr
- type Field
- type FieldDisplay
- type FieldKey
- type FieldKeyKind
- type File
- type FileKind
- type ForEachLevel
- type ForEachSpecLevel
- type FormatOptions
- type Ident
- type Infix
- type InterpolatedPart
- type InterpolatedString
- type Node
- type NullLit
- type NumberLit
- type ObjectLit
- type Position
- type Prefix
- type Span
- type StringForm
- type StringLit
- type TypeAtomic
- type TypeExpr
- type TypeList
- type TypeMap
- type TypeObject
- type TypeObjectField
- type TypeOptional
- type TypeTuple
Constants ¶
const ( FileUnknown = parse.FileUnknown FileFactory = parse.FileFactory FileExportedType = parse.FileExportedType FileConfig = parse.FileConfig FileManifest = parse.FileManifest FieldIdent = parse.FieldIdent FieldString = parse.FieldString FieldPath = parse.FieldPath CompList = parse.CompList CompMap = parse.CompMap StringSingleQuoted = parse.StringSingleQuoted StringTripleQuoteSingleLine = parse.StringTripleQuoteSingleLine StringLiteralClip = parse.StringLiteralClip StringLiteralStrip = parse.StringLiteralStrip StringFoldedClip = parse.StringFoldedClip StringFoldedStrip = parse.StringFoldedStrip StringJoinedClip = parse.StringJoinedClip StringJoinedStrip = parse.StringJoinedStrip ErrUnknown = parse.ErrUnknown ErrParse = parse.ErrParse ErrLex = parse.ErrLex ErrSchema = parse.ErrSchema ErrType = parse.ErrType ErrResolve = parse.ErrResolve )
const CoreNamespace = "@core"
CoreNamespace is the language's function namespace: a call qualified with it resolves against the functions the toolchain provides, with no import. The @ keeps it outside the alias namespace, so an import can never collide with it or stand in for it.
const DefaultMaxColumn = 100
DefaultMaxColumn is the line width used when FormatOptions.MaxColumn is unset.
Variables ¶
var ( NewErrorList = parse.NewErrorList Errorf = parse.Errorf PascalToKebab = parse.PascalToKebab )
Functions ¶
func ConstraintFieldRoots ¶ added in v0.6.0
func ConstraintFieldRoots(c ConstraintEntry) []string
ConstraintFieldRoots returns the names of the inputs a constraint reads: the first path segment of each fields: entry for a set constraint, and of every var reference in the when, require, and @for-each expressions for a predicate. The names are sorted and unique.
func FieldMap ¶ added in v0.6.0
FieldMap returns an object's plain fields by name: every field with an identifier key that is not a meta key. A nil object yields an empty map.
func FieldsConstraintKinds ¶ added in v0.6.0
func FieldsConstraintKinds() []string
FieldsConstraintKinds returns the kinds a `fields:`-based constraint entry may declare, sorted. goschema's constructor table is held to this set by test.
func Format ¶
Format renders a parsed File back to canonical UB source using the default options. Comments captured during parsing are interleaved at their original positions; non-comment whitespace is normalized. Output is stable: re-parsing the result and feeding it back through Format yields the same bytes.
func FormatWith ¶
func FormatWith(file *File, opts FormatOptions) ([]byte, error)
FormatWith renders a parsed File with the supplied options. Zero values fall back to their package defaults.
func OutputDescription ¶ added in v0.6.0
OutputDescription returns the wrapper's `description:` string, or "" when the entry has none. Validation guarantees the value is a string literal; anything else reads as absent here.
func ParseSpecs ¶
func ParseSpecs(specs []ConstraintSpec) ([]ConstraintEntry, *ErrorList)
ParseSpecs parses each spec's When and Require source into expressions and returns entries ready for CheckConstraintEntries. A set constraint (empty When and Require) yields an entry with nil expressions. Parse errors and unknown kinds are collected; a spec with either is skipped. The kind guard matters because the checker silently passes a kind it does not know, so a misspelled kind would otherwise disable its rule without a word.
func Render ¶
Render formats v as a UB literal expression on one line. The canonical set (string, bool, int64, float64, []any, map[string]any, nil) renders directly; types implementing ub.Marshaler control their own form; anything else falls through to Go's default formatter so callers never see an empty string.
func RenderKey ¶
RenderKey returns k as a bare kebab-case identifier when it is a valid one, otherwise as a quoted string. Round trips cleanly through the parser either way.
func RenderPretty ¶
RenderPretty formats v as UB syntax with indented multi-line expansion for non-empty maps and lists. Empty collections render inline as `{}` or `[]`. Atoms render exactly as Render would emit them.
func SensitiveInputs ¶
SensitiveInputs returns the set of input names declared with `@sensitive: true` in an `inputs:` block. Nil block yields an empty map.
func SensitiveOutputs ¶
SensitiveOutputs returns the set of output names declared with `@sensitive: true` inside their wrapper in an `outputs:` block. Nil block yields an empty map. The block must already have been passed through ValidateOutputs; entries that are not wrapper objects are skipped silently.
func TypeMessage ¶
TypeMessage names a parsed value with the unobin type vocabulary so error messages match what the operator wrote, not the Go runtime type they would never see otherwise.
func Walk ¶
Walk invokes visit for e and then for every nested expression in source order. It recurses into object field values, array elements, call args, infix and prefix operands, dot-path index expressions, conditional branches, comprehension parts, and interpolated-string slots. A nil expression is a no-op so callers can recurse through optional fields without guarding first.
Types ¶
type Comprehension ¶
type Comprehension = parse.Comprehension
type ComprehensionKind ¶
type ComprehensionKind = parse.ComprehensionKind
type Conditional ¶
type Conditional = parse.Conditional
type ConstraintEntry ¶
type ConstraintEntry struct {
Kind string
Fields []string
When Expr
Require Expr
Message string
Levels []ForEachLevel
}
ConstraintEntry is one resolved cross-field constraint, independent of whether it was parsed from a UB constraints block or derived from a Go type at compile time. The set kinds use Fields; a predicate uses When and Require (and an optional Message). A Fields name may splat a list ([*]) to run the rule once per element. A predicate may iterate with Levels, checking when and require once per element of each level's iterable with the level's binding set: a single @each level for the bare @for-each form, named levels for a chained one. goschema builds these directly for Go library types so they run through the same check as UB ones.
type ConstraintEvalFunc ¶ added in v0.6.0
type ConstraintEvalFunc func(e Expr, binds []EachBinding) (any, error)
ConstraintEvalFunc reduces a constraint's when, require, or @for-each expression against the input values. binds holds the iteration bindings in scope, outermost first; it is empty outside iteration.
type ConstraintSpec ¶
type ConstraintSpec struct {
Kind string
Fields []string
When string
Require string
Message string
ForEach string
ForEachLevels []ForEachSpecLevel
}
ConstraintSpec is the embeddable, string-only form of a constraint. The predicate When, Require, and iterables are kept as unobin source so the whole set can be generated into a factory and parsed back at plan time; a set constraint leaves them empty and uses Fields. A bare iteration uses ForEach; a chained one uses ForEachLevels, named levels in order. goschema produces these from a Go type, and codegen bakes them into the factory.
type DefaultSpec ¶ added in v0.6.0
DefaultSpec is the embeddable form of one declared input default. Field is the input's var reference (var.mode, var.code.timeout), the spelling every other field reference uses. A Value default holds the default as unobin literal source, parsed and evaluated where it is applied; an Optional marker leaves Value empty and sets Optional, declaring the field may be absent with nothing filled in. goschema produces these from a Go type's Defaults method, and codegen bakes them into the factory.
type DotSegment ¶
type DotSegment = parse.DotSegment
type EachBinding ¶ added in v0.6.0
EachBinding is one named iteration binding in scope for a constraint expression: @each for the bare @for-each form, or a declared name like @rule for a level of a chained one. Either way the binding is a key/value record, read as @name.key and @name.value.
type EachValue ¶ added in v0.6.0
EachValue is one iteration's @each binding, handed to the evaluator alongside a constraint expression when the entry iterates with @for-each: the element index or map key, and the element itself.
type ErrorList ¶
func CheckConstraintEntries ¶
func CheckConstraintEntries( entries []ConstraintEntry, values map[string]any, evalAgainstInputs ConstraintEvalFunc, display FieldDisplay, ) *ErrorList
CheckConstraintEntries checks already-resolved constraint entries, such as those goschema derives from a Go type at compile time, against the values. It is the same check CheckConstraints runs after parsing a UB block, so Go-derived and UB constraints behave identically.
func CheckConstraintEntry ¶ added in v0.6.0
func CheckConstraintEntry( idx int, c ConstraintEntry, values map[string]any, evalAgainstInputs ConstraintEvalFunc, display FieldDisplay, ) *ErrorList
CheckConstraintEntry checks one resolved constraint entry against the values, reporting any failure under index idx, the entry's position in its type's constraint list, so a diagnostic names the same entry no matter which entries the caller checks.
func CheckConstraints ¶
func CheckConstraints( block *ArrayLit, values map[string]any, evalAgainstInputs ConstraintEvalFunc, display FieldDisplay, ) *ErrorList
CheckConstraints evaluates the value-level cross-field constraints in a stack's `constraints:` block against the validated input values. Predicate constraints use evalAgainstInputs to reduce their `when:` and `require:` expressions; pass nil to skip predicate evaluation. Callers should run ValidateConstraints first; malformed entries that slip through are skipped silently.
func ValidateActions ¶
ValidateActions checks the shape of an `actions:` block.
func ValidateCalls ¶ added in v0.5.0
ValidateCalls walks every expression in f and rejects two kinds of function call: a bare call with no qualifier (a call names either an imported library or @core), and a qualified call whose alias is missing from the file's imports block. @core needs no import; any other @-name is rejected, since the language provides only @core. The type constructors in an input declaration's type (list(string), optional(integer, 0)) are left alone: they share call syntax but denote types; a default inside such a type stays a value and is still checked. Whether a named library function exists is not checked here; that is a runtime concern, since a library's function set lives in compiled Go code. The @core set is fixed and the reference checker enforces it.
func ValidateComprehensionBindings ¶ added in v0.6.0
ValidateComprehensionBindings walks every expression in f and requires each comprehension to introduce fresh binding names: a name may not repeat within one comprehension, and a nested comprehension may not rebind a name bound by an enclosing one. A rebound name would silently hide the outer value for the rest of the inner body. A comprehension in source position binds nothing for its own source, so reusing a name there is not shadowing.
func ValidateConfigInputs ¶ added in v0.5.0
ValidateConfigInputs checks that every value in a config file's inputs: block is a static value; see checkStaticConfigBlock.
func ValidateConfigurations ¶ added in v0.5.0
ValidateConfigurations applies the static-value rule to a config file's configurations: block, with one widening: values may reference inputs (var.x), which the runner resolves against the effective inputs before decoding. The block nests by import alias and then configuration name; the walk recurses through both to the leaf values.
func ValidateConstraintReferences ¶
ValidateConstraintReferences checks that every var reference in the `fields:` list of each constraint names a declared input. A reference is checked by the segment after var, the input the path starts from, with any [N] or [*] suffix set aside. Malformed entries are skipped.
func ValidateConstraints ¶
ValidateConstraints walks a `constraints:` array and checks each entry per its declared `kind:`. Field-based kinds take a nonempty `fields:` list of var references, dotted to reach a field inside a nested input; the `predicate` kind takes `when:` and `require:` expressions plus an optional `message:`.
func ValidateDataSources ¶
ValidateDataSources checks the shape of a `data:` block.
func ValidateEncryptionConfig ¶
ValidateEncryptionConfig checks the structure of an `encryption:` sub-block nested inside a `state:` block. Same rules as ValidateStateConfig but with `@key-source:` in place of `@backend:` and no further nested blocks.
func ValidateFactoryConfigurations ¶ added in v0.6.0
ValidateFactoryConfigurations checks the structure of a factory's configurations: block: import alias, then configuration name, then an object literal of fields. Field values are ordinary expressions and are not constrained here.
func ValidateFile ¶
ValidateFile runs every schema check appropriate to f.Kind and returns the combined diagnostics. The file must already be classified; FileUnknown produces only the top-level-keys error directing the caller to classify.
func ValidateImports ¶
ValidateImports checks an `imports:` block: every entry is an identifier alias bound to a quoted string source URL or local path.
func ValidateInputDeclarations ¶
ValidateInputDeclarations checks the shape of an `inputs:` block as it appears in a stack or exported-type body. Every entry must be an identifier name bound to an object declaration carrying a `type:` expression and any number of permitted modifiers; types are promoted here so callers see syntactic and type level errors in one batch.
Config file `inputs:` blocks have a different shape (values, not declarations) and are not validated by this function.
func ValidateInputs ¶
func ValidateInputs( decl *ObjectLit, values map[string]any, evalDefault EvalFunc, ) (map[string]any, *ErrorList)
ValidateInputs validates an operator-supplied values map against a stack's `inputs:` declaration. Returns the validated values with `optional(T, default)` defaults applied. Errors cover missing required inputs, unknown keys, type mismatches, and modifier violations. The decl is the parsed `inputs:` block; values is the map produced by loadConfigInputs + applyEnvOverrides. evalDefault reduces default expressions to Go values; pass nil to refuse any default that requires evaluation.
func ValidateLocals ¶
ValidateLocals checks a `locals:` block. Every entry is a bare identifier name bound to an arbitrary expression; a local's type is inferred from its value, never declared. Names must be unique. The entry is referenced elsewhere as `local.<name>`. The value expression's own validity (references, cycles) is checked in later passes, not here.
func ValidateManifestReplace ¶ added in v0.6.0
ValidateManifestReplace checks a manifest `replace:` block: every entry binds a quoted dependency id (a repo URL) to a quoted local path. The id and path strings are not parsed here; resolution validates the URL and that the path holds a library.
func ValidateManifestRequires ¶ added in v0.6.0
ValidateManifestRequires checks a manifest `requires:` block: every entry binds a quoted dependency id (a repo URL with an optional `//subdir`) to a quoted version floor. The id and version strings are not parsed here; resolution validates the URL and the semver floor.
func ValidateOutputs ¶
ValidateOutputs checks an `outputs:` block. Every entry is a bare identifier name bound to an object wrapper of the form `{ value: expr }`, optionally carrying `description: '...'` and `@sensitive: true`. The wrapper exists so per-output metadata keys can ride alongside the value without ambiguity.
func ValidateResources ¶
ValidateResources checks a `resources:` block: every entry is keyed by a dotted alias.type.name and its body is an object.
func ValidateStateConfig ¶
ValidateStateConfig checks the structure of a state: block in a config file. The block must have exactly one @backend: meta-key whose value is a fully-qualified alias.name reference such as core.local. It may include a nested encryption: object of the same form with @key-source:, plus any number of body fields keyed by bare identifiers. Body values are not type-checked here; the resolver decodes them against each backend's declared configuration.
func ValidateTopLevelKeys ¶
ValidateTopLevelKeys checks that every top-level field in f.Body uses an identifier key permitted for f.Kind, and that no key appears twice. Returns the collected errors. f.Kind must already be classified; FileUnknown produces a single error directing the caller to classify first.
type EvalFunc ¶
EvalFunc reduces an expression to a Go value against an empty context. Defaults inside `optional(T, default)` evaluate this way so the validator can apply them without depending on the runtime package.
type Expr ¶
func OutputValueExpr ¶
OutputValueExpr returns the inner expression of an output entry's wrapper. Every output in an `outputs:` block is of the form `name: { value: expr }`, with optional metadata keys alongside; this helper unwraps to the inner expr. Returns nil when the input is not a wrapper or has no `value:` key (treat as a structural error caught by ValidateOutputs).
type FieldDisplay ¶ added in v0.6.0
type FieldDisplay int
FieldDisplay selects how a constraint diagnostic spells field names. A factory-level check keeps the var root, the name an operator sets; a node-scoped check (a Go type's spec, a composite's own block) prints names relative to the node, matching the keys its body is written with. Lookup always uses the rooted name; only the message changes.
const ( DisplayRooted FieldDisplay = iota DisplayNodeRelative )
type FieldKeyKind ¶
type FieldKeyKind = parse.FieldKeyKind
type File ¶
func ParseSource ¶
ParseSource reads .ub source from b, returns the parsed File, and classifies the result via ClassifyByFilename so callers get a File.Kind without a separate step. The classification is what distinguishes this from parse.ParseSource.
type FileKind ¶
func ClassifyByFilename ¶
ClassifyByFilename returns the file kind implied by the path's basename. `main.ub` is FileFactory and `unobin.manifest` is FileManifest; anything else is FileUnknown. Callers classify FileExportedType (a kind-prefixed `<kind>-<type>.ub` inside a library) and FileConfig (the operator's stack config file, supplied by path flag) from their own context.
type ForEachLevel ¶ added in v0.6.0
ForEachLevel is one link of a chained @for-each: the binding name, @-spelled, and the iterable its elements come from. InText is the iterable's source form, used to name the element a failure is about. The bare form is a single level binding @each.
type ForEachSpecLevel ¶ added in v0.6.0
ForEachSpecLevel is the embeddable form of one chain level, with the iterable kept as unobin source the way a spec's when and require are.
type FormatOptions ¶
type FormatOptions struct {
// MaxColumn is the soft target line width. The formatter prefers
// to break long lines so no rendered line exceeds this width. Some
// constructs (a literal-mode triple-quoted string, or a single
// token that won't fit anywhere) can still go past this width.
MaxColumn int
// WrapStrings, when true, lets the formatter rewrite an overflowing
// single-quoted string as a folded (>-) or joined (\-) triple-quoted
// string so the body wraps within the line budget. When false, a
// single-quoted string keeps its form even when it overflows.
WrapStrings bool
}
FormatOptions configures Format behavior. The zero value means "use defaults": MaxColumn is taken to be DefaultMaxColumn, and WrapStrings is false.
type InterpolatedPart ¶
type InterpolatedPart = parse.InterpolatedPart
type InterpolatedString ¶
type InterpolatedString = parse.InterpolatedString
type ObjectLit ¶
func TopLevelBlock ¶ added in v0.6.0
TopLevelBlock returns the object value of a file's top-level field with the given name. A nil file, an absent field, or a value of another form yields nil; ValidateFile reports the wrong-form case, so callers may treat nil as absence.
type StringForm ¶
type StringForm = parse.StringForm
type TypeAtomic ¶
type TypeAtomic = parse.TypeAtomic
type TypeExpr ¶
func PromoteType ¶
PromoteType interprets e as a type expression and returns the corresponding TypeExpr tree. Type expressions are syntactically a subset of plain expressions: a bare identifier names an atomic type; a bare call names a constructor (list, set, map, tuple, object, optional, open). Anything outside that subset is rejected with an ErrType diagnostic.
Default values inside optional(T, default) are not promoted; they remain plain Expr because TypeOptional.Default is a value, not a type.
type TypeObject ¶
type TypeObject = parse.TypeObject
type TypeObjectField ¶
type TypeObjectField = parse.TypeObjectField
type TypeOptional ¶
type TypeOptional = parse.TypeOptional