{- Text.HTML.Chunks : simple templates with static safety Copyright (C) 2007 Matthew Sackman This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -- | This module parses the templates and produces Chunks. This should -- not normally be needed by end users. See the "Text.HTML.Chunks" -- module for details of the syntax of Chunks. module Text.HTML.Chunks.Parser (findChunks, Chunk(..), Content(..), isText, isVariable ) where import Text.ParserCombinators.Parsec import Data.Either data Chunk = Chunk String [Content] deriving (Show, Eq) data Content = Text String | Variable String deriving (Show, Eq) isText :: Content -> Bool isText (Text _) = True isText _ = False isVariable :: Content -> Bool isVariable (Variable _) = True isVariable _ = False data ParsingUnit = ChunkUnit Chunk | Chr Char | Var String deriving (Show, Eq) -- | Parse the supplied text looking for Chunks. If the supplied text -- does not consist of a single chunk (optionally with nested chunks -- within) then the result will be a Left with a parse error. findChunks :: String -> Either ParseError [Chunk] findChunks = runParser parseChunks () "Text.HTML.Chunks.Parser" parseChunks :: Parser [Chunk] parseChunks = do { name <- parseBegin ; bodyChunks <- try parseBody ; let (body, chunks) = buildBodyAndChunks bodyChunks [] [] ; return ((Chunk name body) : (reverse chunks)) } convertParseUnit :: ParsingUnit -> [Content] -> [Chunk] -> ([Content], [Chunk]) convertParseUnit (Chr c) ((Text t):contents) chunks = (((Text (c:t)):contents), chunks) convertParseUnit pu@(ChunkUnit _) contents chunks = convertParseUnit' pu contents chunks convertParseUnit pu ((Text t):contents) chunks = convertParseUnit' pu ((Text (reverse t)):contents) chunks convertParseUnit pu contents chunks = convertParseUnit' pu contents chunks convertParseUnit' :: ParsingUnit -> [Content] -> [Chunk] -> ([Content], [Chunk]) convertParseUnit' (Chr c) contents chunks = (((Text [c]):contents), chunks) convertParseUnit' (Var v) contents chunks = (((Variable v):contents), chunks) convertParseUnit' (ChunkUnit cu) contents chunks = (contents, (cu:chunks)) buildBodyAndChunks :: [ParsingUnit] -> [Content] -> [Chunk] -> ([Content], [Chunk]) buildBodyAndChunks [] ((Text t):body) chunks = (reverse ((Text (reverse t)):body), chunks) buildBodyAndChunks [] body chunks = (reverse body, chunks) buildBodyAndChunks (pu:pus) body chunks = buildBodyAndChunks pus body' chunks' where (body', chunks') = convertParseUnit pu body chunks parseBegin :: Parser String parseBegin = do { string "" ; return name } parseEnd :: Parser () parseEnd = do { string "" ; return () } parseVar :: Parser String parseVar = do { string "" ; return name } <|> do { string "##" ; name <- many1 (letter <|> char '_') ; string "##" ; return name } parseBody :: Parser [ParsingUnit] parseBody = do { try parseEnd ; return [] } <|> do { var <- try parseVar ; body <- parseBody ; return ((Var var) : body) } <|> do { chunks <- try parseChunks ; body <- parseBody ; return ((map ChunkUnit chunks) ++ body) } <|> do { c <- anyChar ; body <- parseBody ; return ((Chr c) : body) }