aboutsummaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2016-01-18 05:42:41 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2016-01-18 05:42:41 +0000
commit3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7 (patch)
tree627ceb4f46748aafe95e5cce83a308fb99092d00 /spec
parent4914e4d7f638b7771ecdaf24ee0516c05505697c (diff)
downloadthermoprint-3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7.tar
thermoprint-3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7.tar.gz
thermoprint-3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7.tar.bz2
thermoprint-3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7.tar.xz
thermoprint-3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7.zip
prototype: BBCode.DomForest -> Printout
Diffstat (limited to 'spec')
-rw-r--r--spec/src/Thermoprint/Printout/BBCode.hs95
1 files changed, 78 insertions, 17 deletions
diff --git a/spec/src/Thermoprint/Printout/BBCode.hs b/spec/src/Thermoprint/Printout/BBCode.hs
index cee36b8..8df70c0 100644
--- a/spec/src/Thermoprint/Printout/BBCode.hs
+++ b/spec/src/Thermoprint/Printout/BBCode.hs
@@ -1,5 +1,6 @@
1{-# LANGUAGE OverloadedStrings #-} 1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE DeriveGeneric #-} 2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE GADTs #-}
3 4
4-- | Use 'Text.BBCode' to parse BBCode 5-- | Use 'Text.BBCode' to parse BBCode
5module Thermoprint.Printout.BBCode 6module Thermoprint.Printout.BBCode
@@ -12,11 +13,14 @@ module Thermoprint.Printout.BBCode
12import Data.Text (Text) 13import Data.Text (Text)
13import qualified Data.Text as T (unpack) 14import qualified Data.Text as T (unpack)
14 15
16import qualified Data.Text.Lazy as Lazy (Text)
17import qualified Data.Text.Lazy as TL (fromStrict)
18
15import Data.Map (Map) 19import Data.Map (Map)
16import qualified Data.Map as Map (lookup) 20import qualified Data.Map as Map (lookup)
17 21
18import Data.Sequence (Seq) 22import Data.Sequence (Seq)
19import qualified Data.Sequence as Seq () 23import qualified Data.Sequence as Seq (fromList, singleton)
20 24
21import Data.CaseInsensitive (CI) 25import Data.CaseInsensitive (CI)
22import qualified Data.CaseInsensitive as CI 26import qualified Data.CaseInsensitive as CI
@@ -32,7 +36,7 @@ import Text.Read (readMaybe)
32 36
33import Data.Maybe (fromMaybe) 37import Data.Maybe (fromMaybe)
34 38
35import Text.BBCode (DomTree(..), TreeError(..)) 39import Text.BBCode (DomForest, DomTree(..), TreeError(..))
36import qualified Text.BBCode as Raw (bbcode, BBCodeError(..)) 40import qualified Text.BBCode as Raw (bbcode, BBCodeError(..))
37 41
38import Thermoprint.Printout 42import Thermoprint.Printout
@@ -45,30 +49,87 @@ data BBCodeError = LexerError String -- ^ Error while parsing input to stream of
45 49
46instance Exception BBCodeError 50instance Exception BBCodeError
47 51
52morph' :: Raw.BBCodeError -> BBCodeError
53-- ^ Transform 'Raw.BBCodeError' to 'BBCodeError'
54morph' (Raw.LexerError x) = LexerError x
55morph' (Raw.TreeError x) = TreeError x
56
48-- ^ An error ocurred while parsing the DOM-Forest (`['DomTree']`) 57-- ^ An error ocurred while parsing the DOM-Forest (`['DomTree']`)
49data SemanticError = UnmappedTag Text -- ^ An `Element` does not map to any structure in the context it occurred in 58data SemanticError = BlockInLineContext -- ^ A 'Block' structure was encountered when a 'Line' was expected
59 | LineInBlockContext -- ^ A 'Line' structure was encountered when a 'Block' was expected
60 | UnmappedBlockElement Text -- ^ We encountered an 'Element' that, in a 'Block' context, does not map to any structure
61 | UnmappedLineElement Text -- ^ We encountered an 'Element' that, in a 'Line' context, does not map to any structure
50 deriving (Show, Eq, Generic, Typeable) 62 deriving (Show, Eq, Generic, Typeable)
51 63
52instance Exception SemanticError 64instance Exception SemanticError
53 65
66-- | Result of parsing a single 'DomTree'
67data ParseResult = RBlock Block -- ^ Parses only as 'Block'
68 | RLine Line -- ^ Parses only as 'Line'
69 | RAmbiguous Block Line -- ^ Parses as either 'Block' or 'Line' depending on context
70 | RNoParse SemanticError SemanticError -- ^ Does not parse as either 'Block' or 'Line'
71
72-- | Current parser context
73data Context a where
74 BlockCtx :: Context Block -- ^ Parsing 'Block's
75 LineCtx :: Context Line -- ^ Parsing 'Line's
76
77extract :: Context a -> ParseResult -> Either SemanticError a
78-- ^ Extract information from a 'ParseResult' given 'Context'
79extract BlockCtx (RBlock b) = Right b
80extract LineCtx (RLine l) = Right l
81extract BlockCtx (RAmbiguous b _) = Right b
82extract LineCtx (RAmbiguous _ l) = Right l
83extract BlockCtx (RNoParse bErr _) = Left bErr
84extract LineCtx (RNoParse _ lErr) = Left lErr
85extract BlockCtx _ = Left LineInBlockContext
86extract LineCtx _ = Left BlockInLineContext
87
54bbcode :: Text -> Either BBCodeError Printout 88bbcode :: Text -> Either BBCodeError Printout
55-- ^ Parse BBCode 89-- ^ Parse BBCode
56bbcode = join . fmap (first SemanticError) . bimap morph' morph . Raw.bbcode 90bbcode = join . fmap (first SemanticError) . bimap morph' morph . Raw.bbcode
57 91
58morph' :: Raw.BBCodeError -> BBCodeError 92morph :: DomForest -> Either SemanticError Printout
59morph' (Raw.LexerError x) = LexerError x 93-- ^ Parse a list of paragraphs
60morph' (Raw.TreeError x) = TreeError x 94--
61 95-- Since we permit only cooked input via 'Raw' 'Paragraph' is identical to 'Block'
62morph :: [DomTree] -> Either SemanticError Printout 96morph f = Seq.fromList <$> mapM (\t -> Seq.singleton . Cooked <$> parse BlockCtx t) f
63morph = undefined 97
64 98parseDom :: DomTree -> ParseResult
65asBlock :: CI Text -> Map (CI Text) Text -> Either SemanticError Block 99-- ^ Invoke 'asLine' and 'asBlock' to parse a single 'DomTree'
66asBlock "VSpace" = Right . VSpace . lookupAttr "size" 1 100parseDom (Content t) = either RBlock (\l -> RAmbiguous (Line l) l) . text . TL.fromStrict $ t
67asBlock t = Left . const (UnmappedTag . CI.original $ t) 101parseDom (Element t attrs cs)
68 102 | Right blockParse' <- blockParse
69asLine :: CI Text -> Map (CI Text) Text -> Either SemanticError Line 103 , Right lineParse' <- lineParse = RAmbiguous blockParse' lineParse'
70asLine "HSpace" = Right . HSpace . lookupAttr "size" 1 104 | Right blockParse' <- blockParse = RBlock blockParse'
71asLine t = Left . const (UnmappedTag . CI.original $ t) 105 | Right lineParse' <- lineParse = RLine lineParse'
106 | Left bErr <- blockParse
107 , Left lErr <- lineParse = RNoParse bErr lErr
108 where
109 blockParse = asBlock t cs attrs
110 lineParse = asLine t cs attrs
111
112mergeResult :: Monoid a => Context a -> [ParseResult] -> Either SemanticError a
113-- ^ Merge a list of 'ParseResults' in a certain 'Context'
114mergeResult _ [] = Right mempty
115mergeResult BlockCtx xs@((RLine _):_) = bimap (const LineInBlockContext) Line $ mergeResult LineCtx xs
116mergeResult ctx (amb@(RAmbiguous _ _):xs) = mappend <$> extract ctx amb <*> mergeResult ctx xs
117mergeResult ctx (err@(RNoParse _ _):_) = extract ctx err
118mergeResult ctx (x:xs) = mappend <$> extract ctx x <*> mergeResult ctx xs
119
120parse :: Monoid a => Context a -> [DomTree] -> Either SemanticError a
121-- ^ Parse a list of 'DomTree's in a certain 'Context'
122--
123-- @parse ctx = 'mergeResult' ctx . map 'parseDom'@
124parse ctx = mergeResult ctx . map parseDom
72 125
73lookupAttr :: Read a => CI Text -> a -> Map (CI Text) Text -> a 126lookupAttr :: Read a => CI Text -> a -> Map (CI Text) Text -> a
74lookupAttr t def attrs = fromMaybe def $ Map.lookup t attrs >>= (readMaybe . T.unpack) 127lookupAttr t def attrs = fromMaybe def $ Map.lookup t attrs >>= (readMaybe . T.unpack)
128
129asBlock :: CI Text -> [DomTree] -> Map (CI Text) Text -> Either SemanticError Block
130asBlock "VSpace" [] = Right . VSpace . lookupAttr "height" 1
131asBlock t _ = const $ Left . UnmappedBlockElement . CI.original $ t
132
133asLine :: CI Text -> [DomTree] -> Map (CI Text) Text -> Either SemanticError Line
134asLine "HSpace" [] = Right . HSpace . lookupAttr "width" 1
135asLine t _ = const $ Left . UnmappedLineElement . CI.original $ t