feat: add regular expression functions; (#52)

* feat: add regular expression functions;

* add cargo.toml regex

* fix cargo fmt
This commit is contained in:
stefan-gorules
2023-07-02 19:42:58 +02:00
committed by GitHub
parent 9caac0c812
commit fe7ba6996d
7 changed files with 89 additions and 0 deletions
+1
View File
@@ -15,6 +15,7 @@ fastrand = "1.9.0"
humantime = "2.1.0"
once_cell = "1.17.2"
phf = "0.11.1"
regex = "1.8.4"
serde_json = "1.0.96"
hashbrown = "0.13.2"
rust_decimal = "1.29.1"
+1
View File
@@ -17,6 +17,7 @@ humantime = { workspace = true }
fastrand = { workspace = true }
once_cell = { workspace = true }
phf = { workspace = true, features = ["macros"] }
regex = { workspace = true }
serde_json = { workspace = true, features = ["arbitrary_precision"] }
thiserror = { workspace = true }
rust_decimal = { workspace = true, features = ["maths-nopanic"] }
+10
View File
@@ -321,6 +321,16 @@ impl<'a> Compiler<'a> {
self.compile_argument(name, arguments, 1)?;
Ok(self.emit(Opcode::Contains))
}
"matches" => {
self.compile_argument(name, arguments, 0)?;
self.compile_argument(name, arguments, 1)?;
Ok(self.emit(Opcode::Matches))
}
"extract" => {
self.compile_argument(name, arguments, 0)?;
self.compile_argument(name, arguments, 1)?;
Ok(self.emit(Opcode::Extract))
}
"flatten" => {
self.compile_argument(name, arguments, 0)?;
Ok(self.emit(Opcode::Flatten))
+2
View File
@@ -108,6 +108,8 @@ pub enum Opcode<'a> {
Lowercase,
StartsWith,
EndsWith,
Matches,
Extract,
Slice,
Array,
Len,
@@ -44,6 +44,9 @@ pub(crate) static BUILT_INS: Lazy<HashMap<&'static str, BuiltIn, ADefHasher>> =
"startsWith" => BuiltIn { arity: Arity::Dual },
"endsWith" => BuiltIn { arity: Arity::Dual },
"contains" => BuiltIn { arity: Arity::Dual },
"matches" => BuiltIn { arity: Arity::Dual },
"extract" => BuiltIn { arity: Arity::Dual },
"all" => BuiltIn { arity: Arity::Closure },
"some" => BuiltIn { arity: Arity::Closure },
"none" => BuiltIn { arity: Arity::Closure },
+51
View File
@@ -1,6 +1,7 @@
use bumpalo::Bump;
use chrono::NaiveDateTime;
use chrono::{Datelike, Timelike};
use regex::Regex;
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::{Decimal, MathematicalOps};
use thiserror::Error;
@@ -833,6 +834,56 @@ impl<'a> VM<'a> {
}
}
}
Opcode::Matches => {
let b = self.pop()?;
let a = self.pop()?;
let (String(a), String(b)) = (a, b) else {
return Err(OpcodeErr {
opcode: "Matches".into(),
message: "Unsupported type".into(),
})
};
let regex = Regex::new(b).map_err(|_| OpcodeErr {
opcode: "Matches".into(),
message: "Invalid regular expression".into(),
})?;
self.push(Bool(regex.is_match(a)));
}
Opcode::Extract => {
let b = self.pop()?;
let a = self.pop()?;
let (String(a), String(b)) = (a, b) else {
return Err(OpcodeErr {
opcode: "Matches".into(),
message: "Unsupported type".into(),
})
};
let regex = Regex::new(b).map_err(|_| OpcodeErr {
opcode: "Matches".into(),
message: "Invalid regular expression".into(),
})?;
let captures = regex
.captures(a)
.map(|capture| {
capture
.iter()
.map(|c| c.map(|c| c.as_str()))
.filter_map(|c| c)
.map(|s| {
self.bump.alloc(String(self.bump.alloc_str(s))) as &Variable
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
self.push(Array(self.bump.alloc_slice_copy(captures.as_slice())));
}
Opcode::DateManipulation(operation) => {
let timestamp = self.pop()?;
+21
View File
@@ -486,6 +486,27 @@ fn isolate_standard_test() {
},
]),
},
TestEnv {
env: json!({}),
cases: Vec::from([
TestCase {
expr: r#"extract("2022-02-01", "(\d{4})-(\d{2})-(\d{2})")"#,
result: json!(["2022-02-01", "2022", "02", "01"]),
},
TestCase {
expr: r#"all(["babble", "bebble", "bibble", "bobble", "bubble"], matches(#, "b[aeiou]bble"))"#,
result: json!(true),
},
TestCase {
expr: r#"none(["babble", "bebble", "bibble", "bobble", "bubble"], matches(#, "b[aeiou]bblo"))"#,
result: json!(true),
},
TestCase {
expr: r#"extract("foo.bar", "(\w+)\.(\w+)")"#,
result: json!(["foo.bar", "foo", "bar"]),
},
]),
},
]);
let isolate = Isolate::default();