LParse (empty) → 0.1.1.1
raw patch · 9 files changed
+299/−0 lines, 9 filesdep +basesetup-changed
Dependencies added: base
Files
- LICENSE +21/−0
- LParse.cabal +30/−0
- README.md +4/−0
- Setup.hs +2/−0
- src/Control/DoubleContinuations.hs +48/−0
- src/Text/LParse/Atomics.hs +58/−0
- src/Text/LParse/Parser.hs +76/−0
- src/Text/LParse/Transformers.hs +53/−0
- test/Test.hs +7/−0
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2017 Marcus Völker++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ LParse.cabal view
@@ -0,0 +1,30 @@+name: LParse+version: 0.1.1.1+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+license: MIT+license-file: LICENSE+author: Marcus Völker+maintainer: marcus.voelker@rwth-aachen.de+copyright: (c) 2017 Marcus Völker+category: Parsing+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Control.DoubleContinuations Text.LParse.Atomics, Text.LParse.Parser, Text.LParse.Transformers+ build-depends: base >= 4.7 && < 5+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/MarcusVoelker/LParse++test-suite test-lparse+ type: exitcode-stdio-1.0+ main-is: test/Test.hs+ build-depends: base+ default-language: Haskell2010
+ README.md view
@@ -0,0 +1,4 @@+# λParse+++
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Control/DoubleContinuations.hs view
@@ -0,0 +1,48 @@+{-| +Module : Control.DoubleContinuations +Description : Continuations that can succeed or fail +Copyright : (c) Marcus Völker, 2017 +License : MIT +Maintainer : marcus.voelker@rwth-aachen.de + +This module describes DoubleContinuations, which are Continuations that may have succeeded or failed. +Instead of just taking a single function (a -> r) -> r to execute after the computation has run, +a double continuation takes two functions: one to call in case of success and one to call in case of error +This allows for easy implementation of exception handling and structuring control flow in a pass/fail manner +-} +module Control.DoubleContinuations where + +import Control.Applicative +import Control.Monad +import Data.Either + +-- | The double continuation. Takes two functions, one to invoke if the computation is successful, one if it errors +data DCont r e a = DCont {run :: (a -> r) -> (e -> r) -> r} + +-- | Generates a continuation that always fails. For a continuation that always succeeds, see return +throw :: e -- ^ The error to return + -> DCont r e a +throw x = DCont (\_ g -> g x) + +-- | Binding a Continuation means running it, then feeding the result into f to generate a new continuation, and running that +instance Monad (DCont r e) where + return x = DCont (\f _ -> f x) + c >>= f = DCont (\btr etr -> run c (\x -> run (f x) btr etr) etr) + +-- | via Monad/Functor laws +instance Functor (DCont r e) where + fmap = liftM + +-- | via Monad/Applicative laws +instance Applicative (DCont r e) where + pure = return + f <*> a = f >>= (<$> a) + +-- | An empty alternative just fails with an undefined error. Branching means first trying one, and in case of failure, the other +instance Alternative (DCont r e) where + empty = throw undefined + p1 <|> p2 = DCont (\atr etr -> run p1 atr (\_ -> run p2 atr etr)) + +-- | Convenience function to run a computation and put the result into an Either (with Left being the error and Right being the success) +invoke :: DCont (Either e a) e a -> (Either e a) +invoke c = run c Right Left
+ src/Text/LParse/Atomics.hs view
@@ -0,0 +1,58 @@+{-| +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")
+ src/Text/LParse/Parser.hs view
@@ -0,0 +1,76 @@+{-|+Module : Text.LParse.Parser+Description : Core for LParse+Copyright : (c) Marcus Völker, 2017+License : MIT+Maintainer : marcus.voelker@rwth-aachen.de++This module implements LParse's core: The parser data structure, instances of the important typeclasses and functions to run the parser+-}+module Text.LParse.Parser where++import Control.DoubleContinuations++import Control.Applicative+import Control.Monad+import Control.Arrow+import qualified Control.Category as C+import Data.List++-- | The Parser structure itself wraps a function from a collection of tokens (collectively of type t) to a double continuation giving+-- back a string in case of an error (the error message) and a pair (a,t) in case of a success (the parsing result and rest of the input)+data Parser r t a = Parser {pFunc :: t -> DCont r String (a,t)}++-- | via Monad/Functor laws+instance Functor (Parser r t) where+ fmap = liftM++-- | via Monad/Applicative laws+instance Applicative (Parser r t) where+ pure = return+ f <*> a = f >>= (<$> a)++-- | an empty parser in the sense of Alternative always fails and throws nothing. Branching between parsers means trying both in a row and+-- taking the first one that succeeds+instance Alternative (Parser r t) where+ empty = Parser (const $ throw "Empty Fail")+ p1 <|> p2 = Parser ((<|>) <$> (pFunc p1) <*> (pFunc p2))++-- | returning a value means building a parser that consumes no input and just gives back the value (i.e. always succeeds)+-- the bind operator means using the parser, creating a second parser from the result (with the given function) and then parsing with that.+-- Both parsers successively consume input, i.e. @consume "a" >>= (const $ consume "b")@ will consume the string "ab"+instance Monad (Parser r t) where+ return a = Parser (\s -> return (a,s))+ a >>= f = Parser (pFunc a >=> (\(r, s') -> pFunc (f r) s'))++-- | Defined via Alternative+instance MonadPlus (Parser r t) where+ mzero = empty+ mplus = (<|>)++-- | The identity parser returns the input. Concatenating two parsers means using the parsing result of the first as tokens for the second+instance C.Category (Parser r) where+ id = Parser (\s -> return (s,s))+ (.) b a = Parser (\s -> DCont (\btr etr -> run (pFunc a s) (\(x,r) -> run (pFunc b x) (\(y,_) -> btr (y,r)) etr) etr))++-- | Lifting a function to an arrow applies the function to the input. (***) executes two parsers in parallel, giving both results as a pair +-- (but only if both succeed)+instance Arrow (Parser r) where+ arr f = Parser (\s -> return (f s, undefined))+ (***) p1 p2 = Parser (\(a,b) -> DCont (\btr etr -> run (pFunc p1 a) (\(a',ra) -> run (pFunc p2 b) (\(b',rb) -> btr ((a',b'),(ra,rb))) etr) etr))++-- | Runs the parser on the tokens, using two functions to run the contained continuation+parse :: Parser r t a -> t -> (a -> r) -> (String -> r) -> r+parse p s = run (pFunc p s) . (. fst)++-- | Same as @parse@, but giving back the results via @Either@+doParse :: Parser (Either String a) t a -> t -> Either String a+doParse p s = invoke (fst <$> pFunc p s)++-- | Runs the parser and prints the results+debugParse :: (Show a) => Parser (IO ()) t a -> t -> IO ()+debugParse p s = debugParse' p s (putStr . (\x -> show x ++ "\n"))++-- | Runs the parser and prints the results via a custom printing function+debugParse' :: Parser (IO ()) t a -> t -> (a -> IO()) -> IO ()+debugParse' p s a = run (pFunc p s) (a . fst) (\e -> putStr ("Error: "++ e ++ "\n"))
+ src/Text/LParse/Transformers.hs view
@@ -0,0 +1,53 @@+{-| +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))
+ test/Test.hs view
@@ -0,0 +1,7 @@+module Main where + +import System.Exit (exitSuccess) + +main = do + putStrLn "Hier entsteht eine neue Testpräsenz" + exitSuccess