lang: ast, interfaces, interpret: Change the Output sig
This plumbs through the new Output method signature that accepts a table of function pointers to values and relies on the previous storing of the function pointers to be used for the lookup right now. This has the elegant side-effect that Output generation could run in parallel with the graph engine, as the engine only needs to pause to take a snapshot of the current values tables. Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
@@ -103,6 +103,15 @@ const (
|
|||||||
|
|
||||||
// ErrNoStoredScope is an error that tells us we can't get a scope here.
|
// ErrNoStoredScope is an error that tells us we can't get a scope here.
|
||||||
ErrNoStoredScope = interfaces.Error("scope is not stored in this node")
|
ErrNoStoredScope = interfaces.Error("scope is not stored in this node")
|
||||||
|
|
||||||
|
// ErrFuncPointerNil is an error that explains the function pointer for
|
||||||
|
// table lookup is missing. If this happens, it's most likely a
|
||||||
|
// programming error.
|
||||||
|
ErrFuncPointerNil = interfaces.Error("missing func pointer for table")
|
||||||
|
|
||||||
|
// ErrTableNoValue is an error that explains the table is missing a
|
||||||
|
// value. If this happens, it's most likely a programming error.
|
||||||
|
ErrTableNoValue = interfaces.Error("missing value in table")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -261,7 +270,7 @@ func (obj *StmtBind) Graph() (*pgraph.Graph, error) {
|
|||||||
|
|
||||||
// Output for the bind statement produces no output. Any values of interest come
|
// Output for the bind statement produces no output. Any values of interest come
|
||||||
// from the use of the var which this binds the expression to.
|
// from the use of the var which this binds the expression to.
|
||||||
func (obj *StmtBind) Output() (*interfaces.Output, error) {
|
func (obj *StmtBind) Output(map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return interfaces.EmptyOutput(), nil
|
return interfaces.EmptyOutput(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,10 +625,13 @@ func (obj *StmtRes) Graph() (*pgraph.Graph, error) {
|
|||||||
// analogous function for expressions is Value. Those Value functions might get
|
// analogous function for expressions is Value. Those Value functions might get
|
||||||
// called by this Output function if they are needed to produce the output. In
|
// called by this Output function if they are needed to produce the output. In
|
||||||
// the case of this resource statement, this is definitely the case.
|
// the case of this resource statement, this is definitely the case.
|
||||||
func (obj *StmtRes) Output() (*interfaces.Output, error) {
|
func (obj *StmtRes) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
nameValue, err := obj.Name.Value()
|
if obj.namePtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
nameValue, exists := table[obj.namePtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
names := []string{} // list of names to build
|
names := []string{} // list of names to build
|
||||||
@@ -642,18 +654,18 @@ func (obj *StmtRes) Output() (*interfaces.Output, error) {
|
|||||||
resources := []engine.Res{}
|
resources := []engine.Res{}
|
||||||
edges := []*interfaces.Edge{}
|
edges := []*interfaces.Edge{}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
res, err := obj.resource(name)
|
res, err := obj.resource(table, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrapf(err, "error building resource")
|
return nil, errwrap.Wrapf(err, "error building resource")
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeList, err := obj.edges(name)
|
edgeList, err := obj.edges(table, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrapf(err, "error building edges")
|
return nil, errwrap.Wrapf(err, "error building edges")
|
||||||
}
|
}
|
||||||
edges = append(edges, edgeList...)
|
edges = append(edges, edgeList...)
|
||||||
|
|
||||||
if err := obj.metaparams(res); err != nil { // set metaparams
|
if err := obj.metaparams(table, res); err != nil { // set metaparams
|
||||||
return nil, errwrap.Wrapf(err, "error building meta params")
|
return nil, errwrap.Wrapf(err, "error building meta params")
|
||||||
}
|
}
|
||||||
resources = append(resources, res)
|
resources = append(resources, res)
|
||||||
@@ -667,7 +679,7 @@ func (obj *StmtRes) Output() (*interfaces.Output, error) {
|
|||||||
|
|
||||||
// resource is a helper function to generate the res that comes from this.
|
// resource is a helper function to generate the res that comes from this.
|
||||||
// TODO: it could memoize some of the work to avoid re-computation when looped
|
// TODO: it could memoize some of the work to avoid re-computation when looped
|
||||||
func (obj *StmtRes) resource(resName string) (engine.Res, error) {
|
func (obj *StmtRes) resource(table map[interfaces.Func]types.Value, resName string) (engine.Res, error) {
|
||||||
res, err := engine.NewNamedResource(obj.Kind, resName)
|
res, err := engine.NewNamedResource(obj.Kind, resName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errwrap.Wrapf(err, "cannot create resource kind `%s` with named `%s`", obj.Kind, resName)
|
return nil, errwrap.Wrapf(err, "cannot create resource kind `%s` with named `%s`", obj.Kind, resName)
|
||||||
@@ -692,9 +704,12 @@ func (obj *StmtRes) resource(resName string) (engine.Res, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if x.Condition != nil {
|
if x.Condition != nil {
|
||||||
b, err := x.Condition.Value()
|
if x.conditionPtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
b, exists := table[x.conditionPtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !b.Bool() { // if value exists, and is false, skip it
|
if !b.Bool() { // if value exists, and is false, skip it
|
||||||
@@ -731,9 +746,12 @@ func (obj *StmtRes) resource(resName string) (engine.Res, error) {
|
|||||||
return nil, errwrap.Wrapf(err, "resource field `%s` of type `%+v`, cannot take type `%+v", x.Field, t, typ)
|
return nil, errwrap.Wrapf(err, "resource field `%s` of type `%+v`, cannot take type `%+v", x.Field, t, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fv, err := x.Value.Value() // Value method on Expr
|
if x.valuePtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
fv, exists := table[x.valuePtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// mutate the struct field f with the mcl data in fv
|
// mutate the struct field f with the mcl data in fv
|
||||||
@@ -746,7 +764,7 @@ func (obj *StmtRes) resource(resName string) (engine.Res, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// edges is a helper function to generate the edges that come from the resource.
|
// edges is a helper function to generate the edges that come from the resource.
|
||||||
func (obj *StmtRes) edges(resName string) ([]*interfaces.Edge, error) {
|
func (obj *StmtRes) edges(table map[interfaces.Func]types.Value, resName string) ([]*interfaces.Edge, error) {
|
||||||
edges := []*interfaces.Edge{}
|
edges := []*interfaces.Edge{}
|
||||||
|
|
||||||
// to and from self, map of kind, name, notify
|
// to and from self, map of kind, name, notify
|
||||||
@@ -760,9 +778,12 @@ func (obj *StmtRes) edges(resName string) ([]*interfaces.Edge, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if x.Condition != nil {
|
if x.Condition != nil {
|
||||||
b, err := x.Condition.Value()
|
if x.conditionPtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
b, exists := table[x.conditionPtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !b.Bool() { // if value exists, and is false, skip it
|
if !b.Bool() { // if value exists, and is false, skip it
|
||||||
@@ -770,9 +791,12 @@ func (obj *StmtRes) edges(resName string) ([]*interfaces.Edge, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nameValue, err := x.EdgeHalf.Name.Value()
|
if x.EdgeHalf.namePtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
nameValue, exists := table[x.EdgeHalf.namePtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// the edge name can be a single string or a list of strings...
|
// the edge name can be a single string or a list of strings...
|
||||||
@@ -874,7 +898,7 @@ func (obj *StmtRes) edges(resName string) ([]*interfaces.Edge, error) {
|
|||||||
|
|
||||||
// metaparams is a helper function to set the metaparams that come from the
|
// metaparams is a helper function to set the metaparams that come from the
|
||||||
// resource on to the individual resource we're working on.
|
// resource on to the individual resource we're working on.
|
||||||
func (obj *StmtRes) metaparams(res engine.Res) error {
|
func (obj *StmtRes) metaparams(table map[interfaces.Func]types.Value, res engine.Res) error {
|
||||||
meta := engine.DefaultMetaParams.Copy() // defaults
|
meta := engine.DefaultMetaParams.Copy() // defaults
|
||||||
|
|
||||||
var rm *engine.ReversibleMeta
|
var rm *engine.ReversibleMeta
|
||||||
@@ -897,9 +921,12 @@ func (obj *StmtRes) metaparams(res engine.Res) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if x.Condition != nil {
|
if x.Condition != nil {
|
||||||
b, err := x.Condition.Value()
|
if x.conditionPtr == nil {
|
||||||
if err != nil {
|
return ErrFuncPointerNil
|
||||||
return err
|
}
|
||||||
|
b, exists := table[x.conditionPtr]
|
||||||
|
if !exists {
|
||||||
|
return ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !b.Bool() { // if value exists, and is false, skip it
|
if !b.Bool() { // if value exists, and is false, skip it
|
||||||
@@ -907,9 +934,12 @@ func (obj *StmtRes) metaparams(res engine.Res) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := x.MetaExpr.Value()
|
if x.metaExprPtr == nil {
|
||||||
if err != nil {
|
return ErrFuncPointerNil
|
||||||
return err
|
}
|
||||||
|
v, exists := table[x.metaExprPtr]
|
||||||
|
if !exists {
|
||||||
|
return ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch p := strings.ToLower(x.Property); p {
|
switch p := strings.ToLower(x.Property); p {
|
||||||
@@ -2168,13 +2198,17 @@ func (obj *StmtEdge) Graph() (*pgraph.Graph, error) {
|
|||||||
// called by this Output function if they are needed to produce the output. In
|
// called by this Output function if they are needed to produce the output. In
|
||||||
// the case of this edge statement, this is definitely the case. This edge stmt
|
// the case of this edge statement, this is definitely the case. This edge stmt
|
||||||
// returns output consisting of edges.
|
// returns output consisting of edges.
|
||||||
func (obj *StmtEdge) Output() (*interfaces.Output, error) {
|
func (obj *StmtEdge) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
edges := []*interfaces.Edge{}
|
edges := []*interfaces.Edge{}
|
||||||
|
|
||||||
|
// EdgeHalfList goes in a chain, so we increment like i++ and not i+=2.
|
||||||
for i := 0; i < len(obj.EdgeHalfList)-1; i++ {
|
for i := 0; i < len(obj.EdgeHalfList)-1; i++ {
|
||||||
nameValue1, err := obj.EdgeHalfList[i].Name.Value()
|
if obj.EdgeHalfList[i].namePtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
nameValue1, exists := table[obj.EdgeHalfList[i].namePtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// the edge name can be a single string or a list of strings...
|
// the edge name can be a single string or a list of strings...
|
||||||
@@ -2196,9 +2230,12 @@ func (obj *StmtEdge) Output() (*interfaces.Output, error) {
|
|||||||
return nil, fmt.Errorf("unhandled resource name type: %+v", nameValue1.Type())
|
return nil, fmt.Errorf("unhandled resource name type: %+v", nameValue1.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
nameValue2, err := obj.EdgeHalfList[i+1].Name.Value()
|
if obj.EdgeHalfList[i+1].namePtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
nameValue2, exists := table[obj.EdgeHalfList[i+1].namePtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
names2 := []string{} // list of names to build
|
names2 := []string{} // list of names to build
|
||||||
@@ -2714,20 +2751,24 @@ func (obj *StmtIf) Graph() (*pgraph.Graph, error) {
|
|||||||
// is used to build the output graph. This only exists for statements. The
|
// is used to build the output graph. This only exists for statements. The
|
||||||
// analogous function for expressions is Value. Those Value functions might get
|
// analogous function for expressions is Value. Those Value functions might get
|
||||||
// called by this Output function if they are needed to produce the output.
|
// called by this Output function if they are needed to produce the output.
|
||||||
func (obj *StmtIf) Output() (*interfaces.Output, error) {
|
func (obj *StmtIf) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
b, err := obj.Condition.Value()
|
if obj.conditionPtr == nil {
|
||||||
if err != nil {
|
return nil, ErrFuncPointerNil
|
||||||
return nil, err
|
}
|
||||||
|
b, exists := table[obj.conditionPtr]
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrTableNoValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var output *interfaces.Output
|
var output *interfaces.Output
|
||||||
|
var err error
|
||||||
if b.Bool() { // must not panic!
|
if b.Bool() { // must not panic!
|
||||||
if obj.ThenBranch != nil { // logically then branch is optional
|
if obj.ThenBranch != nil { // logically then branch is optional
|
||||||
output, err = obj.ThenBranch.Output()
|
output, err = obj.ThenBranch.Output(table)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if obj.ElseBranch != nil { // else branch is optional
|
if obj.ElseBranch != nil { // else branch is optional
|
||||||
output, err = obj.ElseBranch.Output()
|
output, err = obj.ElseBranch.Output(table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -3793,7 +3834,7 @@ func (obj *StmtProg) Graph() (*pgraph.Graph, error) {
|
|||||||
// is used to build the output graph. This only exists for statements. The
|
// is used to build the output graph. This only exists for statements. The
|
||||||
// analogous function for expressions is Value. Those Value functions might get
|
// analogous function for expressions is Value. Those Value functions might get
|
||||||
// called by this Output function if they are needed to produce the output.
|
// called by this Output function if they are needed to produce the output.
|
||||||
func (obj *StmtProg) Output() (*interfaces.Output, error) {
|
func (obj *StmtProg) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
resources := []engine.Res{}
|
resources := []engine.Res{}
|
||||||
edges := []*interfaces.Edge{}
|
edges := []*interfaces.Edge{}
|
||||||
|
|
||||||
@@ -3813,7 +3854,7 @@ func (obj *StmtProg) Output() (*interfaces.Output, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := stmt.Output()
|
output, err := stmt.Output(table)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -4009,7 +4050,7 @@ func (obj *StmtFunc) Graph() (*pgraph.Graph, error) {
|
|||||||
|
|
||||||
// Output for the func statement produces no output. Any values of interest come
|
// Output for the func statement produces no output. Any values of interest come
|
||||||
// from the use of the func which this binds the function to.
|
// from the use of the func which this binds the function to.
|
||||||
func (obj *StmtFunc) Output() (*interfaces.Output, error) {
|
func (obj *StmtFunc) Output(map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return interfaces.EmptyOutput(), nil
|
return interfaces.EmptyOutput(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4181,8 +4222,8 @@ func (obj *StmtClass) Graph() (*pgraph.Graph, error) {
|
|||||||
// come from the use of the include which this binds the statements to. This is
|
// come from the use of the include which this binds the statements to. This is
|
||||||
// usually called from the parent in StmtProg, but it skips running it so that
|
// usually called from the parent in StmtProg, but it skips running it so that
|
||||||
// it can be called from the StmtInclude Output method.
|
// it can be called from the StmtInclude Output method.
|
||||||
func (obj *StmtClass) Output() (*interfaces.Output, error) {
|
func (obj *StmtClass) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return obj.Body.Output()
|
return obj.Body.Output(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StmtInclude causes a user defined class to get used. It's effectively the way
|
// StmtInclude causes a user defined class to get used. It's effectively the way
|
||||||
@@ -4526,8 +4567,8 @@ func (obj *StmtInclude) Graph() (*pgraph.Graph, error) {
|
|||||||
// called by this Output function if they are needed to produce the output. The
|
// called by this Output function if they are needed to produce the output. The
|
||||||
// ultimate source of this output comes from the previously defined StmtClass
|
// ultimate source of this output comes from the previously defined StmtClass
|
||||||
// which should be found in our scope.
|
// which should be found in our scope.
|
||||||
func (obj *StmtInclude) Output() (*interfaces.Output, error) {
|
func (obj *StmtInclude) Output(table map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return obj.class.Output()
|
return obj.class.Output(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StmtImport adds the exported scope definitions of a module into the current
|
// StmtImport adds the exported scope definitions of a module into the current
|
||||||
@@ -4616,7 +4657,7 @@ func (obj *StmtImport) Graph() (*pgraph.Graph, error) {
|
|||||||
// called by this Output function if they are needed to produce the output. This
|
// called by this Output function if they are needed to produce the output. This
|
||||||
// import statement itself produces no output, as it is only used to populate
|
// import statement itself produces no output, as it is only used to populate
|
||||||
// the scope so that others can use that to produce values and output.
|
// the scope so that others can use that to produce values and output.
|
||||||
func (obj *StmtImport) Output() (*interfaces.Output, error) {
|
func (obj *StmtImport) Output(map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return interfaces.EmptyOutput(), nil
|
return interfaces.EmptyOutput(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4701,7 +4742,7 @@ func (obj *StmtComment) Graph() (*pgraph.Graph, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Output for the comment statement produces no output.
|
// Output for the comment statement produces no output.
|
||||||
func (obj *StmtComment) Output() (*interfaces.Output, error) {
|
func (obj *StmtComment) Output(map[interfaces.Func]types.Value) (*interfaces.Output, error) {
|
||||||
return interfaces.EmptyOutput(), nil
|
return interfaces.EmptyOutput(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,9 @@ type Stmt interface {
|
|||||||
Graph() (*pgraph.Graph, error)
|
Graph() (*pgraph.Graph, error)
|
||||||
|
|
||||||
// Output returns the output that this "program" produces. This output
|
// Output returns the output that this "program" produces. This output
|
||||||
// is what is used to build the output graph.
|
// is what is used to build the output graph. It requires the input
|
||||||
Output() (*Output, error)
|
// table of values that are used to populate each function.
|
||||||
|
Output(map[Func]types.Value) (*Output, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expr represents an expression in the language. Expr implementations must have
|
// Expr represents an expression in the language. Expr implementations must have
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ import (
|
|||||||
"github.com/purpleidea/mgmt/engine"
|
"github.com/purpleidea/mgmt/engine"
|
||||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||||
|
"github.com/purpleidea/mgmt/lang/types"
|
||||||
"github.com/purpleidea/mgmt/pgraph"
|
"github.com/purpleidea/mgmt/pgraph"
|
||||||
"github.com/purpleidea/mgmt/util/errwrap"
|
"github.com/purpleidea/mgmt/util/errwrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interpret runs the program and causes a graph generation as a side effect.
|
// Interpret runs the program and outputs a generated resource graph. It
|
||||||
// You should not run this on the AST if you haven't previously run the function
|
// requires an AST, and the table of values required to populate that AST. Type
|
||||||
// graph engine so that output values have been produced! Type unification is
|
// unification, and earlier steps should obviously be run first so that you can
|
||||||
// another important aspect which needs to have been completed.
|
// actually get a useful resource graph out of this instead of an error!
|
||||||
func Interpret(ast interfaces.Stmt) (*pgraph.Graph, error) {
|
func Interpret(ast interfaces.Stmt, table map[interfaces.Func]types.Value) (*pgraph.Graph, error) {
|
||||||
output, err := ast.Output() // contains resList, edgeList, etc...
|
output, err := ast.Output(table) // contains resList, edgeList, etc...
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user