Search in sources :

Example 91 with Expression

use of org.hl7.fhir.r4.model.Expression in project clinical_quality_language by cqframework.

the class Cql2ElmVisitor method visitWithinIntervalOperatorPhrase.

@Override
public Object visitWithinIntervalOperatorPhrase(cqlParser.WithinIntervalOperatorPhraseContext ctx) {
    // ('starts' | 'ends' | 'occurs')? 'properly'? 'within' quantityLiteral 'of' ('start' | 'end')?
    // A starts within 3 days of start B
    // * start of A in [start of B - 3 days, start of B + 3 days] and start B is not null
    // A starts within 3 days of B
    // * start of A in [start of B - 3 days, end of B + 3 days]
    TimingOperatorContext timingOperator = timingOperators.peek();
    boolean isProper = false;
    for (ParseTree child : ctx.children) {
        if ("starts".equals(child.getText())) {
            Start start = of.createStart().withOperand(timingOperator.getLeft());
            track(start, child);
            libraryBuilder.resolveUnaryCall("System", "Start", start);
            timingOperator.setLeft(start);
            continue;
        }
        if ("ends".equals(child.getText())) {
            End end = of.createEnd().withOperand(timingOperator.getLeft());
            track(end, child);
            libraryBuilder.resolveUnaryCall("System", "End", end);
            timingOperator.setLeft(end);
            continue;
        }
        if ("start".equals(child.getText())) {
            Start start = of.createStart().withOperand(timingOperator.getRight());
            track(start, child);
            libraryBuilder.resolveUnaryCall("System", "Start", start);
            timingOperator.setRight(start);
            continue;
        }
        if ("end".equals(child.getText())) {
            End end = of.createEnd().withOperand(timingOperator.getRight());
            track(end, child);
            libraryBuilder.resolveUnaryCall("System", "End", end);
            timingOperator.setRight(end);
            continue;
        }
        if ("properly".equals(child.getText())) {
            isProper = true;
            continue;
        }
    }
    Quantity quantity = (Quantity) visit(ctx.quantity());
    Expression lowerBound = null;
    Expression upperBound = null;
    Expression initialBound = null;
    if (timingOperator.getRight().getResultType() instanceof IntervalType) {
        lowerBound = of.createStart().withOperand(timingOperator.getRight());
        track(lowerBound, ctx.quantity());
        libraryBuilder.resolveUnaryCall("System", "Start", (Start) lowerBound);
        upperBound = of.createEnd().withOperand(timingOperator.getRight());
        track(upperBound, ctx.quantity());
        libraryBuilder.resolveUnaryCall("System", "End", (End) upperBound);
    } else {
        lowerBound = timingOperator.getRight();
        upperBound = timingOperator.getRight();
        initialBound = lowerBound;
    }
    lowerBound = of.createSubtract().withOperand(lowerBound, quantity);
    track(lowerBound, ctx.quantity());
    libraryBuilder.resolveBinaryCall("System", "Subtract", (BinaryExpression) lowerBound);
    upperBound = of.createAdd().withOperand(upperBound, quantity);
    track(upperBound, ctx.quantity());
    libraryBuilder.resolveBinaryCall("System", "Add", (BinaryExpression) upperBound);
    Interval interval = libraryBuilder.createInterval(lowerBound, !isProper, upperBound, !isProper);
    track(interval, ctx.quantity());
    In in = of.createIn().withOperand(timingOperator.getLeft(), interval);
    libraryBuilder.resolveBinaryCall("System", "In", in);
    // if the within is not proper and the interval is being constructed from a single point, add a null check for that point to ensure correct interpretation
    if (!isProper && (initialBound != null)) {
        IsNull nullTest = of.createIsNull().withOperand(initialBound);
        track(nullTest, ctx.quantity());
        libraryBuilder.resolveUnaryCall("System", "IsNull", nullTest);
        Not notNullTest = of.createNot().withOperand(nullTest);
        track(notNullTest, ctx.quantity());
        libraryBuilder.resolveUnaryCall("System", "Not", notNullTest);
        And and = of.createAnd().withOperand(in, notNullTest);
        track(and, ctx.quantity());
        libraryBuilder.resolveBinaryCall("System", "And", and);
        return and;
    }
    // Otherwise, return the constructed in
    return in;
}
Also used : ParseTree(org.antlr.v4.runtime.tree.ParseTree) Interval(org.hl7.elm.r1.Interval)

Example 92 with Expression

use of org.hl7.fhir.r4.model.Expression in project clinical_quality_language by cqframework.

the class Cql2ElmVisitor method visitBeforeOrAfterIntervalOperatorPhrase.

@Override
public Object visitBeforeOrAfterIntervalOperatorPhrase(cqlParser.BeforeOrAfterIntervalOperatorPhraseContext ctx) {
    // ('starts' | 'ends' | 'occurs')? quantityOffset? ('before' | 'after') dateTimePrecisionSpecifier? ('start' | 'end')?
    // duration before/after
    // A starts 3 days before start B
    // * start of A same day as start of B - 3 days
    // A starts 3 days after start B
    // * start of A same day as start of B + 3 days
    // or more/less duration before/after
    // A starts 3 days or more before start B
    // * start of A <= start of B - 3 days
    // A starts 3 days or more after start B
    // * start of A >= start of B + 3 days
    // A starts 3 days or less before start B
    // * start of A in [start of B - 3 days, start of B) and B is not null
    // A starts 3 days or less after start B
    // * start of A in (start of B, start of B + 3 days] and B is not null
    // less/more than duration before/after
    // A starts more than 3 days before start B
    // * start of A < start of B - 3 days
    // A starts more than 3 days after start B
    // * start of A > start of B + 3 days
    // A starts less than 3 days before start B
    // * start of A in (start of B - 3 days, start of B)
    // A starts less than 3 days after start B
    // * start of A in (start of B, start of B + 3 days)
    TimingOperatorContext timingOperator = timingOperators.peek();
    boolean isBefore = false;
    boolean isInclusive = false;
    for (ParseTree child : ctx.children) {
        if ("starts".equals(child.getText())) {
            Start start = of.createStart().withOperand(timingOperator.getLeft());
            track(start, child);
            libraryBuilder.resolveUnaryCall("System", "Start", start);
            timingOperator.setLeft(start);
            continue;
        }
        if ("ends".equals(child.getText())) {
            End end = of.createEnd().withOperand(timingOperator.getLeft());
            track(end, child);
            libraryBuilder.resolveUnaryCall("System", "End", end);
            timingOperator.setLeft(end);
            continue;
        }
        if ("start".equals(child.getText())) {
            Start start = of.createStart().withOperand(timingOperator.getRight());
            track(start, child);
            libraryBuilder.resolveUnaryCall("System", "Start", start);
            timingOperator.setRight(start);
            continue;
        }
        if ("end".equals(child.getText())) {
            End end = of.createEnd().withOperand(timingOperator.getRight());
            track(end, child);
            libraryBuilder.resolveUnaryCall("System", "End", end);
            timingOperator.setRight(end);
            continue;
        }
    }
    for (ParseTree child : ctx.temporalRelationship().children) {
        if ("before".equals(child.getText())) {
            isBefore = true;
            continue;
        }
        if ("on or".equals(child.getText()) || "or on".equals(child.getText())) {
            isInclusive = true;
            continue;
        }
    }
    String dateTimePrecision = ctx.dateTimePrecisionSpecifier() != null ? ctx.dateTimePrecisionSpecifier().dateTimePrecision().getText() : null;
    if (ctx.quantityOffset() == null) {
        if (isInclusive) {
            if (isBefore) {
                SameOrBefore sameOrBefore = of.createSameOrBefore().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                if (dateTimePrecision != null) {
                    sameOrBefore.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                }
                libraryBuilder.resolveBinaryCall("System", "SameOrBefore", sameOrBefore, true, true);
                return sameOrBefore;
            } else {
                SameOrAfter sameOrAfter = of.createSameOrAfter().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                if (dateTimePrecision != null) {
                    sameOrAfter.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                }
                libraryBuilder.resolveBinaryCall("System", "SameOrAfter", sameOrAfter, true, true);
                return sameOrAfter;
            }
        } else {
            if (isBefore) {
                Before before = of.createBefore().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                if (dateTimePrecision != null) {
                    before.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                }
                libraryBuilder.resolveBinaryCall("System", "Before", before, true, true);
                return before;
            } else {
                After after = of.createAfter().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                if (dateTimePrecision != null) {
                    after.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                }
                libraryBuilder.resolveBinaryCall("System", "After", after, true, true);
                return after;
            }
        }
    } else {
        Quantity quantity = (Quantity) visit(ctx.quantityOffset().quantity());
        if (timingOperator.getLeft().getResultType() instanceof IntervalType) {
            if (isBefore) {
                End end = of.createEnd().withOperand(timingOperator.getLeft());
                track(end, timingOperator.getLeft());
                libraryBuilder.resolveUnaryCall("System", "End", end);
                timingOperator.setLeft(end);
            } else {
                Start start = of.createStart().withOperand(timingOperator.getLeft());
                track(start, timingOperator.getLeft());
                libraryBuilder.resolveUnaryCall("System", "Start", start);
                timingOperator.setLeft(start);
            }
        }
        if (timingOperator.getRight().getResultType() instanceof IntervalType) {
            if (isBefore) {
                Start start = of.createStart().withOperand(timingOperator.getRight());
                track(start, timingOperator.getRight());
                libraryBuilder.resolveUnaryCall("System", "Start", start);
                timingOperator.setRight(start);
            } else {
                End end = of.createEnd().withOperand(timingOperator.getRight());
                track(end, timingOperator.getRight());
                libraryBuilder.resolveUnaryCall("System", "End", end);
                timingOperator.setRight(end);
            }
        }
        if (ctx.quantityOffset().offsetRelativeQualifier() == null && ctx.quantityOffset().exclusiveRelativeQualifier() == null) {
            // For an After, add the quantity to the right operand
            if (isBefore) {
                Subtract subtract = of.createSubtract().withOperand(timingOperator.getRight(), quantity);
                track(subtract, timingOperator.getRight());
                libraryBuilder.resolveBinaryCall("System", "Subtract", subtract);
                timingOperator.setRight(subtract);
            } else {
                Add add = of.createAdd().withOperand(timingOperator.getRight(), quantity);
                track(add, timingOperator.getRight());
                libraryBuilder.resolveBinaryCall("System", "Add", add);
                timingOperator.setRight(add);
            }
            SameAs sameAs = of.createSameAs().withOperand(timingOperator.getLeft(), timingOperator.getRight());
            if (dateTimePrecision != null) {
                sameAs.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
            }
            libraryBuilder.resolveBinaryCall("System", "SameAs", sameAs);
            return sameAs;
        } else {
            boolean isOffsetInclusive = ctx.quantityOffset().offsetRelativeQualifier() != null;
            String qualifier = ctx.quantityOffset().offsetRelativeQualifier() != null ? ctx.quantityOffset().offsetRelativeQualifier().getText() : ctx.quantityOffset().exclusiveRelativeQualifier().getText();
            switch(qualifier) {
                case "more than":
                case "or more":
                    // For an After, add the quantity to the right operand
                    if (isBefore) {
                        Subtract subtract = of.createSubtract().withOperand(timingOperator.getRight(), quantity);
                        track(subtract, timingOperator.getRight());
                        libraryBuilder.resolveBinaryCall("System", "Subtract", subtract);
                        timingOperator.setRight(subtract);
                        if (!isOffsetInclusive) {
                            Before before = of.createBefore().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                            if (dateTimePrecision != null) {
                                before.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                            }
                            libraryBuilder.resolveBinaryCall("System", "Before", before, true, true);
                            return before;
                        } else {
                            SameOrBefore sameOrBefore = of.createSameOrBefore().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                            if (dateTimePrecision != null) {
                                sameOrBefore.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                            }
                            libraryBuilder.resolveBinaryCall("System", "SameOrBefore", sameOrBefore, true, true);
                            return sameOrBefore;
                        }
                    } else {
                        Add add = of.createAdd().withOperand(timingOperator.getRight(), quantity);
                        track(add, timingOperator.getRight());
                        libraryBuilder.resolveBinaryCall("System", "Add", add);
                        timingOperator.setRight(add);
                        if (!isOffsetInclusive) {
                            After after = of.createAfter().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                            if (dateTimePrecision != null) {
                                after.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                            }
                            libraryBuilder.resolveBinaryCall("System", "After", after, true, true);
                            return after;
                        } else {
                            SameOrAfter sameOrAfter = of.createSameOrAfter().withOperand(timingOperator.getLeft(), timingOperator.getRight());
                            if (dateTimePrecision != null) {
                                sameOrAfter.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                            }
                            libraryBuilder.resolveBinaryCall("System", "SameOrAfter", sameOrAfter, true, true);
                            return sameOrAfter;
                        }
                    }
                case "less than":
                case "or less":
                    // For Less Than/Or Less, Use an In
                    // For Before, construct an interval from right - quantity to right
                    // For After, construct an interval from right to right + quantity
                    Expression lowerBound = null;
                    Expression upperBound = null;
                    Expression right = timingOperator.getRight();
                    if (isBefore) {
                        lowerBound = of.createSubtract().withOperand(right, quantity);
                        track(lowerBound, right);
                        libraryBuilder.resolveBinaryCall("System", "Subtract", (BinaryExpression) lowerBound);
                        upperBound = right;
                    } else {
                        lowerBound = right;
                        upperBound = of.createAdd().withOperand(right, quantity);
                        track(upperBound, right);
                        libraryBuilder.resolveBinaryCall("System", "Add", (BinaryExpression) upperBound);
                    }
                    // 3 days or less before -> [B - 3 days, B)
                    // less than 3 days before -> (B - 3 days, B)
                    // 3 days or less after -> (B, B + 3 days]
                    // less than 3 days after -> (B, B + 3 days)
                    Interval interval = isBefore ? libraryBuilder.createInterval(lowerBound, isOffsetInclusive, upperBound, isInclusive) : libraryBuilder.createInterval(lowerBound, isInclusive, upperBound, isOffsetInclusive);
                    track(interval, ctx.quantityOffset());
                    In in = of.createIn().withOperand(timingOperator.getLeft(), interval);
                    if (dateTimePrecision != null) {
                        in.setPrecision(parseComparableDateTimePrecision(dateTimePrecision));
                    }
                    track(in, ctx.quantityOffset());
                    libraryBuilder.resolveBinaryCall("System", "In", in);
                    // if the offset or comparison is inclusive, add a null check for B to ensure correct interpretation
                    if (isOffsetInclusive || isInclusive) {
                        IsNull nullTest = of.createIsNull().withOperand(right);
                        track(nullTest, ctx.quantityOffset());
                        libraryBuilder.resolveUnaryCall("System", "IsNull", nullTest);
                        Not notNullTest = of.createNot().withOperand(nullTest);
                        track(notNullTest, ctx.quantityOffset());
                        libraryBuilder.resolveUnaryCall("System", "Not", notNullTest);
                        And and = of.createAnd().withOperand(in, notNullTest);
                        track(and, ctx.quantityOffset());
                        libraryBuilder.resolveBinaryCall("System", "And", and);
                        return and;
                    }
                    // Otherwise, return the constructed in
                    return in;
            }
        }
    }
    throw new IllegalArgumentException("Unable to resolve interval operator phrase.");
}
Also used : ParseTree(org.antlr.v4.runtime.tree.ParseTree) Interval(org.hl7.elm.r1.Interval)

Example 93 with Expression

use of org.hl7.fhir.r4.model.Expression in project clinical_quality_language by cqframework.

the class PositionOfInvocation method setOperands.

@Override
public void setOperands(Iterable<Expression> operands) {
    Iterator<Expression> it = operands.iterator();
    if (!it.hasNext()) {
        throw new IllegalArgumentException("PositionOf operation requires two operands.");
    }
    PositionOf pos = (PositionOf) expression;
    pos.setPattern(it.next());
    if (!it.hasNext()) {
        throw new IllegalArgumentException("PositionOf operation requires two operands.");
    }
    pos.setString(it.next());
}
Also used : PositionOf(org.hl7.elm.r1.PositionOf) Expression(org.hl7.elm.r1.Expression)

Example 94 with Expression

use of org.hl7.fhir.r4.model.Expression in project clinical_quality_language by cqframework.

the class RoundInvocation method setOperands.

@Override
public void setOperands(Iterable<Expression> operands) {
    Iterator<Expression> it = operands.iterator();
    if (!it.hasNext()) {
        throw new IllegalArgumentException("Round operation requires one or two operands.");
    }
    Round round = (Round) expression;
    round.setOperand(it.next());
    if (it.hasNext()) {
        round.setPrecision(it.next());
    }
}
Also used : Expression(org.hl7.elm.r1.Expression) Round(org.hl7.elm.r1.Round)

Example 95 with Expression

use of org.hl7.fhir.r4.model.Expression in project clinical_quality_language by cqframework.

the class SkipInvocation method setOperands.

@Override
public void setOperands(Iterable<Expression> operands) {
    boolean first = true;
    for (Expression operand : operands) {
        if (first) {
            ((Slice) expression).setSource(operand);
            first = false;
        } else {
            ((Slice) expression).setStartIndex(operand);
        }
    }
}
Also used : Expression(org.hl7.elm.r1.Expression) Slice(org.hl7.elm.r1.Slice)

Aggregations

HashMap (java.util.HashMap)33 Test (org.junit.Test)30 Test (org.junit.jupiter.api.Test)30 Patient (org.hl7.fhir.r4.model.Patient)29 CqlEvaluator (com.ibm.cohort.cql.evaluation.CqlEvaluator)28 CqlVersionedIdentifier (com.ibm.cohort.cql.library.CqlVersionedIdentifier)28 ArrayList (java.util.ArrayList)27 Expression (org.hl7.elm.r1.Expression)26 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)25 CqlEvaluationResult (com.ibm.cohort.cql.evaluation.CqlEvaluationResult)24 FHIRException (org.hl7.fhir.exceptions.FHIRException)19 FhirServerConfig (com.ibm.cohort.fhir.client.config.FhirServerConfig)16 Coding (org.hl7.fhir.r4.model.Coding)15 Complex (org.hl7.fhir.r4.utils.formats.Turtle.Complex)14 Row (org.apache.spark.sql.Row)12 List (java.util.List)11 Complex (org.hl7.fhir.dstu3.utils.formats.Turtle.Complex)11 FhirPath (au.csiro.pathling.fhirpath.FhirPath)10 Nonnull (javax.annotation.Nonnull)10 RestIntegrationTest (org.opencds.cqf.ruler.test.RestIntegrationTest)9