ASSERTION in openehr_expression_104.bmm is not in accordance with the specifications

Sorry, we haven’t done much interoperability with the assertions section. It will need some work in archie to get the EXPR_ added again for the classes that do exists, to get all the class names correct., plus the other fixes. I do not have time to do that soon, I hope to have a bit more time next year to fix this.

1 Like

Now fixed in ITS-BMM - thanks for the report!

Thank you for the quick fix.

I guess the above comment was missed. Here is a longer version :sweat_smile:

May I ask for these to be updated too please :blush:

p.s. I’m putting these BMMs to good use. I can already generate JSON for a COMPOSITION with all the necessary code generated based on the BMM files. Soon any web developer will be able to build an openEHR app without knowing anything about AM/RM. A little scary but this is what they asked for.
Technicaly it will be a big win for computable specifications.

VARIABLE_DECLARATION:

  • “type” should have “is_mandatory=true”

ASSIGNMENT:

  • “target” should have “is_mandatory=true”

  • “source” should have “is_mandatory=true”

EXPR_OPERATOR:

  • missing “operator_def: OPERATOR_DEF” with “is_mandatory=true”

EXPR_UNARY_OPERATOR:

  • missing “operand: EXPRESSION” with “is_mandatory=true”

EXPR_BINARY_OPERATOR:

  • missing “left_operand: EXPRESSION” with “is_mandatory=true”

  • missing “right_operand: EXPRESSION” with “is_mandatory=true”

OPERATOR_DEF:

  • missing class

OPERATOR_DEF_BUILTIN:

  • missing class

BUILTIN_OPERATORS:

  • missing class

EXPR_LITERAL:

  • “item” should have “is_mandatory=true”

EXPR_VARIABLE_REF:

  • “item” should have “is_mandatory=true”

EXPR_FUNCTION:

  • missing “arguments”

  • missing “function_def” with “is_mandatory=true”

FUNCTION_DEF:

  • missing class

FUNCTION_DEF_BUILTIN:

  • missing class

BUILTIN_FUNCTIONS:

  • missing class

EXPR_TYPE_DEF:

  • missing “type_name” with “is_mandatory=true”

  • missing “type_anchor” with “is_mandatory=true”

  • other classes in 4.4. Typing are missing

4.3. Extension Package:

  • these classes are missing too (I’m not sure I’ve seen them used in OPTs)

I’ve made changes to the Expression 1.0.4 BMM; it doesn’t include the extension classes, but it includes everything else and compiles in ADL Workbench.

NB: this version of the expression language is old, incomplete, contains some logical errors and will not be supported in the future - so I would suggest to not create any new work based on it.

Thank you!

One small correction is needed:

  • ASSERTION.expression type should be “EXPRESSION”.

And I had to add these classes to the “packages” for my validator to be happy:

"TYPE_DEF_BOOLEAN", "TYPE_DEF_INTEGER", "TYPE_DEF_REAL", "TYPE_DEF_DATE", "TYPE_DEF_DATE_TIME",
"TYPE_DEF_TIME", "TYPE_DEF_DURATION", "TYPE_DEF_STRING", "TYPE_DEF_URI", "TYPE_DEF_TERMINOLOGY_CODE",
"TYPE_DEF_OBJECT_REF", "OPERATOR_DEF", "FUNCTION_DEF", "OPERATOR_DEF_BUILTIN", "FUNCTION_DEF_BUILTIN",
"BUILTIN_OPERATORS", "BUILTIN_FUNCTIONS"

@pieterbos I’m struggling with OPT2 JSON definition of “EXPR_BINARY_OPERATOR”. Its “left_operand” and “right_operand” should be of type “EXPRESSION” but I found type “MODEL_REFERENCE”:

"left_operand" : {
    "@type" : "MODEL_REFERENCE",
    "precedence_overridden" : false,
    "path" : "archetype_id/value"
},

This type is present in “openEHR_aom_206-generated.bmm” but I don’t find it in the standard AM.

The closest one is “EXPR_LITERAL” but with attribute “item” instead of “path” (path is mentioned in the description of EXPR_LEAF but never used as an attribute name).

It is too late too finish this tonight (and maybe I get a hint from somebody).

This is an example of ASSERTION that doesn’t look right to me (scroll to see everything):

"includes" : [ {
  "@type" : "ASSERTION",
  "string_expression" : "archetype_id/valuematches{/.*/}",
  "expression" : {
    "@type" : "BINARY_OPERATOR",
    "type" : "BOOLEAN",
    "precedence_overridden" : false,
    "operator" : "matches",
    "right_operand" : {
      "@type" : "CONSTRAINT",
      "precedence_overridden" : false,
      "item" : {
        "@type" : "C_STRING",
        "rm_type_name" : "string",
        "node_id" : "id9999",
        "constraint" : [ "/.*/" ],
        "logical_path" : "/",
        "allowed" : true,
        "required" : false,
        "prohibited" : false,
        "attributes" : [ ],
        "path" : "/"
      }
    },
    "left_operand" : {
      "@type" : "MODEL_REFERENCE",
      "precedence_overridden" : false,
      "path" : "archetype_id/value"
    },
    "unary" : false
  },
  "variables" : [ ]
} ],

@pieterbos Was this a solution to get something working with AM 2.0.6 and “openehr_expression_104.bmm” and it doesn’t conform to the specifications? Or am I missing something?

@thomas.beale Are there any OPT2s for AM >= 2.1.0 that you are aware of?

The implementation in Archie of the expression language is what was defined in the standard at the time, which was implemented because we needed a standard way to write the rules section and this was specified in 2.0.6. Thomas then changed the standard, assuming it wasn’t in use. It had at least two implementations, one in ADL 2 and one in ADL 1.4, so the specification was put back in place as good as was possible. So the object model in Archie could be a near match rather than an exact one.

So while it is ‘old’, it is also the only standard that is implemented to express rules in archetypes. Thomas wants something new, but there is still ongoing debate over what it should be. Personally I think it should perhaps be something existing and really not something openehr specific, especially if it is to have more features than the current mini-language.

The Model reference is to express a path in the rules language, so /data[id3]/something would be a model reference. This was following the object model specification at that point in time, and is an expression sub-type. It is apparently called EXPR_ARCHETYPE_REF in the current sspecification, defined here: Archetype Object Model 2 (AOM2). the Archie implementation contains a variable prefix as an addition to the path, because that can appear in the language, specifically but not limited to inside for_all statements.

1 Like

Thank you for the explanation! It makes sense. Real life implementations usually move faster than specifications.

I learned that there are no standard expression models/grammars. In my latest project before I started with openEHR, I wrote an ANTLR4 grammar for an existing programming language. I looked at popular languages and realized they all use different grammars for the expressions.

@thomas.beale The mentioned EXPR_ARCHETYPE_REF is missing in AM 2.1.0 and 2.2.0 BMM files.


It looks to me that when ARCHETYPE_SLOT is reached the pattern of attributes/children isn’t followed anymore and a custom structure is used for ARCHETYPE_SLOT and ASSERTION :thinking:

I’ll try to work with that or I’ll have to write an OPT2 parser :sweat_smile:

@thomas.beale Should I use the new grammars if I decide to write the OPT2 parser?

Just to be clear - it was just a draft i.e. in development - there was never any release - that’s why we have created this BASE 1.0.4 release retrospectively. (Here’s the last commit to BASE that contained the old Expression language, before we inserted the 1.0.4 branch; within this, the Expression language spec was in DEVELOPMENT phase).

well - development on the draft spec just continued. I wouldn’t want anyone to think we don’t have reliable release and versioning processes - we do, but we didn’t use them here. It may have also been the case that the specifications development lifecycle was not as obvious as it should be - for the record it is documented here.

yes - unfortunately, I didn’t know this. If I had, I would have suggested to the SEC to agree on a releasable version of this spec, so that implementations were connected to something stable.

We have much better communication these days, and I don’t think we’ll get into this situation again. We’ve put a fair bit of collective effort into getting this old Expressions spec stabilised retrospectively, so it indeed corresponds as closely as possible to these implementations.

It has been somewhat annoying for everyone, but I guess a good lesson on the importance of getting releasing right.

well, it’s not just some aesthetic preference - this older draft language was never finished, primarily because it contains some design problems. I had not performed a sufficiently full analysis to fix these at the time, so it remained in that state. The new version is:

  • a) based on a much better analysis (including review of features in various other languages);
  • b) has made much better use of current Antlr tools (4.9) which significantly improved a lot of details (more than I had expected I must admit);
  • c) incorporates features that cover the semantics in use by the existing 4 separate expression languages in openEHR (ADL rules; AQL; Task Planning expressions; GDL2)

Well I and others have had a pretty careful look at doing that. The research project I worked at in Intermountain Healthcare doing advanced workflow looked at various existing languages including Java and TypeScript, and decided they did not do what was needed; they rolled their own expression language and had it running in a couple of weeks.

In the Task Planning project, Better went a step further and actually implemented a TypeScript execution container and converted (the relatively simple) expressions from string statements within a Task Plan into a TS internal representation for execution (I suspect via some JSON conversion that was then materialised by the container implementation). This was found to be seriously slow (probably fixable), but also missing some needed operators and features (not fixable). So they also decided to go native.

Implementing a language these days is relatively easy - that’s one reason there are so many programming languages around. The speed at which a language definition can be developed, tested and implemented is much higher than it used to be.

The approach I have taken in more recent times is based on a) a high quality meta-model aka ‘enhanced AST’ (= current version of BMM) and b) a full syntax implementation (= current LANG/Expression Language) in Antlr 4.x, to ensure that there is at least one working syntax. The meta-model provides the semantics, as well as the ability to serialise out to other syntaxes / languages.

This version of BMM and its associated (new) expression language is pretty generic and when it is completed (mainly the expression syntax part), will at least be a candidate for a common expression and decision language for openEHR. There is nothing stopping other development proposals from being created. At the end, the SEC will need to review and consider what to release for the wider community.

Others may have different opinions and approaches - these are all welcome from my point of view. We have the Specifications Editorial Committee to make determinations on a way forward based on what is available at any given time. THis is as it should be I think.

BTW: there is a call-out facility in the new BMM to enable a routine (i.e. set of statements / expressions) to be implemented using any language, which will take care of a) re-use of existing code and b) orgs that want to write expressions in current programming languages. This approach won’t be highly interoperable for decision logic, Task Planning, GDL3 etc, but it will be useful for other purposes.

1 Like

Yes exactly right. The most advanced meta-model I have developed to date is the current BMM, which covers expressions but also literals, full routines, classes, lambdas and so on. The current (incomplete) Expression Language is designed as a syntax for this BMM. I am developing this via Antlr4, which is enabling me to correct both the meta-model and devise a workable syntax. You will notice near the end it has some tabular decision syntax (no if/then statements) as well as other features needed in decision logic.

1 Like

Now fixed.

Not sure of the problem there - these classes were in the package class list (the ADL Workbench won’t pass the model without them either). However, I did correct the structure to put the types in a sub-package as stated in the spec.

1 Like

A bit off-topic, but where is this attribute @type coming from? (which serializer)

Is this not rather _type ?

It is from the otherwise very useful Nedap’s VSCode extension (thank you @pieterbos for it). I believe they are busy with their production systems and don’t have time to update Archie to the current version of the specifications.


Since my OPERATIONAL_TEMPLATE code for de-serialization from JSON is generated from the specifications it expects OPT2 JSON to exactly follow the specifications.

Hence I started writing my own ADL2 to OPT2 JSON converter :blush:

I use the new grammars in github.com/wolandscat/openEHR-antlr4 to parse OPT2 files.

According to ARCHETYPE_SLOT.includes should be of type ASSERTION. The grammar parses it as DELIMITED_REGEX (the comment mentions ASSERTION from EL but it probably isn’t ready yet):

// Slot includes are modelled to support only the simple form of
// path matches {regex}, but this is probably safe. If not, the
// 'assertion' rule from EL is required, which causes the whole EL
// to be sucked in to CADL.
archetypeSlot: SYM_ALLOW_ARCHETYPE rmTypeId nodeId (( cOccurrences? ( SYM_MATCHES '{' cIncludes? cExcludes? '}' )? ) | SYM_CLOSED ) ;
cIncludes : SYM_INCLUDE archetypeIdConstraint+ ;
cExcludes : SYM_EXCLUDE archetypeIdConstraint+ ;
archetypeIdConstraint: archetypeIdPath SYM_MATCHES '{' DELIMITED_REGEX '}' ;

The “stable” version of CADL grammar uses the EXPRESSION expected by AM 2.0.6:

assertion: variableDeclaration | booleanAssertion;
variableDeclaration: VARIABLE_DECLARATION SYM_ASSIGNMENT expression;
booleanAssertion: ( identifier SYM_COLON )? expression ;

I should probably switch to the stable grammars :thinking:

@thomas.beale Unless you wish somebody to test the new ones? :wink:

I switched to adl-antlr grammars.

The ASSERTION part produces this parse tree:

It doesn’t look like any EXPR_ class in AM 2.0.6.

AM 2.0.6 has EXPR_BINARY_OPERATOR. It has left_operand and right_operand of type EXPRESSION. Which sub-types should be used based on the parse tree above?

Should a BINARY_OPERATOR (that was added to Archie’s version of AM 2.0.6) also be added to the official AM 2.0.6?

Adding BINARY_OPERATOR would make Archie’s OPT2 JSON compatible with AM 2.0.6 and the ASSERTION parse tree could be converted into it.

There is a similar EL_BINARY_OPERATOR in the latest release.

I would like to stick to the official BMMs. Otherwise there will be Archie AM, NeoEHR AM,…

Unfortunately you’ll probably have to wait until Christmas / New year before I get back to those grammars - they are a lot better than the current ones, but the Expression part is a) not finished and b) is the new Expression language, not the AM 2.0.6 one. So if you want stability and sanity, … you know what to do :wink:

A report on how I solved the issue of ASSERTION in OPT2 JSON.

ASSERTION.expression is of type EXPRESSION.

“openehr_expression_104.bmm” has EXPR_BINARY_OPERATOR. I used this type for the ASSERTION.expression.

EXPR_BINARY_OPERATOR has left_operand and right_operand of type EXPRESSION.
I used EXPR_LITERAL for “left_operand” and “right_operand” (as mentioned by Pieter EXPR_ARCHETYPE_REF and other needed EL_ classes are only found in the unpublished specifications).

I used OPERATOR_DEF_BUILTIN for the “operator_def”.

In the end I could use only the standard classes in the “openehr_expression_104.bmm”:

{
  "_type": "ASSERTION",
  "string_expression": "archetype_id/valuematches{/.*/}",
  "expression": {
    "_type": "EXPR_BINARY_OPERATOR",
    "left_operand": {
      "_type": "EXPR_LITERAL",
      "item": {
        "_type": "C_STRING",
        "rm_type_name": "String",
        "node_id": "id9999",
        "constraint": [ "archetype_id/value" ]
      }
    },
    "operator_def": {
      "_type": "OPERATOR_DEF_BUILTIN",
      "identifier": "matches",
      "op_table": { "op_matches": [ "matches" ] }
    },
    "right_operand": {
      "_type": "EXPR_LITERAL",
      "item": {
        "_type": "C_STRING",
        "rm_type_name": "String",
        "node_id": "id9999",
        "constraint": [ "/.*/" ]
      }
    }
  }
}

It is similar to OPT2 JSON produced by Nedap’s VS Code extension.

p.s.
Thank you @pieterbos for using C_STRING in your version of OPT2 JSON. I first tried with STRING but couldn’t make it work. Then I saw you used C_STRING and that solved it for me too.

Should the OPERATOR_DEF_BUILTIN inherit from BUILTIN_OPERATORS?

I believe the BUILTIN_OPERATORS should be an enumeration (like OPERATOR_KIND in AOM1.4).

Then the OPERATOR_DEF.identifier would be of type BUILTIN_OPERATORS/OPERATOR_KIND (like it is in AOM1.4 EXPR_OPERATOR.operator.

The JSON would be cleaner:

"operator_def": {
  "_type": "OPERATOR_DEF_BUILTIN",
  "identifier": "matches"
},

op_table is static, so it acts like a singleton object. The owning class just populates the table at startup time. You could also use the GoF Singleton pattern to achieve the same effect (which doesn’t use inheritance). In most languages these days you don’t need to inherit, you can just reference BUILTIN_OPERATORS.op_table(or in Java - BuiltinOperators.opTable().#

It’s not an enumeration though, it’s a table of the form:

|"plus"       | "+ "
|"multiply"   | "*", "."
etc

Any logical operator might have a number of alternative symbols.

I sometimes forget how inexperienced I must sound with such questions :flushed:

We both know that BUILTIN_OPERATORS is a singleton just by looking at the model.
However I’m asking for a friend - my generator :wink:

The generator knows only what is in the computable specifications - the BMM files.

I posted the question because there is no information that BUILTIN_OPERATORS is a singleton or that op_table is static in the BMM.

BUILTIN_OPERATORS is the second ancestor of the OPERATOR_DEF_BUILTIN.
OPERATOR_DEF_BUILTIN “extends” OPEREATOR_DEF and “implements” BUILTIN_OPERATORS.

Since the BUILTIN_OPERATORS.op_table is not marked as static it needs to be “implemented” in OPERATOR_DEF_BUILTIN. The only way I know how to “implement” a property from an ancestor is by adding it to the OPERATOR_DEF_BUILTIN class (I tried with mixins but that didn’t work out).

BUILTIN_OPERATORS.op_table is marked as mandatory in BMM which means it must be present for every OPERATOR_DEF_BUILTIN instance.

A solution would be to add “is_im_singleton” and/or “is_im_static” to the BMM classes/attributes. Then BMMs would have the same information as we. I’m not sure if that is a realistic expectation?

So I have two options with the current BMM:

  • remove BUILTIN_OPERATORS as the second ancestor of OPERATOR_DEF_BUILTIN
  • remove “is_mandatory” for BUILTIN_OPERATORS.op_table so that the “fromJson()” doesn’t report an error if it is missing in OPT2 JSON.

I’m aware of only one other class that has to “implement” properties found in its ancestors. It is where AUTHORED_ARCHETYPE “extends” ARCHETYPE and “implements” AUTHORED_RESOURCE which has mandatory properties.
The generator can handle this scenario even for reading the OPERATIONAL_TEMPLATE from JSON.

Edit:
The problem is setting mandatory properties at the instantiation of OPERATIONAL_TEMPLATE instance:

  • OPERATIONAL_TEMPLATE constructor can call its ancestor’s constructor in AUTHORED_ARCHETYPE.

  • AUTHORED_ARCHETYPE constructor can call its ancestor’s constructor in ARCHETYPE.

  • AUTHORED_ARCHETYPE constructor cannot call its second ancestor’s constructor in AUTHORED_RESOURCE.

  • Thus AUTHORED_RESOURCE’s mandatory properties cannot be set when creating an instance of OPERATIONAL_TEMPLATE.