diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,23 @@
+texmath (0.13.3.4)
+
+  * StarMath writer: fix braces, operator operands, and upright spacing (#304,
+    John Pye).
+
+  * TeX reader: Fix parsing of unbraced numeric script arguments
+    (#306, John Pye). Consume one token after `^` or `_`, so `x^12`
+    and `x_12` mean `x^{1}2` and `x_{1}2`. Preserve braced numbers,
+    commands, and prime handling.
+
+  * TeX reader: support amsmath spacing aliases and `\cr` (#305, faysou).
+    `\thinspace`, `\negthinspace`, `\thickspace`, `\negthickspace`, and
+    `\negmedspace` failed to parse, so one of them made the whole formula
+    fall back to raw TeX. They now map to the same widths as `\,`, `\!`,
+    `\;`, and their negative forms. `\cr` failed to parse in arrays
+    and amsmath environments such as `cases` and `matrix`. It now ends
+    a row like `\\`.
+
+  * Disable line ending conversion for test files.
+
 texmath (0.13.3.3)
 
   * Typst writer: add some needed `$` inside `[..]` content blocks.
diff --git a/src/Text/TeXMath/Readers/TeX.hs b/src/Text/TeXMath/Readers/TeX.hs
--- a/src/Text/TeXMath/Readers/TeX.hs
+++ b/src/Text/TeXMath/Readers/TeX.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE CPP #-}
 {-
 Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>
 
@@ -25,7 +26,11 @@
 module Text.TeXMath.Readers.TeX (readTeX)
 where
 
+#if MIN_VERSION_base(4,20,0)
+import Data.List (intercalate, intersperse)
+#else
 import Data.List (intercalate, intersperse, foldl')
+#endif
 import Control.Monad
 import Data.Char (isDigit, isAscii, isLetter)
 import qualified Data.Map as M
@@ -66,10 +71,13 @@
     _ -> "\x2057" <> getPrimes (drop 4 cs)
 
 expr1 :: TP Exp
-expr1 = choice
+expr1 = expr1With number
+
+expr1With :: TP Exp -> TP Exp
+expr1With numberParser = choice
           [ inbraces
           , variable
-          , number
+          , numberParser
           , operator
           , bareSubSup
           , enclosure
@@ -445,19 +453,19 @@
        Nothing -> mzero
 
 endLine :: TP Char
-endLine = try $ do
+endLine = try (do
   symbol "\\\\"
   optional inbrackets  -- can contain e.g. [1.0in] for a line height, not yet supported
-  return '\n'
+  return '\n') <|> ('\n' <$ ctrlseq "cr")
 
 -- Within environments provided by AMSmath, spaces are not allowed between
 -- the double-backslash command and its optional argument.
 endLineAMS :: TP Char
-endLineAMS = lexeme $ try $ do
+endLineAMS = lexeme (try $ do
   string "\\\\"
   skipMany comment
   optional inbrackets  -- can contain e.g. [1.0in] for a line height, not yet supported
-  return '\n'
+  return '\n') <|> ('\n' <$ ctrlseq "cr")
 
 arrayLine :: TP ArrayLine
 arrayLine =
@@ -623,10 +631,16 @@
 isUnderover (EUnder _  _ (ESymbol TUnder "\x23DF")) = True  -- \underbrace
 isUnderover _ = False
 
+-- Unbraced scripts take one token, so x^12 means x^{1}2. Braced groups
+-- still use the normal expression parser, including multi-digit numbers.
+scriptArg :: TP Exp
+scriptArg = (fst <$> operatorname)
+        <|> expr1With (lexeme $ ENumber . T.singleton <$> digit)
+
 subSup :: Maybe Bool -> Bool -> Exp -> TP Exp
 subSup limits convertible a = try $ do
-  let sub1 = symbol "_" >> expr1  -- see #273
-  let sup1 = (symbol "^" >> expr1) <|> primes
+  let sub1 = symbol "_" >> scriptArg  -- see #273
+  let sup1 = (symbol "^" >> scriptArg) <|> primes
   (b,c) <- try (do {m <- sub1; n <- sup1; return (m,n)})
        <|> (do {n <- sup1; m <- sub1; return (m,n)})
   return $ case limits of
@@ -637,7 +651,7 @@
 
 superOrSubscripted :: Maybe Bool -> Bool -> Exp -> TP Exp
 superOrSubscripted limits convertible a = try $ do
-  (c,b) <- ((,) <$> oneOf "^_" <*> (spaces >> expr))
+  (c,b) <- ((,) <$> oneOf "^_" <*> (ignorable >> scriptArg))
           <|> (('^',) <$> primes)
   case c of
        '^' -> return $ case limits of
diff --git a/src/Text/TeXMath/Readers/TeX/Commands.hs b/src/Text/TeXMath/Readers/TeX/Commands.hs
--- a/src/Text/TeXMath/Readers/TeX/Commands.hs
+++ b/src/Text/TeXMath/Readers/TeX/Commands.hs
@@ -267,6 +267,9 @@
   , ("\\models",ESymbol Rel "\8872")
   , ("\\mu",EIdentifier "\956")
   , ("\\neg",ESymbol Op "\172")
+  , ("\\negmedspace",ESpace ((-2) % 9))
+  , ("\\negthickspace",ESpace ((-5) % 18))
+  , ("\\negthinspace",ESpace ((-1) % 6))
   , ("\\nu",EIdentifier "\957")
   , ("\\omega",EIdentifier "\969")
   , ("\\overbar",ESymbol Accent "\175")
@@ -296,6 +299,8 @@
   , ("\\tau",EIdentifier "\964")
   , ("\\therefore",ESymbol Pun "\8756")
   , ("\\theta",EIdentifier "\952")
+  , ("\\thickspace",ESpace (5 % 18))
+  , ("\\thinspace",ESpace (1 % 6))
   , ("\\triangle",ESymbol Ord "\9651")
   , ("\\triangleleft",ESymbol Bin "\8882")
   , ("\\triangleright",ESymbol Bin "\8883")
diff --git a/src/Text/TeXMath/Types.hs b/src/Text/TeXMath/Types.hs
--- a/src/Text/TeXMath/Types.hs
+++ b/src/Text/TeXMath/Types.hs
@@ -33,19 +33,19 @@
 
 data TeXSymbolType = Ord | Op | Bin | Rel | Open | Close | Pun | Accent
                      | Fence | TOver | TUnder | Alpha | BotAccent | Rad
-                     deriving (Show, Read, Eq, Ord, Data, Typeable)
+                     deriving (Show, Read, Eq, Ord, Data)
 
 data Alignment = AlignLeft | AlignCenter | AlignRight
-                 deriving (Show, Read, Eq, Ord, Data, Typeable)
+                 deriving (Show, Read, Eq, Ord, Data)
 
 data FractionType = NormalFrac   -- ^ Displayed or textual, acc to 'DisplayType'
                   | DisplayFrac  -- ^ Force display mode
                   | InlineFrac   -- ^ Force inline mode (textual)
                   | NoLineFrac   -- ^ No line between top and bottom
-                  deriving (Show, Read, Eq, Ord, Data, Typeable)
+                  deriving (Show, Read, Eq, Ord, Data)
 
 data StrokeType = ForwardSlash | BackSlash | XSlash
-                  deriving (Show, Read, Eq, Ord, Data, Typeable)
+                  deriving (Show, Read, Eq, Ord, Data)
 
 type ArrayLine = [[Exp]]
 
@@ -95,7 +95,7 @@
                   -- the same length.
   | EText TextType T.Text  -- ^ Some normal text, possibly styled.
   | EStyled TextType [Exp] -- ^  A group of styled expressions.
-  deriving (Show, Read, Eq, Ord, Data, Typeable)
+  deriving (Show, Read, Eq, Ord, Data)
 
 -- | An @EDelimited@ element contains a string of ordinary expressions
 -- (represented here as @Right@ values) or fences (represented here as
@@ -121,7 +121,7 @@
               | TextBoldScript
               | TextBoldFraktur
               | TextSansSerifItalic
-              deriving (Show, Read, Eq, Ord, Data, Typeable)
+              deriving (Show, Read, Eq, Ord, Data)
 
 data FormType = FPrefix | FPostfix | FInfix deriving (Show, Ord, Eq)
 
diff --git a/src/Text/TeXMath/Writers/StarMath.hs b/src/Text/TeXMath/Writers/StarMath.hs
--- a/src/Text/TeXMath/Writers/StarMath.hs
+++ b/src/Text/TeXMath/Writers/StarMath.hs
@@ -213,13 +213,14 @@
   rendered <- mapM (renderExpIn dt ctx) exps
   let pieces = zip exps rendered
   let merged = mergePieces pieces
-  let withLhs =
-        if startsWithInfixNeedingLhs exps
-           then "{} " <> T.stripStart merged
-           else merged
-  pure $ if endsWithInfixNeedingRhs exps
-            then T.stripEnd withLhs <> " {}"
-            else withLhs
+  let withLhs = case exps of
+        e : _ | needsNeutralLhs e ->
+          (if isPlusMinusSign e then "{}" else "{} ") <> T.stripStart merged
+        _ -> merged
+  pure $ case reverse exps of
+    e : _ | needsNeutralRhs e ->
+      T.stripEnd withLhs <> (if isPlusMinusSign e then "{}" else " {}")
+    _ -> withLhs
 
 mergePieces :: [(Exp, T.Text)] -> T.Text
 mergePieces [] = ""
@@ -288,6 +289,8 @@
   | isCloseLike prevE && isAccentCommandExp curE        = True
   | isCloseLike prevE && isArrayLike curE               = True
   | isIdentifierLike prevE && isWordStyledExp curE      = True
+  -- Keep an upright letter from merging with the following identifier token.
+  | isUprightMathTextExp prevE && isIdentifierLike curE = True
   | isIdentifierLike prevE && isUprightMathTextExp curE = True
   | isIdentifierLike prevE && isAccentCommandExp curE   = True
   | isIdentifierLike prevE && isWideSpace curE          = True
@@ -488,24 +491,20 @@
     ESubsup base _ _ -> largeOpName base /= Nothing
     _                -> False
 
-startsWithInfixNeedingLhs :: [Exp] -> Bool
-startsWithInfixNeedingLhs exps =
-  case exps of
-    (e : _) -> needsNeutralLhs e
-    _                 -> False
-
-endsWithInfixNeedingRhs :: [Exp] -> Bool
-endsWithInfixNeedingRhs exps =
-  case reverse exps of
-    (e : _) -> needsNeutralRhs e
-    _       -> False
-
 needsNeutralLhs :: Exp -> Bool
 needsNeutralLhs = isInfixLikeExp
 
 needsNeutralRhs :: Exp -> Bool
-needsNeutralRhs = isInfixLikeExp
+-- TeX demotes a trailing binary sign to Ord (for example in r_{j,+}).
+-- StarMath still parses it as an operator and requires a right operand.
+needsNeutralRhs e = isInfixLikeExp e || isPlusMinusSign e
 
+isPlusMinusSign :: Exp -> Bool
+isPlusMinusSign e =
+  case e of
+    ESymbol _ s -> s == "+" || s == "-" || s == "−"
+    _           -> False
+
 needsNeutralScriptOperands :: Exp -> Bool
 needsNeutralScriptOperands e =
   isInfixLikeExp e && not (isAtomicScriptOperator e)
@@ -547,8 +546,8 @@
     EStyled sty xs  -> renderStyled dt ctx sty xs
 
     EFraction frac num den -> do
-      num' <- renderExpIn dt AlignDefault num
-      den' <- renderExpIn dt AlignDefault den
+      num' <- renderFractionArg dt num
+      den' <- renderFractionArg dt den
       let num'' = maybeCenterFractionArg ctx num'
       let den'' = maybeCenterFractionArg ctx den'
       pure $ case frac of
@@ -558,11 +557,11 @@
           | dt == DisplayInline -> renderInlineFraction num'' den''
         _          -> "{" <> num'' <> " over " <> den'' <> "}"
 
-    ESqrt x -> ("sqrt {" <>) . (<> "}") <$> renderExpIn dt ctx x
+    ESqrt x -> ("sqrt " <>) . groupArg dt x <$> renderExpIn dt ctx x
     ERoot idx rad -> do
       idx' <- renderExpIn dt ctx idx
       rad' <- renderExpIn dt ctx rad
-      pure $ "nroot {" <> idx' <> "} {" <> rad' <> "}"
+      pure $ "nroot " <> groupArg dt idx idx' <> " " <> groupArg dt rad rad'
 
     EScaled _ (ESymbol Open "|") ->
       Just "mline"
@@ -601,6 +600,10 @@
           supRendered <- renderScriptArg dt ctx sup
           pure $ renderScriptBase base baseRendered <> "_" <> subRendered <> "^" <> supRendered
 
+    EOver _ (EOver _ base marker) over
+      | Nothing <- braceAnnotationName over
+      , Just brace <- braceAnnotationName marker ->
+          renderBrace dt ctx base brace over
     EOver _ base over
       | Just arrow <- arrowScriptOpName base -> do
           if isEmptyScriptArg over
@@ -611,21 +614,24 @@
       | Just op <- centeredScriptOpName base -> do
           over' <- renderScriptArg dt ctx over
           pure $ "{" <> op <> "} csup " <> centerScriptArg over'
-      | Just brace <- braceAnnotationName over -> do
-          base' <- renderExpIn dt ctx base
-          pure $ renderScriptBase base base' <> " " <> brace
+      | Just brace <- braceAnnotationName over ->
+          renderBrace dt ctx base brace (EGrouped [])
       | isBraceAnnotatedExp base -> do
           base' <- renderExpIn dt ctx base
-          over' <- renderBraceLabel dt ctx over
-          pure $ base' <> " " <> over'
+          over' <- renderScriptArg dt ctx over
+          pure $ base' <> " csup " <> centerScriptArg over'
       | Just accent <- accentName over -> do
           base' <- renderExpIn dt ctx base
-          pure $ accent <> " " <> renderAccentArg base base'
+          pure $ accent <> " " <> renderAccentArg dt base base'
       | Just op <- limitOpName base -> do
           over' <- renderLimitArg dt ctx over
           pure $ op <> " to " <> over' <> " "
       | otherwise -> Nothing
 
+    EUnder _ (EUnder _ base marker) under
+      | Nothing <- braceAnnotationName under
+      , Just brace <- braceAnnotationName marker ->
+          renderBrace dt ctx base brace under
     EUnder _ base under ->
       case arrowScriptOpName base of
         Just arrow ->
@@ -638,18 +644,17 @@
           case underlineMarkerName under of
             Just marker -> do
               base' <- renderExpIn dt ctx base
-              pure $ marker <> " " <> renderAccentArg base base'
+              pure $ marker <> " " <> renderAccentArg dt base base'
             Nothing ->
               case braceAnnotationName under of
-                Just brace -> do
-                  base' <- renderExpIn dt ctx base
-                  pure $ renderScriptBase base base' <> " " <> brace
+                Just brace ->
+                  renderBrace dt ctx base brace (EGrouped [])
                 Nothing ->
                   if isBraceAnnotatedExp base
                      then do
                        base' <- renderExpIn dt ctx base
-                       under' <- renderBraceLabel dt ctx under
-                       pure $ base' <> " " <> under'
+                       under' <- renderScriptArg dt ctx under
+                       pure $ base' <> " csub " <> centerScriptArg under'
                      else
                        case centeredScriptOpName base of
                          Just op -> do
@@ -664,6 +669,16 @@
                                base' <- renderExpIn dt ctx base
                                under' <- renderScriptArg dt ctx under
                                pure $ renderScriptBase base base' <> "_" <> under'
+    EUnderover _ (EUnder _ base marker) under over
+      | Just brace <- braceAnnotationName marker -> do
+          base' <- renderBrace dt ctx base brace under
+          over' <- renderScriptArg dt ctx over
+          pure $ base' <> " csup " <> centerScriptArg over'
+    EUnderover _ (EOver _ base marker) under over
+      | Just brace <- braceAnnotationName marker -> do
+          base' <- renderBrace dt ctx base brace over
+          under' <- renderScriptArg dt ctx under
+          pure $ base' <> " csub " <> centerScriptArg under'
     EUnderover _ base under over ->
       case arrowScriptOpName base of
         Just arrow ->
@@ -1054,6 +1069,14 @@
                 then s
                 else "{" <> s <> "}"
 
+-- A single operator can be a fraction argument without an EGrouped wrapper.
+-- Supply its missing operands within the argument, not around the fraction.
+renderFractionArg :: DisplayType -> Exp -> Maybe T.Text
+renderFractionArg dt e
+  | needsNeutralLhs e || needsNeutralRhs e =
+      ("{" <>) . (<> "}") <$> renderExpsIn dt AlignDefault [e]
+  | otherwise = renderExpIn dt AlignDefault e
+
 renderInlineFraction :: T.Text -> T.Text -> T.Text
 renderInlineFraction num den =
   "size*0.7 {" <> num <> " over " <> den <> "}"
@@ -1225,6 +1248,16 @@
         then rendered
         else "{" <> rendered <> "}"
 
+-- These constructors already render as one complete group. Other expressions
+-- can start and end with braces without being grouped, e.g. {x} csub {a}.
+groupArg :: DisplayType -> Exp -> T.Text -> T.Text
+groupArg dt e rendered =
+  case e of
+    EGrouped{} -> rendered
+    EFraction DisplayFrac _ _ -> rendered
+    EFraction NormalFrac _ _ | dt == DisplayBlock -> rendered
+    _ -> "{" <> rendered <> "}"
+
 renderScriptArg :: DisplayType -> AlignContext -> Exp -> Maybe T.Text
 renderScriptArg dt ctx e = do
   rendered0 <-
@@ -1234,7 +1267,7 @@
   let rendered = T.strip rendered0
   pure $ if (isAtomic e && not (needsNeutralScriptOperands e)) || isQuotedText rendered
             then rendered
-            else "{" <> rendered <> "}"
+            else groupArg dt e rendered
 
 renderPrimeSuffix :: Exp -> Maybe T.Text
 renderPrimeSuffix e =
@@ -1271,12 +1304,12 @@
       EGrouped ys   -> containsRelationLike ys
       _             -> False
 
-renderAccentArg :: Exp -> T.Text -> T.Text
-renderAccentArg e rendered0 =
+renderAccentArg :: DisplayType -> Exp -> T.Text -> T.Text
+renderAccentArg dt e rendered0 =
   let rendered = T.strip rendered0
   in if isAtomic e
         then rendered
-        else "{" <> rendered <> "}"
+        else groupArg dt e rendered
 
 renderPhantomArg :: Exp -> T.Text -> T.Text
 renderPhantomArg e rendered0 =
@@ -1340,13 +1373,24 @@
     ESymbol TUnder "_" -> Just "underline"
     _                  -> Nothing
 
+-- StarMath braces require a label operand, even for an unannotated TeX brace.
+-- Group the complete term so neither operand absorbs adjacent expressions.
+renderBrace :: DisplayType -> AlignContext -> Exp -> T.Text -> Exp
+            -> Maybe T.Text
+renderBrace dt ctx base brace label = do
+  base' <- renderExpIn dt ctx base
+  label' <- if isEmptyScriptArg label
+               then pure "{}"
+               else renderBraceLabel dt ctx label
+  pure $ "{" <> renderScriptBase base base' <> " " <> brace <> " " <> label' <> "}"
+
 renderBraceLabel :: DisplayType -> AlignContext -> Exp -> Maybe T.Text
 renderBraceLabel dt ctx e = do
   rendered0 <- renderExpIn dt ctx e
   let rendered = T.strip rendered0
   pure $ if isQuotedText rendered || isAtomic e
             then rendered
-            else "{" <> rendered <> "}"
+            else groupArg dt e rendered
 
 arrowScriptOpName :: Exp -> Maybe T.Text
 arrowScriptOpName e =
diff --git a/test/reader/tex/cr.test b/test/reader/tex/cr.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/cr.test
@@ -0,0 +1,18 @@
+<<< tex
+\begin{cases} a \cr b \end{cases} \begin{matrix} 1 & 2 \cr 3 & 4 \end{matrix}
+
+>>> native
+[ EDelimited
+    "{"
+    ""
+    [ Right
+        (EArray
+           [ AlignLeft ]
+           [ [ [ EIdentifier "a" ] ] , [ [ EIdentifier "b" ] ] ])
+    ]
+, EArray
+    [ AlignCenter , AlignCenter ]
+    [ [ [ ENumber "1" ] , [ ENumber "2" ] ]
+    , [ [ ENumber "3" ] , [ ENumber "4" ] ]
+    ]
+]
diff --git a/test/reader/tex/decimal_scripts.test b/test/reader/tex/decimal_scripts.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/decimal_scripts.test
@@ -0,0 +1,19 @@
+<<< tex
+x^1.2+x^.5+x^{1.2}+x_1.2+x_.5+x_{1.2}
+>>> native
+[ ESuper (EIdentifier "x") (ENumber "1")
+, ENumber ".2"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ESymbol Ord ".")
+, ENumber "5"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ENumber "1.2")
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "1")
+, ENumber ".2"
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ESymbol Ord ".")
+, ENumber "5"
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "1.2")
+]
diff --git a/test/reader/tex/numeric_script_primes.test b/test/reader/tex/numeric_script_primes.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/numeric_script_primes.test
@@ -0,0 +1,14 @@
+<<< tex
+x_1'+x_12'+x_{1'}+x^12'
+>>> native
+[ ESubsup (EIdentifier "x") (ENumber "1") (ESymbol Pun "\8242")
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "1")
+, ESuper (ENumber "2") (ESymbol Pun "\8242")
+, ESymbol Bin "+"
+, ESub
+    (EIdentifier "x") (ESuper (ENumber "1") (ESymbol Pun "\8242"))
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ENumber "1")
+, ESuper (ENumber "2") (ESymbol Pun "\8242")
+]
diff --git a/test/reader/tex/numeric_scripts.test b/test/reader/tex/numeric_scripts.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/numeric_scripts.test
@@ -0,0 +1,13 @@
+<<< tex
+x^12+x^{12}+x_12+x_{12}
+>>> native
+[ ESuper (EIdentifier "x") (ENumber "1")
+, ENumber "2"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ENumber "12")
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "1")
+, ENumber "2"
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "12")
+]
diff --git a/test/reader/tex/numeric_subsup.test b/test/reader/tex/numeric_subsup.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/numeric_subsup.test
@@ -0,0 +1,15 @@
+<<< tex
+x_1^23+x^1_23+x_12^3+x^12_3
+>>> native
+[ ESubsup (EIdentifier "x") (ENumber "1") (ENumber "2")
+, ENumber "3"
+, ESymbol Bin "+"
+, ESubsup (EIdentifier "x") (ENumber "2") (ENumber "1")
+, ENumber "3"
+, ESymbol Bin "+"
+, ESub (EIdentifier "x") (ENumber "1")
+, ESuper (ENumber "2") (ENumber "3")
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ENumber "1")
+, ESub (ENumber "2") (ENumber "3")
+]
diff --git a/test/reader/tex/script_commands_and_comments.test b/test/reader/tex/script_commands_and_comments.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/script_commands_and_comments.test
@@ -0,0 +1,20 @@
+<<< tex
+x_\alpha^12+x^\mathrm{12}3+x^\operatorname{foo}3+x^% comment
+12+x_% comment
+1^23
+>>> native
+[ ESubsup (EIdentifier "x") (EIdentifier "\945") (ENumber "1")
+, ENumber "2"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (EStyled TextNormal [ ENumber "12" ])
+, ENumber "3"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (EMathOperator "foo")
+, ENumber "3"
+, ESymbol Bin "+"
+, ESuper (EIdentifier "x") (ENumber "1")
+, ENumber "2"
+, ESymbol Bin "+"
+, ESubsup (EIdentifier "x") (ENumber "1") (ENumber "2")
+, ENumber "3"
+]
diff --git a/test/reader/tex/simple_sum_formula.test b/test/reader/tex/simple_sum_formula.test
--- a/test/reader/tex/simple_sum_formula.test
+++ b/test/reader/tex/simple_sum_formula.test
@@ -1,5 +1,5 @@
 <<< tex
-\sum_{i=1}^100 x = \frac{100*101}{2}
+\sum_{i=1}^{100} x = \frac{100*101}{2}
 
 >>> native
 [ EUnderover
diff --git a/test/reader/tex/space_commands.test b/test/reader/tex/space_commands.test
new file mode 100644
--- /dev/null
+++ b/test/reader/tex/space_commands.test
@@ -0,0 +1,16 @@
+<<< tex
+a\thinspace b\negthinspace c\thickspace d\negthickspace e\negmedspace f
+
+>>> native
+[ EIdentifier "a"
+, ESpace (1 % 6)
+, EIdentifier "b"
+, ESpace ((-1) % 6)
+, EIdentifier "c"
+, ESpace (5 % 18)
+, EIdentifier "d"
+, ESpace ((-5) % 18)
+, EIdentifier "e"
+, ESpace ((-2) % 9)
+, EIdentifier "f"
+]
diff --git a/test/test-texmath.hs b/test/test-texmath.hs
--- a/test/test-texmath.hs
+++ b/test/test-texmath.hs
@@ -14,11 +14,10 @@
 import Data.Text.Lazy.Encoding (encodeUtf8)
 import Data.Text.Encoding as TE (decodeUtf8)
 import Data.List (sort)
-import Data.Typeable (Typeable)
 import Data.Proxy
 
 newtype RoundTrip = RoundTrip Bool
-  deriving (Eq, Ord, Typeable)
+  deriving (Eq, Ord)
 
 instance IsOption RoundTrip where
   defaultValue = RoundTrip False
diff --git a/test/writer/starmath/004_square_root.test b/test/writer/starmath/004_square_root.test
--- a/test/writer/starmath/004_square_root.test
+++ b/test/writer/starmath/004_square_root.test
@@ -1,4 +1,4 @@
 <<< tex
 \sqrt{x+1}
 >>> starmath
-sqrt {{x + 1}}
+sqrt {x + 1}
diff --git a/test/writer/starmath/01.test b/test/writer/starmath/01.test
--- a/test/writer/starmath/01.test
+++ b/test/writer/starmath/01.test
@@ -19,4 +19,4 @@
     (EGrouped [ ENumber "2" , EIdentifier "a" ])
 ]
 >>> starmath
-x = {{- b plusminus sqrt {{b^2 - 4ac}}} over {2a}}
+x = {{- b plusminus sqrt {b^2 - 4ac}} over {2a}}
diff --git a/test/writer/starmath/018_integral_with_infinite_upper_limit.test b/test/writer/starmath/018_integral_with_infinite_upper_limit.test
--- a/test/writer/starmath/018_integral_with_infinite_upper_limit.test
+++ b/test/writer/starmath/018_integral_with_infinite_upper_limit.test
@@ -1,4 +1,4 @@
 <<< tex
 \int_{0}^{\infty} e^{-x}\,dx
 >>> starmath
-int_0^infinity e^{{- x}} dx
+int_0^infinity e^{- x} dx
diff --git a/test/writer/starmath/026_greek_identifier_spacing_in_products.test b/test/writer/starmath/026_greek_identifier_spacing_in_products.test
--- a/test/writer/starmath/026_greek_identifier_spacing_in_products.test
+++ b/test/writer/starmath/026_greek_identifier_spacing_in_products.test
@@ -1,4 +1,4 @@
 <<< tex
 E_{k+1}=E_{k+\frac12}-\frac{\dot{Q}_{\text{dis},k}\Delta t}{\eta_{\text{dis}}},
 >>> starmath
-E_{{k + 1}} = E_{{k + {1 over 2}}} - {{{dot Q}_{{"dis", k}} %DELTA t} over %ieta_"dis"},
+E_{k + 1} = E_{k + {1 over 2}} - {{{dot Q}_{"dis", k} %DELTA t} over %ieta_"dis"},
diff --git a/test/writer/starmath/027_math_operators_rendered_as_functions.test b/test/writer/starmath/027_math_operators_rendered_as_functions.test
--- a/test/writer/starmath/027_math_operators_rendered_as_functions.test
+++ b/test/writer/starmath/027_math_operators_rendered_as_functions.test
@@ -1,4 +1,4 @@
 <<< tex
 E_{k+1}\leftarrow\min\!\left(E^{\text{cap}}_{\text{s}},\max(0,E_{k+1})\right).
 >>> starmath
-E_{{k + 1}} leftarrow func min left ( E_"s"^"cap", func max(0, E_{{k + 1}}) right ).
+E_{k + 1} leftarrow func min left ( E_"s"^"cap", func max(0, E_{k + 1}) right ).
diff --git a/test/writer/starmath/028_nested_function_with_left_delimiter_spacing.test b/test/writer/starmath/028_nested_function_with_left_delimiter_spacing.test
--- a/test/writer/starmath/028_nested_function_with_left_delimiter_spacing.test
+++ b/test/writer/starmath/028_nested_function_with_left_delimiter_spacing.test
@@ -1,4 +1,4 @@
 <<< tex
 P_{\text{ch}, k}=\max\!\left(0,\min\!\left(P'_{\text{pv}, k},\,P^{\text{cap}}_{\text{ch}},\,P_{\text{ch,head}, k}\right)\right).
 >>> starmath
-P_{{"ch", k}} = func max left ( 0, func min left ( P_{{"pv", k}}', P_"ch"^"cap", P_{{"ch,head", k}} right ) right ).
+P_{"ch", k} = func max left ( 0, func min left ( P_{"pv", k}', P_"ch"^"cap", P_{"ch,head", k} right ) right ).
diff --git a/test/writer/starmath/041_ordinary_function_with_subscript_keeps_ordinary_script_syntax.test b/test/writer/starmath/041_ordinary_function_with_subscript_keeps_ordinary_script_syntax.test
--- a/test/writer/starmath/041_ordinary_function_with_subscript_keeps_ordinary_script_syntax.test
+++ b/test/writer/starmath/041_ordinary_function_with_subscript_keeps_ordinary_script_syntax.test
@@ -1,4 +1,4 @@
 <<< tex
 \Delta\log_{10}K_j = \log_{10}K_{j,\mathrm{FPROPS}} - \log_{10}K_{j,\mathrm{ref}},\qquad j=1,\dots,r.
 >>> starmath
-%DELTA log_10 K_j = log_10 K_{{j, "FPROPS"}} - log_10 K_{{j, "ref"}}, ~~ j = 1, dotslow , r.
+%DELTA log_10 K_j = log_10 K_{j, "FPROPS"} - log_10 K_{j, "ref"}, ~~ j = 1, dotslow , r.
diff --git a/test/writer/starmath/050_superscript_only_fragment_gets_neutral_base.test b/test/writer/starmath/050_superscript_only_fragment_gets_neutral_base.test
--- a/test/writer/starmath/050_superscript_only_fragment_gets_neutral_base.test
+++ b/test/writer/starmath/050_superscript_only_fragment_gets_neutral_base.test
@@ -1,4 +1,4 @@
 <<< tex
 ^+
 >>> starmath
-{}^{{} + {}}
+{}^{{}+{}}
diff --git a/test/writer/starmath/051_grouped_subscript_only_fragment_gets_neutral_base.test b/test/writer/starmath/051_grouped_subscript_only_fragment_gets_neutral_base.test
--- a/test/writer/starmath/051_grouped_subscript_only_fragment_gets_neutral_base.test
+++ b/test/writer/starmath/051_grouped_subscript_only_fragment_gets_neutral_base.test
@@ -1,4 +1,4 @@
 <<< tex
 _{j,\mathrm{ref}}
 >>> starmath
-{}_{{j, "ref"}}
+{}_{j, "ref"}
diff --git a/test/writer/starmath/062_fallback_to_tex_for_under_over_constructs.test b/test/writer/starmath/062_fallback_to_tex_for_under_over_constructs.test
--- a/test/writer/starmath/062_fallback_to_tex_for_under_over_constructs.test
+++ b/test/writer/starmath/062_fallback_to_tex_for_under_over_constructs.test
@@ -1,4 +1,4 @@
 <<< tex
 \underbrace{x+y}_{z}+\overbrace{x+y}^{z}
 >>> starmath
-{x + y} underbrace z + {x + y} overbrace z
+{{x + y} underbrace z} + {{x + y} overbrace z}
diff --git a/test/writer/starmath/066_standalone_plus_minus_scripts_get_neutral_operands.test b/test/writer/starmath/066_standalone_plus_minus_scripts_get_neutral_operands.test
--- a/test/writer/starmath/066_standalone_plus_minus_scripts_get_neutral_operands.test
+++ b/test/writer/starmath/066_standalone_plus_minus_scripts_get_neutral_operands.test
@@ -1,4 +1,4 @@
 <<< tex
 r_{r,e}=r_{r,e}^{+}-r_{r,e}^{-}
 >>> starmath
-r_{{r, e}} = r_{{r, e}}^{{} + {}} - r_{{r, e}}^{{} - {}}
+r_{r, e} = r_{r, e}^{{}+{}} - r_{r, e}^{{}-{}}
diff --git a/test/writer/starmath/11.test b/test/writer/starmath/11.test
--- a/test/writer/starmath/11.test
+++ b/test/writer/starmath/11.test
@@ -25,4 +25,4 @@
     NormalFrac (ENumber "1") (ESub (EIdentifier "l") (ENumber "0"))
 ]
 >>> starmath
-%iphi_n(%ikappa) = 0.033C_n^2 %ikappa^{{- 11 / 3}}, ~ {1 over L_0} ll %ikappa ll {1 over l_0}
+%iphi_n(%ikappa) = 0.033C_n^2 %ikappa^{- 11 / 3}, ~ {1 over L_0} ll %ikappa ll {1 over l_0}
diff --git a/test/writer/starmath/aligned_rate_contributions.test b/test/writer/starmath/aligned_rate_contributions.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/aligned_rate_contributions.test
@@ -0,0 +1,10 @@
+<<< tex
+\begin{aligned}
+r_{j,\pm}^{0}
+&=\underbrace{A_j\exp\!\left(-\frac{E_j}{R_{\mathrm u}T}\right)}_{k_j(T):\ \text{Arrhenius coefficient}}
+\times\underbrace{(\text{gas factor})_{j,\pm}}_{\text{local hydrogen/steam}}
+\times\underbrace{(\text{solid factor})_{j,\pm}}_{\text{inventory, presence and access}},\\
+r_j^0&=r_{j,+}^{0}-r_{j,-}^{0}.
+\end{aligned}
+>>> starmath
+matrix { alignr r_{j, plusminus {}}^0 # alignl {} = {{A_j exp left ( -{{alignc {E_j}} over {alignc {R_{nitalic u} T}}} right )} underbrace {k_j(T) : "Arrhenius coefficient"}} times {{("gas factor")_{j, plusminus {}}} underbrace "local hydrogen/steam"} times {{("solid factor")_{j, plusminus {}}} underbrace "inventory, presence and access"}, ## alignr r_j^0 # alignl {} = r_{j, +{}}^0 - r_{j, -{}}^0. }
diff --git a/test/writer/starmath/annotated_brace_product.test b/test/writer/starmath/annotated_brace_product.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/annotated_brace_product.test
@@ -0,0 +1,7 @@
+<<< tex
+\text{rate contribution}
+=\underbrace{A_j\exp\!\left(-\frac{E_j}{R_{\mathrm u}T}\right)}_{k_j(T):\ \text{Arrhenius coefficient}}
+\times\underbrace{\text{gas factor}}_{\text{local hydrogen/steam}}
+\times\underbrace{\text{solid factor}}_{\text{inventory, presence and access}}.
+>>> starmath
+"rate contribution" = {{A_j exp left ( -{E_j over {R_{nitalic u} T}} right )} underbrace {k_j(T) : "Arrhenius coefficient"}} times {"gas factor" underbrace "local hydrogen/steam"} times {"solid factor" underbrace "inventory, presence and access"}.
diff --git a/test/writer/starmath/annotated_brace_sum.test b/test/writer/starmath/annotated_brace_sum.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/annotated_brace_sum.test
@@ -0,0 +1,4 @@
+<<< tex
+1+\underbrace{x}_{a}+\underbrace{y}_{b}=\overbrace{x}^{c}+\overbrace{y}^{d}.
+>>> starmath
+1 + {x underbrace a} + {y underbrace b} = {x overbrace c} + {y overbrace d}.
diff --git a/test/writer/starmath/bare_braces.test b/test/writer/starmath/bare_braces.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/bare_braces.test
@@ -0,0 +1,4 @@
+<<< tex
+\overbrace{a}+b=\underbrace{x}+y
+>>> starmath
+{a overbrace {}} + b = {x underbrace {}} + y
diff --git a/test/writer/starmath/bare_braces_inline.test b/test/writer/starmath/bare_braces_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/bare_braces_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\overbrace{a}+b=\underbrace{x}+y
+>>> starmath-inline
+{a overbrace {}} + b = {x underbrace {}} + y
diff --git a/test/writer/starmath/brace_both_scripts.test b/test/writer/starmath/brace_both_scripts.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/brace_both_scripts.test
@@ -0,0 +1,4 @@
+<<< tex
+\underbrace{x}_{a}^2+\overbrace{y}_{b}^{c}
+>>> starmath
+{x underbrace a} csup {2} + {y overbrace c} csub {b}
diff --git a/test/writer/starmath/brace_both_scripts_inline.test b/test/writer/starmath/brace_both_scripts_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/brace_both_scripts_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\underbrace{x}_{a}^2+\overbrace{y}_{b}^{c}
+>>> starmath-inline
+{x underbrace a} csup {2} + {y overbrace c} csub {b}
diff --git a/test/writer/starmath/brace_opposite_scripts.test b/test/writer/starmath/brace_opposite_scripts.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/brace_opposite_scripts.test
@@ -0,0 +1,4 @@
+<<< tex
+\underbrace{x}^{2}+\overbrace{y}_{b}
+>>> starmath
+{x underbrace {}} csup {2} + {y overbrace {}} csub {b}
diff --git a/test/writer/starmath/brace_opposite_scripts_inline.test b/test/writer/starmath/brace_opposite_scripts_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/brace_opposite_scripts_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\underbrace{x}^{2}+\overbrace{y}_{b}
+>>> starmath-inline
+{x underbrace {}} csup {2} + {y overbrace {}} csub {b}
diff --git a/test/writer/starmath/complex_number.test b/test/writer/starmath/complex_number.test
--- a/test/writer/starmath/complex_number.test
+++ b/test/writer/starmath/complex_number.test
@@ -24,4 +24,4 @@
     (EText TextNormal "complex number")
 ]
 >>> starmath
-c = {a underbrace "real" + {b nitalic i} underbrace "imaginary"} overbrace "complex number"
+c = {{{a underbrace "real"} + {{b nitalic i} underbrace "imaginary"}} overbrace "complex number"}
diff --git a/test/writer/starmath/compound_script_signs.test b/test/writer/starmath/compound_script_signs.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/compound_script_signs.test
@@ -0,0 +1,4 @@
+<<< tex
+r_j^0=r_{j,+}^{0}-r_{j,-}^{0}
+>>> starmath
+r_j^0 = r_{j, +{}}^0 - r_{j, -{}}^0
diff --git a/test/writer/starmath/deMorgans_law.test b/test/writer/starmath/deMorgans_law.test
--- a/test/writer/starmath/deMorgans_law.test
+++ b/test/writer/starmath/deMorgans_law.test
@@ -38,4 +38,4 @@
     (ESymbol TOver "\175")
 ]
 >>> starmath
-neg (p and q) dlrarrow (neg p) or (neg q) overline {{oper ∪ from i = 1 to n A_i}} = oper ∩ from i = 1 to n overline {A_i}
+neg (p and q) dlrarrow (neg p) or (neg q) overline {oper ∪ from i = 1 to n A_i} = oper ∩ from i = 1 to n overline {A_i}
diff --git a/test/writer/starmath/differentiable_manifold.test b/test/writer/starmath/differentiable_manifold.test
--- a/test/writer/starmath/differentiable_manifold.test
+++ b/test/writer/starmath/differentiable_manifold.test
@@ -67,4 +67,4 @@
     ]
 ]
 >>> starmath
-%igamma_1 equiv %igamma_2 dlrarrow left lbrace matrix { alignl %igamma_1(0) = %igamma_2(0) = p, " and " ## alignl {left none {{alignc {nitalic d}} over {alignc {nitalic dt}}}%iphi circ %igamma_1(t) right rline}_{{t = 0}} = {left none {{alignc {nitalic d}} over {alignc {nitalic dt}}}%iphi circ %igamma_2(t) right rline}_{{t = 0}} } right none
+%igamma_1 equiv %igamma_2 dlrarrow left lbrace matrix { alignl %igamma_1(0) = %igamma_2(0) = p, " and " ## alignl {left none {{alignc {nitalic d}} over {alignc {nitalic d t}}}%iphi circ %igamma_1(t) right rline}_{t = 0} = {left none {{alignc {nitalic d}} over {alignc {nitalic d t}}}%iphi circ %igamma_2(t) right rline}_{t = 0} } right none
diff --git a/test/writer/starmath/fraction_operator_arguments.test b/test/writer/starmath/fraction_operator_arguments.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/fraction_operator_arguments.test
@@ -0,0 +1,4 @@
+<<< tex
+\frac{a}{-}+\frac{a}{+}+\frac{-}{a}+\frac{a}{\pm}+\frac{a}{-b}+\frac{a}{b-c}
+>>> starmath
+{a over {{}-{}}} + {a over {{}+{}}} + {{{}-{}} over a} + {a over {{} plusminus {}}} + {a over {- b}} + {a over {b - c}}
diff --git a/test/writer/starmath/fraction_operator_arguments_inline.test b/test/writer/starmath/fraction_operator_arguments_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/fraction_operator_arguments_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\frac{a}{-}+\frac{a}{+}+\frac{-}{a}+\frac{a}{\pm}+\frac{a}{-b}+\frac{a}{b-c}
+>>> starmath-inline
+size*0.7 {a over {{}-{}}} + size*0.7 {a over {{}+{}}} + size*0.7 {{{}-{}} over a} + size*0.7 {a over {{} plusminus {}}} + size*0.7 {a over {- b}} + size*0.7 {a over {b - c}}
diff --git a/test/writer/starmath/grouped_argument_boundaries.test b/test/writer/starmath/grouped_argument_boundaries.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_argument_boundaries.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt{x}y+\sqrt{x+y}z+x^{a+b}c+\sqrt{\underbrace{x}_{a}^2}+\hat{\overbrace{x}_{a}}+y^{\overbrace{x}_{a}}
+>>> starmath
+sqrt {x}y + sqrt {x + y}z + x^{a + b} c + sqrt {{x underbrace a} csup {2}} + hat {{x overbrace {}} csub {a}} + y^{{x overbrace {}} csub {a}}
diff --git a/test/writer/starmath/grouped_argument_boundaries_inline.test b/test/writer/starmath/grouped_argument_boundaries_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_argument_boundaries_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt{x}y+\sqrt{x+y}z+x^{a+b}c+\sqrt{\underbrace{x}_{a}^2}+\hat{\overbrace{x}_{a}}+y^{\overbrace{x}_{a}}
+>>> starmath-inline
+sqrt {x}y + sqrt {x + y}z + x^{a + b} c + sqrt {{x underbrace a} csup {2}} + hat {{x overbrace {}} csub {a}} + y^{{x overbrace {}} csub {a}}
diff --git a/test/writer/starmath/grouped_arguments.test b/test/writer/starmath/grouped_arguments.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_arguments.test
@@ -0,0 +1,4 @@
+<<< tex
+x^{a+b}+x_{i,j}+\sqrt{x+y}+\sqrt[1+k]{x+y}+\hat{x+y}+\underbrace{x}_{a+b}
+>>> starmath
+x^{a + b} + x_{i, j} + sqrt {x + y} + nroot {1 + k} {x + y} + hat {x + y} + {x underbrace {a + b}}
diff --git a/test/writer/starmath/grouped_arguments_inline.test b/test/writer/starmath/grouped_arguments_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_arguments_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+x^{a+b}+x_{i,j}+\sqrt{x+y}+\sqrt[1+k]{x+y}+\hat{x+y}+\underbrace{x}_{a+b}
+>>> starmath-inline
+x^{a + b} + x_{i, j} + sqrt {x + y} + nroot {1 + k} {x + y} + hat {x + y} + {x underbrace {a + b}}
diff --git a/test/writer/starmath/grouped_fraction_arguments.test b/test/writer/starmath/grouped_fraction_arguments.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_fraction_arguments.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt{\frac{a}{b}}+\sqrt{\tfrac{a}{b}}+\sqrt{\dfrac{a}{b}}+\sqrt{\binom{a}{b}}+x^{\frac{a}{b}}+\underbrace{x}_{\frac{a}{b}}
+>>> starmath
+sqrt {a over b} + sqrt {size*0.7 {a over b}} + sqrt {a over b} + sqrt {left ( binom{a}{b} right )} + x^{a over b} + {x underbrace {a over b}}
diff --git a/test/writer/starmath/grouped_fraction_arguments_inline.test b/test/writer/starmath/grouped_fraction_arguments_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/grouped_fraction_arguments_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt{\frac{a}{b}}+\sqrt{\tfrac{a}{b}}+\sqrt{\dfrac{a}{b}}+\sqrt{\binom{a}{b}}+x^{\frac{a}{b}}+\underbrace{x}_{\frac{a}{b}}
+>>> starmath-inline
+sqrt {size*0.7 {a over b}} + sqrt {size*0.7 {a over b}} + sqrt {a over b} + sqrt {left ( binom{a}{b} right )} + x^{size*0.7 {a over b}} + {x underbrace {size*0.7 {a over b}}}
diff --git a/test/writer/starmath/moore_determinant.test b/test/writer/starmath/moore_determinant.test
--- a/test/writer/starmath/moore_determinant.test
+++ b/test/writer/starmath/moore_determinant.test
@@ -51,4 +51,4 @@
     ]
 ]
 >>> starmath
-M = left [ matrix { %ialpha_1 # %ialpha_1^q # dotsaxis # %ialpha_1^{q^{{n - 1}}} ## %ialpha_2 # %ialpha_2^q # dotsaxis # %ialpha_2^{q^{{n - 1}}} ## dotsvert # dotsvert # {} dotsdown {} # dotsvert ## %ialpha_m # %ialpha_m^q # dotsaxis # %ialpha_m^{q^{{n - 1}}} } right ]
+M = left [ matrix { %ialpha_1 # %ialpha_1^q # dotsaxis # %ialpha_1^{q^{n - 1}} ## %ialpha_2 # %ialpha_2^q # dotsaxis # %ialpha_2^{q^{n - 1}} ## dotsvert # dotsvert # {} dotsdown {} # dotsvert ## %ialpha_m # %ialpha_m^q # dotsaxis # %ialpha_m^{q^{n - 1}} } right ]
diff --git a/test/writer/starmath/nested_bare_braces.test b/test/writer/starmath/nested_bare_braces.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/nested_bare_braces.test
@@ -0,0 +1,4 @@
+<<< tex
+\overbrace{\overbrace{x}}+\underbrace{\underbrace{y}}+\overbrace{\underbrace{z}_{a}}^{b}
+>>> starmath
+{{x overbrace {}} overbrace {}} + {{y underbrace {}} underbrace {}} + {{z underbrace a} overbrace b}
diff --git a/test/writer/starmath/operatorname.test b/test/writer/starmath/operatorname.test
--- a/test/writer/starmath/operatorname.test
+++ b/test/writer/starmath/operatorname.test
@@ -68,4 +68,4 @@
     ]
 ]
 >>> starmath
-left lbrace matrix { alignl func div_x u func div^x u func div_x^y u func div_x^y u # alignl "operatorname" ## alignl {func "⋆-lim'sup"} csub {{x toward 0}} u {func "⋆-lim'sup"} csup {w} u {func "⋆-lim'sup"} csub {{x toward 0}} csup {w} u {func "⋆-lim'sup"} csub {{x toward 0}} csup {w} u # alignl "operatorname*" ## alignl func "⋆-lim'sup"_{{x toward 0}} u func "⋆-lim'sup"^w u func "⋆-lim'sup"_{{x toward 0}}^w u func "⋆-lim'sup"_{{x toward 0}}^w u # alignl "operatorname*:nolimits" ## alignl func curl_x v {func argmax} csub {K} u # alignl "DeclareMathOperator" } right none
+left lbrace matrix { alignl func div_x u func div^x u func div_x^y u func div_x^y u # alignl "operatorname" ## alignl {func "⋆-lim'sup"} csub {x toward 0} u {func "⋆-lim'sup"} csup {w} u {func "⋆-lim'sup"} csub {x toward 0} csup {w} u {func "⋆-lim'sup"} csub {x toward 0} csup {w} u # alignl "operatorname*" ## alignl func "⋆-lim'sup"_{x toward 0} u func "⋆-lim'sup"^w u func "⋆-lim'sup"_{x toward 0}^w u func "⋆-lim'sup"_{x toward 0}^w u # alignl "operatorname*:nolimits" ## alignl func curl_x v {func argmax} csub {K} u # alignl "DeclareMathOperator" } right none
diff --git a/test/writer/starmath/quadratic_formula.test b/test/writer/starmath/quadratic_formula.test
--- a/test/writer/starmath/quadratic_formula.test
+++ b/test/writer/starmath/quadratic_formula.test
@@ -19,4 +19,4 @@
     (EGrouped [ ENumber "2" , EIdentifier "a" ])
 ]
 >>> starmath
-x = {{- b plusminus sqrt {{b^2 - 4ac}}} over {2a}}
+x = {{- b plusminus sqrt {b^2 - 4ac}} over {2a}}
diff --git a/test/writer/starmath/root_token_boundaries.test b/test/writer/starmath/root_token_boundaries.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/root_token_boundaries.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt xy+\sqrt{x}y+\sqrt{xy}+\sqrt12+\sqrt{12}
+>>> starmath
+sqrt {x}y + sqrt {x}y + sqrt {xy} + sqrt {1}2 + sqrt {12}
diff --git a/test/writer/starmath/root_token_boundaries_inline.test b/test/writer/starmath/root_token_boundaries_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/root_token_boundaries_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+\sqrt xy+\sqrt{x}y+\sqrt{xy}+\sqrt12+\sqrt{12}
+>>> starmath-inline
+sqrt {x}y + sqrt {x}y + sqrt {xy} + sqrt {1}2 + sqrt {12}
diff --git a/test/writer/starmath/script_signs_and_arithmetic.test b/test/writer/starmath/script_signs_and_arithmetic.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/script_signs_and_arithmetic.test
@@ -0,0 +1,4 @@
+<<< tex
+r_{j,+}+r_{j,-}+r_{j,\pm}+x^{-1}+x^{n-1}+x^{-}+x^{+}
+>>> starmath
+r_{j, +{}} + r_{j, -{}} + r_{j, plusminus {}} + x^{- 1} + x^{n - 1} + x^{{}-{}} + x^{{}+{}}
diff --git a/test/writer/starmath/script_token_boundaries.test b/test/writer/starmath/script_token_boundaries.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/script_token_boundaries.test
@@ -0,0 +1,4 @@
+<<< tex
+x^ab+x^{a}b+x^{ab}+x_ab+x_{a}b+x_{ab}+x^12+x^{1}2+x^{12}+x_12+x_{1}2+x_{12}+x_a^bc+x_ab^c
+>>> starmath
+x^a b + x^a b + x^{ab} + x_a b + x_a b + x_{ab} + x^1 2 + x^1 2 + x^12 + x_1 2 + x_1 2 + x_12 + x_a^b c + x_a b^c
diff --git a/test/writer/starmath/script_token_boundaries_inline.test b/test/writer/starmath/script_token_boundaries_inline.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/script_token_boundaries_inline.test
@@ -0,0 +1,4 @@
+<<< tex
+x^ab+x^{a}b+x^{ab}+x_ab+x_{a}b+x_{ab}+x^12+x^{1}2+x^{12}+x_12+x_{1}2+x_{12}+x_a^bc+x_ab^c
+>>> starmath-inline
+x^a b + x^a b + x^{ab} + x_a b + x_a b + x_{ab} + x^1 2 + x^1 2 + x^12 + x_1 2 + x_1 2 + x_12 + x_a^b c + x_a b^c
diff --git a/test/writer/starmath/sophomores_dream.test b/test/writer/starmath/sophomores_dream.test
--- a/test/writer/starmath/sophomores_dream.test
+++ b/test/writer/starmath/sophomores_dream.test
@@ -24,4 +24,4 @@
     ]
 ]
 >>> starmath
-int_0^1 x^x nitalic dx = sum from n = 1 to infinity {(- 1)^{{n + 1}} n^{{- n}}}
+int_0^1 x^x nitalic d x = sum from n = 1 to infinity {(- 1)^{n + 1} n^{- n}}
diff --git a/test/writer/starmath/sphere_volume.test b/test/writer/starmath/sphere_volume.test
--- a/test/writer/starmath/sphere_volume.test
+++ b/test/writer/starmath/sphere_volume.test
@@ -124,4 +124,4 @@
     ]
 ]
 >>> starmath
-S = left lbrace 0 <= %iphi <= 2%ipi , 0 <= %itheta <= %ipi , 0 <= %irho <= R right rbrace matrix { alignr "Volume" # alignl {} = iiint from S %irho^2 sin %itheta nitalic d%irho nitalic d%itheta nitalic d%iphi ## alignr {} # alignl {} = int_0^{{2%ipi}} nitalic d%iphi int_0^%ipi sin %itheta nitalic d%itheta int_0^R %irho^2 nitalic d%irho ## alignr {} # alignl {} = {left none %iphi right rline}_0^{{2%ipi}} {left none left ( - cos %itheta right ) right rline}_0^%ipi size*0.7 {{alignc 1} over {alignc 3}}{left none %irho^3 right rline}_0^R ## alignr {} # alignl {} = 2%ipi times 2 times size*0.7 {{alignc 1} over {alignc 3}}R^3 ## alignr {} # alignl {} = size*0.7 {{alignc 4} over {alignc 3}}%ipi R^3 }
+S = left lbrace 0 <= %iphi <= 2%ipi , 0 <= %itheta <= %ipi , 0 <= %irho <= R right rbrace matrix { alignr "Volume" # alignl {} = iiint from S %irho^2 sin %itheta nitalic d %irho nitalic d %itheta nitalic d %iphi ## alignr {} # alignl {} = int_0^{2%ipi} nitalic d %iphi int_0^%ipi sin %itheta nitalic d %itheta int_0^R %irho^2 nitalic d %irho ## alignr {} # alignl {} = {left none %iphi right rline}_0^{2%ipi} {left none left ( - cos %itheta right ) right rline}_0^%ipi size*0.7 {{alignc 1} over {alignc 3}}{left none %irho^3 right rline}_0^R ## alignr {} # alignl {} = 2%ipi times 2 times size*0.7 {{alignc 1} over {alignc 3}}R^3 ## alignr {} # alignl {} = size*0.7 {{alignc 4} over {alignc 3}}%ipi R^3 }
diff --git a/test/writer/starmath/subsup.test b/test/writer/starmath/subsup.test
--- a/test/writer/starmath/subsup.test
+++ b/test/writer/starmath/subsup.test
@@ -65,4 +65,4 @@
 , ESubsup (ESpace (1 % 1)) (EIdentifier "x") (ENumber "3")
 ]
 >>> starmath
-x_b^a ~ x_b^a ~ {func min} csub {A}~ {func max} csub {B}~ {func det} csub {C}~ {func Pr} csub {A}~ {func gcd} csub {A}~ {dot u}^2 ~ {overline u}_%ivarepsilon ~ {underline u}_b^a ~ {a + b} overbrace "term"~ {a + b} overbrace c~ {a + b} underbrace c~ {a + b} underbrace c{~}^H e3{~}_x A{~}_x^3
+x_b^a ~ x_b^a ~ {func min} csub {A}~ {func max} csub {B}~ {func det} csub {C}~ {func Pr} csub {A}~ {func gcd} csub {A}~ {dot u}^2 ~ {overline u}_%ivarepsilon ~ {underline u}_b^a ~ {{a + b} overbrace "term"}~ {{a + b} overbrace c}~ {{a + b} underbrace c}~ {{a + b} underbrace c}{~}^H e3{~}_x A{~}_x^3
diff --git a/test/writer/starmath/upright_differential_fraction.test b/test/writer/starmath/upright_differential_fraction.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/upright_differential_fraction.test
@@ -0,0 +1,4 @@
+<<< tex
+\frac{\mathrm d n_i}{\mathrm d t}
+>>> starmath
+{{nitalic d n_i} over {nitalic d t}}
diff --git a/test/writer/starmath/upright_identifier_boundaries.test b/test/writer/starmath/upright_identifier_boundaries.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/upright_identifier_boundaries.test
@@ -0,0 +1,4 @@
+<<< tex
+\mathrm d n+\mathrm d n_i+\mathrm d n^2+\mathrm d 2+\mathrm d\alpha+\mathrm d+\mathrm{dt}+\frac{\mathrm d}{t}
+>>> starmath
+nitalic d n + nitalic d n_i + nitalic d n^2 + nitalic d 2 + nitalic d %ialpha + nitalic d + "dt" + {nitalic d over t}
diff --git a/test/writer/starmath/upright_rate_equations.test b/test/writer/starmath/upright_rate_equations.test
new file mode 100644
--- /dev/null
+++ b/test/writer/starmath/upright_rate_equations.test
@@ -0,0 +1,9 @@
+<<< tex
+\frac{\mathrm d n_i}{\mathrm d t}=n_*\sum_{j\in\{1,2,3,Q,F\}}\nu_{ij}r_j,
+\qquad
+\begin{aligned}
+\frac{\mathrm d n_W}{\mathrm d t}&=n_*(3r_2-r_3-2r_Q),\\
+\frac{\mathrm d n_F}{\mathrm d t}&=n_*(r_Q-r_F).
+\end{aligned}
+>>> starmath
+{{nitalic d n_i} over {nitalic d t}} = n_{{} * {}}sum from j in left lbrace 1, 2, 3, Q, F right rbrace %inu_{ij} r_j, ~~ matrix { alignr {{alignc {nitalic d n_W}} over {alignc {nitalic d t}}} # alignl {} = n_{{} * {}}(3r_2 - r_3 - 2r_Q), ## alignr {{alignc {nitalic d n_F}} over {alignc {nitalic d t}}} # alignl {} = n_{{} * {}}(r_Q - r_F). }
diff --git a/texmath.cabal b/texmath.cabal
--- a/texmath.cabal
+++ b/texmath.cabal
@@ -1,5 +1,5 @@
 Name:                texmath
-Version:             0.13.3.3
+Version:             0.13.3.4
 Cabal-Version:       2.0
 Build-type:          Simple
 Synopsis:            Conversion between math formats.
