From ad0e8779ecc8c2c747c68a2ebb6e6daa6894caeb Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Fri, 24 Nov 2023 17:26:54 +0100 Subject: [PATCH] Add ceil(), floor() and round() function Fixes #476 --- builtin/builtin.go | 42 +++++++++++++++++++++++++++++++++++++ builtin/builtin_test.go | 7 +++++++ builtin/func.go | 37 ++++++++++++++++++++++++++++++++ docs/Language-Definition.md | 24 +++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/builtin/builtin.go b/builtin/builtin.go index d7248a867..1c3d919cc 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -126,6 +126,48 @@ var Builtins = []*ast.Function{ return anyType, fmt.Errorf("invalid argument for abs (type %s)", args[0]) }, }, + { + Name: "ceil", + Fast: Ceil, + Validate: func(args []reflect.Type) (reflect.Type, error) { + if len(args) != 1 { + return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + switch kind(args[0]) { + case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface: + return floatType, nil + } + return anyType, fmt.Errorf("invalid argument for ceil (type %s)", args[0]) + }, + }, + { + Name: "floor", + Fast: Floor, + Validate: func(args []reflect.Type) (reflect.Type, error) { + if len(args) != 1 { + return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + switch kind(args[0]) { + case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface: + return floatType, nil + } + return anyType, fmt.Errorf("invalid argument for floor (type %s)", args[0]) + }, + }, + { + Name: "round", + Fast: Round, + Validate: func(args []reflect.Type) (reflect.Type, error) { + if len(args) != 1 { + return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + switch kind(args[0]) { + case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface: + return floatType, nil + } + return anyType, fmt.Errorf("invalid argument for floor (type %s)", args[0]) + }, + }, { Name: "int", Fast: Int, diff --git a/builtin/builtin_test.go b/builtin/builtin_test.go index 533f59a88..45582d00a 100644 --- a/builtin/builtin_test.go +++ b/builtin/builtin_test.go @@ -36,6 +36,13 @@ func TestBuiltin(t *testing.T) { {`abs(-5)`, 5}, {`abs(.5)`, .5}, {`abs(-.5)`, .5}, + {`ceil(5.5)`, 6.0}, + {`ceil(5)`, 5.0}, + {`floor(5.5)`, 5.0}, + {`floor(5)`, 5.0}, + {`round(5.5)`, 6.0}, + {`round(5)`, 5.0}, + {`round(5.49)`, 5.0}, {`int(5.5)`, 5}, {`int(5)`, 5}, {`int("5")`, 5}, diff --git a/builtin/func.go b/builtin/func.go index 7c042c6d2..d9ec4ff42 100644 --- a/builtin/func.go +++ b/builtin/func.go @@ -2,6 +2,7 @@ package builtin import ( "fmt" + "math" "reflect" "strconv" @@ -138,6 +139,42 @@ func Abs(x any) any { panic(fmt.Sprintf("invalid argument for abs (type %T)", x)) } +func Ceil(x any) any { + switch x := x.(type) { + case float32: + return math.Ceil(float64(x)) + case float64: + return math.Ceil(x) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return Float(x) + } + panic(fmt.Sprintf("invalid argument for ceil (type %T)", x)) +} + +func Floor(x any) any { + switch x := x.(type) { + case float32: + return math.Floor(float64(x)) + case float64: + return math.Floor(x) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return Float(x) + } + panic(fmt.Sprintf("invalid argument for floor (type %T)", x)) +} + +func Round(x any) any { + switch x := x.(type) { + case float32: + return math.Round(float64(x)) + case float64: + return math.Round(x) + case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: + return Float(x) + } + panic(fmt.Sprintf("invalid argument for round (type %T)", x)) +} + func Int(x any) any { switch x := x.(type) { case float32: diff --git a/docs/Language-Definition.md b/docs/Language-Definition.md index 352b2ec79..46bad9322 100644 --- a/docs/Language-Definition.md +++ b/docs/Language-Definition.md @@ -380,6 +380,30 @@ min(5, 7) == 5 Returns the absolute value of a number. +### ceil(n) + +Returns the least integer value greater than or equal to x. + +```expr +ceil(1.5) == 2.0 +``` + +### floor(n) + +Returns the greatest integer value less than or equal to x. + +```expr +floor(1.5) == 1.0 +``` + +### round(n) + +Returns the nearest integer, rounding half away from zero. + +```expr +round(1.5) == 2.0 +``` + ## Array Functions ### all(array, predicate)