diff options
author | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:41 +0000 |
---|---|---|
committer | Gregor Kleen <gkleen@yggdrasil.li> | 2016-01-18 05:42:41 +0000 |
commit | 3c0383695b1c1e1a6f7b3f6811dacaa32577f9f7 (patch) | |
tree | 627ceb4f46748aafe95e5cce83a308fb99092d00 /spec | |
parent | 4914e4d7f638b7771ecdaf24ee0516c05505697c (diff) | |
download | thermoprint-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.hs | 95 |
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 |
5 | module Thermoprint.Printout.BBCode | 6 | module Thermoprint.Printout.BBCode |
@@ -12,11 +13,14 @@ module Thermoprint.Printout.BBCode | |||
12 | import Data.Text (Text) | 13 | import Data.Text (Text) |
13 | import qualified Data.Text as T (unpack) | 14 | import qualified Data.Text as T (unpack) |
14 | 15 | ||
16 | import qualified Data.Text.Lazy as Lazy (Text) | ||
17 | import qualified Data.Text.Lazy as TL (fromStrict) | ||
18 | |||
15 | import Data.Map (Map) | 19 | import Data.Map (Map) |
16 | import qualified Data.Map as Map (lookup) | 20 | import qualified Data.Map as Map (lookup) |
17 | 21 | ||
18 | import Data.Sequence (Seq) | 22 | import Data.Sequence (Seq) |
19 | import qualified Data.Sequence as Seq () | 23 | import qualified Data.Sequence as Seq (fromList, singleton) |
20 | 24 | ||
21 | import Data.CaseInsensitive (CI) | 25 | import Data.CaseInsensitive (CI) |
22 | import qualified Data.CaseInsensitive as CI | 26 | import qualified Data.CaseInsensitive as CI |
@@ -32,7 +36,7 @@ import Text.Read (readMaybe) | |||
32 | 36 | ||
33 | import Data.Maybe (fromMaybe) | 37 | import Data.Maybe (fromMaybe) |
34 | 38 | ||
35 | import Text.BBCode (DomTree(..), TreeError(..)) | 39 | import Text.BBCode (DomForest, DomTree(..), TreeError(..)) |
36 | import qualified Text.BBCode as Raw (bbcode, BBCodeError(..)) | 40 | import qualified Text.BBCode as Raw (bbcode, BBCodeError(..)) |
37 | 41 | ||
38 | import Thermoprint.Printout | 42 | import Thermoprint.Printout |
@@ -45,30 +49,87 @@ data BBCodeError = LexerError String -- ^ Error while parsing input to stream of | |||
45 | 49 | ||
46 | instance Exception BBCodeError | 50 | instance Exception BBCodeError |
47 | 51 | ||
52 | morph' :: Raw.BBCodeError -> BBCodeError | ||
53 | -- ^ Transform 'Raw.BBCodeError' to 'BBCodeError' | ||
54 | morph' (Raw.LexerError x) = LexerError x | ||
55 | morph' (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']`) |
49 | data SemanticError = UnmappedTag Text -- ^ An `Element` does not map to any structure in the context it occurred in | 58 | data 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 | ||
52 | instance Exception SemanticError | 64 | instance Exception SemanticError |
53 | 65 | ||
66 | -- | Result of parsing a single 'DomTree' | ||
67 | data 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 | ||
73 | data Context a where | ||
74 | BlockCtx :: Context Block -- ^ Parsing 'Block's | ||
75 | LineCtx :: Context Line -- ^ Parsing 'Line's | ||
76 | |||
77 | extract :: Context a -> ParseResult -> Either SemanticError a | ||
78 | -- ^ Extract information from a 'ParseResult' given 'Context' | ||
79 | extract BlockCtx (RBlock b) = Right b | ||
80 | extract LineCtx (RLine l) = Right l | ||
81 | extract BlockCtx (RAmbiguous b _) = Right b | ||
82 | extract LineCtx (RAmbiguous _ l) = Right l | ||
83 | extract BlockCtx (RNoParse bErr _) = Left bErr | ||
84 | extract LineCtx (RNoParse _ lErr) = Left lErr | ||
85 | extract BlockCtx _ = Left LineInBlockContext | ||
86 | extract LineCtx _ = Left BlockInLineContext | ||
87 | |||
54 | bbcode :: Text -> Either BBCodeError Printout | 88 | bbcode :: Text -> Either BBCodeError Printout |
55 | -- ^ Parse BBCode | 89 | -- ^ Parse BBCode |
56 | bbcode = join . fmap (first SemanticError) . bimap morph' morph . Raw.bbcode | 90 | bbcode = join . fmap (first SemanticError) . bimap morph' morph . Raw.bbcode |
57 | 91 | ||
58 | morph' :: Raw.BBCodeError -> BBCodeError | 92 | morph :: DomForest -> Either SemanticError Printout |
59 | morph' (Raw.LexerError x) = LexerError x | 93 | -- ^ Parse a list of paragraphs |
60 | morph' (Raw.TreeError x) = TreeError x | 94 | -- |
61 | 95 | -- Since we permit only cooked input via 'Raw' 'Paragraph' is identical to 'Block' | |
62 | morph :: [DomTree] -> Either SemanticError Printout | 96 | morph f = Seq.fromList <$> mapM (\t -> Seq.singleton . Cooked <$> parse BlockCtx t) f |
63 | morph = undefined | 97 | |
64 | 98 | parseDom :: DomTree -> ParseResult | |
65 | asBlock :: CI Text -> Map (CI Text) Text -> Either SemanticError Block | 99 | -- ^ Invoke 'asLine' and 'asBlock' to parse a single 'DomTree' |
66 | asBlock "VSpace" = Right . VSpace . lookupAttr "size" 1 | 100 | parseDom (Content t) = either RBlock (\l -> RAmbiguous (Line l) l) . text . TL.fromStrict $ t |
67 | asBlock t = Left . const (UnmappedTag . CI.original $ t) | 101 | parseDom (Element t attrs cs) |
68 | 102 | | Right blockParse' <- blockParse | |
69 | asLine :: CI Text -> Map (CI Text) Text -> Either SemanticError Line | 103 | , Right lineParse' <- lineParse = RAmbiguous blockParse' lineParse' |
70 | asLine "HSpace" = Right . HSpace . lookupAttr "size" 1 | 104 | | Right blockParse' <- blockParse = RBlock blockParse' |
71 | asLine 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 | |||
112 | mergeResult :: Monoid a => Context a -> [ParseResult] -> Either SemanticError a | ||
113 | -- ^ Merge a list of 'ParseResults' in a certain 'Context' | ||
114 | mergeResult _ [] = Right mempty | ||
115 | mergeResult BlockCtx xs@((RLine _):_) = bimap (const LineInBlockContext) Line $ mergeResult LineCtx xs | ||
116 | mergeResult ctx (amb@(RAmbiguous _ _):xs) = mappend <$> extract ctx amb <*> mergeResult ctx xs | ||
117 | mergeResult ctx (err@(RNoParse _ _):_) = extract ctx err | ||
118 | mergeResult ctx (x:xs) = mappend <$> extract ctx x <*> mergeResult ctx xs | ||
119 | |||
120 | parse :: 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'@ | ||
124 | parse ctx = mergeResult ctx . map parseDom | ||
72 | 125 | ||
73 | lookupAttr :: Read a => CI Text -> a -> Map (CI Text) Text -> a | 126 | lookupAttr :: Read a => CI Text -> a -> Map (CI Text) Text -> a |
74 | lookupAttr t def attrs = fromMaybe def $ Map.lookup t attrs >>= (readMaybe . T.unpack) | 127 | lookupAttr t def attrs = fromMaybe def $ Map.lookup t attrs >>= (readMaybe . T.unpack) |
128 | |||
129 | asBlock :: CI Text -> [DomTree] -> Map (CI Text) Text -> Either SemanticError Block | ||
130 | asBlock "VSpace" [] = Right . VSpace . lookupAttr "height" 1 | ||
131 | asBlock t _ = const $ Left . UnmappedBlockElement . CI.original $ t | ||
132 | |||
133 | asLine :: CI Text -> [DomTree] -> Map (CI Text) Text -> Either SemanticError Line | ||
134 | asLine "HSpace" [] = Right . HSpace . lookupAttr "width" 1 | ||
135 | asLine t _ = const $ Left . UnmappedLineElement . CI.original $ t | ||