{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE OverloadedStrings          #-}
{- |
Module      : Text.Pandoc.Parsing.Math
Copyright   : © 2006-2023 John MacFarlane
License     : GPL-2.0-or-later
Maintainer  : John MacFarlane <jgm@berkeley.edu>

Parsing of LaTeX math.
-}

module Text.Pandoc.Parsing.Math
  ( mathDisplay
  , mathInline
  )
where

import Control.Monad (mzero, when)
import Data.Text (Text)
import Text.Parsec ((<|>), ParsecT, Stream(..), notFollowedBy, many1, try)
import Text.Pandoc.Options
  ( Extension(Ext_tex_math_dollars, Ext_tex_math_single_backslash,
              Ext_tex_math_double_backslash) )
import Text.Pandoc.Parsing.Capabilities (HasReaderOptions, guardEnabled)
import Text.Pandoc.Parsing.General
import Text.Pandoc.Shared (trimMath)
import Text.Pandoc.Sources
  (UpdateSourcePos, anyChar, char, digit, newline, satisfy, space, string)

import qualified Data.Text as T

mathInlineWith :: (Stream s m Char, UpdateSourcePos s Char)  => Text -> Text -> ParsecT s st m Text
mathInlineWith :: forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathInlineWith Text
op Text
cl = forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try forall a b. (a -> b) -> a -> b
$ do
  forall s (m :: * -> *) u.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> ParsecT s u m Text
textStr Text
op
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Text
op forall a. Eq a => a -> a -> Bool
== Text
"$") forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) t a u.
(Stream s m t, Show a) =>
ParsecT s u m a -> ParsecT s u m ()
notFollowedBy forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
space
  [Text]
words' <- forall end s (m :: * -> *) t st a.
(Show end, Stream s m t) =>
ParsecT s st m a -> ParsecT s st m end -> ParsecT s st m [a]
many1Till (
                       (Char -> Text
T.singleton forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
                          forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
(Char -> Bool) -> ParsecT s u m Char
satisfy (\Char
c -> Bool -> Bool
not (Char -> Bool
isSpaceChar Char
c Bool -> Bool -> Bool
|| Char
c forall a. Eq a => a -> a -> Bool
== Char
'\\')))
                   forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
char Char
'\\' forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
                           -- This next clause is needed because \text{..} can
                           -- contain $, \(\), etc.
                           (forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try (forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
[Char] -> ParsecT s u m [Char]
string [Char]
"text" forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
                                 ((Text
"\\text" forall a. Semigroup a => a -> a -> a
<>) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> Text -> ParsecT s st m Text
inBalancedBraces Int
0 Text
""))
                            forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|>  (\Char
c -> [Char] -> Text
T.pack [Char
'\\',Char
c]) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
anyChar))
                   forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (Text
"\n" forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Char
blankline forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall b s (m :: * -> *) a st.
(Show b, Stream s m a) =>
ParsecT s st m b -> ParsecT s st m ()
notFollowedBy' forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Char
blankline)
                   forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> ([Char] -> Text
T.pack forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many1 forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Char
spaceChar forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall s (m :: * -> *) t a u.
(Stream s m t, Show a) =>
ParsecT s u m a -> ParsecT s u m ()
notFollowedBy (forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
Char -> ParsecT s u m Char
char Char
'$'))
                    ) (forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) u.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> ParsecT s u m Text
textStr Text
cl)
  forall s (m :: * -> *) t a u.
(Stream s m t, Show a) =>
ParsecT s u m a -> ParsecT s u m ()
notFollowedBy forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
digit  -- to prevent capture of $5
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Text -> Text
trimMath forall a b. (a -> b) -> a -> b
$ [Text] -> Text
T.concat [Text]
words'
 where
  inBalancedBraces :: (Stream s m Char, UpdateSourcePos s Char) => Int -> Text -> ParsecT s st m Text
  inBalancedBraces :: forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> Text -> ParsecT s st m Text
inBalancedBraces Int
n = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Char] -> Text
T.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' Int
n forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Char]
T.unpack

  inBalancedBraces' :: (Stream s m Char, UpdateSourcePos s Char) => Int -> String -> ParsecT s st m String
  inBalancedBraces' :: forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' Int
0 [Char]
"" = do
    Char
c <- forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
anyChar
    if Char
c forall a. Eq a => a -> a -> Bool
== Char
'{'
       then forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' Int
1 [Char]
"{"
       else forall (m :: * -> *) a. MonadPlus m => m a
mzero
  inBalancedBraces' Int
0 [Char]
s = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse [Char]
s
  inBalancedBraces' Int
numOpen (Char
'\\':[Char]
xs) = do
    Char
c <- forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
anyChar
    forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' Int
numOpen (Char
cforall a. a -> [a] -> [a]
:Char
'\\'forall a. a -> [a] -> [a]
:[Char]
xs)
  inBalancedBraces' Int
numOpen [Char]
xs = do
    Char
c <- forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
anyChar
    case Char
c of
         Char
'}' -> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' (Int
numOpen forall a. Num a => a -> a -> a
- Int
1) (Char
cforall a. a -> [a] -> [a]
:[Char]
xs)
         Char
'{' -> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' (Int
numOpen forall a. Num a => a -> a -> a
+ Int
1) (Char
cforall a. a -> [a] -> [a]
:[Char]
xs)
         Char
_   -> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Int -> [Char] -> ParsecT s st m [Char]
inBalancedBraces' Int
numOpen (Char
cforall a. a -> [a] -> [a]
:[Char]
xs)

mathDisplayWith :: (Stream s m Char, UpdateSourcePos s Char) => Text -> Text -> ParsecT s st m Text
mathDisplayWith :: forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathDisplayWith Text
op Text
cl = forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Char] -> Text
T.pack forall a b. (a -> b) -> a -> b
$ do
  forall s (m :: * -> *) u.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> ParsecT s u m Text
textStr Text
op
  forall end s (m :: * -> *) t st a.
(Show end, Stream s m t) =>
ParsecT s st m a -> ParsecT s st m end -> ParsecT s st m [a]
many1Till (forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
(Char -> Bool) -> ParsecT s u m Char
satisfy (forall a. Eq a => a -> a -> Bool
/= Char
'\n') forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall (m :: * -> *) s u.
(Monad m, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s u m Char
newline forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* forall b s (m :: * -> *) a st.
(Show b, Stream s m a) =>
ParsecT s st m b -> ParsecT s st m ()
notFollowedBy' forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Char
blankline))
            (forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) u.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> ParsecT s u m Text
textStr Text
cl)

mathDisplay :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char)
            => ParsecT s st m Text
mathDisplay :: forall st s (m :: * -> *).
(HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Text
mathDisplay =
      (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_dollars forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathDisplayWith Text
"$$" Text
"$$")
  forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_single_backslash forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathDisplayWith Text
"\\[" Text
"\\]")
  forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_double_backslash forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathDisplayWith Text
"\\\\[" Text
"\\\\]")

mathInline :: (HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char)
           => ParsecT s st m Text
mathInline :: forall st s (m :: * -> *).
(HasReaderOptions st, Stream s m Char, UpdateSourcePos s Char) =>
ParsecT s st m Text
mathInline =
      (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_dollars forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathInlineWith Text
"$" Text
"$")
  forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_single_backslash forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathInlineWith Text
"\\(" Text
"\\)")
  forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> (forall s (m :: * -> *) a st.
(Stream s m a, HasReaderOptions st) =>
Extension -> ParsecT s st m ()
guardEnabled Extension
Ext_tex_math_double_backslash forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>
       forall s (m :: * -> *) st.
(Stream s m Char, UpdateSourcePos s Char) =>
Text -> Text -> ParsecT s st m Text
mathInlineWith Text
"\\\\(" Text
"\\\\)")