Skip to content

Sync docs for js_re.ml #5443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 84 additions & 91 deletions jscomp/others/js_re.ml
Original file line number Diff line number Diff line change
@@ -23,169 +23,162 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)

(**
Provides bindings for JavaScript Regular Expressions

{4 Syntax sugar}
ReScript provides a bit of syntax sugar for regex literals: `[%re "/foo/g"]`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this part was removed since there is already some coverage in the manual: https://rescript-lang.org/docs/manual/latest/primitive-types#regular-expression

I guess it would generally be better to document the details in the manual instead to make the information more visible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point. Agree, the intro is better suited for the manual.

So, I’d just live here the note telling the API is not immutable.

will evaluate to a [` t`]() that can be passed around and used like usual.
Provide bindings to JS regular expressions (RegExp).

**Note:** This is not an immutable API. A RegExp object with the `global` ("g")
flag set will modify the [` lastIndex`]() property when the RegExp object is used,
and subsequent uses will ocntinue the search from the previous [` lastIndex`]().

```
let maybeMatches = "banana" |> Js.String.match_ [%re "/na+/g"]
```

**see** [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp)

**see** [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
flag set will modify the [`lastIndex`]() property when the RegExp object is used,
and subsequent uses will continue the search from the previous [`lastIndex`]().
*)

(** the RegExp object *)
(** The RegExp object. *)
type t

(** the result of a executing a RegExp on a string *)
(** The result of a executing a RegExp on a string. *)
type result

(** an array of the match and captures, the first is the full match and the remaining are the substring captures *)
(**
An `array` of the match and captures, the first is the full match and the
remaining are the substring captures.
*)
external captures : result -> string Js.nullable array = "%identity"

(**
an array of the matches, the first is the full match and the remaining are the substring matches
Deprecated. Use `captures` instead.
*)
external matches : result -> string array = "%identity"
[@@deprecated "Use Js.Re.captures instead"]

(** 0-based index of the match in the input string *)
(** 0-based index of the match in the input string. *)
external index : result -> int = "index" [@@bs.get]

(** the original input string *)
(** The original input string. *)
external input : result -> string = "input" [@@bs.get]


(**
Constructs a RegExp object ([` t`]()) from a string

Regex literals (`[%re "/.../"]`) should generally be preferred, but
`fromString` is very useful when you need to insert a string into a regex.

```
(* A function that extracts the content of the first element with the given tag *)

let contentOf tag xmlString =
Js.Re.fromString ("<" ^ tag ^ ">(.*?)<\/" ^ tag ^">")
|> Js.Re.exec xmlString
|> function
| Some result -> Js.Nullable.toOption (Js.Re.captures result).(1)
| None -> None
Constructs a RegExp object (Js.Re.t) from a `string`.
Regex literals `%re("/.../")` should generally be preferred, but `fromString`
is useful when you need to dynamically construct a regex using strings,
exactly like when you do so in JavaScript.

```res example
let firstReScriptFileExtension = (filename, content) => {
let result = Js.Re.fromString(filename ++ "\.(res|resi)")->Js.Re.exec_(content)
switch result {
| Some(r) => Js.Nullable.toOption(Js.Re.captures(r)[1])
| None => None
}
}

// outputs "res"
firstReScriptFileExtension("School", "School.res School.resi Main.js School.bs.js")
```
*)
external fromString : string -> t = "RegExp" [@@bs.new]

(**
Constructs a RegExp object ([` t`]()) from a string with the given `flags`

See [` fromString`]()
Constructs a RegExp object (`Js.Re.t`) from a string with the given flags.
See `Js.Re.fromString`.

Valid flags:
- g: global
- i: ignore case
- m: multiline
- u: unicode (es2015)
- y: sticky (es2015)

- **g** global
- **i** ignore case
- **m** multiline
- **u** unicode (es2015)
- **y** sticky (es2015)
*)
external fromStringWithFlags : string -> flags:string -> t = "RegExp" [@@bs.new]

(** returns the enabled flags as a string *)
(** Returns the enabled flags as a string. *)
external flags : t -> string = "flags" [@@bs.get]

(** returns a bool indicating whether the `global` flag is set *)
(** Returns a `bool` indicating whether the global flag is set. *)
external global : t -> bool = "global" [@@bs.get]

(** returns a bool indicating whether the `ignoreCase` flag is set *)
(** Returns a `bool` indicating whether the ignoreCase flag is set. *)
external ignoreCase : t -> bool = "ignoreCase" [@@bs.get]

(**
returns the index where the next match will start its search

This property will be modified when the RegExp object is used, if the `global` ("g")
flag is set.

```
(* Finds and prints successive matches *)

let re = [%re "/ab*/g"] in
let str = "abbcdefabh" in

let break = ref false in
while not !break do
match re |> Js.Re.exec str with
| None -> break := true
| Some result ->
Js.Nullable.iter (Js.Re.captures result).(0) ((fun match_ ->
let next = string_of_int (Js.Re.lastIndex re) in
Js.log ("Found " ^ match_ ^ ". Next match starts at " ^ next)))
done
Returns the index where the next match will start its search. This property
will be modified when the RegExp object is used, if the global ("g") flag is
set.

```res example
let re = %re("/ab*/g")
let str = "abbcdefabh"

let break = ref(false)
while !break.contents {
switch Js.Re.exec_(re, str) {
| Some(result) => Js.Nullable.iter(Js.Re.captures(result)[0], (. match_) => {
let next = Belt.Int.toString(Js.Re.lastIndex(re))
Js.log("Found " ++ (match_ ++ (". Next match starts at " ++ next)))
})
| None => break := true
}
}
```

**see** [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)
See
[`RegExp: lastIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)
on MDN.
*)
external lastIndex : t -> int = "lastIndex" [@@bs.get]

(** sets the index at which the next match will start its search from *)
(** Sets the index at which the next match will start its search from. *)
external setLastIndex : t -> int -> unit = "lastIndex" [@@bs.set]

(** returns a bool indicating whether the `multiline` flag is set *)
(** Returns a `bool` indicating whether the multiline flag is set. *)
external multiline : t -> bool = "multiline" [@@bs.get]

(** returns the pattern as a string *)
(** Returns the pattern as a `string`. *)
external source : t -> string = "source" [@@bs.get]

(** returns a bool indicating whether the `sticky` flag is set *)
(** Returns a `bool` indicating whether the sticky flag is set. *)
external sticky : t -> bool = "sticky" [@@bs.get]

(** returns a bool indicating whether the `unicode` flag is set *)
(** Returns a `bool` indicating whether the unicode flag is set. *)
external unicode : t -> bool = "unicode" [@@bs.get]

(**
executes a search on a given string using the given RegExp object
Executes a search on a given string using the given RegExp object.
Returns `Some(Js.Re.result)` if a match is found, `None` otherwise.

**return** `Some` [` result`]() if a match is found, `None` otherwise

```
(* Match "quick brown" followed by "jumps", ignoring characters in between
```res example
/* Match "quick brown" followed by "jumps", ignoring characters in between
* Remember "brown" and "jumps"
* Ignore case
*)
*/

let re = [%re "/quick\s(brown).+?(jumps)/ig"] in
let result = re |. Js.Re.exec_ "The Quick Brown Fox Jumps Over The Lazy Dog"
let re = %re("/quick\s(brown).+?(jumps)/ig")
let result = Js.Re.exec_(re, "The Quick Brown Fox Jumps Over The Lazy Dog")
```

**see** [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec)
See
[`RegExp.prototype.exec()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec)
on MDN.
*)
external exec_ : t -> string -> result option = "exec" [@@bs.send] [@@bs.return null_to_opt]


(**
tests whether the given RegExp object will match a given string
Tests whether the given RegExp object will match a given `string`.
Returns true if a match is found, false otherwise.

**return** `true` if a match is found, `false` otherwise

```
(* A simple implementation of Js.String.startsWith *)
```res example
/* A simple implementation of Js.String.startsWith */

let str = "hello world!"

let startsWith target substring =
Js.Re.fromString ("^" ^ substring)
|. Js.Re.test_ target
let startsWith = (target, substring) =>
Js.Re.fromString("^" ++ substring)->Js.Re.test_(target)

let () = Js.log (str |. startsWith "hello") (* prints "true" *)
Js.log(str->startsWith("hello")) /* prints "true" */
```

**see** [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test)
See
[`RegExp.prototype.test()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test)
on MDN.
*)
external test_ : t -> string -> bool = "test" [@@bs.send]