lang: funcs, parser: Add improved panic magic
This is a newer implementation of the panic magic. I kept the old commit in for posterity and to show the difference. The two versions are identical to the end-user with one exception: the newer version doesn't include a useless panic resource in the graph when there is no panic. In this version, the panic function returns false and the if statement it's the condition of, doesn't produce the resource within. On error, we still consume the function in the if expression, and doing so causes everything to shutdown. The other benefit is that the implementation is much cleaner and doesn't need the interpolate hack.
This commit is contained in:
@@ -12,7 +12,6 @@ test "test" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# this is what we're simulating:
|
# this is what we're simulating:
|
||||||
#$_panic1 = panic("whatever1") # this is a function
|
#if panic("whatever1") { # this is a function (and an if statement)
|
||||||
#_panic $_panic1 {} # this is a resource
|
# _panic "panic" {} # this is a resource
|
||||||
#$_panic2 = panic("whatever2")
|
#}
|
||||||
#_panic $_panic2 {}
|
|
||||||
|
|||||||
@@ -291,8 +291,6 @@ type StmtRes struct {
|
|||||||
Name interfaces.Expr // unique name for the res of this kind
|
Name interfaces.Expr // unique name for the res of this kind
|
||||||
namePtr interfaces.Func // ptr for table lookup
|
namePtr interfaces.Func // ptr for table lookup
|
||||||
Contents []StmtResContents // list of fields/edges in parsed order
|
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.
|
// String returns a short representation of this statement.
|
||||||
@@ -321,10 +319,7 @@ 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
|
// Init initializes this branch of the AST, and returns an error if it fails to
|
||||||
// validate.
|
// validate.
|
||||||
func (obj *StmtRes) Init(data *interfaces.Data) error {
|
func (obj *StmtRes) Init(data *interfaces.Data) error {
|
||||||
|
if strings.Contains(obj.Kind, "_") && obj.Kind != interfaces.PanicResKind {
|
||||||
isPanic := obj.allowUnderscores && obj.Kind == interfaces.PanicResKind
|
|
||||||
|
|
||||||
if strings.Contains(obj.Kind, "_") && !isPanic {
|
|
||||||
return fmt.Errorf("kind must not contain underscores")
|
return fmt.Errorf("kind must not contain underscores")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +383,6 @@ func (obj *StmtRes) Interpolate() (interfaces.Stmt, error) {
|
|||||||
Kind: obj.Kind,
|
Kind: obj.Kind,
|
||||||
Name: name,
|
Name: name,
|
||||||
Contents: contents,
|
Contents: contents,
|
||||||
allowUnderscores: obj.allowUnderscores,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +423,6 @@ func (obj *StmtRes) Copy() (interfaces.Stmt, error) {
|
|||||||
Kind: obj.Kind,
|
Kind: obj.Kind,
|
||||||
Name: name,
|
Name: name,
|
||||||
Contents: contents,
|
Contents: contents,
|
||||||
allowUnderscores: obj.allowUnderscores,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2840,8 +2833,6 @@ type StmtProg struct {
|
|||||||
importProgs []*StmtProg // list of child programs after running SetScope
|
importProgs []*StmtProg // list of child programs after running SetScope
|
||||||
importFiles []string // list of files seen during the SetScope import
|
importFiles []string // list of files seen during the SetScope import
|
||||||
|
|
||||||
panicCounter uint // number of possible different panics
|
|
||||||
|
|
||||||
Body []interfaces.Stmt
|
Body []interfaces.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2896,36 +2887,6 @@ func (obj *StmtProg) Interpolate() (interfaces.Stmt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
body = append(body, interpolated)
|
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{
|
return &StmtProg{
|
||||||
data: obj.data,
|
data: obj.data,
|
||||||
@@ -8289,8 +8250,6 @@ type ExprVar struct {
|
|||||||
typ *types.Type
|
typ *types.Type
|
||||||
|
|
||||||
Name string // name of the variable
|
Name string // name of the variable
|
||||||
|
|
||||||
allowUnderscores bool // secret flag to bypass some validation
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a short representation of this expression.
|
// String returns a short representation of this expression.
|
||||||
@@ -8306,9 +8265,6 @@ 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
|
// Init initializes this branch of the AST, and returns an error if it fails to
|
||||||
// validate.
|
// validate.
|
||||||
func (obj *ExprVar) Init(*interfaces.Data) error {
|
func (obj *ExprVar) Init(*interfaces.Data) error {
|
||||||
if obj.allowUnderscores && strings.HasPrefix(obj.Name, interfaces.PanicVarName) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return langutil.ValidateVarName(obj.Name)
|
return langutil.ValidateVarName(obj.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8322,7 +8278,6 @@ func (obj *ExprVar) Interpolate() (interfaces.Expr, error) {
|
|||||||
scope: obj.scope,
|
scope: obj.scope,
|
||||||
typ: obj.typ,
|
typ: obj.typ,
|
||||||
Name: obj.Name,
|
Name: obj.Name,
|
||||||
allowUnderscores: obj.allowUnderscores,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8336,7 +8291,6 @@ func (obj *ExprVar) Copy() (interfaces.Expr, error) {
|
|||||||
scope: obj.scope,
|
scope: obj.scope,
|
||||||
typ: obj.typ,
|
typ: obj.typ,
|
||||||
Name: obj.Name,
|
Name: obj.Name,
|
||||||
allowUnderscores: obj.allowUnderscores,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,21 +26,18 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
simple.Register("panic", &types.FuncValue{
|
simple.Register("panic", &types.FuncValue{
|
||||||
T: types.NewType("func(x str) str"),
|
T: types.NewType("func(x str) bool"),
|
||||||
V: Panic,
|
V: Panic,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic returns an error when it receives a non-empty string. The error should
|
// Panic returns an error when it receives a non-empty string. The error should
|
||||||
// cause the function engine to shutdown.
|
// cause the function engine to shutdown. If there's no error, it returns false.
|
||||||
func Panic(input []types.Value) (types.Value, error) {
|
func Panic(input []types.Value) (types.Value, error) {
|
||||||
if s := input[0].Str(); s != "" {
|
if s := input[0].Str(); s != "" {
|
||||||
// This StrValue not really used here, since we error...
|
return nil, fmt.Errorf("panic occurred: %s", s)
|
||||||
return &types.StrValue{
|
|
||||||
V: s,
|
|
||||||
}, fmt.Errorf("panic occurred: %s", s)
|
|
||||||
}
|
}
|
||||||
return &types.StrValue{
|
return &types.BoolValue{
|
||||||
V: "panic", // name can't be empty
|
V: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,4 @@ const (
|
|||||||
|
|
||||||
// PanicResKind is the kind string used for the panic resource.
|
// PanicResKind is the kind string used for the panic resource.
|
||||||
PanicResKind = "_panic"
|
PanicResKind = "_panic"
|
||||||
|
|
||||||
// PanicVarName is the magic name used for the panic output var.
|
|
||||||
PanicVarName = "_panic"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# This should not panic.
|
# This should not panic.
|
||||||
panic("")
|
panic("")
|
||||||
panic("")
|
panic("")
|
||||||
|
# Empty graph, no output.
|
||||||
-- OUTPUT --
|
-- OUTPUT --
|
||||||
Vertex: _panic[panic]
|
|
||||||
|
|||||||
@@ -3,5 +3,4 @@
|
|||||||
panic("")
|
panic("")
|
||||||
test "hello" {}
|
test "hello" {}
|
||||||
-- OUTPUT --
|
-- OUTPUT --
|
||||||
Vertex: _panic[panic]
|
|
||||||
Vertex: test[hello]
|
Vertex: test[hello]
|
||||||
|
|||||||
@@ -927,17 +927,30 @@ bind:
|
|||||||
;
|
;
|
||||||
panic:
|
panic:
|
||||||
// panic("some error")
|
// panic("some error")
|
||||||
|
// generates:
|
||||||
|
// if panic("some error") {
|
||||||
|
// _panic "_panic" {} # resource
|
||||||
|
//}
|
||||||
PANIC_IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN
|
PANIC_IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN
|
||||||
{
|
{
|
||||||
posLast(yylex, yyDollar) // our pos
|
posLast(yylex, yyDollar) // our pos
|
||||||
call := &ast.ExprCall{
|
call := &ast.ExprCall{
|
||||||
Name: $1.str,
|
Name: $1.str, // the function name
|
||||||
Args: $3.exprs,
|
Args: $3.exprs,
|
||||||
//Var: false, // default
|
//Var: false, // default
|
||||||
}
|
}
|
||||||
$$.stmt = &ast.StmtBind{
|
name := &ast.ExprStr{
|
||||||
Ident: interfaces.PanicVarName, // make up a placeholder var
|
V: $1.str, // any constant, non-empty name
|
||||||
Value: call,
|
}
|
||||||
|
res := &ast.StmtRes{
|
||||||
|
Kind: interfaces.PanicResKind,
|
||||||
|
Name: name,
|
||||||
|
Contents: []ast.StmtResContents{},
|
||||||
|
}
|
||||||
|
$$.stmt = &ast.StmtIf{
|
||||||
|
Condition: call,
|
||||||
|
ThenBranch: res,
|
||||||
|
//ElseBranch: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|||||||
Reference in New Issue
Block a user