-
-
Notifications
You must be signed in to change notification settings - Fork 401
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
<!--- 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`
- Loading branch information
Showing
4 changed files
with
294 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(()) | ||
} |