diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009-2010 Bernard James Pope (also known as Bernie Pope).
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/language-python-test.cabal b/language-python-test.cabal
new file mode 100644
--- /dev/null
+++ b/language-python-test.cabal
@@ -0,0 +1,39 @@
+name:                language-python-test
+version:             0.5.0
+cabal-version:       >= 1.6
+synopsis:            testing code for the language-python library
+description:         testing code for the language-python library
+category:            Language
+license:             BSD3
+license-file:        LICENSE
+copyright:           (c) 2014-2015 Bernard James Pope
+author:              Bernard James Pope (Bernie Pope)
+maintainer:          florbitous@gmail.com
+homepage:            http://github.com/bjpop/language-python-test
+build-type:          Simple
+stability:           experimental
+
+source-repository head
+   type: git
+   location: https://github.com/bjpop/language-python-test
+
+Executable language-python-roundtrip
+   hs-source-dirs:
+      ./src
+   main-is: RoundTrip.hs
+   other-modules:
+   build-depends:    base == 4.*, language-python == 0.5.0
+
+Executable language-python-tokens
+   hs-source-dirs:
+      ./src
+   main-is: Tokens.hs
+   other-modules:
+   build-depends:    base == 4.*, language-python == 0.5.0
+
+Executable language-python-parse-pretty
+   hs-source-dirs:
+      ./src
+   main-is: ParsePretty.hs
+   other-modules:
+   build-depends:    base == 4.*, language-python == 0.5.0
diff --git a/src/ParsePretty.hs b/src/ParsePretty.hs
new file mode 100644
--- /dev/null
+++ b/src/ParsePretty.hs
@@ -0,0 +1,42 @@
+import Language.Python.Common
+import Language.Python.Version2 as V2
+import Language.Python.Version3 as V3
+import System.Exit
+import System.Environment
+
+data PythonVersion = Two | Three
+   deriving (Eq, Show)
+
+type Parser = String -> String -> Either ParseError (ModuleSpan, [Token])
+
+main :: IO ()
+main = do
+   args <- getArgs
+   case args of
+      (versionStr:inFile:_rest) ->
+         case parseVersion versionStr of
+            Nothing -> do
+               putStrLn $ "Unknown Python version: " ++ versionStr
+               exitFailure
+            Just version -> do
+               contents <- readFile inFile
+               let parser = pickParser version
+               case parseAndPretty parser inFile contents of
+                  Left error -> putStrLn $ prettyText error
+                  Right ast -> putStrLn $ prettyText ast 
+      _other -> putStrLn "Incorrect command line. Expected: <2|3|n> inputFileName"
+
+pickParser :: PythonVersion -> Parser
+pickParser Two = V2.parseModule
+pickParser Three = V3.parseModule
+
+parseAndPretty :: Parser -> FilePath -> String -> Either ParseError ModuleSpan
+parseAndPretty parser fileName contents =
+   case parser contents fileName of
+      Left e -> Left e
+      Right (ast, _comments) -> Right ast
+
+parseVersion :: String -> Maybe PythonVersion
+parseVersion "2" = Just Two
+parseVersion "3" = Just Three
+parseVersion _other = Nothing
diff --git a/src/RoundTrip.hs b/src/RoundTrip.hs
new file mode 100644
--- /dev/null
+++ b/src/RoundTrip.hs
@@ -0,0 +1,80 @@
+import Language.Python.Common
+import Language.Python.Version2 as V2
+import Language.Python.Version3 as V3
+import System.Exit
+import System.Environment
+
+data PythonVersion = Two | Three | Both
+   deriving (Eq, Show)
+
+data Comparison = ParseFailed String | Equal | NotEqual String String
+type Parser = String -> String -> Either ParseError (ModuleSpan, [Token])
+
+main :: IO ()
+main = do
+   args <- getArgs
+   case args of
+      (versionStr:inFile:_rest) ->
+         case parseVersion versionStr of
+            Nothing -> do
+               putStrLn $ "Unknown Python version: " ++ versionStr
+               exitFailure
+            Just version -> do
+               contents <- readFile inFile
+               let parsers = pickParsers version
+                   comparisons = [parseAndCompare p inFile contents | p <- parsers]
+               test <- check comparisons
+               if test then exitWith ExitSuccess else exitSuccess
+      _other -> putStrLn "Incorrect command line. Expected: <2|3|n> inputFileName"
+
+check :: [Comparison] -> IO Bool 
+check [] = return True -- must have all been equal
+check (Equal:rest) = check rest
+check (NotEqual s1 s2:_rest) = do
+   doubleLine
+   putStrLn "Round trip parse failed"
+   doubleLine
+   putStrLn "pretty1"
+   line
+   putStrLn s1
+   doubleLine
+   putStrLn "pretty2"
+   line
+   putStrLn s2
+   return False
+check (ParseFailed e:_rest) = do
+   putStrLn "Parse failed with error: "
+   putStrLn e
+   return False
+
+pickParsers :: PythonVersion -> [Parser]
+pickParsers Two = [V2.parseModule]
+pickParsers Three = [V3.parseModule]
+pickParsers Both = [V2.parseModule, V3.parseModule]
+
+parseAndCompare :: Parser -> FilePath -> String -> Comparison
+parseAndCompare parser inFile contents =
+   case parseAndPretty parser inFile contents of
+      Left e -> ParseFailed $ prettyText e
+      Right pretty1 ->
+         case parseAndPretty parser "<pretty printed>" pretty1 of
+            Left e -> ParseFailed $ prettyText e
+            Right pretty2
+               | pretty1 == pretty2 -> Equal
+               | otherwise -> NotEqual pretty1 pretty2
+
+line, doubleLine :: IO ()
+line = putStrLn $ replicate 80 '-'
+doubleLine = putStrLn $ replicate 80 '='
+
+parseAndPretty :: Parser -> FilePath -> String -> Either ParseError String
+parseAndPretty parser fileName contents =
+   case parser contents fileName of
+      Left e -> Left e
+      Right (ast, _comments) -> Right (prettyText ast ++ "\n")
+
+parseVersion :: String -> Maybe PythonVersion
+parseVersion "2" = Just Two
+parseVersion "3" = Just Three
+parseVersion "n" = Just Both
+parseVersion _other = Nothing
diff --git a/src/Tokens.hs b/src/Tokens.hs
new file mode 100644
--- /dev/null
+++ b/src/Tokens.hs
@@ -0,0 +1,39 @@
+import Language.Python.Common
+import Language.Python.Version2 as V2
+import Language.Python.Version3 as V3
+import System.Exit
+import System.Environment
+
+data PythonVersion = Two | Three
+   deriving (Eq, Show)
+
+type Lexer = String -> String -> Either ParseError [Token]
+
+main :: IO ()
+main = do
+   args <- getArgs
+   case args of
+      (versionStr:inFile:_rest) ->
+         case parseVersion versionStr of
+            Nothing -> do
+               putStrLn $ "Unknown Python version: " ++ versionStr
+               exitFailure
+            Just version -> do
+               contents <- readFile inFile
+               runLexer inFile (pickLexer version) contents
+      _other -> putStrLn "Incorrect command line. Expected: <2|3> inputFileName"
+
+pickLexer :: PythonVersion -> Lexer
+pickLexer Two = V2.lex
+pickLexer Three = V3.lex
+
+parseVersion :: String -> Maybe PythonVersion
+parseVersion "2" = Just Two
+parseVersion "3" = Just Three
+parseVersion _other = Nothing
+
+runLexer :: FilePath -> Lexer -> String -> IO ()
+runLexer inFile lex contents = do
+   case lex contents inFile of
+      Left e -> print e
+      Right toks -> putStr $ unlines $ map debugTokenString toks
