diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index 0bd987c..b690639 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -9,7 +9,7 @@ import Control.MonadPlus (class MonadPlus, class MonadZero, class Alternative) import Control.Monad.Rec.Class (class MonadRec, tailRecM, Step(..)) import Control.Plus (class Plus, class Alt) import Control.Lazy (class Lazy) -import Data.Bifunctor (bimap, lmap) +import Data.Bifunctor (lmap) import Data.Either (Either(..)) -- | A position in an input string. @@ -26,26 +26,28 @@ type Pos = Int type PosString = { str :: String, pos :: Pos } -- | The type of parsing errors. -newtype ParseError = ParseError String - -instance showParseError :: Show ParseError where - show (ParseError msg) = msg - -derive instance eqParseError :: Eq ParseError - -derive instance ordParseError :: Ord ParseError - --- | A parser is represented as a function which takes a pair of --- | continuations for failure and success. -newtype Parser a = Parser (PosString -> Either { pos :: Pos, error :: ParseError } { result :: a, suffix :: PosString }) - --- | Run a parser by providing success and failure continuations. -unParser :: forall a. Parser a -> PosString -> Either { pos :: Pos, error :: ParseError } { result :: a, suffix :: PosString } +type ParseError = { error :: String, pos :: Pos } + +-- | A parser is represented as a function that, when successful, returns +-- | a result and the position where the parse finished or, when it fails, +-- | a ParserError with more information on where and why it failed. +-- | See also `printParserError`. +newtype Parser a = Parser (PosString -> Either ParseError { result :: a, suffix :: PosString }) + +-- | Run a parser, allowing the caller to define where to start within the +-- | input `String` and what to do with the unchanged output of the Parser. +-- | See `runparser` for more typical usages. +unParser :: forall a. Parser a -> PosString -> Either ParseError { result :: a, suffix :: PosString } unParser (Parser p) = p --- | Run a parser for an input string, returning either an error or a result. +-- | Run a parser for an input string. See also `printParserError` +-- | and `unParser` for more flexible usages. runParser :: forall a. Parser a -> String -> Either ParseError a -runParser (Parser p) s = bimap _.error _.result (p { str: s, pos: 0 }) +runParser (Parser p) s = map _.result (p { str: s, pos: 0 }) + +-- | Prints a ParseError's the error message and the position of the error. +printParserError :: ParseError -> String +printParserError rec = rec.error <> "; pos = " <> show rec.pos instance functorParser :: Functor Parser where map f (Parser p) = Parser (map (\{ result, suffix } -> { result: f result, suffix }) <<< p) @@ -93,7 +95,7 @@ instance lazyParser :: Lazy (Parser a) where -- | Fail with the specified message. fail :: forall a. String -> Parser a -fail msg = Parser \{ pos } -> Left { pos, error: ParseError msg } +fail error = Parser \{ pos } -> Left { pos, error } -- | In case of error, the default behavior is to backtrack if no input was consumed. -- | diff --git a/src/Text/Parsing/StringParser/CodeUnits.purs b/src/Text/Parsing/StringParser/CodeUnits.purs index 7150be3..2d32c4d 100644 --- a/src/Text/Parsing/StringParser/CodeUnits.purs +++ b/src/Text/Parsing/StringParser/CodeUnits.purs @@ -37,14 +37,14 @@ import Data.String.CodeUnits as SCU import Data.String.Pattern (Pattern(..)) import Data.String.Regex as Regex import Data.String.Regex.Flags (noFlags) -import Text.Parsing.StringParser (Parser(..), ParseError(..), try, fail) +import Text.Parsing.StringParser (Parser(..), try, fail) import Text.Parsing.StringParser.Combinators (many, ()) -- | Match the end of the file. eof :: Parser Unit eof = Parser \s -> case s of - { str, pos } | pos < SCU.length str -> Left { pos, error: ParseError "Expected EOF" } + { str, pos } | pos < SCU.length str -> Left { pos, error: "Expected EOF" } _ -> Right { result: unit, suffix: s } -- | Match any character. This is limited by `Char` to any code points @@ -54,14 +54,14 @@ anyChar :: Parser Char anyChar = Parser \{ str, pos } -> case charAt pos str of Just chr -> Right { result: chr, suffix: { str, pos: pos + 1 } } - Nothing -> Left { pos, error: ParseError "Unexpected EOF" } + Nothing -> Left { pos, error: "Unexpected EOF" } -- | Match any code point, including those above `0xFFFF` anyCodePoint :: Parser SCP.CodePoint anyCodePoint = Parser \rec@{ str, pos } -> case SCP.codePointAt 0 (SCU.drop pos str) of Just cp -> Right { result: cp, suffix: { str, pos: pos + SCU.length (SCP.singleton cp) } } - Nothing -> Left { pos, error: ParseError "Unexpected EOF" } + Nothing -> Left { pos, error: "Unexpected EOF" } -- | Match any digit. anyDigit :: Parser Char @@ -76,7 +76,7 @@ string :: String -> Parser String string nt = Parser \s -> case s of { str, pos } | SCU.indexOf' (Pattern nt) pos str == Just pos -> Right { result: nt, suffix: { str, pos: pos + SCU.length nt } } - { pos } -> Left { pos, error: ParseError ("Expected '" <> nt <> "'.") } + { pos } -> Left { pos, error: "Expected '" <> nt <> "'." } -- | Match a character satisfying the given predicate. satisfy :: (Char -> Boolean) -> Parser Char @@ -158,4 +158,4 @@ regex pat = Just (Just matched) -> Right { result: matched, suffix: { str, pos: pos + SCU.length matched } } _ -> - Left { pos, error: ParseError "no match" } + Left { pos, error: "no match" }