openEHR logo

Expression Language (EL)

Issuer: openEHR Specification Program

Release: BASE Release-1.0.4

Status: TRIAL

Revision: [latest_issue]

Date: [latest_issue_date]

Keywords: openehr, expressions, rules

openEHR components
© 2016 - 2021 The openEHR Foundation

The openEHR Foundation is an independent, non-profit foundation, facilitating the sharing of health records by consumers and clinicians via open specifications, clinical models and open platform implementations.

Licence

image Creative Commons Attribution-NoDerivs 3.0 Unported. https://creativecommons.org/licenses/by-nd/3.0/

Support

Issues: Problem Reports
Web: specifications.openEHR.org

Amendment Record

Issue Details Raiser, Implementer Completed

R E L E A S E     1.0.4

1.1.5

SPECBASE-33. Retrospectively release interim form of Expression Language.

T Beale,
P Bos,
D Bosca

11 Aug 2021

1.1.0

SPECPUB-6. Correct UML package nesting and paths in documents; insert base parent package; rename expressions package to expression.

T Beale

27 Nov 2017

1.0.1

Correct type of OP_DEF_EXISTS.evaluation_agent to FUNCTION<<Any>, Boolean>.

C Nanjo

31 May 2016

1.0.0

Initial writing. Taken from AOM2 2.0.6.

T Beale,
openEHR SEC

15 Feb 2016

Acknowledgements

Primary Author

  • Thomas Beale, Ars Semantica; openEHR Foundation Management Board.

1. Preface

1.1. Purpose

This document specifies the openEHR Expression Model (EOM) and an abstract syntax, denoted the openEHR Expression Language (oEL). The EOM defines the semantics of a first order predicate style logic that can be used to express assertions (for example in archetypes) and rulesfor example in decision support situations.

The intended audience includes:

  • Standards bodies producing health informatics standards;

  • Academic groups using openEHR;

  • Solution vendors;

  • Medical informaticians and clinicians interested in health information.

Prerequisite documents for reading this document include:

Releated documents include:

1.3. Status

The contents of this specification were separated out from the AOM2 specification in order to provide a common model of expressions and rules structures for AOM, GDL and other specifications requiring it.

This specification is in the TRIAL state. The development version of this document can be found at https://specifications.openehr.org/releases/LANG/latest/expression_language.html.

Known omissions or questions are indicated in the text with a 'to be determined' paragraph, as follows:

TBD: (example To Be Determined paragraph)

1.4. Feedback

Feedback may be provided on the openEHR languages specifications forum.

Issues may be raised on the specifications Problem Report tracker.

To see changes made due to previously reported issues, see the BASE component Change Request tracker.

1.5. Conformance

Conformance of a data or software artifact to an openEHR specification is determined by a formal test of that artifact against the relevant openEHR Implementation Technology Specification(s) (ITSs), such as an IDL interface or an XML-schema. Since ITSs are formal derivations from underlying models, ITS conformance indicates model conformance.

2. Overview

The openEHR Expression Object Model (EOM) defines the semantics of all of the elements of computable expressions that are used in openEHR, and are likely to be used in healthcare and life sciences computing where rules and expressions are needed. The model is designed as an extensible core formalism suitable for use and extension in other formalisms (such as the openEHR Archetype formalism).

The openEHR Expression Language (EL) specified here is an abstract syntax counterpart to the Expression Object Model, and may be considered a 'default syntax'. Other syntaxes or syntax variants are certainly possible, and other serialisation methods are possible, such as object graph serialisation into XML, JSON, YAML etc. The EL is provided firstly as a way of specifying and explaining the semantics of the EOM, and secondly, as one way of actually authoring expressions and rules in textual form. This approach is the same as with any programming language, where the usual form for learning and programming is the abstract language form, while the computational form is an abstract syntax tree (AST) or similar.

Accordingly, the EOM should be considered the normative definition of the openEHR Expression formalism, and indeed not all implementations need support the language: they might for example only serialse in JSON or use purely graphical visualisation.

The formalism as defined here requires extensions for use, at a minimum to provide concrete value referencing. Extensions would also typically provide more operators, functions, leaf types and other features that are needed in specific circumstances.

Key features of the formalism include:

  • variable declarations, assignments and expressions;

  • strong typing;

  • standard logical operators including universal and existential quantification, as well as user-defined operators;

  • standard arithmetic and relational comparison operators, enabling the use of numerics;

  • parentheses for overridding operator precedence;

  • functions, including built-ins like current_date, standard functions such as max() defined on primitive types, as well as the ability to use external functions.

For reasons of comprehensibility and explanation, the Expression Language is defined first in this specification.

2.1. Design Background

The openEHR Expression Language is based on a limited first-order predicate logic language. It has similarities with OMG’s OCL (Object Constraint Language), and to the assertion syntax used in the Object-Z language (Smith, 2000). See Sowa (2000), Hein (2002), Kilov & Ross (1994) for an explanation of predicate logic in information modelling. It also draws on the semantics and some of the syntax (particularly agent-related) of the Eiffel Language (ECMA-367). It is not exactly the same as any of these languages because:

  • it has a different target meta-model, namely the BMM expression model;

  • the syntax is designed to be comprehensible to developers familiar with modern mainstream object-oriented and functional languages such as Java, C#, Python, TypeScript etc.

Modern syntaxes that provide a more generic and minimal inspiration for the expression syntax described here include the Xpath syntax.

The semantic requirements are for expressions including arithmetic, boolean, and relational operators, functions, quantifier operators, operator precedence, parentheses, constant values, and certain kinds of variables. However, there is no support in the core specification for procedural semantics or most of the other complexities of full-blown programming languages.

2.2. Execution Model

The assumed execution model of the Expression language is that an EL text is evaluated by an evaluator against a data context, which determines the truth values of the expression(s). The data context typically includes data services such as the EHR, patient demographics, laboratory systems and so on - anything that is available computationally via some service. The text may contain symbols representing internal variables and bound variables, the latter of which map to entities in the data context.

No assumption is made about whether the data context used is complete or faithful to reality. Consequently, the expression universe is technically speaking an 'open world' in the sense of the open-world assumption. However, higher-level applications or components (or users) may have sufficient knowledge about the data as to be able to treat it in a closed world fashion, for example to treat absence of X as negation of X.

2.3. General Structure

The Expression Language may be used to write single Boolean-valued expressions within another computational context, and in future, a multi-section self-standing EL text with a similar structure to an archetype. Expressions may be declarations, assignments or assertions. These are made up of expressions containing symbols which represent typed variables and constants. Variables are either bound or local, and are defined using $ names, e.g. $heart_rate as used in shell script and some other programming languages. A bound variable is mapped to a field in the data context. In the current version of the language, this is achieved by assignment statements that either assign a path in the data context to a symbolic variable (reading) or the reverse (writing back). In this sense, 'assignment' can be understood as associating a data context path with a named variable.

3. The Expression Language

3.1. Overview

This section describes the openEHR Expression Language. In the various contexts where openEHR expressions are used, the syntax provides a foundation for concrete syntaxes supporting specific value-referencing mechanisms, as well as other types of rules, operands etc. The key features of the language are: variable declarations, assignments and expressions. Most of the semantics are in the expression part, which is based on first-order predicate logic with the addition of arithmetic and relational operators to enable the use of numeric elements. Expressions may contain constants, variable references, value references and functions.

3.2. Syntax style

The syntax style used here is generally so-called 'snake_case' rather than so-called 'CamelCase', in common with other openEHR specifications, but either may be used in real applications.

3.3. Typing

The Expression language is fully typed. The type system is the same as that used in other openEHR components, and supports the same set of basic types as described in the openEHR Foundation Types Specification, namely:

  • primitive types;

  • container types:

    • List<T>, i.e. an ordered list of elements of primitive type T;

    • Set<T>, i.e. a set of elements of primitive type T;

    • Hash<K:Ordered,V>, i.e. a Hash table or dictionary;

  • an interval type:

    • Interval<T: Ordered> where T is any ordered primitive type.

3.4. Literals

3.4.1. Primitive Types

The primitive types are shown below, along with typical literal values, which are expressed in the ODIN syntax.

Name Literal value Description

Boolean

True, False

Boolean value

Integer

10, -4, 1024

Integer value

Real

10.0, 0.345

Real value

Date

2004-08-12

ISO8601-format date

Date_time

2004-08-12T12:00:59+0100

ISO8601-format date/time

Time

12:00:59

ISO8601-format time

Duration

P39W

ISO8601-format duration

String

"this is a string"

String

Uri

https://en.wikipedia.org/wiki/Everest

Uri in RFC3986 format

Terminology_code

[snomed_ct::389086002]
[snomed_ct::389086002|Hypoxia|]

Terminology code reference

Automatic type promotion from Integer to Real applies to all integer and real values and expressions, in the same fashion as most programming languages.

TBD: do we need Integer64 and Real64?

3.4.2. Container Types

The same container types as defined in the Foundation Types specification are supported in EL, as follows.

Name Literal value Description

List<T>

[val, val, …​]

Linear list of items of any primitive type, allowing order and repeated membership'

Set<T>

{val, val, …​}

Set of items of any primitive type; no order, unique membership;

Hash<K:Ordered, V>

<
["key1"] = <val1>
["key2"] = <val2>
…​
["keyN"] = <valN>
>

Indexed linear container;
concretely, a table of values of any type V, keyed by values of any Ordered descendant K, typically String or Integer

The above types each have an assumed interface consisting of functions and procedures that apply to all members of the container, consistent with the semantics of the container. These include the following methods that may be accessed in EL via the syntactic operators for_all and there_exists described below.

    for_all (test(v: T): Boolean): Boolean
            -- True if for every v in container, test (v) is True

    there_exists (test(v: T): Boolean): Boolean
            -- True if there is any v in container for which test (v) is True

3.4.3. Interval Type

The same Interval type as defined in the Foundation Types specification is supported in EL, as follows. Literal interval values take the same form as in ODIN.

Name Literal value Description

Interval<T>

Interval of any ordered primitive

|N..M|

the two-sided interval N >= x <= M

|>N..M|

the two-sided interval N > x <= M

|N..<M|

the two-sided interval N >= x < M

|<N|

the one-sided interval x < N

|>N|

the one-sided interval x > N

|<=N|

the one-sided interval x <= N

|>=N|

the one-sided interval x >= N

|N +/-M|

the two-sided interval of N ±M

|N±M|

the two-sided interval of N ±M

Automatic type promotion from Interval<Integer> to Interval<Real> applies to all integer and real values and expressions, in the same fashion as most programming languages.

3.5. Statements

An EL text consists of statements, each of which may be a declaration, an assignment or an assertion.

3.5.1. Declarations

Variables are declared with a formal type. Bound variables additionally include an assignment to a path within an assumed data context, which may be understood as performing the binding. Local variable declaratoins may include an assignment to an expression result, in the manner of common programming languages.

    -- local variable, primitive type
    $date_of_birth: Date

    -- local variable, container type
    $heart_rate_history: List<Real>

    -- local variable with assignment
    $age_in_years: Integer := current_date() - $date_of_birth

    -- a bound variable
    $weight: Quantity := /data[id3]/events[id4]/data[id2]/items[id5]/value

Variables are referenced within assignments and expressions using the same syntax, i.e. var_name and $var_name.

3.5.2. Constants

Constants are defined via the use of the equality operator = in a declaration and a literal value, as follows.

    Mph_to_kmh_factor: Real = 1.6
    Pounds_to_kg: Real = 0.4536
    Systolic_normal_range: Interval<Integer> = |105..135|

3.5.3. Assignment

An assignment to a writable variable is expressed using the := operator. An assignment may be made in a declaration in the same way as in many programming languages. The right hand side of an assignment is any value-returning expression. Typical assignments are illustrated below.

    $speed_kmh: Real                             -- declaration
    $speed_mph: Real := 35.0                     -- assignment in a declaration (not a constant)

    $speed_kmh := $speed_mph * Mph_to_kmh_factor  -- assignment

3.6. Bound Variables, Evaluation and Validity

Variables that are bound to entities in the data context function differently from local variables, since their availability is predicated on the existence of the relevant entities. For example, the variable $body_weight may be bound to a call that retrieves a patient heart rate from the EHR, via an appropriate API call. There is no guarantee that the value is available, so $body_weight may therefore be undefined in a sense not applicable to local variables. In a programming language, if a variable is not explicitly set, it has either the default value of the type (e.g. 0 for Integer) or a random value of the correct type. This behaviour is appropriate for local variables, but for bound variables that cannot be evaluated because the external entity does not exist, we want something like an exception to occur.

The approach used for EL is to allow bound variables to be used freely, as for local variables, but if a bound variable cannot be evaluated from the data context, an 'undefined value' exception should be generated, indicating which variable could not be evaluated. To impose more control, the predicate exists () can be used within an expression or assertion to ensure that one or more variables can be populated before proceeding with logic that depends on them, as follows:

    Check_vs_vars: exists $heart_rate and exists $blood_pressure

To assert that a certain part of a larger data structure exists, the following assertion can be used.

    Smoker_details_recorded: $is_smoker implies exists $smoking_details

3.7. Expressions

Expressions constitute the main part of the Expressions language, and consist of a familiar typed, operator-based syntax common to many programming languages and logics. Formally, an expression is one of the following:

  • terminal entities_;

  • non-terminal entities;

    • operators;

    • functions.

3.7.1. Terminal Entities

Terminal entities in EL are any of the following:

  • literals

  • variable

  • variable with sub-path

  • constant

  • function call

  • raw path

Use of variables, constants and function calls is the same as in common languages, using the following syntax.

    -- expression containing a variable and function call
    current_date() - $date_of_birth

    -- expression containing a variable and a constant
    $speed_mph * Mph_to_kmh_factor

Variables that are bound to paths may be used to generate a reference to a sub-element, using an Xpath-like approach, as follows:

    $event: List<Event> := /data[id2]/events[id3]

    Check_field_vals: $event/data[id4]/items[id7]/value/magnitude =
        $event/data[id4]/items[id5]/value/magnitude - $event/data[id4]/items[id6]/value/magnitude

In the above, the construction $event/data[id4]/items[id7]/value/magnitude references an element in the data context whose location is given by the path to which $event is bound, i.e. /data[id2]/events[id3], concatenated with the subordinate path, giving a resulting path of /data[id2]/events[id3]/data[id4]/items[id7]/value/magnitude.

Finally, in the current version of EL, raw paths may be used directly as variables. This is primarily to allow UI expression building tools that work based on the path map of a data context (e.g. an openEHR archetype) to generate expressions directly using paths rather than introducing variables.

3.7.1.1. Functions

Functions are considered leaf entities in the Expression language, and can be of a built-in type or external (user-defined) type. A simple example is:

    $date_of_birth: Date
    $age: Duration

    $age := current_date() - $date_of_birth

This uses the built-in function current_date() to compute a person’s age in the standard way. The typing is based on the operator - (subtract) in the type Date having the following signature, as defined in the openEHR Foundation Types Specification:

    class Date
        infix '-' alias subtract (Date): Duration

The built-in functions are formally defined in the openEHR Base Types Specification. Some common ones are listed below.

Name Textual Rendering Signature Meaning

Degree 0 functions (no arguments)

current_date

current_date()

:Date

Current date

current_time

current_time()

:Time

Current time

current_date_time

current_date_time()

:Date_time

Curent date time

current_time_zone

current_time_zone()

:Time_zone

Curent time zone

Degree N functions (N arguments)

sum

sum (x, y, …​.)

<Real, …​>: Real

Equivalent to x + y + …​

mean

mean (x, y, …​)

<Real, …​>: Real

The mean (average) value of x, y, …​

max

max (x, y, …​)

<Real, …​>: Real

The maximum value among x, y, …​

min

min (x, y, …​)

<Real, …​>: Real

The minimum value among x, y, …​

Any other functions that are used need to be mapped by the EL engine to implementations with matching signatures.

3.7.2. Operators

Expressions can include arithmetic, relational and boolean operators, plus the existential and universal quantifiers. The full operator set is shown below, along with textual and symbolic renderings. The latter are just standard Unicode symbols. Expression parsers should ideally support these symbols as operator equivalents in addition to the textual form, since it allows expressions to be expressed in a more compact and less language-independent way.

Identifier Textual
Rendering
Symbolic
Rendering
Meaning

Arithmetic Operators - Numeric result; descending precendence order

exp

^

^

Expontentiation

times

*

*

Multiplication

divide

/

/

Division

mod

%

%

Modulo (whole number) division

plus

+

+

Addition

minus

-

-

Subtraction

Relational Operators - Boolean result; equal precedence

eq

=

=

Equality relation between numerics

ne

!=

Inequality relation between numerics

lt

<

<

Less than relation between numerics

le

<=

Less than or equal relation between numerics

gt

>

>

Greater than relation between numerics

ge

>=

Greater than or equal relation between numerics

Logical Operators - Boolean result; descending precendence order

not

not, ~

Negation, "not p"

and

and

Logical conjunction, "p and q"

or

or

Logical disjunction, "p or q"

xor

xor

Exclusive or, "only one of p or q"

implies

implies

Material implication, "p implies q", or "if p then q"

Operator semantics that require further explanation are described below.

3.7.2.1. Logical Negation

All Boolean operators take Boolean operands and generate a Boolean result. The not operator can be applied as a prefix operator to all operators returning a Boolean result as well as a parenthesised Boolean expression.

3.7.2.2. Precedence and Parentheses

The precendence of operators follows the order shown in the operator tables above. To change precedence, parentheses can be used in the fashion typical of most programming languages, as shown below.

    $at_risk := $weight > 120 and ($is_smoker or $is_hypertensive)
3.7.2.3. Container Operators

The two standard quantification operators from predicate logic there exists (∃ operator) and for all (∀ operator) are defined in EL for the container types found in the openEHR Foundation Types.

The textual syntax of there exists is as follows:

    there_exists v : container_var | <Boolean expression mentioning v>

Here, the | symbol is usually read in English as 'such that'. The symbolic equivalent may also be used:

    ∃ v : container_var | <Boolean expression mentioning v>

The for_all operator has similar textual syntax:

    for_all v : container_var | <Boolean expression mentioning v>

Here, the | symbol is normally read in English as as 'it holds that'. The symbolic equivalent may also be used:

    ∀ v : container_var | <Boolean expression mentioning v>

For both operators, the : symbol may be replaced by the keyword in.

4. The Expression Object Model

4.1. Overview

The expression package defines the Expression Object Model (EOM), a model of 'statements' and 'expressions' that can be used in various contexts in openEHR, including inside archetypes and in GDL guidelines. If the Expression Language or other syntax-based artefact is used, the model described here defines the parser output in the form of an expression tree. Other openEHR models designed for a particular purpose may reuse the expression package and further specialise some of its types specifically intended for extension.

The package structure of the expression package is shown below.

BASE expression packages
Figure 1. base.expression Package

4.2. Core Package

The main expression packages core and extension are illustrated below.

BASE expression
Figure 2. base.expression.core Package

A group of statements to be used together is formally defined as one or more STATEMENTs in a STATEMENT_SET. There are three types of statements corresponding to the classes ASSERTION, VARIABLE_DECLARATION and ASSIGNMENT. Assertions consist of an expression tree structure that evaluates to a Boolean value. The root of the tree is a descendant of the class EXPR_ITEM that represents a Boolean-returning operator or value. An Assertion has an optional tag, enabling it to be named.

A VARIABLE_DECLARATION enables a named variable to be declared with a type, which must be one of the descendants of EXPR_DEF_TYPE.

The ASSIGNMENT class defines the usual operation of assigning a value (the source i.e. the notional right-hand side) to a variable, (the target, i.e. left-hand side). The source of an assignment can be any Expression or an EXTERNAL_QUERY, which are sub-types of EXPR_VALUE.

4.2.1. Expressions

Expressions are fully described statement values that may be evaluated internally (as compared to an external query) and are defined by the EXPR_XXX classes. The Expression classes model a typical evaluation tree each of whose nodes is either a leaf or an operator node. The semantics of Expressions are described in the Expressions sub-section in the Language section.

In this model, 'functions' are treated as leaf nodes rather than tree nodes, which would be more typical of a functional language meta-model. The leaf node approach used here treats functions as black boxes requiring an evaluation method, enabling the main expression tree to be evaluated by use of a standard set of operators whose semantics can be safely built in.

Operator semantics are defined by the class OPERATOR_DEF, which includes the two attributes name and symbols which define the name of the operator and its allowable symbols respectively.

TBD: could potentially separate 'text symbols' and 'math symbols' to enable rendering in different ways.

The OPERATOR_DEF_BUILTIN class defines a hook for implementing built-in operators, i.e. those that will be executed by fixed code within the statement evaluator, e.g. a case statement or similar that uses the implementation language native operators.

4.2.2. Other Statement Elements

4.2.2.1. External Query

An EXTERNAL_QUERY is a type of expression representing a call to an external service which obtains a value for it. This enables information items from the exterior computational environment to be treated as abstract typed values within expressions.

4.2.3. Class Descriptions

4.2.3.1. STATEMENT_SET Class

Class

STATEMENT_SET

Description

A container for a specific set of statements intended to be used together.

Attributes

Signature

Meaning

0..1

statement: List<STATEMENT>

The member statements of this statement set.

0..1

name: String

Optional name of this rule set.

Functions

Signature

Meaning

execution_result (): Boolean

Execution result of the whole rule set. Determined by the and-ing of result values of Assertions in the rule set.

4.2.3.2. STATEMENT Class

Class

STATEMENT (abstract)

Description

Abstract concept of any statement in a block of rule statements.

4.2.3.3. ASSERTION Class

Class

ASSERTION

Description

Structural model of a typed first order predicate logic assertion, in the form of an expression tree, including optional variable definitions.

Inherit

STATEMENT

Attributes

Signature

Meaning

0..1

tag: String

Expression tag, used for differentiating multiple assertions.

0..1

string_expression: String

String form of expression, in case an expression evaluator taking String expressions is used for evaluation.

1..1

expression: EXPRESSION

Root of expression tree.

4.2.3.4. VARIABLE_DECLARATION Class

Class

VARIABLE_DECLARATION

Description

Definition of a named variable that can be used in an expression.

Inherit

STATEMENT

Attributes

Signature

Meaning

1..1

name: String

Name of the variable.

1..1

type: EXPR_TYPE_DEF

Primitive type of the variable, enabling its use to be type-checked in expressions.

4.2.3.5. ASSIGNMENT Class

Class

ASSIGNMENT

Description

Abstract ancestor of types representing the assignment statement, which does not produce a value in the way that operators such as '=', 'and' etc do. An assignment associates a named variable with an expression.

Inherit

STATEMENT

Attributes

Signature

Meaning

1..1

target: VARIABLE_DECLARATION

The target variable on the notional left-hand side of this assignment.

1..1

source: EXPR_VALUE

Source right hand side) of the assignment.

4.2.3.6. EXPR_VALUE Class

Class

EXPR_VALUE (abstract)

Description

Any kind of statement element that can be evaluated. The type will either be supplied in descendant types or else will be inferred by an assignment statement linked to a typed variable.

Functions

Signature

Meaning

(abstract)

value (): Any

The computed value of this node as a result of the nodes below it, for operator nodes, or else statically set or otherwise derived values.

4.2.3.7. EXTERNAL_QUERY Class

Class

EXTERNAL_QUERY

Description

Definition of a variable whose value is derived from a query run on a data context in the operational environment. Typical uses of this kind of variable are to obtain values like the patient date of birth, sex, weight, and so on. It could also be used to obtain items from a knowledge context, such as a drug database.

Inherit

EXPR_VALUE

Attributes

Signature

Meaning

1..1

context: String

Optional name of context. This allows a basic separation of query types to be done in more sophisticated environments. Possible values might be “patient”, “medications” and so on. Not yet standardised.

1..1

query_id: String

Identifier of query in the external context, e.g. “date_of_birth”. Not yet standardised.

0..1

query_args: List<String>

Optional arguments to query. Not yet standardised.

4.2.3.8. EXPRESSION Class

Class

EXPRESSION (abstract)

Description

Abstract parent of all typed expression tree items.

Inherit

EXPR_VALUE

Functions

Signature

Meaning

(abstract)

type (): EXPR_TYPE_DEF

The primitive type of this node, which must be determined by redefinitions in concrete classes.

4.2.3.9. EXPR_OPERATOR Class

Class

EXPR_OPERATOR (abstract)

Description

Abstract parent of operator types.

Inherit

EXPRESSION

Attributes

Signature

Meaning

0..1

precedence_overridden: Boolean

True if the natural precedence of operators is overridden in the expression represented by this node of the expression tree. If True, parentheses should be introduced around the totality of the syntax expression corresponding to this operator node and its operands.

1..1

operator_def: OPERATOR_DEF

Operator definition.

0..1

symbol: String

The symbol actually used in the rule, or intended to be used for serialisation. Must be a member of operator_def.symbols.

4.2.3.10. EXPR_UNARY_OPERATOR Class

Class

EXPR_UNARY_OPERATOR

Description

Unary operator expression node.

Inherit

EXPR_OPERATOR

Attributes

Signature

Meaning

1..1

operand: EXPRESSION

Operand node.

4.2.3.11. EXPR_BINARY_OPERATOR Class

Class

EXPR_BINARY_OPERATOR

Description

Binary operator expression node.

Inherit

EXPR_OPERATOR

Attributes

Signature

Meaning

1..1

left_operand: EXPRESSION

Left operand node.

1..1

right_operand: EXPRESSION

Right operand node.

4.2.3.12. OPERATOR_DEF Class

Class

OPERATOR_DEF (abstract)

Description

Abstract definition of an operator for use in the Rules evaluator.

Attributes

Signature

Meaning

1..1

identifier: String

Unique identifier of this operator within the rules system.

Functions

Signature

Meaning

(abstract)

symbols (): List<String>

One or more symbols used to express the operator in textual form. This may include any unicode character.

4.2.3.13. OPERATOR_DEF_BUILTIN Class

Class

OPERATOR_DEF_BUILTIN

Description

Builtin operator pseudo-type. An implementation will typically use the 'identifer' attribute to determine which piece of internal code to execute to evaluate the operator.

Inherit

OPERATOR_DEF, BUILTIN_OPERATORS

Invariants

Valid_operator: op_table.has (name)

4.2.3.14. BUILTIN_OPERATORS Class

Class

BUILTIN_OPERATORS

Description

Singleton accessor object for list of builtin operators.

Constants

Signature

Meaning

1..1

op_table: Hash<String, List<String>>

List of built-in operators in the form of a keyed table of List<String>, where the latter represents one or more lexical symbols used for the operator, e.g. "+", and the key is the operator identifier, e.g. "plus".

Implementations may choose to populate this table with all operators defined in the core specifications, or only partially, in which case operators not in the table are implemented using the closure/agent approach for externally defined operators.

4.2.3.15. EXPR_LEAF Class

Class

EXPR_LEAF (abstract)

Description

Expression tree leaf item representing one of:

  • a manifest constant of any primitive type;

  • a path referring to a value in the archetype;

  • a constraint;

  • a variable reference.

Inherit

EXPRESSION

Attributes

Signature

Meaning

0..1

item: Any

The reference item from which the value of this node can be computed.

4.2.3.16. EXPR_LITERAL Class

Class

EXPR_LITERAL

Description

Literal value expression tree leaf item. This can represent a literal value of any primitive type included in the PRIMITIVE_TYPE enumeration.

Inherit

EXPR_LEAF

Attributes

Signature

Meaning

1..1
(redefined)

item: Any

A statically set constant value of a primitive type.

4.2.3.17. EXPR_VARIABLE_REF Class

Class

EXPR_VARIABLE_REF

Description

Expression tree leaf item representing a reference to a declared variable.

Inherit

EXPR_LEAF

Attributes

Signature

Meaning

1..1
(redefined)

item: VARIABLE_DECLARATION

The variable referred to.

4.2.3.18. EXPR_VALUE_REF Class

Class

EXPR_VALUE_REF (abstract)

Description

Abstract parent type of concrete types enabling access to values in a specific information structure. This type provides an extension point for specific syntaxes to connect a generic rule structure to a data source.

Inherit

EXPR_LEAF

4.2.3.19. EXPR_FUNCTION Class

Class

EXPR_FUNCTION

Description

Node representing a function with 0 or more arguments.

Inherit

EXPR_LEAF

Attributes

Signature

Meaning

0..1

arguments: List<EXPRESSION>

Arguments of this function, which can be from 0 to any number. Functions with no arguments are typically used to represent real world varying values like 'current time' and so on.

1..1

function_def: FUNCTION_DEF

4.2.3.20. FUNCTION_DEF Class

Class

FUNCTION_DEF (abstract)

Description

Ancestor type for types that define a function for use in the Rules evaluator.

Attributes

Signature

Meaning

1..1

identifier: String

Unique identifier of this function within the Rules system.

4.2.3.21. FUNCTION_DEF_BUILTIN Class

Class

FUNCTION_DEF_BUILTIN

Description

Builtin function pseudo-type. An implementation will typically use the 'identifer' attribute to determine which piece of internal code to execute to evaluate the function.

Inherit

FUNCTION_DEF, BUILTIN_FUNCTIONS

Invariants

Valid_function: func_table.has (name)

4.2.3.22. BUILTIN_FUNCTIONS Class

Class

BUILTIN_FUNCTIONS

Description

Singleton accessor object for list of builtin functions.

Constants

Signature

Meaning

1..1

func_table: Hash<String, List<String>>

List of built-in functions in the form of a keyed table of List<String>, where the latter represents one or more lexical symbols used for the function, e.g. "max", and the key is the function identifier, e.g. "max".

Implementations may choose to populate this table with all functions defined in the core specifications, or only partially, in which case functions not in the table are implemented using the closure/agent approach for externally defined functions.

4.3. Extension Package

The Extension package contains classes that provide extension points to the Expression formalism. These are of three kinds:

  • value references;

  • externally defined operators;

  • externally defined functions.

4.3.1. Value References

Value references are defined by creating descandants of the EXPR_VALUE_REF class. These typically contain a reference to a data object, or an identifier that can be resolved to a value via a resolver.

4.3.2. Operators and Functions

Externally defined operators and functions are defined as the separate types OPERATOR_DEF_EXTERNAL and FUNCTION_DEF_EXTERNAL resepectively. They could in theory be defined with a single class, however the use of two classes express the distinction between operators and functions used in the openEHR Expression language.

TBD: this model in its current form treats functions and operators as separate, to allow some convenience in simple implementation of standard operators. But Theoretically, operators are just 1-arg or 2-arg functions, and the extensions could be defined by treating them as functions. However operators also usually have a symbol form, whereas we are assuming a function’s symbol is just its identifier. Also, functions can in general have variable numbers of arguments, and complex implementations. Do we want to implement operators that way?

The OPERATOR_DEF_EXTERNAL class inherits the identifier and symbols features from OPERATOR_DEF, and adds an execution_agent, which is a function object that can compute the operator, given appropriate arguments. The extention part of the UML model above shows how tihs facility can be used to define two operators that would normally be implemented as built-ins, and and exists.

TBD: this is shown as being done with a class per operator, but in fact it only requires instances of OPERATOR_DEF_EXTERNAL which have the appropriate identifiers, symbols and avaluation agent. Should we just define it this way?

The approach to adding custom functions is the same as with operators.

4.3.3. Class Descriptions

4.3.3.1. OPERATOR_DEF_EXTERNAL Class

Class

OPERATOR_DEF_EXTERNAL (abstract)

Description

Ancestor of concrete operator definition types. Adds an optional execution agent, typically implemented by closures / lambdas/ agents in languages supporting functional programming.

Inherit

OPERATOR_DEF

Attributes

Signature

Meaning

1..1

evaluation_agent: FUNCTION<TUPLE,Any>

4.3.3.2. OP_DEF_EXAMPLE Class

Class

OP_DEF_EXAMPLE

Description

Example custom operator definition as a class, for an operator whose identifier is 'xxx' and for which the symbols "xxx" and "%" can be used, and with a two-argument numeric signature.

Inherit

OPERATOR_DEF_EXTERNAL

Attributes

Signature

Meaning

0..1

symbols: List<String> = "xxx", "%"

1..1
(redefined)

identifier: String = "xxx"

1..1
(redefined)

evaluation_agent: FUNCTION<<Numeric,Numeric>,Numeric>

4.3.3.3. OP_DEF_AND Class

Class

OP_DEF_AND

Description

Illustrative class showing what the normally built-in logical 'and' operator looks like as an external definition.

Inherit

OPERATOR_DEF_EXTERNAL

Attributes

Signature

Meaning

0..1

symbols: List<String> = "and","∧"

1..1
(redefined)

identifier: String = "and"

1..1
(redefined)

evaluation_agent: FUNCTION<<Boolean,Boolean>,Boolean>

4.3.3.4. OP_DEF_EXISTS Class

Class

OP_DEF_EXISTS

Description

Illustrative class showing what the normally built-in logical 'exists' operator looks like as an external definition.

Inherit

OPERATOR_DEF_EXTERNAL

Attributes

Signature

Meaning

0..1

symbols: List<String> = "exists", "∃"

1..1
(redefined)

identifier: String = "exists"

1..1
(redefined)

evaluation_agent: FUNCTION<<Any>,Boolean>

4.3.3.5. FUNCTION_DEF_EXTERNAL Class

Class

FUNCTION_DEF_EXTERNAL (abstract)

Description

Ancestor class of externally defined function types.

Inherit

FUNCTION_DEF

Attributes

Signature

Meaning

1..1

evaluation_agent: FUNCTION<TUPLE,Any>

4.3.3.6. FUNCTION_DEF_EXAMPLE Class

Class

FUNCTION_DEF_EXAMPLE

Description

Example of an externally defined function of signature:

func (v: Numeric): Numeric

Inherit

FUNCTION_DEF_EXTERNAL

Attributes

Signature

Meaning

1..1
(redefined)

identifier: String = "funny"

1..1
(redefined)

evaluation_agent: FUNCTION<<Numeric>,Numeric>

4.4. Typing

The expressions.types package is shown below.

BASE expression.types
Figure 3. baes.expression.types Package

The type system of the Expression formalism is defined via the type EXPR_TYPE_DEF and its descendants. All types have a type_name and a type_anchor, which is a variable of the coresponding primtive type from within the openEHR base_types package, i.e. Integer, Real, String, etc. The type anchor can be used for testing assignments within the implementation. A special type TYPE_DEF_OBJECT is included to enable a value reference (descendant of EXPR_VALUE_REF) to refer to a complex object, to which some expression operators can be applied, including equality and existence. The types system is extensible, as described below.

4.4.1. Class Descriptions

4.4.1.1. EXPR_TYPE_DEF Class

Class

EXPR_TYPE_DEF (abstract)

Description

Ancestor class for type definitions known in the openEHR Expression formalism.

Attributes

Signature

Meaning

1..1

type_name: String

Natural language type name of this type as used in abstract rules syntax variable declarations.

1..1

type_anchor: Any

Attribute of the openEHR primitive type (or Any) corresponding to this type definition meta-type.

4.4.1.2. TYPE_DEF_BOOLEAN Class

Class

TYPE_DEF_BOOLEAN

Description

Rules meta-type representing the primitive type Boolean.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Boolean"

1..1
(redefined)

type_anchor: Boolean

4.4.1.3. TYPE_DEF_INTEGER Class

Class

TYPE_DEF_INTEGER

Description

Rules meta-type representing the primitive type Integer.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Integer"

1..1
(redefined)

type_anchor: Integer

4.4.1.4. TYPE_DEF_REAL Class

Class

TYPE_DEF_REAL

Description

Rules meta-type representing the primitive type Real.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Real"

1..1
(redefined)

type_anchor: Real

4.4.1.5. TYPE_DEF_DATE Class

Class

TYPE_DEF_DATE

Description

Rules meta-type representing the primitive type Date.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Date"

1..1
(redefined)

type_anchor: IDate

4.4.1.6. TYPE_DEF_DATE_TIME Class

Class

TYPE_DEF_DATE_TIME

Description

Rules meta-type representing the primitive type Date_time.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Date_time"

1..1
(redefined)

type_anchor: IDate_time

4.4.1.7. TYPE_DEF_TIME Class

Class

TYPE_DEF_TIME

Description

Rules meta-type representing the primitive type Time.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Time"

1..1
(redefined)

type_anchor: ITime

4.4.1.8. TYPE_DEF_DURATION Class

Class

TYPE_DEF_DURATION

Description

Rules meta-type representing the primitive type Duration.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Duration"

1..1
(redefined)

type_anchor: IDuration

4.4.1.9. TYPE_DEF_STRING Class

Class

TYPE_DEF_STRING

Description

Rules meta-type representing the primitive type String.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "String"

1..1
(redefined)

type_anchor: String

4.4.1.10. TYPE_DEF_URI Class

Class

TYPE_DEF_URI

Description

Rules meta-type representing the primitive type Uri.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Uri"

1..1
(redefined)

type_anchor: Integer

4.4.1.11. TYPE_DEF_TERMINOLOGY_CODE Class

Class

TYPE_DEF_TERMINOLOGY_CODE

Description

Rules meta-type representing the primitive type Terminology_code.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Terminology_code"

1..1
(redefined)

type_anchor: Terminology_code

4.4.1.12. TYPE_DEF_OBJECT_REF Class

Class

TYPE_DEF_OBJECT_REF

Description

Rules meta-type representing the type Object_ref, which is assumed to by the type of any non-primitive reference target within a rule.

Inherit

EXPR_TYPE_DEF

Attributes

Signature

Meaning

1..1
(redefined)

type_name: String = "Object_ref"

Appendix A: Syntax Specification

The grammar and lexical specification for the standard Expression syntax is shown below in ANTLR4 form.  

//
//  description: Antlr4 grammar for openEHR Rules core syntax.
//  author:      Thomas Beale <thomas.beale@openehr.org>
//  contributors:Pieter Bos <pieter.bos@nedap.com>
//  support:     openEHR Specifications PR tracker <https://openehr.atlassian.net/projects/SPECPR/issues>
//  copyright:   Copyright (c) 2016- openEHR Foundation <http://www.openEHR.org>
//  license:     Apache 2.0 License <http://www.apache.org/licenses/LICENSE-2.0.html>
//

grammar base_expressions;
import cadl_primitives, odin_values;

//
//  ======================= Top-level _objects ========================
//

statement_block: statement+ EOF ;

// ------------------------- statements ---------------------------
statement: declaration | assignment | assertion;

declaration:
      variable_declaration
    | constant_declaration
    ;

variable_declaration: local_variable ':' type_id ( SYM_ASSIGNMENT expression )? ;

constant_declaration: constant_name ':' type_id  ( SYM_EQ primitive_object )? ;

assignment:
      binding
    | local_assignment
    ;

//
// The following is the means of binding a data context path to a local variable
// TODO: remove this rule when proper external bindings are supported
binding: local_variable SYM_ASSIGNMENT bound_path ;

local_assignment: local_variable SYM_ASSIGNMENT expression ;

assertion: ( ( ALPHA_LC_ID | ALPHA_UC_ID ) ':' )? boolean_expr ;

//
// -------------------------- _expressions --------------------------
//
expression:
      boolean_expr
    | arithmetic_expr
    ;

//
// _expressions evaluating to boolean values, using standard precedence
// The equality_binop ones are not strictly necessary, but allow the use
// of boolean_leaf = true, which some people like
//
boolean_expr:
      SYM_NOT boolean_expr
    | boolean_expr SYM_AND boolean_expr
    | boolean_expr SYM_XOR boolean_expr
    | boolean_expr SYM_OR boolean_expr
    | boolean_expr SYM_IMPLIES boolean_expr
    | boolean_leaf equality_binop boolean_leaf
    | boolean_leaf
    ;

//
// Atomic Boolean-valued expression elements
// TODO: SYM_EXISTS alternative to be replaced by defined() predicate
boolean_leaf:
      boolean_literal
    | for_all_expr
    | there_exists_expr
    | SYM_EXISTS ( bound_path | sub_path_local_variable )
    | '(' boolean_expr ')'
    | relational_expr
    | equality_expr
    | constraint_expr
    | value_ref
    ;

boolean_literal:
      SYM_TRUE
    | SYM_FALSE
    ;

//
//  Universal and existential quantifier
// TODO: 'in' probably isn't needed in the long term
for_all_expr: SYM_FOR_ALL VARIABLE_ID ( ':' | 'in' ) value_ref '|'? boolean_expr ;

there_exists_expr: SYM_THERE_EXISTS VARIABLE_ID ( ':' | 'in' ) value_ref '|'? boolean_expr ;

// Constraint expressions
// This provides a way of using one operator (matches) to compare a
// value (LHS) with a value range (RHS). As per ADL, the value range
// for ordered types like Integer, Date etc may be a single value,
// a list of values, or a list of intervals, and in future, potentially
// other comparators, including functions (e.g. divisible_by_N).
//
// For non-ordered types like String and Terminology_code, the RHS
// is in other forms, e.g. regex for Strings.
//
// The matches operator can be used to generate a Boolean value that
// may be used within an expression like any other Boolean (hence it
// is a booleanLeaf).
// TODO: non-primitive objects might be supported on the RHS in future.
constraint_expr: ( arithmetic_expr | value_ref ) SYM_MATCHES '{' c_inline_primitive_object '}' ;

//
// _expressions evaluating to arithmetic values, using standard precedence
//
arithmetic_expr:
      <assoc=right> arithmetic_expr '^' arithmetic_expr
    | arithmetic_expr ( '/' | '*' | '%' ) arithmetic_expr
    | arithmetic_expr ( '+' | '-' ) arithmetic_expr
    | arithmetic_leaf
    ;

arithmetic_leaf:
      integer_value
    | real_value
    | date_value
    | date_time_value
    | time_value
    | duration_value
    | value_ref
    | '(' arithmetic_expr ')'
    ;

//
// Equality expression between any arithmetic value; precedence is
// lowest, so only needed between leaves, since () will be needed for
// larger expressions anyway
//
equality_expr: arithmetic_expr equality_binop arithmetic_expr ;

equality_binop:
      SYM_EQ
    | SYM_NE
    ;

//
// Relational expressions of arithmetic operands generating Boolean values
//
relational_expr: arithmetic_expr relational_binop arithmetic_expr ;

relational_binop:
      SYM_GT
    | SYM_LT
    | SYM_LE
    | SYM_GE
    ;

//
// instances references: data references, variables, and function calls.
// TODO: Remove bound_path from this rule when external binding supported
//
value_ref:
      function_call
    | bound_path
    | sub_path_local_variable
    | local_variable
    | constant_name
    ;

local_variable: VARIABLE_ID ;

// TODO: change to [] form, e.g.     book_list [{title.contains("Quixote")}]
sub_path_local_variable: VARIABLE_WITH_PATH;

// TODO: Remove this rule when external binding supported
bound_path: ADL_PATH ;

constant_name: ALPHA_UC_ID ;

function_call: ALPHA_LC_ID '(' function_args? ')' ;

function_args: expression ( ',' expression )* ;

type_id: ALPHA_UC_ID ( '<' type_id ( ',' type_id )* '>' )? ;


//
// ---------- Lexer definitions ----------
//

// ---------- lines and comments ----------
CMT_LINE   : '--' .*? EOL -> skip ;             // increment line count
EOL        : '\r'? '\n'   -> channel(HIDDEN) ;  // increment line count
WS         : [ \t\r]+     -> channel(HIDDEN) ;

// --------- symbols ----------
SYM_ASSIGNMENT: ':=' | '::=' ;

SYM_NE : '/=' | '!=' | '≠' ;
SYM_EQ : '=' ;
SYM_GT : '>' ;
SYM_LT : '<' ;
SYM_LE : '<=' | '≤' ;
SYM_GE : '>=' | '≥' ;

SYM_THEN     : [Tt][Hh][Ee][Nn] ;
SYM_AND      : [Aa][Nn][Dd] | '∧' ;
SYM_OR       : [Oo][Rr] | '∨' ;
SYM_XOR      : [Xx][Oo][Rr] ;
SYM_NOT      : [Nn][Oo][Tt] | '!' | '~' | '¬' ;
SYM_IMPLIES  : [Ii][Mm][Pp][Ll][Ii][Ee][Ss] | '⇒' ;
SYM_FOR_ALL  : 'for_all' | '∀' ;
SYM_THERE_EXISTS: 'there_exists' | '∃' ;
SYM_EXISTS   : 'exists' ;
SYM_MATCHES  : [Mm][Aa][Tt][Cc][Hh][Ee][Ss] | [Ii][Ss]'_'[Ii][Nn] | '∈' ;

// TODO: remove when [] path predicates supported
VARIABLE_WITH_PATH: VARIABLE_ID ADL_ABSOLUTE_PATH ;

VARIABLE_ID: '$' ALPHA_LC_ID ;

References

Hein, J. L. (2002). Discrete Structures, Logic and Computability (Second.). Jones and Bartlett.

Kilov, H., & Ross, J. (1994). Information Modelling - an object-oriented approach. Prentice Hall.

Smith, G. (2000). The Object Z Specification Language. Kluwer Academic Publishers. Retrieved from http://www.itee.uq.edu.au/ smith/objectz.html

Sowa, J. F. (2000). Knowledge Representation: Logical, philosophical and Computational Foundations. California: Brooks/Cole.