Copyright © 2006-2010 authors and contributors. All rights reserved.
NRL, as specified in the main NRL specification, is a language for constraining data models. Basic NRL rules serve to specify additional constraints for UML models, XML schema, and so on. The action language is an extension of the NRL that enables rule writers to specify certain actions, like creation of objects, deletion of objects and setting values, that should take place when conditions hold.
This document is a specification of the Natural Rule Language Action Language.
1 Introduction
1.1 Target Audience
1.2 Layout
2 Language Definition
2.1 Action Rules
2.2 Action Fragments
2.3 Complex Actions
2.4 Simple Actions
A Concrete Syntax
B Abstract Syntax
C References
C.1 Normative References
D Acknowledgments (Non-Normative)
E Change History (Non-Normative)
The Natural Rule Language [NRL] is a constraint language that enables rule writers to constraint business models, supplied as UML models, XML Schemas or other suitably structured formats, using a simple English-like language. This specification extends the Natural Rule Language, enabling writes to specify what actions to take when certain conditions hold. This includes, amongst others that will be discussed in the document body, the ability to:
Create new entities
Remove existing entities
Modify attribute values
This extension makes the NRL Action language suitable for common data manipulation tasks such as: mapping between models, enriching XML messages, capturing explicit rules for manipulating objects in memory, or expressing business rules for execution on a rule engine. Like the remainder of NRL, the action language is designed to be easy to read and, importantly, straight-forward to map to execution languages like Java or C dialects.
This specification is not intended as a user manual. It is a reference guide for parser implementors, implementors of mappings that translate the NRL to executable code, and possibly as a last resort for ambiguity issues as the grammar specified in this specification is authoritative.
The specification is laid out much like the [NRL] specification that it seeks to extend: the body of the document is formed by a textual discussion of the constructs in the language that makes heavy reference to the grammar; the grammar itself is kept in the appendix for reference and consist of two parts:
A concrete syntax that specifies how the action language should look in textual documents.
An abstract syntax that specifies the language logically, without syntactic sugar.
Both the textual discussion and the grammars proceed top-down, starting with high level constructs and gradually adding more detail as they move down the language hierarchy.
This section contains a discussion of the grammar specified further below. The discussion begins with top-level constructs that occur in a rule file, followed by a more detailed specification of the action constructs.
ActionRule | ::= | Context "Action Rule" id:DoubleQuotedString Action |
An action rule, which may occur anywhere where a basic rule occurs, consists of a context, an identifier and an action (as detailed in 2.3 Complex Actions). If multiple action rules, or constraint rules coupled with action rules, occur in a file, it is an error to assign the same identifier to more than one rule.
The context defines the initial context of the rule. This must be a model element reference, as defined in the model section of the NRL specification. Action rules may also be written only against context elements that are classifiers.
Context: Trade Action Rule "example-1" Set the notional to 100000000
ActionFragment | ::= | MultipleParameterContext "Action Fragment" id:DoubleQuotedString Action |
An action fragment can also occur anywhere at the top level of the file. It represents a piece of NRL that can be reused. Essentially, an action fragment is exactly equivalent to an action rule but is not itself intended for direct execution. Instead, it is referred to in action rules - and, recursively, in fragments - using an actiion fragment application (see 2.4 Simple Actions for a discussion). Action fragments fulfil the same purpose for action rules that validation fragments fulfil for validation rules in the basic NRL constraint language.
Like an action rule, a fragment consists of a context, an identifier and an action. A fragment can, however, take multiple parameters as a context instead of using a single default context. The same constraints apply to the context parameters and the identifier that also apply to action rules. In actual practice, identifiers for fragments are usually readable sentences rather than technical identifiers, as the following example illustrates:
Context: Trade Action Rule "example-2" If the notional is equal to 0 then cancel the trade; Context: Trade (the "trade") Action Fragment "cancel the trade" Set the trade.status to Status.CANCELLED
Fragments with multiple parameters can be useful because they can make the main rule more readable, and correlate several objects to perform work. For example:
Context: Trade Action Rule "example-2b" If there is an Increase (the "increase") where increase.id is equal to Trade.id then increase the value of the Trade using the Increase; Context: Trade (the "trade"), Increase (the "increase") Action Fragment "increase the value of" Set trade.value.amount to trade.value.amount + increase.amount
Generally, the context parameters defined for a fragment become variables in the fragment that can be referred to as necessary. It is important to notice that there is no current context with a fragment: everything that is referred to in an expression must be referred through a variable:
Context: Trade (the "trade") Action Fragment "expire" Set expiryDate to [today] -- INVALID: No context that expiryDate could be contained in Set trade.expiryDate to [today] -- VALID: Refer to attributes through the parameters
Action | ::= | CompoundAction |
At the top level, an action has to be either a compound action (which will be defined as a single action or a collection of single actions), or an "if-then" style conditional action.
CompoundAction
CompoundAction | ::= | SimpleAction ((",")? ("then")? CompoundAction)? |
Compound actions are simple imperative constructs. The actions are to be executed one after the other. In the concrete syntax, the actions are usually separated using a comma, but the word "then" may also be used to improve readability.
A compound action defines a variable scope. Any variable introduced within a compound action is valid from the point where it is defined forward, up to the end of the compound action.
Set the tradeDate to '2000-01-01', set the amount to 10000000 Set the tradeDate to '2000-01-01', then set the amount to 10000000
SimpleAction | ::= | ConditionalAction | CreateAction | RemoveAction | AddAction | RemoveFromCollectionAction | SetAction | ForEachAction | ActionFragmentApplicationAction | OperatorAction | VariableDeclarationAction |
ConditionalAction
ConditionalAction | ::= | "if" Constraint "then" Action ("else" Action)? ";" |
This is a standard if-then statement that carries an optional else part. If the "if" condition is true, then the "then" action is executed. If it is false, and an "else" is specified, the "else" action is executed. Note that the if condition is an NRL constraint language construct and must be of boolean type.
If the tradeDate is before '2000-01-01' then flag the trade as auditable; If the tradeType is equal to 'Option' then set the value to numberOfOptions * unitPrice else set the value to nominal;
CreateAction
CreateAction | ::= | "create" ("new")? ModelReference "(" var:DoubleQuotedString ")" |
The create action is used to create an instance of a model element. In the case of a UML model, this could be a class being instantiated, in the case of an XML schema, it would involve create of an XML element.
Because the created element has to be accessed somehow for further processing, it must be labelled using a variable that follows the element name. The scope of the introduced variable depends on the containing constructs - which is usually a CompoundAction.
Create a new Trade (the "trade"), set trade.tradeDate to '2000-01-01'
RemoveAction
RemoveAction | ::= | ("Remove" | "Clear") (("each" | "every" | "all" | "any") ("of the")?)?
ModelReference
("(" var:DoubleQuotedString ")" "where"
Constraint)?
|
Remove actions are used for three purposes, depending on what type of model construct they refer to:
To delete attributes, for example by setting them to null, emptying them or assigning empty values. The attribute must be an attribute of the rule context or current context.
To remove elements of a collection attribute that match certain criteria.
To delete model elements if they match certain criteria. For example, one might want to delete "all trade objects with a date after the 1st of January 2005". In these cases, the model reference in the action will point to an element rather than an attribute. A "where" statement similar to an SQL query is used to define the criterion. Note that the NRL does not define the scope to search for elements.
If a where clause is present to specify which elements are to be deleted, a variable must also be introduced. Any reference to the attributes of the element to be deleted must be through the variable. The current context of the where clause remains the previous current context, enabling the clause to refer to attributes of the rule context.
To illustrate the matter, in the statement "Remove any Trade ("x") where x.date = date"
, x.date
refers to the Trade object being considered for deletion and the second date
refers to a date attribute of the current context, likely the rule context.
-- Simple attribute deletion, sets date to null Remove the tradeDate -- Deleting elements in a collection (assuming 'settlements' attribute in context) Remove the settlements ("empty") where empty.amount = 0 -- Deletion with query Remove any Trade ("t") where t.tradeDate is before '2005-01-01'
AddAction
AddAction | ::= | "add" element:ModelReference "to" collection:ModelReference |
"Add" actions are used to add elements to collections. Such elements may be attributes or variables introduced elsewhere, for example by a create action. The following static semantic constraints apply:
Both the element and the collection must be relative references resolvable from the current context.
The element must be of a type that is compatible with the collection. This means that the element must be of the same type as the collection, or a derived type reachable by navigating the inheritance hierarchy downwards.
Create a new TradeLeg (the "leg"), add the leg to the legs
RemoveFromCollectionAction
RemoveFromCollectionAction | ::= | "remove" element:ModelReference "from" collection:ModelReference |
These types of "remove" actions are the inverse of "add" actions and are used to remove elements from collections. The element and the collection must be of compatible types.
The same static semantic constraints apply as for add actions.
Remove the tradeDate from the exerciseDates
SetAction
SetAction | ::= | "set" ModelReference "to" Expression |
Set actions are used to set attribute values. They can be used to set attributes of simple types like strings or numbers to actual values, or to set complex types to attributes obtained elsewhere in the model or created using a create action. The following static semantic constraints apply to this construct:
The target model element of a set action must be an attribute resolvable from the current context.
The expression must be of a type that is compatible with the target model element. If the expression refers to a model element, that model element must be of the same type as the target element, or a derived type reachable by navigating the inheritance hierarchy downwards.
Set the tradeDate to '2005-12-01' Create a new TradeHeader ("header"), set the tradeHeader to header
ForEachAction
ForEachAction | ::= | "for each" ("of the")? ModelReference (",")? Action ";" |
"for each" var:DoubleQuotedString "in the collection of" ModelReference (",")? Action ";" |
The for each action is a simple iterative action to apply other actions to all members of a collection. The action supplied as a child, which may be a compound action, is executed on each member of the collection in turn. In order to delineate the scope of the iteration, it must be terminated with a semi-colon. The following static semantic constraints apply:
The target model element must be an attribute resolvable from the current context and must be a collection attribute, that is, must occur one or more times in the model.
If there is no variable, the current context of the child action is the current element
in the collection being considered in the iteration. For example, in a rule like for each of the trades, set the date to '2005-12-01';
,
the current context of the "set" action is the type of the attribute trades
.
If there is a variable, the current context of the child action is context the entire action is contained in - i.e. the context remains unchanged. This forces the constraint writer to refer to attributes of the collection using the variable.
For each of the trades, set the expiryDate to '2050-01-01', then set the status to 'AUDITED'; For each "trade" in the collection of trades, set trade.expiryDate to '2050-01-01', then set the trade.status to 'AUDITED';
The following example is invalid because the constraint writer forgot to use the variable:
For each "trade" in the collection of trades, set expiryDate to '2050-01-01'; -- Error: expiryDate is not in rule context
ActionFragmentApplicationAction
ActionFragmentApplicationAction | ::= | id:FragmentName ActionFragmentApplicationParameters |
Whereever a fragment application action occurs, it is substituted with the contents of an action fragment (see 2.2 Action Fragments).
Fragment application actions are subject to some constraints:
For each parameter is supplied, then type of the parameter must be the same as, or instance compatible with the matching declared fragment parameter.
Every target parameter supplied must reference a valid attribute accessible from the current context, or a variable declared in the current variable context.
Context: Trade Action Rule "example-2" If the notional is equal to 0 then cancel the Trade; Context: Trade ("trade") Action Fragment "cancel" Set the status of the trade to Status.CANCELED
OperatorAction
OperatorAction | ::= | id:OperatorName (OperatorActionParameters)? | |
OperatorActionParameters | ::= | Expression (("using" | "with" | "from" | "to" | "and") Expression)* |
An operator action is a reference to an externally defined action. This includes, for example, actions that are implemented in programming languages like Java or C#. Since the binding mechanism is highly dependant on the chosen implementation language, the NRL specification does not prescribe it. Instead, some simple static semantic rules constrain the application of operators:
If the operator is being applied without a parameter, it is not necessary that the operator is resolvable to make the rule valid.
If the supplied parameters are a model references, each must reference a valid attribute accessible from the current context, or a variable declared in the current variable context. Implementations may further constraint the type of attribute passed to the operator in order to enforce strict type compatibility.
In order to support diverse types of operations, the NRL provides multiple connector keywords for combining parameters as can be seen in the parameter list non-terminal. Here are some examples:
[send the message] 'Trade received' -- No parameters [send the message] 'Trade received' to senderParty using 'Queue 1' -- Three parameters, one is an attribute
VariableDeclarationAction
VariableDeclarationAction | ::= | SimpleVariableDeclaration |
Variable declarations are brought into the action language using this construct, which simply uses the basic NRL variable declaration.
"today" represents the [system date], Set the tradeDate to today, set the effectiveDate to today
This section defines the complete concrete syntax of the Natural Rule Language (NRL) action language in EBNF notation. The syntax extends the basic NRL constraint syntax defined in the constraint language specification
Pre-processing: In addition to the pre-processing performed in the basic constraint language, which is explained in the concrete syntax section of the constraint language specification, the following applies:
Processors must scan the file before parsing for possible action fragment declarations, and insert curly brackets around possible invocations - eliminating the need for the user to type them
[1] | ActionRule | ::= | Context "Action Rule" id:DoubleQuotedString Action |
[2] | ActionFragment | ::= | MultipleParameterContext "Action Fragment" id:DoubleQuotedString Action |
[3] | Action | ::= | CompoundAction |
[4] | CompoundAction | ::= | SimpleAction ((",")? ("then")? CompoundAction)? |
[5] | SimpleAction | ::= | ConditionalAction | CreateAction | RemoveAction | AddAction | RemoveFromCollectionAction | SetAction | ForEachAction | ActionFragmentApplicationAction | OperatorAction | VariableDeclarationAction |
[6] | ConditionalAction | ::= | "if" Constraint "then" Action ("else" Action)? ";" |
[7] | CreateAction | ::= | "create" ("new")? ModelReference "(" var:DoubleQuotedString ")" |
[8] | RemoveAction | ::= | ("Remove" | "Clear") (("each" | "every" | "all" | "any") ("of the")?)?
ModelReference
("(" var:DoubleQuotedString ")" "where"
Constraint)?
|
[9] | AddAction | ::= | "add" element:ModelReference "to" collection:ModelReference |
[10] | RemoveFromCollectionAction | ::= | "remove" element:ModelReference "from" collection:ModelReference |
[11] | SetAction | ::= | "set" ModelReference "to" Expression |
[12] | ForEachAction | ::= | "for each" ("of the")? ModelReference (",")? Action ";" |
"for each" var:DoubleQuotedString "in the collection of" ModelReference (",")? Action ";" |
[13] | ActionFragmentApplicationAction | ::= | id:FragmentName ActionFragmentApplicationParameters |
[14] | ActionFragmentApplicationParameters | ::= | Expression ( ("using" | "with" | "from" | "to" | "and") ActionFragmentApplicationParameters )? |
[15] | OperatorAction | ::= | id:OperatorName (OperatorActionParameters)? |
[16] | OperatorActionParameters | ::= | Expression (("using" | "with" | "from" | "to" | "and") Expression)* |
[17] | VariableDeclarationAction | ::= | SimpleVariableDeclaration |
This section defines the abstract syntax of the action language in EBNF notation.
To act as the source of any mapping from the action language to a target language
To specify what a parse tree for the language looks like
[18] | AActionRule | ::= | AContext Identifier AAction |
[19] | AActionFragment | ::= | AMultipleParameterContext Identifier AAction |
[20] | AAction | ::= | ACompoundAction |
[21] | ACompoundAction | ::= | (ASimpleAction)* |
[22] | ASimpleAction | ::= | AConditionalAction | ACreateAction | ARemoveAction | AAddAction | ARemoveFromCollectionAction | ASetAction | AForEachAction | AActionFragmentApplicationAction | AOperatorAction | AVariableDeclarationAction |
[23] | AConditionalAction | ::= | if:AConstraint then:AAction (else:AAction)? |
[24] | ACreateAction | ::= | create AModelReference var:Identifier |
[25] | ARemoveAction | ::= | remove
AModelReference
(var:Identifier
AConstraint)?
|
[26] | AAddAction | ::= | add AModelReference to AModelReference |
[27] | ARemoveFromCollectionAction | ::= | remove AModelReference from AModelReference |
[28] | ASetAction | ::= | set AModelReference to AExpression |
[29] | AForEachAction | ::= | foreach (var:Identifier)? AModelReference AAction |
[30] | AActionFragmentApplicationAction | ::= | fragmentId:Identifier (param:AExpression)* |
[31] | AOperatorAction | ::= | Operator (param:AExpression)* |
[32] | AVariableDeclarationAction | ::= | AVariableDeclaration |
These people have contributed to this specification through comments:
Dave Carlson
2010-04-07: Version 1.4 release. "Select", previously deprecated is now removed altogether. The syntax for "ActionRule" is now "Action Rule", and "ActionMacro" has become "Action Fragment".
2009-03-18: CN: First version to be published publicly
2008-01-09: CN: Operators now take expressions as parameters, not just model references; Macros can take multiple parameters now, and macro invocations can pass them along; "clear" is a synonym for "remove".
2007-02-09: CN: Select is deprecated; Added new "for each" syntax that permits the use of a variable.
2006-09-15: CN: Added variable declaration actions, renamed retrieve to select, delete to remove and remove to removefromcollection; Moved conditional action down and added semi-colon so it can be used within rules; Added statement on pre-processing to eliminate quotes around macro applications;
2006-06-26: CN: First draft.