diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Vanessa McHale (c) 2017
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Vanessa McHale nor the names of other
+      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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+## Madlibs DSL for generating random text
+
+This is the Madlibs DSL for generating random text. There is also a vim plugin for highlighting `.mad` files. 
+
+It enables you to generate random, templated text with very little effort or expertise. 
+
+It can be used for twitter bots and more productive things.
+
+### Exmaples
+
+An exmaple is worth a thousand words (?), so suppose you wanted to generate a mediocre fortune telling bot. You could write the following code:
+
+```
+
+:define person
+    0.7 "A close friend will "
+    0.3 "You will "
+:define goodfortune
+    0.2 person "make rain on the planet Mars"
+    0.8 "nice things will happen today :)"
+:define fortune
+    0.5 "drink a boatload of milk"
+    0.5 "get angry for no reason"
+:return
+    0.8 person fortune
+    0.2 goodfortune
+```
+
+There are two "statements" in madlang, `:define` and `:return`. `:return` is the main string we'll be spitting back, so you're only allowed one of them per file. `:define` on the other hand can be used to make as many templates as you want. These templates are combinations of strings (enclosed in quotes) and names of other templates.
+
+Of course, you can't have a circular reference with names - if `goodfortune` depends on `fortune` while `fortune` depends on `goodfortune`, you'll end up with either no fortune or an infinite fortune. So instead we just throw an error. 
+
+## Installation
+
+### Stack
+
+Download `stack` with
+
+```
+curl -sSl http://haskellstack.org | sh
+```
+
+Then run `stack install` and you'll get the `madlang` executable installed on your path. You can even do `stack install madlang` if you'd like. 
+
+### Use
+
+To use it, just try
+
+```
+ $ madlang --input fortune-teller.mad
+```
+
+You can do `madlang --help` if you want a couple other options for debugging.
+
+### Syntax Highlighting
+
+Syntax highlighting for the DSL is provided in the vim plugin [here](http://github.com/vmchale/madlang-vim). You'll have to do `:set syntax=madlang` the first time you run it but everything else should work out of the box.:
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/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import Text.Madlibs (exec)
+
+main :: IO ()
+main = exec
diff --git a/madlang.cabal b/madlang.cabal
new file mode 100644
--- /dev/null
+++ b/madlang.cabal
@@ -0,0 +1,69 @@
+name: madlang
+version: 0.1.0.0
+cabal-version: >=1.10
+build-type: Simple
+license: BSD3
+license-file: LICENSE
+copyright: Copyright: (c) 2016 Vanessa McHale
+maintainer: tmchale@wisc.edu
+homepage: https://github.com/vmchale/madlang#readme
+synopsis: Initial project template from stack
+description:
+    Please see README.md
+category: Web
+author: Vanessa McHale
+extra-source-files:
+    README.md
+
+source-repository head
+    type: git
+    location: https://github.com/vmchale/madlang
+
+library
+    exposed-modules:
+        Text.Madlibs
+    build-depends:
+        base >=4.7 && <5,
+        megaparsec >=5.0.1 && <5.1,
+        text >=1.2.2.1 && <1.3,
+        optparse-generic >=1.1.1 && <1.2,
+        mwc-random >=0.13.5.0 && <0.14,
+        lens ==4.14.*,
+        mtl >=2.2.1 && <2.3,
+        ansi-wl-pprint >=0.6.7.3 && <0.7
+    default-language: Haskell2010
+    default-extensions: OverloadedStrings DeriveGeneric DeriveFunctor
+                        DeriveAnyClass
+    hs-source-dirs: src
+    other-modules:
+        Text.Madlibs.Ana.ParseUtils
+        Text.Madlibs.Cata.Run
+        Text.Madlibs.Ana.Parse
+        Text.Madlibs.Internal.Types
+        Text.Madlibs.Internal.Utils
+        Text.Madlibs.Cata.SemErr
+        Text.Madlibs.Exec.Main
+
+executable madlang
+    main-is: Main.hs
+    build-depends:
+        base >=4.9.0.0 && <4.10,
+        madlang >=0.1.0.0 && <0.2
+    default-language: Haskell2010
+    hs-source-dirs: app
+    ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
+
+test-suite madlang-test
+    type: exitcode-stdio-1.0
+    main-is: Spec.hs
+    build-depends:
+        base >=4.9.0.0 && <4.10,
+        madlang >=0.1.0.0 && <0.2,
+        hspec >=2.2.4 && <2.3,
+        megaparsec >=5.0.1 && <5.1,
+        text >=1.2.2.1 && <1.3,
+        mtl >=2.2.1 && <2.3,
+        hspec-megaparsec >=0.2.1 && <0.3
+    default-language: Haskell2010
+    hs-source-dirs: test
+    ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
diff --git a/src/Text/Madlibs.hs b/src/Text/Madlibs.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs.hs
@@ -0,0 +1,24 @@
+-- | Main module with the import functions
+module Text.Madlibs (
+                    -- * Parsers for `.mad` files
+                    parseTok
+                    , runFile
+                    , parseFile
+                    , templateGen
+                    -- * Functions and constructs for the `RandTok` data type
+                    , run
+                    , RandTok (..)
+                    -- * Types associated with the parser
+                    , Context
+                    , SemanticError (..)
+                    -- * Command-line executable
+                    , exec
+                    ) where
+
+import Text.Madlibs.Ana.Parse
+import Text.Madlibs.Ana.ParseUtils
+import Text.Madlibs.Cata.Run
+import Text.Madlibs.Cata.SemErr
+import Text.Madlibs.Exec.Main
+import Text.Madlibs.Internal.Types
+import Text.Madlibs.Internal.Utils
diff --git a/src/Text/Madlibs/Ana/Parse.hs b/src/Text/Madlibs/Ana/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Ana/Parse.hs
@@ -0,0 +1,109 @@
+-- | Parse our DSL
+module Text.Madlibs.Ana.Parse where
+
+import Text.Madlibs.Internal.Types
+import Text.Madlibs.Internal.Utils
+import Text.Madlibs.Ana.ParseUtils
+import Text.Madlibs.Cata.SemErr
+import qualified Data.Text as T
+import Text.Megaparsec
+import Text.Megaparsec.Text
+import Text.Megaparsec.Char
+import qualified Text.Megaparsec.Lexer as L
+import Data.Monoid
+import Control.Monad
+import Control.Monad.State
+
+-- | Parse a lexeme, aka deal with whitespace nicely. 
+lexeme :: Parser a -> Parser a
+lexeme = L.lexeme spaceConsumer
+
+-- | space consumer with awareness for comments
+spaceConsumer :: Parser ()
+spaceConsumer = L.space (void . some $ spaceChar) (L.skipLineComment "#") (L.skipBlockComment "{#" "#}")
+
+-- | parse a symbol, i.e. string plus surrouding whitespace
+symbol :: String -> Parser String
+symbol = L.symbol spaceConsumer
+
+-- | Parse a number/probability
+float :: Parser Prob
+float = lexeme L.float
+
+-- | Make sure definition blocks start un-indented
+nonIndented = L.nonIndented spaceConsumer
+
+--indentGuard = L.indentGuard spaceConsumer
+
+-- | Parse between quotes
+quote :: Parser a -> Parser a
+quote = between .$ (symbol "\"")
+
+-- | Parse a keyword
+keyword :: String -> Parser String
+keyword str = (pure <$> char ':') <> (symbol str) <?> "keyword"
+
+-- | Parse the `define` keyword.
+define :: Parser ()
+define = (void $ nonIndented (keyword "define"))
+    <?> "define block"
+    --make them more similar/reuse code here!!
+
+-- | Parse the `:return` keyword.
+main :: Parser ()
+main = (void $ nonIndented (keyword "return"))
+    <?> "return block"
+
+-- | Parse a template name (what follows a `:define` or `return` block)
+name :: Parser String
+name = lexeme (some letterChar) <?> "template name"
+
+-- | Parse template into a `PreTok` of referents and strings
+preStr :: Parser PreTok
+preStr = (fmap (Name . T.pack) name) <|>
+    do {
+    s <- quote (many $ noneOf ("\"\'" :: String)) ;
+    pure $ PreTok . T.pack $ s
+    } 
+    <?> "string or function name"
+
+-- | Parse a probability/corresponding template
+pair :: Parser (Prob, [PreTok])
+pair = do
+    --indentGuard
+    p <- float
+    str <- some $ preStr
+    pure (p, str)
+
+-- | Parse a `define` block
+definition :: Parser (Key, [(Prob, [PreTok])])
+definition = do
+    define
+    str <- name
+    val <- some pair
+    --linebreak
+    pure (T.pack str, val)
+
+-- | Parse the `:return` block
+final :: Parser [(Prob, [PreTok])]
+final = do
+    main
+    val <- some pair
+    pure val
+
+-- | Parse the program in terms of `PreTok` and the `Key`s to link them.
+program :: Parser [(Key, [(Prob, [PreTok])])]
+program = sortKeys . checkSemantics <$> do
+    p <- many (try definition <|> ((,) "Template" <$> final))
+    pure p
+
+-- | Parse text as a token + context (aka a reader monad with all the other functions)
+parseTokM :: Parser (Context RandTok)
+parseTokM = fmap build program
+
+-- | Parse text as a token
+--
+-- > f <- readFile "template.mad"
+-- > parseTok f
+parseTok :: T.Text -> Either (ParseError Char Dec) RandTok
+parseTok f = snd . head . (filter (\(i,j) -> i == "Template")) . (flip execState []) <$> runParser parseTokM "" f
diff --git a/src/Text/Madlibs/Ana/ParseUtils.hs b/src/Text/Madlibs/Ana/ParseUtils.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Ana/ParseUtils.hs
@@ -0,0 +1,51 @@
+-- | Helper functions to sort out parsing
+module Text.Madlibs.Ana.ParseUtils where
+
+import Text.Madlibs.Internal.Types
+import Text.Madlibs.Internal.Utils
+import Text.Madlibs.Cata.SemErr
+import Data.List
+import qualified Data.Text as T
+import Control.Monad.State
+import Data.Foldable
+import Control.Exception
+
+--consider moving Ana.ParseUtils to Cata.Sorting
+
+-- | Convert the stuff after the number to a `RandTok`
+concatTok :: T.Text -> Context [PreTok] -> Context RandTok
+concatTok param pretoks = do
+    ctx <- get
+    let unList (List a) = a
+    let toRand (Name str) = List . snd . (head' str param) . (filter ((== str) . fst)). (map (\(i,j) -> (i, unList j))) $ ctx
+        toRand (PreTok txt) = Value txt
+    fold . (map toRand) <$> pretoks
+
+-- | Given keys naming the tokens, and lists of `PreTok`, build our `RandTok`
+build :: [(Key, [(Prob, [PreTok])])] -> Context RandTok
+build list
+    | length list == 1 = do
+        let [(key, pairs)] = list
+        toks <- sequence $ map (\(i,j) -> concatTok key (pure j)) $ pairs
+        let probs = map (fst) $ pairs
+        let tok = List $ zip probs toks
+        state (\s -> (tok,((key, tok):s)))
+        --should do: recurse or take "Template" key
+    | otherwise = do
+        let (x:xs) = list
+        y <- (build [x])
+        ys <- pure <$> build xs
+        pure $ fold (y:ys)
+
+-- | Sort the keys that we have parsed so that dependencies are in the correct places
+sortKeys :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])]
+sortKeys = sortBy orderKeys
+
+-- | Ordering on the keys to account for dependency
+orderKeys :: (Key, [(Prob, [PreTok])]) -> (Key, [(Prob, [PreTok])]) -> Ordering
+orderKeys (key1, l1) (key2, l2)
+    | key1 == "Template" = GT
+    | key2 == "Template" = LT
+    | any (\pair -> any (T.isInfixOf key1) (map unTok . snd $ pair)) l1 = LT
+    | any (\pair -> any (T.isInfixOf key2) (map unTok . snd $ pair)) l1 = GT
+    | otherwise = EQ
diff --git a/src/Text/Madlibs/Cata/Run.hs b/src/Text/Madlibs/Cata/Run.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Cata/Run.hs
@@ -0,0 +1,21 @@
+-- | Module containing functions to get `Text` from `RandTok`
+module Text.Madlibs.Cata.Run where
+
+import Text.Madlibs.Internal.Types
+import Text.Madlibs.Internal.Utils
+import qualified Data.Text as T
+import System.Random.MWC
+
+-- | Generate randomized text from a `RandTok`
+run :: RandTok -> IO T.Text
+run tok@(List rs) = do
+    value <- (withSystemRandom . asGenST $ \gen -> uniform gen)
+    let ret = ((snd . head) . filter ((>=value) . fst)) $ mkCdf tok
+    case ret of
+        (Value txt) -> pure txt
+        tok@(List rs) -> run tok
+run (Value txt) = pure txt
+
+-- | Helper function to compute the cdf when we have a pdf
+mkCdf :: RandTok -> [(Double, RandTok)]
+mkCdf (List rs) = zip (cdf . (map fst) $ rs) (map snd rs)
diff --git a/src/Text/Madlibs/Cata/SemErr.hs b/src/Text/Madlibs/Cata/SemErr.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Cata/SemErr.hs
@@ -0,0 +1,62 @@
+-- | Module defining the SemErr data type
+module Text.Madlibs.Cata.SemErr where
+
+import Text.Madlibs.Internal.Types
+import Data.Typeable
+import Text.PrettyPrint.ANSI.Leijen
+import Control.Exception
+import qualified Data.Text as T
+
+-- | Datatype for a semantic error
+data SemanticError = OverloadedReturns | CircularFunctionCalls T.Text T.Text | ProbSum T.Text
+    deriving (Typeable)
+
+--also consider overloading parseError tbqh
+-- | display a `SemanticError` nicely with coloration & whatnot
+instance Show SemanticError where
+    show OverloadedReturns = show $ semErrStart <> text "File contains multiple declarations of :return"
+    show (CircularFunctionCalls f1 f2) = show $ semErrStart <> text "Circular function declaration between:" <> indent 4 (yellow $ (text' f1) <> (text ", ") <> (text' f2))
+    show (ProbSum f) = show $ semErrStart <> text "Function's options do not sum to 1:\n" <> indent 4 (yellow (text' f))
+    --we probably want to do our instance of `Show` for `ParseError` since that will let us color the position nicely @ least
+
+-- | Constant to start `SemanticError`s
+semErrStart :: Doc
+semErrStart = dullred (text "\n  Semantic Error: ")
+
+-- | Convert a `Text` to a `Doc` for use with a pretty-printer
+text' :: T.Text -> Doc
+text' = text . T.unpack
+
+-- | derived exception instance
+instance Exception SemanticError
+
+-- | big semantics checker that sequences stuff
+checkSemantics :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])]
+checkSemantics = foldr (.) id [ checkProb
+                              , checkReturn ]
+
+-- | checker to verify probabilities sum to 1
+checkProb :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])]
+checkProb = map (\(i,j) -> if sumProb j then (i,j) else throw (ProbSum i))
+--potentially consider throwing mult. errors at once obvi
+
+-- | helper to filter out stuff that doesn't
+sumProb :: [(Prob, [PreTok])] -> Bool
+sumProb = ((==1) . sum . (map fst))
+--check for approximation too
+
+-- | Take the head of the list, or throw the appropriate error given which functions we are trying to call.
+head' :: T.Text -> T.Text -> [a] -> a
+head' _ _ (x:xs) = x
+head' f1 f2 _ = throw (CircularFunctionCalls f1 f2)
+
+-- | checker to verify there is at most one `:return` statement
+checkReturn :: [(Key, [(Prob, [PreTok])])] -> [(Key, [(Prob, [PreTok])])]
+checkReturn keys
+    | singleReturn keys = keys
+    | otherwise = throw OverloadedReturns
+
+-- | Checks that we have at most one `:return` template in the file
+singleReturn :: [(Key, [(Prob, [PreTok])])] -> Bool
+singleReturn = singleton . (filter ((=="Template") . fst))
+    where singleton = not . null
diff --git a/src/Text/Madlibs/Exec/Main.hs b/src/Text/Madlibs/Exec/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Exec/Main.hs
@@ -0,0 +1,60 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- | Provides `madlang` executable
+module Text.Madlibs.Exec.Main where
+
+import Text.Madlibs.Cata.Run
+import Text.Madlibs.Ana.Parse
+import Text.Madlibs.Internal.Types
+import Text.Madlibs.Internal.Utils
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+import Text.Megaparsec
+import Options.Generic
+
+-- | datatype for the program
+data Program = Program { input :: FilePath <?> "filepath to template"
+                       , debug :: Bool <?> "whether to display parsed RandTok" --also: display crontab in that case?
+                       , rep :: Maybe Int <?> "How many times to repeat"
+                       } deriving (Generic)
+
+-- | Generated automatically by optparse-generic.
+instance ParseRecord Program where
+
+-- | Main program action
+exec :: IO ()
+exec = do
+    x <- getRecord "Text.Madlibs templating DSL"
+    case unHelpful . rep $ x of
+        (Just n) -> sequence_ . (take n) . repeat $ template x
+        Nothing -> template x
+
+-- | given a parsed record perform the appropriate IO action
+template :: Program -> IO ()
+template rec = do
+    let filepath = unHelpful . input $ rec
+    parsed <- parseFile filepath
+    runFile filepath >>= TIO.putStrLn
+    if unHelpful . debug $ rec then
+        print parsed
+    else
+        pure ()
+
+-- | Generate randomized text from a template
+templateGen :: T.Text -> Either (ParseError Char Dec) (IO T.Text)
+templateGen txt = run <$> parseTok txt
+
+-- | Generate randomized text from a file conatining a template
+runFile :: FilePath -> IO T.Text
+runFile filepath = do
+    txt <- readFile' filepath
+    either (pure . parseErrorPretty') (>>= (pure . show')) (templateGen txt)
+
+-- | Parse a template file into the `RandTok` data type
+parseFile :: FilePath -> IO (Either (ParseError Char Dec) RandTok)
+parseFile filepath = do
+    txt <- readFile' filepath
+    let val = parseTok txt
+    pure val
diff --git a/src/Text/Madlibs/Internal/Types.hs b/src/Text/Madlibs/Internal/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Internal/Types.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+-- | Module with the type of a random token
+module Text.Madlibs.Internal.Types where
+
+import qualified Data.Text as T
+import Control.Monad.State
+import Data.Functor.Identity
+import Control.Lens hiding (List, Context)
+import Data.Function
+
+-- | datatype for a double representing a probability
+type Prob = Double
+
+-- | dataype for a key aka token name
+type Key = T.Text
+
+-- | Pretoken, aka token as first read in
+data PreTok = Name T.Text | PreTok T.Text 
+    deriving (Show)
+
+-- | datatype for a token returning a random string
+data RandTok = List [(Prob, RandTok)] | Value T.Text 
+    deriving (Show, Eq)
+
+-- | Make `RandTok` a monoid so we can append them together nicely (since they do generate text). 
+--
+-- > (Value "Hello") <> (List [(0.5," you"), (0.5, " me")])
+-- > (List [(0.5,"Hello you"), (0.5, "Hello me")])
+instance Monoid RandTok where
+    mempty = Value ""
+    mappend (Value v1) (Value v2) = Value (T.append v1 v2)
+    mappend (List l1) v@(Value v1) = List $ map (over (_2) (`mappend` v)) l1 
+    mappend v@(Value v2) (List l2) = List $ map (over (_2) (`mappend` v)) l2
+    mappend l@(List l1) (List l2) = List $ [ (p, l `mappend` tok) | (p,tok) <- l2 ]
+
+-- | State monad providing context, i.e. function we've already called before
+type Context a = State [(Key, RandTok)] a
+
+-- | Compare inside the state monad using only the underlying objects
+instance (Eq a) => Eq (Context a) where
+    (==) a b = (on (==) (flip evalState [])) a b
diff --git a/src/Text/Madlibs/Internal/Utils.hs b/src/Text/Madlibs/Internal/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Madlibs/Internal/Utils.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+-- | Internal utils to help out elsewhere
+module Text.Madlibs.Internal.Utils where
+
+import Text.Madlibs.Internal.Types
+import Text.Megaparsec.Text
+import Text.Megaparsec.Error
+import qualified Data.Text as T
+import System.IO.Unsafe
+
+-- | Function to apply a value on both arguments, e.g.
+--
+-- > between .$ (symbol "\'")
+(.$) :: (a -> a -> b) -> a -> b
+(.$) f x = f x x
+
+-- | Add a PR for this? Could be useful in Megaparsec idk
+-- Allows us to use monoidal addition on parsers
+instance (Monoid a) => Monoid (Parser a) where
+    mempty = pure mempty
+    mappend x y = mappend <$> x <*> y
+
+-- | Helper function for creating a cdf from a pdf
+cdf :: [Prob] -> [Prob]
+cdf = (drop 2) . (scanl (+) 0) . ((:) 0)
+
+-- | Show as a T.Text
+show' :: (Show a) => a -> T.Text
+show' = (T.drop 1) . T.init . T.pack . show
+
+-- | Pretty-print a ParseError
+parseErrorPretty' :: ParseError Char Dec -> T.Text
+parseErrorPretty' = T.pack . parseErrorPretty
+
+-- | Strip a pre-token's name
+unTok :: PreTok -> T.Text
+unTok (PreTok txt) = ""
+unTok (Name txt) = txt
+
+-- | Read a file in as a `Text`
+readFile' :: FilePath -> IO T.Text
+readFile' = (fmap T.pack) . readFile
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Test.Hspec
+import Test.Hspec.Megaparsec
+import Text.Madlibs
+import Text.Megaparsec
+import Control.Monad.IO.Class
+import Control.Monad.State
+import Data.Function
+import Control.Exception
+import qualified Data.Text as T
+import System.IO.Unsafe
+
+main :: IO ()
+main = hspec $ do
+    describe "parseTok" $ do
+        it "parses a .mad string" $ do
+            parseTok madFile `shouldParse` (List [(1.0,List [(0.5,Value "heads"),(0.5,Value "tails")])])
+        it "fails when quotes aren't closed" $ do
+            parseTok `shouldFailOn` madFileFailure
+        it "parses when functions are out of order" $ do
+            parseTok `shouldSucceedOn` madComplexFile
+        it "returns a correct string from the template when evaluating a token" $ do
+            (testIO . run) exampleTok `shouldSatisfy` (\a -> on (||) (a ==) "heads" "tails")
+        it "throws exception when two `:return`s are declared" $ do
+            (parseTok `shouldFailOn` semErrFile) `shouldThrow` semErr
+            --this is still behaving weirdly but I don't care
+            --also we need a parse error one but that shouldn't be too hard idk
+
+semErr :: Selector SemanticError
+semErr = const True
+
+exampleTok :: RandTok
+exampleTok = List [(1.0,List [(0.5,Value "heads"),(0.5,Value "tails")])]
+
+madFile :: T.Text
+madFile = ":define something\n    0.5 \"heads\"\n    0.5 \"tails\"\n:return\n    1.0 something"
+
+madFileFailure :: T.Text
+madFileFailure = ":define something\
+\    0.5 \"heads\"\
+\    0.5 \"tails\
+\:return\
+\    1.0 something"
+
+madComplexFile :: T.Text
+madComplexFile = ":define person\
+\    0.7 \"I will \"\
+\    0.3 \"You will \"\
+\:define goodfortune\
+\    0.1 person \"make nice things happen today\"\
+\    0.9 \"nice things will happen today\"\
+\:define fortune\
+\    0.5 \"drink a boatload of milk\"\
+\    0.5 \"get heckin angry\"\
+\:return\
+\    0.8 person fortune\
+\    0.2 goodfortune"
+
+semErrFile :: T.Text
+semErrFile = ":define something\
+\    0.5 \"heads\"\
+\    0.5 \"tails\"\
+\:return\
+\    1.0 something\
+\:return\
+\    0.5 something\
+\    0.5 \"it doesn't matter b/c this is gonna blow up in our faces anyways\""
+
+-- | for the testing framework; to 
+testIO :: IO a -> a
+testIO = unsafePerformIO
