From f0ba9e43f45aa842ff7cab177544508c690dc77c Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Thu, 25 May 2017 20:27:14 -0600 Subject: [PATCH 01/23] Generalize StringLike to StreamLike --- src/Text/Parsing/Parser/Pos.purs | 17 ++-- src/Text/Parsing/Parser/String.purs | 128 ++++++++++++++++++---------- 2 files changed, 92 insertions(+), 53 deletions(-) diff --git a/src/Text/Parsing/Parser/Pos.purs b/src/Text/Parsing/Parser/Pos.purs index e65c6f2..ae8ee6b 100644 --- a/src/Text/Parsing/Parser/Pos.purs +++ b/src/Text/Parsing/Parser/Pos.purs @@ -3,7 +3,7 @@ module Text.Parsing.Parser.Pos where import Prelude import Data.Foldable (foldl) import Data.Newtype (wrap) -import Data.String (split) +import Data.String (toCharArray) -- | `Position` represents the position of the parser in the input. -- | @@ -27,10 +27,11 @@ initialPos = Position { line: 1, column: 1 } -- | Updates a `Position` by adding the columns and lines in `String`. updatePosString :: Position -> String -> Position -updatePosString pos' str = foldl updatePosChar pos' (split (wrap "") str) - where - updatePosChar (Position pos) c = case c of - "\n" -> Position { line: pos.line + 1, column: 1 } - "\r" -> Position { line: pos.line + 1, column: 1 } - "\t" -> Position { line: pos.line, column: pos.column + 8 - ((pos.column - 1) `mod` 8) } - _ -> Position { line: pos.line, column: pos.column + 1 } +updatePosString pos' str = foldl updatePosChar pos' (toCharArray str) + +updatePosChar :: Position -> Char -> Position +updatePosChar (Position pos) c = case c of + '\n' -> Position { line: pos.line + 1, column: 1 } + '\r' -> Position { line: pos.line + 1, column: 1 } + '\t' -> Position { line: pos.line, column: pos.column + 8 - ((pos.column - 1) `mod` 8) } + _ -> Position { line: pos.line, column: pos.column + 1 } diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index fb72c0c..a12adeb 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -2,89 +2,127 @@ module Text.Parsing.Parser.String where + +import Control.Monad.Rec.Class (tailRecM3, Step(..)) import Data.String as S import Control.Monad.State (modify, gets) -import Data.Array (many) -import Data.Foldable (elem, notElem) +import Data.Array (many, toUnfoldable) +import Data.Foldable (elem, notElem, foldMap) +import Data.Unfoldable (class Unfoldable) +import Data.List as L import Data.Maybe (Maybe(..)) -import Data.Newtype (wrap) -import Data.String (Pattern, fromCharArray, length, singleton) +import Data.Either (Either(..)) +import Data.Monoid (class Monoid) 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) +import Data.Foldable (foldl) + +-- | A newtype used in cases where there is a prefix string to droped. +newtype Prefix f = Prefix f + +derive instance eqPrefix :: Eq f => Eq (Prefix f) +derive instance ordPrefix :: Ord f => Ord (Prefix f) +-- derive instance newtypePrefix :: Newtype Prefix _ + +instance showPrefix :: Show f => Show (Prefix f) 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) } + +instance stringLikeString :: StreamLike String Char where + uncons f = S.uncons f <#> \({ head, tail}) -> + { head: head, updatePos: (_ `updatePos` head), tail} + drop (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> + { rest: rest, updatePos: (_ `updatePos` p)} + +instance listcharLikeString :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where + uncons f = L.uncons f <#> \({ head, tail}) -> + { head: head, updatePos: (_ `updatePos` head), tail} + drop (Prefix p') s' = case (tailRecM3 go p' s' id) of -- no MonadRec for Maybe + Right a -> pure a + _ -> Nothing + where + go prefix input updatePos' = case prefix, input of + (L.Cons p ps), (L.Cons i is) | p == i -> pure $ Loop + ({ a: ps, b: is, c: updatePos' >>> (_ `updatePos` p) }) + (L.Nil), is -> pure $ Done + ({ rest: is, updatePos: updatePos' }) + _, _ -> Left unit + +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 :: 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 +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 +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 +char :: forall f c m. StreamLike f c => Eq c => Show c => Monad m => c -> ParserT f m c char c = satisfy (_ == c) ("Expected " <> 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 characters. +whiteSpace :: forall f m g. StreamLike f Char => Unfoldable g => Monoid f => Monad m => ParserT f m (g Char) +whiteSpace = map toUnfoldable whiteSpace' + +-- | Match a whitespace characters but returns them as 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' -- | 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 -oneOf ss = satisfy (flip elem ss) ("Expected one of " <> show ss) +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 -noneOf ss = satisfy (flip notElem ss) ("Expected none of " <> show ss) +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) From a991f94377e15f6eeb185504e8dd80996d8c7f0c Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 4 Jun 2017 20:00:49 +0400 Subject: [PATCH 02/23] update list instance --- bower.json | 2 +- src/Text/Parsing/Parser/String.purs | 45 ++++++++++++----------------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/bower.json b/bower.json index 4284b1b..7a6615b 100644 --- a/bower.json +++ b/bower.json @@ -25,7 +25,7 @@ "purescript-foldable-traversable": "^3.0.0", "purescript-identity": "^3.0.0", "purescript-integers": "^3.0.0", - "purescript-lists": "^4.0.0", + "purescript-lists": "git://github.com/safareli/purescript-lists.git#strip", "purescript-maybe": "^3.0.0", "purescript-strings": "^3.0.0", "purescript-transformers": "^3.0.0", diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index a12adeb..f3429db 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -3,30 +3,29 @@ module Text.Parsing.Parser.String where -import Control.Monad.Rec.Class (tailRecM3, Step(..)) import Data.String as S import Control.Monad.State (modify, gets) import Data.Array (many, toUnfoldable) -import Data.Foldable (elem, notElem, foldMap) +import Data.Foldable (fold, elem, notElem) +import Data.Newtype (class Newtype, unwrap) import Data.Unfoldable (class Unfoldable) import Data.List as L +import Data.Monoid.Endo (Endo(..)) import Data.Maybe (Maybe(..)) -import Data.Either (Either(..)) import Data.Monoid (class Monoid) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (try, ()) import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) import Prelude hiding (between) -import Data.Foldable (foldl) --- | A newtype used in cases where there is a prefix string to droped. -newtype Prefix f = Prefix f +-- | A newtype used in cases where there is a prefix to be droped. +newtype Prefix a = Prefix a -derive instance eqPrefix :: Eq f => Eq (Prefix f) -derive instance ordPrefix :: Ord f => Ord (Prefix f) --- derive instance newtypePrefix :: Newtype Prefix _ +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 f => Show (Prefix f) where +instance showPrefix :: (Show a) => Show (Prefix a) where show (Prefix s) = "(Prefix " <> show s <> ")" class HasUpdatePosition a where @@ -44,28 +43,20 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | 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) } + uncons :: f -> Maybe { head :: c, tail :: f, updatePos :: Position -> Position } + drop :: Prefix f -> f -> Maybe { rest :: f, updatePos :: Position -> Position } -instance stringLikeString :: StreamLike String Char where +instance stringStreamLike :: StreamLike String Char where uncons f = S.uncons f <#> \({ head, tail}) -> - { head: head, updatePos: (_ `updatePos` head), tail} + { head, tail, updatePos: (_ `updatePos` head)} drop (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> - { rest: rest, updatePos: (_ `updatePos` p)} + { rest, updatePos: (_ `updatePos` p)} -instance listcharLikeString :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where +instance listcharStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where uncons f = L.uncons f <#> \({ head, tail}) -> - { head: head, updatePos: (_ `updatePos` head), tail} - drop (Prefix p') s' = case (tailRecM3 go p' s' id) of -- no MonadRec for Maybe - Right a -> pure a - _ -> Nothing - where - go prefix input updatePos' = case prefix, input of - (L.Cons p ps), (L.Cons i is) | p == i -> pure $ Loop - ({ a: ps, b: is, c: updatePos' >>> (_ `updatePos` p) }) - (L.Nil), is -> pure $ Done - ({ rest: is, updatePos: updatePos' }) - _, _ -> Left unit + { head, tail, updatePos: (_ `updatePos` head)} + drop (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> + { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} eof :: forall f c m. StreamLike f c => Monad m => ParserT f m Unit eof = do From 2f59245f68b9be34fc5fe5968aa481abde007c06 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 4 Jun 2017 20:13:40 +0400 Subject: [PATCH 03/23] fix redundant parens and imports --- src/Text/Parsing/Parser/String.purs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index f3429db..8d90c6f 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -2,17 +2,16 @@ module Text.Parsing.Parser.String where - -import Data.String as S -import Control.Monad.State (modify, gets) import Data.Array (many, toUnfoldable) import Data.Foldable (fold, elem, notElem) -import Data.Newtype (class Newtype, unwrap) -import Data.Unfoldable (class Unfoldable) import Data.List as L +import Data.Monoid (class Monoid) import Data.Monoid.Endo (Endo(..)) import Data.Maybe (Maybe(..)) -import Data.Monoid (class Monoid) +import Data.Newtype (class Newtype, unwrap) +import Data.String as S +import Data.Unfoldable (class Unfoldable) +import Control.Monad.State (modify, gets) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (try, ()) import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) @@ -21,11 +20,11 @@ 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 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 +instance showPrefix :: Show a => Show (Prefix a) where show (Prefix s) = "(Prefix " <> show s <> ")" class HasUpdatePosition a where From fdcb5ba0eb3ca09e8e38df003e4b35bcb553ef51 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Mon, 5 Jun 2017 11:44:36 +0400 Subject: [PATCH 04/23] update lists --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 7a6615b..3d23f13 100644 --- a/bower.json +++ b/bower.json @@ -25,7 +25,7 @@ "purescript-foldable-traversable": "^3.0.0", "purescript-identity": "^3.0.0", "purescript-integers": "^3.0.0", - "purescript-lists": "git://github.com/safareli/purescript-lists.git#strip", + "purescript-lists": "^4.6.0", "purescript-maybe": "^3.0.0", "purescript-strings": "^3.0.0", "purescript-transformers": "^3.0.0", From 9ff887be72cba7bdde498118c4a9c4432e1f7f15 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sat, 10 Jun 2017 15:47:18 +0400 Subject: [PATCH 05/23] update description --- src/Text/Parsing/Parser/String.purs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index bce2321..204d0db 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -57,6 +57,7 @@ instance listcharStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List 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 @@ -64,7 +65,7 @@ eof = do Nothing -> pure unit _ -> fail "Expected EOF" --- | Match the specified string. +-- | Match the specified stream. 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 @@ -75,7 +76,7 @@ string str = do pure str _ -> fail ("Expected " <> show str) --- | Match any character. +-- | Match any token. anyChar :: forall f c m. StreamLike f c => Monad m => ParserT f m c anyChar = do input <- gets \(ParseState input _ _) -> input @@ -86,22 +87,22 @@ anyChar = do ParseState tail (updatePos position) true pure head --- | Match a character satisfying the specified predicate. +-- | 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 " <> show c <> " did not satisfy predicate" --- | Match the specified character +-- | 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 many whitespace characters. +-- | 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) whiteSpace = map toUnfoldable whiteSpace' --- | Match a whitespace characters but returns them as Array. +-- | 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' @@ -109,10 +110,10 @@ whiteSpace' = many $ satisfy \c -> c == '\n' || c == '\r' || c == ' ' || c == '\ skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit skipSpaces = void whiteSpace' --- | Match one of the characters in the array. +-- | 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. +-- | 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) From 2471c05a912cdd5b81fbb70a1e9009b786dad0fa Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sat, 10 Jun 2017 16:09:28 +0400 Subject: [PATCH 06/23] add script.test --- .travis.yml | 1 + package.json | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 968390b..2bc26a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ install: - bower install script: - npm run -s build + - npm run -s test after_success: - >- test $TRAVIS_TAG && diff --git a/package.json b/package.json index e6343ef..52fb9bd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "pulp build && pulp test" + "build": "pulp build", + "test": "pulp test" }, "devDependencies": { "pulp": "^11.0.0", From ad4a76c13c69db7702e4ea834917f33a38ffd681 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sat, 10 Jun 2017 16:12:06 +0400 Subject: [PATCH 07/23] remove Token{token,when,match} instead String{anyChar,satisfy,char} chould be used --- src/Text/Parsing/Parser/Token.purs | 27 +-------------------------- test/Main.purs | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/Text/Parsing/Parser/Token.purs b/src/Text/Parsing/Parser/Token.purs index 28fc8ac..47f4abd 100644 --- a/src/Text/Parsing/Parser/Token.purs +++ b/src/Text/Parsing/Parser/Token.purs @@ -1,10 +1,7 @@ -- | Functions for working with streams of tokens. module Text.Parsing.Parser.Token - ( token - , when - , match - , LanguageDef + ( LanguageDef , GenLanguageDef(LanguageDef) , unGenLanguageDef , TokenParser @@ -44,28 +41,6 @@ import Text.Parsing.Parser.Pos (Position) import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, string, char) import Prelude hiding (when,between) --- | Create a parser which Returns the first token in the stream. -token :: forall m a. Monad m => (a -> Position) -> ParserT (List a) m a -token tokpos = do - input <- gets \(ParseState input _ _) -> input - case List.uncons input of - Nothing -> fail "Unexpected EOF" - Just { head, tail } -> do - modify \(ParseState _ position _) -> - ParseState tail (tokpos head) true - pure head - --- | Create a parser which matches any token satisfying the predicate. -when :: forall m a. Monad m => (a -> Position) -> (a -> Boolean) -> ParserT (List a) m a -when tokpos f = try $ do - a <- token tokpos - guard $ f a - pure a - --- | Match the specified token at the head of the stream. -match :: forall a m. Monad m => Eq a => (a -> Position) -> a -> ParserT (List a) m a -match tokpos tok = when tokpos (_ == tok) - type LanguageDef = GenLanguageDef String Identity -- | The `GenLanguageDef` type is a record that contains all parameterizable diff --git a/test/Main.purs b/test/Main.purs index 08b8bcd..8dcd6f1 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -16,8 +16,8 @@ import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (eof, string, char, satisfy, anyChar) -import Text.Parsing.Parser.Token (TokenParser, match, when, token, makeTokenParser) +import Text.Parsing.Parser.String (eof, string, char, satisfy, anyChar, class HasUpdatePosition) +import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) parens :: forall m a. Monad m => ParserT String m a -> ParserT String m a @@ -83,6 +83,9 @@ instance testTokensEq :: Eq TestToken where eq B B = true eq _ _ = false +instance stringHasUpdatePosition :: HasUpdatePosition TestToken where + updatePos (Position { column, line }) tok = Position { column: column + 1, line} + isA :: TestToken -> Boolean isA A = true isA _ = false @@ -438,15 +441,14 @@ main = do parseTest "1*2+3/4-5" (-3) exprTest parseTest "ab?" "ab" manySatisfyTest - let tokpos = const initialPos - parseTest (fromFoldable [A, B]) A (token tokpos) - parseTest (fromFoldable [B, A]) B (token tokpos) + parseTest (fromFoldable [A, B]) A (anyChar) + parseTest (fromFoldable [B, A]) B (anyChar) - parseTest (fromFoldable [A, B]) A (when tokpos isA) + parseTest (fromFoldable [A, B]) A (satisfy isA) - parseTest (fromFoldable [A]) A (match tokpos A) - parseTest (fromFoldable [B]) B (match tokpos B) - parseTest (fromFoldable [A, B]) A (match tokpos A) + parseTest (fromFoldable [A]) A (char A) + parseTest (fromFoldable [B]) B (char B) + parseTest (fromFoldable [A, B]) A (char A) parseErrorTestPosition (string "abc") "bcd" (Position { column: 1, line: 1 }) parseErrorTestPosition (string "abc" *> eof) "abcdefg" (Position { column: 4, line: 1 }) From b89442b039104bc913ab7291c1df056b265e89a2 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 11 Jun 2017 11:25:33 +0400 Subject: [PATCH 08/23] add 'drop (Prefix a) a >>= uncons = Nothing' law --- src/Text/Parsing/Parser/String.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 204d0db..e728ad7 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -40,7 +40,7 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | operations which this modules needs. -- | -- | Instances must satisfy the following laws: --- | +-- | - `drop (Prefix a) a >>= uncons = Nothing` 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 } From 67926bea035d394d35a336c7292c81ba2bdb094c Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 18 Jun 2017 15:45:46 +0400 Subject: [PATCH 09/23] remove String.whitespace --- src/Text/Parsing/Parser/String.purs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index e728ad7..f2ee4de 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -98,17 +98,14 @@ satisfy f = try do 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 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) -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' +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' -- | Skip whitespace characters. skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit -skipSpaces = void whiteSpace' +skipSpaces = void whiteSpace -- | 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 From 453d6a14750d6c9042593e103ed519489b863353 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 18 Jun 2017 15:53:12 +0400 Subject: [PATCH 10/23] rename `String.char` to `String.match` --- src/Text/Parsing/Parser/Combinators.purs | 2 +- src/Text/Parsing/Parser/Language.purs | 6 ++-- src/Text/Parsing/Parser/String.purs | 4 +-- src/Text/Parsing/Parser/Token.purs | 40 ++++++++++++------------ test/Main.purs | 16 +++++----- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Text/Parsing/Parser/Combinators.purs b/src/Text/Parsing/Parser/Combinators.purs index 115be15..7c0b344 100644 --- a/src/Text/Parsing/Parser/Combinators.purs +++ b/src/Text/Parsing/Parser/Combinators.purs @@ -15,7 +15,7 @@ -- | be used in conjunction with `Data.String.fromCharArray` to achieve "Parsec-like" results. -- | -- | ```purescript --- | Text.Parsec.many (char 'x') <=> fromCharArray <$> Data.Array.many (char 'x') +-- | Text.Parsec.many (match 'x') <=> fromCharArray <$> Data.Array.many (match 'x') -- | ``` module Text.Parsing.Parser.Combinators where diff --git a/src/Text/Parsing/Parser/Language.purs b/src/Text/Parsing/Parser/Language.purs index c1f5e25..a7b58ec 100644 --- a/src/Text/Parsing/Parser/Language.purs +++ b/src/Text/Parsing/Parser/Language.purs @@ -12,7 +12,7 @@ import Prelude import Control.Alt ((<|>)) import Text.Parsing.Parser (ParserT) -import Text.Parsing.Parser.String (char, oneOf) +import Text.Parsing.Parser.String (match, oneOf) import Text.Parsing.Parser.Token (LanguageDef, TokenParser, GenLanguageDef(..), unGenLanguageDef, makeTokenParser, alphaNum, letter) ----------------------------------------------------------- @@ -70,7 +70,7 @@ emptyDef = LanguageDef , commentEnd: "" , commentLine: "" , nestedComments: true - , identStart: letter <|> char '_' + , identStart: letter <|> match '_' , identLetter: alphaNum <|> oneOf ['_', '\''] , opStart: op' , opLetter: op' @@ -95,7 +95,7 @@ haskellDef :: LanguageDef haskellDef = case haskell98Def of (LanguageDef def) -> LanguageDef def - { identLetter = def.identLetter <|> char '#' + { identLetter = def.identLetter <|> match '#' , reservedNames = def.reservedNames <> ["foreign","import","export","primitive" ,"_ccall_","_casm_" diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index f2ee4de..554b9bc 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -95,8 +95,8 @@ satisfy f = try do else fail $ "Character " <> show c <> " did not satisfy predicate" -- | 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 :: 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 a whitespace characters but returns them using Array. diff --git a/src/Text/Parsing/Parser/Token.purs b/src/Text/Parsing/Parser/Token.purs index 47f4abd..6799b5b 100644 --- a/src/Text/Parsing/Parser/Token.purs +++ b/src/Text/Parsing/Parser/Token.purs @@ -38,7 +38,7 @@ import Math (pow) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (skipMany1, try, skipMany, notFollowedBy, option, choice, between, sepBy1, sepBy, (), ()) import Text.Parsing.Parser.Pos (Position) -import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, string, char) +import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, string, match) import Prelude hiding (when,between) type LanguageDef = GenLanguageDef String Identity @@ -60,10 +60,10 @@ newtype GenLanguageDef s m -- | Set to `true` if the language supports nested block comments. nestedComments :: Boolean, -- | This parser should accept any start characters of identifiers. For - -- | example `letter <|> char '_'`. + -- | example `letter <|> match '_'`. identStart :: ParserT s m Char, -- | This parser should accept any legal tail characters of identifiers. - -- | For example `alphaNum <|> char '_'`. + -- | For example `alphaNum <|> match '_'`. identLetter :: ParserT s m Char, -- | This parser should accept any start characters of operators. For -- | example `oneOf [':', '+', '=']`. @@ -355,13 +355,13 @@ makeTokenParser (LanguageDef languageDef) charLiteral = lexeme go "character" where go :: ParserT String m Char - go = between (char '\'') (char '\'' "end of character") characterChar + go = between (match '\'') (match '\'' "end of character") characterChar characterChar :: ParserT String m Char characterChar = charLetter <|> charEscape "literal character" charEscape :: ParserT String m Char - charEscape = char '\\' *> escapeCode + charEscape = match '\\' *> escapeCode charLetter :: ParserT String m Char charLetter = satisfy \c -> (c /= '\'') && (c /= '\\') && (c > '\026') @@ -371,7 +371,7 @@ makeTokenParser (LanguageDef languageDef) where go :: ParserT String m String go = do - maybeChars <- between (char '"') (char '"' "end of string") (List.many stringChar) + maybeChars <- between (match '"') (match '"' "end of string") (List.many stringChar) pure $ fromCharArray $ List.toUnfoldable $ foldr folder Nil maybeChars folder :: Maybe Char -> List Char -> List Char @@ -389,14 +389,14 @@ makeTokenParser (LanguageDef languageDef) stringEscape :: ParserT String m (Maybe Char) stringEscape = do - _ <- char '\\' + _ <- match '\\' (escapeGap $> Nothing) <|> (escapeEmpty $> Nothing) <|> (Just <$> escapeCode) escapeEmpty :: ParserT String m Char - escapeEmpty = char '&' + escapeEmpty = match '&' escapeGap :: ParserT String m Char - escapeGap = Array.some space *> char '\\' "end of string gap" + escapeGap = Array.some space *> match '\\' "end of string gap" -- -- escape codes escapeCode :: ParserT String m Char @@ -405,15 +405,15 @@ makeTokenParser (LanguageDef languageDef) charControl :: ParserT String m Char charControl = do - _ <- char '^' + _ <- match '^' code <- upper pure <<< fromCharCode $ toCharCode code - toCharCode 'A' + 1 charNum :: ParserT String m Char charNum = do code <- decimal - <|> ( char 'o' *> number 8 octDigit ) - <|> ( char 'x' *> number 16 hexDigit ) + <|> ( match 'o' *> number 8 octDigit ) + <|> ( match 'x' *> number 16 hexDigit ) if code > 0x10FFFF then fail "invalid escape sequence" else pure $ fromCharCode code @@ -422,7 +422,7 @@ makeTokenParser (LanguageDef languageDef) charEsc = choice (map parseEsc escMap) where parseEsc :: Tuple Char Char -> ParserT String m Char - parseEsc (Tuple c code) = char c $> code + parseEsc (Tuple c code) = match c $> code charAscii :: ParserT String m Char charAscii = choice (map parseAscii asciiMap) @@ -479,7 +479,7 @@ makeTokenParser (LanguageDef languageDef) floating = decimal >>= fractExponent natFloat :: ParserT String m (Either Int Number) - natFloat = char '0' *> zeroNumFloat + natFloat = match '0' *> zeroNumFloat <|> decimalFloat zeroNumFloat :: ParserT String m (Either Int Number) @@ -512,7 +512,7 @@ makeTokenParser (LanguageDef languageDef) fraction :: ParserT String m Number fraction = "fraction" do - _ <- char '.' + _ <- match '.' digits <- Array.some digit "fraction" maybe (fail "not digit") pure $ foldr op (Just 0.0) digits where @@ -541,15 +541,15 @@ makeTokenParser (LanguageDef languageDef) pure $ f n sign :: forall a . (Ring a) => ParserT String m (a -> a) - sign = (char '-' $> negate) - <|> (char '+' $> id) + sign = (match '-' $> negate) + <|> (match '+' $> id) <|> pure id nat :: ParserT String m Int nat = zeroNumber <|> decimal zeroNumber :: ParserT String m Int - zeroNumber = char '0' *> + zeroNumber = match '0' *> ( hexadecimal <|> octal <|> decimal <|> pure 0 ) "" decimal :: ParserT String m Int @@ -625,8 +625,8 @@ makeTokenParser (LanguageDef languageDef) Just { head: c, tail: cs } -> (caseChar c msg) *> walk cs caseChar :: Char -> ParserT String m Char - caseChar c | isAlpha c = char (Unicode.toLower c) <|> char (Unicode.toUpper c) - | otherwise = char c + caseChar c | isAlpha c = match (Unicode.toLower c) <|> match (Unicode.toUpper c) + | otherwise = match c msg :: String msg = show name diff --git a/test/Main.purs b/test/Main.purs index 8dcd6f1..3a98c59 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -16,7 +16,7 @@ import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (eof, string, char, satisfy, anyChar, class HasUpdatePosition) +import Text.Parsing.Parser.String (eof, string, match, satisfy, anyChar, class HasUpdatePosition) import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) @@ -44,7 +44,7 @@ parseErrorTestPosition p input expected = case runParser input p of logShow expected opTest :: Parser String String -opTest = chainl (singleton <$> anyChar) (char '+' $> append) "" +opTest = chainl (singleton <$> anyChar) (match '+' $> append) "" digit :: Parser String Int digit = (string "0" >>= \_ -> pure 0) @@ -69,7 +69,7 @@ exprTest = buildExprParser [ [ Infix (string "/" >>= \_ -> pure (/)) AssocRight manySatisfyTest :: Parser String String manySatisfyTest = do r <- some $ satisfy (\s -> s /= '?') - _ <- char '?' + _ <- match '?' pure (fromCharArray r) data TestToken = A | B @@ -418,14 +418,14 @@ main :: forall eff . Eff (console :: CONSOLE, assert :: ASSERT |eff) Unit main = do parseErrorTestPosition - (many $ char 'f' *> char '?') + (many $ match 'f' *> match '?') "foo" (Position { column: 2, line: 1 }) parseTest "foo" Nil - (many $ try $ char 'f' *> char '?') + (many $ try $ match 'f' *> match '?') parseTest "(((a)))" 3 nested parseTest "aaa" (Cons "a" (Cons "a" (Cons "a" Nil))) $ many (string "a") @@ -446,9 +446,9 @@ main = do parseTest (fromFoldable [A, B]) A (satisfy isA) - parseTest (fromFoldable [A]) A (char A) - parseTest (fromFoldable [B]) B (char B) - parseTest (fromFoldable [A, B]) A (char A) + parseTest (fromFoldable [A]) A (match A) + parseTest (fromFoldable [B]) B (match B) + parseTest (fromFoldable [A, B]) A (match A) parseErrorTestPosition (string "abc") "bcd" (Position { column: 1, line: 1 }) parseErrorTestPosition (string "abc" *> eof) "abcdefg" (Position { column: 4, line: 1 }) From 96dc7da55341a7f678c196db6762fe4cafd0dbb5 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 18 Jun 2017 15:53:49 +0400 Subject: [PATCH 11/23] rename `String.anyChar` to `String.token` --- src/Text/Parsing/Parser/String.purs | 6 +++--- test/Main.purs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 554b9bc..a35f58a 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -77,8 +77,8 @@ string str = do _ -> fail ("Expected " <> show str) -- | Match any token. -anyChar :: forall f c m. StreamLike f c => Monad m => ParserT f m c -anyChar = do +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" @@ -90,7 +90,7 @@ anyChar = do -- | 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 " <> show c <> " did not satisfy predicate" diff --git a/test/Main.purs b/test/Main.purs index 3a98c59..07ac463 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -16,7 +16,7 @@ import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (eof, string, match, satisfy, anyChar, class HasUpdatePosition) +import Text.Parsing.Parser.String (eof, string, match, satisfy, token, class HasUpdatePosition) import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) @@ -44,7 +44,7 @@ parseErrorTestPosition p input expected = case runParser input p of logShow expected opTest :: Parser String String -opTest = chainl (singleton <$> anyChar) (match '+' $> append) "" +opTest = chainl (singleton <$> token) (match '+' $> append) "" digit :: Parser String Int digit = (string "0" >>= \_ -> pure 0) @@ -441,8 +441,8 @@ main = do parseTest "1*2+3/4-5" (-3) exprTest parseTest "ab?" "ab" manySatisfyTest - parseTest (fromFoldable [A, B]) A (anyChar) - parseTest (fromFoldable [B, A]) B (anyChar) + parseTest (fromFoldable [A, B]) A (token) + parseTest (fromFoldable [B, A]) B (token) parseTest (fromFoldable [A, B]) A (satisfy isA) From 95eee9b6215797dc895d67328571f95a08e0e9bd Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 18 Jun 2017 16:00:44 +0400 Subject: [PATCH 12/23] rename `String.string` to `String.prefix` --- src/Text/Parsing/Parser/Combinators.purs | 6 +- src/Text/Parsing/Parser/Expr.purs | 8 +-- src/Text/Parsing/Parser/Indent.purs | 4 +- src/Text/Parsing/Parser/String.purs | 18 ++--- src/Text/Parsing/Parser/Token.purs | 18 ++--- test/Main.purs | 92 ++++++++++++------------ 6 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/Text/Parsing/Parser/Combinators.purs b/src/Text/Parsing/Parser/Combinators.purs index 7c0b344..c327c69 100644 --- a/src/Text/Parsing/Parser/Combinators.purs +++ b/src/Text/Parsing/Parser/Combinators.purs @@ -49,7 +49,7 @@ infix 3 asErrorMessage as -- | For example: -- | -- | ```purescript --- | parens = between (string "(") (string ")") +-- | parens = between (prefix "(") (prefix ")") -- | ``` between :: forall m s a open close. Monad m => ParserT s m open -> ParserT s m close -> ParserT s m a -> ParserT s m a between open close p = open *> p <* close @@ -85,7 +85,7 @@ lookAhead p = (ParserT <<< ExceptT <<< StateT) \s -> do -- | For example: -- | -- | ```purescript --- | digit `sepBy` string "," +-- | digit `sepBy` prefix "," -- | ``` sepBy :: forall m s a sep. Monad m => ParserT s m a -> ParserT s m sep -> ParserT s m (List a) sepBy p sep = sepBy1 p sep <|> pure Nil @@ -122,7 +122,7 @@ endBy p sep = many $ p <* sep -- | For example: -- | -- | ```purescript --- | chainr digit (string "+" *> add) 0 +-- | chainr digit (prefix "+" *> add) 0 -- | ``` chainr :: forall m s a. Monad m => ParserT s m a -> ParserT s m (a -> a -> a) -> a -> ParserT s m a chainr p f a = chainr1 p f <|> pure a diff --git a/src/Text/Parsing/Parser/Expr.purs b/src/Text/Parsing/Parser/Expr.purs index adfa62e..7e9286e 100644 --- a/src/Text/Parsing/Parser/Expr.purs +++ b/src/Text/Parsing/Parser/Expr.purs @@ -32,10 +32,10 @@ type SplitAccum m s a = { rassoc :: List (ParserT s m (a -> a -> a)) -- | For example: -- | -- | ```purescript --- | buildExprParser [ [ Infix (string "/" $> div) AssocRight ] --- | , [ Infix (string "*" $> mul) AssocRight ] --- | , [ Infix (string "-" $> sub) AssocRight ] --- | , [ Infix (string "+" $> add) AssocRight ] +-- | buildExprParser [ [ Infix (prefix "/" $> div) AssocRight ] +-- | , [ Infix (prefix "*" $> mul) AssocRight ] +-- | , [ Infix (prefix "-" $> sub) AssocRight ] +-- | , [ Infix (prefix "+" $> add) AssocRight ] -- | ] digit -- | ``` buildExprParser :: forall m s a. Monad m => OperatorTable m s a -> ParserT s m a -> ParserT s m a diff --git a/src/Text/Parsing/Parser/Indent.purs b/src/Text/Parsing/Parser/Indent.purs index e0b9619..3ed6a01 100644 --- a/src/Text/Parsing/Parser/Indent.purs +++ b/src/Text/Parsing/Parser/Indent.purs @@ -62,7 +62,7 @@ import Data.Maybe (Maybe(..)) import Text.Parsing.Parser (ParserT, ParseState(ParseState), fail) import Text.Parsing.Parser.Combinators (option, optionMaybe) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (string, oneOf) +import Text.Parsing.Parser.String (prefix, oneOf) -- | Indentation sensitive parser type. Usually @ m @ will -- | be @ Identity @ as with any @ ParserT @ @@ -100,7 +100,7 @@ many1 :: forall s m a. (Monad m) => ParserT s m a -> ParserT s m (List a) many1 p = lift2 Cons p (many p) symbol :: forall m. (Monad m) => String -> ParserT String m String -symbol name = (many $ oneOf [' ','\t']) *> (string name) +symbol name = (many $ oneOf [' ','\t']) *> (prefix name) -- | `withBlock f a p` parses `a` -- | followed by an indented block of `p` diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index a35f58a..01ea3ee 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -17,7 +17,7 @@ import Text.Parsing.Parser.Combinators (try, ()) 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. +-- | A newtype used in cases where there is a prefix to be stipPrefixed. newtype Prefix a = Prefix a derive instance eqPrefix :: Eq a => Eq (Prefix a) @@ -40,21 +40,21 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | operations which this modules needs. -- | -- | Instances must satisfy the following laws: --- | - `drop (Prefix a) a >>= uncons = Nothing` +-- | - `stipPrefix (Prefix a) a >>= uncons = Nothing` 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 } + stipPrefix :: 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)} - drop (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> + stipPrefix (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 uncons f = L.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} - drop (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> + stipPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} -- | Match end of stream. @@ -65,11 +65,11 @@ eof = do Nothing -> pure unit _ -> fail "Expected EOF" --- | Match the specified stream. -string :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f -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 input <- gets \(ParseState input _ _) -> input - case drop (Prefix str) input of + case stipPrefix (Prefix str) input of Just {rest, updatePos} -> do modify \(ParseState _ position _) -> ParseState rest (updatePos position) true diff --git a/src/Text/Parsing/Parser/Token.purs b/src/Text/Parsing/Parser/Token.purs index 6799b5b..54807f2 100644 --- a/src/Text/Parsing/Parser/Token.purs +++ b/src/Text/Parsing/Parser/Token.purs @@ -38,7 +38,7 @@ import Math (pow) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (skipMany1, try, skipMany, notFollowedBy, option, choice, between, sepBy1, sepBy, (), ()) import Text.Parsing.Parser.Pos (Position) -import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, string, match) +import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, prefix, match) import Prelude hiding (when,between) type LanguageDef = GenLanguageDef String Identity @@ -428,7 +428,7 @@ makeTokenParser (LanguageDef languageDef) charAscii = choice (map parseAscii asciiMap) where parseAscii :: Tuple String Char -> ParserT String m Char - parseAscii (Tuple asc code) = try $ string asc $> code + parseAscii (Tuple asc code) = try $ prefix asc $> code -- escape code tables escMap :: Array (Tuple Char Char) @@ -579,7 +579,7 @@ makeTokenParser (LanguageDef languageDef) where go :: ParserT String m Unit go = do - _ <- string name + _ <- prefix name notFollowedBy languageDef.opLetter "end of " <> name operator :: ParserT String m String @@ -616,7 +616,7 @@ makeTokenParser (LanguageDef languageDef) go = caseString name *> (notFollowedBy languageDef.identLetter "end of " <> name) caseString :: String -> ParserT String m String - caseString name | languageDef.caseSensitive = string name $> name + caseString name | languageDef.caseSensitive = prefix name $> name | otherwise = walk name $> name where walk :: String -> ParserT String m Unit @@ -657,7 +657,7 @@ makeTokenParser (LanguageDef languageDef) -- White space & symbols ----------------------------------------------------------- symbol :: String -> ParserT String m String - symbol name = lexeme (string name) $> name + symbol name = lexeme (prefix name) $> name lexeme :: forall a . ParserT String m a -> ParserT String m a lexeme p = p <* whiteSpace' (LanguageDef languageDef) @@ -713,11 +713,11 @@ simpleSpace = skipMany1 (satisfy isSpace) oneLineComment :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit oneLineComment (LanguageDef languageDef) = - try (string languageDef.commentLine) *> skipMany (satisfy (_ /= '\n')) + try (prefix languageDef.commentLine) *> skipMany (satisfy (_ /= '\n')) multiLineComment :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit multiLineComment langDef@(LanguageDef languageDef) = - try (string languageDef.commentStart) *> inComment langDef + try (prefix languageDef.commentStart) *> inComment langDef inComment :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit inComment langDef@(LanguageDef languageDef) = @@ -725,7 +725,7 @@ inComment langDef@(LanguageDef languageDef) = inCommentMulti :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit inCommentMulti langDef@(LanguageDef languageDef) = - fix \p -> ( void $ try (string languageDef.commentEnd) ) + fix \p -> ( void $ try (prefix languageDef.commentEnd) ) <|> ( multiLineComment langDef *> p ) <|> ( skipMany1 (noneOf startEnd) *> p ) <|> ( oneOf startEnd *> p ) @@ -736,7 +736,7 @@ inCommentMulti langDef@(LanguageDef languageDef) = inCommentSingle :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit inCommentSingle (LanguageDef languageDef) = - fix \p -> ( void $ try (string languageDef.commentEnd) ) + fix \p -> ( void $ try (prefix languageDef.commentEnd) ) <|> ( skipMany1 (noneOf startEnd) *> p ) <|> ( oneOf startEnd *> p ) "end of comment" diff --git a/test/Main.purs b/test/Main.purs index 07ac463..427cbe4 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -16,16 +16,16 @@ import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (eof, string, match, satisfy, token, class HasUpdatePosition) +import Text.Parsing.Parser.String (eof, prefix, match, satisfy, token, class HasUpdatePosition) import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) parens :: forall m a. Monad m => ParserT String m a -> ParserT String m a -parens = between (string "(") (string ")") +parens = between (prefix "(") (prefix ")") nested :: forall m. Monad m => ParserT String m Int nested = fix \p -> (do - _ <- string "a" + _ <- prefix "a" pure 0) <|> ((+) 1) <$> parens p parseTest :: forall s a eff. Show a => Eq a => s -> a -> Parser s a -> Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit @@ -47,22 +47,22 @@ opTest :: Parser String String opTest = chainl (singleton <$> token) (match '+' $> append) "" digit :: Parser String Int -digit = (string "0" >>= \_ -> pure 0) - <|> (string "1" >>= \_ -> pure 1) - <|> (string "2" >>= \_ -> pure 2) - <|> (string "3" >>= \_ -> pure 3) - <|> (string "4" >>= \_ -> pure 4) - <|> (string "5" >>= \_ -> pure 5) - <|> (string "6" >>= \_ -> pure 6) - <|> (string "7" >>= \_ -> pure 7) - <|> (string "8" >>= \_ -> pure 8) - <|> (string "9" >>= \_ -> pure 9) +digit = (prefix "0" >>= \_ -> pure 0) + <|> (prefix "1" >>= \_ -> pure 1) + <|> (prefix "2" >>= \_ -> pure 2) + <|> (prefix "3" >>= \_ -> pure 3) + <|> (prefix "4" >>= \_ -> pure 4) + <|> (prefix "5" >>= \_ -> pure 5) + <|> (prefix "6" >>= \_ -> pure 6) + <|> (prefix "7" >>= \_ -> pure 7) + <|> (prefix "8" >>= \_ -> pure 8) + <|> (prefix "9" >>= \_ -> pure 9) exprTest :: Parser String Int -exprTest = buildExprParser [ [ Infix (string "/" >>= \_ -> pure (/)) AssocRight ] - , [ Infix (string "*" >>= \_ -> pure (*)) AssocRight ] - , [ Infix (string "-" >>= \_ -> pure (-)) AssocRight ] - , [ Infix (string "+" >>= \_ -> pure (+)) AssocRight ] +exprTest = buildExprParser [ [ Infix (prefix "/" >>= \_ -> pure (/)) AssocRight ] + , [ Infix (prefix "*" >>= \_ -> pure (*)) AssocRight ] + , [ Infix (prefix "-" >>= \_ -> pure (-)) AssocRight ] + , [ Infix (prefix "+" >>= \_ -> pure (+)) AssocRight ] ] digit @@ -283,34 +283,34 @@ tokenParserWhiteSpaceTest = do tokenParserParensTest :: TestM tokenParserParensTest = do -- parse parens - parseTest "(hello)" "hello" $ testTokenParser.parens $ string "hello" + parseTest "(hello)" "hello" $ testTokenParser.parens $ prefix "hello" -- fail on non-closed parens - parseErrorTestPosition (testTokenParser.parens $ string "hello") "(hello" $ mkPos 7 + parseErrorTestPosition (testTokenParser.parens $ prefix "hello") "(hello" $ mkPos 7 tokenParserBracesTest :: TestM tokenParserBracesTest = do -- parse braces - parseTest "{hello}" "hello" $ testTokenParser.braces $ string "hello" + parseTest "{hello}" "hello" $ testTokenParser.braces $ prefix "hello" -- fail on non-closed braces - parseErrorTestPosition (testTokenParser.braces $ string "hello") "{hello" $ mkPos 7 + parseErrorTestPosition (testTokenParser.braces $ prefix "hello") "{hello" $ mkPos 7 tokenParserAnglesTest :: TestM tokenParserAnglesTest = do -- parse angles - parseTest "" "hello" $ testTokenParser.angles $ string "hello" + parseTest "" "hello" $ testTokenParser.angles $ prefix "hello" -- fail on non-closed angles - parseErrorTestPosition (testTokenParser.angles $ string "hello") " match '?') parseTest "(((a)))" 3 nested - parseTest "aaa" (Cons "a" (Cons "a" (Cons "a" Nil))) $ many (string "a") + parseTest "aaa" (Cons "a" (Cons "a" (Cons "a" Nil))) $ many (prefix "a") parseTest "(ab)" (Just "b") $ parens do - _ <- string "a" - optionMaybe $ string "b" - parseTest "a,a,a" (Cons "a" (Cons "a" (Cons "a" Nil))) $ string "a" `sepBy1` string "," + _ <- prefix "a" + optionMaybe $ prefix "b" + parseTest "a,a,a" (Cons "a" (Cons "a" (Cons "a" Nil))) $ prefix "a" `sepBy1` prefix "," parseTest "a,a,a," (Cons "a" (Cons "a" (Cons "a" Nil))) $ do - as <- string "a" `endBy1` string "," + as <- prefix "a" `endBy1` prefix "," eof pure as parseTest "a+b+c" "abc" opTest @@ -450,10 +450,10 @@ main = do parseTest (fromFoldable [B]) B (match B) parseTest (fromFoldable [A, B]) A (match A) - parseErrorTestPosition (string "abc") "bcd" (Position { column: 1, line: 1 }) - parseErrorTestPosition (string "abc" *> eof) "abcdefg" (Position { column: 4, line: 1 }) - parseErrorTestPosition (string "a\nb\nc\n" *> eof) "a\nb\nc\nd\n" (Position { column: 1, line: 4 }) - parseErrorTestPosition (string "\ta" *> eof) "\tab" (Position { column: 10, line: 1 }) + parseErrorTestPosition (prefix "abc") "bcd" (Position { column: 1, line: 1 }) + parseErrorTestPosition (prefix "abc" *> eof) "abcdefg" (Position { column: 4, line: 1 }) + parseErrorTestPosition (prefix "a\nb\nc\n" *> eof) "a\nb\nc\nd\n" (Position { column: 1, line: 4 }) + parseErrorTestPosition (prefix "\ta" *> eof) "\tab" (Position { column: 10, line: 1 }) tokenParserIdentifierTest tokenParserReservedTest From 858fda93c6339ef49f1c3c9e3a37bb32b0fc3444 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 18 Jun 2017 16:03:37 +0400 Subject: [PATCH 13/23] fix compiler warnings --- src/Text/Parsing/Parser/Pos.purs | 1 - src/Text/Parsing/Parser/String.purs | 4 +--- src/Text/Parsing/Parser/Token.purs | 6 ++---- test/Main.purs | 2 +- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Text/Parsing/Parser/Pos.purs b/src/Text/Parsing/Parser/Pos.purs index ae8ee6b..0640d57 100644 --- a/src/Text/Parsing/Parser/Pos.purs +++ b/src/Text/Parsing/Parser/Pos.purs @@ -2,7 +2,6 @@ module Text.Parsing.Parser.Pos where import Prelude import Data.Foldable (foldl) -import Data.Newtype (wrap) import Data.String (toCharArray) -- | `Position` represents the position of the parser in the input. diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 01ea3ee..3ae1274 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -2,15 +2,13 @@ module Text.Parsing.Parser.String where -import Data.Array (many, toUnfoldable) +import Data.Array (many) 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 Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (try, ()) diff --git a/src/Text/Parsing/Parser/Token.purs b/src/Text/Parsing/Parser/Token.purs index 54807f2..3b4774f 100644 --- a/src/Text/Parsing/Parser/Token.purs +++ b/src/Text/Parsing/Parser/Token.purs @@ -22,8 +22,7 @@ import Data.Array as Array import Data.Char.Unicode as Unicode import Data.List as List import Control.Lazy (fix) -import Control.Monad.State (modify, gets) -import Control.MonadPlus (guard, (<|>)) +import Control.MonadPlus ((<|>)) import Data.Char (fromCharCode, toCharCode) import Data.Char.Unicode (digitToInt, isAlpha, isAlphaNum, isDigit, isHexDigit, isOctDigit, isSpace, isUpper) import Data.Either (Either(..)) @@ -35,9 +34,8 @@ import Data.Maybe (Maybe(..), maybe) import Data.String (toCharArray, null, toLower, fromCharArray, singleton, uncons) import Data.Tuple (Tuple(..)) import Math (pow) -import Text.Parsing.Parser (ParseState(..), ParserT, fail) +import Text.Parsing.Parser (ParserT, fail) import Text.Parsing.Parser.Combinators (skipMany1, try, skipMany, notFollowedBy, option, choice, between, sepBy1, sepBy, (), ()) -import Text.Parsing.Parser.Pos (Position) import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, prefix, match) import Prelude hiding (when,between) diff --git a/test/Main.purs b/test/Main.purs index 427cbe4..f9e912d 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -15,7 +15,7 @@ import Text.Parsing.Parser (Parser, ParserT, runParser, parseErrorPosition) import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl, between) import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) -import Text.Parsing.Parser.Pos (Position(..), initialPos) +import Text.Parsing.Parser.Pos (Position(..)) import Text.Parsing.Parser.String (eof, prefix, match, satisfy, token, class HasUpdatePosition) import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) From 478be1e03472fed00ad713820694d857b6715d43 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Tue, 27 Jun 2017 21:16:18 +0400 Subject: [PATCH 14/23] fix typo and whitespace char order --- src/Text/Parsing/Parser/String.purs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 3ae1274..a09a7ca 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -15,7 +15,7 @@ import Text.Parsing.Parser.Combinators (try, ()) import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) import Prelude hiding (between) --- | A newtype used in cases where there is a prefix to be stipPrefixed. +-- | 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) @@ -38,21 +38,21 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | operations which this modules needs. -- | -- | Instances must satisfy the following laws: --- | - `stipPrefix (Prefix a) a >>= uncons = Nothing` +-- | - `stripPrefix (Prefix a) a >>= uncons = Nothing` class StreamLike f c | f -> c where uncons :: f -> Maybe { head :: c, tail :: f, updatePos :: Position -> Position } - stipPrefix :: Prefix f -> f -> Maybe { rest :: 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)} - stipPrefix (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> + stripPrefix (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 +instance listStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) a where uncons f = L.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} - stipPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> + stripPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} -- | Match end of stream. @@ -67,7 +67,7 @@ eof = do prefix :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f prefix str = do input <- gets \(ParseState input _ _) -> input - case stipPrefix (Prefix str) input of + case stripPrefix (Prefix str) input of Just {rest, updatePos} -> do modify \(ParseState _ position _) -> ParseState rest (updatePos position) true @@ -99,7 +99,7 @@ match c = satisfy (_ == c) show c -- | 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' +whiteSpace = many $ satisfy \c -> c == ' ' || c == '\n' || c == '\t' || c == '\r' -- | Skip whitespace characters. skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit From b4dc8ce205c4c5c5dcdbc1a6e4c27affd1732f4e Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Wed, 12 Jul 2017 13:15:40 +0400 Subject: [PATCH 15/23] update Prefix comment --- src/Text/Parsing/Parser/String.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index a09a7ca..b1398be 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -15,7 +15,7 @@ import Text.Parsing.Parser.Combinators (try, ()) 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. +-- | A newtype used to identify a prefix of a string newtype Prefix a = Prefix a derive instance eqPrefix :: Eq a => Eq (Prefix a) From 902e4db59fd791e3ffc511769bbc36f5a48924ba Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Wed, 12 Jul 2017 13:15:50 +0400 Subject: [PATCH 16/23] update prefix variable name --- src/Text/Parsing/Parser/String.purs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index b1398be..e2d9b87 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -65,14 +65,14 @@ eof = 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 +prefix p = do input <- gets \(ParseState input _ _) -> input - case stripPrefix (Prefix str) input of + case stripPrefix (Prefix p) input of Just {rest, updatePos} -> do modify \(ParseState _ position _) -> ParseState rest (updatePos position) true - pure str - _ -> fail ("Expected " <> show str) + pure p + _ -> fail ("Expected " <> show p) -- | Match any token. token :: forall f c m. StreamLike f c => Monad m => ParserT f m c From e8c9bdb16bcfd8b254233eb12ae6b28f5228ccdf Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Wed, 12 Jul 2017 13:20:18 +0400 Subject: [PATCH 17/23] add Lazy List instance for StreamLike --- src/Text/Parsing/Parser/String.purs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index e2d9b87..3740c7f 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -5,6 +5,7 @@ module Text.Parsing.Parser.String where import Data.Array (many) import Data.Foldable (fold, elem, notElem) import Data.List as L +import Data.List.Lazy as LazyL import Data.Monoid.Endo (Endo(..)) import Data.Maybe (Maybe(..)) import Data.Newtype (class Newtype, unwrap) @@ -55,6 +56,12 @@ instance listStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (L.List a) stripPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} +instance lazyListStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) a where + uncons f = LazyL.uncons f <#> \({ head, tail}) -> + { head, tail, updatePos: (_ `updatePos` head)} + stripPrefix (Prefix p) s = LazyL.stripPrefix (LazyL.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 From 19e1ed4a701ee97244504ffb7c259e603cb71eaa Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Wed, 12 Jul 2017 13:30:47 +0400 Subject: [PATCH 18/23] move some parsers to String module; take out Stream module --- src/Text/Parsing/Parser/Indent.purs | 2 +- src/Text/Parsing/Parser/Language.purs | 5 +- src/Text/Parsing/Parser/Stream.purs | 112 +++++++++++++++++++++ src/Text/Parsing/Parser/String.purs | 138 ++++++-------------------- src/Text/Parsing/Parser/Token.purs | 44 +------- test/Main.purs | 2 +- 6 files changed, 153 insertions(+), 150 deletions(-) create mode 100644 src/Text/Parsing/Parser/Stream.purs diff --git a/src/Text/Parsing/Parser/Indent.purs b/src/Text/Parsing/Parser/Indent.purs index 3ed6a01..c7f8e14 100644 --- a/src/Text/Parsing/Parser/Indent.purs +++ b/src/Text/Parsing/Parser/Indent.purs @@ -62,7 +62,7 @@ import Data.Maybe (Maybe(..)) import Text.Parsing.Parser (ParserT, ParseState(ParseState), fail) import Text.Parsing.Parser.Combinators (option, optionMaybe) import Text.Parsing.Parser.Pos (Position(..), initialPos) -import Text.Parsing.Parser.String (prefix, oneOf) +import Text.Parsing.Parser.Stream (prefix, oneOf) -- | Indentation sensitive parser type. Usually @ m @ will -- | be @ Identity @ as with any @ ParserT @ diff --git a/src/Text/Parsing/Parser/Language.purs b/src/Text/Parsing/Parser/Language.purs index a7b58ec..837010a 100644 --- a/src/Text/Parsing/Parser/Language.purs +++ b/src/Text/Parsing/Parser/Language.purs @@ -12,8 +12,9 @@ import Prelude import Control.Alt ((<|>)) import Text.Parsing.Parser (ParserT) -import Text.Parsing.Parser.String (match, oneOf) -import Text.Parsing.Parser.Token (LanguageDef, TokenParser, GenLanguageDef(..), unGenLanguageDef, makeTokenParser, alphaNum, letter) +import Text.Parsing.Parser.Stream (match, oneOf) +import Text.Parsing.Parser.String (alphaNum, letter) +import Text.Parsing.Parser.Token (LanguageDef, TokenParser, GenLanguageDef(..), unGenLanguageDef, makeTokenParser) ----------------------------------------------------------- -- Styles: haskellStyle, javaStyle diff --git a/src/Text/Parsing/Parser/Stream.purs b/src/Text/Parsing/Parser/Stream.purs new file mode 100644 index 0000000..48492f5 --- /dev/null +++ b/src/Text/Parsing/Parser/Stream.purs @@ -0,0 +1,112 @@ +-- | Primitive parsers for working with an `StreamLike` input. + +module Text.Parsing.Parser.Stream where + +import Data.Foldable (fold, elem, notElem) +import Data.List as L +import Data.List.Lazy as LazyL +import Data.Monoid.Endo (Endo(..)) +import Data.Maybe (Maybe(..)) +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 (Position, updatePosString, updatePosChar) +import Prelude hiding (between) + +-- | A newtype used to identify a prefix of a stream +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. +-- | +-- | 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)))} + +instance lazyListStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) a where + uncons f = LazyL.uncons f <#> \({ head, tail}) -> + { head, tail, updatePos: (_ `updatePos` head)} + stripPrefix (Prefix p) s = LazyL.stripPrefix (LazyL.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 + case uncons input of + Nothing -> pure unit + _ -> fail "Expected EOF" + +-- | Match the specified prefix. +prefix :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f +prefix p = do + input <- gets \(ParseState input _ _) -> input + case stripPrefix (Prefix p) input of + Just {rest, updatePos} -> do + modify \(ParseState _ position _) -> + ParseState rest (updatePos position) true + pure p + _ -> fail ("Expected " <> show p) + +-- | 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, updatePos, tail }) -> do + modify \(ParseState _ position _) -> + ParseState tail (updatePos position) true + pure head + +-- | 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 <- token + if f c then pure c + 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 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 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) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 3740c7f..6507c35 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -3,119 +3,45 @@ module Text.Parsing.Parser.String where import Data.Array (many) -import Data.Foldable (fold, elem, notElem) -import Data.List as L -import Data.List.Lazy as LazyL -import Data.Monoid.Endo (Endo(..)) -import Data.Maybe (Maybe(..)) -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 (Position, updatePosString, updatePosChar) +import Data.Char.Unicode (isAlpha, isAlphaNum, isDigit, isHexDigit, isOctDigit, isSpace, isUpper) import Prelude hiding (between) - --- | A newtype used to identify a prefix of a string -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. --- | --- | 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)))} - -instance lazyListStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) a where - uncons f = LazyL.uncons f <#> \({ head, tail}) -> - { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = LazyL.stripPrefix (LazyL.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 - case uncons input of - Nothing -> pure unit - _ -> fail "Expected EOF" - --- | Match the specified prefix. -prefix :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f -prefix p = do - input <- gets \(ParseState input _ _) -> input - case stripPrefix (Prefix p) input of - Just {rest, updatePos} -> do - modify \(ParseState _ position _) -> - ParseState rest (updatePos position) true - pure p - _ -> fail ("Expected " <> show p) - --- | 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, updatePos, tail }) -> do - modify \(ParseState _ position _) -> - ParseState tail (updatePos position) true - pure head - --- | 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 <- token - if f c then pure c - 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 - +import Text.Parsing.Parser (ParserT) +import Text.Parsing.Parser.Combinators (()) +import Text.Parsing.Parser.Stream (class StreamLike, satisfy) -- | 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' +whiteSpace = many space -- | Skip whitespace characters. skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit skipSpaces = void whiteSpace --- | 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) +-- | Parse a digit. Matches any char that satisfies `Data.Char.Unicode.isDigit`. +digit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +digit = satisfy isDigit "digit" + +-- | Parse a hex digit. Matches any char that satisfies `Data.Char.Unicode.isHexDigit`. +hexDigit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +hexDigit = satisfy isHexDigit "hex digit" + +-- | Parse an octal digit. Matches any char that satisfies `Data.Char.Unicode.isOctDigit`. +octDigit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +octDigit = satisfy isOctDigit "oct digit" + +-- | Parse an uppercase letter. Matches any char that satisfies `Data.Char.Unicode.isUpper`. +upper :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +upper = satisfy isUpper "uppercase letter" + +-- | Parse a space character. Matches any char that satisfies `Data.Char.Unicode.isSpace`. +space :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +space = satisfy isSpace "space" + +-- | Parse an alphabetical character. Matches any char that satisfies `Data.Char.Unicode.isAlpha`. +letter :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +letter = satisfy isAlpha "letter" --- | 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) +-- | Parse an alphabetical or numerical character. +-- | Matches any char that satisfies `Data.Char.Unicode.isAlphaNum`. +alphaNum :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +alphaNum = satisfy isAlphaNum "letter or digit" diff --git a/src/Text/Parsing/Parser/Token.purs b/src/Text/Parsing/Parser/Token.purs index 3b4774f..a41ced8 100644 --- a/src/Text/Parsing/Parser/Token.purs +++ b/src/Text/Parsing/Parser/Token.purs @@ -7,14 +7,6 @@ module Text.Parsing.Parser.Token , TokenParser , GenTokenParser , makeTokenParser - -- should these be exported? Maybe they should go in a different module? - , digit - , hexDigit - , octDigit - , upper - , space - , letter - , alphaNum ) where @@ -24,7 +16,7 @@ import Data.List as List import Control.Lazy (fix) import Control.MonadPlus ((<|>)) import Data.Char (fromCharCode, toCharCode) -import Data.Char.Unicode (digitToInt, isAlpha, isAlphaNum, isDigit, isHexDigit, isOctDigit, isSpace, isUpper) +import Data.Char.Unicode (digitToInt, isAlpha) import Data.Either (Either(..)) import Data.Foldable (foldl, foldr) import Data.Identity (Identity) @@ -36,7 +28,8 @@ import Data.Tuple (Tuple(..)) import Math (pow) import Text.Parsing.Parser (ParserT, fail) import Text.Parsing.Parser.Combinators (skipMany1, try, skipMany, notFollowedBy, option, choice, between, sepBy1, sepBy, (), ()) -import Text.Parsing.Parser.String (satisfy, oneOf, noneOf, prefix, match) +import Text.Parsing.Parser.Stream (satisfy, oneOf, noneOf, prefix, match) +import Text.Parsing.Parser.String (digit, hexDigit, octDigit, space, upper) import Prelude hiding (when,between) type LanguageDef = GenLanguageDef String Identity @@ -707,7 +700,7 @@ whiteSpace' langDef@(LanguageDef languageDef) skipMany (simpleSpace <|> oneLineComment langDef <|> multiLineComment langDef "") simpleSpace :: forall m . Monad m => ParserT String m Unit -simpleSpace = skipMany1 (satisfy isSpace) +simpleSpace = skipMany1 (space) oneLineComment :: forall m . Monad m => GenLanguageDef String m -> ParserT String m Unit oneLineComment (LanguageDef languageDef) = @@ -745,32 +738,3 @@ inCommentSingle (LanguageDef languageDef) = ------------------------------------------------------------------------- -- Helper functions that should maybe go in Text.Parsing.Parser.String -- ------------------------------------------------------------------------- - --- | Parse a digit. Matches any char that satisfies `Data.Char.Unicode.isDigit`. -digit :: forall m . Monad m => ParserT String m Char -digit = satisfy isDigit "digit" - --- | Parse a hex digit. Matches any char that satisfies `Data.Char.Unicode.isHexDigit`. -hexDigit :: forall m . Monad m => ParserT String m Char -hexDigit = satisfy isHexDigit "hex digit" - --- | Parse an octal digit. Matches any char that satisfies `Data.Char.Unicode.isOctDigit`. -octDigit :: forall m . Monad m => ParserT String m Char -octDigit = satisfy isOctDigit "oct digit" - --- | Parse an uppercase letter. Matches any char that satisfies `Data.Char.Unicode.isUpper`. -upper :: forall m . Monad m => ParserT String m Char -upper = satisfy isUpper "uppercase letter" - --- | Parse a space character. Matches any char that satisfies `Data.Char.Unicode.isSpace`. -space :: forall m . Monad m => ParserT String m Char -space = satisfy isSpace "space" - --- | Parse an alphabetical character. Matches any char that satisfies `Data.Char.Unicode.isAlpha`. -letter :: forall m . Monad m => ParserT String m Char -letter = satisfy isAlpha "letter" - --- | Parse an alphabetical or numerical character. --- | Matches any char that satisfies `Data.Char.Unicode.isAlphaNum`. -alphaNum :: forall m . Monad m => ParserT String m Char -alphaNum = satisfy isAlphaNum "letter or digit" diff --git a/test/Main.purs b/test/Main.purs index f9e912d..299d2c3 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -16,7 +16,7 @@ import Text.Parsing.Parser.Combinators (endBy1, sepBy1, optionMaybe, try, chainl import Text.Parsing.Parser.Expr (Assoc(..), Operator(..), buildExprParser) import Text.Parsing.Parser.Language (javaStyle, haskellStyle, haskellDef) import Text.Parsing.Parser.Pos (Position(..)) -import Text.Parsing.Parser.String (eof, prefix, match, satisfy, token, class HasUpdatePosition) +import Text.Parsing.Parser.Stream (eof, prefix, match, satisfy, token, class HasUpdatePosition) import Text.Parsing.Parser.Token (TokenParser, makeTokenParser) import Prelude hiding (between,when) From 499c1d0656481ba3cc30b3b09ac59941cc9312fb Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 30 Jul 2017 22:06:57 +0400 Subject: [PATCH 19/23] add m to StreamLike --- src/Text/Parsing/Parser/Stream.purs | 51 +++++++++++++++-------------- src/Text/Parsing/Parser/String.purs | 18 +++++----- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/Text/Parsing/Parser/Stream.purs b/src/Text/Parsing/Parser/Stream.purs index 48492f5..37b7505 100644 --- a/src/Text/Parsing/Parser/Stream.purs +++ b/src/Text/Parsing/Parser/Stream.purs @@ -2,18 +2,19 @@ module Text.Parsing.Parser.Stream where +import Control.Monad.State (modify, gets) +import Control.Monad.Trans.Class (lift) import Data.Foldable (fold, elem, notElem) import Data.List as L import Data.List.Lazy as LazyL -import Data.Monoid.Endo (Endo(..)) import Data.Maybe (Maybe(..)) +import Data.Monoid.Endo (Endo(..)) import Data.Newtype (class Newtype, unwrap) import Data.String as S -import Control.Monad.State (modify, gets) +import Prelude hiding (between) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (try, ()) import Text.Parsing.Parser.Pos (Position, updatePosString, updatePosChar) -import Prelude hiding (between) -- | A newtype used to identify a prefix of a stream newtype Prefix a = Prefix a @@ -39,41 +40,41 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | -- | 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 } +class StreamLike s m t | s -> t where + uncons :: s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position }) + stripPrefix :: Prefix s -> s -> m (Maybe { rest :: s, updatePos :: Position -> Position }) -instance stringStreamLike :: StreamLike String Char where - uncons f = S.uncons f <#> \({ head, tail}) -> +instance stringStreamLike :: (Applicative m) => StreamLike String m Char where + uncons f = pure $ S.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = S.stripPrefix (S.Pattern p) s <#> \rest -> + stripPrefix (Prefix p) s = pure $ 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}) -> +instance listStreamLike :: (Applicative m, Eq a, HasUpdatePosition a) => StreamLike (L.List a) m a where + uncons f = pure $ L.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = L.stripPrefix (L.Pattern p) s <#> \rest -> + stripPrefix (Prefix p) s = pure $ L.stripPrefix (L.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} -instance lazyListStreamLike :: (Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) a where - uncons f = LazyL.uncons f <#> \({ head, tail}) -> +instance lazyListStreamLike :: (Applicative m, Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) m a where + uncons f = pure $ LazyL.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = LazyL.stripPrefix (LazyL.Pattern p) s <#> \rest -> + stripPrefix (Prefix p) s = pure $ LazyL.stripPrefix (LazyL.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 :: forall s t m. StreamLike s m t => Monad m => ParserT s m Unit eof = do input <- gets \(ParseState input _ _) -> input - case uncons input of + (lift $ uncons input) >>= case _ of Nothing -> pure unit _ -> fail "Expected EOF" -- | Match the specified prefix. -prefix :: forall f c m. StreamLike f c => Show f => Monad m => f -> ParserT f m f +prefix :: forall f c m. StreamLike f m c => Show f => Monad m => f -> ParserT f m f prefix p = do input <- gets \(ParseState input _ _) -> input - case stripPrefix (Prefix p) input of + (lift $ stripPrefix (Prefix p) input) >>= case _ of Just {rest, updatePos} -> do modify \(ParseState _ position _) -> ParseState rest (updatePos position) true @@ -81,10 +82,10 @@ prefix p = do _ -> fail ("Expected " <> show p) -- | Match any token. -token :: forall f c m. StreamLike f c => Monad m => ParserT f m c +token :: forall s t m. StreamLike s m t => Monad m => ParserT s m t token = do input <- gets \(ParseState input _ _) -> input - case uncons input of + (lift $ uncons input) >>= case _ of Nothing -> fail "Unexpected EOF" Just ({ head, updatePos, tail }) -> do modify \(ParseState _ position _) -> @@ -92,21 +93,21 @@ token = do pure head -- | 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 :: forall s t m. StreamLike s m t => Show t => Monad m => (t -> Boolean) -> ParserT s m t satisfy f = try do c <- token if f c then pure c 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 :: forall s t m. StreamLike s m t => Eq t => Show t => Monad m => t -> ParserT s m t match c = satisfy (_ == c) show c -- | 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 :: forall s t m. StreamLike s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t oneOf ss = satisfy (flip elem ss) ("one of " <> show ss) -- | 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 :: forall s t m. StreamLike s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t noneOf ss = satisfy (flip notElem ss) ("none of " <> show ss) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index 6507c35..da81500 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -10,38 +10,38 @@ import Text.Parsing.Parser.Combinators (()) import Text.Parsing.Parser.Stream (class StreamLike, satisfy) -- | Match a whitespace characters but returns them using Array. -whiteSpace :: forall f m. StreamLike f Char => Monad m => ParserT f m (Array Char) +whiteSpace :: forall s m. StreamLike s m Char => Monad m => ParserT s m (Array Char) whiteSpace = many space -- | Skip whitespace characters. -skipSpaces :: forall f m. StreamLike f Char => Monad m => ParserT f m Unit +skipSpaces :: forall s m. StreamLike s m Char => Monad m => ParserT s m Unit skipSpaces = void whiteSpace -- | Parse a digit. Matches any char that satisfies `Data.Char.Unicode.isDigit`. -digit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +digit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char digit = satisfy isDigit "digit" -- | Parse a hex digit. Matches any char that satisfies `Data.Char.Unicode.isHexDigit`. -hexDigit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +hexDigit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char hexDigit = satisfy isHexDigit "hex digit" -- | Parse an octal digit. Matches any char that satisfies `Data.Char.Unicode.isOctDigit`. -octDigit :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +octDigit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char octDigit = satisfy isOctDigit "oct digit" -- | Parse an uppercase letter. Matches any char that satisfies `Data.Char.Unicode.isUpper`. -upper :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +upper :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char upper = satisfy isUpper "uppercase letter" -- | Parse a space character. Matches any char that satisfies `Data.Char.Unicode.isSpace`. -space :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +space :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char space = satisfy isSpace "space" -- | Parse an alphabetical character. Matches any char that satisfies `Data.Char.Unicode.isAlpha`. -letter :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +letter :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char letter = satisfy isAlpha "letter" -- | Parse an alphabetical or numerical character. -- | Matches any char that satisfies `Data.Char.Unicode.isAlphaNum`. -alphaNum :: forall f m . StreamLike f Char => Monad m => ParserT f m Char +alphaNum :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char alphaNum = satisfy isAlphaNum "letter or digit" From 9c7e9e913fd180e1c814f34e1b63ddf02616d4f0 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 30 Jul 2017 22:07:46 +0400 Subject: [PATCH 20/23] replace StreamLike to Stream --- src/Text/Parsing/Parser/Stream.purs | 24 ++++++++++++------------ src/Text/Parsing/Parser/String.purs | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Text/Parsing/Parser/Stream.purs b/src/Text/Parsing/Parser/Stream.purs index 37b7505..f9930e3 100644 --- a/src/Text/Parsing/Parser/Stream.purs +++ b/src/Text/Parsing/Parser/Stream.purs @@ -1,4 +1,4 @@ --- | Primitive parsers for working with an `StreamLike` input. +-- | Primitive parsers for working with an `Stream` input. module Text.Parsing.Parser.Stream where @@ -40,30 +40,30 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where -- | -- | Instances must satisfy the following laws: -- | - `stripPrefix (Prefix a) a >>= uncons = Nothing` -class StreamLike s m t | s -> t where +class Stream s m t | s -> t where uncons :: s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position }) stripPrefix :: Prefix s -> s -> m (Maybe { rest :: s, updatePos :: Position -> Position }) -instance stringStreamLike :: (Applicative m) => StreamLike String m Char where +instance stringStream :: (Applicative m) => Stream String m Char where uncons f = pure $ S.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} stripPrefix (Prefix p) s = pure $ S.stripPrefix (S.Pattern p) s <#> \rest -> { rest, updatePos: (_ `updatePos` p)} -instance listStreamLike :: (Applicative m, Eq a, HasUpdatePosition a) => StreamLike (L.List a) m a where +instance listStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (L.List a) m a where uncons f = pure $ L.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} stripPrefix (Prefix p) s = pure $ L.stripPrefix (L.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} -instance lazyListStreamLike :: (Applicative m, Eq a, HasUpdatePosition a) => StreamLike (LazyL.List a) m a where +instance lazyListStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (LazyL.List a) m a where uncons f = pure $ LazyL.uncons f <#> \({ head, tail}) -> { head, tail, updatePos: (_ `updatePos` head)} stripPrefix (Prefix p) s = pure $ LazyL.stripPrefix (LazyL.Pattern p) s <#> \rest -> { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} -- | Match end of stream. -eof :: forall s t m. StreamLike s m t => Monad m => ParserT s m Unit +eof :: forall s t m. Stream s m t => Monad m => ParserT s m Unit eof = do input <- gets \(ParseState input _ _) -> input (lift $ uncons input) >>= case _ of @@ -71,7 +71,7 @@ eof = do _ -> fail "Expected EOF" -- | Match the specified prefix. -prefix :: forall f c m. StreamLike f m c => Show f => Monad m => f -> ParserT f m f +prefix :: forall f c m. Stream f m c => Show f => Monad m => f -> ParserT f m f prefix p = do input <- gets \(ParseState input _ _) -> input (lift $ stripPrefix (Prefix p) input) >>= case _ of @@ -82,7 +82,7 @@ prefix p = do _ -> fail ("Expected " <> show p) -- | Match any token. -token :: forall s t m. StreamLike s m t => Monad m => ParserT s m t +token :: forall s t m. Stream s m t => Monad m => ParserT s m t token = do input <- gets \(ParseState input _ _) -> input (lift $ uncons input) >>= case _ of @@ -93,21 +93,21 @@ token = do pure head -- | Match a token satisfying the specified predicate. -satisfy :: forall s t m. StreamLike s m t => Show t => Monad m => (t -> Boolean) -> ParserT s m t +satisfy :: forall s t m. Stream s m t => Show t => Monad m => (t -> Boolean) -> ParserT s m t satisfy f = try do c <- token if f c then pure c else fail $ "Character " <> show c <> " did not satisfy predicate" -- | Match the specified token -match :: forall s t m. StreamLike s m t => Eq t => Show t => Monad m => t -> ParserT s m t +match :: forall s t m. Stream s m t => Eq t => Show t => Monad m => t -> ParserT s m t match c = satisfy (_ == c) show c -- | Match one of the tokens in the array. -oneOf :: forall s t m. StreamLike s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t +oneOf :: forall s t m. Stream s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t oneOf ss = satisfy (flip elem ss) ("one of " <> show ss) -- | Match any token not in the array. -noneOf :: forall s t m. StreamLike s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t +noneOf :: forall s t m. Stream s m t => Show t => Eq t => Monad m => Array t -> ParserT s m t noneOf ss = satisfy (flip notElem ss) ("none of " <> show ss) diff --git a/src/Text/Parsing/Parser/String.purs b/src/Text/Parsing/Parser/String.purs index da81500..a5f7d38 100644 --- a/src/Text/Parsing/Parser/String.purs +++ b/src/Text/Parsing/Parser/String.purs @@ -7,41 +7,41 @@ import Data.Char.Unicode (isAlpha, isAlphaNum, isDigit, isHexDigit, isOctDigit, import Prelude hiding (between) import Text.Parsing.Parser (ParserT) import Text.Parsing.Parser.Combinators (()) -import Text.Parsing.Parser.Stream (class StreamLike, satisfy) +import Text.Parsing.Parser.Stream (class Stream, satisfy) -- | Match a whitespace characters but returns them using Array. -whiteSpace :: forall s m. StreamLike s m Char => Monad m => ParserT s m (Array Char) +whiteSpace :: forall s m. Stream s m Char => Monad m => ParserT s m (Array Char) whiteSpace = many space -- | Skip whitespace characters. -skipSpaces :: forall s m. StreamLike s m Char => Monad m => ParserT s m Unit +skipSpaces :: forall s m. Stream s m Char => Monad m => ParserT s m Unit skipSpaces = void whiteSpace -- | Parse a digit. Matches any char that satisfies `Data.Char.Unicode.isDigit`. -digit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +digit :: forall s m . Stream s m Char => Monad m => ParserT s m Char digit = satisfy isDigit "digit" -- | Parse a hex digit. Matches any char that satisfies `Data.Char.Unicode.isHexDigit`. -hexDigit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +hexDigit :: forall s m . Stream s m Char => Monad m => ParserT s m Char hexDigit = satisfy isHexDigit "hex digit" -- | Parse an octal digit. Matches any char that satisfies `Data.Char.Unicode.isOctDigit`. -octDigit :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +octDigit :: forall s m . Stream s m Char => Monad m => ParserT s m Char octDigit = satisfy isOctDigit "oct digit" -- | Parse an uppercase letter. Matches any char that satisfies `Data.Char.Unicode.isUpper`. -upper :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +upper :: forall s m . Stream s m Char => Monad m => ParserT s m Char upper = satisfy isUpper "uppercase letter" -- | Parse a space character. Matches any char that satisfies `Data.Char.Unicode.isSpace`. -space :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +space :: forall s m . Stream s m Char => Monad m => ParserT s m Char space = satisfy isSpace "space" -- | Parse an alphabetical character. Matches any char that satisfies `Data.Char.Unicode.isAlpha`. -letter :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +letter :: forall s m . Stream s m Char => Monad m => ParserT s m Char letter = satisfy isAlpha "letter" -- | Parse an alphabetical or numerical character. -- | Matches any char that satisfies `Data.Char.Unicode.isAlphaNum`. -alphaNum :: forall s m . StreamLike s m Char => Monad m => ParserT s m Char +alphaNum :: forall s m . Stream s m Char => Monad m => ParserT s m Char alphaNum = satisfy isAlphaNum "letter or digit" From ecb6a3f0c556abf914973949339e8ec8dfc369f0 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 30 Jul 2017 22:21:52 +0400 Subject: [PATCH 21/23] resolve ShadowedName position --- src/Text/Parsing/Parser.purs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Text/Parsing/Parser.purs b/src/Text/Parsing/Parser.purs index 5502ded..77a132c 100644 --- a/src/Text/Parsing/Parser.purs +++ b/src/Text/Parsing/Parser.purs @@ -122,8 +122,8 @@ instance monadTransParserT :: MonadTrans (ParserT s) where -- | Set the consumed flag. consume :: forall s m. Monad m => ParserT s m Unit -consume = modify \(ParseState input position _) -> - ParseState input position true +consume = modify \(ParseState input pos _) -> + ParseState input pos true -- | Returns the current position in the stream. position :: forall s m. Monad m => ParserT s m Position @@ -135,4 +135,4 @@ fail message = failWithPosition message =<< position -- | Fail with a message and a position. failWithPosition :: forall m s a. Monad m => String -> Position -> ParserT s m a -failWithPosition message position = throwError (ParseError message position) +failWithPosition message pos = throwError (ParseError message pos) From ea96e7349471eb1c546fb6923d6fffacfe753e71 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 30 Jul 2017 22:22:11 +0400 Subject: [PATCH 22/23] use correct wording in setisfy --- src/Text/Parsing/Parser/Stream.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Parsing/Parser/Stream.purs b/src/Text/Parsing/Parser/Stream.purs index 14252a9..0377876 100644 --- a/src/Text/Parsing/Parser/Stream.purs +++ b/src/Text/Parsing/Parser/Stream.purs @@ -97,7 +97,7 @@ satisfy :: forall s t m. Stream s m t => Show t => Monad m => (t -> Boolean) -> satisfy f = tryRethrow do c <- token if f c then pure c - else fail $ "Character " <> show c <> " did not satisfy predicate" + else fail $ "Token " <> show c <> " did not satisfy predicate" -- | Match the specified token match :: forall s t m. Stream s m t => Eq t => Show t => Monad m => t -> ParserT s m t From 61d6317403aae0f386ff69b16df69cce59f085e4 Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Sun, 3 Dec 2017 02:08:13 +0100 Subject: [PATCH 23/23] Avoids closure in Stream class s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position }) instead of having updatePos as a result of uncons or stripPrefix now this operations take position with input which is part of a parser state. this way we should allocation less of intermediate objects. --- src/Text/Parsing/Parser.purs | 47 +++++++++++----- src/Text/Parsing/Parser/Combinators.purs | 33 +++++------ src/Text/Parsing/Parser/Indent.purs | 4 +- src/Text/Parsing/Parser/Stream.purs | 71 ++++++++++++------------ 4 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/Text/Parsing/Parser.purs b/src/Text/Parsing/Parser.purs index 77a132c..258ce70 100644 --- a/src/Text/Parsing/Parser.purs +++ b/src/Text/Parsing/Parser.purs @@ -7,6 +7,8 @@ module Text.Parsing.Parser , Parser , runParser , runParserT + , unParserT + , inParserT , hoistParserT , mapParserT , consume @@ -22,14 +24,14 @@ import Control.Lazy (defer, class Lazy) import Control.Monad.Error.Class (class MonadThrow, throwError) import Control.Monad.Except (class MonadError, ExceptT(..), runExceptT, mapExceptT) import Control.Monad.Rec.Class (class MonadRec) -import Control.Monad.State (runStateT, class MonadState, StateT(..), gets, evalStateT, mapStateT, modify) +import Control.Monad.State (runStateT, class MonadState, StateT(..), gets, mapStateT, modify) import Control.Monad.Trans.Class (class MonadTrans, lift) import Control.MonadPlus (class Alternative, class MonadZero, class MonadPlus, class Plus) import Data.Either (Either(..)) import Data.Identity (Identity) import Data.Monoid (class Monoid, mempty) import Data.Newtype (class Newtype, unwrap, over) -import Data.Tuple (Tuple(..)) +import Data.Tuple (Tuple(..), fst) import Text.Parsing.Parser.Pos (Position, initialPos) -- | A parsing error, consisting of a message and position information. @@ -49,7 +51,9 @@ derive instance eqParseError :: Eq ParseError derive instance ordParseError :: Ord ParseError -- | Contains the remaining input and current position. -data ParseState s = ParseState s Position Boolean +-- data ParseState s = ParseState s Position Boolean +newtype ParseState s = ParseState + { input :: s, pos :: Position, consumed :: Boolean } -- | The Parser monad transformer. -- | @@ -61,8 +65,23 @@ derive instance newtypeParserT :: Newtype (ParserT s m a) _ -- | Apply a parser, keeping only the parsed 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 +runParserT input p = fst <$> unParserT p initialState + where + initialState = ParseState { input, pos: initialPos, consumed: false } + +-- Reveals inner function of parser +unParserT :: forall m s a + . Monad m + => ParserT s m a + -> (ParseState s -> m (Tuple (Either ParseError a) (ParseState s))) +unParserT (ParserT p) = runStateT $ runExceptT p + +-- Takes inner function of Parser and constructs one +inParserT :: forall m s a + . Monad m + => (ParseState s -> m (Tuple (Either ParseError a) (ParseState s))) + -> ParserT s m a +inParserT = ParserT <<< ExceptT <<< StateT -- | The `Parser` monad is a synonym for the parser monad transformer applied to the `Identity` monad. type Parser s = ParserT s Identity @@ -101,12 +120,12 @@ derive newtype instance monadThrowParserT :: Monad m => MonadThrow ParseError (P derive newtype instance monadErrorParserT :: Monad m => MonadError ParseError (ParserT s m) instance altParserT :: Monad m => Alt (ParserT s m) where - 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') + alt p1 p2 = inParserT \(ParseState state) -> + unParserT p1 (ParseState (state{consumed = false})) >>= \(Tuple e (ParseState nextState)) -> + case e of + Left err + | not nextState.consumed -> unParserT p2 (ParseState state) + _ -> pure (Tuple e (ParseState nextState)) instance plusParserT :: Monad m => Plus (ParserT s m) where empty = fail "No alternative" @@ -122,12 +141,12 @@ instance monadTransParserT :: MonadTrans (ParserT s) where -- | Set the consumed flag. consume :: forall s m. Monad m => ParserT s m Unit -consume = modify \(ParseState input pos _) -> - ParseState input pos true +consume = modify \(ParseState state) -> + ParseState state{consumed = true} -- | Returns the current position in the stream. position :: forall s m. Monad m => ParserT s m Position -position = gets \(ParseState _ pos _) -> pos +position = gets \(ParseState state) -> state.pos -- | Fail with a message. fail :: forall m s a. Monad m => String -> ParserT s m a diff --git a/src/Text/Parsing/Parser/Combinators.purs b/src/Text/Parsing/Parser/Combinators.purs index cb32ea7..74d515d 100644 --- a/src/Text/Parsing/Parser/Combinators.purs +++ b/src/Text/Parsing/Parser/Combinators.purs @@ -21,16 +21,13 @@ module Text.Parsing.Parser.Combinators where import Prelude -import Control.Monad.Except (runExceptT, ExceptT(..)) -import Control.Monad.State (StateT(..), runStateT) import Control.Plus (empty, (<|>)) import Data.Either (Either(..)) import Data.Foldable (class Foldable, foldl) import Data.List (List(..), (:), many, some, singleton) import Data.Maybe (Maybe(..)) -import Data.Newtype (unwrap) import Data.Tuple (Tuple(..)) -import Text.Parsing.Parser (ParseState(..), ParserT(..), ParseError(..), fail) +import Text.Parsing.Parser (ParseState(..), ParserT, ParseError(..), unParserT, inParserT, fail) -- | Provide an error message in the case of failure. withErrorMessage :: forall m s a. Monad m => ParserT s m a -> String -> ParserT s m a @@ -68,24 +65,28 @@ optionMaybe p = option Nothing (Just <$> p) -- | In case of failure, reset the stream to the unconsumed state. try :: forall m s a. Monad m => ParserT s m a -> ParserT s m a -try p = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState _ _ consumed)) -> do - Tuple e s'@(ParseState input position _) <- runStateT (runExceptT (unwrap p)) s - case e of - Left _ -> pure (Tuple e (ParseState input position consumed)) - _ -> pure (Tuple e s') +try p = inParserT \(ParseState state) -> + unParserT p (ParseState state) <#> \(Tuple e (ParseState nextState)) -> + case e of + Left _ -> Tuple e (ParseState nextState{consumed = state.consumed}) + Right _ -> Tuple e (ParseState nextState) -- | Like `try`, but will reannotate the error location to the `try` point. tryRethrow :: forall m s a. Monad m => ParserT s m a -> ParserT s m a -tryRethrow p = (ParserT <<< ExceptT <<< StateT) \(s@(ParseState _ position consumed)) -> do - Tuple e s'@(ParseState input' position' _) <- runStateT (runExceptT (unwrap p)) s - case e of - Left (ParseError err _) -> pure (Tuple (Left (ParseError err position)) (ParseState input' position' consumed)) - _ -> pure (Tuple e s') +tryRethrow p = inParserT \(ParseState state) -> + unParserT p (ParseState state) <#> \(Tuple e (ParseState nextState)) -> + case e of + Left (ParseError err _) -> + Tuple + (Left (ParseError err state.pos)) + (ParseState nextState{consumed = state.consumed}) + Right _ -> + Tuple e (ParseState nextState) -- | Parse a phrase, without modifying the consumed state or stream position. lookAhead :: forall s a m. Monad m => ParserT s m a -> ParserT s m a -lookAhead p = (ParserT <<< ExceptT <<< StateT) \s -> do - Tuple e _ <- runStateT (runExceptT (unwrap p)) s +lookAhead p = inParserT \s -> do + Tuple e _ <- unParserT p s pure (Tuple e s) -- | Parse phrases delimited by a separator. diff --git a/src/Text/Parsing/Parser/Indent.purs b/src/Text/Parsing/Parser/Indent.purs index c7f8e14..f83c06d 100644 --- a/src/Text/Parsing/Parser/Indent.purs +++ b/src/Text/Parsing/Parser/Indent.purs @@ -59,7 +59,7 @@ import Control.Monad.State.Trans (get, put) import Control.Monad.Trans.Class (lift) import Data.List (List(..), many) import Data.Maybe (Maybe(..)) -import Text.Parsing.Parser (ParserT, ParseState(ParseState), fail) +import Text.Parsing.Parser (ParserT, ParseState(..), fail) import Text.Parsing.Parser.Combinators (option, optionMaybe) import Text.Parsing.Parser.Pos (Position(..), initialPos) import Text.Parsing.Parser.Stream (prefix, oneOf) @@ -71,7 +71,7 @@ type IndentParser s a = ParserT s (State Position) a -- | @ getPosition @ returns current position -- | should probably be added to Text.Parsing.Parser.Pos getPosition :: forall m s. (Monad m) => ParserT s m Position -getPosition = gets \(ParseState _ pos _) -> pos +getPosition = gets \(ParseState state) -> state.pos -- | simple helper function to avoid typ-problems with MonadState instance get' :: forall s. IndentParser s Position diff --git a/src/Text/Parsing/Parser/Stream.purs b/src/Text/Parsing/Parser/Stream.purs index 0377876..7ccce0f 100644 --- a/src/Text/Parsing/Parser/Stream.purs +++ b/src/Text/Parsing/Parser/Stream.purs @@ -2,15 +2,15 @@ module Text.Parsing.Parser.Stream where -import Control.Monad.State (modify, gets) +import Control.Monad.State (put, get) import Control.Monad.Trans.Class (lift) -import Data.Foldable (fold, elem, notElem) +import Data.Foldable (foldl, elem, notElem) import Data.List as L import Data.List.Lazy as LazyL import Data.Maybe (Maybe(..)) -import Data.Monoid.Endo (Endo(..)) -import Data.Newtype (class Newtype, unwrap) +import Data.Newtype (class Newtype) import Data.String as S +import Data.Tuple (Tuple(..)) import Prelude hiding (between) import Text.Parsing.Parser (ParseState(..), ParserT, fail) import Text.Parsing.Parser.Combinators (tryRethrow, ()) @@ -36,60 +36,63 @@ instance charHasUpdatePosition :: HasUpdatePosition Char where updatePos = updatePosChar -- | This class exists to abstract over streams which support the string-like --- | operations which this modules needs. +-- | operations with position tracking, which this modules needs. -- | -- | Instances must satisfy the following laws: --- | - `stripPrefix (Prefix a) a >>= uncons = Nothing` +-- | - `stripPrefix (Prefix input) {input, position} >>= uncons = Nothing` + class Stream s m t | s -> t where - uncons :: s -> m (Maybe { head :: t, tail :: s, updatePos :: Position -> Position }) - stripPrefix :: Prefix s -> s -> m (Maybe { rest :: s, updatePos :: Position -> Position }) + uncons :: forall r. ParserCursor s r -> m (Maybe (Tuple t (ParserCursor s r))) + stripPrefix :: forall r. Prefix s -> ParserCursor s r -> m (Maybe (ParserCursor s r)) + +-- Part or ParseState which is exposed to Stream instances +type ParserCursor s r = { input :: s, pos :: Position | r} + -instance stringStream :: (Applicative m) => Stream String m Char where - uncons f = pure $ S.uncons f <#> \({ head, tail}) -> - { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = pure $ S.stripPrefix (S.Pattern p) s <#> \rest -> - { rest, updatePos: (_ `updatePos` p)} +instance stringStream :: (Applicative m) => Stream String m Char where + uncons state = pure $ S.uncons state.input <#> \({ head, tail}) -> + Tuple head state{input = tail, pos = updatePos state.pos head } + stripPrefix (Prefix p) state = pure $ S.stripPrefix (S.Pattern p) state.input <#> \rest -> + state{input = rest, pos = updatePos state.pos p} instance listStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (L.List a) m a where - uncons f = pure $ L.uncons f <#> \({ head, tail}) -> - { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = pure $ L.stripPrefix (L.Pattern p) s <#> \rest -> - { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} + uncons state = pure $ L.uncons state.input <#> \({ head, tail}) -> + Tuple head state{input = tail, pos = updatePos state.pos head } + stripPrefix (Prefix p) state = pure $ L.stripPrefix (L.Pattern p) state.input <#> \rest -> + state{input = rest, pos = foldl updatePos state.pos p} instance lazyListStream :: (Applicative m, Eq a, HasUpdatePosition a) => Stream (LazyL.List a) m a where - uncons f = pure $ LazyL.uncons f <#> \({ head, tail}) -> - { head, tail, updatePos: (_ `updatePos` head)} - stripPrefix (Prefix p) s = pure $ LazyL.stripPrefix (LazyL.Pattern p) s <#> \rest -> - { rest, updatePos: unwrap (fold (p <#> (flip updatePos >>> Endo)))} + uncons state = pure $ LazyL.uncons state.input <#> \({ head, tail}) -> + Tuple head state{input = tail, pos = updatePos state.pos head } + stripPrefix (Prefix p) state = pure $ LazyL.stripPrefix (LazyL.Pattern p) state.input <#> \rest -> + state{input = rest, pos = foldl updatePos state.pos p} -- | Match end of stream. eof :: forall s t m. Stream s m t => Monad m => ParserT s m Unit eof = do - input <- gets \(ParseState input _ _) -> input - (lift $ uncons input) >>= case _ of + ParseState state <- get + (lift $ uncons state) >>= case _ of Nothing -> pure unit _ -> fail "Expected EOF" -- | Match the specified prefix. prefix :: forall f c m. Stream f m c => Show f => Monad m => f -> ParserT f m f prefix p = do - input <- gets \(ParseState input _ _) -> input - (lift $ stripPrefix (Prefix p) input) >>= case _ of - Just {rest, updatePos} -> do - modify \(ParseState _ position _) -> - ParseState rest (updatePos position) true + ParseState state <- get + (lift $ stripPrefix (Prefix p) state) >>= case _ of + Nothing -> fail $ "Expected " <> show p + Just nextState -> do + put $ ParseState nextState{consumed = true} pure p - _ -> fail ("Expected " <> show p) -- | Match any token. token :: forall s t m. Stream s m t => Monad m => ParserT s m t token = do - input <- gets \(ParseState input _ _) -> input - (lift $ uncons input) >>= case _ of + ParseState state <- get + (lift $ uncons state) >>= case _ of Nothing -> fail "Unexpected EOF" - Just ({ head, updatePos, tail }) -> do - modify \(ParseState _ position _) -> - ParseState tail (updatePos position) true + Just (Tuple head nextState) -> do + put $ ParseState nextState{consumed = true} pure head -- | Match a token satisfying the specified predicate.