diff --git a/core/expression/src/compiler.rs b/core/expression/src/compiler.rs index ad0acd51..a0acdbeb 100644 --- a/core/expression/src/compiler.rs +++ b/core/expression/src/compiler.rs @@ -11,7 +11,7 @@ use crate::ast::Node; use crate::compiler::CompilerError::{ ArgumentNotFound, UnknownBinaryOperator, UnknownBuiltIn, UnknownUnaryOperator, }; -use crate::opcodes::{Opcode, TypeConversionKind, Variable}; +use crate::opcodes::{Opcode, TypeCheckKind, TypeConversionKind, Variable}; type Bytecode<'a> = Rc>>>; @@ -399,6 +399,10 @@ impl<'a> Compiler<'a> { self.compile_argument(name, arguments, 0)?; Ok(self.emit(Opcode::TypeConversion(TypeConversionKind::Number))) } + "isNumeric" => { + self.compile_argument(name, arguments, 0)?; + Ok(self.emit(Opcode::TypeCheck(TypeCheckKind::Numeric))) + } "startOf" | "endOf" => { self.compile_argument(name, arguments, 0)?; self.compile_argument(name, arguments, 1)?; diff --git a/core/expression/src/opcodes.rs b/core/expression/src/opcodes.rs index 6d499aee..c5239052 100644 --- a/core/expression/src/opcodes.rs +++ b/core/expression/src/opcodes.rs @@ -143,6 +143,7 @@ pub enum Opcode<'a> { End, Flatten, TypeConversion(TypeConversionKind), + TypeCheck(TypeCheckKind), } #[derive(Debug)] @@ -151,6 +152,11 @@ pub enum TypeConversionKind { String, } +#[derive(Debug)] +pub enum TypeCheckKind { + Numeric, +} + impl TryFrom<&Variable<'_>> for Value { type Error = (); diff --git a/core/expression/src/parser/standard/constants.rs b/core/expression/src/parser/standard/constants.rs index f825a162..2c52ab24 100644 --- a/core/expression/src/parser/standard/constants.rs +++ b/core/expression/src/parser/standard/constants.rs @@ -22,6 +22,7 @@ pub(crate) static BUILT_INS: Lazy> = "string" => BuiltIn { arity: Arity::Single }, "number" => BuiltIn { arity: Arity::Single }, + "isNumeric" => BuiltIn { arity: Arity::Single }, "abs" => BuiltIn { arity: Arity::Single }, "sum" => BuiltIn { arity: Arity::Single }, diff --git a/core/expression/src/parser/unary/constants.rs b/core/expression/src/parser/unary/constants.rs index 353a90b6..d5d71fd2 100644 --- a/core/expression/src/parser/unary/constants.rs +++ b/core/expression/src/parser/unary/constants.rs @@ -10,6 +10,7 @@ pub(crate) const BUILT_INS: Map<&'static str, BuiltIn> = phf_map! { "string" => BuiltIn { arity: Arity::Single }, "number" => BuiltIn { arity: Arity::Single }, + "isNumeric" => BuiltIn { arity: Arity::Single }, "upper" => BuiltIn { arity: Arity::Single }, "lower" => BuiltIn { arity: Arity::Single }, diff --git a/core/expression/src/vm.rs b/core/expression/src/vm.rs index d80e826f..880c9a14 100644 --- a/core/expression/src/vm.rs +++ b/core/expression/src/vm.rs @@ -14,7 +14,7 @@ use thiserror::Error; use crate::helpers::{date_time, date_time_end_of, date_time_start_of, time}; use crate::opcodes::Variable::{Array, Bool, Null, Number, Object, String}; -use crate::opcodes::{IntervalObject, Opcode, TypeConversionKind, Variable}; +use crate::opcodes::{IntervalObject, Opcode, TypeCheckKind, TypeConversionKind, Variable}; use crate::vm::VMError::{OpcodeErr, OpcodeOutOfBounds, ParseDateTimeErr, StackOutOfBounds}; const NULL_VAR: &'static Variable = &Null; @@ -1206,6 +1206,19 @@ impl<'a> VM<'a> { self.stack.push(self.bump.alloc(Number(dur.into()))); } + Opcode::TypeCheck(check) => { + let var = self.pop()?; + + let is_equal = match (check, var) { + (TypeCheckKind::Numeric, String(str)) => { + Decimal::from_str_exact(str).is_ok() + } + (TypeCheckKind::Numeric, Number(_)) => true, + (TypeCheckKind::Numeric, _) => false, + }; + + self.push(Bool(is_equal)); + } Opcode::TypeConversion(conversion) => { let var = self.pop()?; diff --git a/core/expression/tests/data/standard.csv b/core/expression/tests/data/standard.csv index 7ca8fcdd..b43917d6 100644 --- a/core/expression/tests/data/standard.csv +++ b/core/expression/tests/data/standard.csv @@ -234,4 +234,12 @@ number('123');;123 number(123.123);;123.123 number(123);;123 number(true);;1 -number(false);;0 \ No newline at end of file +number(false);;0 + +# Type check +isNumeric(123.123);;true +isNumeric(123);;true +isNumeric('123.123');;true +isNumeric('123');;true +isNumeric('string');;false +isNumeric(true);;false \ No newline at end of file