Files
n8n-openai-adapter/lib/regex.ex
T
2012-05-13 15:33:57 +02:00

239 lines
7.7 KiB
Elixir

defmodule Regex do
@moduledoc %B"""
Regular expressions for Elixir built on top of the re module
in the Erlang Standard Library. More information can be found
on re documentation: http://www.erlang.org/doc/man/re.html
Regular expressions in Elixir can be created using Regex.compile
or using the special form with `%r`:
# A simple regular expressions that matches foo anywhere in the string
%r/foo/
# A regular expression with case insensitive options and handle unicode chars
%r/foo/iu
The re module provides several options, some of them are not
available in Elixir while others are enabled by default. The
ones enabled by default are:
* multiline - the given string is always considered to be multiline, so
`^` and `$` marks the beginning and end of each line. You need to use
`\A` and `\z` to match the end or beginning of the string
The available options, followed by their shortcut in parenthesis, are:
* unicode (u) - used when you want to match against specific unicode characters
* caseless (i) - add case insensitivity
* dotall (m) - causes dot to match newlines and also set newline to anycrlf.
The new line setting can be overwritten by setting `(*CR)` or `(*LF)` or
`(*CRLF)` or `(*ANY)` according to re documentation
* extended (x) - whitespace characters are ignored except when escaped and
allow `#` to delimit comments
* firstline (f) - forces the unanchored pattern to match before or at the first
newline, though the matched text may continue over the newline
* ungreedy (r) - invert the "greediness" of the regexp
The options not available are:
* anchored - not available, use `^` or `\A` instead
* dollar_endonly - not available, use `\z` instead
* no_auto_capture - not available, use `?:` instead
* newline - not available, use `(*CR)` or `(*LF)` or `(*CRLF)` or `(*ANYCRLF)`
or `(*ANY)` at the beginning of the regexp according to the re documentation
Most of the functions in this module accept either a binary or a char list
as string subject. The result is based on the argument (a binary will return
a binary, a char list will return a char list).
"""
@doc """
Compile the regular expression according to the given options.
The result returned is a record named :re_pattern and its
length can be modified in future releases.
Check the module documentation for more information
about the options supported by compile.
"""
def compile(source, options // "") do
source = to_binary(source)
options = to_binary(options)
re_opts = [:multiline|translate_options(options)]
{ :ok, compiled } = Erlang.re.compile(source, re_opts)
{ Regex, compiled, source, options }
end
@doc """
Returns a boolean if there was a match or not.
## Examples
Regex.match? %r/foo/, "foo" #=> true
Regex.match? %r/foo/, "bar" #=> false
"""
def match?({ Regex, compiled, _, _ }, string) do
:nomatch != Erlang.re.run(string, compiled)
end
@doc """
Runs the regular expression against the given string.
It returns a list with all matches or nil if no match ocurred.
## Examples
Regex.run %r/c(d)/, "abcd" #=> ["cd", "d"]
Regex.run %r/e/, "abcd" #=> nil
"""
def run({ Regex, compiled, _, _ }, string) do
case Erlang.re.run(string, compiled, [{ :capture, :all, return_for(string) }]) do
:nomatch ->
nil
{ :match, results } ->
results
end
end
@doc """
Returns a list with the match indexes in the given string.
The matches are tuples where the first element is the index
(zero indexed) the match happened and the second is the length
of the match.
## Examples
Regex.run %r/c(d)/, "abcd" #=> [{2,2},{3,1}]
Regex.run %r/e/, "abcd" #=> nil
"""
def indexes({ Regex, compiled, _, _ }, string) do
case Erlang.re.run(string, compiled, [{ :capture, :all, :index }, { :offset, 0 }]) do
:nomatch ->
nil
{ :match, results } ->
results
end
end
@doc """
Returns the underlying re_pattern in the regular expression.
"""
def re_pattern({ Regex, compiled, _, _ }) do
compiled
end
@doc """
Returns the regex source as binary.
## Examples
Regex.source %r(foo) #=> "foo"
"""
def source({ Regex, _, source, _ }) do
source
end
@doc """
Returns the regex options as a list.
## Examples
Regex.opts %r(foo)m #=> 'm'
"""
def opts({ Regex, _, _, opts }) do
opts
end
@doc """
Same as run, but scans the target several times collecting all matches of
the regular expression. A list is returned with each match. If the item in
the list is a binary, it means there were no captures. If the item is another
list, each element in this secondary list is a capture.
## Examples
Regex.scan %r/c(d|e)/, "abcd abce" #=> [["d"], ["e"]]
Regex.scan %r/c(?:d|e)/, "abcd abce" #=> ["cd", "ce"]
Regex.scan %r/e/, "abcd" #=> []
"""
def scan({ Regex, compiled, _, _ }, string) do
options = [{ :capture, :all, return_for(string) }, :global, { :offset, 0 }]
case Erlang.re.run(string, compiled, options) do
:nomatch -> []
{ :match, results } ->
lc result in results do
case result do
[t] -> t
[h|t] -> t
end
end
end
end
@doc """
Split the given target in the number of parts specified. If no ammount
of parts is given, it defaults to :infinity.
"""
def split({ Regex, compiled, _, _ }, string, parts // :infinity) do
options = [{ :return, return_for(string) }, :trim, { :parts, parts }]
Erlang.re.split(string, compiled, options)
end
@doc %B"""
Receives a string and a replacement and returns a string where the
first match of the regular expressions is replaced by replacement.
Inside the replacement, you can either give "&" to access the whole
regular expression or \N, where N is in integer to access an specific
matching parens.
## Examples
Regex.replace(%r/d/, "abc", "d") #=> "abc"
Regex.replace(%r/b/, "abc", "d") #=> "adc"
Regex.replace(%r/b/, "abc", "[&]") #=> "a[b]c"
Regex.replace(%r/b/, "abc", "[\\&]") #=> "a[&]c"
Regex.replace(%r/(b)/, "abc", "[\\1]") #=> "a[b]c"
"""
def replace({ Regex, compiled, _, _ }, string, replacement) do
Erlang.re.replace(string, compiled, replacement, [{ :return, return_for(string) }])
end
@doc """
The same as replace, but replaces all parts where the regular
expressions matches in the string. Please read `replace/3` for
documentation and examples.
"""
def replace_all({ Regex, compiled, _, _ }, string, replacement) do
Erlang.re.replace(string, compiled, replacement, [{ :return, return_for(string) }, :global])
end
# Helpers
@doc false
# Unescape map function used by Binary.unescape.
def unescape_map(?f), do: ?\f
def unescape_map(?n), do: ?\n
def unescape_map(?r), do: ?\r
def unescape_map(?t), do: ?\t
def unescape_map(?v), do: ?\v
def unescape_map(_), do: false
# Private Helpers
defp return_for(element) when is_binary(element), do: :binary
defp return_for(element) when is_list(element), do: :list
defp translate_options(<<?u, t|:binary>>), do: [:unicode|translate_options(t)]
defp translate_options(<<?i, t|:binary>>), do: [:caseless|translate_options(t)]
defp translate_options(<<?x, t|:binary>>), do: [:extended|translate_options(t)]
defp translate_options(<<?f, t|:binary>>), do: [:firstline|translate_options(t)]
defp translate_options(<<?r, t|:binary>>), do: [:ungreedy|translate_options(t)]
defp translate_options(<<?m, t|:binary>>), do: [:dotall,{:newline,:anycrlf}|translate_options(t)]
defp translate_options(<<>>), do: []
end