Documentation ¶
Index ¶
- Constants
- func ExprListToExprMap(exprList []interfaces.Expr) map[interfaces.Expr]struct{}
- func ExprMapToExprList(exprMap map[interfaces.Expr]struct{}) []interfaces.Expr
- func SimpleInvariantSolverLogger(logf func(format string, v ...interface{})) func([]interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error)
- func UniqueExprList(exprList []interfaces.Expr) []interfaces.Expr
- type AnyInvariant
- type ConjunctionInvariant
- type EqualityInvariant
- type EqualityInvariantList
- type EqualityWrapCallInvariant
- func (obj *EqualityWrapCallInvariant) ExprList() []interfaces.Expr
- func (obj *EqualityWrapCallInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
- func (obj *EqualityWrapCallInvariant) Possible(partials []interfaces.Invariant) error
- func (obj *EqualityWrapCallInvariant) String() string
- type EqualityWrapFuncInvariant
- func (obj *EqualityWrapFuncInvariant) ExprList() []interfaces.Expr
- func (obj *EqualityWrapFuncInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
- func (obj *EqualityWrapFuncInvariant) Possible(partials []interfaces.Invariant) error
- func (obj *EqualityWrapFuncInvariant) String() string
- type EqualityWrapListInvariant
- func (obj *EqualityWrapListInvariant) ExprList() []interfaces.Expr
- func (obj *EqualityWrapListInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
- func (obj *EqualityWrapListInvariant) Possible(partials []interfaces.Invariant) error
- func (obj *EqualityWrapListInvariant) String() string
- type EqualityWrapMapInvariant
- func (obj *EqualityWrapMapInvariant) ExprList() []interfaces.Expr
- func (obj *EqualityWrapMapInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
- func (obj *EqualityWrapMapInvariant) Possible(partials []interfaces.Invariant) error
- func (obj *EqualityWrapMapInvariant) String() string
- type EqualityWrapStructInvariant
- func (obj *EqualityWrapStructInvariant) ExprList() []interfaces.Expr
- func (obj *EqualityWrapStructInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
- func (obj *EqualityWrapStructInvariant) Possible(partials []interfaces.Invariant) error
- func (obj *EqualityWrapStructInvariant) String() string
- type EqualsInvariant
- type ExclusiveInvariant
- type InvariantSolution
- type Unifier
Constants ¶
const ( // Name is the prefix for our solver log messages. Name = "solver: simple" // ErrAmbiguous means we couldn't find a solution, but we weren't // inconsistent. ErrAmbiguous = interfaces.Error("can't unify, no equalities were consumed, we're ambiguous") // AllowRecursion specifies whether we're allowed to use the recursive // solver or not. It uses an absurd amount of memory, and might hang // your system if a simple solution doesn't exist. AllowRecursion = false // RecursionDepthLimit specifies the max depth that is allowed. // FIXME: RecursionDepthLimit is not currently implemented RecursionDepthLimit = 5 // TODO: pick a better value ? // RecursionInvariantLimit specifies the max number of invariants we can // recurse into. RecursionInvariantLimit = 5 // TODO: pick a better value ? )
Variables ¶
This section is empty.
Functions ¶
func ExprListToExprMap ¶
func ExprListToExprMap(exprList []interfaces.Expr) map[interfaces.Expr]struct{}
ExprListToExprMap converts a list of expressions to a map that has the unique expr pointers as the keys. This is just an alternate representation of the same data structure. If you have any duplicate values in your list, they'll get removed when stored as a map.
func ExprMapToExprList ¶
func ExprMapToExprList(exprMap map[interfaces.Expr]struct{}) []interfaces.Expr
ExprMapToExprList converts a map of expressions to a list that has the unique expr pointers as the values. This is just an alternate representation of the same data structure.
func SimpleInvariantSolverLogger ¶
func SimpleInvariantSolverLogger(logf func(format string, v ...interface{})) func([]interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error)
SimpleInvariantSolverLogger is a wrapper which returns a SimpleInvariantSolver with the log parameter of your choice specified. The result satisfies the correct signature for the solver parameter of the Unification function.
func UniqueExprList ¶
func UniqueExprList(exprList []interfaces.Expr) []interfaces.Expr
UniqueExprList returns a unique list of expressions with no duplicates. It does this my converting it to a map and then back. This isn't necessarily the most efficient way, and doesn't preserve list ordering.
Types ¶
type AnyInvariant ¶
type AnyInvariant struct {
Expr interfaces.Expr
}
AnyInvariant is an invariant that symbolizes that the expression can be any type. It is sometimes used to ensure that an expr actually gets a solution type so that it is not left unreferenced, and as a result, unsolved. TODO: is there a better name than AnyInvariant
func (*AnyInvariant) ExprList ¶
func (obj *AnyInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*AnyInvariant) Matches ¶
func (obj *AnyInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*AnyInvariant) Possible ¶
func (obj *AnyInvariant) Possible([]interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation always returns nil.
func (*AnyInvariant) String ¶
func (obj *AnyInvariant) String() string
String returns a representation of this invariant.
type ConjunctionInvariant ¶
type ConjunctionInvariant struct {
Invariants []interfaces.Invariant
}
ConjunctionInvariant represents a list of invariants which must all be true together. In other words, it's a grouping construct for a set of invariants.
func (*ConjunctionInvariant) ExprList ¶
func (obj *ConjunctionInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*ConjunctionInvariant) Matches ¶
func (obj *ConjunctionInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*ConjunctionInvariant) Possible ¶
func (obj *ConjunctionInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*ConjunctionInvariant) String ¶
func (obj *ConjunctionInvariant) String() string
String returns a representation of this invariant.
type EqualityInvariant ¶
type EqualityInvariant struct { Expr1 interfaces.Expr Expr2 interfaces.Expr }
EqualityInvariant is an invariant that symbolizes that the two expressions must have equivalent types. TODO: is there a better name than EqualityInvariant
func (*EqualityInvariant) ExprList ¶
func (obj *EqualityInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityInvariant) Matches ¶
func (obj *EqualityInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityInvariant) Possible ¶
func (obj *EqualityInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one.
func (*EqualityInvariant) String ¶
func (obj *EqualityInvariant) String() string
String returns a representation of this invariant.
type EqualityInvariantList ¶
type EqualityInvariantList struct {
Exprs []interfaces.Expr
}
EqualityInvariantList is an invariant that symbolizes that all the expressions listed must have equivalent types.
func (*EqualityInvariantList) ExprList ¶
func (obj *EqualityInvariantList) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityInvariantList) Matches ¶
func (obj *EqualityInvariantList) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityInvariantList) Possible ¶
func (obj *EqualityInvariantList) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one.
func (*EqualityInvariantList) String ¶
func (obj *EqualityInvariantList) String() string
String returns a representation of this invariant.
type EqualityWrapCallInvariant ¶
type EqualityWrapCallInvariant struct { Expr1 interfaces.Expr Expr2Func interfaces.Expr }
EqualityWrapCallInvariant expresses that a call result that happened in Expr1 must match the type of the function result listed in Expr2. In this case, Expr2 will be a function expression, and the returned expression should match with the Expr1 expression, when comparing types. TODO: should this be named EqualityWrapFuncInvariant or not? TODO: should Expr1 and Expr2 be reversed???
func (*EqualityWrapCallInvariant) ExprList ¶
func (obj *EqualityWrapCallInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityWrapCallInvariant) Matches ¶
func (obj *EqualityWrapCallInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityWrapCallInvariant) Possible ¶
func (obj *EqualityWrapCallInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*EqualityWrapCallInvariant) String ¶
func (obj *EqualityWrapCallInvariant) String() string
String returns a representation of this invariant.
type EqualityWrapFuncInvariant ¶
type EqualityWrapFuncInvariant struct { Expr1 interfaces.Expr Expr2Map map[string]interfaces.Expr Expr2Ord []string Expr2Out interfaces.Expr }
EqualityWrapFuncInvariant expresses that a func in Expr1 must have args that match the type of the expressions listed in Expr2Map and a return value that matches the type of the expression in Expr2Out. TODO: should this be named EqualityWrapCallInvariant or not?
func (*EqualityWrapFuncInvariant) ExprList ¶
func (obj *EqualityWrapFuncInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityWrapFuncInvariant) Matches ¶
func (obj *EqualityWrapFuncInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityWrapFuncInvariant) Possible ¶
func (obj *EqualityWrapFuncInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*EqualityWrapFuncInvariant) String ¶
func (obj *EqualityWrapFuncInvariant) String() string
String returns a representation of this invariant.
type EqualityWrapListInvariant ¶
type EqualityWrapListInvariant struct { Expr1 interfaces.Expr Expr2Val interfaces.Expr }
EqualityWrapListInvariant expresses that a list in Expr1 must have elements that have the same type as the expression in Expr2Val.
func (*EqualityWrapListInvariant) ExprList ¶
func (obj *EqualityWrapListInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityWrapListInvariant) Matches ¶
func (obj *EqualityWrapListInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityWrapListInvariant) Possible ¶
func (obj *EqualityWrapListInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*EqualityWrapListInvariant) String ¶
func (obj *EqualityWrapListInvariant) String() string
String returns a representation of this invariant.
type EqualityWrapMapInvariant ¶
type EqualityWrapMapInvariant struct { Expr1 interfaces.Expr Expr2Key interfaces.Expr Expr2Val interfaces.Expr }
EqualityWrapMapInvariant expresses that a map in Expr1 must have keys that match the type of the expression in Expr2Key and values that match the type of the expression in Expr2Val.
func (*EqualityWrapMapInvariant) ExprList ¶
func (obj *EqualityWrapMapInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityWrapMapInvariant) Matches ¶
func (obj *EqualityWrapMapInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityWrapMapInvariant) Possible ¶
func (obj *EqualityWrapMapInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*EqualityWrapMapInvariant) String ¶
func (obj *EqualityWrapMapInvariant) String() string
String returns a representation of this invariant.
type EqualityWrapStructInvariant ¶
type EqualityWrapStructInvariant struct { Expr1 interfaces.Expr Expr2Map map[string]interfaces.Expr Expr2Ord []string }
EqualityWrapStructInvariant expresses that a struct in Expr1 must have fields that match the type of the expressions listed in Expr2Map.
func (*EqualityWrapStructInvariant) ExprList ¶
func (obj *EqualityWrapStructInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualityWrapStructInvariant) Matches ¶
func (obj *EqualityWrapStructInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualityWrapStructInvariant) Possible ¶
func (obj *EqualityWrapStructInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*EqualityWrapStructInvariant) String ¶
func (obj *EqualityWrapStructInvariant) String() string
String returns a representation of this invariant.
type EqualsInvariant ¶
type EqualsInvariant struct { Expr interfaces.Expr Type *types.Type }
EqualsInvariant is an invariant that symbolizes that the expression has a known type. TODO: is there a better name than EqualsInvariant
func (*EqualsInvariant) ExprList ¶
func (obj *EqualsInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*EqualsInvariant) Matches ¶
func (obj *EqualsInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors.
func (*EqualsInvariant) Possible ¶
func (obj *EqualsInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one.
func (*EqualsInvariant) String ¶
func (obj *EqualsInvariant) String() string
String returns a representation of this invariant.
type ExclusiveInvariant ¶
type ExclusiveInvariant struct {
Invariants []interfaces.Invariant
}
ExclusiveInvariant represents a list of invariants where one and *only* one should hold true. To combine multiple invariants in one of the list elements, you can group multiple invariants together using a ConjunctionInvariant. Do note that the solver might not verify that only one of the invariants in the list holds true, as it might choose to be lazy and pick the first solution found.
func (*ExclusiveInvariant) ExprList ¶
func (obj *ExclusiveInvariant) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions in this invariant.
func (*ExclusiveInvariant) Matches ¶
func (obj *ExclusiveInvariant) Matches(solved map[interfaces.Expr]*types.Type) (bool, error)
Matches returns whether an invariant matches the existing solution. If it is inconsistent, then it errors. Because this partial invariant requires only one to be true, it will mask children errors, since it's normal for only one to be consistent.
func (*ExclusiveInvariant) Possible ¶
func (obj *ExclusiveInvariant) Possible(partials []interfaces.Invariant) error
Possible returns an error if it is certain that it is NOT possible to get a solution with this invariant and the set of partials. In certain cases, it might not be able to determine that it's not possible, while simultaneously not being able to guarantee a possible solution either. In this situation, it should return nil, since this is used as a filtering mechanism, and the nil result of possible is preferred over eliminating a tricky, but possible one. This particular implementation is currently not implemented!
func (*ExclusiveInvariant) String ¶
func (obj *ExclusiveInvariant) String() string
String returns a representation of this invariant.
type InvariantSolution ¶
type InvariantSolution struct {
Solutions []*EqualsInvariant // list of trivial solutions for each node
}
InvariantSolution lists a trivial set of EqualsInvariant mappings so that you can populate your AST with SetType calls in a simple loop.
func SimpleInvariantSolver ¶
func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfaces.Expr, logf func(format string, v ...interface{})) (*InvariantSolution, error)
SimpleInvariantSolver is an iterative invariant solver for AST expressions. It is intended to be very simple, even if it's computationally inefficient.
func (*InvariantSolution) ExprList ¶
func (obj *InvariantSolution) ExprList() []interfaces.Expr
ExprList returns the list of valid expressions. This struct is not part of the invariant interface, but it implements this anyways.
type Unifier ¶
type Unifier struct { // AST is the input abstract syntax tree to unify. AST interfaces.Stmt // Solver is the solver algorithm implementation to use. Solver func([]interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error) Debug bool Logf func(format string, v ...interface{}) }
Unifier holds all the data that the Unify function will need for it to run.
func (*Unifier) Unify ¶
Unify takes an AST expression tree and attempts to assign types to every node using the specified solver. The expression tree returns a list of invariants (or constraints) which must be met in order to find a unique value for the type of each expression. This list of invariants is passed into the solver, which hopefully finds a solution. If it cannot find a unique solution, then it will return an error. The invariants are available in different flavours which describe different constraint scenarios. The simplest expresses that a a particular node id (it's pointer) must be a certain type. More complicated invariants might express that two different node id's must have the same type. This function and logic was invented after the author could not find any proper literature or examples describing a well-known implementation of this process. Improvements and polite recommendations are welcome.