diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,1 @@
+license here BSD-like
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+# language-pig #
+
+Parser and pretty printer for the Apache Pig scripting language (http://pig.apache.org/). The current version is implemented using Parsec parser combinators.
+
+# Install #
+
+Cabal project, now on hackage, so the usual
+
+```
+cabal install language-pig
+```
+
+Or from source
+
+```
+git clone ...
+cd language-pig
+cabal install
+```
+
+# Use #
+
+Parse an expression:
+
+```
+parseString :: [Char] -> Root
+```
+
+Returns an AST (type is the root node).
+
+Parse a file:
+
+```
+parseFile :: FilePath -> IO Root
+```
+
+Returns an AST (type = Root, which is the root node).
+
+Pretty print the produced tree:
+```
+putStrLn $ prettyPrint tree
+```
+
+So to round it up, if you want to parse and pretty print the parsed AST of a Pig file (using Control.Applicative (<$>))
+
+```
+prettyPrint <$> parseFile "example.pig" >>= putStrLn
+```
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/example.pig b/example.pig
new file mode 100644
--- /dev/null
+++ b/example.pig
@@ -0,0 +1,34 @@
+users = LOAD 'sorted_log/user_registration/$date/*' USING LogStorage() AS (date:chararray, time:chararray, user_id:long);
+
+users = FOREACH users GENERATE *, ((user_id % 100) / 10) AS cohort;
+users = FOREACH users GENERATE *, (cohort <= 4 ? '04' : '59') AS herd;
+
+
+active_users = LOAD 'warehouse/active_users/daily/point/{$visit_dates}*' USING ColumnStorage(' ') AS (date:chararray, user_id:long);
+
+active_users = JOIN users BY user_id, active_users BY user_id;
+active_users = FOREACH active_users GENERATE active_users::date AS date, active_users::user_id AS user_id, users::herd AS herd;
+
+
+visits = GROUP active_users BY herd;
+visits = FOREACH visits GENERATE group AS herd, COUNT(active_users) AS visits;
+
+DESCRIBE visits;
+
+report = GROUP active_users BY (date, herd);
+
+report = FOREACH report GENERATE FLATTEN(group) AS (date, herd), COUNT(active_users) AS day_visits;
+DESCRIBE report;
+
+
+report = JOIN report BY herd, visits BY herd;
+report = FOREACH report GENERATE report::date AS date, report::herd AS herd, report::day_visits AS day_visits, visits::visits AS visits;
+
+
+define RESOLVE `python delta.py $date` SHIP('delta.py');
+
+report = STREAM report THROUGH RESOLVE AS (day:chararray, herd:chararray, day_visits:int, visits:int);
+
+report = FOREACH report GENERATE '$date' AS date, *;
+
+STORE report INTO '$output' USING ColumnStorage(',');
diff --git a/language-pig.cabal b/language-pig.cabal
new file mode 100644
--- /dev/null
+++ b/language-pig.cabal
@@ -0,0 +1,52 @@
+-- Initial language-pig.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                language-pig
+version:             0.1.0.0
+synopsis:            Pig parser in haskell.
+description:         Parser and pretty printer for the Apache Pig scripting language (http://pig.apache.org/). The current version is implemented using Parsec parser combinators.
+-- description:         
+license:             MIT
+license-file:        LICENSE
+author:              Elise Huard
+maintainer:          elise@jabberwocky.eu
+-- copyright:           
+category:            Language
+build-type:          Simple
+cabal-version:       >=1.8
+Stability:           Experimental
+
+Extra-Source-Files:
+  example.pig
+  README.md
+
+source-repository head
+  type:     git
+  location: https://github.com/elisehuard/language-pig
+
+library
+  hs-source-dirs: src
+  -- exposed-modules:     
+  Exposed-modules:      Language.Pig.Parser
+                        Language.Pig.Parser.Parser
+                        Language.Pig.Parser.AST
+                        Language.Pig.Pretty
+  -- other-modules:       
+  -- Other-modules:        Language.Pig.Parser.Token
+  build-depends:       base ==4.6.*, Cabal >= 1.16.0
+                       , parsec >= 3.1.3
+                       , containers
+                       , pretty-tree
+
+test-suite Tests
+  hs-source-dirs: test
+  main-is: tests.hs
+  Type: exitcode-stdio-1.0
+  build-depends:       base ==4.6.*, Cabal >= 1.16.0
+                       , language-pig
+                       , QuickCheck
+                       , HUnit
+                       , test-framework
+                       , test-framework-hunit
+                       , test-framework-quickcheck2
+                       , text
diff --git a/src/Language/Pig/Parser.hs b/src/Language/Pig/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Pig/Parser.hs
@@ -0,0 +1,7 @@
+module Language.Pig.Parser
+        (
+          PA.parseString
+        , PA.parseFile
+        ) where
+
+import qualified Language.Pig.Parser.Parser as PA
diff --git a/src/Language/Pig/Parser/AST.hs b/src/Language/Pig/Parser/AST.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Pig/Parser/AST.hs
@@ -0,0 +1,119 @@
+{-# LANGUAGE CPP, DeriveDataTypeable #-}
+module Language.Pig.Parser.AST where
+
+#define DERIVE deriving (Eq,Ord,Show,Typeable,Data)
+
+import Data.Data
+import Data.Typeable
+
+-- source:
+-- http://wiki.apache.org/pig/PigLexer
+-- http://wiki.apache.org/pig/PigParser
+
+data Root = Seq [Statement]
+            DERIVE
+
+data Statement = Assignment Alias OpClause
+               | Describe Alias
+               | DefineUDF Alias Command DefineSpec
+               | Store Alias Path Function
+               DERIVE
+
+data OpClause = LoadClause Path Function TupleDef
+              | ForeachClause Alias GenBlock
+              | GroupClause Alias GroupBy
+              | InnerJoinClause [Join]
+              | StreamClause Alias Alias TupleDef
+              DERIVE
+
+data GenBlock = GenBlock [Transform]
+                DERIVE
+
+data GroupBy = SingleColumn Alias
+             | MultipleColumn Tuple
+             DERIVE
+
+data Transform = Flatten String Tuple -- foreach flatten transform
+               | TupleFieldGlob
+               | AliasTransform Alias Alias
+               | ExpressionTransform Expression Alias
+               | FunctionTransform Function Alias
+               | EnvTransform Scalar Alias
+               DERIVE
+
+data Join = Join String String
+            DERIVE
+
+data DefineSpec = Ship Path
+                  DERIVE
+
+data Alias = Identifier String
+             DERIVE
+
+data Path = Filename String
+          | Directory String
+          DERIVE
+
+data Command = Exec String
+               DERIVE
+
+data Function = Function String [Argument]
+                DERIVE
+
+data Argument = StringArgument Scalar
+              | AliasArgument Alias
+              DERIVE
+
+data TupleDef = TupleDef [Field]
+                DERIVE
+
+data Tuple = Tuple [Alias]
+             DERIVE
+
+data Field = Field Alias SimpleType
+             DERIVE
+
+data Expression = Unary Operator Expression
+                | Binary Operator Expression Expression
+                | BinCond BooleanExpression Expression Expression
+                | ScalarTerm Scalar
+                | AliasTerm Alias
+                DERIVE
+
+data BooleanExpression = BooleanExpression ComparisonOperator Expression Expression
+                       | BooleanUnary BooleanOperator BooleanExpression
+                       | BooleanBinary BooleanOperator BooleanExpression BooleanExpression
+                       DERIVE
+
+data Scalar = Number (Either Integer Double)
+            | String String
+            DERIVE
+
+data SimpleType = Int
+                | Long
+                | Float
+                | Double
+                | CharArray
+                | ByteArray
+                DERIVE
+
+data BooleanOperator = And
+                     | Or
+                     | Not
+                     DERIVE
+
+data Operator = Neg
+              | Add
+              | Subtract
+              | Multiply
+              | Divide
+              | Modulo
+              DERIVE
+
+data ComparisonOperator = Equal
+                        | NotEqual
+                        | Greater
+                        | Less
+                        | GreaterEqual
+                        | LessEqual
+                        DERIVE
diff --git a/src/Language/Pig/Parser/Parser.hs b/src/Language/Pig/Parser/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Pig/Parser/Parser.hs
@@ -0,0 +1,314 @@
+module Language.Pig.Parser.Parser (
+  parseString
+  , parseFile
+  , module Language.Pig.Parser.AST
+) where
+
+import System.IO
+import Text.ParserCombinators.Parsec
+import Text.ParserCombinators.Parsec.Expr
+import Text.ParserCombinators.Parsec.Language
+import qualified Text.ParserCombinators.Parsec.Token as Token
+import Data.List (intercalate)
+import Control.Applicative ((<$>), (<*>), (*>), (<*))
+
+import Language.Pig.Parser.AST
+
+
+-- Lexer
+
+specialChar = oneOf "_"
+
+pigLanguageDef :: LanguageDef st
+pigLanguageDef = emptyDef {
+            Token.commentStart = "/*"
+          , Token.commentEnd = "*/"
+          , Token.commentLine = "--"
+          , Token.nestedComments = True
+          , Token.identStart     = letter
+          , Token.identLetter    = alphaNum <|> specialChar
+          , Token.reservedNames = ["LOAD", "USING", "AS", -- todo case insensitivity of these keywords
+                                   "FOREACH", "GENERATE", "FLATTEN",
+                                   "JOIN", "BY",
+                                   "GROUP",
+                                   "DESCRIBE", "SHIP",
+                                   "DEFINE",
+                                   "STREAM", "THROUGH",
+                                   "STORE", "INTO", "USING",
+                                   "int", "long", "float", "double", "chararray", "bytearray", "*"]
+          , Token.reservedOpNames = ["=", "+", "-", "*", "/", "%", "?", ":"]
+          , Token.caseSensitive = False
+        }
+
+lexer = Token.makeTokenParser pigLanguageDef
+
+identifier = Token.identifier lexer
+reserved = Token.reserved lexer
+reservedOp = Token.reservedOp lexer
+integer = Token.integer lexer
+naturalOrFloat = Token.naturalOrFloat lexer
+semi = Token.semi lexer
+comma = Token.comma lexer
+whiteSpace = Token.whiteSpace lexer
+parens = Token.parens lexer
+lexeme = Token.lexeme lexer
+
+-- parser to handle double colon.
+pigIdentifier = try(detailedIdentifier) <|> identifier
+detailedIdentifier :: Parser String
+detailedIdentifier = lexeme $
+                     (intercalate "::") <$> sepBy1 identifierPart (string "::")
+identifierPart = (:) <$> letter <*> many1 (alphaNum <|> specialChar)
+
+
+-- Parser: top-down
+
+parseString :: [Char] -> Root
+parseString input = case parsePig input of
+    Left msg -> error (show msg)
+    Right p -> p
+
+parseFile :: FilePath -> IO Root
+parseFile filename = parseString <$> readFile (filename)
+
+parsePig :: String -> Either ParseError Root
+parsePig input = parse pigParser "pigParser error" input
+
+pigParser :: Parser Root
+pigParser = whiteSpace >> statements
+
+statements :: Parser Root
+statements = Seq <$> endBy statement semi
+
+statement :: Parser Statement
+statement = query <|> describe <|> define <|> store
+
+query :: Parser Statement
+query = Assignment <$> 
+           (Identifier <$> identifier) <*>
+           (reservedOp "=" *> opClause)
+
+describe :: Parser Statement
+describe = Describe <$> (reserved "DESCRIBE" *> pigVar)
+
+define :: Parser Statement
+define = DefineUDF <$>
+           (reserved "define" *>
+           pigVar) <*>
+           executable <*>
+           shipClause -- could be input, output, ship, cache, stderr in full pig grammar
+
+store :: Parser Statement
+store = Store <$>
+        (reserved "STORE" *>
+         pigVar) <*>
+        (reserved "INTO" *>
+         pigQuotedString Directory) <*>
+        (reserved "USING" *>
+         pigFunc)
+
+
+opClause :: Parser OpClause
+opClause = loadClause
+       <|> foreachClause
+       <|> innerJoinClause
+       <|> groupClause
+       <|> streamClause
+
+loadClause :: Parser OpClause
+loadClause = LoadClause <$>
+                (reserved "LOAD" *>
+                pigQuotedString Filename) <*>
+                (reserved "USING" *>
+                pigFunc) <*>
+                (reserved "AS" *>
+                pigTupleDef)
+
+-- foreach: only the block (outer bag) version
+foreachClause :: Parser OpClause
+foreachClause = ForeachClause <$>
+                    (reserved "FOREACH" *>
+                    pigVar) <*>
+                    (reserved "GENERATE" *>
+                    (GenBlock <$> sepBy transform comma))
+
+innerJoinClause :: Parser OpClause
+innerJoinClause = InnerJoinClause <$>
+                    (reserved "JOIN" *>
+                    sepBy joinTable comma)
+
+groupClause :: Parser OpClause
+groupClause = GroupClause <$>
+                (reserved "GROUP" *>
+                pigVar) <*>
+                (reserved "BY" *>
+                (MultipleColumn <$> tuple <|> SingleColumn <$> name))
+
+streamClause :: Parser OpClause
+streamClause = StreamClause <$>
+                (reserved "STREAM" *>
+                pigVar) <*>
+                (reserved "THROUGH" *>
+                pigVar) <*>
+                (reserved "AS" *>
+                pigTupleDef)
+
+joinTable :: Parser Join
+joinTable = Join <$>
+               pigIdentifier <*>
+               (reserved "BY" *>
+               pigIdentifier)
+
+shipClause :: Parser DefineSpec
+shipClause = Ship . Filename <$> 
+               (reserved "SHIP" *>
+                parens quotedString)
+
+pigVar :: Parser Alias
+pigVar = Identifier <$> pigIdentifier
+
+pigQuotedString :: (String -> a) -> Parser a
+pigQuotedString constructor = constructor <$> quotedString
+
+pigFunc :: Parser Function
+pigFunc = Function <$>
+            identifier <*>
+            parens arguments
+
+arguments :: Parser [Argument]
+arguments = sepBy argument comma
+
+argument :: Parser Argument
+argument = (StringArgument . String <$> quotedString) <|> 
+           (AliasArgument <$> pigVar)
+
+quotedString :: Parser String
+quotedString = (char '\'' *> (many $ noneOf "\'")) <* char '\'' <* whiteSpace -- doesn't take into account escaped quotes
+
+executable :: Parser Command
+executable = Exec <$> (char '`' *> (many $ noneOf "`") <* char '`' <* whiteSpace)
+
+pigTupleDef :: Parser TupleDef
+pigTupleDef = TupleDef <$> parens tupleDef
+
+tupleDef :: Parser [Field]
+tupleDef = sepBy field comma
+
+field :: Parser Field
+field = Field <$>
+            pigVar
+            <* char ':'
+            <*> pigType
+
+pigType :: Parser SimpleType
+pigType = pigSimpleType "int" Int <|>
+          pigSimpleType "long" Long <|>
+          pigSimpleType "float" Float <|>
+          pigSimpleType "double" Double <|>
+          pigSimpleType "chararray" CharArray <|>
+          pigSimpleType "bytearray" ByteArray
+
+pigSimpleType :: String -> SimpleType -> Parser SimpleType
+pigSimpleType typeString constructor = reserved typeString >> return constructor
+              
+transform :: Parser Transform
+transform = try(aliasTransform)
+         <|> flattenTransform
+         <|> tupleFieldGlob
+         <|> expressionTransform
+         <|> functionTransform
+         <|> envTransform
+
+flattenTransform :: Parser Transform
+flattenTransform = Flatten <$>
+                      (reserved "FLATTEN" *>
+                       parens pigIdentifier) <*>
+                      (reserved "AS" *>
+                       tuple)
+
+expressionTransform :: Parser Transform
+expressionTransform = ExpressionTransform <$>
+                       generalExpression <*>
+                       (reserved "AS" *>
+                        (Identifier <$> identifier))
+
+functionTransform :: Parser Transform
+functionTransform = FunctionTransform <$>
+                      pigFunc <*>
+                      (reserved "AS" *>
+                       (Identifier <$> identifier))
+
+aliasTransform :: Parser Transform
+aliasTransform = AliasTransform <$>
+                   (Identifier <$> pigIdentifier) <*>
+                   (reserved "AS" *>
+                   (Identifier <$> identifier))
+
+envTransform :: Parser Transform
+envTransform = EnvTransform <$>
+                  pigQuotedString String <*>
+                  (reserved "AS" *>
+                   (Identifier <$> identifier))
+
+-- general expression:
+-- fieldExpression or literal or function or binary operation (+-*/%) or bincond (?:)
+-- bincond: boolean expression (==, !=, >, <, >=, <=) (and, or, not)
+generalExpression :: Parser Expression
+generalExpression = parens calculation
+
+-- conditional is ternary operator, so lookahead to try and parse it first.
+calculation :: Parser Expression
+calculation = try(conditional) <|> buildExpressionParser pigOperators pigTerm
+
+pigOperators = [[Prefix (reservedOp "-" >> return (Unary Neg))]
+               ,[Infix (reservedOp "*" >> return (Binary Multiply)) AssocLeft]
+               ,[Infix (reservedOp "/" >> return (Binary Divide)) AssocLeft]
+               ,[Infix (reservedOp "%" >> return (Binary Modulo)) AssocLeft]
+               ,[Infix (reservedOp "+" >> return (Binary Add)) AssocLeft]
+               ,[Infix (reservedOp "-" >> return (Binary Subtract)) AssocLeft]]
+
+pigTerm :: Parser Expression
+pigTerm = (ScalarTerm . String <$> quotedString)
+      <|> (ScalarTerm <$> number)
+      <|> generalExpression
+      <|> (AliasTerm <$> name)
+
+number = Number <$> naturalOrFloat -- for now - could be naturalOrFloat for inclusion
+
+conditional :: Parser Expression
+conditional = BinCond <$>
+                booleanExpression <*>
+                (reserved "?" *>
+                 calculation) <*>
+                (reserved ":" *>
+                 calculation)
+
+booleanExpression = buildExpressionParser booleanOperators booleanTerm
+
+booleanTerm = parens booleanExpression
+          <|> comparisonExpression
+
+booleanOperators = [ [Prefix (reservedOp "not" >> return (BooleanUnary Not))]
+                   , [Infix  (reservedOp "and" >> return (BooleanBinary And)) AssocLeft]
+                   , [Infix  (reservedOp "or"  >> return (BooleanBinary Or)) AssocLeft]]
+
+comparisonExpression :: Parser BooleanExpression
+comparisonExpression = flippedBooleanExpression <$> pigTerm <*> relation <*> pigTerm
+                      where flippedBooleanExpression expr1 op expr2 = BooleanExpression op expr1 expr2
+
+-- bincond: boolean expression (==, !=, >, <, >=, <=) (and, or, not)
+relation = (reservedOp ">" >> return Greater) <|>
+           (reservedOp "<" >> return Less) <|>
+           (reservedOp "<=" >> return LessEqual) <|>
+           (reservedOp ">=" >> return GreaterEqual) <|>
+           (reservedOp "==" >> return Equal) <|>
+           (reservedOp "!=" >> return NotEqual)
+
+tupleFieldGlob :: Parser Transform
+tupleFieldGlob = reserved "*" >> return TupleFieldGlob
+
+tuple :: Parser Tuple
+tuple = Tuple <$> parens (sepBy name comma)
+
+name :: Parser Alias
+name = Identifier <$> pigIdentifier
diff --git a/src/Language/Pig/Pretty.hs b/src/Language/Pig/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Pig/Pretty.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+module Language.Pig.Pretty
+    ( prettyPrint )
+where
+
+import Language.Pig.Parser.Parser
+import Text.ParserCombinators.Parsec
+
+import Data.List (intercalate)
+import Data.Tree
+import Data.Tree.Pretty
+
+-- patterns:
+--  node name + list nodes
+--  node name + literal to display = terminalnode
+--  node name + arguments used as lists to use as nodes
+
+class Treeable a where
+  toTree :: a -> (Tree String)
+
+instance Treeable Root where
+  toTree (Seq stmts) = Node "sequence of statements" (map toTree stmts)
+
+instance Treeable Statement where
+  toTree (Assignment a b) = Node "assignment" [toTree a, toTree b]
+  toTree (Describe a) = Node "describe statement" [toTree a]
+  toTree (DefineUDF a b c) = Node "define UDF statement" [toTree a, toTree b, toTree c]
+  toTree (Store a b c) = Node "store statement" [toTree a, toTree b, toTree c]
+
+instance Treeable OpClause where
+  toTree (LoadClause a b c) = Node "LOAD clause" [toTree a, toTree b, toTree c]
+  toTree (ForeachClause a b) = Node "FOREACH clause" [toTree a, toTree b]
+  toTree (GroupClause a b) = Node "GROUP clause" [toTree a, toTree b]
+  toTree (InnerJoinClause joins) = Node "JOIN clause" (map toTree joins)
+  toTree (StreamClause a b c) = Node "STREAM clause" [toTree a, toTree b, toTree c]
+
+instance Treeable GenBlock where
+  toTree (GenBlock transforms) = Node "transformation block" (map toTree transforms)
+
+instance Treeable GroupBy where
+  toTree (SingleColumn a) = Node "group by" [toTree a]
+  toTree (MultipleColumn a) = Node "group by" [toTree a]
+
+instance Treeable Transform where
+  toTree (Flatten a b) = Node ("FLATTEN: " ++ a) [toTree b]
+  toTree (TupleFieldGlob) = Node "*" []
+  toTree (AliasTransform a b) = Node "alias" [toTree a, toTree b]
+  toTree (ExpressionTransform a b) = Node "calculate" [toTree a, toTree b]
+  toTree (FunctionTransform a b) = Node "function expression" [toTree a, toTree b]
+  toTree (EnvTransform a b) = Node "name variable" [toTree a, toTree b]
+
+instance Treeable Join where
+  toTree (Join a b) = Node ("join " ++ a ++ " by " ++ b) []
+
+instance Treeable DefineSpec where
+  toTree (Ship p) = Node "SHIP" [toTree p]
+
+instance Treeable Alias where
+  toTree (Identifier s) = Node ("identifier: " ++ s) []
+
+instance Treeable Path where
+  toTree (Filename s) = Node ("filename: \"" ++ s ++ "\"") []
+  toTree (Directory s) = Node ("directory: \"" ++ s ++ "\"") []
+
+instance Treeable Command where
+  toTree (Exec s) = Node ("execute command: " ++ s) []
+
+instance Treeable Function where
+  toTree (Function s a) = Node ("function " ++ s) (map toTree a)
+
+instance Treeable Argument where
+  toTree (StringArgument (String s)) = Node ("string argument: \"" ++ s ++ "\"") []
+  toTree (StringArgument (Number s)) = Node ("number argument: " ++ show s) []
+  toTree (AliasArgument (Identifier s)) = Node ("identifier argument: \"" ++ s ++ "\"") []
+
+instance Treeable TupleDef where
+  toTree (TupleDef f) = Node "tuple def" (map toTree f)
+
+instance Treeable Tuple where
+  toTree (Tuple t) = Node "tuple" (map toTree t)
+
+instance Treeable Field where
+  toTree (Field (Identifier s) t) = Node ("field: " ++ s ++ " of type " ++ show t) []
+
+instance Treeable Expression where
+  toTree (Unary o e) = Node "unary expression" [toTree o, toTree e]
+  toTree (Binary o e1 e2) = Node "binary expression" [toTree o, toTree e1, toTree e2]
+  toTree (BinCond e1 e2 e3) = Node "ternary conditional expression" [toTree e1, toTree e2, toTree e3]
+  toTree (ScalarTerm (String s)) = Node ("scalar: string " ++ s) []
+  toTree (ScalarTerm number) = toTree number
+  toTree (AliasTerm alias) = toTree alias
+
+instance Treeable BooleanExpression where
+  toTree (BooleanExpression o e1 e2) = Node "comparison expression" [toTree o, toTree e1, toTree e2]
+  toTree (BooleanUnary o e) = Node "boolean unary expression" [toTree o, toTree e]
+  toTree (BooleanBinary o e1 e2) = Node "boolean binary expression" [toTree o, toTree e1, toTree e2]
+
+instance Treeable Scalar where
+  toTree (Number (Right i)) = Node ("integer:" ++ show i) []
+  toTree (Number (Left f)) = Node ("double:" ++ show f) []
+  toTree (String s) = Node ("string: \"" ++ s ++ "\"") []
+
+instance Treeable SimpleType where
+  toTree c = Node (show c) []
+
+instance Treeable Operator where
+  toTree c = Node (show c) []
+
+instance Treeable BooleanOperator where
+  toTree c = Node (show c) []
+
+instance Treeable ComparisonOperator where
+  toTree c = Node (show c) []
+
+prettyPrint :: Root -> String
+prettyPrint ast = drawVerticalTree $ toTree ast
diff --git a/test/tests.hs b/test/tests.hs
new file mode 100644
--- /dev/null
+++ b/test/tests.hs
@@ -0,0 +1,11 @@
+module Main
+       where
+
+import Test.Framework (defaultMain)
+
+import Language.Pig.Parser.Test
+import Language.Pig.Pretty.Test
+
+
+main :: IO ()
+main = defaultMain [parserSuite, prettyPrintSuite]
