parcom-lib (empty) → 0.1.0.0
raw patch · 11 files changed
+402/−0 lines, 11 filesdep +basedep +bytestringdep +containerssetup-changed
Dependencies added: base, bytestring, containers, word8
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- Text/Parcom/ByteString.hs +6/−0
- Text/Parcom/ByteString/Strict.hs +14/−0
- Text/Parcom/Combinators.hs +78/−0
- Text/Parcom/Core.hs +161/−0
- Text/Parcom/Internal.hs +8/−0
- Text/Parcom/Prim.hs +42/−0
- Text/Parcom/Stream.hs +23/−0
- Text/Parcom/Word8.hs +10/−0
- parcom-lib.cabal +28/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2013, Tobias Dammers++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Tobias Dammers nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ Text/Parcom/ByteString.hs view
@@ -0,0 +1,6 @@+module Text.Parcom.ByteString+( module Text.Parcom.ByteString.Strict+)+where++import Text.Parcom.ByteString.Strict
+ Text/Parcom/ByteString/Strict.hs view
@@ -0,0 +1,14 @@+{-#LANGUAGE MultiParamTypeClasses #-}+module Text.Parcom.ByteString.Strict+where++import Prelude hiding (head, null, tail)+import Data.ByteString+import Data.Word8+import Text.Parcom.Stream+import Text.Parcom.Word8++instance Stream ByteString Word8 where+ peek = head+ atEnd = null+ consume = tail
+ Text/Parcom/Combinators.hs view
@@ -0,0 +1,78 @@+module Text.Parcom.Combinators+( choice, namedChoice+, before, between+, many, many1, manySepBy+, times+, skip+)+where++import Text.Parcom.Core+import Text.Parcom.Internal++-- | Walk a list of options, return the first one that succeeds.+choice :: (Stream s t) => [Parcom s t a] -> Parcom s t a+choice xs = foldl (<|>) empty xs <?> "I tried to make a choice, but couldn't"++-- | Like @choice@, but each choice tagged with a human-readable name for+-- better error reporting.+namedChoice :: (Stream s t) => [(String, Parcom s t a)] -> Parcom s t a+namedChoice xs = choice (map snd xs) <?> (formatOptionList . map fst) xs++-- | Match two consecutive parser, return the first parser's result iff both+-- succeed.+before :: (Stream s t) => Parcom s t a -> Parcom s t b -> Parcom s t a+before p q = do { v <- p; q; return v }++-- | Match three consecutive parsers, return the middle parser's result iff+-- all three match. Parsers are given in the order inner, left, right.+between :: (Stream s t) => Parcom s t a -> Parcom s t l -> Parcom s t r -> Parcom s t a+between p l r = do { l; v <- p; r; return v }++-- | Match zero or more occurrences of a parser+many :: (Stream s t) => Parcom s t a -> Parcom s t [a]+many p =+ handle p f m+ where+ f e = return []+ m x = do+ xs <- many p+ return (x:xs)++-- | Match one or more occurrences of a parser+many1 :: (Stream s t) => Parcom s t a -> Parcom s t [a]+many1 p = do+ xs <- many p+ if null xs+ then fail "Expected at least one item"+ else return xs++-- | Given an item parser and a separator parser, keep parsing until the+-- separator or the item fails.+manySepBy :: (Stream s t) => Parcom s t a -> Parcom s t b -> Parcom s t [a]+manySepBy p s = go+ where+ go = do+ -- try an item+ handle p f m+ where+ -- item does not parse: return empty list (no matches)+ f e = return []+ -- item does parse: keep the item, try the separator.+ m x = handle s+ -- separator does not parse: return the one item we have+ (\e -> return [x])+ -- separator does parse: recurse and prepend our item+ (\_ -> go >>= \xs -> return (x:xs))++-- | Run the given parser n times, returning all the results as a list.+times :: (Stream s t) => Int -> Parcom s t a -> Parcom s t [a]+times 0 p = return []+times n p = do+ x <- p+ xs <- times (n - 1) p+ return (x:xs)++-- | Ignore the result of a parser.+skip :: Parcom s t a -> Parcom s t ()+skip p = p >> return ()
+ Text/Parcom/Core.hs view
@@ -0,0 +1,161 @@+{-#LANGUAGE MultiParamTypeClasses #-}+module Text.Parcom.Core+( ParcomError (..)+, SourcePosition (..)+, Parcom+, parse+, peek, next, atEnd+, try, handle+, notFollowedBy+, (<?>), (<|>), empty+, Stream, Token+)+where++import qualified Text.Parcom.Stream as Stream+import Text.Parcom.Stream (Stream, Token)+import Control.Monad (liftM, ap)+import Control.Applicative++data SourcePosition =+ SourcePosition+ { posFileName :: String+ , posLine :: Int+ , posColumn :: Int+ }+ deriving (Show)++data ParcomError =+ ParcomError+ { peErrorDescription :: String+ , peSourcePosition :: SourcePosition+ }+ deriving (Show)++data ParcomState s =+ ParcomState+ { psSourcePosition :: SourcePosition+ , psStream :: s+ }++newtype Parcom s t a = Parcom { runParcom :: ParcomState s -> (Either ParcomError a, ParcomState s) }++instance Monad (Parcom s t) where+ return x = Parcom (\s -> (Right x, s))+ fail err = Parcom (\s -> (Left $ ParcomError err (psSourcePosition s), s))+ m >>= f = Parcom $ \s ->+ let (a, s') = runParcom m s+ in case a of+ Left e -> (Left e, s')+ Right x -> runParcom (f x) s'++instance Functor (Parcom s t) where+ fmap f xs = xs >>= return . f++instance Applicative (Parcom s t) where+ pure = return+ (<*>) = ap++instance Alternative (Parcom s t) where+ (<|>) = alt+ empty = fail "empty"++runParser :: (Stream s t, Token t) => Parcom s t a -> String -> s -> (Either ParcomError a, ParcomState s)+runParser p fn str =+ runParcom p state+ where+ state =+ ParcomState+ { psSourcePosition = SourcePosition fn 1 1+ , psStream = str }++parse :: (Stream s t, Token t) => Parcom s t a -> String -> s -> Either ParcomError a+parse p fn str = fst $ runParser p fn str++getState :: Parcom s t (ParcomState s)+getState = Parcom $ \s -> (Right s, s)++useState :: (ParcomState s -> a) -> Parcom s t a+useState f = Parcom $ \s -> (Right $ f s, s)++setState :: ParcomState s -> Parcom s t ()+setState s = Parcom $ \_ -> (Right (), s)++modifyState :: (ParcomState s -> ParcomState s) -> Parcom s t ()+modifyState f = Parcom $ \s -> (Right (), f s)++handle :: Parcom s t a -> (ParcomError -> Parcom s t b) -> (a -> Parcom s t b) -> Parcom s t b+handle p f t = Parcom $ \s ->+ let (r', s') = runParcom p s+ in case r' of+ -- parse failed: run the error handler+ Left e -> runParcom (f e) s'+ -- parse succeeded: run the success handler+ Right x -> runParcom (t x) s'+++-- | Backtracking modifier; restores the parser state to the previous situation+-- if the wrapped parser fails.+try :: Parcom s t a -> Parcom s t a+try p = Parcom $ \s ->+ let (r', s') = runParcom p s+ in case r' of+ -- parse failed: return the error and restore the old state+ Left e -> (Left e, s)+ -- parse succeeded: return the result and the new state+ Right x -> (Right x, s')++-- | Return the result of the first parser that succeeds.+alt :: Parcom s t a -> Parcom s t a -> Parcom s t a+alt a b = Parcom $ \s ->+ let (r', s') = runParcom a s+ in case r' of+ Left _ -> runParcom b s+ Right x -> (Right x, s')++-- | Succeeds iff the given parser fails+notFollowedBy :: (Stream s t) => Parcom s t a -> Parcom s t ()+notFollowedBy p = Parcom $ \s ->+ let (r', s') = runParcom p s+ in case r' of+ Left _ -> (Right (), s)+ Right x -> runParcom (fail "something followed that shouldn't") s+ +-- | Gets the next token from the stream without consuming it.+-- Fails at end-of-input.+peek :: (Stream s t) => Parcom s t t+peek = do+ str <- psStream `liftM` getState+ if Stream.atEnd str+ then fail "Unexpected end of input"+ else return (Stream.peek str)++-- | Checks whether end-of-input has been reached.+atEnd :: (Stream s t) => Parcom s t Bool+atEnd = useState (Stream.atEnd . psStream)++nextLine :: SourcePosition -> SourcePosition+nextLine s =+ s { posLine = posLine s + 1, posColumn = 1 }++nextColumn :: SourcePosition -> SourcePosition+nextColumn s =+ s { posColumn = posColumn s + 1 }++-- | Gets the next token from the stream and consumes it.+-- Fails at end-of-input.+next :: (Stream s t, Token t) => Parcom s t t+next = do+ str <- psStream `liftM` getState+ if Stream.atEnd str+ then fail "Unexpected end of input"+ else do+ let (t, str') = Stream.pop str+ modifyState $ \state -> state { psStream = str' }+ if Stream.isLineDelimiter t+ then modifyState $ \s -> s { psSourcePosition = nextLine (psSourcePosition s) }+ else modifyState $ \s -> s { psSourcePosition = nextColumn (psSourcePosition s) }+ return t++(<?>) :: (Stream s t) => Parcom s t a -> String -> Parcom s t a+p <?> expected = p <|> fail ("Expected " ++ expected)
+ Text/Parcom/Internal.hs view
@@ -0,0 +1,8 @@+module Text.Parcom.Internal+where++formatOptionList :: [String] -> String+formatOptionList (x:y:[]) = x ++ " or " ++ y+formatOptionList (x:[]) = x+formatOptionList [] = ""+formatOptionList (x:xs) = x ++ ", " ++ formatOptionList xs
+ Text/Parcom/Prim.hs view
@@ -0,0 +1,42 @@+module Text.Parcom.Prim+( anyToken, oneOf, eof+, satisfy+, token, tokens+)+where++import Text.Parcom.Core+import Text.Parcom.Internal++-- | Gets the next token from the stream+anyToken :: (Stream s t, Token t) => Parcom s t t+anyToken = next++-- | Succeeds iff end-of-input has been reached+eof :: (Stream s t, Token t) => Parcom s t ()+eof = notFollowedBy anyToken <?> "end of input"++-- | Matches one token against a list of possible tokens; returns the+-- matching token.+oneOf :: (Stream s t, Token t, Show t, Eq t) => [t] -> Parcom s t t+oneOf xs = satisfy (`elem` xs) <?> (formatOptionList . map show) xs++-- | Succeeds if the given predicate is met for the next token.+satisfy :: (Stream s t, Token t) => (t -> Bool) -> Parcom s t t+satisfy p = do+ c <- peek+ if p c+ then next+ else fail "Predicate not met"++-- | Exactly match one particular token+token :: (Stream s t, Token t, Show t, Eq t) => t -> Parcom s t t+token t = satisfy (== t) <?> show t++-- | Match a series of tokens exactly+tokens :: (Stream s t, Token t, Eq t, Show t) => [t] -> Parcom s t [t]+tokens [] = return []+tokens (x:xs) = do+ c <- token x+ cs <- tokens xs+ return (c:cs)
+ Text/Parcom/Stream.hs view
@@ -0,0 +1,23 @@+{-#LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}+module Text.Parcom.Stream+where++class Stream s t | s -> t where+ peek :: s -> t+ atEnd :: s -> Bool+ consume :: s -> s+ pop :: s -> (t, s)+ consume = snd . pop+ pop s = (peek s, consume s)++instance Stream [a] a where+ peek = head+ atEnd = null+ consume = tail++class Token t where+ isLineDelimiter :: t -> Bool++instance Token Char where+ isLineDelimiter '\n' = True+ isLineDelimiter _ = False
+ Text/Parcom/Word8.hs view
@@ -0,0 +1,10 @@+module Text.Parcom.Word8+(+)+where++import Text.Parcom.Stream+import Data.Word8++instance Token Word8 where+ isLineDelimiter = (== 13)
+ parcom-lib.cabal view
@@ -0,0 +1,28 @@+-- Initial parcom-lib.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: parcom-lib+version: 0.1.0.0+synopsis: A simple parser-combinator library, a bit like Parsec but without the frills+description: Parcom provides parser combinator functionality in a string-type-agnostic way;+ it supports strict ByteStrings (with Word8 tokens) and any list type (with+ the element type as the token type) out-of-the-box, including plain old String.+ Any other stream-of-tokens type can be hooked into the library; unlike Parsec,+ none of the built-in parsers assumes char-like tokens.+homepage: https://bitbucket.org/tdammers/parcom-lib+license: BSD3+license-file: LICENSE+author: Tobias Dammers+maintainer: tdammers@gmail.com+-- copyright: +category: Text+build-type: Simple+cabal-version: >=1.8++library+ exposed-modules: Text.Parcom.Stream, Text.Parcom.Core, Text.Parcom.Word8, Text.Parcom.ByteString, Text.Parcom.Internal, Text.Parcom.Combinators, Text.Parcom.Prim, Text.Parcom.ByteString.Strict+ -- other-modules: + build-depends: base ==4.5.*+ , containers ==0.4.*+ , bytestring ==0.9.*+ , word8