packages feed

orchid-0.0.6: src/Text/Document/Plugin.hs

module Text.Document.Plugin where

import Control.Monad (liftM)
import Control.Applicative ((<$>))
import Text.ParserCombinators.Parsec (GenParser)
import qualified Text.XML.Light as XL

import Text.Xml.Xml (Node)
import Misc.Commons (branch, fmap2)

{-
List of formats currently supported for both the parser and the pretty printer
side. Plugins for every format plugins might support parsing or printing.
-}

data Format =
    Wiki
  | XML
  | XHTML
  | LaTeX
  | ADT
  deriving (Eq, Show)

{-
Instead of only allowing text pretty printing for plugins a more flexible
Output type is used. This type should allow plugins to be more precise in their
output domain. E.g. XML/Xhtml pretty printer can simply out XML nodes and do
not have to worry about conversion to strings.
-}

data Output = 
    OutputPlain    String
  | OutputXML      Node        -- Do not use internal nodes.
  | OutputXMLLight XL.Content

data Context = ContextWiki Int
  deriving (Eq, Show)

{-
A plugin contains an identifying name and a transformation function. This
transformation function is indexed by a format identifier and might deliver a
parser for that format. The parse result is a pretty printer function indexed
by a format identifier as well, Given this format the function might deliver a
pretty printed string in that format. Due to these two `Maybe' types the plugin
may support any of the possible combinations of parsing and pretty printing for
arbitrary types.

Because the pretty printer will eventually end up in the document tree, it has
the plugin name included. This way the plugins can easily be identified,
compared or used for error reporting.

Non-pure function can make use of the processor computation that receives a
filepath they can use as a working directory. A bit clumsy, but that's the
burden of purity.
-}

type Parser doc = Format -> Maybe (Maybe Context -> GenParser Char () doc)

data PrettyPrinter doc =
  Printer {
    printerName :: String
  , printer     :: Format -> Maybe (doc -> Output)
  }

data Processor doc sem =
  Processor {
    processorName :: String
  , processor     :: Format -> Maybe (FilePath -> doc -> sem -> IO ())
  }

type Transformer doc sem = Parser (PrettyPrinter doc, Processor doc sem)

data Plugin doc sem =
  Plugin {
    pluginName  :: String
  , transformer :: Transformer doc sem
  }

{-
Plugins are only showable and comparable using there (hopefully) uniquely
identifying name.
-}

instance Eq (PrettyPrinter doc) where
  a == b = printerName a == printerName b

instance Eq (Processor doc sem) where
  a == b = processorName a == processorName b

{-
When a plugin support a pretty printer for the ADT format that produces a plain
string as output, this printer will be used as the show instance for this
plugin.
-}

instance Show (Processor doc sem) where
  show = processorName

instance Show (PrettyPrinter doc) where
  show = printerName

instance Eq (Plugin doc sem) where
  a == b = pluginName a == pluginName b

instance Show (Plugin doc sem) where
  show = pluginName

-------- plugin helper functions ----------------------------------------------

{-
Create a plugin transformer function from both a mapping from format
identifiers to general parsers and a mapping from format identifiers to pretty
printers. The pretty printers should take the result of the parsers.
-}

-- FIX:

makePlugin :: 
     String
  -> [(Format, Maybe Context -> GenParser Char () int)]
  -> [(Format, int -> doc -> Output)]
  -> [(Format, int -> FilePath -> doc -> sem -> IO ())]
  -> Plugin doc sem

makePlugin name ps pps ios = Plugin name $ \fmt -> do
  fmtParser <- lookup fmt ps
  let printer   int fmt' = lookup fmt' pps >>= return . ($int)
  let processor int fmt' = lookup fmt' ios >>= return . ($int)
  return (\a -> 
        fmap2 (Printer name) (Processor name)
    <$> branch printer processor
    <$> fmtParser a)