Skip to content

RegexFlags as newtype #22

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

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
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
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,30 +154,84 @@

#### `RegexFlags`

type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean }
newtype RegexFlags
= RegexFlags { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean }


### Type Class Instances

#### `boolLikeRegexFlags`

instance boolLikeRegexFlags :: BoolLike RegexFlags

#### `eqRegexFlags`

instance eqRegexFlags :: Eq RegexFlags

#### `semiGroupRegexFlags`

Example usage:
`regex "Foo" $ global <> ignoreCase`
is equivalent to
`/Foo/ig`

instance semiGroupRegexFlags :: Semigroup RegexFlags

#### `showRegex`

instance showRegex :: Show Regex

#### `showRegexFlags`

instance showRegexFlags :: Show RegexFlags


### Values

#### `flags`

flags :: Regex -> RegexFlags

#### `global`

Flags where `global : true` and all others are false

global :: RegexFlags

#### `ignoreCase`

Flags where `ignoreCase : true` and all others are false

ignoreCase :: RegexFlags

#### `match`

match :: Regex -> String -> Maybe [String]

#### `multiline`

Flags where `multiline : true` and all others are false

multiline :: RegexFlags

#### `newRegexFlags`

Produce a new `RegexFlags` from `Booleans`

newRegexFlags :: Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> RegexFlags

#### `noFlags`

All flags are set to false. Useful as a base for building flags or a default.

noFlags :: RegexFlags

#### `onRegexFlags`

Perform a `Boolean` comparison across `RegexFlags`

onRegexFlags :: (Boolean -> Boolean -> Boolean) -> RegexFlags -> RegexFlags -> RegexFlags

#### `parseFlags`

parseFlags :: String -> RegexFlags
Expand All @@ -198,6 +252,12 @@

replace' :: Regex -> (String -> [String] -> String) -> String -> String

#### `runRegexFlags`

Unwrap `RegexFlags` type to the underlying record

runRegexFlags :: RegexFlags -> { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean }

#### `search`

search :: Regex -> String -> Number
Expand All @@ -210,10 +270,22 @@

split :: Regex -> String -> [String]

#### `sticky`

Flags where `sticky : true` and all others are false

sticky :: RegexFlags

#### `test`

test :: Regex -> String -> Boolean

#### `unicode`

Flags where `unicode : true` and all others are false

unicode :: RegexFlags


## Module Data.String.Unsafe

Expand Down
108 changes: 94 additions & 14 deletions src/Data/String/Regex.purs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ module Data.String.Regex
, search
, split
, noFlags
, global
, ignoreCase
, multiline
, sticky
, unicode
, runRegexFlags
, onRegexFlags
, newRegexFlags
) where

import Data.Function
Expand All @@ -31,20 +39,91 @@ foreign import showRegex'
instance showRegex :: Show Regex where
show = showRegex'

type RegexFlags =
{ global :: Boolean
newtype RegexFlags = RegexFlags
{ global :: Boolean
, ignoreCase :: Boolean
, multiline :: Boolean
, sticky :: Boolean
, unicode :: Boolean
}
, multiline :: Boolean
, sticky :: Boolean
, unicode :: Boolean }

noFlags :: RegexFlags
noFlags = { global : false
, ignoreCase : false
, multiline : false
, sticky : false
, unicode : false }
-- | Unwrap `RegexFlags` type to the underlying record
runRegexFlags :: RegexFlags ->
{ global :: Boolean
, ignoreCase :: Boolean
, multiline :: Boolean
, sticky :: Boolean
, unicode :: Boolean }
runRegexFlags (RegexFlags x) = x

-- | Produce a new `RegexFlags` from `Booleans`
newRegexFlags :: Boolean -> Boolean -> Boolean -> Boolean -> Boolean -> RegexFlags
newRegexFlags g i m s u = RegexFlags
{ global : g
, ignoreCase : i
, multiline : m
, sticky : s
, unicode : u }

-- | All flags are set to false. Useful as a base for building flags or a default.
noFlags :: RegexFlags
noFlags = newRegexFlags false false false false false

-- | Flags where `global : true` and all others are false
global :: RegexFlags
global = newRegexFlags true false false false false

-- | Flags where `ignoreCase : true` and all others are false
ignoreCase :: RegexFlags
ignoreCase = newRegexFlags false true false false false

-- | Flags where `multiline : true` and all others are false
multiline :: RegexFlags
multiline = newRegexFlags false false true false false

-- | Flags where `sticky : true` and all others are false
sticky :: RegexFlags
sticky = newRegexFlags false false false true false

-- | Flags where `unicode : true` and all others are false
unicode :: RegexFlags
unicode = newRegexFlags false false false false true

-- | Perform a `Boolean` comparison across `RegexFlags`
onRegexFlags :: (Boolean -> Boolean -> Boolean) -> RegexFlags -> RegexFlags -> RegexFlags
onRegexFlags f x' y' = RegexFlags
{ global : x.global `f` y.global
, ignoreCase : x.ignoreCase `f` y.ignoreCase
, multiline : x.multiline `f` y.multiline
, sticky : x.sticky `f` y.sticky
, unicode : x.unicode `f` y.unicode }
where
x = runRegexFlags x'
y = runRegexFlags y'

instance showRegexFlags :: Show RegexFlags where
show (RegexFlags rf) = "RegexFlags " ++
"{ global : " ++ show rf.global ++ "\n" ++
", ignoreCase : " ++ show rf.ignoreCase ++ "\n" ++
", multiline : " ++ show rf.multiline ++ "\n" ++
", sticky : " ++ show rf.sticky ++ "\n" ++
", unicode : " ++ show rf.unicode ++ "\n" ++ " }"

instance eqRegexFlags :: Eq RegexFlags where
(==) x y = case onRegexFlags (==) x y of
RegexFlags rf -> rf.global && rf.ignoreCase && rf.multiline && rf.sticky && rf.unicode
(/=) x y = not (x == y)

instance boolLikeRegexFlags :: BoolLike RegexFlags where
(&&) = onRegexFlags (&&)
(||) = onRegexFlags (||)
not = onRegexFlags (const not) noFlags

-- | Example usage:
-- | `regex "Foo" $ global <> ignoreCase`
-- | is equivalent to
-- | `/Foo/ig`
instance semiGroupRegexFlags :: Semigroup RegexFlags where
(<>) = (||)

foreign import regex'
"""
Expand All @@ -58,6 +137,7 @@ foreign import regex'
regex :: String -> RegexFlags -> Regex
regex source flags = regex' source $ renderFlags flags


foreign import source
"""
function source(r) {
Expand All @@ -79,15 +159,15 @@ foreign import flags
""" :: Regex -> RegexFlags

renderFlags :: RegexFlags -> String
renderFlags flags =
renderFlags = runRegexFlags >>> \flags ->
(if flags.global then "g" else "") ++
(if flags.ignoreCase then "i" else "") ++
(if flags.multiline then "m" else "") ++
(if flags.sticky then "y" else "") ++
(if flags.unicode then "u" else "")

parseFlags :: String -> RegexFlags
parseFlags s =
parseFlags s = RegexFlags
{ global: indexOf "g" s >= 0
, ignoreCase: indexOf "i" s >= 0
, multiline: indexOf "m" s >= 0
Expand Down