mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
feat(ltl): bound fields on AlwaysFormula and named thunks
Add StepBound/Duration/Deadline to AlwaysFormula as the dual of bounded Eventually, give ThunkFormula a Name for stable identity, add ThunkNamed, and surface both in describe() and MarshalJSON.
This commit is contained in:
1 parent
88db9653e5
commit
4163f43736
1 file changed
+56
-11
+56
-11
@@ -13,9 +13,9 @@ type Formula interface {
|
|||||||
describe() string
|
describe() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// PredicateLabel lets a ThunkFormula carry a human-readable name for the
|
// PredicateLabel lets a ThunkFormula expose the identity of the closure it
|
||||||
// closure it wraps. Verifier wires this in when the spec gives the predicate
|
// wraps. ThunkFormula satisfies it through its Name field; an empty name
|
||||||
// a property name; otherwise it stays empty and serializes without a name.
|
// serializes without a name.
|
||||||
type PredicateLabel interface {
|
type PredicateLabel interface {
|
||||||
PredicateName() string
|
PredicateName() string
|
||||||
}
|
}
|
||||||
@@ -33,18 +33,33 @@ func (e ErrorFormula) describe() string {
|
|||||||
return fmt.Sprintf("Error(%q)", e.Message)
|
return fmt.Sprintf("Error(%q)", e.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AlwaysFormula obliges its inner formula to hold at every step. A bounded
|
||||||
|
// Always (the dual of a bounded Eventually) holds for the steps inside its
|
||||||
|
// window and is vacuously satisfied once the window closes. An unbounded
|
||||||
|
// Always carries no bound fields and is checked at every observed step.
|
||||||
type AlwaysFormula struct {
|
type AlwaysFormula struct {
|
||||||
Inner Formula
|
Inner Formula
|
||||||
|
StepBound int
|
||||||
|
HasStepBound bool
|
||||||
|
Duration time.Duration
|
||||||
|
Deadline time.Time
|
||||||
|
HasDeadline bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type PureFormula struct {
|
type PureFormula struct {
|
||||||
Value bool
|
Value bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThunkFormula wraps an opaque predicate closure. Name carries the predicate's
|
||||||
|
// identity so two distinct predicates produce distinct describe() keys and are
|
||||||
|
// never merged during obligation collapse.
|
||||||
type ThunkFormula struct {
|
type ThunkFormula struct {
|
||||||
Func func() bool
|
Func func() bool
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t ThunkFormula) PredicateName() string { return t.Name }
|
||||||
|
|
||||||
// NowFormula marks its inner formula for evaluation at the current step only.
|
// NowFormula marks its inner formula for evaluation at the current step only.
|
||||||
// Primarily used so that now(...).implies(...) parses unambiguously.
|
// Primarily used so that now(...).implies(...) parses unambiguously.
|
||||||
type NowFormula struct {
|
type NowFormula struct {
|
||||||
@@ -98,6 +113,10 @@ func Pure(value bool) Formula { return PureFormula{Value: value} }
|
|||||||
|
|
||||||
func Thunk(function func() bool) Formula { return ThunkFormula{Func: function} }
|
func Thunk(function func() bool) Formula { return ThunkFormula{Func: function} }
|
||||||
|
|
||||||
|
func ThunkNamed(name string, function func() bool) Formula {
|
||||||
|
return ThunkFormula{Func: function, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
func Now(inner Formula) Formula { return NowFormula{Inner: inner} }
|
func Now(inner Formula) Formula { return NowFormula{Inner: inner} }
|
||||||
|
|
||||||
func Next(inner Formula) Formula { return NextFormula{Inner: inner} }
|
func Next(inner Formula) Formula { return NextFormula{Inner: inner} }
|
||||||
@@ -137,9 +156,25 @@ func (OrFormula) isFormula() {}
|
|||||||
func (AndFormula) isFormula() {}
|
func (AndFormula) isFormula() {}
|
||||||
func (NotFormula) isFormula() {}
|
func (NotFormula) isFormula() {}
|
||||||
|
|
||||||
func (a AlwaysFormula) describe() string { return "Always(" + a.Inner.describe() + ")" }
|
func (a AlwaysFormula) describe() string {
|
||||||
func (p PureFormula) describe() string { return fmt.Sprintf("Pure(%t)", p.Value) }
|
parts := []string{a.Inner.describe()}
|
||||||
func (ThunkFormula) describe() string { return "Thunk(...)" }
|
if a.HasStepBound {
|
||||||
|
parts = append(parts, fmt.Sprintf("steps=%d", a.StepBound))
|
||||||
|
}
|
||||||
|
if a.HasDeadline {
|
||||||
|
parts = append(parts, "deadline="+a.Deadline.Format(time.RFC3339Nano))
|
||||||
|
} else if a.Duration > 0 {
|
||||||
|
parts = append(parts, "within="+a.Duration.String())
|
||||||
|
}
|
||||||
|
return "Always(" + strings.Join(parts, ", ") + ")"
|
||||||
|
}
|
||||||
|
func (p PureFormula) describe() string { return fmt.Sprintf("Pure(%t)", p.Value) }
|
||||||
|
func (t ThunkFormula) describe() string {
|
||||||
|
if t.Name != "" {
|
||||||
|
return "Thunk(" + t.Name + ")"
|
||||||
|
}
|
||||||
|
return "Thunk(...)"
|
||||||
|
}
|
||||||
func (n NowFormula) describe() string { return "Now(" + n.Inner.describe() + ")" }
|
func (n NowFormula) describe() string { return "Now(" + n.Inner.describe() + ")" }
|
||||||
func (n NextFormula) describe() string { return "Next(" + n.Inner.describe() + ")" }
|
func (n NextFormula) describe() string { return "Next(" + n.Inner.describe() + ")" }
|
||||||
func (e EventuallyFormula) describe() string {
|
func (e EventuallyFormula) describe() string {
|
||||||
@@ -176,10 +211,20 @@ type withinNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AlwaysFormula) MarshalJSON() ([]byte, error) {
|
func (a AlwaysFormula) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(struct {
|
payload := struct {
|
||||||
Op string `json:"op"`
|
Op string `json:"op"`
|
||||||
Arg Formula `json:"arg"`
|
Arg Formula `json:"arg"`
|
||||||
}{"always", a.Inner})
|
Within *withinNode `json:"within,omitempty"`
|
||||||
|
}{Op: "always", Arg: a.Inner}
|
||||||
|
switch {
|
||||||
|
case a.HasStepBound:
|
||||||
|
payload.Within = &withinNode{Amount: int64(a.StepBound), Unit: "steps"}
|
||||||
|
case a.Duration > 0:
|
||||||
|
payload.Within = &withinNode{Amount: a.Duration.Milliseconds(), Unit: "milliseconds"}
|
||||||
|
case a.HasDeadline:
|
||||||
|
payload.Within = &withinNode{Amount: a.Deadline.UnixMilli(), Unit: "deadline"}
|
||||||
|
}
|
||||||
|
return json.Marshal(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n NowFormula) MarshalJSON() ([]byte, error) {
|
func (n NowFormula) MarshalJSON() ([]byte, error) {
|
||||||
|
|||||||
Reference in new issue
Block a user