Bc_rule Syntax
Bc_rule
Backward-chaining rules have four parts:
- A unique name.
- A use clause.
- An optional when clause.
- An optional with clause.
bc_rule ::= IDENTIFIER NL INDENT use [when] [with] DEINDENT
The IDENTIFIER is the unique name for this rule and is used as the corresponding Python function name in the generated <rb_name>_bc.py file and also for the Python function name of the plan function (if any) associated with the rule. This name will show up in stack traces associated with exceptions raised during inferencing or plan execution.
Use Clause
The use clause is the then part of the rule. It identifies the goal that this rule is prepared to prove.
use ::= 'use' IDENTIFIER '(' {pattern,} ')' ['taking' '(' <python_arg_spec> ')'] NL | 'use' IDENTIFIER '(' {pattern,} ')' NL INDENT 'taking' '(' <python_arg_spec> ')' NL DEINDENT
Notice that it uses a single IDENTIFIER. The rule base name is implied as the rule base category name (the name of the root rule base, see extending clause) for the rule base containing this rule.
When Clause
The when clause is the if part of the rule. It defines the premises that must be true for this rule to succeed.
If the when clause is omitted, the only requirement for the rule to succeed is that the use clause pattern matches the goal.
If the when clause is specified, the rule succeeds for each combination of true premises (see backtracking).
when ::= 'when' NL INDENT {bc_premise NL} DEINDENT bc_premise ::= ['!'] [ name '.' ] name '(' {pattern,} ')' [ plan_spec ] | compound_premise | python_premise name ::= IDENTIFIER | '$'IDENTIFIER
Here are the links to the definitions for pattern, compound_premise and python_premise.
If the bc_premise includes the !, an AssertionError will be raised if the premise fails on the first try. This can help in debugging.
Note
This does not apply when the premise fails on backtracking (in which case it has already succeeded at least once).
If a single name is used in the bc_premise, the rule base category for the current rule base (the root rule base name, see extending clause) is assumed.
If two names are used in the bc_premise, the first may name a rule base category or some other knowledge base.
If a rule base category name is used (or assumed), the currently active rule base for that category is used to prove the premise.
Note
If the rule base category name is omitted, and therefore assumed to be the current rule base's rule base category, the current rule base does not have to be the active rule base for that category. It could be the case that a derived rule base is the active rule base. In that case, the derived rule base is used to prove the premise.
In this way, different rules may be used to prove the same premise, depending upon which rule base has been activated.
Plan_spec
A plan_spec is required for each premise that returns a subordinate plan. This shows what should be done with that subordinate plan function.
Thus, a rule's plan function is composed first of the collected python_statements taken from its plan_specs (as described below), followed by the python_statements within its with clause (if any). The inclusion of any plan_spec containing a python_statement will cause a plan function to be generated for this rule, even if the rule lacks a with clause.
plan_spec ::= [ 'step' NUMBER ] NL INDENT {<python_statement> NL} DEINDENT | 'as' '$'IDENTIFIER NL
Within each python_statement, the subordinate plan function is indicated by $$. The result of this function may be assigned to a Python variable, but not a pattern variable ($variable). Parameters from the rule's taking clause may be passed on to the subordinate plan functions.
When multiple premises have python_statements in their plan_specs, the python_statements in plan_specs without a step clause are executed first in the order that they appear in the when clause.
Then the python_statements in plan_specs with a step clause are executed in ascending NUMBER sequence. It is permissible for the NUMBER to be negative or a float.
If the as clause is used, the plan function is bound to the pattern variable as a Python function, but not automatically executed. This allows you to call the function (or not) when and as many times as you please. The parameters required are defined in the taking clause of the rule used to prove the premise.
With Clause
The with clause contains Python statements to include in the plan produced by this rule. These Python statements may include pattern variables whose values will be cooked into these statements when the plan is created.
with ::= 'with' NL INDENT {<python_statement> NL} DEINDENT
The python_statements are included in the rule's plan function after all of the calls to the subordinate plan functions made from the plan_specs in the when clause.
If the with clause is omitted, but the when clause has plan_specs (excluding the as clause), a plan function is still generated for this rule so that the subordinate plan functions are still called.
The python_statements are not parsed. They are simply scanned for $ pattern variables that don't occur within string literals or comments. The values bound to these variables are cooked into the code to produce the plan.
Thus, all pattern variables used within python_statements (both in the plan_specs and the when clause) must be bound to a value. This value is a constant value that never changes for this plan.
Note
This occurs after the entire top-level goal is proven so that it is permissible to bind these pattern variables to values following the execution of the rule containing them.