-
Notifications
You must be signed in to change notification settings - Fork 51
Updates for 0.10 #36
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
Merged
Updates for 0.10 #36
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,107 @@ | ||
module Text.Parsing.Parser where | ||
module Text.Parsing.Parser | ||
( ParseError | ||
, parseErrorMessage | ||
, parseErrorPosition | ||
, ParseState(..) | ||
, ParserT(..) | ||
, Parser | ||
, runParser | ||
, consume | ||
, fail | ||
) where | ||
|
||
import Prelude | ||
|
||
import Control.Lazy (class Lazy) | ||
import Control.Monad.State.Class (class MonadState) | ||
import Control.Monad.Trans (class MonadTrans) | ||
import Control.MonadPlus (class MonadPlus, class MonadZero, class Alternative) | ||
import Control.Plus (class Plus, class Alt) | ||
import Control.Alt (class Alt) | ||
import Control.Lazy (defer, class Lazy) | ||
import Control.Monad.Except (class MonadError, ExceptT(..), throwError, runExceptT) | ||
import Control.Monad.Rec.Class (class MonadRec) | ||
import Control.Monad.State (runStateT, class MonadState, StateT(..), gets, evalStateT, modify) | ||
import Control.Monad.Trans.Class (lift, class MonadTrans) | ||
import Control.MonadPlus (class Alternative, class MonadZero, class MonadPlus, class Plus) | ||
import Data.Either (Either(..)) | ||
import Data.Identity (Identity, runIdentity) | ||
import Data.Identity (Identity) | ||
import Data.Newtype (class Newtype, unwrap) | ||
import Data.Tuple (Tuple(..)) | ||
import Text.Parsing.Parser.Pos (Position, initialPos) | ||
|
||
-- | A parsing error, consisting of a message and position information. | ||
data ParseError = ParseError | ||
{ message :: String | ||
, position :: Position | ||
} | ||
data ParseError = ParseError String Position | ||
|
||
parseErrorMessage :: ParseError -> String | ||
parseErrorMessage (ParseError msg _) = msg | ||
|
||
parseErrorPosition :: ParseError -> Position | ||
parseErrorPosition (ParseError _ pos) = pos | ||
|
||
instance showParseError :: Show ParseError where | ||
show (ParseError msg) = "ParseError { message: " <> msg.message <> ", position: " <> show msg.position <> " }" | ||
show (ParseError msg pos) = | ||
"(ParseError " <> show msg <> show pos <> ")" | ||
|
||
instance eqParseError :: Eq ParseError where | ||
eq (ParseError {message : m1, position : p1}) (ParseError {message : m2, position : p2}) = m1 == m2 && p1 == p2 | ||
derive instance eqParseError :: Eq ParseError | ||
derive instance ordParseError :: Ord ParseError | ||
|
||
-- | `PState` contains the remaining input and current position. | ||
data PState s = PState | ||
{ input :: s | ||
, position :: Position | ||
} | ||
-- | Contains the remaining input and current position. | ||
data ParseState s = ParseState s Position Boolean | ||
|
||
-- | The Parser monad transformer. | ||
-- | | ||
-- | The first type argument is the stream type. Typically, this is either `String`, or some sort of token stream. | ||
newtype ParserT s m a = ParserT (PState s -> m { input :: s, result :: Either ParseError a, consumed :: Boolean, position :: Position }) | ||
-- | The first type argument is the stream type. Typically, this is either `String`, | ||
-- | or some sort of token stream. | ||
newtype ParserT s m a = ParserT (ExceptT ParseError (StateT (ParseState s) m) a) | ||
|
||
-- | Apply a parser by providing an initial state. | ||
unParserT :: forall m s a. ParserT s m a -> PState s -> m { input :: s, result :: Either ParseError a, consumed :: Boolean, position :: Position } | ||
unParserT (ParserT p) = p | ||
derive instance newtypeParserT :: Newtype (ParserT s m a) _ | ||
|
||
-- | Apply a parser, keeping only the parsed result. | ||
runParserT :: forall m s a. Monad m => PState s -> ParserT s m a -> m (Either ParseError a) | ||
runParserT s p = do | ||
o <- unParserT p s | ||
pure o.result | ||
runParserT :: forall m s a. Monad m => s -> ParserT s m a -> m (Either ParseError a) | ||
runParserT s p = evalStateT (runExceptT (unwrap p)) initialState where | ||
initialState = ParseState s initialPos false | ||
|
||
-- | The `Parser` monad is a synonym for the parser monad transformer applied to the `Identity` monad. | ||
type Parser s a = ParserT s Identity a | ||
|
||
-- | Apply a parser, keeping only the parsed result. | ||
runParser :: forall s a. s -> Parser s a -> Either ParseError a | ||
runParser s = runIdentity <<< runParserT (PState { input: s, position: initialPos }) | ||
runParser s = unwrap <<< runParserT s | ||
|
||
instance functorParserT :: (Functor m) => Functor (ParserT s m) where | ||
map f p = ParserT $ \s -> f' <$> unParserT p s | ||
where | ||
f' o = { input: o.input, result: f <$> o.result, consumed: o.consumed, position: o.position } | ||
|
||
instance applyParserT :: Monad m => Apply (ParserT s m) where | ||
apply = ap | ||
instance lazyParserT :: Lazy (ParserT s m a) where | ||
defer f = ParserT (ExceptT (defer (runExceptT <<< unwrap <<< f))) | ||
|
||
instance applicativeParserT :: Monad m => Applicative (ParserT s m) where | ||
pure a = ParserT $ \(PState { input: s, position: pos }) -> pure { input: s, result: Right a, consumed: false, position: pos } | ||
derive newtype instance functorParserT :: Functor m => Functor (ParserT s m) | ||
derive newtype instance applyParserT :: Monad m => Apply (ParserT s m) | ||
derive newtype instance applicativeParserT :: Monad m => Applicative (ParserT s m) | ||
derive newtype instance bindParserT :: Monad m => Bind (ParserT s m) | ||
derive newtype instance monadParserT :: Monad m => Monad (ParserT s m) | ||
derive newtype instance monadRecParserT :: MonadRec m => MonadRec (ParserT s m) | ||
derive newtype instance monadStateParserT :: Monad m => MonadState (ParseState s) (ParserT s m) | ||
derive newtype instance monadErrorParserT :: Monad m => MonadError ParseError (ParserT s m) | ||
|
||
instance altParserT :: Monad m => Alt (ParserT s m) where | ||
alt p1 p2 = ParserT $ \s -> unParserT p1 s >>= \o -> | ||
case o.result of | ||
Left _ | not o.consumed -> unParserT p2 s | ||
_ -> pure o | ||
alt p1 p2 = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState i p _)) -> do | ||
Tuple e s'@(ParseState i' p' c') <- runStateT (runExceptT (unwrap p1)) (ParseState i p false) | ||
case e of | ||
Left err | ||
| not c' -> runStateT (runExceptT (unwrap p2)) s | ||
_ -> pure (Tuple e s') | ||
|
||
instance plusParserT :: Monad m => Plus (ParserT s m) where | ||
empty = fail "No alternative" | ||
|
||
instance alternativeParserT :: Monad m => Alternative (ParserT s m) | ||
|
||
instance bindParserT :: Monad m => Bind (ParserT s m) where | ||
bind p f = ParserT $ \s -> unParserT p s >>= \o -> | ||
case o.result of | ||
Left err -> pure { input: o.input, result: Left err, consumed: o.consumed, position: o.position } | ||
Right a -> updateConsumedFlag o.consumed <$> unParserT (f a) (PState { input: o.input, position: o.position }) | ||
where | ||
updateConsumedFlag c o = { input: o.input, consumed: c || o.consumed, result: o.result, position: o.position } | ||
|
||
instance monadParserT :: Monad m => Monad (ParserT s m) | ||
|
||
instance monadZeroParserT :: Monad m => MonadZero (ParserT s m) | ||
|
||
instance monadPlusParserT :: Monad m => MonadPlus (ParserT s m) | ||
|
||
instance monadTransParserT :: MonadTrans (ParserT s) where | ||
lift m = ParserT $ \(PState { input: s, position: pos }) -> (\a -> { input: s, consumed: false, result: Right a, position: pos }) <$> m | ||
|
||
instance monadStateParserT :: Monad m => MonadState s (ParserT s m) where | ||
state f = ParserT $ \(PState { input: s, position: pos }) -> | ||
pure $ case f s of | ||
Tuple a s' -> { input: s', consumed: false, result: Right a, position: pos } | ||
|
||
instance lazyParserT :: Lazy (ParserT s m a) where | ||
defer f = ParserT $ \s -> unParserT (f unit) s | ||
lift = ParserT <<< lift <<< lift | ||
|
||
-- | Set the consumed flag. | ||
consume :: forall s m. Monad m => ParserT s m Unit | ||
consume = ParserT $ \(PState { input: s, position: pos }) -> pure { consumed: true, input: s, result: Right unit, position: pos } | ||
consume = modify \(ParseState input position _) -> | ||
ParseState input position true | ||
|
||
-- | Fail with a message. | ||
fail :: forall m s a. Monad m => String -> ParserT s m a | ||
fail message = ParserT $ \(PState { input: s, position: pos }) -> pure $ parseFailed s pos message | ||
|
||
-- | Creates a failed parser state for the remaining input `s` and current position | ||
-- | with an error message. | ||
-- | | ||
-- | Most of the time, `fail` should be used instead. | ||
parseFailed :: forall s a. s -> Position -> String -> { input :: s, result :: Either ParseError a, consumed :: Boolean, position :: Position } | ||
parseFailed s pos message = { input: s, consumed: false, result: Left (ParseError { message: message, position: pos }), position: pos } | ||
fail message = do | ||
position <- gets \(ParseState _ pos _) -> pos | ||
throwError (ParseError message position) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Derive
Newtype
too? It might be uncommonly used, but may as well since theParseError
ctor isn't private or anything.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.
Can't, because of the record restriction. I could write it by hand with
type-equality
, but do you think it's worth it?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.
Ohh, yeah. I keep forgetting about that 😄.
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 going to add that deriving with
type-equality
thing for 0.10.2? Or is it not too straightforward?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 that if we do it for everything then it's not too difficult. Trying to pick out the record parts of a type would be much more difficult, or impossible in general, I think.
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.
The downside of doing it for everything is that it makes writing the instance derivation slightly trickier.