Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ Implementors should be aware of the following limitations and gaps in `cql-execu
* Issues typically associated with floating point arithmetic
* Decimals without a decimal portion (e.g., `2.0`) may be treated as CQL `Integer`s
* The following STU (non-normative) features introduced in CQL 1.5 are not yet supported:
* `Long` datatype
* Fluent functions
* Retrieve search paths
* Retrieve includes
* In addition the following features defined prior to CQL 1.5 are also not yet supported:
Expand Down
885 changes: 574 additions & 311 deletions examples/browser/cql4browsers.js

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/cql-patient.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as DT from './datatypes/datatypes';
import { DataProvider, NamedTypeSpecifier, PatientObject, RecordObject } from './types';
import { ELM_NAMED_TYPE_SPECIFIER } from './util/elmTypes';

export class Record implements RecordObject {
json: any;
Expand All @@ -20,13 +21,13 @@ export class Record implements RecordObject {
return [
{
name: `{https://github.com/cqframework/cql-execution/simple}${this.json.recordType}`,
type: 'NamedTypeSpecifier'
type: ELM_NAMED_TYPE_SPECIFIER
},
{
name: '{https://github.com/cqframework/cql-execution/simple}Record',
type: 'NamedTypeSpecifier'
type: ELM_NAMED_TYPE_SPECIFIER
},
{ name: '{urn:hl7-org:elm-types:r1}Any', type: 'NamedTypeSpecifier' }
{ name: '{urn:hl7-org:elm-types:r1}Any', type: ELM_NAMED_TYPE_SPECIFIER }
];
}

Expand Down
20 changes: 20 additions & 0 deletions src/datatypes/bigint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// By default, BigInt throws a TypeError if you attempt to JSON.stringify it.
// You can avoid the TypeError by defining a `toJSON()` function for BigInt.
// We will use the same serialization approach as FHIR uses for integer64: a string.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
// See: https://hl7.org/fhir/R5/json.html#primitive

declare global {
interface BigInt {
toJSON?(): unknown;
}
}

if (BigInt.prototype.toJSON === undefined) {
BigInt.prototype.toJSON = function () {
return this.toString();
};
}

// empty export forces typescript to recognize this file as a module
export {};
1 change: 1 addition & 0 deletions src/datatypes/datatypes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './bigint';
export * from './logic';
export * from './clinical';
export * from './uncertainty';
Expand Down
44 changes: 31 additions & 13 deletions src/datatypes/interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ import {
minValueForType
} from '../util/math';
import * as cmp from '../util/comparison';
import {
ELM_INTEGER_TYPE,
ELM_DECIMAL_TYPE,
ELM_LONG_TYPE,
ELM_TIME_TYPE,
ELM_DATE_TYPE,
ELM_DATETIME_TYPE,
ELM_QUANTITY_TYPE
} from '../util/elmTypes';

export class Interval {
constructor(
Expand All @@ -32,17 +41,17 @@ export class Interval {
const point = this.low != null ? this.low : this.high;
if (point != null) {
if (typeof point === 'number') {
pointType = Number.isInteger(point)
? '{urn:hl7-org:elm-types:r1}Integer'
: '{urn:hl7-org:elm-types:r1}Decimal';
pointType = Number.isInteger(point) ? ELM_INTEGER_TYPE : ELM_DECIMAL_TYPE;
} else if (typeof point === 'bigint') {
pointType = ELM_LONG_TYPE;
} else if (point.isTime && point.isTime()) {
pointType = '{urn:hl7-org:elm-types:r1}Time';
pointType = ELM_TIME_TYPE;
} else if (point.isDate) {
pointType = '{urn:hl7-org:elm-types:r1}Date';
pointType = ELM_DATE_TYPE;
} else if (point.isDateTime) {
pointType = '{urn:hl7-org:elm-types:r1}DateTime';
pointType = ELM_DATETIME_TYPE;
} else if (point.isQuantity) {
pointType = '{urn:hl7-org:elm-types:r1}Quantity';
pointType = ELM_QUANTITY_TYPE;
}
}
if (pointType == null && this.defaultPointType != null) {
Expand Down Expand Up @@ -288,7 +297,7 @@ export class Interval {
!other.highClosed &&
!this.highClosed)
) {
if (typeof this.low === 'number') {
if (typeof this.low === 'number' || typeof this.low === 'bigint') {
if (!(this.start() === other.start())) {
return false;
}
Expand All @@ -302,7 +311,7 @@ export class Interval {
(this.low == null && other.low != null && this.high != null && other.high != null) ||
(this.low == null && other.low == null && this.high != null && other.high != null)
) {
if (typeof this.high === 'number') {
if (typeof this.high === 'number' || typeof this.high === 'bigint') {
if (!(this.end() === other.end())) {
return false;
}
Expand Down Expand Up @@ -337,7 +346,7 @@ export class Interval {
return false;
}

if (typeof this.low === 'number') {
if (typeof this.low === 'number' || typeof this.low === 'bigint') {
return this.start() === other.start() && this.end() === other.end();
} else {
return (
Expand Down Expand Up @@ -497,6 +506,8 @@ export class Interval {
let diff = Math.abs(highValue - lowValue);
diff = Math.round(diff * Math.pow(10, 8)) / Math.pow(10, 8);
return new Quantity(diff, closed.low.unit);
} else if (typeof closed.low === 'bigint') {
return closed.high >= closed.low ? closed.high - closed.low : closed.low - closed.high;
} else {
// TODO: Fix precision to 8 decimals in other places that return numbers
const diff = Math.abs(closed.high - closed.low);
Expand Down Expand Up @@ -528,6 +539,9 @@ export class Interval {
let diff = Math.abs(highValue - lowValue) + pointSize.value;
diff = Math.round(diff * Math.pow(10, 8)) / Math.pow(10, 8);
return new Quantity(diff, closed.low.unit);
} else if (typeof closed.low === 'bigint') {
const diff = closed.high >= closed.low ? closed.high - closed.low : closed.low - closed.high;
return diff + 1n;
} else {
const diff = Math.abs(closed.high - closed.low) + pointSize.value;
return Math.round(diff * Math.pow(10, 8)) / Math.pow(10, 8);
Expand Down Expand Up @@ -556,8 +570,8 @@ export class Interval {
throw new Error('Point type of intervals cannot be determined.');
}

if (typeof pointSize === 'number') {
pointSize = new Quantity(pointSize, '1');
if (typeof pointSize === 'number' || typeof pointSize === 'bigint') {
pointSize = new Quantity(Number(pointSize), '1');
}

return pointSize;
Expand Down Expand Up @@ -610,7 +624,11 @@ function areDateTimes(x: any, y: any) {

function areNumeric(x: any, y: any) {
return [x, y].every(z => {
return typeof z === 'number' || (z != null && z.isUncertainty && typeof z.low === 'number');
return (
typeof z === 'number' ||
typeof z === 'bigint' ||
(z != null && z.isUncertainty && (typeof z.low === 'number' || typeof z.low === 'bigint'))
);
});
}

Expand Down
7 changes: 4 additions & 3 deletions src/datatypes/quantity.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ELM_DECIMAL_TYPE } from '../util/elmTypes';
import { decimalAdjust, isValidDecimal, overflowsOrUnderflows } from '../util/math';
import {
checkUnit,
Expand Down Expand Up @@ -125,7 +126,7 @@ export class Quantity {
const resultUnit = getQuotientOfUnits(unit1, unit2);

// Check for invalid unit or value
if (resultUnit == null || overflowsOrUnderflows(resultValue)) {
if (resultUnit == null || overflowsOrUnderflows(resultValue, ELM_DECIMAL_TYPE)) {
return null;
}
return new Quantity(decimalAdjust('round', resultValue, -8), resultUnit);
Expand All @@ -149,7 +150,7 @@ export class Quantity {
const resultUnit = getProductOfUnits(unit1, unit2);

// Check for invalid unit or value
if (resultUnit == null || overflowsOrUnderflows(resultValue)) {
if (resultUnit == null || overflowsOrUnderflows(resultValue, ELM_DECIMAL_TYPE)) {
return null;
}
return new Quantity(decimalAdjust('round', resultValue, -8), resultUnit);
Expand Down Expand Up @@ -188,7 +189,7 @@ function doScaledAddition(a: any, b: any, scaleForB: any) {
return null;
}
const sum = val1 + val2;
if (overflowsOrUnderflows(sum)) {
if (overflowsOrUnderflows(sum, ELM_DECIMAL_TYPE)) {
return null;
}
return new Quantity(sum, unit1);
Expand Down
14 changes: 10 additions & 4 deletions src/elm/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Context } from '../runtime/context';
import { Exception } from '../datatypes/exception';
import { greaterThan, lessThan } from '../util/comparison';
import { build } from './builder';
import { overflowsOrUnderflows } from '../util/math';
import { ELM_DECIMAL_TYPE } from '../util/elmTypes';

class AggregateExpression extends Expression {
source: any;
Expand Down Expand Up @@ -53,9 +55,10 @@ export class Sum extends AggregateExpression {
if (hasOnlyQuantities(items)) {
const values = getValuesFromQuantities(items);
const sum = values.reduce((x, y) => x + y);
return new Quantity(sum, items[0].unit);
return overflowsOrUnderflows(sum, ELM_DECIMAL_TYPE) ? null : new Quantity(sum, items[0].unit);
} else {
return items.reduce((x: number, y: number) => x + y);
const sum = items.reduce((x: any, y: any) => x + y);
return overflowsOrUnderflows(sum, this.resultTypeName) ? null : sum;
}
}
}
Expand Down Expand Up @@ -339,9 +342,12 @@ export class Product extends AggregateExpression {
const values = getValuesFromQuantities(items);
const product = values.reduce((x, y) => x * y);
// Units are not multiplied for the geometric product
return new Quantity(product, items[0].unit);
return overflowsOrUnderflows(product, ELM_DECIMAL_TYPE)
? null
: new Quantity(product, items[0].unit);
} else {
return items.reduce((x: number, y: number) => x * y);
const product = items.reduce((x: any, y: any) => x * y);
return overflowsOrUnderflows(product, this.resultTypeName) ? null : product;
}
}
}
Expand Down
Loading
Loading