mirror of https://github.com/boa-dev/boa.git
Browse Source
<!--- Thank you for contributing to Boa! Please fill out the template below, and remove or add any information as you feel necessary. ---> This Pull Request is related to #2098. It changes the following: - Implements `JsRegExp` - Adds a brief `JsRegExp` example under `boa_examples`pull/2347/head
Kevin
2 years ago
4 changed files with 294 additions and 2 deletions
@ -0,0 +1,269 @@ |
|||||||
|
//! This module implements a wrapper for the `RegExp` Builtin Javascript Object
|
||||||
|
use crate::{ |
||||||
|
builtins::RegExp, |
||||||
|
object::{JsArray, JsObject, JsObjectType}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
use boa_gc::{Finalize, Trace}; |
||||||
|
use std::ops::Deref; |
||||||
|
|
||||||
|
/// `JsRegExp` provides a wrapper for Boa's implementation of the JavaScript `RegExp` builtin object
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Create a `JsRegExp` and run RegExp.prototype.test( String )
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
///
|
||||||
|
/// // Initialize the `Context`
|
||||||
|
/// let context = &mut Context::default();
|
||||||
|
///
|
||||||
|
/// // Create a new RegExp with pattern and flags
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
///
|
||||||
|
/// let test_result = regexp.test("football", context).unwrap();
|
||||||
|
/// assert!(test_result);
|
||||||
|
///
|
||||||
|
/// let to_string = regexp.to_string(context).unwrap();
|
||||||
|
/// assert_eq!(to_string, String::from("/foo/gi"));
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
#[derive(Debug, Clone, Trace, Finalize)] |
||||||
|
pub struct JsRegExp { |
||||||
|
inner: JsObject, |
||||||
|
} |
||||||
|
|
||||||
|
impl JsRegExp { |
||||||
|
/// Create a new `JsRegExp` object
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
/// // Initialize the `Context`
|
||||||
|
/// let context = &mut Context::default();
|
||||||
|
///
|
||||||
|
/// // Create a new RegExp with pattern and flags
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
/// ```
|
||||||
|
#[inline] |
||||||
|
pub fn new<S>(pattern: S, flags: S, context: &mut Context) -> JsResult<Self> |
||||||
|
where |
||||||
|
S: Into<JsValue>, |
||||||
|
{ |
||||||
|
let constructor = &context |
||||||
|
.intrinsics() |
||||||
|
.constructors() |
||||||
|
.regexp() |
||||||
|
.constructor() |
||||||
|
.into(); |
||||||
|
let obj = RegExp::alloc(constructor, context)?; |
||||||
|
|
||||||
|
let regexp = RegExp::initialize(obj, &pattern.into(), &flags.into(), context)? |
||||||
|
.as_object() |
||||||
|
.expect("RegExp::initialize must return a RegExp object") |
||||||
|
.clone(); |
||||||
|
|
||||||
|
Ok(Self { inner: regexp }) |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a `JsRegExp` from a regular expression `JsObject`
|
||||||
|
#[inline] |
||||||
|
pub fn from_object(object: JsObject, context: &mut Context) -> JsResult<Self> { |
||||||
|
if object.borrow().is_regexp() { |
||||||
|
Ok(Self { inner: object }) |
||||||
|
} else { |
||||||
|
context.throw_type_error("object is not a RegExp") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `d` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn has_indices(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_has_indices(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `g` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn global(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_global(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `i` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn ignore_case(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_ignore_case(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `m` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn multiline(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_multiline(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `s` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn dot_all(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_dot_all(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `u` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn unicode(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_unicode(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a boolean value for whether the `y` flag is present in `JsRegExp` flags
|
||||||
|
#[inline] |
||||||
|
pub fn sticky(&self, context: &mut Context) -> JsResult<bool> { |
||||||
|
RegExp::get_sticky(&self.inner.clone().into(), &[], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the flags of `JsRegExp` as a string
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
/// # let context = &mut Context::default();
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
///
|
||||||
|
/// let flags = regexp.flags(context).unwrap();
|
||||||
|
/// assert_eq!(flags, String::from("gi"));
|
||||||
|
/// ```
|
||||||
|
#[inline] |
||||||
|
pub fn flags(&self, context: &mut Context) -> JsResult<String> { |
||||||
|
RegExp::get_flags(&self.inner.clone().into(), &[], context).map(|v| { |
||||||
|
v.as_string() |
||||||
|
.expect("value must be string") |
||||||
|
.to_std_string() |
||||||
|
.expect("flags must be a valid string") |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the source pattern of `JsRegExp` as a string
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
/// # let context = &mut Context::default();
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
///
|
||||||
|
/// let src = regexp.source(context).unwrap();
|
||||||
|
/// assert_eq!(src, String::from("foo"));
|
||||||
|
/// ```
|
||||||
|
#[inline] |
||||||
|
pub fn source(&self, context: &mut Context) -> JsResult<String> { |
||||||
|
RegExp::get_source(&self.inner.clone().into(), &[], context).map(|v| { |
||||||
|
v.as_string() |
||||||
|
.expect("value must be string") |
||||||
|
.to_std_string() |
||||||
|
.expect("source must be a valid string") |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Executes a search for a match between `JsRegExp` and the provided string
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
/// # let context = &mut Context::default();
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
///
|
||||||
|
/// let test_result = regexp.test("football", context).unwrap();
|
||||||
|
/// assert!(test_result);
|
||||||
|
/// ```
|
||||||
|
#[inline] |
||||||
|
pub fn test<S>(&self, search_string: S, context: &mut Context) -> JsResult<bool> |
||||||
|
where |
||||||
|
S: Into<JsValue>, |
||||||
|
{ |
||||||
|
RegExp::test(&self.inner.clone().into(), &[search_string.into()], context) |
||||||
|
.map(|v| v.as_boolean().expect("value must be a bool")) |
||||||
|
} |
||||||
|
|
||||||
|
/// Executes a search for a match in a specified string
|
||||||
|
///
|
||||||
|
/// Returns a `JsArray` containing matched value and updates the `lastIndex` property, or `None`
|
||||||
|
#[inline] |
||||||
|
pub fn exec<S>(&self, search_string: S, context: &mut Context) -> JsResult<Option<JsArray>> |
||||||
|
where |
||||||
|
S: Into<JsValue>, |
||||||
|
{ |
||||||
|
RegExp::exec(&self.inner.clone().into(), &[search_string.into()], context).map(|v| { |
||||||
|
if v.is_null() { |
||||||
|
None |
||||||
|
} else { |
||||||
|
Some( |
||||||
|
JsArray::from_object( |
||||||
|
v.to_object(context).expect("v must be an array"), |
||||||
|
context, |
||||||
|
) |
||||||
|
.expect("from_object must not fail if v is an array object"), |
||||||
|
) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Return a string representing the regular expression.
|
||||||
|
/// ```
|
||||||
|
/// # use boa_engine::{
|
||||||
|
/// # object::builtins::JsRegExp,
|
||||||
|
/// # Context, JsValue,
|
||||||
|
/// # };
|
||||||
|
/// # let context = &mut Context::default();
|
||||||
|
/// let regexp = JsRegExp::new("foo", "gi", context).unwrap();
|
||||||
|
///
|
||||||
|
/// let to_string = regexp.to_string(context).unwrap();
|
||||||
|
/// assert_eq!(to_string, String::from("/foo/gi"));
|
||||||
|
/// ```
|
||||||
|
#[inline] |
||||||
|
pub fn to_string(&self, context: &mut Context) -> JsResult<String> { |
||||||
|
RegExp::to_string(&self.inner.clone().into(), &[], context).map(|v| { |
||||||
|
v.as_string() |
||||||
|
.expect("value must be a string") |
||||||
|
.to_std_string() |
||||||
|
.expect("to_string value must be a valid string") |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<JsRegExp> for JsObject { |
||||||
|
#[inline] |
||||||
|
fn from(o: JsRegExp) -> Self { |
||||||
|
o.inner.clone() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<JsRegExp> for JsValue { |
||||||
|
#[inline] |
||||||
|
fn from(o: JsRegExp) -> Self { |
||||||
|
o.inner.clone().into() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Deref for JsRegExp { |
||||||
|
type Target = JsObject; |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn deref(&self) -> &Self::Target { |
||||||
|
&self.inner |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl JsObjectType for JsRegExp {} |
@ -0,0 +1,21 @@ |
|||||||
|
use boa_engine::{object::builtins::JsRegExp, Context, JsResult}; |
||||||
|
|
||||||
|
fn main() -> JsResult<()> { |
||||||
|
let context = &mut Context::default(); |
||||||
|
|
||||||
|
let regexp = JsRegExp::new("foo", "gi", context)?; |
||||||
|
|
||||||
|
let test_result = regexp.test("football", context)?; |
||||||
|
assert!(test_result); |
||||||
|
|
||||||
|
let flags = regexp.flags(context)?; |
||||||
|
assert_eq!(flags, String::from("gi")); |
||||||
|
|
||||||
|
let src = regexp.source(context)?; |
||||||
|
assert_eq!(src, String::from("foo")); |
||||||
|
|
||||||
|
let to_string = regexp.to_string(context)?; |
||||||
|
assert_eq!(to_string, String::from("/foo/gi")); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
Loading…
Reference in new issue