@@ -412,6 +412,82 @@ defmodule Regex do
412
412
opts
413
413
end
414
414
415
+ @ doc """
416
+ Returns the pattern as an embeddable string.
417
+
418
+ If the pattern was compiled with an option which cannot be represented
419
+ as an embeddable modifier in the current version of PCRE and strict is true
420
+ (the default) then an ArgumentError exception will be raised.
421
+
422
+ When the `:strict` option is false the pattern will be returned as though
423
+ any offending options had not be used and the function will not raise any
424
+ exceptions.
425
+
426
+ Embeddable modifiers/options are currently:
427
+
428
+ * 'i' - `:caseless`
429
+ * 'm' - `:multiline`
430
+ * 's' - `:dotall, {:newline, :anycrlf}`
431
+ * 'x' - `:extended`
432
+
433
+ Unembeddable modifiers are:
434
+
435
+ * 'f' - `:firstline`
436
+ * 'U' - `:ungreedy`
437
+ * 'u' - `:unicode, :ucp`
438
+
439
+ Any other regex compilation option not listed here is considered unembeddable
440
+ and will raise an exception unless the `:strict` option is false.
441
+
442
+ ## Examples
443
+ iex> Regex.to_embed(~r/foo/)
444
+ "(?-imsx:foo)"
445
+
446
+ iex> Regex.to_embed(~r/^foo/m)
447
+ "(?m-isx:^foo)"
448
+
449
+ iex> Regex.to_embed(~r/foo # comment/ix)
450
+ "(?ix-ms:foo # comment\\ n)"
451
+
452
+ iex> Regex.to_embed(~r/foo/iu)
453
+ ** (ArgumentError) regex compiled with options [:ucp, :unicode] which cannot be represented as an embedded pattern in this version of PCRE
454
+
455
+ iex> Regex.to_embed(~r/foo/imsxu, strict: false)
456
+ "(?imsx:foo\\ n)"
457
+
458
+ """
459
+ @ doc since: "1.19.0"
460
+ @ spec to_embed ( t , strict: boolean ( ) ) :: String . t ( )
461
+ def to_embed ( % Regex { source: source , opts: regex_opts } , embed_opts \\ [ ] ) do
462
+ strict = Keyword . get ( embed_opts , :strict , true )
463
+
464
+ modifiers =
465
+ case embeddable_modifiers ( regex_opts ) do
466
+ { :ok , modifiers } ->
467
+ modifiers
468
+
469
+ { :error , modifiers , untranslatable } ->
470
+ if strict do
471
+ raise ArgumentError ,
472
+ "regex compiled with options #{ inspect ( untranslatable ) } which cannot be " <>
473
+ "represented as an embedded pattern in this version of PCRE"
474
+ else
475
+ modifiers
476
+ end
477
+ end
478
+
479
+ disabled = [ ?i , ?m , ?s , ?x ] -- modifiers
480
+
481
+ disabled = if disabled != [ ] , do: "-#{ disabled } " , else: ""
482
+
483
+ # Future proof option ordering consistency by sorting
484
+ modifiers = Enum . sort ( modifiers )
485
+
486
+ nl = if Enum . member? ( regex_opts , :extended ) , do: "\n " , else: ""
487
+
488
+ "(?#{ modifiers } #{ disabled } :#{ source } #{ nl } )"
489
+ end
490
+
415
491
@ doc """
416
492
Returns a list of names in the regex.
417
493
@@ -845,6 +921,29 @@ defmodule Regex do
845
921
846
922
# Helpers
847
923
924
+ # translate options to modifiers as required for emedding
925
+ defp embeddable_modifiers ( list ) , do: embeddable_modifiers ( list , [ ] , [ ] )
926
+
927
+ defp embeddable_modifiers ( [ :dotall , { :newline , :anycrlf } | t ] , acc , err ) ,
928
+ do: embeddable_modifiers ( t , [ ?s | acc ] , err )
929
+
930
+ defp embeddable_modifiers ( [ :caseless | t ] , acc , err ) ,
931
+ do: embeddable_modifiers ( t , [ ?i | acc ] , err )
932
+
933
+ defp embeddable_modifiers ( [ :extended | t ] , acc , err ) ,
934
+ do: embeddable_modifiers ( t , [ ?x | acc ] , err )
935
+
936
+ defp embeddable_modifiers ( [ :multiline | t ] , acc , err ) ,
937
+ do: embeddable_modifiers ( t , [ ?m | acc ] , err )
938
+
939
+ defp embeddable_modifiers ( [ option | t ] , acc , err ) ,
940
+ do: embeddable_modifiers ( t , acc , [ option | err ] )
941
+
942
+ defp embeddable_modifiers ( [ ] , acc , [ ] ) , do: { :ok , acc }
943
+ defp embeddable_modifiers ( [ ] , acc , err ) , do: { :error , acc , err }
944
+
945
+ # translate modifers to options
946
+
848
947
defp translate_options ( << ?s , t :: binary >> , acc ) ,
849
948
do: translate_options ( t , [ :dotall , { :newline , :anycrlf } | acc ] )
850
949
0 commit comments