diff options
Diffstat (limited to 'src/Text/Pandoc/Writers/Custom.hs')
| -rw-r--r-- | src/Text/Pandoc/Writers/Custom.hs | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/Text/Pandoc/Writers/Custom.hs b/src/Text/Pandoc/Writers/Custom.hs new file mode 100644 index 000000000..cf641dcd6 --- /dev/null +++ b/src/Text/Pandoc/Writers/Custom.hs @@ -0,0 +1,322 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} +{-# LANGUAGE FlexibleInstances, OverloadedStrings, + ScopedTypeVariables, DeriveDataTypeable, CPP #-} +#if MIN_VERSION_base(4,8,0) +#else +{-# LANGUAGE OverlappingInstances #-} +#endif +{- Copyright (C) 2012-2015 John MacFarlane <jgm@berkeley.edu> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-} + +{- | + Module : Text.Pandoc.Writers.Custom + Copyright : Copyright (C) 2012-2015 John MacFarlane + License : GNU GPL, version 2 or above + + Maintainer : John MacFarlane <jgm@berkeley.edu> + Stability : alpha + Portability : portable + +Conversion of 'Pandoc' documents to custom markup using +a lua writer. +-} +module Text.Pandoc.Writers.Custom ( writeCustom ) where +import Text.Pandoc.Definition +import Text.Pandoc.Options +import Data.List ( intersperse ) +import Data.Char ( toLower ) +import Data.Typeable +import Scripting.Lua (LuaState, StackValue, callfunc) +import Text.Pandoc.Writers.Shared +import qualified Scripting.Lua as Lua +import qualified Text.Pandoc.UTF8 as UTF8 +import Control.Monad (when) +import Control.Exception +import qualified Data.Map as M +import Text.Pandoc.Templates +import GHC.IO.Encoding (getForeignEncoding,setForeignEncoding, utf8) + +attrToMap :: Attr -> M.Map String String +attrToMap (id',classes,keyvals) = M.fromList + $ ("id", id') + : ("class", unwords classes) + : keyvals + +#if MIN_VERSION_hslua(0,4,0) +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue [Char] where +#else +instance StackValue [Char] where +#endif + push lua cs = Lua.push lua (UTF8.fromString cs) + peek lua i = do + res <- Lua.peek lua i + return $ UTF8.toString `fmap` res + valuetype _ = Lua.TSTRING +#else +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue a => StackValue [a] where +#else +instance StackValue a => StackValue [a] where +#endif + push lua xs = do + Lua.createtable lua (length xs + 1) 0 + let addValue (i, x) = Lua.push lua x >> Lua.rawseti lua (-2) i + mapM_ addValue $ zip [1..] xs + peek lua i = do + top <- Lua.gettop lua + let i' = if i < 0 then top + i + 1 else i + Lua.pushnil lua + lst <- getList lua i' + Lua.pop lua 1 + return (Just lst) + valuetype _ = Lua.TTABLE + +getList :: StackValue a => LuaState -> Int -> IO [a] +getList lua i' = do + continue <- Lua.next lua i' + if continue + then do + next <- Lua.peek lua (-1) + Lua.pop lua 1 + x <- maybe (fail "peek returned Nothing") return next + rest <- getList lua i' + return (x : rest) + else return [] +#endif + +instance StackValue Format where + push lua (Format f) = Lua.push lua (map toLower f) + peek l n = fmap Format `fmap` Lua.peek l n + valuetype _ = Lua.TSTRING + +instance (StackValue a, StackValue b) => StackValue (M.Map a b) where + push lua m = do + let xs = M.toList m + Lua.createtable lua (length xs + 1) 0 + let addValue (k, v) = Lua.push lua k >> Lua.push lua v >> + Lua.rawset lua (-3) + mapM_ addValue xs + peek _ _ = undefined -- not needed for our purposes + valuetype _ = Lua.TTABLE + +instance (StackValue a, StackValue b) => StackValue (a,b) where + push lua (k,v) = do + Lua.createtable lua 2 0 + Lua.push lua k + Lua.push lua v + Lua.rawset lua (-3) + peek _ _ = undefined -- not needed for our purposes + valuetype _ = Lua.TTABLE + +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue [Inline] where +#else +instance StackValue [Inline] where +#endif + push l ils = Lua.push l =<< inlineListToCustom l ils + peek _ _ = undefined + valuetype _ = Lua.TSTRING + +#if MIN_VERSION_base(4,8,0) +instance {-# OVERLAPS #-} StackValue [Block] where +#else +instance StackValue [Block] where +#endif + push l ils = Lua.push l =<< blockListToCustom l ils + peek _ _ = undefined + valuetype _ = Lua.TSTRING + +instance StackValue MetaValue where + push l (MetaMap m) = Lua.push l m + push l (MetaList xs) = Lua.push l xs + push l (MetaBool x) = Lua.push l x + push l (MetaString s) = Lua.push l s + push l (MetaInlines ils) = Lua.push l ils + push l (MetaBlocks bs) = Lua.push l bs + peek _ _ = undefined + valuetype (MetaMap _) = Lua.TTABLE + valuetype (MetaList _) = Lua.TTABLE + valuetype (MetaBool _) = Lua.TBOOLEAN + valuetype (MetaString _) = Lua.TSTRING + valuetype (MetaInlines _) = Lua.TSTRING + valuetype (MetaBlocks _) = Lua.TSTRING + +instance StackValue Citation where + push lua cit = do + Lua.createtable lua 6 0 + let addValue (k :: String, v) = Lua.push lua k >> Lua.push lua v >> + Lua.rawset lua (-3) + addValue ("citationId", citationId cit) + addValue ("citationPrefix", citationPrefix cit) + addValue ("citationSuffix", citationSuffix cit) + addValue ("citationMode", show (citationMode cit)) + addValue ("citationNoteNum", citationNoteNum cit) + addValue ("citationHash", citationHash cit) + peek = undefined + valuetype _ = Lua.TTABLE + +data PandocLuaException = PandocLuaException String + deriving (Show, Typeable) + +instance Exception PandocLuaException + +-- | Convert Pandoc to custom markup. +writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO String +writeCustom luaFile opts doc@(Pandoc meta _) = do + luaScript <- UTF8.readFile luaFile + enc <- getForeignEncoding + setForeignEncoding utf8 + lua <- Lua.newstate + Lua.openlibs lua + status <- Lua.loadstring lua luaScript luaFile + -- check for error in lua script (later we'll change the return type + -- to handle this more gracefully): + when (status /= 0) $ +#if MIN_VERSION_hslua(0,4,0) + Lua.tostring lua 1 >>= throw . PandocLuaException . UTF8.toString +#else + Lua.tostring lua 1 >>= throw . PandocLuaException +#endif + Lua.call lua 0 0 + -- TODO - call hierarchicalize, so we have that info + rendered <- docToCustom lua opts doc + context <- metaToJSON opts + (blockListToCustom lua) + (inlineListToCustom lua) + meta + Lua.close lua + setForeignEncoding enc + let body = rendered + case writerTemplate opts of + Nothing -> return body + Just tpl -> return $ renderTemplate' tpl $ setField "body" body context + +docToCustom :: LuaState -> WriterOptions -> Pandoc -> IO String +docToCustom lua opts (Pandoc (Meta metamap) blocks) = do + body <- blockListToCustom lua blocks + callfunc lua "Doc" body metamap (writerVariables opts) + +-- | Convert Pandoc block element to Custom. +blockToCustom :: LuaState -- ^ Lua state + -> Block -- ^ Block element + -> IO String + +blockToCustom _ Null = return "" + +blockToCustom lua (Plain inlines) = callfunc lua "Plain" inlines + +blockToCustom lua (Para [Image attr txt (src,tit)]) = + callfunc lua "CaptionedImage" src tit txt (attrToMap attr) + +blockToCustom lua (Para inlines) = callfunc lua "Para" inlines + +blockToCustom lua (LineBlock linesList) = callfunc lua "LineBlock" linesList + +blockToCustom lua (RawBlock format str) = + callfunc lua "RawBlock" format str + +blockToCustom lua HorizontalRule = callfunc lua "HorizontalRule" + +blockToCustom lua (Header level attr inlines) = + callfunc lua "Header" level inlines (attrToMap attr) + +blockToCustom lua (CodeBlock attr str) = + callfunc lua "CodeBlock" str (attrToMap attr) + +blockToCustom lua (BlockQuote blocks) = callfunc lua "BlockQuote" blocks + +blockToCustom lua (Table capt aligns widths headers rows') = + callfunc lua "Table" capt (map show aligns) widths headers rows' + +blockToCustom lua (BulletList items) = callfunc lua "BulletList" items + +blockToCustom lua (OrderedList (num,sty,delim) items) = + callfunc lua "OrderedList" items num (show sty) (show delim) + +blockToCustom lua (DefinitionList items) = + callfunc lua "DefinitionList" items + +blockToCustom lua (Div attr items) = + callfunc lua "Div" items (attrToMap attr) + +-- | Convert list of Pandoc block elements to Custom. +blockListToCustom :: LuaState -- ^ Options + -> [Block] -- ^ List of block elements + -> IO String +blockListToCustom lua xs = do + blocksep <- callfunc lua "Blocksep" + bs <- mapM (blockToCustom lua) xs + return $ mconcat $ intersperse blocksep bs + +-- | Convert list of Pandoc inline elements to Custom. +inlineListToCustom :: LuaState -> [Inline] -> IO String +inlineListToCustom lua lst = do + xs <- mapM (inlineToCustom lua) lst + return $ concat xs + +-- | Convert Pandoc inline element to Custom. +inlineToCustom :: LuaState -> Inline -> IO String + +inlineToCustom lua (Str str) = callfunc lua "Str" str + +inlineToCustom lua Space = callfunc lua "Space" + +inlineToCustom lua SoftBreak = callfunc lua "SoftBreak" + +inlineToCustom lua (Emph lst) = callfunc lua "Emph" lst + +inlineToCustom lua (Strong lst) = callfunc lua "Strong" lst + +inlineToCustom lua (Strikeout lst) = callfunc lua "Strikeout" lst + +inlineToCustom lua (Superscript lst) = callfunc lua "Superscript" lst + +inlineToCustom lua (Subscript lst) = callfunc lua "Subscript" lst + +inlineToCustom lua (SmallCaps lst) = callfunc lua "SmallCaps" lst + +inlineToCustom lua (Quoted SingleQuote lst) = callfunc lua "SingleQuoted" lst + +inlineToCustom lua (Quoted DoubleQuote lst) = callfunc lua "DoubleQuoted" lst + +inlineToCustom lua (Cite cs lst) = callfunc lua "Cite" lst cs + +inlineToCustom lua (Code attr str) = + callfunc lua "Code" str (attrToMap attr) + +inlineToCustom lua (Math DisplayMath str) = + callfunc lua "DisplayMath" str + +inlineToCustom lua (Math InlineMath str) = + callfunc lua "InlineMath" str + +inlineToCustom lua (RawInline format str) = + callfunc lua "RawInline" format str + +inlineToCustom lua (LineBreak) = callfunc lua "LineBreak" + +inlineToCustom lua (Link attr txt (src,tit)) = + callfunc lua "Link" txt src tit (attrToMap attr) + +inlineToCustom lua (Image attr alt (src,tit)) = + callfunc lua "Image" alt src tit (attrToMap attr) + +inlineToCustom lua (Note contents) = callfunc lua "Note" contents + +inlineToCustom lua (Span attr items) = + callfunc lua "Span" items (attrToMap attr) |
