-
Notifications
You must be signed in to change notification settings - Fork 50
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
Generalize StringLike to StreamLike fix #58 #62
Changes from 8 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,118 @@ | |
|
||
module Text.Parsing.Parser.String where | ||
|
||
import Data.Array (many, toUnfoldable) | ||
import Data.Foldable (fold, elem, notElem) | ||
import Data.List as L | ||
import Data.Monoid (class Monoid) | ||
import Data.Monoid.Endo (Endo(..)) | ||
import Data.Maybe (Maybe(..)) | ||
import Data.Newtype (class Newtype, unwrap) | ||
import Data.String as S | ||
import Data.Unfoldable (class Unfoldable) | ||
import Control.Monad.State (modify, gets) | ||
import Data.Array (many) | ||
import Data.Foldable (elem, notElem) | ||
import Data.Maybe (Maybe(..)) | ||
import Data.Newtype (wrap) | ||
import Data.String (Pattern, fromCharArray, length, singleton) | ||
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 droped. | ||
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 | ||
-- | | ||
-- | Instances must satisfy the following laws: | ||
-- | | ||
class StreamLike f c | f -> c where | ||
uncons :: f -> Maybe { head :: c, tail :: f, updatePos :: Position -> Position } | ||
drop :: Prefix f -> f -> Maybe { rest :: f, updatePos :: Position -> Position } | ||
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 this name? stipPrefix could be used instead too |
||
|
||
instance stringStreamLike :: StreamLike String Char where | ||
uncons f = S.uncons f <#> \({ head, tail}) -> | ||
{ head, tail, updatePos: (_ `updatePos` head)} | ||
drop (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> | ||
{ rest, updatePos: (_ `updatePos` p)} | ||
|
||
instance listcharStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where | ||
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. Name of this instance shouldn't mention 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. Also, can you please add a lazy list instance too? 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. will do once this is merged purescript/purescript-lists#126 |
||
uncons f = L.uncons f <#> \({ head, tail}) -> | ||
{ head, tail, updatePos: (_ `updatePos` head)} | ||
drop (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 | ||
-- | Match the specified stream. | ||
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 wis descriptions? 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. Yes, I think that's better. So maybe 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. ok will use:
Will also rename |
||
string :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f | ||
string str = do | ||
input <- gets \(ParseState input _ _) -> input | ||
case indexOf (wrap str) input of | ||
Just 0 -> do | ||
case drop (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 | ||
-- | Match any token. | ||
anyChar :: forall f c m. StreamLike f c => Monad m => ParserT f m c | ||
anyChar = 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 | ||
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 character | ||
char :: forall s m. StringLike s => Monad m => Char -> ParserT s m Char | ||
-- | Match the specified token | ||
char :: forall f c m. StreamLike f c => Eq c => Show c => Monad m => c -> ParserT f m c | ||
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 many whitespace character in some Unfoldable. | ||
whiteSpace :: forall f m g. StreamLike f Char => Unfoldable g => Monoid f => Monad m => ParserT f m (g Char) | ||
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 you fine with the signature? 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. We can remove this function as it's still braking change 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. Remind me again why we'd need to remove it? 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. If you are operating on list of some tokens you most likely are not gonna use it. Major use case of this would be to get I think we can just returning If you agree i would remove this function and rename 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. Sounds good, thanks! |
||
whiteSpace = map toUnfoldable whiteSpace' | ||
|
||
-- | 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 == '\n' || c == '\r' || c == ' ' || c == '\t' | ||
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 sure |
||
|
||
-- | Skip whitespace characters. | ||
skipSpaces :: forall s m. StringLike s => Monad m => ParserT s m Unit | ||
skipSpaces = void whiteSpace | ||
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.
this description is outdated
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.
are we fine with description and the law?
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 think it's fine, yeah.