{Begin SubSec The Rule Language} {Title The Rule Language} {Text {Begin SubSec Rule Forms} {Title Rule Forms} {Text {Tag MetaDescriptions} A rule in Loops describes actions to be taken when specified conditions are satisfied. A rule has three major parts called the {it left hand side}{index left hand side} (LHS){index LHS} for describing the conditions, the {it right hand side}{index right hand side} (RHS){index RHS} for describing the actions, and the {it meta-description}{index meta-description} (MD){index MD} for describing the rule itself. In the simplest case without a meta-description, there are two equivalent syntactic forms: {lispcode {arg LHS} -> {arg RHS};} {lispcode IF {arg LHS} THEN {arg RHS};} The {lisp If} and {lisp Then} tokens are recognized in several combinations of upper and lower case letters. The syntax for LHSs and RHSs is given below. In addition, a rule can have no conditions (meaning always perform the actions) as follows: {lispcode -> {arg RHS};} {lispcode if T then {arg RHS};} Rules can be preceded by a meta-description in braces as in: {lispcode {bracket {arg MD}} {arg LHS} -> {arg RHS};} {lispcode {bracket {arg MD}} If {arg LHS} Then {arg RHS};} {lispcode {bracket {arg MD}} {arg RHS};} Examples of meta-information include rule-specific control information, rule descriptions, audit instructions, and debugging instructions. For example, the syntax for one-shot rules shown on {PageRef Tag OneShotRules}: {lispcode {1} IF {arg condition{sub 1}} {arg condition{sub 2}} THEN {arg action{sub 1}};} is an example of a meta-description. Another example is the use of meta-assignment statements for describing audit trails and rules. These statements are discussed on {PageRef Tag MetaAssignments}. {it LHS Syntax:} The clauses on the LHS of a rule are evaluated in order from left to right to determine whether the LHS is satisfied. If they are all satisfied, then the rule is satisfied. For example: {lispcode A B C+D (Prime D) -> {arg RHS};} In this rule, there are four clauses on the LHS. If the values of some of the clauses are {lisp NIL} during evaluation, the remaining clauses are not evaluated. For example, if {lisp A} is non-{lisp NIL} but {lisp B} is {lisp NIL}, then the LHS is not satisfied and {lisp C+D} will not be evaluated. {it RHS Syntax:} The RHS of a rule consists of actions to be performed if the LHS of the rule is satisfied. These actions are evaluated in order from left to right. Actions can be the invocation of RuleSets, the sending of Loops messages, Interlisp function calls, variables, or special termination actions. RuleSets always return a value. The value returned by a RuleSet is the value of the last rule that was executed. Rules can have multiple actions on the right hand side. Unless there is a {lisp Stop} statement or transfer call as described later, the value of a rule is the value of the last action. When a rule has no actions on its RHS, it returns {lisp NIL} as its value. {it Comments:} Comments can be inserted between rules in the RuleSet. They are enclosed in parentheses with an asterisk for the first character as follows: {lispcode (* This is a comment)} }{End SubSec Rule Forms} {Begin SubSec Kinds of Variables} {Title Kinds of Variables} {Text Loops distinguishes the following kinds of variables: {it RuleSet arguments:} All RuleSets have the variable {var self}{index self Var} as their workspace. References to {var self} can often be elided in the RuleSet syntax. For example, the expression {lisp self.Print} means to send a {lisp Print} message to {lisp self}. This expression can be shortened to {lisp .Print} . Other arguments can be defined for RuleSets. These are declared in an {lisp Args:} declaration. {it Instance variables:} All RuleSets use a Loops object for their workSpace. In the LHS and RHS of a rule, the first interpretation tried for an undeclared literal is as an instance variable in the work space. Instance variables can be indicated unambiguously by preceding them with a colon, (e.g., {lisp :{arg varName}} or {lisp {arg obj}:{arg varName}}). {it Class variables:} Literals can be used to refer to class variables of Loops objects. These variables must be preceded by a double colon in the rule language, (e.g., {lisp ::{arg classVarName}} or {lisp {arg obj}::{arg classVarName}}). {it Temporary variables:} Literals can also be used to refer to temporary variables allocated for a specific invocation of a RuleSet. These variables are initialized to {lisp NIL} when a RuleSet is invoked. Temporary variables are declared in the {lisp Temporary Vars} declaration in a RuleSet. {it Task variables:} [not implemented yet.] Task variables are used for saving information state information related to particular invocations of RuleSets. Unlike temporary variables which are reset to {lisp NIL} at the beginning of RuleSet execution, Task variables are associated with Task objects and keep their values indefinitely. Task variables are used to hold information about a computational process, such as indices for generator Tasks. Task variables are declared indirectly -- they are the instance variables of the class declared as the {it Task Class}{index Task Class} of the RuleSet. {it Audit record variables:} Literals can also be used to refer to instance variables of audit records created by rules. These literals are used only in {it meta-assignment} statements in the MD part of a rule. They are used to describe the information saved in audit records, which can be created as a side-effect of rule execution. These variables are ignored if a RuleSet is not compiled in {it audit} mode. Undeclared variables appearing on the left side of assignment statements in the MD part of a rule are treated as audit record variables by default. These variables are declared indirectly -- they are the instance variables of the class declared as the {it Audit Class}{index Audit Class} of the RuleSet. {it Rule variables:} [Not implemented yet.] Literals can also be used to hold descriptions of the rules themselves. These variables are used only in {it meta-assignment} statements in the MD part of a rule. They describe information to be saved in the rule objects, which are created as a side-effect of RuleSet compilation. Rule variables are declared indirectly -- they are the instance variables in the {it Rule Class}{index Rule Class} declaration. {it Interlisp variables:} Literals can also be used to refer to Interlisp variables during the invocation of a RuleSet. These variables can be global to the Interlisp environment, or are bound in some calling function. Interlisp variables can be used when procedure-oriented and rule-oriented programs are intermixed. Interlisp variables must be preceded by a backSlash in the syntax of the rule language (e.g., {lisp \{arg lispVarName}}). {it Reserved Words:} The following literals are treated as {it read-only} variables with special interpretations: {VarDef {Name self} {Text The current work space. }} {VarDef {Name rs} {Text The current RuleSet. }} {VarDef {Name task} {Text The Task representing the current invocation of this RuleSet. }} {VarDef {Name caller} {Text The RuleSet that invoked the current RuleSet, or {lisp NIL} if invoked otherwise. }} {VarDef {Name ruleApplied} {Text Set to {lisp T} if some rule was applied in this cycle. (For use only in while-conditions). }} The following reserved words are intended mainly for use in creating audit trails: {VarDef {Name ruleObject} {Text Variable bound to the object representing the rule itself. }} {VarDef {Name ruleNumber} {Text Variable bound to the sequence number of the rule in a RuleSet. }} {VarDef {Name ruleLabel} {Text Variable bound to the label of a rule or {lisp NIL}. }} {note *** The following three reserved words are not implemented yet. ***} {VarDef {Name reasons} {Text Variable bound a list of audit records supporting the instance variables mentioned on the LHS of the rule. (Computed at run time.) }} {VarDef {Name auditObject} {Text Variable bound to the object to which the reason record will be attached. (Computed at run time.) }} {VarDef {Name auditVarName} {Text Variable bound to the name of the variable on which the reason will be attached as a property. }} {it Other Literals:} As described later, literals can also refer to Interlisp functions, Loops objects, and message selectors. They can also be used in strings and quoted constants. The determination of the meaning of a literal is done at compile time using the declarations and syntax of RuleSets. The characters used in literals are limited to alphabetic characters and numbers. The first character of a literal must be alphabetic. The syntax of literals also includes a compact notation for sending unary messages and for accessing instance variables of Loops objects. This notation uses {it compound literals}.{index compound literals} A compound literal is a literal composed of multiple parts separated by a periods, colons, and commas. }{End SubSec Kinds of Variables} {Begin SubSec Constants} {Title Rule Forms} {Text {it Quoted Constants:} The quote sign is used to indicate constant literals: {lispcode a b=3 c='open d=f e='(This is a quoted expression) -> ...} In this example, the LHS is satisfied if {lisp a} is non-{lisp NIL}, and the value of {lisp b} is 3, and the value of {lisp c} is exactly the atom {lisp open}, the value of {lisp d} is the same as the value of {lisp f}, and the value of {lisp e} is the list {lisp (This is a quoted expression)}. {it Strings:} The double quote sign is used to indicate string constants: {lispcode IF a b=3 c='open d=f e=="This is a string" THEN (WRITE "Begin configuration task") ... ;} In this example, the LHS is satisfied if {lisp a} is non-{lisp NIL}, and the value of {lisp b} is 3, and the value of {lisp c} is exactly the atom {lisp open}, the value of {lisp d} is the same as the value of {lisp f}, and the value of {lisp e} equal to the string {lisp "This is a string"}. {it Interlisp Constants:} The literals {lisp T} and {lisp NIL} are interpreted as the Interlisp constants of the same name. {lispcode a (Foo x NIL b) -> x←T ...;} In this example, the function {lisp Foo} is called with the arguments {lisp x}, {lisp NIL}, and {lisp b}. Then the variable {lisp x} is set to {lisp T}. }{End SubSec Constants} {Begin SubSec Infix Operators and Brackets} {Title Infix Operators and Brackets} {Text To enhance the readability of rules, a few infix operators are provided. The following are infix binary operators in the rule syntax: {Def {Type (Rule Infix Operator)} {Name +} {Text Addition. }} {Def {Type (Rule Infix Operator)} {Name ++} {Text Addition modulo 4. }} {Def {Type (Rule Infix Operator)} {Name -} {Text Subtraction. }} {Def {Type (Rule Infix Operator)} {Name -{rm}-} {Text Subtraction modulo 4. }} {Def {Type (Rule Infix Operator)} {Name *} {Text Multiplication. }} {Def {Type (Rule Infix Operator)} {Name /} {Text Division. }} {Def {Type (Rule Infix Operator)} {Name >} {Text Greater than. }} {Def {Type (Rule Infix Operator)} {Name <} {Text Less than. }} {Def {Type (Rule Infix Operator)} {Name >=} {Text Greater than or equal. }} {Def {Type (Rule Infix Operator)} {Name <=} {Text Less than or equal. }} {Def {Type (Rule Infix Operator)} {Name =} {Text {fn EQ} -- simple form of equals. Works for atoms, objects, and small integers. }} {Def {Type (Rule Infix Operator)} {Name ~=} {Text {fn NEQ}. (Not {fn EQ}.) }} {Def {Type (Rule Infix Operator)} {Name ==} {Text {fn EQUAL} -- long form of equals. }} {Def {Type (Rule Infix Operator)} {Name <<} {Text Member of a list. ({fn FMEMB}) }} In addition, the rule syntax provides two unary operators as follows: {Def {Type (Rule Unary Operator)} {Name -} {Text Minus. }} {Def {Type (Rule Unary Operator)} {Name ~} {Text Not. }} The precedence of operators in rule syntax follows the usual convention of programming languages. For example {lispcode 1+5*3 = 16} and {lispcode [3 < 2 + 4] = T} Brackets can be used to control the order of evaluation: {lispcode [1+5]*3 = 18} {it Ambiguity of the minus sign:} Whenever there is an ambiguity about the interpretation of a minus sign as a unary or binary operator, the rule syntax interprets it as a binary minus. For example {lispcode a-b c d -e [-f] (g -h) (← $Foo Move -j) -> ...} In this example, the first and second minus signs are both treated as binary subtraction statements. That is, the first three clauses are (1) {lisp a-b}, (2) {lisp c} and (3) {lisp d-e}. Because the rule syntax allows arbitary spacing between symbols and there is no syntax to separate clauses on the LHS of a rule, the interpretation of "{lisp d -e}" is as a single clause (with the subtraction) instead of two clauses. To force the interpretation as a unary minus operator, one must use brackets as illustrated in the next clause. In this clause, the minus sign in the clause {lisp [-f]} is treated as a unary minus because of the brackets. The minus sign in the function call {lisp (g -h)} is treated as unary because there is no preceding argument. Similarly, the {lisp -j} in the message expression is treated as unary because there is no preceding argument. }{End SubSec Infix Operators and Brackets} {Begin SubSec Interlisp Functions and Message Sending} {Title Interlisp Functions and Message Sending} {Text Calls to Interlisp functions are parenthesized with the function name as the first literal after the left parenthesis. Each expression after the function name is treated as an argument to the function. For example: {lispcode a (Prime b) [a -b] -> c (Display b c+4 (Cursor x y) 2) ;} In this example, {lisp Prime}, {lisp Display}, and {lisp Cursor} are interpreted as the names of Interlisp functions. Since the expression {lisp [a -b]} is surrounded by brackets instead of parentheses, it is recognized as meaning {lisp a} minus {lisp b} as opposed to a call to the function {lisp a} with the argument minus {lisp b}. In the example above, the call to the Interlisp function {lisp Display} has four arguments: {lisp b}, {lisp c+4}, the value of the function call {lisp (Cursor x y)}, and {lisp 2}. The use of Interlisp functions is usually outside the spirit of the rule language. However, it enables the use of Boolean expressions on the LHS beyond simple conjunctions. For example: {lispcode a (OR (NOT b) x y) z -> ... ;} {it Loops Objects and Message Sending:} Loops classes and other named objects can be referenced by using the dollar notation. The sending of Loops messages is indicated by using a left arrow. For example: {lispcode IF cell←(← $LowCell Occupied? 'Heavy) THEN (← cell Move 3 'North);} In the LHS, an {lisp Occupied?} message is sent to the object named {lisp LowCell}. In the message expression on the RHS, there is no dollar sign preceding {lisp cell}. Hence, the message is sent to the object that is the value of the variable {lisp cell}. For unary messages (i.e., messages with only the selector specified and the implicit argument {lisp self}), a more compact notation is available as described selow. {it Unary Message Sending:} When a period is used as the separator in a compound literal, it indicates that a unary message is to be sent to an object. (We will alternatively refer to a period as a {it dot}.){index dot} For example: {lispcode tile.Type='BlueGreenCross command.Type='Slide4 -> ... ;} In this example, the object to receive the unary message {lisp Type} is referenced indirectly through the {lisp tile} instance variable in the work space. The left literal is the variable {lisp tile} and its value must be a Loops object at execution time. The right literal must be a method selector for that object. The dot notation can be combined with the dollar notation to send unary messages to named Loops objects. For example, {lispcode $Tile.Type='BlueGreenCross ...} In this example, a unary {lisp Type} message is sent to the Loops object whose name is {lisp Tile}. The dot notation can also be used to send a message to the work space of the RuleSet, that is, {lisp self}. For example, the rule {lisp IF scale>7 THEN .DisplayLarge;} would cause a {lisp DisplayLarge} message to be sent to {lisp self}. This is an abbreviation for {lispcode IF scale>7 THEN self.DisplayLarge;} }{End SubSec Interlisp Functions and Message Sending} {Begin SubSec Variables and Properties} {Title Variables and Properties} {Text When a single colon is used in a literal, it indicates access to an instance variable of an object. For example: {lispcode tile:type='BlueGreenCross command:type=Slide4 -> ... ;} In this example, access to the Loops object is indirect in that it is referenced through an instance variable of the work space. The left literal is the variable {lisp tile}, and its value must be a Loops object when the rule is executed. The right literal {lisp type} must be the name of an instance variable of that object. The compound literal {lisp tile:type} refers to the value of the {lisp type} instance variable of the object in the instance variable {lisp tile}. The colon notation can be combined with the dollar notation to access a variable in a named Loops object. For example, {lispcode $TopTile:type='BlueGreenCross ...} refers to the {lisp type} variable of the object whose Loops name is {lisp TopTile}. A double colon notation is provided for accessing class variables. For example {lispcode truck::MaxGas<45 ::ValueAdded>600 -> ... ;} In this example, {lisp MaxGas} is a class variable of the object bound to {lisp truck}. {lisp ValueAdded} is a class variable of {lisp self}. A colon-comma notation is provided for accessing property values of class and instance variables. For example {lispcode wire:,capacitance>5 wire:voltage:,support='simulation -> ... } In the first clause, {lisp wire} is an instance variable of the work space and {lisp capacitance} is a property of that variable. The interpretation of the second clause is left to right as usual: (1) the object that is the value of the variable {lisp wire} is retrieved, and (2) the {lisp support} property of the {lisp voltage} variable of that object is retrieved. For properties of class variables {lispcode ::Wire:,capacitance>5 node::Voltage:,support='simulation -> ... } In the first clause, {lisp wire} is a class variable of the work space and {lisp capacitance} is a property of that variable. In the second clause, {lisp node} is an instance variable bound to some object. {lisp Voltage} is a class variable of that object, and {lisp Support} is a property of that class variable. {note In the next version we may move to a comma-comma notation for properties. Comments are solicited.} The property notation is illegal for ruleVars and lispVars since those variables cannot have properties. {note In general, property values are used to provide an extra level of information about values of variables. See the Loops manual for a discussion of their use.} }{End SubSec Variables and Properties} {Begin SubSec Perspectives} {Title Perspectives} {Text {index perspectives} * * * Not implemented yet in the rule language * * * In many cases it is useful to organize information in terms of multiple points of view. For example, information about a man might be organized in terms of his role as a {it father}, as an {it employee}, and as a {it traveler}. Each point of view, called a {it perspective}, contains information for a different purpose. The perspectives are related to each other in the sense that they collectively provide information about the same object. As described in the Loops manual, Loops supports this organizational metaphor by providing special mixin classes called {lisp perspectives} and {lisp nodes}. Loops perspectives can be accessed in the rule language by using a comma notation. In the following rule, the variable {lisp washingMachine} is bound to an object with three perspectives: {lisp commodity}, {lisp electrical}, and {lisp cleaning}. The rule accesses the {lisp voltage} variable of the object that is the {lisp electrical} perspective. {lispcode IF washingMachine,electrical:voltage<100 THEN ....} In this syntax, the term before the comma names a variable, and the term after the comma is the name of the perspective. }{End SubSec Perspectives} {Begin SubSec Computing Selectors and Variable Names} {Title Computing Selectors and Variable Names} {Text The short notations for instance variables, properties, perspectives, and unary messages all show the selector, variable, and perspective names {it as they actually appear} in the object. {lispcode {arg object}.{arg selector} {arg object}:{arg ivName} {arg object}::{arg cvName} {arg object}:{arg varname}:,{arg propName} {arg object},{arg perspName} (← {arg object} {arg selector} {arg arg{sub 1}} {arg arg{sub 2}})} For example, {lispcode apple:flavor} refers to the {lisp flavor} instance variable of the object bound to the variable {lisp apple}. In Interlisp terminology, this implies implicit quoting of the name of the instance variable ({lisp flavor}). In some applications it is desired to be able to compute the names, For this, the Loops rule language provides analogous notations with an added exclamation sign. After the exclamation sign, the interpretation of the variable being evaluated starts over again. For example {lispcode apple:!\x} refers to the same thing as {lisp apple:flavor} if the Interlisp variable {lisp x} is bound to {lisp flavor}. The fact that {lisp x} is a Lisp variable is indicated by the backSlash. If {lisp x} is an instance variable of {lisp self} or a temporary variable, we could use the notation: {lispcode apple:!x} If {lisp x} is a class variable of {lisp self}, we could use the notation: {lispcode apple:!::x} All combinations are possible, including: {lispcode {arg object}.!{arg selector} {arg object}.!\{arg selector} {arg object}.!::{arg selector} {arg object}:!{arg ivName} {arg object}::!{arg cvName} {arg object}:!{arg varname}:,{arg propName} {arg object},!{arg perspName} (←! {arg object} {arg selector} {arg arg{sub 1}} {arg arg{sub 2}})} }{End SubSec Computing Selectors and Variable Names} {Begin SubSec Recursive Compound Literals} {Title Recursive Compound Literals} {Text Multiple colons or periods can be used in a literal, For example: {lispcode a:b:c} means to (1) get the object that is the value of {lisp a}, (2) get the object that is the value of the {lisp b} instance variable of {lisp a}, and finally (3) get the value of the {lisp c} instance variable of that object. Similarly, the notation {lispcode a.b:c} means to get the {lisp c} variable of the object returned after sending a {lisp b} message to the object that is the value of the variable {lisp a}. Again, the operations are carried out left to right: (1) the object that is the value of the variable {lisp a} is retrieved, (2) it is sent a {lisp b} message which must return an object, and then (3) the value of the {lisp c} variable of that object is retrieved. Compound literal notation can be nested arbitrarily deeply. }{End SubSec Recursive Compound Literals} {Begin SubSec Assignment Statements} {Title Assignment Statements} {Text An assignment statement using a left arrow can be used for setting all kinds of variables. For example, {lispcode x←a;} sets the value of the variable {lisp x} to the value of {lisp a}. The same notation works if {lisp x} is a task variable, rule variable, class variable, temporary variable, or work space variable. The right side of an assignment statement can be an expression as in: {lispcode x←a*b + 17*(LOG d);} The assignment statement can also be used with the colon notation to set values of instance variables of objects. For example: {lispcode y:b←0 ;} In this example, first the object that is the value of {lisp y}is computed, then the value of its instance variable {lisp b} is set to {lisp 0}. {it Properties and perspectives:} Assignment statements can also be used to set property values as in: {lispcode box:x:,origin←47 fact:,reason←currentSupport;} or variables of perspectives as in:. {lispcode washingMachine,electrical:voltage←110;} {it Nesting:} Assignment statements can be nested as in {lispcode a←b←c:d←3;} This statement sets the values of {lisp a}, {lisp b}, and the {lisp d} instance variable of {lisp c} to {lisp 3}. The value of an assignment statement itself is the new assigned value. }{End SubSec Assignment Statements} {Begin SubSec Meta-Assignment Statements} {Title Meta-Assignment Statements} {Text {Tag MetaAssignments} Meta-assignment statements are assignment statements used for specifying rule descriptions and audit trails. These statements appear in the MD part of rules. {it Audit Trails:} The default interpretation of meta-assignment statements for undeclared variables is as audit trail specifications. Each meta-assignment statement specifies information to be saved in audit records when a rule is applied. In the following example from {FigureRef EvaluateWashingMachine}, the audit record must have variables named {lisp basis} and {lisp cf}: {lispcode {(basis←'Fact cf←1)} IF buyer:familySize>2 machine:capacity<20 THEN suitability←'Poor;} In this example, the RHS of the rule assigns the value of the work space instance variable {lisp suitability} to {lisp 'Poor} if the conditions of the rule are satisfied. In addition, if the RuleSet was compiled in {it audit} mode, then during RuleSet execution an audit record is created as a side-effect of the assignment. The audit record is attached to the {lisp reason} property of the suitability variable. It has instance variables {lisp basis} and {lisp cf}. In general, an audit description consists of a sequence of meta-assignment statements. The assignment variable on the left must be an instance variable of the audit record. The class of the audit record is declared in the {it Audit Class}{index Audit Class} declaration of the RuleSet. The expression on the right is in terms of the variables accessible by the RuleSet. If the conditions of a rule are satisfied, an audit record is instantiated. Then the meta-assignment statements are evaluated in the execution context of the RuleSet and their values are put into the audit record. A separate audit record is created for each of the object variables that are set by the rule. {note In the current implementation, the RHS of a meta-assignment statement cannot be an expression; it must be either a constant or a variable to be evaluated after the LHS of the rule is evaluated and before the RHS of the rule is applied.} {note * * * Rule Descriptions are not implemented yet * * *} {it Rule Descriptions:} Meta-assignment statements can also be used to set variables in the objects that represent individual rules. This interpretation of meta-assignment statements is indicated when the assignment variable of the meta-assignment statement has been declared to be a rule variable. For example, if the variable {lisp cf} in the previous example was declared to be a rule variable, then the meta-assignment statement would set the {lisp cf} instance variable of the rule object to {lisp .5} at compilation time, instead of saving a {lisp cf} in every audit record for every rule application at execution time. The value on the right hand side of the meta-assignment statement for a rule variable must be known at compile time. }{End SubSec Meta-Assignment Statements} {Begin SubSec Push and Pop Statements} {Title Push and Pop Statements} {Text A compact notation is provided for pushing and popping values from lists. To push a new value onto a list, the notation {lisp ←+}{index ←+ (Rule Infix Operator)} is used: {lispcode myList←+newItem;} {lispcode focus:goals←+newGoal;} To pop an item from a list, the {lisp ←-}{index ←- (Rule Infix Operator)} notation is used: {lispcode item←-myList;} {lispcode nextGoal←-focus:goals;} As with the assignment operator, the push and pop notation works for all kinds of variables and properties. They can be used in conjunction with infix operator {lisp <<} for membership testing. }{End SubSec Push and Pop Statements} {Begin SubSec Invoking RuleSets} {Title Invoking RuleSets} {Text One of the ways to cause RuleSets to be executed is to invoke them from rules. This is used on the LHS of rules to express predicates in terms of RuleSets, and on the RHS of rules to express actions in terms of RuleSets. A short double-dot syntax for this is provided that invokes a RuleSet on a work space: {lispcode Rs1..ws1} In this example, the RuleSet bound to the variable {lisp Rs1} is invoked with the value of the variable {lisp ws1} as its work space. The value of the invocation expression is the value returned by the RuleSet. The double-dot syntax can be combined with the dollar notation to invoke a RuleSet by its Loops name, as in {lispcode $MyRules..ws1} which invokes the RuleSet object that has the Loops name {lisp MyRules}. This form of RuleSet invocation is like subroutine calling, in that it creates an implicit stack of arguments and return addresses. This feature can be used as a mechanism for {it meta-control} of RuleSets as in: {lispcode IF breaker:status='Open THEN source←$OverLoadRules..washingMachine; IF source='NotFound THEN $ShortCircuitRules..washingMachine;} In this example, two "meta-rules" are used to control the invocation of specialized RuleSets for diagnosing overloads or short circuits. }{End SubSec Invoking RuleSets} {Begin SubSec Transfer Calls} {Title Transfer Calls} {Text {note * * * Not implemented in StopGap version. * * *} An important optimization in many recursive programs is the elimination of tail recursion. For example, suppose that the RuleSet A calls B, B calls C, and C calls A recursively. If the first invocation of A must do some more work after returning from B, then it is useful to save the intermediate states of each of the procedures in frames on the calling stack. For such programs, the space allocation for the stack must be enough to accommodate the maximum depth of the calls. There is a common and special case, however, in which it is unnecessary to save more than one frame on the stack. In this case each RuleSet has no more work to do after invoking the other RuleSets, and the value of each RuleSet is the value returned by the RuleSet that it invokes. RuleSet invocation in this case amounts to the evaluation of arguments followed by a direct transfer of control. We call such invocations transfer calls. The Loops rule language extends the syntax for RuleSet invocation and message sending to provide this as follows: {lispcode RS..*ws} The RuleSet {lisp RS} is invoked on the work space {lisp ws}. With transfer calls, RuleSet invocations can be arbitrarily deep without using proportional stack space. }{End SubSec Transfer Calls} {Begin SubSec Task Operations} {Title Task Operations} {Text {note * * * Task Operations are not fully implemented yet. * * *} Tasks in the Loops rule language represent the invocation of RuleSets. They provide a mechanism for specifying and controlling processes in terms of tasks that can be created, started, suspended, and restarted. They also provide a handle for specifying concurrent processing. A Task records the work space of a RuleSet ({lisp ws}), the value returned ({lisp value}), and two special variables called the {lisp status} and {lisp reason}. A Task can also have RuleSet-specific instance variables called task variables for saving process information. {it Creating Tasks:} A Task is represented as a Loops object and can be created and associated with a work space as follows: {lispcode Task6←(← $Task New {arg RuleSet} {arg workSpace})} The {arg workSpace} argument is optional. Specialized versions of {lisp Task} will eventually be available, such as {lisp RemoteTask}, Information about a Task is stored in its instance variables, and can be accessed like other Loops variables: {lispcode Task6:status Task6:reason Task6:ws Task6:value} {it Starting Tasks:} The primary operations on Tasks are starting them and waiting for them to finish execution. These operations have been designed to work when Loops is extended for concurrent processing. The operations for starting tasks are as follows: {FnDef {Name Start1} {Args taskList} } {FnDef {Name StartAll} {Args taskList} } {FnDef {Name StartAll} {Args taskList} {Text Each of the start operations takes an argument {arg taskList} which is either a Task object, or a list of Task objects. A Task cannot be started if it is already running, as indicated by its {lisp status} variable. {fn Start1} iterates through its {arg taskList} and starts the first Task that is not already running. The value of {arg Start1} is the Task that was started. {fn StartAll} starts all of the tasks, and does not return control until all of the tasks have been started. {fn StartTogether} is like {fn StartAll} except that none of the tasks are started until all of them are ready. The synchronization aspect of {fn StartTogether} is important for avoiding Task deadlock situations in programs that share Tasks as resources. (It avoids the difficulties associated with partial allocation of Tasks when a complete set of Tasks is needed.) }} {it Waiting for Tasks:} The following operations are provided for waiting for Tasks: {FnDef {Name Wait1} {Args taskList} } {FnDef {Name WaitAll} {Args taskList} {Text {fn Wait1} iterates through its {arg taskList} and returns as its value the first Task that is not running. {fn WaitAll} returns when all of its Tasks have finished running The value returned by the RuleSet that ran in a Task can be obtained from the Task object, as in: {lispcode task6:value.} }} {it Running Tasks:} In many cases, the specification of Task control can be simplified by using a {it run} operation that combines the start and wait operations. The run operations are as follows: {FnDef {Name Run1} {Args taskList} } {FnDef {Name RunAll} {Args taskList} } {FnDef {Name RunTogether} {Args taskList} {Text {fn Run1} goes through its arguments left to right and selects the first Task that is not running. It starts that Task and then waits for it to complete. The value of {fn Run1} is the Task that was executed. {fn RunAll} starts all of the Tasks running and then waits for them all to complete. {fn RunTogether} waits for all of the Tasks to become available, runs them all, and then waits for them all to complete. }} }{End SubSec Task Operations} {Begin SubSec Stop Statements} {Title Stop Statements} {Text At invocation, the {lisp status} in the Task is set to {lisp Running}. If a RuleSet ends normally, the {lisp status} in the Task is set to {lisp Done} and the {lisp reason} saved in the RuleStep is {lisp Success}. Other terminations can be specified in a Stop statement as follows: {Def {Type (RuleSet Statement)} {Name Stop} {Args value status reason} {PrintName {lisp (Stop {arg value} {arg status} {arg reason})}} {Text {arg value} is the value to be returned by the RuleSet, {arg status} characterizes the termination of the Task, and {arg reason} is a symbolic reason for the status. Typical examples of the use of Stop are: {lispcode (Stop {arg value} 'Aborted {arg reason}) (Stop {arg value} 'Suspended {arg reason})} where {lisp Aborted} means that the RuleSet has failed, and {lisp Suspended} means that the RuleSet has stopped but may be re-invoked. Particular applications will probably develop standardized notations for status and reason. Values for these can be Interlisp atoms or Loops objects. The arguments {arg status} and {arg reason} are optional in a {lisp Stop} statement. }} }{End SubSec Stop Statements} }{End SubSec The Rule Language}