Skip to content

Commit e5699a9

Browse files
authored
Merge pull request #28 from newlandsvalley/master
Add a regex combinator
2 parents 1ebc4e3 + cf2e897 commit e5699a9

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

src/Text/Parsing/StringParser/String.purs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ module Text.Parsing.StringParser.String
1515
, upperCaseChar
1616
, anyLetter
1717
, alphaNum
18+
, regex
1819
) where
1920

2021
import Prelude
2122

2223
import Control.Alt ((<|>))
23-
import Data.Array ((..))
24+
import Data.Array ((..), uncons)
2425
import Data.Char (toCharCode)
2526
import Data.Either (Either(..))
2627
import Data.Foldable (class Foldable, foldMap, elem, notElem)
27-
import Data.Maybe (Maybe(..))
28-
import Data.String (Pattern(..), charAt, length, indexOf', singleton)
28+
import Data.Maybe (Maybe(..), fromMaybe)
29+
import Data.String (Pattern(..), charAt, drop, length, indexOf', singleton, stripPrefix)
30+
import Data.String.Regex as Regex
31+
import Data.String.Regex.Flags (noFlags)
2932
import Text.Parsing.StringParser (Parser(..), ParseError(..), try, fail)
3033
import Text.Parsing.StringParser.Combinators (many, (<?>))
3134

@@ -111,3 +114,31 @@ anyLetter = lowerCaseChar <|> upperCaseChar <?> "Expected a letter"
111114
-- | Match a letter or a number.
112115
alphaNum :: Parser Char
113116
alphaNum = anyLetter <|> anyDigit <?> "Expected a letter or a number"
117+
118+
-- | match the regular expression
119+
regex :: String -> Parser String
120+
regex pat =
121+
case Regex.regex pattern noFlags of
122+
Left _ ->
123+
fail $ "Text.Parsing.StringParser.String.regex': illegal regex " <> pat
124+
Right r ->
125+
matchRegex r
126+
where
127+
-- ensure the pattern only matches the current position in the parse
128+
pattern =
129+
case stripPrefix (Pattern "^") pat of
130+
Nothing ->
131+
"^" <> pat
132+
_ ->
133+
pat
134+
matchRegex :: Regex.Regex -> Parser String
135+
matchRegex r =
136+
Parser \{ str, pos } ->
137+
let
138+
remainder = drop pos str
139+
in
140+
case uncons $ fromMaybe [] $ Regex.match r remainder of
141+
Just { head: Just matched, tail: _ } ->
142+
Right { result: matched, suffix: { str, pos: pos + length matched } }
143+
_ ->
144+
Left { pos, error: ParseError "no match" }

test/Main.purs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Test.Assert (assert', ASSERT, assert)
1515
import Text.Parsing.StringParser (Parser, runParser, try)
1616
import Text.Parsing.StringParser.Combinators (many1, endBy1, sepBy1, optionMaybe, many, chainl, fix, between)
1717
import Text.Parsing.StringParser.Expr (Assoc(..), Operator(..), buildExprParser)
18-
import Text.Parsing.StringParser.String (anyDigit, eof, string, anyChar)
18+
import Text.Parsing.StringParser.String (anyDigit, eof, string, anyChar, regex)
1919

2020
parens :: forall a. Parser a -> Parser a
2121
parens = between (string "(") (string ")")
@@ -48,6 +48,7 @@ exprTest = buildExprParser [ [Infix (string "/" >>= \_ -> pure div) AssocRight]
4848
] digit
4949

5050
tryTest :: Parser String
51+
-- reduce the possible array of matches to 0 or 1 elements to aid Array pattern matching
5152
tryTest = try ((<>) <$> string "aa" <*> string "bb") <|>
5253
(<>) <$> string "aa" <*> string "cc"
5354

@@ -84,3 +85,4 @@ main = do
8485
assert' "tryTest "$ canParse tryTest "aacc"
8586
assert $ expectResult ('0':'1':'2':'3':'4':Nil) (many1 anyDigit) "01234/"
8687
assert $ expectResult ('5':'6':'7':'8':'9':Nil) (many1 anyDigit) "56789:"
88+
assert $ expectResult "aaaa" (regex "a+") "aaaab"

0 commit comments

Comments
 (0)