diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-12 05:21:16 +0000 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-12 05:21:16 +0000 |
commit | fe17c720081798175c9936d1624f428b565e310f (patch) | |
tree | 8cb2b9520f90a21e54e5b6dac4588ef4ed6e3a9c /bbcode | |
parent | a5d285a8b74d2278e8549909d29c01b62dc84424 (diff) | |
download | thermoprint-fe17c720081798175c9936d1624f428b565e310f.tar thermoprint-fe17c720081798175c9936d1624f428b565e310f.tar.gz thermoprint-fe17c720081798175c9936d1624f428b565e310f.tar.bz2 thermoprint-fe17c720081798175c9936d1624f428b565e310f.tar.xz thermoprint-fe17c720081798175c9936d1624f428b565e310f.zip |
Rose-Tree labels now carry attributes
Diffstat (limited to 'bbcode')
-rw-r--r-- | bbcode/src/Text/BBCode.hs | 28 | ||||
-rw-r--r-- | bbcode/src/Text/BBCode/Lexer.hs | 5 | ||||
-rw-r--r-- | bbcode/thermoprint-bbcode.cabal | 1 | ||||
-rw-r--r-- | bbcode/thermoprint-bbcode.nix | 6 |
4 files changed, 28 insertions, 12 deletions
diff --git a/bbcode/src/Text/BBCode.hs b/bbcode/src/Text/BBCode.hs index a6de7b4..3828c22 100644 --- a/bbcode/src/Text/BBCode.hs +++ b/bbcode/src/Text/BBCode.hs | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | module Text.BBCode | 4 | module Text.BBCode |
5 | ( TreeError(..) | 5 | ( TreeError(..) |
6 | , BBLabel | ||
6 | , rose | 7 | , rose |
7 | , matches | 8 | , matches |
8 | ) where | 9 | ) where |
@@ -14,6 +15,7 @@ import Control.Exception (Exception) | |||
14 | import Data.Typeable (Typeable) | 15 | import Data.Typeable (Typeable) |
15 | 16 | ||
16 | import Control.Monad (unless) | 17 | import Control.Monad (unless) |
18 | import Data.Function (on) | ||
17 | 19 | ||
18 | import Text.BBCode.Lexer (BBToken(..), token) | 20 | import Text.BBCode.Lexer (BBToken(..), token) |
19 | 21 | ||
@@ -21,32 +23,42 @@ import Data.Tree | |||
21 | import Data.Tree.Zipper (TreePos, Empty, Full) | 23 | import Data.Tree.Zipper (TreePos, Empty, Full) |
22 | import qualified Data.Tree.Zipper as Z | 24 | import qualified Data.Tree.Zipper as Z |
23 | 25 | ||
26 | import Data.Map (Map) | ||
27 | import qualified Data.Map as Map | ||
28 | |||
29 | import Data.CaseInsensitive (CI) | ||
30 | import qualified Data.CaseInsensitive as CI | ||
31 | |||
24 | data TreeError = ImbalancedTags Text Text | 32 | data TreeError = ImbalancedTags Text Text |
25 | | LeftoverClose Text | 33 | | LeftoverClose Text |
26 | deriving (Show, Eq, Generic, Typeable) | 34 | deriving (Show, Eq, Generic, Typeable) |
27 | 35 | ||
28 | instance Exception TreeError | 36 | instance Exception TreeError |
29 | 37 | ||
38 | type BBLabel = (Text, Map Text (Maybe Text)) | ||
39 | |||
30 | matches :: Text -> Text -> Bool | 40 | matches :: Text -> Text -> Bool |
31 | -- ^ @`matches` "open" "close"@ should be true iff @[/close]@ is a valid closing tag for @[open]@ | 41 | -- ^ @`matches` "open" "close"@ should be true iff @[/close]@ is a valid closing tag for @[open]@ |
32 | -- | 42 | -- |
33 | -- Until we allow for attributes this is equality according to `(==)` | 43 | -- > (==) `on` CI.mk |
34 | matches = (==) | 44 | matches = (==) `on` CI.mk |
35 | 45 | ||
36 | rose :: [BBToken] -> Either TreeError (Forest Text) | 46 | rose :: [BBToken] -> Either TreeError (Forest BBLabel) |
37 | -- ^ Assuming that both tags and content have the same type (we use 'Text') bbcode is a flat representation of a rose tree | 47 | -- ^ Assuming that both tags and content have the same type (we use 'Text') bbcode is a flat representation of a rose tree |
38 | rose = fmap Z.toForest . flip rose' (Z.fromForest []) | 48 | rose = fmap Z.toForest . flip rose' (Z.fromForest []) |
39 | where | 49 | where |
40 | rose' :: [BBToken] -> TreePos Empty Text -> Either TreeError (TreePos Empty Text) | 50 | rose' :: [BBToken] -> TreePos Empty BBLabel -> Either TreeError (TreePos Empty BBLabel) |
41 | rose' [] = return | 51 | rose' [] = return |
42 | rose' (x:xs) = (>>= rose' xs) . rose'' x | 52 | rose' (x:xs) = (>>= rose' xs) . rose'' x |
43 | 53 | ||
44 | rose'' (BBStr t) = return . Z.nextSpace . Z.insert (Node t []) | 54 | rose'' (BBStr t) = return . Z.nextSpace . Z.insert (Node (t, Map.empty) []) |
45 | rose'' (BBOpen t _) = return . Z.children . Z.insert (Node t []) | 55 | rose'' (BBOpen t attrs) = return . Z.children . Z.insert (Node (t, Map.fromList attrs) []) |
46 | rose'' (BBClose t) = close t -- for more pointless | 56 | rose'' (BBClose t) = close t -- for more pointless |
47 | 57 | ||
48 | close :: Text -> TreePos Empty Text -> Either TreeError (TreePos Empty Text) | 58 | close :: Text -> TreePos Empty BBLabel -> Either TreeError (TreePos Empty BBLabel) |
49 | close tag pos = do | 59 | close tag pos = do |
50 | pos' <- maybe (Left $ LeftoverClose tag) Right $ Z.parent pos | 60 | pos' <- maybe (Left $ LeftoverClose tag) Right $ Z.parent pos |
51 | unless (Z.label pos' `matches` tag) . Left $ ImbalancedTags (Z.label pos') tag -- The structure shows that this mode of failure is not logically required -- it's just nice to have | 61 | let |
62 | pTag = fst $ Z.label pos' | ||
63 | unless (pTag `matches` tag) . Left $ ImbalancedTags pTag tag -- The structure shows that this mode of failure is not logically required -- it's just nice to have | ||
52 | return $ Z.nextSpace pos' | 64 | return $ Z.nextSpace pos' |
diff --git a/bbcode/src/Text/BBCode/Lexer.hs b/bbcode/src/Text/BBCode/Lexer.hs index ad26113..2eb0022 100644 --- a/bbcode/src/Text/BBCode/Lexer.hs +++ b/bbcode/src/Text/BBCode/Lexer.hs | |||
@@ -29,9 +29,12 @@ data BBToken = BBOpen Text [(Text, Maybe Text)] -- ^ Tag open with attributes | |||
29 | token :: Parser BBToken | 29 | token :: Parser BBToken |
30 | -- ^ Tokenizer | 30 | -- ^ Tokenizer |
31 | token = BBClose <$> ("[/" *> escapedText' [']'] <* "]") | 31 | token = BBClose <$> ("[/" *> escapedText' [']'] <* "]") |
32 | <|> BBOpen <$> ("[" *> escapedText' [']', ' ']) <*> (option [] attrs <* "]") | 32 | <|> uncurry BBOpen <$> openTag |
33 | <|> BBStr <$> escapedText ['['] | 33 | <|> BBStr <$> escapedText ['['] |
34 | 34 | ||
35 | openTag :: Parser (Text, [(Text, Maybe Text)]) | ||
36 | openTag = (,) <$> ("[" *> escapedText' [']', ' ']) <*> (option [] attrs <* "]") | ||
37 | |||
35 | attrs :: Parser [(Text, Maybe Text)] | 38 | attrs :: Parser [(Text, Maybe Text)] |
36 | attrs = (:) <$> (attrs' <* takeWhile isSpace) <*> (option [] $ attrs) | 39 | attrs = (:) <$> (attrs' <* takeWhile isSpace) <*> (option [] $ attrs) |
37 | where | 40 | where |
diff --git a/bbcode/thermoprint-bbcode.cabal b/bbcode/thermoprint-bbcode.cabal index 80a9da5..f25ffdf 100644 --- a/bbcode/thermoprint-bbcode.cabal +++ b/bbcode/thermoprint-bbcode.cabal | |||
@@ -26,6 +26,7 @@ library | |||
26 | , text >=1.2.1 && <2 | 26 | , text >=1.2.1 && <2 |
27 | , containers >=0.4.0 && <1 | 27 | , containers >=0.4.0 && <1 |
28 | , rosezipper >=0.2 && <1 | 28 | , rosezipper >=0.2 && <1 |
29 | , case-insensitive >=1.2.0 && <2 | ||
29 | hs-source-dirs: src | 30 | hs-source-dirs: src |
30 | default-language: Haskell2010 | 31 | default-language: Haskell2010 |
31 | 32 | ||
diff --git a/bbcode/thermoprint-bbcode.nix b/bbcode/thermoprint-bbcode.nix index c379053..896bb04 100644 --- a/bbcode/thermoprint-bbcode.nix +++ b/bbcode/thermoprint-bbcode.nix | |||
@@ -1,12 +1,12 @@ | |||
1 | { mkDerivation, attoparsec, base, containers, hspec, QuickCheck | 1 | { mkDerivation, attoparsec, base, case-insensitive, containers |
2 | , quickcheck-instances, rosezipper, stdenv, text | 2 | , hspec, QuickCheck, quickcheck-instances, rosezipper, stdenv, text |
3 | }: | 3 | }: |
4 | mkDerivation { | 4 | mkDerivation { |
5 | pname = "thermoprint-bbcode"; | 5 | pname = "thermoprint-bbcode"; |
6 | version = "0.0.0"; | 6 | version = "0.0.0"; |
7 | src = ./.; | 7 | src = ./.; |
8 | libraryHaskellDepends = [ | 8 | libraryHaskellDepends = [ |
9 | attoparsec base containers rosezipper text | 9 | attoparsec base case-insensitive containers rosezipper text |
10 | ]; | 10 | ]; |
11 | testHaskellDepends = [ | 11 | testHaskellDepends = [ |
12 | attoparsec base hspec QuickCheck quickcheck-instances text | 12 | attoparsec base hspec QuickCheck quickcheck-instances text |