|
2 | 2 |
|
3 | 3 | module Text.Parsing.Parser.String where
|
4 | 4 |
|
| 5 | + |
| 6 | +import Control.Monad.Rec.Class (tailRecM3, Step(..)) |
5 | 7 | import Data.String as S
|
6 | 8 | import Control.Monad.State (modify, gets)
|
7 |
| -import Data.Array (many) |
8 |
| -import Data.Foldable (elem, notElem) |
| 9 | +import Data.Array (many, toUnfoldable) |
| 10 | +import Data.Foldable (elem, notElem, foldMap) |
| 11 | +import Data.Unfoldable (class Unfoldable) |
| 12 | +import Data.List as L |
9 | 13 | import Data.Maybe (Maybe(..))
|
10 |
| -import Data.Newtype (wrap) |
11 |
| -import Data.String (Pattern, fromCharArray, length, singleton) |
| 14 | +import Data.Either (Either(..)) |
| 15 | +import Data.Monoid (class Monoid) |
12 | 16 | import Text.Parsing.Parser (ParseState(..), ParserT, fail)
|
13 | 17 | import Text.Parsing.Parser.Combinators (try, (<?>))
|
14 |
| -import Text.Parsing.Parser.Pos (updatePosString) |
| 18 | +import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) |
15 | 19 | import Prelude hiding (between)
|
| 20 | +import Data.Foldable (foldl) |
| 21 | + |
| 22 | +-- | A newtype used in cases where there is a prefix string to droped. |
| 23 | +newtype Prefix f = Prefix f |
| 24 | + |
| 25 | +derive instance eqPrefix :: Eq f => Eq (Prefix f) |
| 26 | +derive instance ordPrefix :: Ord f => Ord (Prefix f) |
| 27 | +-- derive instance newtypePrefix :: Newtype Prefix _ |
| 28 | + |
| 29 | +instance showPrefix :: Show f => Show (Prefix f) where |
| 30 | + show (Prefix s) = "(Prefix " <> show s <> ")" |
| 31 | + |
| 32 | +class HasUpdatePosition a where |
| 33 | + updatePos :: Position -> a -> Position |
| 34 | + |
| 35 | +instance stringHasUpdatePosition :: HasUpdatePosition String where |
| 36 | + updatePos = updatePosString |
| 37 | + |
| 38 | +instance charHasUpdatePosition :: HasUpdatePosition Char where |
| 39 | + updatePos = updatePosChar |
16 | 40 |
|
17 | 41 | -- | This class exists to abstract over streams which support the string-like
|
18 | 42 | -- | operations which this modules needs.
|
19 |
| -class StringLike s where |
20 |
| - drop :: Int -> s -> s |
21 |
| - indexOf :: Pattern -> s -> Maybe Int |
22 |
| - null :: s -> Boolean |
23 |
| - uncons :: s -> Maybe { head :: Char, tail :: s } |
24 |
| - |
25 |
| -instance stringLikeString :: StringLike String where |
26 |
| - uncons = S.uncons |
27 |
| - drop = S.drop |
28 |
| - indexOf = S.indexOf |
29 |
| - null = S.null |
30 |
| - |
31 |
| --- | Match end-of-file. |
32 |
| -eof :: forall s m. StringLike s => Monad m => ParserT s m Unit |
| 43 | +-- | |
| 44 | +-- | Instances must satisfy the following laws: |
| 45 | +-- | |
| 46 | +class StreamLike f c | f -> c where |
| 47 | + uncons :: f -> Maybe { head :: c, tail :: f, updatePos :: (Position -> Position) } |
| 48 | + drop :: Prefix f -> f -> Maybe { rest :: f, updatePos :: (Position -> Position) } |
| 49 | + |
| 50 | +instance stringLikeString :: StreamLike String Char where |
| 51 | + uncons f = S.uncons f <#> \({ head, tail}) -> |
| 52 | + { head: head, updatePos: (_ `updatePos` head), tail} |
| 53 | + drop (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> |
| 54 | + { rest: rest, updatePos: (_ `updatePos` p)} |
| 55 | + |
| 56 | +instance listcharLikeString :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where |
| 57 | + uncons f = L.uncons f <#> \({ head, tail}) -> |
| 58 | + { head: head, updatePos: (_ `updatePos` head), tail} |
| 59 | + drop (Prefix p') s' = case (tailRecM3 go p' s' id) of -- no MonadRec for Maybe |
| 60 | + Right a -> pure a |
| 61 | + _ -> Nothing |
| 62 | + where |
| 63 | + go prefix input updatePos' = case prefix, input of |
| 64 | + (L.Cons p ps), (L.Cons i is) | p == i -> pure $ Loop |
| 65 | + ({ a: ps, b: is, c: updatePos' >>> (_ `updatePos` p) }) |
| 66 | + (L.Nil), is -> pure $ Done |
| 67 | + ({ rest: is, updatePos: updatePos' }) |
| 68 | + _, _ -> Left unit |
| 69 | + |
| 70 | +eof :: forall f c m. StreamLike f c => Monad m => ParserT f m Unit |
33 | 71 | eof = do
|
34 | 72 | input <- gets \(ParseState input _ _) -> input
|
35 |
| - unless (null input) (fail "Expected EOF") |
| 73 | + case uncons input of |
| 74 | + Nothing -> pure unit |
| 75 | + _ -> fail "Expected EOF" |
36 | 76 |
|
37 | 77 | -- | Match the specified string.
|
38 |
| -string :: forall s m. StringLike s => Monad m => String -> ParserT s m String |
| 78 | +string :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f |
39 | 79 | string str = do
|
40 | 80 | input <- gets \(ParseState input _ _) -> input
|
41 |
| - case indexOf (wrap str) input of |
42 |
| - Just 0 -> do |
| 81 | + case drop (Prefix str) input of |
| 82 | + Just {rest, updatePos} -> do |
43 | 83 | modify \(ParseState _ position _) ->
|
44 |
| - ParseState (drop (length str) input) |
45 |
| - (updatePosString position str) |
46 |
| - true |
| 84 | + ParseState rest (updatePos position) true |
47 | 85 | pure str
|
48 | 86 | _ -> fail ("Expected " <> show str)
|
49 | 87 |
|
50 | 88 | -- | Match any character.
|
51 |
| -anyChar :: forall s m. StringLike s => Monad m => ParserT s m Char |
| 89 | +anyChar :: forall f c m. StreamLike f c => Monad m => ParserT f m c |
52 | 90 | anyChar = do
|
53 | 91 | input <- gets \(ParseState input _ _) -> input
|
54 | 92 | case uncons input of
|
55 | 93 | Nothing -> fail "Unexpected EOF"
|
56 |
| - Just { head, tail } -> do |
| 94 | + Just ({ head, updatePos, tail }) -> do |
57 | 95 | modify \(ParseState _ position _) ->
|
58 |
| - ParseState tail |
59 |
| - (updatePosString position (singleton head)) |
60 |
| - true |
| 96 | + ParseState tail (updatePos position) true |
61 | 97 | pure head
|
62 | 98 |
|
63 | 99 | -- | Match a character satisfying the specified predicate.
|
64 |
| -satisfy :: forall s m. StringLike s => Monad m => (Char -> Boolean) -> ParserT s m Char |
| 100 | +satisfy :: forall f c m. StreamLike f c => Show c => Monad m => (c -> Boolean) -> ParserT f m c |
65 | 101 | satisfy f = try do
|
66 | 102 | c <- anyChar
|
67 | 103 | if f c then pure c
|
68 |
| - else fail $ "Character '" <> singleton c <> "' did not satisfy predicate" |
| 104 | + else fail $ "Character " <> show c <> " did not satisfy predicate" |
69 | 105 |
|
70 | 106 | -- | Match the specified character
|
71 |
| -char :: forall s m. StringLike s => Monad m => Char -> ParserT s m Char |
| 107 | +char :: forall f c m. StreamLike f c => Eq c => Show c => Monad m => c -> ParserT f m c |
72 | 108 | char c = satisfy (_ == c) <?> ("Expected " <> show c)
|
73 | 109 |
|
74 |
| --- | Match a whitespace character. |
75 |
| -whiteSpace :: forall s m. StringLike s => Monad m => ParserT s m String |
76 |
| -whiteSpace = do |
77 |
| - cs <- many $ satisfy \c -> c == '\n' || c == '\r' || c == ' ' || c == '\t' |
78 |
| - pure $ fromCharArray cs |
| 110 | +-- | Match many whitespace characters. |
| 111 | +whiteSpace :: forall f m g. StreamLike f Char => Unfoldable g => Monoid f => Monad m => ParserT f m (g Char) |
| 112 | +whiteSpace = map toUnfoldable whiteSpace' |
| 113 | + |
| 114 | +-- | Match a whitespace characters but returns them as Array. |
| 115 | +whiteSpace' :: forall f m. StreamLike f Char => Monad m => ParserT f m (Array Char) |
| 116 | +whiteSpace' = many $ satisfy \c -> c == '\n' || c == '\r' || c == ' ' || c == '\t' |
79 | 117 |
|
80 | 118 | -- | Skip whitespace characters.
|
81 |
| -skipSpaces :: forall s m. StringLike s => Monad m => ParserT s m Unit |
82 |
| -skipSpaces = void whiteSpace |
| 119 | +skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit |
| 120 | +skipSpaces = void whiteSpace' |
83 | 121 |
|
84 | 122 | -- | Match one of the characters in the array.
|
85 |
| -oneOf :: forall s m. StringLike s => Monad m => Array Char -> ParserT s m Char |
86 |
| -oneOf ss = satisfy (flip elem ss) <?> ("Expected one of " <> show ss) |
| 123 | +oneOf :: forall f c m. StreamLike f c => Show c => Eq c => Monad m => Array c -> ParserT f m c |
| 124 | +oneOf ss = satisfy (flip elem ss) <?> ("one of " <> show ss) |
87 | 125 |
|
88 | 126 | -- | Match any character not in the array.
|
89 |
| -noneOf :: forall s m. StringLike s => Monad m => Array Char -> ParserT s m Char |
90 |
| -noneOf ss = satisfy (flip notElem ss) <?> ("Expected none of " <> show ss) |
| 127 | +noneOf :: forall f c m. StreamLike f c => Show c => Eq c => Monad m => Array c -> ParserT f m c |
| 128 | +noneOf ss = satisfy (flip notElem ss) <?> ("none of " <> show ss) |
0 commit comments