packages feed

abnf 0.1.0.0 → 0.2.0.0

raw patch · 14 files changed

+419/−392 lines, 14 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Text.ABNF.Canonicalizer: canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule
- Text.ABNF.Document: generateParser :: Rule -> Parser Document
- Text.ABNF.Document: oneOf :: String -> Parser Char
- Text.ABNF.Document: parseElem :: Element -> Parser [Content]
- Text.ABNF.Document: parseLiteral :: Literal -> Parser [Content]
- Text.ABNF.Document: parseNumLit :: NumLit -> Parser Text
- Text.ABNF.Document: parseProdSpec :: ProductSpec -> Parser [Content]
- Text.ABNF.Document: parseRepetition :: Repetition -> Parser [Content]
- Text.ABNF.Document: parseRule :: Rule -> Parser Document
- Text.ABNF.Document: parseSumSpec :: SumSpec -> Parser [Content]
- Text.ABNF.Document: toList :: a -> [a]
- Text.ABNF.Parser: alternation :: Parser SumSpec
- Text.ABNF.Parser: bin_val :: Parser NumLit
- Text.ABNF.Parser: c_nl :: Parser String
- Text.ABNF.Parser: c_wsp :: Parser String
- Text.ABNF.Parser: char_val :: Parser Text
- Text.ABNF.Parser: comment :: Parser String
- Text.ABNF.Parser: concatenation :: Parser ProductSpec
- Text.ABNF.Parser: dec_val :: Parser NumLit
- Text.ABNF.Parser: defined_as :: Parser DefinedAs
- Text.ABNF.Parser: element :: Parser Element
- Text.ABNF.Parser: elements :: Parser SumSpec
- Text.ABNF.Parser: group :: Parser Group
- Text.ABNF.Parser: hex_val :: Parser NumLit
- Text.ABNF.Parser: identifier :: Parser Identifier
- Text.ABNF.Parser: literal :: Parser Literal
- Text.ABNF.Parser: num_val :: Parser NumLit
- Text.ABNF.Parser: num_val' :: Char -> Parser Int -> Parser NumLit
- Text.ABNF.Parser: option_ :: Parser Group
- Text.ABNF.Parser: prose_val :: Parser Text
- Text.ABNF.Parser: repeat :: Parser Repeat
- Text.ABNF.Parser: repetition :: Parser Repetition
- Text.ABNF.Parser: rule :: Parser Rule
- Text.ABNF.Parser: rulelist :: Parser [Rule]
- Text.ABNF.Parser: vchar :: Parser Char
- Text.ABNF.Parser: wsp :: Parser Char
- Text.ABNF.Parser.Types: Adds :: DefinedAs
- Text.ABNF.Parser.Types: CharLit :: Text -> Literal
- Text.ABNF.Parser.Types: Equals :: DefinedAs
- Text.ABNF.Parser.Types: Group :: SumSpec -> Group
- Text.ABNF.Parser.Types: GroupElement :: Group -> Element
- Text.ABNF.Parser.Types: IntLit :: [Int] -> NumLit
- Text.ABNF.Parser.Types: LiteralElement :: Literal -> Element
- Text.ABNF.Parser.Types: NumLit :: NumLit -> Literal
- Text.ABNF.Parser.Types: OptionElement :: Group -> Element
- Text.ABNF.Parser.Types: ProductSpec :: [Repetition] -> ProductSpec
- Text.ABNF.Parser.Types: RangeLit :: Int -> Int -> NumLit
- Text.ABNF.Parser.Types: Repeat :: Int -> (Maybe Int) -> Repeat
- Text.ABNF.Parser.Types: Repetition :: Repeat -> Element -> Repetition
- Text.ABNF.Parser.Types: Rule :: Identifier -> DefinedAs -> SumSpec -> Rule
- Text.ABNF.Parser.Types: RuleElement :: Rule -> Element
- Text.ABNF.Parser.Types: RuleElement' :: Identifier -> Element
- Text.ABNF.Parser.Types: SumSpec :: [ProductSpec] -> SumSpec
- Text.ABNF.Parser.Types: data DefinedAs
- Text.ABNF.Parser.Types: data Element
- Text.ABNF.Parser.Types: data Group
- Text.ABNF.Parser.Types: data Literal
- Text.ABNF.Parser.Types: data NumLit
- Text.ABNF.Parser.Types: data ProductSpec
- Text.ABNF.Parser.Types: data Repeat
- Text.ABNF.Parser.Types: data Repetition
- Text.ABNF.Parser.Types: data Rule
- Text.ABNF.Parser.Types: data SumSpec
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.DefinedAs
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Element
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Group
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Literal
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.NumLit
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.ProductSpec
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Repeat
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Repetition
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.Rule
- Text.ABNF.Parser.Types: instance GHC.Classes.Eq Text.ABNF.Parser.Types.SumSpec
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.DefinedAs
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Element
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Group
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Literal
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.NumLit
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.ProductSpec
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Repeat
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Repetition
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.Rule
- Text.ABNF.Parser.Types: instance GHC.Show.Show Text.ABNF.Parser.Types.SumSpec
- Text.ABNF.Parser.Types: type Identifier = Text
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.DefinedAs
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Element
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Group
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Literal
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.NumLit
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.ProductSpec
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Repeat
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Repetition
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.Rule
- Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.Parser.Types.SumSpec
+ Text.ABNF.ABNF.Canonicalizer: canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule
+ Text.ABNF.ABNF.Parser: alternation :: Parser SumSpec
+ Text.ABNF.ABNF.Parser: bin_val :: Parser NumLit
+ Text.ABNF.ABNF.Parser: c_nl :: Parser String
+ Text.ABNF.ABNF.Parser: c_wsp :: Parser String
+ Text.ABNF.ABNF.Parser: char_val :: Parser Text
+ Text.ABNF.ABNF.Parser: comment :: Parser String
+ Text.ABNF.ABNF.Parser: concatenation :: Parser ProductSpec
+ Text.ABNF.ABNF.Parser: dec_val :: Parser NumLit
+ Text.ABNF.ABNF.Parser: defined_as :: Parser DefinedAs
+ Text.ABNF.ABNF.Parser: element :: Parser Element
+ Text.ABNF.ABNF.Parser: elements :: Parser SumSpec
+ Text.ABNF.ABNF.Parser: group :: Parser Group
+ Text.ABNF.ABNF.Parser: hex_val :: Parser NumLit
+ Text.ABNF.ABNF.Parser: identifier :: Parser Identifier
+ Text.ABNF.ABNF.Parser: literal :: Parser Literal
+ Text.ABNF.ABNF.Parser: num_val :: Parser NumLit
+ Text.ABNF.ABNF.Parser: num_val' :: Char -> Parser Int -> Parser NumLit
+ Text.ABNF.ABNF.Parser: option_ :: Parser Group
+ Text.ABNF.ABNF.Parser: prose_val :: Parser Text
+ Text.ABNF.ABNF.Parser: repeat :: Parser Repeat
+ Text.ABNF.ABNF.Parser: repetition :: Parser Repetition
+ Text.ABNF.ABNF.Parser: rule :: Parser Rule
+ Text.ABNF.ABNF.Parser: rulelist :: Parser [Rule]
+ Text.ABNF.ABNF.Parser: vchar :: Parser Char
+ Text.ABNF.ABNF.Parser: wsp :: Parser Char
+ Text.ABNF.ABNF.Types: Adds :: DefinedAs
+ Text.ABNF.ABNF.Types: CharLit :: Text -> Literal
+ Text.ABNF.ABNF.Types: Equals :: DefinedAs
+ Text.ABNF.ABNF.Types: Group :: SumSpec -> Group
+ Text.ABNF.ABNF.Types: GroupElement :: Group -> Element
+ Text.ABNF.ABNF.Types: IntLit :: [Int] -> NumLit
+ Text.ABNF.ABNF.Types: LiteralElement :: Literal -> Element
+ Text.ABNF.ABNF.Types: NumLit :: NumLit -> Literal
+ Text.ABNF.ABNF.Types: OptionElement :: Group -> Element
+ Text.ABNF.ABNF.Types: ProductSpec :: [Repetition] -> ProductSpec
+ Text.ABNF.ABNF.Types: RangeLit :: Int -> Int -> NumLit
+ Text.ABNF.ABNF.Types: Repeat :: Int -> (Maybe Int) -> Repeat
+ Text.ABNF.ABNF.Types: Repetition :: Repeat -> Element -> Repetition
+ Text.ABNF.ABNF.Types: Rule :: Identifier -> DefinedAs -> SumSpec -> Rule
+ Text.ABNF.ABNF.Types: RuleElement :: Rule -> Element
+ Text.ABNF.ABNF.Types: RuleElement' :: Identifier -> Element
+ Text.ABNF.ABNF.Types: SumSpec :: [ProductSpec] -> SumSpec
+ Text.ABNF.ABNF.Types: data DefinedAs
+ Text.ABNF.ABNF.Types: data Element
+ Text.ABNF.ABNF.Types: data Group
+ Text.ABNF.ABNF.Types: data Literal
+ Text.ABNF.ABNF.Types: data NumLit
+ Text.ABNF.ABNF.Types: data ProductSpec
+ Text.ABNF.ABNF.Types: data Repeat
+ Text.ABNF.ABNF.Types: data Repetition
+ Text.ABNF.ABNF.Types: data Rule
+ Text.ABNF.ABNF.Types: data SumSpec
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.DefinedAs
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Element
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Group
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Literal
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.NumLit
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.ProductSpec
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Repeat
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Repetition
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.Rule
+ Text.ABNF.ABNF.Types: instance GHC.Classes.Eq Text.ABNF.ABNF.Types.SumSpec
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.DefinedAs
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Element
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Group
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Literal
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.NumLit
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.ProductSpec
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Repeat
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Repetition
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.Rule
+ Text.ABNF.ABNF.Types: instance GHC.Show.Show Text.ABNF.ABNF.Types.SumSpec
+ Text.ABNF.ABNF.Types: type Identifier = Text
+ Text.ABNF.Document.Parser: generateParser :: Rule -> Parser Document
+ Text.ABNF.Document.Parser: oneOf :: String -> Parser Char
+ Text.ABNF.Document.Parser: parseElem :: Element -> Parser [Content]
+ Text.ABNF.Document.Parser: parseLiteral :: Literal -> Parser [Content]
+ Text.ABNF.Document.Parser: parseNumLit :: NumLit -> Parser Text
+ Text.ABNF.Document.Parser: parseProdSpec :: ProductSpec -> Parser [Content]
+ Text.ABNF.Document.Parser: parseRepetition :: Repetition -> Parser [Content]
+ Text.ABNF.Document.Parser: parseRule :: Rule -> Parser Document
+ Text.ABNF.Document.Parser: parseSumSpec :: SumSpec -> Parser [Content]
+ Text.ABNF.Document.Parser: toList :: a -> [a]
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.DefinedAs
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Element
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Group
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Literal
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.NumLit
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.ProductSpec
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Repeat
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Repetition
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.Rule
+ Text.ABNF.PrettyPrinter: instance Text.ABNF.PrettyPrinter.Pretty Text.ABNF.ABNF.Types.SumSpec

Files

abnf.cabal view
@@ -2,7 +2,7 @@ -- see http://haskell.org/cabal/users-guide/  name:                abnf-version:             0.1.0.0+version:             0.2.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@@ -23,10 +23,12 @@   location:            https://github.com/Xandaros/abnf.git  library-  exposed-modules:     Text.ABNF.Parser,-                       Text.ABNF.Parser.Types,-                       Text.ABNF.Canonicalizer,+  exposed-modules:     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.PrettyPrinter   ghc-options:         -W@@ -44,7 +46,8 @@   type:                exitcode-stdio-1.0   main-is:             Main.hs   other-modules:       ABNF,-                       Document+                       Document,+                       Util   hs-source-dirs:      test   build-depends:       base >= 4.8 && <4.9,                        text,
+ src/Text/ABNF/ABNF.hs view
@@ -0,0 +1,1 @@+module Text.ABNF.ABNF where
+ src/Text/ABNF/ABNF/Canonicalizer.hs view
@@ -0,0 +1,84 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Text.ABNF.ABNF.Canonicalizer+Description : Canonicalize a list of rules+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : non-portable+-}++module Text.ABNF.ABNF.Canonicalizer (canonicalizeRules) where++import Data.List (partition)+import Data.Monoid ((<>))+import qualified Data.Map.Strict as Map+import qualified Data.Text as Text++import Text.ABNF.ABNF.Types++type RuleMap = Map.Map Identifier Rule++canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule+canonicalizeRules mainRuleIdent rules = do+    let (defs, adds) = partition isAdd rules+        ruleMap  = foldr (\rule@(Rule ident _ _) curMap ->+                              Map.insert ident rule curMap)+                         Map.empty+                         defs+        ruleMap' = foldr (\rule@(Rule ident _ _) curMap ->+                              Map.insertWith mergeRules ident rule curMap)+                         ruleMap+                         adds+    mainRule <- Map.lookup mainRuleIdent ruleMap'+    pure $ inlineRulesRule ruleMap' mainRule --TODO: Catch missing rules here+    where+        isAdd (Rule _ Equals _) = False+        isAdd (Rule _ Adds _) = True++mergeRules :: Rule -> Rule -> Rule+mergeRules (Rule ident Equals left) (Rule ident2 Adds right)+  | ident == ident2 = Rule ident Equals+    ( SumSpec [ ProductSpec [ Repetition (Repeat 1 (Just 1))+                                (GroupElement (Group left))+                            ]+              , ProductSpec [ Repetition (Repeat 1 (Just 1))+                                (GroupElement (Group right))+                            ]+              ]+    ) +  -- TODO: Print location of error+  | otherwise = error . Text.unpack+      $  "Error while canocicalizing ABNF: Rule " <> ident+      <> " appended (=/) without ever assigning(=)"+mergeRules _ _ = error "Bug in ABNF canonicalizer (mergeRules)!"++inlineRulesRule :: RuleMap -> Rule -> Rule+inlineRulesRule rulemap (Rule ident def spec) =+    Rule ident def (inlineRulesSumSpec rulemap spec)++inlineRulesSumSpec :: RuleMap -> SumSpec -> SumSpec+inlineRulesSumSpec rulemap (SumSpec specs) =+    SumSpec $ inlineRulesProdSpec rulemap <$> specs++inlineRulesProdSpec :: RuleMap -> ProductSpec -> ProductSpec+inlineRulesProdSpec rulemap (ProductSpec reps) =+    ProductSpec $ inlineRulesRepetition rulemap <$> reps++inlineRulesRepetition :: RuleMap -> Repetition -> Repetition+inlineRulesRepetition rulemap (Repetition rep elem) =+    Repetition rep $ inlineRulesElement rulemap elem++inlineRulesElement :: RuleMap -> Element -> Element+inlineRulesElement rulemap oldrule@(RuleElement' ruleName) =+    let rule = Map.lookup ruleName rulemap+    in  maybe oldrule (RuleElement . inlineRulesRule rulemap) rule+inlineRulesElement rulemap (GroupElement grp) =+    GroupElement $ inlineRulesGroup rulemap grp+inlineRulesElement rulemap (OptionElement grp) =+    OptionElement $ inlineRulesGroup rulemap grp+inlineRulesElement _ old = old++inlineRulesGroup :: RuleMap -> Group -> Group+inlineRulesGroup rulemap (Group sumspec) = Group (inlineRulesSumSpec rulemap sumspec)
+ src/Text/ABNF/ABNF/Parser.hs view
@@ -0,0 +1,170 @@+{-|+Module      : Text.ABNF.ABNF.Parser+Description : ABNF Parser+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : portable++This module provides facilities to parse ABNF documents.+To parse documents using ABNF, see "Text.ABNF.Document.Parser"++The parser you will most likely be interested in is 'rulelist'+-}+module Text.ABNF.ABNF.Parser where++import Prelude hiding (repeat)++import Data.Char (ord, isHexDigit, digitToInt)+import Data.Maybe (catMaybes)+import qualified Data.Text as Text+import Numeric (readInt)+import Text.Megaparsec+import Text.Megaparsec.Text++import Text.ABNF.ABNF.Types++identifier :: Parser Identifier+identifier = Text.pack <$> do+    firstChar <- letterChar+    otherChars <- many $ alphaNumChar <|> char '-'+    pure (firstChar:otherChars)++rulelist :: Parser [Rule]+rulelist = catMaybes <$> (some $ Just <$> rule <|> (many wsp *> c_nl *> pure Nothing))++rule :: Parser Rule+rule = Rule <$> identifier+            <*> defined_as+            <*> elements+            <* c_nl++defined_as :: Parser DefinedAs+defined_as = many c_wsp *> ((try (string "=/") *> pure Adds)+                         <|> try (string "=")  *> pure Equals) <* many c_wsp++elements :: Parser SumSpec+elements = alternation <* many wsp++c_wsp :: Parser String+c_wsp = sequence [wsp] <|> (try $ do+    newl <- c_nl+    white <- wsp+    pure $ newl ++ [white])++c_nl :: Parser String+c_nl = comment <|> crlf++comment :: Parser String+comment = char ';' *> many (wsp <|> vchar) <* crlf++alternation :: Parser SumSpec+alternation = (do+    first <- concatenation+    rest <- many (try (many c_wsp *> char '/') *> many c_wsp *> concatenation)+    pure . SumSpec $ first:rest) <?> "alternation"++concatenation :: Parser ProductSpec+concatenation = (do+    first <- repetition+    rest <- many (try $ some c_wsp *> repetition)+    pure . ProductSpec $ first:rest) <?> "concatenation"++repetition :: Parser Repetition+repetition = Repetition <$> repeat <*> element++repeat :: Parser Repeat+repeat = try asteriskNumbers <|> try singleNumber <|> pure (Repeat 1 (Just 1))+    where+        singleNumber = Repeat 1 <$> (Just . read <$> some digitChar)+        asteriskNumbers = do+            firstNumber <- option 0 (read <$> some digitChar)+            char '*'+            secondNumber <- optional (read <$> some digitChar)+            pure $ Repeat firstNumber secondNumber++element :: Parser Element+element = RuleElement' <$> identifier+      <|> GroupElement <$> group+      <|> OptionElement <$> option_+      <|> LiteralElement <$> literal++group :: Parser Group+group = Group <$>+    (char '(' *> many c_wsp *> alternation <* many c_wsp <* char ')')++option_ :: Parser Group+option_ = Group <$>+    (char '[' *> many c_wsp *> alternation <* many c_wsp <* char ']')++literal :: Parser Literal+literal = CharLit <$> char_val <|> NumLit <$> num_val <|> CharLit <$> prose_val++char_val :: Parser Text.Text+char_val = Text.pack <$> (char '"' *> many schar <* char '"')+    where+        schar = satisfy (\c -> ord c >= 0x20 && ord c <= 0x21+                            || ord c >= 0x23 && ord c <= 0x7E)++num_val :: Parser NumLit+num_val = char '%' *> (bin_val <|> dec_val <|> hex_val)++{-# WARNING bin_val "readBinInt is unsafe" #-}+bin_val :: Parser NumLit+bin_val = num_val' 'b' binInt+    where+        readBinInt :: String -> Int+        readBinInt = fst . head . readInt 2+            (`elem` ['0', '1'])+            digitToInt+        binInt = readBinInt <$> many (char '0' <|> char '1')++dec_val :: Parser NumLit+dec_val = num_val' 'd' readInt+    where+        readInt :: Parser Int+        readInt = read <$> some digitChar++{-# WARNING hex_val "readHexInt is unsafe" #-}+hex_val :: Parser NumLit+hex_val = num_val' 'x' hexInt+    where+        readHexInt :: String -> Int+        readHexInt = fst . head . readInt 16 isHexDigit digitToInt+        hexInt = readHexInt <$> many hexDigitChar++num_val' :: Char -> Parser Int -> Parser NumLit+num_val' c hexInt = do+    char c+    digits <- hexInt+    intLit digits <|> rangeLit digits <|> pure (IntLit [digits])+    where+        intLit' :: Parser [Int]+        intLit' = some $ char '.' *> hexInt++        intLit first = do+            rest <- intLit'+            pure $ IntLit (first:rest)++        rangeLit :: Int -> Parser NumLit+        rangeLit startRange = do+            char '-'+            endRange <- hexInt+            pure $ RangeLit startRange endRange++        --readHexInt :: String -> Int+        --readHexInt = fst . head . readInt 16 isHexDigit digitToInt+        --hexInt = readHexInt <$> many hexDigitChar++prose_val :: Parser Text.Text+prose_val = Text.pack <$> (char '<' *> many pchar <* char '>')+    where+        pchar = satisfy (\c -> ord c >= 0x20 && ord c <= 0x3D+                            || ord c >= 0x3F && ord c <= 0x7E)++vchar :: Parser Char+vchar = satisfy (\c -> ord c >= 0x21 && ord c <= 0x7E)++wsp :: Parser Char+wsp = char ' ' <|> char '\t'
+ src/Text/ABNF/ABNF/Types.hs view
@@ -0,0 +1,52 @@+{-|+Module      : Text.ABNF.ABNF.Types+Description : Types used by the parser+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : portable++These types are used by the parser and are loosely modeled after the ABNF+privded in <https://tools.ietf.org/html/rfc5234#section-4 RFC 5234>+-}+module Text.ABNF.ABNF.Types where++import qualified Data.Text as Text++type Identifier = Text.Text++data Rule = Rule Identifier DefinedAs SumSpec+    deriving (Show, Eq)++data SumSpec = SumSpec [ProductSpec]+    deriving (Show, Eq)++data ProductSpec = ProductSpec [Repetition]+    deriving (Show, Eq)++data Repetition = Repetition Repeat Element+    deriving (Show, Eq)++data Repeat = Repeat Int (Maybe Int)+    deriving (Show, Eq)++data Element = RuleElement' Identifier+             | RuleElement Rule+             | GroupElement Group+             | OptionElement Group+             | LiteralElement Literal+             deriving (Show, Eq)++data Group = Group SumSpec+    deriving (Show, Eq)++data Literal = CharLit Text.Text | NumLit NumLit+    deriving (Show, Eq)++data NumLit = IntLit [Int]+            | RangeLit Int Int+            deriving (Show, Eq)++data DefinedAs = Equals | Adds+    deriving (Show, Eq)
− src/Text/ABNF/Canonicalizer.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Text.ABNF.Canonicalizer (canonicalizeRules) where--import Data.List (partition)-import Data.Monoid ((<>))-import qualified Data.Map.Strict as Map-import qualified Data.Text as Text--import Text.ABNF.Parser.Types--type RuleMap = Map.Map Identifier Rule--canonicalizeRules :: Identifier -> [Rule] -> Maybe Rule-canonicalizeRules mainRuleIdent rules = do-    let (defs, adds) = partition isAdd rules-        ruleMap  = foldr (\rule@(Rule ident _ _) curMap ->-                              Map.insert ident rule curMap)-                         Map.empty-                         defs-        ruleMap' = foldr (\rule@(Rule ident _ _) curMap ->-                              Map.insertWith mergeRules ident rule curMap)-                         ruleMap-                         adds-    mainRule <- Map.lookup mainRuleIdent ruleMap'-    pure $ inlineRulesRule ruleMap' mainRule --TODO: Catch missing rules here-    where-        isAdd (Rule _ Equals _) = False-        isAdd (Rule _ Adds _) = True--mergeRules :: Rule -> Rule -> Rule-mergeRules (Rule ident Equals left) (Rule ident2 Adds right)-  | ident == ident2 = Rule ident Equals-    ( SumSpec [ ProductSpec [ Repetition (Repeat 1 (Just 1))-                                (GroupElement (Group left))-                            ]-              , ProductSpec [ Repetition (Repeat 1 (Just 1))-                                (GroupElement (Group right))-                            ]-              ]-    ) -  -- TODO: Print location of error-  | otherwise = error . Text.unpack-      $  "Error while canocicalizing ABNF: Rule " <> ident-      <> " appended (=/) without ever assigning(=)"-mergeRules _ _ = error "Bug in ABNF canonicalizer (mergeRules)!"--inlineRulesRule :: RuleMap -> Rule -> Rule-inlineRulesRule rulemap (Rule ident def spec) =-    Rule ident def (inlineRulesSumSpec rulemap spec)--inlineRulesSumSpec :: RuleMap -> SumSpec -> SumSpec-inlineRulesSumSpec rulemap (SumSpec specs) =-    SumSpec $ inlineRulesProdSpec rulemap <$> specs--inlineRulesProdSpec :: RuleMap -> ProductSpec -> ProductSpec-inlineRulesProdSpec rulemap (ProductSpec reps) =-    ProductSpec $ inlineRulesRepetition rulemap <$> reps--inlineRulesRepetition :: RuleMap -> Repetition -> Repetition-inlineRulesRepetition rulemap (Repetition rep elem) =-    Repetition rep $ inlineRulesElement rulemap elem--inlineRulesElement :: RuleMap -> Element -> Element-inlineRulesElement rulemap oldrule@(RuleElement' ruleName) =-    let rule = Map.lookup ruleName rulemap-    in  maybe oldrule (RuleElement . inlineRulesRule rulemap) rule-inlineRulesElement rulemap (GroupElement grp) =-    GroupElement $ inlineRulesGroup rulemap grp-inlineRulesElement rulemap (OptionElement grp) =-    OptionElement $ inlineRulesGroup rulemap grp-inlineRulesElement _ old = old--inlineRulesGroup :: RuleMap -> Group -> Group-inlineRulesGroup rulemap (Group sumspec) = Group (inlineRulesSumSpec rulemap sumspec)
src/Text/ABNF/Document.hs view
@@ -1,86 +1,1 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Text.ABNF.Document-Description : Tools to parse ABNF into a Document-Copyright   : (c) Martin Zeller, 2016-License     : BSD2-Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>-Stability   : experimental-Portability : non-portable--This module takes a canonicalized rule to parse a document described by it.-You use the function 'generateParser' to generate an attoparsec parser.--}- module Text.ABNF.Document where--import Control.Applicative (liftA2, (<|>), many)-import Control.Monad (join, mzero)-import Data.Char (chr)-import Data.Foldable (asum)-import Data.Monoid ((<>))--import qualified Data.Text as Text-import Data.Attoparsec.Text--import Text.ABNF.Parser.Types-import Text.ABNF.Document.Types--import Debug.Trace--generateParser :: Rule -> Parser Document-generateParser = parseRule--parseRule :: Rule -> Parser Document-parseRule (Rule ident _ spec) = Document ident <$>-    (traceM ("Rule: " ++ Text.unpack ident) *> parseSumSpec spec <?> "Rule")--parseSumSpec :: SumSpec -> Parser [Content]-parseSumSpec (SumSpec prodspecs) = asum (map parseProdSpec prodspecs) <?> "Sum"--parseProdSpec :: ProductSpec -> Parser [Content]-parseProdSpec (ProductSpec reps) =-    join <$> (sequence $ map parseRepetition reps) <?> "Product"--parseRepetition :: Repetition -> Parser [Content]--- any number of times-parseRepetition (Repetition (Repeat 0 Nothing) elem) =-    join <$> (many $ parseElem elem)--- Zero times-parseRepetition (Repetition (Repeat 0 (Just 0)) _) = pure []--- Less than n times-parseRepetition (Repetition (Repeat 0 (Just n)) elem) = do-    el <- (Just <$> parseElem elem) <|> pure Nothing-    case el of-      Just el' -> liftA2 (++) (pure el')-                    (parseRepetition (Repetition (Repeat 0 (Just (n-1))) elem))-      Nothing -> pure []--- Between n and m times-parseRepetition (Repetition (Repeat n (Just m)) elem) =-    liftA2 (++) (parseElem elem)-                (parseRepetition (Repetition (Repeat (n-1) (Just (m-1))) elem))--- At least n times-parseRepetition (Repetition (Repeat n x) elem) =-    liftA2 (++) (parseElem elem)-                (parseRepetition (Repetition (Repeat (n-1) x) elem))--parseElem :: Element -> Parser [Content]-parseElem (RuleElement rule) = toList . NonTerminal <$> parseRule rule <?> "Rule element"-parseElem (RuleElement' ruleName) = fail . Text.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 (CharLit lit) = trace (Text.unpack 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"--toList :: a -> [a]-toList = pure--oneOf :: String -> Parser Char-oneOf = foldr (<|>) mzero . fmap char
+ src/Text/ABNF/Document/Parser.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Text.ABNF.Document.Parser+Description : Tools to parse ABNF into a Document+Copyright   : (c) Martin Zeller, 2016+License     : BSD2+Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>+Stability   : experimental+Portability : non-portable++This module takes a canonicalized rule to parse a document described by it.+You use the function 'generateParser' to generate an attoparsec parser.+-}++module Text.ABNF.Document.Parser where++import Control.Applicative (liftA2, (<|>), many)+import Control.Monad (join, mzero)+import Data.Char (chr)+import Data.Foldable (asum)+import Data.Monoid ((<>))++import qualified Data.Text as Text+import Data.Attoparsec.Text++import Text.ABNF.ABNF.Types+import Text.ABNF.Document.Types++generateParser :: Rule -> Parser Document+generateParser = parseRule++parseRule :: Rule -> Parser Document+parseRule (Rule ident _ spec) = Document ident <$> parseSumSpec spec <?> "Rule"++parseSumSpec :: SumSpec -> Parser [Content]+parseSumSpec (SumSpec prodspecs) = asum (map parseProdSpec prodspecs) <?> "Sum"++parseProdSpec :: ProductSpec -> Parser [Content]+parseProdSpec (ProductSpec reps) =+    join <$> (sequence $ map parseRepetition reps) <?> "Product"++parseRepetition :: Repetition -> Parser [Content]+-- any number of times+parseRepetition (Repetition (Repeat 0 Nothing) elem) =+    join <$> (many $ parseElem elem)+-- Zero times+parseRepetition (Repetition (Repeat 0 (Just 0)) _) = pure []+-- Less than n times+parseRepetition (Repetition (Repeat 0 (Just n)) elem) = do+    el <- (Just <$> parseElem elem) <|> pure Nothing+    case el of+      Just el' -> liftA2 (++) (pure el')+                    (parseRepetition (Repetition (Repeat 0 (Just (n-1))) elem))+      Nothing -> pure []+-- Between n and m times+parseRepetition (Repetition (Repeat n (Just m)) elem) =+    liftA2 (++) (parseElem elem)+                (parseRepetition (Repetition (Repeat (n-1) (Just (m-1))) elem))+-- At least n times+parseRepetition (Repetition (Repeat n x) elem) =+    liftA2 (++) (parseElem elem)+                (parseRepetition (Repetition (Repeat (n-1) x) elem))++parseElem :: Element -> Parser [Content]+parseElem (RuleElement rule) = toList . NonTerminal <$> parseRule rule <?> "Rule element"+parseElem (RuleElement' ruleName) = fail . Text.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 (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"++toList :: a -> [a]+toList = pure++oneOf :: String -> Parser Char+oneOf = foldr (<|>) mzero . fmap char
− src/Text/ABNF/Parser.hs
@@ -1,170 +0,0 @@-{-|-Module      : Text.ABNF.Parser-Description : ABNF Parser-Copyright   : (c) Martin Zeller, 2016-License     : BSD2-Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>-Stability   : experimental-Portability : portable--This module provides facilities to parse ABNF documents.-To parse documents using ABNF, see "Text.ABNF.Document.Parser"--The parser you will most likely be interested in is 'rulelist'--}-module Text.ABNF.Parser where--import Prelude hiding (repeat)--import Data.Char (ord, isHexDigit, digitToInt)-import Data.Maybe (catMaybes)-import qualified Data.Text as Text-import Numeric (readInt)-import Text.Megaparsec-import Text.Megaparsec.Text--import Text.ABNF.Parser.Types--identifier :: Parser Identifier-identifier = Text.pack <$> do-    firstChar <- letterChar-    otherChars <- many $ alphaNumChar <|> char '-'-    pure (firstChar:otherChars)--rulelist :: Parser [Rule]-rulelist = catMaybes <$> (some $ Just <$> rule <|> (many wsp *> c_nl *> pure Nothing))--rule :: Parser Rule-rule = Rule <$> identifier-            <*> defined_as-            <*> elements-            <* c_nl--defined_as :: Parser DefinedAs-defined_as = many c_wsp *> ((try (string "=/") *> pure Adds)-                         <|> try (string "=")  *> pure Equals) <* many c_wsp--elements :: Parser SumSpec-elements = alternation <* many wsp--c_wsp :: Parser String-c_wsp = sequence [wsp] <|> (try $ do-    newl <- c_nl-    white <- wsp-    pure $ newl ++ [white])--c_nl :: Parser String-c_nl = comment <|> crlf--comment :: Parser String-comment = char ';' *> many (wsp <|> vchar) <* crlf--alternation :: Parser SumSpec-alternation = (do-    first <- concatenation-    rest <- many (try (many c_wsp *> char '/') *> many c_wsp *> concatenation)-    pure . SumSpec $ first:rest) <?> "alternation"--concatenation :: Parser ProductSpec-concatenation = (do-    first <- repetition-    rest <- many (try $ some c_wsp *> repetition)-    pure . ProductSpec $ first:rest) <?> "concatenation"--repetition :: Parser Repetition-repetition = Repetition <$> repeat <*> element--repeat :: Parser Repeat-repeat = try asteriskNumbers <|> try singleNumber <|> pure (Repeat 1 (Just 1))-    where-        singleNumber = Repeat 1 <$> (Just . read <$> some digitChar)-        asteriskNumbers = do-            firstNumber <- option 0 (read <$> some digitChar)-            char '*'-            secondNumber <- optional (read <$> some digitChar)-            pure $ Repeat firstNumber secondNumber--element :: Parser Element-element = RuleElement' <$> identifier-      <|> GroupElement <$> group-      <|> OptionElement <$> option_-      <|> LiteralElement <$> literal--group :: Parser Group-group = Group <$>-    (char '(' *> many c_wsp *> alternation <* many c_wsp <* char ')')--option_ :: Parser Group-option_ = Group <$>-    (char '[' *> many c_wsp *> alternation <* many c_wsp <* char ']')--literal :: Parser Literal-literal = CharLit <$> char_val <|> NumLit <$> num_val <|> CharLit <$> prose_val--char_val :: Parser Text.Text-char_val = Text.pack <$> (char '"' *> many schar <* char '"')-    where-        schar = satisfy (\c -> ord c >= 0x20 && ord c <= 0x21-                            || ord c >= 0x23 && ord c <= 0x7E)--num_val :: Parser NumLit-num_val = char '%' *> (bin_val <|> dec_val <|> hex_val)--{-# WARNING bin_val "readBinInt is unsafe" #-}-bin_val :: Parser NumLit-bin_val = num_val' 'b' binInt-    where-        readBinInt :: String -> Int-        readBinInt = fst . head . readInt 2-            (`elem` ['0', '1'])-            digitToInt-        binInt = readBinInt <$> many (char '0' <|> char '1')--dec_val :: Parser NumLit-dec_val = num_val' 'd' readInt-    where-        readInt :: Parser Int-        readInt = read <$> some digitChar--{-# WARNING hex_val "readHexInt is unsafe" #-}-hex_val :: Parser NumLit-hex_val = num_val' 'x' hexInt-    where-        readHexInt :: String -> Int-        readHexInt = fst . head . readInt 16 isHexDigit digitToInt-        hexInt = readHexInt <$> many hexDigitChar--num_val' :: Char -> Parser Int -> Parser NumLit-num_val' c hexInt = do-    char c-    digits <- hexInt-    intLit digits <|> rangeLit digits <|> pure (IntLit [digits])-    where-        intLit' :: Parser [Int]-        intLit' = some $ char '.' *> hexInt--        intLit first = do-            rest <- intLit'-            pure $ IntLit (first:rest)--        rangeLit :: Int -> Parser NumLit-        rangeLit startRange = do-            char '-'-            endRange <- hexInt-            pure $ RangeLit startRange endRange--        --readHexInt :: String -> Int-        --readHexInt = fst . head . readInt 16 isHexDigit digitToInt-        --hexInt = readHexInt <$> many hexDigitChar--prose_val :: Parser Text.Text-prose_val = Text.pack <$> (char '<' *> many pchar <* char '>')-    where-        pchar = satisfy (\c -> ord c >= 0x20 && ord c <= 0x3D-                            || ord c >= 0x3F && ord c <= 0x7E)--vchar :: Parser Char-vchar = satisfy (\c -> ord c >= 0x21 && ord c <= 0x7E)--wsp :: Parser Char-wsp = char ' ' <|> char '\t'
− src/Text/ABNF/Parser/Types.hs
@@ -1,52 +0,0 @@-{-|-Module      : Text.ABNF.Parser.Types-Description : Types used by the parser-Copyright   : (c) Martin Zeller, 2016-License     : BSD2-Maintainer  : Martin Zeller <mz.bremerhaven@gmail.com>-Stability   : experimental-Portability : portable--These types are used by the parser and are loosely modeled after the ABNF-privded in <https://tools.ietf.org/html/rfc5234#section-4 RFC 5234>--}-module Text.ABNF.Parser.Types where--import qualified Data.Text as Text--type Identifier = Text.Text--data Rule = Rule Identifier DefinedAs SumSpec-    deriving (Show, Eq)--data SumSpec = SumSpec [ProductSpec]-    deriving (Show, Eq)--data ProductSpec = ProductSpec [Repetition]-    deriving (Show, Eq)--data Repetition = Repetition Repeat Element-    deriving (Show, Eq)--data Repeat = Repeat Int (Maybe Int)-    deriving (Show, Eq)--data Element = RuleElement' Identifier-             | RuleElement Rule-             | GroupElement Group-             | OptionElement Group-             | LiteralElement Literal-             deriving (Show, Eq)--data Group = Group SumSpec-    deriving (Show, Eq)--data Literal = CharLit Text.Text | NumLit NumLit-    deriving (Show, Eq)--data NumLit = IntLit [Int]-            | RangeLit Int Int-            deriving (Show, Eq)--data DefinedAs = Equals | Adds-    deriving (Show, Eq)
src/Text/ABNF/PrettyPrinter.hs view
@@ -1,5 +1,5 @@ {-|-Module      : Text.ABNF.PrettyPrinter+Module      : Text.ABNF.ABNF.PrettyPrinter Description : Pretty printer for ABNF rules Copyright   : (c) Martin Zeller, 2016 License     : BSD2@@ -13,7 +13,7 @@ import Data.List (intersperse) import qualified Data.Text as Text -import Text.ABNF.Parser.Types+import Text.ABNF.ABNF.Types  class Pretty a where     prettyShow :: a -> String
test/ABNF.hs view
@@ -8,8 +8,8 @@ import Text.Megaparsec.Text import qualified Data.Text as T -import Text.ABNF.Parser-import Text.ABNF.Parser.Types+import Text.ABNF.ABNF.Parser+import Text.ABNF.ABNF.Types import Util  testParser :: (Eq a, Show a) => Parser a -> T.Text -> a -> Assertion
test/Document.hs view
@@ -6,8 +6,8 @@ import Test.Tasty import Test.Tasty.HUnit -import Text.ABNF.Parser.Types-import Text.ABNF.Document+import Text.ABNF.ABNF.Types+import Text.ABNF.Document.Parser import Text.ABNF.Document.Types  import Util
+ test/Util.hs view
@@ -0,0 +1,15 @@+module Util where++import Text.ABNF.ABNF.Types++simpleSum :: Element -> SumSpec+simpleSum = SumSpec . (:[]) . simpleProd++simpleProd :: Element -> ProductSpec+simpleProd = ProductSpec . (:[]) . single++single :: Element -> Repetition+single = Repetition (Repeat 1 (Just 1))++wsElement :: Element+wsElement = LiteralElement (NumLit (IntLit [0x20]))