Skip to content
This repository was archived by the owner on Sep 20, 2023. It is now read-only.

Commit 3fe9792

Browse files
committed
add few utility functions
1 parent 78af45b commit 3fe9792

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

Foundation/Parser.hs

+40
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
{-# LANGUAGE Rank2Types #-}
2020
{-# LANGUAGE FlexibleContexts #-}
21+
{-# LANGUAGE BangPatterns #-}
2122

2223
module Foundation.Parser
2324
( Parser(..)
@@ -37,6 +38,10 @@ module Foundation.Parser
3738
, skip
3839
, skipWhile
3940
, skipAll
41+
-- utils
42+
, optional
43+
, many, many'
44+
, atLeast, atLeast'
4045
) where
4146

4247
import Control.Applicative (Alternative, empty, (<|>))
@@ -254,3 +259,38 @@ skipWhile p = Parser $ \buf err ok ->
254259
-- stream
255260
skipAll :: Sequential input => Parser input ()
256261
skipAll = Parser $ \buf err ok -> runParser flushAll buf err ok
262+
263+
-- | make parsing an element optional
264+
--
265+
-- > newtype HELP = HELP (Maybe String) -- the SMTP help command
266+
-- > parserHELP :: Parser (UArray Word8) HELP
267+
-- > parserHELP = do
268+
-- > elements "HELP"
269+
-- > mstr <- (HELP <$> optional (takeWhileNotCRLF))
270+
-- > elements "\r\n"
271+
-- > return $ HELP mstr
272+
--
273+
optional :: Parser input a -> Parser input (Maybe a)
274+
optional p = (Just <$> p) <|> pure Nothing
275+
276+
-- | apply the parser 0 or more times
277+
--
278+
-- > many anyElement === takeAll
279+
many :: (Sequential c, Item c ~ a) => Parser input a -> Parser input c
280+
many !p = (p >>= \v -> mappend (singleton v) <$> many p) <|> pure mempty
281+
282+
-- | same as `many` but specialised for list
283+
many' :: Parser input a -> Parser input [a]
284+
many' = many
285+
286+
-- | apply the parser at least a certain amount of time
287+
--
288+
-- > (head <$> atLeast 1 p) === anyElement
289+
--
290+
atLeast :: (Sequential c, Item c ~ a) => Int -> Parser input a -> Parser input c
291+
atLeast 0 !p = many p
292+
atLeast !n !p = p >>= \x -> mappend (singleton x) <$> atLeast (n - 1) p
293+
294+
-- | same as `atLeast` but specialised for list
295+
atLeast' :: Int -> Parser input a -> Parser input [a]
296+
atLeast' = atLeast

tests/Parser.hs

+12-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ module Parser
66
) where
77

88
import Foundation
9-
import Foundation.String
109
import Foundation.Parser
1110

1211
import Test.Tasty
@@ -23,7 +22,7 @@ parseTestCase :: (Show a, Eq a)
2322
-> Parser String a
2423
-> TestCaseRes a
2524
-> Assertion
26-
parseTestCase buff parser res = check (parse parser buff) res
25+
parseTestCase buff parser = check (parse parser buff)
2726
check :: (Show a, Eq a) => Result String a -> TestCaseRes a -> Assertion
2827
check r e = case (r, e) of
2928
(ParseOK remain a, TestCaseOk eRemain ea) -> do
@@ -71,7 +70,7 @@ parseTestCases = testGroup "units"
7170
, testCase "MoreFail" $ parseTestCase "aa" (takeWhile (' ' /=)) $ TestCaseMore Nothing TestCaseFail
7271
]
7372
, testGroup "takeAll"
74-
[ testCase "OK" $ parseTestCase "abc" (takeAll) (TestCaseMore Nothing $ TestCaseOk "" "abc")
73+
[ testCase "OK" $ parseTestCase "abc" takeAll (TestCaseMore Nothing $ TestCaseOk "" "abc")
7574
]
7675
, testGroup "skip"
7776
[ testCase "OK" $ parseTestCase "a" (skip 1) (TestCaseOk "" ())
@@ -86,7 +85,16 @@ parseTestCases = testGroup "units"
8685
, testCase "MoreFail" $ parseTestCase "aa" (skipWhile (' ' /=)) $ TestCaseMore Nothing TestCaseFail
8786
]
8887
, testGroup "skipAll"
89-
[ testCase "OK" $ parseTestCase "abc" (skipAll) (TestCaseMore Nothing $ TestCaseOk "abc" ())
88+
[ testCase "OK" $ parseTestCase "abc" skipAll (TestCaseMore Nothing $ TestCaseOk "abc" ())
89+
]
90+
, testGroup "optional"
91+
[ testCase "Nothing" $ parseTestCase "aaa" (optional $ elements "bbb") (TestCaseOk "aaa" Nothing)
92+
, testCase "Just" $ parseTestCase "aaa" (optional $ elements "a") (TestCaseOk "aa" (Just ()))
93+
]
94+
, testGroup "many"
95+
[ testCase "many elements" $ parseTestCase "101010\0"
96+
(many ((element '1' >> pure True) <|> (element '0' >> pure False) ) )
97+
(TestCaseOk "\0" [True, False, True, False, True, False])
9098
]
9199
]
92100

0 commit comments

Comments
 (0)