beanmachine.ppl.compiler.rules module

A rules engine for tree transformation

class beanmachine.ppl.compiler.rules.AllChildren(rule: beanmachine.ppl.compiler.rules.Rule, get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any], name: str = 'all_children')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all children or list members.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
combined_rule: beanmachine.ppl.compiler.rules.Rule
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.AllListEditMembers(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'all_list_edit_members')

Bases: beanmachine.ppl.compiler.rules.Rule

Rules which are intended to modify a parent list by adding or removing items return a ListEdit([…]) object, but in cases where a rule then recursese upon children – like top_down – we’ll potentially need to rewrite the elements in edit list. This combinator implements that.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.AllListMembers
class beanmachine.ppl.compiler.rules.AllListMembers(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'all_list_members')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all members. Succeeds if the rule succeeds for all members, and returns a list with the members replaced with the new values. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.AllOf(rules: List[beanmachine.ppl.compiler.rules.Rule], name: str = 'all_of')

Bases: beanmachine.ppl.compiler.rules.Rule

Takes a list of rules and composes together all of them. All must succeed, otherwise the rule fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rules: List[beanmachine.ppl.compiler.rules.Rule]
class beanmachine.ppl.compiler.rules.AllTermChildren(rule: beanmachine.ppl.compiler.rules.Rule, get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any], name: str = 'all_children')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all children. Succeeds if the rule succeeds for all children, and returns a constructed object with the children replaced with the new values. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
construct: Callable[[type, Dict[str, Any]], Any]
get_children: Callable[[Any], Dict[str, Any]]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.Check(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'check')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the given rule; if it fails, fail. If it succeeds, the result is the original test value, not the transformed value. This is useful for scenarios where we wish to know if a particular thing is true of a node before we apply an expensive rule to it.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.Choose(condition: beanmachine.ppl.compiler.rules.Rule, consequence: beanmachine.ppl.compiler.rules.Rule, alternative: beanmachine.ppl.compiler.rules.Rule, name: str = 'choose')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the condition rule to the test. If it succeeds, apply the rule in the consequence to its output. If it fails, apply the rule in the alternative to the test. That is, Choose(a, b, c)(test) has the semantics of if a(test) then b(a(test)) else c(test)

alternative: beanmachine.ppl.compiler.rules.Rule
always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
condition: beanmachine.ppl.compiler.rules.Rule
consequence: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.Compose(first: beanmachine.ppl.compiler.rules.Rule, second: beanmachine.ppl.compiler.rules.Rule, name: str = 'compose')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the first rule to the test. If it succeeds, apply the second rule to its output. That is, Compose(a, b)(test) has the semantics of if a(test) then b(a(test)) else fail

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
first: beanmachine.ppl.compiler.rules.Rule
second: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.Fail(test: Optional[Any] = None)

Bases: beanmachine.ppl.compiler.rules.RuleResult

expect_success() Any
is_fail() bool
is_success() bool
test: Any
class beanmachine.ppl.compiler.rules.FirstMatch(rules: Iterable[beanmachine.ppl.compiler.rules.Rule], name: str = 'first_match')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply each rule to the test until one succeeds; if none succeed, then fail.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rules: List[beanmachine.ppl.compiler.rules.Rule]
class beanmachine.ppl.compiler.rules.IgnoreException(rule: beanmachine.ppl.compiler.rules.Rule, expected: List[type] = [<class 'Exception'>], name: str = 'handle')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the given rule; if it throws an exception, the rule fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
expected: List[type]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.ListEdit(edits: List[Any])

Bases: object

Consider a rule which descends through an AST looking for a particular statement to replace. If the rule replaces a particular statement with another statement, we can express that with a straightforward rule that succeeds and produces the new statement. But how can we represent rules that either delete a statement (that is, replace it with nothing) or replace it with more than one statement? To express this concept, a rule should succeed and return a ListEdit([s1, s2…]) where the list contains the replacements; if the list is empty then the element is deleted.

edits: List[Any]
class beanmachine.ppl.compiler.rules.OneChild(rule: beanmachine.ppl.compiler.rules.Rule, get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any], name: str = 'one_child')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all children until the first success. Succeeds if it finds one success and returns a constructed object with the child replaced with the new value. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
construct: Callable[[type, Dict[str, Any]], Any]
get_children: Callable[[Any], Dict[str, Any]]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.OneListMember(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'one_child')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all members until the first success. Succeeds if it finds one success and returns a list with the child replaced with the new value. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.OrElse(first: beanmachine.ppl.compiler.rules.Rule, second: beanmachine.ppl.compiler.rules.Rule, name: str = 'or_else')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the first rule to the test. If it succeeds, use that result. If it fails, apply the second rule to the test and return that.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
first: beanmachine.ppl.compiler.rules.Rule
second: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.PatternRule(pattern: Optional[Union[beanmachine.ppl.compiler.patterns.PatternBase, int, str, float, type, list]], projection: Callable[[Any], Any] = <function _identity>, name: str = 'pattern')

Bases: beanmachine.ppl.compiler.rules.Rule

If the test value matches the pattern, then the test value is passed to the projection and the rule succeeds. Otherwise, the rule fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
pattern: Optional[Union[beanmachine.ppl.compiler.patterns.PatternBase, int, str, float, type, list]]
projection: Callable[[Any], Any]
class beanmachine.ppl.compiler.rules.Recursive(rule_maker: Callable[[], beanmachine.ppl.compiler.rules.Rule], name: str = 'recursive')

Bases: beanmachine.ppl.compiler.rules.Rule

Delay construction of a rule until we need it, so as to avoid recursion.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule_maker: Callable[[], beanmachine.ppl.compiler.rules.Rule]
class beanmachine.ppl.compiler.rules.Rule(name: str = '')

Bases: abc.ABC

A rule represents a partial function that transforms a value.

abstract always_succeeds() bool
abstract apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
name: str
class beanmachine.ppl.compiler.rules.RuleDomain(get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any])

Bases: object

all_children(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'all_children') beanmachine.ppl.compiler.rules.Rule
bottom_up(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'bottom_up') beanmachine.ppl.compiler.rules.Rule

The bottom-up combinator applies a rule to all leaves, then to the rewritten parent, and so on up to the root.

construct: Callable[[type, Dict[str, Any]], Any]
descend_until(test: beanmachine.ppl.compiler.rules.Rule, rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'descend_until') beanmachine.ppl.compiler.rules.Rule

descend_until starts at the top of the tree and descends down it checking every node to see if “test” succeeds. If it does, it stops descending and runs “rule” on that node. It does this on every node that meets the test starting from the root.

down_then_up(pre_rule: beanmachine.ppl.compiler.rules.Rule, post_rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'down_then_up') beanmachine.ppl.compiler.rules.Rule

The down-then-up combinator is a combination of the bottom-up and top-down combinators; it applies the ‘pre’ rule in a top-down traversal and then the ‘post’ rule on the way back up.

get_children: Callable[[Any], Dict[str, Any]]
one_child(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'one_child') beanmachine.ppl.compiler.rules.Rule
some_bottom_up(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'some_bottom_up') beanmachine.ppl.compiler.rules.Rule

The some-bottom-up combinator is like bottom_up, in that it applies a rule to every node in the tree starting from the leaves. However, bottom_up requires that the rule succeed for all nodes in the tree; some_bottom_up applies the rule to as many nodes in the tree as possible, leaves alone nodes for which it fails, and fails only if the rule applied to no node.

some_children(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'some_children') beanmachine.ppl.compiler.rules.Rule
some_top_down(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'some_top_down') beanmachine.ppl.compiler.rules.Rule

The some-top-down combinator is like top_down, in that it applies a rule to every node in the tree starting from the top. However, top_down requires that the rule succeed for all nodes in the tree; some_top_down applies the rule to as many nodes in the tree as possible, leaves alone nodes for which it fails (aside from possibly rewriting the children), and fails only if the rule applied to no node.

specific_child(child: str, rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'specific_child') beanmachine.ppl.compiler.rules.Rule

Apply a rule to a specific child. If it succeeds, replace the child with the new value; otherwise, fail. The child is required to exist.

top_down(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'top_down') beanmachine.ppl.compiler.rules.Rule

The top-down combinator applies a rule to the root, then to the new root’s children, and so on down to the leaves. It succeeds iff the rule succeeds on every node.

class beanmachine.ppl.compiler.rules.RuleResult(test: Any)

Bases: abc.ABC

abstract expect_success() Any
abstract is_fail() bool
abstract is_success() bool
test: Any
class beanmachine.ppl.compiler.rules.SomeChildren(rule: beanmachine.ppl.compiler.rules.Rule, get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any], name: str = 'some_children')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all children. Succeeds if the rule succeeds for one or more children, and returns a constructed object with the children replaced with the new values. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
construct: Callable[[type, Dict[str, Any]], Any]
get_children: Callable[[Any], Dict[str, Any]]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.SomeListMembers(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'some_list_members')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to all members. Succeeds if the rule succeeds for one or more members, and returns a list with the children replaced with the new values. Otherwise, fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.SomeOf(rules: List[beanmachine.ppl.compiler.rules.Rule], name: str = 'some_of')

Bases: beanmachine.ppl.compiler.rules.Rule

Takes a list of rules and composes together as many of them as succeed. At least one must succeed, otherwise the rule fails.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rules: List[beanmachine.ppl.compiler.rules.Rule]
class beanmachine.ppl.compiler.rules.SpecificChild(child: str, rule: beanmachine.ppl.compiler.rules.Rule, get_children: Callable[[Any], Dict[str, Any]], construct: Callable[[type, Dict[str, Any]], Any], name: str = 'specific_child')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply a rule to a specific child. If it succeeds, replace the child with the new value; otherwise, fail. The child is required to exist.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
child: str
construct: Callable[[type, Dict[str, Any]], Any]
get_children: Callable[[Any], Dict[str, Any]]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.Success(test: Any, result: Any)

Bases: beanmachine.ppl.compiler.rules.RuleResult

expect_success() Any
is_fail() bool
is_success() bool
result: Any
class beanmachine.ppl.compiler.rules.Trace(rule: beanmachine.ppl.compiler.rules.Rule, logger: Callable[[beanmachine.ppl.compiler.rules.Rule, Any], None])

Bases: beanmachine.ppl.compiler.rules.Rule

This combinator introduces a side effect to be executed every time the child rule is executed, and when it succeeds or fails. It is useful for debugging.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
logger: Callable[[beanmachine.ppl.compiler.rules.Rule, Any], None]
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.TryMany(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'try_many')

Bases: beanmachine.ppl.compiler.rules.Rule

Repeatedly apply a rule; the result is that of the last application that succeeded, or the original test if none succeeded. This rule always succeeds.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
class beanmachine.ppl.compiler.rules.TryOnce(rule: beanmachine.ppl.compiler.rules.Rule, name: str = 'try_once')

Bases: beanmachine.ppl.compiler.rules.Rule

Apply the rule to the test. If it succeeds, use that result. If it fails, use the test as the result and succeed. This rule always succeeds.

always_succeeds() bool
apply(test: Any) beanmachine.ppl.compiler.rules.RuleResult
rule: beanmachine.ppl.compiler.rules.Rule
beanmachine.ppl.compiler.rules.always_replace(value: Any, name: str = 'always_replace') beanmachine.ppl.compiler.rules.Rule

always_replace(value) produces a rule that replaces anything with the given value. It always succeeds.

beanmachine.ppl.compiler.rules.at_least_once(rule: beanmachine.ppl.compiler.rules.Rule) beanmachine.ppl.compiler.rules.Rule

Try a rule once; if it fails, fail. If it succeeds, try it again as many times as it keeps succeeding.

beanmachine.ppl.compiler.rules.either_or_both(first: beanmachine.ppl.compiler.rules.Rule, second: beanmachine.ppl.compiler.rules.Rule, name: str = 'either_or_both') beanmachine.ppl.compiler.rules.Rule

Do the first rule; if it succeeds, try doing the second rule, but do not worry if it fails. If the first rule fails, do the second rule. The net effect is, either first, or second, or first-then-second happens, or both fail.

beanmachine.ppl.compiler.rules.if_then(condition: beanmachine.ppl.compiler.rules.Rule, consequence: beanmachine.ppl.compiler.rules.Rule, alternative: beanmachine.ppl.compiler.rules.Rule = <beanmachine.ppl.compiler.rules.PatternRule object>) beanmachine.ppl.compiler.rules.Rule

Apply the condition rule, then apply the original test to either the consequence or the alternative, depending on whether the condition succeeded or failed. Note that this is different than Choose. Choose applies the condition to the result of the condition, not to the original test.

beanmachine.ppl.compiler.rules.ignore_div_zero(rule: beanmachine.ppl.compiler.rules.Rule) beanmachine.ppl.compiler.rules.Rule
beanmachine.ppl.compiler.rules.ignore_runtime_error(rule: beanmachine.ppl.compiler.rules.Rule) beanmachine.ppl.compiler.rules.Rule
beanmachine.ppl.compiler.rules.ignore_value_error(rule: beanmachine.ppl.compiler.rules.Rule) beanmachine.ppl.compiler.rules.Rule
beanmachine.ppl.compiler.rules.list_member_children(rule: beanmachine.ppl.compiler.rules.Rule) beanmachine.ppl.compiler.rules.Rule
beanmachine.ppl.compiler.rules.make_logger(log: List[str]) Callable[[beanmachine.ppl.compiler.rules.Rule], beanmachine.ppl.compiler.rules.Rule]
beanmachine.ppl.compiler.rules.pattern_rules(pairs: List[Tuple[Optional[Union[beanmachine.ppl.compiler.patterns.PatternBase, int, str, float, type, list]], Callable[[Any], Any]]], name: str = 'pattern_rules') beanmachine.ppl.compiler.rules.Rule

Constructs a rule from a sequence of pairs of patterns and projections. Patterns are checked in order, and the first one that matches is used for the projection; if none match then the rule fails.

beanmachine.ppl.compiler.rules.projection_rule(projection: Callable[[Any], Any], name: str = 'projection') beanmachine.ppl.compiler.rules.Rule