engine: resources, lang: funcs, parser: Add panic magic
It's valuable to check your runtime values and to shut down the entire engine in case something doesn't match. This patch adds some magic plumbing to support a "panic" mechanism. A new "panic" statement gets transparently converted into a panic function and panic resource. The former errors if the input is not empty. The latter must be present to consume the value, but doesn't actually do anything.
This commit is contained in:
@@ -291,6 +291,8 @@ type StmtRes struct {
|
||||
Name interfaces.Expr // unique name for the res of this kind
|
||||
namePtr interfaces.Func // ptr for table lookup
|
||||
Contents []StmtResContents // list of fields/edges in parsed order
|
||||
|
||||
allowUnderscores bool // secret flag to bypass some validation
|
||||
}
|
||||
|
||||
// String returns a short representation of this statement.
|
||||
@@ -319,7 +321,10 @@ func (obj *StmtRes) Apply(fn func(interfaces.Node) error) error {
|
||||
// Init initializes this branch of the AST, and returns an error if it fails to
|
||||
// validate.
|
||||
func (obj *StmtRes) Init(data *interfaces.Data) error {
|
||||
if strings.Contains(obj.Kind, "_") {
|
||||
|
||||
isPanic := obj.allowUnderscores && obj.Kind == interfaces.PanicResKind
|
||||
|
||||
if strings.Contains(obj.Kind, "_") && !isPanic {
|
||||
return fmt.Errorf("kind must not contain underscores")
|
||||
}
|
||||
|
||||
@@ -379,10 +384,11 @@ func (obj *StmtRes) Interpolate() (interfaces.Stmt, error) {
|
||||
}
|
||||
|
||||
return &StmtRes{
|
||||
data: obj.data,
|
||||
Kind: obj.Kind,
|
||||
Name: name,
|
||||
Contents: contents,
|
||||
data: obj.data,
|
||||
Kind: obj.Kind,
|
||||
Name: name,
|
||||
Contents: contents,
|
||||
allowUnderscores: obj.allowUnderscores,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -419,10 +425,11 @@ func (obj *StmtRes) Copy() (interfaces.Stmt, error) {
|
||||
return obj, nil
|
||||
}
|
||||
return &StmtRes{
|
||||
data: obj.data,
|
||||
Kind: obj.Kind,
|
||||
Name: name,
|
||||
Contents: contents,
|
||||
data: obj.data,
|
||||
Kind: obj.Kind,
|
||||
Name: name,
|
||||
Contents: contents,
|
||||
allowUnderscores: obj.allowUnderscores,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2833,6 +2840,8 @@ type StmtProg struct {
|
||||
importProgs []*StmtProg // list of child programs after running SetScope
|
||||
importFiles []string // list of files seen during the SetScope import
|
||||
|
||||
panicCounter uint // number of possible different panics
|
||||
|
||||
Body []interfaces.Stmt
|
||||
}
|
||||
|
||||
@@ -2887,6 +2896,36 @@ func (obj *StmtProg) Interpolate() (interfaces.Stmt, error) {
|
||||
return nil, err
|
||||
}
|
||||
body = append(body, interpolated)
|
||||
|
||||
// If we have the magic bind statement, then add the res.
|
||||
// NOTE: We could have used a custom StmtPanic instead here...
|
||||
if bind, ok := interpolated.(*StmtBind); ok && bind.Ident == interfaces.PanicVarName {
|
||||
// TODO: does it still work if we have multiple StmtProg's?
|
||||
obj.panicCounter++
|
||||
name := fmt.Sprintf("%s%d", interfaces.PanicVarName, obj.panicCounter)
|
||||
bind.Ident = name // change name to magic name
|
||||
exprVar := &ExprVar{
|
||||
Name: name, // use magic name to match
|
||||
|
||||
allowUnderscores: true, // allow our magic name
|
||||
}
|
||||
if err := exprVar.Init(obj.data); err != nil {
|
||||
return nil, errwrap.Wrapf(err, "special panic ExprVar Init error during interpolate")
|
||||
}
|
||||
stmtRes := &StmtRes{
|
||||
Kind: interfaces.PanicResKind, // special resource kind
|
||||
Name: exprVar,
|
||||
Contents: []StmtResContents{},
|
||||
|
||||
allowUnderscores: true, // allow our magic kind
|
||||
}
|
||||
if err := stmtRes.Init(obj.data); err != nil {
|
||||
return nil, errwrap.Wrapf(err, "special panic StmtRes Init error during interpolate")
|
||||
}
|
||||
|
||||
body = append(body, stmtRes)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return &StmtProg{
|
||||
data: obj.data,
|
||||
@@ -8250,6 +8289,8 @@ type ExprVar struct {
|
||||
typ *types.Type
|
||||
|
||||
Name string // name of the variable
|
||||
|
||||
allowUnderscores bool // secret flag to bypass some validation
|
||||
}
|
||||
|
||||
// String returns a short representation of this expression.
|
||||
@@ -8265,6 +8306,9 @@ func (obj *ExprVar) Apply(fn func(interfaces.Node) error) error { return fn(obj)
|
||||
// Init initializes this branch of the AST, and returns an error if it fails to
|
||||
// validate.
|
||||
func (obj *ExprVar) Init(*interfaces.Data) error {
|
||||
if obj.allowUnderscores && strings.HasPrefix(obj.Name, interfaces.PanicVarName) {
|
||||
return nil
|
||||
}
|
||||
return langutil.ValidateVarName(obj.Name)
|
||||
}
|
||||
|
||||
@@ -8275,9 +8319,10 @@ func (obj *ExprVar) Init(*interfaces.Data) error {
|
||||
// support variable, variables or anything crazy like that.
|
||||
func (obj *ExprVar) Interpolate() (interfaces.Expr, error) {
|
||||
return &ExprVar{
|
||||
scope: obj.scope,
|
||||
typ: obj.typ,
|
||||
Name: obj.Name,
|
||||
scope: obj.scope,
|
||||
typ: obj.typ,
|
||||
Name: obj.Name,
|
||||
allowUnderscores: obj.allowUnderscores,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -8288,9 +8333,10 @@ func (obj *ExprVar) Interpolate() (interfaces.Expr, error) {
|
||||
// and they won't be able to have different values.
|
||||
func (obj *ExprVar) Copy() (interfaces.Expr, error) {
|
||||
return &ExprVar{
|
||||
scope: obj.scope,
|
||||
typ: obj.typ,
|
||||
Name: obj.Name,
|
||||
scope: obj.scope,
|
||||
typ: obj.typ,
|
||||
Name: obj.Name,
|
||||
allowUnderscores: obj.allowUnderscores,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user