packages feed

abnf 0.2.0.0 → 0.3.0.0

raw patch · 10 files changed

+225/−28 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Text.ABNF.ABNF.Types: type Identifier = Text
- Text.ABNF.Document.Types: instance GHC.Classes.Eq Text.ABNF.Document.Types.Content
- Text.ABNF.Document.Types: instance GHC.Classes.Eq Text.ABNF.Document.Types.Document
- Text.ABNF.Document.Types: instance GHC.Show.Show Text.ABNF.Document.Types.Content
- Text.ABNF.Document.Types: instance GHC.Show.Show Text.ABNF.Document.Types.Document
- Text.ABNF.Document.Types: type Identifier = Text
+ Text.ABNF.ABNF: Rule :: Text -> DefinedAs -> SumSpec -> Rule
+ Text.ABNF.ABNF: canonicalizeRules :: Text -> [Rule] -> Maybe Rule
+ Text.ABNF.ABNF: data Rule
+ Text.ABNF.ABNF: parseABNF :: String -> Text -> Either (ParseError Char Dec) [Rule]
+ Text.ABNF.ABNF: rulelist :: Parser [Rule]
+ Text.ABNF.ABNF.Parser: parseABNF :: String -> Text -> Either (ParseError Char Dec) [Rule]
+ Text.ABNF.Document: Document :: Text -> [Content a] -> Document a
+ Text.ABNF.Document: NonTerminal :: (Document a) -> Content a
+ Text.ABNF.Document: Terminal :: a -> Content a
+ Text.ABNF.Document: data Content a
+ Text.ABNF.Document: data Document a
+ Text.ABNF.Document: filterDocument :: (Document a -> Bool) -> Document a -> Maybe (Document a)
+ Text.ABNF.Document: generateParser :: Rule -> Parser (Document Text)
+ Text.ABNF.Document: parseDocument :: Rule -> Text -> Either String (Document Text)
+ Text.ABNF.Document: squashContent :: Monoid a => [Content a] -> a
+ Text.ABNF.Document: squashDocument :: Monoid a => Document a -> Document a
+ Text.ABNF.Document: squashDocumentOn :: Monoid a => (Document a -> Bool) -> Document a -> Document a
+ Text.ABNF.Document.Operations: filterDocument :: (Document a -> Bool) -> Document a -> Maybe (Document a)
+ Text.ABNF.Document.Operations: squashContent :: Monoid a => [Content a] -> a
+ Text.ABNF.Document.Operations: squashDocument :: Monoid a => Document a -> Document a
+ Text.ABNF.Document.Operations: squashDocumentOn :: Monoid a => (Document a -> Bool) -> Document a -> Document a
+ Text.ABNF.Document.Parser: parseDocument :: Rule -> Text -> Either String (Document Text)
+ Text.ABNF.Document.Types: instance GHC.Base.Applicative Text.ABNF.Document.Types.Content
+ Text.ABNF.Document.Types: instance GHC.Base.Functor Text.ABNF.Document.Types.Content
+ Text.ABNF.Document.Types: instance GHC.Base.Functor Text.ABNF.Document.Types.Document
+ Text.ABNF.Document.Types: instance GHC.Base.Monad Text.ABNF.Document.Types.Content
+ Text.ABNF.Document.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Text.ABNF.Document.Types.Content a)
+ Text.ABNF.Document.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Text.ABNF.Document.Types.Document a)
+ Text.ABNF.Document.Types: instance GHC.Show.Show a => GHC.Show.Show (Text.ABNF.Document.Types.Content a)
+ Text.ABNF.Document.Types: instance GHC.Show.Show a => GHC.Show.Show (Text.ABNF.Document.Types.Document a)
- Text.ABNF.ABNF.Canonicalizer: canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule
+ Text.ABNF.ABNF.Canonicalizer: canonicalizeRules :: Text -> [Rule] -> Maybe Rule
- Text.ABNF.ABNF.Parser: identifier :: Parser Identifier
+ Text.ABNF.ABNF.Parser: identifier :: Parser Text
- Text.ABNF.ABNF.Types: Rule :: Identifier -> DefinedAs -> SumSpec -> Rule
+ Text.ABNF.ABNF.Types: Rule :: Text -> DefinedAs -> SumSpec -> Rule
- Text.ABNF.ABNF.Types: RuleElement' :: Identifier -> Element
+ Text.ABNF.ABNF.Types: RuleElement' :: Text -> Element
- Text.ABNF.Document.Parser: generateParser :: Rule -> Parser Document
+ Text.ABNF.Document.Parser: generateParser :: Rule -> Parser (Document Text)
- Text.ABNF.Document.Parser: parseElem :: Element -> Parser [Content]
+ Text.ABNF.Document.Parser: parseElem :: Element -> Parser [Content Text]
- Text.ABNF.Document.Parser: parseLiteral :: Literal -> Parser [Content]
+ Text.ABNF.Document.Parser: parseLiteral :: Literal -> Parser [Content Text]
- Text.ABNF.Document.Parser: parseProdSpec :: ProductSpec -> Parser [Content]
+ Text.ABNF.Document.Parser: parseProdSpec :: ProductSpec -> Parser [Content Text]
- Text.ABNF.Document.Parser: parseRepetition :: Repetition -> Parser [Content]
+ Text.ABNF.Document.Parser: parseRepetition :: Repetition -> Parser [Content Text]
- Text.ABNF.Document.Parser: parseRule :: Rule -> Parser Document
+ Text.ABNF.Document.Parser: parseRule :: Rule -> Parser (Document Text)
- Text.ABNF.Document.Parser: parseSumSpec :: SumSpec -> Parser [Content]
+ Text.ABNF.Document.Parser: parseSumSpec :: SumSpec -> Parser [Content Text]
- Text.ABNF.Document.Types: Document :: Identifier -> [Content] -> Document
+ Text.ABNF.Document.Types: Document :: Text -> [Content a] -> Document a
- Text.ABNF.Document.Types: NonTerminal :: Document -> Content
+ Text.ABNF.Document.Types: NonTerminal :: (Document a) -> Content a
- Text.ABNF.Document.Types: Terminal :: Text -> Content
+ Text.ABNF.Document.Types: Terminal :: a -> Content a
- Text.ABNF.Document.Types: data Content
+ Text.ABNF.Document.Types: data Content a
- Text.ABNF.Document.Types: data Document
+ Text.ABNF.Document.Types: data Document a

Files

abnf.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/  name:                abnf-version:             0.2.0.0+version:             0.3.0.0 synopsis:            Parse ABNF and generate parsers for the specified document description:         You can use this library to parse an ABNF document and                      generate a parser from that ABNF to read a document@@ -15,6 +15,7 @@ homepage:            https://github.com/Xandaros/abnf.git category:            Text build-type:          Simple+stability:           alpha -- extra-source-files:   cabal-version:       >=1.10 @@ -23,13 +24,15 @@   location:            https://github.com/Xandaros/abnf.git  library-  exposed-modules:     Text.ABNF.ABNF,+  exposed-modules:     Text.ABNF,+                       Text.ABNF.ABNF,                        Text.ABNF.ABNF.Parser,                        Text.ABNF.ABNF.Types,                        Text.ABNF.ABNF.Canonicalizer,                        Text.ABNF.Document,                        Text.ABNF.Document.Parser,                        Text.ABNF.Document.Types,+                       Text.ABNF.Document.Operations,                        Text.ABNF.PrettyPrinter   ghc-options:         -W   -- other-modules:       
+ src/Text/ABNF.hs view
@@ -0,0 +1,17 @@+{-|+Module      : Text.ABNF+Description : Top-level module+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : non-portable+-}++module Text.ABNF+    ( module Text.ABNF.ABNF+    , module Text.ABNF.Document+    ) where++import Text.ABNF.ABNF+import Text.ABNF.Document
src/Text/ABNF/ABNF.hs view
@@ -1,1 +1,27 @@-module Text.ABNF.ABNF where+{-|+Module      : Text.ABNF.ABNF+Description : ABNF+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : non-portable+-}++module Text.ABNF.ABNF+    (+    -- * ABNF types+    -- | Re-exported from "Text.ABNF.ABNF.Types"+      Rule(..)+    -- * Parsing ABNF Rules+    -- | Re-exported from "Text.ABNF.ABNF.Parser"+    , rulelist+    , parseABNF+    -- * Canonicalizing ABNF Rules+    -- | Re-exported from "Text.ABNF.ABNF.Canonicalizer"+    , canonicalizeRules+    ) where++import Text.ABNF.ABNF.Types (Rule(..))+import Text.ABNF.ABNF.Parser (rulelist, parseABNF)+import Text.ABNF.ABNF.Canonicalizer (canonicalizeRules)
src/Text/ABNF/ABNF/Canonicalizer.hs view
@@ -18,9 +18,14 @@  import Text.ABNF.ABNF.Types -type RuleMap = Map.Map Identifier Rule+type RuleMap = Map.Map Text.Text Rule -canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule+-- | Canonicalize a list of 'Rule's, leaving only a single 'Rule'.+canonicalizeRules :: Text.Text  -- ^ The main, or top-level, 'Rule'+                  -> [Rule]     -- ^ List of 'Rule's to canonicalize+                  -> Maybe Rule -- ^ The operation may fail for a number of+                                -- reasons, for example, because the main 'Rule'+                                -- does not exist canonicalizeRules mainRuleIdent rules = do     let (defs, adds) = partition isAdd rules         ruleMap  = foldr (\rule@(Rule ident _ _) curMap ->
src/Text/ABNF/ABNF/Parser.hs view
@@ -25,12 +25,22 @@  import Text.ABNF.ABNF.Types -identifier :: Parser Identifier+identifier :: Parser Text.Text identifier = Text.pack <$> do     firstChar <- letterChar     otherChars <- many $ alphaNumChar <|> char '-'     pure (firstChar:otherChars) +-- | Convencience function to directly parse a list of rules.+-- This is equivalent to+--+-- @+-- 'parse' 'rulelist'+-- @+parseABNF :: String -> Text.Text -> Either (ParseError Char Dec) [Rule]+parseABNF = parse rulelist++-- | The main parser, which parses a list of 'Rule's. rulelist :: Parser [Rule] rulelist = catMaybes <$> (some $ Just <$> rule <|> (many wsp *> c_nl *> pure Nothing)) 
src/Text/ABNF/ABNF/Types.hs view
@@ -14,9 +14,13 @@  import qualified Data.Text as Text -type Identifier = Text.Text--data Rule = Rule Identifier DefinedAs SumSpec+-- | A 'Rule' represents a single entry in your ABNF. It could, for example,+-- look like this:+--+-- @+-- CRLF = %x0D.0A+-- @+data Rule = Rule Text.Text DefinedAs SumSpec     deriving (Show, Eq)  data SumSpec = SumSpec [ProductSpec]@@ -31,7 +35,7 @@ data Repeat = Repeat Int (Maybe Int)     deriving (Show, Eq) -data Element = RuleElement' Identifier+data Element = RuleElement' Text.Text              | RuleElement Rule              | GroupElement Group              | OptionElement Group
src/Text/ABNF/Document.hs view
@@ -1,1 +1,48 @@-module Text.ABNF.Document where+{-|+Module      : Text.ABNF.Document+Description : Documents according to an ABNF definition+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : non-portable+-}++module Text.ABNF.Document+    (+    -- * Document types+    -- | Re-exported from "Text.ABNF.Document.Types"+      Document(..)+    , Content(..)+    -- * Reducing documents+    -- | Re-exported from "Text.ABNF.Document.Operations"+    --+    -- In most cases, you don't want to work with the full tree of a 'Document'.+    -- You can use these cases to 'filterDocument' away any branches you do not+    -- need and 'squashDocumentOn' those, where you don't need it as+    -- fine-grained.+    --+    -- This is incredibly useful if you have rules that parse single characters.+    , filterDocument+    , squashDocument+    , squashDocumentOn+    , squashContent+    -- * Parsing documents+    -- | Re-exported from "Text.ABNF.Document.Parser"+    , generateParser+    , parseDocument+    ) where++import Text.ABNF.Document.Types ( Document(..)+                                , Content(..)+                                )++import Text.ABNF.Document.Operations ( filterDocument+                                     , squashDocument+                                     , squashDocumentOn+                                     , squashContent+                                     )++import Text.ABNF.Document.Parser ( generateParser+                                 , parseDocument+                                 )
+ src/Text/ABNF/Document/Operations.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-|+Module      : Text.ABNF.Document.Operations+Description : Some operations on documents+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : ScopedTypeVariables+-}++module Text.ABNF.Document.Operations where++import Data.Maybe (catMaybes)+import Data.Monoid ((<>))++import Text.ABNF.Document.Types++-- | Filter documents according to some predicate.+-- Similar to 'filter' in the Prelude.+filterDocument :: forall a. (Document a -> Bool) -- ^ Predicate to check+               -> Document a                     -- ^ Document to filter+               -> Maybe (Document a)             -- ^ Returns 'Nothing' if the+                                                 --   predicate fails, cascades+                                                 --   otherwise+filterDocument pred doc@(Document ident conts) | pred doc = Just . Document ident $ (catMaybes . fmap filterNT $ conts)+                                               | otherwise = Nothing+    where+        filterNT :: Content a -> Maybe (Content a)+        filterNT a@(Terminal _) = Just a+        filterNT (NonTerminal doc) | pred doc = NonTerminal <$> filterDocument pred doc+                                   | otherwise = Nothing++-- | Squash all contents of a 'Document' into a single 'Terminal'+squashDocument :: Monoid a => Document a -> Document a+squashDocument (Document ident conts) = Document ident [Terminal $ squashContent conts]++-- | Squash all contents of a 'Document' which matches the predicate+-- See also 'squashDocument'+squashDocumentOn :: forall a. Monoid a => (Document a -> Bool) -> Document a -> Document a+squashDocumentOn pred doc@(Document ident conts) | pred doc = squashDocument doc+                                                 | otherwise = Document ident (squashNT <$> conts)+    where+        squashNT :: Content a -> Content a+        squashNT (Terminal a) = Terminal a+        squashNT (NonTerminal doc) = NonTerminal $ squashDocumentOn pred doc++-- | Squash all contents using the 'Monoid' instance of @a@, cascading into+-- 'NonTerminal's.+squashContent :: Monoid a => [Content a] -> a+squashContent [] = mempty+squashContent ((Terminal a):xs) = a <> squashContent xs+squashContent ((NonTerminal (Document _ conts)):xs) = squashContent conts <> squashContent xs
src/Text/ABNF/Document/Parser.hs view
@@ -20,26 +20,40 @@ import Data.Foldable (asum) import Data.Monoid ((<>)) -import qualified Data.Text as Text+import qualified Data.Text as T import Data.Attoparsec.Text  import Text.ABNF.ABNF.Types import Text.ABNF.Document.Types -generateParser :: Rule -> Parser Document+-- | Convenience function to directly parse a 'Document'+parseDocument :: Rule                            -- ^ 'Rule' to parse against+              -> T.Text                          -- ^ 'Text' to parse+              -> Either String (Document T.Text) -- ^ Return a 'String'+                                                 -- describing the error or the+                                                 -- successfully parsed+                                                 -- 'Document'.+                                                 --+                                                 -- The format of the 'String'+                                                 -- is as returned by+                                                 -- <https://hackage.haskell.org/package/attoparsec attoparsec>.+parseDocument = parseOnly . generateParser++-- | Generate an <https://hackage.haskell.org/package/attoparsec attoparsec> parser+generateParser :: Rule -> Parser (Document T.Text) generateParser = parseRule -parseRule :: Rule -> Parser Document+parseRule :: Rule -> Parser (Document T.Text) parseRule (Rule ident _ spec) = Document ident <$> parseSumSpec spec <?> "Rule" -parseSumSpec :: SumSpec -> Parser [Content]+parseSumSpec :: SumSpec -> Parser [Content T.Text] parseSumSpec (SumSpec prodspecs) = asum (map parseProdSpec prodspecs) <?> "Sum" -parseProdSpec :: ProductSpec -> Parser [Content]+parseProdSpec :: ProductSpec -> Parser [Content T.Text] parseProdSpec (ProductSpec reps) =     join <$> (sequence $ map parseRepetition reps) <?> "Product" -parseRepetition :: Repetition -> Parser [Content]+parseRepetition :: Repetition -> Parser [Content T.Text] -- any number of times parseRepetition (Repetition (Repeat 0 Nothing) elem) =     join <$> (many $ parseElem elem)@@ -61,20 +75,20 @@     liftA2 (++) (parseElem elem)                 (parseRepetition (Repetition (Repeat (n-1) x) elem)) -parseElem :: Element -> Parser [Content]+parseElem :: Element -> Parser [Content T.Text] parseElem (RuleElement rule) = toList . NonTerminal <$> parseRule rule <?> "Rule element"-parseElem (RuleElement' ruleName) = fail . Text.unpack $ "Unknown rule: " <> ruleName+parseElem (RuleElement' ruleName) = fail . T.unpack $ "Unknown rule: " <> ruleName parseElem (GroupElement (Group spec)) = parseSumSpec spec <?> "Group element" parseElem (OptionElement (Group spec)) = parseSumSpec spec <|> pure [] <?> "Optional element" parseElem (LiteralElement lit) = parseLiteral lit <?> "Literal element" -parseLiteral :: Literal -> Parser [Content]+parseLiteral :: Literal -> Parser [Content T.Text] parseLiteral (CharLit lit) = toList . Terminal <$> asciiCI lit <?> "String literal" parseLiteral (NumLit lit) = toList . Terminal <$> parseNumLit lit -parseNumLit :: NumLit -> Parser Text.Text-parseNumLit (IntLit ints) = (Text.pack <$> (sequence (char . chr <$> ints)) <?> "Int-defined character")-parseNumLit (RangeLit x1 x2) = Text.pack . toList <$> (oneOf $ chr <$> [x1..x2]) <?> "Range literal"+parseNumLit :: NumLit -> Parser T.Text+parseNumLit (IntLit ints) = (T.pack <$> (sequence (char . chr <$> ints)) <?> "Int-defined character")+parseNumLit (RangeLit x1 x2) = T.pack . toList <$> (oneOf $ chr <$> [x1..x2]) <?> "Range literal"  toList :: a -> [a] toList = pure
src/Text/ABNF/Document/Types.hs view
@@ -11,11 +11,29 @@  import qualified Data.Text as Text -type Identifier = Text.Text--data Document = Document Identifier [Content]+-- | A 'Document' represents a tree of the parsed input. Directly after parsing,+-- every 'Rule' that was applied, gets a node in the tree.+--+-- You can use 'Text.ABNF.Document.filterDocument' and+-- 'Text.ABNF.Document.squashDocumentOn' to reduce the tree into a more+-- managable shape.+data Document a = Document Text.Text [Content a]     deriving (Show, Eq) -data Content = Terminal Text.Text-             | NonTerminal Document+data Content a = Terminal a+               | NonTerminal (Document a)              deriving (Show, Eq)++instance Functor Document where+    fmap f (Document ident conts) = Document ident $ fmap (fmap f) conts++instance Functor Content where+    fmap f (Terminal a) = Terminal $ f a+    fmap f (NonTerminal doc) = NonTerminal $ fmap f doc++instance Applicative Content++instance Monad Content where+    return = Terminal+    Terminal a >>= f = f a+    NonTerminal (Document ident cs) >>= f =  NonTerminal (Document ident (map (>>= f) cs))