diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,63 +1,67 @@
-# Changelog
-
-## 0.3
-
-Highlights:
-
-* Adding `Metaparser`, a parser that constructs a parser from a grammar similar to EBNF
-
-### 0.3.0
-
-* Merged `Atomics` and `Transformers` into `Prebuilt`
-* Removed deprecated `skipN`
-* Renamed `TokenStream` functions to share the names of `Prelude` functions on lists
-* Added `Metaparser`, a parser parsing a grammar into a parser
-* Added `pfix`, a fixed point combinator for parsers depending on their own result
-* Added convenience function `wrap` to `DCont`
-* Added `forceParse` function to parse assuming success
-* Added `surround` parser transformer for parsing parenthesised expressions
-
-## 0.2
-
-Highlights:
-
-* Switching from list-based parsing to `TokenStream`
-
-### 0.2.3
-
-* Added `nParse` (cParse for single token) and `try` (parse with Maybe) Transformers, `sInteger` (signed integers) Atomic
-
-### 0.2.2
-
-* Added Either instance for `TokenStream`
-* Auto-`success` atomic, atomics for splitting an integer into digits
-
-### 0.2.1
-
-* Deprecated `skipN`, replaced with `sDrop` 
-
-### 0.2.0
-
-* Added `TokenStream`, an abstraction of lists
-* Used `TokenStream` to reformulate Atomics and Transformers
-
-## 0.1
-
-Highlights:
-
-* Initial Version
-
-### 0.1.4
-
-* Added `digit` and `letter` parsers
-
-### 0.1.3
-
-* Improved testing facilities
-* Added `check` function
-
-## 0.1.2
-
-### 0.1.2.0
-
+# Changelog
+
+## 0.3
+
+Highlights:
+
+* Adding `Metaparser`, a parser that constructs a parser from a grammar similar to EBNF
+
+### 0.3.1
+
+* Adding `MonadFail` instances to `Parser` and `DCont`
+
+### 0.3.0
+
+* Merged `Atomics` and `Transformers` into `Prebuilt`
+* Removed deprecated `skipN`
+* Renamed `TokenStream` functions to share the names of `Prelude` functions on lists
+* Added `Metaparser`, a parser parsing a grammar into a parser
+* Added `pfix`, a fixed point combinator for parsers depending on their own result
+* Added convenience function `wrap` to `DCont`
+* Added `forceParse` function to parse assuming success
+* Added `surround` parser transformer for parsing parenthesised expressions
+
+## 0.2
+
+Highlights:
+
+* Switching from list-based parsing to `TokenStream`
+
+### 0.2.3
+
+* Added `nParse` (cParse for single token) and `try` (parse with Maybe) Transformers, `sInteger` (signed integers) Atomic
+
+### 0.2.2
+
+* Added Either instance for `TokenStream`
+* Auto-`success` atomic, atomics for splitting an integer into digits
+
+### 0.2.1
+
+* Deprecated `skipN`, replaced with `sDrop` 
+
+### 0.2.0
+
+* Added `TokenStream`, an abstraction of lists
+* Used `TokenStream` to reformulate Atomics and Transformers
+
+## 0.1
+
+Highlights:
+
+* Initial Version
+
+### 0.1.4
+
+* Added `digit` and `letter` parsers
+
+### 0.1.3
+
+* Improved testing facilities
+* Added `check` function
+
+## 0.1.2
+
+### 0.1.2.0
+
 * Added `peek` function
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2017-2018 Marcus Völker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+MIT License
+
+Copyright (c) 2017-2019 Marcus Völker
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LParse.cabal b/LParse.cabal
--- a/LParse.cabal
+++ b/LParse.cabal
@@ -1,31 +1,31 @@
-name:                LParse
-version:             0.3.0.0
-synopsis:            A continuation-based parser library
-description:         A parser library using continuations with a possibility for failure to build parsers in a clear and concise manner.
-homepage:            https://github.com/MarcusVoelker/LParse#readme
-license:             MIT
-license-file:        LICENSE
-author:              Marcus Völker
-maintainer:          marcus.voelker@rwth-aachen.de
-copyright:           (c) 2017-2018 Marcus Völker
-category:            Parsing
-build-type:          Simple
-extra-source-files:  README.md CHANGELOG.md
-cabal-version:       >=1.10
-
-library
-  hs-source-dirs:      src
-  exposed-modules:     Control.DoubleContinuations, Text.LParse.Metaparser, Text.LParse.Parser, Text.LParse.Prebuilt, Text.LParse.TokenStream
-  build-depends:       base >= 4.7 && < 5, containers
-  default-language:    Haskell2010
-
-source-repository head
-  type:     git
-  location: https://github.com/MarcusVoelker/LParse
-
-test-suite test-lparse
-  type:     exitcode-stdio-1.0
-  main-is:  test/Test.hs
-  build-depends:  base
-                , LParse
+name:                LParse
+version:             0.3.1.0
+synopsis:            A continuation-based parser library
+description:         A parser library using continuations with a possibility for failure to build parsers in a clear and concise manner.
+homepage:            https://github.com/MarcusVoelker/LParse#readme
+license:             MIT
+license-file:        LICENSE
+author:              Marcus Völker
+maintainer:          marcus.voelker@rwth-aachen.de
+copyright:           (c) 2017-2019 Marcus Völker
+category:            Parsing
+build-type:          Simple
+extra-source-files:  README.md CHANGELOG.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Control.DoubleContinuations, Text.LParse.Metaparser, Text.LParse.Parser, Text.LParse.Prebuilt, Text.LParse.TokenStream
+  build-depends:       base >= 4.7 && < 5, containers
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/MarcusVoelker/LParse
+
+test-suite test-lparse
+  type:     exitcode-stdio-1.0
+  main-is:  test/Test.hs
+  build-depends:  base
+                , LParse
   default-language: Haskell2010
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,32 +1,32 @@
-# λParse  ![Travis-CI](https://travis-ci.org/MarcusVoelker/LParse.svg?branch=master) [![Hackage](https://img.shields.io/hackage/v/LParse.svg)](https://hackage.haskell.org/package/LParse)
-
-A parser library using monads and arrows. Supports both horizontal and vertical composition of parsers.
-
-# Short Guide
-
-## Creating a parser
-
-A parser is, at its heart, nothing more than a function that takes some input and either returns a result along with some residual, unconsumed input or that fails for some reason and returns an error message.
-
-Since LParse is written in continuation-passing-style, this is modelled with a concept of DoubleContinuations - continuations that not only take one function to continue with, but one function to continue with in the case of a successful computation and one function to continue with in the case of a failure. This, of course, gives rise to classic parser semantics: To concatenate two parsers, the second one is put into the successful continuation of the first one, while for an alternative we put the second parser into the failure continuation of the first one.
-
-These semantics are modelled with Monads and Alternatives, respectively, to make use of the familar syntax of these classes:
-
-* `p1 >> p2` runs `p1` and `p2` in succession and gives the result of `p2` only
-* `(,) <$> p1 <*> p2` runs `p1` and `p2` in succession and gives the results of `p1` and `p2` as a pair
-* `p1 <|> p2` runs `p1`. On a success, its result is returned, on a fail, `p2` is run. On a success, its result is returned, on a failure, the whole parser fails
-
-The parser can be built from scratch by constructing a parser object with the appropriate function, but a variety of common atomic parsers and parser transformers are provided in `Text.LParse.Atomics` and `Text.LParse.Transformers`, respectively.
-
-The above construction is referred to as _horizontal composition_, i.e., running parsers successively on the same input.
-The dual concept to this we refer to as _vertical composition_, where the result of one parser is fed into the next one as an input. An application for this could be one parser `lex` that transforms a string into a list of tokens (a lexer) and a second parser `par` that transforms a list of tokens into a syntax tree. Then we could join these to create a parser that directly transforms a string into a syntax tree as `lex >>> par`
-
-As the notation above implies, LParse realises vertical composition with arrows enabling all the other features of arrows, such as the proc notation, to be used with parsers.
-
-## Running a parser
-
-Once you have obtained a parser object that represents your entire parser, you have two options
-
-1. You can supply a success and a failure function. When the parser is run, the appropriate function will be called, either with the result of the parse or an error message
-
+# λParse  ![Travis-CI](https://travis-ci.org/MarcusVoelker/LParse.svg?branch=master) [![Hackage](https://img.shields.io/hackage/v/LParse.svg)](https://hackage.haskell.org/package/LParse)
+
+A parser library using monads and arrows. Supports both horizontal and vertical composition of parsers.
+
+# Short Guide
+
+## Creating a parser
+
+A parser is, at its heart, nothing more than a function that takes some input and either returns a result along with some residual, unconsumed input or that fails for some reason and returns an error message.
+
+Since LParse is written in continuation-passing-style, this is modelled with a concept of DoubleContinuations - continuations that not only take one function to continue with, but one function to continue with in the case of a successful computation and one function to continue with in the case of a failure. This, of course, gives rise to classic parser semantics: To concatenate two parsers, the second one is put into the successful continuation of the first one, while for an alternative we put the second parser into the failure continuation of the first one.
+
+These semantics are modelled with Monads and Alternatives, respectively, to make use of the familar syntax of these classes:
+
+* `p1 >> p2` runs `p1` and `p2` in succession and gives the result of `p2` only
+* `(,) <$> p1 <*> p2` runs `p1` and `p2` in succession and gives the results of `p1` and `p2` as a pair
+* `p1 <|> p2` runs `p1`. On a success, its result is returned, on a fail, `p2` is run. On a success, its result is returned, on a failure, the whole parser fails
+
+The parser can be built from scratch by constructing a parser object with the appropriate function, but a variety of common atomic parsers and parser transformers are provided in `Text.LParse.Atomics` and `Text.LParse.Transformers`, respectively.
+
+The above construction is referred to as _horizontal composition_, i.e., running parsers successively on the same input.
+The dual concept to this we refer to as _vertical composition_, where the result of one parser is fed into the next one as an input. An application for this could be one parser `lex` that transforms a string into a list of tokens (a lexer) and a second parser `par` that transforms a list of tokens into a syntax tree. Then we could join these to create a parser that directly transforms a string into a syntax tree as `lex >>> par`
+
+As the notation above implies, LParse realises vertical composition with arrows enabling all the other features of arrows, such as the proc notation, to be used with parsers.
+
+## Running a parser
+
+Once you have obtained a parser object that represents your entire parser, you have two options
+
+1. You can supply a success and a failure function. When the parser is run, the appropriate function will be called, either with the result of the parse or an error message
+
 2. You can retrieve the double continuation from the parser and continue to build with it. This is appropriate if your program itself is written in CPS.
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,2 +1,2 @@
-import Distribution.Simple
-main = defaultMain
+import Distribution.Simple
+main = defaultMain
diff --git a/src/Control/DoubleContinuations.hs b/src/Control/DoubleContinuations.hs
--- a/src/Control/DoubleContinuations.hs
+++ b/src/Control/DoubleContinuations.hs
@@ -1,56 +1,61 @@
-{-|
-Module      : Control.DoubleContinuations 
-Description : Continuations that can succeed or fail
-Copyright   : (c) Marcus Völker, 2017-2018
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module describes DoubleContinuations, which are Continuations that may have succeeded or failed.
-Instead of just taking a single function (a -> r) -> r to execute after the computation has run,
-a double continuation takes two functions: one to call in case of success and one to call in case of error
-This allows for easy implementation of exception handling and structuring control flow in a pass/fail manner
--}
-module Control.DoubleContinuations where 
-
-import Control.Applicative
-import Control.Monad
-import Data.Either
-
--- | The double continuation. Takes two functions, one to invoke if the computation is successful, one if it errors
-data DCont r e a = DCont {run :: (a -> r) -> (e -> r) -> r}
-
--- | Generates a continuation that always fails. For a continuation that always succeeds, see return
-throw :: e  -- ^ The error to return
-    -> DCont r e a
-throw x = DCont (\_ g -> g x)
-
--- | Binding a Continuation means running it, then feeding the result into f to generate a new continuation, and running that
-instance Monad (DCont r e) where
-    return x = DCont (\f _ -> f x)
-    c >>= f = DCont (\btr etr -> run c (\x -> run (f x) btr etr) etr)
-
--- | @MonadFix@-analogue for DoubleContinuations. Since it doesn't fit in the signature of @mfix@, it is defined separately
-dfix :: (Either e a -> DCont (Either e a) e a) -> DCont r e a
-dfix f = let ea = run (f ea) Right Left in wrap ea
-
--- | via Monad/Functor laws
-instance Functor (DCont r e) where
-    fmap = liftM
-
--- | via Monad/Applicative laws
-instance Applicative (DCont r e) where
-    pure = return
-    f <*> a = f >>= (<$> a)
-
--- | An empty alternative just fails with an undefined error. Branching means first trying one, and in case of failure, the other
-instance Alternative (DCont r e) where
-    empty = throw undefined
-    p1 <|> p2 = DCont (\atr etr -> run p1 atr (\_ -> run p2 atr etr))
-
--- | Convenience function to run a computation and put the result into an Either (with Left being the error and Right being the success)
-invoke :: DCont (Either e a) e a -> Either e a
-invoke c = run c Right Left
-
--- | Convenience function to put an @Either@ into a @DCont@
-wrap :: Either e a -> DCont r e a
+{-|
+Module      : Control.DoubleContinuations 
+Description : Continuations that can succeed or fail
+Copyright   : (c) Marcus Völker, 2017-2018
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module describes DoubleContinuations, which are Continuations that may have succeeded or failed.
+Instead of just taking a single function (a -> r) -> r to execute after the computation has run,
+a double continuation takes two functions: one to call in case of success and one to call in case of error
+This allows for easy implementation of exception handling and structuring control flow in a pass/fail manner
+-}
+module Control.DoubleContinuations where 
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.Fail
+import Data.Either
+
+-- | The double continuation. Takes two functions, one to invoke if the computation is successful, one if it errors
+data DCont r e a = DCont {run :: (a -> r) -> (e -> r) -> r}
+
+-- | Generates a continuation that always fails. For a continuation that always succeeds, see return
+throw :: e  -- ^ The error to return
+    -> DCont r e a
+throw x = DCont (\_ g -> g x)
+
+-- | Binding a Continuation means running it, then feeding the result into f to generate a new continuation, and running that
+instance Monad (DCont r e) where
+    return x = DCont (\f _ -> f x)
+    c >>= f = DCont (\btr etr -> run c (\x -> run (f x) btr etr) etr)
+
+-- | Failing a Continuation means returning a continuation that always fails.
+instance MonadFail (DCont r e) where
+    fail _ = empty
+
+-- | @MonadFix@-analogue for DoubleContinuations. Since it doesn't fit in the signature of @mfix@, it is defined separately
+dfix :: (Either e a -> DCont (Either e a) e a) -> DCont r e a
+dfix f = let ea = run (f ea) Right Left in wrap ea
+
+-- | via Monad/Functor laws
+instance Functor (DCont r e) where
+    fmap = liftM
+
+-- | via Monad/Applicative laws
+instance Applicative (DCont r e) where
+    pure = return
+    f <*> a = f >>= (<$> a)
+
+-- | An empty alternative just fails with an undefined error. Branching means first trying one, and in case of failure, the other
+instance Alternative (DCont r e) where
+    empty = throw undefined
+    p1 <|> p2 = DCont (\atr etr -> run p1 atr (\_ -> run p2 atr etr))
+
+-- | Convenience function to run a computation and put the result into an Either (with Left being the error and Right being the success)
+invoke :: DCont (Either e a) e a -> Either e a
+invoke c = run c Right Left
+
+-- | Convenience function to put an @Either@ into a @DCont@
+wrap :: Either e a -> DCont r e a
 wrap = either throw return
diff --git a/src/Text/LParse/Metaparser.hs b/src/Text/LParse/Metaparser.hs
--- a/src/Text/LParse/Metaparser.hs
+++ b/src/Text/LParse/Metaparser.hs
@@ -1,179 +1,179 @@
-{-|
-Module      : Text.LParse.Metaparser
-Description : Underlying data structure for sequential parsing
-Copyright   : (c) Marcus Völker, 2018
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module contains the Metaparser, which is a parser that parses a grammar in EBNF and returns a parser that parses that grammar into an AST
--}
-module Text.LParse.Metaparser (specParse,metaParser,AST) where
-
-import Control.Applicative
-import Control.Arrow
-import Control.Monad.Fix
-
-import Data.Char
-import Data.List
-import qualified Data.Map.Strict as M
-import Data.Maybe
-
-import Text.LParse.Parser
-import Text.LParse.Prebuilt
-
-data Token = Literal Char | CharClass String | RuleName String | Integer | Digit | Word | Star | Plus | May | Or | Is | Eoi | LParen | RParen | Sep | Whitespace deriving (Show,Eq)
-
--- | Abstract Syntax Tree generated by the metaparser, suitable for postprocessing
-data AST = Node String [AST] | ILeaf Integer | SLeaf String | EOI deriving Eq
-
-instance Show AST where
-    show (Node s cs) = s ++ "(" ++ intercalate "," (map show cs) ++ ")"
-    show (ILeaf i) = show i
-    show (SLeaf s) = s
-    show EOI = "$"
-
-escaped :: Parser r String Token
-escaped = consumeSReturn 'i' Integer
-    <|> consumeSReturn 'd' Digit
-    <|> consumeSReturn 'w' Word
-    <|> consumeSReturn '\\' (Literal '\\')
-    <|> consumeSReturn '*' (Literal '*')
-    <|> consumeSReturn '+' (Literal '+')
-    <|> consumeSReturn '?' (Literal '?')
-    <|> consumeSReturn '[' (Literal '[')
-    <|> consumeSReturn ']' (Literal ']')
-    <|> consumeSReturn '(' (Literal '(')
-    <|> consumeSReturn ')' (Literal ')')
-    <|> consumeSReturn '$' (Literal '$')
-
-charclass :: Parser r String Token
-charclass = CharClass <$> some (nParse (/=']') tokenReturn "Expected character")
-
-simpleSpecial :: Parser r String Token
-simpleSpecial = consumeSReturn '*' Star
-    <|> consumeSReturn '+' Plus
-    <|> consumeSReturn '?' May
-    <|> consumeSReturn '|' Or
-    <|> consumeSReturn '$' Eoi
-    <|> consumeSReturn '(' LParen
-    <|> consumeSReturn ')' RParen
-    <|> consumeReturn "::=" Is
-    <|> consumeSReturn ';' Sep
-
-whitespace :: Parser r String Token
-whitespace = some (nParse isSpace tokenReturn "Expected Space") >> return Whitespace
-
-ruleName :: Parser r String Token
-ruleName = consumeSingle '%' >> (RuleName <$> word)
-
-metaTokenizer :: Parser r String [Token]
-metaTokenizer = many (
-    (consumeSingle '\\' >> escaped)
-    <|> surround "[]" charclass
-    <|> simpleSpecial
-    <|> ruleName
-    <|> whitespace
-    <|> (Literal <$> tokenReturn)
-    )
-
-iLeafParser :: Parser r [Token] (Parser r' String AST)
-iLeafParser = consumeSReturn Integer (ILeaf <$> integer)
-
-dLeafParser :: Parser r [Token] (Parser r' String AST)
-dLeafParser = consumeSReturn Digit (ILeaf <$> digit)
-
-sLeafParser :: Parser r [Token] (Parser r' String AST)
-sLeafParser = consumeSReturn Word (SLeaf <$> word)
-
-eoiParser :: Parser r [Token] (Parser r' String AST)
-eoiParser = consumeSReturn Eoi (eoi >> return EOI)
-
-isRuleName :: Token -> Bool
-isRuleName (RuleName _) = True
-isRuleName _ = False
-
-getRuleName :: Token -> String
-getRuleName (RuleName s) = s
-
-subRuleParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String AST)
-subRuleParser m = nParse isRuleName (tokenParse ((m M.!) . getRuleName)) "Expected RuleName"
-
-charClassCharParser :: String -> Parser r String Char
-charClassCharParser (c:s) | c == '^' = nParse (not . (`elem` s)) tokenReturn ("Expected not [" ++ s ++ "]")
-charClassCharParser s = nParse (`elem` s) tokenReturn ("Expected [" ++ s ++ "]")
-
-isCharClass :: Token -> Bool
-isCharClass (CharClass _) = True
-isCharClass _ = False
-
-getCharClass :: Token -> String
-getCharClass (CharClass s) = s
-
-charClassParser :: Parser r [Token] (Parser r' String AST)
-charClassParser = nParse isCharClass (tokenParse (fmap (SLeaf . return) . charClassCharParser . getCharClass)) "Expected character class"
-
-isLiteral :: Token -> Bool
-isLiteral (Literal _) = True
-isLiteral _ = False
-
-getLiteral :: Token -> Char
-getLiteral (Literal s) = s
-
-parseLiteral :: Parser r [Token] Char
-parseLiteral = nParse isLiteral (tokenParse getLiteral) "Expected Literal"
-
-charParser :: Parser r [Token] (Parser r' String AST)
-charParser = fmap (SLeaf . return) . (\c -> consumeSReturn c c) <$> parseLiteral
-
-atomParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String AST)
-atomParser m = iLeafParser
-    <|> dLeafParser
-    <|> sLeafParser
-    <|> eoiParser
-    <|> charClassParser
-    <|> subRuleParser m
-    <|> charParser
-
-starFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
-starFreeParser m = surround [LParen,RParen] (cfexParser m) <|> (fmap return <$> atomParser m)
-
-concatFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST]) 
-concatFreeParser m = do
-    sf <- starFreeParser m
-    (consumeSingle Star >> return (concat <$> many sf))
-        <|> (consumeSingle Plus >> return (concat <$> some sf))
-        <|> (consumeSingle May >> return (concat . maybeToList <$> try sf))
-        <|> return sf
-
-orFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
-orFreeParser m = fmap concat . sequenceA <$> some (concatFreeParser m)
-
-cfexParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
-cfexParser m = foldl1 (<|>) <$> sepSome (consumeSingle Or) (orFreeParser m)
-
-ruleParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (String,Parser r' String AST)
-ruleParser m = (\s as-> (s,Node s <$> as)) <$> (many (nParse isLiteral (tokenParse getLiteral) "Expected Literal") << consumeSingle Is) <*> cfexParser m
-
-rulesetParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (M.Map String (Parser r' String AST))
-rulesetParser m = M.fromList<$> sepMany (consumeSingle Sep) (ruleParser m)
-
-rulesetLoop :: Parser r [Token] (M.Map String (Parser r' String AST))
-rulesetLoop = pfix rulesetParser
-
-combine :: Maybe (M.Map String (Parser r' String AST)) -> Parser r [Token] (Parser r' String [AST])
-combine Nothing = cfexParser M.empty
-combine (Just rs) = cfexParser rs 
-
-fullParser :: Parser r [Token] (Parser r' String AST)
-fullParser = try (rulesetLoop << consumeSingle Sep) >>= (fmap (fmap (Node "_")) . combine)
-
-parserParser :: Parser r [Token] (Parser r' String AST)
-parserParser = fullParser << eoi
-
--- | Parser that takes a grammar and returns a parser that parses that grammar into an @AST@
-metaParser :: Parser r String (Parser r' String AST)
-metaParser = metaTokenizer >>> skip [Whitespace] parserParser
-
--- | Convenience function chaining creation and usage of the @metaParser@ into a single invocation.
-specParse :: String -> String -> Either String AST
+{-|
+Module      : Text.LParse.Metaparser
+Description : Underlying data structure for sequential parsing
+Copyright   : (c) Marcus Völker, 2018
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module contains the Metaparser, which is a parser that parses a grammar in EBNF and returns a parser that parses that grammar into an AST
+-}
+module Text.LParse.Metaparser (specParse,metaParser,AST (..)) where
+
+import Control.Applicative
+import Control.Arrow
+import Control.Monad.Fix
+
+import Data.Char
+import Data.List
+import qualified Data.Map.Strict as M
+import Data.Maybe
+
+import Text.LParse.Parser
+import Text.LParse.Prebuilt
+
+data Token = Literal Char | CharClass String | RuleName String | Integer | Digit | Word | Star | Plus | May | Or | Is | Eoi | LParen | RParen | Sep | Whitespace deriving (Show,Eq)
+
+-- | Abstract Syntax Tree generated by the metaparser, suitable for postprocessing
+data AST = Node String [AST] | ILeaf Integer | SLeaf String | EOI deriving Eq
+
+instance Show AST where
+    show (Node s cs) = s ++ "(" ++ intercalate "," (map show cs) ++ ")"
+    show (ILeaf i) = show i
+    show (SLeaf s) = s
+    show EOI = "$"
+
+escaped :: Parser r String Token
+escaped = consumeSReturn 'i' Integer
+    <|> consumeSReturn 'd' Digit
+    <|> consumeSReturn 'w' Word
+    <|> consumeSReturn '\\' (Literal '\\')
+    <|> consumeSReturn '*' (Literal '*')
+    <|> consumeSReturn '+' (Literal '+')
+    <|> consumeSReturn '?' (Literal '?')
+    <|> consumeSReturn '[' (Literal '[')
+    <|> consumeSReturn ']' (Literal ']')
+    <|> consumeSReturn '(' (Literal '(')
+    <|> consumeSReturn ')' (Literal ')')
+    <|> consumeSReturn '$' (Literal '$')
+
+charclass :: Parser r String Token
+charclass = CharClass <$> some (nParse (/=']') tokenReturn "Expected character")
+
+simpleSpecial :: Parser r String Token
+simpleSpecial = consumeSReturn '*' Star
+    <|> consumeSReturn '+' Plus
+    <|> consumeSReturn '?' May
+    <|> consumeSReturn '|' Or
+    <|> consumeSReturn '$' Eoi
+    <|> consumeSReturn '(' LParen
+    <|> consumeSReturn ')' RParen
+    <|> consumeReturn "::=" Is
+    <|> consumeSReturn ';' Sep
+
+whitespace :: Parser r String Token
+whitespace = some (nParse isSpace tokenReturn "Expected Space") >> return Whitespace
+
+ruleName :: Parser r String Token
+ruleName = consumeSingle '%' >> (RuleName <$> word)
+
+metaTokenizer :: Parser r String [Token]
+metaTokenizer = many (
+    (consumeSingle '\\' >> escaped)
+    <|> surround "[]" charclass
+    <|> simpleSpecial
+    <|> ruleName
+    <|> whitespace
+    <|> (Literal <$> tokenReturn)
+    )
+
+iLeafParser :: Parser r [Token] (Parser r' String AST)
+iLeafParser = consumeSReturn Integer (ILeaf <$> integer)
+
+dLeafParser :: Parser r [Token] (Parser r' String AST)
+dLeafParser = consumeSReturn Digit (ILeaf <$> digit)
+
+sLeafParser :: Parser r [Token] (Parser r' String AST)
+sLeafParser = consumeSReturn Word (SLeaf <$> word)
+
+eoiParser :: Parser r [Token] (Parser r' String AST)
+eoiParser = consumeSReturn Eoi (eoi >> return EOI)
+
+isRuleName :: Token -> Bool
+isRuleName (RuleName _) = True
+isRuleName _ = False
+
+getRuleName :: Token -> String
+getRuleName (RuleName s) = s
+
+subRuleParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String AST)
+subRuleParser m = nParse isRuleName (tokenParse ((m M.!) . getRuleName)) "Expected RuleName"
+
+charClassCharParser :: String -> Parser r String Char
+charClassCharParser (c:s) | c == '^' = nParse (not . (`elem` s)) tokenReturn ("Expected not [" ++ s ++ "]")
+charClassCharParser s = nParse (`elem` s) tokenReturn ("Expected [" ++ s ++ "]")
+
+isCharClass :: Token -> Bool
+isCharClass (CharClass _) = True
+isCharClass _ = False
+
+getCharClass :: Token -> String
+getCharClass (CharClass s) = s
+
+charClassParser :: Parser r [Token] (Parser r' String AST)
+charClassParser = nParse isCharClass (tokenParse (fmap (SLeaf . return) . charClassCharParser . getCharClass)) "Expected character class"
+
+isLiteral :: Token -> Bool
+isLiteral (Literal _) = True
+isLiteral _ = False
+
+getLiteral :: Token -> Char
+getLiteral (Literal s) = s
+
+parseLiteral :: Parser r [Token] Char
+parseLiteral = nParse isLiteral (tokenParse getLiteral) "Expected Literal"
+
+charParser :: Parser r [Token] (Parser r' String AST)
+charParser = fmap (SLeaf . return) . (\c -> consumeSReturn c c) <$> parseLiteral
+
+atomParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String AST)
+atomParser m = iLeafParser
+    <|> dLeafParser
+    <|> sLeafParser
+    <|> eoiParser
+    <|> charClassParser
+    <|> subRuleParser m
+    <|> charParser
+
+starFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
+starFreeParser m = surround [LParen,RParen] (cfexParser m) <|> (fmap return <$> atomParser m)
+
+concatFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST]) 
+concatFreeParser m = do
+    sf <- starFreeParser m
+    (consumeSingle Star >> return (concat <$> many sf))
+        <|> (consumeSingle Plus >> return (concat <$> some sf))
+        <|> (consumeSingle May >> return (concat . maybeToList <$> try sf))
+        <|> return sf
+
+orFreeParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
+orFreeParser m = fmap concat . sequenceA <$> some (concatFreeParser m)
+
+cfexParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (Parser r' String [AST])
+cfexParser m = foldl1 (<|>) <$> sepSome (consumeSingle Or) (orFreeParser m)
+
+ruleParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (String,Parser r' String AST)
+ruleParser m = (\s as-> (s,Node s <$> as)) <$> (many (nParse isLiteral (tokenParse getLiteral) "Expected Literal") << consumeSingle Is) <*> cfexParser m
+
+rulesetParser :: M.Map String (Parser r' String AST) -> Parser r [Token] (M.Map String (Parser r' String AST))
+rulesetParser m = M.fromList<$> sepMany (consumeSingle Sep) (ruleParser m)
+
+rulesetLoop :: Parser r [Token] (M.Map String (Parser r' String AST))
+rulesetLoop = pfix rulesetParser
+
+combine :: Maybe (M.Map String (Parser r' String AST)) -> Parser r [Token] (Parser r' String [AST])
+combine Nothing = cfexParser M.empty
+combine (Just rs) = cfexParser rs 
+
+fullParser :: Parser r [Token] (Parser r' String AST)
+fullParser = try (rulesetLoop << consumeSingle Sep) >>= (fmap (fmap (Node "_")) . combine)
+
+parserParser :: Parser r [Token] (Parser r' String AST)
+parserParser = fullParser << eoi
+
+-- | Parser that takes a grammar and returns a parser that parses that grammar into an @AST@
+metaParser :: Parser r String (Parser r' String AST)
+metaParser = metaTokenizer >>> skip [Whitespace] parserParser
+
+-- | Convenience function chaining creation and usage of the @metaParser@ into a single invocation.
+specParse :: String -> String -> Either String AST
 specParse g i = doParse metaParser g >>= (`doParse` i)
diff --git a/src/Text/LParse/Parser.hs b/src/Text/LParse/Parser.hs
--- a/src/Text/LParse/Parser.hs
+++ b/src/Text/LParse/Parser.hs
@@ -1,90 +1,95 @@
-{-|
-Module      : Text.LParse.Parser
-Description : Core for LParse
-Copyright   : (c) Marcus Völker, 2017-2018
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module implements LParse's core: The parser data structure, instances of the important typeclasses and functions to run the parser
--}
-module Text.LParse.Parser where
-
-import Control.DoubleContinuations
-
-import Control.Applicative
-import Control.Arrow
-import qualified Control.Category as C
-import Control.Monad
-
-import Data.Either
-import Data.List
-
--- | The Parser structure itself wraps a function from a collection of tokens (collectively of type t) to a double continuation giving
--- back a string in case of an error (the error message) and a pair (a,t) in case of a success (the parsing result and rest of the input)
-data Parser r t a = Parser {pFunc :: t -> DCont r String (a,t)}
-
--- | via Monad/Functor laws
-instance Functor (Parser r t) where
-    fmap = liftM
-
--- | via Monad/Applicative laws
-instance Applicative (Parser r t) where
-    pure = return
-    f <*> a = f >>= (<$> a)
-
--- | an empty parser in the sense of Alternative always fails and throws nothing. Branching between parsers means trying both in a row and
--- taking the first one that succeeds
-instance Alternative (Parser r t) where
-    empty = Parser (const $ throw "Empty Fail")
-    p1 <|> p2 = Parser ((<|>) <$> pFunc p1 <*> pFunc p2)
-
--- | returning a value means building a parser that consumes no input and just gives back the value (i.e. always succeeds)
--- the bind operator means using the parser, creating a second parser from the result (with the given function) and then parsing with that.
--- Both parsers successively consume input, i.e. @consume "a" >>= (const $ consume "b")@ will consume the string "ab"
-instance Monad (Parser r t) where
-    return a = Parser (\s -> return (a,s))
-    a >>= f = Parser (pFunc a >=> (\(r, s') -> pFunc (f r) s'))
-
--- | Defined via Alternative
-instance MonadPlus (Parser r t) where
-    mzero = empty
-    mplus = (<|>)
-
--- | @MonadFix@-analogue for @Parser@, using the @DCont@ function @dfix@
-pfix :: (a -> Parser (Either String (a,t)) t a) -> Parser r t a
-pfix f = Parser (dfix . flip (pFunc . f . fst . fromRight undefined)) 
-
--- | The identity parser returns the input. Concatenating two parsers means using the parsing result of the first as tokens for the second
-instance C.Category (Parser r) where
-    id = Parser (\s -> return (s,s))
-    (.) b a = Parser (\s -> DCont (\btr etr -> run (pFunc a s) (\(x,r) -> run (pFunc b x) (\(y,_) -> btr (y,r)) etr) etr))
-
--- | Lifting a function to an arrow applies the function to the input. (***) executes two parsers in parallel, giving both results as a pair 
--- (but only if both succeed)
-instance Arrow (Parser r) where
-    arr f = Parser (\s -> return (f s, undefined))
-    (***) p1 p2 = Parser (\(a,b) -> DCont (\btr etr -> run (pFunc p1 a) (\(a',ra) -> run (pFunc p2 b) (\(b',rb) -> btr ((a',b'),(ra,rb))) etr) etr))
-
--- | Runs the parser on the tokens and returns whether the parse succeeded. Results are discarded.
-check :: Parser Bool t a -> t -> Bool
-check p s = parse p s (const True) (const False)
-
--- | Runs the parser on the tokens, using two functions to run the contained continuation
-parse :: Parser r t a -> t -> (a -> r) -> (String -> r) -> r
-parse p s = run (pFunc p s) . (. fst)
-
--- | Same as @parse@, but giving back the results via @Either@
-doParse :: Parser (Either String a) t a -> t -> Either String a
-doParse p s = invoke (fst <$> pFunc p s)
-
--- | Same as @parse@, but assuming the parsing succeeds, hard failing via @undefined@ otherwise
-forceParse :: Parser a t a -> t -> a
-forceParse p s = parse p s id undefined 
-
--- | Runs the parser and prints the results
-debugParse :: (Show a) => Parser (IO ()) t a -> t -> IO ()
-debugParse p s = debugParse' p s (putStr . (\x -> show x ++ "\n"))
-
--- | Runs the parser and prints the results via a custom printing function
-debugParse' :: Parser (IO ()) t a -> t -> (a -> IO()) ->  IO ()
+{-|
+Module      : Text.LParse.Parser
+Description : Core for LParse
+Copyright   : (c) Marcus Völker, 2017-2018
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module implements LParse's core: The parser data structure, instances of the important typeclasses and functions to run the parser
+-}
+module Text.LParse.Parser where
+
+import Control.DoubleContinuations
+
+import Control.Applicative
+import Control.Arrow
+import qualified Control.Category as C
+import Control.Monad
+import Control.Monad.Fail
+
+import Data.Either
+import Data.List
+
+-- | The Parser structure itself wraps a function from a collection of tokens (collectively of type t) to a double continuation giving
+-- back a string in case of an error (the error message) and a pair (a,t) in case of a success (the parsing result and rest of the input)
+data Parser r t a = Parser {pFunc :: t -> DCont r String (a,t)}
+
+-- | via Monad/Functor laws
+instance Functor (Parser r t) where
+    fmap = liftM
+
+-- | via Monad/Applicative laws
+instance Applicative (Parser r t) where
+    pure = return
+    f <*> a = f >>= (<$> a)
+
+-- | an empty parser in the sense of Alternative always fails and throws nothing. Branching between parsers means trying both in a row and
+-- taking the first one that succeeds
+instance Alternative (Parser r t) where
+    empty = Parser (const $ throw "Empty Fail")
+    p1 <|> p2 = Parser ((<|>) <$> pFunc p1 <*> pFunc p2)
+
+-- | returning a value means building a parser that consumes no input and just gives back the value (i.e. always succeeds)
+-- the bind operator means using the parser, creating a second parser from the result (with the given function) and then parsing with that.
+-- Both parsers successively consume input, i.e. @consume "a" >>= (const $ consume "b")@ will consume the string "ab"
+instance Monad (Parser r t) where
+    return a = Parser (\s -> return (a,s))
+    a >>= f = Parser (pFunc a >=> (\(r, s') -> pFunc (f r) s'))
+
+-- | Defined via Alternative
+instance MonadPlus (Parser r t) where
+    mzero = empty
+    mplus = (<|>)
+
+-- | Pattern match failure via parsing failure
+instance MonadFail (Parser r t) where
+    fail s = Parser (const $ throw s)
+
+-- | @MonadFix@-analogue for @Parser@, using the @DCont@ function @dfix@
+pfix :: (a -> Parser (Either String (a,t)) t a) -> Parser r t a
+pfix f = Parser (dfix . flip (pFunc . f . fst . fromRight undefined)) 
+
+-- | The identity parser returns the input. Concatenating two parsers means using the parsing result of the first as tokens for the second
+instance C.Category (Parser r) where
+    id = Parser (\s -> return (s,s))
+    (.) b a = Parser (\s -> DCont (\btr etr -> run (pFunc a s) (\(x,r) -> run (pFunc b x) (\(y,_) -> btr (y,r)) etr) etr))
+
+-- | Lifting a function to an arrow applies the function to the input. (***) executes two parsers in parallel, giving both results as a pair 
+-- (but only if both succeed)
+instance Arrow (Parser r) where
+    arr f = Parser (\s -> return (f s, undefined))
+    (***) p1 p2 = Parser (\(a,b) -> DCont (\btr etr -> run (pFunc p1 a) (\(a',ra) -> run (pFunc p2 b) (\(b',rb) -> btr ((a',b'),(ra,rb))) etr) etr))
+
+-- | Runs the parser on the tokens and returns whether the parse succeeded. Results are discarded.
+check :: Parser Bool t a -> t -> Bool
+check p s = parse p s (const True) (const False)
+
+-- | Runs the parser on the tokens, using two functions to run the contained continuation
+parse :: Parser r t a -> t -> (a -> r) -> (String -> r) -> r
+parse p s = run (pFunc p s) . (. fst)
+
+-- | Same as @parse@, but giving back the results via @Either@
+doParse :: Parser (Either String a) t a -> t -> Either String a
+doParse p s = invoke (fst <$> pFunc p s)
+
+-- | Same as @parse@, but assuming the parsing succeeds, hard failing via @undefined@ otherwise
+forceParse :: Parser a t a -> t -> a
+forceParse p s = parse p s id undefined 
+
+-- | Runs the parser and prints the results
+debugParse :: (Show a) => Parser (IO ()) t a -> t -> IO ()
+debugParse p s = debugParse' p s (putStr . (\x -> show x ++ "\n"))
+
+-- | Runs the parser and prints the results via a custom printing function
+debugParse' :: Parser (IO ()) t a -> t -> (a -> IO()) ->  IO ()
 debugParse' p s a = run (pFunc p s) (a . fst) (\e -> putStr ("Error: "++ e ++ "\n"))
diff --git a/src/Text/LParse/Prebuilt.hs b/src/Text/LParse/Prebuilt.hs
--- a/src/Text/LParse/Prebuilt.hs
+++ b/src/Text/LParse/Prebuilt.hs
@@ -1,151 +1,174 @@
-{-|
-Module      : Text.LParse.Prebuilt
-Description : Core for LParse
-Copyright   : (c) Marcus Völker, 2017-2018
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module contains prebuilt parsers that fulfill a certain job (formerly in @Text.LParse.Atomics@) and parser transformers that one or more parsers and modify/combine them in a certain way (formerly in @Text.LParse.Transformers@)
-
-Some of these parsers depend on their input being given in the form of a @TokenStream@.
--}
-
-module Text.LParse.Prebuilt where
-
-
-import Control.Applicative
-import Control.DoubleContinuations
-import Control.Monad
-import Data.Char
-
-import Text.LParse.Parser
-import qualified Text.LParse.TokenStream as T
-import Text.LParse.TokenStream (TokenStream,top,rest,nil,cons)
-
-
--- | A parser that always succeeds, parses nothing and returns unit
-noop :: Parser r t ()
-noop = return ()
-
--- | A parser that consumes the whole input and returns it unchanged
-full :: Parser r [t] [t]
-full = many tokenReturn
-
--- | A parser that consumes the whole input and discards it, successfully
-discard :: Parser r [t] ()
-discard = void full
-
--- | A parser that parses nothing, but only succeeds if the input is empty
-eoi :: Parser r [t] ()
-eoi = cParse null noop "Input not fully consumed"
-
--- | Extracts the first token from the input and applies the given function to it
-tokenParse :: (TokenStream s) => (t -> a) -> Parser r (s t) a
-tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ top s,rest s)))
-
--- | Consumes and returns the first token of the input
-tokenReturn :: (TokenStream s) => Parser r (s a) a
-tokenReturn = tokenParse id
-
--- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence
-consume :: (Eq t, Show (s t), TokenStream s) => s t -> Parser r (s t) ()
-consume pre = cParse ((&&) <$> (and . T.zipWith (==) pre) <*> ((>= length pre) . length)) (pParse (T.drop (length pre)) noop) ("Expected " ++ show pre)
-
--- | Consumes exactly the given input and then returns the given constant result
-consumeReturn :: (Eq t, Show (s t), TokenStream s) => s t -> a -> Parser r (s t) a
-consumeReturn t a = consume t >> return a
-
--- | Succeeds exactly if the input begins with the given token. On success, consumes that token
-consumeSingle :: (Eq t, Show t, TokenStream s) => t -> Parser r (s t) ()
-consumeSingle t = cParse (\s -> not (null s) && top s == t) (pParse rest noop) ("Expected " ++ show t)
-
--- | Consumes exactly the given token and then returns the given constant result
-consumeSReturn :: (Eq t, Show t, TokenStream s) => t -> a -> Parser r (s t) a
-consumeSReturn t a = consumeSingle t >> return a
-
--- | Extracts the first digit and returns it
-digit :: Parser r String Integer
-digit = read . return <$> cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit"
-
--- | Extracts the first digit and returns it
-letter :: Parser r String Char
-letter = cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter"
-
--- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it
-word :: Parser r String String
-word = some letter 
-
--- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it
-integer :: Parser r String Integer
-integer = foldl (\x y -> x*10+y) 0 <$> some digit
-
--- | Extracts the first signed integer (i.e. contiguous string of digits) from the input and returns it
-sInteger :: Parser r String Integer
-sInteger = (\m i -> case m of (Just _) -> -i; Nothing -> i) <$> try (consumeSingle '-') <*> integer
-
--- | Succeeds if the first token matches the given function, without consuming it
-peek :: (TokenStream s) => (t -> Bool) -> String -> Parser r (s t) ()
-peek c = cParse (c . top) noop
-
--- | A parser that always succeeds with the given function
-success :: (t -> (a,t)) -> Parser r t a
-success = Parser . (return .)
-
--- | Parses an integer by removing a single digit in the given base from it. Zero is considered to have no digits
-bDigit :: Integer -> Parser r Integer Integer
-bDigit b = cParse (> 0) (success (\i -> (i `mod` b,i `div` b))) "Empty number!"
-
--- | Parses an integer by removing a single digit in the given base from it. Zero is considered to have no digits
-bDigits :: Integer -> Parser r Integer [Integer]
-bDigits b = many $ bDigit b
-
-------------------------- Transformers
-
--- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@
-(<<) :: (Monad m) => m a -> m b -> m a
-a << b = a >>= ((b >>) . return)
-
--- | Takes a condition the parser's input has to fulfil in order for the parser to succeed
-cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a
-cParse c p err = Parser (\s -> if c s then pFunc p s else throw err)
-
--- | Takes  condition the next token has to fulfil in order for the parser to succeed
-nParse :: (TokenStream s, Eq (s t)) => (t -> Bool) -> Parser r (s t) a -> String -> Parser r (s t) a
-nParse c = cParse (\s -> nil /= s && c (top s))
-
--- | Transforms the input before applying the parser
-pParse :: (t -> t) -> Parser r t a -> Parser r t a
-pParse f p = Parser (pFunc p . f)
-
--- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source)
--- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@
-sepSome :: Parser r t () -> Parser r t a -> Parser r t [a]
-sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p
-
--- | Same as @sepSome@, but allows empty lists
-sepMany :: Parser r t () -> Parser r t a -> Parser r t [a]
+{-|
+Module      : Text.LParse.Prebuilt
+Description : Core for LParse
+Copyright   : (c) Marcus Völker, 2017-2018
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module contains prebuilt parsers that fulfill a certain job (formerly in @Text.LParse.Atomics@) and parser transformers that one or more parsers and modify/combine them in a certain way (formerly in @Text.LParse.Transformers@)
+
+Some of these parsers depend on their input being given in the form of a @TokenStream@.
+-}
+
+module Text.LParse.Prebuilt where
+
+
+import Control.Applicative
+import Control.DoubleContinuations
+import Control.Monad
+import Data.Char
+
+import Text.LParse.Parser
+import qualified Text.LParse.TokenStream as T
+import Text.LParse.TokenStream (TokenStream,top,rest,nil,cons)
+
+-- * Atomic Parsers
+
+-- ** Empty Parsers
+
+-- | A parser that always succeeds, parses nothing and returns unit
+noop :: Parser r t ()
+noop = return ()
+
+-- | Succeeds if the first token matches the given function, without consuming it
+peek :: (TokenStream s) => (t -> Bool) -> String -> Parser r (s t) ()
+peek c = cParse (c . top) noop
+
+-- | A parser that parses nothing, but only succeeds if the input is empty
+eoi :: Parser r [t] ()
+eoi = cParse null noop "Input not fully consumed"
+
+-- *** Consumers
+
+-- | A parser that consumes the whole input and discards it, successfully
+discard :: Parser r [t] ()
+discard = void full
+
+-- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence
+consume :: (Eq t, Show (s t), TokenStream s) => s t -> Parser r (s t) ()
+consume pre = cParse ((&&) <$> (and . T.zipWith (==) pre) <*> ((>= length pre) . length)) (pParse (T.drop (length pre)) noop) ("Expected " ++ show pre)
+
+-- | Consumes exactly the given input and then returns the given constant result
+consumeReturn :: (Eq t, Show (s t), TokenStream s) => s t -> a -> Parser r (s t) a
+consumeReturn t a = consume t >> return a
+
+-- | Succeeds exactly if the input begins with the given token. On success, consumes that token
+consumeSingle :: (Eq t, Show t, TokenStream s) => t -> Parser r (s t) ()
+consumeSingle t = cParse (\s -> not (null s) && top s == t) (pParse rest noop) ("Expected " ++ show t)
+
+-- | Consumes exactly the given token and then returns the given constant result
+consumeSReturn :: (Eq t, Show t, TokenStream s) => t -> a -> Parser r (s t) a
+consumeSReturn t a = consumeSingle t >> return a
+
+-- ** Input Transformers
+
+-- *** Identity Transformers
+
+-- | Consumes and returns the first token of the input
+tokenReturn :: (TokenStream s) => Parser r (s a) a
+tokenReturn = tokenParse id
+
+-- | A parser that consumes the whole input and returns it unchanged
+full :: Parser r [t] [t]
+full = many tokenReturn
+
+-- *** Arbitrary transformers
+
+-- | Extracts the first token from the input and applies the given function to it
+tokenParse :: (TokenStream s) => (t -> a) -> Parser r (s t) a
+tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ top s,rest s)))
+
+-- | A parser that always succeeds with the given function
+success :: (t -> (a,t)) -> Parser r t a
+success = Parser . (return .)
+
+-- ** Entity Parsers
+
+-- | Extracts the first digit and returns it
+digit :: Parser r String Integer
+digit = read . return <$> cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit"
+
+-- | Extracts the first digit and returns it
+letter :: Parser r String Char
+letter = cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter"
+
+-- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it
+word :: Parser r String String
+word = some letter 
+
+-- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it
+integer :: Parser r String Integer
+integer = foldl (\x y -> x*10+y) 0 <$> some digit
+
+-- | Extracts the first signed integer (i.e. contiguous string of digits) from the input and returns it
+sInteger :: Parser r String Integer
+sInteger = (\m i -> case m of (Just _) -> -i; Nothing -> i) <$> try (consumeSingle '-') <*> integer
+
+-- | Parses an integer by removing a single digit in the given base from it. Zero is considered to have no digits
+bDigit :: Integer -> Parser r Integer Integer
+bDigit b = cParse (> 0) (success (\i -> (i `mod` b,i `div` b))) "Empty number!"
+
+-- | Parses an integer by removing a single digit in the given base from it. Zero is considered to have no digits
+bDigits :: Integer -> Parser r Integer [Integer]
+bDigits b = many $ bDigit b
+
+-- * Parser Transformers
+
+-- ** Single Parser Transformers
+
+-- | Tries to run the given parser, giving back Just result or Nothing
+try :: Parser r t a -> Parser r t (Maybe a)
+try p = (Just <$> p) <|> return Nothing
+
+-- | Parses a character before and a character after the given parser, useful for parentheses
+surround :: (Eq t, Show t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
+surround [l,r] p = consumeSingle l >> p << consumeSingle r
+
+-- *** Conditional Transformers
+
+-- | Takes a condition the parser's input has to fulfil in order for the parser to succeed
+cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a
+cParse c p err = Parser (\s -> if c s then pFunc p s else throw err)
+
+-- | Takes  condition the next token has to fulfil in order for the parser to succeed
+nParse :: (TokenStream s, Eq (s t)) => (t -> Bool) -> Parser r (s t) a -> String -> Parser r (s t) a
+nParse c = cParse (\s -> nil /= s && c (top s))
+
+-- *** Input-modifying Transformers
+
+-- | Transforms the input before applying the parser
+pParse :: (t -> t) -> Parser r t a -> Parser r t a
+pParse f p = Parser (pFunc p . f)
+
+-- | Removes all tokens from the given list from the input
+skip :: (Eq t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
+skip s = skipBy (not . (`elem` s))
+
+-- | Same as skip, but with a custom comparator
+skipBy :: (TokenStream s) => (t -> Bool) -> Parser r (s t) a -> Parser r (s t) a
+skipBy f = pParse (T.filter f)
+
+-- | Skips standard whitespace characters from a String input
+skipWhitespace :: Parser r String a -> Parser r String a
+skipWhitespace = skipBy (not . isSpace)
+
+-- | Replaces the first token by applying the given function
+replace :: (TokenStream s) => (t -> t) -> Parser r (s t) a -> Parser r (s t) a
+replace f p = Parser (pFunc p . (\x -> f (top x) `cons` rest x))
+
+-- ** Parser Combinators
+
+-- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@
+(<<) :: (Monad m) => m a -> m b -> m a
+a << b = a >>= ((b >>) . return)
+
+-- *** @Alternative@ variants
+
+-- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source)
+-- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@
+sepSome :: Parser r t () -> Parser r t a -> Parser r t [a]
+sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p
+
+-- | Same as @sepSome@, but allows empty lists
+sepMany :: Parser r t () -> Parser r t a -> Parser r t [a]
 sepMany sep p = sepSome sep p <|> return []
-
--- | Removes all tokens from the given list from the input
-skip :: (Eq t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
-skip s = skipBy (not . (`elem` s))
-
--- | Same as skip, but with a custom comparator
-skipBy :: (TokenStream s) => (t -> Bool) -> Parser r (s t) a -> Parser r (s t) a
-skipBy f = pParse (T.filter f)
-
--- | Skips standard whitespace characters from a String input
-skipWhitespace :: Parser r String a -> Parser r String a
-skipWhitespace = skipBy (not . isSpace)
-
--- | Replaces the first token by applying the given function
-replace :: (TokenStream s) => (t -> t) -> Parser r (s t) a -> Parser r (s t) a
-replace f p = Parser (pFunc p . (\x -> f (top x) `cons` rest x))
-
--- | Tries to run the given parser, giving back Just result or Nothing
-try :: Parser r t a -> Parser r t (Maybe a)
-try p = (Just <$> p) <|> return Nothing
-
--- | Parses a character before and a character after the given parser, useful for parentheses
-surround :: (Eq t, Show t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
-surround [l,r] p = consumeSingle l >> p << consumeSingle r
diff --git a/src/Text/LParse/TokenStream.hs b/src/Text/LParse/TokenStream.hs
--- a/src/Text/LParse/TokenStream.hs
+++ b/src/Text/LParse/TokenStream.hs
@@ -1,65 +1,65 @@
-{-|
-Module      : Text.LParse.TokenStream
-Description : Underlying data structure for sequential parsing
-Copyright   : (c) Marcus Völker, 2017-2018
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module contains the `TokenStream` class, an abstraction of lists, similar to `Traversable`, but geared for use with LParse
--}
-module Text.LParse.TokenStream where 
-
-import Data.Either
-import Data.Maybe
-import Data.Traversable
-
-import Prelude hiding (filter,zip,zipWith,drop)
-
--- | `TokenStream` abstracts a list, i.e., something that has a next element to process and a rest afterwards
-class (Functor t, Foldable t) => TokenStream t where
-    -- | `top` gives the next element to process. Similar to `head`
-    top :: t a -> a
-    -- | `rest` gives what is left after processing `top`. Similar to `tail`
-    rest :: t a -> t a
-    -- | `nil` gives the empty `TokenStream`. Similar to `[]`
-    nil :: t a
-    -- | `cons` prepends an element to the `TokenStream`. Similar to `(:)`
-    cons :: a -> t a -> t a
-
-instance TokenStream [] where
-    top = head
-    rest = tail
-    nil = []
-    cons = (:)
-
-instance TokenStream Maybe where
-    top = fromJust
-    rest = const Nothing
-    nil = Nothing
-    cons a _ = Just a
-
-instance TokenStream (Either a) where
-    top = head . rights . return
-    rest x = if isLeft x then x else nil
-    nil = Left undefined
-    cons a _ = Right a
-
--- | `TokenStream` version of `drop` 
-drop ::  (TokenStream s) => Int -> s a -> s a
-drop 0 x = x
-drop n x = rest $ drop (n-1) x
-
--- | `TokenStream` version of `zip`
-zip :: (TokenStream s) => s a -> s b -> s (a,b)
-zip = zipWith (,)
-
--- | `TokenStream` version of `zipWith`
-zipWith :: (TokenStream s) => (a -> b -> c) -> s a -> s b -> s c
-zipWith f l r | null l || null r = nil
-               | otherwise      = f (top l) (top r) `cons` zipWith f (rest l) (rest r)
-
--- | `TokenStream` version of `filter`
-filter :: (TokenStream s) => (a -> Bool) -> s a -> s a
-filter c x | null x = nil
-            | c (top x) = top x `cons` filter c (rest x)
+{-|
+Module      : Text.LParse.TokenStream
+Description : Underlying data structure for sequential parsing
+Copyright   : (c) Marcus Völker, 2017-2018
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module contains the `TokenStream` class, an abstraction of lists, similar to `Traversable`, but geared for use with LParse
+-}
+module Text.LParse.TokenStream where 
+
+import Data.Either
+import Data.Maybe
+import Data.Traversable
+
+import Prelude hiding (filter,zip,zipWith,drop)
+
+-- | `TokenStream` abstracts a list, i.e., something that has a next element to process and a rest afterwards
+class (Functor t, Foldable t) => TokenStream t where
+    -- | `top` gives the next element to process. Similar to `head`
+    top :: t a -> a
+    -- | `rest` gives what is left after processing `top`. Similar to `tail`
+    rest :: t a -> t a
+    -- | `nil` gives the empty `TokenStream`. Similar to `[]`
+    nil :: t a
+    -- | `cons` prepends an element to the `TokenStream`. Similar to `(:)`
+    cons :: a -> t a -> t a
+
+instance TokenStream [] where
+    top = head
+    rest = tail
+    nil = []
+    cons = (:)
+
+instance TokenStream Maybe where
+    top = fromJust
+    rest = const Nothing
+    nil = Nothing
+    cons a _ = Just a
+
+instance TokenStream (Either a) where
+    top = head . rights . return
+    rest x = if isLeft x then x else nil
+    nil = Left undefined
+    cons a _ = Right a
+
+-- | `TokenStream` version of `drop` 
+drop ::  (TokenStream s) => Int -> s a -> s a
+drop 0 x = x
+drop n x = rest $ drop (n-1) x
+
+-- | `TokenStream` version of `zip`
+zip :: (TokenStream s) => s a -> s b -> s (a,b)
+zip = zipWith (,)
+
+-- | `TokenStream` version of `zipWith`
+zipWith :: (TokenStream s) => (a -> b -> c) -> s a -> s b -> s c
+zipWith f l r | null l || null r = nil
+               | otherwise      = f (top l) (top r) `cons` zipWith f (rest l) (rest r)
+
+-- | `TokenStream` version of `filter`
+filter :: (TokenStream s) => (a -> Bool) -> s a -> s a
+filter c x | null x = nil
+            | c (top x) = top x `cons` filter c (rest x)
             | otherwise = filter c (rest x)
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -1,110 +1,112 @@
-module Main where 
-
-import Text.LParse.Parser
-import Text.LParse.Prebuilt
-import Text.LParse.Metaparser
-
-import Control.Applicative
-import Control.Arrow
-import Control.Monad
-import Data.Either
-import Data.List
-import Data.Maybe
-import System.Exit (exitSuccess,exitFailure)
-
-bracks :: Parser r String ()
-bracks = surround "()" nesting
-    <|> surround "[]" nesting
-    <|> surround "{}" nesting
-    <|> surround "<>" nesting
-
-nesting :: Parser r String ()
-nesting = void $ many bracks
-
-succCases :: [(Parser r String (),String)]
-succCases = [
-    (noop,""),
-    (eoi,""),
-    (discard,"lel"),
-    (discard >> eoi,"lorem ipsum"),
-    (consume "prefix","prefixed"),
-    (consume "", "foo"),
-    (consume "", ""),
-    (letter >> eoi, "b"),
-    (digit >> eoi, "4"),
-    (word >> eoi, "banana"),
-    (nesting >> eoi, "({()}[])"),
-    (try word >> integer >> eoi, "123"),
-    (try word >> integer >> eoi, "super123")
-    ]
-
-failCases :: [(Parser r String (),String)]
-failCases = [
-    (eoi,"foo"),
-    (consume "prefix", "freepix"),
-    (consume "prefix", ""),
-    (letter >> eoi, "banana"),
-    (digit >> eoi, "42"),
-    (word >> eoi, "banana bread"),
-    (nesting >> eoi, "({(})[])"),
-    (void $ nParse (=='1') integer "Expected '1'", "234")
-    ]
-
-stringCases :: [(Parser r String String, String, String)]
-stringCases = [
-    (word,"sufficient example","sufficient"),
-    (integer >>> (show <$> bDigits 2), "19", "[1,1,0,0,1]")
-    ]
-
-intCases :: [(Parser r String Integer, String, Integer)]
-intCases = [
-    (integer,"123 is a nice number",123),
-    (digit,"123 is a nice number",1),
-    (sum <$> sepMany (consume " ") integer,"1 4 12 61 192",1+4+12+61+192),
-    (integer >>> (sum <$> bDigits 2), "19", 3),
-    (integer >>> (foldr (\x y -> x + y * 2) 0 <$> bDigits 2), "19", 19),
-    ((\x y -> x*10+y) <$> sInteger <*> (consumeSingle ' ' >> sInteger), "-123 123", (-123*10) + 123),
-    (nParse (=='1') integer "Expected '1'", "123", 123)
-    ]
-
-metaCases :: [(String, String, String)]
-metaCases = 
-    [ ("\\w$","oha","_(oha,$)")
-    , ("\\d*$","123123","_(1,2,3,1,2,3,$)")
-    , ("(\\w\\d)+$","abc3def1g0","_(abc,3,def,1,g,0,$)")
-    , ("abc\\d$","abc3","_(a,b,c,3,$)")
-    , ("t::=abc;%t$","abc","_(t(a,b,c),$)")
-    , ("t::=a|c;%t$","a","_(t(a),$)")
-    , ("t::=a%t|c;%t$","aaac","_(t(a,t(a,t(a,t(c)))),$)")
-    , ("t::=t%s|t;s::=s%t|s;%t$","tststs","_(t(t,s(s,t(t,s(s,t(t,s(s)))))),$)")
-    , ("t::=t%s?;\ns::=s%t?;\n%t$","tststs","_(t(t,s(s,t(t,s(s,t(t,s(s)))))),$)")
-    ]
-
-runTests :: [(Parser (Either String a) t a,t)] -> [Either String a]
-runTests = map (uncurry doParse)
-
-eqTest :: (Eq a, Show a) => (Parser (Either String ()) t a, t, a) -> Either String ()
-eqTest (p,i,e) = parse p i (\r -> if r == e then Right () else Left ("Expected " ++ show e ++ ", but got " ++ show r)) (\e -> Left $ "Parser error: " ++ e)
-
-succTest :: [Either String a] -> IO ()
-succTest res = unless (all isRight res) $ mapM_ putStrLn (lefts res) >> exitFailure
-
-failTest :: [Either String a] -> IO ()
-failTest res = unless (all isLeft res) $ putStrLn "Fail Test Succeeded" >> exitFailure
-
-metaTest :: (String,String,String) -> Either String AST
-metaTest (g,i,a) = either (\a -> Left ("Case " ++ show (g,i)  ++ ": " ++ a)) (\ast -> if show ast == a then Right ast else Left ("Expected AST " ++ a ++ " but got " ++ show ast)) (specParse g i)
-
-main ::IO ()
-main = do
-    let sres = runTests succCases
-    let fres = runTests failCases
-    let seres = map eqTest stringCases
-    let ieres = map eqTest intCases
-    let mres = map metaTest metaCases
-    succTest sres
-    failTest fres
-    succTest seres
-    succTest ieres
-    succTest mres
+module Main where 
+
+import Text.LParse.Parser
+import Text.LParse.Prebuilt
+import Text.LParse.Metaparser
+
+import Control.Applicative
+import Control.Arrow
+import Control.Monad
+import Data.Either
+import Data.List
+import Data.Maybe
+import System.Exit (exitSuccess,exitFailure)
+
+bracks :: Parser r String ()
+bracks = surround "()" nesting
+    <|> surround "[]" nesting
+    <|> surround "{}" nesting
+    <|> surround "<>" nesting
+
+nesting :: Parser r String ()
+nesting = void $ many bracks
+
+succCases :: [(Parser r String (),String)]
+succCases = [
+    (noop,""),
+    (eoi,""),
+    (discard,"lel"),
+    (discard >> eoi,"lorem ipsum"),
+    (consume "prefix","prefixed"),
+    (consume "", "foo"),
+    (consume "", ""),
+    (letter >> eoi, "b"),
+    (digit >> eoi, "4"),
+    (word >> eoi, "banana"),
+    (nesting >> eoi, "({()}[])"),
+    (try word >> integer >> eoi, "123"),
+    (try word >> integer >> eoi, "super123")
+    ]
+
+failCases :: [(Parser r String (),String)]
+failCases = [
+    (eoi,"foo"),
+    (consume "prefix", "freepix"),
+    (consume "prefix", ""),
+    (letter >> eoi, "banana"),
+    (digit >> eoi, "42"),
+    (word >> eoi, "banana bread"),
+    (nesting >> eoi, "({(})[])"),
+    (void $ nParse (=='1') integer "Expected '1'", "234")
+    ]
+
+stringCases :: [(Parser r String String, String, String)]
+stringCases = [
+    (word,"sufficient example","sufficient"),
+    (integer >>> (show <$> bDigits 2), "19", "[1,1,0,0,1]")
+    ]
+
+intCases :: [(Parser r String Integer, String, Integer)]
+intCases = [
+    (integer,"123 is a nice number",123),
+    (digit,"123 is a nice number",1),
+    (sum <$> sepMany (consume " ") integer,"1 4 12 61 192",1+4+12+61+192),
+    (integer >>> (sum <$> bDigits 2), "19", 3),
+    (integer >>> (foldr (\x y -> x + y * 2) 0 <$> bDigits 2), "19", 19),
+    ((\x y -> x*10+y) <$> sInteger <*> (consumeSingle ' ' >> sInteger), "-123 123", (-123*10) + 123),
+    (nParse (=='1') integer "Expected '1'", "123", 123)
+    ]
+
+metaCases :: [(String, String, String)]
+metaCases = 
+    [ ("\\w$","oha","_(oha,$)")
+    , ("\\d*$","123123","_(1,2,3,1,2,3,$)")
+    , ("(\\w\\d)+$","abc3def1g0","_(abc,3,def,1,g,0,$)")
+    , ("abc\\d$","abc3","_(a,b,c,3,$)")
+    , ("t::=abc;%t$","abc","_(t(a,b,c),$)")
+    , ("t::=a|c;%t$","a","_(t(a),$)")
+    , ("t::=a%t|c;%t$","aaac","_(t(a,t(a,t(a,t(c)))),$)")
+    , ("t::=t%s|t;s::=s%t|s;%t$","tststs","_(t(t,s(s,t(t,s(s,t(t,s(s)))))),$)")
+    , ("t::=t%s?;\ns::=s%t?;\n%t$","tststs","_(t(t,s(s,t(t,s(s,t(t,s(s)))))),$)")
+    , ("t::=t%t|s%s|a;s::=s%s|t%t|b;%t","tsb","_(t(t,t(s,s(b))))")
+    , ("p::=\\(%e\\);b::=\\[%e\\];c::={%e};a::=<%e>;e::=(%p|%b|%c|%a)*;%e$","({[]}()[[]])","_(e(p((,e(c({,e(b([,e(),])),}),p((,e(),)),b([,e(b([,e(),])),])),))),$)")
+    ]
+
+runTests :: [(Parser (Either String a) t a,t)] -> [Either String a]
+runTests = map (uncurry doParse)
+
+eqTest :: (Eq a, Show a) => (Parser (Either String ()) t a, t, a) -> Either String ()
+eqTest (p,i,e) = parse p i (\r -> if r == e then Right () else Left ("Expected " ++ show e ++ ", but got " ++ show r)) (\e -> Left $ "Parser error: " ++ e)
+
+succTest :: [Either String a] -> IO ()
+succTest res = unless (all isRight res) $ mapM_ putStrLn (lefts res) >> exitFailure
+
+failTest :: [Either String a] -> IO ()
+failTest res = unless (all isLeft res) $ putStrLn "Fail Test Succeeded" >> exitFailure
+
+metaTest :: (String,String,String) -> Either String AST
+metaTest (g,i,a) = either (\a -> Left ("Case " ++ show (g,i)  ++ ": " ++ a)) (\ast -> if show ast == a then Right ast else Left ("Expected AST " ++ a ++ " but got " ++ show ast)) (specParse g i)
+
+main ::IO ()
+main = do
+    let sres = runTests succCases
+    let fres = runTests failCases
+    let seres = map eqTest stringCases
+    let ieres = map eqTest intCases
+    let mres = map metaTest metaCases
+    succTest sres
+    failTest fres
+    succTest seres
+    succTest ieres
+    succTest mres
     exitSuccess
