-
Notifications
You must be signed in to change notification settings - Fork 50
Generalize StringLike to StreamLike fix #58 #62
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
Changes from 15 commits
f0ba9e4
a991f94
2f59245
fdcb5ba
4f74e34
9ff887b
2471c05
ad4a76c
b89442b
67926be
453d6a1
96dc7da
95eee9b
858fda9
478be1e
b4dc8ce
902e4db
e8c9bdb
19e1ed4
499c1d0
9c7e9e9
5b38fe8
ecb6a3f
ea96e73
61d6317
13d4bf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,89 +2,113 @@ | |
|
||
module Text.Parsing.Parser.String where | ||
|
||
import Data.String as S | ||
import Control.Monad.State (modify, gets) | ||
import Data.Array (many) | ||
import Data.Foldable (elem, notElem) | ||
import Data.Foldable (fold, elem, notElem) | ||
import Data.List as L | ||
import Data.Monoid.Endo (Endo(..)) | ||
import Data.Maybe (Maybe(..)) | ||
import Data.Newtype (wrap) | ||
import Data.String (Pattern, fromCharArray, length, singleton) | ||
import Data.Newtype (class Newtype, unwrap) | ||
import Data.String as S | ||
import Control.Monad.State (modify, gets) | ||
import Text.Parsing.Parser (ParseState(..), ParserT, fail) | ||
import Text.Parsing.Parser.Combinators (try, (<?>)) | ||
import Text.Parsing.Parser.Pos (updatePosString) | ||
import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) | ||
import Prelude hiding (between) | ||
|
||
-- | A newtype used in cases where there is a prefix to be stripPrefixed. | ||
newtype Prefix a = Prefix a | ||
|
||
derive instance eqPrefix :: Eq a => Eq (Prefix a) | ||
derive instance ordPrefix :: Ord a => Ord (Prefix a) | ||
derive instance newtypePrefix :: Newtype (Prefix a) _ | ||
|
||
instance showPrefix :: Show a => Show (Prefix a) where | ||
show (Prefix s) = "(Prefix " <> show s <> ")" | ||
|
||
class HasUpdatePosition a where | ||
updatePos :: Position -> a -> Position | ||
|
||
instance stringHasUpdatePosition :: HasUpdatePosition String where | ||
updatePos = updatePosString | ||
|
||
instance charHasUpdatePosition :: HasUpdatePosition Char where | ||
updatePos = updatePosChar | ||
|
||
-- | This class exists to abstract over streams which support the string-like | ||
-- | operations which this modules needs. | ||
class StringLike s where | ||
drop :: Int -> s -> s | ||
indexOf :: Pattern -> s -> Maybe Int | ||
null :: s -> Boolean | ||
uncons :: s -> Maybe { head :: Char, tail :: s } | ||
|
||
instance stringLikeString :: StringLike String where | ||
uncons = S.uncons | ||
drop = S.drop | ||
indexOf = S.indexOf | ||
null = S.null | ||
|
||
-- | Match end-of-file. | ||
eof :: forall s m. StringLike s => Monad m => ParserT s m Unit | ||
-- | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this description is outdated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are we fine with description and the law? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine, yeah. |
||
-- | Instances must satisfy the following laws: | ||
-- | - `stripPrefix (Prefix a) a >>= uncons = Nothing` | ||
class StreamLike f c | f -> c where | ||
uncons :: f -> Maybe { head :: c, tail :: f, updatePos :: Position -> Position } | ||
stripPrefix :: Prefix f -> f -> Maybe { rest :: f, updatePos :: Position -> Position } | ||
|
||
instance stringStreamLike :: StreamLike String Char where | ||
uncons f = S.uncons f <#> \({ head, tail}) -> | ||
{ head, tail, updatePos: (_ `updatePos` head)} | ||
stripPrefix (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> | ||
{ rest, updatePos: (_ `updatePos` p)} | ||
|
||
instance listStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where | ||
uncons f = L.uncons f <#> \({ head, tail}) -> | ||
{ head, tail, updatePos: (_ `updatePos` head)} | ||
stripPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> | ||
{ rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} | ||
|
||
-- | Match end of stream. | ||
eof :: forall f c m. StreamLike f c => Monad m => ParserT f m Unit | ||
eof = do | ||
input <- gets \(ParseState input _ _) -> input | ||
unless (null input) (fail "Expected EOF") | ||
case uncons input of | ||
Nothing -> pure unit | ||
_ -> fail "Expected EOF" | ||
|
||
-- | Match the specified string. | ||
string :: forall s m. StringLike s => Monad m => String -> ParserT s m String | ||
string str = do | ||
-- | Match the specified prefix. | ||
prefix :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f | ||
prefix str = do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The argument name |
||
input <- gets \(ParseState input _ _) -> input | ||
case indexOf (wrap str) input of | ||
Just 0 -> do | ||
case stripPrefix (Prefix str) input of | ||
Just {rest, updatePos} -> do | ||
modify \(ParseState _ position _) -> | ||
ParseState (drop (length str) input) | ||
(updatePosString position str) | ||
true | ||
ParseState rest (updatePos position) true | ||
pure str | ||
_ -> fail ("Expected " <> show str) | ||
|
||
-- | Match any character. | ||
anyChar :: forall s m. StringLike s => Monad m => ParserT s m Char | ||
anyChar = do | ||
-- | Match any token. | ||
token :: forall f c m. StreamLike f c => Monad m => ParserT f m c | ||
token = do | ||
input <- gets \(ParseState input _ _) -> input | ||
case uncons input of | ||
Nothing -> fail "Unexpected EOF" | ||
Just { head, tail } -> do | ||
Just ({ head, updatePos, tail }) -> do | ||
modify \(ParseState _ position _) -> | ||
ParseState tail | ||
(updatePosString position (singleton head)) | ||
true | ||
ParseState tail (updatePos position) true | ||
pure head | ||
|
||
-- | Match a character satisfying the specified predicate. | ||
satisfy :: forall s m. StringLike s => Monad m => (Char -> Boolean) -> ParserT s m Char | ||
-- | Match a token satisfying the specified predicate. | ||
satisfy :: forall f c m. StreamLike f c => Show c => Monad m => (c -> Boolean) -> ParserT f m c | ||
satisfy f = try do | ||
c <- anyChar | ||
c <- token | ||
if f c then pure c | ||
else fail $ "Character '" <> singleton c <> "' did not satisfy predicate" | ||
else fail $ "Character " <> show c <> " did not satisfy predicate" | ||
|
||
-- | Match the specified token | ||
match :: forall f c m. StreamLike f c => Eq c => Show c => Monad m => c -> ParserT f m c | ||
match c = satisfy (_ == c) <?> show c | ||
|
||
-- | Match the specified character | ||
char :: forall s m. StringLike s => Monad m => Char -> ParserT s m Char | ||
char c = satisfy (_ == c) <?> show c | ||
|
||
-- | Match a whitespace character. | ||
whiteSpace :: forall s m. StringLike s => Monad m => ParserT s m String | ||
whiteSpace = do | ||
cs <- many $ satisfy \c -> c == '\n' || c == '\r' || c == ' ' || c == '\t' | ||
pure $ fromCharArray cs | ||
-- | Match a whitespace characters but returns them using Array. | ||
whiteSpace :: forall f m. StreamLike f Char => Monad m => ParserT f m (Array Char) | ||
whiteSpace = many $ satisfy \c -> c == ' ' || c == '\n' || c == '\t' || c == '\r' | ||
|
||
-- | Skip whitespace characters. | ||
skipSpaces :: forall s m. StringLike s => Monad m => ParserT s m Unit | ||
skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit | ||
skipSpaces = void whiteSpace | ||
|
||
-- | Match one of the characters in the array. | ||
oneOf :: forall s m. StringLike s => Monad m => Array Char -> ParserT s m Char | ||
-- | Match one of the tokens in the array. | ||
oneOf :: forall f c m. StreamLike f c => Show c => Eq c => Monad m => Array c -> ParserT f m c | ||
oneOf ss = satisfy (flip elem ss) <?> ("one of " <> show ss) | ||
|
||
-- | Match any character not in the array. | ||
noneOf :: forall s m. StringLike s => Monad m => Array Char -> ParserT s m Char | ||
-- | Match any token not in the array. | ||
noneOf :: forall f c m. StreamLike f c => Show c => Eq c => Monad m => Array c -> ParserT f m c | ||
noneOf ss = satisfy (flip notElem ss) <?> ("none of " <> show ss) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just say "a newtype used to identify a prefix of a string".