// The shared argument rule of the built-in functions (spec M4d1 §4).
///|
/// The values a built-in function accepts at one argument position. NULL and
/// MISSING are handled by `apply`, never by a `Param`.
priv enum Param {
/// A character string (an Ion symbol is a string after unboxing).
Text
/// A character or binary string: String, Blob or Clob.
Bytes
/// A list, bag, tuple or map.
Collection
Tuple
Map
/// Any value (a map key; the map decides compatibility).
Any
/// An integer or a decimal with scale 0 (SQL MOD: exact numeric, scale 0).
Exact
/// A number or an interval (ABS).
Numeric
/// An integer: Int or BigInt.
Integer
/// A datetime (DATE, TIME, TIMESTAMP) or an interval.
Temporal
/// A datetime: DATE, TIME or TIMESTAMP.
Datetime
}
///|
fn Param::accepts(self : Param, v : @value.Value) -> Bool {
match (self, v) {
(Text, String(_)) => true
(Bytes, String(_) | Blob(_) | Clob(_)) => true
(Collection, List(_) | Bag(_) | Tuple(_) | Map(_)) => true
(Tuple, Tuple(_)) => true
(Map, Map(_)) => true
(Any, _) => true
(Exact, Int(_) | BigInt(_)) => true
(Exact, Decimal(d)) => d.exponent() == 0
(Numeric, Int(_) | BigInt(_) | Decimal(_) | Float(_)) => true
(Numeric, IntervalYM(_) | IntervalDT(_)) => true
(Integer, Int(_) | BigInt(_)) => true
(Temporal, Date(_) | Time(_) | Timestamp(_)) => true
(Temporal, IntervalYM(_) | IntervalDT(_)) => true
(Datetime, Date(_) | Time(_) | Timestamp(_)) => true
_ => false
}
}
///|
/// Runs a built-in on its arguments (spec M4d1 §4.3; partiql-tests
/// mod.ion:159-204, substring.ion:974, position.ion:110): unbox the Ion
/// values; a non-absent argument of a type its parameter does not accept is
/// a `TypeFailure`, also when another argument is MISSING or NULL; then any
/// MISSING gives MISSING; then any NULL gives NULL; else `compute`.
fn apply(
name : String,
params : Array[Param],
args : Array[@value.Value],
compute : (Array[@value.Value]) -> @value.Value raise OpFailure,
) -> @value.Value raise OpFailure {
guard params.length() == args.length() else {
raise DataFailure("\{name} got \{args.length()} arguments")
}
let values : Array[@value.Value] = []
for arg in args {
values.push(boxed(arg))
}
for i, v in values {
if !v.is_absent() && !params[i].accepts(v) {
raise TypeFailure("argument \{i + 1} of \{name} has the wrong type")
}
}
if values.iter().any(v => v is Missing) {
return Missing
}
if values.iter().any(v => v is Null) {
return Null
}
compute(values)
}