{Begin SubSec Using Rules in LOOPS} {Title Using Rules in LOOPS} {Text The Loops rules language is supported by an integrated programming environment for creating, editing, compiling, and debugging RuleSets. This section describes how to use that environment. {Begin SubSec Creating RuleSets} {Title Creating RuleSets} {Text RuleSets are named Loops objects and are created by sending the class {lisp RuleSet}{index RuleSet (Class)} a {lisp New} message as follows: {lispcode (_ $RuleSet New)} After entering this form, the user will be prompted for a Loops name as {lispcode RuleSet name: {arg RuleSetName}} Afterwards, the RuleSet can be referenced using Loops dollar sign notation as usual. It is also possible to include the RuleSet name in the {lisp New} message as follows: {lispcode (_ $RuleSet New NIL {arg RuleSetName})} {note The {lisp NIL} argument is necessary in this form. It is a placeholder for the {arg assoclist} argument to the {lisp New} method for {lisp RuleSet}, which follows the Loops Template instantiation protocol.} }{End SubSec Creating RuleSets} {Begin SubSec Editing RuleSets} {Title Editing RuleSets} {Text A RuleSet is created empty of rules. The RuleSet editor is used to enter and modify rules. The editor can be invoked with an {lisp EditRules}{index EditRules (Message)} message (or {lisp ER}{index ER (Message)} shorthand message) as follows: {lispcode (_ {arg RuleSet} EditRules) (_ {arg RuleSet} ER)} If a RuleSet is installed as a method of a class, it can be edited conveniently by selecting the {lisp EM} option from a browser containing the class. Alternatively, the {fn EM} function or {lisp EditMethod} message can be used: {Def {Type (Message)} {Name EditMethod} {Args ClassName selector} {PrintName {lisp (_ {arg ClassName} EditMethod {arg selector})} } {Text}} {FnDef {Name EM} {Args ClassName selector} {Text}} Both approaches to editing retrieve the source of the RuleSet and put the user into the TTYIN editor, treating the rule source as text. {note The rule source is saved internally as a LISP string, to avoid conflicts with any read or printing MACROS which may have been defined in the Interlisp system.} Initially, the source is a template for RuleSets as follows: {Begin Figure} {Text {lispcode RuleSet Name: RuleSetName; WorkSpace Class: ClassName; Control Structure: doAll; While Condition: ; Audit Class: StandardAuditRecord; Rule Class: Rule; Task Class: ; Meta Assignments: ; Temporary Vars:; Lisp Vars: ; Debug Vars: ; Compiler Options: ; (* Rules for whatever. Comment goes here.)} } {Caption Initial template for a RuleSet. The rules are entered after the comment at the bottom. The declarations at the beginning are filled in as needed and superfluous declarations can be discarded. } {End Figure} The user can then edit this template to enter rules and set the declarations at the beginning. In the current version of the rule editor, most of these declarations are left out. If the user chooses the {lisp EditAllDecls} option in the RuleSet editor menu, the declarations and default values will be printed in full. The template is only a guide. Declarations that are not needed can be deleted. For example, if there are no temporary variables for this RuleSet, the {lisp Temporary Vars} declaration can be deleted. If the control structure is not one of the while control structures, then the {lisp While Condition} declaration can be deleted. If the compiler option {lisp A} is not chosen, then the {lisp Audit Class} declaration can be deleted. When the user leaves the editor, the RuleSet is compiled automatically into a LISP function. {note If the user has selected the {lisp L} compiler option, the resulting LISP function is itself compiled into byte codes. In more typical usage, the RuleSet is not itself compiled until the RuleSet is debugged and a file is saved at the end of the session.} If a syntax error is detected during compilation, an error message is printed and the user is given another opportunity to edit the RuleSet. }{End SubSec Editing RuleSets} {Begin SubSec Copying RuleSets} {Title Copying RuleSets} {Text Sometimes it is convenient to create new RuleSets by editing a copy of an existing RuleSet. For this purpose, the method {lisp CopyRules} is provided as follows: {Def {Type (Message)} {Name CopyRules} {Args oldRuleSet newRuleSetName} {PrintName {lisp (_ {arg oldRuleSet} CopyRules {arg newRuleSetName})} } {Text}} This creates a new RuleSet by some of the information from the pespectives of the old RuleSet. It also updates the source text of the new RuleSet to contain the new name. }{End SubSec Copying RuleSets} {Begin SubSec Saving RuleSets on LISP Files} {Title Saving RuleSets on LISP Files} {Text RuleSets can be saved on LISP files just like other Loops objects. In addition, it is usually useful to save the LISP functions that result from RuleSet compilation. In the current implementation, these functions have the same names as the RuleSets themselves. To save RuleSets on a file, it is necessary to add two statements to the file commands for the file as follows: {lispcode (FNS * MyRuleSetNames) (INSTANCES * MyRuleSetNames)} where {lisp MyRuleSetNames} is a LISP variable whose value is a list of the names of the RuleSets to be saved. }{End SubSec Saving RuleSets on LISP Files} {Begin SubSec Printing RuleSets} {Title Printing RuleSets} {Text To print a RuleSet without editing it, one can send a {lisp PPRules} or {lisp PPR} message as follows: {Def {Type (Message)} {Name PPRules} {Args RuleSet} {PrintName {lisp (_ {arg RuleSet} PPRules)} } } {Def {Type (Message)} {Name PPR} {Args RuleSet} {PrintName {lisp (_ {arg RuleSet} PPR)} } {Text}} A convenient way to make hardcopy listings of RuleSets is to use the function {fn ListRuleSets}.{index ListRuleSets Fn} The files will be printed on the {var DEFAULTPRINTINGHOST} as is standard in Interlisp-D. {fn ListRuleSets} can be given three kinds of arguments as follows: {lispcode (ListRuleSets {arg RuleSetName}) (ListRuleSets {arg ListOfRuleSetNames}) (ListRuleSets {arg ClassName}) (ListRuleSets {arg FileName})} In the {arg ClassName} case, all of the RuleSets that have been installed as methods of the class will be printed. In the last case, all of the RuleSets stored in the file will be printed. }{End SubSec Printing RuleSets} {Begin SubSec Running RuleSets from Loops} {Title Running RuleSets from Loops} {Text RuleSets can be invoked from Loops using any of the usual protocols. {it Procedure-oriented Protocol:} The way to invoke a RuleSet from Loops is to use the {fn RunRS} function: {FnDef {Name RunRS} {Args RuleSet workSpace arg{sub 2} {ellipsis} arg{sub N}} {Text {arg workSpace} is the Loops object to be used as the work space. This is "procedural" in the sense that the RuleSet is invoked by its name. {arg RuleSet} can be either a RuleSet object or its name. }} {it Object-oriented Protocol:} When RuleSets are installed as methods in Loops classes, they can be invoked in the usual way by sending a message to an instance of the class. For example, if {lisp WashingMachine} is a class with a RuleSet installed for its {lisp Simulate} method, the RuleSet is invoked as follows: {lispcode (_ washingMachineInstance Simulate)} {it Data-oriented Protocol:} When RuleSets are installed in active values, they are invoked by side-effect as a result of accessing the variable on which they are installed. }{End SubSec Running RuleSets from Loops} {Begin SubSec Installing RuleSets as Methods} {Title Installing RuleSets as Methods} {Text RuleSets can also be used as methods for classes. This is done by installing automatically-generated invocation functions that invoke the RuleSets. For example: {lispcode [DEFCLASS WashingMachine (MetaClass Class doc (* comment) ...) ... (InstanceVariables (owner ...)) (Methods (Simulate RunSimulateWMRules) (Check RunCheckWMRules doc (* Rules to Check a washing machine.)) ...]} When an instance of the class {lisp WashingMachine} receives a {lisp Simulate} message, the RuleSet {lisp SimulateWMRules} will be invoked with the instance as its work space. To simplify the definition of RuleSets intended to be used as Methods, the function {fn DefRSM} (for "Define Rule Set as a Method") is provided: {FnDef {Name DefRSM} {Args ClassName Selector RuleSetName} {Text If the optional argument {arg RuleSetName} is given, {fn DefRSM} installs that RuleSet as a method using the {arg ClassName} and {arg Selector}. It does this by automatically generating an installation function as a method to invoke the RuleSet. {fn DefRSM} automatically documents the installation function and the method. If the argument {arg RuleSetName} is {lisp NIL}, then {fn DefRSM} creates the RuleSet object, puts the user into an Editor to enter the rules, compiles the rules into a LISP function, and installs the RuleSet as before. }} }{End SubSec Installing RuleSets as Methods} {Begin SubSec Installing RuleSets in ActiveValues} {Title Installing RuleSets in ActiveValues} {Text RuleSets can also be used in data-oriented programming so that they are invoked when data is accessed. To use a RuleSet as a {arg getFn}, the function {fn RSGetFn}{index RSGetFn Fn} is used with the property {lisp RSGet}{index RSGet Prop} as follows: {lispcode ... (InstanceVariables ({arg myVar} #({arg myVal} RSGetFn NIL) RSGet {arg RuleSetName})) ...} {fn RSGetFn} is a Loops system function that can be used in an active value to invoke a RuleSet in response to a Loops get operation (e.g., {fn GetValue}) is performed. It requires that the name of the RuleSet be found on the {lisp RSGet} property of the item. {fn RSGetFn} activates the RuleSet using the local state as the work space. The value returned by the RuleSet is returned as the value of the get operation. To use a RuleSet as a {arg putFn}, the function {fn RSPutFn}{index RSPutFn Fn} is used with the property {lisp RSPut}{index RSPut Prop} as follows: {lispcode ... (InstanceVariables ({arg myVar} #({arg myVal} NIL RSPutFn) RSPut {arg RuleSetName})) ...} {fn RSPutFn} is a function that can be used in an active value to invoke a RuleSet in response to a Loops put operation (e.g., {fn PutValue}). It requires that the name of the RuleSet be found on the {lisp RSPut} property of the item. {fn RSGetFn} activates the RuleSet using the {arg newValue} from the put operation as the work space. The value returned by the RuleSet is put into the local state of the active value. }{End SubSec Installing RuleSets in ActiveValues} {Begin SubSec Tracing and Breaking RuleSets} {Title Tracing and Breaking RuleSets} {Text Loops provides breaking and tracing facilities to aid in debugging RuleSets. These can be used in conjunction with the auditing facilities and the rule executive for debugging RuleSets. {FigureRef compileroptions} summarizes the compiler options for breaking and tracing: {Begin Figure} {Tag compileroptions} {Text {Begin LabeledList} {Label {lisp T}{index T (RuleSet Compiler Option)}} {Text Trace if rule is satisfied. Useful for creating a running display of executed rules.} {Label {lisp TT}{index TT (RuleSet Compiler Option)}} {Text Trace if rule is tested.} {Label {lisp B}{index B (RuleSet Compiler Option)}} {Text Break if rule is satisfied.} {Label {lisp BT}{index BT (RuleSet Compiler Option)}} {Text Break if rule is tested. Useful for stepping through the execution of a RuleSet.} {End LabeledList} } {Caption Compiler options for Breaking and Tracing the execution of RuleSets. } {End Figure} Specifying the declaration {lisp Compiler Options: T;} in a RuleSet indicates that tracing information should be displayed when a rule is satisfied. To specify the tracing of just an individual rule in the RuleSet, the {lisp T} meta-descriptions should be used as follows: {lispcode {T} IF {arg cond} THEN {arg action};} This tracing specification causes Loops to print a message whenever the LHS of the rule is tested, or the RHS of the rule is executed. It is also possible to specify that the values of some variables (and compound literals) are to be printed when a rule is traced. This is done by listing the variables in the {lisp Debug Vars} declaration in the RuleSet: {lispcode Debug Vars: a a:b a:b.c;} This will print the values of {lisp a}, {lisp a:b}, and {lisp a:b.c} when any rule is traced or broken. Analogous specifications are provided for breaking rules. For example, the declaration {lisp Compiler Options: B;} indicates that Loops is to enter the rule executive (see next section) after the LHS is satisfied and before the RHS is executed. The rule-specific form: {lispcode {B} IF {arg cond} THEN {arg action};} indicates that Loops is to break before the execution of a particular rule. Sometimes it is convenient in debugging to display the source code of a rule when it is traced or broken. This can be effected by using the {lisp PR}{index PR (RuleSet Compiler Option)} compiler option as in {lispcode Compiler Options: T PR;} which prints out the source of a rule when the LHS of the rule is tested and {lispcode Compiler Options: B PR;} which prints out the source of a rule when the LHS of a rule is satisfied, and before entering the break. }{End SubSec Tracing and Breaking RuleSets} {Begin SubSec The Rule Exec} {Title The Rule Exec} {Text A Read-Compile-Evaluate-Print loop, called the rule executive, is provided for the rule language. The rule executive can be entered during a break by invoking the LISP function {fn RE}.{index RE Fn} During RuleSet execution, the rule executive can be entered by typing {lisp ^f} (-f) on the keyboard. On the first invocation, {fn RE} prompts the user for a window. It then displays a stack of RuleSet invocations in a menu to the left of this window in a manner similar to the Interlisp-D Break Package. Using the left mouse button in this window creates an Inspector window for the work space for the RuleSet. Using the middle mouse button pretty prints the RuleSet in the default prettyprint window. In the main rule executive window, {fn RE} prompts the user with "{lisp re:}". Anything in the rule language (other than declarations) that is typed to this executive will be compiled and executed immediately and its value printed out. For example, a user may type rules to see whether they execute or variable names to determine their values. For example: {lispcode re: trafficLight:color Red re:} this example shows how to get the value of the {lisp color} variable of the {lisp trafficLight} object. If the value of a variable was set by a RuleSet running with auditing, then a {lisp why} question can be typed to the rule executive as follows: {lispcode re: why trafficLight:color IF highLight:color = 'Green farmRoadSensor:cars timer.TL THEN highLight:color _ 'Yellow timer.Start; Rule 3 of RuleSet LightRules Edited: Conway "13-Oct-82" re:} The rule executive may be exited by typing {lisp OK}. }{End SubSec The Rule Exec} {Begin SubSec Auditing RuleSets} {Title Auditing RuleSets} {Text Two declarations at the beginning of a RuleSet affect the auditing. Auditing is turned on by the compiler option {lisp A}.{index A (RuleSet Compiler Option)} The simplest form of this is {lispcode Compiler Options: A;} The {lisp Audit Class} declaration indicates the class of the audit record to be used with this RuleSet if it is compiled in {it audit} mode. {lispcode Audit Class: StandardAuditRecord;} A {lisp Meta Assignments} declaration can be used to indicate the audit description to be used for the rules unless overridden by a rule-specific meta-assignment statement in braces. {lispcode Meta Assignments: ( cf_.5 support_'GroundWff);} }{End SubSec Auditing RuleSets} }{End SubSec Using Rules in LOOPS}