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" []