Code Monkey home page Code Monkey logo

firely-cql-sdk's People

Contributors

alexzautke avatar awalley-ncqa avatar basetwo avatar cheng13231 avatar damienm419 avatar damienmosier avatar dependabot[bot] avatar drivin-ncqa avatar evanmachusak avatar evanmachusakncqa avatar ewoutkramer avatar fweiand-ncqa avatar gabisonia avatar gshakya-ncqa avatar igajurel23 avatar igajurelncqa avatar marcovisserfurore avatar mmsmits avatar mryanncqa avatar patburke234 avatar pburkencqa avatar richfirely avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

firely-cql-sdk's Issues

CqlTags aren't being written during packaging for annotations

When calling the PackagerCLI, CQL files with annotations aren't having the CqlTag attribute added to the output C# files.

To Reproduce
The following CQL will not generate the CqlTag attributes in the converted C# files.


/*
* @group: 1
* @population: initial-population
*/
define "Initial population":
    true

Expected behavior
The CqlTag attributes will be present for group and population attributes.

Version used:
Latest code branch available

Age/AgeAt not implemented

We have implemented the operation, but the ExpressionBuilder will not recognize the Age/AgeAt ELM nodes.

Unit conversion is not adhering to the specification

Our SDK performs unit equivalency for syntactic date units by normalizing them immediately to their UCUM counterparts.

For example, CqlDate line 208

This behavior is contrary to the Author's Guide §4.2.1 Quantities.

In all cases, we should preserve the original unit and instead whenever it is required to compare units as equal or equivalent, we should invoke a not yet implemented UnitComparer that will compare UCUM and syntactic units per the specification.

Here's some CQL that could reproduce it:

define function "Is year precision"(quantity System.Quantity): quantity.unit = 'a'
define "Year equal to 'a'": "Is year precision"(1 year)

In our current implementation, Year equal to 'a' returns true where pursuant to the specification, it should return false:

1 year is not equal (=) to 1 'a' (defined in UCUM as 365.25 'd'), but it is equivalent (~) to 1 'a'.

CodeSystem needs the same treatment as ValueSets

CodeSystemRef isn't implemented currently, which means this code will not work:

library RefTest version '1.0.0'

private codesystem CS: 'http://fire.ly' version '1.0.0'

define private "Code system ref": "CS"
define "in codesystem": System.Code { code: 'sample', system: 'http://fire.ly' } in CS

This is valid code that does not work.

We need a CodeSystemFacade implementation to go along with ValueSetFacade. In addition to in checks against code systems, you can also enumerate the codes in a code system as well with a query pattern e.g.

from CS code return code

Verify we have implemented missing converters for tuples in modelinfo

See #129

We should create some test code that:

  • Dynamically generates Copy T functions for all T of type ClassInfo present in a ModelInfo class which creates a new named tuple and assigns all of its properties to obj's value, e.g.:
define function "Copy Patient"(obj FHIR.Patient):
  FHIR.Patient {
     id: obj.id,
     birthDate: obj.birthDate,
     ... all other elements of Patient follow ...
}
  • Fix the cryptic ArgumentException that gets thrown when a conversion required for the member binding is missing, with information about which conversion is being attempted and not available
  • Add all missing converters for the FHIR model

This will allow us to be sure that we can dynamically create FHIR model tuples in CQL.

Generate C# output files on the fly

Current Problem

During the development of the PackageCLI tool, bugs are discovered on new input sets and fixed, however, as part of fixing one bug in one file, another one pops up in another file. Since there is no output generated until all input files are processed successfully in-memory, it is impossible to know if the first one was fixed correctly.

Solution

Generate files as soon as possible during the processing of input files. Input files need to be ordered deterministically and such that files with no dependencies are run first. Temporary files must be created during the process, after which original output files can be overwritten.

Propose adding another argument for the PackageCLI specifying where input files are generated. Temp files must have a temp file extension, and only after everything is generated, can they replace the orginal files.

Performance with FhriDateTime and strings

We saw a performance issue when converting to Firely SDK that took a deck from 20 minutes to 72 minutes to run. The issue was traced down to FhirDateTime objects.

The FhirTypeConverter.cs contained an FhirdateTime to CqlDateTime converter but never used.

add((M.FhirDateTime f) => f.TryToDateTime(out var dt) ?
    new CqlDateTime(
        dt!.Years!.Value, dt.Months,
        dt.Days, dt.Hours, dt.Minutes, dt.Seconds, dt.Millis,
        dt.HasOffset ? dt.Offset!.Value.Hours : null, dt.HasOffset ? dt.Offset!.Value.Minutes : null) : null);

In the FhirHelpers cql (https://fhir.org/guides/cqf/common/Library-FHIRHelpers.html) we have ToInterval

define function ToInterval(period FHIR.Period):
    if period is null then
        null
    else
        if period."start" is null then
            Interval(period."start".value, period."end".value]
        else
            Interval[period."start".value, period."end".value]

The ".value" portion of the CQL creates lambda's like this where the value is referenced.

.If ($var8 != null) {
                                (.Extension<Hl7.Cql.Compiler.NullConditionalMemberExpression> {
                                    .Block(Hl7.Fhir.Model.Period $var10) {
                                        $var10 = $period;
                                        .If ($var10 != null) {
                                            $period.StartElement
                                        } .Else {
                                            .Default(Hl7.Fhir.Model.FhirDateTime)
                                        }
                                    }
                                }).Value

In the ExpressionBuilder.cs file there is a PropertyHelper the value is accessed as a string and eventually drills down and calls CqlOperators.ConversionFunctionName to get "ConvertStringToFhirDateTime" which parses the date into the DateTimeIso8601.TryParse. This uses regex to parse the date. After profiling this is indicative of a huge performance issue.

Need to implement a way to indicate not to drill down into the "value" property and to use the source object (FhirDateTime) so the converter is used to parse the date.

ToInterval in ExpressionBuilders is needed for CMS FollowUpCareforChildrenPrescribedADHDMedicationADDFHIR-0.1.000

Describe the bug
Hl7.Cql.Compiler.ExpressionBuilder[0] Building expressions for FollowUpCareforChildrenPrescribedADHDMedicationADDFHIR-0.1.000 Unhandled exception. System.NotImplementedException: The method or operation is not implemented. at Hl7.Cql.Compiler.ExpressionBuilder.GetFunctionRefReturnType(FunctionRef op, IEnumerable`1 operandTypes, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2466 at Hl7.Cql.Compiler.ExpressionBuilder.FunctionRef(FunctionRef op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2325 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 584 at Hl7.Cql.Compiler.ExpressionBuilder.As(As as, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.TypeOperators.cs:line 55 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 443 at Hl7.Cql.Compiler.ExpressionBuilder.<>c__DisplayClass115_0.<FunctionRef>b__0(Expression operand) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2320 at System.Linq.Enumerable.SelectArrayIterator`2.ToArray() at Hl7.Cql.Compiler.ExpressionBuilder.FunctionRef(FunctionRef op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2319 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 584 at Hl7.Cql.Compiler.ExpressionBuilder.IncludedIn(IncludedIn e, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.IntervalOperators.cs:line 174 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 623 at Hl7.Cql.Compiler.ExpressionBuilder.SingleSourceQuery(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1102 at Hl7.Cql.Compiler.ExpressionBuilder.Query(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1029 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 806 at Hl7.Cql.Compiler.ExpressionBuilder.Build() in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 360 at Hl7.Cql.Packaging.LibraryPackager.PackageResources(DirectoryInfo elmDirectory, DirectoryInfo cqlDirectory, DirectedGraph packageGraph, TypeResolver typeResolver, OperatorBinding operatorBinding, TypeManager typeManager, Func`2 canon, ILoggerFactory logFactory) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/LibraryPackager.cs:line 157 at Hl7.Cql.Packaging.ResourcePackager.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, Action`1 afterPackageMutator) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/ResourcePackager.cs:line 42 at Hl7.Cql.Packager.Program.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, DirectoryInfo csDir, DirectoryInfo fhirDir) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 109 at Hl7.Cql.Packager.Program.Main(String[] args) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 77

To Reproduce
Measures are committed to branch inspect-cms-measures. Executing the PackagerCli in that branch will trigger the error.

Version used:
Current develop version of the PackagerCli

Demo project does not build on non-Windows OS

Describe the bug
We tried running the demo project on a Mac laptop, this is what we found:

  • There is no bin directory included with the Maven distribution, and even if it was, we cannot use it on Mac.
  • We got JNI errors until we ran the java bits on openJSK 17
  • There are backward-slashed-paths everywhere in the csproj files and build targets.
  • The PackagerCLI is invoked using PackagerCLI.exe, while this suffix does not work on Mac.
  • The java classpath is using a wildcard (*), in which case the classpath needs to be quoted with single quotes on Mac.

Exception when compiling QICoreCommon-2.0.000

Describe the bug
info: Hl7.Cql.Compiler.ExpressionBuilder[0] Building expressions for QICoreCommon-2.0.000 Unhandled exception. System.InvalidOperationException: Duplicate code detected: billing from Diagnosis Role (http://terminology.hl7.org/CodeSystem/diagnosis-role) at Hl7.Cql.Compiler.ExpressionBuilder.Build() in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 169 at Hl7.Cql.Packaging.LibraryPackager.PackageResources(DirectoryInfo elmDirectory, DirectoryInfo cqlDirectory, DirectedGraph packageGraph, TypeResolver typeResolver, OperatorBinding operatorBinding, TypeManager typeManager, Func`2 canon, ILoggerFactory logFactory) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/LibraryPackager.cs:line 157 at Hl7.Cql.Packaging.ResourcePackager.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, Action`1 afterPackageMutator) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/ResourcePackager.cs:line 42 at Hl7.Cql.Packager.Program.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, DirectoryInfo csDir, DirectoryInfo fhirDir) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 109 at Hl7.Cql.Packager.Program.Main(String[] args) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 77

To Reproduce
Measures are committed to branch inspect-cms-measures. Executing the PackagerCli in that branch will trigger the error.

Version used:
Current develop version of the PackagerCli

Correct all to/from string conversions

Describe the bug
The unit tests only worked on the build server in the US, but not on our Dutch machines. This turns out to be caused by code that does not account for localization, especially where objects are converted to strings and vice versa.

To Reproduce
The code contains constructs like the following:

  • Convert.ChangeType(object, Type), where the object can be a string, and the Type a decimal. In a non-US locale like the Netherlands, Convert.ChangeType("7.0", typeof(decimal)) will result in the decimal 70M, not 7.0M
  • ToString() is called on objects. This will almost always fail. I.e. 18.55.ToString() = "18,55" (COMMA!) in the Netherlands.
  • Interpolation strings like $"{someDecimal}" are used, which causes the same problems as above.
  • Methods like "decimal.TryParse()" are used, which will also not work.

We will need to find all places in the code where this happens. Probably mostly the parse/ToString() routines of the Cql datatypes.

Implement uncertainty

We don't implement uncertainty in the same way as the other two open source implementations, and this will cause testing differences that would be otherwise unresolvable.

We need to replace the use of int?, long?, and decimal? as the default bound types for the ELM System.Integer, System.Long, and System.Decimal types respectively to CQL types like CqlInteger, CqlLong, and CqlDecimal. These new structures need to contain their primitive value, and must also contain a property that can contain the uncertainty range. When the uncertainty range is needed and is populated, the result value is considered uncertain. Uncertainty will need to be taken into account in a myriad of operators, mainly boolean comparison operators.

These types should be implicitly convertible from their respective primitive types. It is not clear whether they should be implicitly convertible to their primitive types, since when they are uncertain, we would be collapsing a range down to a scalar value. It may be reasonable to return null in such cases, or throw a runtime exception. It may also be preferable to make the cast possible but explicit.

We will need comparers and type converters implemented for these new types.

The signatures of all of the ICqlOperators interface will need to have their primitive return types changed to use these structures so that uncertainty can be propagated.

This is a significant change and will result in a large pull request.

Inconsistent behavior with "in" operator

The following CQL code behaves differently when a ValueSetFacade is passed in and when a List<Code> is passed in.

To Reproduce

define fluent function "Code in"(coding FHIR.Coding, codes List<System.Code>):
coding in codes

The C# falls into context.Operators.CodeInList() function and it behaves differently for ValueSetFacade and List<Code>. When ValueSetFacade is passed in, only the code & system are checked. When a List<Code> is passed in then the code, system, version and display are checked.

Expected behavior
The function should behave the same. There needs to be a way to specify that you want the CodeInList operator to behave like Equivalent and not Equals.

Introduce PackageCLI argument for specifying a fixed datetime used in output library resource json files

When the library resource json files are generated, the current datetime is used for the "date" property in the output e.g.
image

This is all good for production purposes, but during development of the code generator, it would be more beneficial to fix this value so that the file stays the same as new features are added, and won't appear as hundreds of files changing in the source repo.

Propose adding an argument for packageCLI to take a datetime value for this, so that a fixed value can be used during development

CQLOperatorsBinding.BindConvert() returning Argument Exception for MedicationRequest.status

Hello,

I'm not sure if this is a bug, but would like to get some feedback on what I'm seeing.

Attached is a CQL function that returns a MedicationRequest resource. After converting it into ELM and running it through the PackagerCLI, I receive the following error:

image

The error occurs in CQLOperatorsBinding.BindConvert(), approximately line 752.

Tracing through the code, it looks like it attempts to determine if it can perform a conversion on MedicationRequest.status prior to the thrown exception. The check returns the conversion type as 'incompatible' because there doesn't appear to be a matching type in the TypeConverter dictionary.

image

I've attached a CSV export of any references to 'MedicationRequest' found in the TypeConverter dictionary.

TypeConverter.csv

Just wanted to see if there is a way to resolve the conversion error. Are there additional type references that need to be added to the TypeConverter in CQL.Conversion? Please let me know if any additional information is needed.

Any help would be appreciated!

CQL and Elm files:
cql_and_elm_example.zip

Call stack:
call_stack.txt

ELM parser for negation is implemented incorrectly

When an expression contains the unary '-', our current parser only accepts literals and quantities and uses string concatenation and multiplication to execute the negation. However, negation is not limited to literals as the following is valid CQL:

library BareMinimum version '0.0.1'
    
using FHIR version '4.0.1'

include cq_core.FHIRHelpers version '4.0.1' called FHIRHelpers

context Patient

define x: 4

define y: -"x"

define z: -5

define q: 4 'kg'

define m: -"q"

The ELM tree should simply use the Negate ELM node instead, and accept any expression with the correct type. See https://cql.hl7.org/04-logicalspecification.html#negate

Implicit DateIso8601 Converters

Describe the bug
There's a strange developer usability issue with DateTimeIso8601.

In the DateTimeIso8601 class, there's an internal String constructor, we did this likely to keep people from using it. However, passing a string to the ctor is still semantically correct because there's an implicit operator override on DateIso8601 (no time) that converts string -> DateIso8601.

So, I have a datetime value as a string, I try to pass it to:
new DateTimeIso8601("2024-01-01T00:00:00")

This implicitly tries to convert it to a DateIso which fails since it has time values in the string.

To Reproduce
new DateTimeIso8601("2024-01-01T00:00:00")

Expected behavior
Instantiation of a DateTimeIso8601

Screenshots
image

Version used:

  • Latest

Create standard function library

In addition to the operators with specific syntax, there is a whole list of "normal" standard functions which are represented using their own ELM nodes. We need to create a "System" library with all these functions consisting of:

Since the System library (much like the System types) is always "included" we do need to add some magic to make sure these are always in scope as well.

Implement implicit casts from List<X> to List<Y>, if X is convertible to Y

Bryn's compiler supports conversions from List<X> to a List<Y>, where X is convertible to Y. Lacking a foreach construct, the actual conversion is carried out using a query.

The fact that this is possible, means it is also allowed to mix List<X> and List<Y>, as in the following List constructor:

define y: { {4}, {6.0} }

The ELM required to make this work looks like this:

 "expression": {
            "type": "List",
            "element": [
              {
                "type": "Query",
                "source": [
                  {
                    "type": "AliasedQuerySource",
                    "expression": {
                      "type": "List",
                      "element": [
                        {
                          "type": "Literal",
                          "locator": "11:14",
                          "resultTypeName": "{urn:hl7-org:elm-types:r1}Integer",
                          "valueType": "{urn:hl7-org:elm-types:r1}Integer",
                          "value": "4"
                        }
                      ],
                      "resultTypeSpecifier": {
                        "type": "ListTypeSpecifier",
                        "elementType": {
                          "type": "NamedTypeSpecifier",
                          "name": "{urn:hl7-org:elm-types:r1}Integer"
                        }
                      },
                      "locator": "11:13-11:15"
                    },
                    "alias": "X"
                  }
                ],
                "return": {
                  "type": "ReturnClause",
                  "expression": {
                    "type": "ToDecimal",
                    "operand": {
                      "type": "AliasRef",
                      "name": "X"
                    }
                  },
                  "distinct": false
                }
              },
              {
                "type": "List",
                "element": [
                  {
                    "type": "Literal",
                    "locator": "11:19-11:21",
                    "resultTypeName": "{urn:hl7-org:elm-types:r1}Decimal",
                    "valueType": "{urn:hl7-org:elm-types:r1}Decimal",
                    "value": "6.0"
                  }
                ],
                "resultTypeSpecifier": {
                  "type": "ListTypeSpecifier",
                  "elementType": {
                    "type": "NamedTypeSpecifier",
                    "name": "{urn:hl7-org:elm-types:r1}Decimal"
                  }
                },
                "locator": "11:18-11:22"
              }
            ],
            "resultTypeSpecifier": {
              "type": "ListTypeSpecifier",
              "elementType": {
                "type": "ListTypeSpecifier",
                "elementType": {
                  "type": "NamedTypeSpecifier",
                  "name": "{urn:hl7-org:elm-types:r1}Decimal"
                }
              }
            },
            "locator": "11:11-11:24"
          },
          "resultTypeSpecifier": {
            "type": "ListTypeSpecifier",
            "elementType": {
              "type": "ListTypeSpecifier",
              "elementType": {
                "type": "NamedTypeSpecifier",
                "name": "{urn:hl7-org:elm-types:r1}Decimal"
              }
            }
          },
          "locator": "11:1-11:24",
          "name": "y",
          "context": "Patient",
          "accessLevel": "Public"
        }
      ]
    },

Note how the type of the outer list is determined to be List<List<Decimal>>, and the elements of the List<Integer> are translated to a List<Decimal> using a query construct.

ELM produced by Java tooling contains error annotations

I have added a check in the ExpressionBuilder so it refuses to build ELM that has error annotations. This makes many of our unit tests fail, since the generated ELM by the java tooling contains errors.

Is this known?

I will now try to add a setting to enable/disable compiling elm with errors.

PackageCLI to take a command line argument to replace the cononical root url '#' with an actual value

Background

The PackageCLI generates library resources as json files (as described here). These files need to be manually updated before sending them to the Create Library endpoint on Firely Server. The url property starts with a # sign, and this needs to be replaced with the actual root url.

Solution

Look at specs and update the PackageCLI to take an additional argument for the root uri and replace the # with that value if it is provided.

Work on ANTLR grammar rules

We need to go over the rules in the CQL grammar one by one to make sure we

  • Translate it correctly
  • Make sure we've implemented the operator involved
  • Test the translation by testing the produced Elm node.
  • Test actually running an expression using the grammar piece

System.ArgumentException: Cannot generate a hash code for Encounter (Parameter 'x')

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.ArgumentException: Cannot generate a hash code for Encounter (Parameter 'x')
   at Hl7.Cql.Comparers.CqlComparers.GetHashCode(Object x)
   at Hl7.Cql.Runtime.CqlOperators.GetHashCode(Object x)
   at Hl7.Cql.Comparers.CqlComparerBridge`1.GetHashCode(T obj)
   at System.Collections.Generic.HashSet`1.AddIfNotPresent(T value, Int32& location)
   at System.Collections.Generic.HashSet`1.Add(T item)
   at System.Linq.Enumerable.UnionIterator`1.StoreFirst()
   at System.Linq.Enumerable.UnionIterator`1.MoveNext()
   at System.Linq.Enumerable.CastIterator[TResult](IEnumerable source)+MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Hl7.Cql.Runtime.CqlOperators.ListUnion[T](IEnumerable`1 left, IEnumerable`1 right)
   at AdultOutpatientEncountersFHIR4_2_2_000.Qualifying_Encounters_Value()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.get_Value()
   at AdultOutpatientEncountersFHIR4_2_2_000.Qualifying_Encounters()

Enable performant filtering of `Bundle.entry` by type

A common usecase is filtering Bundle.Entry entries by type of resource. Filtering on type is currently O(n), since the entries are in a list. We should build a dictionary when adding entries to make it possible to do performant selections on resource type.

CqlDateTimeMath.WholeCalendarPeriodsBetween() - Leap Year/Day Handling

Describe the bug
Method is intended to handle calendar duration calculations for differing precisions.

Lines 143-146.

if (DateTime.IsLeapYear(firstDto.Year) && firstDayInYear > 60)
    firstDayInYear -= 1;
else if (DateTime.IsLeapYear(secondDto.Year) && secondDayInYear > 60)
    secondDayInYear -= 1;

The else on 145 prevents day count from being updated in the event that both years are leap years. This causes downstream calculations to produce unreliable results for cases where 'low' and 'high' are both leap years.

To Reproduce
Example arguments:
low: 4/11/2008
high: 4/10/2024
precision: year/'a'

Both 2008 and 2024 are leap years.
Whole calendar period difference: 15 years (1 day short of 16 calendar years)

Initial case values:
yearDiff: 16
firstDayInYear: 102
secondDayInYear: 101

As written, only firstDayInYear is updated from 102 to 101 resulting in firstDayInYear == secondDayInYear. As a result, yearDiff is not adjusted from 16 to 15. Removing the 'else' allows both day count values to be conditionally updated and resolves the issue.

Expected behavior
Since 'low' and 'high' arguments are leap years, day counts should be adjusted down to account for leap days in cases where firstDayInYear/secondDayInYear fall after 2/29.

CollapseHelper does not currently handle properly included intervals

Description
The current implementation of Collapse does not handle cases when one interval properly includes the other.

Currently, after sorting the intervals using their low value, the intervals are combined two at a time if the intervals meet or overlap.
The resulting interval always uses the low value from the first interval and the high value from the second.
return new CqlInterval<T?>(x.low, y.high, x.lowClosed, y.highClosed);

https://github.com/FirelyTeam/firely-cql-sdk/blob/62ee395cecccca95566d56cefdbc72c6c53825e6/Cql/Cql.Operators/CqlOperators.IntervalOperators.cs#L369C21-L369C90

However, in a scenario when the first interval properly includes the second this causes a bug as the first interval has both the lowest and highest value.

To Reproduce
Steps to reproduce the behavior:

  1. Create two intervals where one is properly included in the other
    define "Interval 1": Interval[22, 28]
    define "Interval 2": Interval[23, 25]

  2. Collapse the intervals
    define "Result": collapse("Interval 1", "Interval 2")

Expected behavior
The expected result is an interval from the lowest value to the highest:
Interval[22,28]
The actual result is an interval with the lowest value from the first interval and the highest value from the second.
Interval[22,25]

VariableDeduper will remove calls to functions with side-effects

As nicely functional as our generated code is, we call functions like:

  • context.Deeper()
  • operators.Message()

which has side-effects. The LocalVariableDeduper does not account for this and will delete expressions with these function calls.

We should probably mark functions with side-effects with an attribute and then, in the deduper, make sure we do not remove assignments where any child of the rhs has a MethodInvocation for a method that carries that attribute.

Unclear whether promotion influences the in/as/cast as behaviour.

The CQL spec never explicitly states that the arguments to is/as/cast as are exempt from automatic promotion/demotion, although it probably makes sense (and would be consistent with the behaviour of, say, the C# as and is, which also will not do implicit conversions).

Trying to reverse engineer the Java engines behaviour I noticed this:

// This works
define "y": {true} is Boolean

// This does not
define "z": {true} as Boolean

which leads me to think that in the Java engine promotion does apply to the is operator, but not to the as, which seems inconsistent to me. ("So some x is of type Y but I cannot actually cast it to Y?").

Produced Library resources contain named expressions as parameters instead as data requirements

Describe the bug
Take the following CQL expressions:

define "Qualifying Encounters":
  (
                    [Encounter: "Office Visit"]
                  		union [Encounter: "Annual Wellness Visit"]
                  		union [Encounter: "Preventive Care Services - Established Office Visit, 18 and Up"]
                  		union [Encounter: "Preventive Care Services-Initial Office Visit, 18 and Up"]
                  		union [Encounter: "Home Healthcare Services"]
                  ) ValidEncounter
                		where ValidEncounter.status  = 'finished'
                		and Global."Normalize Interval"(ValidEncounter.period) during "Measurement Period"

The SDK will produce a library resource based on this where "Qualifying Encounters" is mentioned as a parameter. However according to it should show up as a data requirement in the Library resource. Additionally, the "type" element in the "parameter" element of the Library is set to "List". Make sure to use the correct resource type.

Expected behavior
Only parameters declared as parameters should be in the parameters section of the Library.

Version used:
1.0.0-rc2

Introduce architecture/design testing

Architecture testing is a crucial practice in software development that ensures a solid and robust foundation for building applications. 

Architecture rules maintain design quality and consistency in a growing code base, especially where a team is distributed.

Suggest [https://www.infoworld.com/article/3656703/how-to-enforce-architecture-rules-in-csharp.html](introducing architecture testing) to the solution

Support contexts

We support context Patient by creating a method inside the measure class that returns a single Patient.

But we don't support context Population meaningfully. We don't add attributes to methods to indicate that they are context Population methods. We don't pass the type of context to the IDataSource interface to help implementers to understand whether they need to apply a Patient filter or not.

Consider allowing a function return type to be compatible to the body's type.

Although in many places CQL allows substitutability of types, this is not the case for return types of functions and their bodies. E.g.

define function x() returns Decimal: 4

is not allowed, only strict subtyping:

define function y() returns DomainResource: Patient { ... }

Given the flexibility in other places of the CQL language, we think the first case should be allowed too.

Exception when compiling CMS measure ProstateCaAvoidanceBoneScanOveruseFHIR-0.2.000

Describe the bug
Hl7.Cql.Compiler.ExpressionBuilder[0] Building expressions for ProstateCaAvoidanceBoneScanOveruseFHIR-0.2.000 warn: Hl7.Cql.Compiler.ExpressionBuilder[0] ProstateCaAvoidanceBoneScanOveruseFHIR-0.2.000 line 100:11-100:38: Potentially unsafe cast from Object to type CqlConcept Unhandled exception. System.ArgumentException: Cannot convert System.Nullable`1[Hl7.Fhir.Model.ObservationStatus] to Hl7.Fhir.Model.Code`1[Hl7.Fhir.Model.ObservationStatus] (Parameter 'source') at Hl7.Cql.Compiler.CqlOperatorsBinding.BindConvert(MemberExpression operators, Expression source, Expression typeExpression) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/CqlOperatorsBinding.cs:line 752 at Hl7.Cql.Compiler.CqlOperatorsBinding.Bind(CqlOperator operator, Expression runtimeContext, Expression[] parameters) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/CqlOperatorsBinding.cs:line 79 at Hl7.Cql.Compiler.ExpressionBuilder.ChangeType(Expression input, Type outputType, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.TypeOperators.cs:line 254 at Hl7.Cql.Compiler.ExpressionBuilder.PropertyHelper(Expression source, String path, Type expectedType, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2312 at Hl7.Cql.Compiler.ExpressionBuilder.Property(Property op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2264 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 800 at Hl7.Cql.Compiler.ExpressionBuilder.In(In e, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.IntervalOperators.cs:line 117 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 644 at Hl7.Cql.Compiler.ExpressionBuilder.BinaryOperator(CqlOperator operator, BinaryExpression be, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 950 at Hl7.Cql.Compiler.ExpressionBuilder.And(And e, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.LogicalOperators.cs:line 24 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 440 at Hl7.Cql.Compiler.ExpressionBuilder.WithToSelectManyBody(String outerScope, Type outerElementType, RelationshipClause with, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 2056 at Hl7.Cql.Compiler.ExpressionBuilder.SingleSourceQuery(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1061 at Hl7.Cql.Compiler.ExpressionBuilder.Query(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1029 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 806 at Hl7.Cql.Compiler.ExpressionBuilder.Last(Last e, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.ListOperators.cs:line 48 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 659 at Hl7.Cql.Compiler.ExpressionBuilder.SingleSourceQuery(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1043 at Hl7.Cql.Compiler.ExpressionBuilder.Query(Query query, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 1029 at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 806 at Hl7.Cql.Compiler.ExpressionBuilder.Build() in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Compiler/ExpressionBuilder.cs:line 360 at Hl7.Cql.Packaging.LibraryPackager.PackageResources(DirectoryInfo elmDirectory, DirectoryInfo cqlDirectory, DirectedGraph packageGraph, TypeResolver typeResolver, OperatorBinding operatorBinding, TypeManager typeManager, Func`2 canon, ILoggerFactory logFactory) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/LibraryPackager.cs:line 157 at Hl7.Cql.Packaging.ResourcePackager.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, Action`1 afterPackageMutator) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/Cql.Packaging/ResourcePackager.cs:line 42 at Hl7.Cql.Packager.Program.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, DirectoryInfo csDir, DirectoryInfo fhirDir) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 109 at Hl7.Cql.Packager.Program.Main(String[] args) in /Users/alex/Dev/Firely/SDK/firely-cql-sdk/Cql/PackagerCLI/Program.cs:line 77

To Reproduce
Measures are committed to branch inspect-cms-measures. Executing the PackagerCli in that branch will trigger the error.

Version used:
Current develop version of the PackagerCli

Implement overloaded functions

Currently, we can define exactly one function with a given name, as the key in the symbol table is the name of the function. To fix this, let's follow .NET's solution, where functions are part of a method group, so when we add a function to the symbol table of a library, we introduce a single MethodGroup with that name, which contains all overloaded functions with the same name.

When invoking a function, we resolve the method group, then use the existing InvocationBuilder to select the best match amongst the overloads, based on score. This will also make sure we cast all arguments to the required types of the function's parameters (which we also currently do not yet do).

To make this work, we define our own MethodGroup(Def), which implements IDefinitionElement, so it can be added to a symboltable. The Add of the symbol table will create the MethodGroup when the first function in the group gets added, and will add overloads to that group in subsequent adds.

Demo project missing Maven binaries

Describe the bug
According to Getting started with the Firely SDK CQL repository, building the Demo project is all that is necessary to get going. In particular, a full install of Maven should be present in the repository, and there is a build task called Download CQL to ELM CLI via Maven.

However, the bin folder in the path Demo\Cql\Build\apache-maven-3.8.8\bin\mvn of the build task is not present in the repo. So upon attempting to build, the following error occurs:

image

To Reproduce
Confirm the Build\apache-maven-3.8.8\bin folder is missing in the repo.

Expected behavior
Add the bin folder to the repo so that a build error does not occur.

Alternatively, list Java (the build task already implicitly assumes java is installed with a correct %PATH%) and Maven as external prerequisites and change the build task to use mvn relying on the %PATH% having been set in the user's operating system. In other words,

  1. Change <Exec Command="Build\apache-maven-3.8.8\bin\mvn -f Build\pom.xml dependency:copy-dependencies"></Exec> to <Exec Command="mvn -f Build\pom.xml dependency:copy-dependencies"></Exec>
  2. Remove the Demo\Cql\Build\apache-maven-3.8.8 folder and rely on the user to install Maven correctly with %PATH% already set.

Use dependency injection (part 1 - introduce the host builder and load arguments with IOptions)

Background

Currently services leak out their dependencies via properties e.g ExpressionBuildsr leaks out TypeManager and TypeManager leaks out TypeResolver.

Use dependencies via DI and keep them private.

Solution

The solution is over two parts:

  1. (This issue) Introduce the host builder in the Program Main. Use IOptions<> to extract commandline arguments.
  2. Register classes like ExpressionBuilder, TypeManager, etc as services and use the hosting framework to for managing their lifetime and injecting dependencies. (Issue #179)

Have TypeResolver provide a reference to the CQL Model used.

We should make the ModelInfo an abstract property on TypeResolver and require all implementations to provide one. ExpressionBuilder can refer to it through its TypeResolver. Theoretically, we need 1 TypeResolver per model declared in using statements in the provided library, e.g. to handle

using FHIR version '4.0.1'
using QICore version '1.0.0'
A single TypeResolver won't be enough for this.

When we've done this, we should correct the current implementation of the Binder, which now has a hard-coded reference on FHIR Model 4.0.x. (at https://github.com/FirelyTeam/firely-cql-sdk/pull/44/files/cedac43551a3d3603d6dc62f33d3e8b2413e7f3f#diff-b08896773220dcd7ca3e029d3c661ef480a7dd3730ef4fe8e467048f75afe32a, line 2043 more or less).

Implement all implicit casts

In the InvocationBuilder, I have made placeholders where we still need to add support for some implicit casts (most notably Choice<X,Y> -> X), including links to the spec where the cast is described.

We need to go over these and complete the casting logic.

Exception when compiling CMS measure HFBetaBlockerTherapyforLVSDFHIR-1.3.000

Getting ArgumentException while building statement for definition 'Initial Population 'in library 'HFBetaBlockerTherapyforLVSDFHIR-1.3.000'.

System.ArgumentException: Cannot resolve type for expression
   at Hl7.Cql.Compiler.TypeManager.TypeFor(Element element, ExpressionBuilderContext context, Boolean throwIfNotFound)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\TypeManager.cs:line 130
   at Hl7.Cql.Compiler.TypeManager.TypeFor(Element element, ExpressionBuilderContext context, Boolean throwIfNotFound)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\TypeManager.cs:line 104
   at Hl7.Cql.Compiler.ExpressionBuilder.Property(Property op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 2266
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 803
   at Hl7.Cql.Compiler.ExpressionBuilder.Property(Property op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 2247
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 803
   at Hl7.Cql.Compiler.ExpressionBuilder.CalculateAgeAt(CalculateAgeAt e, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.ClinicalOperators.cs:line 34
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 461
   at Hl7.Cql.Compiler.ExpressionBuilder.BinaryOperator(CqlOperator operator, BinaryExpression be, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 952
   at Hl7.Cql.Compiler.ExpressionBuilder.GreaterOrEqual(GreaterOrEqual e, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.ComparisonOperators.cs:line 123
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 608
   at Hl7.Cql.Compiler.ExpressionBuilder.BinaryOperator(CqlOperator operator, BinaryExpression be, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 952
   at Hl7.Cql.Compiler.ExpressionBuilder.And(And e, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.LogicalOperators.cs:line 24
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 443
   at Hl7.Cql.Compiler.ExpressionBuilder.BinaryOperator(CqlOperator operator, BinaryExpression be, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 952
   at Hl7.Cql.Compiler.ExpressionBuilder.And(And e, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.LogicalOperators.cs:line 24
   at Hl7.Cql.Compiler.ExpressionBuilder.TranslateExpression(Element op, ExpressionBuilderContext ctx)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 443
   at Hl7.Cql.Compiler.ExpressionBuilder.Build()
      in C:\Dev\firely-cql-sdk\Cql\Cql.Compiler\ExpressionBuilder.cs:line 363
   at Hl7.Cql.Packaging.LibraryPackager.PackageResources(DirectoryInfo elmDirectory, DirectoryInfo cqlDirectory, DirectedGraph packageGraph, TypeResolver typeResolver, OperatorBinding operatorBinding, TypeManager typeManager, Func`2 canon, ILoggerFactory logFactory)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Packaging\LibraryPackager.cs:line 161
   at Hl7.Cql.Packaging.ResourcePackager.PackageCore(DirectoryInfo elmDir, DirectoryInfo cqlDir, Action`1 afterPackageMutator, String resourceCanonicalRootUrl)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Packaging\ResourcePackager.cs:line 53
   at Hl7.Cql.Packaging.ResourcePackager.Package(PackageArgs args)
      in C:\Dev\firely-cql-sdk\Cql\Cql.Packaging\ResourcePackager.cs:line 42
   at Hl7.Cql.Packager.Program.Package(DirectoryInfo elmDir, DirectoryInfo cqlDir, DirectoryInfo csDir, DirectoryInfo fhirDir, String resourceCanonicalRootUrl)
      in C:\Dev\firely-cql-sdk\Cql\PackagerCLI\Program.cs:line 133
   at Hl7.Cql.Packager.Program.Main(String[] args)
      in C:\Dev\firely-cql-sdk\Cql\PackagerCLI\Program.cs:line 101

To Reproduce
Measures are committed to branch inspect-cms-measures. Executing the PackagerCli in that branch will trigger the error.

Version used:
Current develop version of the PackagerCli

Remove references to NCQA

There are a number of locations throughout the code that refer to NCQA, or are using a pattern that is specific to an implementation requirement of NCQA products. These should be removed and alternatives created.

This issue supercedes #97.

Enum parsing is failing due to casing

When converting the following CQL to C#, the Cql.Packaging fails on the crosswalk due to the casing being incorrect.

Steps to reproduce the behavior:
Return the id element of any resource so the result would be a List

define "Claim Ids":
[Claim] c
return c.id

Generated response ELM is

"resultTypeSpecifier" : {
"type" : "ListTypeSpecifier",
"elementType" : {
"type" : "NamedTypeSpecifier",
"name" : "{http://hl7.org/fhir}id"
}
}

Enum contains "Id" and not "id" so it is not found when parsing.

image

Make SystemLibrary injectable

Currently, SystemLibrary is a static class containing the definition of "all" CQL functions.

It is preferable to turn this into an instance, so it can be injected as a singleton in services that need it.

Improve semantic checks in VisitRetrieve

The Visitor for the Retrieve takes both the contextIdentifier (which should be a resolvable identifier) and codePath at face value, while the compiler should verify the validity of both.

Default value of type Period in Library has invalid syntax

Describe the bug
When using a default value for a period, we will write an extension "https://ncqa.org/fhir/StructureDefinition/ext-parameter.cql-default-value to the Library.parameter" containing a period. Unfortunately, the timestamp for the parameter is formatted incorrectly: it does not have a "Z" suffix:

  {
          "url": "https://ncqa.org/fhir/StructureDefinition/ext-parameter.cql-default-value",
          "valuePeriod": {
            "start": "2019-01-01T00:00:00.000",
            "end": "2019-12-31T23:59:59.999Z"
          }
        },

Better error handling of unresolved overloads

If the user made an error in such a way that an overloaded methodgroup either has no exact match or an ambiguous match, we can improve our error reporting by typing the erroneous node with a (subtype) of ChoiceType, including all possible types found in the return types of the highest scoring selected overloads.

E.g. if we have a function f<T>(T,T):T, and the CQL text reads f('string', 4), T cannot be determined. In this case we could type the node with all possible types, i.e. give it a type of Choice(int,string), this way, we would correctly deduce the type in a surrounding statement, e.g. f('hello', 4) + 5.

The same is true for a group of overloads of course.

Our current invocationBuilder is almost ready to handle this situation, simply by taking the possible generic parameter assignments and/or return types of the highest scoring alternatives.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.