packages feed

ideas-1.7: src/Ideas/Text/MathML.hs

-----------------------------------------------------------------------------
-- Copyright 2018, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------

module Ideas.Text.MathML
   ( MathML(..), xml2mathml, mathml2xml
   ) where

import Data.Either
import Data.Maybe
import Ideas.Text.XML
import Ideas.Utils.Uniplate hiding (children)

data MathML = MRow [MathML]
            | MId String
            | MNumber String
            | MOperator String
            | MString String
            | MText String
            | MSqrt MathML
            | MRoot MathML MathML
            | MSup MathML MathML -- base, superscript
            | MSub MathML MathML -- base, subscript
            | MSubSup MathML MathML MathML -- base, subscript, superscript
            | MFrac MathML MathML -- numerator, denominator
            | MFenced String String MathML -- left, right, content
            | MSpace
            | MStyle
            | MPadded
            | MPhantom
            | MError
            | MEnclose
            | MUnder
            | MOver
            | MUnderOver
            | MTable
            | MTableRow
            | MLabeledTableRow
            | MTableData
   deriving (Show, Eq)
{-
from: https://www.w3.org/TR/MathML2/chapter3.html#presm.mn @ 3.2.1
Conversely, since mn is a presentation element, there are a few situations where it may desirable to include arbitrary text in the content of an mn that should merely render as a numeric literal
-}

instance InXML MathML where
   toXML   = mathml2xml
   fromXML = either fail return . xml2mathml

instance Uniplate MathML where
   uniplate math =
      case math of
         MRow xs       -> plate MRow ||* xs
         _             -> plate math

----------------------------------------------------------
-- conversion functions: XML <-> MathML

xml2mathml :: XML -> Either String MathML
xml2mathml = rec
 where
   rec xml = case xml of
       Element "mrow" _ _ -> MRow <$> mapM rec (children xml)
       Element "mi" _ [Left s] -> return (MId s)
       Element "mn" _ [Left s] -> return (MNumber s)
       Element "mo" _ [Left s] -> return (MOperator s)
       Element "ms" _ [Left s] -> return (MString s)
       Element "mtext" _ [] -> return (MText "")
       Element "mtext" _ [Left s] -> return (MText s)
       Element "mroot" _ [Right c, Right d] -> MRoot <$> rec c <*> rec d
       Element "msup" _ [Right c, Right d] -> MSup <$> rec c <*> rec d
       Element "msub" _ [Right c, Right d] -> MSub <$> rec c <*> rec d
       Element "msubsup" _ [Right c, Right d, Right e] -> MSubSup <$> rec c <*> rec d <*> rec e
       Element "mfrac" _ [Right c, Right d] -> MFrac <$> rec c <*> rec d
       Element "mfenced" _ [Right c] -> MFenced (fromMaybe "(" (findAttribute "open" xml)) (fromMaybe ")" (findAttribute "close" xml)) <$> rec c
       Element "mspace" _ _ -> return MSpace
       Element "mtable" _ _ -> return MTable
       Element "mtr" _ _ -> return MTableRow
       Element "mlabeledtr" _ _ -> return MLabeledTableRow
       Element "munder" _ _ -> return MUnder
       Element "mover" _ _ -> return MOver
       Element "munderover" _ _ -> return MUnderOver
       -- below are cases that have 1* arguments, when none-one an mrow is implied.
       Element "math" _ xs -> impliedMRow xs
       Element "msqrt" _ xs -> MSqrt <$> impliedMRow xs
       Element "mphantom" _ _ -> return MPhantom
       Element "mpadded" _ _ -> return MPadded
       Element "mstyle" _ _ -> return MStyle
       Element "merror" _ _ -> return MError
       Element "mtd" _ _ -> return MTableData
       Element "menclose" _ _ -> return MEnclose
       _ -> fail ("unsupported MathML: " ++ show xml)
   impliedMRow :: [Either String Element] -> Either String MathML
   impliedMRow [Right r] = rec r
   impliedMRow xs = MRow <$> mapM rec (rights xs)

mathml2xml :: MathML -> XML
mathml2xml = makeXML "math" . rec
 where
   rec :: MathML -> XMLBuilder
   rec math =
      case math of
         MRow ms           -> element "mrow" (map rec ms)
         MId s             -> element "mi" [string s]
         MNumber s         -> element "mn" [string s]
         MOperator s       -> element "mo" [string s]
         MString s         -> element "ms" [string s]
         MText s           -> element "mtext" [string s]
         MSqrt m           -> element "msqrt" [rec m]
         MRoot m1 m2       -> element "mroot" [rec m1, rec m2]
         MSup m1 m2        -> element "msup" [rec m1, rec m2]
         MSub m1 m2        -> element "msub" [rec m1, rec m2]
         MSubSup m1 m2 m3  -> element "msubsup" [rec m1, rec m2, rec m3]
         MFrac m1 m2       -> element "mfrac" [rec m1, rec m2]
         MFenced s1 s2 m   -> element "mfenced" ["open" .=. s1, "close" .=. s2, rec m]
         MSpace            -> element "mspace" []
         MStyle            -> element "mstyle" []
         MPadded           -> element "mpadded" []
         MPhantom          -> element "mphantom" []
         MError            -> element "merror" []
         MEnclose          -> element "menclose" []
         MUnder            -> element "munder" []
         MOver             -> element "mover" []
         MUnderOver        -> element "munderover" []
         MTable            -> element "mtable" []
         MTableRow         -> element "mtr" []
         MLabeledTableRow  -> element "mlabeledtr" []
         MTableData        -> element "mtd" []