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 Canonicalize(name string, src []byte) ([]byte, error)
- 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))
- func WriteCanonical(path string, src []byte) error
- 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, locals map[string]bool) *ErrorList
- func ValidateConfigLocals(block *ObjectLit) *ErrorList
- func ValidateConfigurations(block *ObjectLit, locals map[string]bool) *ErrorList
- func ValidateConstraintReferences(constraints *ArrayLit, inputs *ObjectLit) *ErrorList
- func ValidateConstraints(arr *ArrayLit) *ErrorList
- func ValidateDataSources(block *ObjectLit) *ErrorList
- func ValidateEncryptionConfig(block *ObjectLit, locals map[string]bool) *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, locals map[string]bool) *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 Canonicalize ¶ added in v0.7.0
Canonicalize parses UB source and reformats it in canonical form with string wrapping enabled: the same output `unobin fmt --wrap-strings` produces. Tools that generate .ub or config content pass their draft bytes through it so written files match the formatter. The name labels parse errors only.
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.
func WriteCanonical ¶ added in v0.7.0
WriteCanonical canonicalizes src and atomically writes it to path. The path's basename labels parse errors. This is the single way generated .ub and config files reach disk, so a new writer formats by default instead of emitting ad-hoc bytes.
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. locals holds the names declared by the file's locals: block, referenceable from any config value.
func ValidateConfigLocals ¶ added in v0.7.0
ValidateConfigLocals checks the values of a config file's locals: block. A config local is the file's own scope: a static value that may reference other locals, but never inputs. The config supplies input values to the factory without being able to read them back, so a var.x here is rejected with wording that says why. Locals referencing each other in a loop are reported as cycles.
func ValidateConfigurations ¶ added in v0.5.0
ValidateConfigurations checks a config file's configurations: block. Every entry is keyed by a dotted alias.name path, the same key form a factory's block has. Unlike a factory's, a body here may be any expression: the runner evaluates it at load and requires a map, so a whole body can come from a local. Values follow the static-value rule with one widening: they may reference inputs (var.x), which the runner resolves against the effective inputs before decoding. locals holds the names declared by the file's locals: block.
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 an `encryption:` block in a config file. Same rules as ValidateStateConfig but with `@key-source:` in place of `@backend:`.
func ValidateFactoryConfigurations ¶ added in v0.6.0
ValidateFactoryConfigurations checks the structure of a factory's configurations: block: every entry is keyed by a dotted alias.name path and binds an object literal of fields. No meta keys are valid in a configuration body. 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 a state: block in a config file. The block must have exactly one @backend: meta-key whose value is a bare backend name such as s3, plus any number of body fields keyed by bare identifiers. Body values must be static config values, with the file's locals referenceable; they are not type-checked here, since 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