packages feed

texmath 0.3.0.3 → 0.3.1.3

raw patch · 12 files changed

+295/−24 lines, 12 filessetup-changed

Files

Setup.hs view
@@ -4,4 +4,4 @@  main = defaultMainWithHooks $ simpleUserHooks { runTests  = runTestSuite } -runTestSuite _ _ _ _ = system "cd tests && ./runtests.sh" >>= exitWith+runTestSuite _ _ _ _ = system "cd tests && sh runtests.sh" >>= exitWith
+ Text/TeXMath/Macros.hs view
@@ -0,0 +1,172 @@+{-+Copyright (C) 2010 John MacFarlane <jgm@berkeley.edu>++This program is free software; you can redistribute it and/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation; either version 2 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA+-}++{- | Functions for parsing LaTeX macro definitions and applying macros+ to LateX expressions.+-}++module Text.TeXMath.Macros ( Macro(..)+                           , pMacroDefinition+                           , pSkipSpaceComments+                           , applyMacros+                           )+where++import Data.Char (isDigit, isLetter)+import Control.Monad+import Text.ParserCombinators.Parsec++newtype Macro = Macro { macroParser :: Parser String }++-- | Parses a @\\newcommand@ or @\\renewcommand@ macro definition and+-- returns a 'Macro'.+pMacroDefinition :: Parser Macro+pMacroDefinition = newcommand++-- | Skip whitespace and comments.+pSkipSpaceComments :: Parser ()+pSkipSpaceComments = spaces >> skipMany (comment >> spaces)++-- | Applies a list of macros to a string recursively until a fixed+-- point is reached.  If there are several macros in the list with the+-- same name, later ones will shadow earlier ones.+applyMacros :: [Macro] -> String -> String+applyMacros [] = id+applyMacros ms = iterateToFixedPoint ((2 * length ms) + 1) $+                  applyMacrosOnce $ reverse ms -- we reverse so that the most recently+                                               -- defined macros will be applied first+                                               -- and shadow others with the same name++------------------------------------------------------------------------------------++iterateToFixedPoint :: Eq a => Int -> (a -> a) -> a -> a+iterateToFixedPoint 0     _ _ = error $+  "Macro application did not terminate in a reasonable time.\n" +++  "Check your macros for loops."+iterateToFixedPoint limit f x =+  if x' == x+     then x'+     else iterateToFixedPoint (limit - 1) f x'+    where x' = f x++applyMacrosOnce :: [Macro] -> String -> String+applyMacrosOnce ms s =+  case parse (many tok) "input" s of+       Right r -> concat r+       Left _  -> s  -- just return original on error+    where tok = try $ do+                  skipComment+                  choice [ escaped "\\"+                         , choice (map macroParser ms)+                         , ctrlseq+                         , count 1 anyChar ]+          ctrlseq = do+                    char '\\'+                    res <- many1 letter <|> count 1 anyChar+                    return $ '\\' : res++newcommand :: Parser Macro+newcommand = try $ do+  char '\\'+  optional $ try $ string "re"+  string "newcommand"+  pSkipSpaceComments+  name <- inbraces+  guard (take 1 name == "\\")+  let name' = drop 1 name+  pSkipSpaceComments+  numargs <- numArgs+  pSkipSpaceComments+  optarg <- if numargs > 0+               then optArg+               else return Nothing+  let numargs' = case optarg of+                   Just _  -> numargs - 1+                   Nothing -> numargs+  pSkipSpaceComments+  body <- inbraces+  return $ Macro $ try $ do+    char '\\'+    string name'+    when (all isLetter name') $+      notFollowedBy letter+    opt <- case optarg of+                Nothing  -> return Nothing+                Just _   -> liftM (`mplus` optarg) optArg+    args <- count numargs' inbraces+    let args' = case opt of+                     Just x  -> x : args+                     Nothing -> args+    return $ apply args' body++apply :: [String] -> String -> String+apply args ('#':d:xs) | isDigit d =+  let argnum = read [d]+  in  if length args >= argnum+         then args !! (argnum - 1) ++ apply args xs+         else '#' : d : apply args xs+apply args ('\\':'#':xs) = '\\':'#' : apply args xs+apply args (x:xs) = x : apply args xs+apply _ "" = ""++skipComment :: Parser ()+skipComment = skipMany comment++comment :: Parser ()+comment = do+  char '%'+  skipMany (notFollowedBy newline >> anyChar)+  newline+  return ()++numArgs :: Parser Int+numArgs = option 0 $ do+  pSkipSpaceComments+  char '['+  pSkipSpaceComments+  n <- digit+  pSkipSpaceComments+  char ']'+  return $ read [n]++optArg :: Parser (Maybe String)+optArg = option Nothing $ (liftM Just $ inBrackets)++escaped :: String -> Parser String+escaped xs = try $ char '\\' >> oneOf xs >>= \x -> return ['\\',x]++inBrackets :: Parser String+inBrackets = try $ do+  char '['+  pSkipSpaceComments+  res <- manyTill (skipComment >> (escaped "[]" <|> count 1 anyChar))+          (try $ pSkipSpaceComments >> char ']')+  return $ concat res++inbraces :: Parser String+inbraces = try $ do+  char '{'+  res <- manyTill (skipComment >> (inbraces' <|> count 1 anyChar <|> escaped "{}"))+    (try $ skipComment >> char '}')+  return $ concat res++inbraces' :: Parser String+inbraces' = do+  res <- inbraces+  return $ '{' : (res ++ "}")+
Text/TeXMath/Parser.hs view
@@ -95,6 +95,7 @@   , diacritical   , escaped   , unicode+  , ensuremath   ]  formula :: GenParser Char st [Exp]@@ -296,6 +297,10 @@  unicode :: GenParser Char st Exp unicode = lexeme $ liftM (ESymbol Ord . (:[])) $ satisfy (not . isAscii)++ensuremath :: GenParser Char st Exp+ensuremath = lexeme $ try $+             string "\\ensuremath" >> inbraces  command :: GenParser Char st String command = try $ char '\\' >> liftM ('\\':) (identifier <|> lexeme (count 1 anyChar))
cgi/texmath-cgi.hs view
@@ -2,19 +2,34 @@ import Network.CGI import Text.XML.Light import Text.TeXMath+import Text.TeXMath.Macros import Data.Maybe (fromMaybe) import Control.Monad import Text.JSON import Codec.Binary.UTF8.String (decodeString)+import Text.ParserCombinators.Parsec (Parser, parse, many, try, eof) +pMacros :: Parser [Macro]+pMacros = do+  macros <- many (try $ pSkipSpaceComments >> pMacroDefinition)+  pSkipSpaceComments >> eof+  return macros+ cgiMain :: CGI CGIResult cgiMain = do-  latexFormula <- liftM (decodeString . fromMaybe "") $ getInput "latexFormula"   setHeader "Content-type" "text/xhtml; charset=UTF-8"+  latexFormula <- liftM (decodeString . fromMaybe "") $ getInput "latexFormula"+  rawMacros <- liftM (decodeString . fromMaybe "") $ getInput "macros"   output . encodeStrict $-    case texMathToMathML DisplayBlock latexFormula of-       Left e     -> toJSObject [("success", JSBool False), ("error", JSString $ toJSString e)]-       Right v    -> toJSObject [("success", JSBool True), ("mathml", JSString $ toJSString $ ppElement v)]+    case parse pMacros "macros" (rawMacros ++ "\n") of+         Left err  -> toJSObject [("success", JSBool False)+                                 ,("error", JSString $ toJSString (show err))]+         Right ms  -> case texMathToMathML DisplayBlock (applyMacros ms latexFormula) of+                       Left e  -> toJSObject [("success", JSBool False)+                                             , ("error", JSString $ toJSString e)]+                       Right v -> toJSObject [("success", JSBool True)+                                             , ("mathml", JSString $ toJSString $+                                                          ppElement v)]  main :: IO () main = runCGI $ handleErrors cgiMain
cgi/texmath.xhtml view
@@ -14,7 +14,8 @@       <![CDATA[       $(document).ready(function(){         $('#convert').click(function() {-          $.getJSON("/cgi-bin/texmath-cgi", { 'latexFormula' : $('#latexFormula').val() },+          $.getJSON("/cgi-bin/texmath-cgi", { 'latexFormula' : $('#latexFormula').val()+                                            , 'macros' : $('#macros').val() },             function(result){                    if (result.success) {                      if ($.browser.mozilla) {@@ -36,11 +37,15 @@   <body>   <h1>LaTeX to MathML converter</h1>   <label for="latexFormula">Enter a LaTeX formula here:</label><br/>-  <textarea name="latexFormula" rows="10" cols="60" id="latexFormula">\phi_n(\kappa) =+  <textarea name="latexFormula" rows="8" cols="60" id="latexFormula">\phi_n(\kappa) =  \frac{1}{4\pi^2\kappa^2} \int_0^\infty  \frac{\sin(\kappa R)}{\kappa R}  \frac{\partial}{\partial R}  \left[R^2\frac{\partial D_n(R)}{\partial R}\right]\,dR+  </textarea><br/>+  <label for="latexFormula">Enter your macros (\newcommand, \renewcommand) here:</label><br/>+  <textarea name="macros" rows="8" cols="60" id="macros">+\renewcommand{\sin}{\mathbf{sin}}   </textarea><br/>   <input type="submit" id="convert" value="Convert" /> using   <a href="http://github.com/jgm/texmath/tree/master">texmath</a>
testTeXMathML.hs view
@@ -2,7 +2,8 @@  import Text.TeXMath import Text.XML.Light-+import Text.TeXMath.Macros+import Text.ParserCombinators.Parsec import System.IO  inHtml :: Element -> Element@@ -15,9 +16,18 @@         unode "meta" ()     , unode "body" x ] +stripMacroDefs :: Parser ([Macro], String)+stripMacroDefs = do+  macros <- many (try $ pSkipSpaceComments >> pMacroDefinition)+  rest <- getInput+  return (macros, rest)+ main :: IO () main = do   inp <- getContents-  case (texMathToMathML DisplayBlock $! inp) of-       Left err         -> hPutStrLn stderr err-       Right v          -> putStr . ppTopElement . inHtml $ v+  case parse stripMacroDefs "stdin" inp of+       Left err -> error $ show err+       Right (ms, rest)->+          case (texMathToMathML DisplayBlock $! applyMacros ms rest) of+               Left err         -> hPutStrLn stderr err+               Right v          -> putStr . ppTopElement . inHtml $ v
+ tests/ensuremath.tex view
@@ -0,0 +1,1 @@+\ensuremath{\pi^2}
+ tests/ensuremath.xhtml view
@@ -0,0 +1,16 @@+<?xml version='1.0' ?>+<html xmlns="http://www.w3.org/1999/xhtml">+  <head>+    <meta content="application/xhtml+xml; charset=UTF-8" http-equiv="Content-Type" />+  </head>+  <body>+    <math display="block" xmlns="http://www.w3.org/1998/Math/MathML">+      <mrow>+        <msup>+          <mo>&#960;</mo>+          <mn>2</mn>+        </msup>+      </mrow>+    </math>+  </body>+</html>
+ tests/macros.tex view
@@ -0,0 +1,18 @@+\newcommand{\abc}{5}+\newcommand{\def}[2][x]{%+#1 + #2%+}+\renewcommand{%+\phi%+}{%+\theta%+}+% comment+\newcommand{\a}{\b{2}}+\renewcommand{\phi}{\a}+\newcommand{\b}[1]{#1}++\phi+\abc+\def[y]{3}+\def{3}
+ tests/macros.xhtml view
@@ -0,0 +1,20 @@+<?xml version='1.0' ?>+<html xmlns="http://www.w3.org/1999/xhtml">+  <head>+    <meta content="application/xhtml+xml; charset=UTF-8" http-equiv="Content-Type" />+  </head>+  <body>+    <math display="block" xmlns="http://www.w3.org/1998/Math/MathML">+      <mrow>+        <mn>2</mn>+        <mn>5</mn>+        <mi>y</mi>+        <mo>+</mo>+        <mn>3</mn>+        <mi>x</mi>+        <mo>+</mo>+        <mn>3</mn>+      </mrow>+    </math>+  </body>+</html>
tests/runtests.sh view
@@ -6,15 +6,19 @@ # Exit status is number of failed tests. TESTPROG=../dist/build/testTeXMathML/testTeXMathML failures=0-for t in *.tex; do-    $TESTPROG <$t >tmp-    diff ${t%.tex}.xhtml tmp >tmpdiff-    if [ "$?" -ne "0" ]; then-        echo "Test ${t%.tex} FAILED (< expected, > actual):"-        cat tmpdiff-        let "failures=$failures+1"-    else-        echo "Test ${t%.tex} PASSED"-    fi-done+if [ -f $TESTPROG ]; then+    for t in *.tex; do+        $TESTPROG <$t >tmp+        diff ${t%.tex}.xhtml tmp >tmpdiff+        if [ "$?" -ne "0" ]; then+            echo "Test ${t%.tex} FAILED (< expected, > actual):"+            cat tmpdiff+            let "failures=$failures+1"+        else+            echo "Test ${t%.tex} PASSED"+        fi+    done+else+    echo "Test executable not built. NOT running tests."+fi exit $failures
texmath.cabal view
@@ -1,5 +1,5 @@ Name:                texmath-Version:             0.3.0.3+Version:             0.3.1.3 Cabal-version:       >= 1.2 Build-type:          Custom Synopsis:            Conversion of LaTeX math formulas to MathML.@@ -46,6 +46,8 @@                      tests/sophomores_dream.tex, tests/sophomores_dream.xhtml,                      tests/sphere_volume.tex, tests/sphere_volume.xhtml,                      tests/unicode.tex, tests/unicode.xhtml+                     tests/ensuremath.tex, tests/ensuremath.xhtml+                     tests/macros.tex, tests/macros.xhtml  Flag cgi   description:       Compile cgi executable.@@ -61,7 +63,10 @@       Build-depends: base >= 4 && < 5, syb     else       Build-depends: base >= 3 && < 4-    Exposed-modules:     Text.TeXMath, Text.TeXMath.Parser, Text.TeXMath.MathMLWriter+    Exposed-modules:     Text.TeXMath+                         Text.TeXMath.Parser+                         Text.TeXMath.MathMLWriter+                         Text.TeXMath.Macros      if impl(ghc >= 6.12)       Ghc-Options:     -Wall -fno-warn-unused-do-bind