diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+* 0.1.2.3: 12 Oct 2012
+
+  - add Text.Ott.Pretty to other-modules field so it gets included
+    in the tarball
+
 * 0.1.2.2: 21 Aug 2012
 
   - bump upper bound on base to <4.7 and split to <0.3
diff --git a/ottparse-pretty.cabal b/ottparse-pretty.cabal
--- a/ottparse-pretty.cabal
+++ b/ottparse-pretty.cabal
@@ -1,5 +1,5 @@
 Name:                ottparse-pretty
-Version:             0.1.2.2
+Version:             0.1.2.3
 Synopsis:            Pretty-printer for Ott parse trees
 Description:         Ott (<http://www.cl.cam.ac.uk/~pes20/ott/>) is a tool for
 		     writing formal definitions of programming
@@ -36,3 +36,4 @@
                      parsec >= 3.0 && < 3.2,
                      containers >= 0.3 && < 0.6,
                      uniplate >= 1.6 && < 1.7
+  Other-modules:     Text.Ott.Pretty
diff --git a/src/Text/Ott/Pretty.hs b/src/Text/Ott/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Ott/Pretty.hs
@@ -0,0 +1,76 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Text.Ott.Pretty
+-- Copyright   :  (c) Brent Yorgey 2011
+-- License     :  BSD-style (see LICENSE)
+-- Maintainer  :  Brent Yorgey <byorgey@cis.upenn.edu>
+-- Stability   :  experimental
+--
+-- Ott (<http://www.cl.cam.ac.uk/~pes20/ott/>) is a tool for writing
+-- formal definitions of programming languages and calculi.  Often the
+-- Ott grammars one defines end up being ambiguous, and Ott signals
+-- its displeasure by spewing forth several massive parse trees in a
+-- format requiring formidable patience to read.  Finding the slight
+-- differences between two such parse trees is an exercise in
+-- seizure-inducing tedium.
+--
+-- To the rescue comes ottparse-pretty!  Simply paste in each parse
+-- and it is shown to you in a nicely formatted tree form with all the
+-- extra meaningless cruft removed.
+--
+-- This module implements the internals of the tool: a simple parser
+-- and a set of tree transformations.
+-----------------------------------------------------------------------------
+module Text.Ott.Pretty where
+
+import Text.Parsec
+import Control.Applicative ((<$>), (<*>), (*>), (<*), pure)
+
+import Data.Tree
+import Data.List.Split
+
+import Data.Generics.Uniplate.Data
+
+
+type Parser a = Parsec String () a
+
+type T = Tree String
+
+-- | Parse a string dump of an Ott parse tree.
+parseTree :: Parser T
+parseTree = char '(' *> parseApp <* char ')' <* spaces
+        <|> Node <$> parseId <*> pure [] <* spaces
+
+-- | Parse an application, i.e. a parent node with one or more children.
+parseApp :: Parser T
+parseApp = Node <$> parseId <*> many1 parseTree
+
+-- | Parse an identifier.
+parseId :: Parser String
+parseId = spaces *> many1 (alphaNum <|> char '_' <|> char ':' <|> char '\'') <* spaces
+
+-- | \"Normalize\" a parse tree by deleting meaningless cruft.
+normalizeTree :: T -> T
+normalizeTree = transformBis [ [ transformer stripColonNames ]
+                             , [ transformer deleteSteSt ]
+                             , [ transformer deleteNonterm ]
+                             , [ transformer replaceStNode ]
+                             ]
+
+deleteSteSt :: T -> T
+deleteSteSt (Node "Ste_st" [c]) = c
+deleteSteSt t = t
+
+deleteNonterm :: T -> T
+deleteNonterm (Node "St_nonterm" [n@(Node _ [])]) = n
+deleteNonterm t = t
+
+replaceStNode :: T -> T
+replaceStNode (Node "St_node" (Node name [] : cs)) = Node name cs
+replaceStNode t = t
+
+stripColonNames :: T -> T
+stripColonNames (Node s cs) =
+  case split (dropDelims . dropBlanks $ onSublist ":") s of
+    [_,n] -> Node n cs
+    _     -> Node s cs
