From b5384548160039988c11f8f02e58d0854b28c6cd Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Mon, 7 Dec 2020 20:32:51 -0800 Subject: [PATCH 1/7] Breaking change: runParser prints error message and position on failure --- src/Text/Parsing/StringParser.purs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index 0bd987c..16a2f40 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -43,9 +43,15 @@ newtype Parser a = Parser (PosString -> Either { pos :: Pos, error :: ParseError unParser :: forall a. Parser a -> PosString -> Either { pos :: Pos, error :: ParseError } { result :: a, suffix :: PosString } unParser (Parser p) = p --- | Run a parser for an input string, returning either an error or a result. -runParser :: forall a. Parser a -> String -> Either ParseError a -runParser (Parser p) s = bimap _.error _.result (p { str: s, pos: 0 }) +-- | Run a parser for an input string. If the parser succeeds, the +-- | result will be returned (i.e. `Right a`). If it fails, the message and +-- | the position where the parser failed will be outputted +-- | as a `String` (i.e. `Left (msg <> " ; pos = " <> pos)`) +runParser :: forall a. Parser a -> String -> Either String a +runParser (Parser p) s = bimap printError _.result (p { str: s, pos: 0 }) + where + printError :: { pos :: Pos, error :: ParseError } -> String + printError rec = show rec.error <> "; pos = " <> show rec.pos instance functorParser :: Functor Parser where map f (Parser p) = Parser (map (\{ result, suffix } -> { result: f result, suffix }) <<< p) From bc25bae8d88d02d82a5d8e99e34699885c7f6e50 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Mon, 7 Dec 2020 20:35:08 -0800 Subject: [PATCH 2/7] Change ParseError to a record type with error message and position --- src/Text/Parsing/StringParser.purs | 21 +++++++------------- src/Text/Parsing/StringParser/CodeUnits.purs | 12 +++++------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index 16a2f40..ad09ff6 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -26,32 +26,25 @@ 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 +type ParseError = { error :: String, pos :: Pos } -- | 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 }) +newtype Parser a = Parser (PosString -> Either 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 } +unParser :: forall a. Parser a -> PosString -> Either ParseError { result :: a, suffix :: PosString } unParser (Parser p) = p -- | Run a parser for an input string. If the parser succeeds, the -- | result will be returned (i.e. `Right a`). If it fails, the message and -- | the position where the parser failed will be outputted --- | as a `String` (i.e. `Left (msg <> " ; pos = " <> pos)`) +-- | as a `String` (i.e. `Left (error <> " ; pos = " <> pos)`) runParser :: forall a. Parser a -> String -> Either String a runParser (Parser p) s = bimap printError _.result (p { str: s, pos: 0 }) where - printError :: { pos :: Pos, error :: ParseError } -> String - printError rec = show rec.error <> "; pos = " <> show rec.pos + printError :: ParseError -> String + printError 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) @@ -99,7 +92,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..6c27615 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(..), ParseError, 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" } From 6a9f0932068d9e69e8a1be361999d829c4d5e171 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Mon, 7 Dec 2020 20:36:46 -0800 Subject: [PATCH 3/7] Remove unused import --- src/Text/Parsing/StringParser/CodeUnits.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Parsing/StringParser/CodeUnits.purs b/src/Text/Parsing/StringParser/CodeUnits.purs index 6c27615..2d32c4d 100644 --- a/src/Text/Parsing/StringParser/CodeUnits.purs +++ b/src/Text/Parsing/StringParser/CodeUnits.purs @@ -37,7 +37,7 @@ 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. From c225067618080eaa30c1207d760ddeaa1a706c61 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Mon, 7 Dec 2020 21:03:48 -0800 Subject: [PATCH 4/7] Update runParser to return ParseError rather than String on Left --- src/Text/Parsing/StringParser.purs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index ad09ff6..0e6f25d 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -36,15 +36,13 @@ newtype Parser a = Parser (PosString -> Either ParseError { result :: a, suffix unParser :: forall a. Parser a -> PosString -> Either ParseError { result :: a, suffix :: PosString } unParser (Parser p) = p --- | Run a parser for an input string. If the parser succeeds, the --- | result will be returned (i.e. `Right a`). If it fails, the message and --- | the position where the parser failed will be outputted --- | as a `String` (i.e. `Left (error <> " ; pos = " <> pos)`) -runParser :: forall a. Parser a -> String -> Either String a -runParser (Parser p) s = bimap printError _.result (p { str: s, pos: 0 }) - where - printError :: ParseError -> String - printError rec = rec.error <> "; pos = " <> show rec.pos +-- | Run a parser for an input string. See also `printParserError`. +runParser :: forall a. Parser a -> String -> Either ParseError a +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) From 182c4aaf81e884d4fb98a72683ec14faf601ad58 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Mon, 7 Dec 2020 21:05:46 -0800 Subject: [PATCH 5/7] Remove unused import --- src/Text/Parsing/StringParser.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index 0e6f25d..bd267c4 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. From 39e2b168673da40a9af06eb522aac9f216502708 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 8 Dec 2020 18:28:19 -0800 Subject: [PATCH 6/7] Remove 'continuation' comments in docs --- src/Text/Parsing/StringParser.purs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index bd267c4..f09514b 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -28,11 +28,15 @@ type PosString = { str :: String, pos :: Pos } -- | The type of parsing errors. type ParseError = { error :: String, pos :: Pos } --- | A parser is represented as a function which takes a pair of --- | continuations for failure and success. +-- | 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 by providing success and failure continuations. +-- | 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 From 8ae7cbbc667e7fa1edaa09030440cbde5d3eae13 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 8 Dec 2020 18:28:34 -0800 Subject: [PATCH 7/7] Provide contrast in runParser's docs to unParser docs --- src/Text/Parsing/StringParser.purs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Text/Parsing/StringParser.purs b/src/Text/Parsing/StringParser.purs index f09514b..b690639 100644 --- a/src/Text/Parsing/StringParser.purs +++ b/src/Text/Parsing/StringParser.purs @@ -40,7 +40,8 @@ newtype Parser a = Parser (PosString -> Either ParseError { result :: a, suffix unParser :: forall a. Parser a -> PosString -> Either ParseError { result :: a, suffix :: PosString } unParser (Parser p) = p --- | Run a parser for an input string. See also `printParserError`. +-- | 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 = map _.result (p { str: s, pos: 0 })