LParse 0.1.3.1 → 0.2.1.0
raw patch · 6 files changed
+242/−127 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.LParse.Atomics: digit :: Parser r String Integer
+ Text.LParse.Atomics: letter :: Parser r String Char
+ Text.LParse.TokenStream: class (Functor t, Foldable t) => TokenStream t
+ Text.LParse.TokenStream: cons :: TokenStream t => a -> t a -> t a
+ Text.LParse.TokenStream: instance Text.LParse.TokenStream.TokenStream GHC.Base.Maybe
+ Text.LParse.TokenStream: instance Text.LParse.TokenStream.TokenStream []
+ Text.LParse.TokenStream: nil :: TokenStream t => t a
+ Text.LParse.TokenStream: rest :: TokenStream t => t a -> t a
+ Text.LParse.TokenStream: sDrop :: (TokenStream s) => Int -> s a -> s a
+ Text.LParse.TokenStream: sFilter :: (TokenStream s) => (a -> Bool) -> s a -> s a
+ Text.LParse.TokenStream: sZip :: (TokenStream s) => s a -> s b -> s (a, b)
+ Text.LParse.TokenStream: sZipWith :: (TokenStream s) => (a -> b -> c) -> s a -> s b -> s c
+ Text.LParse.TokenStream: skipN :: (TokenStream s) => Int -> s a -> s a
+ Text.LParse.TokenStream: top :: TokenStream t => t a -> a
- Text.LParse.Atomics: consume :: (Eq t, Show t) => [t] -> Parser r [t] ()
+ Text.LParse.Atomics: consume :: (Eq t, Show (s t), TokenStream s) => (s t) -> Parser r (s t) ()
- Text.LParse.Atomics: consumeSingle :: (Eq t, Show t) => t -> Parser r [t] ()
+ Text.LParse.Atomics: consumeSingle :: (Eq t, Show t, TokenStream s) => t -> Parser r (s t) ()
- Text.LParse.Atomics: peek :: (t -> Bool) -> String -> Parser r [t] ()
+ Text.LParse.Atomics: peek :: (TokenStream s) => (t -> Bool) -> String -> Parser r (s t) ()
- Text.LParse.Atomics: tokenParse :: (t -> a) -> Parser r [t] a
+ Text.LParse.Atomics: tokenParse :: (TokenStream s) => (t -> a) -> Parser r (s t) a
- Text.LParse.Atomics: tokenReturn :: Parser r [a] a
+ Text.LParse.Atomics: tokenReturn :: (TokenStream s) => Parser r (s a) a
- Text.LParse.Transformers: replace :: (t -> t) -> Parser r [t] a -> Parser r [t] a
+ Text.LParse.Transformers: replace :: (TokenStream s) => (t -> t) -> Parser r (s t) a -> Parser r (s t) a
- Text.LParse.Transformers: skip :: (Eq t) => [t] -> Parser r [t] a -> Parser r [t] a
+ Text.LParse.Transformers: skip :: (Eq t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
- Text.LParse.Transformers: skipBy :: (t -> Bool) -> Parser r [t] a -> Parser r [t] a
+ Text.LParse.Transformers: skipBy :: (TokenStream s) => (t -> Bool) -> Parser r (s t) a -> Parser r (s t) a
Files
- CHANGELOG.md +48/−9
- LParse.cabal +2/−2
- src/Text/LParse/Atomics.hs +71/−63
- src/Text/LParse/TokenStream.hs +62/−0
- src/Text/LParse/Transformers.hs +54/−53
- test/Test.hs +5/−0
CHANGELOG.md view
@@ -1,10 +1,49 @@-# Changelog - -## 0.1.3.0 - -* Added `check` function -* Improved testing facilities - -## 0.1.2.0 - +# Changelog++# 0.2++Highlights:++* Switching from list-based parsing to `TokenStream`++## 0.2.1++### 0.2.1.0++* Deprecated `skipN`, replaced with `sDrop` ++## 0.2.0++### 0.2.0.0++* Added `TokenStream`, an abstraction of lists+* Used `TokenStream` to reformulate Atomics and Transformers+++# 0.1++Highlights:++* Initial Version++## 0.1.4++### 0.1.4.0++* Added `digit` and `letter` parsers++## 0.1.3++### 0.1.3.1++* Improved testing facilities++### 0.1.3.0++* Added `check` function++## 0.1.2++### 0.1.2.0+ * Added `peek` function
LParse.cabal view
@@ -1,5 +1,5 @@ name: LParse-version: 0.1.3.1+version: 0.2.1.0 synopsis: A continuation-based parser library description: A parser library using continuations with a possibility for failure to build parsers in a clear and concise manner. homepage: https://github.com/MarcusVoelker/LParse#readme@@ -15,7 +15,7 @@ library hs-source-dirs: src- exposed-modules: Control.DoubleContinuations Text.LParse.Atomics, Text.LParse.Parser, Text.LParse.Transformers+ exposed-modules: Control.DoubleContinuations Text.LParse.Atomics, Text.LParse.Parser, Text.LParse.Transformers, Text.LParse.TokenStream build-depends: base >= 4.7 && < 5 default-language: Haskell2010
src/Text/LParse/Atomics.hs view
@@ -1,63 +1,71 @@-{-| -Module : Text.LParse.Atomics -Description : Atomic parsers for use with LParse -Copyright : (c) Marcus Völker, 2017 -License : MIT -Maintainer : marcus.voelker@rwth-aachen.de - -This module implements LParse's atomic parsers, i.e., parsers that are not built up from other parsers. --} -module Text.LParse.Atomics where - -import Control.DoubleContinuations - -import Control.Applicative -import Control.Monad -import Data.Char -import Text.LParse.Parser -import Text.LParse.Transformers - --- | A parser that always succeeds, parses nothing and returns unit -noop :: Parser r t () -noop = return () - --- | A parser that consumes the whole input and returns it unchanged -full :: Parser r [t] [t] -full = many tokenReturn - --- | A parser that consumes the whole input and discards it, successfully -discard :: Parser r [t] () -discard = void full - --- | A parser that parses nothing, but only succeeds if the input is empty -eoi :: Parser r [t] () -eoi = cParse null noop ("Input not fully consumed") - --- | Extracts the first token from the input and applies the given function to it -tokenParse :: (t -> a) -> Parser r [t] a -tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ head s,tail s))) - --- | Consumes and returns the first token of the input -tokenReturn :: Parser r [a] a -tokenReturn = tokenParse id - --- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence -consume :: (Eq t, Show t) => [t] -> Parser r [t] () -consume pre = cParse ((&&) <$> (all id . zipWith (==) pre) <*> ((>= length pre) . length)) (pParse (drop (length pre)) noop) ("Expected " ++ show pre) - --- | Succeeds exactly if the input begins with the given token. On success, consumes that token -consumeSingle :: (Eq t, Show t) => t -> Parser r [t] () -consumeSingle t = cParse (\s -> not (null s) && head s == t) (pParse tail noop) ("Expected " ++ show t) - --- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it -word :: Parser r String String -word = some (cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter") - --- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it -integer :: Parser r String Integer -integer = read <$> some (cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit") - - --- | Succeeds if the first token matches the given function, without consuming it -peek :: (t -> Bool) -> String -> Parser r [t] () -peek c = cParse (c . head) noop+{-|+Module : Text.LParse.Atomics+Description : Atomic parsers for use with LParse+Copyright : (c) Marcus Völker, 2017+License : MIT+Maintainer : marcus.voelker@rwth-aachen.de++This module implements LParse's atomic parsers, i.e., parsers that are not built up from other parsers.+-}+module Text.LParse.Atomics where ++import Control.DoubleContinuations++import Control.Applicative+import Control.Monad+import Data.Char+import Text.LParse.Parser+import Text.LParse.Transformers+import Text.LParse.TokenStream++-- | A parser that always succeeds, parses nothing and returns unit+noop :: Parser r t ()+noop = return ()++-- | A parser that consumes the whole input and returns it unchanged+full :: Parser r [t] [t]+full = many tokenReturn++-- | A parser that consumes the whole input and discards it, successfully+discard :: Parser r [t] ()+discard = void full++-- | A parser that parses nothing, but only succeeds if the input is empty+eoi :: Parser r [t] ()+eoi = cParse null noop ("Input not fully consumed")++-- | Extracts the first token from the input and applies the given function to it+tokenParse :: (TokenStream s) => (t -> a) -> Parser r (s t) a+tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ top s,rest s)))++-- | Consumes and returns the first token of the input+tokenReturn :: (TokenStream s) => Parser r (s a) a+tokenReturn = tokenParse id++-- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence+consume :: (Eq t, Show (s t), TokenStream s) => (s t) -> Parser r (s t) ()+consume pre = cParse ((&&) <$> (all id . sZipWith (==) pre) <*> ((>= length pre) . length)) (pParse (sDrop (length pre)) noop) ("Expected " ++ show pre)++-- | Succeeds exactly if the input begins with the given token. On success, consumes that token+consumeSingle :: (Eq t, Show t, TokenStream s) => t -> Parser r (s t) ()+consumeSingle t = cParse (\s -> not (null s) && top s == t) (pParse rest noop) ("Expected " ++ show t)++-- | Extracts the first digit and returns it+digit :: Parser r String Integer+digit = read . return <$> cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit"++-- | Extracts the first digit and returns it+letter :: Parser r String Char+letter = cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter"++-- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it+word :: Parser r String String+word = some letter ++-- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it+integer :: Parser r String Integer+integer = foldl (\x y -> x*10+y) 0 <$> some digit++-- | Succeeds if the first token matches the given function, without consuming it+peek :: (TokenStream s) => (t -> Bool) -> String -> Parser r (s t) ()+peek c = cParse (c . top) noop
+ src/Text/LParse/TokenStream.hs view
@@ -0,0 +1,62 @@+{-|+Module : Text.LParse.TokenStream+Description : Underlying data structure for sequential parsing+Copyright : (c) Marcus Völker, 2017+License : MIT+Maintainer : marcus.voelker@rwth-aachen.de++This module contains the `TokenStream` class, an abstraction of lists, similar to `Traversable`, but geared for use with LParse+-}+module Text.LParse.TokenStream where ++import Data.Maybe+import Data.Traversable++{-# DEPRECATED skipN "Use sDrop in place of skipN" #-}++-- | `TokenStream` abstracts a list, i.e., something that has a next element to process and a rest afterwards+class (Functor t, Foldable t) => TokenStream t where+ -- | `top` gives the next element to process. Similar to `head`+ top :: t a -> a+ -- | `rest` gives what is left after processing `top`. Similar to `tail`+ rest :: t a -> t a+ -- | `nil` gives the empty `TokenStream`. Similar to `[]`+ nil :: t a+ -- | `cons` prepends an element to the `TokenStream`. Similar to `(:)`+ cons :: a -> t a -> t a++instance TokenStream [] where+ top = head+ rest = tail+ nil = []+ cons = (:)++instance TokenStream Maybe where+ top = fromJust+ rest = const Nothing+ nil = Nothing+ cons a _ = Just a++-- | Deprecated version of `sDrop`+skipN :: (TokenStream s) => Int -> s a -> s a+skipN = sDrop ++-- | `TokenStream` version of `drop` +sDrop :: (TokenStream s) => Int -> s a -> s a+sDrop 0 x = x+sDrop n x = rest $ sDrop (n-1) x++-- | `TokenStream` version of `zip`+sZip :: (TokenStream s) => s a -> s b -> s (a,b)+sZip = sZipWith (,)++-- | `TokenStream` version of `zipWith`+sZipWith :: (TokenStream s) => (a -> b -> c) -> s a -> s b -> s c+sZipWith f l r | null l || null r = nil+ | otherwise = f (top l) (top r) `cons` (sZipWith f (rest l) (rest r))++-- | `TokenStream` version of `filter`+sFilter :: (TokenStream s) => (a -> Bool) -> s a -> s a+sFilter c x | null x = nil+ | c (top x) = (top x) `cons` sFilter c (rest x)+ | otherwise = sFilter c (rest x)
src/Text/LParse/Transformers.hs view
@@ -1,53 +1,54 @@-{-| -Module : Text.LParse.Transformers -Description : Parser transformers for use with LParse -Copyright : (c) Marcus Völker, 2017 -License : MIT -Maintainer : marcus.voelker@rwth-aachen.de - -This module implements LParse's transformers, i.e. functions that build new parsers from other parsers --} -module Text.LParse.Transformers where - -import Control.DoubleContinuations -import Text.LParse.Parser - -import Control.Applicative -import Data.Char - --- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@ -(<<) :: (Monad m) => m a -> m b -> m a -a << b = a >>= ((b >>) . return) - --- | Takes a condition the parser's input has to fulfil in order for the parser to succeed -cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a -cParse c p err = Parser (\s -> if c s then pFunc p s else throw err) - --- | Transforms the input before applying the parser -pParse :: (t -> t) -> Parser r t a -> Parser r t a -pParse f p = Parser (pFunc p . f) - --- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source) --- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@ -sepSome :: Parser r t () -> Parser r t a -> Parser r t [a] -sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p - --- | Same as @sepSome@, but allows empty lists -sepMany :: Parser r t () -> Parser r t a -> Parser r t [a] -sepMany sep p = sepSome sep p <|> return [] - --- | Removes all tokens from the given list from the input -skip :: (Eq t) => [t] -> Parser r [t] a -> Parser r [t] a -skip s = skipBy (not . (`elem` s)) - --- | Same as skip, but with a custom comparator -skipBy :: (t -> Bool) -> Parser r [t] a -> Parser r [t] a -skipBy f = pParse (filter f) - --- | Skips standard whitespace characters from a String input -skipWhitespace :: Parser r String a -> Parser r String a -skipWhitespace = skipBy (not . isSpace) - --- | Replaces the first token by applying the given function -replace :: (t -> t) -> Parser r [t] a -> Parser r [t] a -replace f p = Parser (pFunc p . (\(x:xs) -> f x:xs))+{-|+Module : Text.LParse.Transformers+Description : Parser transformers for use with LParse+Copyright : (c) Marcus Völker, 2017+License : MIT+Maintainer : marcus.voelker@rwth-aachen.de++This module implements LParse's transformers, i.e. functions that build new parsers from other parsers+-}+module Text.LParse.Transformers where ++import Control.DoubleContinuations+import Text.LParse.Parser+import Text.LParse.TokenStream++import Control.Applicative+import Data.Char++-- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@+(<<) :: (Monad m) => m a -> m b -> m a+a << b = a >>= ((b >>) . return)++-- | Takes a condition the parser's input has to fulfil in order for the parser to succeed+cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a+cParse c p err = Parser (\s -> if c s then pFunc p s else throw err)++-- | Transforms the input before applying the parser+pParse :: (t -> t) -> Parser r t a -> Parser r t a+pParse f p = Parser (pFunc p . f)++-- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source)+-- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@+sepSome :: Parser r t () -> Parser r t a -> Parser r t [a]+sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p++-- | Same as @sepSome@, but allows empty lists+sepMany :: Parser r t () -> Parser r t a -> Parser r t [a]+sepMany sep p = sepSome sep p <|> return []++-- | Removes all tokens from the given list from the input+skip :: (Eq t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a+skip s = skipBy (not . (`elem` s))++-- | Same as skip, but with a custom comparator+skipBy :: (TokenStream s) => (t -> Bool) -> Parser r (s t) a -> Parser r (s t) a+skipBy f = pParse (sFilter f)++-- | Skips standard whitespace characters from a String input+skipWhitespace :: Parser r String a -> Parser r String a+skipWhitespace = skipBy (not . isSpace)++-- | Replaces the first token by applying the given function+replace :: (TokenStream s) => (t -> t) -> Parser r (s t) a -> Parser r (s t) a+replace f p = Parser (pFunc p . (\x -> f (top x) `cons` rest x))
test/Test.hs view
@@ -20,6 +20,8 @@ (consume "prefix","prefixed"), (consume "", "foo"), (consume "", ""), + (letter >> eoi, "b"), + (digit >> eoi, "4"), (word >> eoi, "banana") ] @@ -28,6 +30,8 @@ (eoi,"foo"), (consume "prefix", "freepix"), (consume "prefix", ""), + (letter >> eoi, "banana"), + (digit >> eoi, "42"), (word >> eoi, "banana bread") ] @@ -39,6 +43,7 @@ intCases :: [(Parser r String Integer, String, Integer)] intCases = [ (integer,"123 is a nice number",123), + (digit,"123 is a nice number",1), (sum <$> sepMany (consume " ") integer,"1 4 12 61 192",1+4+12+61+192) ]