var Debug struct { // UnifyLog, if non-nil, receives a streaming text trace of unification. UnifyLog io.Writer // HTML, if non-nil, writes an HTML trace of unification to HTML. HTML io.Writer }
type Closure struct {
// contains filtered or unexported fields
}
func NewSum(vs ...*Value) Closure
func Read(r io.Reader, path string, opts ReadOpts) (Closure, error)
Read reads a Closure in YAML format from r, using path for error messages.
It maps YAML nodes into terminal Values as follows:
- "_" or !top _ is the top value (Top).
- "_|_" or !bottom _ is the bottom value. This is an error during unmarshaling, but can appear in marshaled values.
- "$<name>" or !var <name> is a variable (Var). Everywhere the same name appears within a single unmarshal operation, it is mapped to the same variable. Different unmarshal operations get different variables, even if they have the same string name.
- !regex "x" is a regular expression (String), as is any string that doesn't match "_", "_|_", or "$...". Regular expressions are implicitly anchored at the beginning and end. If the string doesn't contain any meta-characters (that is, it's a "literal" regular expression), then it's treated as an exact string.
- !string "x", or any int, float, bool, or binary value is an exact string (String).
- !regex [x, y, ...] is an intersection of regular expressions (String).
It maps YAML nodes into non-terminal Values as follows:
- Sequence nodes like [x, y, z] are tuples (Tuple).
- !repeat [x] is a repeated tuple (Tuple), which is 0 or more instances of x. There must be exactly one element in the list.
- Mapping nodes like {a: x, b: y} are defs (Def). Any fields not listed are implicitly top.
- !sum [x, y, z] is a sum of its children. This can be thought of as a union of the values x, y, and z, or as a non-deterministic choice between x, y, and z. If a variable appears both inside the sum and outside of it, only the non-deterministic choice view really works. The unifier does not directly implement sums; instead, this is decoded as a fresh variable that's simultaneously bound to x, y, and z.
- !import glob is like a !sum, but its children are read from all files matching the given glob pattern, which is interpreted relative to the current file path. Each file gets its own variable scope.
func ReadFile(path string, opts ReadOpts) (Closure, error)
ReadFile reads a Closure in YAML format from a file.
The file must consist of a single YAML document.
If opts.FS is not set, this sets it to a FS rooted at path's directory.
See Read for details.
func Unify(closures ...Closure) (Closure, error)
Unify computes a Closure that satisfies each input Closure. If no such Closure exists, it returns bottom.
func (c Closure) All() iter.Seq[*Value]
All enumerates all possible concrete values of c by substituting variables from the environment.
E.g., enumerating this Value
a: !sum [1, 2] b: !sum [3, 4]
results in
▹ Example (Def)
▹ Example (Tuple)
func (c Closure) IsBottom() bool
IsBottom returns whether c consists of no values.
func (c Closure) MarshalYAML() (any, error)
func (c Closure) String() string
func (c Closure) Summands() iter.Seq[*Value]
Summands returns the top-level Values of c. This assumes the top-level of c was constructed as a sum, and is mostly useful for debugging.
func (c *Closure) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML implements yaml.Unmarshaler.
Since there is no way to pass ReadOpts to this function, it assumes default options.
Decoder can be implemented by types as a custom implementation of [Decode] for that type.
type Decoder interface {
DecodeUnified(v *Value) error
}
A Def is a mapping from field names to [Value]s. Any fields not explicitly listed have Value Top.
type Def struct {
// contains filtered or unexported fields
}
func (d Def) All() iter.Seq2[string, *Value]
func (d Def) Exact() bool
Exact returns true if all field Values are exact.
func (d Def) WhyNotExact() string
WhyNotExact returns why the value is not exact
A DefBuilder builds a Def one field at a time. The zero value is an empty Def.
type DefBuilder struct {
// contains filtered or unexported fields
}
func (b *DefBuilder) Add(name string, v *Value)
func (b *DefBuilder) Build() Def
Build constructs a Def from the fields added to this builder.
A Domain is a non-empty set of values, all of the same kind.
Domain may be a scalar:
Or a composite:
Def - A mapping from fixed keys to [Domain]s.
Tuple - A fixed-length sequence of [Domain]s or all possible lengths repeating a Domain.
Or top or bottom:
Top - Represents all possible values of all kinds.
nil - Represents no values.
Or a variable:
type Domain interface {
Exact() bool
WhyNotExact() string
// contains filtered or unexported methods
}
type Pos struct {
Path string
Line int
}
func (p Pos) AppendText(b []byte) ([]byte, error)
func (p Pos) String() string
ReadOpts provides options to Read and related functions. The zero value is the default options.
type ReadOpts struct {
// FS, if non-nil, is the file system from which to resolve !import file
// names.
FS fs.FS
}
A String represents a set of strings. It can represent the intersection of a set of regexps, or a single exact string. In general, the domain of a String is non-empty, but we do not attempt to prove emptiness of a regexp value.
type String struct {
// contains filtered or unexported fields
}
func NewStringExact(s string) String
func NewStringRegex(exprs ...string) (String, error)
func (d String) Exact() bool
Exact returns whether this Value is known to consist of a single string.
func (d String) WhyNotExact() string
Top represents all possible values of all possible types.
type Top struct{}
func (t Top) Exact() bool
func (t Top) WhyNotExact() string
A Tuple is a sequence of Values in one of two forms: 1. a fixed-length tuple, where each Value can be different or 2. a "repeated tuple", which is a Value repeated 0 or more times.
type Tuple struct {
// contains filtered or unexported fields
}
func NewRepeat(gens ...func(envSet) (*Value, envSet)) Tuple
func NewTuple(vs ...*Value) Tuple
func (d Tuple) Exact() bool
func (d Tuple) WhyNotExact() string
A Value represents a structured, non-deterministic value consisting of strings, tuples of Values, and string-keyed maps of Values. A non-deterministic Value will also contain variables, which are resolved via an environment as part of a Closure.
For debugging, a Value can also track the source position it was read from in an input file, and its provenance from other Values.
type Value struct {
Domain Domain
// contains filtered or unexported fields
}
func NewValue(d Domain) *Value
NewValue returns a new Value with the given domain and no position information.
func NewValuePos(d Domain, p Pos) *Value
NewValuePos returns a new Value with the given domain at position p.
func (v *Value) Decode(into any) error
Decode decodes v into a Go value.
v must be exact, except that it can include Top. into must be a pointer. [Def]s are decoded into structs. [Tuple]s are decoded into slices. [String]s are decoded into strings or ints. Any field can itself be a pointer to one of these types. Top can be decoded into a pointer-typed field and will set the field to nil. Anything else will allocate a value if necessary.
Any type may implement Decoder, in which case its DecodeUnified method will be called instead of using the default decoding scheme.
func (v *Value) Exact() bool
func (v *Value) MarshalYAML() (any, error)
func (v *Value) Pos() Pos
func (v *Value) PosString() string
func (v *Value) Provenance() iter.Seq[*Value]
Provenance iterates over all of the source Values that have contributed to this Value.
func (v *Value) String() string
func (v *Value) WhyNotExact() string
type Var struct {
// contains filtered or unexported fields
}
func (d Var) Exact() bool
func (d Var) WhyNotExact() string