@@ -101,6 +101,13 @@ defmodule Regex do
101101 * `:ungreedy` (U) - inverts the "greediness" of the regexp
102102 (the previous `r` option is deprecated in favor of `U`)
103103
104+ * `:export` (E) (since Elixir 1.20) - uses an exported pattern
105+ which can be shared across nodes or through config, at the cost of a runtime
106+ overhead every time to re-import it every time it is executed.
107+ This modifier only has an effect starting on Erlang/OTP 28, and it is ignored
108+ on older versions (i.e. `~r/foo/E == ~r/foo/`). This is because patterns cannot
109+ and do not need to be exported in order to be shared in these versions.
110+
104111 ## Captures
105112
106113 Many functions in this module handle what to capture in a regex
@@ -515,7 +522,7 @@ defmodule Regex do
515522 """
516523 @ spec names ( t ) :: [ String . t ( ) ]
517524 def names ( % Regex { re_pattern: re_pattern } ) do
518- { :namelist , names } = :re . inspect ( re_pattern , :namelist )
525+ { :namelist , names } = :re . inspect ( maybe_import_pattern ( re_pattern ) , :namelist )
519526 names
520527 end
521528
@@ -585,10 +592,16 @@ defmodule Regex do
585592 % Regex { source: source , opts: compile_opts } = regex
586593 :re . run ( string , source , compile_opts ++ options )
587594 else
588- _ -> :re . run ( string , re_pattern , options )
595+ _ -> :re . run ( string , maybe_import_pattern ( re_pattern ) , options )
589596 end
590597 end
591598
599+ @ compile { :inline , maybe_import_pattern: 1 }
600+ defp maybe_import_pattern ( { :re_exported_pattern , _ , _ , _ , _ } = exported ) ,
601+ do: :re . import ( exported )
602+
603+ defp maybe_import_pattern ( pattern ) , do: pattern
604+
592605 @ typedoc """
593606 Options for regex functions that capture matches.
594607 """
@@ -1007,6 +1020,16 @@ defmodule Regex do
10071020 translate_options ( t , [ :ungreedy | acc ] )
10081021 end
10091022
1023+ defp translate_options ( << ?E , t :: binary >> , acc ) do
1024+ # on OTP 27-, the E modifier is a no-op since the feature doesn't exist but isn't needed
1025+ # (regexes aren't using references and can be shared across nodes or stored in config)
1026+ # TODO: remove this check on Erlang/OTP 28+ and update docs
1027+ case Code . ensure_loaded? ( :re ) and function_exported? ( :re , :import , 1 ) do
1028+ true -> translate_options ( t , [ :export | acc ] )
1029+ false -> translate_options ( t , acc )
1030+ end
1031+ end
1032+
10101033 defp translate_options ( << >> , acc ) , do: acc
10111034 defp translate_options ( t , _acc ) , do: { :error , t }
10121035
@@ -1022,6 +1045,9 @@ defmodule Regex do
10221045 :erlang . system_info ( :otp_release ) < [ ?2 , ?8 ] ->
10231046 Macro . escape ( regex . re_pattern )
10241047
1048+ :lists . member ( :export , regex . opts ) ->
1049+ Macro . escape ( regex . re_pattern )
1050+
10251051 # OTP 28.1+ introduced the ability to export and import regexes from compiled binaries
10261052 Code . ensure_loaded? ( :re ) and function_exported? ( :re , :import , 1 ) ->
10271053 { :ok , exported } = :re . compile ( regex . source , [ :export ] ++ regex . opts )
0 commit comments