diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:26 +0000 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:26 +0000 |
| commit | 4914e4d7f638b7771ecdaf24ee0516c05505697c (patch) | |
| tree | 6c89d30ae2ea97f14f4a35af9013dc74e747492c | |
| parent | 539b69b3e3b0c4ffee2323a8fe6239ebda4af48c (diff) | |
| download | thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.gz thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.bz2 thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.tar.xz thermoprint-4914e4d7f638b7771ecdaf24ee0516c05505697c.zip | |
Disallow paragraphs everywhere but at the top
| -rw-r--r-- | bbcode/bbcode.cabal | 2 | ||||
| -rw-r--r-- | bbcode/bbcode.nix | 2 | ||||
| -rw-r--r-- | bbcode/src/Text/BBCode.hs | 37 | ||||
| -rw-r--r-- | bbcode/test/Text/BBCodeSpec.hs | 42 |
4 files changed, 44 insertions, 39 deletions
diff --git a/bbcode/bbcode.cabal b/bbcode/bbcode.cabal index d2f4932..f899d49 100644 --- a/bbcode/bbcode.cabal +++ b/bbcode/bbcode.cabal | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | -- documentation, see http://haskell.org/cabal/users-guide/ | 2 | -- documentation, see http://haskell.org/cabal/users-guide/ |
| 3 | 3 | ||
| 4 | name: bbcode | 4 | name: bbcode |
| 5 | version: 0.0.0 | 5 | version: 1.0.0 |
| 6 | synopsis: A parser for bbcode | 6 | synopsis: A parser for bbcode |
| 7 | -- description: | 7 | -- description: |
| 8 | homepage: http://dirty-haskell.org/tags/thermoprint.html | 8 | homepage: http://dirty-haskell.org/tags/thermoprint.html |
diff --git a/bbcode/bbcode.nix b/bbcode/bbcode.nix index 82ba2f4..97f8982 100644 --- a/bbcode/bbcode.nix +++ b/bbcode/bbcode.nix | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | }: | 3 | }: |
| 4 | mkDerivation { | 4 | mkDerivation { |
| 5 | pname = "bbcode"; | 5 | pname = "bbcode"; |
| 6 | version = "0.0.0"; | 6 | version = "1.0.0"; |
| 7 | src = ./.; | 7 | src = ./.; |
| 8 | libraryHaskellDepends = [ | 8 | libraryHaskellDepends = [ |
| 9 | attoparsec base case-insensitive containers rosezipper text | 9 | attoparsec base case-insensitive containers rosezipper text |
diff --git a/bbcode/src/Text/BBCode.hs b/bbcode/src/Text/BBCode.hs index fbd8a33..8f04f19 100644 --- a/bbcode/src/Text/BBCode.hs +++ b/bbcode/src/Text/BBCode.hs | |||
| @@ -6,6 +6,7 @@ module Text.BBCode | |||
| 6 | ( bbcode | 6 | ( bbcode |
| 7 | , BBCodeError(..) | 7 | , BBCodeError(..) |
| 8 | , TreeError(..) | 8 | , TreeError(..) |
| 9 | , DomForest | ||
| 9 | , DomTree(..) | 10 | , DomTree(..) |
| 10 | -- , dom | 11 | -- , dom |
| 11 | -- , BBLabel | 12 | -- , BBLabel |
| @@ -40,19 +41,23 @@ import Data.Bifunctor (Bifunctor(first)) | |||
| 40 | 41 | ||
| 41 | -- | Our target structure -- a rose tree with an explicit terminal constructor | 42 | -- | Our target structure -- a rose tree with an explicit terminal constructor |
| 42 | data DomTree = Element (CI Text) (Map (CI Text) Text) [DomTree] | 43 | data DomTree = Element (CI Text) (Map (CI Text) Text) [DomTree] |
| 43 | | Paragraph [DomTree] | ||
| 44 | | Content Text | 44 | | Content Text |
| 45 | deriving (Show, Eq) | 45 | deriving (Show, Eq) |
| 46 | 46 | ||
| 47 | dom :: Forest BBLabel -> [DomTree] | 47 | -- | List of paragraphs, which are comprised of lists of 'DomTree's |
| 48 | type DomForest = [[DomTree]] | ||
| 49 | |||
| 50 | dom :: Forest BBLabel -> DomForest | ||
| 48 | -- ^ Parse semantically constrained rose tree to syntactically constrained version | 51 | -- ^ Parse semantically constrained rose tree to syntactically constrained version |
| 49 | -- | 52 | -- |
| 50 | -- Silently drops children of semantically terminal nodes ('BBPlain') | 53 | -- Silently drops children of semantically terminal nodes ('BBPlain') and paragraphs anywhere but at top level |
| 51 | dom = map dom' | 54 | dom = map fromPar |
| 52 | where | 55 | where |
| 53 | dom' (Node (BBPlain t) _) = Content t | 56 | fromPar (Node BBPar ts) = concatMap dom' ts |
| 54 | dom' (Node (BBTag t attrs) ts) = Element (CI.mk t) (Map.mapKeys CI.mk attrs) $ map dom' ts | 57 | fromPar x = dom' x |
| 55 | dom' (Node BBPar ts) = Paragraph $ map dom' ts | 58 | dom' (Node (BBPlain t) _) = pure $ Content t |
| 59 | dom' (Node (BBTag t attrs) ts) = pure . Element (CI.mk t) (Map.mapKeys CI.mk attrs) $ concatMap dom' ts | ||
| 60 | dom' (Node BBPar _) = [] | ||
| 56 | 61 | ||
| 57 | -- | Errors encountered during parsing | 62 | -- | Errors encountered during parsing |
| 58 | data BBCodeError = LexerError String -- ^ Error while parsing input to stream of tokens | 63 | data BBCodeError = LexerError String -- ^ Error while parsing input to stream of tokens |
| @@ -61,13 +66,14 @@ data BBCodeError = LexerError String -- ^ Error while parsing input to stream of | |||
| 61 | 66 | ||
| 62 | instance Exception BBCodeError | 67 | instance Exception BBCodeError |
| 63 | 68 | ||
| 64 | bbcode :: Text -> Either BBCodeError [DomTree] | 69 | bbcode :: Text -> Either BBCodeError DomForest |
| 65 | -- ^ Parse BBCode | 70 | -- ^ Parse BBCode |
| 66 | bbcode t = fmap dom $ first LexerError (parseOnly (many token <* endOfInput) t) >>= first TreeError . rose | 71 | bbcode t = fmap dom $ first LexerError (parseOnly (many token <* endOfInput) t) >>= first TreeError . rose |
| 67 | 72 | ||
| 68 | -- | Errors in input encountered during parsing of lexed token-stream | 73 | -- | Errors in input encountered during parsing of lexed token-stream |
| 69 | data TreeError = MismatchedTags Text Text -- ^ Closing tags label does not match opening tags | 74 | data TreeError = MismatchedTags Text Text -- ^ Closing tags label does not match opening tags |
| 70 | | ImbalancedTags Text -- ^ We found an extraneous closing tag | 75 | | ImbalancedTags Text -- ^ We found an extraneous closing tag |
| 76 | | ParagraphWithinTag -- ^ We found a paragraph-break within a tag | ||
| 71 | deriving (Show, Eq, Generic, Typeable) | 77 | deriving (Show, Eq, Generic, Typeable) |
| 72 | 78 | ||
| 73 | instance Exception TreeError | 79 | instance Exception TreeError |
| @@ -93,7 +99,7 @@ rose :: [BBToken] -> Either TreeError (Forest BBLabel) | |||
| 93 | rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) | 99 | rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) |
| 94 | where | 100 | where |
| 95 | rose' (BBStr t) = return . Z.nextSpace . Z.insert (Node (BBPlain t) []) | 101 | rose' (BBStr t) = return . Z.nextSpace . Z.insert (Node (BBPlain t) []) |
| 96 | rose' BBNewPar = return . parBreak -- for more pointless | 102 | rose' BBNewPar = parBreak -- for more pointless |
| 97 | rose' (BBOpen t attrs) = return . Z.children . Z.insert (Node (BBTag t $ Map.fromList attrs) []) | 103 | rose' (BBOpen t attrs) = return . Z.children . Z.insert (Node (BBTag t $ Map.fromList attrs) []) |
| 98 | rose' (BBContained t attrs) = return . Z.nextSpace . Z.insert (Node (BBTag t $ Map.fromList attrs) []) | 104 | rose' (BBContained t attrs) = return . Z.nextSpace . Z.insert (Node (BBTag t $ Map.fromList attrs) []) |
| 99 | rose' (BBClose t) = close t -- for more pointless | 105 | rose' (BBClose t) = close t -- for more pointless |
| @@ -110,15 +116,14 @@ rose = fmap Z.toForest . foldM (flip rose') (Z.fromForest []) | |||
| 110 | | isPar . Z.tree $ pos = Z.parent pos >>= traversePars | 116 | | isPar . Z.tree $ pos = Z.parent pos >>= traversePars |
| 111 | | otherwise = return pos | 117 | | otherwise = return pos |
| 112 | 118 | ||
| 113 | parBreak :: TreePos Empty BBLabel -> TreePos Empty BBLabel | 119 | parBreak :: TreePos Empty BBLabel -> Either TreeError (TreePos Empty BBLabel) |
| 114 | parBreak z = let siblings = reverse $ Z.before z -- We only move ever move right so Z.after will always be empty | 120 | parBreak z = let siblings = reverse $ Z.before z |
| 115 | 121 | ||
| 116 | siblingsAsPars | 122 | siblingsAsPars |
| 117 | | all isPar siblings = z | 123 | | all isPar siblings = Right z |
| 118 | | otherwise = case Z.parent z of | 124 | | Z.isRoot z = Right . Z.fromForest $ [Node BBPar siblings] |
| 119 | Nothing -> Z.fromForest $ [Node BBPar siblings] | 125 | | otherwise = Left ParagraphWithinTag |
| 120 | Just p -> Z.children . Z.modifyTree (\(Node l _) -> Node l [Node BBPar siblings]) $ p | 126 | in Z.children . Z.insert (Node BBPar []) . Z.last <$> siblingsAsPars |
| 121 | in Z.children . Z.insert (Node BBPar []) . Z.last $ siblingsAsPars | ||
| 122 | 127 | ||
| 123 | isPar (Node BBPar _) = True | 128 | isPar (Node BBPar _) = True |
| 124 | isPar _ = False | 129 | isPar _ = False |
diff --git a/bbcode/test/Text/BBCodeSpec.hs b/bbcode/test/Text/BBCodeSpec.hs index e24c661..241cd76 100644 --- a/bbcode/test/Text/BBCodeSpec.hs +++ b/bbcode/test/Text/BBCodeSpec.hs | |||
| @@ -20,44 +20,44 @@ spec = do | |||
| 20 | example n (s, ts) = let str = "Example " <> show n | 20 | example n (s, ts) = let str = "Example " <> show n |
| 21 | in specify str (bbcode s == Right ts) | 21 | in specify str (bbcode s == Right ts) |
| 22 | 22 | ||
| 23 | examples :: [(Text, [DomTree])] | 23 | examples :: [(Text, DomForest)] |
| 24 | examples = [ ("[t]test[/t]" | 24 | examples = [ ("[t]test[/t]" |
| 25 | , [Element "t" [] [Content "test"]]) | 25 | , pure [Element "t" [] [Content "test"]]) |
| 26 | , ("[t]te\\st[/t]" | 26 | , ("[t]te\\st[/t]" |
| 27 | , [Element "t" [] [Content "te\\st"]]) | 27 | , pure [Element "t" [] [Content "te\\st"]]) |
| 28 | , ("[t]te\\[st[/t]" | 28 | , ("[t]te\\[st[/t]" |
| 29 | , [Element "t" [] [Content "te[st"]]) | 29 | , pure [Element "t" [] [Content "te[st"]]) |
| 30 | , ("[t]test\\\\[/t]" | 30 | , ("[t]test\\\\[/t]" |
| 31 | , [Element "t" [] [Content "test\\"]]) | 31 | , pure [Element "t" [] [Content "test\\"]]) |
| 32 | , ("[\\t]test[/\\t]" | 32 | , ("[\\t]test[/\\t]" |
| 33 | , [Element "\\t" [] [Content "test"]]) | 33 | , pure [Element "\\t" [] [Content "test"]]) |
| 34 | , ("[t attr]test[/t]" | 34 | , ("[t attr]test[/t]" |
| 35 | , [Element "t" [("attr", "")] [Content "test"]]) | 35 | , pure [Element "t" [("attr", "")] [Content "test"]]) |
| 36 | , ("[t=attr]test[/t]" | 36 | , ("[t=attr]test[/t]" |
| 37 | , [Element "t" [("", "attr")] [Content "test"]]) | 37 | , pure [Element "t" [("", "attr")] [Content "test"]]) |
| 38 | , ("[t attr=val]test[/t]" | 38 | , ("[t attr=val]test[/t]" |
| 39 | , [Element "t" [("attr", "val")] [Content "test"]]) | 39 | , pure [Element "t" [("attr", "val")] [Content "test"]]) |
| 40 | , ("[t attr=\"val\"]test[/t]" | 40 | , ("[t attr=\"val\"]test[/t]" |
| 41 | , [Element "t" [("attr", "val")] [Content "test"]]) | 41 | , pure [Element "t" [("attr", "val")] [Content "test"]]) |
| 42 | , ("[t attr=\"va]l\"]test[/t]" | 42 | , ("[t attr=\"va]l\"]test[/t]" |
| 43 | , [Element "t" [("attr", "va]l")] [Content "test"]]) | 43 | , pure [Element "t" [("attr", "va]l")] [Content "test"]]) |
| 44 | , ("[t attr=\"va\\\"l\"]test[/t]" | 44 | , ("[t attr=\"va\\\"l\"]test[/t]" |
| 45 | , [Element "t" [("attr", "va\"l")] [Content "test"]]) | 45 | , pure [Element "t" [("attr", "va\"l")] [Content "test"]]) |
| 46 | , ("[t attr=\"val\" attr2=\"val2\" ]test[/t]" | 46 | , ("[t attr=\"val\" attr2=\"val2\" ]test[/t]" |
| 47 | , [Element "t" [("attr", "val"), ("attr2", "val2")] [Content "test"]]) | 47 | , pure [Element "t" [("attr", "val"), ("attr2", "val2")] [Content "test"]]) |
| 48 | , ("[br/]" | 48 | , ("[br/]" |
| 49 | , [Element "br" [] []]) | 49 | , pure [Element "br" [] []]) |
| 50 | , ("[br attr/]" | 50 | , ("[br attr/]" |
| 51 | , [Element "br" [("attr", "")] []]) | 51 | , pure [Element "br" [("attr", "")] []]) |
| 52 | , ("[br=val/]" | 52 | , ("[br=val/]" |
| 53 | , [Element "br" [("", "val")] []]) | 53 | , pure [Element "br" [("", "val")] []]) |
| 54 | , ("[br attr=val/]" | 54 | , ("[br attr=val/]" |
| 55 | , [Element "br" [("attr", "val")] []]) | 55 | , pure [Element "br" [("attr", "val")] []]) |
| 56 | , ("[br attr=val val2/]" | 56 | , ("[br attr=val val2/]" |
| 57 | , [Element "br" [("attr", "val"), ("val2", "")] []]) | 57 | , pure [Element "br" [("attr", "val"), ("val2", "")] []]) |
| 58 | , ("foo\n\nbar" | 58 | , ("foo\n\nbar" |
| 59 | , [Paragraph [Content "foo"], Paragraph [Content "bar"]]) | 59 | , [[Content "foo"], [Content "bar"]]) |
| 60 | , ("[b]foo\n\nbar[/b]" | 60 | , ("[b]foo[/b]\n\n[b]bar[/b]" |
| 61 | , [Element "b" [] [Paragraph [Content "foo"], Paragraph [Content "bar"]]]) | 61 | , [[Element "b" [] [Content "foo"]], [Element "b" [] [Content "bar"]]]) |
| 62 | ] | 62 | ] |
| 63 | 63 | ||
