Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for column separators in arrays (#127). #168

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 9 additions & 1 deletion src/Text/TeXMath/Readers/MathML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,9 @@ table e = do
let (onlyAligns, exprs) = (map .map) fst &&& (map . map) snd $ rs
let rs' = map (pad (maximum (map length rs))) exprs
let aligns = map findAlign (transpose onlyAligns)
return $ EArray aligns rs'
let canonicalizeColSeps cs = take (length aligns-1) $ cycle cs -- e.g. expand columnlines="solid" to "solid solid solid" when there are 3 rows
colseps <- maybe (replicate (length aligns-1) CSNone) (canonicalizeColSeps . map toColSep . T.splitOn " ") <$> findAttrQ "columnlines" e
return $ EArray aligns rs' colseps
where
findAlign xs = if null xs then AlignCenter
else foldl1 combine xs
Expand Down Expand Up @@ -602,6 +604,12 @@ toAlignment "center" = AlignCenter
toAlignment "right" = AlignRight
toAlignment _ = AlignCenter

toColSep :: T.Text -> ColumnSeparator
toColSep "none" = CSNone
toColSep "dashed" = CSDashed
toColSep "solid" = CSSolid
toColSep _ = CSNone

getPosition :: FormType -> TeXSymbolType
getPosition (FPrefix) = Open
getPosition (FPostfix) = Close
Expand Down
4 changes: 2 additions & 2 deletions src/Text/TeXMath/Readers/OMML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ elemToExps' element | isElem "m" "eqArr" element =
let expLst = mapMaybe elemToBases (elChildren element)
expLst' = map (\es -> [map filterAmpersand es]) expLst
in
return [EArray [] expLst']
return [EArray [] expLst' []]
elemToExps' element | isElem "m" "f" element = do
num <- filterChildName (hasElemName "m" "num") element
den <- filterChildName (hasElemName "m" "den") element
Expand Down Expand Up @@ -365,7 +365,7 @@ elemToExps' element | isElem "m" "m" element =
(elChildren mr))
rows
in
return [EArray [AlignCenter] rowExps]
return [EArray [AlignCenter] rowExps []]
elemToExps' element | isElem "m" "nary" element = do
let naryPr = filterChildName (hasElemName "m" "naryPr") element
naryChr = naryPr >>=
Expand Down
40 changes: 23 additions & 17 deletions src/Text/TeXMath/Readers/TeX.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
module Text.TeXMath.Readers.TeX (readTeX)
where

import Data.List (intercalate, intersperse, find)
import Data.List (intercalate, intersperse, find, elemIndices)
import Data.Ratio ((%))
import Control.Monad
import Data.Char (isDigit, isAscii, isLetter)
Expand Down Expand Up @@ -250,7 +250,7 @@ genfrac _ = mzero
substack :: Text -> TP Exp
substack "\\substack" = do
formulas <- braces $ ignorable >> (manyExp expr) `sepEndBy` endLine
return $ EArray [AlignCenter] $ map (\x -> [[x]]) formulas
return $ EArray [AlignCenter] (map (\x -> [[x]]) formulas) []
substack _ = mzero

asGroup :: [Exp] -> Exp
Expand Down Expand Up @@ -372,14 +372,17 @@ arrayLine = notFollowedBy (ctrlseq "end" >> return '\n') >>
optional (try (ctrlseq "hline" >> ignorable'))
-- we don't represent the line, but it shouldn't crash parsing

arrayAlignments :: TP [Alignment]
arrayAlignments :: TP ([Alignment], [ColumnSeparator])
arrayAlignments = try $ do
as <- braces (many (letter <|> char '|'))
let letterToAlignment 'l' = AlignLeft
letterToAlignment 'c' = AlignCenter
letterToAlignment 'r' = AlignRight
letterToAlignment _ = AlignCenter
return $ map letterToAlignment $ filter (/= '|') as
-- this will omit borders for `|c|c|`, [0..n] should handle that (changing the colseps invariant from n-1 to n+1), at the cost of complicating mathML emission
let generate n f = map f [1..n-1]
let findColSeps = let seperatorPositions = elemIndices '|' as in generate (length as-1) (\i -> if i `elem` seperatorPositions then CSSolid else CSNone)
return $ (map letterToAlignment $ filter (/= '|') as, findColSeps)

environment :: Text -> TP Exp
environment "\\begin" = do
Expand Down Expand Up @@ -421,29 +424,31 @@ environments = M.fromList
, ("equation", equation)
]

alignsFromRows :: Alignment -> [ArrayLine] -> [Alignment]
alignsFromRows _ [] = []
alignsFromRows defaultAlignment (r:_) = replicate (length r) defaultAlignment
alignsFromRows :: Alignment -> ColumnSeparator -> [ArrayLine] -> ([Alignment], [ColumnSeparator])
alignsFromRows _ _ [] = ([], [])
alignsFromRows defaultAlignment defaultColSep (r:_) = let n = length r in (replicate n defaultAlignment, replicate (n-1) defaultColSep)

matrixWith :: Text -> Text -> TP Exp
matrixWith opendelim closedelim = do
lines' <- sepEndBy1 arrayLine endLineAMS
let aligns = alignsFromRows AlignCenter lines'
let (aligns, colseps) = alignsFromRows AlignCenter CSNone lines'
return $ if T.null opendelim && T.null closedelim
then EArray aligns lines'
then EArray aligns lines' colseps
else EDelimited opendelim closedelim
[Right $ EArray aligns lines']
[Right $ EArray aligns lines' colseps]

stdarray :: TP Exp
stdarray = do
aligns <- arrayAlignments
(aligns, colseps) <- arrayAlignments
--aligns <- arrayAlignments
lines' <- sepEndBy1 arrayLine endLine
return $ EArray aligns lines'
return $ EArray aligns lines' colseps

gather :: TP Exp
gather = do
rows <- sepEndBy arrayLine endLineAMS
return $ EArray (alignsFromRows AlignCenter rows) rows
let (aligns, colseps) = (alignsFromRows AlignCenter CSNone rows)
return $ EArray aligns rows colseps

equation :: TP Exp
equation = do
Expand All @@ -454,24 +459,25 @@ eqnarray :: TP Exp
eqnarray = do
rows <- sepEndBy1 arrayLine endLine
let n = maximum $ map length rows
return $ EArray (take n $ cycle [AlignRight, AlignCenter, AlignLeft]) rows
return $ EArray (take n $ cycle [AlignRight, AlignCenter, AlignLeft]) rows (replicate (n-1) CSNone)

align :: TP Exp
align = do
rows <- sepEndBy1 arrayLine endLineAMS
let n = maximum $ map length rows
return $ EArray (take n $ cycle [AlignRight, AlignLeft]) rows
return $ EArray (take n $ cycle [AlignRight, AlignLeft]) rows (replicate (n-1) CSNone)

flalign :: TP Exp
flalign = do
rows <- sepEndBy1 arrayLine endLineAMS
let n = maximum $ map length rows
return $ EArray (take n $ cycle [AlignLeft, AlignRight]) rows
return $ EArray (take n $ cycle [AlignLeft, AlignRight]) rows (replicate (n-1) CSNone)

cases :: TP Exp
cases = do
rs <- sepEndBy1 arrayLine endLineAMS
return $ EDelimited "{" "" [Right $ EArray (alignsFromRows AlignLeft rs) rs]
let (aligns, colseps) = (alignsFromRows AlignLeft CSNone rs)
return $ EDelimited "{" "" [Right $ EArray aligns rs colseps]

variable :: TP Exp
variable = do
Expand Down
12 changes: 8 additions & 4 deletions src/Text/TeXMath/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

module Text.TeXMath.Types (Exp(..), TeXSymbolType(..), ArrayLine,
FractionType(..), TextType(..),
Alignment(..), DisplayType(..),
Alignment(..), ColumnSeparator(..), DisplayType(..),
Operator(..), FormType(..), Record(..),
Property, Position(..), Env, defaultEnv,
InEDelimited)
Expand All @@ -38,6 +38,9 @@ data TeXSymbolType = Ord | Op | Bin | Rel | Open | Close | Pun | Accent
data Alignment = AlignLeft | AlignCenter | AlignRight
deriving (Show, Read, Eq, Ord, Data, Typeable)

data ColumnSeparator = CSNone | CSDashed | CSSolid
deriving (Show, Read, Eq, Ord, Data, Typeable)

data FractionType = NormalFrac -- ^ Displayed or textual, acc to 'DisplayType'
| DisplayFrac -- ^ Force display mode
| InlineFrac -- ^ Force inline mode (textual)
Expand Down Expand Up @@ -85,10 +88,11 @@ data Exp =
| ESqrt Exp -- ^ A square root.
| EScaled Rational Exp -- ^ An expression that is scaled to some factor
-- of its normal size.
| EArray [Alignment] [ArrayLine] -- ^ An array or matrix. The first argument
| EArray [Alignment] [ArrayLine] [ColumnSeparator] -- ^ An array or matrix. The first argument
-- specifies the alignments of the columns; the second gives
-- the contents of the lines. All of these lists should be
-- the same length.
-- the contents of the lines; the third gives the type of separator between
-- columns. The first two of these lists should be the same length, the last
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite right, is it? Rather, each ArrayLine should be the same length as the list of alignments.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually, I'd prefer

EArray [Alignment] [ColumnSeparator] [ArrayLine]

with the content at the end and the "form" at the beginning.

-- be one element shorter.
| EText TextType T.Text -- ^ Some normal text, possibly styled.
| EStyled TextType [Exp] -- ^ A group of styled expressions.
deriving (Show, Read, Eq, Ord, Data, Typeable)
Expand Down
2 changes: 1 addition & 1 deletion src/Text/TeXMath/Writers/Eqn.hs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ writeExp (EStyled ttype es) =
TextBold -> "bold " <> contents
TextBoldItalic -> "bold italic " <> contents
_ -> contents
writeExp (EArray aligns rows) =
writeExp (EArray aligns rows _) = -- TODO: does \matrix support column separators?
"matrix{\n" <> T.concat cols <> "}"
where cols = zipWith tocol aligns (transpose rows)
tocol al cs =
Expand Down
29 changes: 20 additions & 9 deletions src/Text/TeXMath/Writers/MathML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,25 @@ makeText a s = case (leadingSp, trailingSp) of
_ -> False
attr = getMMLType a

makeArray :: TextType -> [Alignment] -> [ArrayLine] -> Element
makeArray tt as ls = unode "mtable" $
map (unode "mtr" .
zipWith (\a -> setAlignment a . unode "mtd". map (showExp tt)) as') ls
where setAlignment AlignLeft = withAttribute "columnalign" "left"
setAlignment AlignRight = withAttribute "columnalign" "right"
setAlignment AlignCenter = withAttribute "columnalign" "center"
as' = as ++ cycle [AlignCenter]
-- "In this context, a single value specifies the value to be used for all rows (resp., columns or gaps)."
-- https://www.w3.org/TR/MathML3/chapter3.html#presm.mtable
dedupMtableAttribute :: Eq a => String -> a -> (a -> T.Text) -> [a] -> Element -> Element
dedupMtableAttribute attrName defaultValue renderValue values = case values of
_ | all (== defaultValue) values -> id -- handles [] as well as just a list of the default
(v:vs) | all (== v) vs -> withAttribute attrName (renderValue v)
_ -> withAttribute attrName $ T.intercalate " " (map renderValue values)

makeArray :: TextType -> [Alignment] -> [ArrayLine] -> [ColumnSeparator] -> Element
makeArray tt as ls cs = setColumnLines . setAlignments $ unode "mtable" $
map (unode "mtr" . map (unode "mtd" . map (showExp tt))) ls
where setAlignments = dedupMtableAttribute "columnalign" AlignCenter renderAlignment as
setColumnLines = dedupMtableAttribute "columnlines" CSNone renderColSep cs
renderAlignment AlignLeft = "left"
renderAlignment AlignRight = "right"
renderAlignment AlignCenter = "center"
renderColSep CSNone = "none"
renderColSep CSDashed = "dashed"
renderColSep CSSolid = "solid"

-- Kept as String for Text.XML.Light
withAttribute :: String -> T.Text -> Element -> Element
Expand Down Expand Up @@ -176,7 +187,7 @@ showExp tt e =
ESqrt x -> unode "msqrt" $ showExp tt x
ERoot i x -> unode "mroot" [showExp tt x, showExp tt i]
EScaled s x -> makeScaled s $ showExp tt x
EArray as ls -> makeArray tt as ls
EArray as ls cs -> makeArray tt as ls cs
EText a s -> makeText a s
EStyled a es -> makeStyled a $ map (showExp a) es

Expand Down
6 changes: 3 additions & 3 deletions src/Text/TeXMath/Writers/OMML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ maximum' :: [Int] -> Int
maximum' [] = 0
maximum' xs = maximum xs

makeArray :: [Element] -> [Alignment] -> [ArrayLine] -> Element
makeArray props as rs = mnode "m" $ mProps : map toMr rs
makeArray :: [Element] -> [Alignment] -> [ArrayLine] -> [ColumnSeparator] -> Element
makeArray props as rs _ = mnode "m" $ mProps : map toMr rs
where mProps = mnode "mPr"
[ mnodeA "baseJc" "center" ()
, mnodeA "plcHide" "1" ()
Expand Down Expand Up @@ -269,7 +269,7 @@ showExp props e =
, mnode "e" $ showExp props x]]
EBoxed x -> [mnode "borderBox" [ mnode "e" $ showExp props x]]
EScaled _ x -> showExp props x -- no support for scaler?
EArray as ls -> [makeArray props as ls]
EArray as ls cs -> [makeArray props as ls cs]
EText a s -> [makeText a s]
EStyled a es -> concatMap (showExp (setProps a)) es

Expand Down
2 changes: 1 addition & 1 deletion src/Text/TeXMath/Writers/Pandoc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ expToInlines tt (EOver convertible b e)
expToInlines _ (EUnderover _ _ _ _) = Nothing
expToInlines _ (EPhantom _) = Nothing
expToInlines _ (EBoxed _) = Nothing
expToInlines _ (EArray _ _) = Nothing
expToInlines _ (EArray _ _ _) = Nothing
41 changes: 22 additions & 19 deletions src/Text/TeXMath/Writers/TeX.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,25 +115,25 @@ writeExp (EDelimited "\x27E8" "\x27E9" [Right (EFraction NoLineFrac x y)]) = do
writeBinom "\\bangle" x y
writeExp (EDelimited open close [Right (EFraction NoLineFrac x y)]) = do
writeExp (EDelimited open close [Right (EArray [AlignCenter]
[[[x]],[[y]]])])
writeExp (EDelimited open close [Right (EArray aligns rows)]) = do
[[[x]],[[y]]] [])])
writeExp (EDelimited open close [Right (EArray aligns rows colseps)]) = do
env <- asks mathEnv
case ("amsmath" `elem` env, open, close) of
(True, "{", "") | aligns == [AlignLeft, AlignLeft] ->
table "cases" [] rows
table "cases" [] rows colseps
(True, "(", ")") | all (== AlignCenter) aligns ->
table "pmatrix" [] rows
table "pmatrix" [] rows colseps
(True, "[", "]") | all (== AlignCenter) aligns ->
table "bmatrix" [] rows
table "bmatrix" [] rows colseps
(True, "{", "}") | all (== AlignCenter) aligns ->
table "Bmatrix" [] rows
table "Bmatrix" [] rows colseps
(True, "\x2223", "\x2223") | all (== AlignCenter) aligns ->
table "vmatrix" [] rows
table "vmatrix" [] rows colseps
(True, "\x2225", "\x2225") | all (== AlignCenter) aligns ->
table "Vmatrix" [] rows
table "Vmatrix" [] rows colseps
_ -> do
writeDelim DLeft open
writeExp (EArray aligns rows)
writeExp (EArray aligns rows colseps)
writeDelim DRight close
writeExp (EDelimited open close es) = do
writeDelim DLeft open
Expand Down Expand Up @@ -247,30 +247,33 @@ writeExp (EStyled ttype es) = do
txtcmd <- (flip S.getLaTeXTextCommand ttype) <$> asks mathEnv
tell [ControlSeq txtcmd]
tellGroup (mapM_ writeExp $ everywhere (mkT (fromUnicode ttype)) es)
writeExp (EArray [AlignRight, AlignLeft] rows) = do
writeExp (EArray [AlignRight, AlignLeft] rows colseps) = do
env <- asks mathEnv
if "amsmath" `elem` env
then table "aligned" [] rows
else table "array" [AlignRight, AlignLeft] rows
writeExp (EArray aligns rows) = do
then table "aligned" [] rows colseps
else table "array" [AlignRight, AlignLeft] rows colseps
writeExp (EArray aligns rows colseps) = do
env <- asks mathEnv
if "amsmath" `elem` env && all (== AlignCenter) aligns
then table "matrix" [] rows
else table "array" aligns rows
then table "matrix" [] rows colseps
else table "array" aligns rows colseps

table :: T.Text -> [Alignment] -> [ArrayLine] -> Math ()
table name aligns rows = do
table :: T.Text -> [Alignment] -> [ArrayLine] -> [ColumnSeparator] -> Math ()
table name aligns rows colseps = do
tell [ControlSeq "\\begin", Grouped [Literal name]]
unless (null aligns) $
tell [Grouped [Literal columnAligns]]
tell [Token '\n']
mapM_ row rows
tell [ControlSeq "\\end", Grouped [Literal name]]
where
columnAligns = T.pack $ map alignmentToLetter aligns
columnAligns = T.pack . concat $ zipWith (\a c -> alignmentToLetter a:colsepToString c) aligns (colseps ++ [CSNone])
alignmentToLetter AlignLeft = 'l'
alignmentToLetter AlignCenter = 'c'
alignmentToLetter AlignRight = 'r'
colsepToString CSNone = ""
colsepToString CSDashed = "|" -- Lossy, CSDashed can only come from MathML input
colsepToString CSSolid = "|"

row :: ArrayLine -> Math ()
row [] = tell [Space, Literal "\\\\", Token '\n']
Expand Down Expand Up @@ -330,7 +333,7 @@ writeScript pos convertible b e1 = do

-- Replace an array with a substack if appropriate.
checkSubstack :: Exp -> Math ()
checkSubstack e@(EArray [AlignCenter] rows) = do
checkSubstack e@(EArray [AlignCenter] rows _) = do
env <- asks mathEnv
if "amsmath" `elem` env
then do
Expand Down
Loading