diff --git a/Text/TeXMath.hs b/Text/TeXMath.hs
--- a/Text/TeXMath.hs
+++ b/Text/TeXMath.hs
@@ -22,12 +22,9 @@
 module Text.TeXMath ( texMathToMathML, DisplayType(..) )
 where
 import Text.TeXMath.Parser
-import Text.TeXMath.MathMLWriter
+import Text.TeXMath.MathML
 import Text.XML.Light
-import Text.ParserCombinators.Parsec (parse)
 
 texMathToMathML :: DisplayType -> String -> Either String Element
 texMathToMathML dt inp = inp `seq`
-  case parse formula "TeX math input" inp of
-       Left err        -> Left (show err)
-       Right v         -> Right $ toMathML dt v
+  either Left (Right . toMathML dt) $ parseFormula inp
diff --git a/Text/TeXMath/Macros.hs b/Text/TeXMath/Macros.hs
--- a/Text/TeXMath/Macros.hs
+++ b/Text/TeXMath/Macros.hs
@@ -21,9 +21,8 @@
  to LateX expressions.
 -}
 
-module Text.TeXMath.Macros ( Macro(..)
-                           , pMacroDefinition
-                           , pSkipSpaceComments
+module Text.TeXMath.Macros ( Macro
+                           , parseMacroDefinitions
                            , applyMacros
                            )
 where
@@ -33,10 +32,28 @@
 import Text.ParserCombinators.Parsec
 
 data Macro = Macro { macroDefinition :: String
-                   , macroParser :: forall st . GenParser Char st String }
+                   , macroParser     :: forall st . GenParser Char st String }
 
 instance Show Macro where
   show m = "Macro " ++ show (macroDefinition m)
+
+-- | Parses a string for a list of macro definitions, optionally
+-- separated and ended by spaces and TeX comments.  Returns
+-- the list of macros (which may be empty) and the unparsed
+-- portion of the input string.
+parseMacroDefinitions :: String -> ([Macro], String)
+parseMacroDefinitions s =
+  case parse pMacroDefinitions "input" s of
+       Left _       -> ([], s)
+       Right res    -> res
+
+-- | Parses one or more macro definitions separated by comments & space.
+-- Return list of macros parsed + remainder of string.
+pMacroDefinitions :: GenParser Char st ([Macro], String)
+pMacroDefinitions = do
+  defs <- sepEndBy pMacroDefinition pSkipSpaceComments
+  rest <- getInput
+  return (reverse defs, rest)  -- reversed so later macros shadow earlier
 
 -- | Parses a @\\newcommand@ or @\\renewcommand@ macro definition and
 -- returns a 'Macro'.
diff --git a/Text/TeXMath/MathML.hs b/Text/TeXMath/MathML.hs
new file mode 100644
--- /dev/null
+++ b/Text/TeXMath/MathML.hs
@@ -0,0 +1,177 @@
+{-
+Copyright (C) 2009 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 writing a parsed formula as MathML.
+-}
+
+module Text.TeXMath.MathML (toMathML, DisplayType(..), showExp)
+where
+
+import qualified Data.Map as M
+import Text.XML.Light
+import Text.TeXMath.Types
+import Data.Generics (everywhere, mkT)
+
+data DisplayType = DisplayBlock
+                 | DisplayInline
+                 deriving Show
+
+toMathML :: DisplayType -> [Exp] -> Element
+toMathML dt exprs =
+  add_attr dtattr $ math $ map showExp $ everywhere (mkT $ handleDownup dt) exprs
+    where dtattr = Attr (unqual "display") dt'
+          dt' =  case dt of
+                      DisplayBlock  -> "block"
+                      DisplayInline -> "inline"
+
+math :: [Element] -> Element
+math = add_attr (Attr (unqual "xmlns") "http://www.w3.org/1998/Math/MathML") . unode "math" . unode "mrow"
+
+mrow :: [Element] -> Element
+mrow = unode "mrow"
+
+{- Firefox seems to set spacing based on its own dictionary,
+-  so I believe this is unnecessary.
+ 
+setSpacing :: String -> String -> Bool -> Element -> Element
+setSpacing left right stretchy elt =
+  add_attr (Attr (unqual "lspace") left) $
+  add_attr (Attr (unqual "rspace") right) $
+  if stretchy
+     then add_attr (Attr (unqual "stretchy") "true") elt
+     else elt
+
+showSymbol (ESymbol s x) =
+  case s of
+    Ord   x  -> unode "mo" x
+    Op    x  -> setSpacing "0" "0.167em" True $ unode "mo" x
+    Bin   x  -> setSpacing "0.222em" "0.222em" False $ unode "mo" x
+    Rel   x  -> setSpacing "0.278em" "0.278em" False $ unode "mo" x
+    Open  x  -> setSpacing "0" "0" True $ unode "mo" x
+    Close x  -> setSpacing "0" "0" True $ unode "mo" x
+    Pun   x  -> setSpacing "0" "0.167em" False $ unode "mo" x
+-}
+
+unaryOps :: M.Map String String
+unaryOps = M.fromList
+  [ ("\\sqrt", "msqrt")
+  , ("\\surd", "msqrt")
+  ]
+
+showUnary :: String -> Exp -> Element
+showUnary c x =
+  case M.lookup c unaryOps of
+       Just c'  -> unode c' (showExp x)
+       Nothing  -> error $ "Unknown unary op: " ++ c
+
+binaryOps :: M.Map String ([Element] -> Element)
+binaryOps = M.fromList
+  [ ("\\frac", unode "mfrac")
+  , ("\\tfrac", withAttribute "displaystyle" "false" .
+                  unode "mstyle" . unode "mfrac")
+  , ("\\dfrac", withAttribute "displaystyle" "true" .
+                  unode "mstyle" . unode "mfrac")
+  , ("\\sqrt", unode "mroot")
+  , ("\\stackrel", unode "mover")
+  , ("\\overset", unode "mover")
+  , ("\\underset", unode "munder")
+  , ("\\binom", showBinom)
+  ]
+
+showBinom :: [Element] -> Element
+showBinom lst = unode "mfenced" $ withAttribute "linethickness" "0" $ unode "mfrac" lst
+
+showBinary :: String -> Exp -> Exp -> Element
+showBinary c x y =
+  case M.lookup c binaryOps of
+       Just f   -> f [showExp x, showExp y]
+       Nothing  -> error $ "Unknown binary op: " ++ c
+
+spaceWidth :: String -> Element
+spaceWidth w = withAttribute "width" w $ unode "mspace" ()
+
+makeStretchy :: Element -> Element
+makeStretchy = withAttribute "stretchy" "true"
+
+makeScaled :: String -> Element -> Element
+makeScaled s = withAttribute "minsize" s . withAttribute "maxsize" s
+
+makeText :: String -> String -> Element
+makeText a s = if trailingSp
+                  then mrow [s', sp]
+                  else s'
+  where sp = spaceWidth "0.333em"
+        s' = withAttribute "mathvariant" a $ unode "mtext" s
+        trailingSp = not (null s) && last s `elem` " \t"
+
+makeArray :: [Alignment] -> [ArrayLine] -> Element
+makeArray as ls = unode "mtable" $
+  map (unode "mtr" .
+    zipWith (\a -> setAlignment a .  unode "mtd". map showExp) as') ls
+   where setAlignment AlignLeft    = withAttribute "columnalign" "left"
+         setAlignment AlignRight   = withAttribute "columnalign" "right"
+         setAlignment AlignCenter  = withAttribute "columnalign" "center"
+         setAlignment AlignDefault = id 
+         as'                       = as ++ cycle [AlignDefault]
+
+withAttribute :: String -> String -> Element -> Element
+withAttribute a = add_attr . Attr (unqual a)
+
+accent :: String -> Element
+accent = add_attr (Attr (unqual "accent") "true") .
+           unode "mo"
+
+handleDownup :: DisplayType -> Exp -> Exp
+handleDownup DisplayInline (EDown x y)     = ESub x y
+handleDownup DisplayBlock  (EDown x y)     = EUnder x y
+handleDownup DisplayInline (EUp x y)       = ESuper x y
+handleDownup DisplayBlock  (EUp x y)       = EOver x y
+handleDownup DisplayInline (EDownup x y z) = ESubsup x y z
+handleDownup DisplayBlock  (EDownup x y z) = EUnderover x y z
+handleDownup _             x               = x
+
+showExp :: Exp -> Element
+showExp e =
+ case e of
+   ENumber x        -> unode "mn" x
+   EGrouped [x]     -> showExp x
+   EGrouped xs      -> mrow $ map showExp xs
+   EIdentifier x    -> unode "mi" x
+   EMathOperator x  -> unode "mi" x
+   ESymbol Accent x -> accent x
+   EStretchy (ESymbol Open x)  -> makeStretchy $ unode "mo" x
+   EStretchy (ESymbol Close x) -> makeStretchy $ unode "mo" x
+   ESymbol Open x   -> withAttribute "stretchy" "false" $ unode "mo" x
+   ESymbol Close x  -> withAttribute "stretchy" "false" $ unode "mo" x
+   ESymbol _ x      -> unode "mo" x
+   ESpace x         -> spaceWidth x
+   EBinary c x y    -> showBinary c x y
+   ESub x y         -> unode "msub" $ map showExp [x, y]
+   ESuper x y       -> unode "msup" $ map showExp [x, y]
+   ESubsup x y z    -> unode "msubsup" $ map showExp [x, y, z]
+   EUnder x y       -> unode "munder" $ map showExp [x, y]
+   EOver x y        -> unode "mover" $ map showExp [x, y]
+   EUnderover x y z -> unode "munderover" $ map showExp [x, y, z]
+   EUnary c x       -> showUnary c x
+   EStretchy x      -> makeStretchy $ showExp x
+   EScaled s x      -> makeScaled s $ showExp x
+   EArray as ls     -> makeArray as ls
+   EText a s        -> makeText a s
+   x                -> error $ "showExp encountered " ++ show x
+                       -- note: EUp, EDown, EDownup should be removed by handleDownup
+
diff --git a/Text/TeXMath/MathMLWriter.hs b/Text/TeXMath/MathMLWriter.hs
deleted file mode 100644
--- a/Text/TeXMath/MathMLWriter.hs
+++ /dev/null
@@ -1,177 +0,0 @@
-{-
-Copyright (C) 2009 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 writing a parsed formula as MathML.
--}
-
-module Text.TeXMath.MathMLWriter (toMathML, DisplayType(..), showExp)
-where
-
-import qualified Data.Map as M
-import Text.XML.Light
-import Text.TeXMath.Parser
-import Data.Generics (everywhere, mkT)
-
-data DisplayType = DisplayBlock
-                 | DisplayInline
-                 deriving Show
-
-toMathML :: DisplayType -> [Exp] -> Element
-toMathML dt exprs =
-  add_attr dtattr $ math $ map showExp $ everywhere (mkT $ handleDownup dt) exprs
-    where dtattr = Attr (unqual "display") dt'
-          dt' =  case dt of
-                      DisplayBlock  -> "block"
-                      DisplayInline -> "inline"
-
-math :: [Element] -> Element
-math = add_attr (Attr (unqual "xmlns") "http://www.w3.org/1998/Math/MathML") . unode "math" . unode "mrow"
-
-mrow :: [Element] -> Element
-mrow = unode "mrow"
-
-{- Firefox seems to set spacing based on its own dictionary,
--  so I believe this is unnecessary.
- 
-setSpacing :: String -> String -> Bool -> Element -> Element
-setSpacing left right stretchy elt =
-  add_attr (Attr (unqual "lspace") left) $
-  add_attr (Attr (unqual "rspace") right) $
-  if stretchy
-     then add_attr (Attr (unqual "stretchy") "true") elt
-     else elt
-
-showSymbol (ESymbol s x) =
-  case s of
-    Ord   x  -> unode "mo" x
-    Op    x  -> setSpacing "0" "0.167em" True $ unode "mo" x
-    Bin   x  -> setSpacing "0.222em" "0.222em" False $ unode "mo" x
-    Rel   x  -> setSpacing "0.278em" "0.278em" False $ unode "mo" x
-    Open  x  -> setSpacing "0" "0" True $ unode "mo" x
-    Close x  -> setSpacing "0" "0" True $ unode "mo" x
-    Pun   x  -> setSpacing "0" "0.167em" False $ unode "mo" x
--}
-
-unaryOps :: M.Map String String
-unaryOps = M.fromList
-  [ ("\\sqrt", "msqrt")
-  , ("\\surd", "msqrt")
-  ]
-
-showUnary :: String -> Exp -> Element
-showUnary c x =
-  case M.lookup c unaryOps of
-       Just c'  -> unode c' (showExp x)
-       Nothing  -> error $ "Unknown unary op: " ++ c
-
-binaryOps :: M.Map String ([Element] -> Element)
-binaryOps = M.fromList
-  [ ("\\frac", unode "mfrac")
-  , ("\\tfrac", withAttribute "displaystyle" "false" .
-                  unode "mstyle" . unode "mfrac")
-  , ("\\dfrac", withAttribute "displaystyle" "true" .
-                  unode "mstyle" . unode "mfrac")
-  , ("\\sqrt", unode "mroot")
-  , ("\\stackrel", unode "mover")
-  , ("\\overset", unode "mover")
-  , ("\\underset", unode "munder")
-  , ("\\binom", showBinom)
-  ]
-
-showBinom :: [Element] -> Element
-showBinom lst = unode "mfenced" $ withAttribute "linethickness" "0" $ unode "mfrac" lst
-
-showBinary :: String -> Exp -> Exp -> Element
-showBinary c x y =
-  case M.lookup c binaryOps of
-       Just f   -> f [showExp x, showExp y]
-       Nothing  -> error $ "Unknown binary op: " ++ c
-
-spaceWidth :: String -> Element
-spaceWidth w = withAttribute "width" w $ unode "mspace" ()
-
-makeStretchy :: Element -> Element
-makeStretchy = withAttribute "stretchy" "true"
-
-makeScaled :: String -> Element -> Element
-makeScaled s = withAttribute "minsize" s . withAttribute "maxsize" s
-
-makeText :: String -> String -> Element
-makeText a s = if trailingSp
-                  then mrow [s', sp]
-                  else s'
-  where sp = spaceWidth "0.333em"
-        s' = withAttribute "mathvariant" a $ unode "mtext" s
-        trailingSp = not (null s) && last s `elem` " \t"
-
-makeArray :: [Alignment] -> [ArrayLine] -> Element
-makeArray as ls = unode "mtable" $
-  map (unode "mtr" .
-    zipWith (\a -> setAlignment a .  unode "mtd". map showExp) as') ls
-   where setAlignment AlignLeft    = withAttribute "columnalign" "left"
-         setAlignment AlignRight   = withAttribute "columnalign" "right"
-         setAlignment AlignCenter  = withAttribute "columnalign" "center"
-         setAlignment AlignDefault = id 
-         as'                       = as ++ cycle [AlignDefault]
-
-withAttribute :: String -> String -> Element -> Element
-withAttribute a = add_attr . Attr (unqual a)
-
-accent :: String -> Element
-accent = add_attr (Attr (unqual "accent") "true") .
-           unode "mo"
-
-handleDownup :: DisplayType -> Exp -> Exp
-handleDownup DisplayInline (EDown x y)     = ESub x y
-handleDownup DisplayBlock  (EDown x y)     = EUnder x y
-handleDownup DisplayInline (EUp x y)       = ESuper x y
-handleDownup DisplayBlock  (EUp x y)       = EOver x y
-handleDownup DisplayInline (EDownup x y z) = ESubsup x y z
-handleDownup DisplayBlock  (EDownup x y z) = EUnderover x y z
-handleDownup _             x               = x
-
-showExp :: Exp -> Element
-showExp e =
- case e of
-   ENumber x        -> unode "mn" x
-   EGrouped [x]     -> showExp x
-   EGrouped xs      -> mrow $ map showExp xs
-   EIdentifier x    -> unode "mi" x
-   EMathOperator x  -> unode "mi" x
-   ESymbol Accent x -> accent x
-   EStretchy (ESymbol Open x)  -> makeStretchy $ unode "mo" x
-   EStretchy (ESymbol Close x) -> makeStretchy $ unode "mo" x
-   ESymbol Open x   -> withAttribute "stretchy" "false" $ unode "mo" x
-   ESymbol Close x  -> withAttribute "stretchy" "false" $ unode "mo" x
-   ESymbol _ x      -> unode "mo" x
-   ESpace x         -> spaceWidth x
-   EBinary c x y    -> showBinary c x y
-   ESub x y         -> unode "msub" $ map showExp [x, y]
-   ESuper x y       -> unode "msup" $ map showExp [x, y]
-   ESubsup x y z    -> unode "msubsup" $ map showExp [x, y, z]
-   EUnder x y       -> unode "munder" $ map showExp [x, y]
-   EOver x y        -> unode "mover" $ map showExp [x, y]
-   EUnderover x y z -> unode "munderover" $ map showExp [x, y, z]
-   EUnary c x       -> showUnary c x
-   EStretchy x      -> makeStretchy $ showExp x
-   EScaled s x      -> makeScaled s $ showExp x
-   EArray as ls     -> makeArray as ls
-   EText a s        -> makeText a s
-   x                -> error $ "showExp encountered " ++ show x
-                       -- note: EUp, EDown, EDownup should be removed by handleDownup
-
diff --git a/Text/TeXMath/Parser.hs b/Text/TeXMath/Parser.hs
--- a/Text/TeXMath/Parser.hs
+++ b/Text/TeXMath/Parser.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE DeriveDataTypeable #-}
 {-
 Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>
 
@@ -20,7 +19,7 @@
 {- | Functions for parsing a LaTeX formula to a Haskell representation.
 -}
 
-module Text.TeXMath.Parser (expr, formula, Exp(..), TeXSymbolType(..), ArrayLine, Alignment(..))
+module Text.TeXMath.Parser (parseFormula)
 where
 
 import Control.Monad
@@ -29,39 +28,7 @@
 import Text.ParserCombinators.Parsec
 import qualified Text.ParserCombinators.Parsec.Token as P
 import Text.ParserCombinators.Parsec.Language
-import Data.Generics
-
-data TeXSymbolType = Ord | Op | Bin | Rel | Open | Close | Pun | Accent
-                     deriving (Show, Read, Eq, Data, Typeable)
-
-data Alignment = AlignLeft | AlignCenter | AlignRight | AlignDefault
-                 deriving (Show, Read, Eq, Data, Typeable)
-
-type ArrayLine = [[Exp]]
-
-data Exp =
-    ENumber String
-  | EGrouped [Exp]
-  | EIdentifier String
-  | EMathOperator String
-  | ESymbol TeXSymbolType String
-  | ESpace String
-  | EBinary String Exp Exp
-  | ESub Exp Exp
-  | ESuper Exp Exp
-  | ESubsup Exp Exp Exp
-  | EOver Exp Exp
-  | EUnder Exp Exp
-  | EUnderover Exp Exp Exp
-  | EUp Exp Exp
-  | EDown Exp Exp
-  | EDownup Exp Exp Exp
-  | EUnary String Exp
-  | EScaled String Exp
-  | EStretchy Exp
-  | EArray [Alignment] [ArrayLine]
-  | EText String String
-  deriving (Show, Read, Eq, Data, Typeable)
+import Text.TeXMath.Types
 
 texMathDef :: LanguageDef st
 texMathDef = LanguageDef 
@@ -98,6 +65,11 @@
   , ensuremath
   ]
 
+-- | Parse a formula, returning a list of 'Exp'.
+parseFormula :: String -> Either String [Exp]
+parseFormula inp =
+  either (Left . show) (Right . id) $ parse formula "formula" inp
+
 formula :: GenParser Char st [Exp]
 formula = do
   whiteSpace
@@ -452,7 +424,7 @@
              , ("\\rbrack", ESymbol Close "]")
              , ("\\rbrace", ESymbol Close "}")
              , ("\\llbracket", ESymbol Open "\x27E6")
-             , ("\\rrbracket", ESymbol Close "\x230B")
+             , ("\\rrbracket", ESymbol Close "\x27E7")
              , ("\\langle", ESymbol Open "\x27E8")
              , ("\\rangle", ESymbol Close "\x27E9")
              , ("\\lfloor", ESymbol Open "\x230A")
diff --git a/Text/TeXMath/Types.hs b/Text/TeXMath/Types.hs
new file mode 100644
--- /dev/null
+++ b/Text/TeXMath/Types.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-
+Copyright (C) 2009 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
+-}
+
+{- | Types for representing a structured formula.
+-}
+
+module Text.TeXMath.Types (Exp(..), TeXSymbolType(..), ArrayLine, Alignment(..))
+where
+
+import Data.Generics
+
+data TeXSymbolType = Ord | Op | Bin | Rel | Open | Close | Pun | Accent
+                     deriving (Show, Read, Eq, Data, Typeable)
+
+data Alignment = AlignLeft | AlignCenter | AlignRight | AlignDefault
+                 deriving (Show, Read, Eq, Data, Typeable)
+
+type ArrayLine = [[Exp]]
+
+data Exp =
+    ENumber String
+  | EGrouped [Exp]
+  | EIdentifier String
+  | EMathOperator String
+  | ESymbol TeXSymbolType String
+  | ESpace String
+  | EBinary String Exp Exp
+  | ESub Exp Exp
+  | ESuper Exp Exp
+  | ESubsup Exp Exp Exp
+  | EOver Exp Exp
+  | EUnder Exp Exp
+  | EUnderover Exp Exp Exp
+  | EUp Exp Exp
+  | EDown Exp Exp
+  | EDownup Exp Exp Exp
+  | EUnary String Exp
+  | EScaled String Exp
+  | EStretchy Exp
+  | EArray [Alignment] [ArrayLine]
+  | EText String String
+  deriving (Show, Read, Eq, Data, Typeable)
diff --git a/cgi/texmath-cgi.hs b/cgi/texmath-cgi.hs
--- a/cgi/texmath-cgi.hs
+++ b/cgi/texmath-cgi.hs
@@ -7,30 +7,20 @@
 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
   setHeader "Content-type" "text/xhtml; charset=UTF-8"
   latexFormula <- liftM (decodeString . fromMaybe "") $ getInput "latexFormula"
   rawMacros <- liftM (decodeString . fromMaybe "") $ getInput "macros"
+  let (ms, _) = parseMacroDefinitions rawMacros
   output . encodeStrict $
-    case parse pMacros "macros" (rawMacros ++ "\n") of
-         Left err  -> toJSObject [("success", JSBool False)
-                                 ,("error", JSString $ toJSString (show err))]
-         Right ms  -> case texMathToMathML DisplayBlock
-                               (applyMacros (reverse ms) latexFormula) of
-                       Left e  -> toJSObject [("success", JSBool False)
-                                             , ("error", JSString $ toJSString e)]
-                       Right v -> toJSObject [("success", JSBool True)
-                                             , ("mathml", JSString $ toJSString $
-                                                          ppElement v)]
+    case texMathToMathML DisplayBlock (applyMacros (reverse 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
diff --git a/texmath.cabal b/texmath.cabal
--- a/texmath.cabal
+++ b/texmath.cabal
@@ -1,5 +1,5 @@
 Name:                texmath
-Version:             0.4
+Version:             0.5
 Cabal-version:       >= 1.2
 Build-type:          Custom
 Synopsis:            Conversion of LaTeX math formulas to MathML.
@@ -73,8 +73,9 @@
     else
       Build-depends: base >= 3 && < 4
     Exposed-modules:     Text.TeXMath
+                         Text.TeXMath.Types
                          Text.TeXMath.Parser
-                         Text.TeXMath.MathMLWriter
+                         Text.TeXMath.MathML
                          Text.TeXMath.Macros
 
     if impl(ghc >= 6.12)
diff --git a/texmath.hs b/texmath.hs
--- a/texmath.hs
+++ b/texmath.hs
@@ -3,7 +3,6 @@
 import Text.TeXMath
 import Text.XML.Light
 import Text.TeXMath.Macros
-import Text.ParserCombinators.Parsec
 import System.IO
 
 inHtml :: Element -> Element
@@ -16,18 +15,10 @@
         unode "meta" ()
     , unode "body" x ]
 
-stripMacroDefs :: Parser ([Macro], String)
-stripMacroDefs = do
-  macros <- many (try $ pSkipSpaceComments >> pMacroDefinition)
-  rest <- getInput
-  return (reverse macros, rest)  -- reversed so later ones will shadow earlier
-
 main :: IO ()
 main = do
   inp <- getContents
-  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
+  let (ms, rest) = parseMacroDefinitions inp
+  case (texMathToMathML DisplayBlock $! applyMacros ms rest) of
+        Left err         -> hPutStrLn stderr err
+        Right v          -> putStr . ppTopElement . inHtml $ v
