Skip to content

Commit

Permalink
Add support for Ceiling() functional operator.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 646246300
  • Loading branch information
evan-gordon authored and copybara-github committed Jun 25, 2024
1 parent 5479999 commit 3941e63
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 1 deletion.
16 changes: 16 additions & 0 deletions interpreter/operator_arithmetic.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ func evalAbsQuantity(_ model.IUnaryExpression, obj result.Value) (result.Value,
return result.New(val)
}

// Ceiling(argument Decimal) Integer
// https://cql.hl7.org/09-b-cqlreference.html#ceiling
func evalCeiling(_ model.IUnaryExpression, obj result.Value) (result.Value, error) {
if result.IsNull(obj) {
return result.New(nil)
}
val, err := result.ToFloat64(obj)
if err != nil {
return result.Value{}, err
}
if val <= math.MinInt32-1 || val > math.MaxInt32 {
return result.New(nil)
}
return result.New(int32(math.Ceil(val)))
}

// op(left Integer, right Integer) Integer
// https://cql.hl7.org/09-b-cqlreference.html#add
// https://cql.hl7.org/09-b-cqlreference.html#subtract
Expand Down
7 changes: 7 additions & 0 deletions interpreter/operator_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ func (i *interpreter) unaryOverloads(m model.IUnaryExpression) ([]convert.Overlo
Result: evalAbsQuantity,
},
}, nil
case *model.Ceiling:
return []convert.Overload[evalUnarySignature]{
{
Operands: []types.IType{types.Decimal},
Result: evalCeiling,
},
}, nil
case *model.Exists:
return []convert.Overload[evalUnarySignature]{
{
Expand Down
6 changes: 6 additions & 0 deletions model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,9 @@ var _ IUnaryExpression = &Last{}
// Abs is https://cql.hl7.org/04-logicalspecification.html#abs.
type Abs struct{ *UnaryExpression }

// Ceiling is https://cql.hl7.org/04-logicalspecification.html#ceiling.
type Ceiling struct{ *UnaryExpression }

// SingletonFrom is https://cql.hl7.org/04-logicalspecification.html#singletonfrom.
type SingletonFrom struct{ *UnaryExpression }

Expand Down Expand Up @@ -1117,6 +1120,9 @@ type OperandRef struct {
// GetName returns the name of the system operator.
func (a *Abs) GetName() string { return "Abs" }

// GetName returns the name of the system operator.
func (a *Ceiling) GetName() string { return "Ceiling" }

// GetName returns the name of the system operator.
func (a *As) GetName() string { return "As" }

Expand Down
11 changes: 11 additions & 0 deletions parser/operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,17 @@ func (p *Parser) loadSystemOperators() error {
operands: [][]types.IType{{types.Quantity, types.Quantity}},
model: addModel(types.Quantity),
},
{
name: "Ceiling",
operands: [][]types.IType{{types.Decimal}},
model: func() model.IExpression {
return &model.Ceiling{
UnaryExpression: &model.UnaryExpression{
Expression: model.ResultType(types.Integer),
},
}
},
},
{
name: "Negate",
operands: [][]types.IType{{types.Integer}},
Expand Down
10 changes: 10 additions & 0 deletions parser/operators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,16 @@ func TestBuiltInFunctions(t *testing.T) {
},
},
},
{
name: "Arithmetic Ceiling",
cql: "Ceiling(41.1)",
want: &model.Ceiling{
UnaryExpression: &model.UnaryExpression{
Operand: model.NewLiteral("41.1", types.Decimal),
Expression: model.ResultType(types.Integer),
},
},
},
{
name: "Arithmetic Addition",
cql: "Add(1, 2)",
Expand Down
103 changes: 103 additions & 0 deletions tests/enginetests/operator_arithmetic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,109 @@ func TestAbs(t *testing.T) {
}
}

func TestCeiling(t *testing.T) {
tests := []struct {
name string
cql string
wantModel model.IExpression
wantResult result.Value
}{
{
name: "Decimal",
cql: "Ceiling(41.1)",
wantModel: &model.Ceiling{
UnaryExpression: &model.UnaryExpression{
Operand: model.NewLiteral("41.1", types.Decimal),
Expression: model.ResultType(types.Integer),
},
},
wantResult: newOrFatal(t, 42),
},
{
name: "Negative",
cql: "Ceiling(-2.1)",
wantResult: newOrFatal(t, -2),
},
{
name: "Zero",
cql: "Ceiling(0.0)",
wantResult: newOrFatal(t, 0),
},
{
name: "Integer",
cql: "Ceiling(2)",
wantResult: newOrFatal(t, 2),
},
{
name: "Minimum Integer",
cql: "Ceiling(-2147483648)",
wantResult: newOrFatal(t, -2147483648),
},
{
name: "Minimum Decimal out of range",
cql: "Ceiling(-99999999999999999999.99999999)",
wantResult: newOrFatal(t, nil),
},
{
name: "Maximum Decimal out of range",
cql: "Ceiling(99999999999999999999.99999999)",
wantResult: newOrFatal(t, nil),
},
{
name: "Just less than min int32",
cql: "Ceiling(-2147483648.5)",
wantResult: newOrFatal(t, math.MinInt32),
},
{
name: "More than one less than min int32",
cql: "Ceiling(-2147483649.5)",
wantResult: newOrFatal(t, nil),
},
{
name: "Just more than max int32",
cql: "Ceiling(2147483647.5)",
wantResult: newOrFatal(t, nil),
},
{
name: "equal to min int32",
cql: "Ceiling(-2147483648.0)",
wantResult: newOrFatal(t, math.MinInt32),
},
{
name: "equal to max int32",
cql: "Ceiling(2147483647.0)",
wantResult: newOrFatal(t, math.MaxInt32),
},
{
name: "Null",
cql: "Ceiling(null as Decimal)",
wantResult: newOrFatal(t, nil),
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
p := newFHIRParser(t)
parsedLibs, err := p.Libraries(context.Background(), wrapInLib(t, tc.cql), parser.Config{})
if err != nil {
t.Fatalf("Parse returned unexpected error: %v", err)
}
if diff := cmp.Diff(tc.wantModel, getTESTRESULTModel(t, parsedLibs)); tc.wantModel != nil && diff != "" {
t.Errorf("Parse diff (-want +got):\n%s", diff)
}

results, err := interpreter.Eval(context.Background(), parsedLibs, defaultInterpreterConfig(t, p))
if err != nil {
t.Fatalf("Eval returned unexpected error: %v", err)
}

if diff := cmp.Diff(tc.wantResult, getTESTRESULT(t, results), protocmp.Transform()); diff != "" {
t.Errorf("Eval diff (-want +got)\n%v", diff)
}
})
}
}

func TestAdd(t *testing.T) {
tests := []struct {
name string
Expand Down
1 change: 0 additions & 1 deletion tests/spectests/exclusions/exclusions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ func XMLTestFileExclusionDefinitions() map[string]XMLTestFileExclusions {
"CqlArithmeticFunctionsTest.xml": XMLTestFileExclusions{
GroupExcludes: []string{
// TODO: b/342061715 - unsupported operators.
"Ceiling",
"Floor",
"Exp",
"HighBoundary",
Expand Down

0 comments on commit 3941e63

Please sign in to comment.